%{
# define	Y_TAB_H
# include	"crunch.h"

extern int crunch_lang;
%}

%union {
	char		 *sval;
	struct node_leaf *nval;
	struct func_t	*fval;	/* Function.		*/
	double		floatval;
	long		 ival;
	enum ntypes	 eval;	/* Same as ival but enumerated so the 	*/
				/* debugger can see them.		*/
	}
%token	<ival>	K_BLOCK		/* Used as a marker for {..} blocks for */
				/* the compilation code only - not a syntactic */
				/* token.				*/
%token	<ival>	K_INIT		/* Used to mark place in tree where	*/
				/* we have a set of initialisors a (Head_p)*/
				/* instead of a (node_t *).		*/
%token 	<ival>	K_SWITCH K_CASE K_DEFAULT 
%token 	<ival>	K_LIST K_INT K_STRING K_DECLARE K_GLOBAL K_FLOAT K_DOUBLE
%token	<ival>	K_NOOP K_FUNCALL K_DOTS
%token	<ival>	K_STRUCT K_UNION 
%token	<ival>	K_CONST	K_VOLATILE
%token	<ival>	K_CHAR K_LONG K_SHORT K_UNSIGNED K_VOID K_SIGNED
%token	<ival>	K_SIZEOF

%token	<ival>	K_IF K_ELSE 
%token	<ival>	K_FOR K_BREAK K_BREAKSW K_CONTINUE K_WHILE K_DO
%token	<ival>	K_RETURN
%token	<ival>	K_INITLIST K_ENUM
%token	<ival>	K_AUTO K_REGISTER K_STATIC K_EXTERN K_TYPEDEF

%token	<sval>	TYPEDEF_NAME

%token 	<ival>	OCURLY CCURLY OROUND CROUND COMMA SEMICOLON COLON LSHIFT RSHIFT
%token	<ival>	QUESTION
%token	<ival>	OSQUARE CSQUARE	/* [ and ] */
%token	<ival>	DOT ARROW		/* For C-like structure members	*/
%token	<ival>	INTEGER
%token	<floatval> FLOAT_CONST
%token	<ival>	EQ PLUS_EQ MINUS_EQ MUL_EQ DIV_EQ MOD_EQ AND_EQ OR_EQ XOR_EQ
%token	<ival>	LSHIFT_EQ RSHIFT_EQ
%token	<ival>	EQ_OP NE_OP GT_OP GE_OP LT_OP LE_OP
%token	<ival>	PLUS MINUS MUL DIV MOD OR AND XOR NOT COMPLEMENT
%token	<ival>	PLUS_PLUS MINUS_MINUS POST_PLUS_PLUS POST_MINUS_MINUS
%token	<ival>	CAND COR
%token 	<sval>	SYMBOL STRING

%type	<ival>	assignment_operator
%type	<ival>	unary_operator
%type	<ival>	struct_or_union
%type	<ival>	storage_class_specifier
%type	<ival>	decl_specs
%type	<ival>	type_specifier
%type	<ival>	type_qualifier_list type_qualifier
%type	<ival>	specifier_qualifier_list
%type	<ival>	struct_or_union_specifier

%type	<nval>	pointer
%type	<nval>	declaration_specifiers 
%type	<nval>	struct_declaration struct_declaration_list

%type	<nval>	direct_declarator declarator
%type	<nval>	xstmts

%type	<nval>	labeled_statement
%type	<nval>	expression_statement
%type	<nval>	parameter_declaration
%type	<nval>	parameter_list
%type	<nval>	parameter_type_list
%type	<nval>	struct_declarator struct_declarator_list
%type	<nval>	init_declarator_list init_declarator
%type	<nval>	enum_specifier
%type	<nval>	identifier_list
%type	<nval>	initializer initializer_list
%type	<nval>	enumerator_list enumerator
%type	<nval>	declaration_list declaration
%type	<nval>	compound_statement
%type	<nval>	simple_if selection_statement
%type	<nval>	for_stmt
%type	<nval>	jump_statement
%type	<nval>	statement statement_list
%type	<nval>	expression
%type	<nval>	assignment_expression
%type	<nval>	conditional_expression
%type	<nval>	constant_expression
%type	<nval>	logical_OR_expression
%type	<nval>	logical_AND_expression
%type	<nval>	inclusive_OR_expression
%type	<nval>	exclusive_OR_expression
%type	<nval>	AND_expression
%type	<nval>	equality_expression
%type	<nval>	relational_expression
%type	<nval>	shift_expression
%type	<nval>	additive_expression
%type	<nval>	multiplicative_expression
%type	<nval>	cast_expression
%type	<nval>	unary_expression
%type	<nval>	postfix_expression
%type	<nval>	primary_expression
%type	<nval>	iteration_statement
%type	<nval>	argument_expression_list
%type	<fval>	function_definition

