/*
 * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
 * See the copyright notice in the ACK home directory, in the file "Copyright".
 */
/* $Header$ */
/*	STATEMENT SYNTAX PARSER	*/

{
#include	"lint.h"
#ifndef	LINT
#include	<em.h>
#else
#include	"l_em.h"
#include	"l_lint.h"
#endif	/* LINT */

#include	"debug.h"
#include	"botch_free.h"
#include	"dbsymtab.h"

#include	"arith.h"
#include	"LLlex.h"
#include	"type.h"
#include	"idf.h"
#include	"label.h"
#include	"expr.h"
#include	"code.h"
#include	"stack.h"
#include	"def.h"
#ifdef DBSYMTAB
#include	<stb.h>
#endif /* DBSYMTAB */

extern int level;
extern char options[];
}

/*	Each statement construction is stacked in order to trace a ???
	statement to such a construction. Example: a case statement should
	be recognized as a piece of the most enclosing switch statement.
*/

/* 9 */
statement
	{
#ifdef	LINT
		lint_statement();
#endif	/* LINT */
	}
:
%if (AHEAD != ':')
	expression_statement
|
	label ':' statement
|
	compound_statement
|
	if_statement
|
	while_statement
|
	do_statement
|
	for_statement
|
	switch_statement
|
	case_statement
|
	default_statement
|
	BREAK
	{
		code_break();
#ifdef	LINT
		lint_break_stmt();
#endif	/* LINT */
	}
	';'
|
	CONTINUE
	{
		code_continue();
#ifdef	LINT
		lint_continue_stmt();
#endif	/* LINT */
	}
	';'
|
	return_statement
|
	jump
|
	';'
|
	asm_statement
;


expression_statement
	{	struct expr *expr;
	}
:
	expression(&expr)
	';'
		{
#ifdef	DEBUG
			print_expr("expression_statement", expr);
#endif	/* DEBUG */
			code_expr(expr, RVAL, FALSE, NO_LABEL, NO_LABEL);
			free_expression(expr);
		}
;

label
	{	struct idf *idf;
	}
:
	identifier(&idf)
	{
		/*	This allows the following absurd case:

				typedef int grz;
				main()	{
					grz: printf("A labelled statement\n");
				}
		*/
#ifdef	LINT
		lint_label();
#endif	/* LINT */
		define_label(idf);
		C_df_ilb((label)idf->id_def->df_address);
	}
;

if_statement
	{
		struct expr *expr;
		label l_true = text_label();
		label l_false = text_label();
		label l_end = text_label();
	}
:
	IF
	'('
	expression(&expr)
		{
			opnd2test(&expr, IF);
			if (is_cp_cst(expr))	{
				/*	The comparison has been optimized
					to a 0 or 1.
				*/
				if (expr->VL_VALUE == (arith)0)	{
					C_bra(l_false);
				}
				/* else fall through */
#ifdef	LINT
				start_if_part(1);
#endif	/* LINT */
			}
			else	{
				code_expr(expr, RVAL, TRUE, l_true, l_false);
				C_df_ilb(l_true);
#ifdef	LINT
				start_if_part(0);
#endif	/* LINT */
			}
			free_expression(expr);
		}
	')'
	statement
	[%prefer
		ELSE
			{
#ifdef	LINT
				start_else_part();
#endif	/* LINT */
				C_bra(l_end);
				C_df_ilb(l_false);
			}
		statement
			{	C_df_ilb(l_end);
#ifdef	LINT
				end_if_else_stmt();
#endif	/* LINT */
			}
	|
		empty
			{	C_df_ilb(l_false);
#ifdef	LINT
				end_if_stmt();
#endif	/* LINT */
			}
	]
;

while_statement
	{
		struct expr *expr;
		label l_break = text_label();
		label l_continue = text_label();
		label l_body = text_label();
	}
:
	WHILE
		{
			stack_stmt(l_break, l_continue);
			C_df_ilb(l_continue);
		}
	'('
	expression(&expr)
		{
			opnd2test(&expr, WHILE);
			if (is_cp_cst(expr))	{
				if (expr->VL_VALUE == (arith)0)	{
					C_bra(l_break);
				}
			}
			else	{
				code_expr(expr, RVAL, TRUE, l_body, l_break);
				C_df_ilb(l_body);
			}

#ifdef	LINT
			start_while_stmt(expr);
#endif	/* LINT */

		}
	')'
	statement
		{
			C_bra(l_continue);
			C_df_ilb(l_break);
			unstack_stmt();
			free_expression(expr);
#ifdef	LINT
			end_loop_body();
			end_loop_stmt();
#endif	/* LINT */
		}
;

do_statement
	{	struct expr *expr;
		label l_break = text_label();
		label l_continue = text_label();
		label l_body = text_label();
	}
