shithub: scc

ref: d27d299ee050ee5a26d277cbf9839a7d3eb2fb14
dir: /cc1/decl.c/

View raw version

#include <inttypes.h>
#include <setjmp.h>
#include <stdio.h>
#include <string.h>

#include "../inc/sizes.h"
#include "../inc/cc.h"
#include "cc1.h"

struct dcldata {
	unsigned char op;
	unsigned short nelem;
	unsigned char ndcl;
	void *data;
};

static struct dcldata *
queue(struct dcldata *dp, unsigned op, short nelem, void *data)
{
	unsigned n;

	if ((n = dp->ndcl) == NR_DECLARATORS)
		error("too much declarators");
	dp->op = op;
	dp->nelem = nelem;
	dp->data = data;
	++dp;
	dp->ndcl = n+1;
	return dp;
}

static struct dcldata *
arydcl(struct dcldata *dp)
{
	Node *np = NULL;
	TINT n;

	expect('[');
	if (yytoken != ']') {
		if ((np = iconstexpr()) == NULL)
			error("invalid storage size");
	}
	expect(']');

	n = (np == NULL) ? 0 : np->sym->u.i;
	freetree(np);

	return queue(dp, ARY, n, NULL);
}

static void
parameter(Symbol *sym, int sclass, Type *data)
{
	Type *tp = sym->type, *funtp = data;
	size_t n = funtp->n.elem;

	if (tp == voidtype) {
		if (n != 0)
			error("incorrect void parameter");
		funtp->n.elem = -1;
		return;
	}

	if (n == -1)
		error("'void' must be the only parameter");
	tp = sym->type;
	if (tp->op == FTN)
		error("incorrect function type for a function parameter");
	if (tp->op == ARY)
		tp = mktype(tp->type, PTR, 0, NULL);
	if (!sclass)
		sym->flags |= ISAUTO;
	if (sym->flags & (ISSTATIC|ISEXTERN))
		error("bad storage class in function parameter");
	if (n++ == NR_FUNPARAM)
		error("too much parameters in function definition");
	funtp->pars = xrealloc(funtp->pars, n * sizeof(Type *));
	funtp->pars[n-1] = tp;
	funtp->n.elem = n;
}

static Symbol *dodcl(int rep,
                     void (*fun)(Symbol *, int, Type *),
                     uint8_t ns, Type *type);

static struct dcldata *
fundcl(struct dcldata *dp)
{
	Type type = {.n = {.elem = -1}, .pars = NULL};
	Symbol *syms[NR_FUNPARAM], **sp;
	size_t size;
	void *p;

	pushctx();
	expect('(');

	if (accept(')')) {
		dp = queue(dp, FTN, type.n.elem, type.pars);
	} else {
		type.n.elem = 0;
		sp = syms;
		do
			*sp++ = dodcl(0, parameter, NS_IDEN, &type);
		while (accept(','));

		expect(')');

		dp = queue(dp, FTN, type.n.elem, type.pars);
		if (type.n.elem != -1) {
			size = type.n.elem * sizeof(Symbol *);
			p = memcpy(xmalloc(size), syms, size);
			dp = queue(dp, PARS, 0, p);
		}
	}

	switch (yytoken) {
	default:
		/* This is not a function */
		popctx();
	case '{':
	case TYPEIDEN:
	case TYPE:
	case TQUALIFIER:
	case SCLASS:
		/* This can be a function (K&R included) */
		break;
	}
	return dp;
}

static struct dcldata *declarator0(struct dcldata *dp, unsigned ns);

static struct dcldata *
directdcl(struct dcldata *dp, unsigned ns)
{
	Symbol *sym;

	if (accept('(')) {
		dp = declarator0(dp, ns);
		expect(')');
	} else {
		/* TODO: check type of the function */
		/* TODO: check function is not redefined */
		if (yytoken == IDEN || yytoken == TYPEIDEN) {
			if ((sym = install(ns)) == NULL)
				error("redeclaration of '%s'", yytext);
			next();
		} else {
			sym = newsym(ns);
		}
		dp = queue(dp, IDEN, 0, sym);
	}

	for (;;) {
		switch (yytoken) {
		case '(':  dp = fundcl(dp); break;
		case '[':  dp = arydcl(dp); break;
		default:   return dp;
		}
	}
}

static struct dcldata*
declarator0(struct dcldata *dp, unsigned ns)
{
	unsigned  n;

	for (n = 0; accept('*'); ++n) {
		while (accept(TQUALIFIER))
			/* nothing */;
	}

	dp = directdcl(dp, ns);

	while (n--)
		dp = queue(dp, PTR, 0, NULL);

	return dp;
}