%left	COMMA
%right	EQ PLUS_EQ MINUS_EQ MUL_EQ DIV_EQ MOD_EQ AND_EQ OR_EQ XOR_EQ LSHIFT_EQ RSHIFT_EQ
%right	QUESTION COLON
%left	COR
%left	CAND
%left	OR
%left	XOR
%left	AND
%left	EQ_OP NE_OP
%left	LE_OP GE_OP LT_OP GT_OP 
%left	LSHIFT RSHIFT
%left	PLUS MINUS
%left	MUL DIV MOD
%right	PLUS_PLUS MINUS_MINUS COMPLEMENT NOT UMINUS UPLUS
%left	OROUND CROUND OSQUARE CSQUARE DOT ARROW

%nonassoc	K_IF 
%nonassoc	K_ELSE
%{

# define	YYDEBUG	1

# define	MAX_LOOP_NESTING	128	/* Level of loops/switches nesting */

int	loop_stack[MAX_LOOP_NESTING];
int	loop_top;

int	inside_struct = FALSE;
int	anon_cnt = 0;

extern Head_p	hd_stmt, hd_init, hd_arglist,
		hd_globals, hd_syms, hd_funcs, hd_undef;
extern node_t	*main_tree;
extern int block_level, break_level, continue_level;
extern Head_p hd_block;
int	switch_level;	/* Used to detect 'case' statements outside of 	*/
			/* switches.					*/
Head_p	hd_struct;	/* Stack of structures which we are defining. 	*/
symbol_t	*struct_sp;	/* Pointer to symbol table entry for current */
				/* structure. Used so that we can associate the */
				/* structure name with a variable of type	*/
				/* struct/union.				*/
char *strdup();
extern int indent;
extern char *current_fn;

/**********************************************************************/
/*   The  following  function  keeps  track  of  whether we are in a  */
/*   switch  or  a  loop  construct  so  that when we see a break we  */
/*   know whether to generate code for it or not.		      */
/**********************************************************************/
void
enter_loop_or_switch(type)
int	type;
{
	if (loop_top >= MAX_LOOP_NESTING)
		yyerror("Maximum loop/switch nesting exceeded.");
	loop_stack[loop_top++] = type;
}
/**********************************************************************/
/*   Exit from a loop or switch construct.			      */
/**********************************************************************/
void
exit_loop_or_switch()
{
	assert(loop_top > 0);
	loop_top--;
}
%}
%start program	
%%
program :		/* empty */
		| program 
		  external_declaration
		;
external_declaration:
		  function_definition
		  	{ func_t	*fp = $1;
			  char	*name = pop_id();
			  current_fn = NULL;
			  if (strcmp(name, "main") == 0) {
			  	if (hd_arglist && ll_first(hd_arglist) != NULL)
					yywarn("arguments to main() are ignored.");
				main_tree = fp->f_body;
			  	}
			  else
				compile_func(name, fp->f_arglist, fp->f_body);
			  chk_free((void *) fp);
			  chk_free((void *) name);
			  free_list(&hd_arglist);
			  free_list(&hd_undef);
			}
		| declaration
			{ }
		;
function_definition :
		  declaration_specifiers 
		  declarator 
		  	{ start_function($1, $2); 
			  pop_decl();
			}
		  declaration_list 
		  compound_statement
		  	{ func_t *fp = (func_t *) chk_alloc(sizeof(func_t));
			  fp->f_type = $1;
			  fp->f_name = $2;
			  fp->f_arglist = $4;
			  fp->f_body = $5;
			  $$ = fp;
			}
		| 
		  declarator 
		  	{ start_function((node_t *) NULL, $1); }
		  declaration_list 
		  compound_statement
		  	{ func_t *fp = (func_t *) chk_alloc(sizeof(func_t));
			  fp->f_type = NULL;
			  fp->f_name = $1;
			  fp->f_arglist = $3;
			  fp->f_body = $4;
			  $$ = fp;
			}
		| declaration_specifiers 
		  declarator 
		  	{ start_function($1, $2);
			  enter_block();
			  pop_decl();
			}
		  compound_statement
		  	{ func_t *fp = (func_t *) chk_alloc(sizeof(func_t));
			  fp->f_type = $1;
			  fp->f_name = $2;
			  fp->f_arglist = (node_t *) hd_arglist;
			  fp->f_body = $4;
			  $$ = fp;
			  exit_block();
			  if (block_level)
			  	exit_block();
			}
		| 
		  declarator 
		  	{ 
			  start_function((node_t *) NULL, $1);
			  enter_block();
			}
		  compound_statement
		  	{ func_t *fp;
			  fp = (func_t *) chk_alloc(sizeof(func_t));
			  fp->f_type = NULL;
			  fp->f_name = (node_t *) peek_id();
			  fp->f_arglist = (node_t *) hd_arglist;
			  fp->f_body = $3;
			  $$ = fp;
			  exit_block();
			  if (block_level)
				exit_block();
			}
		;
declaration:
		  declaration_specifiers
		  init_declarator_list
		  SEMICOLON
		  		{ pop_decl(); 
				}
		|
		  declaration_specifiers
		  SEMICOLON
		  		{ $$ = $1; 
				  pop_decl();
				}
		| error SEMICOLON 	{ $$ = NULL; }
		| error CCURLY		{ $$ = NULL; }
		;
