wcc/ww: tag sep-built dotted-path packages by full path not leaf (#57)
A separately-compiled package's primary body was emitted under a bare `//ww:module-reset`, so its own `package <leaf>;` clause set curmod to the leaf (e.g. utf8) while the importer spliced the .wwi under the full `//ww:module encoding.utf8` — definer mangled `utf8.X`, importer wanted `encoding.utf8.X`, unresolved. Thread the dotted path through the directive: `//ww:module-reset <path>` sets curmod to the dotted path (imported stays 0, so the root `fn main` stays bare per #32), and the body's package clause is demoted to a leaf==last-component assertion instead of overwriting curmod. Aligns sep-build to the M1 path-mangle model; only the SEP emitter changes (the combined build_one arm is untouched, so all combined byte-id gates hold). Both stages mirrored. Commit-6 broad-soak prerequisite. Gate 989_sepdotpath_run sep-builds a 2-level dotted package and proves definer==importer qualification + single-component non-vacuity, cs==ww.
This commit is contained in:
@@ -63,6 +63,11 @@ type lex = struct {
|
||||
// lexnext emits TK_MODPATH carrying this dotted path (M1 #22).
|
||||
modpathset: i32,
|
||||
modpath: str,
|
||||
// a `//ww:module-reset <path>` directive was seen; the next TK_MODRESET
|
||||
// carries this dotted path so the sep primary body mangles on the path,
|
||||
// not its leaf clause (#57).
|
||||
modresetpathset: i32,
|
||||
modresetpath: str,
|
||||
};
|
||||
|
||||
export fn lexinit(l: *lex, file: str, src: *u8, len: u64) void = {
|
||||
@@ -75,6 +80,7 @@ export fn lexinit(l: *lex, file: str, src: *u8, len: u64) void = {
|
||||
l.errs = 0;
|
||||
l.modreset = 0;
|
||||
l.modpathset = 0;
|
||||
l.modresetpathset = 0;
|
||||
};
|
||||
|
||||
// srcb — byte at offset; helper that lifts the cast out of indexing.
|
||||
@@ -168,7 +174,42 @@ fn skipws(l: *lex) bool = {
|
||||
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 (af < 0) { l.modreset = 1; }
|
||||
else { if (af == ' ' || af == '\t') {
|
||||
// `//ww:module-reset <path>` — sep
|
||||
// primary body tagged by its full
|
||||
// dotted import path (#57).
|
||||
let k: u64 =
|
||||
(pre.len + rest.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;
|
||||
};
|
||||
l.modreset = 1;
|
||||
if (k > s0) {
|
||||
let view: str;
|
||||
view.ptr =
|
||||
l.src + l.lpos + s0;
|
||||
view.len = (k - s0): i32;
|
||||
l.modresetpath =
|
||||
strings.dup(view);
|
||||
l.modresetpathset = 1;
|
||||
};
|
||||
}; }; };
|
||||
};
|
||||
} else { if (nx == ' ' || nx == '\t') {
|
||||
// `//ww:module <path>` — M1 import boundary.
|
||||
@@ -709,6 +750,11 @@ export fn lexnext(l: *lex, out: *tok) void = {
|
||||
if (l.modreset != 0) {
|
||||
l.modreset = 0;
|
||||
emitsimple(&start, tkind.TK_MODRESET, out);
|
||||
// path-carrying reset → text=path (#57); bare reset → text empty
|
||||
if (l.modresetpathset != 0) {
|
||||
l.modresetpathset = 0;
|
||||
out.text = l.modresetpath;
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (l.modpathset != 0) {
|
||||
|
||||
@@ -48,6 +48,11 @@ type parser = struct {
|
||||
// decls stamp nmod=pathmod and imported=1, and the in-file `package`
|
||||
// clause is an assertion. "" means inactive (root/primary).
|
||||
pathmod: str,
|
||||
// #57: active `//ww:module-reset <path>` dotted path. Mangles decls on
|
||||
// the path WITHOUT imported=1 (primary-ness for -c and the #32 bare-main
|
||||
// rule stay intact), and the in-file `package` clause asserts (leaf ==
|
||||
// last component) instead of overwriting curmod. "" means inactive.
|
||||
resetmod: str,
|
||||
};
|
||||
|
||||
fn refill(p: *parser) void = {
|
||||
@@ -68,6 +73,7 @@ export fn parserinit(p: *parser, l: *lex) void = {
|
||||
p.errs = 0;
|
||||
p.nocast = 0;
|
||||
p.pathmod = "";
|
||||
p.resetmod = "";
|
||||
refill(p);
|
||||
};
|
||||
|
||||
@@ -414,14 +420,18 @@ export fn parsefile(p: *parser) *node = {
|
||||
let name: str;
|
||||
expectident(p, &name);
|
||||
expecttok(p, tkind.TK_SEMI, "expected ';' after module name");
|
||||
if (p.pathmod.len != 0) {
|
||||
if (p.pathmod.len != 0 || p.resetmod.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, ".");
|
||||
// overwrite the path-derived module. #57 extends this
|
||||
// to the sep primary-reset path (resetmod): the dotted
|
||||
// reset path is authoritative, the clause asserts.
|
||||
let active: str = p.pathmod;
|
||||
if (p.pathmod.len == 0) { active = p.resetmod; };
|
||||
let (pre, post) = strings.rcut(active, ".");
|
||||
let last: str = post;
|
||||
if (post.len == 0) { last = p.pathmod; };
|
||||
if (post.len == 0) { last = active; };
|
||||
if (strings.compare(name, last) != 0) {
|
||||
errmsg(p, "package does not match import path");
|
||||
};
|
||||
@@ -437,6 +447,7 @@ export fn parsefile(p: *parser) *node = {
|
||||
if (p.curkind == tkind.TK_MODPATH) {
|
||||
p.pathmod = p.curtext;
|
||||
p.curmod = p.curtext;
|
||||
p.resetmod = "";
|
||||
advance(p);
|
||||
continue;
|
||||
};
|
||||
@@ -449,12 +460,25 @@ export fn parsefile(p: *parser) *node = {
|
||||
// directive after a mid-file `package` would strip subsequent
|
||||
// decls to bare — that usage is deliberate-only.
|
||||
if (p.curkind == tkind.TK_MODRESET) {
|
||||
// #57: a path-carrying reset (sep primary body) mangles decls
|
||||
// on the dotted path so definer == importer, but leaves
|
||||
// imported==0 (curmod set, pathmod "") so -c primary-ness and
|
||||
// the #32 bare-main rule are intact; the body's `package`
|
||||
// clause then asserts (resetmod). A bare reset is the
|
||||
// root/package-less boundary: curmod "" → bare, today's path.
|
||||
let rp: str = p.curtext;
|
||||
advance(p);
|
||||
let empty: str;
|
||||
empty.ptr = nil;
|
||||
empty.len = 0;
|
||||
p.curmod = empty;
|
||||
p.pathmod = "";
|
||||
if (rp.len != 0) {
|
||||
p.curmod = rp;
|
||||
p.resetmod = rp;
|
||||
} else {
|
||||
let empty: str;
|
||||
empty.ptr = nil;
|
||||
empty.len = 0;
|
||||
p.curmod = empty;
|
||||
p.resetmod = "";
|
||||
};
|
||||
continue;
|
||||
};
|
||||
let attrs = parseattrs(p);
|
||||
|
||||
Reference in New Issue
Block a user