/*
 * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
 * See the copyright notice in the ACK home directory, in the file "Copyright".
 *
 * Author: Ceriel J.H. Jacobs
 */

/* D E C L A R A T I O N S */

/* $Id$ */

{
#include 	"parameters.h"
#include	"debug.h"

#include	<assert.h>
#include	"em_arith.h"
#include	"em_label.h"
#include	"alloc.h"

#include	"idf.h"
#include	"LLlex.h"
#include	"def.h"
#include	"type.h"
#include	"scope.h"
#include	"node.h"
#include	"enter.h"
#include	"error.h"
#include	"misc.h"
#include	"main.h"
#include	"typequiv.h"
#include	"chk_expr.h"
#include	"warning.h"

int		proclevel = 0;		/* nesting level of procedures */
int		return_occurred;	/* set if a return occurs in a block */

extern struct node	*EmptyStatement;

#define needs_static_link()	(proclevel > 1)
}

/* inline in declaration: need space
 * ProcedureDeclaration
 * {
 * 	struct def *df;
 * } :
 * 			{	++proclevel; }
 * 	ProcedureHeading(&df, D_PROCEDURE)
 * 	';' block(&(df->prc_body))
 * 	IDENT
 * 			{	EndProc(df, dot.TOK_IDF);
 * 				--proclevel;
 * 			}
 * ;
*/

ProcedureHeading(struct def **pdf; int type;)
{
	struct type	*tp = 0;
	arith	parmaddr = needs_static_link() ? pointer_size : 0;
	struct paramlist	*pr = 0;
} :
	PROCEDURE IDENT
			{ *pdf = DeclProc(type, dot.TOK_IDF); }
	[
		'('
		[
			FPSection(&pr, &parmaddr)
			[
				';' FPSection(&pr, &parmaddr)
			]*
		|
		]
		')'
		[	':' qualtype(&tp)
		|
		]
	|
	]
			{ CheckWithDef(*pdf, proc_type(tp, pr, parmaddr));
#ifndef NOSTRICT
			  if (tp && IsConstructed(tp)) {
warning(W_STRICT, "procedure \"%s\" has a constructed result type",
	(*pdf)->df_idf->id_text);
			  }
#endif
			}
;

block(struct node **pnd;) :
	[	%persistent
		declaration
	]*
			{ return_occurred = 0; }
	[	%default
		BEGIN
		StatementSequence(pnd)
	|
			{ *pnd = EmptyStatement; }
	]
	END
;

declaration
{
	struct def *df;
} :
	CONST [ ConstantDeclaration ';' ]*
|
	TYPE [ TypeDeclaration ';' ]*
|
	VAR [ VariableDeclaration ';' ]*
|
			{	++proclevel; }
	ProcedureHeading(&df, D_PROCEDURE)
			{	
			}
	';'
	block(&(df->prc_body))
	IDENT
			{
				EndProc(df, dot.TOK_IDF);
				--proclevel;
			}
	';'
|
	ModuleDeclaration ';'
;

/* inline in procedureheading: need space
 * FormalParameters(struct paramlist **ppr; arith *parmaddr; struct type **ptp;):
 * 	'('
 * 	[
 * 		FPSection(ppr, parmaddr)
 * 		[
 * 			';' FPSection(ppr, parmaddr)
 * 		]*
 *	|
 * 	]
 * 	')'
 * 	[	':' qualtype(ptp)
 *	|
 * 	]
 * ;
*/

FPSection(struct paramlist **ppr; arith *parmaddr;)
{
	struct node	*FPList;
	struct type	*tp;
	int	VARp;
} :
	var(&VARp) IdentList(&FPList) ':' FormalType(&tp)
			{ EnterParamList(ppr, FPList, tp, VARp, parmaddr); }
;

FormalType(struct type **ptp;)
		/* index type of conformant array is "CARDINAL".
		   Recognize a conformant array by size 0.
		*/
{	register struct type *tp;
} :
	ARRAY OF
		{ tp = construct_type(T_ARRAY, card_type); }
	qualtype(&(tp->arr_elem))
		{ ArrayElSize(tp);
		  *ptp = tp;
		}
|
	qualtype(ptp)
;

TypeDeclaration
{
	struct def *df;
	struct type *tp;
	register struct node *nd;
}:
	IDENT		{ df = define(dot.TOK_IDF, CurrentScope, D_TYPE);
			  nd = dot2leaf(Name);
			}
	'=' type(&tp)
			{ DeclareType(nd, df, tp);
			  FreeNode(nd);
			}
;

type(register struct type **ptp;):
	%default SimpleType(ptp)
|
	ArrayType(ptp)
|
	RecordType(ptp)
|
	SetType(ptp)
|
	PointerType(ptp)
|
	ProcedureType(ptp)
;

SimpleType(register struct type **ptp;) :
	qualtype(ptp)
	[
		/* nothing */
	|
		SubrangeType(ptp)
		/* The subrange type is given a base type by the
		   qualident (this is new modula-2).
		*/
	]
