selfhost/cmd/ww: strip *arena cascade (γ-5)
amalloc has 0 callers post-γ-2; the *arena threaded through ww driver's importpathform / locatein / locateimport / enumeratedir / peekpackage / arenadupcstr / builddirmodulepath / buildsearchpath / resolvemodule and the expctx.a field are vestigial. Drop `import mem;`, remove expctx.a, strip *arena from 9 signatures, update 9 call sites. Drop 4 dead `let a: *arena = newarena();` in buildone/dobuild/dorun/dotest. Comments retidied. main.combined.ww auto-regenerated. builddirmodulepath body intact — still 0 callers; γ-5b drops it. arenadupcstr name is now a misnomer (task #10). Verified 132/132 incl. 993_ww_ww + 995_self_rebuild byte-identity.
This commit is contained in:
@@ -750,98 +750,6 @@ package rt;
|
|||||||
// a future task (task #39). ref/hare/rt/malloc.ha:27.
|
// a future task (task #39). ref/hare/rt/malloc.ha:27.
|
||||||
@symbol("rt_malloc") export fn malloc(n: u64) *void;
|
@symbol("rt_malloc") export fn malloc(n: u64) *void;
|
||||||
|
|
||||||
// selfhost/cmd/wcc/mem.ww — port of cmd/wcc/mem.c.
|
|
||||||
//
|
|
||||||
// Bump arena allocator. Backed by the runtime page allocator
|
|
||||||
// (rt_malloc / rt_free), no libc. Each chunk is mmap'd; when the
|
|
||||||
// current chunk runs out we link a fresh one. Freeing the arena
|
|
||||||
// unmaps the chain.
|
|
||||||
//
|
|
||||||
// Memory handed out is 16-byte aligned. The C version under
|
|
||||||
// cmd/wcc/ is retained until the three-stage bootstrap diffs clean.
|
|
||||||
|
|
||||||
package wcc;
|
|
||||||
|
|
||||||
import os;
|
|
||||||
import rt;
|
|
||||||
|
|
||||||
def ALIGN: u64 = 16u64;
|
|
||||||
def INIT_CHUNK: u64 = 65536u64;
|
|
||||||
def MAX_CHUNK: u64 = 4194304u64;
|
|
||||||
def ARENA_SZ: u64 = 48u64; // sizeof(arena), kept in sync below
|
|
||||||
|
|
||||||
type arena = struct {
|
|
||||||
buf: *u8,
|
|
||||||
off: u64,
|
|
||||||
cap: u64,
|
|
||||||
next: *arena,
|
|
||||||
total: u64,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn roundup(n: u64, a: u64) u64 = {
|
|
||||||
return (n + a - 1u64) & ~(a - 1u64);
|
|
||||||
};
|
|
||||||
|
|
||||||
export fn newarena() *arena = {
|
|
||||||
let a: *arena = rt.malloc(ARENA_SZ): *arena;
|
|
||||||
a.buf = rt.malloc(INIT_CHUNK): *u8;
|
|
||||||
a.off = 0u64;
|
|
||||||
a.cap = INIT_CHUNK;
|
|
||||||
a.next = nil;
|
|
||||||
a.total = 0u64;
|
|
||||||
return a;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Grow: link a fresh chunk in front of the head. We push the old
|
|
||||||
// chunk into `next` so the head always describes the current bump
|
|
||||||
// region. Chunk size doubles up to MAX_CHUNK.
|
|
||||||
fn grow(a: *arena, need: u64) bool = {
|
|
||||||
let want: u64 = a.cap * 2u64;
|
|
||||||
if (want < need) { want = need; };
|
|
||||||
if (want > MAX_CHUNK) { want = MAX_CHUNK; };
|
|
||||||
if (want < need) { return false; }; // single allocation too big
|
|
||||||
|
|
||||||
let old: *arena = rt.malloc(ARENA_SZ): *arena;
|
|
||||||
old.buf = a.buf;
|
|
||||||
old.off = a.off;
|
|
||||||
old.cap = a.cap;
|
|
||||||
old.next = a.next;
|
|
||||||
old.total = 0u64;
|
|
||||||
|
|
||||||
a.buf = rt.malloc(want): *u8;
|
|
||||||
a.off = 0u64;
|
|
||||||
a.cap = want;
|
|
||||||
a.next = old;
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
export fn amalloc(a: *arena, n: u64) *void = {
|
|
||||||
let need: u64 = roundup(n, ALIGN);
|
|
||||||
if (need > a.cap - a.off) {
|
|
||||||
if (!grow(a, need)) { return nil; };
|
|
||||||
};
|
|
||||||
let p: *u8 = a.buf + a.off;
|
|
||||||
a.off += need;
|
|
||||||
a.total += need;
|
|
||||||
// Zero the region. Plan 9 amalloc zeroes; we mirror that here so
|
|
||||||
// the checker can assume freshly allocated nodes start at 0.
|
|
||||||
let i: u64 = 0u64;
|
|
||||||
for (i < need) {
|
|
||||||
p[i] = 0u8;
|
|
||||||
i += 1u64;
|
|
||||||
};
|
|
||||||
return p: *void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export fn freearena(a: *arena) void = {
|
|
||||||
for (a != nil) {
|
|
||||||
let next: *arena = a.next;
|
|
||||||
os.free(a.buf: *void, a.cap);
|
|
||||||
os.free(a: *void, ARENA_SZ);
|
|
||||||
a = next;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// types — integer limits. Mirrors Hare's types::limits (I8_MAX, …)
|
// types — integer limits. Mirrors Hare's types::limits (I8_MAX, …)
|
||||||
// platform-fixed for amd64. Numeric helpers live in lib/math, matching
|
// platform-fixed for amd64. Numeric helpers live in lib/math, matching
|
||||||
// Hare's split between types::limits and math::.
|
// Hare's split between types::limits and math::.
|
||||||
@@ -2790,7 +2698,6 @@ package main;
|
|||||||
|
|
||||||
import os;
|
import os;
|
||||||
import rt;
|
import rt;
|
||||||
import mem;
|
|
||||||
import strings;
|
import strings;
|
||||||
|
|
||||||
// All path/string scratch buffers go on the runtime page allocator.
|
// All path/string scratch buffers go on the runtime page allocator.
|
||||||
@@ -2807,7 +2714,7 @@ fn cstrlen(p: *u8) u64 = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// pathstr — view a NUL-terminated *u8 as a str. Bridges the
|
// pathstr — view a NUL-terminated *u8 as a str. Bridges the
|
||||||
// driver's argv/arena *u8 paths to lib/os entrypoints (str
|
// driver's argv-style *u8 paths to lib/os entrypoints (str
|
||||||
// post-task-#23).
|
// post-task-#23).
|
||||||
fn pathstr(p: *u8) str = {
|
fn pathstr(p: *u8) str = {
|
||||||
let r: str;
|
let r: str;
|
||||||
@@ -2978,7 +2885,6 @@ type strnode = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type expctx = struct {
|
type expctx = struct {
|
||||||
a: *arena, // arena for path strings + the visited list
|
|
||||||
out: i32, // fd we're writing the combined source to
|
out: i32, // fd we're writing the combined source to
|
||||||
dirs: *u8, // ":"-separated search path (NUL-terminated)
|
dirs: *u8, // ":"-separated search path (NUL-terminated)
|
||||||
visit: *strnode,
|
visit: *strnode,
|
||||||
@@ -3011,7 +2917,7 @@ fn visitadd(c: *expctx, path: str) void = {
|
|||||||
// use-path → fs-path mapping
|
// use-path → fs-path mapping
|
||||||
// (ref/hare/hare/module/srcs.ha:78 builds the same shape via
|
// (ref/hare/hare/module/srcs.ha:78 builds the same shape via
|
||||||
// path::push per ident part).
|
// path::push per ident part).
|
||||||
fn importpathform(a: *arena, name: *u8, namelen: u64) *u8 = {
|
fn importpathform(name: *u8, namelen: u64) *u8 = {
|
||||||
let buf: []u8 = alloc([], namelen + 1u64)!;
|
let buf: []u8 = alloc([], namelen + 1u64)!;
|
||||||
let i: u64 = 0u64;
|
let i: u64 = 0u64;
|
||||||
for (i < namelen) {
|
for (i < namelen) {
|
||||||
@@ -3029,7 +2935,7 @@ fn importpathform(a: *arena, name: *u8, namelen: u64) *u8 = {
|
|||||||
// form was dropped in task #22 — directory-as-module enumeration
|
// form was dropped in task #22 — directory-as-module enumeration
|
||||||
// replaces it, mirroring ref/hare/hare/module/srcs.ha (Hare has no
|
// replaces it, mirroring ref/hare/hare/module/srcs.ha (Hare has no
|
||||||
// `foo/foo.ha` fallback; a module IS the directory).
|
// `foo/foo.ha` fallback; a module IS the directory).
|
||||||
fn locatein(a: *arena, dir: *u8, dirlen: u64,
|
fn locatein(dir: *u8, dirlen: u64,
|
||||||
pathform: *u8, pflen: u64, isdir: *i32) *u8 = {
|
pathform: *u8, pflen: u64, isdir: *i32) *u8 = {
|
||||||
let buf: []u8 = alloc([], PATH_MAX)!;
|
let buf: []u8 = alloc([], PATH_MAX)!;
|
||||||
let off: u64 = 0u64;
|
let off: u64 = 0u64;
|
||||||
@@ -3078,9 +2984,9 @@ fn locatein(a: *arena, dir: *u8, dirlen: u64,
|
|||||||
|
|
||||||
// Walk a colon-separated dirlist, return first hit or nil. Sets
|
// Walk a colon-separated dirlist, return first hit or nil. Sets
|
||||||
// *isdir on hit.
|
// *isdir on hit.
|
||||||
fn locateimport(a: *arena, dirs: *u8, name: *u8, namelen: u64,
|
fn locateimport(dirs: *u8, name: *u8, namelen: u64,
|
||||||
isdir: *i32) *u8 = {
|
isdir: *i32) *u8 = {
|
||||||
let pathform: *u8 = importpathform(a, name, namelen);
|
let pathform: *u8 = importpathform(name, namelen);
|
||||||
let pflen: u64 = cstrlen(pathform);
|
let pflen: u64 = cstrlen(pathform);
|
||||||
let total: u64 = cstrlen(dirs);
|
let total: u64 = cstrlen(dirs);
|
||||||
let p: u64 = 0u64;
|
let p: u64 = 0u64;
|
||||||
@@ -3092,7 +2998,7 @@ fn locateimport(a: *arena, dirs: *u8, name: *u8, namelen: u64,
|
|||||||
};
|
};
|
||||||
let seglen: u64 = q - p;
|
let seglen: u64 = q - p;
|
||||||
if (seglen > 0u64) {
|
if (seglen > 0u64) {
|
||||||
let hit: *u8 = locatein(a, dirs + p, seglen,
|
let hit: *u8 = locatein(dirs + p, seglen,
|
||||||
pathform, pflen, isdir);
|
pathform, pflen, isdir);
|
||||||
if (hit != nil) { return hit; };
|
if (hit != nil) { return hit; };
|
||||||
};
|
};
|
||||||
@@ -3170,8 +3076,8 @@ fn bytecmp(a: *u8, alen: u64, b: *u8, blen: u64) i32 = {
|
|||||||
|
|
||||||
// enumeratedir — list *.ww entries of `dirpath` (less *test.ww and
|
// enumeratedir — list *.ww entries of `dirpath` (less *test.ww and
|
||||||
// *.combined.ww), byte-sort. Returns (names[], nnames) with each
|
// *.combined.ww), byte-sort. Returns (names[], nnames) with each
|
||||||
// name a NUL-terminated arena copy.
|
// name a NUL-terminated heap copy.
|
||||||
fn enumeratedir(a: *arena, dirpath: *u8) (**u8, i32) = {
|
fn enumeratedir(dirpath: *u8) (**u8, i32) = {
|
||||||
let fd: i32 = os.open(pathstr(dirpath), os.flag.RDONLY, 0i32);
|
let fd: i32 = os.open(pathstr(dirpath), os.flag.RDONLY, 0i32);
|
||||||
if (fd < 0) { return nil: **u8, 0; };
|
if (fd < 0) { return nil: **u8, 0; };
|
||||||
let maxnames: i32 = 256;
|
let maxnames: i32 = 256;
|
||||||
@@ -3332,7 +3238,7 @@ fn expand(c: *expctx, pathcs: *u8) void = {
|
|||||||
idp, idn = scanuse(bufp + i, j - i);
|
idp, idn = scanuse(bufp + i, j - i);
|
||||||
if (idp != nil) {
|
if (idp != nil) {
|
||||||
let isdir: i32 = 0;
|
let isdir: i32 = 0;
|
||||||
let ipath: *u8 = locateimport(c.a, c.dirs, idp, idn,
|
let ipath: *u8 = locateimport(c.dirs, idp, idn,
|
||||||
&isdir);
|
&isdir);
|
||||||
if (ipath != nil) {
|
if (ipath != nil) {
|
||||||
if (isdir != 0) { expanddir(c, ipath); }
|
if (isdir != 0) { expanddir(c, ipath); }
|
||||||
@@ -3347,9 +3253,10 @@ fn expand(c: *expctx, pathcs: *u8) void = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Scan `pathcs` for its first non-comment-non-blank line; if it
|
// Scan `pathcs` for its first non-comment-non-blank line; if it
|
||||||
// starts with `package <name>;` return the package name as a
|
// starts with `package <name>;` return the package name as a fresh
|
||||||
// borrowed-arena str, else nil. Same shape as cstage peek_package.
|
// heap-allocated NUL-terminated *u8, else nil. Same shape as cstage
|
||||||
fn peekpackage(a: *arena, pathcs: *u8) *u8 = {
|
// peek_package.
|
||||||
|
fn peekpackage(pathcs: *u8) *u8 = {
|
||||||
let fd: i32 = os.open(pathstr(pathcs), os.flag.RDONLY, 0i32);
|
let fd: i32 = os.open(pathstr(pathcs), os.flag.RDONLY, 0i32);
|
||||||
if (fd < 0) { return nil; };
|
if (fd < 0) { return nil; };
|
||||||
let buf: []u8 = alloc([], 2048u64)!;
|
let buf: []u8 = alloc([], 2048u64)!;
|
||||||
@@ -3451,7 +3358,7 @@ fn strictpkgmismatch(file: *u8, pkg: *u8, dirpkg: *u8, dirpath: *u8) void = {
|
|||||||
fn expanddir(c: *expctx, dirpath: *u8) void = {
|
fn expanddir(c: *expctx, dirpath: *u8) void = {
|
||||||
let names: **u8;
|
let names: **u8;
|
||||||
let n: i32;
|
let n: i32;
|
||||||
names, n = enumeratedir(c.a, dirpath);
|
names, n = enumeratedir(dirpath);
|
||||||
let dlen: u64 = cstrlen(dirpath);
|
let dlen: u64 = cstrlen(dirpath);
|
||||||
let dirpkg: *u8 = nil;
|
let dirpkg: *u8 = nil;
|
||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
@@ -3464,7 +3371,7 @@ fn expanddir(c: *expctx, dirpath: *u8) void = {
|
|||||||
k = 0u64;
|
k = 0u64;
|
||||||
for (k < nlen) { fp[dlen + 1u64 + k] = names[i][k]; k += 1u64; };
|
for (k < nlen) { fp[dlen + 1u64 + k] = names[i][k]; k += 1u64; };
|
||||||
fp[dlen + 1u64 + nlen] = 0u8;
|
fp[dlen + 1u64 + nlen] = 0u8;
|
||||||
let pkg: *u8 = peekpackage(c.a, fp.ptr);
|
let pkg: *u8 = peekpackage(fp.ptr);
|
||||||
if (pkg != nil) {
|
if (pkg != nil) {
|
||||||
if (dirpkg == nil) { dirpkg = pkg; }
|
if (dirpkg == nil) { dirpkg = pkg; }
|
||||||
else { if (!cstreq(dirpkg, pkg)) {
|
else { if (!cstreq(dirpkg, pkg)) {
|
||||||
@@ -3532,8 +3439,6 @@ type lflags = struct {
|
|||||||
// still drives the C-built w6c/w6a/w6l. Test 993 pins the two
|
// still drives the C-built w6c/w6a/w6l. Test 993 pins the two
|
||||||
// pipelines to byte-identical output on a corpus.
|
// pipelines to byte-identical output on a corpus.
|
||||||
fn buildone(selfdir: *u8, src: *u8, entryisdir: i32, out: *u8, incs: *u8, lf: *lflags) i32 = {
|
fn buildone(selfdir: *u8, src: *u8, entryisdir: i32, out: *u8, incs: *u8, lf: *lflags) i32 = {
|
||||||
let a: *arena = newarena();
|
|
||||||
|
|
||||||
let c6: *u8 = joinpathlit(selfdir, "w6c_ww");
|
let c6: *u8 = joinpathlit(selfdir, "w6c_ww");
|
||||||
let a6: *u8 = joinpathlit(selfdir, "w6a_ww");
|
let a6: *u8 = joinpathlit(selfdir, "w6a_ww");
|
||||||
let l6: *u8 = joinpathlit(selfdir, "w6l_ww");
|
let l6: *u8 = joinpathlit(selfdir, "w6l_ww");
|
||||||
@@ -3638,7 +3543,6 @@ fn buildone(selfdir: *u8, src: *u8, entryisdir: i32, out: *u8, incs: *u8, lf: *l
|
|||||||
};
|
};
|
||||||
{
|
{
|
||||||
let c: expctx;
|
let c: expctx;
|
||||||
c.a = a;
|
|
||||||
c.out = cf;
|
c.out = cf;
|
||||||
c.dirs = searchpath.ptr;
|
c.dirs = searchpath.ptr;
|
||||||
c.visit = nil;
|
c.visit = nil;
|
||||||
@@ -3762,8 +3666,8 @@ fn basenameoff(p: *u8, plen: u64) u64 = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// arenadupcstr — copy `plen` bytes from `src` into a fresh NUL-sealed
|
// arenadupcstr — copy `plen` bytes from `src` into a fresh NUL-sealed
|
||||||
// arena buffer.
|
// heap buffer.
|
||||||
fn arenadupcstr(a: *arena, src: *u8, plen: u64) *u8 = {
|
fn arenadupcstr(src: *u8, plen: u64) *u8 = {
|
||||||
let buf: []u8 = alloc([], plen + 1u64)!;
|
let buf: []u8 = alloc([], plen + 1u64)!;
|
||||||
let i: u64 = 0u64;
|
let i: u64 = 0u64;
|
||||||
for (i < plen) { buf[i] = src[i]; i += 1u64; };
|
for (i < plen) { buf[i] = src[i]; i += 1u64; };
|
||||||
@@ -3771,8 +3675,8 @@ fn arenadupcstr(a: *arena, src: *u8, plen: u64) *u8 = {
|
|||||||
return buf.ptr;
|
return buf.ptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
// builddirmodulepath — alloc <dir>/<base>.ww, NUL-terminated, in `a`.
|
// builddirmodulepath — alloc <dir>/<base>.ww, NUL-terminated.
|
||||||
fn builddirmodulepath(a: *arena, dir: *u8, dlen: u64,
|
fn builddirmodulepath(dir: *u8, dlen: u64,
|
||||||
base: *u8, blen: u64) *u8 = {
|
base: *u8, blen: u64) *u8 = {
|
||||||
let need: u64 = dlen + 1u64 + blen + 3u64 + 1u64;
|
let need: u64 = dlen + 1u64 + blen + 3u64 + 1u64;
|
||||||
let buf: []u8 = alloc([], need)!;
|
let buf: []u8 = alloc([], need)!;
|
||||||
@@ -3791,7 +3695,7 @@ fn builddirmodulepath(a: *arena, dir: *u8, dlen: u64,
|
|||||||
|
|
||||||
// buildsearchpath — compose the colon-separated lookup path used by
|
// buildsearchpath — compose the colon-separated lookup path used by
|
||||||
// resolvemodule's case (4). Order: "." : <incs> : <selfdir>/../../lib
|
// resolvemodule's case (4). Order: "." : <incs> : <selfdir>/../../lib
|
||||||
fn buildsearchpath(a: *arena, selfdir: *u8, incs: *u8) *u8 = {
|
fn buildsearchpath(selfdir: *u8, incs: *u8) *u8 = {
|
||||||
let buf: []u8 = alloc([], PATH_MAX * 2u64)!;
|
let buf: []u8 = alloc([], PATH_MAX * 2u64)!;
|
||||||
let off: u64 = 0u64;
|
let off: u64 = 0u64;
|
||||||
buf[off] = 46u8; off += 1u64; // '.'
|
buf[off] = 46u8; off += 1u64; // '.'
|
||||||
@@ -3811,14 +3715,14 @@ fn buildsearchpath(a: *arena, selfdir: *u8, incs: *u8) *u8 = {
|
|||||||
// resolvemodule — map a name like "foo", "lib/foo", "foo.ww", or
|
// resolvemodule — map a name like "foo", "lib/foo", "foo.ww", or
|
||||||
// "." to a concrete entry path. Sets *isdir when the entry is a
|
// "." to a concrete entry path. Sets *isdir when the entry is a
|
||||||
// module directory (caller will dir-enumerate).
|
// module directory (caller will dir-enumerate).
|
||||||
fn resolvemodule(a: *arena, selfdir: *u8, name: *u8, incs: *u8, isdir: *i32) *u8 = {
|
fn resolvemodule(selfdir: *u8, name: *u8, incs: *u8, isdir: *i32) *u8 = {
|
||||||
let nlen: u64 = cstrlen(name);
|
let nlen: u64 = cstrlen(name);
|
||||||
|
|
||||||
// (1) Literal file that exists → use as-is.
|
// (1) Literal file that exists → use as-is.
|
||||||
if (cstrendswithlit(name, ".ww")) {
|
if (cstrendswithlit(name, ".ww")) {
|
||||||
if (os.access(pathstr(name), 0i32) == 0) {
|
if (os.access(pathstr(name), 0i32) == 0) {
|
||||||
*isdir = 0;
|
*isdir = 0;
|
||||||
return arenadupcstr(a, name, nlen);
|
return arenadupcstr(name, nlen);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -3837,12 +3741,12 @@ fn resolvemodule(a: *arena, selfdir: *u8, name: *u8, incs: *u8, isdir: *i32) *u8
|
|||||||
};
|
};
|
||||||
if (found) {
|
if (found) {
|
||||||
*isdir = foundisdir;
|
*isdir = foundisdir;
|
||||||
return arenadupcstr(a, name, nlen);
|
return arenadupcstr(name, nlen);
|
||||||
};
|
};
|
||||||
|
|
||||||
// (3) Search-path lookup with dot-to-slash path translation.
|
// (3) Search-path lookup with dot-to-slash path translation.
|
||||||
let search: *u8 = buildsearchpath(a, selfdir, incs);
|
let search: *u8 = buildsearchpath(selfdir, incs);
|
||||||
return locateimport(a, search, name, nlen, isdir);
|
return locateimport(search, name, nlen, isdir);
|
||||||
};
|
};
|
||||||
|
|
||||||
// ---- Subcommand handlers ----------------------------------------------
|
// ---- Subcommand handlers ----------------------------------------------
|
||||||
@@ -3891,7 +3795,6 @@ fn defaultoutpath(src: *u8) *u8 = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||||
let a: *arena = newarena();
|
|
||||||
let src: *u8 = nil;
|
let src: *u8 = nil;
|
||||||
let incs: []u8 = alloc([], PATH_MAX * 2u64)!;
|
let incs: []u8 = alloc([], PATH_MAX * 2u64)!;
|
||||||
incs.len = (PATH_MAX * 2u64): i32;
|
incs.len = (PATH_MAX * 2u64): i32;
|
||||||
@@ -3980,7 +3883,7 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
|||||||
src = &dot[0];
|
src = &dot[0];
|
||||||
};
|
};
|
||||||
let isdir: i32 = 0;
|
let isdir: i32 = 0;
|
||||||
let resolved: *u8 = resolvemodule(a, selfdir, src, incs.ptr, &isdir);
|
let resolved: *u8 = resolvemodule(selfdir, src, incs.ptr, &isdir);
|
||||||
if (resolved == nil) {
|
if (resolved == nil) {
|
||||||
os.write(2, "ww build: cannot find module\n".ptr, 29u64);
|
os.write(2, "ww build: cannot find module\n".ptr, 29u64);
|
||||||
return 1;
|
return 1;
|
||||||
@@ -4042,7 +3945,6 @@ fn makeruntmp(buf: *u8) void = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn dorun(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
fn dorun(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||||
let a: *arena = newarena();
|
|
||||||
let src: *u8 = nil;
|
let src: *u8 = nil;
|
||||||
let passstart: i32 = -1; // first argv idx to pass through to program
|
let passstart: i32 = -1; // first argv idx to pass through to program
|
||||||
let incs: []u8 = alloc([], PATH_MAX * 2u64)!;
|
let incs: []u8 = alloc([], PATH_MAX * 2u64)!;
|
||||||
@@ -4139,7 +4041,7 @@ fn dorun(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
|||||||
src = &dot[0];
|
src = &dot[0];
|
||||||
};
|
};
|
||||||
let isdir: i32 = 0;
|
let isdir: i32 = 0;
|
||||||
let resolved: *u8 = resolvemodule(a, selfdir, src, incs.ptr, &isdir);
|
let resolved: *u8 = resolvemodule(selfdir, src, incs.ptr, &isdir);
|
||||||
if (resolved == nil) {
|
if (resolved == nil) {
|
||||||
os.write(2, "ww run: cannot find module\n".ptr, 27u64);
|
os.write(2, "ww run: cannot find module\n".ptr, 27u64);
|
||||||
return 1;
|
return 1;
|
||||||
@@ -4278,7 +4180,6 @@ fn rundirtests(selfdir: *u8, dir: *u8) i32 = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||||
let a: *arena = newarena();
|
|
||||||
let target: *u8;
|
let target: *u8;
|
||||||
if (start >= argc) {
|
if (start >= argc) {
|
||||||
let dot: [2]u8 = ['.': u8, 0u8];
|
let dot: [2]u8 = ['.': u8, 0u8];
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ package main;
|
|||||||
|
|
||||||
import os;
|
import os;
|
||||||
import rt;
|
import rt;
|
||||||
import mem;
|
|
||||||
import strings;
|
import strings;
|
||||||
|
|
||||||
// All path/string scratch buffers go on the runtime page allocator.
|
// All path/string scratch buffers go on the runtime page allocator.
|
||||||
@@ -32,7 +31,7 @@ fn cstrlen(p: *u8) u64 = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// pathstr — view a NUL-terminated *u8 as a str. Bridges the
|
// pathstr — view a NUL-terminated *u8 as a str. Bridges the
|
||||||
// driver's argv/arena *u8 paths to lib/os entrypoints (str
|
// driver's argv-style *u8 paths to lib/os entrypoints (str
|
||||||
// post-task-#23).
|
// post-task-#23).
|
||||||
fn pathstr(p: *u8) str = {
|
fn pathstr(p: *u8) str = {
|
||||||
let r: str;
|
let r: str;
|
||||||
@@ -203,7 +202,6 @@ type strnode = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type expctx = struct {
|
type expctx = struct {
|
||||||
a: *arena, // arena for path strings + the visited list
|
|
||||||
out: i32, // fd we're writing the combined source to
|
out: i32, // fd we're writing the combined source to
|
||||||
dirs: *u8, // ":"-separated search path (NUL-terminated)
|
dirs: *u8, // ":"-separated search path (NUL-terminated)
|
||||||
visit: *strnode,
|
visit: *strnode,
|
||||||
@@ -236,7 +234,7 @@ fn visitadd(c: *expctx, path: str) void = {
|
|||||||
// use-path → fs-path mapping
|
// use-path → fs-path mapping
|
||||||
// (ref/hare/hare/module/srcs.ha:78 builds the same shape via
|
// (ref/hare/hare/module/srcs.ha:78 builds the same shape via
|
||||||
// path::push per ident part).
|
// path::push per ident part).
|
||||||
fn importpathform(a: *arena, name: *u8, namelen: u64) *u8 = {
|
fn importpathform(name: *u8, namelen: u64) *u8 = {
|
||||||
let buf: []u8 = alloc([], namelen + 1u64)!;
|
let buf: []u8 = alloc([], namelen + 1u64)!;
|
||||||
let i: u64 = 0u64;
|
let i: u64 = 0u64;
|
||||||
for (i < namelen) {
|
for (i < namelen) {
|
||||||
@@ -254,7 +252,7 @@ fn importpathform(a: *arena, name: *u8, namelen: u64) *u8 = {
|
|||||||
// form was dropped in task #22 — directory-as-module enumeration
|
// form was dropped in task #22 — directory-as-module enumeration
|
||||||
// replaces it, mirroring ref/hare/hare/module/srcs.ha (Hare has no
|
// replaces it, mirroring ref/hare/hare/module/srcs.ha (Hare has no
|
||||||
// `foo/foo.ha` fallback; a module IS the directory).
|
// `foo/foo.ha` fallback; a module IS the directory).
|
||||||
fn locatein(a: *arena, dir: *u8, dirlen: u64,
|
fn locatein(dir: *u8, dirlen: u64,
|
||||||
pathform: *u8, pflen: u64, isdir: *i32) *u8 = {
|
pathform: *u8, pflen: u64, isdir: *i32) *u8 = {
|
||||||
let buf: []u8 = alloc([], PATH_MAX)!;
|
let buf: []u8 = alloc([], PATH_MAX)!;
|
||||||
let off: u64 = 0u64;
|
let off: u64 = 0u64;
|
||||||
@@ -303,9 +301,9 @@ fn locatein(a: *arena, dir: *u8, dirlen: u64,
|
|||||||
|
|
||||||
// Walk a colon-separated dirlist, return first hit or nil. Sets
|
// Walk a colon-separated dirlist, return first hit or nil. Sets
|
||||||
// *isdir on hit.
|
// *isdir on hit.
|
||||||
fn locateimport(a: *arena, dirs: *u8, name: *u8, namelen: u64,
|
fn locateimport(dirs: *u8, name: *u8, namelen: u64,
|
||||||
isdir: *i32) *u8 = {
|
isdir: *i32) *u8 = {
|
||||||
let pathform: *u8 = importpathform(a, name, namelen);
|
let pathform: *u8 = importpathform(name, namelen);
|
||||||
let pflen: u64 = cstrlen(pathform);
|
let pflen: u64 = cstrlen(pathform);
|
||||||
let total: u64 = cstrlen(dirs);
|
let total: u64 = cstrlen(dirs);
|
||||||
let p: u64 = 0u64;
|
let p: u64 = 0u64;
|
||||||
@@ -317,7 +315,7 @@ fn locateimport(a: *arena, dirs: *u8, name: *u8, namelen: u64,
|
|||||||
};
|
};
|
||||||
let seglen: u64 = q - p;
|
let seglen: u64 = q - p;
|
||||||
if (seglen > 0u64) {
|
if (seglen > 0u64) {
|
||||||
let hit: *u8 = locatein(a, dirs + p, seglen,
|
let hit: *u8 = locatein(dirs + p, seglen,
|
||||||
pathform, pflen, isdir);
|
pathform, pflen, isdir);
|
||||||
if (hit != nil) { return hit; };
|
if (hit != nil) { return hit; };
|
||||||
};
|
};
|
||||||
@@ -395,8 +393,8 @@ fn bytecmp(a: *u8, alen: u64, b: *u8, blen: u64) i32 = {
|
|||||||
|
|
||||||
// enumeratedir — list *.ww entries of `dirpath` (less *test.ww and
|
// enumeratedir — list *.ww entries of `dirpath` (less *test.ww and
|
||||||
// *.combined.ww), byte-sort. Returns (names[], nnames) with each
|
// *.combined.ww), byte-sort. Returns (names[], nnames) with each
|
||||||
// name a NUL-terminated arena copy.
|
// name a NUL-terminated heap copy.
|
||||||
fn enumeratedir(a: *arena, dirpath: *u8) (**u8, i32) = {
|
fn enumeratedir(dirpath: *u8) (**u8, i32) = {
|
||||||
let fd: i32 = os.open(pathstr(dirpath), os.flag.RDONLY, 0i32);
|
let fd: i32 = os.open(pathstr(dirpath), os.flag.RDONLY, 0i32);
|
||||||
if (fd < 0) { return nil: **u8, 0; };
|
if (fd < 0) { return nil: **u8, 0; };
|
||||||
let maxnames: i32 = 256;
|
let maxnames: i32 = 256;
|
||||||
@@ -557,7 +555,7 @@ fn expand(c: *expctx, pathcs: *u8) void = {
|
|||||||
idp, idn = scanuse(bufp + i, j - i);
|
idp, idn = scanuse(bufp + i, j - i);
|
||||||
if (idp != nil) {
|
if (idp != nil) {
|
||||||
let isdir: i32 = 0;
|
let isdir: i32 = 0;
|
||||||
let ipath: *u8 = locateimport(c.a, c.dirs, idp, idn,
|
let ipath: *u8 = locateimport(c.dirs, idp, idn,
|
||||||
&isdir);
|
&isdir);
|
||||||
if (ipath != nil) {
|
if (ipath != nil) {
|
||||||
if (isdir != 0) { expanddir(c, ipath); }
|
if (isdir != 0) { expanddir(c, ipath); }
|
||||||
@@ -572,9 +570,10 @@ fn expand(c: *expctx, pathcs: *u8) void = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Scan `pathcs` for its first non-comment-non-blank line; if it
|
// Scan `pathcs` for its first non-comment-non-blank line; if it
|
||||||
// starts with `package <name>;` return the package name as a
|
// starts with `package <name>;` return the package name as a fresh
|
||||||
// borrowed-arena str, else nil. Same shape as cstage peek_package.
|
// heap-allocated NUL-terminated *u8, else nil. Same shape as cstage
|
||||||
fn peekpackage(a: *arena, pathcs: *u8) *u8 = {
|
// peek_package.
|
||||||
|
fn peekpackage(pathcs: *u8) *u8 = {
|
||||||
let fd: i32 = os.open(pathstr(pathcs), os.flag.RDONLY, 0i32);
|
let fd: i32 = os.open(pathstr(pathcs), os.flag.RDONLY, 0i32);
|
||||||
if (fd < 0) { return nil; };
|
if (fd < 0) { return nil; };
|
||||||
let buf: []u8 = alloc([], 2048u64)!;
|
let buf: []u8 = alloc([], 2048u64)!;
|
||||||
@@ -676,7 +675,7 @@ fn strictpkgmismatch(file: *u8, pkg: *u8, dirpkg: *u8, dirpath: *u8) void = {
|
|||||||
fn expanddir(c: *expctx, dirpath: *u8) void = {
|
fn expanddir(c: *expctx, dirpath: *u8) void = {
|
||||||
let names: **u8;
|
let names: **u8;
|
||||||
let n: i32;
|
let n: i32;
|
||||||
names, n = enumeratedir(c.a, dirpath);
|
names, n = enumeratedir(dirpath);
|
||||||
let dlen: u64 = cstrlen(dirpath);
|
let dlen: u64 = cstrlen(dirpath);
|
||||||
let dirpkg: *u8 = nil;
|
let dirpkg: *u8 = nil;
|
||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
@@ -689,7 +688,7 @@ fn expanddir(c: *expctx, dirpath: *u8) void = {
|
|||||||
k = 0u64;
|
k = 0u64;
|
||||||
for (k < nlen) { fp[dlen + 1u64 + k] = names[i][k]; k += 1u64; };
|
for (k < nlen) { fp[dlen + 1u64 + k] = names[i][k]; k += 1u64; };
|
||||||
fp[dlen + 1u64 + nlen] = 0u8;
|
fp[dlen + 1u64 + nlen] = 0u8;
|
||||||
let pkg: *u8 = peekpackage(c.a, fp.ptr);
|
let pkg: *u8 = peekpackage(fp.ptr);
|
||||||
if (pkg != nil) {
|
if (pkg != nil) {
|
||||||
if (dirpkg == nil) { dirpkg = pkg; }
|
if (dirpkg == nil) { dirpkg = pkg; }
|
||||||
else { if (!cstreq(dirpkg, pkg)) {
|
else { if (!cstreq(dirpkg, pkg)) {
|
||||||
@@ -757,8 +756,6 @@ type lflags = struct {
|
|||||||
// still drives the C-built w6c/w6a/w6l. Test 993 pins the two
|
// still drives the C-built w6c/w6a/w6l. Test 993 pins the two
|
||||||
// pipelines to byte-identical output on a corpus.
|
// pipelines to byte-identical output on a corpus.
|
||||||
fn buildone(selfdir: *u8, src: *u8, entryisdir: i32, out: *u8, incs: *u8, lf: *lflags) i32 = {
|
fn buildone(selfdir: *u8, src: *u8, entryisdir: i32, out: *u8, incs: *u8, lf: *lflags) i32 = {
|
||||||
let a: *arena = newarena();
|
|
||||||
|
|
||||||
let c6: *u8 = joinpathlit(selfdir, "w6c_ww");
|
let c6: *u8 = joinpathlit(selfdir, "w6c_ww");
|
||||||
let a6: *u8 = joinpathlit(selfdir, "w6a_ww");
|
let a6: *u8 = joinpathlit(selfdir, "w6a_ww");
|
||||||
let l6: *u8 = joinpathlit(selfdir, "w6l_ww");
|
let l6: *u8 = joinpathlit(selfdir, "w6l_ww");
|
||||||
@@ -863,7 +860,6 @@ fn buildone(selfdir: *u8, src: *u8, entryisdir: i32, out: *u8, incs: *u8, lf: *l
|
|||||||
};
|
};
|
||||||
{
|
{
|
||||||
let c: expctx;
|
let c: expctx;
|
||||||
c.a = a;
|
|
||||||
c.out = cf;
|
c.out = cf;
|
||||||
c.dirs = searchpath.ptr;
|
c.dirs = searchpath.ptr;
|
||||||
c.visit = nil;
|
c.visit = nil;
|
||||||
@@ -987,8 +983,8 @@ fn basenameoff(p: *u8, plen: u64) u64 = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// arenadupcstr — copy `plen` bytes from `src` into a fresh NUL-sealed
|
// arenadupcstr — copy `plen` bytes from `src` into a fresh NUL-sealed
|
||||||
// arena buffer.
|
// heap buffer.
|
||||||
fn arenadupcstr(a: *arena, src: *u8, plen: u64) *u8 = {
|
fn arenadupcstr(src: *u8, plen: u64) *u8 = {
|
||||||
let buf: []u8 = alloc([], plen + 1u64)!;
|
let buf: []u8 = alloc([], plen + 1u64)!;
|
||||||
let i: u64 = 0u64;
|
let i: u64 = 0u64;
|
||||||
for (i < plen) { buf[i] = src[i]; i += 1u64; };
|
for (i < plen) { buf[i] = src[i]; i += 1u64; };
|
||||||
@@ -996,8 +992,8 @@ fn arenadupcstr(a: *arena, src: *u8, plen: u64) *u8 = {
|
|||||||
return buf.ptr;
|
return buf.ptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
// builddirmodulepath — alloc <dir>/<base>.ww, NUL-terminated, in `a`.
|
// builddirmodulepath — alloc <dir>/<base>.ww, NUL-terminated.
|
||||||
fn builddirmodulepath(a: *arena, dir: *u8, dlen: u64,
|
fn builddirmodulepath(dir: *u8, dlen: u64,
|
||||||
base: *u8, blen: u64) *u8 = {
|
base: *u8, blen: u64) *u8 = {
|
||||||
let need: u64 = dlen + 1u64 + blen + 3u64 + 1u64;
|
let need: u64 = dlen + 1u64 + blen + 3u64 + 1u64;
|
||||||
let buf: []u8 = alloc([], need)!;
|
let buf: []u8 = alloc([], need)!;
|
||||||
@@ -1016,7 +1012,7 @@ fn builddirmodulepath(a: *arena, dir: *u8, dlen: u64,
|
|||||||
|
|
||||||
// buildsearchpath — compose the colon-separated lookup path used by
|
// buildsearchpath — compose the colon-separated lookup path used by
|
||||||
// resolvemodule's case (4). Order: "." : <incs> : <selfdir>/../../lib
|
// resolvemodule's case (4). Order: "." : <incs> : <selfdir>/../../lib
|
||||||
fn buildsearchpath(a: *arena, selfdir: *u8, incs: *u8) *u8 = {
|
fn buildsearchpath(selfdir: *u8, incs: *u8) *u8 = {
|
||||||
let buf: []u8 = alloc([], PATH_MAX * 2u64)!;
|
let buf: []u8 = alloc([], PATH_MAX * 2u64)!;
|
||||||
let off: u64 = 0u64;
|
let off: u64 = 0u64;
|
||||||
buf[off] = 46u8; off += 1u64; // '.'
|
buf[off] = 46u8; off += 1u64; // '.'
|
||||||
@@ -1036,14 +1032,14 @@ fn buildsearchpath(a: *arena, selfdir: *u8, incs: *u8) *u8 = {
|
|||||||
// resolvemodule — map a name like "foo", "lib/foo", "foo.ww", or
|
// resolvemodule — map a name like "foo", "lib/foo", "foo.ww", or
|
||||||
// "." to a concrete entry path. Sets *isdir when the entry is a
|
// "." to a concrete entry path. Sets *isdir when the entry is a
|
||||||
// module directory (caller will dir-enumerate).
|
// module directory (caller will dir-enumerate).
|
||||||
fn resolvemodule(a: *arena, selfdir: *u8, name: *u8, incs: *u8, isdir: *i32) *u8 = {
|
fn resolvemodule(selfdir: *u8, name: *u8, incs: *u8, isdir: *i32) *u8 = {
|
||||||
let nlen: u64 = cstrlen(name);
|
let nlen: u64 = cstrlen(name);
|
||||||
|
|
||||||
// (1) Literal file that exists → use as-is.
|
// (1) Literal file that exists → use as-is.
|
||||||
if (cstrendswithlit(name, ".ww")) {
|
if (cstrendswithlit(name, ".ww")) {
|
||||||
if (os.access(pathstr(name), 0i32) == 0) {
|
if (os.access(pathstr(name), 0i32) == 0) {
|
||||||
*isdir = 0;
|
*isdir = 0;
|
||||||
return arenadupcstr(a, name, nlen);
|
return arenadupcstr(name, nlen);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1062,12 +1058,12 @@ fn resolvemodule(a: *arena, selfdir: *u8, name: *u8, incs: *u8, isdir: *i32) *u8
|
|||||||
};
|
};
|
||||||
if (found) {
|
if (found) {
|
||||||
*isdir = foundisdir;
|
*isdir = foundisdir;
|
||||||
return arenadupcstr(a, name, nlen);
|
return arenadupcstr(name, nlen);
|
||||||
};
|
};
|
||||||
|
|
||||||
// (3) Search-path lookup with dot-to-slash path translation.
|
// (3) Search-path lookup with dot-to-slash path translation.
|
||||||
let search: *u8 = buildsearchpath(a, selfdir, incs);
|
let search: *u8 = buildsearchpath(selfdir, incs);
|
||||||
return locateimport(a, search, name, nlen, isdir);
|
return locateimport(search, name, nlen, isdir);
|
||||||
};
|
};
|
||||||
|
|
||||||
// ---- Subcommand handlers ----------------------------------------------
|
// ---- Subcommand handlers ----------------------------------------------
|
||||||
@@ -1116,7 +1112,6 @@ fn defaultoutpath(src: *u8) *u8 = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||||
let a: *arena = newarena();
|
|
||||||
let src: *u8 = nil;
|
let src: *u8 = nil;
|
||||||
let incs: []u8 = alloc([], PATH_MAX * 2u64)!;
|
let incs: []u8 = alloc([], PATH_MAX * 2u64)!;
|
||||||
incs.len = (PATH_MAX * 2u64): i32;
|
incs.len = (PATH_MAX * 2u64): i32;
|
||||||
@@ -1205,7 +1200,7 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
|||||||
src = &dot[0];
|
src = &dot[0];
|
||||||
};
|
};
|
||||||
let isdir: i32 = 0;
|
let isdir: i32 = 0;
|
||||||
let resolved: *u8 = resolvemodule(a, selfdir, src, incs.ptr, &isdir);
|
let resolved: *u8 = resolvemodule(selfdir, src, incs.ptr, &isdir);
|
||||||
if (resolved == nil) {
|
if (resolved == nil) {
|
||||||
os.write(2, "ww build: cannot find module\n".ptr, 29u64);
|
os.write(2, "ww build: cannot find module\n".ptr, 29u64);
|
||||||
return 1;
|
return 1;
|
||||||
@@ -1267,7 +1262,6 @@ fn makeruntmp(buf: *u8) void = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn dorun(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
fn dorun(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||||
let a: *arena = newarena();
|
|
||||||
let src: *u8 = nil;
|
let src: *u8 = nil;
|
||||||
let passstart: i32 = -1; // first argv idx to pass through to program
|
let passstart: i32 = -1; // first argv idx to pass through to program
|
||||||
let incs: []u8 = alloc([], PATH_MAX * 2u64)!;
|
let incs: []u8 = alloc([], PATH_MAX * 2u64)!;
|
||||||
@@ -1364,7 +1358,7 @@ fn dorun(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
|||||||
src = &dot[0];
|
src = &dot[0];
|
||||||
};
|
};
|
||||||
let isdir: i32 = 0;
|
let isdir: i32 = 0;
|
||||||
let resolved: *u8 = resolvemodule(a, selfdir, src, incs.ptr, &isdir);
|
let resolved: *u8 = resolvemodule(selfdir, src, incs.ptr, &isdir);
|
||||||
if (resolved == nil) {
|
if (resolved == nil) {
|
||||||
os.write(2, "ww run: cannot find module\n".ptr, 27u64);
|
os.write(2, "ww run: cannot find module\n".ptr, 27u64);
|
||||||
return 1;
|
return 1;
|
||||||
@@ -1503,7 +1497,6 @@ fn rundirtests(selfdir: *u8, dir: *u8) i32 = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||||
let a: *arena = newarena();
|
|
||||||
let target: *u8;
|
let target: *u8;
|
||||||
if (start >= argc) {
|
if (start >= argc) {
|
||||||
let dot: [2]u8 = ['.': u8, 0u8];
|
let dot: [2]u8 = ['.': u8, 0u8];
|
||||||
|
|||||||
Reference in New Issue
Block a user