189 lines
		
	
	
	
		
			5 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
			
		
		
	
	
			189 lines
		
	
	
	
		
			5 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
/*
 | 
						|
 * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
 | 
						|
 * See the copyright notice in the ACK home directory, in the file "Copyright".
 | 
						|
 */
 | 
						|
/* $Header$ */
 | 
						|
/* PROGRAM PARSER */
 | 
						|
 | 
						|
/*	The presence of typedef declarations renders it impossible to
 | 
						|
	make a context-free grammar of C. Consequently we need
 | 
						|
	context-sensitive parsing techniques, the simplest one being
 | 
						|
	a subtle cooperation between the parser and the lexical scanner.
 | 
						|
	The lexical scanner has to know whether to return IDENTIFIER
 | 
						|
	or TYPE_IDENTIFIER for a given tag, and it obtains this information
 | 
						|
	from the definition list, as constructed by the parser.
 | 
						|
	The present grammar is essentially LL(2), and is processed by
 | 
						|
	a parser generator which accepts LL(1) with tie breaking rules
 | 
						|
	in C, of the form %if(cond) and %while(cond). To solve the LL(1)
 | 
						|
	ambiguities, the lexical scanner does a one symbol look-ahead.
 | 
						|
	This symbol, however, cannot always be correctly assessed, since
 | 
						|
	the present symbol may cause a change in the definition list
 | 
						|
	which causes the identification of the look-ahead symbol to be
 | 
						|
	invalidated.
 | 
						|
	The lexical scanner relies on the parser (or its routines) to
 | 
						|
	detect this situation and then update the look-ahead symbol.
 | 
						|
	An alternative approach would be to reassess the look-ahead symbol
 | 
						|
	in the lexical scanner when it is promoted to dot symbol. This
 | 
						|
	would be more beautiful but less correct, since then for a short
 | 
						|
	while there would be a discrepancy between the look-ahead symbol
 | 
						|
	and the definition list; I think it would nevertheless work in
 | 
						|
	correct programs.
 | 
						|
	A third solution would be to enter the identifier as soon as it
 | 
						|
	is found; its storage class is then known, although its full type
 | 
						|
	isn't. We would have to fill that in afterwards.
 | 
						|
 | 
						|
	At block exit the situation is even worse. Upon reading the
 | 
						|
	closing brace, the names declared inside the function are cleared
 | 
						|
	from the name list. This action may expose a type identifier that
 | 
						|
	is the same as the identifier in the look-ahead symbol. This
 | 
						|
	situation certainly invalidates the third solution, and casts
 | 
						|
	doubts upon the second.
 | 
						|
*/
 | 
						|
 | 
						|
%lexical	LLlex;
 | 
						|
%start		C_program, program;
 | 
						|
%start		If_expr, control_if_expression;
 | 
						|
 | 
						|
{
 | 
						|
#include	"nopp.h"
 | 
						|
#include	"arith.h"
 | 
						|
#include	"LLlex.h"
 | 
						|
#include	"idf.h"
 | 
						|
#include	"label.h"
 | 
						|
#include	"type.h"
 | 
						|
#include	"declar.h"
 | 
						|
#include	"decspecs.h"
 | 
						|
#include	"code.h"
 | 
						|
#include	"expr.h"
 | 
						|
#include	"def.h"
 | 
						|
 | 
						|
#ifndef NOPP
 | 
						|
extern arith ifval;
 | 
						|
#endif NOPP
 | 
						|
 | 
						|
/*VARARGS*/
 | 
						|
extern error();
 | 
						|
}
 | 
						|
 | 
						|
control_if_expression
 | 
						|
	{
 | 
						|
		struct expr *exprX;
 | 
						|
	}
 | 
						|
:
 | 
						|
	constant_expression(&exprX)
 | 
						|
		{
 | 
						|
#ifndef NOPP
 | 
						|
			register struct expr *expr = exprX;
 | 
						|
			if (expr->ex_flags & EX_SIZEOF)
 | 
						|
				expr_error(expr,
 | 
						|
					"sizeof not allowed in preprocessor");
 | 
						|
			ifval = expr->VL_VALUE;
 | 
						|
			free_expression(expr);
 | 
						|
#endif NOPP
 | 
						|
		}
 | 
						|
;
 | 
						|
 | 
						|
/* 10 */
 | 
						|
program:
 | 
						|
	[%persistent external_definition]*
 | 
						|
	{unstack_world();}
 | 
						|
