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;
};