|
	enumeration(ptp)
|			{ *ptp = 0;	/* no qualification */ }
	SubrangeType(ptp)
;

enumeration(struct type **ptp;)
{
	struct node *EnumList;
} :
	'(' IdentList(&EnumList) ')'
		{ *ptp = enum_type(EnumList); }
;

IdentList(struct node **p;)
{
	register struct node *q;
} :
	IDENT		{ *p = q = dot2leaf(Select); }
	[ %persistent
		',' IDENT
			{ q->nd_NEXT = dot2leaf(Select);
			  q = q->nd_NEXT;
			}
	]*
;

SubrangeType(struct type **ptp;)
{
	struct node *nd1, *nd2;
}:
	/*
	   This is not exactly the rule in the new report, but see
	   the rule for "SimpleType".
	*/
	'[' ConstExpression(&nd1)
	UPTO ConstExpression(&nd2)
	']'
			{ *ptp = subr_type(nd1, nd2, *ptp);
			  FreeNode(nd1);
			  FreeNode(nd2);
			}
;

ArrayType(struct type **ptp;)
{
	struct type *tp;
	register struct type *tp1, *tp2;
} :
	ARRAY SimpleType(&tp)
			{ tp1 = tp2 = construct_type(T_ARRAY, tp); }
	[
		',' SimpleType(&tp)
			{ tp2->arr_elem = construct_type(T_ARRAY, tp);
			  tp2 = tp2->arr_elem;
			}
	]* OF type(&tp)
			{ tp2->arr_elem = tp;
			  ArraySizes(tp1);
			  *ptp = tp1;
			}
;

RecordType(struct type **ptp;)
{
	register struct scope *scope;
	arith size = 0;
	int xalign = struct_align;
}
:
	RECORD
		{ scope = open_and_close_scope(OPENSCOPE); }
	FieldListSequence(scope, &size, &xalign)
		{ if (size == 0) {
			warning(W_ORDINARY, "empty record declaration");
			size = 1;
		  }
		  *ptp = standard_type(T_RECORD, xalign, align(size, xalign));
		  (*ptp)->rec_scope = scope;
		  Reverse(&(scope->sc_def));
		}
	END
;

FieldListSequence(struct scope *scope; arith *cnt; int *palign;):
	FieldList(scope, cnt, palign)
	[
		';' FieldList(scope, cnt, palign)
	]*
;

FieldList(struct scope *scope; arith *cnt; int *palign;)
{
	struct node *FldList;
	struct type *tp;
	struct node *nd;
	register struct def *df;
	arith tcnt, max;
} :
[
	IdentList(&FldList) ':' type(&tp)
			{
			  *palign = lcm(*palign, tp->tp_align);
			  EnterFieldList(FldList, tp, scope, cnt);
			}
|
	CASE
	/* Also accept old fashioned Modula-2 syntax, but give a warning.
	   Sorry for the complicated code.
	*/
	[ qualident(&nd)
	  [ ':' qualtype(&tp)
			/* This is correct, in both kinds of Modula-2, if
		   	   the first qualident is a single identifier.
			*/
			{ if (nd->nd_class != Name) {
				error("illegal variant tag");
		  	  }
			  else {
			  	df = define(nd->nd_IDF, scope, D_FIELD);
			  	*palign = lcm(*palign, tp->tp_align);
			  	if (!(tp->tp_fund & T_DISCRETE)) {
					error("illegal type in variant");
			  	}
			  	df->df_type = tp;
			  	df->fld_off = align(*cnt, tp->tp_align);
			  	*cnt = df->fld_off + tp->tp_size;
			  	df->df_flags |= D_QEXPORTED;
			  }
			  FreeNode(nd);
			}
	  |		/* Old fashioned! the first qualident now represents
			   the type
			*/
			{
#ifndef STRICT_3RD_ED
			  if (! options['3']) warning(W_OLDFASHIONED,
			      "old fashioned Modula-2 syntax; ':' missing");
			  else
#endif
			  error("':' missing");
			  tp = qualified_type(&nd);
			}
	  ]
	| ':' qualtype(&tp)
	  /* Aha, third edition. Well done! */
	]
			{ tcnt = *cnt; }
	OF variant(scope, &tcnt, tp, palign)
			{ max = tcnt; tcnt = *cnt; }
	[
	  '|' variant(scope, &tcnt, tp, palign)
			{ if (tcnt > max) max = tcnt; tcnt = *cnt; }
	]*
	[ ELSE FieldListSequence(scope, &tcnt, palign)
			{ if (tcnt > max) max = tcnt; }
	|
	]
	END
			{ *cnt = max; }
|
]
;

variant(struct scope *scope; arith *cnt; struct type *tp; int *palign;)
{
	struct node *nd;
} :
	[
		CaseLabelList(&tp, &nd)
			{ /* Ignore the cases for the time being.
			     Maybe a checking version will be supplied
			     later ???
			  */
			  FreeNode(nd);
			}
		':' FieldListSequence(scope, cnt, palign)
	|
	]
			/* Changed rule in new modula-2 */
