aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bootstrap/all.h15
-rw-r--r--bootstrap/cgen.c27
-rw-r--r--bootstrap/dump.c2
-rw-r--r--bootstrap/parse.c66
-rw-r--r--bootstrap/test2.cff7
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);