selfhost: mirror enum tokens + AST + parsetype branch
Parses byte-identical to the C frontend on enum sources (verified via `diff` of wwdump vs wwdump_ww -a on an enum-using fixture). The selfhost side reserves the slot in the AST and TY_* enums so later check.ww and cgen mirror work doesn't shift numeric IDs. Codegen-side enum support (member-value folding in cgdot, enum↔int pass-through in cgtypeassert) is deferred — current selfhost sources don't use enum, so 990_selfhost / 995_self_rebuild stay green. main.combined.ww in wwdump/ and w6c/ regenerated by `ww build` as a side effect of `make wwstage`.
This commit is contained in:
@@ -91,8 +91,10 @@ def N_TYPEASSERT: i32 = 61;
|
||||
def N_VOIDLIT: i32 = 62;
|
||||
def N_TBANG: i32 = 63;
|
||||
def N_YIELD: i32 = 64;
|
||||
def N_TENUM: i32 = 65;
|
||||
def N_TENUMMEMBER: i32 = 66;
|
||||
|
||||
def N_LAST: i32 = 65;
|
||||
def N_LAST: i32 = 67;
|
||||
|
||||
// ---- Node -------------------------------------------------------------
|
||||
|
||||
@@ -196,6 +198,8 @@ fn nkname(k: i32) str = {
|
||||
if (k == N_VOIDLIT) { return "voidlit"; };
|
||||
if (k == N_TBANG) { return "tbang"; };
|
||||
if (k == N_YIELD) { return "yield"; };
|
||||
if (k == N_TENUM) { return "tenum"; };
|
||||
if (k == N_TENUMMEMBER) { return "tenummember"; };
|
||||
if (k == N_LAST) { return "last"; };
|
||||
return "?";
|
||||
};
|
||||
@@ -281,6 +285,7 @@ fn pr(fd: i32, n: *node, d: i32) void = {
|
||||
n.kind == N_LET ||
|
||||
n.kind == N_TNAME ||
|
||||
n.kind == N_TFIELD ||
|
||||
n.kind == N_TENUMMEMBER ||
|
||||
n.kind == N_FIELD ||
|
||||
n.kind == N_ATTR
|
||||
) {
|
||||
|
||||
1014
lib/ww/lex/lex.combined.ww
Normal file
1014
lib/ww/lex/lex.combined.ww
Normal file
File diff suppressed because it is too large
Load Diff
@@ -111,8 +111,9 @@ def TK_FATARROW: i32 = 81;
|
||||
def TK_IS: i32 = 82;
|
||||
def TK_VOID: i32 = 83;
|
||||
def TK_YIELD: i32 = 84;
|
||||
def TK_ENUM: i32 = 85;
|
||||
|
||||
def TK_LAST: i32 = 85;
|
||||
def TK_LAST: i32 = 86;
|
||||
|
||||
// ---- Pos / Tok --------------------------------------------------------
|
||||
//
|
||||
@@ -166,6 +167,7 @@ export fn kwlookup(p: *u8, n: i32) i32 = {
|
||||
if (streqn(p, "def", n)) { return TK_DEF; };
|
||||
if (streqn(p, "defer", n)) { return TK_DEFER; };
|
||||
if (streqn(p, "else", n)) { return TK_ELSE; };
|
||||
if (streqn(p, "enum", n)) { return TK_ENUM; };
|
||||
if (streqn(p, "export", n)) { return TK_EXPORT; };
|
||||
if (streqn(p, "false", n)) { return TK_FALSE; };
|
||||
if (streqn(p, "fn", n)) { return TK_FN; };
|
||||
@@ -232,6 +234,7 @@ export fn tokname(k: i32) str = {
|
||||
if (k == TK_MATCH) { return "match"; };
|
||||
if (k == TK_CONST) { return "const"; };
|
||||
if (k == TK_UNDER) { return "_"; };
|
||||
if (k == TK_ENUM) { return "enum"; };
|
||||
|
||||
if (k == TK_LPAREN) { return "("; };
|
||||
if (k == TK_RPAREN) { return ")"; };
|
||||
|
||||
@@ -192,6 +192,40 @@ fn parsetype(p: *parser) *node = {
|
||||
return n;
|
||||
};
|
||||
|
||||
if (p.curkind == TK_ENUM) {
|
||||
// `enum [storage] { NAME [= expr], ... }`
|
||||
// Storage defaults to i32 (lhs == nil). Each member is an
|
||||
// N_TENUMMEMBER with str=name and lhs = value expr or nil
|
||||
// (auto-increment when omitted).
|
||||
advance(p);
|
||||
let n: *node = newnode(p.a, N_TENUM, pf, pl, pc);
|
||||
if (p.curkind != TK_LBRACE) {
|
||||
n.lhs = parsetype(p);
|
||||
};
|
||||
expecttok(p, TK_LBRACE, "expected '{' after enum");
|
||||
let mhead: *node = nil;
|
||||
let mtail: *node = nil;
|
||||
for (p.curkind != TK_RBRACE) {
|
||||
if (p.curkind == TK_EOF) { break; };
|
||||
let mpf: str = p.curfile;
|
||||
let mpl: i32 = p.curline;
|
||||
let mpc: i32 = p.curcol;
|
||||
let m: *node = newnode(p.a, N_TENUMMEMBER, mpf, mpl, mpc);
|
||||
let mid: str;
|
||||
expectident(p, &mid);
|
||||
m.str = mid;
|
||||
if (accepttok(p, TK_ASSIGN)) {
|
||||
m.lhs = parseexpr(p);
|
||||
};
|
||||
if (mhead == nil) { mhead = m; mtail = m; }
|
||||
else { mtail.next = m; mtail = m; };
|
||||
if (!accepttok(p, TK_COMMA)) { break; };
|
||||
};
|
||||
expecttok(p, TK_RBRACE, "expected '}' after enum members");
|
||||
n.list = mhead;
|
||||
return n;
|
||||
};
|
||||
|
||||
if (p.curkind == TK_VOID) {
|
||||
// `void` keyword in type-expr context — emit as N_TNAME so
|
||||
// resolution treats it like any other primitive name.
|
||||
|
||||
@@ -49,6 +49,10 @@ def TY_UNTYPED_STR: i32 = 31;
|
||||
def TY_UNTYPED_RUNE: i32 = 32;
|
||||
def TY_UNTYPED_BOOL: i32 = 33;
|
||||
def TY_UNTYPED_NIL: i32 = 34;
|
||||
// Appended at the tail to keep prior TY_* values stable — they're
|
||||
// mirrored in cmd/wcc/ww.h and the selfhost selfcheck depends on
|
||||
// matching numeric layout.
|
||||
def TY_ENUM: i32 = 35;
|
||||
|
||||
// ---- tinfo / tfield / tparam -----------------------------------------
|
||||
|
||||
@@ -223,6 +227,7 @@ export fn typeisint(t: *tinfo) bool = {
|
||||
if (k == TY_RUNE){ return true; };
|
||||
if (k == TY_UNTYPED_INT) { return true; };
|
||||
if (k == TY_UNTYPED_RUNE) { return true; };
|
||||
if (k == TY_ENUM) { return typeisint(t.sub); };
|
||||
if (k == TY_NAMED) { return typeisint(t.under); };
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -546,8 +546,9 @@ def TK_FATARROW: i32 = 81;
|
||||
def TK_IS: i32 = 82;
|
||||
def TK_VOID: i32 = 83;
|
||||
def TK_YIELD: i32 = 84;
|
||||
def TK_ENUM: i32 = 85;
|
||||
|
||||
def TK_LAST: i32 = 85;
|
||||
def TK_LAST: i32 = 86;
|
||||
|
||||
// ---- Pos / Tok --------------------------------------------------------
|
||||
//
|
||||
@@ -601,6 +602,7 @@ export fn kwlookup(p: *u8, n: i32) i32 = {
|
||||
if (streqn(p, "def", n)) { return TK_DEF; };
|
||||
if (streqn(p, "defer", n)) { return TK_DEFER; };
|
||||
if (streqn(p, "else", n)) { return TK_ELSE; };
|
||||
if (streqn(p, "enum", n)) { return TK_ENUM; };
|
||||
if (streqn(p, "export", n)) { return TK_EXPORT; };
|
||||
if (streqn(p, "false", n)) { return TK_FALSE; };
|
||||
if (streqn(p, "fn", n)) { return TK_FN; };
|
||||
@@ -667,6 +669,7 @@ export fn tokname(k: i32) str = {
|
||||
if (k == TK_MATCH) { return "match"; };
|
||||
if (k == TK_CONST) { return "const"; };
|
||||
if (k == TK_UNDER) { return "_"; };
|
||||
if (k == TK_ENUM) { return "enum"; };
|
||||
|
||||
if (k == TK_LPAREN) { return "("; };
|
||||
if (k == TK_RPAREN) { return ")"; };
|
||||
@@ -1736,8 +1739,10 @@ def N_TYPEASSERT: i32 = 61;
|
||||
def N_VOIDLIT: i32 = 62;
|
||||
def N_TBANG: i32 = 63;
|
||||
def N_YIELD: i32 = 64;
|
||||
def N_TENUM: i32 = 65;
|
||||
def N_TENUMMEMBER: i32 = 66;
|
||||
|
||||
def N_LAST: i32 = 65;
|
||||
def N_LAST: i32 = 67;
|
||||
|
||||
// ---- Node -------------------------------------------------------------
|
||||
|
||||
@@ -1841,6 +1846,8 @@ fn nkname(k: i32) str = {
|
||||
if (k == N_VOIDLIT) { return "voidlit"; };
|
||||
if (k == N_TBANG) { return "tbang"; };
|
||||
if (k == N_YIELD) { return "yield"; };
|
||||
if (k == N_TENUM) { return "tenum"; };
|
||||
if (k == N_TENUMMEMBER) { return "tenummember"; };
|
||||
if (k == N_LAST) { return "last"; };
|
||||
return "?";
|
||||
};
|
||||
@@ -1926,6 +1933,7 @@ fn pr(fd: i32, n: *node, d: i32) void = {
|
||||
n.kind == N_LET ||
|
||||
n.kind == N_TNAME ||
|
||||
n.kind == N_TFIELD ||
|
||||
n.kind == N_TENUMMEMBER ||
|
||||
n.kind == N_FIELD ||
|
||||
n.kind == N_ATTR
|
||||
) {
|
||||
@@ -3074,6 +3082,40 @@ fn parsetype(p: *parser) *node = {
|
||||
return n;
|
||||
};
|
||||
|
||||
if (p.curkind == TK_ENUM) {
|
||||
// `enum [storage] { NAME [= expr], ... }`
|
||||
// Storage defaults to i32 (lhs == nil). Each member is an
|
||||
// N_TENUMMEMBER with str=name and lhs = value expr or nil
|
||||
// (auto-increment when omitted).
|
||||
advance(p);
|
||||
let n: *node = newnode(p.a, N_TENUM, pf, pl, pc);
|
||||
if (p.curkind != TK_LBRACE) {
|
||||
n.lhs = parsetype(p);
|
||||
};
|
||||
expecttok(p, TK_LBRACE, "expected '{' after enum");
|
||||
let mhead: *node = nil;
|
||||
let mtail: *node = nil;
|
||||
for (p.curkind != TK_RBRACE) {
|
||||
if (p.curkind == TK_EOF) { break; };
|
||||
let mpf: str = p.curfile;
|
||||
let mpl: i32 = p.curline;
|
||||
let mpc: i32 = p.curcol;
|
||||
let m: *node = newnode(p.a, N_TENUMMEMBER, mpf, mpl, mpc);
|
||||
let mid: str;
|
||||
expectident(p, &mid);
|
||||
m.str = mid;
|
||||
if (accepttok(p, TK_ASSIGN)) {
|
||||
m.lhs = parseexpr(p);
|
||||
};
|
||||
if (mhead == nil) { mhead = m; mtail = m; }
|
||||
else { mtail.next = m; mtail = m; };
|
||||
if (!accepttok(p, TK_COMMA)) { break; };
|
||||
};
|
||||
expecttok(p, TK_RBRACE, "expected '}' after enum members");
|
||||
n.list = mhead;
|
||||
return n;
|
||||
};
|
||||
|
||||
if (p.curkind == TK_VOID) {
|
||||
// `void` keyword in type-expr context — emit as N_TNAME so
|
||||
// resolution treats it like any other primitive name.
|
||||
@@ -3316,6 +3358,10 @@ def TY_UNTYPED_STR: i32 = 31;
|
||||
def TY_UNTYPED_RUNE: i32 = 32;
|
||||
def TY_UNTYPED_BOOL: i32 = 33;
|
||||
def TY_UNTYPED_NIL: i32 = 34;
|
||||
// Appended at the tail to keep prior TY_* values stable — they're
|
||||
// mirrored in cmd/wcc/ww.h and the selfhost selfcheck depends on
|
||||
// matching numeric layout.
|
||||
def TY_ENUM: i32 = 35;
|
||||
|
||||
// ---- tinfo / tfield / tparam -----------------------------------------
|
||||
|
||||
@@ -3490,6 +3536,7 @@ export fn typeisint(t: *tinfo) bool = {
|
||||
if (k == TY_RUNE){ return true; };
|
||||
if (k == TY_UNTYPED_INT) { return true; };
|
||||
if (k == TY_UNTYPED_RUNE) { return true; };
|
||||
if (k == TY_ENUM) { return typeisint(t.sub); };
|
||||
if (k == TY_NAMED) { return typeisint(t.under); };
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -546,8 +546,9 @@ def TK_FATARROW: i32 = 81;
|
||||
def TK_IS: i32 = 82;
|
||||
def TK_VOID: i32 = 83;
|
||||
def TK_YIELD: i32 = 84;
|
||||
def TK_ENUM: i32 = 85;
|
||||
|
||||
def TK_LAST: i32 = 85;
|
||||
def TK_LAST: i32 = 86;
|
||||
|
||||
// ---- Pos / Tok --------------------------------------------------------
|
||||
//
|
||||
@@ -601,6 +602,7 @@ export fn kwlookup(p: *u8, n: i32) i32 = {
|
||||
if (streqn(p, "def", n)) { return TK_DEF; };
|
||||
if (streqn(p, "defer", n)) { return TK_DEFER; };
|
||||
if (streqn(p, "else", n)) { return TK_ELSE; };
|
||||
if (streqn(p, "enum", n)) { return TK_ENUM; };
|
||||
if (streqn(p, "export", n)) { return TK_EXPORT; };
|
||||
if (streqn(p, "false", n)) { return TK_FALSE; };
|
||||
if (streqn(p, "fn", n)) { return TK_FN; };
|
||||
@@ -667,6 +669,7 @@ export fn tokname(k: i32) str = {
|
||||
if (k == TK_MATCH) { return "match"; };
|
||||
if (k == TK_CONST) { return "const"; };
|
||||
if (k == TK_UNDER) { return "_"; };
|
||||
if (k == TK_ENUM) { return "enum"; };
|
||||
|
||||
if (k == TK_LPAREN) { return "("; };
|
||||
if (k == TK_RPAREN) { return ")"; };
|
||||
@@ -1736,8 +1739,10 @@ def N_TYPEASSERT: i32 = 61;
|
||||
def N_VOIDLIT: i32 = 62;
|
||||
def N_TBANG: i32 = 63;
|
||||
def N_YIELD: i32 = 64;
|
||||
def N_TENUM: i32 = 65;
|
||||
def N_TENUMMEMBER: i32 = 66;
|
||||
|
||||
def N_LAST: i32 = 65;
|
||||
def N_LAST: i32 = 67;
|
||||
|
||||
// ---- Node -------------------------------------------------------------
|
||||
|
||||
@@ -1841,6 +1846,8 @@ fn nkname(k: i32) str = {
|
||||
if (k == N_VOIDLIT) { return "voidlit"; };
|
||||
if (k == N_TBANG) { return "tbang"; };
|
||||
if (k == N_YIELD) { return "yield"; };
|
||||
if (k == N_TENUM) { return "tenum"; };
|
||||
if (k == N_TENUMMEMBER) { return "tenummember"; };
|
||||
if (k == N_LAST) { return "last"; };
|
||||
return "?";
|
||||
};
|
||||
@@ -1926,6 +1933,7 @@ fn pr(fd: i32, n: *node, d: i32) void = {
|
||||
n.kind == N_LET ||
|
||||
n.kind == N_TNAME ||
|
||||
n.kind == N_TFIELD ||
|
||||
n.kind == N_TENUMMEMBER ||
|
||||
n.kind == N_FIELD ||
|
||||
n.kind == N_ATTR
|
||||
) {
|
||||
@@ -3074,6 +3082,40 @@ fn parsetype(p: *parser) *node = {
|
||||
return n;
|
||||
};
|
||||
|
||||
if (p.curkind == TK_ENUM) {
|
||||
// `enum [storage] { NAME [= expr], ... }`
|
||||
// Storage defaults to i32 (lhs == nil). Each member is an
|
||||
// N_TENUMMEMBER with str=name and lhs = value expr or nil
|
||||
// (auto-increment when omitted).
|
||||
advance(p);
|
||||
let n: *node = newnode(p.a, N_TENUM, pf, pl, pc);
|
||||
if (p.curkind != TK_LBRACE) {
|
||||
n.lhs = parsetype(p);
|
||||
};
|
||||
expecttok(p, TK_LBRACE, "expected '{' after enum");
|
||||
let mhead: *node = nil;
|
||||
let mtail: *node = nil;
|
||||
for (p.curkind != TK_RBRACE) {
|
||||
if (p.curkind == TK_EOF) { break; };
|
||||
let mpf: str = p.curfile;
|
||||
let mpl: i32 = p.curline;
|
||||
let mpc: i32 = p.curcol;
|
||||
let m: *node = newnode(p.a, N_TENUMMEMBER, mpf, mpl, mpc);
|
||||
let mid: str;
|
||||
expectident(p, &mid);
|
||||
m.str = mid;
|
||||
if (accepttok(p, TK_ASSIGN)) {
|
||||
m.lhs = parseexpr(p);
|
||||
};
|
||||
if (mhead == nil) { mhead = m; mtail = m; }
|
||||
else { mtail.next = m; mtail = m; };
|
||||
if (!accepttok(p, TK_COMMA)) { break; };
|
||||
};
|
||||
expecttok(p, TK_RBRACE, "expected '}' after enum members");
|
||||
n.list = mhead;
|
||||
return n;
|
||||
};
|
||||
|
||||
if (p.curkind == TK_VOID) {
|
||||
// `void` keyword in type-expr context — emit as N_TNAME so
|
||||
// resolution treats it like any other primitive name.
|
||||
@@ -3316,6 +3358,10 @@ def TY_UNTYPED_STR: i32 = 31;
|
||||
def TY_UNTYPED_RUNE: i32 = 32;
|
||||
def TY_UNTYPED_BOOL: i32 = 33;
|
||||
def TY_UNTYPED_NIL: i32 = 34;
|
||||
// Appended at the tail to keep prior TY_* values stable — they're
|
||||
// mirrored in cmd/wcc/ww.h and the selfhost selfcheck depends on
|
||||
// matching numeric layout.
|
||||
def TY_ENUM: i32 = 35;
|
||||
|
||||
// ---- tinfo / tfield / tparam -----------------------------------------
|
||||
|
||||
@@ -3490,6 +3536,7 @@ export fn typeisint(t: *tinfo) bool = {
|
||||
if (k == TY_RUNE){ return true; };
|
||||
if (k == TY_UNTYPED_INT) { return true; };
|
||||
if (k == TY_UNTYPED_RUNE) { return true; };
|
||||
if (k == TY_ENUM) { return typeisint(t.sub); };
|
||||
if (k == TY_NAMED) { return typeisint(t.under); };
|
||||
return false;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user