ww+wcc: emit and lex // MODULE: <name> directive in combined.ww
This commit is contained in:
@@ -113,6 +113,36 @@ locate_import(const char *dirs, const char *name, char *out, size_t outsz)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* module_of — pick the source's containing-directory basename.
|
||||
* lib/os/os.ww -> "os"
|
||||
* selfhost/cmd/wcc/sym.ww -> "wcc"
|
||||
* bare "main.ww" -> "main"
|
||||
* Writes into dst (size ≥ 1); always NUL-terminates. */
|
||||
static void
|
||||
module_of(const char *path, char *dst, size_t dstn)
|
||||
{
|
||||
if (dstn == 0) return;
|
||||
dst[0] = '\0';
|
||||
const char *last = strrchr(path, '/');
|
||||
if (last == NULL) {
|
||||
/* bare filename — use the stem (path minus .ww). */
|
||||
size_t n = strlen(path);
|
||||
if (n >= 3 && strcmp(path + n - 3, ".ww") == 0) n -= 3;
|
||||
if (n >= dstn) n = dstn - 1;
|
||||
memcpy(dst, path, n);
|
||||
dst[n] = '\0';
|
||||
return;
|
||||
}
|
||||
/* Basename of the parent directory. */
|
||||
const char *prev = last - 1;
|
||||
while (prev >= path && *prev != '/') prev--;
|
||||
prev++;
|
||||
size_t n = (size_t)(last - prev);
|
||||
if (n >= dstn) n = dstn - 1;
|
||||
memcpy(dst, prev, n);
|
||||
dst[n] = '\0';
|
||||
}
|
||||
|
||||
/* Recursively expand `path`: for each top-level `use IDENT;` we find,
|
||||
* resolve the import and expand it first, then append our own bytes.
|
||||
* Already-visited paths are skipped. */
|
||||
@@ -156,6 +186,15 @@ expand(FILE *out, const char *path, struct ImportSet *visited,
|
||||
expand(out, ipath, visited, libdir);
|
||||
}
|
||||
|
||||
/* Prefix a `// MODULE: <name>` directive so the ww-side wcc lexer
|
||||
* can stamp each top-level decl with its originating module. The
|
||||
* marker is a comment to every other reader (including the C-side
|
||||
* wcc), so it's safe to emit unconditionally. */
|
||||
char modname[128];
|
||||
module_of(path, modname, sizeof modname);
|
||||
if (modname[0] != '\0')
|
||||
fprintf(out, "// MODULE: %s\n", modname);
|
||||
|
||||
rewind(in);
|
||||
int ch;
|
||||
while ((ch = fgetc(in)) != EOF) fputc(ch, out);
|
||||
|
||||
@@ -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: w6a
|
||||
// selfhost/cmd/w6a/types.ww — types + constants shared across the
|
||||
// w6a port. Mirrors cmd/w6a/a.h and cmd/w6c/6.out.h.
|
||||
|
||||
@@ -508,6 +511,7 @@ type asm_ = struct {
|
||||
errs: i32,
|
||||
};
|
||||
|
||||
// MODULE: w6a
|
||||
// selfhost/cmd/w6a/lex.ww — port of cmd/w6a/lex.c.
|
||||
//
|
||||
// Character-level helpers for w6a's line-oriented parser. The parser
|
||||
@@ -575,6 +579,7 @@ export fn parsenum(p: *u8, n: u64) (i64, u64) = {
|
||||
return v, i;
|
||||
};
|
||||
|
||||
// MODULE: w6a
|
||||
// selfhost/cmd/w6a/parse.ww — port of cmd/w6a/parse.c.
|
||||
//
|
||||
// Line-oriented parser for the asm subset emitted by w6c.
|
||||
@@ -1151,6 +1156,7 @@ export fn parse(a: *asm_) i32 = {
|
||||
return a.errs;
|
||||
};
|
||||
|
||||
// MODULE: w6a
|
||||
// selfhost/cmd/w6a/asm.ww — port of cmd/w6a/asm.c.
|
||||
//
|
||||
// Encode the parsed aprog list into amd64 machine bytes, appending to
|
||||
@@ -1833,6 +1839,7 @@ export fn encode(a: *asm_) i32 = {
|
||||
return a.errs;
|
||||
};
|
||||
|
||||
// MODULE: w6a
|
||||
// selfhost/cmd/w6a/obj.ww — port of cmd/w6a/obj.c.
|
||||
//
|
||||
// Emit a tiny ELF64 relocatable object. Layout (in file order):
|
||||
@@ -2127,6 +2134,7 @@ export fn emitelf(a: *asm_, fd: i32) i32 = {
|
||||
return 0;
|
||||
};
|
||||
|
||||
// MODULE: w6a
|
||||
// selfhost/cmd/w6a/main.ww — port of cmd/w6a/main.c.
|
||||
//
|
||||
// w6a = amd64 assembler. Read .s, parse, encode, emit ELF .o.
|
||||
|
||||
@@ -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: strconv
|
||||
// strconv — number↔string conversions. Decimal i64 to/from a fixed
|
||||
// buffer. Two error idioms ship side by side:
|
||||
// - Plan 9 style (atoi64): tuple `(value, ok)`. Pre-dates the
|
||||
@@ -434,6 +437,7 @@ export fn parseu64(s: str) (u64 | str) = {
|
||||
return v;
|
||||
};
|
||||
|
||||
// MODULE: wcc
|
||||
// selfhost/cmd/wcc/tok.ww — port of cmd/wcc/tok.c plus the Tkind /
|
||||
// Tok / Pos shapes from cmd/wcc/ww.h.
|
||||
//
|
||||
@@ -829,6 +833,7 @@ export fn tokprint(fd: i32, t: *tok) void = {
|
||||
fputcbyte(fd, 10u8); // '\n'
|
||||
};
|
||||
|
||||
// MODULE: ascii
|
||||
// ascii — byte-class predicates and case folding for the ASCII range.
|
||||
// Matches Hare's ascii::isdigit family. Bytes outside 0..127 always
|
||||
// answer `false`. The lexer hot path uses these inline; they are
|
||||
@@ -922,6 +927,7 @@ export fn toupper(c: u8) u8 = {
|
||||
return c;
|
||||
};
|
||||
|
||||
// MODULE: wcc
|
||||
// selfhost/cmd/wcc/lex.ww — port of cmd/wcc/lex.c.
|
||||
//
|
||||
// The DFA, the helpers, and the order of decisions all mirror the C
|
||||
@@ -949,6 +955,7 @@ type lex = struct {
|
||||
col: i32,
|
||||
a: *arena,
|
||||
errs: i32,
|
||||
module: str, // current module from `// MODULE: foo` directive; "" if none
|
||||
};
|
||||
|
||||
export fn lexinit(l: *lex, a: *arena, file: str, src: *u8, len: u64) void = {
|
||||
@@ -960,6 +967,10 @@ export fn lexinit(l: *lex, a: *arena, file: str, src: *u8, len: u64) void = {
|
||||
l.col = 1;
|
||||
l.a = a;
|
||||
l.errs = 0;
|
||||
let empty: str;
|
||||
empty.ptr = nil;
|
||||
empty.len = 0;
|
||||
l.module = empty;
|
||||
};
|
||||
|
||||
// srcb — byte at offset; helper that lifts the cast out of indexing.
|
||||
@@ -1037,6 +1048,32 @@ fn skipws(l: *lex) bool = {
|
||||
if (c == 47) { // '/'
|
||||
let c2: i32 = lpeek(l, 1u64);
|
||||
if (c2 == 47) {
|
||||
lget(l); lget(l); // consume '//'
|
||||
// Driver injects `// MODULE: foo` before each
|
||||
// source file's contents; capture so cgen can
|
||||
// mangle private symbols by module.
|
||||
if (lpeek(l, 0u64) == 32) { // ' '
|
||||
if (lpeek(l, 1u64) == 77) { // 'M'
|
||||
if (lpeek(l, 2u64) == 79) { // 'O'
|
||||
if (lpeek(l, 3u64) == 68) { // 'D'
|
||||
if (lpeek(l, 4u64) == 85) { // 'U'
|
||||
if (lpeek(l, 5u64) == 76) { // 'L'
|
||||
if (lpeek(l, 6u64) == 69) { // 'E'
|
||||
if (lpeek(l, 7u64) == 58) { // ':'
|
||||
if (lpeek(l, 8u64) == 32) { // ' '
|
||||
let i: i32 = 0;
|
||||
for (i < 9) { lget(l); i += 1; };
|
||||
let start: u64 = l.lpos;
|
||||
for (true) {
|
||||
let cx: i32 = lpeek(l, 0u64);
|
||||
if (cx < 0) { break; };
|
||||
if (cx == 10) { break; };
|
||||
if (cx == 13) { break; };
|
||||
lget(l);
|
||||
};
|
||||
let n: u64 = l.lpos - start;
|
||||
l.module = astrndup(l.a, l.src + start, n);
|
||||
};};};};};};};};};
|
||||
for (true) {
|
||||
let cx: i32 = lpeek(l, 0u64);
|
||||
if (cx < 0) { return false; };
|
||||
@@ -1579,6 +1616,7 @@ export fn lexnext(l: *lex, out: *tok) void = {
|
||||
out.text = astrndup(l.a, one.ptr, 1u64);
|
||||
};
|
||||
|
||||
// MODULE: wcc
|
||||
// selfhost/cmd/wcc/ast.ww — port of cmd/wcc/ast.c (Node defs + printer).
|
||||
//
|
||||
// Status: AST printer is fully ported. Constructor `newnode` is here.
|
||||
@@ -1915,6 +1953,7 @@ export fn astprint(fd: i32, n: *node) void = {
|
||||
pr(fd, n, 0);
|
||||
};
|
||||
|
||||
// MODULE: wcc
|
||||
// selfhost/cmd/wcc/parse.ww — port of cmd/wcc/parse.c.
|
||||
//
|
||||
// Status: GROWING stub. Currently handles top-level `use IDENT;`,
|
||||
@@ -2898,6 +2937,7 @@ export fn parsefile(p: *parser) *node = {
|
||||
return f;
|
||||
};
|
||||
|
||||
// MODULE: wcc
|
||||
// selfhost/cmd/wcc/type.ww — port of cmd/wcc/type.c.
|
||||
//
|
||||
// Status: full structural port. The C version uses module-globals for
|
||||
@@ -3228,6 +3268,7 @@ export fn typeeq(a: *tinfo, b: *tinfo) bool = {
|
||||
return true; // primitives match by kind alone
|
||||
};
|
||||
|
||||
// MODULE: wcc
|
||||
// selfhost/cmd/wcc/sym.ww — port of cmd/wcc/sym.c.
|
||||
//
|
||||
// Per-scope hashtable, chained to the parent. Lookup walks up.
|
||||
@@ -3342,6 +3383,7 @@ export fn scopedefine(s: *scope, name: str, k: i32, t: *tinfo, decl: *node) *sym
|
||||
return sy;
|
||||
};
|
||||
|
||||
// MODULE: wcc
|
||||
// selfhost/cmd/wcc/check.ww — minimal port of cmd/wcc/check.c.
|
||||
//
|
||||
// Status: name-resolution + primitive-type seeding only. Full type
|
||||
@@ -3590,6 +3632,7 @@ export fn checkfile(c: *checker, file: *node) void = {
|
||||
};
|
||||
};
|
||||
|
||||
// MODULE: wcc
|
||||
// selfhost/cmd/wcc/cgen.ww — port of cmd/w6c/cgen.c.
|
||||
//
|
||||
// Status: GROWING. Each subsystem we add is verified by `wwdump_ww -c`
|
||||
@@ -6673,6 +6716,7 @@ export fn cgfile(c: *cgen, file: *node) void = {
|
||||
emitdefconstants(c, file);
|
||||
};
|
||||
|
||||
// MODULE: w6c
|
||||
// selfhost/cmd/w6c/main.ww — port of cmd/w6c/main.c.
|
||||
//
|
||||
// w6c = amd64 compiler. Read .ww, parse, codegen, emit Plan 9 amd64
|
||||
|
||||
@@ -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: w6l
|
||||
// selfhost/cmd/w6l/sym.ww — port of cmd/w6l/sym.c.
|
||||
//
|
||||
// Linker symbol table. Singly-linked list, usually a few hundred
|
||||
@@ -416,6 +419,7 @@ export fn lookup(l: *lnk, name: str) *lsym = {
|
||||
return nil;
|
||||
};
|
||||
|
||||
// MODULE: w6l
|
||||
// selfhost/cmd/w6l/obj.ww — port of cmd/w6l/obj.c.
|
||||
//
|
||||
// Loads relocatable ELF64 .o files emitted by w6a, appends .text to
|
||||
@@ -903,6 +907,7 @@ fn loadimage(l: *lnk, path: *u8, buf: *u8, len: u64) i32 = {
|
||||
return 0;
|
||||
};
|
||||
|
||||
// MODULE: w6l
|
||||
// selfhost/cmd/w6l/dyn.ww — port of cmd/w6l/dyn.c.
|
||||
//
|
||||
// Load a shared object (ET_DYN) so the linker knows which symbols it
|
||||
@@ -1302,6 +1307,7 @@ export fn soversion(so: *lso, name: str) str = {
|
||||
|
||||
// `streq` lives in sym.ww — same bundle, single definition.
|
||||
|
||||
// MODULE: w6l
|
||||
// selfhost/cmd/w6l/pass.ww — port of cmd/w6l/pass.c.
|
||||
//
|
||||
// Resolution + relocation. l_resolve flags every undefined symbol
|
||||
@@ -1418,6 +1424,7 @@ export fn relocate(l: *lnk, base: u64) i32 = {
|
||||
return l.errs;
|
||||
};
|
||||
|
||||
// MODULE: w6l
|
||||
// selfhost/cmd/w6l/dynout.ww — port of cmd/w6l/dynout.c.
|
||||
//
|
||||
// Emit a dynamic-linked ELF executable. The shape is the simplest
|
||||
@@ -2162,6 +2169,7 @@ export fn emitdynelf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
return 0;
|
||||
};
|
||||
|
||||
// MODULE: w6l
|
||||
// selfhost/cmd/w6l/out.ww — port of cmd/w6l/out.c.
|
||||
//
|
||||
// Emit a static ELF64 executable. File layout (per the C original):
|
||||
@@ -2263,6 +2271,7 @@ export fn emitelf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
return 0;
|
||||
};
|
||||
|
||||
// MODULE: w6l
|
||||
// selfhost/cmd/w6l/main.ww — port of cmd/w6l/main.c.
|
||||
//
|
||||
// w6l = amd64 linker. Reads relocatable ELF .o files, SysV `ar`
|
||||
|
||||
@@ -25,6 +25,7 @@ type lex = struct {
|
||||
col: i32,
|
||||
a: *arena,
|
||||
errs: i32,
|
||||
module: str, // current module from `// MODULE: foo` directive; "" if none
|
||||
};
|
||||
|
||||
export fn lexinit(l: *lex, a: *arena, file: str, src: *u8, len: u64) void = {
|
||||
@@ -36,6 +37,10 @@ export fn lexinit(l: *lex, a: *arena, file: str, src: *u8, len: u64) void = {
|
||||
l.col = 1;
|
||||
l.a = a;
|
||||
l.errs = 0;
|
||||
let empty: str;
|
||||
empty.ptr = nil;
|
||||
empty.len = 0;
|
||||
l.module = empty;
|
||||
};
|
||||
|
||||
// srcb — byte at offset; helper that lifts the cast out of indexing.
|
||||
@@ -113,6 +118,32 @@ fn skipws(l: *lex) bool = {
|
||||
if (c == 47) { // '/'
|
||||
let c2: i32 = lpeek(l, 1u64);
|
||||
if (c2 == 47) {
|
||||
lget(l); lget(l); // consume '//'
|
||||
// Driver injects `// MODULE: foo` before each
|
||||
// source file's contents; capture so cgen can
|
||||
// mangle private symbols by module.
|
||||
if (lpeek(l, 0u64) == 32) { // ' '
|
||||
if (lpeek(l, 1u64) == 77) { // 'M'
|
||||
if (lpeek(l, 2u64) == 79) { // 'O'
|
||||
if (lpeek(l, 3u64) == 68) { // 'D'
|
||||
if (lpeek(l, 4u64) == 85) { // 'U'
|
||||
if (lpeek(l, 5u64) == 76) { // 'L'
|
||||
if (lpeek(l, 6u64) == 69) { // 'E'
|
||||
if (lpeek(l, 7u64) == 58) { // ':'
|
||||
if (lpeek(l, 8u64) == 32) { // ' '
|
||||
let i: i32 = 0;
|
||||
for (i < 9) { lget(l); i += 1; };
|
||||
let start: u64 = l.lpos;
|
||||
for (true) {
|
||||
let cx: i32 = lpeek(l, 0u64);
|
||||
if (cx < 0) { break; };
|
||||
if (cx == 10) { break; };
|
||||
if (cx == 13) { break; };
|
||||
lget(l);
|
||||
};
|
||||
let n: u64 = l.lpos - start;
|
||||
l.module = astrndup(l.a, l.src + start, n);
|
||||
};};};};};};};};};
|
||||
for (true) {
|
||||
let cx: i32 = lpeek(l, 0u64);
|
||||
if (cx < 0) { return false; };
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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: strconv
|
||||
// strconv — number↔string conversions. Decimal i64 to/from a fixed
|
||||
// buffer. Two error idioms ship side by side:
|
||||
// - Plan 9 style (atoi64): tuple `(value, ok)`. Pre-dates the
|
||||
@@ -434,6 +437,7 @@ export fn parseu64(s: str) (u64 | str) = {
|
||||
return v;
|
||||
};
|
||||
|
||||
// MODULE: wcc
|
||||
// selfhost/cmd/wcc/tok.ww — port of cmd/wcc/tok.c plus the Tkind /
|
||||
// Tok / Pos shapes from cmd/wcc/ww.h.
|
||||
//
|
||||
@@ -829,6 +833,7 @@ export fn tokprint(fd: i32, t: *tok) void = {
|
||||
fputcbyte(fd, 10u8); // '\n'
|
||||
};
|
||||
|
||||
// MODULE: ascii
|
||||
// ascii — byte-class predicates and case folding for the ASCII range.
|
||||
// Matches Hare's ascii::isdigit family. Bytes outside 0..127 always
|
||||
// answer `false`. The lexer hot path uses these inline; they are
|
||||
@@ -922,6 +927,7 @@ export fn toupper(c: u8) u8 = {
|
||||
return c;
|
||||
};
|
||||
|
||||
// MODULE: wcc
|
||||
// selfhost/cmd/wcc/lex.ww — port of cmd/wcc/lex.c.
|
||||
//
|
||||
// The DFA, the helpers, and the order of decisions all mirror the C
|
||||
@@ -949,6 +955,7 @@ type lex = struct {
|
||||
col: i32,
|
||||
a: *arena,
|
||||
errs: i32,
|
||||
module: str, // current module from `// MODULE: foo` directive; "" if none
|
||||
};
|
||||
|
||||
export fn lexinit(l: *lex, a: *arena, file: str, src: *u8, len: u64) void = {
|
||||
@@ -960,6 +967,10 @@ export fn lexinit(l: *lex, a: *arena, file: str, src: *u8, len: u64) void = {
|
||||
l.col = 1;
|
||||
l.a = a;
|
||||
l.errs = 0;
|
||||
let empty: str;
|
||||
empty.ptr = nil;
|
||||
empty.len = 0;
|
||||
l.module = empty;
|
||||
};
|
||||
|
||||
// srcb — byte at offset; helper that lifts the cast out of indexing.
|
||||
@@ -1037,6 +1048,32 @@ fn skipws(l: *lex) bool = {
|
||||
if (c == 47) { // '/'
|
||||
let c2: i32 = lpeek(l, 1u64);
|
||||
if (c2 == 47) {
|
||||
lget(l); lget(l); // consume '//'
|
||||
// Driver injects `// MODULE: foo` before each
|
||||
// source file's contents; capture so cgen can
|
||||
// mangle private symbols by module.
|
||||
if (lpeek(l, 0u64) == 32) { // ' '
|
||||
if (lpeek(l, 1u64) == 77) { // 'M'
|
||||
if (lpeek(l, 2u64) == 79) { // 'O'
|
||||
if (lpeek(l, 3u64) == 68) { // 'D'
|
||||
if (lpeek(l, 4u64) == 85) { // 'U'
|
||||
if (lpeek(l, 5u64) == 76) { // 'L'
|
||||
if (lpeek(l, 6u64) == 69) { // 'E'
|
||||
if (lpeek(l, 7u64) == 58) { // ':'
|
||||
if (lpeek(l, 8u64) == 32) { // ' '
|
||||
let i: i32 = 0;
|
||||
for (i < 9) { lget(l); i += 1; };
|
||||
let start: u64 = l.lpos;
|
||||
for (true) {
|
||||
let cx: i32 = lpeek(l, 0u64);
|
||||
if (cx < 0) { break; };
|
||||
if (cx == 10) { break; };
|
||||
if (cx == 13) { break; };
|
||||
lget(l);
|
||||
};
|
||||
let n: u64 = l.lpos - start;
|
||||
l.module = astrndup(l.a, l.src + start, n);
|
||||
};};};};};};};};};
|
||||
for (true) {
|
||||
let cx: i32 = lpeek(l, 0u64);
|
||||
if (cx < 0) { return false; };
|
||||
@@ -1579,6 +1616,7 @@ export fn lexnext(l: *lex, out: *tok) void = {
|
||||
out.text = astrndup(l.a, one.ptr, 1u64);
|
||||
};
|
||||
|
||||
// MODULE: wcc
|
||||
// selfhost/cmd/wcc/ast.ww — port of cmd/wcc/ast.c (Node defs + printer).
|
||||
//
|
||||
// Status: AST printer is fully ported. Constructor `newnode` is here.
|
||||
@@ -1915,6 +1953,7 @@ export fn astprint(fd: i32, n: *node) void = {
|
||||
pr(fd, n, 0);
|
||||
};
|
||||
|
||||
// MODULE: wcc
|
||||
// selfhost/cmd/wcc/parse.ww — port of cmd/wcc/parse.c.
|
||||
//
|
||||
// Status: GROWING stub. Currently handles top-level `use IDENT;`,
|
||||
@@ -2898,6 +2937,7 @@ export fn parsefile(p: *parser) *node = {
|
||||
return f;
|
||||
};
|
||||
|
||||
// MODULE: wcc
|
||||
// selfhost/cmd/wcc/type.ww — port of cmd/wcc/type.c.
|
||||
//
|
||||
// Status: full structural port. The C version uses module-globals for
|
||||
@@ -3228,6 +3268,7 @@ export fn typeeq(a: *tinfo, b: *tinfo) bool = {
|
||||
return true; // primitives match by kind alone
|
||||
};
|
||||
|
||||
// MODULE: wcc
|
||||
// selfhost/cmd/wcc/sym.ww — port of cmd/wcc/sym.c.
|
||||
//
|
||||
// Per-scope hashtable, chained to the parent. Lookup walks up.
|
||||
@@ -3342,6 +3383,7 @@ export fn scopedefine(s: *scope, name: str, k: i32, t: *tinfo, decl: *node) *sym
|
||||
return sy;
|
||||
};
|
||||
|
||||
// MODULE: wcc
|
||||
// selfhost/cmd/wcc/check.ww — minimal port of cmd/wcc/check.c.
|
||||
//
|
||||
// Status: name-resolution + primitive-type seeding only. Full type
|
||||
@@ -3590,6 +3632,7 @@ export fn checkfile(c: *checker, file: *node) void = {
|
||||
};
|
||||
};
|
||||
|
||||
// MODULE: wcc
|
||||
// selfhost/cmd/wcc/cgen.ww — port of cmd/w6c/cgen.c.
|
||||
//
|
||||
// Status: GROWING. Each subsystem we add is verified by `wwdump_ww -c`
|
||||
@@ -6673,6 +6716,7 @@ export fn cgfile(c: *cgen, file: *node) void = {
|
||||
emitdefconstants(c, file);
|
||||
};
|
||||
|
||||
// MODULE: wwdump
|
||||
// selfhost/cmd/wwdump/main.ww — ww-side port of cmd/wwdump/main.c.
|
||||
//
|
||||
// Reads a .ww file, runs the ww-side lexer, prints tokens through
|
||||
|
||||
@@ -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: strconv
|
||||
// strconv — number↔string conversions. Decimal i64 to/from a fixed
|
||||
// buffer. Two error idioms ship side by side:
|
||||
// - Plan 9 style (atoi64): tuple `(value, ok)`. Pre-dates the
|
||||
@@ -327,6 +329,7 @@ export fn parseu64(s: str) (u64 | str) = {
|
||||
return v;
|
||||
};
|
||||
|
||||
// MODULE: ascii
|
||||
// ascii — byte-class predicates and case folding for the ASCII range.
|
||||
// Matches Hare's ascii::isdigit family. Bytes outside 0..127 always
|
||||
// answer `false`. The lexer hot path uses these inline; they are
|
||||
@@ -420,6 +423,7 @@ export fn toupper(c: u8) u8 = {
|
||||
return c;
|
||||
};
|
||||
|
||||
// MODULE: test
|
||||
// selfhost/test/smoke.ww — end-to-end smoke for the selfhost path.
|
||||
//
|
||||
// Exercises the patterns the real ww-side compiler port will use:
|
||||
|
||||
Reference in New Issue
Block a user