From 215c18ebc86b116827d3af19ca0779316e1a0e15 Mon Sep 17 00:00:00 2001 From: lemon Date: Mon, 8 Aug 2022 08:48:21 +0200 Subject: break & continue --- bootstrap/all.h | 11 +++++++++ bootstrap/cgen.c | 20 +++++++++++---- bootstrap/dump.c | 7 +++--- bootstrap/parse.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++----- bootstrap/test2.cff | 10 ++++++++ 5 files changed, 104 insertions(+), 15 deletions(-) (limited to 'bootstrap') diff --git a/bootstrap/all.h b/bootstrap/all.h index 3145674..1629455 100644 --- a/bootstrap/all.h +++ b/bootstrap/all.h @@ -35,6 +35,7 @@ struct span { _(break) \ _(case) \ _(const) \ + _(continue) \ _(def) \ _(defmacro) \ _(do) \ @@ -74,6 +75,7 @@ enum toktype { TKgensym, TKtype, TKexpr, + TKlabel, TKeof, }; @@ -137,6 +139,7 @@ struct parser { bool save_envage; int envage; int varid; + int loopid, curloop; }; enum typetype { @@ -237,6 +240,7 @@ enum decltype { Ddef, Dmacro, Dtepl, + Dlabel, }; struct decl { @@ -268,6 +272,7 @@ struct decl { struct toktree toks; int envage; } tepl; + int loopid; }; int age; }; @@ -394,6 +399,8 @@ enum stmttype { Siswitch, Sreturn, Seuswitch, + Sbreak, + Scontinue, }; struct iswitchcase { @@ -424,6 +431,7 @@ struct stmt { } ifelse; struct { slice_t(struct stmt) ini; + int id; struct expr test; struct expr *next; struct blockstmt body; @@ -440,6 +448,9 @@ struct stmt { bool byptr; } euswitch; struct expr *retex; + struct { + int id; + } brkcon; }; }; diff --git a/bootstrap/cgen.c b/bootstrap/cgen.c index db477fd..433a5a0 100644 --- a/bootstrap/cgen.c +++ b/bootstrap/cgen.c @@ -324,7 +324,7 @@ genstmt(struct stmt *stmt) { if (decl.externp) genstatic(1, decl.name, &decl.var); break; - case Ddef: case Dtype: case Dmacro: case Dtepl: + case Ddef: case Dtype: case Dmacro: case Dtepl: case Dlabel: break; } break; @@ -337,8 +337,9 @@ genstmt(struct stmt *stmt) { } break; case Swhile: - pri("while (%e) ", &stmt->loop.test); + pri("while (%e) {", &stmt->loop.test); genblock(stmt->loop.body); + pri("_cont%d:;} _brk%d:;\n", stmt->loop.id, stmt->loop.id); break; case Sfor: pri("{\n"); @@ -348,9 +349,10 @@ genstmt(struct stmt *stmt) { pri("%e;", &stmt->loop.test); if (stmt->loop.next) pri(" %e", stmt->loop.next); - pri(")"); + pri(") {"); genblock(stmt->loop.body); - pri("}\n"); + pri("_cont%d:; }", stmt->loop.id); + pri("} _brk%d:;\n", stmt->loop.id); break; case Siswitch: pri("switch (%e) {", &stmt->iswitch.test); @@ -409,6 +411,12 @@ genstmt(struct stmt *stmt) { pri(" %e", stmt->retex); pri(";\n"); break; + case Sbreak: + pri("goto _brk%d;\n", stmt->brkcon.id); + break; + case Scontinue: + pri("goto _cont%d;\n", stmt->brkcon.id); + break; } } @@ -563,6 +571,8 @@ liftnested(struct stmt *stmt) { if (stmt->euswitch.f) liftnested(blocktostmt(*stmt->euswitch.f)); break; + case Sbreak: case Scontinue: + break; } } @@ -618,7 +628,7 @@ gendecl(struct decl *decl, bool toplevel) { case Dlet: assert(!toplevel); break; - case Ddef: case Dtype: case Dtepl: case Dmacro: + case Ddef: case Dtype: case Dtepl: case Dmacro: case Dlabel: break; } } diff --git a/bootstrap/dump.c b/bootstrap/dump.c index 8c34716..157b12f 100644 --- a/bootstrap/dump.c +++ b/bootstrap/dump.c @@ -123,7 +123,7 @@ tok2str(struct tok tok) { if (tok.t == TKintlit) { snprintf(buf, sizeof buf - 1, "`%llu'", (unsigned long long)tok.ilit.i); - } else if (tok.t == TKident || tok.t == TKmacident) { + } else if (tok.t == TKident || tok.t == TKmacident || tok.t == TKlabel) { snprintf(buf, sizeof buf - 1, "`%s'", tok.str); } else if (tok.t == TKgensym) { snprintf(buf, sizeof buf - 1, "`$%s'", tok.str); @@ -385,8 +385,7 @@ dumpstmt(const struct stmt *stmt, int ws) { else epri("%wreturn;\n", ws); break; - case Seuswitch: - break; + default:assert(0); } } @@ -439,7 +438,7 @@ dumpdecl(const struct decl *decl, int ws) { } epri("}\n"); break; - case Dtepl: + case Dtepl: case Dlabel: assert(0); } } diff --git a/bootstrap/parse.c b/bootstrap/parse.c index 02980cc..631d007 100644 --- a/bootstrap/parse.c +++ b/bootstrap/parse.c @@ -373,6 +373,9 @@ lex(struct parser *P) { tok.t = '#len'; } else if (!strcmp(s, "#?")) { tok.t = '#?'; + } else if (!strncmp(s, "#:", 2)) { + tok.t = TKlabel; + tok.str = xstrdup(s); } else { fatal(P, P->tokspan, "invalid #keyword"); } @@ -1860,7 +1863,7 @@ pstforletyield(struct stmt *st, void *arg) { } static struct stmt -pstfor(struct parser *P) { +pstfor(struct parser *P, const char *label) { struct stmt st = {Sfor}; struct tok tok; @@ -1895,7 +1898,16 @@ pstfor(struct parser *P) { st.loop.next = exprdup(parseexpr(P)); lexexpect(P, '{'); } - st.loop.body = parseblock(P).block; + st.loop.id = P->loopid++; + struct env env = {P->curenv}; + if (label) { + pushenv(P, &env); + putdecl(P, tok.span, &(struct decl) { Dlabel, label, .loopid = st.loop.id }); + } + WITH_TMPCHANGE(int, P->curloop, st.loop.id) + st.loop.body = parseblock(P).block; + if (label) + popenv(P); return st; } @@ -2158,6 +2170,7 @@ static void parsestmt(stmt_yielder_t yield, void *yarg, struct parser *P) { struct stmt st = {0}; struct tok tok; + const char *label = NULL; if (lexmatch(P, &tok, '{')) { st.span = tok.span; @@ -2168,7 +2181,16 @@ parsestmt(stmt_yielder_t yield, void *yarg, struct parser *P) { } else if (lexmatch(P, &tok, TKkw_if)) { st.span = tok.span; st = pstifelse(P); + } else if (lexmatch(P, &tok, TKlabel)) { + label = tok.str; + if (lexmatch(P, &tok, TKkw_while)) + goto whilel; + else if (lexmatch(P, &tok, TKkw_for)) + goto forl; + else + fatal(P, tok.span, "cannot use label for this statement"); } else if (lexmatch(P, &tok, TKkw_while)) { + whilel: st.t = Swhile; st.span = tok.span; st.loop.test = parseexpr(P); @@ -2177,10 +2199,20 @@ parsestmt(stmt_yielder_t yield, void *yarg, struct parser *P) { "while statement condition must be bool or pointer (%t)", st.loop.test.ty); lexexpect(P, '{'); - st.loop.body = parseblock(P).block; + st.loop.id = P->loopid++; + struct env env = { P->curenv }; + if (label) { + pushenv(P, &env); + putdecl(P, tok.span, &(struct decl) { Dlabel, label, .loopid = st.loop.id }); + } + WITH_TMPCHANGE(int, P->curloop, st.loop.id) + st.loop.body = parseblock(P).block; + if (label) + popenv(P); } else if (lexmatch(P, &tok, TKkw_for)) { + forl: st.span = tok.span; - st = pstfor(P); + st = pstfor(P, label); } else if (lexmatch(P, &tok, TKkw_switch)) { st = pstswitch(P); st.span = tok.span; @@ -2201,6 +2233,32 @@ parsestmt(stmt_yielder_t yield, void *yarg, struct parser *P) { fatal(P, tok.span, "return statement in non-void function must return a value"); } + } else if (lexmatch(P, &tok, TKkw_break)) { + if (P->curloop < 0) + fatal(P, tok.span, "break outside loop"); + st.t = Sbreak; + st.brkcon.id = P->curloop; + if (lexmatch(P, &tok, TKlabel)) { + const struct decl *decl = finddecl(P, tok.str); + if (!decl) + fatal(P, tok.span, "no such label %T", tok); + assert(decl->t == Dlabel); + st.brkcon.id = decl->loopid; + } + lexexpect(P, ';'); + } else if (lexmatch(P, &tok, TKkw_continue)) { + if (P->curloop < 0) + fatal(P, tok.span, "continue outside loop"); + st.t = Scontinue; + st.brkcon.id = P->curloop; + if (lexmatch(P, &tok, TKlabel)) { + const struct decl *decl = finddecl(P, tok.str); + if (!decl) + fatal(P, tok.span, "no such label %T", tok); + assert(decl->t == Dlabel); + st.brkcon.id = decl->loopid; + } + lexexpect(P, ';'); } else if (isdecltokt((tok = lexpeek(P)).t)) { st.t = Sdecl; st.span = tok.span; @@ -2327,8 +2385,9 @@ parsefn(struct decl *decl, struct parser *P) { putdecl(P, tok.span, &decl); } fn->body = xcalloc(1, sizeof *fn->body); - WITH_TMPCHANGE(int, P->varid, 0) - *fn->body = parseblock(P); + WITH_TMPCHANGE(int, P->curloop, -1) WITH_TMPCHANGE(int, P->loopid, 0) + WITH_TMPCHANGE(int, P->varid, 0) + *fn->body = parseblock(P); popenv(P); } } diff --git a/bootstrap/test2.cff b/bootstrap/test2.cff index b94a74e..fdb30cb 100644 --- a/bootstrap/test2.cff +++ b/bootstrap/test2.cff @@ -46,6 +46,16 @@ extern fn main() void { *f += 1.0f; } + #:outer for let i = 0; i++ < 10; { + printf("%d\n", i); + while #t { + if i < 2 { + continue #:outer; + } + break #:outer; + } + } + let x = Option:Some 3; let x = Option:None; -- cgit v1.2.3