aboutsummaryrefslogtreecommitdiff
path: root/stmt.c
diff options
context:
space:
mode:
Diffstat (limited to 'stmt.c')
-rw-r--r--stmt.c280
1 files changed, 280 insertions, 0 deletions
diff --git a/stmt.c b/stmt.c
new file mode 100644
index 0000000..866d3ff
--- /dev/null
+++ b/stmt.c
@@ -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;
+ }
+}