/* $Id: fn_param.c,v 1.3 2009/01/04 15:49:31 kiesling Exp $ -*-c-*-**/

/*
  This file is part of Ctalk.
  Copyright  2005-2009 Robert Kiesling, ctalk@ctalklang.org.
  Permission is granted to copy this software provided that this copyright
  notice is included in all source code modules.

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <stdio.h>
#include <string.h>
#include "object.h"
#include "message.h"
#include "ctalk.h"
#include "lex.h"
#include "typeof.h"
#include "cvar.h"
#include "parser.h"

int function_param_states[] = {
#include "paramstate.h"
  -1,   0
};

#define FUNCTION_PARAM_STATE_COLS 2

#define FUNCTION_PARAM_STATE(x) (check_state ((x), messages, \
              function_param_states, FUNCTION_PARAM_STATE_COLS))

/*
 *  Call with start_ptr and end_ptr pointing to the opening
 *  and closing parentheses of the argument list.
 *  Note the states for prototype declarations, above.  If
 *  the function encounters a prototype declaration without
 *  the parameter name, it skips the declaration.  This is
 *  consistent with is_c_var_declaration_msg ().
 */

int fn_param_declarations (MESSAGE_STACK messages, int start_ptr, int end_ptr,
			       char **args, int *n_args) {
  int j, k;
  int state;
  int last_state = ERROR;               /* Avoid warnings. */
  int n_parens;
  int param_start_ptr = start_ptr;
  MESSAGE *m_tok;
  char param_buf[MAXMSG];

  for (j = start_ptr, *n_args = 0, n_parens = 0; j >= end_ptr; j--) {

    if ((state = FUNCTION_PARAM_STATE (j)) == ERROR) {
      /* Checking here for white space simplifies the state 
	 diagram, and check_state skips the whitespace tokens,
	 but the whitespace still ends up in the parameter in 
	 the args array.
	 
	 GNU C attributes are not in the state transition diagram,
	 either.
      */
#ifdef __GNUC__
      if ((j != end_ptr) || 
	  ((j == end_ptr) && (!fn_has_gnu_attribute (messages, j)))) {
 	if (!M_ISSPACE (messages[j])) {
 	  warning (messages[j], "Function argument is not a C expression (in progress).");
	  return ERROR;
	}
      }
#else
      if (!M_ISSPACE (messages[j]))
	error (messages[j], "Parse error.");
#endif
    }

    m_tok = messages[j];

    switch (m_tok -> tokentype)
      {
      case LABEL:
	if (last_state == 0 || last_state == 6) {
	  if (n_parens == 1) {
	    if ((state == 4) && (last_state == 0))  /* "void" alone. */
	      param_start_ptr = ERROR;
	    else
	      param_start_ptr = j;
	  }
	}
	break;
      case CLOSEPAREN:
	--n_parens;
	if ((last_state != 19) && (last_state != 20)) {
	  /*
	   *  Make sure that the paren is the end 
	   *  of the parameter list.
	   */
	  if ((param_start_ptr != ERROR) && (j == end_ptr)) {
	    for (k = param_start_ptr, *param_buf = '\0'; k > j; k--)
	      strcat (param_buf, messages[k] -> name);
	    args[(*n_args)++] = strdup (param_buf);
	  }
	}
	break;
      case ARGSEPARATOR:
	if ((last_state != 19) && (last_state != 20)) {
	  if (param_start_ptr != ERROR) {
	    for (k = param_start_ptr, *param_buf = '\0'; k > j; k--)
	      strcat (param_buf, messages[k] -> name);
	    args[(*n_args)++] = strdup (param_buf);
	  }
	}
	break;
      case ARRAYOPEN:
	j = find_arg_end (messages, j);
	++j;
	break;
      case ELLIPSIS:
	param_start_ptr = j;
	break;
      case OPENPAREN:
	++n_parens;
	if (((state == 0) && (last_state == 96)) ||/* Typecast: , <(>label..*/
	    ((state == 0) && (last_state == 97))){ /* Typecast: (<(>label...*/
	  param_start_ptr = j;
	}
	break;
      case INTEGER:
	if ((last_state == 82) || (last_state == 48)) {
	  param_start_ptr = j;
	}
	break;
      case DOUBLE:  /* Float or double. */
	if ((last_state == 83) || (last_state == 84) || 
	    (last_state == 49) || (last_state == 50)) {
	  param_start_ptr = j;
	}
	break;
      case LITERAL:
	if ((last_state == 79) || (last_state == 16)) {
	  param_start_ptr = j;
	}
	break;
      case LITERAL_CHAR:
	if ((last_state == 80) || (last_state == 100)) {
	  param_start_ptr = j;
	}
	break;
      case LONG:
	if ((last_state == 85) || (last_state == 51)) {
	  param_start_ptr = j;
	}
	break;
      case LONGLONG:
	if ((last_state == 86) || (last_state == 52)) {
	  param_start_ptr = j;
	}
	break;
      case AMPERSAND:
	if ((last_state == 87) || (last_state == 53)) {
	  param_start_ptr = j;
	}
	break;
      case EXCLAM:
	if ((last_state == 88) || (last_state == 54)) {
	  param_start_ptr = j;
	}
	break;
      case INCREMENT:
	if ((last_state == 89) || (last_state == 55)) {
	  param_start_ptr = j;
	}
	break;
      case DECREMENT:
	if ((last_state == 90) || (last_state == 56)) {
	  param_start_ptr = j;
	}
	break;
      case BIT_COMP:
	if ((last_state == 91) || (last_state == 57)) {
	  param_start_ptr = j;
	}
	break;
      case ASTERISK:
	if ((last_state == 92) || (last_state == 58)) {
	  param_start_ptr = j;
	}
	break;
      default:
	break;
      }

    /* See the comment above. */
    if ((m_tok -> tokentype != WHITESPACE) &&
	(m_tok -> tokentype != NEWLINE))
    last_state = state;
  }

  return SUCCESS;
}

