build: omit named test source operands

This commit is contained in:
2026-08-22 21:53:10 +09:00
parent 3922f1c34e
commit 2e5451a601
13 changed files with 1666 additions and 21 deletions

View File

@@ -410,6 +410,75 @@ fn skipws(l: *lex) bool = {
return false;
};
// Return the next initial import without lexing the first ordinary body token.
// Header trivia is still decoded, so reached NUL and comment diagnostics keep
// loader precedence; a malformed UTF-8 byte or middle BOM that begins the body
// is deliberately outside this pass.
export fn lexheaderimport(l: *lex, out: *tok) bool = {
for (true) {
if (l.lpos >= l.srclen) { return false; };
let c: i32 = srcb(l, l.lpos);
if (c == 0) {
lskipnul(l);
return false;
};
if ((c >= 128 && !utf8bytevalid(l.src, l.srclen, l.lpos))
|| bomat(l.src, l.srclen, l.lpos)) {
return false;
};
if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
lget(l);
continue;
};
if (c == '/' && l.lpos + 1u64 < l.srclen
&& srcb(l, l.lpos + 1u64) == '/') {
lget(l); lget(l);
linecomment(l);
if (l.errs != 0) { return false; };
continue;
};
if (c == '/' && l.lpos + 1u64 < l.srclen
&& srcb(l, l.lpos + 1u64) == '*') {
lget(l); lget(l);
let prev: i32 = -1;
for (true) {
let x: i32 = lget(l);
if (x < 0) {
let cp: pos;
curpos(l, &cp);
errat(l, &cp, "unterminated /* comment");
return false;
};
if (prev == '*' && x == '/') { break; };
prev = x;
};
if (l.errs != 0) { return false; };
continue;
};
break;
};
if (l.modreset != 0 || l.modpathset != 0) { return false; };
let kw: str = tokname(tkind.TK_USE);
if (l.srclen - l.lpos < kw.len: u64) { return false; };
let i: i32 = 0;
for (i < kw.len) {
if (srcb(l, l.lpos + (i: u64)) != kw[i]: i32) {
return false;
};
i += 1;
};
if (l.srclen - l.lpos > kw.len: u64) {
let next: i32 = srcb(l, l.lpos + (kw.len: u64));
if ((next >= 'a' && next <= 'z')
|| (next >= 'A' && next <= 'Z')
|| (next >= '0' && next <= '9') || next == '_') {
return false;
};
};
lexnext(l, out);
return out.kind == tkind.TK_USE;
};
fn parseint(p: *u8, n: u64, base: i32, ok: *bool) u64 = {
let v: u64 = 0u64;
let b: u64 = base: u64;

View File

@@ -57,9 +57,7 @@ export type parser = struct {
commandpackage: bool,
};
fn refill(p: *parser) void = {
let t: tok;
lexnext(p.l, &t);
fn installtok(p: *parser, t: *tok) void = {
p.curkind = t.kind;
p.curfile = t.file;
p.curline = t.line;
@@ -70,6 +68,19 @@ fn refill(p: *parser) void = {
p.curtsuffix = t.tsuffix;
};
fn refill(p: *parser) void = {
let t: tok;
lexnext(p.l, &t);
installtok(p, &t);
};
fn advanceheaderimport(p: *parser) bool = {
let t: tok;
if (!lexheaderimport(p.l, &t)) { return false; };
installtok(p, &t);
return true;
};
export fn parserinit(p: *parser, l: *lex) void = {
p.l = l;
p.errs = 0;
@@ -490,6 +501,121 @@ fn skipimportattrs(p: *parser) void = {
};
};
// Parse one initial import for the named-source header pass. Unlike the full
// recovery scanner, this stops on the first malformed header token and never
// consumes an ordinary declaration body.
fn parseheaderuse(p: *parser) *node = {
let pf: str = p.curfile;
let pl: i32 = p.curline;
let pc: i32 = p.curcol;
advance(p);
let n: *node = newnode(nkind.N_USE, pf, pl, pc);
n.usefile = p.curfile;
n.useline = p.curline;
n.usecol = p.curcol;
let alias: str;
let first: str;
if (p.curkind == tkind.TK_UNDER) {
n.useblank = 1;
advance(p);
};
if (p.curkind != tkind.TK_IDENT) {
errmsg(p, strings.concat("expected identifier, got ",
tokname(p.curkind)));
return n;
};
first = p.curtext;
advance(p);
if (n.useblank == 0 && p.curkind == tkind.TK_IDENT) {
alias = first;
first = p.curtext;
advance(p);
};
let leaf: str = first;
let path: str = leaf;
for (p.curkind == tkind.TK_DOT) {
advance(p);
if (p.curkind != tkind.TK_IDENT) {
errmsg(p, strings.concat("expected identifier, got ",
tokname(p.curkind)));
return n;
};
leaf = p.curtext;
path = strings.concat(path, ".", leaf);
advance(p);
};
if (n.useblank != 0) { n.str = ""; }
else { if (alias.len > 0) { n.str = alias; } else { n.str = leaf; }; };
n.usesource = path;
n.usepath = path;
n.usealias = alias;
if (p.curkind != tkind.TK_SEMI) {
errmsg(p, "expected ';' after import");
return n;
};
return n;
};
// A valid named source is loader-visible only through its initial package and
// contiguous import section. Keep parseimports for graph-bearing source
// recovery; this boundary deliberately ignores every ordinary declaration.
export fn parsepackageheader(p: *parser) *node = {
let f: *node = newnode(nkind.N_FILE, p.curfile, 1, 1);
let head: *node = nil;
let tail: *node = nil;
for (p.curkind == tkind.TK_MODPATH
|| p.curkind == tkind.TK_MODRESET) {
if (p.curkind == tkind.TK_MODPATH) {
p.pathmod = p.curtext;
p.curmod = p.curtext;
p.resetmod = "";
} else {
p.pathmod = "";
p.curmod = p.curtext;
p.resetmod = p.curtext;
};
p.sourceid += 1;
advance(p);
};
if (p.curkind != tkind.TK_MODULE) {
errmsg(p, "invalid or missing package clause");
return f;
};
let pf: str = p.curfile;
let pl: i32 = p.curline;
let pc: i32 = p.curcol;
advance(p);
if (p.curkind != tkind.TK_IDENT) {
errmsg(p, "invalid or missing package clause");
return f;
};
let name: str = p.curtext;
advance(p);
if (p.curkind != tkind.TK_SEMI) {
errmsg(p, "expected ';' after package name");
return f;
};
p.curpkg = name;
if (p.pathmod.len == 0 && p.resetmod.len == 0) { p.curmod = name; };
f.nmod = name;
f.pkgname = name;
f.sourceid = p.sourceid;
f.file = pf;
f.line = pl;
f.col = pc;
for (advanceheaderimport(p)) {
let d: *node = parseheaderuse(p);
d.nmod = p.curmod;
d.pkgname = p.curpkg;
d.sourceid = p.sourceid;
if (head == nil) { head = d; } else { tail.next = d; };
tail = d;
if (p.errs != 0) { break; };
};
f.list = head;
return f;
};
export fn parseimports(p: *parser) *node = {
let f = newnode(nkind.N_FILE, p.curfile, 1, 1);
let head: *node = nil;