shithub: scc

ref: 4f8c064dd03b39ff214733ab69fb49662e75a8dd
dir: /cc1/stmt.c/

View raw version

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

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

Symbol *curfun;

static void stmt(Symbol *lbreak, Symbol *lcont, Caselist *lswitch);

static void
label(void)
{
	Symbol *sym;

	switch (yytoken) {
	case IDEN:
	case TYPEIDEN:
		/*
		 * We cannot call to insert() because the call to lookup in
		 * lex.c was done in NS_IDEN namespace, and it is impossibe
		 * to fix this point, because an identifier at the beginning
		 * of a statement may be part of an expression or part of a
		 * label. This double call to lookup() is going to generate
		 * an undefined symbol that is not going to be used ever.
		 */
		sym = lookup(NS_LABEL);
		if (sym->flags & ISDECLARED)
			error("label '%s' already defined", yytoken);
		sym->flags |= ISDECLARED;
		emit(OLABEL, sym);
		next();
		expect(':');
		break;
	default:
		unexpected();
	}
}

static void
stmtexp(Symbol *lbreak, Symbol *lcont, Caselist *lswitch)
{
	if (accept(';'))
		return;
	if (ahead() == ':') {
		label();
		stmt(lbreak, lcont, lswitch);
		return;
	}
	emit(OEXPR, expr());
	expect(';');
}

static void
While(Symbol *lbreak, Symbol *lcont, Caselist *lswitch)
{
	Symbol *begin, *cond, *end;
	Node *np;

	begin = newsym(NS_LABEL);
	end = newsym(NS_LABEL);
	cond = newsym(NS_LABEL);

	expect(WHILE);
	np = condition();
	emit(OJUMP, cond);
	emit(OBLOOP, NULL);
	emit(OLABEL, begin);
	stmt(end, begin, lswitch);
	emit(OLABEL, cond);
	emit(OBRANCH, begin);
	emit(OEXPR, np);
	emit(OELOOP, NULL);
	emit(OLABEL, end);
}

static void
For(Symbol *lbreak, Symbol *lcont, Caselist *lswitch)
{
	Symbol *begin, *cond, *end;
	Node *econd, *einc, *einit;

	begin = newsym(NS_LABEL);
	end = newsym(NS_LABEL);
	cond = newsym(NS_LABEL);

	expect(FOR);
	expect('(');
	einit = (yytoken != ';') ? expr() : NULL;
	expect(';');
	econd = (yytoken != ';') ? exp2cond(expr(), 0) : NULL;
	expect(';');
	einc = (yytoken != ')') ? expr() : NULL;
	expect(')');

	emit(OEXPR, einit);
	emit(OJUMP, cond);
	emit(OBLOOP, NULL);
	emit(OLABEL, begin);
	stmt(end, begin, lswitch);
	emit(OEXPR, einc);
	emit(OLABEL, cond);
	emit(OBRANCH, begin);
	emit(OEXPR, econd);
	emit(OELOOP, NULL);
	emit(OLABEL, end);
}

static void
Dowhile(Symbol *lbreak, Symbol *lcont, Caselist *lswitch)
{
	Symbol *begin, *end;
	Node *np;

	begin = newsym(NS_LABEL);
	end = newsym(NS_LABEL);
	expect(DO);
	emit(OBLOOP, NULL);
	emit(OLABEL, begin);
	stmt(end, begin, lswitch);
	expect(WHILE);
	np = condition();
	emit(OBRANCH, begin);
	emit(OEXPR, np);
	emit(OELOOP, NULL);
	emit(OLABEL, end);
}

static void
Return(Symbol *lbreak, Symbol *lcont, Caselist *lswitch)
{
	Node *np;
	Type *tp = curfun->type->type;

	expect(RETURN);
	np = (yytoken != ';') ? eval(expr()) : NULL;
	expect(';');
	if (!np) {
		if (tp != voidtype)
			warn("function returning non void returns no value");
		tp = voidtype;
	} else if (np->type != tp) {
		if (tp == voidtype)
			warn("function returning void returns a value");
		else if ((np = convert(np, tp, 0)) == NULL)
			error("incorrect type in return");
	}
	emit(ORET, tp);
	emit(OEXPR, np);
}

static void
Break(Symbol *lbreak, Symbol *lcont, Caselist *lswitch)
{
	expect(BREAK);
	if (!lbreak)
		error("break statement not within loop or switch");
	emit(OJUMP, lbreak);
	expect(';');
}

static void
Continue(Symbol *lbreak, Symbol *lcont, Caselist *lswitch)
{
	expect(CONTINUE);
	if (!lcont)
		error("continue statement not within loop");
	emit(OJUMP, lcont);
	expect(';');
}

