decl.c

This source file is part of the SubC compiler, which is described in the book

Practical Compiler Construction.

You might prefer to download the compiler source code. It is in the public domain.

Declaration Parser

This module of the compiler parses declarations and definitions, including function definitions, local variables, global data objects, and function prototypes. This is the most top-level part of the parser.

/*
 *	NMH's Simple C Compiler, 2011,2012
 *	Declaration parser
 */

#include "defs.h"
#include "data.h"
#include "decl.h"

int declarator(int arg, int scls, char *name, int *pprim, int *psize,
		int *pval, int *pinit);

/*
 * enumdecl := { enumlist }
 *
 * enumlist :=
 *	  enumerator
 *	| enumerator , enumlist
 *
 * enumerator :=
 *	  IDENT
 *	| IDENT = constexpr
 */

static void enumdecl(void) {
	int	v = 0;
	char	name[NAMELEN+1];

	Token = scan();
	if (IDENT == Token)
		Token = scan();
	lbrace();
	for (;;) {
		copyname(name, Text);
		ident();
		if (ASSIGN == Token) {
			Token = scan();
			v = constexpr();
		}
		addglob(name, PINT, TCONSTANT, 0, 0, v++, NULL, 0);
		if (Token != COMMA)
			break;
		Token = scan();
		if (eofcheck()) return;
	}
	match(RBRACE, "'}'");
	semi();
}

/*
 * initlist :=
 *	  { const_list }
 *	| STRLIT
 *
 * const_list :=
 *	  constexpr
 *	| constexpr , const_list
 */

static int initlist(char *name, int prim) {
	int	n = 0, v;
	char	buf[30];

	gendata();
	genname(name);
	if (STRLIT == Token) {
		if (PCHAR != prim)
			error("initializer type mismatch: %s", name);
		gendefs(Text, Value);
		gendefb(0);
		Token = scan();
		return Value -1;
	}
	lbrace();
	while (Token != RBRACE) {
		v = constexpr();
		if (PCHAR == prim) {
			if (v < 0 || v > 255) {
				sprintf(buf, "%d", v);
				error("initializer out of range: %s", buf);
			}
			gendefb(v);
		}
		else {
			gendefw(v);
		}
		n++;
		if (COMMA == Token)
			Token = scan();
		else
			break;
		if (eofcheck()) return 0;
	}
	Token = scan();
	if (!n) error("too few initializers", NULL);
	return n;
}

int primtype(int t) {
	return t == CHAR? PCHAR:
		t == INT? PINT:
		PVOID;
}

/*
 * pmtrdecl :=
 *	  ( )
 *	| ( pmtrlist )
 *	| ( pmtrlist , ... )
 *
 * pmtrlist :=
 *	  primtype declarator
 *	| primtype declarator , pmtrlist
 */

static int pmtrdecls(void) {
	char	name[NAMELEN+1];
	int	prim, type, size, na, y, addr;
	int	dummy;

	if (RPAREN == Token)
		return 0;
	na = 0;
	for (;;) {
		if (na > 0 && ELLIPSIS == Token) {
			Token = scan();
			na = -(na + 1);
			break;
		}
		else if (IDENT == Token) {
			prim = PINT;
		}
		else {
			if (Token != CHAR && Token != INT && Token != VOID) {
				error("type specifier expected at: %s", Text);
				Token = synch(RPAREN);
				return na;
			}
			name[0] = 0;
			prim = primtype(Token);
			Token = scan();
			if (RPAREN == Token && prim == PVOID && !na)
				return 0;
		}
		size = 1;
		type = declarator(1, CAUTO, name, &prim, &size, &dummy,
				&dummy);
		addloc(name, prim, type, CAUTO, size, 0, 0);
		na++;
		if (COMMA == Token)
			Token = scan();
		else
			break;
	}
	addr = INTSIZE*2;
	for (y = Locs; y < NSYMBOLS; y++) {
		addr += INTSIZE;
		Vals[y] = addr;
	}
	return na;
}