;
 | 
						|
 | 
						|
/*	A C identifier definition is remarkable in that it formulates
 | 
						|
	the declaration in a way different from most other languages:
 | 
						|
	e.g., rather than defining x as a pointer-to-integer, it defines
 | 
						|
	*x as an integer and lets the compiler deduce that x is actually
 | 
						|
	pointer-to-integer.  This has profound consequences, both for the
 | 
						|
	structure of an identifier definition and for the compiler.
 | 
						|
	
 | 
						|
	A definition starts with a decl_specifiers, which contains things
 | 
						|
	like
 | 
						|
		typedef int
 | 
						|
	which is implicitly repeated for every definition in the list, and
 | 
						|
	then for each identifier a declarator is given, of the form
 | 
						|
		*a()
 | 
						|
	or so.  The decl_specifiers is kept in a struct decspecs, to be
 | 
						|
	used again and again, while the declarator is stored in a struct
 | 
						|
	declarator, only to be passed to declare_idf together with the
 | 
						|
	struct decspecs.
 | 
						|
*/
 | 
						|
 | 
						|
external_definition
 | 
						|
	{
 | 
						|
		struct decspecs Ds;
 | 
						|
		struct declarator Dc;
 | 
						|
	}
 | 
						|
:
 | 
						|
	{
 | 
						|
		Ds = null_decspecs;
 | 
						|
		Dc = null_declarator;
 | 
						|
	}
 | 
						|
	ext_decl_specifiers(&Ds)
 | 
						|
	[
 | 
						|
		declarator(&Dc)
 | 
						|
		{declare_idf(&Ds, &Dc, level);}
 | 
						|
		[%if (Dc.dc_idf->id_def->df_type->tp_fund == FUNCTION)
 | 
						|
			/*	int i (1) {2, 3}
 | 
						|
				is a function, not an old-fashioned
 | 
						|
				initialization.
 | 
						|
			*/
 | 
						|
			function(&Dc)
 | 
						|
		|
 | 
						|
			non_function(&Ds, &Dc)
 | 
						|
		]
 | 
						|
	|
 | 
						|
		';'
 | 
						|
	]
 | 
						|
	{remove_declarator(&Dc);}
 | 
						|
|
 | 
						|
	asm_statement			/* top level, would you believe */
 | 
						|
;
 | 
						|
 | 
						|
ext_decl_specifiers(struct decspecs *ds;) :
 | 
						|
%prefer /* the thin ice in  R.M. 11.1 */
 | 
						|
	decl_specifiers(ds)
 | 
						|
|
 | 
						|
	empty
 | 
						|
	{do_decspecs(ds);}
 | 
						|
;
 | 
						|
 | 
						|
non_function(register struct decspecs *ds; register struct declarator *dc;)
 | 
						|
:
 | 
						|
	{reject_params(dc);}
 | 
						|
	[
 | 
						|
		initializer(dc->dc_idf, ds->ds_sc)
 | 
						|
	|
 | 
						|
		{ code_declaration(dc->dc_idf, (struct expr *) 0, level, ds->ds_sc); }
 | 
						|
	]
 | 
						|
	[
 | 
						|
		','
 | 
						|
		init_declarator(ds)
 | 
						|
	]*
 | 
						|
	';'
 | 
						|
;
 | 
						|
 | 
						|
/* 10.1 */
 | 
						|
function(struct declarator *dc;)
 | 
						|
	{
 | 
						|
		arith fbytes, nbytes;
 | 
						|
	}
 | 
						|
:
 | 
						|
	{	register struct idf *idf = dc->dc_idf;
 | 
						|
		
 | 
						|
		init_idf(idf);
 | 
						|
		stack_level();		/* L_FORMAL1 declarations */
 | 
						|
		declare_params(dc);
 | 
						|
		begin_proc(idf->id_text, idf->id_def);
 | 
						|
		stack_level();		/* L_FORMAL2 declarations */
 | 
						|
	}
 | 
						|
	declaration*
 | 
						|
	{
 | 
						|
		declare_formals(&fbytes);
 | 
						|
	}
 | 
						|
	compound_statement
 | 
						|
	{
 | 
						|
		end_proc(fbytes);
 | 
						|
		unstack_level();	/* L_FORMAL2 declarations */
 | 
						|
		unstack_level();	/* L_FORMAL1 declarations */
 | 
						|
	}
 | 
						|
;
 |