wcc: match-as-expression with yield

`match (e) { ... }` can now sit in expression position, with each
arm using `yield expr;` to produce the match's value:

    let v = match (r) {
    case let n: i32 => yield n + 1;
    case let s: str => yield s.len: i32 + 100;
    };

TK_YIELD keyword + N_YIELD AST node, both appended at the tail of
their enums to keep prior numeric values byte-stable for the
wwdump-diff gates.

Checker: cexpr for N_MATCH walks each arm's body looking for the
first N_YIELD; the match's type is the unified yield type (or
ty_void if no yield, preserving the statement-form semantics).
Mismatched arm yields are flagged.

Cgen: a yield-target stack (separate from the loop break stack)
holds each enclosing match's end label. N_YIELD evaluates its
expression into AX (and BX for str) and JMPs to the topmost entry.
cgmatch pushes its end label on entry and pops on exit.

Selfhost mirror: lib/ww/lex/tok.ww kwtab+name, lib/ww/ast.ww
N_YIELD def+print, lib/ww/parse/stmt.ww yield-stmt; selfhost cgen
adds a yieldbuf to the cgen struct and a cgyield helper. Verified
end-to-end: a yield-using program compiled via the wwstage cgen
matches the C-cgen build's exit code.
This commit is contained in:
2026-05-12 03:08:00 +09:00
parent 67e27589fd
commit f267f99a2b
15 changed files with 231 additions and 14 deletions

View File

@@ -177,6 +177,7 @@ typedef enum {
* the selfhost wwdump-diff test (990) is byte-sensitive. */
TK_IS, /* Hare-style type test: e is T */
TK_VOID, /* `void` — both a type name and a zero-size value */
TK_YIELD, /* `yield expr;` — value-return from a match arm */
TK_LAST /* sentinel for tables */
} Tkind;
@@ -297,6 +298,8 @@ typedef enum {
N_TYPEASSERT, /* lhs as T → T (abort if tag mismatch) */
N_VOIDLIT, /* `void` as expression — zero-size void value */
N_TBANG, /* `!T` — error-flagged type. lhs = inner type. */
N_YIELD, /* `yield expr;` — set the enclosing match's value-
* return and jump to its end label. lhs = value. */
N_LAST
} Nkind;