int pointerto(int prim) {
	if (CHARPP == prim || INTPP == prim || VOIDPP == prim ||
	    FUNPTR == prim
	)
		error("too many levels of indirection", NULL);
	return PINT == prim? INTPTR:
		PCHAR == prim? CHARPTR:
		PVOID == prim? VOIDPTR:
		INTPTR == prim? INTPP:
		CHARPTR == prim? CHARPP: VOIDPP;
}

/*
 * declarator :=
 *	  IDENT
 *	| * IDENT
 *	| * * IDENT
 *	| * IDENT [ constexpr ]
 *	| IDENT [ constexpr ]
 *	| IDENT = constexpr
 *	| IDENT [ ] = initlist
 *	| IDENT pmtrdecl
 *	| IDENT [ ]
 *	| * IDENT [ ]
 *	| ( * IDENT ) ( )
 */

int declarator(int pmtr, int scls, char *name, int *pprim, int *psize,
		int *pval, int *pinit)
{
	int	type = TVARIABLE;
	int	ptrptr = 0;

	if (STAR == Token) {
		Token = scan();
		*pprim = pointerto(*pprim);
		if (STAR == Token) {
			Token = scan();
			*pprim = pointerto(*pprim);
			ptrptr = 1;
		}
	}
	else if (LPAREN == Token) {
		if (*pprim != PINT)
			error("function pointers are limited to 'int'",
				NULL);
		Token = scan();
		*pprim = FUNPTR;
		match(STAR, "(*name)()");
	}
	if (IDENT != Token) {
		error("missing identifier at: %s", Text);
		name[0] = 0;
	}
	else {
		copyname(name, Text);
		Token = scan();
	}
	if (FUNPTR == *pprim) {
		rparen();
		lparen();
		rparen();
	}
	if (!pmtr && ASSIGN == Token) {
		Token = scan();
		*pval = constexpr();
		if (*pval && !inttype(*pprim))
			error("non-null pointer initialization", NULL);
		*pinit = 1;
	}
	else if (!pmtr && LPAREN == Token) {
		Token = scan();
		*psize = pmtrdecls();
		rparen();
		return TFUNCTION;
	}
	else if (LBRACK == Token) {
		if (ptrptr)
			error("too many levels of indirection: %s", name);
		Token = scan();
		if (RBRACK == Token) {
			Token = scan();
			if (pmtr) {
				*pprim = pointerto(*pprim);
			}
			else {
				type = TARRAY;
				*psize = 1;
				if (ASSIGN == Token) {
					Token = scan();
					if (!inttype(*pprim))
						error("initialization of"
							" pointer array not"
							" supported",
							NULL);
					*psize = initlist(name, *pprim);
					if (CAUTO == scls)
						error("initialization of"
							" local arrays"
							" not supported: %s",
							name);
					*pinit = 1;
				}
				else if (CEXTERN != scls) {
					error("automatically-sized array"
						" lacking initialization: %s",
						name);
				}
			}
		}
		else {
			if (pmtr) error("array size not supported in "
					"parameters: %s", name);
			*psize = constexpr();
			if (*psize <= 0) {
				error("invalid array size", NULL);
				*psize = 1;
			}
			type = TARRAY;
			rbrack();
		}
	}
	if (PVOID == *pprim)
		error("'void' is not a valid type: %s", name);
	return type;
}

void signature(int fn, int from, int to) {
	char	types[MAXFNARGS+1];
	int	i;

	if (to - from > MAXFNARGS)
		error("too many function parameters", Names[fn]);
	for (i=0; i<MAXFNARGS && from < to; i++)
		types[i] = Prims[--to];
	types[i] = 0;
	if (NULL == Mtext[fn])
		Mtext[fn] = globname(types);
	else if (Sizes[fn] >= 0 && strcmp(Mtext[fn], types))
		error("redefinition does not match prior type: %s",
			Names[fn]);
}

/*
 * localdecls :=
 *        ldecl
 *      | ldecl localdecls
 *
 * ldecl :=
 *	  primtype ldecl_list ;
 *	| STATIC ldecl_list ;
 *	| STATIC primtype ldecl_list ;
 *
 * ldecl_list :=
 *	  declarator
 *	| declarator , ldecl_list
 */

