Switch symbol mangling from the import leaf clause to the full dotted import path for directory packages; single-file imports keep package-clause mangling (isdir-gate: imported<=>directory-import). The root build unit's fn main stays bare, every other top-level decl mangles, closing #31's duplicate-main hazard by construction (#32). Both stages, byte-identical. Single commit, not split: the bare rename (f244af3) is red on its own because it unmasks cross-module resolution gaps that do not reproduce pre-M1, so the fixes are intrinsic to making the rename correct. Included: wwstage fnret/fnparamslookupmod map import alias->path (#199b cross-module union-variant scrutinee resolved the wrong fn's union); cstage use_path prefers the referencing module's import for an ambiguous leaf alias (sha256 crypto.math vs strconv math). Tests table-driven: 989_m1mangle_run/_sym, 989_m1union_run (gate-visible per-arm exit codes + cs==ww byte-id).
This commit is contained in:
@@ -137,13 +137,17 @@ type node = struct {
|
||||
type_: *void, // filled in by checker; type.ww treats it as *tinfo
|
||||
tsuffix: str, // typed numeric literal suffix ("i32", "u64", ...)
|
||||
nmod: str, // originating module from `// MODULE: foo`; "" if none
|
||||
usepath: str, // on an N_USE: full dotted IMPORT path vs leaf alias
|
||||
// in `str` (M1 #22); "" otherwise
|
||||
imported: i32, // M1 #22: decl reached via `//ww:module <path>` import
|
||||
// boundary (vs root/primary); gates root-only bare main
|
||||
};
|
||||
|
||||
export fn newnode(k: nkind, file: str, line: i32, col: i32) *node = {
|
||||
// fval cast-init: 990's wwdump TK_FLOAT diff requires this file
|
||||
// to tokenise identically through C and ww (lex.ww:382 has the
|
||||
// same workaround for the cstage %g-formats vs ww-skips divergence).
|
||||
let n: *node = alloc(node{kind=k, file=file, line=line, col=col, op=tkind.TK_NONE, str="", uval=0u64, fval=0: f64, lhs=nil, rhs=nil, cond=nil, body=nil, els=nil, list=nil, next=nil, attr=nil, exported=0, type_=nil, tsuffix="", nmod=""})!;
|
||||
let n: *node = alloc(node{kind=k, file=file, line=line, col=col, op=tkind.TK_NONE, str="", uval=0u64, fval=0: f64, lhs=nil, rhs=nil, cond=nil, body=nil, els=nil, list=nil, next=nil, attr=nil, exported=0, type_=nil, tsuffix="", nmod="", usepath="", imported=0})!;
|
||||
return n;
|
||||
};
|
||||
|
||||
|
||||
@@ -59,6 +59,10 @@ type lex = struct {
|
||||
// a `//ww:module-reset` directive was seen in the last skipped run;
|
||||
// lexnext emits TK_MODRESET before the next real token (#16 opt-B).
|
||||
modreset: i32,
|
||||
// a `//ww:module <path>` directive was seen in the last skipped run;
|
||||
// lexnext emits TK_MODPATH carrying this dotted path (M1 #22).
|
||||
modpathset: i32,
|
||||
modpath: str,
|
||||
};
|
||||
|
||||
export fn lexinit(l: *lex, file: str, src: *u8, len: u64) void = {
|
||||
@@ -70,6 +74,7 @@ export fn lexinit(l: *lex, file: str, src: *u8, len: u64) void = {
|
||||
l.col = 1;
|
||||
l.errs = 0;
|
||||
l.modreset = 0;
|
||||
l.modpathset = 0;
|
||||
};
|
||||
|
||||
// srcb — byte at offset; helper that lifts the cast out of indexing.
|
||||
@@ -137,19 +142,62 @@ fn skipws(l: *lex) bool = {
|
||||
// The body is then skipped like any comment.
|
||||
// Mirrors cstage lex.c skipws. Compare via lpeek
|
||||
// (no consume) so the skip loop below is unchanged.
|
||||
let dir: str = "ww:module-reset";
|
||||
let pre: str = "ww:module";
|
||||
let di: i32 = 0;
|
||||
let matched: bool = true;
|
||||
for (di < dir.len) {
|
||||
if (lpeek(l, di: u64) != dir[di]: i32) {
|
||||
for (di < pre.len) {
|
||||
if (lpeek(l, di: u64) != pre[di]: i32) {
|
||||
matched = false; break;
|
||||
};
|
||||
di += 1;
|
||||
};
|
||||
if (matched) {
|
||||
let nx: i32 = lpeek(l, dir.len: u64);
|
||||
if (nx == '\n') { l.modreset = 1; }
|
||||
else { if (nx < 0) { l.modreset = 1; }; };
|
||||
let nx: i32 = lpeek(l, pre.len: u64);
|
||||
if (nx == '-') {
|
||||
let rest: str = "-reset";
|
||||
let rj: i32 = 0;
|
||||
let rm: bool = true;
|
||||
for (rj < rest.len) {
|
||||
if (lpeek(l, (pre.len + rj): u64)
|
||||
!= rest[rj]: i32) {
|
||||
rm = false; break;
|
||||
};
|
||||
rj += 1;
|
||||
};
|
||||
if (rm) {
|
||||
let af: i32 = lpeek(l,
|
||||
(pre.len + rest.len): u64);
|
||||
if (af == '\n') { l.modreset = 1; }
|
||||
else { if (af < 0) { l.modreset = 1; }; };
|
||||
};
|
||||
} else { if (nx == ' ' || nx == '\t') {
|
||||
// `//ww:module <path>` — M1 import boundary.
|
||||
let k: u64 = pre.len: u64;
|
||||
for (true) {
|
||||
let sc: i32 = lpeek(l, k);
|
||||
if (sc == ' ' || sc == '\t') {
|
||||
k += 1u64; continue;
|
||||
};
|
||||
break;
|
||||
};
|
||||
let s0: u64 = k;
|
||||
for (true) {
|
||||
let pc: i32 = lpeek(l, k);
|
||||
if (pc < 0) { break; };
|
||||
if (pc == '\n' || pc == '\r'
|
||||
|| pc == ' ' || pc == '\t') {
|
||||
break;
|
||||
};
|
||||
k += 1u64;
|
||||
};
|
||||
if (k > s0) {
|
||||
let view: str;
|
||||
view.ptr = l.src + l.lpos + s0;
|
||||
view.len = (k - s0): i32;
|
||||
l.modpath = strings.dup(view);
|
||||
l.modpathset = 1;
|
||||
};
|
||||
}; };
|
||||
};
|
||||
for (true) {
|
||||
let cx: i32 = lpeek(l, 0u64);
|
||||
@@ -663,6 +711,12 @@ export fn lexnext(l: *lex, out: *tok) void = {
|
||||
emitsimple(&start, tkind.TK_MODRESET, out);
|
||||
return;
|
||||
};
|
||||
if (l.modpathset != 0) {
|
||||
l.modpathset = 0;
|
||||
emitsimple(&start, tkind.TK_MODPATH, out);
|
||||
out.text = l.modpath;
|
||||
return;
|
||||
};
|
||||
if (!more) {
|
||||
emitsimple(&start, tkind.TK_EOF, out);
|
||||
return;
|
||||
|
||||
@@ -121,7 +121,10 @@ type tkind = enum i32 {
|
||||
TK_MODRESET = 87, // `//ww:module-reset` driver bundle boundary:
|
||||
// reset curmod to "" before a package-less file
|
||||
// (#16 option-B; cstage TK_MODRESET twin)
|
||||
TK_LAST = 88,
|
||||
TK_MODPATH = 88, // `//ww:module <dotted-path>` driver import
|
||||
// boundary; decls mangle on the path, not the
|
||||
// leaf `package` clause (M1 #22; cstage twin)
|
||||
TK_LAST = 89,
|
||||
};
|
||||
|
||||
// ---- Pos / Tok --------------------------------------------------------
|
||||
@@ -244,6 +247,7 @@ export fn tokname(k: tkind) str = {
|
||||
case tkind.TK_ENUM: return "enum";
|
||||
case tkind.TK_MODULE: return "package";
|
||||
case tkind.TK_MODRESET: return "//ww:module-reset";
|
||||
case tkind.TK_MODPATH: return "//ww:module";
|
||||
|
||||
case tkind.TK_LPAREN: return "(";
|
||||
case tkind.TK_RPAREN: return ")";
|
||||
|
||||
@@ -129,11 +129,13 @@ fn checkname(k: tkind, want: str) void = {
|
||||
checkname(tkind.TK_FATARROW, "=>");
|
||||
|
||||
checkname(tkind.TK_MODRESET, "//ww:module-reset");
|
||||
checkname(tkind.TK_MODPATH, "//ww:module");
|
||||
checkname(tkind.TK_LAST, "<last>");
|
||||
|
||||
// Unknown kind → the post-switch fallback. TK_LAST is the highest
|
||||
// named value (88); 89 is out of band, exercising the "<?>" tail.
|
||||
checkname(89: tkind, "<?>");
|
||||
// named value (89, after TK_MODPATH=88 landed); 90 is out of band,
|
||||
// exercising the "<?>" tail.
|
||||
checkname(90: tkind, "<?>");
|
||||
};
|
||||
|
||||
fn checkkw(s: str, want: tkind) void = {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
package parse;
|
||||
|
||||
import os;
|
||||
import strings;
|
||||
import tok;
|
||||
|
||||
// `import encoding.utf8;` — the driver resolves the dotted path to
|
||||
@@ -17,13 +18,19 @@ fn parseuse(p: *parser) *node = {
|
||||
advance(p); // past `use`
|
||||
let n: *node = newnode(nkind.N_USE, pf, pl, pc);
|
||||
n.nmod = p.curmod;
|
||||
// M1 #22: accumulate the full dotted import path (n.usepath) for the
|
||||
// checker's path-keyed module match; n.str stays the leaf alias the
|
||||
// user writes (`utf8.x`).
|
||||
let leaf: str;
|
||||
expectident(p, &leaf);
|
||||
let path: str = leaf;
|
||||
for (p.curkind == tkind.TK_DOT) {
|
||||
advance(p); // past `.`
|
||||
expectident(p, &leaf);
|
||||
path = strings.concat(path, ".", leaf);
|
||||
};
|
||||
n.str = leaf;
|
||||
n.usepath = path;
|
||||
expecttok(p, tkind.TK_SEMI, "expected ';' after use");
|
||||
return n;
|
||||
};
|
||||
|
||||
@@ -16,6 +16,7 @@ package parse;
|
||||
// dir-enum when callers `import parse;` (which dir-enums
|
||||
// lib/ww/parse/).
|
||||
import os;
|
||||
import strings;
|
||||
import tok;
|
||||
|
||||
type parser = struct {
|
||||
@@ -43,6 +44,10 @@ type parser = struct {
|
||||
// multi-file streams successive `module` decls mark per-file
|
||||
// section boundaries. Mirrors cstage Parser.curmod.
|
||||
curmod: str,
|
||||
// M1 #22: active `//ww:module <path>` dotted import path. While set,
|
||||
// decls stamp nmod=pathmod and imported=1, and the in-file `package`
|
||||
// clause is an assertion. "" means inactive (root/primary).
|
||||
pathmod: str,
|
||||
};
|
||||
|
||||
fn refill(p: *parser) void = {
|
||||
@@ -62,6 +67,7 @@ export fn parserinit(p: *parser, l: *lex) void = {
|
||||
p.l = l;
|
||||
p.errs = 0;
|
||||
p.nocast = 0;
|
||||
p.pathmod = "";
|
||||
refill(p);
|
||||
};
|
||||
|
||||
@@ -408,7 +414,30 @@ export fn parsefile(p: *parser) *node = {
|
||||
let name: str;
|
||||
expectident(p, &name);
|
||||
expecttok(p, tkind.TK_SEMI, "expected ';' after module name");
|
||||
p.curmod = name;
|
||||
if (p.pathmod.len != 0) {
|
||||
// M1 #22: while an import path is active the in-file
|
||||
// `package` clause is an ASSERTION — its leaf must
|
||||
// equal the path's last component; it does NOT
|
||||
// overwrite the path-derived module.
|
||||
let (pre, post) = strings.rcut(p.pathmod, ".");
|
||||
let last: str = post;
|
||||
if (post.len == 0) { last = p.pathmod; };
|
||||
if (strings.compare(name, last) != 0) {
|
||||
errmsg(p, "package does not match import path");
|
||||
};
|
||||
} else {
|
||||
p.curmod = name;
|
||||
};
|
||||
continue;
|
||||
};
|
||||
// `//ww:module <path>` — M1 #22 import boundary. The following
|
||||
// file's decls mangle on the full dotted import path, not the
|
||||
// leaf `package` clause, and are flagged imported (gates the
|
||||
// root-only bare-`main` rule, #32).
|
||||
if (p.curkind == tkind.TK_MODPATH) {
|
||||
p.pathmod = p.curtext;
|
||||
p.curmod = p.curtext;
|
||||
advance(p);
|
||||
continue;
|
||||
};
|
||||
// `//ww:module-reset` — bundle boundary before a package-less
|
||||
@@ -425,6 +454,7 @@ export fn parsefile(p: *parser) *node = {
|
||||
empty.ptr = nil;
|
||||
empty.len = 0;
|
||||
p.curmod = empty;
|
||||
p.pathmod = "";
|
||||
continue;
|
||||
};
|
||||
let attrs = parseattrs(p);
|
||||
@@ -476,6 +506,9 @@ export fn parsefile(p: *parser) *node = {
|
||||
};};};};};};
|
||||
|
||||
if (d != nil) {
|
||||
// M1 #22: flag decls reached via an import-path boundary
|
||||
// (gates the root-only bare-`main` rule, #32).
|
||||
if (p.pathmod.len != 0) { d.imported = 1; };
|
||||
if (head == nil) {
|
||||
head = d;
|
||||
tail = d;
|
||||
|
||||
Reference in New Issue
Block a user