aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bootstrap/all.h11
-rw-r--r--bootstrap/cgen.c20
-rw-r--r--bootstrap/dump.c7
-rw-r--r--bootstrap/parse.c71
-rw-r--r--bootstrap/test2.cff10
5 files changed, 104 insertions, 15 deletions
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<int>:Some 3;
let x = Option<f32>:None;