/* S T A T E M E N T S */
{
#include	<alloc.h>
#include	<em.h>
#include	<stb.h>

#include	"LLlex.h"
#include	"chk_expr.h"
#include	"def.h"
#include	"desig.h"
#include	"f_info.h"
#include	"idf.h"
#include	"main.h"
#include	"misc.h"
#include	"node.h"
#include	"scope.h"
#include	"type.h"
#include	"dbsymtab.h"

int slevel = 0;		/* nesting level of statements */
}


/* ISO section 6.8.3.2, p. 128 */
CompoundStatement:
	BEGIN StatementSequence END
;

/* ISO section 6.8.3.1, p. 128 */
StatementSequence:
	Statement
	[ %persistent
		';' Statement
	]*
					{ chk_labels(slevel + 1); }
;

/* ISO section 6.8.1, p. 126 */
Statement
{
	struct node *nd;
} :
					{
					  slevel++;
					}
	[ Label(&nd) ':'
					{ if( nd ) DefLabel(nd, slevel); }
	]?
					{ if( !options['L'] )
						C_lin((arith) dot.tk_lineno);
#ifdef DBSYMTAB
					  if (options['g']) {
					    static int ms_lineno;

					    if (ms_lineno != dot.tk_lineno) {
						C_ms_std((char *) 0, N_SLINE, (int) dot.tk_lineno);
						ms_lineno = dot.tk_lineno;
					    }
					  }
#endif /* DBSYMTAB */
					}
	[
		SimpleStatement
	|
		StructuredStatement
	]
					{ slevel--; }
;

/* ISO section 6.8.2.1, p. 126 */
SimpleStatement
{
	struct node *pnd, *expp;
	unsigned short line;
} :
	/* This is a changed rule, because the grammar as specified in the
	 * reference is not LL(1), and this gives conflicts.
	 * Note : the grammar states : AssignmentStatement |
	 *				ProcedureStatement | ...
	 * In order to add assertions, there is an extra entry, which gives
	 * a conflict. This conflict is then resolved using an %if clause.
	 */
	EmptyStatement
|
	GotoStatement
|
	/* Evidently this is the beginning of the changed part
	 */
	%if( !options['s'] && !strcmp(dot.TOK_IDF->id_text, "assert") )
	IDENT			{ line = LineNumber; }
		Expression(&expp)
				{ AssertStat(expp, line);
				  FreeNode(expp);
				}
|
	IDENT			{ pnd = MkLeaf(Name, &dot); }
	[	%default

		/* At this point the IDENT can be a FunctionIdentifier in
		 * which case the VariableAccessTail must be empty.
		 */
		VariableAccessTail(&pnd)
		[
			%default
			BECOMES
		|
			'='	{ error("':=' expected instead of '='"); }
		]
		Expression(&expp)
				{ AssignStat(pnd, expp); }
	|
				{ pnd = MkNode(Call, pnd, NULLNODE, &dot); }
		ActualParameterList(&(pnd->nd_right))?
				{ ProcStat(pnd);

				  if( !err_occurred )
					CodeCall(pnd);

				  FreeNode(pnd);
				}

	]
|
	InputOutputStatement
	/* end of changed part
	 */
;

InputOutputStatement
{
	struct node *nd = NULLNODE;
} :
	/* This is a new rule because the grammar specified by the standard
	 * is not exactly LL(1) (see SimpleStatement).
	 */
	[
		READ ReadParameterList(&nd)		{ ChkRead(nd); }
	|
		READLN ReadParameterList(&nd)?		{ ChkReadln(nd); }
	|
		WRITE WriteParameterList(&nd)		{ ChkWrite(nd); }
	|
		WRITELN WriteParameterList(&nd)?	{ ChkWriteln(nd); }
	]
							{ FreeNode(nd); }
;

EmptyStatement:
	/* empty */
;

/* ISO section 6.8.3.1, p. 128 */
StructuredStatement:
	CompoundStatement
|
	ConditionalStatement
|
	RepetitiveStatement
|
	WithStatement
;

/* ISO section 6.8.2.4, p. 127 */
GotoStatement
{
	struct node *nd;
} :
	GOTO Label(&nd)
					{ if( nd ) TstLabel(nd, slevel); }
