lib/ww/lex: kwlookup if-ladder -> [N]str parallel-table linear scan (kwtab, rides #18/#8)

Replace kwlookup's 30-arm streqn if-ladder with two parallel module-level
tables — kwnames: [30]str + kwkinds: [30]tkind — scanned linearly via
strings.compare, and delete the hand-rolled streqn. Rides #18 (module-
level [N]str static-init + relocations) for the kwnames data and #8
([N]enum element sizing) for the kwkinds[i] read, which is itself the
construct that surfaced the #8 elemsizeofc/array-init-store miscompile.

Two parallel arrays rather than a [N]kwent array-of-struct: a str inside
an aggregate element is the filed #18 follow-up. Mirrors the C twin
cmd/wcc/tok.c kwlookup (N=30, linear, no hash). Source re-applied on top
of the #8 cgen fix and regenerated fresh: the wwstage-compiled toktest
now runs correctly (exit 0, no segfault) where pre-#8 it smashed the
frame on the local [N]tkind init store.
This commit is contained in:
2026-06-03 14:50:18 +09:00
parent d39691a3d7
commit 5c1e9c0bfb
4 changed files with 178 additions and 160 deletions

View File

@@ -6386,6 +6386,7 @@ package lex;
import os;
import strconv;
import strings;
// ---- tkind ------------------------------------------------------------
// Mirror of the C `Tkind` enum in cmd/wcc/ww.h. Numeric values are
@@ -6522,50 +6523,46 @@ type tok = struct {
// ---- keyword lookup ---------------------------------------------------
fn streqn(a: *u8, b: str, n: i32) bool = {
if (b.len != n) { return false; };
let i: i32 = 0;
for (i < n) {
if (a[i] != b[i]) { return false; };
i += 1;
};
return true;
};
// keep alphabetised, so kwlookup is easy to read — mirrors the C twin
// cmd/wcc/tok.c:18-47. Two parallel arrays, not a [N]kwent array-of-
// struct: a str *inside* an aggregate element is the filed #18 follow-up
// (str-in-aggregate). `let`, not `def`: #18's module-level [N]str static
// init is scoped to the DATAW (`let`) directive (A_DATAR needs a DATAW
// holder); `def [N]str` is the same filed follow-up. kwkinds is a plain
// [N]tkind enum byte-array (pre-#18 path). Explicit [30], NOT [_]:
// [_] static-init silently miscompiles to a zero-length array in-tree
// (probe at cc69daf — len() returns 0, no diagnostic); filed bug.
let kwnames: [30]str = [
"as", "break", "case", "chan", "const", "continue", "def", "defer",
"else", "enum", "export", "false", "fn", "for", "if", "is",
"import", "let", "match", "nil", "package", "proc", "return",
"static", "struct", "switch", "true", "type", "void", "yield",
];
let kwkinds: [30]tkind = [
tkind.TK_AS, tkind.TK_BREAK, tkind.TK_CASE, tkind.TK_CHAN,
tkind.TK_CONST, tkind.TK_CONTINUE, tkind.TK_DEF, tkind.TK_DEFER,
tkind.TK_ELSE, tkind.TK_ENUM, tkind.TK_EXPORT, tkind.TK_FALSE,
tkind.TK_FN, tkind.TK_FOR, tkind.TK_IF, tkind.TK_IS,
tkind.TK_USE, tkind.TK_LET, tkind.TK_MATCH, tkind.TK_NIL,
tkind.TK_MODULE, tkind.TK_PROC, tkind.TK_RETURN, tkind.TK_STATIC,
tkind.TK_STRUCT, tkind.TK_SWITCH, tkind.TK_TRUE, tkind.TK_TYPE,
tkind.TK_VOID, tkind.TK_YIELD,
];
// kwlookup — returns the matching TK_* keyword kind for a byte run,
// or tkind.TK_NONE if it's an ordinary identifier. Linear search over a
// small alphabetised list, matching cmd/wcc/tok.c.
// or tkind.TK_NONE if it's an ordinary identifier. Linear scan over the
// table, matching cmd/wcc/tok.c:kwlookup (N=30, no hash).
export fn kwlookup(p: *u8, n: i32) tkind = {
if (streqn(p, "as", n)) { return tkind.TK_AS; };
if (streqn(p, "break", n)) { return tkind.TK_BREAK; };
if (streqn(p, "case", n)) { return tkind.TK_CASE; };
if (streqn(p, "chan", n)) { return tkind.TK_CHAN; };
if (streqn(p, "const", n)) { return tkind.TK_CONST; };
if (streqn(p, "continue", n)) { return tkind.TK_CONTINUE; };
if (streqn(p, "def", n)) { return tkind.TK_DEF; };
if (streqn(p, "defer", n)) { return tkind.TK_DEFER; };
if (streqn(p, "else", n)) { return tkind.TK_ELSE; };
if (streqn(p, "enum", n)) { return tkind.TK_ENUM; };
if (streqn(p, "export", n)) { return tkind.TK_EXPORT; };
if (streqn(p, "false", n)) { return tkind.TK_FALSE; };
if (streqn(p, "fn", n)) { return tkind.TK_FN; };
if (streqn(p, "for", n)) { return tkind.TK_FOR; };
if (streqn(p, "if", n)) { return tkind.TK_IF; };
if (streqn(p, "is", n)) { return tkind.TK_IS; };
if (streqn(p, "let", n)) { return tkind.TK_LET; };
if (streqn(p, "import", n)) { return tkind.TK_USE; };
if (streqn(p, "match", n)) { return tkind.TK_MATCH; };
if (streqn(p, "nil", n)) { return tkind.TK_NIL; };
if (streqn(p, "package", n)) { return tkind.TK_MODULE; };
if (streqn(p, "proc", n)) { return tkind.TK_PROC; };
if (streqn(p, "return", n)) { return tkind.TK_RETURN; };
if (streqn(p, "static", n)) { return tkind.TK_STATIC; };
if (streqn(p, "struct", n)) { return tkind.TK_STRUCT; };
if (streqn(p, "switch", n)) { return tkind.TK_SWITCH; };
if (streqn(p, "true", n)) { return tkind.TK_TRUE; };
if (streqn(p, "type", n)) { return tkind.TK_TYPE; };
if (streqn(p, "void", n)) { return tkind.TK_VOID; };
if (streqn(p, "yield", n)) { return tkind.TK_YIELD; };
let cand: str;
cand.ptr = p;
cand.len = n;
let i: i32 = 0;
for (i < len(kwnames)) {
if (strings.compare(cand, kwnames[i]) == 0) {
return kwkinds[i];
};
i += 1;
};
return tkind.TK_NONE;
};

View File

@@ -6386,6 +6386,7 @@ package lex;
import os;
import strconv;
import strings;
// ---- tkind ------------------------------------------------------------
// Mirror of the C `Tkind` enum in cmd/wcc/ww.h. Numeric values are
@@ -6522,50 +6523,46 @@ type tok = struct {
// ---- keyword lookup ---------------------------------------------------
fn streqn(a: *u8, b: str, n: i32) bool = {
if (b.len != n) { return false; };
let i: i32 = 0;
for (i < n) {
if (a[i] != b[i]) { return false; };
i += 1;
};
return true;
};
// keep alphabetised, so kwlookup is easy to read — mirrors the C twin
// cmd/wcc/tok.c:18-47. Two parallel arrays, not a [N]kwent array-of-
// struct: a str *inside* an aggregate element is the filed #18 follow-up
// (str-in-aggregate). `let`, not `def`: #18's module-level [N]str static
// init is scoped to the DATAW (`let`) directive (A_DATAR needs a DATAW
// holder); `def [N]str` is the same filed follow-up. kwkinds is a plain
// [N]tkind enum byte-array (pre-#18 path). Explicit [30], NOT [_]:
// [_] static-init silently miscompiles to a zero-length array in-tree
// (probe at cc69daf — len() returns 0, no diagnostic); filed bug.
let kwnames: [30]str = [
"as", "break", "case", "chan", "const", "continue", "def", "defer",
"else", "enum", "export", "false", "fn", "for", "if", "is",
"import", "let", "match", "nil", "package", "proc", "return",
"static", "struct", "switch", "true", "type", "void", "yield",
];
let kwkinds: [30]tkind = [
tkind.TK_AS, tkind.TK_BREAK, tkind.TK_CASE, tkind.TK_CHAN,
tkind.TK_CONST, tkind.TK_CONTINUE, tkind.TK_DEF, tkind.TK_DEFER,
tkind.TK_ELSE, tkind.TK_ENUM, tkind.TK_EXPORT, tkind.TK_FALSE,
tkind.TK_FN, tkind.TK_FOR, tkind.TK_IF, tkind.TK_IS,
tkind.TK_USE, tkind.TK_LET, tkind.TK_MATCH, tkind.TK_NIL,
tkind.TK_MODULE, tkind.TK_PROC, tkind.TK_RETURN, tkind.TK_STATIC,
tkind.TK_STRUCT, tkind.TK_SWITCH, tkind.TK_TRUE, tkind.TK_TYPE,
tkind.TK_VOID, tkind.TK_YIELD,
];
// kwlookup — returns the matching TK_* keyword kind for a byte run,
// or tkind.TK_NONE if it's an ordinary identifier. Linear search over a
// small alphabetised list, matching cmd/wcc/tok.c.
// or tkind.TK_NONE if it's an ordinary identifier. Linear scan over the
// table, matching cmd/wcc/tok.c:kwlookup (N=30, no hash).
export fn kwlookup(p: *u8, n: i32) tkind = {
if (streqn(p, "as", n)) { return tkind.TK_AS; };
if (streqn(p, "break", n)) { return tkind.TK_BREAK; };
if (streqn(p, "case", n)) { return tkind.TK_CASE; };
if (streqn(p, "chan", n)) { return tkind.TK_CHAN; };
if (streqn(p, "const", n)) { return tkind.TK_CONST; };
if (streqn(p, "continue", n)) { return tkind.TK_CONTINUE; };
if (streqn(p, "def", n)) { return tkind.TK_DEF; };
if (streqn(p, "defer", n)) { return tkind.TK_DEFER; };
if (streqn(p, "else", n)) { return tkind.TK_ELSE; };
if (streqn(p, "enum", n)) { return tkind.TK_ENUM; };
if (streqn(p, "export", n)) { return tkind.TK_EXPORT; };
if (streqn(p, "false", n)) { return tkind.TK_FALSE; };
if (streqn(p, "fn", n)) { return tkind.TK_FN; };
if (streqn(p, "for", n)) { return tkind.TK_FOR; };
if (streqn(p, "if", n)) { return tkind.TK_IF; };
if (streqn(p, "is", n)) { return tkind.TK_IS; };
if (streqn(p, "let", n)) { return tkind.TK_LET; };
if (streqn(p, "import", n)) { return tkind.TK_USE; };
if (streqn(p, "match", n)) { return tkind.TK_MATCH; };
if (streqn(p, "nil", n)) { return tkind.TK_NIL; };
if (streqn(p, "package", n)) { return tkind.TK_MODULE; };
if (streqn(p, "proc", n)) { return tkind.TK_PROC; };
if (streqn(p, "return", n)) { return tkind.TK_RETURN; };
if (streqn(p, "static", n)) { return tkind.TK_STATIC; };
if (streqn(p, "struct", n)) { return tkind.TK_STRUCT; };
if (streqn(p, "switch", n)) { return tkind.TK_SWITCH; };
if (streqn(p, "true", n)) { return tkind.TK_TRUE; };
if (streqn(p, "type", n)) { return tkind.TK_TYPE; };
if (streqn(p, "void", n)) { return tkind.TK_VOID; };
if (streqn(p, "yield", n)) { return tkind.TK_YIELD; };
let cand: str;
cand.ptr = p;
cand.len = n;
let i: i32 = 0;
for (i < len(kwnames)) {
if (strings.compare(cand, kwnames[i]) == 0) {
return kwkinds[i];
};
i += 1;
};
return tkind.TK_NONE;
};