From cbed5a14aa8c1624f6f350d92c1de0c9ef55e40c Mon Sep 17 00:00:00 2001 From: lemon Date: Mon, 8 Aug 2022 05:33:56 +0200 Subject: basic pattern matching --- bootstrap/parse.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 6 deletions(-) (limited to 'bootstrap/parse.c') 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, ',')) { @@ -1941,6 +1946,53 @@ pstiswitch(struct parser *P, const struct expr *test) { return st; } +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; @@ -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"); } -- cgit v1.2.3