;

/* ISO section 6.8.3.3, p. 128 */
ConditionalStatement:
	%default
	CaseStatement
|
	IfStatement
;

/* ISO section 6.8.3.6, p. 129 */
RepetitiveStatement:
	RepeatStatement
|
	WhileStatement
|
	ForStatement
;

/* ISO section 6.8.3.10, p. 132 */
WithStatement
{
	struct scopelist *Save = CurrVis;
	struct node *nd;
} :
	WITH
	RecordVariableList(&nd)
	DO
	Statement	{ EndWith(Save, nd);
			  chk_labels(slevel + 1);
			}
;

RecordVariableList(register struct node **pnd;)
{
	struct node *nd;
} :
	RecordVariable(&nd)
				{ *pnd = nd = MkNode(Link, nd, NULLNODE, &dot);
				  nd->nd_symb = ',';
				}
	[ %persistent
		','		{ nd->nd_right = MkLeaf(Link, &dot);
				  nd = nd->nd_right;
				}
		RecordVariable(&(nd->nd_left))
	]*
;

RecordVariable(register struct node **pnd;):
	VariableAccess(pnd)
				{ WithStat(*pnd); }
;

/* ISO section 6.8.3.4, p. 128 */
IfStatement
{
	struct node *nd;
	label l1 = ++text_label;
	label l2 = ++text_label;
} :
	IF
	BooleanExpression(&nd)
					{ struct desig ds;
					
					  ds = InitDesig;
					  if( !err_occurred )
						CodeExpr(nd, &ds, l1);
					  FreeNode(nd);
					}
	THEN
	Statement			{ chk_labels(slevel + 1); }
	[ %prefer	/* closest matching */
		ELSE
					{ C_bra(l2);
					  C_df_ilb(l1);
					}
		Statement
					{ C_df_ilb(l2);
					  chk_labels(slevel + 1);
					}
	|
		/* empty */
					{ C_df_ilb(l1); }
	]
;

/* ISO section 6.8.3.5, p. 128 */
CaseStatement
{
	struct node *casend, *nd;
	label exit_label;
} :
	/* This is a changed rule, because the grammar as specified in the
	 * reference states that a semicolon is optional before END,
	 * and this is not LL(1).
	 */
	CASE				{ casend = nd = MkLeaf(Link, &dot);
					  casend->nd_lab = ++text_label;
					  exit_label = ++text_label;
					}
	Expression(&(nd->nd_left))
					{ CaseExpr(casend); }
	OF
	CaseListElement(&(nd->nd_right), exit_label)
					{ nd = nd->nd_right; }
	CaseListElementTail(&(nd->nd_right), exit_label)
	END
					{ CaseEnd(casend, exit_label); }
;

CaseListElementTail(register struct node **pnd; label exit_label;):
	/* This is a new rule, all because of a silly semicolon
	 */
	/* empty */
|
%default
	';'
	[
		/* empty */
	|
		CaseListElement(pnd, exit_label)
		CaseListElementTail(&((*pnd)->nd_right), exit_label)
	]
;

CaseListElement(register struct node **pnd; label exit_label;):
	CaseConstantList(pnd)
	':'
				{ *pnd = MkNode(Link, *pnd, NULLNODE, &dot);
				  (*pnd)->nd_lab = ++text_label;
				  C_df_ilb(text_label);
				}
	Statement		{ C_bra(exit_label);
				  chk_labels(slevel + 1);
				}
;

/* ISO section 6.8.3.7, p. 129 */
RepeatStatement
{
	struct node *nd;
	label repeatlb = ++text_label;
} :
	REPEAT
					{ C_df_ilb(repeatlb); }
	StatementSequence
	UNTIL
	BooleanExpression(&nd)
					{ struct desig ds;

					  ds = InitDesig;
					  if( !err_occurred )
						CodeExpr(nd, &ds, repeatlb);
					  FreeNode(nd);
					}
;

