From bdb0276b534b817afb0b79f8e63196eed5d8bd7f Mon Sep 17 00:00:00 2001 From: lemon Date: Mon, 26 Jun 2023 00:06:26 +0200 Subject: frontend: add labels and goto statement --- c.c | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- common.h | 1 + 2 files changed, 120 insertions(+), 8 deletions(-) diff --git a/c.c b/c.c index 27abfe9..61b7890 100644 --- a/c.c +++ b/c.c @@ -10,6 +10,17 @@ struct comp { struct span fnblkspan; uint loopdepth, switchdepth; struct block *loopbreak, *loopcont; + pmap_of(struct label) labels; +}; + +struct label { + struct span usespan; + struct block *blk; + /* if usespan.ex.len == 0, this label is resolved and blk is the block that + * the label starts, otherwise the label is unresolved and blk is the head + * of a linked list of relocations, the next list entry is in blk->s1, etc, + * terminated by NULL */ + }; /** Parsing helper functions **/ @@ -674,8 +685,11 @@ tkprec(int tt) return ((uint)tt < arraylength(bintab)) ? bintab[tt].prec : 0; } +/* parse an expression with the given operator precedence */ +/* param ident is a kludge to support block labels without backtracking or extra lookahead + * see stmt() */ static struct expr -exprparse(struct comp *cm, int prec) +exprparse(struct comp *cm, int prec, const struct token *ident) { struct token tk, tk2; struct span span; @@ -696,6 +710,13 @@ exprparse(struct comp *cm, int prec) } unops[4]; int nunop = 0; + if (ident) { + assert(ident->t == TKIDENT); + tk = *ident; + ident = NULL; + goto Ident; + } + Unary: switch (lex(cm, &tk)) { /* unary operators (gather) */ @@ -706,7 +727,7 @@ Unary: unops[nunop].t0 = 0; unops[nunop].tt = tk.t; if (++nunop >= arraylength(unops)) { - ex = exprparse(cm, 999); + ex = exprparse(cm, 999, NULL); break; } goto Unary; @@ -724,6 +745,7 @@ Unary: mkarrtype(mktype(TYCHAR), 0, tk.len+1), .s = { (uchar *)tk.s, tk.len }); break; case TKIDENT: + Ident: decl = finddecl(cm, tk.s); if (!decl) { if (peek(cm, NULL) == '(') { /* implicit function decl? */ @@ -756,7 +778,7 @@ Unary: unops[nunop].span = tk.span; unops[nunop].ty = decl.ty; if (++nunop >= arraylength(unops)) { - ex = exprparse(cm, 999); + ex = exprparse(cm, 999, NULL); break; } goto Unary; @@ -940,7 +962,7 @@ Postfix: /* ex OP rhs */ span.sl = tk.span.sl; span.ex = ex.span.ex; - rhs = exprparse(cm, opprec + leftassoc); + rhs = exprparse(cm, opprec + leftassoc, NULL); if (!joinspan(&span.ex, tk.span.ex) || !joinspan(&span.ex, rhs.span.ex)) span.ex = tk.span.ex; ty = bintypecheck(&span, tk.t, &ex, &rhs); @@ -977,13 +999,13 @@ Postfix: static struct expr expr(struct comp *cm) { - return exprparse(cm, 2); /* non-comma expr */ + return exprparse(cm, 2, NULL); /* non-comma expr */ } static struct expr commaexpr(struct comp *cm) { - return exprparse(cm, 1); + return exprparse(cm, 1, NULL); } /*****************/ @@ -1616,6 +1638,39 @@ static void block(struct comp *cm, struct function *fn); static bool stmt(struct comp *cm, struct function *fn); static void localdecl(struct comp *cm, struct function *fn, bool forinit); +static void +deflabel(struct comp *cm, struct function *fn, const struct span *span, const char *name) +{ + struct label *label = pmap_get(&cm->labels, name); + if (label && label->usespan.ex.len == 0) { + error(span, "redefinition of label '%s'", name); + } else if (label) { + struct block *new; + if (!nerror) { + new = newblk(fn); + if (fn->curblk) putbranch(fn, new); + } + /* fix up relocations */ + for (struct block *list = label->blk, *next; list; list = next) { + next = list->s1; + if (!nerror) { + useblk(fn, list); + putbranch(fn, new); + } + } + label->usespan = (struct span){0}; + label->blk = fn->curblk; + if (!nerror) useblk(fn, new); + } else { + if (!nerror) { + struct block *new = newblk(fn); + if (fn->curblk) putbranch(fn, new); + useblk(fn, new); + } + pmap_set(&cm->labels, name, ((struct label) { .blk = fn->curblk })); + } +} + static bool loopbody(struct comp *cm, struct function *fn, struct block *brk, struct block *cont) { @@ -1643,10 +1698,23 @@ stmt(struct comp *cm, struct function *fn) union ref r; struct token tk; bool terminates = 0; - const bool doemit = fn->curblk; + bool doemit = fn->curblk; #define EMITS if (doemit && !nerror) + while (match(cm, &tk, TKIDENT)) { + if (match(cm, NULL, ':')){ + /*