declaration_list:
		  declaration
		  		{ $$ = NULL; }
		| declaration_list declaration
				{ $$ = NULL; }
		;
declaration_specifiers:
		  decl_specs
		  	{push_decl($1); }
		;
decl_specs:
		  storage_class_specifier
		  decl_specs
		  			{ $$ = $2 | $1; }
		|
		  storage_class_specifier
		  			{ $$ = $1; }
		| type_specifier
		  decl_specs
		  			{ $$ = $1 | ($2 & ~TY_MASK) | (($2 & TY_MASK) << TY_SHIFT); }
		| type_specifier	{ $$ = $1; }
		| type_qualifier
		  decl_specs
		  			{ $$ = $2 | $1; }
		| type_qualifier	{ $$ = $1; }
		;
storage_class_specifier:
		  K_AUTO		{ $$ = ((long) SC_AUTO) << SC_SHIFT; }
		| K_REGISTER		{ $$ = ((long) SC_REGISTER) << SC_SHIFT; }
		| K_STATIC		{ $$ = ((long) SC_STATIC) << SC_SHIFT; }
		| K_EXTERN		{ $$ = ((long) SC_EXTERN) << SC_SHIFT; }
		| K_TYPEDEF		{ $$ = ((long) SC_TYPEDEF) << SC_SHIFT; }
		| K_GLOBAL		{ $$ = ((long) SC_GLOBAL) << SC_SHIFT; }
		;
type_specifier :
		  K_INT		{ $$ = TY_INT; }
		| K_STRING	{ $$ = TY_STRING; }
		| K_LIST	{ $$ = TY_LIST; }
		| K_DECLARE	{ $$ = TY_DECLARE; }
		| K_CHAR	{ $$ = TY_CHAR; }
		| K_LONG	{ $$ = TY_LONG; }
		| K_SHORT	{ $$ = TY_SHORT; }
		| K_UNSIGNED	{ $$ = TY_UNSIGNED; }
		| K_VOID	{ $$ = TY_VOID; }
		| K_DOUBLE	{ $$ = TY_DOUBLE; }
		| K_FLOAT	{ $$ = TY_FLOAT; }
		| TYPEDEF_NAME	{ $$ = get_typedef_type($1); }
		| struct_or_union_specifier
				{ $$ = $1 == TY_STRUCT ? TY_STRUCTI : TY_UNIONI; }
		| enum_specifier
				{ $$ = TY_ENUM; }
		;
type_qualifier_list :
		  type_qualifier
				{ $$ = $1; }
		| type_qualifier_list type_qualifier
				{ $$ = $1 | $2; }
		;
type_qualifier :
		  K_CONST	{ $$ = 1 << TQ_SHIFT; }
		| K_VOLATILE	{ $$ = 2 << TQ_SHIFT; }
		;
struct_or_union_specifier :
		  struct_or_union SYMBOL
				{
				defining_struct($2, $1);
				chk_free((void *) $2);
				}
		  OCURLY 
		  struct_declaration_list
		  CCURLY
				{ 
				  ending_struct();
				  $$ = $1;
				}
		| struct_or_union
				{ char	buf[32];
				  sprintf(buf, "*ANON_%d*", anon_cnt++);
				  defining_struct(buf, $1);
				}
		  OCURLY 
		  struct_declaration_list
		  CCURLY
				{ 
				  ending_struct();
				  $$ = $1;
				}
		| struct_or_union SYMBOL
				{
				  struct_sp = lookup_struct($2);
				  chk_free((void *) $2);
				  $$ = $1;
				}
		;
struct_or_union :
		  K_STRUCT	{ $$ = TY_STRUCT; }
		| K_UNION	{ $$ = TY_UNION; }
		;
struct_declaration_list :
			struct_declaration
		|	struct_declaration_list struct_declaration
		;
init_declarator_list:
		  init_declarator	
		  		{ add_decl($1); }
		| init_declarator_list COMMA init_declarator
				{ add_decl($3); }
		;
init_declarator:
		  declarator
		  		{ $$ = $1; }
		| declarator EQ initializer
				{ node_t *np = node((long) EQ, 
						   string_node(peek_id()), 
						   (node_t *) $3);
				  if (block_level == 0) {
					ll_push(hd_globals, (char *) np);
					}
				  else {
				  	add_stmt(&hd_init, np);
				  	}
				}
		
struct_declaration :
		specifier_qualifier_list 
				{ push_decl($1); }
		struct_declarator_list SEMICOLON
				{ pop_decl(); }
		;
specifier_qualifier_list :
		  type_specifier specifier_qualifier_list
		  		{ $$ = $1 | ($2 & ~TY_MASK) | (($2 & TY_MASK) << TY_SHIFT); }
		| type_specifier
				{ $$ = $1; }
		| type_qualifier specifier_qualifier_list
				{ $$ = ($1 << TQ_SHIFT) | $2; }
		| type_qualifier
				{ $$ = $1; }
		;
