aboutsummaryrefslogtreecommitdiff
path: root/bootstrap
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2022-08-04 07:39:23 +0200
committerlemon <lsof@mailbox.org>2022-08-04 07:39:23 +0200
commitbb1d4b4a3e51a06fb0530dfc271a97a6cd88cc73 (patch)
treef300325814bc30e64f858ee313b8260a14d8df90 /bootstrap
parent1625c50f0c0e4b1c7ba01a5df5713efaf6dce606 (diff)
stuff
Diffstat (limited to 'bootstrap')
-rw-r--r--bootstrap/all.h58
-rw-r--r--bootstrap/cgen.c139
-rw-r--r--bootstrap/dump.c5
-rw-r--r--bootstrap/env.c6
-rw-r--r--bootstrap/parse.c207
-rw-r--r--bootstrap/test.cff59
-rw-r--r--bootstrap/util.c4
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, &params);
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);
}