static int localdecls(void) {
	char	name[NAMELEN+1];
	int	prim, type, size, addr = 0, val, ini;
	int	stat;
	int	pbase, rsize;

	Nli = 0;
	while (	STATIC == Token ||
		INT == Token || CHAR == Token || VOID == Token
	) {
		stat = 0;
		if (STATIC == Token) {
			Token = scan();
			stat = 1;
			if (INT == Token || CHAR == Token || VOID == Token) {
				prim = primtype(Token);
				Token = scan();
			}
			else
				prim = PINT;
		}
		else {
			prim = primtype(Token);
			Token = scan();
		}
		pbase = prim;
		for (;;) {
			prim = pbase;
			if (eofcheck()) return 0;
			size = 1;
			ini = val = 0;
			type = declarator(0, CAUTO, name, &prim, &size,
					&val, &ini);
			rsize = objsize(prim, type, size);
			rsize = (rsize + INTSIZE-1) / INTSIZE * INTSIZE;
			if (stat) {
				addloc(name, prim, type, CLSTATC, size,
					label(), val);
			}
			else {
				addr -= rsize;
				addloc(name, prim, type, CAUTO, size, addr, 0);
			}
			if (ini && !stat) {
				if (Nli >= MAXLOCINIT) {
					error("too many local initializers",
						NULL);
					Nli = 0;
				}
				LIaddr[Nli] = addr;
				LIval[Nli++] = val;
			}
			if (COMMA == Token)
				Token = scan();
			else
				break;
		}
		semi();
	}
	return addr;
}

/*
 * decl :=
 *	  declarator { localdecls stmt_list }
 *	| decl_list ;
 *
 * decl_list :=
 *	  declarator
 *	| declarator , decl_list
 */

void decl(int clss, int prim) {
	char	name[NAMELEN+1];
	int	pbase, type, size = 0, val, init;
	int	lsize;

	pbase = prim;
	for (;;) {
		prim = pbase;
		val = 0;
		init = 0;
		type = declarator(0, clss, name, &prim, &size, &val, &init);
		if (TFUNCTION == type) {
			if (SEMI == Token) {
				Token = scan();
				clss = CEXTERN;
			}
			Thisfn = addglob(name, prim, type, clss, size, 0,
					NULL, 0);
			signature(Thisfn, Locs, NSYMBOLS);
			if (clss != CEXTERN) {
				lbrace();
				lsize = localdecls();
				gentext();
				if (CPUBLIC == clss) genpublic(name);
				genname(name);
				genentry();
				genstack(lsize);
				genlocinit();
				Retlab = label();
				compound(0);
				genlab(Retlab);
				genstack(-lsize);
				genexit();
				if (O_debug & D_LSYM)
					dumpsyms("LOCALS: ", name, Locs,
						NSYMBOLS);
			}
			clrlocs();
			return;
		}
		if (CEXTERN == clss && init) {
			error("initialization of 'extern': %s", name);
		}
		addglob(name, prim, type, clss, size, val, NULL, init);
		if (COMMA == Token)
			Token = scan();
		else
			break;
	}
	semi();
}

/*
 * top :=
 *	  ENUM enumdecl
 *	| decl
 *	| primtype decl
 *	| storclass decl
 *	| storclass primtype decl
 *
 * storclass :=
 *	  EXTERN
 *	| STATIC
 */

void top(void) {
	int	prim, clss = CPUBLIC;

	switch (Token) {
	case EXTERN:	clss = CEXTERN; Token = scan(); break;
	case STATIC:	clss = CSTATIC; Token = scan(); break;
	}
	switch (Token) {
	case ENUM:
		enumdecl();
		break;
	case CHAR:
	case INT:
	case VOID:
		prim = primtype(Token);
		Token = scan();
		decl(clss, prim);
		break;
	case IDENT:
		decl(clss, PINT);
		break;
	default:
		error("type specifier expected at: %s", Text);
		Token = synch(SEMI);
		break;
	}
}

contact