compiler: implement blank package name semantics

This commit is contained in:
2026-08-23 15:33:59 +09:00
parent e67b8e1bc4
commit 0b24b11d65
14 changed files with 2862 additions and 112 deletions

View File

@@ -133,6 +133,9 @@ export type node = struct {
usefile: str, // N_USE: first spec token (alias, otherwise path)
useline: i32,
usecol: i32,
usepathfile: str,// N_USE: first path token, independent of alias
usepathline: i32,
usepathcol: i32,
usepkgname: str,// N_USE: imported declared package name
useblank: i32, // N_USE: `_` spelling; no source binding
pkgname: str, // declared package name; independent of canonical nmod
@@ -153,7 +156,7 @@ 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, packed=0, type_=nil, tsuffix="", nmod="", usesource="", usepath="", usealias="", usefile="", useline=0, usecol=0, usepkgname="", useblank=0, pkgname="", sourceid=0, used=0, initfn=0, initsynthetic=0, runtimeinit=0, initorder=0u64, linksym="", refdecl=nil, initmark=0u64, imported=0})!;
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, packed=0, type_=nil, tsuffix="", nmod="", usesource="", usepath="", usealias="", usefile="", useline=0, usecol=0, usepathfile="", usepathline=0, usepathcol=0, usepkgname="", useblank=0, pkgname="", sourceid=0, used=0, initfn=0, initsynthetic=0, runtimeinit=0, initorder=0u64, linksym="", refdecl=nil, initmark=0u64, imported=0})!;
return n;
};

View File

@@ -19,19 +19,31 @@ fn parseuse(p: *parser) *node = {
n.usefile = p.curfile;
n.useline = p.curline;
n.usecol = p.curcol;
let pathfile: str = p.curfile;
let pathline: i32 = p.curline;
let pathcol: i32 = p.curcol;
let alias: str;
let first: str;
if (p.curkind == tkind.TK_UNDER) {
n.useblank = 1;
advance(p);
pathfile = p.curfile;
pathline = p.curline;
pathcol = p.curcol;
expectident(p, &first);
} else {
expectident(p, &first);
if (p.curkind == tkind.TK_IDENT) {
alias = first;
pathfile = p.curfile;
pathline = p.curline;
pathcol = p.curcol;
expectident(p, &first);
};
};
n.usepathfile = pathfile;
n.usepathline = pathline;
n.usepathcol = pathcol;
let leaf: str = first;
let path: str = leaf;
for (p.curkind == tkind.TK_DOT) {

View File

@@ -145,6 +145,19 @@ fn expectident(p: *parser, into: *str) bool = {
return true;
};
// Package declarations admit the blank identifier syntactically. Keep this
// private to the package-name slot; the checker owns BlankPkgName rejection.
fn expectpackagename(p: *parser, into: *str) bool = {
if (p.curkind != tkind.TK_IDENT && p.curkind != tkind.TK_UNDER) {
errmsg(p, strings.concat("expected identifier, got ",
tokname(p.curkind)));
return false;
};
*into = p.curtext;
advance(p);
return true;
};
// On `_`, returns "" so the checker skips scope_define for the binding.
fn expectbindname(p: *parser, into: *str) bool = {
if (p.curkind == tkind.TK_UNDER) {
@@ -513,11 +526,17 @@ fn parseheaderuse(p: *parser) *node = {
n.usefile = p.curfile;
n.useline = p.curline;
n.usecol = p.curcol;
let pathfile: str = p.curfile;
let pathline: i32 = p.curline;
let pathcol: i32 = p.curcol;
let alias: str;
let first: str;
if (p.curkind == tkind.TK_UNDER) {
n.useblank = 1;
advance(p);
pathfile = p.curfile;
pathline = p.curline;
pathcol = p.curcol;
};
if (p.curkind != tkind.TK_IDENT) {
errmsg(p, strings.concat("expected identifier, got ",
@@ -528,9 +547,15 @@ fn parseheaderuse(p: *parser) *node = {
advance(p);
if (n.useblank == 0 && p.curkind == tkind.TK_IDENT) {
alias = first;
pathfile = p.curfile;
pathline = p.curline;
pathcol = p.curcol;
first = p.curtext;
advance(p);
};
n.usepathfile = pathfile;
n.usepathline = pathline;
n.usepathcol = pathcol;
let leaf: str = first;
let path: str = leaf;
for (p.curkind == tkind.TK_DOT) {
@@ -585,12 +610,12 @@ export fn parsepackageheader(p: *parser) *node = {
let pl: i32 = p.curline;
let pc: i32 = p.curcol;
advance(p);
if (p.curkind != tkind.TK_IDENT) {
if (p.curkind != tkind.TK_IDENT && p.curkind != tkind.TK_UNDER) {
errmsg(p, "invalid or missing package clause");
return f;
};
let name: str = p.curtext;
advance(p);
let name: str;
expectpackagename(p, &name);
if (p.curkind != tkind.TK_SEMI) {
errmsg(p, "expected ';' after package name");
return f;
@@ -659,14 +684,15 @@ export fn parseimports(p: *parser) *node = {
let pc: i32 = p.curcol;
previmport = true;
advance(p);
if (p.curkind != tkind.TK_IDENT) {
if (p.curkind != tkind.TK_IDENT
&& p.curkind != tkind.TK_UNDER) {
errmsg(p, "invalid or missing package clause");
sawpackage = true;
skipimportdecl(p);
continue;
};
let name: str;
expectident(p, &name);
expectpackagename(p, &name);
expecttok(p, tkind.TK_SEMI, "expected ';' after module name");
p.curpkg = name;
if (p.pathmod.len == 0 && p.resetmod.len == 0) {
@@ -771,14 +797,25 @@ export fn parsefile(p: *parser) *node = {
sawpackage = 1;
previmport = true;
advance(p);
let nf: str = p.curfile;
let nl: i32 = p.curline;
let nc: i32 = p.curcol;
let name: str;
expectident(p, &name);
expectpackagename(p, &name);
expecttok(p, tkind.TK_SEMI, "expected ';' after module name");
p.curpkg = name;
if (p.pathmod.len == 0 && p.resetmod.len == 0) {
p.curmod = name;
};
let pm: *node = newnode(nkind.N_FILE, pf, pl, pc);
let mf: str = pf;
let ml: i32 = pl;
let mc: i32 = pc;
if (streq(name, "_")) {
mf = nf;
ml = nl;
mc = nc;
};
let pm: *node = newnode(nkind.N_FILE, mf, ml, mc);
pm.nmod = p.curmod;
pm.pkgname = name;
pm.sourceid = p.sourceid;