struct_declarator_list :
		  struct_declarator
				{ add_member($1); }
		| struct_declarator_list COMMA struct_declarator
				{ add_member($3); }
		;
struct_declarator :
		  declarator
				{ $$ = $1; }
		| declarator COLON constant_expression
				{ $$ = $1; }
		| COLON constant_expression
				{ 
				static int bit_field = 1;
				char buf[64];
				sprintf(buf, "<bit-field-%d>", bit_field++);
				push_id(buf);
				$$ = NULL; 
				}
		;
enum_specifier :
		  K_ENUM SYMBOL OCURLY enumerator_list CCURLY
		  		{ $$ = $4; }
		| K_ENUM OCURLY enumerator_list CCURLY
				{ $$ = $3; }
		| K_ENUM SYMBOL
				{ $$ = NULL; }
		;
enumerator_list:
		  enumerator
		  		{ $$ = $1; }
		| enumerator_list COMMA enumerator
				{ $$ = node((long) K_ENUM, $1, $3); }
		;
enumerator:
		  SYMBOL
		  		{ $$ = string_node($1); }
		| SYMBOL EQ constant_expression
				{ $$ = string_node($1); }
		;
		
declarator:
			pointer direct_declarator
				{ $$ = node((long) TO_PTR, $2, (node_t *) NULL); }
		|	direct_declarator
				{ $$ = $1; 
				}
		;
direct_declarator:
		  SYMBOL
		  	{ push_id($1);
			$$ = NULL;
			}
		| OROUND declarator CROUND
			{
			$$ = $2;
			}
		| direct_declarator OSQUARE constant_expression CSQUARE
			{ $$ = node((long) TO_ARRAY, $1, (node_t *) $3); }
		| direct_declarator OSQUARE CSQUARE
			{ $$ = node((long) TO_ARRAY, $1, (node_t *) NULL); }
		| direct_declarator OROUND
			  	{ enter_block();}
		  parameter_type_list 		
				{ 
				  if (block_level > 1)
				  	exit_block();
				}
		  CROUND
				{ $$ = node((long) TO_FUNC, $1, (node_t *) $4); }
		| direct_declarator OROUND identifier_list CROUND
			{ $$ = node((long) TO_FUNC, $1, (node_t *) $3); }
		| direct_declarator OROUND CROUND
			{ $$ = node((long) TO_FUNC, $1, (node_t *) NULL); }
		;
pointer :
		  MUL 
		  type_qualifier_list
		  	{ $$ = node((long) TO_PTR, (node_t *) $2, (node_t *) NULL); }
		| MUL 
		  pointer
		  	{ $$ = node((long) TO_PTR, $2, (node_t *) NULL); }
		| MUL 
		  type_qualifier_list pointer
		  	{ $$ = node((long) TO_PTR, $3, (node_t *) NULL); }
		| MUL
		  	{ $$ = node((long) TO_PTR, (node_t *) NULL, (node_t *) NULL); }
		;
parameter_type_list :
		  parameter_list
		  		{ $$ = $1; }
		| parameter_list COMMA K_DOTS
		  		{ $$ = $1; }
		| K_DOTS	/* crunch language only */
			{
			if (!crunch_lang) {
				yyerror("Ellipsis must be preceded by at least one parameter type.");
				}
			$$ = NULL;
			}
		;
parameter_list :
		  parameter_declaration
		  		{ $$ = $1; }
		| parameter_list COMMA parameter_declaration
				{ $$ = node((long) K_NOOP, $1, $3); }
		;
		
parameter_declaration :
		  declaration_specifiers declarator
		  		{ node_t *np;
				char *cp = pop_id();
				if (hd_arglist == NULL)
					hd_arglist = ll_init();
				np = node(peek_decl(), (node_t *) NULL, string_node1(cp));
add_symbol(cp, np, peek_decl());
				ll_append(hd_arglist, (char *) np);
				pop_decl();
				$$ = $2; 
				}
/*		| declaration_specifiers abstract_declarator*/
		| COMPLEMENT declaration_specifiers 
				{ node_t *np;
				if (!crunch_lang) {
					yyerror("invalid parameter declaration.");
					}
				if (hd_arglist == NULL)
					hd_arglist = ll_init();
				np = node(peek_decl(), (node_t *) NULL, (node_t *) NULL);
				ll_append(hd_arglist, (char *) np);
				$$ = NULL; 
				pop_decl();
				}
		| COMPLEMENT declaration_specifiers declarator
				{ node_t *np;
				if (!crunch_lang) {
					yyerror("invalid parameter declaration.");
					}
				if (hd_arglist == NULL)
					hd_arglist = ll_init();
				np = node(peek_decl(), (node_t *) NULL, (node_t *) NULL);
				ll_append(hd_arglist, (char *) np);
				$$ = NULL; 
				pop_decl();
				}
		| declaration_specifiers 
				{ $$ = NULL; 
				pop_decl();
				}
		;
