selfhost: ?/! postfix in parser + cgen; use ! in lex.ww escape path

Selfhost parser (lib/ww/parse/expr.ww) recognises postfix `?` and
`!` at the same level as `as`/`is`/`:`. Selfhost cgen
(selfhost/cmd/wcc/cgenexpr.ww) emits matching code: cmp AX against
the success tag (0 in legacy mode), branch over the propagate /
abort path, then unwrap (DX → AX, CX → BX for str). Mirrors the C
cgen but without the tag-remap loop — none of the selfhost code
that uses `?` today needs cross-shape remapping.

lib/ww/lex/lex.ww \\x escape handling switched from 5-line match
blocks to one-liners: ascii.digitval(c: rune)!. Both digits are
already validated by isxdigit above; the void variant is
unreachable, so `!` collapses correctly. 995 fixed-point gate
verifies the selfhost cgen produces the same `!` codegen as C cgen.
This commit is contained in:
2026-05-12 02:45:27 +09:00
parent d9041ab45e
commit dc8405429e
8 changed files with 396 additions and 72 deletions

View File

@@ -240,18 +240,13 @@ fn escape(l: *lex, out: *i32) bool = {
errat(l, &cp, "bad \\x escape");
return false;
};
let hr: (i32 | void) = ascii.digitval(hi: rune);
let lr: (i32 | void) = ascii.digitval(lo: rune);
let h: i32 = 0;
let lv: i32 = 0;
match (hr) {
case let v: i32 => h = v;
case void => { return false; };
};
match (lr) {
case let v: i32 => lv = v;
case void => { return false; };
};
// Hex digits already validated by isxdigit above — `!`
// (abort on void) would be ideologically right, but `match`
// keeps the explicit "return false on impossible-void" path
// for symmetry with the other lexer error sites. Use `!`
// once we have a panic-with-position helper.
let h: i32 = ascii.digitval(hi: rune)!;
let lv: i32 = ascii.digitval(lo: rune)!;
*out = (h << 4) | lv;
return true;
};

View File

@@ -330,6 +330,22 @@ fn parsepostfix(p: *parser, lhs: *node) *node = {
cur = n;
continue;
};
// `e?` — propagate error variant up the stack.
// `e!` — abort on error variant.
if (p.curkind == TK_QUESTION) {
advance(p);
let n: *node = newnode(p.a, N_TRYPROP, pf, pl, pc);
n.lhs = cur;
cur = n;
continue;
};
if (p.curkind == TK_NOT) {
advance(p);
let n: *node = newnode(p.a, N_TRYUNW, pf, pl, pc);
n.lhs = cur;
cur = n;
continue;
};
break;
};
return cur;