aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/cc/ast.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/cc/ast.c')
-rw-r--r--src/cmd/cc/ast.c2139
1 files changed, 2139 insertions, 0 deletions
diff --git a/src/cmd/cc/ast.c b/src/cmd/cc/ast.c
new file mode 100644
index 0000000..4330bcc
--- /dev/null
+++ b/src/cmd/cc/ast.c
@@ -0,0 +1,2139 @@
+#include "cc.h"
+
+// -----------------------------------------------------------------------
+// helper macros
+
+#define alloc(ptr) (ptr) = mem·arenaalloc(C.heap, 1, sizeof *(ptr))
+#define copyarray(dst, arr, n) (dst) = mem·arenaalloc(C.heap, (n), sizeof *(arr)), memcpy((dst), (arr), n * sizeof *(arr))
+#define movearray(dst, arr, n) copyarray(dst,arr,n), free(arr)
+
+#define attop(prs) ((uintptr)prs->sp == (uintptr)prs->spstk)
+#define peek(p, i) (p->tok[i])
+#define iskw(t, k) (((t).kind == Akeywd) && (t).val.i == (k))
+#define advance(p, l) (p->tok[0] = p->tok[1], p->tok[1] = lex(l), p->tok[0])
+
+#define Bit(i) (1 << (i))
+
+// -----------------------------------------------------------------------
+// helper functions
+
+static
+string
+nameof(Name *n)
+{
+ switch (n->kind) {
+ /* 0 corresponds to no state - i.e. an abstract name */
+ case Nnil:
+ return nil;
+ case Nident:
+ return n->ident;
+ case Nparen:
+ return nameof(n->paren->name);
+ case Nindex:
+ case Ncall:
+ return nameof(n->sfx.name);
+ }
+ panicf("unreachable");
+ return nil;
+}
+
+static
+void
+openscope(Parser *p)
+{
+ if (++p->sp >= arrend(p->spstk))
+ panicf("scope stack overflow");
+}
+
+/*
+ * TODO: save the symbol table with the ast node
+ * write a "copy(move)scope"
+ */
+
+static
+void
+closescope(Parser *p)
+{
+ if (p->sp <= p->spstk)
+ panicf("scope stack underflow");
+
+ forgetall(&p->sp->objs);
+ forgetall(&p->sp->tags);
+ p->sp--;
+}
+
+/* temporary stack helpers */
+static
+Name*
+getname(Parser *p)
+{
+ if (p->nm >= arrend(p->nmstk))
+ panicf("name stack overflow");
+ return p->nm++;
+}
+
+static void putdtor(Parser *p, Dtor *dt);
+
+static
+void
+putname(Parser *p, Name *n)
+{
+ if (p->nm <= p->nmstk)
+ panicf("name stack underflow");
+
+ switch (n->kind) {
+ case Nnil:
+ case Nident:
+ break;
+ case Nparen:
+ putdtor(p, n->paren);
+ break;
+ case Nindex:
+ case Ncall:
+ putname(p, n->sfx.name);
+ break;
+ default:
+ panicf("unrecognized name kind");
+ }
+ *p->nm-- = (Name){0};
+}
+
+static
+Ptr*
+getptr(Parser *p)
+{
+ if (p->pt >= arrend(p->ptstk))
+ panicf("pointer stack overflow");
+
+ return p->pt++;
+}
+
+static
+void
+putptr(Parser *p, Ptr *ptr)
+{
+ if (p->pt <= p->ptstk)
+ panicf("pointer stack underflow");
+
+ while ((ptr = ptr->link))
+ putptr(p, ptr);
+
+ *p->pt-- = (Ptr){0};
+}
+
+
+static
+Dtor*
+getdtor(Parser *p)
+{
+ if (p->dt >= arrend(p->dtstk))
+ panicf("dtor stack overflow");
+
+ p->dt->name = getname(p);
+ return p->dt++;
+}
+
+static
+void
+putdtor(Parser *p, Dtor *dt)
+{
+ if (p->dt <= p->dtstk)
+ panicf("dtor stack underflow");
+
+ /* release the pointer overflow if we had to use it */
+ if (p->dt->ptr.link)
+ putptr(p, p->dt->ptr.link);
+
+ /* the dtor could encompass multiple names hierarchically */
+ putname(p, dt->name);
+ *p->dt-- = (Dtor){0};
+}
+
+/* TODO: This will fail for forward declarations */
+static
+void
+declareobj(Parser *p, Decl *d)
+{
+ Sym *sym;
+ string ident;
+ uint32 kind;
+ struct Decls *link;
+
+ switch (d->kind) {
+ case Dfunc:
+ case Dvar:
+ kind = Svar;
+ goto one;
+ case Dtype:
+ kind = Stype;
+ one:
+ ident = d->name;
+ break;
+
+ case Dvars:
+ kind = Svar;
+ goto many;
+ case Dtypes:
+ kind = Stype;
+ many:
+ while (link = &d->list, link != nil) {
+ ident = link->name;
+ sym = lookup(&p->sp->objs, ident);
+ if (sym) {
+ errorat(peek(p, 0).pos, "redeclaration of name '%s' in object space", ident);
+ return;
+ }
+ sym = define(&p->sp->objs, ident, kind);
+ if (kind == Svar)
+ sym->obj = d;
+ else
+ sym->type = d->type;
+ }
+ break;
+
+ default:
+ panicf("unrecognized node kind %d. expected declaration", d->kind);
+ }
+ sym = lookup(&p->sp->objs, ident);
+ if (sym) {
+ errorat(peek(p, 0).pos, "redeclaration of name '%s' in object space", ident);
+ return;
+ }
+ sym = define(&p->sp->objs, ident, kind);
+ if (kind == Svar)
+ sym->obj = d;
+ else
+ sym->type = d->type;
+}
+
+/* enters the object identifier space */
+static
+void
+declareenum(Parser *p, int n, string *elts, Expr *vals)
+{
+ int i;
+ Sym *s;
+
+ for (i = 0; i < n; i++) {
+ s = lookup(&p->sp->objs, elts[i]);
+ if (s) {
+ errorat(peek(p, 0).pos, "redeclaration of name %s in object space", elts[i]);
+ continue;
+ }
+ s = define(&p->sp->objs, elts[i], Senum);
+ s->val = vals + i;
+ }
+}
+
+static
+void
+declaretag(Parser *p, uint32 t, string name)
+{
+ Sym *sym;
+ sym = lookup(&p->sp->tags, name);
+ if (sym) {
+ errorat(peek(p, 0).pos, "redeclaration of name '%s' in tag space", name);
+ return;
+ }
+
+ sym = define(&p->sp->tags, name, Stype);
+ sym->type = t;
+}
+
+static
+Sym *
+lookupobj(Parser *p, string ident)
+{
+ Sym *sym;
+ Scope *it;
+
+ it = p->sp;
+ do {
+ sym = lookup(&it->objs, ident);
+ } while (sym == nil && --it >= p->spstk);
+
+ return sym;
+}
+
+static
+Sym *
+lookuptag(Parser *p, string ident)
+{
+ Sym *sym;
+ Scope *it;
+
+ it = p->sp;
+ do {
+ sym = lookup(&it->tags, ident);
+ } while (sym == nil && --it >= p->spstk);
+
+ return sym;
+}
+
+static
+int
+nomatch(Token t, vlong kind)
+{
+ if (t.kind == kind)
+ return 0;
+
+ if (t.kind == Akeywd)
+ errorat(t.pos, "expected token '%s', instead found keyword '%s'", tokens[kind], keywords[t.val.i]);
+ else
+ errorat(t.pos, "expected token '%s', instead found '%s'", tokens[kind], tokens[t.kind]);
+ return 1;
+}
+
+// -----------------------------------------------------------------------
+// needed forward declarations
+
+static error spec(Parser *, Lexer *, uint64 *);
+static uint32 basetype(Parser *, Lexer *, uint64 *s);
+static string namedecl(Parser *, Lexer *, uint32 *, int);
+static uint32 typename(Parser *, Lexer *, uint32 *);
+
+static error dtor(Parser *p, Lexer *lx, Dtor *d, int ab);
+static uint32 typeofdtor(Dtor *, uint32);
+
+
+static Decl *decl(Parser *, Lexer *);
+
+static Expr *ternary(Parser *, Lexer *);
+static Expr *expr(Parser *, Lexer *);
+
+static error blkstmt(Parser *, Lexer *, Stmt **);
+
+
+// -----------------------------------------------------------------------
+// expressions
+
+#define MAKEX(x, state) alloc((x)), (x)->kind = X##state
+
+static
+Expr*
+primary(Parser *p, Lexer *lx)
+{
+ int k;
+ Expr *x;
+ Token t;
+ Pos b;
+
+ t = peek(p, 0);
+ b = t.pos;
+ switch (k = (t.kind & Vmask)) {
+ case Aident:
+ MAKEX(x, ident);
+ x->pos.beg = b;
+ x->pos.end = lx->pos;
+ x->name = t.val.s;
+ break;
+
+ case Alit:
+ MAKEX(x, lit);
+ x->pos.beg = b;
+ x->pos.end = lx->pos;
+ x->val.kind = t.kind & ~Vmask;
+ x->val.v = t.val;
+ break;
+
+ case Alparen:
+ advance(p, lx);
+ x = expr(p, lx);
+ t = peek(p, 0);
+ if (nomatch(t, Arparen)) {
+ errorat(lx->pos, "unterminated paren expression");
+ goto Bad;
+ }
+ break;
+
+ default:
+ panicf("unreachable");
+ }
+
+ advance(p, lx);
+ return x;
+Bad:
+ errorat(lx->pos, "unable to parse operand expression");
+ return nil;
+}
+
+static
+int
+istypename(Parser *p, Token t)
+{
+ Sym *sym;
+
+ if (t.kind == Akeywd && (Kconst <= t.val.i && t.val.i <= Kenum))
+ return 1;
+ if (t.kind == Aident) {
+ sym = lookupobj(p, t.val.s);
+ return (sym != nil) && sym->kind == Stype;
+ }
+
+ return 0;
+}
+
+static Expr* initx(Parser *p, Lexer *lx);
+
+static
+Expr*
+initlist(Parser *p, Lexer *lx)
+{
+ Token t;
+ int c, n;
+ Expr *x, **a;
+ struct Key *k;
+
+ MAKEX(x, initlist);
+ x->pos.beg = lx->pos;
+ x->init.n = 0;
+ if (t.kind == Arbrace) {
+ x->init.k = nil;
+ x->init.v = nil;
+ return x;
+ }
+
+ c = 0;
+ n = 0;
+ a = nil;
+ k = nil;
+Key0:
+ if (n >= c) {
+ c += 20;
+ k = realloc(k, c * sizeof(*k));
+ a = realloc(a, c * sizeof(*a));
+ }
+Key1:
+ switch (t.kind) {
+ case Adot:
+ t = advance(p, lx);
+ if (t.kind != Aident) {
+ errorat(t.pos, "dot designator must be followed by identifier");
+ goto Bad;
+ }
+ k[n++] = (struct Key) {
+ .kind = (uint32)x->init.n,
+ .s = t.val.s,
+ };
+ t = advance(p, lx);
+ goto Key0;
+
+ case Albrakt:
+ t = advance(p, lx);
+ k[n++] = (struct Key) {
+ .kind = (uint32)x->init.n | (1ULL << 32),
+ .x = expr(p, lx),
+ };
+ t = peek(p, 0);
+ goto Key0;
+
+ case Aeq:
+ t = advance(p, lx);
+ /* fallthrough */
+ default:
+ a[x->init.n++] = initx(p, lx);
+
+ t = peek(p, 0);
+ switch (t.kind) {
+ case Arbrace:
+ break;
+ case Acomma:
+ advance(p, lx);
+ /* fallthrough */
+ default:
+ goto Key0;
+ }
+ break;
+
+ case Acomma:
+ t = advance(p, lx);
+ break;
+ }
+ movearray(x->init.k, k, n);
+ movearray(x->init.v, a, x->init.n);
+ return x;
+Bad:
+ errorat(t.pos, "could not parse initializer list");
+ return nil;
+}
+
+static
+Expr*
+initx(Parser *p, Lexer *lx)
+{
+ Expr *x;
+ Token t;
+
+ t = peek(p, 0);
+ if (t.kind != Albrace)
+ return ternary(p, lx);
+
+ advance(p, lx);
+ x = initlist(p, lx);
+ t = peek(p, 0);
+ if (nomatch(t, Arbrace)) {
+ errorat(t.pos, "unmatched brace in initializer list, found %s instead", tokens[t.kind]);
+ advance(p, lx);
+ }
+
+ return x;
+}
+
+static
+Expr*
+postfix(Parser *p, Lexer *lx)
+{
+ Pos b;
+ Token t;
+ int c, n;
+ uint32 type, qual;
+ Expr *x, *y, **a;
+
+ t = peek(p, 0);
+ if (t.kind == Alparen)
+ if (istypename(p, peek(p, 1))) {
+ t = advance(p, lx);
+ type = typename(p, lx, &qual);
+ t = peek(p, 0);
+ if (nomatch(t, Arparen)) {
+ errorat(lx->pos, "unmatched paren: found %s instead", tokens[t.kind]);
+ goto Bad;
+ }
+ t = advance(p, lx);
+ if (nomatch(t, Albrace)) {
+ errorat(lx->pos, "bad initializer list: found %s", tokens[t.kind]);
+ goto Bad;
+ }
+
+ x = initlist(p, lx);
+
+ t = peek(p, 0);
+ if (nomatch(t, Arbrace)) {
+ errorat(lx->pos, "unmatched brace: found %s instead", tokens[t.kind]);
+ goto Bad;
+ }
+
+ x->type = type;
+ x->qual = qual;
+ return x;
+ }
+
+ x = primary(p, lx);
+ t = peek(p, 0);
+ for (;;) {
+ b = x->pos.beg;
+ switch (t.kind) {
+ case Ainc:
+ MAKEX(y, postinc);
+ goto Postfix;
+ case Adec:
+ MAKEX(y, postdec);
+ Postfix:
+ y->pos.beg = b;
+ y->pos.end = lx->pos;
+ y->unary.post = x;
+ x = y, y = nil;
+ break;
+
+ case Adot:
+ MAKEX(y, self);
+ goto Select;
+ case Aarrow:
+ MAKEX(y, selp);
+ Select:
+ t = advance(p, lx);
+ if (t.kind != Aident) {
+ errorat(t.pos, "invalid operand of selector expression");
+ goto Bad;
+ }
+ y->pos.beg = b;
+ y->pos.end = lx->pos;
+
+ y->idx.f = t.val.s;
+ y->idx.x = x;
+ x = y, y = nil;
+ break;
+
+ case Albrakt:
+ t = advance(p, lx);
+ if (t.kind == Arbrakt) {
+ errorat(t.pos, "empty index expression");
+ goto Bad;
+ }
+ MAKEX(y, index);
+ y->idx.x = x;
+ y->idx.i = expr(p, lx);
+
+ t = peek(p, 0);
+ if (t.kind != Albrakt) {
+ errorat(t.pos, "malformed index expression");
+ goto Bad;
+ }
+
+ x = y, y = nil;
+ break;
+
+ case Alparen:
+ t = advance(p, lx);
+ MAKEX(y, call);
+ y->call.fn = x;
+ y->pos.beg = b;
+ y->call.n = 0;
+ if (t.kind == Arparen) {
+ y->call.arg = nil;
+ goto Endfunc;
+ }
+ c = 0;
+ a = nil;
+ Arg:
+ if (y->call.n >= c) {
+ c += 20;
+ a = realloc(a, c * sizeof(*a));
+ }
+ a[y->call.n++] = expr(p, lx);
+ t = peek(p, 0);
+ if (t.kind == Acomma) {
+ advance(p, lx);
+ goto Arg;
+ }
+ if (t.kind != Arparen) {
+ errorat(t.pos, "invalid token '%s' found in call argument");
+ goto Bad;
+ }
+ movearray(y->call.arg, a, y->call.n);
+ Endfunc:
+ y->pos.end = lx->pos;
+ x = y, y = nil;
+ break;
+
+ default:
+ return x;
+ }
+ t = advance(p, lx);
+ }
+ return x;
+Bad:
+ errorat(lx->pos, "failed to parse primary expression");
+ return nil;
+}
+
+static
+uint32
+typename(Parser *p, Lexer *lx, uint32 *spec)
+{
+ uint32 base;
+ uint64 s;
+
+ base = basetype(p, lx, &s);
+ if (!base) {
+ errorat(lx->pos, "failed to parse type name specifiers");
+ return 0;
+ }
+ *spec = (uint32)s;
+ namedecl(p, lx, &base, 1);
+
+ return base;
+}
+
+static Expr* cast(Parser *p, Lexer *lx);
+
+static
+Expr*
+unary(Parser *p, Lexer *lx)
+{
+ Expr *x;
+ Token t;
+
+ t = peek(p, 0);
+ switch (t.kind) {
+ case Ainc: MAKEX(x, preinc); goto Prefix;
+ case Adec: MAKEX(x, predec); /* fallthrough */
+ Prefix:
+ advance(p, lx);
+ x->pos.beg = t.pos;
+ x->unary.pre = unary(p, lx);
+ x->pos.end = x->unary.pre->pos.end;
+ return x;
+
+ case Aneg: MAKEX(x, neg); goto Unary;
+ case Aand: MAKEX(x, ref); goto Unary;
+ case Anot: MAKEX(x, not); goto Unary;
+ case Astar: MAKEX(x, star); goto Unary;
+ case Aadd: MAKEX(x, plus); goto Unary;
+ case Asub: MAKEX(x, minus); /* fallthrough */
+ Unary:
+ advance(p, lx);
+ x->pos.beg = t.pos;
+ x->unary.pre = cast(p, lx);
+ x->pos.end = x->unary.pre->pos.end;
+ return x;
+
+ case Akeywd:
+ switch (t.val.i) {
+ case Ksizeof:
+ MAKEX(x, sizeof);
+ goto Key;
+ case Kalignof:
+ MAKEX(x, alignof);
+ /* fallthrough */
+ Key:
+ t = advance(p, lx);
+ if (t.kind == Alparen)
+ if (istypename(p, peek(p, 1))) {
+ t = advance(p, lx);
+ x->info.type = 0;
+ x->info.of.type = typename(p, lx, &x->info.of.qual);
+
+ t = peek(p, 0);
+ if (nomatch(t, Arparen)) {
+ errorat(t.pos, "missing paren for size/alignof statement");
+ goto Bad;
+ }
+ advance(p, lx);
+ return x;
+ }
+
+ x->info.type = 1;
+ x->info.x = unary(p, lx);
+ return x;
+
+ default:
+ ;
+ }
+ /* fallthrough */
+ default:
+ return postfix(p, lx);
+ }
+Bad:
+ return nil;
+}
+
+static
+Expr*
+cast(Parser *p, Lexer *lx)
+{
+ Expr *x;
+ Token t;
+
+ t = peek(p, 0);
+ if (t.kind == Alparen && istypename(p, peek(p,1))) {
+ t = advance(p, lx);
+ MAKEX(x, cast);
+
+ x->pos.beg = t.pos;
+ x->cast.to.type = typename(p, lx, &x->cast.to.qual);
+ if (!x->cast.to.type) {
+ errorat(lx->pos, "invalid type operand of cast");
+ goto Bad;
+ }
+
+ t = peek(p, 0);
+ if (nomatch(t, Arparen)) {
+ errorat(lx->pos, "missing closing paren after cast expression");
+ goto Bad;
+ }
+ advance(p, lx);
+
+ x->cast.x = cast(p, lx);
+ x->pos.beg = lx->pos;
+ return x;
+ }
+ return unary(p, lx);
+
+Bad:
+ errorat(lx->pos, "failed to parse cast expression");
+ return nil;
+}
+
+/* static data for binary operators */
+#define OPERATORS \
+ OPERATOR(Astar, 10, Xmul) \
+ OPERATOR(Adiv, 10, Xdiv) \
+ OPERATOR(Amod, 10, Xmod) \
+ OPERATOR(Aadd, 9, Xadd) \
+ OPERATOR(Asub, 9, Xsub) \
+ OPERATOR(Alsft, 8, Xlsft) \
+ OPERATOR(Arsft, 8, Xrsft) \
+ OPERATOR(Agteq, 7, Xgteq) \
+ OPERATOR(Alteq, 7, Xlteq) \
+ OPERATOR(Alt, 7, Xlt) \
+ OPERATOR(Agt, 7, Xgt) \
+ OPERATOR(Aeq, 6, Xeql) \
+ OPERATOR(Aneq, 6, Xneq) \
+ OPERATOR(Aand, 5, Xand) \
+ OPERATOR(Axor, 4, Xxor) \
+ OPERATOR(Aor, 3, Xor) \
+ OPERATOR(Aandand, 2, Xandand) \
+ OPERATOR(Aoror, 1, Xoror)
+
+static int prectab[NUM_TOKENS] =
+{
+#define OPERATOR(a, b, c) [a] = b,
+ OPERATORS
+#undef OPERATOR
+};
+
+static int optab[NUM_TOKENS] =
+{
+#define OPERATOR(a, b, c) [a] = c,
+ OPERATORS
+#undef OPERATOR
+};
+#undef OPERATORS
+
+static
+Expr*
+binary(Parser *p, Lexer *lx, int prec)
+{
+ Token t;
+ int k, np;
+ Expr *l, *x;
+
+ l = cast(p, lx);
+ for (;;) {
+ t = peek(p, 0);
+ k = t.kind;
+ np = prectab[k];
+ if (np < prec)
+ return l;
+
+ alloc(x);
+ t = advance(p, lx);
+
+ x->pos.beg = l->pos.beg;
+ x->kind = optab[k];
+ x->binary.l = l;
+ x->binary.r = binary(p, lx, np + 1);
+ x->pos.end = x->binary.r->pos.end;
+
+ l = x;
+ }
+ return l;
+Bad:
+ errorat(t.pos, "failed to parse expression");
+ return nil;
+}
+
+static
+Expr*
+ternary(Parser *p, Lexer *lx)
+{
+ Pos b;
+ Token t;
+ Expr *x, *y;
+
+ x = binary(p, lx, 1);
+ t = peek(p, 0);
+ b = t.pos;
+
+ switch (t.kind) {
+ case Aqmark:
+ t = advance(p, lx);
+ y = x;
+ MAKEX(x, ternary);
+ x->pos.beg = b;
+ x->kind = Xternary;
+ x->cond.c = y;
+ x->cond.t = expr(p, lx);
+
+ t = peek(p, 0);
+ if (nomatch(t, Acolon)) {
+ errorat(t.pos, "ternary expression missing ':'");
+ goto Bad;
+ }
+ t = advance(p, lx);
+ x->cond.e = expr(p, lx);
+ x->pos.end = lx->pos;
+ break;
+
+ case Aasn: MAKEX(y, asn); goto Assign;
+ case Aorasn: MAKEX(y, orasn); goto Assign;
+ case Axorasn: MAKEX(y, xorasn); goto Assign;
+ case Aandasn: MAKEX(y, andasn); goto Assign;
+ case Asubasn: MAKEX(y, subasn); goto Assign;
+ case Amulasn: MAKEX(y, mulasn); goto Assign;
+ case Adivasn: MAKEX(y, divasn); goto Assign;
+ case Amodasn: MAKEX(y, modasn); goto Assign;
+ case Alsftasn: MAKEX(y, lsftasn); goto Assign;
+ case Arsftasn: MAKEX(y, rsftasn); goto Assign;
+ Assign:
+ advance(p, lx);
+
+ y->asn.l = x;
+ y->asn.r = ternary(p, lx);
+ x = y;
+ x->pos.beg = b;
+ x->pos.end = lx->pos;
+ break;
+ default:
+ ;
+ }
+
+ return x;
+Bad:
+ errorat(lx->pos, "failing expression parse");
+ return nil;
+}
+
+static
+Expr*
+expr(Parser *p, Lexer *lx)
+{
+ Pos b;
+ Token t;
+ Expr *x, *y;
+
+ x = ternary(p, lx);
+ while (t = peek(p, 0), t.kind == Acomma) {
+ advance(p, lx);
+ y = x;
+ MAKEX(x, comma);
+ x->pos.beg = y->pos.beg;
+ x->comma.x[0] = y;
+ x->comma.x[1] = ternary(p, lx);
+ x->pos.end = lx->pos;
+ y = nil;
+ }
+
+ return x;
+}
+
+// -----------------------------------------------------------------------
+// statements
+
+static
+struct Node*
+stmt(Parser *p, Lexer *lx)
+{
+ int k;
+ Stmt *s;
+ Sym *sym;
+ Token t;
+
+ t = peek(p, 0);
+ k = t.kind;
+
+ /* intercept decl before allocating a statement */
+ if (k == Aident) {
+ if (peek(p, 1).kind == Acolon)
+ goto Tlabel;
+ sym = lookupobj(p, t.val.s);
+ if (!sym) {
+ errorat(lx->pos, "unrecognized type identifier '%s'", t.val.s);
+ goto Bad;
+ }
+
+ if (sym->kind == Stype)
+ goto Tdecl;
+ if (sym->kind == Svar) {
+ alloc(s);
+ s->pos.beg = lx->pos;
+ goto Texpr;
+ }
+
+ errorat(lx->pos, "bad symbol type used as type identifier");
+ goto Bad;
+ }
+
+ if (k == Akeywd) {
+ if ((Kauto <= t.val.i && t.val.i <= Ktypedef) || (Kconst <= t.val.i && t.val.i <= Kenum)) {
+ Tdecl:
+ return (Node *)decl(p, lx);
+ }
+ }
+
+ alloc(s);
+ s->pos.beg = lx->pos;
+
+ switch (k) {
+ case Akeywd:
+ switch (k = t.val.i) {
+ case Kif:
+ t = advance(p, lx);
+ s->kind = Sif;
+
+ if (nomatch(t, Alparen)) {
+ errorat(lx->pos, "missing opening paren before if conditional");
+ goto Bad;
+ }
+ s->br.cond = expr(p, lx);
+ if (nomatch(t, Arparen)) {
+ errorat(lx->pos, "missing closing paren after if conditional");
+ goto Bad;
+ }
+ s->br.body = stmt(p, lx);
+
+ t = peek(p, 0);
+ if (iskw(t, Kelse))
+ s->br.orelse = stmt(p, lx);
+ else
+ s->br.orelse = nil;
+
+ break;
+
+ case Kswitch:
+ t = advance(p, lx);
+ s->kind = Sswitch;
+
+ if (nomatch(t, Alparen)) {
+ errorat(lx->pos, "missing opening paren before switch conditional");
+ goto Bad;
+ }
+ s->br.cond = expr(p, lx);
+ if (nomatch(t, Arparen)) {
+ errorat(lx->pos, "missing closing paren after switch conditional");
+ goto Bad;
+ }
+ s->br.body = stmt(p, lx);
+ s->br.orelse = nil;
+
+ break;
+
+ case Kfor:
+ t = advance(p, lx);
+ s->kind = Sfor;
+
+ if (nomatch(t, Alparen)) {
+ errorat(lx->pos, "missing opening paren before for loop preamble");
+ goto Bad;
+ }
+
+ if (t.kind == Asemi)
+ s->loop.init = nil;
+ else {
+ // TODO: test for declaration
+ s->loop.init = (Node *)expr(p, lx);
+ }
+
+ if (nomatch(t, Asemi)) {
+ errorat(lx->pos, "missing semicolon");
+ goto Bad;
+ }
+
+ if (t.kind == Asemi)
+ s->loop.cond = nil;
+ else
+ s->loop.cond = expr(p, lx);
+
+ if (nomatch(t, Asemi)) {
+ errorat(lx->pos, "missing semicolon");
+ goto Bad;
+ }
+
+ if (t.kind == Asemi)
+ s->loop.step = nil;
+ else
+ s->loop.step = expr(p, lx);
+
+ if (nomatch(t, Alparen)) {
+ errorat(lx->pos, "missing closing paren after for loop preamble");
+ goto Bad;
+ }
+ s->loop.body = stmt(p, lx);
+ break;
+
+ case Kwhile:
+ t = advance(p, lx);
+ s->kind = Swhile;
+ if (nomatch(t, Alparen)) {
+ errorat(lx->pos, "missing opening paren before while loop conditional");
+ goto Bad;
+ }
+ s->loop.cond = expr(p, lx);
+ if (nomatch(t, Arparen)) {
+ errorat(t.pos, "missing closing paren after while loop conditional");
+ goto Bad;
+ }
+
+ s->loop.init = nil;
+ s->loop.step = nil;
+ s->loop.body = stmt(p, lx);
+ break;
+
+ case Kdo:
+ t = advance(p, lx);
+ s->kind = Sdo;
+ s->loop.body = stmt(p, lx);
+
+ if (!iskw(t, Kwhile)) {
+ errorat(t.pos, "missing while statement conditional after do body");
+ goto Bad;
+ }
+ t = advance(p, lx);
+ if (nomatch(t, Alparen)) {
+ errorat(t.pos, "missing open paren after while conditional");
+ goto Bad;
+ }
+
+ s->loop.init = nil;
+ s->loop.step = nil;
+ s->loop.cond = expr(p, lx);
+ break;
+
+ case Kgoto:
+ t = advance(p, lx);
+ s->kind = Sgoto;
+ if (t.kind != Aident) {
+ errorat(t.pos, "invalid argument to goto");
+ goto Bad;
+ }
+ s->jmp.lbl = t.val.s;
+ t = advance(p, lx);
+ if (nomatch(t, Asemi)) {
+ errorat(t.pos, "missing semicolon after goto");
+ goto Bad;
+ }
+ advance(p, lx);
+ break;
+
+ case Kcontinue:
+ t = advance(p, lx);
+ s->kind = Scontin;
+ s->jmp.lbl = nil;
+ s->jmp.x = nil;
+ if (nomatch(t, Asemi)) {
+ errorat(t.pos, "missing semicolon after continue");
+ goto Bad;
+ }
+ advance(p, lx);
+ break;
+
+ case Kbreak:
+ t = advance(p, lx);
+ s->kind = Sbreak;
+ s->jmp.lbl = nil;
+ s->jmp.x = nil;
+ if (nomatch(t, Asemi)) {
+ errorat(t.pos, "missing semicolon after break");
+ goto Bad;
+ }
+ advance(p, lx);
+ break;
+
+ case Kreturn:
+ t = advance(p, lx);
+ s->kind = Sreturn;
+
+ s->jmp.lbl = nil;
+ s->jmp.x = (t.kind == Asemi) ? nil : expr(p, lx);
+
+ t = peek(p, 0);
+ if (nomatch(t, Asemi)) {
+ errorat(t.pos, "missing semicolon after return statement");
+ goto Bad;
+ }
+ advance(p, lx);
+ break;
+
+ case Kcase:
+ t = advance(p, lx);
+ s->kind = Scase;
+ s->lbl.x = expr(p, lx);
+ if (nomatch(t, Acolon)) {
+ errorat(t.pos, "missing colon after default label");
+ goto Bad;
+ }
+ t = advance(p, lx);
+ s->lbl.stmt = stmt(p, lx);
+ break;
+
+ case Kdefault:
+ t = advance(p, lx);
+ s->kind = Scase;
+ s->lbl.x = nil;
+ if (nomatch(t, Acolon)) {
+ errorat(t.pos, "missing colon after default label");
+ goto Bad;
+ }
+ t = advance(p, lx);
+ s->lbl.stmt = stmt(p, lx);
+ break;
+
+ default:
+ panicf("unexpected statement keyword %s", keywords[k]);
+ }
+ break;
+ case Albrace:
+ s->kind = Sblock;
+ openscope(p);
+ if (blkstmt(p, lx, &s)) {
+ errorat(lx->pos, "failed to parse block statement");
+ goto Bad;
+ }
+ closescope(p);
+ break;
+
+ case Asemi:
+ t = advance(p, lx);
+ s->kind = Sempty;
+ break;
+
+ case Aident:
+ Tlabel:
+ t = advance(p, lx);
+ s->kind = Slabel;
+ if (nomatch(t, Acolon)) {
+ errorat(t.pos, "missing colon after labelled block");
+ goto Bad;
+ }
+ t = advance(p, lx);
+ s->lbl.stmt = stmt(p, lx);
+ break;
+
+ default:
+ Texpr:
+ s->kind = Sexpr;
+ s->x = expr(p, lx);
+
+ t = peek(p, 0);
+ if (nomatch(t, Asemi)) {
+ errorat(t.pos, "missing semicolon after statement expression");
+ goto Bad;
+ }
+ advance(p, lx);
+ }
+
+ s->pos.end = lx->pos;
+ return (Node *)s;
+Bad:
+ errorat(lx->pos, "failed to parse statement");
+ return nil;
+}
+
+static
+error
+blkstmt(Parser *p, Lexer *lx, Stmt **s)
+{
+ Token t;
+ int len;
+ int cap;
+ Node **ns;
+
+ alloc(*s);
+ (*s)->kind = Sblock;
+ (*s)->pos.beg = lx->pos;
+
+ t = peek(p, 0);
+ if (nomatch(t, Albrace))
+ goto Bad;
+ t = advance(p, lx);
+
+ len = 0, cap = 20;
+ ns = malloc(cap*sizeof(*ns));
+ while (t.kind != Arbrace) {
+ if (cap == len) {
+ cap += 20;
+ ns = realloc(ns, cap*sizeof(*ns));
+ }
+ ns[len++] = stmt(p, lx);
+ t = peek(p, 0);
+ }
+ advance(p, lx);
+
+ (*s)->pos.end = lx->pos;
+ (*s)->blk.n = len;
+ movearray((*s)->blk.item, ns, len);
+ return 0;
+Bad:
+ errorat(lx->pos, "failed to parse block statement");
+ free(ns);
+ return 1;
+}
+
+// -----------------------------------------------------------------------
+// types
+
+uint32
+ptrtype(uint32 base, uint32 qual)
+{
+ uint32 i;
+ Type *t;
+
+ i = type();
+ t = C.type.info + i;
+ t->kind = Tptr;
+ t->ptr.base = base;
+ t->ptr.qual = qual;
+ t->size = pointer.size;
+ t->align = pointer.align;
+ t->sign = pointer.sign;
+
+ return i;
+}
+
+uint32
+arraytype(uint32 base, uint32 qual, Expr *ix)
+{
+ int i, n;
+ Type *t;
+
+ /* TODO: evaluate the length */
+ n = 10;
+ i = type();
+ t = C.type.info + i;
+ t->kind = Tarray;
+ t->ptr.base = base;
+ t->size = n * C.type.info[base].size;
+ t->align = C.type.info[base].align;
+ t->sign = 0;
+
+ return i;
+}
+
+uint32
+functype(uint32 ret, int n, Field *args, int dots)
+{
+ uint32 i;
+ Type *t;
+
+ i = type();
+ t = C.type.info + i;
+ t->kind = Tfunc;
+ t->size = pointer.size;
+ t->align = pointer.align;
+ t->sign = pointer.sign;
+
+ t->func.ret = ret;
+ t->func.n = n;
+ t->func.arg = args;
+ t->func.dots = dots;
+
+ return i;
+}
+
+#define ALIGN_DOWN(n, a) ((n) & ~((a)-1))
+#define ALIGN_UP(n, a) ALIGN_DOWN((n) + (a)-1, (a))
+uint32
+structtype(int n, Field *field, Expr *bits)
+{
+ uint32 i;
+ Type *t;
+ Field *f, *e;
+
+ i = type();
+ t = C.type.info + i;
+ t->kind = Tstruct;
+ t->size = 0;
+ t->align = 0;
+ for (f = field, e = field+n; f != e; ++f) {
+ t->size += C.type.info[f->type].size + ALIGN_UP(t->size, C.type.info[f->type].align);
+ t->align = MAX(t->align, C.type.info[f->type].align);
+ }
+ t->aggr.len = n;
+ t->aggr.f = field;
+ t->aggr.x = bits;
+
+ return i;
+}
+
+uint32
+uniontype(int n, Field *field, Expr *bits)
+{
+ uint32 i;
+ Type *t;
+ Field *f, *e;
+
+ i = type();
+ t = C.type.info + i;
+ t->kind = Tstruct;
+ t->size = 0;
+ t->align = 0;
+ for (f = field, e = field+n; f != e; ++f) {
+ t->size = MAX(t->size, C.type.info[f->type].size);
+ t->align = MAX(t->align, C.type.info[f->type].align);
+ }
+ t->aggr.len = n;
+ t->aggr.f = field;
+ t->aggr.x = bits;
+
+ return i;
+}
+
+uint32
+enumtype(int n, string *elts, Expr *vals)
+{
+ uint32 i;
+ Type *t;
+ Field *f, *e;
+
+ i = type();
+ t = C.type.info + i;
+ t->kind = Tenum;
+ /* TODO: dont hardcode int32 */
+ t->size = 4;
+ t->align = 4;
+ t->enm.len = n;
+ t->enm.elt = elts;
+ t->enm.val = vals;
+
+ return i;
+}
+#undef ALIGN_UP
+#undef ALIGN_DOWN
+
+/* unpacking C declarations into sensible types */
+static
+uint32
+typeofname(Name *name, uint32 base)
+{
+ switch (name->kind) {
+ /* Nnil corresponds to an abstract declarator (i.e. no identifier) */
+ case Nnil:
+ case Nident:
+ return base;
+ case Nparen:
+ return typeofdtor(name->paren, base);
+ case Nindex:
+ return typeofname(name->sfx.name, arraytype(base, name->sfx.idx.q, name->sfx.idx.x));
+ case Ncall:
+ return typeofname(name->sfx.name, functype(base, name->sfx.call.n, name->sfx.call.arg, name->sfx.call.dots));
+ default:
+ panicf("unreachable");
+ }
+ return 0;
+}
+
+static
+uint32
+typeofdtor(Dtor *decl, uint32 base)
+{
+ int n;
+ Ptr *p;
+ uint64 b, tmp;
+
+ n = 0;
+ p = &decl->ptr;
+ b = p->kind;
+ while (b & 1) {
+ base = ptrtype(base, b >> 1);
+ if (++n >= 8) {
+ p = p->link;
+ b = p->kind;
+ } else {
+ b >>= 6;
+ }
+ }
+
+ return typeofname(decl->name, base);
+}
+
+static
+uint32
+basetype(Parser *p, Lexer *lx, uint64 *s)
+{
+ int n;
+ uint64 m;
+
+ if (spec(p, lx, s)) {
+ errorat(lx->pos, "failed to parse type specifier");
+ return 0;
+ }
+
+ m = (((*s<<32)>>32) & ~(MaskQul|MaskMem|MaskFcn));
+ for (n = 0; n < arrlen(validtypespec); n++) {
+ if (validtypespec[n] == m) {
+ if (indextypespec[n] < 0) {
+ m = *s >> 32;
+ if (!m) {
+ errorat(lx->pos, "not a valid type identifier");
+ return 0;
+ }
+ return m;
+ }
+ return indextypespec[n];
+ }
+ }
+
+ errorat(lx->pos, "invalid type specifier");
+ return 0;
+}
+
+static
+string
+namedecl(Parser *p, Lexer *lx, uint32 *base, int noname)
+{
+ Dtor *dt;
+ string name;
+ Type *t;
+
+ dt = getdtor(p);
+ name = nil;
+ if (dtor(p, lx, dt, noname)) {
+ errorat(lx->pos, "invalid declarator");
+ goto End;
+ }
+ if (!noname || noname == 2 && dt->name->kind)
+ name = nameof(dt->name);
+
+ *base = typeofdtor(dt, *base);
+ putdtor(p, dt);
+ return name;
+End:
+ putdtor(p, dt);
+ return nil;
+}
+
+// -----------------------------------------------------------------------
+// declarations
+
+static
+uint32
+enumerate(Parser *p, Lexer *lx, string name, int kind)
+{
+ int i, n;
+ uint64 s;
+ uint32 t;
+ Token tk;
+ /* TODO: think of a better soln */
+ string nm[1024], *elts;
+ Expr *cx[1024], *vals;
+
+ for (n = 0; tk.kind != Arbrace && n < arrlen(nm); n++) {
+ if (tk.kind != Aident) {
+ errorat(tk.pos, "invalid token %s in enum declaration", tokens[tk.kind]);
+ goto Bad;
+ }
+ nm[n] = tk.val.s;
+ cx[n] = nil;
+
+ tk = advance(p, lx);
+ switch(tk.kind) {
+ case Aeq:
+ advance(p, lx);
+ cx[n] = expr(p, lx);
+ tk = peek(p, 0);
+ if (tk.kind != Acomma)
+ continue;
+ /* fallthrough */
+ case Acomma:
+ tk = advance(p, lx);
+ }
+ }
+ copyarray(elts, nm, n);
+ copyarray(vals, cx, n);
+
+ t = enumtype(n, elts, vals);
+ declareenum(p, n, elts, vals);
+ return t;
+Bad:
+ errorat(tk.pos, "failed to parse enum declaration");
+ return 0;
+}
+
+static
+uint32
+aggregate(Parser *p, Lexer *lx, string name, int kind)
+{
+ int n;
+ uint64 s;
+ Token tk;
+ /* TODO: think of a better soln */
+ static Field fs[1024];
+ Field *f;
+ static Expr *cx[1024];
+ Expr *x;
+
+ for (n = 0, tk = peek(p, 0); tk.kind != Arbrace && n < arrlen(fs); n++) {
+ fs[n].type = basetype(p, lx, &s);
+ fs[n].qual = (uint32)(s & ~(MaskTyp|MaskInt|MaskFlt));
+ Field:
+ fs[n].name = namedecl(p, lx, &fs[n].type, 0);
+ tk = peek(p, 0);
+ switch (tk.kind) {
+ case Acolon:
+ advance(p, lx);
+ cx[n] = expr(p, lx);
+ tk = peek(p, 0);
+ if (tk.kind == Asemi) {
+ tk = advance(p, lx);
+ continue;
+ }
+ if (tk.kind != Acomma) {
+ errorat(tk.pos, "unrecognized token %s in struct field declaration", tokens[tk.kind]);
+ goto Bad;
+ }
+ /* fallthrough */
+ case Acomma:
+ advance(p, lx);
+ n++;
+ goto Field;
+
+ case Asemi:
+ tk = advance(p, lx);
+ continue;
+
+ default:
+ errorat(tk.pos, "unrecognized token %s in struct field declaration", tokens[tk.kind]);
+ goto Bad;
+ }
+ }
+ copyarray(f, fs, n);
+ copyarray(x, cx, n);
+ return (kind == Tstruct) ? structtype(n, f, x) : uniontype(n, f, x);
+Bad:
+ errorat(tk.pos, "failed to parse aggregate declaration");
+ return 0;
+}
+
+static
+error
+spec(Parser *p, Lexer *lx, uint64 *spec)
+{
+ Token t;
+ int n, i;
+ Sym *typ;
+ string name;
+ uint32 tag;
+ uint64 s, sm;
+ static uint32 (*aggrfunc[2])(Parser *, Lexer *, string , int) = {aggregate, enumerate};
+
+ s = 0;
+ while (t = peek(p, 0), t.kind >= Aident) {
+ /* typename */
+ if (t.kind == Aident) {
+ typ = lookupobj(p, t.val.s);
+ if (!typ || (typ && typ->kind != Stype))
+ break;
+
+ sm = typ->type;
+ s |= (sm << 32 | Tname);
+ advance(p, lx);
+ continue;
+ }
+
+ /* keyword */
+ switch (n = t.val.i) {
+ case Kauto: case Kregister: case Kstatic: case Kextern: case Ktypedef: case Ktls:
+ if (s & MaskMem) {
+ errorat(lx->pos, "multiple storage class specifiers: second was %s", keywords[n]);
+ goto Bad;
+ }
+ break;
+
+ case Kinline: case Knoret:
+ if (s & Bit(n))
+ warnat(lx->pos, "duplicate %s function specifier", keywords[n]);
+ break;
+
+ case Kconst: case Kvolatile:
+ if (s & Bit(n))
+ warnat(lx->pos, "duplicate %s specifier found in declaration", keywords[n]);
+ break;
+
+ case Ksigned: case Kunsigned:
+ if (s & MaskSgn) {
+ if (s & Bit(n)) {
+ warnat(lx->pos, "duplicated storage class specifier: second was %s", keywords[n]);
+ break;
+ }
+ errorat(lx->pos, "multiple storage class specifiers");
+ goto Bad;
+ }
+ break;
+
+ case Kshort:
+ if (s & Tshort) {
+ warnat(lx->pos, "duplicated short specifier");
+ break;
+ }
+ break;
+
+ case Klong:
+ if ((s >> Klong) & 2) {
+ errorat(lx->pos, "cannot chain three or more long specifiers");
+ goto Bad;
+ }
+ s += Bit(n);
+ t = advance(p, lx);
+ continue;
+
+ case Kvoid: case Kchar: case Kint: case Kfloat: case Kdouble:
+ if (s & MaskTyp) {
+ errorat(lx->pos, "more than one base type specified");
+ goto Bad;
+ }
+ break;
+
+ case Kstruct: case Kunion:
+ i = 0;
+ goto Aggr;
+ case Kenum:
+ i = 1;
+ Aggr:
+ if (s & (Tstruct | Tunion | Tenum)) {
+ errorat(lx->pos, "more than one aggregate/enum type specified");
+ goto Bad;
+ }
+ t = advance(p, lx);
+ if (t.kind != Aident && t.kind != Albrace) {
+ errorat(t.pos, "enum specifier missing valid declaration");
+ goto Bad;
+ }
+
+ /* NOTE: This offset is needed to correctly obtain Tstruct */
+ n++;
+ name = nil;
+ tag = 0;
+ if (t.kind == Aident) {
+ name = t.val.s;
+ t = advance(p, lx);
+ }
+ if (t.kind == Albrace) {
+ /* TODO: we need check if the name exists. */
+ t = advance(p, lx);
+ /* NOTE: This depends on the enum order. KEEP IN SYNC */
+ tag = aggrfunc[i](p, lx, name, Bit(n));
+ if (t = peek(p, 0), nomatch(t, Arbrace)) {
+ errorat(t.pos, "invalid token %s in aggregate/enum declaration", tokens[t.kind]);
+ goto Bad;
+ }
+ /* high bits encode the type index */
+ s |= (uint64)tag << 32;
+ }
+ /* TODO: if name does not exist, enter in an incomplete type! */
+ if (name)
+ declaretag(p, tag, name);
+
+ break;
+
+ default:
+ errorat(t.pos, "invalid keyword '%s' found in declaration specifier", keywords[n]);
+ }
+
+ s |= Bit(n);
+ advance(p, lx);
+ }
+
+ *spec = s;
+ return 0;
+
+Bad:
+ /* TODO: serialize bitflags to string for nice error message */
+ errorat(lx->pos, "ignoring specifier");
+ *spec = Sbad;
+ return 1;
+}
+
+/*
+ * name declaration
+ * see dtor for valid values of ab
+ */
+static
+error
+name(Parser *p, Lexer *lx, Name **nmp, int ab)
+{
+ Token t;
+ int n, k;
+ uint64 s;
+ Sym *sym;
+ Name *nm, *tmp;
+
+ /* max args = 100 */
+ struct Field args[100];
+
+ nm = *nmp;
+ t = peek(p, 0);
+ switch (k = t.kind) {
+ case Aident:
+ if (ab == 1) {
+ errorat(t.pos, "identifier not allowed in abstract declarator");
+ goto Bad;
+ }
+ nm->kind = Nident;
+ nm->ident = t.val.s;
+ break;
+
+ case Alparen:
+ advance(p, lx);
+ nm->kind = Nparen;
+ nm->paren = getdtor(p);
+ if (dtor(p, lx, nm->paren, ab)) {
+ putdtor(p, nm->paren);
+ nm->paren = nil;
+ errorat(lx->pos, "invalid declarator in parenthesis");
+ goto Bad;
+ }
+
+ t = peek(p, 0);
+ if (nomatch(t, Arparen)) {
+ putdtor(p, nm->paren);
+ nm->paren = nil;
+ errorat(lx->pos, "missing closing paren in declarator");
+ goto Bad;
+ }
+ break;
+
+ case Albrakt:
+ if (ab)
+ goto Sfx;
+ errorat(lx->pos, "missing identifier in non-abstract declarator");
+ /* fallthrough */
+ default:
+ if (ab)
+ goto Sfx;
+ errorat(lx->pos, "invalid token '%s' in name declaration", tokens[k]);
+ goto Bad;
+ }
+
+ t = advance(p, lx);
+Sfx:
+ for (;;) {
+ switch (k = t.kind) {
+ case Albrakt:
+ tmp = getname(p);
+ tmp->kind = Nindex;
+ tmp->sfx.name = nm;
+
+ nm = tmp, tmp = nil;
+
+ t = advance(p, lx);
+ if (t.kind == Arbrakt) {
+ nm->sfx.idx.q = 0;
+ Iend:
+ nm->sfx.idx.x = nil;
+ t = advance(p, lx);
+ break;
+ }
+ if (t.kind == Astar) {
+ nm->sfx.idx.q = -1;
+ IStar:
+ nm->sfx.idx.x = nil;
+ t = advance(p, lx);
+ if (t.kind != Arbrakt) {
+ errorat(t.pos, "invalid '*' syntax in index expression");
+ goto Bad;
+ }
+ t = advance(p, lx);
+ break;
+ }
+
+ if (spec(p, lx, &s)) {
+ errorat(lx->pos, "invalid type qualifier list in index expression");
+ goto Bad;
+ }
+
+ nm->sfx.idx.q = (uint32)s;
+ t = peek(p, 0);
+
+ if (t.kind == Astar)
+ goto IStar;
+
+ if (t.kind == Arbrakt)
+ goto Iend;
+
+ nm->sfx.idx.x = expr(p, lx);
+
+ t = peek(p, 0);
+ if (nomatch(t, Arbrakt)) {
+ errorat(t.pos, "unterminated index expression");
+ goto Bad;
+ }
+
+ t = advance(p, lx);
+ continue;
+
+ case Alparen:
+ tmp = getname(p);
+ tmp->kind = Ncall;
+ tmp->sfx.name = nm;
+
+ nm = tmp, tmp = nil;
+
+ t = advance(p, lx);
+ nm->sfx.call.n = 0;
+ switch (t.kind) {
+ case Arparen:
+ nm->sfx.call.arg = nil;
+ break;
+
+ case Aident:
+ sym = lookupobj(p, t.val.s);
+ if (!sym || (sym && sym->kind != Stype)) {
+ while (t.kind == Aident) {
+ if (nm->sfx.call.n >= arrlen(args))
+ panicf("ident stack overflow");
+ args[nm->sfx.call.n++] = (struct Field) {
+ .qual = 0,
+ .type = 0,
+ .name = t.val.s,
+ };
+ t = advance(p, lx);
+ }
+ if (nomatch(t, Arparen)) {
+ errorat(t.pos, "token '%s' found in function parameter identifier list");
+ goto Bad;
+ }
+ copyarray(nm->sfx.call.arg, args, nm->sfx.call.n);
+ break;
+ }
+ goto ParamLoop;
+
+ case Akeywd:
+ if (t.val.i < Kconst || t.val.i > Kenum) {
+ errorat(t.pos, "invalid keyword %s inside function signature");
+ goto Bad;
+ }
+
+ ParamLoop:
+ if (nm->sfx.call.n >= arrlen(args)-1)
+ panicf("out of argument buffer");
+
+ args[nm->sfx.call.n].type = basetype(p, lx, &s);
+ if (!args[nm->sfx.call.n].type) {
+ errorat(lx->pos, "could not parse base type in function call");
+ goto Bad;
+ }
+
+ args[nm->sfx.call.n].qual = (uint32)s & ~(MaskTyp|MaskInt|MaskFlt);
+ args[nm->sfx.call.n].name = namedecl(p, lx, &args[nm->sfx.call.n].type, 2);
+
+ nm->sfx.call.n++;
+ if ((t = peek(p, 0)).kind == Acomma) {
+ advance(p, lx);
+ goto ParamLoop;
+ }
+
+ if (t.kind == Aellip) {
+ nm->sfx.call.dots = 1;
+ t = advance(p, lx);
+ }
+
+ if (nomatch(t, Arparen)) {
+ errorat(t.pos, "token '%s' found in function parameter list");
+ goto Bad;
+ }
+ copyarray(nm->sfx.call.arg, args, nm->sfx.call.n);
+ break;
+
+ default:
+ errorat(t.pos, "invalid token %s inside function call signature", tokens[t.kind]);
+ goto Bad;
+ }
+
+ t = advance(p, lx);
+ continue;
+
+ default:
+ break;
+ }
+ break;
+ }
+
+ *nmp = nm;
+ return 0;
+Bad:
+ return 1;
+}
+
+/* pointer kind is partitioned into 8x6 regions
+ * ab => abstract
+ * @ 0: must have identifier
+ * @ 1: must not have identifier
+ * @ 2: don't care
+ * else: undefined
+ */
+static
+error
+dtor(Parser *p, Lexer *lx, Dtor *d, int ab)
+{
+ int n, k;
+ error err;
+ Token t;
+ Dtor *link;
+ Ptr *ptr, *x;
+
+ err = 1;
+
+ ptr = &d->ptr;
+ ptr->kind = 0;
+ ptr->link = nil;
+
+ t = peek(p, 0);
+ if (t.kind != Astar) {
+ if (ab || t.kind == Aident || t.kind == Arparen)
+ goto Name;
+ goto Bad;
+ }
+ n = 0;
+Ptr:
+ ptr->kind |= Bit(n);
+ advance(p, lx);
+Key:
+ t = peek(p, 0);
+ switch (k = t.kind) {
+ case Akeywd:
+ if (Kconst <= t.val.i && t.val.i <= Katomic)
+ ptr->kind |= Bit(6*n + (t.val.i - Kconst + 1));
+ else {
+ errorat(lx->pos, "invalid keyword '%s' modifies pointer", keywords[t.val.i]);
+ goto Bad;
+ }
+ advance(p, lx);
+ goto Key;
+
+ case Astar:
+ if (++n >= 8) {
+ x = getptr(p);
+ x->kind = 0;
+ x->link = nil;
+ ptr->link = x;
+ ptr = x;
+ n = 0;
+ }
+ goto Ptr;
+
+ case Aident:
+ case Alparen:
+ goto Name;
+
+ default:
+ if (ab)
+ goto Name;
+ errorat(lx->pos, "invalid token '%s' modifies pointer specification", tokens[t.kind]);
+ goto Bad;
+ }
+Name:
+ return name(p, lx, &d->name, ab);
+Bad:
+ return err;
+}
+
+static
+Decl *
+decl(Parser *p, Lexer *lx)
+{
+ uint64 s;
+ Token t;
+ Decl *d;
+ Expr *x;
+ string name;
+ struct Decls *ds;
+ uint32 base, type;
+
+ alloc(d);
+
+ d->kind = 0;
+ d->pos.beg = lx->pos;
+
+ base = basetype(p, lx, &s);
+ if (!base) {
+ errorat(lx->pos, "could not parse type declaration");
+ goto Bad;
+ }
+
+ x = nil;
+ d->spec = (uint32)s & ~(MaskInt|MaskFlt|MaskTyp);
+ d->type = base;
+ d->name = namedecl(p, lx, &d->type, 0);
+ /* TODO: think about functions (both decls and defs) */
+ d->kind = (s & Mtype) ? Dtype : Dvar;
+
+ switch (t = peek(p, 0), t.kind) {
+ case Aeq:
+ if (s & Mtype) {
+ errorat(d->pos.beg, "initialization of type not allowed");
+ goto Bad;
+ }
+ t = advance(p, lx);
+ x = initx(p, lx);
+ d->kind = Dvar;
+ if (t.kind != Acomma) {
+ d->init = x;
+ goto Semi;
+ }
+ /* fallthrough */
+ case Acomma:
+ d->kind |= Dlist;
+ d->list.init = x;
+ /* move singleton data over */
+ name = d->name;
+ type = d->type;
+ d->list.name = name;
+ d->list.type = type;
+ ds = &d->list;
+ /* iterate until we hit end of list */
+ while (t.kind == Acomma) {
+ t = advance(p, lx);
+
+ alloc(ds->link);
+ ds = ds->link;
+ ds->type = base;
+ ds->name = namedecl(p, lx, &ds->type, 0);
+
+ t = peek(p, 0);
+ if (t.kind == Aeq) {
+ t = advance(p, lx);
+ ds->init = initx(p, lx);
+ } else
+ ds->init = nil;
+ }
+ goto Semi;
+
+ case Albrace:
+ d->kind = Dfunc;
+ alloc(d->body);
+
+ if (!attop(p)) {
+ errorat(lx->pos, "nested function declarations are illegal");
+ goto Bad;
+ }
+
+ if (C.type.info[d->type].kind != Tfunc) {
+ errorat(lx->pos, "attempted to define function body for non function type");
+ goto Bad;
+ }
+
+ openscope(p);
+ if (blkstmt(p, lx, &d->body)) {
+ errorat(lx->pos, "failed to parse function body");
+ goto Bad;
+ }
+ closescope(p);
+ break;
+
+ default:
+ Semi:
+ if (nomatch(t, Asemi)) {
+ errorat(t.pos, "no semicolon after declaration");
+ goto Bad;
+ }
+ t = advance(p, lx);
+ }
+
+ d->pos.end = lx->pos;
+ declareobj(p, d);
+ return d;
+Bad:
+ errorat(lx->pos, "failed to parse top level declaration");
+ return nil;
+}
+
+// -----------------------------------------------------------------------
+// top level api
+
+void
+setup(Parser *p, Lexer *lx)
+{
+ advance(p,lx);
+ advance(p,lx);
+
+ /* define all builtin typedefs */
+ declareobj(p, &C.builtin.vargs);
+}
+
+error
+parse(Parser *p, Lexer *lx)
+{
+ Token tok;
+
+ setup(p, lx);
+ while ((tok = peek(p, 0)), tok.kind > Aeof) {
+ if (p->ast.len >= p->ast.cap) {
+ p->ast.cap += 20;
+ p->ast.decls = realloc(p->ast.decls, p->ast.cap*sizeof(*p->ast.decls));
+ }
+ p->ast.decls[p->ast.len++] = decl(p, lx);
+ }
+
+ return 0;
+}