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:
@@ -86,6 +86,8 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
||||
|
||||
if (k == N_ASSIGN) { cgassign(c, n); return; };
|
||||
|
||||
if (k == N_TRYPROP) { cgtryprop(c, n); return; };
|
||||
if (k == N_TRYUNW) { cgtryunw(c, n); return; };
|
||||
if (k == N_TYPETEST) { cgtypetest(c, n); return; };
|
||||
if (k == N_TYPEASSERT) { cgtypeassert(c, n); return; };
|
||||
};
|
||||
@@ -114,6 +116,100 @@ fn cgtagvariantidx(tagged: *node, vt: *node) i32 = {
|
||||
return -1;
|
||||
};
|
||||
|
||||
// cgtryprop — `e?` propagates the error variant up the stack.
|
||||
// Legacy semantics only (success tag = 0). No tag remap; the
|
||||
// selfhost code that uses ? today has the same variant order in
|
||||
// operand and enclosing fn.
|
||||
fn cgtryprop(c: *cgen, n: *node) void = {
|
||||
cgexpr(c, n.lhs);
|
||||
// AX = tag. If non-zero, this is an error; pop frame and RET.
|
||||
let cl: str = mklabel(c, "tryprop_ok");
|
||||
emitline("\tCMPQ\t$0, AX\n");
|
||||
emitline("\tJE\t");
|
||||
emitline(cl);
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\tBP, SP\n\tPOPQ\tBP\n\tRET\n");
|
||||
emitlabel(cl);
|
||||
// Success: unwrap value. Tag-only result was AX; the rest of
|
||||
// the codegen expects the success value in AX (and BX for str).
|
||||
// AX=tag, DX=val0, CX=val1 from the call ABI. For str success,
|
||||
// shuffle (DX,CX) → (AX,BX); else move DX → AX.
|
||||
let succisstr: bool = false;
|
||||
if (n.lhs != nil) {
|
||||
if (n.lhs.kind == N_CALL) {
|
||||
let callee: *node = n.lhs.lhs;
|
||||
if (callee != nil) {
|
||||
let cname: str;
|
||||
cname.ptr = nil; cname.len = 0;
|
||||
if (callee.kind == N_IDENT) { cname = callee.str; };
|
||||
if (callee.kind == N_DOT) { cname = callee.str; };
|
||||
if (cname.len > 0) {
|
||||
let rt: *node = fnretlookup(c, cname);
|
||||
if (rt != nil) {
|
||||
if (rt.kind == N_TTAGGED) {
|
||||
let first: *node = rt.list;
|
||||
if (first != nil) {
|
||||
if (isstrtype(c, first)) {
|
||||
succisstr = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
if (succisstr) {
|
||||
emitline("\tMOVQ\tCX, BX\n");
|
||||
};
|
||||
emitline("\tMOVQ\tDX, AX\n");
|
||||
return;
|
||||
};
|
||||
|
||||
// cgtryunw — `e!` aborts on the error variant via exit(1). Legacy
|
||||
// semantics (success tag = 0).
|
||||
fn cgtryunw(c: *cgen, n: *node) void = {
|
||||
cgexpr(c, n.lhs);
|
||||
let cl: str = mklabel(c, "tryunw_ok");
|
||||
emitline("\tCMPQ\t$0, AX\n");
|
||||
emitline("\tJE\t");
|
||||
emitline(cl);
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\t$1, DI\n\tMOVQ\t$60, AX\n\tSYSCALL\n");
|
||||
emitlabel(cl);
|
||||
// Unwrap success value. (Same shuffle pattern as cgtryprop.)
|
||||
let succisstr: bool = false;
|
||||
if (n.lhs != nil) {
|
||||
if (n.lhs.kind == N_CALL) {
|
||||
let callee: *node = n.lhs.lhs;
|
||||
if (callee != nil) {
|
||||
let cname: str;
|
||||
cname.ptr = nil; cname.len = 0;
|
||||
if (callee.kind == N_IDENT) { cname = callee.str; };
|
||||
if (callee.kind == N_DOT) { cname = callee.str; };
|
||||
if (cname.len > 0) {
|
||||
let rt: *node = fnretlookup(c, cname);
|
||||
if (rt != nil) {
|
||||
if (rt.kind == N_TTAGGED) {
|
||||
let first: *node = rt.list;
|
||||
if (first != nil) {
|
||||
if (isstrtype(c, first)) {
|
||||
succisstr = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
if (succisstr) {
|
||||
emitline("\tMOVQ\tCX, BX\n");
|
||||
};
|
||||
emitline("\tMOVQ\tDX, AX\n");
|
||||
return;
|
||||
};
|
||||
|
||||
fn cgtypetest(c: *cgen, n: *node) void = {
|
||||
// `e is T` — load the lhs's tag, compare against T's variant
|
||||
// index, set AX = (tag == idx). Result type is bool.
|
||||
|
||||
Reference in New Issue
Block a user