From 96cf957b07cac06109baa69edb1d6c593aa52074 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Wed, 3 Jun 2026 00:59:13 +0900 Subject: [PATCH] lib/ww/lex: fputq + tokprint if-ladders -> switch (Wave-2 structural) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fputq: 6-deep escape if/else -> switch (c); named escapes \\ " \n \t \r as explicit cases, empty-label default folds the control-char arms into (c<' '||c==127)->fputhex2 else fputcbyte, mirroring cmd/wcc/tok.c:172. tokprint: 4-deep kind if-ladder -> switch (t.kind) with comma-cases (IDENT,STR,ERR -> fputq; INT,RUNE -> u64tos; default -> nothing), mirroring tok.c:194. toktest gains @test tokprint_cases: drives tokprint over a temp fd and packs every fputq escape (\\ " \n \t \r, c<0x20, c==0x7f, printable) into a TK_STR text — branches 990_selfhost's corpus never reaches. w6c + wwdump combined.ww regenerated. --- lib/ww/lex/tok.ww | 53 +++++-------------- lib/ww/lex/toktest.ww | 79 +++++++++++++++++++++++++++- selfhost/cmd/w6c/main.combined.ww | 53 +++++-------------- selfhost/cmd/wwdump/main.combined.ww | 53 +++++-------------- 4 files changed, 119 insertions(+), 119 deletions(-) diff --git a/lib/ww/lex/tok.ww b/lib/ww/lex/tok.ww index 024dbc8a..e921b002 100644 --- a/lib/ww/lex/tok.ww +++ b/lib/ww/lex/tok.ww @@ -337,33 +337,17 @@ fn fputq(fd: i32, p: *u8, n: i32) void = { let i: i32 = 0; for (i < n) { let c: u8 = p[i]; - if (c == '\\') { - fputsstr(fd, "\\\\"); - } else { - if (c == '"') { - fputsstr(fd, "\\\""); + switch (c) { + case '\\': fputsstr(fd, "\\\\"); + case '"': fputsstr(fd, "\\\""); + case '\n': fputsstr(fd, "\\n"); + case '\t': fputsstr(fd, "\\t"); + case '\r': fputsstr(fd, "\\r"); + case: + if (c < ' ' || c == 127u8) { + fputhex2(fd, c); } else { - if (c == '\n') { - fputsstr(fd, "\\n"); - } else { - if (c == '\t') { - fputsstr(fd, "\\t"); - } else { - if (c == '\r') { - fputsstr(fd, "\\r"); - } else { - if (c < ' ') { - fputhex2(fd, c); - } else { - if (c == 127u8) { - fputhex2(fd, c); - } else { - fputcbyte(fd, c); - }; - }; - }; - }; - }; + fputcbyte(fd, c); }; }; i += 1; @@ -397,24 +381,15 @@ export fn tokprint(fd: i32, t: *tok) void = { fputcbyte(fd, ' '); fputsstr(fd, tokname(t.kind)); - if (t.kind == tkind.TK_IDENT) { + switch (t.kind) { + case tkind.TK_IDENT, tkind.TK_STR, tkind.TK_ERR: fputcbyte(fd, ' '); fputq(fd, ttext.ptr, ttext.len); - } else { if (t.kind == tkind.TK_STR) { - fputcbyte(fd, ' '); - fputq(fd, ttext.ptr, ttext.len); - } else { if (t.kind == tkind.TK_ERR) { - fputcbyte(fd, ' '); - fputq(fd, ttext.ptr, ttext.len); - } else { if (t.kind == tkind.TK_INT) { + case tkind.TK_INT, tkind.TK_RUNE: fputcbyte(fd, ' '); let us: str = strconv.u64tos(t.uval, strconv.base.DEC); os.write(fd, us.ptr, us.len: u64); - } else { if (t.kind == tkind.TK_RUNE) { - fputcbyte(fd, ' '); - let us: str = strconv.u64tos(t.uval, strconv.base.DEC); - os.write(fd, us.ptr, us.len: u64); - };};};};}; + }; // tkind.TK_FLOAT is intentionally not handled here — %g formatting // won't byte-match across implementations. Diff fixtures must // be float-free until we implement a stable float formatter. diff --git a/lib/ww/lex/toktest.ww b/lib/ww/lex/toktest.ww index 55ce77f8..95d551b7 100644 --- a/lib/ww/lex/toktest.ww +++ b/lib/ww/lex/toktest.ww @@ -1,10 +1,17 @@ -// toktest — functional-equivalence pin for [[tokname]] and -// [[kwlookup]] after the if-ladder → switch fold (struct fold S1). +// toktest — functional-equivalence pin for [[tokname]], [[kwlookup]] +// (struct fold S1) and [[tokprint]]/fputq (struct fold S9, the +// if-ladder → switch folds in tok.ww). // Run with `ww run -I lib/ww/lex lib/ww/lex/toktest.ww`. // // tokname is checked against every tkind value (the full ladder the // switch replaced, plus the unknown-kind fallback); kwlookup is // checked against every keyword it recognises plus a non-keyword. +// tokprint is driven over a temp file: it pins the kind-dispatch +// switch (STR/IDENT/ERR vs INT/RUNE vs the value-less default) and, +// through the STR text, fputq's full escape switch (\\, ", \n, \t, +// \r, the c<0x20 and c==0x7f \xNN arms, and the printable tail) — +// branches the 990_selfhost corpus does not exercise (source tokens +// hold raw `\`+`n`, never a literal control byte). // A non-zero exit pinpoints the failing row (signalled + 10), same // convention as asciitest. `package main` + bare `import tok/lex` // mirrors wwdump (the only other external lex consumer). @@ -171,8 +178,76 @@ fn checkkw(s: str, want: tkind) void = { signalled = 231; checkkw("fns", tkind.TK_NONE); }; +// checkprint — tokprint `t` to a freshly-rewound fd, read the bytes +// back, and assert they equal `want`. The fd is RDWR; we lseek to 0 +// before each write so earlier (possibly longer) content past want.len +// is irrelevant — only want.len bytes from offset 0 are compared. +fn checkprint(fd: i32, t: *tok, want: str) void = { + if (os.lseek(fd, 0i64, os.whence.SET) != 0i64) { fail(); }; + tokprint(fd, t); + if (os.lseek(fd, 0i64, os.whence.SET) != 0i64) { fail(); }; + let rbuf: [256]u8; + let z: i32 = 0; + for (z < 256) { rbuf[z] = 0u8; z += 1; }; + let rd: i64 = os.read(fd, &rbuf[0], want.len: u64); + if (rd != want.len: i64) { fail(); }; + let j: i32 = 0; + for (j < want.len) { + if (rbuf[j] != want.ptr[j]) { fail(); }; + j += 1; + }; +}; + +// One token per fputq/tokprint dispatch branch. The STR text packs +// every fputq escape: \\ " \n \t \r, a c<0x20 byte (0x01), c==0x7f, +// and a printable ('A'). `want` spells the exact emitted line. +@test fn tokprint_cases() void = { + let flags: os.flag = os.flag.RDWR | os.flag.CREATE | os.flag.TRUNC; + let fd: i32 = os.open("/tmp/ww_s9_tok.tmp", flags, 384i32); + if (fd < 0) { fail(); }; + + let t: tok; + t.file = "t"; + t.line = 1; + t.col = 1; + + signalled = 300; + t.kind = tkind.TK_STR; + t.text = "\\\"\n\t\r\x01\x7fA"; + checkprint(fd, &t, "t:1:1 STR \"\\\\\\\"\\n\\t\\r\\x01\\x7fA\"\n"); + + signalled = 301; + t.kind = tkind.TK_IDENT; + t.text = "name"; + checkprint(fd, &t, "t:1:1 IDENT \"name\"\n"); + + signalled = 302; + t.kind = tkind.TK_ERR; + t.text = "oops"; + checkprint(fd, &t, "t:1:1 ERR \"oops\"\n"); + + signalled = 303; + t.kind = tkind.TK_INT; + t.uval = 42u64; + checkprint(fd, &t, "t:1:1 INT 42\n"); + + signalled = 304; + t.kind = tkind.TK_RUNE; + t.uval = 65u64; + checkprint(fd, &t, "t:1:1 RUNE 65\n"); + + // Default arm: a kind outside the switch appends no value. + signalled = 305; + t.kind = tkind.TK_NONE; + checkprint(fd, &t, "t:1:1 \n"); + + if (os.close(fd) != 0) { fail(); }; + if (os.remove("/tmp/ww_s9_tok.tmp") != 0) { fail(); }; +}; + export fn main() i32 = { signalled = 1; tokname_cases(); signalled = 2; kwlookup_cases(); + signalled = 3; tokprint_cases(); return 0; }; diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index 64f75874..b0f98f9e 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -6710,33 +6710,17 @@ fn fputq(fd: i32, p: *u8, n: i32) void = { let i: i32 = 0; for (i < n) { let c: u8 = p[i]; - if (c == '\\') { - fputsstr(fd, "\\\\"); - } else { - if (c == '"') { - fputsstr(fd, "\\\""); + switch (c) { + case '\\': fputsstr(fd, "\\\\"); + case '"': fputsstr(fd, "\\\""); + case '\n': fputsstr(fd, "\\n"); + case '\t': fputsstr(fd, "\\t"); + case '\r': fputsstr(fd, "\\r"); + case: + if (c < ' ' || c == 127u8) { + fputhex2(fd, c); } else { - if (c == '\n') { - fputsstr(fd, "\\n"); - } else { - if (c == '\t') { - fputsstr(fd, "\\t"); - } else { - if (c == '\r') { - fputsstr(fd, "\\r"); - } else { - if (c < ' ') { - fputhex2(fd, c); - } else { - if (c == 127u8) { - fputhex2(fd, c); - } else { - fputcbyte(fd, c); - }; - }; - }; - }; - }; + fputcbyte(fd, c); }; }; i += 1; @@ -6770,24 +6754,15 @@ export fn tokprint(fd: i32, t: *tok) void = { fputcbyte(fd, ' '); fputsstr(fd, tokname(t.kind)); - if (t.kind == tkind.TK_IDENT) { + switch (t.kind) { + case tkind.TK_IDENT, tkind.TK_STR, tkind.TK_ERR: fputcbyte(fd, ' '); fputq(fd, ttext.ptr, ttext.len); - } else { if (t.kind == tkind.TK_STR) { - fputcbyte(fd, ' '); - fputq(fd, ttext.ptr, ttext.len); - } else { if (t.kind == tkind.TK_ERR) { - fputcbyte(fd, ' '); - fputq(fd, ttext.ptr, ttext.len); - } else { if (t.kind == tkind.TK_INT) { + case tkind.TK_INT, tkind.TK_RUNE: fputcbyte(fd, ' '); let us: str = strconv.u64tos(t.uval, strconv.base.DEC); os.write(fd, us.ptr, us.len: u64); - } else { if (t.kind == tkind.TK_RUNE) { - fputcbyte(fd, ' '); - let us: str = strconv.u64tos(t.uval, strconv.base.DEC); - os.write(fd, us.ptr, us.len: u64); - };};};};}; + }; // tkind.TK_FLOAT is intentionally not handled here — %g formatting // won't byte-match across implementations. Diff fixtures must // be float-free until we implement a stable float formatter. diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index ad5e6a3e..229edcee 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -6710,33 +6710,17 @@ fn fputq(fd: i32, p: *u8, n: i32) void = { let i: i32 = 0; for (i < n) { let c: u8 = p[i]; - if (c == '\\') { - fputsstr(fd, "\\\\"); - } else { - if (c == '"') { - fputsstr(fd, "\\\""); + switch (c) { + case '\\': fputsstr(fd, "\\\\"); + case '"': fputsstr(fd, "\\\""); + case '\n': fputsstr(fd, "\\n"); + case '\t': fputsstr(fd, "\\t"); + case '\r': fputsstr(fd, "\\r"); + case: + if (c < ' ' || c == 127u8) { + fputhex2(fd, c); } else { - if (c == '\n') { - fputsstr(fd, "\\n"); - } else { - if (c == '\t') { - fputsstr(fd, "\\t"); - } else { - if (c == '\r') { - fputsstr(fd, "\\r"); - } else { - if (c < ' ') { - fputhex2(fd, c); - } else { - if (c == 127u8) { - fputhex2(fd, c); - } else { - fputcbyte(fd, c); - }; - }; - }; - }; - }; + fputcbyte(fd, c); }; }; i += 1; @@ -6770,24 +6754,15 @@ export fn tokprint(fd: i32, t: *tok) void = { fputcbyte(fd, ' '); fputsstr(fd, tokname(t.kind)); - if (t.kind == tkind.TK_IDENT) { + switch (t.kind) { + case tkind.TK_IDENT, tkind.TK_STR, tkind.TK_ERR: fputcbyte(fd, ' '); fputq(fd, ttext.ptr, ttext.len); - } else { if (t.kind == tkind.TK_STR) { - fputcbyte(fd, ' '); - fputq(fd, ttext.ptr, ttext.len); - } else { if (t.kind == tkind.TK_ERR) { - fputcbyte(fd, ' '); - fputq(fd, ttext.ptr, ttext.len); - } else { if (t.kind == tkind.TK_INT) { + case tkind.TK_INT, tkind.TK_RUNE: fputcbyte(fd, ' '); let us: str = strconv.u64tos(t.uval, strconv.base.DEC); os.write(fd, us.ptr, us.len: u64); - } else { if (t.kind == tkind.TK_RUNE) { - fputcbyte(fd, ' '); - let us: str = strconv.u64tos(t.uval, strconv.base.DEC); - os.write(fd, us.ptr, us.len: u64); - };};};};}; + }; // tkind.TK_FLOAT is intentionally not handled here — %g formatting // won't byte-match across implementations. Diff fixtures must // be float-free until we implement a stable float formatter.