static Symbol *
declarator(Type *tp, unsigned ns)
{
	struct dcldata data[NR_DECLARATORS+1];
	struct dcldata *bp;
	Symbol *sym, **pars = NULL;

	data[0].ndcl = 0;
	for (bp = declarator0(data, ns); bp-- > data; ) {
		switch (bp->op) {
		case IDEN:
			sym = bp->data;
			break;
		case PARS:
			pars = bp->data;
			break;
		default:
			tp = mktype(tp, bp->op, bp->nelem, bp->data);
			break;
		}
	}

	sym->u.pars = pars;

	/* TODO: deal with external array declarations of []  */
	if (!tp->defined && sym->name)
		error("declared variable '%s' of incomplete type", sym->name);
	sym->type = tp;
	return sym;
}

static Type *structdcl(void), *enumdcl(void);

static Type *
specifier(int *sclass)
{
	Type *tp = NULL;
	unsigned spec, qlf, sign, type, cls, size;

	spec = qlf = sign = type = cls = size = 0;

	for (;;) {
		unsigned *p;
		Type *(*dcl)(void) = NULL;

		switch (yytoken) {
		case SCLASS:
			p = &cls;
			break;
		case TQUALIFIER:
			if ((qlf |= yylval.token) & RESTRICT)
				goto invalid_type;
			next();
			continue;
		case TYPEIDEN:
			if (type)
				goto return_type;
			tp = yylval.sym->type;
			p = &type;
			break;
		case TYPE:
			switch (yylval.token) {
			case ENUM:
				dcl = enumdcl;
				p = &type;
				break;
			case STRUCT:
			case UNION:
				dcl = structdcl;
				p = &type;
				break;
			case VOID:
			case BOOL:
			case CHAR:
			case INT:
			case FLOAT:
			case DOUBLE:
				p = &type;
				break;
			case SIGNED:
			case UNSIGNED:
				p = &sign;
				break;
			case LONG:
				if (size == LONG) {
					size = LLONG;
					break;
				}
			case SHORT:
				p = &size;
				break;
			}
			break;
		default:
			goto return_type;
		}
		if (*p)
			goto invalid_type;
		*p = yylval.token;
		if (dcl) {
			if (size || sign)
				goto invalid_type;
			tp = (*dcl)();
			goto return_type;
		} else {
			next();
		}
		spec = 1;
	}

return_type:
	if (sclass)
		*sclass = cls;
	if (!tp && spec)
		tp = ctype(type, sign, size);
	return tp;

invalid_type:
	error("invalid type specification");
}

/* TODO: check correctness of the initializator  */
/* TODO: emit initializer */
static struct node *
initializer(Symbol *sym)
{
	if (!(sym->flags & ISEXTERN))
		error("'%s' initialized and declared extern", sym->name);

	if (accept('{')) {
		initializer(sym);
		expect('}');
	} else {
		do {
			expr();
		} while (accept(','));
	}
	return NULL;
}

static Symbol *
newtag(void)
{
	Symbol *sym;
	int op, tag = yylval.token;
	static unsigned ns = NS_STRUCTS;

	setnamespace(NS_TAG);
	next();
	switch (yytoken) {
	case IDEN:
	case TYPEIDEN:
		sym = yylval.sym;
		if ((sym->flags & ISDEFINED) == 0)
			install(NS_TAG);
		next();
		break;
	default:
		sym = newsym(NS_TAG);
		break;
	}
	if (!sym->type) {
		if (ns == NS_STRUCTS + NR_MAXSTRUCTS)
			error("too much tags declared");
		sym->type = mktype(NULL, tag, 0, NULL);
		sym->type->ns = ns++;
	}

	if ((op = sym->type->op) != tag &&  op != INT)
		error("'%s' defined as wrong kind of tag", sym->name);
	return sym;
}

/* TODO: bitfields */

static void fieldlist(Type *tp);

static Type *
structdcl(void)
{
	Symbol *sym;
	Type *tp;

	sym = newtag();
	tp = sym->type;
	if (!accept('{'))
		return tp;

	if (tp->defined)
		error("redefinition of struct/union '%s'", sym->name);
	tp->defined = 1;

	while (!accept('}'))
		fieldlist(tp);
	return tp;
}

