ww: cgen trap batch (def-str field, chained-ptr write, scalar+str tuple ABI)

This commit is contained in:
2026-05-11 20:49:21 +09:00
parent 2ac15e4e99
commit c5f30f2fce
14 changed files with 1629 additions and 67 deletions

View File

@@ -10,4 +10,4 @@ This directory is **not** a Hare-style stdlib module — it's a compiler-fronten
When porting from `cmd/wcc/*.c`:
- Keep the same data layout. The selfhost goal is byte-compatible structures so dump/inspect tools work either way.
- Read `cmd/wcc/ww.h` first for canonical field names and ordering.
- See `selfhost/CLAUDE.md` for the wwstage cgen traps to avoid (two-level field write, `(scalar,str)` tuple return, `def : str`, undersized `amalloc`).
- See `selfhost/CLAUDE.md` for the wwstage cgen traps to avoid (undersized `amalloc`).

View File

@@ -281,9 +281,17 @@ fn parsepostfix(p: *parser, lhs: *node) *node = {
advance(p);
let n: *node = newnode(p.a, N_DOT, pf, pl, pc);
n.lhs = cur;
let id: str;
expectident(p, &id);
n.str = id;
// Hare-style tuple field access: `t.0`, `t.1`. The
// numeric literal becomes the field name string so the
// cgen tuple-positional path matches `cmd/wcc/parse.c`.
if (p.curkind == TK_INT) {
n.str = p.curtext;
advance(p);
} else {
let id: str;
expectident(p, &id);
n.str = id;
};
cur = n;
continue;
};

View File

@@ -12,6 +12,42 @@ fn parseletlocal(p: *parser) *node = {
let is_const: i32 = 0;
if (p.curkind == TK_CONST) { is_const = 1; };
advance(p);
// Hare-style tuple destructure: `let (a, b) = expr;`.
// Types are optional per binding (matches C parser; Hare itself
// doesn't allow types here, but cmd/wcc/parse.c does).
if (p.curkind == TK_LPAREN) {
advance(p);
let m: *node = newnode(p.a, N_MLET, pf, pl, pc);
let head: *node = nil;
let tail: *node = nil;
for (true) {
let lpf: str = p.curfile;
let lpl: i32 = p.curline;
let lpc: i32 = p.curcol;
let l: *node = newnode(p.a, N_LET, lpf, lpl, lpc);
let id: str;
expectbindname(p, &id);
l.str = id;
if (accepttok(p, TK_COLON)) { l.lhs = parsetype(p); };
if (head == nil) { head = l; }
else { tail.next = l; };
tail = l;
if (!accepttok(p, TK_COMMA)) { break; };
};
expecttok(p, TK_RPAREN, "expected ')' in let destructure");
expecttok(p, TK_ASSIGN, "expected '=' after let destructure");
m.rhs = parseexpr(p);
expecttok(p, TK_SEMI, "expected ';' after let");
m.list = head;
if (is_const != 0) {
m.op = TK_CONST;
let lc: *node = head;
for (lc != nil) { lc.op = TK_CONST; lc = lc.next; };
};
return m;
};
let n: *node = newnode(p.a, N_LET, pf, pl, pc);
let id: str;
expectbindname(p, &id);
@@ -19,6 +55,36 @@ fn parseletlocal(p: *parser) *node = {
if (accepttok(p, TK_COLON)) {
n.lhs = parsetype(p);
};
// Comma-multi-let: `let n, s = call();` (ww extension over Hare).
// Collects (name, type) pairs, then '=' rhs. Each binding gets
// its own N_LET; the wrapping N_MLET carries the rhs.
if (p.curkind == TK_COMMA) {
let m: *node = newnode(p.a, N_MLET, pf, pl, pc);
let head: *node = n;
let tail: *node = n;
for (accepttok(p, TK_COMMA)) {
let lpf: str = p.curfile;
let lpl: i32 = p.curline;
let lpc: i32 = p.curcol;
let l: *node = newnode(p.a, N_LET, lpf, lpl, lpc);
let id2: str;
expectbindname(p, &id2);
l.str = id2;
if (accepttok(p, TK_COLON)) { l.lhs = parsetype(p); };
tail.next = l;
tail = l;
};
expecttok(p, TK_ASSIGN, "expected '=' after let names");
m.rhs = parseexpr(p);
expecttok(p, TK_SEMI, "expected ';' after let");
m.list = head;
if (is_const != 0) {
m.op = TK_CONST;
let lc: *node = head;
for (lc != nil) { lc.op = TK_CONST; lc = lc.next; };
};
return m;
};
if (accepttok(p, TK_ASSIGN)) {
n.rhs = parseexpr(p);
};