wcc+ww: //ww:module-reset boundary directive; package-less files keep module "" (#16 PREP-main)
Driver twins emit the line-comment directive only before package-less files (peekpackage==nil); both lexers tokenize it (TK_MODRESET, appended =87 so existing token values hold) and both parsers reset curmod — a package-less file's decls get module "" instead of inheriting the last bundled package (the sticky-curmod leak, task #11). Withdrawn alternative: injecting 'package main' flips non-entry symbols bare->main-prefixed (FFI-visible, broke 764). Codegen-neutral by proof: bare symbols preserved, both stages emit byte-identical asm for a directive-bearing combined. Transitional until strict-package rejects package-less files outright. Includes 737 bad-deep pin for the PREP-peek >2048 edge + 904/toktest rows for the new token.
This commit is contained in:
@@ -95,6 +95,23 @@ skipws(Lex *l)
|
|||||||
}
|
}
|
||||||
if (c == '/' && lpeek(l, 1) == '/') {
|
if (c == '/' && lpeek(l, 1) == '/') {
|
||||||
lget(l); lget(l); /* consume '//' */
|
lget(l); lget(l); /* consume '//' */
|
||||||
|
/* #16 option-B: the driver emits `//ww:module-reset`
|
||||||
|
* before a package-less file's bytes; recognize the
|
||||||
|
* whole-line directive (without consuming differently)
|
||||||
|
* and flag it so lexnext emits TK_MODRESET. The body is
|
||||||
|
* then skipped like any comment. Mirrors the removed
|
||||||
|
* `// MODULE:` lexer directive. */
|
||||||
|
{
|
||||||
|
static const char dir[] = "ww:module-reset";
|
||||||
|
size_t i = 0;
|
||||||
|
while (dir[i] && lpeek(l, i) == dir[i])
|
||||||
|
i++;
|
||||||
|
if (dir[i] == '\0') {
|
||||||
|
int nx = lpeek(l, i);
|
||||||
|
if (nx == '\n' || nx < 0)
|
||||||
|
l->modreset = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
while ((c = lpeek(l, 0)) >= 0 && c != '\n')
|
while ((c = lpeek(l, 0)) >= 0 && c != '\n')
|
||||||
lget(l);
|
lget(l);
|
||||||
continue;
|
continue;
|
||||||
@@ -381,12 +398,15 @@ lexrune(Lex *l, Pos start)
|
|||||||
Tok
|
Tok
|
||||||
lexnext(Lex *l)
|
lexnext(Lex *l)
|
||||||
{
|
{
|
||||||
if (!skipws(l)) {
|
int more = skipws(l);
|
||||||
Pos p = lpos(l);
|
Pos start = lpos(l);
|
||||||
Tok t = (Tok){ TK_EOF, p, "", 0, {0}, TK_NONE };
|
/* A `//ww:module-reset` seen in the skipped run surfaces as its own
|
||||||
|
* token before the next real one (#16 option-B boundary reset). */
|
||||||
|
if (l->modreset) { l->modreset = 0; EMIT(TK_MODRESET); }
|
||||||
|
if (!more) {
|
||||||
|
Tok t = (Tok){ TK_EOF, start, "", 0, {0}, TK_NONE };
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
Pos start = lpos(l);
|
|
||||||
int c = lpeek(l, 0);
|
int c = lpeek(l, 0);
|
||||||
|
|
||||||
if (isidstart(c))
|
if (isidstart(c))
|
||||||
|
|||||||
@@ -1363,6 +1363,19 @@ parsefile(Parser *p)
|
|||||||
p->curmod = name;
|
p->curmod = name;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
/* `//ww:module-reset` — bundle boundary before a package-less
|
||||||
|
* file. Reset curmod to NULL so the file's decls (and its own
|
||||||
|
* `import os;`) are attributed to the primary module (""), not
|
||||||
|
* the preceding bundled package. Codegen-neutral: NULL curmod
|
||||||
|
* keeps bare symbols. (#16 option-B; closes task #11.)
|
||||||
|
* Driver-emitted ONLY before package-less files; a hand-placed
|
||||||
|
* directive after a mid-file `package` would strip subsequent
|
||||||
|
* decls to bare — that usage is deliberate-only. */
|
||||||
|
if (p->cur.kind == TK_MODRESET) {
|
||||||
|
advance(p);
|
||||||
|
p->curmod = NULL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
Node *attrs = parseattrs(p);
|
Node *attrs = parseattrs(p);
|
||||||
int exp = accept(p, TK_EXPORT);
|
int exp = accept(p, TK_EXPORT);
|
||||||
Node *d = NULL;
|
Node *d = NULL;
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ tokname(Tkind k)
|
|||||||
case TK_UNDER: return "_";
|
case TK_UNDER: return "_";
|
||||||
case TK_ENUM: return "enum";
|
case TK_ENUM: return "enum";
|
||||||
case TK_MODULE: return "package";
|
case TK_MODULE: return "package";
|
||||||
|
case TK_MODRESET: return "//ww:module-reset";
|
||||||
|
|
||||||
case TK_LPAREN: return "(";
|
case TK_LPAREN: return "(";
|
||||||
case TK_RPAREN: return ")";
|
case TK_RPAREN: return ")";
|
||||||
|
|||||||
@@ -180,6 +180,10 @@ typedef enum {
|
|||||||
TK_YIELD, /* `yield expr;` — value-return from a match arm */
|
TK_YIELD, /* `yield expr;` — value-return from a match arm */
|
||||||
TK_ENUM, /* Hare-style `enum [storage] { ... }` type form */
|
TK_ENUM, /* Hare-style `enum [storage] { ... }` type form */
|
||||||
TK_MODULE, /* `module foo;` — directory-as-module declaration */
|
TK_MODULE, /* `module foo;` — directory-as-module declaration */
|
||||||
|
TK_MODRESET, /* `//ww:module-reset` — driver bundle boundary: reset
|
||||||
|
* curmod to NULL before a package-less file's bytes
|
||||||
|
* (#16 option-B; the package-less-entry attribution fix
|
||||||
|
* that replaces the withdrawn `package main` inject). */
|
||||||
|
|
||||||
TK_LAST /* sentinel for tables */
|
TK_LAST /* sentinel for tables */
|
||||||
} Tkind;
|
} Tkind;
|
||||||
@@ -207,6 +211,9 @@ struct Lex {
|
|||||||
i32 col;
|
i32 col;
|
||||||
Arena *a; /* token-text arena */
|
Arena *a; /* token-text arena */
|
||||||
int errs;
|
int errs;
|
||||||
|
int modreset; /* a `//ww:module-reset` directive was seen in
|
||||||
|
* the last skipped run; lexnext emits TK_MODRESET
|
||||||
|
* before the next real token. */
|
||||||
};
|
};
|
||||||
|
|
||||||
void lexinit(Lex*, Arena*, const char *file, const char *src, u64 len);
|
void lexinit(Lex*, Arena*, const char *file, const char *src, u64 len);
|
||||||
|
|||||||
@@ -306,6 +306,19 @@ expand(FILE *out, const char *path, struct ImportSet *visited,
|
|||||||
else expand(out, ipath, visited, libdir);
|
else expand(out, ipath, visited, libdir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* #16 option-B: a package-less file's decls would otherwise inherit
|
||||||
|
* the preceding bundled module's sticky curmod (parser parse.c). Emit
|
||||||
|
* a curmod-reset boundary directive so the lexer/parser attribute the
|
||||||
|
* file to the primary module ("") — fixes the self-import false-fire
|
||||||
|
* and the leaked-prefix bug, codegen-neutral (bare symbols kept; not
|
||||||
|
* `package main`, which would main-prefix them). A packaged file's own
|
||||||
|
* `package` decl already sets curmod, so it needs nothing — keeping
|
||||||
|
* the directive out of every tracked combined.ww. (Task #11.) */
|
||||||
|
{
|
||||||
|
char pkg[128];
|
||||||
|
if (!peek_package(path, pkg, sizeof pkg))
|
||||||
|
fputs("//ww:module-reset\n", out);
|
||||||
|
}
|
||||||
rewind(in);
|
rewind(in);
|
||||||
int ch;
|
int ch;
|
||||||
while ((ch = fgetc(in)) != EOF) fputc(ch, out);
|
while ((ch = fgetc(in)) != EOF) fputc(ch, out);
|
||||||
|
|||||||
@@ -56,6 +56,9 @@ type lex = struct {
|
|||||||
line: i32,
|
line: i32,
|
||||||
col: i32,
|
col: i32,
|
||||||
errs: i32,
|
errs: i32,
|
||||||
|
// 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,
|
||||||
};
|
};
|
||||||
|
|
||||||
export fn lexinit(l: *lex, file: str, src: *u8, len: u64) void = {
|
export fn lexinit(l: *lex, file: str, src: *u8, len: u64) void = {
|
||||||
@@ -66,6 +69,7 @@ export fn lexinit(l: *lex, file: str, src: *u8, len: u64) void = {
|
|||||||
l.line = 1;
|
l.line = 1;
|
||||||
l.col = 1;
|
l.col = 1;
|
||||||
l.errs = 0;
|
l.errs = 0;
|
||||||
|
l.modreset = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// srcb — byte at offset; helper that lifts the cast out of indexing.
|
// srcb — byte at offset; helper that lifts the cast out of indexing.
|
||||||
@@ -127,6 +131,26 @@ fn skipws(l: *lex) bool = {
|
|||||||
let c2: i32 = lpeek(l, 1u64);
|
let c2: i32 = lpeek(l, 1u64);
|
||||||
if (c2 == '/') {
|
if (c2 == '/') {
|
||||||
lget(l); lget(l); // consume '//'
|
lget(l); lget(l); // consume '//'
|
||||||
|
// #16 opt-B: recognize the driver's curmod-reset
|
||||||
|
// boundary directive `//ww:module-reset` (whole
|
||||||
|
// line) and flag it; lexnext emits TK_MODRESET.
|
||||||
|
// 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 di: i32 = 0;
|
||||||
|
let matched: bool = true;
|
||||||
|
for (di < dir.len) {
|
||||||
|
if (lpeek(l, di: u64) != dir[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; }; };
|
||||||
|
};
|
||||||
for (true) {
|
for (true) {
|
||||||
let cx: i32 = lpeek(l, 0u64);
|
let cx: i32 = lpeek(l, 0u64);
|
||||||
if (cx < 0) { return false; };
|
if (cx < 0) { return false; };
|
||||||
@@ -629,12 +653,19 @@ export fn lexnext(l: *lex, out: *tok) void = {
|
|||||||
out.text = empty;
|
out.text = empty;
|
||||||
out.tsuffix = empty;
|
out.tsuffix = empty;
|
||||||
|
|
||||||
if (!skipws(l)) {
|
let more: bool = skipws(l);
|
||||||
let p: pos; curpos(l, &p);
|
let start: pos; curpos(l, &start);
|
||||||
emitsimple(&p, tkind.TK_EOF, out);
|
// A `//ww:module-reset` seen in the skipped run surfaces as its own
|
||||||
|
// token before the next real one (#16 opt-B boundary reset).
|
||||||
|
if (l.modreset != 0) {
|
||||||
|
l.modreset = 0;
|
||||||
|
emitsimple(&start, tkind.TK_MODRESET, out);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if (!more) {
|
||||||
|
emitsimple(&start, tkind.TK_EOF, out);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let start: pos; curpos(l, &start);
|
|
||||||
let c: i32 = lpeek(l, 0u64);
|
let c: i32 = lpeek(l, 0u64);
|
||||||
|
|
||||||
if (c >= 0) {
|
if (c >= 0) {
|
||||||
|
|||||||
@@ -118,7 +118,10 @@ type tkind = enum i32 {
|
|||||||
TK_YIELD = 84,
|
TK_YIELD = 84,
|
||||||
TK_ENUM = 85,
|
TK_ENUM = 85,
|
||||||
TK_MODULE = 86, // `module foo;` — directory-as-module decl
|
TK_MODULE = 86, // `module foo;` — directory-as-module decl
|
||||||
TK_LAST = 87,
|
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,
|
||||||
};
|
};
|
||||||
|
|
||||||
// ---- Pos / Tok --------------------------------------------------------
|
// ---- Pos / Tok --------------------------------------------------------
|
||||||
@@ -240,6 +243,7 @@ export fn tokname(k: tkind) str = {
|
|||||||
case tkind.TK_UNDER: return "_";
|
case tkind.TK_UNDER: return "_";
|
||||||
case tkind.TK_ENUM: return "enum";
|
case tkind.TK_ENUM: return "enum";
|
||||||
case tkind.TK_MODULE: return "package";
|
case tkind.TK_MODULE: return "package";
|
||||||
|
case tkind.TK_MODRESET: return "//ww:module-reset";
|
||||||
|
|
||||||
case tkind.TK_LPAREN: return "(";
|
case tkind.TK_LPAREN: return "(";
|
||||||
case tkind.TK_RPAREN: return ")";
|
case tkind.TK_RPAREN: return ")";
|
||||||
|
|||||||
@@ -128,11 +128,12 @@ fn checkname(k: tkind, want: str) void = {
|
|||||||
signalled = 185; checkname(tkind.TK_ARROW, "->");
|
signalled = 185; checkname(tkind.TK_ARROW, "->");
|
||||||
signalled = 186; checkname(tkind.TK_FATARROW, "=>");
|
signalled = 186; checkname(tkind.TK_FATARROW, "=>");
|
||||||
|
|
||||||
signalled = 187; checkname(tkind.TK_LAST, "<last>");
|
signalled = 187; checkname(tkind.TK_MODRESET, "//ww:module-reset");
|
||||||
|
signalled = 188; checkname(tkind.TK_LAST, "<last>");
|
||||||
|
|
||||||
// Unknown kind → the post-switch fallback. TK_LAST is the highest
|
// Unknown kind → the post-switch fallback. TK_LAST is the highest
|
||||||
// named value (87); 88 is out of band, exercising the "<?>" tail.
|
// named value (88); 89 is out of band, exercising the "<?>" tail.
|
||||||
signalled = 188; checkname(88: tkind, "<?>");
|
signalled = 189; checkname(89: tkind, "<?>");
|
||||||
};
|
};
|
||||||
|
|
||||||
fn checkkw(s: str, want: tkind) void = {
|
fn checkkw(s: str, want: tkind) void = {
|
||||||
|
|||||||
@@ -411,6 +411,22 @@ export fn parsefile(p: *parser) *node = {
|
|||||||
p.curmod = name;
|
p.curmod = name;
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
// `//ww:module-reset` — bundle boundary before a package-less
|
||||||
|
// file. Reset curmod to "" so the file's decls (and its own
|
||||||
|
// `import os;`) are attributed to the primary module, not the
|
||||||
|
// preceding bundled package. Codegen-neutral: "" curmod keeps
|
||||||
|
// bare symbols. (#16 option-B; closes task #11.)
|
||||||
|
// Driver-emitted ONLY before package-less files; a hand-placed
|
||||||
|
// directive after a mid-file `package` would strip subsequent
|
||||||
|
// decls to bare — that usage is deliberate-only.
|
||||||
|
if (p.curkind == tkind.TK_MODRESET) {
|
||||||
|
advance(p);
|
||||||
|
let empty: str;
|
||||||
|
empty.ptr = nil;
|
||||||
|
empty.len = 0;
|
||||||
|
p.curmod = empty;
|
||||||
|
continue;
|
||||||
|
};
|
||||||
let attrs = parseattrs(p);
|
let attrs = parseattrs(p);
|
||||||
let exported: i32 = 0;
|
let exported: i32 = 0;
|
||||||
if (p.curkind == tkind.TK_EXPORT) { exported = 1; advance(p); };
|
if (p.curkind == tkind.TK_EXPORT) { exported = 1; advance(p); };
|
||||||
|
|||||||
@@ -6441,7 +6441,10 @@ type tkind = enum i32 {
|
|||||||
TK_YIELD = 84,
|
TK_YIELD = 84,
|
||||||
TK_ENUM = 85,
|
TK_ENUM = 85,
|
||||||
TK_MODULE = 86, // `module foo;` — directory-as-module decl
|
TK_MODULE = 86, // `module foo;` — directory-as-module decl
|
||||||
TK_LAST = 87,
|
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,
|
||||||
};
|
};
|
||||||
|
|
||||||
// ---- Pos / Tok --------------------------------------------------------
|
// ---- Pos / Tok --------------------------------------------------------
|
||||||
@@ -6563,6 +6566,7 @@ export fn tokname(k: tkind) str = {
|
|||||||
case tkind.TK_UNDER: return "_";
|
case tkind.TK_UNDER: return "_";
|
||||||
case tkind.TK_ENUM: return "enum";
|
case tkind.TK_ENUM: return "enum";
|
||||||
case tkind.TK_MODULE: return "package";
|
case tkind.TK_MODULE: return "package";
|
||||||
|
case tkind.TK_MODRESET: return "//ww:module-reset";
|
||||||
|
|
||||||
case tkind.TK_LPAREN: return "(";
|
case tkind.TK_LPAREN: return "(";
|
||||||
case tkind.TK_RPAREN: return ")";
|
case tkind.TK_RPAREN: return ")";
|
||||||
@@ -6775,6 +6779,9 @@ type lex = struct {
|
|||||||
line: i32,
|
line: i32,
|
||||||
col: i32,
|
col: i32,
|
||||||
errs: i32,
|
errs: i32,
|
||||||
|
// 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,
|
||||||
};
|
};
|
||||||
|
|
||||||
export fn lexinit(l: *lex, file: str, src: *u8, len: u64) void = {
|
export fn lexinit(l: *lex, file: str, src: *u8, len: u64) void = {
|
||||||
@@ -6785,6 +6792,7 @@ export fn lexinit(l: *lex, file: str, src: *u8, len: u64) void = {
|
|||||||
l.line = 1;
|
l.line = 1;
|
||||||
l.col = 1;
|
l.col = 1;
|
||||||
l.errs = 0;
|
l.errs = 0;
|
||||||
|
l.modreset = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// srcb — byte at offset; helper that lifts the cast out of indexing.
|
// srcb — byte at offset; helper that lifts the cast out of indexing.
|
||||||
@@ -6846,6 +6854,26 @@ fn skipws(l: *lex) bool = {
|
|||||||
let c2: i32 = lpeek(l, 1u64);
|
let c2: i32 = lpeek(l, 1u64);
|
||||||
if (c2 == '/') {
|
if (c2 == '/') {
|
||||||
lget(l); lget(l); // consume '//'
|
lget(l); lget(l); // consume '//'
|
||||||
|
// #16 opt-B: recognize the driver's curmod-reset
|
||||||
|
// boundary directive `//ww:module-reset` (whole
|
||||||
|
// line) and flag it; lexnext emits TK_MODRESET.
|
||||||
|
// 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 di: i32 = 0;
|
||||||
|
let matched: bool = true;
|
||||||
|
for (di < dir.len) {
|
||||||
|
if (lpeek(l, di: u64) != dir[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; }; };
|
||||||
|
};
|
||||||
for (true) {
|
for (true) {
|
||||||
let cx: i32 = lpeek(l, 0u64);
|
let cx: i32 = lpeek(l, 0u64);
|
||||||
if (cx < 0) { return false; };
|
if (cx < 0) { return false; };
|
||||||
@@ -7348,12 +7376,19 @@ export fn lexnext(l: *lex, out: *tok) void = {
|
|||||||
out.text = empty;
|
out.text = empty;
|
||||||
out.tsuffix = empty;
|
out.tsuffix = empty;
|
||||||
|
|
||||||
if (!skipws(l)) {
|
let more: bool = skipws(l);
|
||||||
let p: pos; curpos(l, &p);
|
let start: pos; curpos(l, &start);
|
||||||
emitsimple(&p, tkind.TK_EOF, out);
|
// A `//ww:module-reset` seen in the skipped run surfaces as its own
|
||||||
|
// token before the next real one (#16 opt-B boundary reset).
|
||||||
|
if (l.modreset != 0) {
|
||||||
|
l.modreset = 0;
|
||||||
|
emitsimple(&start, tkind.TK_MODRESET, out);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if (!more) {
|
||||||
|
emitsimple(&start, tkind.TK_EOF, out);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let start: pos; curpos(l, &start);
|
|
||||||
let c: i32 = lpeek(l, 0u64);
|
let c: i32 = lpeek(l, 0u64);
|
||||||
|
|
||||||
if (c >= 0) {
|
if (c >= 0) {
|
||||||
@@ -8913,6 +8948,22 @@ export fn parsefile(p: *parser) *node = {
|
|||||||
p.curmod = name;
|
p.curmod = name;
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
// `//ww:module-reset` — bundle boundary before a package-less
|
||||||
|
// file. Reset curmod to "" so the file's decls (and its own
|
||||||
|
// `import os;`) are attributed to the primary module, not the
|
||||||
|
// preceding bundled package. Codegen-neutral: "" curmod keeps
|
||||||
|
// bare symbols. (#16 option-B; closes task #11.)
|
||||||
|
// Driver-emitted ONLY before package-less files; a hand-placed
|
||||||
|
// directive after a mid-file `package` would strip subsequent
|
||||||
|
// decls to bare — that usage is deliberate-only.
|
||||||
|
if (p.curkind == tkind.TK_MODRESET) {
|
||||||
|
advance(p);
|
||||||
|
let empty: str;
|
||||||
|
empty.ptr = nil;
|
||||||
|
empty.len = 0;
|
||||||
|
p.curmod = empty;
|
||||||
|
continue;
|
||||||
|
};
|
||||||
let attrs = parseattrs(p);
|
let attrs = parseattrs(p);
|
||||||
let exported: i32 = 0;
|
let exported: i32 = 0;
|
||||||
if (p.curkind == tkind.TK_EXPORT) { exported = 1; advance(p); };
|
if (p.curkind == tkind.TK_EXPORT) { exported = 1; advance(p); };
|
||||||
|
|||||||
@@ -3315,6 +3315,19 @@ fn expand(c: *expctx, pathcs: *u8) void = {
|
|||||||
i = j + 1u64;
|
i = j + 1u64;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// #16 option-B: a package-less file's decls would otherwise inherit
|
||||||
|
// the preceding bundled module's sticky curmod (parser parse.ww). Emit
|
||||||
|
// a curmod-reset boundary directive so the lexer/parser attribute the
|
||||||
|
// file to the primary module ("") — fixes the self-import false-fire
|
||||||
|
// and the leaked-prefix bug, codegen-neutral (bare symbols kept; not
|
||||||
|
// `package main`, which would main-prefix them). A packaged file's own
|
||||||
|
// `package` decl already sets curmod, so it needs nothing — keeping
|
||||||
|
// the directive out of every tracked combined.ww. (Task #11.)
|
||||||
|
if (peekpackage(pathcs) == nil) {
|
||||||
|
let d: str = "//ww:module-reset\n";
|
||||||
|
os.writeall(c.out, d.ptr, d.len: u64);
|
||||||
|
};
|
||||||
|
|
||||||
os.writeall(c.out, bufp, blen);
|
os.writeall(c.out, bufp, blen);
|
||||||
os.writeall(c.out, "\n".ptr, 1u64);
|
os.writeall(c.out, "\n".ptr, 1u64);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -529,6 +529,19 @@ fn expand(c: *expctx, pathcs: *u8) void = {
|
|||||||
i = j + 1u64;
|
i = j + 1u64;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// #16 option-B: a package-less file's decls would otherwise inherit
|
||||||
|
// the preceding bundled module's sticky curmod (parser parse.ww). Emit
|
||||||
|
// a curmod-reset boundary directive so the lexer/parser attribute the
|
||||||
|
// file to the primary module ("") — fixes the self-import false-fire
|
||||||
|
// and the leaked-prefix bug, codegen-neutral (bare symbols kept; not
|
||||||
|
// `package main`, which would main-prefix them). A packaged file's own
|
||||||
|
// `package` decl already sets curmod, so it needs nothing — keeping
|
||||||
|
// the directive out of every tracked combined.ww. (Task #11.)
|
||||||
|
if (peekpackage(pathcs) == nil) {
|
||||||
|
let d: str = "//ww:module-reset\n";
|
||||||
|
os.writeall(c.out, d.ptr, d.len: u64);
|
||||||
|
};
|
||||||
|
|
||||||
os.writeall(c.out, bufp, blen);
|
os.writeall(c.out, bufp, blen);
|
||||||
os.writeall(c.out, "\n".ptr, 1u64);
|
os.writeall(c.out, "\n".ptr, 1u64);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6441,7 +6441,10 @@ type tkind = enum i32 {
|
|||||||
TK_YIELD = 84,
|
TK_YIELD = 84,
|
||||||
TK_ENUM = 85,
|
TK_ENUM = 85,
|
||||||
TK_MODULE = 86, // `module foo;` — directory-as-module decl
|
TK_MODULE = 86, // `module foo;` — directory-as-module decl
|
||||||
TK_LAST = 87,
|
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,
|
||||||
};
|
};
|
||||||
|
|
||||||
// ---- Pos / Tok --------------------------------------------------------
|
// ---- Pos / Tok --------------------------------------------------------
|
||||||
@@ -6563,6 +6566,7 @@ export fn tokname(k: tkind) str = {
|
|||||||
case tkind.TK_UNDER: return "_";
|
case tkind.TK_UNDER: return "_";
|
||||||
case tkind.TK_ENUM: return "enum";
|
case tkind.TK_ENUM: return "enum";
|
||||||
case tkind.TK_MODULE: return "package";
|
case tkind.TK_MODULE: return "package";
|
||||||
|
case tkind.TK_MODRESET: return "//ww:module-reset";
|
||||||
|
|
||||||
case tkind.TK_LPAREN: return "(";
|
case tkind.TK_LPAREN: return "(";
|
||||||
case tkind.TK_RPAREN: return ")";
|
case tkind.TK_RPAREN: return ")";
|
||||||
@@ -6775,6 +6779,9 @@ type lex = struct {
|
|||||||
line: i32,
|
line: i32,
|
||||||
col: i32,
|
col: i32,
|
||||||
errs: i32,
|
errs: i32,
|
||||||
|
// 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,
|
||||||
};
|
};
|
||||||
|
|
||||||
export fn lexinit(l: *lex, file: str, src: *u8, len: u64) void = {
|
export fn lexinit(l: *lex, file: str, src: *u8, len: u64) void = {
|
||||||
@@ -6785,6 +6792,7 @@ export fn lexinit(l: *lex, file: str, src: *u8, len: u64) void = {
|
|||||||
l.line = 1;
|
l.line = 1;
|
||||||
l.col = 1;
|
l.col = 1;
|
||||||
l.errs = 0;
|
l.errs = 0;
|
||||||
|
l.modreset = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// srcb — byte at offset; helper that lifts the cast out of indexing.
|
// srcb — byte at offset; helper that lifts the cast out of indexing.
|
||||||
@@ -6846,6 +6854,26 @@ fn skipws(l: *lex) bool = {
|
|||||||
let c2: i32 = lpeek(l, 1u64);
|
let c2: i32 = lpeek(l, 1u64);
|
||||||
if (c2 == '/') {
|
if (c2 == '/') {
|
||||||
lget(l); lget(l); // consume '//'
|
lget(l); lget(l); // consume '//'
|
||||||
|
// #16 opt-B: recognize the driver's curmod-reset
|
||||||
|
// boundary directive `//ww:module-reset` (whole
|
||||||
|
// line) and flag it; lexnext emits TK_MODRESET.
|
||||||
|
// 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 di: i32 = 0;
|
||||||
|
let matched: bool = true;
|
||||||
|
for (di < dir.len) {
|
||||||
|
if (lpeek(l, di: u64) != dir[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; }; };
|
||||||
|
};
|
||||||
for (true) {
|
for (true) {
|
||||||
let cx: i32 = lpeek(l, 0u64);
|
let cx: i32 = lpeek(l, 0u64);
|
||||||
if (cx < 0) { return false; };
|
if (cx < 0) { return false; };
|
||||||
@@ -7348,12 +7376,19 @@ export fn lexnext(l: *lex, out: *tok) void = {
|
|||||||
out.text = empty;
|
out.text = empty;
|
||||||
out.tsuffix = empty;
|
out.tsuffix = empty;
|
||||||
|
|
||||||
if (!skipws(l)) {
|
let more: bool = skipws(l);
|
||||||
let p: pos; curpos(l, &p);
|
let start: pos; curpos(l, &start);
|
||||||
emitsimple(&p, tkind.TK_EOF, out);
|
// A `//ww:module-reset` seen in the skipped run surfaces as its own
|
||||||
|
// token before the next real one (#16 opt-B boundary reset).
|
||||||
|
if (l.modreset != 0) {
|
||||||
|
l.modreset = 0;
|
||||||
|
emitsimple(&start, tkind.TK_MODRESET, out);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if (!more) {
|
||||||
|
emitsimple(&start, tkind.TK_EOF, out);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let start: pos; curpos(l, &start);
|
|
||||||
let c: i32 = lpeek(l, 0u64);
|
let c: i32 = lpeek(l, 0u64);
|
||||||
|
|
||||||
if (c >= 0) {
|
if (c >= 0) {
|
||||||
@@ -8913,6 +8948,22 @@ export fn parsefile(p: *parser) *node = {
|
|||||||
p.curmod = name;
|
p.curmod = name;
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
// `//ww:module-reset` — bundle boundary before a package-less
|
||||||
|
// file. Reset curmod to "" so the file's decls (and its own
|
||||||
|
// `import os;`) are attributed to the primary module, not the
|
||||||
|
// preceding bundled package. Codegen-neutral: "" curmod keeps
|
||||||
|
// bare symbols. (#16 option-B; closes task #11.)
|
||||||
|
// Driver-emitted ONLY before package-less files; a hand-placed
|
||||||
|
// directive after a mid-file `package` would strip subsequent
|
||||||
|
// decls to bare — that usage is deliberate-only.
|
||||||
|
if (p.curkind == tkind.TK_MODRESET) {
|
||||||
|
advance(p);
|
||||||
|
let empty: str;
|
||||||
|
empty.ptr = nil;
|
||||||
|
empty.len = 0;
|
||||||
|
p.curmod = empty;
|
||||||
|
continue;
|
||||||
|
};
|
||||||
let attrs = parseattrs(p);
|
let attrs = parseattrs(p);
|
||||||
let exported: i32 = 0;
|
let exported: i32 = 0;
|
||||||
if (p.curkind == tkind.TK_EXPORT) { exported = 1; advance(p); };
|
if (p.curkind == tkind.TK_EXPORT) { exported = 1; advance(p); };
|
||||||
|
|||||||
@@ -133,6 +133,46 @@ main(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* bad-deep: same mismatch, but one file's `package` decl sits behind
|
||||||
|
* a >2048-byte comment header. Pins the PREP-peek whole-file read
|
||||||
|
* (#16): the old 2048-capped peek missed that decl, so the strict-
|
||||||
|
* same-package check skipped the file and the mismatch went
|
||||||
|
* undetected (build wrongly succeeded); the whole-file peek finds it
|
||||||
|
* → "differs from". Both stages. (reviewer-batcha pin.) */
|
||||||
|
{
|
||||||
|
const char *src = "test/wcc/data/direnum/bad_deep_entry.ww";
|
||||||
|
snprintf(errp, sizeof errp, "/tmp/direnum_%d_baddeep.err", getpid());
|
||||||
|
|
||||||
|
char cmd[2048];
|
||||||
|
snprintf(cmd, sizeof cmd, "%s build %s 2>%s >/dev/null",
|
||||||
|
ww, src, errp);
|
||||||
|
int rc = runwait(cmd);
|
||||||
|
total++;
|
||||||
|
if (rc == 0) {
|
||||||
|
fprintf(stderr, "737[bad-deep-cstage]: expected build failure, succeeded\n");
|
||||||
|
fail++;
|
||||||
|
} else if (!stderr_contains(errp, "differs from")) {
|
||||||
|
fprintf(stderr, "737[bad-deep-cstage]: stderr missing 'differs from'\n");
|
||||||
|
fail++;
|
||||||
|
}
|
||||||
|
unlink(errp);
|
||||||
|
|
||||||
|
if (access(ww_ww, X_OK) == 0) {
|
||||||
|
snprintf(cmd, sizeof cmd, "%s build %s 2>%s >/dev/null",
|
||||||
|
ww_ww, src, errp);
|
||||||
|
rc = runwait(cmd);
|
||||||
|
total++;
|
||||||
|
if (rc == 0) {
|
||||||
|
fprintf(stderr, "737[bad-deep-wwstage]: expected build failure, succeeded\n");
|
||||||
|
fail++;
|
||||||
|
} else if (!stderr_contains(errp, "differs from")) {
|
||||||
|
fprintf(stderr, "737[bad-deep-wwstage]: stderr missing 'differs from'\n");
|
||||||
|
fail++;
|
||||||
|
}
|
||||||
|
unlink(errp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (fail) {
|
if (fail) {
|
||||||
fprintf(stderr, "737_direnum: %d/%d fixtures failed\n", fail, total);
|
fprintf(stderr, "737_direnum: %d/%d fixtures failed\n", fail, total);
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
5
test/wcc/data/direnum/bad_deep_entry.ww
Normal file
5
test/wcc/data/direnum/bad_deep_entry.ww
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package main;
|
||||||
|
|
||||||
|
import bad_deep_pkg;
|
||||||
|
|
||||||
|
export fn main() i32 = { return 0; };
|
||||||
3
test/wcc/data/direnum/bad_deep_pkg/a.ww
Normal file
3
test/wcc/data/direnum/bad_deep_pkg/a.ww
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
package alpha;
|
||||||
|
|
||||||
|
fn aaa() i32 = { return 1; };
|
||||||
54
test/wcc/data/direnum/bad_deep_pkg/b.ww
Normal file
54
test/wcc/data/direnum/bad_deep_pkg/b.ww
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
// b.ww — #16 PREP-main peek-edge pin (reviewer-batcha).
|
||||||
|
// This file's `package beta;` decl sits behind a comment header
|
||||||
|
// longer than 2048 bytes ON PURPOSE: the old wwstage peekpackage
|
||||||
|
// capped its read at 2048 bytes and would MISS the package decl
|
||||||
|
// (return nil), so the strict-same-package dir-enum check would
|
||||||
|
// skip this file and the alpha/beta mismatch would go undetected
|
||||||
|
// (build wrongly succeeds). The PREP-peek whole-file read finds
|
||||||
|
// `package beta;`, so the mismatch fires 'differs from' and the
|
||||||
|
// build fails — which is what 737's bad_deep family asserts on
|
||||||
|
// both stages. Do not shorten this header below 2048 bytes.
|
||||||
|
//
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
// padding line to push the package decl past the 2048-byte peek window.
|
||||||
|
package beta;
|
||||||
|
|
||||||
|
fn bbb() i32 = { return 2; };
|
||||||
Reference in New Issue
Block a user