static Type *
enumdcl(void)
{
	Type *tp;
	Symbol *sym, *tagsym;
	int val;

	tagsym = newtag();
	tp = tagsym->type;

	if (!accept('{'))
		return tp;
	if (tp->defined)
		error("redefinition of enumeration '%s'", tagsym->name);
	tp->defined = 1;
	for (val = 0; yytoken != ')'; ++val) {
		if (yytoken != IDEN)
			unexpected();
		if ((sym = install(NS_IDEN)) == NULL) {
			error("'%s' redeclared as different kind of symbol",
			      yytext);
		}
		next();
		sym->flags |= ISCONSTANT;
		sym->type = inttype;
		if (accept('=')) {
			Node *np = iconstexpr();

			if (np == NULL)
				error("invalid enumeration value");
			val = np->sym->u.i;
			freetree(np);
		}
		sym->u.i = val;
		if (!accept(','))
			break;
	}
	expect('}');

	return tp;
}

static void
type(Symbol *sym, int sclass, Type *data)
{
	if (sclass)
		error("class storage in type name");
	if (sym->name)
		error("unexpected identifier in type name");
}

static void
field(Symbol *sym, int sclass, Type *data)
{
	Type *tp = sym->type, *funtp = data;
	size_t n = funtp->n.elem;

	if (sclass)
		error("storage class in struct/union field");
	if (!sym->name)
		error("missed identifier in field declaration");
	if (tp->op == FTN)
		error("invalid type in struct/union");
	sym->flags |= ISFIELD;
	if (n++ == NR_FUNPARAM)
		error("too much fields in struct/union");
	funtp->pars = xrealloc(funtp->pars, n);
	funtp->pars[n-1] = tp;
	funtp->n.elem = n;
}

static void
internal(Symbol *sym, int sclass, Type *data)
{
	if (!sym->name) {
		warn("empty declaration");
		return;
	}
	if (sym->type->op != FTN) {
		if (!sclass)
			sym->flags |= ISAUTO;
		if (accept('='))
			initializer(sym);
		/* TODO: check if the variable is extern and has initializer */
	}
	emit(ODECL, sym);
}

static void
external(Symbol *sym, int sclass, Type *data)
{
	if (!sym->name) {
		warn("empty declaration");
		return;
	}
	sym->flags |= ISSTATIC|ISGLOBAL;

	if (sym->flags & (ISREGISTER|ISAUTO))
		error("incorrect storage class for file-scope declaration");

	if (sym->type->op == FTN && yytoken == '{') {
		if (sym->token == TYPEIDEN)
			error("function definition declared 'typedef'");
		curfun = sym;
		sym->flags |= ISDEFINED;
		emit(OFUN, sym);
		compound(NULL, NULL, NULL);
		emit(OEFUN, NULL);
		return;
	}
	if (accept('='))
		initializer(sym);
	/* TODO: check if the variable is extern and has initializer */
	emit(ODECL, sym);
}

static Symbol *
dodcl(int rep, void (*fun)(Symbol *, int, Type *), uint8_t ns, Type *type)
{
	Symbol *sym;
	Type *base, *tp;
	int sclass;

	if ((base = specifier(&sclass)) == NULL) {
		if (curctx != 0)
			unexpected();
		warn("type defaults to 'int' in declaration");
		base = inttype;
	}

	do {
		sym = declarator(base, ns);
		tp = sym->type;

		switch (sclass) {
		case REGISTER:
			sym->flags |= ISREGISTER;
			break;
		case AUTO:
			sym->flags |= ISAUTO;
			break;
		case STATIC:
			sym->flags |= ISSTATIC;
			break;
		case EXTERN:
			sym->flags |= ISEXTERN;
			break;
		case TYPEDEF:
			sym->token = TYPEIDEN;
			break;
		}
		if (tp->op == FTN && (sym->flags & (ISREGISTER|ISAUTO))) {
			error("invalid storage class for function '%s'",
			      sym->name);
		}
		(*fun)(sym, sclass, type);
	} while (rep && !curfun && accept(','));

	return sym;
}

void
decl(void)
{
	if (accept(';'))
		return;
	if (!dodcl(1, (curctx == 0) ? external : internal, NS_IDEN, NULL))
		return;
	if (curfun)
		curfun == NULL;
	else
		expect(';');
}

static void
fieldlist(Type *tp)
{
	if (yytoken != ';')
		dodcl(1, field, tp->ns, tp);
	expect(';');
}

Type *
typename(void)
{
	return dodcl(0, type, 0, NULL)->type;
}