:
	DO
		{	C_df_ilb(l_body);
			stack_stmt(l_break, l_continue);
#ifdef	LINT
			start_do_stmt();
#endif	/* LINT */
		}
	statement
	WHILE
	'('
		{
#ifdef	LINT
			end_loop_body();
#endif	/* LINT */
			C_df_ilb(l_continue);
		}
	expression(&expr)
		{
			opnd2test(&expr, WHILE);
			if (is_cp_cst(expr))	{
				if (expr->VL_VALUE == (arith)1)	{
					C_bra(l_body);
				}
#ifdef	LINT
				end_do_stmt(1, expr->VL_VALUE != (arith)0);
#endif	/* LINT */
			}
			else	{
				code_expr(expr, RVAL, TRUE, l_body, l_break);
#ifdef	LINT
				end_do_stmt(0, 0);
#endif	/* LINT */
			}
			C_df_ilb(l_break);
		}
	')'
	';'
		{
			unstack_stmt();
			free_expression(expr);
		}
;

for_statement
	{	struct expr *e_init = 0, *e_test = 0, *e_incr = 0;
		label l_break = text_label();
		label l_continue = text_label();
		label l_body = text_label();
		label l_test = text_label();
	}
:
	FOR
		{	stack_stmt(l_break, l_continue);
		}
	'('
	[
		expression(&e_init)
		{	code_expr(e_init, RVAL, FALSE, NO_LABEL, NO_LABEL);
		}
	]?
	';'
		{	C_df_ilb(l_test);
		}
	[
		expression(&e_test)
		{
			opnd2test(&e_test, FOR);
			if (is_cp_cst(e_test))	{
				if (e_test->VL_VALUE == (arith)0)	{
					C_bra(l_break);
				}
			}
			else	{
				code_expr(e_test, RVAL, TRUE, l_body, l_break);
				C_df_ilb(l_body);
			}
		}
	]?
	';'
	expression(&e_incr)?
	')'
		{
#ifdef	LINT
			start_for_stmt(e_test);
#endif	/* LINT */
		}
	statement
		{
#ifdef	LINT
			end_loop_body();
#endif	/* LINT */
			C_df_ilb(l_continue);
			if (e_incr)
				code_expr(e_incr, RVAL, FALSE,
							NO_LABEL, NO_LABEL);
			C_bra(l_test);
			C_df_ilb(l_break);
			unstack_stmt();
			free_expression(e_init);
			free_expression(e_test);
			free_expression(e_incr);
#ifdef	LINT
			end_loop_stmt();
#endif	/* LINT */
		}
;

switch_statement
	{
		struct expr *expr;
	}
:
	SWITCH
	'('
	expression(&expr)
		{
			code_startswitch(&expr);
#ifdef	LINT
			start_switch_part(is_cp_cst(expr));
#endif	/* LINT */
		}
	')'
	statement
		{
#ifdef	LINT
			end_switch_stmt();
#endif	/* LINT */
			code_endswitch();
			free_expression(expr);
		}
;

case_statement
	{
		struct expr *expr;
	}
:
	CASE
	constant_expression(&expr)
		{
#ifdef	LINT
			lint_case_stmt(0);
#endif	/* LINT */
			code_case(expr);
			free_expression(expr);
		}
	':'
	statement
;

default_statement
:
	DEFAULT
		{
#ifdef	LINT
			lint_case_stmt(1);
#endif	/* LINT */
			code_default();
		}
	':'
	statement
;

return_statement
	{	struct expr *expr = 0;
	}
:
	RETURN
	[
		expression(&expr)
		{
#ifdef	LINT
			lint_ret_conv(expr);
#endif	/* LINT */

			do_return_expr(expr);
			free_expression(expr);
#ifdef	LINT
			lint_return_stmt(VALRETURNED);
#endif	/* LINT */
		}
	|
		empty
		{
			do_return();
#ifdef	LINT
			lint_return_stmt(NOVALRETURNED);
#endif	/* LINT */
		}
	]
	';'
;

jump
	{	struct idf *idf;
	}
:
	GOTO
	identifier(&idf)
	';'
		{
			apply_label(idf);
			C_bra((label)idf->id_def->df_address);
#ifdef	LINT
			lint_jump_stmt(idf);
#endif	/* LINT */
		}
;

compound_statement
	{
#ifdef DBSYMTAB
	static int	brc_level = 1;
	int		decl_seen = brc_level == 1;
#endif /* DBSYMTAB */
	}
:
	'{'
		{
			stack_level();
		}
	[%while ((DOT != IDENTIFIER && AHEAD != ':') ||
		 (DOT == IDENTIFIER && AHEAD == IDENTIFIER))
			/* >>> conflict on TYPE_IDENTIFIER, IDENTIFIER */
		declaration
		{
#ifdef DBSYMTAB
			decl_seen++;
#endif /* DBSYMTAB */
		}
	]*
		{
#ifdef DBSYMTAB
			++brc_level;
			if (options['g'] && decl_seen) {
				C_ms_std((char *) 0, N_LBRAC, brc_level);
			}
#endif /* DBSYMTAB */
		}
	[%persistent
		statement
	]*
	'}'
		{
			unstack_level();
#ifdef DBSYMTAB
			if (options['g'] && decl_seen) {
				C_ms_std((char *) 0, N_RBRAC, brc_level);
			}
			brc_level--;
#endif /* DBSYMTAB */
		}
;

asm_statement
	{	char *asm_bts;
		int asm_len;
	}
:
	ASM
	'('
	STRING
		{	asm_bts = dot.tk_bts;
			asm_len = dot.tk_len;
		}
	')'
	';'
		{	code_asm(asm_bts, asm_len);
		}
;