/* * 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 #include 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 int advanceheaderimport(Parser *p) { Tok next; if (!lexheaderimport(p->l, &next)) return 0; p->cur = next; p->hasla = 0; return 1; } 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 ""; } 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); static void skipdecl(Parser *p) { int paren = 0, bracket = 0, brace = 0; while (p->cur.kind != TK_EOF) { switch (p->cur.kind) { case TK_LPAREN: paren++; break; case TK_RPAREN: if (paren > 0) paren--; break; case TK_LBRACK: bracket++; break; case TK_RBRACK: if (bracket > 0) bracket--; break; case TK_LBRACE: brace++; break; case TK_RBRACE: if (brace > 0) brace--; break; case TK_SEMI: advance(p); if (paren == 0 && bracket == 0 && brace == 0) return; continue; default: break; } advance(p); } } /* Consume attribute syntax without parsing its expressions or any following * declaration. The imports-only pass needs only to recognize that the next * declaration is an attributed import, which full parsing rejects. */ static void skipimportattrs(Parser *p) { while (p->cur.kind == TK_AT) { advance(p); if (p->cur.kind == TK_IDENT) advance(p); else { errorf(p->cur.pos, "expected identifier, got %s", tokname(p->cur.kind)); p->errs++; } if (!accept(p, TK_LPAREN)) continue; int depth = 1; while (p->cur.kind != TK_EOF && depth > 0) { if (p->cur.kind == TK_LPAREN) depth++; else if (p->cur.kind == TK_RPAREN) depth--; advance(p); } if (depth > 0) { errorf(p->cur.pos, "expected ')' after attribute"); p->errs++; } } } 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); Node *n = newnode(p->a, N_TSTRUCT, pp); /* `@packed` is an inline struct TYPE attribute (harec * ast.h:95 `bool packed`), sitting after `struct` and * before `{` — NOT a fn-decl attr, so it does not route * through parseattrs. */ if (p->cur.kind == TK_AT) { advance(p); const char *an = expectident(p); /* errorf only reports; p->errs gates the build * (cmd/w6c/main.c:82). */ if (strcmp(an, "packed") != 0) { errorf(p->cur.pos, "unknown struct attribute '@%s'", an); p->errs++; } else n->packed = 1; } expect(p, TK_LBRACE); 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); } } /* 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; } /* #76: a qualified path `pkg.Type` reaches a struct-literal handoff as an * N_DOT chain (parseprimary's dotted fold). Flatten it into one N_TNAME * whose str joins the chain in SOURCE order — byte-identical to parsetype's * dotted collapse (parse.c: aprintf("%s.%s", ...)) — so resolve_type's * existing N_TNAME arm resolves it with no new checker arm. */ static const char * flattendotstr(Parser *p, Node *n) { if (n->kind == N_IDENT) return n->str; return aprintf(p->a, "%s.%s", flattendotstr(p, n->lhs), n->str); } static Node * flattendot(Parser *p, Node *n) { Node *t = newnode(p->a, N_TNAME, n->pos); t->str = flattendotstr(p, n); t->strlen = strlen(t->str); return t; } 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); while (p->cur.kind == TK_DOT && peek(p).kind == TK_IDENT) { advance(p); 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; } if (p->cur.kind == TK_LBRACE) { /* #76: bare `Foo{}` keeps the N_IDENT fast-path; a * qualified `pkg.Type{}` (N_DOT chain) flattens first. */ if (n->kind == N_DOT) n = flattendot(p, n); 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); } static Node * parselet(Parser *p) { 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; } return m; } 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) { 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; } return m; } if (accept(p, TK_ASSIGN)) first->rhs = parseexpr(p); expect(p, TK_SEMI); if (is_const) first->op = TK_CONST; 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) { /* Range and three-clause forms share the `let` prefix. */ advance(p); 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) { const char *nm = p->cur.text; int isunder = p->cur.kind == TK_UNDER; Tok la = peek(p); if (la.kind == TK_DOTDOT) { advance(p); advance(p); Node *rng = newnode(p->a, N_FORRANGE, pp); rng->str = isunder ? "" : nm; rng->lhs = parseexpr(p); expect(p, TK_RPAREN); rng->body = parseblock(p); 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); 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; } /* The optional alias is source-local; the dotted path remains the dependency * identity supplied to the package driver. */ static Node * parseuse(Parser *p) { Pos pp = p->cur.pos; expect(p, TK_USE); Node *n = newnode(p->a, N_USE, pp); /* Keep n->pos at the import keyword for structural diagnostics. Go's * import declaration position is the first spec token: the explicit alias * when present, otherwise the path. */ n->usefile = p->cur.pos.file; n->useline = p->cur.pos.line; n->usecol = p->cur.pos.col; const char *alias = NULL; const char *first; if (p->cur.kind == TK_UNDER) { n->useblank = 1; advance(p); first = expectident(p); } else { first = expectident(p); if (p->cur.kind == TK_IDENT) { alias = first; first = expectident(p); } } const char *leaf = first; const char *path = leaf; while (accept(p, TK_DOT)) { leaf = expectident(p); path = aprintf(p->a, "%s.%s", path, leaf); } if (!n->useblank) { n->str = alias ? alias : leaf; n->strlen = strlen(n->str); } n->usesource = path; n->usepath = path; n->usealias = alias; expect(p, TK_SEMI); return n; } /* Parse one initial import without pulling the declaration that follows it * into the header pass. This intentionally has stricter, single-error * recovery than parseuse: named test-source build loading needs only decide * whether the package/import header itself is valid. */ static Node * parseheaderuse(Parser *p) { Pos pp = p->cur.pos; advance(p); Node *n = newnode(p->a, N_USE, pp); n->usefile = p->cur.pos.file; n->useline = p->cur.pos.line; n->usecol = p->cur.pos.col; const char *alias = NULL; const char *first; if (p->cur.kind == TK_UNDER) { n->useblank = 1; advance(p); } else if (p->cur.kind != TK_IDENT) { errorf(p->cur.pos, "expected identifier, got %s", tokname(p->cur.kind)); p->errs++; return n; } if (p->cur.kind != TK_IDENT) { errorf(p->cur.pos, "expected identifier, got %s", tokname(p->cur.kind)); p->errs++; return n; } first = p->cur.text; advance(p); if (!n->useblank && p->cur.kind == TK_IDENT) { alias = first; first = p->cur.text; advance(p); } const char *leaf = first; const char *path = leaf; while (p->cur.kind == TK_DOT) { advance(p); if (p->cur.kind != TK_IDENT) { errorf(p->cur.pos, "expected identifier, got %s", tokname(p->cur.kind)); p->errs++; return n; } leaf = p->cur.text; path = aprintf(p->a, "%s.%s", path, leaf); advance(p); } if (!n->useblank) { n->str = alias ? alias : leaf; n->strlen = strlen(n->str); } n->usesource = path; n->usepath = path; n->usealias = alias; if (p->cur.kind != TK_SEMI) { errorf(p->cur.pos, "expected ';' after import"); p->errs++; return n; } return n; } /* Go's named-file loader parses a valid source only through its initial * package/import section. Keep the broader parseimports recovery pass for * graph-bearing sources, but give actionless test-source omission a boundary * that cannot diagnose late imports or an invalid ordinary declaration body. */ Node * parsepackageheader(Parser *p) { Pos fp = { p->l->file, 1, 1 }; Node *file = newnode(p->a, N_FILE, fp); Node *head = NULL, *tail = NULL; while (p->cur.kind == TK_MODPATH || p->cur.kind == TK_MODRESET) { if (p->cur.kind == TK_MODPATH) { p->pathmod = p->cur.text; p->curmod = p->cur.text; p->resetmod = NULL; } else { p->pathmod = NULL; p->curmod = p->cur.text; p->resetmod = p->cur.text; } p->sourceid++; advance(p); } if (p->cur.kind != TK_MODULE) { errorf(p->cur.pos, "invalid or missing package clause"); p->errs++; return file; } Pos pp = p->cur.pos; advance(p); if (p->cur.kind != TK_IDENT) { errorf(p->cur.pos, "invalid or missing package clause"); p->errs++; return file; } const char *name = p->cur.text; advance(p); if (p->cur.kind != TK_SEMI) { errorf(p->cur.pos, "expected ';' after package name"); p->errs++; return file; } p->curpkg = name; if (p->pathmod == NULL && p->resetmod == NULL) p->curmod = name; file->module = name; file->pkgname = name; file->sourceid = p->sourceid; file->pos = pp; while (advanceheaderimport(p)) { Node *d = parseheaderuse(p); d->module = p->curmod; d->pkgname = p->curpkg; d->sourceid = p->sourceid; if (head == NULL) head = d; else tail->next = d; tail = d; if (p->errs != 0) break; } file->list = head; return file; } Node * parseimports(Parser *p) { Pos fp = { p->l->file, 1, 1 }; Node *file = newnode(p->a, N_FILE, fp); Node *head = NULL, *tail = NULL; Node *packages = NULL, *packagetail = NULL; int sawpackage = 0; /* Go source files admit one import section before ordinary declarations. * Keep recovery permissive, but diagnose the first import in each later * section. Compiler-owned bundle markers begin a fresh source section. */ int previmport = 1; while (p->cur.kind != TK_EOF) { /* Compiler/driver bundle markers carry package identity out of * band. They are not source declarations, so keep scanning for * the following package clause and imports. */ if (p->cur.kind == TK_MODPATH) { sawpackage = 0; previmport = 1; p->sourceid++; p->pathmod = p->cur.text; p->curmod = p->cur.text; p->resetmod = NULL; p->curpkg = NULL; advance(p); continue; } if (p->cur.kind == TK_MODRESET) { const char *rp = p->cur.text; sawpackage = 0; previmport = 1; p->sourceid++; advance(p); p->pathmod = NULL; p->curmod = rp; p->resetmod = rp; p->curpkg = NULL; continue; } if (p->cur.kind == TK_MODULE) { Pos pp = p->cur.pos; previmport = 1; advance(p); if (p->cur.kind != TK_IDENT) { errorf(p->cur.pos, "invalid or missing package clause"); p->errs++; sawpackage = 1; skipdecl(p); continue; } const char *name = expectident(p); expect(p, TK_SEMI); p->curpkg = name; if (p->pathmod == NULL && p->resetmod == NULL) p->curmod = name; Node *package = newnode(p->a, N_FILE, pp); package->module = p->curmod; package->pkgname = name; package->sourceid = p->sourceid; if (packages == NULL) packages = package; else packagetail->next = package; packagetail = package; if (!sawpackage) { file->module = name; file->pkgname = name; file->sourceid = p->sourceid; file->pos = pp; sawpackage = 1; } continue; } if (!sawpackage && p->pathmod == NULL && p->resetmod == NULL) { errorf(p->cur.pos, "invalid or missing package clause"); p->errs++; sawpackage = 1; } if (p->cur.kind == TK_USE) { if (!previmport) { errorf(p->cur.pos, "imports must appear before other declarations"); p->errs++; } previmport = 1; Node *d = parseuse(p); d->module = p->curmod; d->pkgname = p->curpkg; d->sourceid = p->sourceid; if (head == NULL) head = d; else tail->next = d; tail = d; continue; } previmport = 0; if (p->cur.kind == TK_AT) { skipimportattrs(p); if (p->cur.kind == TK_EXPORT) advance(p); if (p->cur.kind == TK_USE) { errorf(p->cur.pos, "import cannot be exported or attributed"); p->errs++; Node *d = parseuse(p); d->module = p->curmod; d->pkgname = p->curpkg; d->sourceid = p->sourceid; if (head == NULL) head = d; else tail->next = d; tail = d; continue; } skipdecl(p); continue; } if (p->cur.kind == TK_EXPORT && peek(p).kind == TK_USE) { advance(p); errorf(p->cur.pos, "import cannot be exported or attributed"); p->errs++; Node *d = parseuse(p); d->module = p->curmod; d->pkgname = p->curpkg; d->sourceid = p->sourceid; if (head == NULL) head = d; else tail->next = d; tail = d; continue; } skipdecl(p); } file->list = head; /* The package-clause chain lets a directory loader validate every * selected file without inventing a second header grammar. It lives in * body because list is the public imports chain. */ file->body = packages; return file; } 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); /* A value-LESS `def X: T;` is an interface prototype (an aggregate- * init `.wwi` def whose DATA lives in the defining package — BUG-2 / * task #71), mirroring parsefn's bodyless-prototype arm. rhs stays * NULL; the checker's fold pass (`if (d->rhs)`) and cgen's emit_defs/ * emit_lets (`d->rhs == NULL` continue) already expect this shape. */ if (accept(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; Node *packages = NULL, *packagetail = NULL; int sawpackage = 0; /* Semantic twin of parseimports: full/direct parsing reports the same * per-source import-section ordering error while retaining the AST. */ int previmport = 1; while (p->cur.kind != TK_EOF) { /* `package foo;` — directory-as-module declaration. Every * primary section opens with one (`package main;` for an * executable); a missing clause on the first real decl is a * hard error (strict-package, #24a). Imported (pathmod) and * sep primary-reset (resetmod) regions carry identity * out-of-band and are exempt. */ if (p->cur.kind == TK_MODULE) { Pos packagepos = p->cur.pos; sawpackage = 1; previmport = 1; advance(p); const char *name = expectident(p); expect(p, TK_SEMI); p->curpkg = name; if (p->pathmod == NULL && p->resetmod == NULL) { p->curmod = name; } Node *package = newnode(p->a, N_FILE, packagepos); package->module = p->curmod; package->pkgname = name; package->sourceid = p->sourceid; package->imported = p->pathmod != NULL; if (packages == NULL) packages = package; else packagetail->next = package; packagetail = package; if (file->pkgname == NULL) { file->pkgname = name; file->sourceid = p->sourceid; } continue; } /* `//ww:module ` — M1 #22 import boundary. The following * file's decls mangle on the full dotted import path independently * of its `package` clause, and are flagged imported (gates the * root-only bare-`main` rule, #32). */ if (p->cur.kind == TK_MODPATH) { sawpackage = 0; previmport = 1; p->sourceid++; p->pathmod = p->cur.text; p->curmod = p->cur.text; p->resetmod = NULL; p->curpkg = NULL; if (file->module == NULL) file->module = p->cur.text; advance(p); continue; } /* `//ww:module-reset` — bundle boundary before a package-less * file. Reset curmod to NULL so the file's decls (and its own * `import os;`) are attributed to the primary module (""), not * the preceding bundled package. Codegen-neutral: NULL curmod * keeps bare symbols. (#16 option-B; closes task #11.) * Driver-emitted ONLY before package-less files; a hand-placed * directive after a mid-file `package` would strip subsequent * decls to bare — that usage is deliberate-only. */ if (p->cur.kind == TK_MODRESET) { sawpackage = 0; previmport = 1; p->sourceid++; /* #57: a path-carrying reset (sep primary body) mangles * decls on the dotted path so definer == importer, but * leaves imported==0 (curmod set, pathmod NULL) so -c * primary-ness and the #32 bare-main rule are intact; * the body's `package` clause then asserts (resetmod). * A bare reset is the root/package-less boundary: curmod * NULL → bare symbols, today's behavior. */ const char *rp = p->cur.text; advance(p); p->pathmod = NULL; p->curpkg = NULL; if (rp != NULL) { p->curmod = rp; p->resetmod = rp; /* #11: path-carrying reset is a primary body * identity (sep); stamp it for the wwi leaf * fallback. A bare reset (rp==NULL) is the * root/package-less boundary and MUST keep the * "main" default — so do NOT stamp there. */ if (file->module == NULL) file->module = rp; } else { p->curmod = NULL; p->resetmod = NULL; } continue; } /* strict-package: a primary section's first real decl must be * preceded by a `package` clause. Imported (pathmod) and sep * primary-reset (resetmod) regions carry identity out-of-band, * so they are exempt. Wording mirrors Go's missing-`package` * diagnostic (rule 5; Hare has no clause to port). */ if (p->pathmod == NULL && p->resetmod == NULL && !sawpackage) { errorf(p->cur.pos, "missing package clause"); p->errs++; sawpackage = 1; } int thisimport = p->cur.kind == TK_USE; if (thisimport && !previmport) { errorf(p->cur.pos, "imports must appear before other declarations"); p->errs++; } previmport = thisimport; 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, "import 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); 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 = p->curmod; d->pkgname = p->curpkg; d->sourceid = p->sourceid; d->imported = (p->pathmod != NULL); } if (head == NULL) head = d; else tail->next = d; tail = d; } file->list = head; file->body = packages; return file; }