;

CaseLabelList(struct type **ptp; struct node **pnd;):
	CaseLabels(ptp, pnd)
	[	
			{ *pnd = dot2node(Link, *pnd, NULLNODE); }
		',' CaseLabels(ptp, &((*pnd)->nd_RIGHT))
			{ pnd = &((*pnd)->nd_RIGHT); }
	]*
;

CaseLabels(struct type **ptp; register struct node **pnd;)
{
	register struct node *nd;
}:
	ConstExpression(pnd)
			{ 
			  if (*ptp != 0) {
				struct type *tp = intorcard(*ptp,
					BaseType((*pnd)->nd_type));
				if (tp) *ptp = tp;
				ChkCompat(pnd, *ptp, "case label");
			  }
			  nd = *pnd;
			  nd->nd_type = BaseType(nd->nd_type);	/* ??? */
			  if (! (nd->nd_type->tp_fund & T_DISCRETE) ||
			      nd->nd_type->tp_size > word_size) {
				node_error(nd, "illegal type in case label");
			  }
			}
	[
		UPTO	{ *pnd = nd = dot2node(Link,nd,NULLNODE);
			  nd->nd_type = nd->nd_LEFT->nd_type;
			}
		ConstExpression(&(*pnd)->nd_RIGHT)
			{ if (!ChkCompat(&((*pnd)->nd_RIGHT), nd->nd_type,
					 "case label")) {
			  	nd->nd_type = error_type;
			  }
			  else if (! chk_bounds(nd->nd_LEFT->nd_INT,
						nd->nd_RIGHT->nd_INT,
						nd->nd_type->tp_fund)) {
			    node_error(nd,
			   "lower bound exceeds upper bound in case label range");
			  }

			}
	|
	]
			{
			  *ptp = nd->nd_type;
			}
;

SetType(struct type **ptp;)
{	struct type *tp;
} :
	SET OF SimpleType(&tp)
			{ *ptp = set_type(tp); }
;

/*	In a pointer type definition, the type pointed at does not
	have to be declared yet, so be careful about identifying
	type-identifiers.
*/
PointerType(register struct type **ptp;)
{	register struct type *tp;
} :
			{ tp = construct_type(T_POINTER, NULLTYPE); }
	POINTER TO
	[ %if	(type_or_forward(tp))
	  type(&(tp->tp_next)) 
	|
	  IDENT
	]
			{ *ptp = tp; }
;

qualtype(struct type **ptp;)
{
	struct node *nd;
} :
	qualident(&nd)
		{ *ptp = qualified_type(&nd); }
;

ProcedureType(struct type **ptp;)
{
	struct paramlist *pr = 0;
	arith parmaddr = 0;
	struct type *tp = 0;
} :
	PROCEDURE 
	[
	  	FormalTypeList(&pr, &parmaddr, &tp)
	|
	]
			{ *ptp = proc_type(tp, pr, parmaddr); }
;

FormalTypeList(struct paramlist **ppr; arith *pparmaddr; struct type **ptp;) :
	'('
	[
		VarFormalType(ppr, pparmaddr)
		[
			',' VarFormalType(ppr, pparmaddr)
		]*
	|
	]
	')'
	[ ':' qualtype(ptp)
	|
	]
;

VarFormalType(struct paramlist **ppr; arith *pparmaddr;)
{
	struct type *tp;
	int isvar;
} :
	var(&isvar)
	FormalType(&tp)
			{ EnterParamList(ppr,NULLNODE,tp,isvar,pparmaddr); }
;

var(int *VARp;) :
	[
		VAR		{ *VARp = D_VARPAR; }
	|
		/* empty */	{ *VARp = D_VALPAR; }
	]
;

ConstantDeclaration
{
	struct idf *id;
	struct node *nd;
	register struct def *df;
}:
	IDENT		{ id = dot.TOK_IDF; }
	'=' ConstExpression(&nd)
			{ df = define(id,CurrentScope,D_CONST);
			  df->con_const = nd->nd_token;
			  df->df_type = nd->nd_type;
			  FreeNode(nd);
			}
;

VariableDeclaration
{
	struct node *VarList;
	register struct node *nd;
	struct type *tp;
} :
	IdentAddr(&VarList)
			{ nd = VarList; }
	[ %persistent
		',' IdentAddr(&(nd->nd_RIGHT))
			{ nd = nd->nd_RIGHT; }
	]*
	':' type(&tp)
			{ EnterVarList(VarList, tp, proclevel > 0); }
;

IdentAddr(struct node **pnd;) 
{
	register struct node *nd;
} :
	IDENT		{ nd = dot2leaf(Name);
			  *pnd = dot2node(Link, nd, NULLNODE);
			}
	[	'['
		ConstExpression(&(nd->nd_NEXT))
		']'
	|
	]
;