lib/path: #138 c2-stack buffer port — buffer/push/appendnorm/appendlit/string/isroot/set + dot/dotdot, cstage @test
Faithful realignment of lib/path to Hare's buffer-centric API
(ref/hare/path/{stack,buffer}.ha). Replaces the old str-only path.ww
wholesale (zero consumers). Scope = stack-core: init() deferred (returns
~4KB (buffer|error), rides the #40 arc / #147); abs/dirname/basename land
in c3; extension/join dropped.
appendlit uses the #145 slice-copy-assign arm (buf.buf[lo:hi]=bs), no hand
loop. dot/dotdot are faithful module-global []u8 (D2 #148). MAX =
os.PATH_MAX-1. Divergences (size->i32 indices, frombytes, module-global
consts) cited inline.
Tests: lib/path/pathtest.ww (table-driven), wired at test/wcc/989_path_run.c;
push rows mirror stack.ha:107-111 verbatim incl the restored "/d"
intermediate. Slot 989 (overflow bucket) since 970 is taken.
C-first: cstage @test green; wwstage byte-id deferred to the #125 batch.
path classified M_WWREJECT in 989_lib_byteid (w6c_ww rejects the module-
global slice consts = #120/#29; + the #148 twin #151) — self-graduates back
to M_ID the day wwstage accepts. path moved off the 900_stdlib standalone
list (import-dependent: os.PATH_MAX def-dim + match over imported error
types; coverage at 989_path_run), per the bytes/fmt/os precedent.
Gate: all 324 passed, byte-id 990-997 green, w6c/w6c_ww unchanged
(lib-only, non-embedded).
This commit is contained in:
5
Makefile
5
Makefile
@@ -447,6 +447,7 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \
|
||||
$(BIN)/test_intdiv_signed \
|
||||
$(BIN)/test_strings_run \
|
||||
$(BIN)/test_hex_run $(BIN)/test_utf8_run $(BIN)/test_bytes_run \
|
||||
$(BIN)/test_path_run \
|
||||
$(BIN)/test_decimal_run $(BIN)/test_strconv_int_run \
|
||||
$(BIN)/test_stof_run $(BIN)/test_ftos_run \
|
||||
$(BIN)/test_memio_run $(BIN)/test_temp_run $(BIN)/test_getopt_run \
|
||||
@@ -1992,6 +1993,10 @@ $(BIN)/test_bytes_run: test/wcc/967_bytes_run.c $(BIN)/ww $(BIN)/w6c \
|
||||
$(BIN)/w6a $(BIN)/w6l $(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_path_run: test/wcc/989_path_run.c $(BIN)/ww $(BIN)/w6c \
|
||||
$(BIN)/w6a $(BIN)/w6l $(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_ascii_run: test/wcc/904_ascii_run.c $(BIN)/ww $(BIN)/w6c \
|
||||
$(BIN)/w6a $(BIN)/w6l $(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
235
lib/path/path.ww
235
lib/path/path.ww
@@ -1,134 +1,139 @@
|
||||
// path — filesystem path manipulation. UTF-8 paths, '/' separator.
|
||||
// Mirrors Hare's path:: surface. ww doesn't yet ship the stack-buffer
|
||||
// path::buffer; the functions here all take a `str` and return a
|
||||
// borrowed view (basename / dirname) or a fresh owned str (join).
|
||||
// Mirrors Hare's path:: surface (ref/hare/path/). This is the
|
||||
// path::buffer stack-buffer port (c2-stack subset: error/posix defs +
|
||||
// buffer + isroot + appendlit + appendnorm + push + string + set).
|
||||
// init/abs/dirname/basename and the rest land in later folds.
|
||||
|
||||
package path;
|
||||
|
||||
import bytes;
|
||||
import strings;
|
||||
import os;
|
||||
|
||||
def SEP: u8 = '/';
|
||||
// ref/hare/path/error.ha:6-12. The 3 !void singletons are
|
||||
// structurally identical; cstage discriminates them correctly
|
||||
// (#142 is a wwstage-only flatvariantidxt ambiguity, rides #125).
|
||||
export type cant_extend = !void;
|
||||
export type too_long = !void;
|
||||
export type not_prefix = !void;
|
||||
export type error = !(cant_extend | too_long | not_prefix);
|
||||
|
||||
// abs — `p` is an absolute path (starts with '/').
|
||||
export fn abs(p: str) bool = {
|
||||
if (p.len == 0) { return false; };
|
||||
return p[0] == SEP;
|
||||
// ref/hare/path/error.ha:15-24.
|
||||
export fn strerror(e: error) str = {
|
||||
match (e) {
|
||||
case cant_extend => return "Can't add extension (filename is root or all dots)";
|
||||
case too_long => return "Path buffer overflow";
|
||||
case not_prefix => return "Prefix not present";
|
||||
};
|
||||
};
|
||||
|
||||
// dirname — directory component of `p`, POSIX-style. Returns a
|
||||
// borrowed view of `p` (or the static "." / "/" strings). Mirrors
|
||||
// Hare's path::dirname.
|
||||
export fn dirname(p: str) str = {
|
||||
if (p.len == 0) { return "."; };
|
||||
// Strip trailing separators.
|
||||
let n: i32 = p.len;
|
||||
for (n > 0) {
|
||||
if (p[n - 1] != SEP) { break; };
|
||||
n -= 1;
|
||||
};
|
||||
if (n == 0) { return "/"; };
|
||||
// Last separator in the trimmed prefix.
|
||||
let i: i32 = n - 1;
|
||||
for (i >= 0) {
|
||||
if (p[i] == SEP) { break; };
|
||||
i -= 1;
|
||||
};
|
||||
if (i < 0) { return "."; };
|
||||
// Strip trailing separators on the directory part too.
|
||||
for (i > 0) {
|
||||
if (p[i - 1] != SEP) { break; };
|
||||
i -= 1;
|
||||
};
|
||||
if (i == 0) { return "/"; };
|
||||
let r: str;
|
||||
r.ptr = p.ptr;
|
||||
r.len = i;
|
||||
return r;
|
||||
// ref/hare/path/+linux.ha:7.
|
||||
export def SEP: u8 = '/';
|
||||
// ref/hare/path/+linux.ha:9. No c2 use; c3 abs/isroot-str consume it.
|
||||
const sepstr: str = "/";
|
||||
// ref/hare/path/+linux.ha:18 (sys::PATH_MAX-1). Div: route through
|
||||
// os.PATH_MAX (lib/os/os.ww:84, i32=4096) → MAX=4095; const-folds as
|
||||
// the buffer array dim (c1 #141). No hardcoded literal (rule-13).
|
||||
export def MAX: i32 = os.PATH_MAX - 1;
|
||||
|
||||
// ref/hare/path/buffer.ha:6-9. Div: end size→i32 (lib/CLAUDE.md
|
||||
// str.len:i32; bytes.index→(i32|void)).
|
||||
export type buffer = struct {
|
||||
buf: [MAX]u8,
|
||||
end: i32,
|
||||
};
|
||||
|
||||
// basename — final path component of `p`, POSIX-style. Returns a
|
||||
// borrowed view of `p` (or "." / "/" sentinel strings). Mirrors
|
||||
// Hare's path::basename.
|
||||
export fn basename(p: str) str = {
|
||||
if (p.len == 0) { return "."; };
|
||||
let n: i32 = p.len;
|
||||
for (n > 0) {
|
||||
if (p[n - 1] != SEP) { break; };
|
||||
n -= 1;
|
||||
};
|
||||
if (n == 0) { return "/"; };
|
||||
let i: i32 = n - 1;
|
||||
for (i >= 0) {
|
||||
if (p[i] == SEP) { break; };
|
||||
i -= 1;
|
||||
};
|
||||
let r: str;
|
||||
r.ptr = p.ptr + ((i + 1): u64);
|
||||
r.len = n - (i + 1);
|
||||
return r;
|
||||
// ref/hare/path/stack.ha:30-31. Faithful module-global []u8 (D2 #148
|
||||
// fixes the by-value-arg garbage header) — no [N]u8, no copy-loop.
|
||||
const dot: []u8 = ['.'];
|
||||
const dotdot: []u8 = ['.', '.'];
|
||||
|
||||
// ref/hare/path/buffer.ha:44-51. c3 WIDENS to isroot(path:(*buffer|str))
|
||||
// alongside abs; c2 needs only the *buffer arm (appendnorm/appendlit
|
||||
// callers).
|
||||
export fn isroot(buf: *buffer) bool = {
|
||||
return buf.end == 1 && buf.buf[0] == SEP;
|
||||
};
|
||||
|
||||
// extension — the final ".ext" suffix of `basename(p)`, including
|
||||
// the dot, or "" if none. Returns a borrowed view of `p`. Mirrors
|
||||
// Hare's path::extension.
|
||||
export fn extension(p: str) str = {
|
||||
let b: str = basename(p);
|
||||
let i: i32 = b.len - 1;
|
||||
for (i > 0) {
|
||||
if (b[i] == '.') {
|
||||
let r: str;
|
||||
r.ptr = b.ptr + (i: u64);
|
||||
r.len = b.len - i;
|
||||
return r;
|
||||
// ref/hare/path/stack.ha:63-74.
|
||||
fn appendlit(buf: *buffer, bs: []u8) (i32 | error) = {
|
||||
let newend: i32 = buf.end;
|
||||
if (buf.end == 0 || isroot(buf)) {
|
||||
if (MAX < buf.end + bs.len) {
|
||||
// cstage rejects bare `return too_long` into the nested
|
||||
// (i32|error); two-step widen (io precedent stream.ww:56-58).
|
||||
let e: error = too_long; return e;
|
||||
};
|
||||
i -= 1;
|
||||
} else {
|
||||
if (MAX < buf.end + bs.len + 1) {
|
||||
let e: error = too_long; return e;
|
||||
};
|
||||
buf.buf[buf.end] = SEP;
|
||||
newend += 1;
|
||||
};
|
||||
let empty: str;
|
||||
empty.ptr = nil;
|
||||
empty.len = 0;
|
||||
return empty;
|
||||
// Div: Hare `buf.buf[newend..newend+len(bs)] = bs` in colon
|
||||
// spelling — the #145 slice-copy-assign LHS arm (stack.ha:72).
|
||||
buf.buf[newend:newend+bs.len] = bs;
|
||||
return newend + bs.len;
|
||||
};
|
||||
|
||||
// join — concatenate two path components with a single '/' separator.
|
||||
// Returns owned str (release via os.free). If `b` is absolute, the
|
||||
// result is `b`. Mirrors Hare's path::buffer init+push, narrowed to
|
||||
// two-arg join (no variadic).
|
||||
export fn join(a: str, b: str) str = {
|
||||
if (abs(b)) {
|
||||
let buf: []u8 = alloc([], b.len: u64)!;
|
||||
let i: i32 = 0;
|
||||
for (i < b.len) { buf[i] = b[i]; i += 1; };
|
||||
buf.len = b.len;
|
||||
return strings.frombytes(buf);
|
||||
// ref/hare/path/stack.ha:43-59. Div: `..` reslice→colon; Hare if-expr
|
||||
// → if-statement; `0z` yield → `0i32` (match arms must agree, bare 0
|
||||
// is untyped_int).
|
||||
fn appendnorm(buf: *buffer, seg: []u8) (i32 | error) = {
|
||||
if (seg.len == 0 || bytes.equal(dot, seg)) { return buf.end; };
|
||||
if (bytes.equal(dotdot, seg)) {
|
||||
if (isroot(buf)) { return buf.end; };
|
||||
let isep: i32 = match (bytes.rindex(buf.buf[0:buf.end], SEP)) {
|
||||
case void => yield 0i32;
|
||||
case let i: i32 => yield i + 1;
|
||||
};
|
||||
if (buf.end == 0 || bytes.equal(buf.buf[isep:buf.end], dotdot)) {
|
||||
return appendlit(buf, dotdot)?;
|
||||
} else {
|
||||
if (isep <= 1) { return isep; };
|
||||
return isep - 1;
|
||||
};
|
||||
} else {
|
||||
return appendlit(buf, seg)?;
|
||||
};
|
||||
if (a.len == 0) {
|
||||
let buf: []u8 = alloc([], b.len: u64)!;
|
||||
let i: i32 = 0;
|
||||
for (i < b.len) { buf[i] = b[i]; i += 1; };
|
||||
buf.len = b.len;
|
||||
return strings.frombytes(buf);
|
||||
};
|
||||
if (b.len == 0) {
|
||||
let buf: []u8 = alloc([], a.len: u64)!;
|
||||
let i: i32 = 0;
|
||||
for (i < a.len) { buf[i] = a[i]; i += 1; };
|
||||
buf.len = a.len;
|
||||
return strings.frombytes(buf);
|
||||
};
|
||||
// Trim trailing '/' from a; b never starts with '/' here (checked
|
||||
// above via abs(b)).
|
||||
let an: i32 = a.len;
|
||||
for (an > 0) {
|
||||
if (a[an - 1] != SEP) { break; };
|
||||
an -= 1;
|
||||
};
|
||||
let total: i32 = an + 1 + b.len;
|
||||
let buf: []u8 = alloc([], total: u64)!;
|
||||
let i: i32 = 0;
|
||||
for (i < an) { buf[i] = a[i]; i += 1; };
|
||||
buf[an] = SEP;
|
||||
let j: i32 = 0;
|
||||
for (j < b.len) { buf[an + 1 + j] = b[j]; j += 1; };
|
||||
buf.len = total;
|
||||
return strings.frombytes(buf);
|
||||
};
|
||||
|
||||
// ref/hare/path/buffer.ha:28-31. Div: fromutf8_unsafe→strings.frombytes
|
||||
// (rule-9 carve-out); `..` reslice→colon.
|
||||
export fn string(buf: *buffer) str = {
|
||||
if (buf.end == 0) { return "."; };
|
||||
return strings.frombytes(buf.buf[0:buf.end]);
|
||||
};
|
||||
|
||||
// ref/hare/path/stack.ha:9-28. Div: `..` reslice→colon; len(x)→x.len.
|
||||
export fn push(buf: *buffer, items: str...) (str | error) = {
|
||||
for (let item .. items) {
|
||||
let elem: []u8 = strings.toutf8(item);
|
||||
// Hare's `for(true) match{...}` (stack.ha:13-25) breaks out of
|
||||
// the void arm and re-loops the j-arm; ww spells the loop step
|
||||
// as explicit `continue` (j-arm) + `break` (after match).
|
||||
for (true) {
|
||||
match (bytes.index(elem, SEP)) {
|
||||
case void => { buf.end = appendnorm(buf, elem)?; };
|
||||
case let j: i32 => {
|
||||
if (j == 0 && buf.end == 0) {
|
||||
buf.buf[0] = SEP; buf.end = 1;
|
||||
} else {
|
||||
buf.end = appendnorm(buf, elem[0:j])?;
|
||||
};
|
||||
elem = elem[j+1:];
|
||||
continue;
|
||||
};
|
||||
};
|
||||
break;
|
||||
};
|
||||
};
|
||||
return string(buf);
|
||||
};
|
||||
|
||||
// ref/hare/path/buffer.ha:20-23. (str|error) ≤32B return → in c2-stack.
|
||||
export fn set(buf: *buffer, items: str...) (str | error) = {
|
||||
buf.end = 0;
|
||||
return push(buf, items...);
|
||||
};
|
||||
|
||||
123
lib/path/pathtest.ww
Normal file
123
lib/path/pathtest.ww
Normal file
@@ -0,0 +1,123 @@
|
||||
// pathtest — exercises lib/path (c2-stack subset). Run with
|
||||
// `out/bin/ww run lib/path/pathtest.ww`. Same signalled-then-fail()
|
||||
// -with-+10 pattern as bytes / getopt tests: a non-zero exit code
|
||||
// pinpoints the failing scenario.
|
||||
//
|
||||
// Vectors mirror Hare's @test fns in ref/hare/path/stack.ha:77-119
|
||||
// and buffer.ha. The absolute rows (Hare's `local("/")`-seeded) DEFER
|
||||
// to c3 (abs/local unimplemented); only the relative rows run here.
|
||||
//
|
||||
// Parallel `[N]str` row arrays (rather than `[N]struct{...}`) sidestep
|
||||
// the cstage cgen chained `arr[i].field` store gap (getopttest.ww:6).
|
||||
// push is stateful, so each row is applied in sequence to one buffer;
|
||||
// the table holds {segment, expected-string} pairs (stack.ha:36-42 is
|
||||
// the normalization spec these rows encode).
|
||||
|
||||
package path;
|
||||
|
||||
import path;
|
||||
import os;
|
||||
|
||||
let signalled: i32 = 0;
|
||||
fn fail() void = { os.exit(signalled + 10); };
|
||||
|
||||
// ---- push() + appendnorm normalization --------------------------------
|
||||
// ref/hare/path/stack.ha:77-119 (relative rows only; absolute local()
|
||||
// rows defer to c3). The dot/dotdot handling exercised here IS the
|
||||
// appendnorm normalization spec (stack.ha:36-42), so no separate
|
||||
// appendnorm @test is needed.
|
||||
|
||||
@test fn push_cases() void = {
|
||||
let buf = buffer { ... };
|
||||
if (string(&buf) != ".") { fail(); };
|
||||
|
||||
// current-dir + parent-dir invariants (stack.ha:82-88). Single
|
||||
// segment per row → table-driven sequential apply.
|
||||
let segs: [5]str;
|
||||
let wants: [5]str;
|
||||
segs[0]=""; wants[0]=".";
|
||||
segs[1]="."; wants[1]=".";
|
||||
segs[2]=".."; wants[2]="..";
|
||||
segs[3]=""; wants[3]="..";
|
||||
segs[4]="."; wants[4]="..";
|
||||
let i: i32 = 0;
|
||||
for (i < 5) {
|
||||
if (push(&buf, segs[i])! != wants[i]) { fail(); };
|
||||
i += 1;
|
||||
};
|
||||
|
||||
// set(&buf) resets to "." (stack.ha:91). set forwards an EMPTY
|
||||
// variadic into push (KEN-oracle flag 1: zero-arg gather+forward).
|
||||
if (set(&buf)! != ".") { fail(); };
|
||||
|
||||
// regular path + parent (stack.ha:101-104, minus the local() row).
|
||||
let segs2: [3]str;
|
||||
let wants2: [3]str;
|
||||
segs2[0]="foo"; wants2[0]="foo";
|
||||
segs2[1]="."; wants2[1]="foo";
|
||||
segs2[2]=".."; wants2[2]=".";
|
||||
let k: i32 = 0;
|
||||
for (k < 3) {
|
||||
if (push(&buf, segs2[k])! != wants2[k]) { fail(); };
|
||||
k += 1;
|
||||
};
|
||||
|
||||
// multi-segment (stack.ha:107-111). Variadic arity varies (1 or 2)
|
||||
// → inline.
|
||||
if (push(&buf, "a", "b")! != "a/b") { fail(); };
|
||||
if (push(&buf, "..", "c")! != "a/c") { fail(); };
|
||||
if (push(&buf, "..")! != "a") { fail(); };
|
||||
// stack.ha:110; local()→literal, SEP='/' on Linux, proper local() rides c3
|
||||
if (push(&buf, "/d")! != "a/d") { fail(); };
|
||||
if (push(&buf, "..", "..")! != ".") { fail(); }; // stack.ha:111
|
||||
|
||||
// leading-SEP segment into an EMPTY buffer exercises push's
|
||||
// `j==0 && buf.end==0` root-seed arm (stack.ha:18-20), unreached
|
||||
// by the relative rows above. Hare covers it via local("/")-seeded
|
||||
// rows that defer to c3; literal "/foo" stands in (SEP='/' on Linux).
|
||||
if (set(&buf)! != ".") { fail(); };
|
||||
if (push(&buf, "/foo")! != "/foo") { fail(); };
|
||||
};
|
||||
|
||||
// ---- isroot() ---------------------------------------------------------
|
||||
// ref/hare/path/buffer.ha:44-51 (*buffer arm). Heterogeneous seeds
|
||||
// (manual root seed vs push) → inline rather than a uniform table.
|
||||
|
||||
@test fn isroot_cases() void = {
|
||||
let buf = buffer { ... };
|
||||
// empty buffer (end 0)
|
||||
if (isroot(&buf)) { fail(); };
|
||||
|
||||
// "/" — root. local() is c3, so seed the SEP byte directly.
|
||||
buf.buf[0] = SEP; buf.end = 1;
|
||||
if (!isroot(&buf)) { fail(); };
|
||||
if (string(&buf) != "/") { fail(); };
|
||||
|
||||
// "foo" — relative, not root.
|
||||
buf.end = 0;
|
||||
if (push(&buf, "foo")! != "foo") { fail(); };
|
||||
if (isroot(&buf)) { fail(); };
|
||||
|
||||
// "/foo" — absolute but not root.
|
||||
buf.buf[0] = SEP; buf.end = 1;
|
||||
if (push(&buf, "foo")! != "/foo") { fail(); };
|
||||
if (isroot(&buf)) { fail(); };
|
||||
};
|
||||
|
||||
// ---- string() ---------------------------------------------------------
|
||||
// ref/hare/path/buffer.ha:28-31. Empty buffer views as "."; a filled
|
||||
// buffer views the byte prefix.
|
||||
|
||||
@test fn string_cases() void = {
|
||||
let buf = buffer { ... };
|
||||
if (string(&buf) != ".") { fail(); };
|
||||
if (push(&buf, "foo")! != "foo") { fail(); };
|
||||
if (string(&buf) != "foo") { fail(); };
|
||||
};
|
||||
|
||||
export fn main() i32 = {
|
||||
signalled = 1; push_cases();
|
||||
signalled = 2; isroot_cases();
|
||||
signalled = 3; string_cases();
|
||||
return 0;
|
||||
};
|
||||
@@ -15,7 +15,6 @@ static const char *modules[] = {
|
||||
"lib/io/io.ww",
|
||||
"lib/strconv/strconv.ww",
|
||||
"lib/sort/sort.ww",
|
||||
"lib/path/path.ww",
|
||||
"lib/encoding/utf8/utf8.ww",
|
||||
"lib/encoding/base32/base32.ww",
|
||||
"lib/hash/fnv/fnv.ww",
|
||||
@@ -41,13 +40,17 @@ static const char *modules[] = {
|
||||
* lib/encoding/base64 graduated to Hare's io-streaming surface
|
||||
* (hex references io.handle / fmt.fprint / memio.dynamic / strconv /
|
||||
* errors.invalid; base64 references io.handle / memio.dynamic /
|
||||
* bytes.zero / strings.frombytes / errors.invalid). Coverage lives at
|
||||
* bytes.zero / strings.frombytes / errors.invalid); lib/path.path
|
||||
* is import-dependent post-c2-stack realignment ([MAX]u8 via
|
||||
* os.PATH_MAX def-dim + match over imported path error types).
|
||||
* Coverage lives at
|
||||
* lib/bufio/bufiotest.ww + lib/bytes/bytestest.ww +
|
||||
* lib/errors/errnotest.ww + lib/fmt/fmttest.ww + lib/os/stattest.ww
|
||||
* + lib/strings/stringstest.ww + lib/encoding/hex/hextest.ww +
|
||||
* lib/encoding/base64/base64_test.ww (wired at 998_bufio_run.c,
|
||||
* 967_bytes_run.c, 902_errno_run.c, 970_fmt_run.c, 976_stat_run.c,
|
||||
* 966_strings_run.c, 979_hex_run.c, 984_base64_run.c), plus the
|
||||
* 966_strings_run.c, 979_hex_run.c, 984_base64_run.c) + path at
|
||||
* lib/path/pathtest.ww (989_path_run.c), plus the
|
||||
* bufio.scanline / fmt.println e2e rows in test/wcc/700_e2e.c. */
|
||||
"lib/net/net.ww",
|
||||
NULL
|
||||
|
||||
@@ -107,8 +107,14 @@ static const struct ent ents[] = {
|
||||
/* fixtureless modules, import-probe shape */
|
||||
{ .probe = "package main;\nimport sort;\nfn main() i32 = { return 0; };\n",
|
||||
.mode = M_ID, .sentinel = "package sort;", .moddir = "lib/sort" },
|
||||
/* c2-stack path::buffer realignment: w6c compiles, w6c_ww rejects
|
||||
* path's module-global slice consts (const dot/dotdot: []u8) = the
|
||||
* #120/#29 acceptance divergence (module-level composite globals);
|
||||
* + the #148 twin #151 (global []u8 by-value arg). Both → #125
|
||||
* batch. cstage runtime is covered by 989_path_run. M_WWREJECT
|
||||
* self-graduates: flips RED to M_ID the day wwstage accepts this. */
|
||||
{ .probe = "package main;\nimport path;\nfn main() i32 = { return 0; };\n",
|
||||
.mode = M_ID, .sentinel = "package path;", .moddir = "lib/path" },
|
||||
.mode = M_WWREJECT, .cite = "#120/#29", .sentinel = "package path;", .moddir = "lib/path" },
|
||||
{ .probe = "package main;\nimport endian;\nfn main() i32 = { return 0; };\n",
|
||||
.mode = M_ID, .sentinel = "package endian;", .moddir = "lib/endian" },
|
||||
{ .probe = "package main;\nimport net;\nfn main() i32 = { return 0; };\n",
|
||||
|
||||
49
test/wcc/989_path_run.c
Normal file
49
test/wcc/989_path_run.c
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 989_path_run — execute the lib/path @test fixture under the
|
||||
* C-side `ww run` driver and assert exit 0.
|
||||
*
|
||||
* Same thin-wrapper shape as 979_hex_run / 967_bytes_run:
|
||||
* pathtest.ww carries its own `export fn main()` that drives the
|
||||
* @test fns and signals which case failed via the exit code.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[1024];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
|
||||
const char *src = "lib/path/pathtest.ww";
|
||||
char path[1024], cmd[2048];
|
||||
snprintf(path, sizeof path, "%s/%s", cwd, src);
|
||||
snprintf(cmd, sizeof cmd, "%s/ww run %s", bin, path);
|
||||
int rc = runwait(cmd);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "path_run FAIL: %s exited %d\n", src, rc);
|
||||
return 1;
|
||||
}
|
||||
printf("path_run: %s ok\n", src);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user