wcc,lib/ww/syntax: resolve qualified struct-literal pkg.Type{...} (#76)
The parser folded a qualified type pkg.Type into two different node shapes by position: declaration position collapsed it into one N_TNAME (resolved via the strrchr-leaf path), but literal position left an N_DOT chain that the struct-literal typeref handoff had no resolver arm for, so pkg.Type{...} rejected with "expected type expression".
Normalize the literal-position N_DOT chain into the same source-order N_TNAME the declaration path emits, reusing the existing resolver; no new checker arm. cstage flattens at parseprimary struct-lit handoff; wwstage (no token peek) folds dots in parsepostfix and normalizes there, guarding numeric tuple components and staying in the postfix loop so trailing ops still chain. Both stages emit identical N_STRUCTLIT(N_TNAME). Prereq for qualifying wcc syntax refs (#75).
This commit is contained in:
@@ -7249,6 +7249,31 @@ fn parsearglist(p: *parser, closekind: tkind, headout: **node) void = {
|
||||
*headout = head;
|
||||
};
|
||||
|
||||
// #76: flatten a qualified `pkg.Type` N_DOT chain into its source-order
|
||||
// dotted string ("a.b.C"), byte-identical to cstage parsetype's aprintf
|
||||
// accumulation (cmd/wcc/parse.c) and to flattendotstr in parse.c.
|
||||
fn flattendotstr(n: *node) str = {
|
||||
if (n.kind == nkind.N_IDENT) { return n.str; };
|
||||
return joindotted(flattendotstr(n.lhs), n.str);
|
||||
};
|
||||
|
||||
// #76: an N_DOT chain qualifies as a type path only when it is rooted at
|
||||
// a bare IDENT and every component is an identifier. cstage folds only
|
||||
// peek==TK_IDENT dots in parseprimary, so its chain is inherently
|
||||
// all-ident; ww folds ALL dots (incl tuple `t.0`) in parsepostfix, so we
|
||||
// guard out numeric-first-byte components here to restore exact symmetry.
|
||||
fn isqualpath(n: *node) bool = {
|
||||
if (n.kind != nkind.N_DOT) { return false; };
|
||||
let cur: *node = n;
|
||||
for (cur.kind == nkind.N_DOT) {
|
||||
if (cur.str.len < 1) { return false; };
|
||||
let c0: u8 = cur.str[0];
|
||||
if (c0 >= '0') { if (c0 <= '9') { return false; }; };
|
||||
cur = cur.lhs;
|
||||
};
|
||||
return cur.kind == nkind.N_IDENT;
|
||||
};
|
||||
|
||||
fn parsepostfix(p: *parser, lhs: *node) *node = {
|
||||
let cur: *node = lhs;
|
||||
for (true) {
|
||||
@@ -7334,6 +7359,51 @@ fn parsepostfix(p: *parser, lhs: *node) *node = {
|
||||
cur = n;
|
||||
continue;
|
||||
};
|
||||
// #76: qualified struct literal `pkg.Type{...}`. cstage folds
|
||||
// the dotted chain in parseprimary (peek) and hits `{` there;
|
||||
// the ww parser has no token-peek and folds dots HERE, so the
|
||||
// struct-lit handoff lives here too. Fire only on an all-ident
|
||||
// N_DOT chain (isqualpath); bare `Foo{` stays in parseprimary.
|
||||
// Build the literal IN-LOOP and `continue` (req a) so trailing
|
||||
// ops (`.f`, `[i]`, `()`, `as T`) still apply, matching cstage's
|
||||
// return-to-outer-parsepostfix. The flattened N_TNAME (req c:
|
||||
// source order) lets the checker's existing N_TNAME arm resolve.
|
||||
if (p.curkind == tkind.TK_LBRACE) {
|
||||
if (isqualpath(cur)) {
|
||||
let tn: *node = newnode(nkind.N_TNAME, pf, pl, pc);
|
||||
tn.str = flattendotstr(cur);
|
||||
advance(p);
|
||||
let s: *node = newnode(nkind.N_STRUCTLIT, pf, pl, pc);
|
||||
s.lhs = tn;
|
||||
let head: *node = nil;
|
||||
let tail: *node = nil;
|
||||
for (p.curkind != tkind.TK_RBRACE) {
|
||||
if (p.curkind == tkind.TK_EOF) { break; };
|
||||
if (p.curkind == tkind.TK_ELLIPSIS) {
|
||||
advance(p);
|
||||
s.op = tkind.TK_ELLIPSIS;
|
||||
break;
|
||||
};
|
||||
let fpf: str = p.curfile;
|
||||
let fpl: i32 = p.curline;
|
||||
let fpc: i32 = p.curcol;
|
||||
let id: str;
|
||||
expectident(p, &id);
|
||||
expecttok(p, tkind.TK_ASSIGN, "expected '=' in struct lit field");
|
||||
let v: *node = parseexpr(p);
|
||||
let f: *node = newnode(nkind.N_FIELD, fpf, fpl, fpc);
|
||||
f.str = id;
|
||||
f.lhs = v;
|
||||
if (head == nil) { head = f; tail = f; }
|
||||
else { tail.next = f; tail = f; };
|
||||
if (!accepttok(p, tkind.TK_COMMA)) { break; };
|
||||
};
|
||||
expecttok(p, tkind.TK_RBRACE, "expected '}' after struct literal");
|
||||
s.list = head;
|
||||
cur = s;
|
||||
continue;
|
||||
};
|
||||
};
|
||||
if (p.curkind == tkind.TK_COLON) {
|
||||
if (p.nocast != 0) {
|
||||
return cur;
|
||||
|
||||
@@ -7249,6 +7249,31 @@ fn parsearglist(p: *parser, closekind: tkind, headout: **node) void = {
|
||||
*headout = head;
|
||||
};
|
||||
|
||||
// #76: flatten a qualified `pkg.Type` N_DOT chain into its source-order
|
||||
// dotted string ("a.b.C"), byte-identical to cstage parsetype's aprintf
|
||||
// accumulation (cmd/wcc/parse.c) and to flattendotstr in parse.c.
|
||||
fn flattendotstr(n: *node) str = {
|
||||
if (n.kind == nkind.N_IDENT) { return n.str; };
|
||||
return joindotted(flattendotstr(n.lhs), n.str);
|
||||
};
|
||||
|
||||
// #76: an N_DOT chain qualifies as a type path only when it is rooted at
|
||||
// a bare IDENT and every component is an identifier. cstage folds only
|
||||
// peek==TK_IDENT dots in parseprimary, so its chain is inherently
|
||||
// all-ident; ww folds ALL dots (incl tuple `t.0`) in parsepostfix, so we
|
||||
// guard out numeric-first-byte components here to restore exact symmetry.
|
||||
fn isqualpath(n: *node) bool = {
|
||||
if (n.kind != nkind.N_DOT) { return false; };
|
||||
let cur: *node = n;
|
||||
for (cur.kind == nkind.N_DOT) {
|
||||
if (cur.str.len < 1) { return false; };
|
||||
let c0: u8 = cur.str[0];
|
||||
if (c0 >= '0') { if (c0 <= '9') { return false; }; };
|
||||
cur = cur.lhs;
|
||||
};
|
||||
return cur.kind == nkind.N_IDENT;
|
||||
};
|
||||
|
||||
fn parsepostfix(p: *parser, lhs: *node) *node = {
|
||||
let cur: *node = lhs;
|
||||
for (true) {
|
||||
@@ -7334,6 +7359,51 @@ fn parsepostfix(p: *parser, lhs: *node) *node = {
|
||||
cur = n;
|
||||
continue;
|
||||
};
|
||||
// #76: qualified struct literal `pkg.Type{...}`. cstage folds
|
||||
// the dotted chain in parseprimary (peek) and hits `{` there;
|
||||
// the ww parser has no token-peek and folds dots HERE, so the
|
||||
// struct-lit handoff lives here too. Fire only on an all-ident
|
||||
// N_DOT chain (isqualpath); bare `Foo{` stays in parseprimary.
|
||||
// Build the literal IN-LOOP and `continue` (req a) so trailing
|
||||
// ops (`.f`, `[i]`, `()`, `as T`) still apply, matching cstage's
|
||||
// return-to-outer-parsepostfix. The flattened N_TNAME (req c:
|
||||
// source order) lets the checker's existing N_TNAME arm resolve.
|
||||
if (p.curkind == tkind.TK_LBRACE) {
|
||||
if (isqualpath(cur)) {
|
||||
let tn: *node = newnode(nkind.N_TNAME, pf, pl, pc);
|
||||
tn.str = flattendotstr(cur);
|
||||
advance(p);
|
||||
let s: *node = newnode(nkind.N_STRUCTLIT, pf, pl, pc);
|
||||
s.lhs = tn;
|
||||
let head: *node = nil;
|
||||
let tail: *node = nil;
|
||||
for (p.curkind != tkind.TK_RBRACE) {
|
||||
if (p.curkind == tkind.TK_EOF) { break; };
|
||||
if (p.curkind == tkind.TK_ELLIPSIS) {
|
||||
advance(p);
|
||||
s.op = tkind.TK_ELLIPSIS;
|
||||
break;
|
||||
};
|
||||
let fpf: str = p.curfile;
|
||||
let fpl: i32 = p.curline;
|
||||
let fpc: i32 = p.curcol;
|
||||
let id: str;
|
||||
expectident(p, &id);
|
||||
expecttok(p, tkind.TK_ASSIGN, "expected '=' in struct lit field");
|
||||
let v: *node = parseexpr(p);
|
||||
let f: *node = newnode(nkind.N_FIELD, fpf, fpl, fpc);
|
||||
f.str = id;
|
||||
f.lhs = v;
|
||||
if (head == nil) { head = f; tail = f; }
|
||||
else { tail.next = f; tail = f; };
|
||||
if (!accepttok(p, tkind.TK_COMMA)) { break; };
|
||||
};
|
||||
expecttok(p, tkind.TK_RBRACE, "expected '}' after struct literal");
|
||||
s.list = head;
|
||||
cur = s;
|
||||
continue;
|
||||
};
|
||||
};
|
||||
if (p.curkind == tkind.TK_COLON) {
|
||||
if (p.nocast != 0) {
|
||||
return cur;
|
||||
|
||||
Reference in New Issue
Block a user