ww+wcc: emit and lex // MODULE: <name> directive in combined.ww

This commit is contained in:
2026-05-11 14:54:56 +09:00
parent 5d91ee778e
commit d7036be0e5
9 changed files with 296 additions and 2 deletions

View File

@@ -1,3 +1,4 @@
// MODULE: os
// os — process and filesystem facade. The body of each call lands
// either in libwwrt.a (rt_syscall trampoline) or libc bindings,
// depending on how the program was linked.
@@ -207,6 +208,7 @@ export fn getdents64(fd: i32, buf: *u8, n: u64) i64 = {
return syscall3(SYS_GETDENTS64, fd: i64, buf: i64, n: i64);
};
// MODULE: wcc
// selfhost/cmd/wcc/mem.ww — port of cmd/wcc/mem.c.
//
// Bump arena allocator. Backed by the runtime page allocator
@@ -314,6 +316,7 @@ export fn freearena(a: *arena) void = {
};
};
// MODULE: ww
// selfhost/cmd/ww/main.ww — port of cmd/ww/main.c.
//
// The user-facing driver. Plan 9 cc(1) / Hare hare(1) analogue:
@@ -652,6 +655,50 @@ fn scanuse(src: *u8, len: u64) (*u8, u64) = {
// Recursively expand `path` into c.out. Imported files are emitted
// before their importer; cycles are broken via the visited set.
// modulename — pick the source's containing-directory basename. So
// `lib/os/os.ww` → "os"; `selfhost/cmd/wcc/sym.ww` → "wcc". Falls back
// to the file's own basename (sans .ww) when there is no parent dir.
// Returns ("",0) if `path` ends in a '/' (degenerate).
fn modulename(path: *u8, plen: u64) (*u8, u64) = {
if (plen == 0u64) { return nil, 0u64; };
// Find the last '/'.
let last: u64 = plen;
let i: u64 = plen;
for (i > 0u64) {
i -= 1u64;
if (path[i] == 47u8) { last = i; i = 0u64; }
else { if (i == 0u64) { last = plen; }; };
};
if (last == plen) {
// No '/' — path is a bare filename. Use its stem.
let n: u64 = plen;
if (n >= 3u64) {
if (path[n - 3u64] == 46u8) {
if (path[n - 2u64] == 119u8) {
if (path[n - 1u64] == 119u8) {
n = n - 3u64;
};
};
};
};
return path, n;
};
// Find the '/' before `last` — segment between is the dir basename.
let prev: u64 = 0u64;
let found: bool = false;
let j: u64 = last;
for (j > 0u64) {
j -= 1u64;
if (path[j] == 47u8) {
prev = j + 1u64;
found = true;
j = 0u64;
};
};
if (!found) { prev = 0u64; };
return path + prev, last - prev;
};
fn expand(c: *expctx, path_cs: *u8) void = {
let plen: u64 = cstrlen(path_cs);
let pathstr: str = astrndup(c.a, path_cs, plen);
@@ -687,7 +734,19 @@ fn expand(c: *expctx, path_cs: *u8) void = {
i = j + 1u64;
};
// Pass 2: emit our own bytes, then a trailing newline.
// Pass 2: prefix a `// MODULE: <name>` directive, then emit our
// bytes. The marker is a comment to the C-side wcc and any other
// reader; the ww-side wcc lexer recognises it and stamps each
// top-level decl with the originating module so cgen can mangle
// non-exported names by module.
let mp: *u8;
let mn: u64;
mp, mn = modulename(path_cs, plen);
if (mn > 0u64) {
os.writefull(c.out, "// MODULE: ".ptr, 11u64);
os.writefull(c.out, mp, mn);
os.writefull(c.out, "\n".ptr, 1u64);
};
os.writefull(c.out, bufp, blen);
os.writefull(c.out, "\n".ptr, 1u64);
};

View File

@@ -336,6 +336,50 @@ fn scanuse(src: *u8, len: u64) (*u8, u64) = {
// Recursively expand `path` into c.out. Imported files are emitted
// before their importer; cycles are broken via the visited set.
// modulename — pick the source's containing-directory basename. So
// `lib/os/os.ww` → "os"; `selfhost/cmd/wcc/sym.ww` → "wcc". Falls back
// to the file's own basename (sans .ww) when there is no parent dir.
// Returns ("",0) if `path` ends in a '/' (degenerate).
fn modulename(path: *u8, plen: u64) (*u8, u64) = {
if (plen == 0u64) { return nil, 0u64; };
// Find the last '/'.
let last: u64 = plen;
let i: u64 = plen;
for (i > 0u64) {
i -= 1u64;
if (path[i] == 47u8) { last = i; i = 0u64; }
else { if (i == 0u64) { last = plen; }; };
};
if (last == plen) {
// No '/' — path is a bare filename. Use its stem.
let n: u64 = plen;
if (n >= 3u64) {
if (path[n - 3u64] == 46u8) {
if (path[n - 2u64] == 119u8) {
if (path[n - 1u64] == 119u8) {
n = n - 3u64;
};
};
};
};
return path, n;
};
// Find the '/' before `last` — segment between is the dir basename.
let prev: u64 = 0u64;
let found: bool = false;
let j: u64 = last;
for (j > 0u64) {
j -= 1u64;
if (path[j] == 47u8) {
prev = j + 1u64;
found = true;
j = 0u64;
};
};
if (!found) { prev = 0u64; };
return path + prev, last - prev;
};
fn expand(c: *expctx, path_cs: *u8) void = {
let plen: u64 = cstrlen(path_cs);
let pathstr: str = astrndup(c.a, path_cs, plen);
@@ -371,7 +415,19 @@ fn expand(c: *expctx, path_cs: *u8) void = {
i = j + 1u64;
};
// Pass 2: emit our own bytes, then a trailing newline.
// Pass 2: prefix a `// MODULE: <name>` directive, then emit our
// bytes. The marker is a comment to the C-side wcc and any other
// reader; the ww-side wcc lexer recognises it and stamps each
// top-level decl with the originating module so cgen can mangle
// non-exported names by module.
let mp: *u8;
let mn: u64;
mp, mn = modulename(path_cs, plen);
if (mn > 0u64) {
os.writefull(c.out, "// MODULE: ".ptr, 11u64);
os.writefull(c.out, mp, mn);
os.writefull(c.out, "\n".ptr, 1u64);
};
os.writefull(c.out, bufp, blen);
os.writefull(c.out, "\n".ptr, 1u64);
};