diff options
Diffstat (limited to 'stmt.c')
-rw-r--r-- | stmt.c | 280 |
1 files changed, 280 insertions, 0 deletions
@@ -0,0 +1,280 @@ +#include <assert.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "util.h" +#include "decl.h" +#include "emit.h" +#include "expr.h" +#include "pp.h" +#include "scope.h" +#include "stmt.h" +#include "token.h" +#include "type.h" + +/* 6.8 Statements and blocks */ +void +stmt(struct function *f, struct scope *s) +{ + char *name; + struct expression *e; + struct type *t; + struct value *v, *label[4]; + struct switchcases swtch = {0}; + uint64_t i; + + switch (tok.kind) { + /* 6.8.1 Labeled statements */ + case TCASE: + next(); + if (!s->switchcases) + error(&tok.loc, "'case' label must be in switch"); + label[0] = mkblock("switch_case"); + funclabel(f, label[0]); + i = intconstexpr(s); + switchcase(s->switchcases, i, label[0]); + expect(TCOLON, "after case expression"); + stmt(f, s); + break; + case TDEFAULT: + next(); + if (!s->switchcases) + error(&tok.loc, "'default' label must be in switch"); + if (s->switchcases->defaultlabel) + error(&tok.loc, "multiple 'default' labels"); + expect(TCOLON, "after 'default'"); + s->switchcases->defaultlabel = mkblock("switch_default"); + funclabel(f, s->switchcases->defaultlabel); + stmt(f, s); + break; + + /* 6.8.2 Compound statement */ + case TLBRACE: + next(); + s = mkscope(s); + while (tok.kind != TRBRACE) { + if (!decl(s, f)) + stmt(f, s); + } + s = delscope(s); + next(); + break; + + /* 6.8.3 Expression statement */ + case TSEMICOLON: + next(); + break; + case TIDENT: + name = tok.lit; + if (peek(TCOLON)) { + struct gotolabel *g = funcgoto(f, name); + g->defined = true; + funclabel(f, g->label); + stmt(f, s); + break; + } + /* fallthrough */ + default: + e = expr(s); + v = funcexpr(f, e); + delexpr(e); + expect(TSEMICOLON, "after expression statement"); + break; + + /* 6.8.4 Selection statement */ + case TIF: + next(); + s = mkscope(s); + expect(TLPAREN, "after 'if'"); + e = exprconvert(expr(s), &typebool); + v = funcexpr(f, e); + delexpr(e); + expect(TRPAREN, "after expression"); + + label[0] = mkblock("if_true"); + label[1] = mkblock("if_false"); + funcjnz(f, v, label[0], label[1]); + + funclabel(f, label[0]); + s = mkscope(s); + stmt(f, s); + s = delscope(s); + + if (consume(TELSE)) { + label[2] = mkblock("if_join"); + funcjmp(f, label[2]); + funclabel(f, label[1]); + s = mkscope(s); + stmt(f, s); + s = delscope(s); + funclabel(f, label[2]); + } else { + funclabel(f, label[1]); + } + s = delscope(s); + break; + case TSWITCH: + next(); + + s = mkscope(s); + expect(TLPAREN, "after 'switch'"); + e = expr(s); + expect(TRPAREN, "after expression"); + + t = typeunqual(e->type, NULL); + if (!(typeprop(t) & PROPINT)) + error(&tok.loc, "controlling expression of switch statement must have integer type"); + e = exprconvert(e, typeintpromote(t)); + + label[0] = mkblock("switch_cond"); + label[1] = mkblock("switch_join"); + + v = funcexpr(f, e); + funcjmp(f, label[0]); + s = mkscope(s); + s->breaklabel = label[1]; + s->switchcases = &swtch; + stmt(f, s); + funcjmp(f, label[1]); + + funclabel(f, label[0]); + funcswitch(f, v, &swtch, swtch.defaultlabel ? swtch.defaultlabel : label[1]); + s = delscope(s); + + funclabel(f, label[1]); + s = delscope(s); + break; + + /* 6.8.5 Iteration statements */ + case TWHILE: + next(); + s = mkscope(s); + expect(TLPAREN, "after 'while'"); + e = expr(s); + expect(TRPAREN, "after expression"); + + label[0] = mkblock("while_cond"); + label[1] = mkblock("while_body"); + label[2] = mkblock("while_join"); + + funclabel(f, label[0]); + v = funcexpr(f, e); + funcjnz(f, v, label[1], label[2]); + funclabel(f, label[1]); + s = mkscope(s); + s->continuelabel = label[0]; + s->breaklabel = label[2]; + stmt(f, s); + s = delscope(s); + funcjmp(f, label[0]); + funclabel(f, label[2]); + s = delscope(s); + break; + case TDO: + next(); + + label[0] = mkblock("do_body"); + label[1] = mkblock("do_join"); + + s = mkscope(s); + s = mkscope(s); + s->continuelabel = label[0]; + s->breaklabel = label[1]; + funclabel(f, label[0]); + stmt(f, s); + s = delscope(s); + + expect(TWHILE, "after 'do' statement"); + expect(TLPAREN, "after 'while'"); + e = expr(s); + expect(TRPAREN, "after expression"); + + v = funcexpr(f, e); + funcjnz(f, v, label[0], label[1]); // XXX: compare to 0 + funclabel(f, label[1]); + s = delscope(s); + expect(TSEMICOLON, "after 'do' statement"); + break; + case TFOR: + next(); + expect(TLPAREN, "after while"); + s = mkscope(s); + if (!decl(s, f)) { + if (tok.kind != TSEMICOLON) { + e = expr(s); + funcexpr(f, e); + delexpr(e); + } + expect(TSEMICOLON, NULL); + } + + label[0] = mkblock("for_cond"); + label[1] = mkblock("for_body"); + label[2] = mkblock("for_cont"); + label[3] = mkblock("for_join"); + + funclabel(f, label[0]); + if (tok.kind != TSEMICOLON) { + e = expr(s); + v = funcexpr(f, e); + funcjnz(f, v, label[1], label[3]); + delexpr(e); + } + expect(TSEMICOLON, NULL); + e = tok.kind == TRPAREN ? NULL : expr(s); + expect(TRPAREN, NULL); + + funclabel(f, label[1]); + s = mkscope(s); + s->breaklabel = label[3]; + s->continuelabel = label[2]; + stmt(f, s); + s = delscope(s); + + funclabel(f, label[2]); + if (e) { + funcexpr(f, e); + delexpr(e); + } + funcjmp(f, label[0]); + funclabel(f, label[3]); + s = delscope(s); + break; + + /* 6.8.6 Jump statements */ + case TGOTO: + next(); + name = expect(TIDENT, "after 'goto'"); + funcjmp(f, funcgoto(f, name)->label); + expect(TSEMICOLON, "after 'goto' statement"); + break; + case TCONTINUE: + next(); + if (!s->continuelabel) + error(&tok.loc, "'continue' statement must be in loop"); + funcjmp(f, s->continuelabel); + expect(TSEMICOLON, "after 'continue' statement"); + break; + case TBREAK: + next(); + if (!s->breaklabel) + error(&tok.loc, "'break' statement must be in loop or switch"); + funcjmp(f, s->breaklabel); + expect(TSEMICOLON, "after 'break' statement"); + break; + case TRETURN: + next(); + if (f->type->base != &typevoid) { + e = exprconvert(expr(s), f->type->base); + v = funcexpr(f, e); + delexpr(e); + } else { + v = NULL; + } + funcret(f, v); + expect(TSEMICOLON, "after 'return' statement"); + break; + } +} |