/* ISO section 6.8.3.8, p. 129 */
WhileStatement
{
	struct node *nd;
	label whilelb = ++text_label;
	label exitlb = ++text_label;

} :
	WHILE
					{ C_df_ilb(whilelb); }
	BooleanExpression(&nd)
					{ struct desig ds;

					  ds = InitDesig;
					  if( !err_occurred )
						CodeExpr(nd, &ds, exitlb);
					  FreeNode(nd);
					}
	DO
	Statement
					{ C_bra(whilelb);
					  C_df_ilb(exitlb);
					  chk_labels(slevel + 1);
					}
;

/* ISO section 6.8.3.9, p. 130 */
ForStatement
{
	register struct node *nd;
	int stepsize;
	label l1 = ++text_label;
	label l2 = ++text_label;
	arith tmp2 = (arith) 0;
} :
	FOR
	/* ControlVariable must be an EntireVariable */
	IDENT			{ nd = MkLeaf(Name, &dot); }
	BECOMES
	Expression(&(nd->nd_left))
	[
		TO		{ stepsize = 1; }
	|
		DOWNTO		{ stepsize = -1; }
	]
	Expression(&(nd->nd_right))
				{ ChkForStat(nd);
				  if( !err_occurred )	{
					CodePExpr(nd->nd_left);
					C_dup(int_size);
					tmp2 = CodeInitFor(nd->nd_right, 2);
				  	CodeFor(nd, stepsize, l1, l2);
				  }
				}
	DO
	Statement
				{ if( !err_occurred )
				       CodeEndFor(nd, stepsize, l1, l2, tmp2);
				  EndForStat(nd);
				  chk_labels(slevel + 1);
				  FreeNode(nd);
				  if( tmp2 ) FreeInt(tmp2);
				}
;

/* SPECIALSPECIALSPECIALSPECIALSPECIALSPECIALSPECIALSPECIALSPECIALSPECIAL */
/* ISO section 6.9, p. 132-136 */
ReadParameterList(register struct node **pnd;)
{
	register struct node *nd;
} :
	/* This is a changed rule, because the grammar as specified in the
	 * reference is not LL(1), and this gives conflicts.
	 */
	'('
		VariableAccess(pnd)	/* possibly a FileVariable */
					{ *pnd = nd =
					     MkNode(Link, *pnd, NULLNODE, &dot);
					  nd->nd_symb = ',';
					}
		[ %persistent
			',' 		{ nd->nd_right = MkLeaf(Link, &dot);
					  nd = nd->nd_right;
					}
			VariableAccess(&(nd->nd_left))
		]*
	')'
;

WriteParameterList(register struct node **pnd;)
{
	register struct node *nd;
} :
	/* This is a changed rule, because the grammar as specified in the
	 * reference is not LL(1), and this gives conflicts.
	 */
	'('
		/* Only the first WriteParameter can be a FileVariable !!
		 */
		WriteParameter(pnd)
					{ *pnd = nd =
					     MkNode(Link, *pnd, NULLNODE, &dot);
					  nd->nd_symb = ',';
					}
		[ %persistent
			',' 		{ nd->nd_right = MkLeaf(Link, &dot);
					  nd = nd->nd_right;
					}
			WriteParameter(&(nd->nd_left))
		]*
	')'
;

WriteParameter(register struct node **pnd;)
{
	register struct node *nd;
} :
	Expression(pnd)
					{ if( !ChkExpression(*pnd) )
						(*pnd)->nd_type = error_type;
					  MarkUsed(*pnd);
					  *pnd = nd =
					     MkNode(Link, *pnd, NULLNODE, &dot);
					  nd->nd_symb = ':';
					}
	[
	/* Here the first Expression can't be a FileVariable
	 */
		':'			{ nd->nd_right = MkLeaf(Link, &dot);
					  nd = nd->nd_right;
					}
		Expression(&(nd->nd_left))
					{ if( !ChkExpression(nd->nd_left) )
					      nd->nd_left->nd_type = error_type;
					  MarkUsed(nd->nd_left);
					}
		[
			':'		{ nd->nd_right = MkLeaf(Link, &dot);
					  nd = nd->nd_right;
					}
			Expression(&(nd->nd_left))
					{ if( !ChkExpression(nd->nd_left) )
					      nd->nd_left->nd_type = error_type;
					  MarkUsed(nd->nd_left);
					}
		]?
	]?
;