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

@@ -47,6 +47,13 @@ static const char *loop_cont[LOOP_MAX];
static const char *loop_brk[LOOP_MAX];
static int nloops;
/* Yield-target stack. Each entry is the end label of an enclosing
* match-as-expression; `yield expr;` evaluates expr (AX) and JMPs
* to the topmost entry. */
#define YIELD_MAX 16
static const char *yield_target[YIELD_MAX];
static int nyields;
static int
cg_isfloat(Type *t)
{
@@ -1928,6 +1935,10 @@ cgexpr(Cg *c, Node *n, Local *locals)
}
}
char *end = mklabel(c, "match_end");
/* Push the end label as the yield target for arm bodies. */
if (nyields < YIELD_MAX) {
yield_target[nyields++] = end;
}
for (Node *cs = n->list; cs; cs = cs->next) {
char *next = mklabel(c, "match_next");
if (cs->type != NULL) {
@@ -2009,6 +2020,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
label(c, next);
}
label(c, end);
if (nyields > 0) nyields--;
break;
}
case N_TRYPROP: {
@@ -3254,6 +3266,14 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
defers[ndefers++] = n->lhs;
}
break;
case N_YIELD:
/* Evaluate the value into AX, then jump to the enclosing
* match's end label. str-typed yields land in (AX, BX);
* the consumer's let-init or call-arg site reads both. */
if (n->lhs) cgexpr(c, n->lhs, *locals);
if (nyields > 0)
ins1(c, A_JMP, abranch(yield_target[nyields - 1]));
break;
case N_BREAK:
if (nloops > 0)
ins1(c, A_JMP, abranch(loop_brk[nloops - 1]));