shithub: scc

ref: 671acae249562120edaec2fb6ff24411ef9be84c
dir: /cc1/init.c/

View raw version

#include <stdio.h>
#include <stdlib.h>

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

struct designator {
	TINT pos;
	struct designator *next;
};

struct inititlizer {
	Node *expr;
	struct designator *dp;
	struct inititalizer *next;
};

static TINT
arydesig(Type *tp)
{
	TINT npos;
	Node *np;

	if (tp->op != ARY)
		errorp("array index in non-array initializer");
	next();
	np = iconstexpr();
	npos = np->sym->u.i;
	freetree(np);
	expect(']');
	return npos;
}

static TINT
fielddesig(Type *tp)
{
	TINT npos;
	int ons;
	Symbol *sym, **p;

	if (!tp->aggreg)
		errorp("field name not in record or union initializer");
	ons = namespace;
	namespace = tp->ns;
	next();
	namespace = ons;
	if (yytoken != IDEN)
		unexpected();
	sym = yylval.sym;
	if ((sym->flags & ISDECLARED) == 0) {
		errorp(" unknown field '%s' specified in initializer",
		      sym->name);
		return 0;
	}
	for (p = tp->p.fields; *p != sym; ++p)
		/* nothing */;
	return p - tp->p.fields;
}

static struct designator *
designation(Type *tp)
{
	struct designator *dp, *d, *head;
	TINT (*fun)(Type *);

	for (;;) {
		switch (yytoken) {
		case '[': fun = arydesig;   break;
		case '.': fun = fielddesig; break;
		default:
			if (head)
				expect('=');
			return head;
		}
		d = xmalloc(sizeof(*d));
		d->next = NULL;

		if (!head) {
			head = dp = d;
		} else {
			dp->next = d;
			dp = d;
		}
		dp->pos  = (*fun)(tp);
	}
}

static struct designator *
initlist(Symbol *sym, Type *tp)
{
	struct designator *dp;
	struct inititlizer *ip;
	int toomany = 0;
	TINT n;
	Type *newtp;

	for (n = 0; ; ++n) {
		if ((dp = designation(tp)) == NULL) {
			dp = xmalloc(sizeof(*dp));
			dp->pos = n;
		} else {
			n = dp->pos;
		}
		switch (tp->op) {
		case ARY:
			if (tp->defined && n >= tp->n.elem) {
				if (!toomany)
					warn("excess elements in array initializer");
				toomany = 1;
				sym = NULL;
			}
			newtp = tp->type;
			break;
		case STRUCT:
			if (n >= tp->n.elem) {
				if (!toomany)
					warn("excess elements in struct initializer");
				toomany = 1;
				sym = NULL;
			} else {
				sym = tp->p.fields[n];
				newtp = sym->type;
			}
			break;
		default:
			newtp = tp;
			warn("braces around scalar initializer");
			if (n > 0) {
				if (!toomany)
					warn("excess elements in scalar initializer");
				toomany = 1;
				sym = NULL;
			}
			break;
		}
		if (accept('{'))
			return initlist(sym, tp);
		ip->expr = assign(NULL);

		if (!accept(','))
			break;
	}
	expect('}');

	if (tp->op == ARY && !tp->defined) {
		tp->n.elem = n + 1;
		tp->defined = 1;
	}
	return dp;
}

void
initializer(Symbol *sym, Type *tp, int nelem)
{
	Node *np;
	int flags = sym->flags;

	if (tp->op == FTN)
		errorp("function '%s' is initialized like a variable", sym->name);

	switch (yytoken) {
	case '{':
		initlist(sym, tp);
		return;
	case '=':
		np = assign(varnode(sym));
		break;
	}

	if (flags & ISDEFINED) {
		errorp("redeclaration of '%s'", sym->name);
	} else if ((flags & (ISGLOBAL|ISLOCAL|ISPRIVATE)) != 0) {
		if (!np->right->constant)
			errorp("initializer element is not constant");
		emit(OINIT, np);
		sym->flags |= ISDEFINED;
	} else if ((flags & (ISEXTERN|ISTYPEDEF)) != 0) {
		errorp("'%s' has both '%s' and initializer",
		       sym->name, (flags&ISEXTERN) ? "extern" : "typedef");
	} else {
		np->op = OASSIGN;
		emit(OEXPR, np);
	}
}