diff options
| -rw-r--r-- | bootstrap/all.h | 15 | ||||
| -rw-r--r-- | bootstrap/cgen.c | 27 | ||||
| -rw-r--r-- | bootstrap/dump.c | 2 | ||||
| -rw-r--r-- | bootstrap/parse.c | 66 | ||||
| -rw-r--r-- | bootstrap/test2.cff | 7 |
5 files changed, 110 insertions, 7 deletions
diff --git a/bootstrap/all.h b/bootstrap/all.h index 6e63058..6055a07 100644 --- a/bootstrap/all.h +++ b/bootstrap/all.h @@ -136,6 +136,7 @@ struct parser { bool is_header; bool save_envage; int envage; + int varid; }; enum typetype { @@ -392,6 +393,7 @@ enum stmttype { Sfor, Siswitch, Sreturn, + Seuswitch, }; struct iswitchcase { @@ -399,6 +401,14 @@ struct iswitchcase { struct blockstmt t; }; +struct euswitchcase { + const char *capt; + int captid; + int vval; + struct aggfield *fld; + struct blockstmt t; +}; + struct stmt { enum stmttype t; struct span span; @@ -422,6 +432,11 @@ struct stmt { slice_t(struct iswitchcase) cs; struct blockstmt *f; } iswitch; + struct { + struct expr test; + slice_t(struct euswitchcase) cs; + struct blockstmt *f; + } euswitch; struct expr *retex; }; }; diff --git a/bootstrap/cgen.c b/bootstrap/cgen.c index 8bea8bc..769d2e9 100644 --- a/bootstrap/cgen.c +++ b/bootstrap/cgen.c @@ -367,6 +367,25 @@ genstmt(struct stmt *stmt) { } pri("}\n"); break; + case Seuswitch: + pri("{ %t __stmp = %e;\n", stmt->euswitch.test.ty, &stmt->euswitch.test); + pri("switch (__stmp.t) {", &stmt->euswitch.test); + for (int i = 0; i < stmt->euswitch.cs.n; ++i) { + struct euswitchcase c = stmt->euswitch.cs.d[i]; + pri("case /* %s */ %I: ", c.fld->name, c.vval); + if (c.capt) + pri("{ %t %s_%d = __stmp.u.%s;\n", c.fld->ty, c.capt, c.captid, c.fld->name); + genblock(c.t); + if (c.capt) + pri("}\n"); + pri("break;\n"); + } + if (stmt->iswitch.f) { + pri("default: "); + genblock(*stmt->iswitch.f); + } + pri("}\n"); pri("}\n"); + break; case Sreturn: pri("return"); if (stmt->retex) @@ -519,6 +538,14 @@ liftnested(struct stmt *stmt) { if (stmt->retex) liftnestedex(stmt->retex); break; + case Seuswitch: + liftnestedex(&stmt->euswitch.test); + for (int i = 0; i < stmt->euswitch.cs.n; ++i) { + liftnested(blocktostmt(stmt->euswitch.cs.d[i].t)); + } + if (stmt->euswitch.f) + liftnested(blocktostmt(*stmt->euswitch.f)); + break; } } diff --git a/bootstrap/dump.c b/bootstrap/dump.c index 047e77b..8c34716 100644 --- a/bootstrap/dump.c +++ b/bootstrap/dump.c @@ -385,6 +385,8 @@ dumpstmt(const struct stmt *stmt, int ws) { else epri("%wreturn;\n", ws); break; + case Seuswitch: + break; } } diff --git a/bootstrap/parse.c b/bootstrap/parse.c index 963b986..cd41b47 100644 --- a/bootstrap/parse.c +++ b/bootstrap/parse.c @@ -840,6 +840,12 @@ structfldnam2idx(const struct type *ty, const char *name) { return -1; } +static struct aggfield * +findaggfield(const struct type *ty, const char *name) { + int i = structfldnam2idx(ty, name); + return i < 0 ? NULL : &ty->agg.flds.d[i]; +} + static struct decl * findaggdecl(const struct type *ty, const char *name) { assert(name); @@ -1021,10 +1027,10 @@ pexprimary(struct parser *P) { if (decl->t == Dtype) { ty = decl->ty; typedecl: - if (ty->t == TYenum) { + if (ty->t == TYenum || ty->t == TYeunion) { lexexpect(P, ':'); P->targty = ty; - goto enumlit; + goto variant; } else if (ty->t == TYstruct || ty->t == TYunion) { if (lexmatch(P, &tok, ':')) { const char *nam = (tok = lexexpect(P, TKident)).str; @@ -1089,7 +1095,7 @@ pexprimary(struct parser *P) { } else if (lexmatch(P, &tok, ':')) { const char *vname; int i; - enumlit: + variant: vname = (tok = lexexpect(P, TKident)).str; if (!(ex.ty = P->targty) || (ex.ty->t != TYenum && ex.ty->t != TYeunion)) fatal(P, tok.span, "cannot infer type for enum literal `:%s'", vname); @@ -1728,7 +1734,6 @@ parsevardecl(decl_yielder_t yield, void *yarg, struct parser *P, bool let, bool const struct type *ty = NULL; struct expr *ini = NULL; bool konst = 0; - static int id; if (lexmatch(P, NULL, TKkw_const)) konst = 1; @@ -1786,7 +1791,7 @@ parsevardecl(decl_yielder_t yield, void *yarg, struct parser *P, bool let, bool decl.span = tok.span; decl.var.ty = ty; decl.var.ini = ini; - decl.var.id = id++; + decl.var.id = P->varid++; decl.var.fnid = P->curfn ? P->curfn->id : -1; yield(memcpy(xmalloc(sizeof decl), &decl, sizeof decl), yarg); if (!lexmatch(P, &tok, ',')) { @@ -1942,6 +1947,53 @@ pstiswitch(struct parser *P, const struct expr *test) { } static struct stmt +psteuswitch(struct parser *P, const struct expr *test) { + struct stmt st = {Seuswitch}; + struct tok tok; + vec_t(struct euswitchcase) cs = {0}; + struct blockstmt *f = NULL; + + lexexpect(P, '{'); + while (!lexmatch(P, &tok, '}')) { + struct euswitchcase c = {0}; + + lexexpect(P, TKkw_case); + + if (lexmatch(P, &tok, TKkw_else)) { + if (f) + fatal(P, tok.span, "duplicate 'case else' block"); + f = malloc(sizeof *f); + *f = parseblock0(P); + } else { + const char *vname = (tok = lexexpect(P, TKident)).str; + c.fld = findaggfield(test->ty, vname); + c.vval = c.fld - test->ty->agg.flds.d; + if (!c.fld) + fatal(P, tok.span, "%t has no such variant %T", test->ty, tok); + if (c.fld->ty && lexmatch(P, &tok, TKident)) { + struct env env = {P->curenv}; + c.capt = tok.str; + pushenv(P, &env); + putdecl(P, tok.span, &(struct decl) { + Dlet, c.capt, .span = tok.span, .var = { + c.fld->ty, NULL, c.captid = P->varid++, P->curfn->id + } + }); + } + c.t = parseblock0(P); + if (c.capt) + popenv(P); + vec_push(&cs, c); + } + } + + st.iswitch.test = *test; + vec_slice_cpy(&st.euswitch.cs, &cs); + st.iswitch.f = f; + return st; +} + +static struct stmt pstswitch(struct parser *P) { struct tok tok; struct expr test; @@ -1950,8 +2002,10 @@ pstswitch(struct parser *P) { test = parseexpr(P); if (test.ty->t == TYint || test.ty->t == TYenum) return pstiswitch(P, &test); + else if (test.ty->t == TYeunion) + return psteuswitch(P, &test); else - assert(0 && "NYI"); + fatal(P, test.span, "bad switch test expression (%t)", test.ty); } else { assert(0 && "NYI"); } diff --git a/bootstrap/test2.cff b/bootstrap/test2.cff index e5e484a..4690f72 100644 --- a/bootstrap/test2.cff +++ b/bootstrap/test2.cff @@ -31,8 +31,13 @@ extern fn main() void { let x int #?; x = X + 1 + Y; - let v Value = :None; + let v= Value:None; v = :Int 3; + switch (v) { + case None; + case Int i; + i; + } printf("n %d\n", n.value); printf("n link %d\n", n.link.value); |