identifier_list :
		  SYMBOL
		  		{ $$ = string_node1($1); }
		| identifier_list COMMA SYMBOL
			{ node_t *np = string_node1($3);
			  $$ = node((long) K_NOOP, $1, np);
			  }
		;
initializer:
		  assignment_expression
		| OCURLY initializer_list CCURLY
			{ $$ = node((long) K_INITLIST, (node_t *) NULL, $2); }
		| OCURLY initializer_list COMMA CCURLY
			{ $$ = node((long) K_INITLIST, (node_t *) NULL, $2); }
		;
initializer_list :
		  initializer
		| initializer_list COMMA initializer
			{ $$ = node((long) K_NOOP, $1, $3); }
		;
/*type_name :
		  specifier_qualifier_list abstract_declarator
		| specifier_qualifier_list 
		;
*/
/*abstract_declarator :
		  pointer
		| pointer direct_abstract_declarator
		| direct_abstract_declarator
		;
direct_abstract_declarator :
		  OROUND abstract_declarator CROUND
		| direct_abstract_declarator OSQUARE constant_expression CSQUARE
		| direct_abstract_declarator OSQUARE CSQUARE
		| OSQUARE constant_expression CSQUARE
		| OSQUARE CSQUARE
		| direct_abstract_declarator OROUND constant_expression CROUND
		| direct_abstract_declarator OROUND CROUND
		| OROUND constant_expression CROUND
		| OROUND CROUND
		;
*/
/*
typedef_name :
		  SYMBOL
		;
*/
statement:
		  labeled_statement		{ add_stmt(&hd_stmt, $1); }
		| expression_statement		{ add_stmt(&hd_stmt, $1); }
		| compound_statement		{ add_stmt(&hd_stmt, $1); }
		| selection_statement		{ add_stmt(&hd_stmt, $1); }
		| iteration_statement		{ add_stmt(&hd_stmt, $1); }
		| jump_statement		{ add_stmt(&hd_stmt, $1); }
		;
labeled_statement :
		  SYMBOL COLON statement
		  		{ $$ = $3; 
				  chk_free((void *) $1);
				}
		| K_DEFAULT COLON
				{ 
				  if (switch_level == 0) {
				  	yyerror("'default' not in switch statement");
					}
				  start_case(NULL);
				}
		  statement
		  		{ $$ = NULL; }
				
		| K_CASE 
				{ 
				  if (switch_level == 0) {
				  	yyerror("case not in switch statement");
					}
				}
		  expression COLON 
		  		{ start_case($3); }
		  statement
		  		{ $$ = NULL; }
		;
expression_statement :
		  expression SEMICOLON
		  		{ $$ = $1; }
		| SEMICOLON
				{ $$ = NULL; }
		;
compound_statement :
		  OCURLY 
		  CCURLY
				{ $$ = NULL; }
		| OCURLY 
		  		{ 
				enter_block(); 
				}
		  declaration_list 
		  xstmts
		  CCURLY
		  		{ node_t *np1;
				  Head_p hd = hd_stmt;
				  np1 = exit_block();
				  if (hd == NULL)
				  	$$ = np1;
				  else {
					  if (np1)
						  ll_push(hd, (char *) np1);
					  $$ = node((long) K_BLOCK, (node_t *) NULL, (node_t *) hd);
					  }
				}
		| OCURLY 
		  		{ 
				  enter_block();
				}
		  statement_list
		  CCURLY
				{ 
				  $$ = node((long) K_BLOCK, (node_t *) NULL, (node_t *) hd_stmt); 
				  exit_block();
				}
		;
xstmts:
		  /* empty */
		  	{ $$ = NULL; }
		| statement_list 
			{ $$ = $1; }
		;
statement_list:
		  statement
		| statement_list statement
		;
simple_if :
		  K_IF OROUND expression CROUND 
		  		{ 
				enter_block();
				}
		  statement
		  		{ node_t *np = alloc_node((long) K_IF);
				  block_t *bp;
				  np->left = $3;
				  block_level--;
				  /***********************************************/
				  /*   If  we  got a null statement then we put  */
				  /*   a   no-op  in  there  otherwise  we  can  */
				  /*   generate  bad  code  for an if-then-else  */
				  /*   clause.					 */
				  /***********************************************/
				  if (hd_stmt == NULL || ll_first(hd_stmt) == NULL ||
				      ll_elem(ll_first(hd_stmt)) == NULL) 
				  	add_stmt(&hd_stmt, 
						node(K_FUNCALL, string_node("nothing"), (node_t *) NULL));
				  np->right = (node_t *) hd_stmt;
				  bp = (block_t *) ll_elem(ll_first(hd_block));
				  hd_stmt = bp->b_stmt;
				  hd_init = bp->b_init;
				  chk_free((void *) bp);
				  ll_pop(hd_block);
				  $$ = np;
				  }
		;
