aboutsummaryrefslogtreecommitdiff
path: root/bootstrap/parse.c
diff options
context:
space:
mode:
author lemon<lsof@mailbox.org>2022-08-08 05:33:56 +0200
committer lemon<lsof@mailbox.org>2022-08-08 05:33:56 +0200
commitcbed5a14aa8c1624f6f350d92c1de0c9ef55e40c (patch)
treefc0c7a6877baa50bdac78e898866a0f241ed1cb6 /bootstrap/parse.c
parent96ef1857bac446f4e065e7c10530213e361d726a (diff)
basic pattern matching
Diffstat (limited to 'bootstrap/parse.c')
-rw-r--r--bootstrap/parse.c66
1 files changed, 60 insertions, 6 deletions
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");
}