Files
ww/lib/ww/parse/decl.ww
Hojun-Cho 353dffb5e8 lib/ww + wcc + w6c + wwdump: strip *arena cascade (γ-6)
amalloc has 0 callers post-γ-2; the *arena threaded through
newnode/newscope/newtype/prim/typesinit/type{ptr,slice,array,chan,
named}/lexinit/parserinit/joindotted/checkinit/arenau64tos/cgeninit
and the scope.a / tctx.a / lex.a / parser.a / checker.a / cgen.a
fields are vestigial.

Drop `import mem;` from 15 files, remove six struct fields, strip
*arena from 14 signatures, update ~120 call sites across lib/ww +
wcc + w6c + wwdump. selfhost/test/sym_link.ww fixture drops the
newarena/freearena probe; still exits 42 on scopedefine/scopelookup.
Both main.combined.ww auto-regenerated.

Comments retidied: typ.ww "once per arena" → "once per program";
parse.ww drops "arena-build" qualifier on joindotted; sym.ww drops
mem-sibling-imports rationale.

Verified 132/132 incl. 994_w6c_ww + 995_self_rebuild byte-identity
(the primary symmetric-stages gate).
2026-05-21 13:01:57 +09:00

182 lines
5.2 KiB
Plaintext

// lib/ww/parse/decl.ww — declaration parsing, split out of parse.ww.
package parse;
import os;
import tok;
// `import encoding.utf8;` — the driver resolves the dotted path to
// a directory; only the leaf (`utf8`) is needed downstream as the
// module bareword for n_use → decl disambiguation, mirroring Hare's
// `use encoding::utf8;` → `utf8::name` (ref/hare/hare/ast/import.ha:7
// stores `[]str` but identifier-resolution uses the last component).
fn parseuse(p: *parser) *node = {
let pf: str = p.curfile;
let pl: i32 = p.curline;
let pc: i32 = p.curcol;
advance(p); // past `use`
let n: *node = newnode(nkind.N_USE, pf, pl, pc);
n.nmod = p.curmod;
let leaf: str;
expectident(p, &leaf);
for (p.curkind == tkind.TK_DOT) {
advance(p); // past `.`
expectident(p, &leaf);
};
n.str = leaf;
expecttok(p, tkind.TK_SEMI, "expected ';' after use");
return n;
};
fn parsedef(p: *parser, exported: i32) *node = {
let pf: str = p.curfile;
let pl: i32 = p.curline;
let pc: i32 = p.curcol;
advance(p); // past `def`
let n: *node = newnode(nkind.N_DEF, pf, pl, pc);
n.nmod = p.curmod;
let id: str;
expectident(p, &id);
n.str = id;
expecttok(p, tkind.TK_COLON, "expected ':' in def");
n.lhs = parsetype(p);
expecttok(p, tkind.TK_ASSIGN, "expected '=' in def");
n.rhs = parseexpr(p);
expecttok(p, tkind.TK_SEMI, "expected ';' after def");
n.exported = exported;
return n;
};
fn parselet(p: *parser, exported: i32) *node = {
let pf: str = p.curfile;
let pl: i32 = p.curline;
let pc: i32 = p.curcol;
// Accept `let` or `const`. Const-bound bindings are marked via
// n.op = tkind.TK_CONST so the checker can reject reassignment.
let is_const: i32 = 0;
if (p.curkind == tkind.TK_CONST) { is_const = 1; };
advance(p);
let n: *node = newnode(nkind.N_LET, pf, pl, pc);
n.nmod = p.curmod;
let id: str;
expectbindname(p, &id);
n.str = id;
if (accepttok(p, tkind.TK_COLON)) {
n.lhs = parsetype(p);
};
if (accepttok(p, tkind.TK_ASSIGN)) {
n.rhs = parseexpr(p);
};
expecttok(p, tkind.TK_SEMI, "expected ';' after let");
n.exported = exported;
if (is_const != 0) { n.op = tkind.TK_CONST; };
return n;
};
fn parseattrs(p: *parser) *node = {
let head: *node = nil;
let tail: *node = nil;
for (p.curkind == tkind.TK_AT) {
let pf: str = p.curfile;
let pl: i32 = p.curline;
let pc: i32 = p.curcol;
advance(p);
let a: *node = newnode(nkind.N_ATTR, pf, pl, pc);
let id: str;
expectident(p, &id);
a.str = id;
// `@name(args...)` for FFI-style attrs; `@name` for marker-
// only attrs like @test (no parens).
if (accepttok(p, tkind.TK_LPAREN)) {
let arghead: *node = nil;
parsearglist(p, tkind.TK_RPAREN, &arghead);
a.list = arghead;
expecttok(p, tkind.TK_RPAREN, "expected ')' after attribute args");
};
if (head == nil) { head = a; tail = a; }
else { tail.next = a; tail = a; };
};
return head;
};
fn parseparams(p: *parser) *node = {
if (p.curkind == tkind.TK_RPAREN) { return nil; };
let head: *node = nil;
let tail: *node = nil;
for (true) {
let pf: str = p.curfile;
let pl: i32 = p.curline;
let pc: i32 = p.curcol;
let n: *node = newnode(nkind.N_PARAM, pf, pl, pc);
// Param form: (IDENT|'_') ':' type. Anonymous-type-only params
// (used in fn type expressions) aren't yet wired here.
let id: str;
expectbindname(p, &id);
n.str = id;
expecttok(p, tkind.TK_COLON, "expected ':' in parameter");
n.lhs = parsetype(p);
// Hare-style variadic: `name: T...`. Marker on n.op so check
// promotes the param's type to []T and call sites gather /
// forward. Mirrors cmd/wcc/parse.c parseparams.
if (accepttok(p, tkind.TK_ELLIPSIS)) {
n.op = tkind.TK_ELLIPSIS;
};
if (head == nil) { head = n; tail = n; }
else { tail.next = n; tail = n; };
if (n.op == tkind.TK_ELLIPSIS) {
break; // variadic must be the last param
};
if (!accepttok(p, tkind.TK_COMMA)) { break; };
if (p.curkind == tkind.TK_RPAREN) { break; };
};
return head;
};
fn parsefn(p: *parser, exported: i32, attrs: *node) *node = {
let pf: str = p.curfile;
let pl: i32 = p.curline;
let pc: i32 = p.curcol;
advance(p); // past `fn`
let n: *node = newnode(nkind.N_FNDECL, pf, pl, pc);
n.nmod = p.curmod;
let id: str;
expectident(p, &id);
n.str = id;
expecttok(p, tkind.TK_LPAREN, "expected '(' after fn name");
n.list = parseparams(p);
expecttok(p, tkind.TK_RPAREN, "expected ')' after params");
if (p.curkind != tkind.TK_ASSIGN) {
if (p.curkind != tkind.TK_SEMI) {
n.lhs = parsetype(p);
};
};
if (accepttok(p, tkind.TK_ASSIGN)) {
n.body = parseblock(p);
expecttok(p, tkind.TK_SEMI, "expected ';' after fn body");
} else {
// Body-less fn: FFI declaration (`fn name(args) ret;`).
expecttok(p, tkind.TK_SEMI, "expected ';' after fn header");
};
n.exported = exported;
n.attr = attrs;
return n;
};
fn parsetypedecl(p: *parser, exported: i32) *node = {
let pf: str = p.curfile;
let pl: i32 = p.curline;
let pc: i32 = p.curcol;
advance(p); // past `type`
let n: *node = newnode(nkind.N_TYPEDECL, pf, pl, pc);
n.nmod = p.curmod;
let id: str;
expectident(p, &id);
n.str = id;
expecttok(p, tkind.TK_ASSIGN, "expected '=' in type decl");
n.lhs = parsetype(p);
expecttok(p, tkind.TK_SEMI, "expected ';' after type decl");
n.exported = exported;
return n;
};