selection_statement:
		  simple_if
				{ enter_block(); }
		  K_ELSE statement
		  		{ node_t *np = alloc_node((long) K_ELSE);
				  block_t *bp;
				  np->left = $1;
				  block_level--;
				  np->right = (node_t *) hd_stmt;
				  bp = (block_t *) ll_elem(ll_first(hd_block));
				  hd_stmt = bp->b_stmt;
				  hd_init = bp->b_init;
				  chk_free((void *) bp);
				  ll_pop(hd_block);
				  $$ = np;
				  }
		| simple_if
		  		{ $$ = $1; }
		| K_SWITCH OROUND expression CROUND 
				{
				enter_loop_or_switch(INSIDE_SWITCH);
				start_switch();
				break_level++;
				}
		  statement
				{
				exit_loop_or_switch();
				$$ = end_switch($3);
				break_level--;
				}
		;
iteration_statement :
		  K_WHILE OROUND expression CROUND 
		  		{ enter_loop(); }
		  statement
				{node_t	*np = alloc_node((long) K_WHILE);
				np->left = $3;
				np->right = (node_t *) hd_stmt;
				exit_loop();
				$$ = np;
				}
		|
		  /***********************************************/
		  /*   Convert do statement of the form:	 */
		  /*   						 */
		  /*   	  do {					 */
		  /*   	      stmts;				 */
		  /*   	      }					 */
		  /*   	  while (expr);				 */
		  /*   						 */
		  /*   to the following:			 */
		  /*   						 */
		  /*   	   while (1) {				 */
		  /*   	       stmts;	    			 */
		  /*   	       if (!(expr))    			 */
		  /*   		    break;			 */
		  /*   	       }	    			 */
		  /***********************************************/
		  K_DO 
		  		{ enter_loop(); }
		  statement 
		  		{break_level--;
				continue_level--;
				}
		  K_WHILE OROUND expression CROUND SEMICOLON
		  		{ node_t *np = alloc_node((long) K_WHILE);
				  node_t *np1 = alloc_node((long) K_IF);
				  node_t	*np2 = alloc_node((long) NOT);
				  Head_p	hd;
				  np->left = (node_t *) new_number(1L);
				  add_stmt(&hd_stmt, np1);
				  np->right = (node_t *) hd_stmt;
				  np2->left = $7;
				  np1->left = np2;
				  hd = ll_init();
				  ll_push(hd, (char *) alloc_node((long) K_BREAK));
				  np1->right = (node_t *) hd;
				  $$ = np;
				  /***********************************************/
				  /*   exit_loop()  plays  with these variables  */
				  /*   so pretend we're still inside this loop.  */
				  /***********************************************/
		  		  break_level++;
				  continue_level++;
				  exit_loop();
				}
				  
		| for_stmt 
		;
for_stmt :
		  K_FOR OROUND expression SEMICOLON expression SEMICOLON expression CROUND 
		  		{ enter_loop(); }
		  statement
		  		{ node_t *np = alloc_node((long) K_FOR);
				  node_t *np1 = alloc_node((long) K_NOOP);
				  node_t *np2 = alloc_node((long) K_NOOP);
				  np->left = $3;
				  np->right = np1;
				  np1->left = $5;
				  np1->right = np2;
				  np2->left = $7;
				  np2->right = (node_t *) hd_stmt;
				  $$ = np;
				  exit_loop();
				  }
		| K_FOR OROUND expression SEMICOLON expression SEMICOLON CROUND 
		  		{ enter_loop(); }
		  statement
		  		{ node_t *np = alloc_node((long) K_FOR);
				  node_t *np1 = alloc_node((long) K_NOOP);
				  node_t *np2 = alloc_node((long) K_NOOP);
				  np->left = $3;
				  np->right = np1;
				  np1->left = $5;
				  np1->right = np2;
				  np2->left = NULL;
				  np2->right = (node_t *) hd_stmt;
				  $$ = np;
				  exit_loop();
				  }
		
		| K_FOR OROUND expression SEMICOLON SEMICOLON expression CROUND 
		  		{ enter_loop(); }
		  statement
		  		{ node_t *np = alloc_node((long) K_FOR);
				  node_t *np1 = alloc_node((long) K_NOOP);
				  node_t *np2 = alloc_node((long) K_NOOP);
				  np->left = $3;
				  np->right = np1;
				  np1->left = (node_t *) new_number(1L);
				  np1->right = np2;
				  np2->left = $6;
				  np2->right = (node_t *) hd_stmt;
				  $$ = np;
				  exit_loop();
				  }
		| K_FOR OROUND expression SEMICOLON SEMICOLON CROUND 
		  		{ enter_loop(); }
		  statement
		  		{ node_t *np = alloc_node((long) K_WHILE);
				  node_t *np1 = alloc_node((long) K_NOOP);
				  np->left = new_number(1L);
				  np->right = (node_t *) hd_stmt;
				  np1->left = $3;
				  np1->right = np;
				  $$ = np1;
				  exit_loop();
				  }
		| K_FOR OROUND SEMICOLON expression SEMICOLON expression CROUND 
		  		{ enter_loop(); }
		  statement
		  		{ node_t *np = alloc_node((long) K_FOR);
				  node_t *np1 = alloc_node((long) K_NOOP);
				  node_t *np2 = alloc_node((long) K_NOOP);
				  np->left = NULL;
				  np->right = np1;
				  np1->left = $4;
				  np1->right = np2;
				  np2->left = $6;
				  np2->right = (node_t *) hd_stmt;
				  $$ = np;
				  exit_loop();
				  }
		| K_FOR OROUND SEMICOLON expression SEMICOLON CROUND 
		  		{ enter_loop(); }
		  statement
		  		{ node_t *np = alloc_node((long) K_WHILE);
				  np->left = $4;
				  np->right = (node_t *) hd_stmt;
				  $$ = np;
				  exit_loop();
				  }
		| K_FOR OROUND SEMICOLON SEMICOLON expression CROUND 
		  		{ enter_loop(); }
		  statement
		  		{ node_t *np = alloc_node((long) K_FOR);
				  node_t *np1 = alloc_node((long) K_NOOP);
				  node_t *np2 = alloc_node((long) K_NOOP);
				  np->left = NULL;
				  np->right = np1;
				  np1->left = new_number(1L);
				  np1->right = np2;
				  np2->left = $5;
				  np2->right = (node_t *) hd_stmt;
				  $$ = np;
				  exit_loop();
				  }
		| K_FOR OROUND SEMICOLON SEMICOLON CROUND 
		  		{ enter_loop(); }
		  statement
				{ node_t *np = alloc_node((long) K_WHILE);
				  np->left = new_number(1L);
				  np->right = (node_t *) hd_stmt;
				  $$ = np;
				  exit_loop();
				}
		;
		
