diff options
| author | 2022-08-04 07:39:23 +0200 | |
|---|---|---|
| committer | 2022-08-04 07:39:23 +0200 | |
| commit | bb1d4b4a3e51a06fb0530dfc271a97a6cd88cc73 (patch) | |
| tree | f300325814bc30e64f858ee313b8260a14d8df90 | |
| parent | 1625c50f0c0e4b1c7ba01a5df5713efaf6dce606 (diff) | |
stuff
| -rw-r--r-- | bootstrap/all.h | 58 | ||||
| -rw-r--r-- | bootstrap/cgen.c | 139 | ||||
| -rw-r--r-- | bootstrap/dump.c | 5 | ||||
| -rw-r--r-- | bootstrap/env.c | 6 | ||||
| -rw-r--r-- | bootstrap/parse.c | 207 | ||||
| -rw-r--r-- | bootstrap/test.cff | 59 | ||||
| -rw-r--r-- | bootstrap/util.c | 4 |
7 files changed, 351 insertions, 127 deletions
diff --git a/bootstrap/all.h b/bootstrap/all.h index 7f26372..6b5fd7d 100644 --- a/bootstrap/all.h +++ b/bootstrap/all.h @@ -29,25 +29,26 @@ struct span { /* must be alpha sorted */ #define LIST_KEYWORDS(_) \ - _(and) \ - _(break) \ - _(case) \ - _(const) \ - _(defmacro) \ - _(do) \ - _(else) \ - _(extern) \ - _(fn) \ - _(for) \ - _(if) \ - _(let) \ - _(not) \ - _(or) \ - _(return) \ - _(switch) \ - _(typedef) \ - _(typeof) \ - _(while) \ + _(and) \ + _(break) \ + _(case) \ + _(const) \ + _(defmacro) \ + _(do) \ + _(else) \ + _(extern) \ + _(fn) \ + _(for) \ + _(if) \ + _(let) \ + _(not) \ + _(or) \ + _(return) \ + _(static) \ + _(switch) \ + _(typedef) \ + _(typeof) \ + _(while) enum toktype { #define KWTK(kw) TKkw_##kw, @@ -60,6 +61,7 @@ enum toktype { TKchrlit, TKnullit, TKident, + TKmacident, TKgensym, TKeof, NUM_LEXTOKENS @@ -179,7 +181,8 @@ struct macro { enum decltype { Dtype, Dfn, - Dvar, + Dlet, + Dstatic, Dmacro, }; @@ -192,7 +195,7 @@ struct decl { union { const struct type *ty; struct fn fn; - struct { + struct var { const struct type *ty; struct expr *ini; int fnid; @@ -221,6 +224,12 @@ enum exprtype { Econd, Ecall, Eindex, + Eblock, +}; + +struct blockstmt { + struct env env; + slice_t(struct stmt) stmts; }; struct expr { @@ -253,6 +262,7 @@ struct expr { struct { struct expr *lhs, *rhs; } index; + struct blockstmt block; }; }; @@ -267,11 +277,6 @@ enum stmttype { Sreturn, }; -struct blockstmt { - struct env env; - slice_t(struct stmt) stmts; -}; - struct iswitchcase { slice_t(struct expr) es; struct blockstmt t; @@ -279,6 +284,7 @@ struct iswitchcase { struct stmt { enum stmttype t; + struct span span; union { struct blockstmt block; struct expr expr; diff --git a/bootstrap/cgen.c b/bootstrap/cgen.c index 6445bf8..938e8f3 100644 --- a/bootstrap/cgen.c +++ b/bootstrap/cgen.c @@ -73,7 +73,9 @@ pri(const char *fmt, ...) { break; case 'c': ch = bswap32(va_arg(ap, int)); - while (ch) { + if (ch == 0) + fputc(ch, outfp); + else while (ch) { if (ch & 0xFF) fputc(ch, outfp); ch >>= 8; @@ -143,16 +145,16 @@ genexpr(struct expr *ex) { pri("NULL"); break; case Ename: - if (ex->ref->t == Dfn && ex->ref->_cname && *ex->ref->_cname) + if ((ex->ref->t == Dfn || ex->ref->t == Dstatic) && *ex->ref->_cname) pri("%s", *ex->ref->_cname); else pri("%s", ex->ref->name); break; case Eprefix: - pri("%c(%e)", ex->unop.op, ex->unop.child); + pri("(%c(%e))", ex->unop.op, ex->unop.child); break; case Epostfix: - pri("(%e)%c", ex->unop.op, ex->unop.child); + pri("(%e)%c", ex->unop.child, ex->unop.op); break; case Ebinop: pri("(%e %c %e)", ex->binop.lhs, ex->binop.op, ex->binop.rhs); @@ -172,14 +174,24 @@ genexpr(struct expr *ex) { case Eindex: pri("%e[%e]", ex->index.lhs, ex->index.rhs); break; - + case Eblock: + pri("(\n"); + genblock(ex->block); + pri(")"); + break; + default: + assert(0); } } -static void genfn(const char *cname, struct fn *fn); +static void genfn(bool externp, const char *cname, struct fn *fn); +static void genstatic(bool externp, const char *cname, struct var *var); static void genstmt(struct stmt *stmt) { + const char *p = fileid2path(stmt->span.fileid); + if (stmt->t != Sblock) + pri("#line %d %S\n", stmt->span.line, p, (u64)strlen(p)); switch (stmt->t) { case Sblock: genblock(stmt->block); @@ -191,7 +203,7 @@ genstmt(struct stmt *stmt) { case Sdecl: ; struct decl decl = stmt->decl; switch (decl.t) { - case Dvar: + case Dlet: pri("%t %s", decl.var.ty, decl.name); if (decl.var.ini) pri(" = %e", decl.var.ini); @@ -199,7 +211,11 @@ genstmt(struct stmt *stmt) { break; case Dfn: if (decl.externp) - genfn(decl.fn.name, &decl.fn); + genfn(1, decl.fn.name, &decl.fn); + break; + case Dstatic: + if (decl.externp) + genstatic(1, decl.name, &decl.var); break; case Dtype: case Dmacro: break; @@ -218,15 +234,29 @@ genstmt(struct stmt *stmt) { genblock(stmt->loop.body); break; case Sfor: - pri("for ("); + pri("for (\n"); if (stmt->loop.ini) genstmt(stmt->loop.ini); pri("%e;", &stmt->loop.test); if (stmt->loop.next) - pri(" %e", &stmt->loop.next); + pri(" %e", stmt->loop.next); pri(")"); genblock(stmt->loop.body); + break; case Siswitch: + pri("switch (%e) {", &stmt->iswitch.test); + for (int i = 0; i < stmt->iswitch.cs.n; ++i) { + struct iswitchcase c = stmt->iswitch.cs.d[i]; + for (int i = 0; i < c.es.n; ++i) + pri("case %e: ", &c.es.d[i]); + genblock(c.t); + pri("break;\n"); + } + if (stmt->iswitch.f) { + pri("default: "); + genblock(*stmt->iswitch.f); + } + pri("}\n"); break; case Sreturn: pri("return"); @@ -245,16 +275,51 @@ genblock(struct blockstmt block) { pri("}\n"); } -static void -liftnestedex(struct expr *expr) { - // TODO implement when statement expressions exist - //switch (expr->t) { - //} -} - #define blocktostmt(Block) \ &(struct stmt) {Sblock, .block = Block} + +static void liftnested(struct stmt *stmt); +// lift nested fns and static vars + +static void +liftnestedex(struct expr *ex) { + switch (ex->t) { + case Eintlit: case Eflolit: case Estrlit: + case Eboolit: case Enullit: case Ename: + break; + case Eprefix: + liftnestedex(ex->unop.child); + break; + case Epostfix: + liftnestedex(ex->unop.child); + break; + case Ebinop: + liftnestedex(ex->binop.lhs); + liftnestedex(ex->binop.rhs); + break; + case Econd: + liftnestedex(ex->cond.test); + liftnestedex(ex->cond.t); + liftnestedex(ex->cond.f); + break; + case Ecall: + liftnestedex(ex->call.callee); + for (int i = 0; i < ex->call.args.n; ++i) + liftnestedex(&ex->call.args.d[i]); + break; + case Eindex: + liftnestedex(ex->index.lhs); + liftnestedex(ex->index.rhs); + break; + case Eblock: + liftnested(blocktostmt(ex->block)); + break; + default: + assert(0); + } +} + static void // lift nested fns and static vars liftnested(struct stmt *stmt) { static int id; @@ -273,10 +338,17 @@ liftnested(struct stmt *stmt) { case Dfn: if (stmt->decl.fn.body) { *stmt->decl._cname = xasprintf("__f%s_%d", stmt->decl.fn.name, id++); - genfn(*stmt->decl._cname, &stmt->decl.fn); + genfn(stmt->decl.externp, *stmt->decl._cname, &stmt->decl.fn); } break; - case Dvar: + case Dstatic: + if (!stmt->decl.externp) { + assert(stmt->decl.var.ini); + *stmt->decl._cname = xasprintf("__s%s_%d", stmt->decl.name, id++); + genstatic(stmt->decl.externp, *stmt->decl._cname, &stmt->decl.var); + } + break; + case Dlet: liftnestedex(stmt->decl.var.ini); break; default: @@ -312,10 +384,10 @@ liftnested(struct stmt *stmt) { } static void -genfn(const char *cname, struct fn *fn) { +genfn(bool externp, const char *cname, struct fn *fn) { liftnested(fn->body); - if (fn->body) - pri("\n"); + if (!externp) + pri("static "); pri("%t %s(", fn->retty, cname); for (int i = 0; i < fn->params.n; ++i) { pri("%t %s", fn->params.d[i].ty, fn->params.d[i].name); @@ -330,22 +402,37 @@ genfn(const char *cname, struct fn *fn) { } else { genblock(fn->body->block); } +} +static void +genstatic(bool externp, const char *cname, struct var *var) { + if (externp && !var->ini) + pri("extern "); + else if (!externp) + pri("static "); + pri("%t %s",var->ty, cname); + if (var->ini) + pri(" = %e", var->ini); + pri(";\n"); } static void gendecl(struct decl *decl, bool toplevel) { + const char *p = fileid2path(decl->span.fileid); switch (decl->t) { case Dfn: - if (!decl->externp) - pri("static "); + pri("#line %d %S\n", decl->span.line, p, (u64)strlen(p)); if (decl->fn.body) assert(toplevel); if (!*decl->_cname) *decl->_cname = (char *)decl->fn.name; - genfn(*decl->_cname, &decl->fn); + genfn(decl->externp, *decl->_cname, &decl->fn); + break; + case Dstatic: + pri("#line %d %S\n", decl->span.line, p, (u64)strlen(p)); + genstatic(decl->externp, *decl->_cname ? *decl->_cname : decl->name, &decl->var); break; - case Dvar: + case Dlet: assert(!toplevel); break; case Dtype: diff --git a/bootstrap/dump.c b/bootstrap/dump.c index e4f35c9..4b337b7 100644 --- a/bootstrap/dump.c +++ b/bootstrap/dump.c @@ -98,7 +98,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) { + } else if (tok.t == TKident || tok.t == TKmacident) { snprintf(buf, sizeof buf - 1, "`%s'", tok.str); } else if (tok.t == TKgensym) { snprintf(buf, sizeof buf - 1, "`$%s'", tok.str); @@ -336,7 +336,8 @@ dumpdecl(const struct decl *decl, int ws) { case Dtype: pri("<type> %t\n", decl->ty); break; - case Dvar: + case Dlet: + case Dstatic: pri("<var> %t", decl->var.ty); if (decl->var.ini) pri(" = %e", decl->var.ini); diff --git a/bootstrap/env.c b/bootstrap/env.c index 513b4d9..0c0021b 100644 --- a/bootstrap/env.c +++ b/bootstrap/env.c @@ -14,14 +14,16 @@ envfind(const struct env *env, const char *name) { static bool declsshadowable(const struct decl *a, const struct decl *b) { - return b->t == Dvar; + return b->t == Dlet; } static bool declscompatible(const struct decl *a, const struct decl *b) { if (a->t != b->t) return 0; - if (a->t == Dfn && a->fn.selfty == b->fn.selfty && !a->fn.body) + if (a->t == Dfn && a->externp == b->externp && a->fn.selfty == b->fn.selfty && !a->fn.body) + return 1; + if (a->t == Dstatic && a->externp == b->externp && a->var.ty == b->var.ty && !a->var.ini) return 1; if (a->t == Dtype && a->ty == b->ty) return 1; diff --git a/bootstrap/parse.c b/bootstrap/parse.c index 96a185c..3d609c4 100644 --- a/bootstrap/parse.c +++ b/bootstrap/parse.c @@ -227,6 +227,7 @@ readstrlit(struct parser *P, u8 delim, char *s, size_t n) { #define MAX_MACROEXPAND_ITER 100 +/* // guard used at macro arg expansion to avoid it expanding into itself, e.g.: // defmacro foo(x,y) [(x+y)] // let x, y ...; @@ -240,6 +241,7 @@ ismacrodupe(struct parser *P, struct toktree toks) { } return 0; } +*/ static struct tok lex(struct parser *P) { @@ -269,18 +271,15 @@ lex(struct parser *P) { } tok = ep->toks.d[ep->idx++]; // expand macro arg? - if (tok.t == TKident) { + if (tok.t == TKmacident) { for (struct expan *ep = P->curexpan; ep; ep = ep->prev) { for (int i = 0; i < ep->args.n; ++i) { if (!strcmp(tok.str, ep->args.d[i].name)) { - if (!ismacrodupe(P, ep->args.d[i].toks)) { - ++P->expanno; - P->curexpan = memcpy(xmalloc(sizeof *ep), &(struct expan) { - P->curexpan, {0}, ep->args.d[i].toks, - }, sizeof *ep); - return lex(P); - } else - return tok; + ++P->expanno; + P->curexpan = memcpy(xmalloc(sizeof *ep), &(struct expan) { + P->curexpan, {0}, ep->args.d[i].toks, + }, sizeof *ep); + return lex(P); } } } @@ -314,6 +313,8 @@ lex(struct parser *P) { tok.boolit = s[1] == 't'; } else if (!strcmp(s, "#null")) { tok.t = TKnullit; + } else if (!strcmp(s, "##")) { + tok.t = '##'; } else if (c == '#') { fatal(P, P->tokspan, "identifier cannot start with '#'"); } else if (c == '$') { @@ -333,7 +334,7 @@ lex(struct parser *P) { n = readstrlit(P, delim, s, sizeof s); if (delim == '"') { tok.t = TKstrlit; - tok.str = strcpy(xmalloc(n + 1), s); + tok.str = memcpy(xmalloc(n + 1), s, n + 1); tok.strlen = n; } else { if (n == 0) @@ -651,6 +652,12 @@ typeof2(const struct type *a, const struct type *b) { } if (a == b) return a; + if (unconstify(a) == b) + return a; + if (a == unconstify(b)) + return b; + if (a->t == TYarr && b->t == TYarr) + a = arraydecay(a); if (a->t == TYptr && b->t == TYarr) b = arraydecay(b); if (a->t == TYarr && b->t == TYptr) @@ -707,6 +714,13 @@ exprdup(struct expr ex) { static struct blockstmt parseblock0(struct parser *P); static struct stmt parseblock(struct parser *P); static void parseexpandmacro(struct parser *P, const struct macro *macro); +static const struct type * +mkarraytype(const struct type *child, size_t n) { + return interntype((struct type) { + TYarr, n * child->size, 1, 0, + .child = child, .length = n + }); +} static struct expr pexprimary(struct parser *P) { @@ -728,10 +742,13 @@ pexprimary(struct parser *P) { ex.span = tok.span; ex.strlit.d = tok.str; ex.strlit.n = tok.strlen; - ex.ty = interntype((struct type) { - TYarr, tok.strlen + 1, 1, 0, - .child = constify(ty_u8), .length = tok.strlen + 1 - }); + while (lexmatch(P, &tok, TKstrlit)) { + ex.strlit.d = xrealloc((char *)ex.strlit.d, ex.strlit.n + tok.strlen + 1); + memcpy((char *)ex.strlit.d + ex.strlit.n, tok.str, tok.strlen + 1); + free((char *)tok.str); + ex.strlit.n += tok.strlen; + } + ex.ty = mkarraytype(constify(ty_u8), ex.strlit.n + 1); } else if (lexmatch(P, &tok, TKboolit)) { ex.t = Eboolit; ex.span = tok.span; @@ -759,18 +776,35 @@ pexprimary(struct parser *P) { ex.t = Ename; ex.span = tok.span; ex.ref = decl; - if (decl->t == Dvar) { + if (decl->t == Dlet) { if (decl->var.fnid != P->curfn->id) fatal(P, tok.span, "cannot access local variable `%s' belonging to outer function", tok.str); ex.ty = decl->var.ty; + } else if (decl->t == Dstatic) { + ex.ty = decl->var.ty; } else if (decl->t == Dfn) { ex.ty = decl->fn.selfty; } else assert(0); } } else if (lexmatch(P, &tok, '(')) { - ex = parseexpr(P); + if (lexpeek(P).t == TKkw_do) { + struct blockstmt block; + lex(P); + block = parseblock0(P); + ex.t = Eblock; + ex.block = block; + ex.ty = ty_void; + if (ex.block.stmts.n > 0) { + struct stmt *last = &block.stmts.d[block.stmts.n - 1]; + if (last->t == Sexpr) + ex.ty = last->expr.ty; + + } + } else { + ex = parseexpr(P); + } lexexpect(P, ')'); } else { fatal(P, tok.span, "expected expression (near %s)", tok2str(tok)); @@ -894,7 +928,7 @@ pexprefix(struct parser *P) { struct expr ex = pexprefix(P); if (ex.ty->t != TYptr) fatal(P, ex.span, "invalid operand type to dereference, not pointer"); - if (!completetype(ex.ty->child)) + if (!completetype(ex.ty->child) && ex.ty->child->t != TYfn) fatal(P, ex.span, "invalid operand type to dereference, incomplete"); return (struct expr) { Eprefix, tok.span, ex.ty->child, .unop = { @@ -904,7 +938,7 @@ pexprefix(struct parser *P) { } else if (lexmatch(P, &tok, '&')) { struct expr ex = pexprefix(P); struct type ty2 = { TYptr, g_targ.ptrsize, .child = ex.ty}; - if (!islvalue(&ex)) + if (!islvalue(&ex) && !(ex.t == Ename && ex.ref->t == Dfn)) fatal(P, ex.span, "invalid operand type to address-of, not an lvalue"); return (struct expr) { Eprefix, tok.span, interntype(ty2), .unop = { @@ -927,6 +961,8 @@ peeksbitarithop(struct parser *P, struct tok* tokp) { return 1; case '&': case '|': case '^': case '<<': case '>>': return 2; + case '##': + return 3; } return 0; } @@ -957,7 +993,7 @@ pexbitarith(struct parser *P) { } } else if (tokt == '-' && ex.ty->t == TYptr && rhs.ty->t == TYptr) { ty = ty_isize; - } else if (!isnumtype(ty)) { + } else if (tokt != '##' && !isnumtype(ty)) { err: fatal(P, tok.span, "invalid operands to binary operator %s", tokt2str(tokt)); @@ -965,11 +1001,28 @@ pexbitarith(struct parser *P) { if (oret == 2 && ty->t == TYfloat) fatal(P, tok.span, "invalid operands to bitwise operator: not integral", tokt2str(tokt)); - ex = (struct expr) { - Ebinop, tok.span, ty, .binop = { - tokt, exprdup(ex), exprdup(rhs) - } - }; + if (tokt != '##') { + ex = (struct expr) { + Ebinop, tok.span, ty, .binop = { + tokt, exprdup(ex), exprdup(rhs) + } + }; + } else { + char *s; + size_t n = ex.strlit.n + rhs.strlit.n; + + /// TODO fold + if (ex.t != Estrlit || rhs.t != Estrlit) + fatal(P, tok.span, + "operands to `##' operator are not string literals"); + s = xmalloc(n + 1); + memcpy(s, ex.strlit.d, ex.strlit.n); + memcpy(s + ex.strlit.n, rhs.strlit.d, rhs.strlit.n + 1); + ex = (struct expr) { + Estrlit, tok.span, mkarraytype(constify(ty_u8), n + 1), + .strlit.d = s, .strlit.n = n, + }; + } } @@ -1128,9 +1181,8 @@ stmtdup(struct stmt st) { return memcpy(xmalloc(sizeof st), &st, sizeof st); } -static struct stmt -pstlet(struct parser *P) { - struct stmt st = {0}; +static void +parsevardecl(struct parser *P, struct decl *decl) { struct tok tok; const char *name; const struct type *ty = NULL; @@ -1148,10 +1200,13 @@ pstlet(struct parser *P) { ty = ini->ty; } else { ty = parsetype(P); - lexexpect(P, '='); - ini = exprdup(parseexpr(P)); + if (lexmatch(P, NULL, '=')) { + ini = exprdup(parseexpr(P)); + } else if (decl->t == Dlet) { + fatal(P, tok.span, "variable must be initialized"); + } } - if (!typeof2(ty, ini->ty)) + if (ini && !typeof2(ty, ini->ty)) fatal(P, tok.span, "incompatible initializer type"); if (!completetype(ty)) @@ -1161,15 +1216,18 @@ pstlet(struct parser *P) { ty = constify(ty); assert(ty); - st.t = Sdecl; - st.decl = (struct decl) { - Dvar, name, .var = { - .ty = ty, - .ini = ini, - .fnid = P->curfn->id, - } - }; - putdecl(P, tok.span, &st.decl); + decl->name = name; + decl->var.ty = ty; + decl->var.ini = ini; + decl->var.fnid = P->curfn ? P->curfn->id : -1; +} + +static struct stmt +pstlet(struct parser *P) { + struct stmt st = {Sdecl}; + st.decl.t = Dlet; + parsevardecl(P, &st.decl); + putdecl(P, st.decl.span, &st.decl); return st; } @@ -1296,7 +1354,8 @@ static void parsedecl(struct decl *, struct parser *, bool toplevel); static bool isdecltokt(int tokt) { switch (tokt) - case TKkw_extern: case TKkw_fn: case TKkw_typedef: case TKkw_defmacro: + case TKkw_extern: case TKkw_fn: case TKkw_typedef: + case TKkw_defmacro: case TKkw_static: return 1; return 0; } @@ -1404,39 +1463,49 @@ parsestmt(struct parser *P) { struct stmt st = {0}; struct tok tok; - if (lexmatch(P, NULL, '{')) { - return parseblock(P); - } else if (lexmatch(P, NULL, TKkw_let)) { + if (lexmatch(P, &tok, '{')) { + st.span = tok.span; + st = parseblock(P); + } else if (lexmatch(P, &tok, TKkw_let)) { st = pstlet(P); + st.span = tok.span; lexexpect(P, ';'); - } else if (lexmatch(P, NULL, TKkw_if)) { + } else if (lexmatch(P, &tok, TKkw_if)) { + st.span = tok.span; st = pstifelse(P); - } else if (lexmatch(P, NULL, TKkw_while)) { + } else if (lexmatch(P, &tok, TKkw_while)) { st.t = Swhile; + st.span = tok.span; st.loop.test = parseexpr(P); if (st.loop.test.ty->t != TYbool && st.loop.test.ty->t != TYptr) fatal(P, st.loop.test.span, "while statement condition must be bool or pointer"); lexexpect(P, '{'); st.loop.body = parseblock(P).block; - } else if (lexmatch(P, NULL, TKkw_for)) { + } else if (lexmatch(P, &tok, TKkw_for)) { + st.span = tok.span; st = pstfor(P); - } else if (lexmatch(P, NULL, TKkw_switch)) { + } else if (lexmatch(P, &tok, TKkw_switch)) { st = pstswitch(P); + st.span = tok.span; } else if (lexmatch(P, &tok, TKkw_return)) { + if (!P->curfn) + fatal(P, tok.span, "return disallowed here"); st.t = Sreturn; - if (!lexmatch(P, NULL, ';')) { + st.span = tok.span; + if (!lexmatch(P, &tok, ';')) { st.retex = exprdup(parseexpr(P)); lexexpect(P, ';'); - if (P->curfn->retty == ty_void) + if (!typeof2(st.retex->ty, P->curfn->retty)) fatal(P, st.retex->span, - "return statement in void function must not return a value"); + "incompatible type in return statement"); } else if (P->curfn->retty != ty_void) { fatal(P, tok.span, "return statement in non-void function must return a value"); } } else if (isdecltokt((tok = lexpeek(P)).t)) { st.t = Sdecl; + st.span = tok.span; parsedecl(&st.decl, P, 0); if (st.decl.t == Dfn && !st.decl.externp && !st.decl.fn.body) fatal(P, tok.span, @@ -1447,6 +1516,7 @@ parsestmt(struct parser *P) { "cannot define external function inside another function"); } else { struct expr ex; + st.span = tok.span; if ((tok = lexpeek(P)).t == TKident) { const struct decl *decl; @@ -1550,7 +1620,7 @@ parsefn(struct decl *decl, struct parser *P) { }); for (int i = 0; i < params.length; ++i) { struct decl decl = { - Dvar, params.data[i].name, .var = { + Dlet, params.data[i].name, .var = { .ty = params.data[i].ty, .fnid = fn->id, } @@ -1613,7 +1683,11 @@ parsemacrocase(struct parser *P) { case TKeof: fatal(P, espan, "unterminated macro definition"); } - if (tok.t == TKgensym) { + if (tok.t == TKident) { + for (int i = 0; i < params.length; ++i) + if (!strcmp(params.data[i], tok.str)) + tok.t = TKmacident; + } else if (tok.t == TKgensym) { struct gensyms *gs; for (gs = gensyms; gs; gs = gs->next) { if (!strcmp(gs->name, tok.str)) { @@ -1625,8 +1699,7 @@ parsemacrocase(struct parser *P) { gs->name = tok.str; gs->tok.t = TKident; gs->tok.span = tok.span; - gs->tok.str = xmalloc(strlen(tok.str) + 24); - sprintf((char *)gs->tok.str, "__fG%s_%d", tok.str, gensymid++); + gs->tok.str = xasprintf("__fG%s_%d", tok.str, gensymid++); tok = gs->tok; gs->next = gensyms; gensyms = gs; @@ -1636,6 +1709,12 @@ parsemacrocase(struct parser *P) { } (void)vec_pop(&body); + for (struct gensyms *gs = gensyms, *next; gs; gs = next) { + next = gs->next; + free((char *)gs->name); + free(gs); + } + vec_slice_cpy(&c.params, ¶ms); vec_slice_cpy(&c.body, &body); return c; @@ -1673,9 +1752,8 @@ parsedecl(struct decl *decl, struct parser *P, bool toplevel) { bool externp = 0; memset(decl, 0, sizeof *decl); - if (lexmatch(P, &tok, TKkw_extern)) { + if (lexmatch(P, &tok, TKkw_extern)) externp = 1; - } if (lexmatch(P, &tok, TKkw_fn)) { const char *name = lexexpects(P, TKident, "function name").str; @@ -1685,19 +1763,34 @@ parsedecl(struct decl *decl, struct parser *P, bool toplevel) { decl->_cname = xcalloc(1, sizeof (char *)); parsefn(decl, P); decl->externp = externp; + } else if (lexmatch(P, &tok, TKkw_static)) { + decl->t = Dstatic; + decl->externp = externp; + decl->_cname = xcalloc(1, sizeof (char *)); + parsevardecl(P, decl); + if (!toplevel && !externp && !decl->var.ini) + fatal(P, decl->span, + "static variable inside function cannot be forward-declared"); + if (!toplevel && externp && decl->var.ini) + fatal(P, decl->span, + "extern static variable inside function cannot be initialized"); + lexexpect(P, ';'); } else if (lexmatch(P, &tok, TKkw_typedef)) { + if (externp) + fatal(P, tok.span, "typedef cannot be `extern'"); decl->t = Dtype; decl->name = lexexpects(P, TKident, "typedef name").str; decl->ty = parsetype(P); decl->_cname = xcalloc(1, sizeof(char *)); lexexpect(P, ';'); } else if (lexmatch(P, &tok, TKkw_defmacro)) { + if (externp) + fatal(P, tok.span, "macro cannot be `extern'"); const char *name = lexexpects(P, TKident, "macro name").str; decl->t = Dmacro; decl->name = name; decl->macro = parsemacro(P); decl->macro.name = name; - } else { fatal(P, tok.span, "expected declaration (near %s)", tok2str(tok)); diff --git a/bootstrap/test.cff b/bootstrap/test.cff index 7749345..61fcbf3 100644 --- a/bootstrap/test.cff +++ b/bootstrap/test.cff @@ -7,13 +7,23 @@ defmacro add { (x, y, ...rest) [ (x) + add(y, rest) ] } -defmacro swap(x, y) [{ - let $x = &(x); - let $y = &(y); - let $z = *($x); - *($x) = *($y); - *($y) = ($z); -}] +defmacro swap(x, y) [ + (do + let $x = &(x); + let $y = &(y); + let $z = *$x; + *$x = *$y; + *$y = $z;) +] + +defmacro map { +(f, x) [ f(x) ], +(f, x, ...rest) [ f(x) map(f, rest) ] +} + +defmacro printints_s(x) [ "%d " ## ] +defmacro printints(...rest) [ printf(map(printints_s, rest) "\n", rest) ] + fn fact(x usize) usize { fn f(acc usize, n usize) usize { @@ -22,20 +32,45 @@ fn fact(x usize) usize { return f(1, x); } -extern fn main (argc int, argv **u8) int { +defmacro lambda(tys, body) [ + (do + fn $lam tys body + &$lam;) +] + +fn counter() int { + static xs int = 0; + extern static glob int; + glob; + return xs++; +} + +extern static glob int = 42; + +extern fn main (argc int, argv **u8) void { extern fn printf(fmt *const u8, ...) int; fmt("%d\n", add(1, 2, 3, 4)); let x = 0; let y = 7; + switch y { + case 0, 1 do + printf("wow\n"); + case else + printf("p\n"); + } printf("x: %d; y: %d\n", x, y); swap(x, y); printf("x: %d; y: %d\n", x, y); printf("fact(6) = %zu\n", fact(6)); - fn foo(n int) int { - return n + 1; - } + printints(1, 7, 8 + 9, x, x / (++y ^ 2)); + + let z = (do + printf("hi"); + x + 1;); + + let fo = lambda((x int) void, {}); - return foo(-1); + return (*fo)(0); } diff --git a/bootstrap/util.c b/bootstrap/util.c index 4aebbe6..855091a 100644 --- a/bootstrap/util.c +++ b/bootstrap/util.c @@ -61,13 +61,13 @@ xrealloc(void *p, size_t n) { char * xasprintf(const char *fmt, ...) { va_list ap, aq; - int n = 32, m; + int n = 16, m; char *str = xcalloc(n, 1); va_start(ap, fmt); m = vsnprintf(str, n, fmt, ap) + 1; + str = xrealloc(str, m); if (m > n) { va_copy(aq, ap); - str = xrealloc(str, m); vsprintf(str, fmt, ap); va_end(aq); } |