1382 lines
32 KiB
C
1382 lines
32 KiB
C
/*
|
|
* parse.c — recursive descent + Pratt expression parser.
|
|
*
|
|
* Plan 9 style: hand-rolled, no yacc, errors are reported via errorf
|
|
* but we keep going where possible so the user gets multiple
|
|
* diagnostics from one run.
|
|
*
|
|
* Module navigation and field access both use '.'. We disambiguate
|
|
* structurally: the parser eats `IDENT('.'IDENT)*` greedily for type
|
|
* names and `use` paths; in expressions, postfix `.IDENT` becomes
|
|
* N_DOT and the checker decides if it's a module-qualified ref or a
|
|
* struct field.
|
|
*/
|
|
#include "ww.h"
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
void
|
|
parserinit(Parser *p, Arena *a, Lex *l)
|
|
{
|
|
memset(p, 0, sizeof *p);
|
|
p->l = l;
|
|
p->a = a;
|
|
p->cur = lexnext(l);
|
|
}
|
|
|
|
static void
|
|
advance(Parser *p)
|
|
{
|
|
if (p->hasla) {
|
|
p->cur = p->la;
|
|
p->hasla = 0;
|
|
} else {
|
|
p->cur = lexnext(p->l);
|
|
}
|
|
}
|
|
|
|
static Tok
|
|
peek(Parser *p)
|
|
{
|
|
if (!p->hasla) {
|
|
p->la = lexnext(p->l);
|
|
p->hasla = 1;
|
|
}
|
|
return p->la;
|
|
}
|
|
|
|
static int
|
|
accept(Parser *p, Tkind k)
|
|
{
|
|
if (p->cur.kind == k) {
|
|
advance(p);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
expect(Parser *p, Tkind k)
|
|
{
|
|
if (p->cur.kind == k) {
|
|
advance(p);
|
|
return 1;
|
|
}
|
|
errorf(p->cur.pos, "expected %s, got %s", tokname(k), tokname(p->cur.kind));
|
|
p->errs++;
|
|
return 0;
|
|
}
|
|
|
|
static const char *
|
|
expectident(Parser *p)
|
|
{
|
|
if (p->cur.kind != TK_IDENT) {
|
|
errorf(p->cur.pos, "expected identifier, got %s",
|
|
tokname(p->cur.kind));
|
|
p->errs++;
|
|
return "<err>";
|
|
}
|
|
const char *s = p->cur.text;
|
|
advance(p);
|
|
return s;
|
|
}
|
|
|
|
/* Like expectident but also accepts a bare `_` discard marker. The
|
|
* returned string is the empty string "" so the checker skips
|
|
* scope_define. Callers that care can detect this with `s[0] == '\0'`. */
|
|
static const char *
|
|
expectbindname(Parser *p)
|
|
{
|
|
if (p->cur.kind == TK_UNDER) {
|
|
advance(p);
|
|
return "";
|
|
}
|
|
return expectident(p);
|
|
}
|
|
|
|
static Node *parseexpr(Parser *p);
|
|
static Node *parseunary(Parser *p);
|
|
static Node *parsetype(Parser *p);
|
|
static Node *parseblock(Parser *p);
|
|
static Node *parsestmt(Parser *p);
|
|
|
|
/* ------- type expressions ------------------------------------------ */
|
|
|
|
static Node *
|
|
parseparams(Parser *p)
|
|
{
|
|
Node *head = NULL, *tail = NULL;
|
|
if (p->cur.kind == TK_RPAREN)
|
|
return NULL;
|
|
for (;;) {
|
|
Pos pp = p->cur.pos;
|
|
if (p->cur.kind == TK_ELLIPSIS) {
|
|
advance(p);
|
|
Node *n = newnode(p->a, N_PARAM, pp);
|
|
n->str = "...";
|
|
n->strlen = 3;
|
|
if (head == NULL) head = n;
|
|
else tail->next = n;
|
|
tail = n;
|
|
break;
|
|
}
|
|
Node *n = newnode(p->a, N_PARAM, pp);
|
|
/* IDENT ':' type OR `_' ':' type OR type-only.
|
|
* Disambiguate: if current is IDENT or '_' and next is ':',
|
|
* it's a named param. Otherwise treat as anonymous. */
|
|
int named = (p->cur.kind == TK_IDENT || p->cur.kind == TK_UNDER)
|
|
&& peek(p).kind == TK_COLON;
|
|
if (named) {
|
|
n->str = expectbindname(p);
|
|
expect(p, TK_COLON);
|
|
n->lhs = parsetype(p);
|
|
} else {
|
|
n->str = "";
|
|
n->strlen = 0;
|
|
n->lhs = parsetype(p);
|
|
}
|
|
/* Hare-style variadic: `name: T...`. The trailing `...`
|
|
* after the type promotes the param's type to []T at type-
|
|
* resolution time; call sites gather N args or forward a
|
|
* `xs...` spread. Marked on n->op so check.c and selfhost
|
|
* recognise it without needing a new Node kind. */
|
|
if (accept(p, TK_ELLIPSIS))
|
|
n->op = TK_ELLIPSIS;
|
|
if (head == NULL) head = n;
|
|
else tail->next = n;
|
|
tail = n;
|
|
if (n->op == TK_ELLIPSIS)
|
|
break; /* Hare-style variadic must be the last param */
|
|
if (!accept(p, TK_COMMA))
|
|
break;
|
|
if (p->cur.kind == TK_RPAREN) /* trailing comma */
|
|
break;
|
|
}
|
|
return head;
|
|
}
|
|
|
|
static Node *
|
|
parsetype(Parser *p)
|
|
{
|
|
Pos pp = p->cur.pos;
|
|
switch (p->cur.kind) {
|
|
case TK_NOT: {
|
|
/* `!T` — error-flagged type. The flag propagates through
|
|
* NAMED aliases and lives on the underlying Type, not on
|
|
* a wrapper. The AST keeps an N_TBANG wrapper so prints
|
|
* and selfhost can recognise the marker. */
|
|
advance(p);
|
|
Node *n = newnode(p->a, N_TBANG, pp);
|
|
n->lhs = parsetype(p);
|
|
return n;
|
|
}
|
|
case TK_STAR: {
|
|
advance(p);
|
|
Node *n = newnode(p->a, N_TPTR, pp);
|
|
n->lhs = parsetype(p);
|
|
return n;
|
|
}
|
|
case TK_LBRACK: {
|
|
advance(p);
|
|
if (accept(p, TK_RBRACK)) {
|
|
Node *n = newnode(p->a, N_TSLICE, pp);
|
|
n->lhs = parsetype(p);
|
|
return n;
|
|
}
|
|
Node *n = newnode(p->a, N_TARRAY, pp);
|
|
/* `[_]T` — length inferred from the initialiser. Marked by
|
|
* leaving n->rhs == NULL; check.c fills in the length from
|
|
* the array literal's element count. */
|
|
if (!accept(p, TK_UNDER))
|
|
n->rhs = parseexpr(p);
|
|
expect(p, TK_RBRACK);
|
|
n->lhs = parsetype(p);
|
|
return n;
|
|
}
|
|
case TK_FN: {
|
|
advance(p);
|
|
expect(p, TK_LPAREN);
|
|
Node *n = newnode(p->a, N_TFN, pp);
|
|
n->list = parseparams(p);
|
|
expect(p, TK_RPAREN);
|
|
n->lhs = parsetype(p);
|
|
return n;
|
|
}
|
|
case TK_STRUCT: {
|
|
advance(p);
|
|
expect(p, TK_LBRACE);
|
|
Node *n = newnode(p->a, N_TSTRUCT, pp);
|
|
Node *head = NULL, *tail = NULL;
|
|
while (p->cur.kind != TK_RBRACE && p->cur.kind != TK_EOF) {
|
|
Pos fp = p->cur.pos;
|
|
Node *f = newnode(p->a, N_TFIELD, fp);
|
|
/* Three member forms:
|
|
* name: type — regular field
|
|
* struct { ... } — anonymous embedded struct
|
|
* Identifier — bare-name embedded type
|
|
* Embeds carry f->str == NULL and the type in f->lhs. */
|
|
if (p->cur.kind == TK_STRUCT) {
|
|
f->lhs = parsetype(p);
|
|
} else if (p->cur.kind == TK_IDENT
|
|
&& peek(p).kind != TK_COLON) {
|
|
f->lhs = parsetype(p);
|
|
} else {
|
|
f->str = expectident(p);
|
|
expect(p, TK_COLON);
|
|
f->lhs = parsetype(p);
|
|
}
|
|
if (head == NULL) head = f;
|
|
else tail->next = f;
|
|
tail = f;
|
|
if (!accept(p, TK_COMMA))
|
|
break;
|
|
}
|
|
expect(p, TK_RBRACE);
|
|
n->list = head;
|
|
return n;
|
|
}
|
|
case TK_CHAN: {
|
|
advance(p);
|
|
Node *n = newnode(p->a, N_TCHAN, pp);
|
|
n->lhs = parsetype(p);
|
|
return n;
|
|
}
|
|
case TK_ENUM: {
|
|
/* `enum [storage] { NAME [= expr] [, ...] }`
|
|
* storage defaults to i32 (lhs == NULL).
|
|
* Each member is N_TENUMMEMBER with str=name and
|
|
* lhs=value-expr (NULL means auto-increment from previous). */
|
|
advance(p);
|
|
Node *n = newnode(p->a, N_TENUM, pp);
|
|
if (p->cur.kind != TK_LBRACE)
|
|
n->lhs = parsetype(p);
|
|
expect(p, TK_LBRACE);
|
|
Node *head = NULL, *tail = NULL;
|
|
while (p->cur.kind != TK_RBRACE && p->cur.kind != TK_EOF) {
|
|
Pos mp = p->cur.pos;
|
|
Node *m = newnode(p->a, N_TENUMMEMBER, mp);
|
|
m->str = expectident(p);
|
|
m->strlen = strlen(m->str);
|
|
if (accept(p, TK_ASSIGN))
|
|
m->lhs = parseexpr(p);
|
|
if (head == NULL) head = m;
|
|
else tail->next = m;
|
|
tail = m;
|
|
if (!accept(p, TK_COMMA))
|
|
break;
|
|
}
|
|
expect(p, TK_RBRACE);
|
|
n->list = head;
|
|
return n;
|
|
}
|
|
case TK_VOID: {
|
|
/* `void` keyword in type-expr context. Synthesise an
|
|
* N_TNAME so type-resolution treats it like any other
|
|
* primitive name. */
|
|
Node *n = newnode(p->a, N_TNAME, pp);
|
|
n->str = "void";
|
|
n->strlen = 4;
|
|
advance(p);
|
|
return n;
|
|
}
|
|
case TK_IDENT: {
|
|
Node *n = newnode(p->a, N_TNAME, pp);
|
|
n->str = p->cur.text;
|
|
n->strlen = p->cur.tlen;
|
|
advance(p);
|
|
/* dotted path: pkg.Type — collapse into a single TNAME */
|
|
while (accept(p, TK_DOT)) {
|
|
if (p->cur.kind != TK_IDENT) {
|
|
errorf(p->cur.pos, "expected identifier after '.'");
|
|
p->errs++;
|
|
break;
|
|
}
|
|
n->str = aprintf(p->a, "%s.%s", n->str, p->cur.text);
|
|
n->strlen = strlen(n->str);
|
|
advance(p);
|
|
}
|
|
return n;
|
|
}
|
|
case TK_LPAREN: {
|
|
/* Three forms inside the parens:
|
|
* (T) — parenthesised single type
|
|
* (T, T2, ...) — tuple type
|
|
* (T | T2 | ...) — tagged-union type (Hare-style sum)
|
|
*
|
|
* Each tagged variant may be prefixed with `...` to mark
|
|
* a spread: when the variant resolves to another tagged
|
|
* union its variants are flattened into the enclosing
|
|
* union. We tag the spread on Node.op = TK_ELLIPSIS so
|
|
* resolve_type can distinguish intent (today the checker
|
|
* flattens any nested tagged unconditionally, matching
|
|
* Hare's structural-equivalence rule, but the marker is
|
|
* preserved for future nominal handling). */
|
|
advance(p);
|
|
int first_spread = accept(p, TK_ELLIPSIS);
|
|
Node *first = parsetype(p);
|
|
if (first_spread) first->op = TK_ELLIPSIS;
|
|
if (accept(p, TK_PIPE)) {
|
|
Node *t = newnode(p->a, N_TTAGGED, pp);
|
|
Node *head = first, *tail = first;
|
|
for (;;) {
|
|
int spread = accept(p, TK_ELLIPSIS);
|
|
Node *e = parsetype(p);
|
|
if (spread) e->op = TK_ELLIPSIS;
|
|
tail->next = e;
|
|
tail = e;
|
|
if (!accept(p, TK_PIPE)) break;
|
|
}
|
|
expect(p, TK_RPAREN);
|
|
t->list = head;
|
|
return t;
|
|
}
|
|
if (first_spread) {
|
|
errorf(pp, "spread '...' only valid before tagged-union variants");
|
|
p->errs++;
|
|
}
|
|
if (!accept(p, TK_COMMA)) {
|
|
expect(p, TK_RPAREN);
|
|
return first;
|
|
}
|
|
Node *t = newnode(p->a, N_TTUPLE, pp);
|
|
Node *head = first, *tail = first;
|
|
for (;;) {
|
|
Node *e = parsetype(p);
|
|
tail->next = e;
|
|
tail = e;
|
|
if (!accept(p, TK_COMMA)) break;
|
|
if (p->cur.kind == TK_RPAREN) break;
|
|
}
|
|
expect(p, TK_RPAREN);
|
|
t->list = head;
|
|
return t;
|
|
}
|
|
default:
|
|
errorf(pp, "expected type, got %s", tokname(p->cur.kind));
|
|
p->errs++;
|
|
advance(p);
|
|
return newnode(p->a, N_TNAME, pp);
|
|
}
|
|
}
|
|
|
|
/* ------- expressions (Pratt) ---------------------------------------- */
|
|
|
|
/* binary precedence; 0 = not a binary op */
|
|
static int
|
|
bprec(Tkind k)
|
|
{
|
|
switch (k) {
|
|
case TK_OR: return 1;
|
|
case TK_AND: return 2;
|
|
case TK_EQ:
|
|
case TK_NEQ: return 3;
|
|
case TK_LT:
|
|
case TK_LE:
|
|
case TK_GT:
|
|
case TK_GE: return 4;
|
|
case TK_PIPE: return 5;
|
|
case TK_CARET: return 6;
|
|
case TK_AMP: return 7;
|
|
case TK_LSHIFT:
|
|
case TK_RSHIFT: return 8;
|
|
case TK_PLUS:
|
|
case TK_MINUS: return 9;
|
|
case TK_STAR:
|
|
case TK_SLASH:
|
|
case TK_PERCENT: return 10;
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
static int
|
|
isassignop(Tkind k)
|
|
{
|
|
switch (k) {
|
|
case TK_ASSIGN: case TK_PLUSEQ: case TK_MINUSEQ:
|
|
case TK_STAREQ: case TK_SLASHEQ: case TK_PERCENTEQ:
|
|
case TK_AMPEQ: case TK_PIPEEQ: case TK_CARETEQ:
|
|
case TK_LSHIFTEQ: case TK_RSHIFTEQ:
|
|
return 1;
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
static Node *
|
|
parsearglist(Parser *p, Tkind close)
|
|
{
|
|
Node *head = NULL, *tail = NULL;
|
|
if (p->cur.kind == close)
|
|
return NULL;
|
|
for (;;) {
|
|
Node *e = parseexpr(p);
|
|
/* Hare-style spread: `expr...` in an arg slot becomes a
|
|
* marker node that the callee/builtin can iterate. */
|
|
if (accept(p, TK_ELLIPSIS)) {
|
|
Node *sp = newnode(p->a, N_SPREAD, e->pos);
|
|
sp->lhs = e;
|
|
e = sp;
|
|
}
|
|
if (head == NULL) head = e;
|
|
else tail->next = e;
|
|
tail = e;
|
|
if (!accept(p, TK_COMMA))
|
|
break;
|
|
if (p->cur.kind == close)
|
|
break;
|
|
}
|
|
return head;
|
|
}
|
|
|
|
static Node *
|
|
parsestructlit(Parser *p, Node *typeref)
|
|
{
|
|
Pos pp = p->cur.pos;
|
|
expect(p, TK_LBRACE);
|
|
Node *n = newnode(p->a, N_STRUCTLIT, pp);
|
|
n->lhs = typeref;
|
|
Node *head = NULL, *tail = NULL;
|
|
while (p->cur.kind != TK_RBRACE && p->cur.kind != TK_EOF) {
|
|
Pos fp = p->cur.pos;
|
|
/* Trailing `...` after the last comma (or as the only entry)
|
|
* means "zero-init all unmentioned fields". Marked on the
|
|
* literal node via op = TK_ELLIPSIS; cgen consumes it. */
|
|
if (p->cur.kind == TK_ELLIPSIS) {
|
|
advance(p);
|
|
n->op = TK_ELLIPSIS;
|
|
break;
|
|
}
|
|
const char *name = expectident(p);
|
|
expect(p, TK_ASSIGN);
|
|
Node *val = parseexpr(p);
|
|
Node *f = newnode(p->a, N_FIELD, fp);
|
|
f->str = name;
|
|
f->lhs = val;
|
|
if (head == NULL) head = f;
|
|
else tail->next = f;
|
|
tail = f;
|
|
if (!accept(p, TK_COMMA))
|
|
break;
|
|
}
|
|
expect(p, TK_RBRACE);
|
|
n->list = head;
|
|
return n;
|
|
}
|
|
|
|
static Node *
|
|
parsearrlit(Parser *p)
|
|
{
|
|
Pos pp = p->cur.pos;
|
|
expect(p, TK_LBRACK);
|
|
Node *n = newnode(p->a, N_ARRLIT, pp);
|
|
Node *head = NULL, *tail = NULL;
|
|
while (p->cur.kind != TK_RBRACK && p->cur.kind != TK_EOF) {
|
|
Node *e = parseexpr(p);
|
|
if (head == NULL) head = e;
|
|
else tail->next = e;
|
|
tail = e;
|
|
if (accept(p, TK_ELLIPSIS)) {
|
|
/* repeat suffix marker; encode as a flag node */
|
|
Node *rep = newnode(p->a, N_FIELD, p->cur.pos);
|
|
rep->str = "...";
|
|
rep->strlen = 3;
|
|
tail->next = rep;
|
|
tail = rep;
|
|
break;
|
|
}
|
|
if (!accept(p, TK_COMMA))
|
|
break;
|
|
}
|
|
expect(p, TK_RBRACK);
|
|
n->list = head;
|
|
return n;
|
|
}
|
|
|
|
static Node *
|
|
parseprimary(Parser *p)
|
|
{
|
|
Pos pp = p->cur.pos;
|
|
Tok t = p->cur;
|
|
switch (t.kind) {
|
|
case TK_INT: {
|
|
Node *n = newnode(p->a, N_INTLIT, pp);
|
|
n->uval = t.v.uval;
|
|
n->str = t.text;
|
|
n->tsuffix = t.tsuffix;
|
|
advance(p);
|
|
return n;
|
|
}
|
|
case TK_FLOAT: {
|
|
Node *n = newnode(p->a, N_FLOATLIT, pp);
|
|
n->fval = t.v.fval;
|
|
n->str = t.text;
|
|
n->tsuffix = t.tsuffix;
|
|
advance(p);
|
|
return n;
|
|
}
|
|
case TK_STR: {
|
|
Node *n = newnode(p->a, N_STRLIT, pp);
|
|
n->str = t.text;
|
|
n->strlen = t.tlen;
|
|
advance(p);
|
|
return n;
|
|
}
|
|
case TK_RUNE: {
|
|
Node *n = newnode(p->a, N_RUNELIT, pp);
|
|
n->uval = t.v.uval;
|
|
advance(p);
|
|
return n;
|
|
}
|
|
case TK_TRUE: advance(p); return newnode(p->a, N_TRUE, pp);
|
|
case TK_FALSE: advance(p); return newnode(p->a, N_FALSE, pp);
|
|
case TK_NIL: advance(p); return newnode(p->a, N_NIL, pp);
|
|
case TK_VOID: advance(p); return newnode(p->a, N_VOIDLIT, pp);
|
|
case TK_UNDER: {
|
|
/* Bare `_` — valid only as a discard lvalue. We yield an N_IDENT
|
|
* with empty str; the checker rejects it outside assignment
|
|
* lvalue positions. */
|
|
advance(p);
|
|
Node *n = newnode(p->a, N_IDENT, pp);
|
|
n->str = "";
|
|
n->strlen = 0;
|
|
return n;
|
|
}
|
|
case TK_LPAREN: {
|
|
advance(p);
|
|
Node *e = parseexpr(p);
|
|
/* tuple literal: (e1, e2, ...) — at least 2 elements;
|
|
* a single (e) is a parenthesised expression */
|
|
if (accept(p, TK_COMMA)) {
|
|
Node *t = newnode(p->a, N_TUPLE, pp);
|
|
t->list = e;
|
|
Node *tt = e;
|
|
for (;;) {
|
|
Node *en = parseexpr(p);
|
|
tt->next = en;
|
|
tt = en;
|
|
if (!accept(p, TK_COMMA)) break;
|
|
if (p->cur.kind == TK_RPAREN) break;
|
|
}
|
|
expect(p, TK_RPAREN);
|
|
return t;
|
|
}
|
|
expect(p, TK_RPAREN);
|
|
return e;
|
|
}
|
|
case TK_LBRACK:
|
|
return parsearrlit(p);
|
|
case TK_MATCH: {
|
|
/* match (e) {
|
|
* case let v: T => stmt;
|
|
* case T => stmt;
|
|
* case => stmt; — default
|
|
* };
|
|
*/
|
|
advance(p);
|
|
expect(p, TK_LPAREN);
|
|
Node *m = newnode(p->a, N_MATCH, pp);
|
|
m->lhs = parseexpr(p);
|
|
expect(p, TK_RPAREN);
|
|
expect(p, TK_LBRACE);
|
|
Node *head = NULL, *tail = NULL;
|
|
while (p->cur.kind == TK_CASE) {
|
|
Pos cp = p->cur.pos;
|
|
advance(p);
|
|
Node *cs = newnode(p->a, N_MCASE, cp);
|
|
if (p->cur.kind == TK_LET) {
|
|
/* `case let v: T =>` — bind variant to v.
|
|
* Single-type only: multi-pattern with a
|
|
* binding would force the binding to take the
|
|
* union type, which we don't support. */
|
|
advance(p);
|
|
cs->str = expectident(p);
|
|
expect(p, TK_COLON);
|
|
cs->lhs = parsetype(p);
|
|
} else if (p->cur.kind == TK_FATARROW) {
|
|
/* `case =>` — default arm (cs->lhs left NULL) */
|
|
} else if (p->cur.kind != TK_ELSE && p->cur.kind != TK_LBRACE) {
|
|
/* `case T =>` — match variant by type.
|
|
* `case T1 | T2 | T3 =>` — match if scrutinee
|
|
* is any of the listed variants. The first type
|
|
* goes on cs->lhs (so single-pattern callers
|
|
* still work); additional types chain through
|
|
* cs->list. */
|
|
cs->lhs = parsetype(p);
|
|
Node *tail = NULL;
|
|
while (accept(p, TK_PIPE)) {
|
|
Node *more = parsetype(p);
|
|
if (cs->list == NULL) cs->list = more;
|
|
else tail->next = more;
|
|
tail = more;
|
|
}
|
|
}
|
|
expect(p, TK_FATARROW);
|
|
cs->body = parsestmt(p);
|
|
if (head == NULL) head = cs;
|
|
else tail->next = cs;
|
|
tail = cs;
|
|
}
|
|
expect(p, TK_RBRACE);
|
|
m->list = head;
|
|
return m;
|
|
}
|
|
case TK_IDENT: {
|
|
Node *n = newnode(p->a, N_IDENT, pp);
|
|
n->str = t.text;
|
|
n->strlen = t.tlen;
|
|
advance(p);
|
|
/* dotted ident chain folded into one IDENT for type-ish refs */
|
|
while (p->cur.kind == TK_DOT && peek(p).kind == TK_IDENT) {
|
|
advance(p);
|
|
n = (Node*)n; /* keep stable */
|
|
Node *mr = newnode(p->a, N_DOT, pp);
|
|
mr->lhs = n;
|
|
mr->str = p->cur.text;
|
|
mr->strlen = p->cur.tlen;
|
|
advance(p);
|
|
n = mr;
|
|
}
|
|
/* struct literal: ident '{' ... '}' (only if ident-shaped) */
|
|
if (p->cur.kind == TK_LBRACE)
|
|
return parsestructlit(p, n);
|
|
return n;
|
|
}
|
|
default:
|
|
errorf(pp, "unexpected token in expression: %s",
|
|
tokname(t.kind));
|
|
p->errs++;
|
|
advance(p);
|
|
return newnode(p->a, N_NONE, pp);
|
|
}
|
|
}
|
|
|
|
static Node *
|
|
parsepostfix(Parser *p, Node *lhs)
|
|
{
|
|
for (;;) {
|
|
Pos pp = p->cur.pos;
|
|
switch (p->cur.kind) {
|
|
case TK_LPAREN: {
|
|
advance(p);
|
|
Node *n = newnode(p->a, N_CALL, pp);
|
|
n->lhs = lhs;
|
|
/* size(T)/align(T): the single arg is a type expression,
|
|
* not a regular expression — types like []u8 cannot parse
|
|
* as expressions. Special-case at the parser. */
|
|
if (lhs->kind == N_IDENT && lhs->str &&
|
|
(strcmp(lhs->str, "size") == 0 ||
|
|
strcmp(lhs->str, "align") == 0)) {
|
|
n->list = parsetype(p);
|
|
} else {
|
|
n->list = parsearglist(p, TK_RPAREN);
|
|
}
|
|
expect(p, TK_RPAREN);
|
|
lhs = n;
|
|
break;
|
|
}
|
|
case TK_LBRACK: {
|
|
advance(p);
|
|
/* Three forms inside the brackets:
|
|
* [hi] — slice of [0:hi] (rare)
|
|
* [lo:hi] — slice
|
|
* [expr] — index
|
|
* We disambiguate by checking for ':' after the first
|
|
* expr (suppressing cast there since ':' is the slicer). */
|
|
if (p->cur.kind == TK_COLON) {
|
|
advance(p);
|
|
Node *n = newnode(p->a, N_SLICE, pp);
|
|
n->lhs = lhs;
|
|
if (p->cur.kind != TK_RBRACK)
|
|
n->cond = parseexpr(p);
|
|
expect(p, TK_RBRACK);
|
|
lhs = n;
|
|
break;
|
|
}
|
|
int prev_nocast = p->nocast;
|
|
p->nocast = 1;
|
|
Node *e = parseexpr(p);
|
|
p->nocast = prev_nocast;
|
|
if (p->cur.kind == TK_COLON) {
|
|
advance(p);
|
|
Node *n = newnode(p->a, N_SLICE, pp);
|
|
n->lhs = lhs;
|
|
n->rhs = e;
|
|
if (p->cur.kind != TK_RBRACK)
|
|
n->cond = parseexpr(p);
|
|
expect(p, TK_RBRACK);
|
|
lhs = n;
|
|
break;
|
|
}
|
|
Node *n = newnode(p->a, N_INDEX, pp);
|
|
n->lhs = lhs;
|
|
n->rhs = e;
|
|
expect(p, TK_RBRACK);
|
|
lhs = n;
|
|
break;
|
|
}
|
|
case TK_DOT: {
|
|
advance(p);
|
|
Node *n = newnode(p->a, N_DOT, pp);
|
|
n->lhs = lhs;
|
|
if (p->cur.kind == TK_INT) {
|
|
/* Hare-style tuple field access: t.0, t.1 */
|
|
n->str = aprintf(p->a, "%llu",
|
|
(unsigned long long)p->cur.v.uval);
|
|
advance(p);
|
|
} else {
|
|
n->str = expectident(p);
|
|
}
|
|
lhs = n;
|
|
break;
|
|
}
|
|
case TK_COLON: {
|
|
/* cast: expr ':' type — suppressed inside case-selectors,
|
|
* where ':' separates the selector from the body. */
|
|
if (p->nocast)
|
|
return lhs;
|
|
advance(p);
|
|
Node *n = newnode(p->a, N_CAST, pp);
|
|
n->lhs = lhs;
|
|
n->rhs = parsetype(p);
|
|
lhs = n;
|
|
break;
|
|
}
|
|
case TK_QUESTION: {
|
|
advance(p);
|
|
Node *n = newnode(p->a, N_TRYPROP, pp);
|
|
n->lhs = lhs;
|
|
lhs = n;
|
|
break;
|
|
}
|
|
case TK_NOT: {
|
|
/* Could be unary '!' starting a new expression, but as
|
|
* a *postfix* it's the Hare error-unwrap. We're inside
|
|
* parsepostfix, so it's postfix. */
|
|
advance(p);
|
|
Node *n = newnode(p->a, N_TRYUNW, pp);
|
|
n->lhs = lhs;
|
|
lhs = n;
|
|
break;
|
|
}
|
|
case TK_AS:
|
|
case TK_IS: {
|
|
/* Hare-style:
|
|
* e as T — assert lhs is variant T of its tagged
|
|
* union; abort if not. Yields T.
|
|
* e is T — bool: does lhs currently hold variant T?
|
|
* Postfix, same level as `:` cast. */
|
|
Tkind k = p->cur.kind;
|
|
advance(p);
|
|
Node *n = newnode(p->a,
|
|
k == TK_AS ? N_TYPEASSERT : N_TYPETEST, pp);
|
|
n->lhs = lhs;
|
|
n->rhs = parsetype(p);
|
|
lhs = n;
|
|
break;
|
|
}
|
|
default:
|
|
return lhs;
|
|
}
|
|
}
|
|
}
|
|
|
|
static Node *
|
|
parseunary(Parser *p)
|
|
{
|
|
Pos pp = p->cur.pos;
|
|
switch (p->cur.kind) {
|
|
case TK_MINUS:
|
|
case TK_PLUS:
|
|
case TK_NOT:
|
|
case TK_TILDE:
|
|
case TK_STAR: /* deref */
|
|
case TK_AMP: { /* address-of */
|
|
Tkind op = p->cur.kind;
|
|
advance(p);
|
|
Node *n = newnode(p->a, N_UN, pp);
|
|
n->op = op;
|
|
n->lhs = parseunary(p);
|
|
return n;
|
|
}
|
|
case TK_LARROW: { /* chan recv */
|
|
advance(p);
|
|
Node *n = newnode(p->a, N_RECV, pp);
|
|
n->lhs = parseunary(p);
|
|
return n;
|
|
}
|
|
default:
|
|
return parsepostfix(p, parseprimary(p));
|
|
}
|
|
}
|
|
|
|
static Node *
|
|
parsebin(Parser *p, Node *lhs, int min)
|
|
{
|
|
for (;;) {
|
|
Tkind op = p->cur.kind;
|
|
int pr = bprec(op);
|
|
if (pr == 0 || pr < min)
|
|
return lhs;
|
|
Pos pp = p->cur.pos;
|
|
advance(p);
|
|
Node *rhs = parseunary(p);
|
|
while (bprec(p->cur.kind) > pr)
|
|
rhs = parsebin(p, rhs, bprec(p->cur.kind));
|
|
Node *n = newnode(p->a, N_BIN, pp);
|
|
n->op = op;
|
|
n->lhs = lhs;
|
|
n->rhs = rhs;
|
|
lhs = n;
|
|
}
|
|
}
|
|
|
|
static Node *
|
|
parseexpr_noassign(Parser *p)
|
|
{
|
|
return parsebin(p, parseunary(p), 1);
|
|
}
|
|
|
|
static Node *
|
|
parseexpr(Parser *p)
|
|
{
|
|
Node *e = parsebin(p, parseunary(p), 1);
|
|
if (isassignop(p->cur.kind)) {
|
|
Pos pp = p->cur.pos;
|
|
Tkind op = p->cur.kind;
|
|
advance(p);
|
|
Node *n = newnode(p->a, N_ASSIGN, pp);
|
|
n->op = op;
|
|
n->lhs = e;
|
|
n->rhs = parseexpr(p); /* right-assoc */
|
|
return n;
|
|
}
|
|
return e;
|
|
}
|
|
|
|
Node *
|
|
parseexpr_top(Parser *p)
|
|
{
|
|
return parseexpr(p);
|
|
}
|
|
|
|
/* ------- statements ------------------------------------------------- */
|
|
|
|
static Node *
|
|
parselet(Parser *p, int top)
|
|
{
|
|
Pos pp = p->cur.pos;
|
|
int is_const = 0;
|
|
if (p->cur.kind == TK_CONST) {
|
|
is_const = 1;
|
|
advance(p);
|
|
} else {
|
|
expect(p, TK_LET);
|
|
}
|
|
|
|
/* Hare-style tuple destructure: `let (a, b) = expr;` */
|
|
if (p->cur.kind == TK_LPAREN) {
|
|
advance(p);
|
|
Node *m = newnode(p->a, N_MLET, pp);
|
|
Node *head = NULL, *tail = NULL;
|
|
for (;;) {
|
|
Pos lpp = p->cur.pos;
|
|
Node *l = newnode(p->a, N_LET, lpp);
|
|
l->str = expectbindname(p);
|
|
if (accept(p, TK_COLON)) l->lhs = parsetype(p);
|
|
if (head == NULL) head = l;
|
|
else tail->next = l;
|
|
tail = l;
|
|
if (!accept(p, TK_COMMA)) break;
|
|
}
|
|
expect(p, TK_RPAREN);
|
|
expect(p, TK_ASSIGN);
|
|
m->rhs = parseexpr(p);
|
|
expect(p, TK_SEMI);
|
|
m->list = head;
|
|
if (is_const) {
|
|
m->op = TK_CONST;
|
|
for (Node *l = head; l; l = l->next) l->op = TK_CONST;
|
|
}
|
|
(void)top;
|
|
return m;
|
|
}
|
|
|
|
/* parse first binding */
|
|
Pos lp = p->cur.pos;
|
|
Node *first = newnode(p->a, N_LET, lp);
|
|
first->str = expectbindname(p);
|
|
if (accept(p, TK_COLON))
|
|
first->lhs = parsetype(p);
|
|
|
|
if (p->cur.kind == TK_COMMA) {
|
|
/* multi-let: collect (name, type) pairs, then '=' rhs */
|
|
Node *m = newnode(p->a, N_MLET, pp);
|
|
Node *head = first, *tail = first;
|
|
while (accept(p, TK_COMMA)) {
|
|
Pos lpp = p->cur.pos;
|
|
Node *l = newnode(p->a, N_LET, lpp);
|
|
l->str = expectbindname(p);
|
|
if (accept(p, TK_COLON))
|
|
l->lhs = parsetype(p);
|
|
tail->next = l;
|
|
tail = l;
|
|
}
|
|
expect(p, TK_ASSIGN);
|
|
m->rhs = parseexpr(p);
|
|
expect(p, TK_SEMI);
|
|
m->list = head;
|
|
if (is_const) {
|
|
m->op = TK_CONST;
|
|
for (Node *l = head; l; l = l->next) l->op = TK_CONST;
|
|
}
|
|
(void)top;
|
|
return m;
|
|
}
|
|
|
|
if (accept(p, TK_ASSIGN))
|
|
first->rhs = parseexpr(p);
|
|
expect(p, TK_SEMI);
|
|
if (is_const) first->op = TK_CONST;
|
|
(void)top;
|
|
return first;
|
|
}
|
|
|
|
static Node *
|
|
parseif(Parser *p)
|
|
{
|
|
Pos pp = p->cur.pos;
|
|
expect(p, TK_IF);
|
|
expect(p, TK_LPAREN);
|
|
Node *n = newnode(p->a, N_IF, pp);
|
|
n->cond = parseexpr(p);
|
|
expect(p, TK_RPAREN);
|
|
n->body = parseblock(p);
|
|
if (accept(p, TK_ELSE)) {
|
|
if (p->cur.kind == TK_IF)
|
|
n->els = parseif(p);
|
|
else
|
|
n->els = parseblock(p);
|
|
}
|
|
return n;
|
|
}
|
|
|
|
static Node *
|
|
parse_for_else(Parser *p, Node *n)
|
|
{
|
|
/* `for (cond) { body } else { else_body }` — the else block runs
|
|
* when the loop exits normally (cond → false) and is skipped by
|
|
* `break`. Hare-style "did the loop find anything" idiom. */
|
|
if (accept(p, TK_ELSE))
|
|
n->els = parseblock(p);
|
|
return n;
|
|
}
|
|
|
|
static Node *
|
|
parsefor(Parser *p)
|
|
{
|
|
Pos pp = p->cur.pos;
|
|
expect(p, TK_FOR);
|
|
Node *n = newnode(p->a, N_FOR, pp);
|
|
if (p->cur.kind == TK_LBRACE) {
|
|
n->body = parseblock(p);
|
|
return parse_for_else(p, n);
|
|
}
|
|
expect(p, TK_LPAREN);
|
|
if (p->cur.kind == TK_RPAREN) {
|
|
advance(p);
|
|
n->body = parseblock(p);
|
|
return n;
|
|
}
|
|
/* First clause may be 'let' init or expr. If 'let' or terminated
|
|
* by ';', it's a C-for. Otherwise it's just `for (cond)`. */
|
|
if (p->cur.kind == TK_LET) {
|
|
/* Disambiguate range form: `let IDENT .. expr`. We peek
|
|
* for IDENT then DOTDOT before committing to parselet.
|
|
* Tuple destructure: `let (a, b) .. expr`. */
|
|
Tok save_cur = p->cur;
|
|
(void)save_cur;
|
|
advance(p); /* consume LET */
|
|
if (p->cur.kind == TK_LPAREN) {
|
|
advance(p);
|
|
Node *names = NULL, *tail = NULL;
|
|
for (;;) {
|
|
Pos np = p->cur.pos;
|
|
Node *e = newnode(p->a, N_IDENT, np);
|
|
e->str = expectbindname(p);
|
|
if (names == NULL) names = e;
|
|
else tail->next = e;
|
|
tail = e;
|
|
if (!accept(p, TK_COMMA)) break;
|
|
}
|
|
expect(p, TK_RPAREN);
|
|
expect(p, TK_DOTDOT);
|
|
Node *rng = newnode(p->a, N_FORRANGE, pp);
|
|
rng->str = NULL;
|
|
rng->list = names;
|
|
rng->lhs = parseexpr(p);
|
|
expect(p, TK_RPAREN);
|
|
rng->body = parseblock(p);
|
|
return parse_for_else(p, rng);
|
|
}
|
|
if (p->cur.kind == TK_IDENT || p->cur.kind == TK_UNDER) {
|
|
Pos ip = p->cur.pos;
|
|
const char *nm = p->cur.text;
|
|
int isunder = p->cur.kind == TK_UNDER;
|
|
Tok la = peek(p);
|
|
if (la.kind == TK_DOTDOT) {
|
|
advance(p); /* consume IDENT/UNDER */
|
|
advance(p); /* consume DOTDOT */
|
|
Node *rng = newnode(p->a, N_FORRANGE, pp);
|
|
rng->str = isunder ? "" : nm;
|
|
rng->lhs = parseexpr(p);
|
|
expect(p, TK_RPAREN);
|
|
rng->body = parseblock(p);
|
|
(void)ip;
|
|
return parse_for_else(p, rng);
|
|
}
|
|
}
|
|
/* Not a range. Build a synthetic LET stmt manually
|
|
* since we already consumed `let`. */
|
|
Pos lp = p->cur.pos;
|
|
Node *first = newnode(p->a, N_LET, lp);
|
|
first->str = expectbindname(p);
|
|
if (accept(p, TK_COLON))
|
|
first->lhs = parsetype(p);
|
|
if (accept(p, TK_ASSIGN))
|
|
first->rhs = parseexpr(p);
|
|
expect(p, TK_SEMI);
|
|
n->lhs = first;
|
|
if (p->cur.kind != TK_SEMI && p->cur.kind != TK_RPAREN)
|
|
n->cond = parseexpr(p);
|
|
if (accept(p, TK_SEMI))
|
|
n->rhs = parseexpr(p);
|
|
} else {
|
|
n->cond = parseexpr(p);
|
|
if (accept(p, TK_SEMI)) {
|
|
/* second clause: just consumed ';' but actually
|
|
* we need cond/init pattern. Simplify: only support
|
|
* `for (cond)` and `for (let init; cond; post)`.
|
|
* Treat the first form as having no init.
|
|
*/
|
|
}
|
|
}
|
|
expect(p, TK_RPAREN);
|
|
n->body = parseblock(p);
|
|
return parse_for_else(p, n);
|
|
}
|
|
|
|
static Node *
|
|
parseswitch(Parser *p)
|
|
{
|
|
Pos pp = p->cur.pos;
|
|
expect(p, TK_SWITCH);
|
|
expect(p, TK_LPAREN);
|
|
Node *n = newnode(p->a, N_SWITCH, pp);
|
|
n->lhs = parseexpr(p);
|
|
expect(p, TK_RPAREN);
|
|
expect(p, TK_LBRACE);
|
|
Node *head = NULL, *tail = NULL;
|
|
while (p->cur.kind == TK_CASE) {
|
|
Pos cp = p->cur.pos;
|
|
advance(p);
|
|
Node *c = newnode(p->a, N_CASE, cp);
|
|
Node *eh = NULL, *et = NULL;
|
|
if (p->cur.kind != TK_COLON) {
|
|
p->nocast = 1;
|
|
for (;;) {
|
|
Node *e = parseexpr(p);
|
|
if (eh == NULL) eh = e;
|
|
else et->next = e;
|
|
et = e;
|
|
if (!accept(p, TK_COMMA))
|
|
break;
|
|
}
|
|
p->nocast = 0;
|
|
}
|
|
c->list = eh;
|
|
expect(p, TK_COLON);
|
|
Node *bh = NULL, *bt = NULL;
|
|
while (p->cur.kind != TK_CASE && p->cur.kind != TK_RBRACE
|
|
&& p->cur.kind != TK_EOF) {
|
|
Node *s = parsestmt(p);
|
|
if (bh == NULL) bh = s;
|
|
else bt->next = s;
|
|
bt = s;
|
|
}
|
|
Node *blk = newnode(p->a, N_BLOCK, cp);
|
|
blk->list = bh;
|
|
c->body = blk;
|
|
if (head == NULL) head = c;
|
|
else tail->next = c;
|
|
tail = c;
|
|
}
|
|
expect(p, TK_RBRACE);
|
|
n->list = head;
|
|
return n;
|
|
}
|
|
|
|
static Node *
|
|
parsestmt(Parser *p)
|
|
{
|
|
/* Hare's `static` qualifies a statement to mean "the storage
|
|
* grown by this statement comes from the function's stack
|
|
* frame, not the heap". We don't optimise around it yet, so
|
|
* parse-and-ignore: the inner statement is unchanged. */
|
|
if (p->cur.kind == TK_STATIC) advance(p);
|
|
Pos pp = p->cur.pos;
|
|
switch (p->cur.kind) {
|
|
case TK_LBRACE: {
|
|
Node *b = parseblock(p);
|
|
expect(p, TK_SEMI);
|
|
return b;
|
|
}
|
|
case TK_LET:
|
|
case TK_CONST: return parselet(p, 0);
|
|
case TK_IF: {
|
|
Node *n = parseif(p);
|
|
expect(p, TK_SEMI);
|
|
return n;
|
|
}
|
|
case TK_FOR: {
|
|
Node *n = parsefor(p);
|
|
expect(p, TK_SEMI);
|
|
return n;
|
|
}
|
|
case TK_SWITCH: {
|
|
Node *n = parseswitch(p);
|
|
expect(p, TK_SEMI);
|
|
return n;
|
|
}
|
|
case TK_RETURN: {
|
|
advance(p);
|
|
Node *n = newnode(p->a, N_RETURN, pp);
|
|
if (p->cur.kind != TK_SEMI) {
|
|
Node *first = parseexpr(p);
|
|
if (p->cur.kind == TK_COMMA) {
|
|
Node *t = newnode(p->a, N_TUPLE, first->pos);
|
|
t->list = first;
|
|
Node *tail = first;
|
|
while (accept(p, TK_COMMA)) {
|
|
Node *e = parseexpr(p);
|
|
tail->next = e;
|
|
tail = e;
|
|
}
|
|
n->lhs = t;
|
|
} else {
|
|
n->lhs = first;
|
|
}
|
|
}
|
|
expect(p, TK_SEMI);
|
|
return n;
|
|
}
|
|
case TK_DEFER: {
|
|
advance(p);
|
|
Node *n = newnode(p->a, N_DEFER, pp);
|
|
n->lhs = parseexpr(p);
|
|
expect(p, TK_SEMI);
|
|
return n;
|
|
}
|
|
case TK_YIELD: {
|
|
advance(p);
|
|
Node *n = newnode(p->a, N_YIELD, pp);
|
|
n->lhs = parseexpr(p);
|
|
expect(p, TK_SEMI);
|
|
return n;
|
|
}
|
|
case TK_BREAK: {
|
|
advance(p);
|
|
Node *n = newnode(p->a, N_BREAK, pp);
|
|
expect(p, TK_SEMI);
|
|
return n;
|
|
}
|
|
case TK_CONTINUE: {
|
|
advance(p);
|
|
Node *n = newnode(p->a, N_CONTINUE, pp);
|
|
expect(p, TK_SEMI);
|
|
return n;
|
|
}
|
|
default: {
|
|
Node *e = parseexpr(p);
|
|
if (p->cur.kind == TK_COMMA) {
|
|
/* multi-assign: a, b, ... = expr;
|
|
* lvalues must not consume the trailing '=' as an
|
|
* assignment-chain (parseexpr would). */
|
|
Node *m = newnode(p->a, N_MASSIGN, pp);
|
|
Node *head = e, *tail = e;
|
|
while (accept(p, TK_COMMA)) {
|
|
Node *lv = parseexpr_noassign(p);
|
|
tail->next = lv;
|
|
tail = lv;
|
|
}
|
|
expect(p, TK_ASSIGN);
|
|
m->rhs = parseexpr(p);
|
|
m->list = head;
|
|
expect(p, TK_SEMI);
|
|
return m;
|
|
}
|
|
Node *n = newnode(p->a, N_EXPRSTMT, pp);
|
|
n->lhs = e;
|
|
expect(p, TK_SEMI);
|
|
return n;
|
|
}
|
|
}
|
|
}
|
|
|
|
static Node *
|
|
parseblock(Parser *p)
|
|
{
|
|
Pos pp = p->cur.pos;
|
|
expect(p, TK_LBRACE);
|
|
Node *n = newnode(p->a, N_BLOCK, pp);
|
|
Node *head = NULL, *tail = NULL;
|
|
while (p->cur.kind != TK_RBRACE && p->cur.kind != TK_EOF) {
|
|
Node *s = parsestmt(p);
|
|
if (head == NULL) head = s;
|
|
else tail->next = s;
|
|
tail = s;
|
|
}
|
|
expect(p, TK_RBRACE);
|
|
n->list = head;
|
|
return n;
|
|
}
|
|
|
|
/* ------- top-level decls ------------------------------------------- */
|
|
|
|
static Node *
|
|
parseuse(Parser *p)
|
|
{
|
|
Pos pp = p->cur.pos;
|
|
expect(p, TK_USE);
|
|
Node *n = newnode(p->a, N_USE, pp);
|
|
const char *base = expectident(p);
|
|
while (accept(p, TK_DOT))
|
|
base = aprintf(p->a, "%s.%s", base, expectident(p));
|
|
n->str = base;
|
|
n->strlen = strlen(base);
|
|
expect(p, TK_SEMI);
|
|
return n;
|
|
}
|
|
|
|
static Node *
|
|
parsedef(Parser *p, int exp)
|
|
{
|
|
Pos pp = p->cur.pos;
|
|
expect(p, TK_DEF);
|
|
Node *n = newnode(p->a, N_DEF, pp);
|
|
n->str = expectident(p);
|
|
expect(p, TK_COLON);
|
|
n->lhs = parsetype(p);
|
|
expect(p, TK_ASSIGN);
|
|
n->rhs = parseexpr(p);
|
|
expect(p, TK_SEMI);
|
|
n->export = exp;
|
|
return n;
|
|
}
|
|
|
|
static Node *
|
|
parsetypedecl(Parser *p, int exp)
|
|
{
|
|
Pos pp = p->cur.pos;
|
|
expect(p, TK_TYPE);
|
|
Node *n = newnode(p->a, N_TYPEDECL, pp);
|
|
n->str = expectident(p);
|
|
expect(p, TK_ASSIGN);
|
|
n->lhs = parsetype(p);
|
|
expect(p, TK_SEMI);
|
|
n->export = exp;
|
|
return n;
|
|
}
|
|
|
|
static Node *
|
|
parseattrs(Parser *p)
|
|
{
|
|
Node *head = NULL, *tail = NULL;
|
|
while (p->cur.kind == TK_AT) {
|
|
Pos pp = p->cur.pos;
|
|
advance(p);
|
|
Node *a = newnode(p->a, N_ATTR, pp);
|
|
a->str = expectident(p);
|
|
/* `@name(args...)` for FFI-style attributes;
|
|
* `@name` (no parens) for marker-only attributes like
|
|
* `@test`. */
|
|
if (accept(p, TK_LPAREN)) {
|
|
a->list = parsearglist(p, TK_RPAREN);
|
|
expect(p, TK_RPAREN);
|
|
}
|
|
if (head == NULL) head = a;
|
|
else tail->next = a;
|
|
tail = a;
|
|
}
|
|
return head;
|
|
}
|
|
|
|
static Node *
|
|
parsefn(Parser *p, int exp, Node *attrs)
|
|
{
|
|
Pos pp = p->cur.pos;
|
|
expect(p, TK_FN);
|
|
Node *n = newnode(p->a, N_FNDECL, pp);
|
|
n->str = expectident(p);
|
|
expect(p, TK_LPAREN);
|
|
n->list = parseparams(p);
|
|
expect(p, TK_RPAREN);
|
|
/* return type is required: void if absent only via explicit name */
|
|
if (p->cur.kind != TK_ASSIGN && p->cur.kind != TK_SEMI)
|
|
n->lhs = parsetype(p);
|
|
if (accept(p, TK_ASSIGN)) {
|
|
n->body = parseblock(p);
|
|
expect(p, TK_SEMI);
|
|
} else {
|
|
expect(p, TK_SEMI);
|
|
}
|
|
n->export = exp;
|
|
n->attr = attrs;
|
|
return n;
|
|
}
|
|
|
|
Node *
|
|
parsefile(Parser *p)
|
|
{
|
|
Pos pp = { p->l->file, 1, 1 };
|
|
Node *file = newnode(p->a, N_FILE, pp);
|
|
Node *head = NULL, *tail = NULL;
|
|
while (p->cur.kind != TK_EOF) {
|
|
/* Stamp the lex's current `// MODULE: foo` directive on the
|
|
* decl BEFORE parsing. cgen uses this for name-mangling and
|
|
* check uses it for cross-module type disambiguation. Capture
|
|
* before parsing so the closing `expect(SEMI)` doesn't
|
|
* accidentally advance the lexer past the *next* `// MODULE:`
|
|
* directive — that would stamp this decl with the next
|
|
* module's name. */
|
|
const char *mod = p->l->module;
|
|
Node *attrs = parseattrs(p);
|
|
int exp = accept(p, TK_EXPORT);
|
|
Node *d = NULL;
|
|
switch (p->cur.kind) {
|
|
case TK_USE:
|
|
if (attrs || exp) {
|
|
errorf(p->cur.pos, "use cannot be exported or attributed");
|
|
p->errs++;
|
|
}
|
|
d = parseuse(p);
|
|
break;
|
|
case TK_DEF: d = parsedef(p, exp); break;
|
|
case TK_TYPE: d = parsetypedecl(p, exp); break;
|
|
case TK_FN: d = parsefn(p, exp, attrs); break;
|
|
case TK_LET:
|
|
case TK_CONST: d = parselet(p, 1); d->export = exp; break;
|
|
default:
|
|
errorf(p->cur.pos, "expected top-level decl, got %s",
|
|
tokname(p->cur.kind));
|
|
p->errs++;
|
|
advance(p);
|
|
continue;
|
|
}
|
|
if (d != NULL) d->module = mod;
|
|
if (head == NULL) head = d;
|
|
else tail->next = d;
|
|
tail = d;
|
|
}
|
|
file->list = head;
|
|
return file;
|
|
}
|