jump_statement :
		  K_BREAK SEMICOLON
			{ 
			if (break_level <= 0)
				yyerror("'break' not inside loop/switch stmt");
			if (loop_stack[loop_top-1] == INSIDE_LOOP)
				$$ = alloc_node((long) K_BREAK);
			else
				$$ = alloc_node((long) K_BREAKSW);
			}
		| K_CONTINUE SEMICOLON
			{
			if (continue_level <= 0)
				yyerror("'continue' not inside loop stmt");
			$$ = alloc_node((long) K_CONTINUE);
			}
		| K_RETURN SEMICOLON
			{ 
			  $$ = alloc_node((long) K_RETURN); 
			  return_value(FALSE);
			}
		| K_RETURN expression SEMICOLON
			{ node_t *np = alloc_node((long) K_RETURN);
			  np->right = $2;
			  $$ = np;
			  return_value(TRUE);
			}
		;
expression:
		  assignment_expression
		  	{ $$ = $1; }
		| expression COMMA assignment_expression
			{ $$ = node((long) K_NOOP, $1, $3); }
		;
assignment_expression :
		  conditional_expression
		  	{ $$ = $1;  }
		| unary_expression assignment_operator assignment_expression
			{ $$ = node_lvalue((long) $2, $1, $3); }
		;
assignment_operator :
		  EQ		{ $$ = EQ; }
		| MUL_EQ	{ $$ = MUL_EQ; }
		| DIV_EQ	{ $$ = DIV_EQ; }
		| MOD_EQ	{ $$ = MOD_EQ; }
		| PLUS_EQ	{ $$ = PLUS_EQ; }
		| MINUS_EQ	{ $$ = MINUS_EQ; }
		| LSHIFT_EQ	{ $$ = LSHIFT_EQ; }
		| RSHIFT_EQ	{ $$ = RSHIFT_EQ; }
		| AND_EQ	{ $$ = AND_EQ; }
		| XOR_EQ	{ $$ = XOR_EQ; }
		| OR_EQ		{ $$ = OR_EQ; }
		;
conditional_expression :
		  logical_OR_expression
		| logical_OR_expression QUESTION expression COLON conditional_expression
			{ node_t *np = alloc_node((long) K_ELSE);
			  node_t *np1 = alloc_node((long) K_IF);
			  Head_p	hd = ll_init();
			  Head_p	hd1 = ll_init();
			  np1->left = $1;
			  np1->right = (node_t *) hd;
			  np->left = np1;
			  np->right = (node_t *) hd1;
			  ll_push(hd, (char *) $3);
			  ll_push(hd1, (char *) $5);
			  $$ = np;
			}
		;
constant_expression :
		  conditional_expression
		;
logical_OR_expression:
		  logical_AND_expression
		| logical_OR_expression COR logical_AND_expression
			{ $$ = node((long) COR, $1, $3); }
		;
logical_AND_expression:
		  inclusive_OR_expression
		| logical_AND_expression CAND inclusive_OR_expression
			{ $$ = node((long) CAND, $1, $3); }
		;
inclusive_OR_expression:
		  exclusive_OR_expression
		| inclusive_OR_expression OR exclusive_OR_expression
			{ $$ = node_opt((long) OR, $1, $3); }
		;