static void
Goto(Symbol *lbreak, Symbol *lcont, Caselist *lswitch)
{
	setnamespace(NS_LABEL);
	next();
	if (yytoken != IDEN)
		unexpected();
	yylval.sym->flags |= ISUSED;
	emit(OJUMP, yylval.sym);
	next();
	expect(';');
}

static void
Switch(Symbol *lbreak, Symbol *lcont, Caselist *lswitch)
{
	Caselist lcase = {.nr = 0, .head = NULL, .deflabel = NULL};
	Node *cond;

	expect(SWITCH);
	expect ('(');
	cond = eval(expr());
	/* TODO: why can I not call directly to convert here? */

	switch (BTYPE(cond)) {
	case INT:
	case ENUM:
		cond = convert(cond, inttype, 0);
		break;
	default:
		error("incorrect type in switch statement");
	}
	expect (')');

	lcase.expr = cond;
	lcase.lbreak = newsym(NS_LABEL);
	lcase.ltable = newsym(NS_LABEL);

	emit(OSWITCH, &lcase);
	stmt(lbreak, lcont, &lcase);
	emit(OJUMP, lcase.lbreak);
	emit(OLABEL, lcase.ltable);
	emit(OSWITCHT, &lcase);
	emit(OLABEL, lcase.lbreak);
}

static void
Case(Symbol *lbreak, Symbol *lcont, Caselist *lswitch)
{
	Node *np;
	struct scase *pcase;

	expect(CASE);
	if (!lswitch)
		error("case label not within a switch statement");
	if ((np = iconstexpr()) == NULL)
		error("case label does not reduce to an integer constant");
	expect(':');
	pcase = xmalloc(sizeof(*pcase));
	pcase->expr = np;
	pcase->next = lswitch->head;
	emit(OLABEL, pcase->label = newsym(NS_LABEL));
	lswitch->head = pcase;
	++lswitch->nr;
	stmt(lbreak, lcont, lswitch);
}

static void
Default(Symbol *lbreak, Symbol *lcont, Caselist *lswitch)
{
	Symbol *ldefault = newsym(NS_LABEL);

	expect(DEFAULT);
	expect(':');
	emit(OLABEL, ldefault);
	lswitch->deflabel = ldefault;
	++lswitch->nr;
	stmt(lbreak, lcont, lswitch);
}

static void
If(Symbol *lbreak, Symbol *lcont, Caselist *lswitch)
{
	Symbol *end, *lelse;
	Node *np;

	lelse = newsym(NS_LABEL);
	expect(IF);
	np = condition();
	emit(OBRANCH, lelse);
	emit(OEXPR, negate(np));
	stmt(lbreak, lcont, lswitch);
	if (accept(ELSE)) {
		end = newsym(NS_LABEL);
		emit(OJUMP, end);
		emit(OLABEL, lelse);
		stmt(lbreak, lcont, lswitch);
		emit(OLABEL, end);
	} else {
		emit(OLABEL, lelse);
	}
}

static void
blockit(Symbol *lbreak, Symbol *lcont, Caselist *lswitch)
{
	switch (yytoken) {
	case TYPEIDEN:
		if (ahead() == ':')
			goto parse_stmt;
		/* PASSTHROUGH */
	case TYPE:
	case TQUALIFIER:
	case SCLASS:
		decl();
		return;
	default:
	parse_stmt:
		stmt(lbreak, lcont, lswitch);
	}
}

void
compound(Symbol *lbreak, Symbol *lcont, Caselist *lswitch)
{
	extern jmp_buf recover;

	pushctx();
	expect('{');

	for (;;) {
		setsafe(END_COMP);
		setjmp(recover);
		if (yytoken == '}')
			break;
		blockit(lbreak, lcont, lswitch);
	}

	popctx();
	/*
	 * curctx == GLOBALCTX+1 means we are at the end of a function
	 * so we have to pop the context related to the parameters
	 */
	if (curctx == GLOBALCTX+1)
		popctx();
	expect('}');
}

static void
stmt(Symbol *lbreak, Symbol *lcont, Caselist *lswitch)
{
	void (*fun)(Symbol *, Symbol *, Caselist *);

	switch (yytoken) {
	case '{':      fun = compound; break;
	case RETURN:   fun = Return;   break;
	case WHILE:    fun = While;    break;
	case FOR:      fun = For;      break;
	case DO:       fun = Dowhile;  break;
	case IF:       fun = If;       break;
	case BREAK:    fun = Break;    break;
	case CONTINUE: fun = Continue; break;
	case GOTO:     fun = Goto;     break;
	case SWITCH:   fun = Switch;   break;
	case CASE:     fun = Case;     break;
	case DEFAULT:  fun = Default;  break;
	default:       fun = stmtexp;  break;
	}
	(*fun)(lbreak, lcont, lswitch);
}