exclusive_OR_expression:
		  AND_expression
		| exclusive_OR_expression XOR AND_expression
			{ $$ = node_opt((long) XOR, $1, $3); }
		;
AND_expression:
	  	  equality_expression
		| AND_expression AND equality_expression
			{ $$ = node_opt((long) AND, $1, $3); }
		;
equality_expression:
		  relational_expression
		| equality_expression EQ_OP relational_expression
			{ $$ = node((long) EQ_OP, $1, $3); }
		| equality_expression NE_OP relational_expression
			{ $$ = node((long) NE_OP, $1, $3); }
		;
relational_expression :
		  shift_expression
		| relational_expression LT_OP shift_expression
			{ $$ = node((long) LT_OP, $1, $3); }
		| relational_expression GT_OP shift_expression
			{ $$ = node((long) GT_OP, $1, $3); }
		| relational_expression LE_OP shift_expression
			{ $$ = node((long) LE_OP, $1, $3); }
		| relational_expression GE_OP shift_expression
			{ $$ = node((long) GE_OP, $1, $3); }
		;
shift_expression :
		  additive_expression
		| shift_expression LSHIFT additive_expression
				{ $$ = node((long) LSHIFT, $1, $3); }
		| shift_expression RSHIFT additive_expression
				{ $$ = node((long) RSHIFT, $1, $3); }
		;
additive_expression :
		  multiplicative_expression
		| additive_expression PLUS multiplicative_expression
				{ $$ = node_opt((long) PLUS, $1, $3); }
		| additive_expression MINUS multiplicative_expression
				{ $$ = node_opt((long) MINUS, $1, $3); }
		;
multiplicative_expression:
		  cast_expression
		| multiplicative_expression MUL cast_expression
				{ $$ = node_opt((long) MUL, $1, $3); }
		| multiplicative_expression DIV cast_expression
				{ $$ = node_opt((long) DIV, $1, $3); }
		| multiplicative_expression MOD cast_expression
				{ $$ = node_opt((long) MOD, $1, $3); }
		;
cast_expression :
	  	  unary_expression
		  		{ $$ = $1; }
/*		| OROUND type_name CROUND cast_expression*/
	  	;
unary_expression :
		  postfix_expression
		  	{ $$ = $1; }
		| PLUS_PLUS unary_expression
			{ $$ = node((long) PLUS_PLUS, $2, (node_t *) NULL); }
		| MINUS_MINUS unary_expression
			{ $$ = node((long) MINUS_MINUS, $2, (node_t *) NULL); }
		| unary_operator cast_expression
			{ 
			if ($1 == MINUS)
				$$ = node_opt((long) MINUS, (node_t *) new_number(0L), $2);
			else
				$$ = node_opt($1, $2, (node_t *) NULL); 
			}
		| K_SIZEOF unary_expression
			{ $$ = node((long) K_SIZEOF, $2, (node_t *) NULL);}
/*		| K_SIZEOF OROUND type_name CROUND
*/
		;
unary_operator :
		  AND 		{ $$ = AND; }
		| MUL		{ $$ = MUL; }
		| PLUS		{ $$ = PLUS; }
		| MINUS		{ $$ = MINUS; }
		| NOT		{ $$ = NOT; }
		| COMPLEMENT	{ $$ = COMPLEMENT; }
		;
postfix_expression :
		  primary_expression
		| postfix_expression OSQUARE expression CSQUARE
			{ $$ = node((long) OSQUARE, $1, $3); }
		| postfix_expression OROUND argument_expression_list CROUND
			{ $$ = node((long) K_FUNCALL, $1, $3); }
		| postfix_expression OROUND CROUND
			{ $$ = node((long) K_FUNCALL, $1, (node_t *) NULL); }
		| postfix_expression DOT SYMBOL
		| postfix_expression ARROW SYMBOL
		| postfix_expression PLUS_PLUS
			{ $$ = node((long) POST_PLUS_PLUS, $1, (node_t *) NULL); }
		| postfix_expression MINUS_MINUS
			{ $$ = node((long) POST_MINUS_MINUS, $1, (node_t *) NULL); }
		;
primary_expression :
/*********/
		  SYMBOL OROUND CROUND
		  		{ $$ = node((long) K_FUNCALL, string_node1($1), (node_t *) NULL); 
				}
		| SYMBOL OROUND argument_expression_list CROUND
		  		{ $$ = node((long) K_FUNCALL, string_node1($1), $3); 
				}
		| 
/**********/
		  SYMBOL
		  		{ $$ = string_node1($1); 
				  lookup_sym($1, FALSE);
				}
		| INTEGER
				{ $$ = new_number($1);}
		| FLOAT_CONST
				{ $$ = new_float($1);}
		| STRING
				{ node_t *np = string_node1($1); 
				np->type = node_string;
				$$ = np;
				}
		| OROUND expression CROUND
				{ $$ = $2; }
		;
argument_expression_list :
		  assignment_expression
		| argument_expression_list COMMA assignment_expression
			{ $$ = node((long) K_NOOP, $1, $3); }
		;
