diff --git a/lib/ascii/asciitest.ww b/lib/ascii/asciitest.ww index e127257b..d4fcec2b 100644 --- a/lib/ascii/asciitest.ww +++ b/lib/ascii/asciitest.ww @@ -1,79 +1,73 @@ // asciitest — exercises lib/ascii case folding. Run with -// `ww run lib/ascii/asciitest.ww`. Same signalled-then-fail()-with-+10 -// pattern as bytestest: a non-zero exit pinpoints the failing scenario. +// `ww run lib/ascii/asciitest.ww`. A failing row aborts via the +// assert/abort builtin (task #5 @test conversion). // // Vectors mirror Hare's @test fn strcasecmp in ref/hare/ascii/string.ha. package ascii_test; import ascii; -import os; - -let signalled: i32 = 0; -fn fail() void = { os.exit(signalled + 10); }; // checkfold — one table row: strlower(in) == lo and strupper(in) == up. fn checkfold(in: str, lo: str, up: str) void = { match (ascii.strlower(in)) { - case let got: str => { if (got != lo) { fail(); }; }; - case nomem => { fail(); }; + case let got: str => { assert(!(got != lo)); }; + case nomem => { abort(); }; }; match (ascii.strupper(in)) { - case let got: str => { if (got != up) { fail(); }; }; - case nomem => { fail(); }; + case let got: str => { assert(!(got != up)); }; + case nomem => { abort(); }; }; }; // ref/hare/ascii/string.ha:70 case-fold vectors. The こ row pins that a // UTF-8 multibyte sequence (all bytes >=0x80) passes through unchanged. @test fn strfold_cases() void = { - signalled = 100; checkfold("ABC", "abc", "ABC"); - signalled = 101; checkfold("abc", "abc", "ABC"); - signalled = 102; checkfold("[[[", "[[[", "[[["); - signalled = 103; checkfold("こ", "こ", "こ"); - signalled = 104; checkfold("", "", ""); - signalled = 105; checkfold("aB1z", "ab1z", "AB1Z"); + checkfold("ABC", "abc", "ABC"); + checkfold("abc", "abc", "ABC"); + checkfold("[[[", "[[[", "[[["); + checkfold("こ", "こ", "こ"); + checkfold("", "", ""); + checkfold("aB1z", "ab1z", "AB1Z"); }; // checkbuf — strlower_buf/strupper_buf into an exactly-sized buf. fn checkbuf(in: str, lo: str, up: str) void = { let lstore: [16]u8; let lbuf: []u8 = lstore[0:16]; lbuf.len = 0; match (ascii.strlower_buf(in, lbuf)) { - case let got: str => { if (got != lo) { fail(); }; }; - case nomem => { fail(); }; + case let got: str => { assert(!(got != lo)); }; + case nomem => { abort(); }; }; let ustore: [16]u8; let ubuf: []u8 = ustore[0:16]; ubuf.len = 0; match (ascii.strupper_buf(in, ubuf)) { - case let got: str => { if (got != up) { fail(); }; }; - case nomem => { fail(); }; + case let got: str => { assert(!(got != up)); }; + case nomem => { abort(); }; }; }; @test fn strfold_buf_cases() void = { - signalled = 110; checkbuf("ABC", "abc", "ABC"); - signalled = 111; checkbuf("aB1z", "ab1z", "AB1Z"); - signalled = 112; checkbuf("", "", ""); + checkbuf("ABC", "abc", "ABC"); + checkbuf("aB1z", "ab1z", "AB1Z"); + checkbuf("", "", ""); // non-ASCII pins UTF-8 multibyte passthrough through the _buf path // directly (all bytes >=0x80, untouched by the byte-wise fold). - signalled = 113; checkbuf("こ", "こ", "こ"); + checkbuf("こ", "こ", "こ"); // cap exactly == s.len must succeed — pins the `<` boundary in the // buf.cap check (a `<=` off-by-one would wrongly return nomem here). - signalled = 114; let exact: [3]u8; let ebuf: []u8 = exact[0:3]; ebuf.len = 0; match (ascii.strlower_buf("ABC", ebuf)) { - case let got: str => { if (got != "abc") { fail(); }; }; - case nomem => { fail(); }; + case let got: str => { assert(!(got != "abc")); }; + case nomem => { abort(); }; }; - signalled = 115; let small: [2]u8; let sbuf: []u8 = small[0:2]; sbuf.len = 0; match (ascii.strlower_buf("ABC", sbuf)) { - case let got: str => { fail(); }; + case let got: str => { abort(); }; case nomem => void; }; }; export fn main() i32 = { - signalled = 1; strfold_cases(); - signalled = 2; strfold_buf_cases(); + strfold_cases(); + strfold_buf_cases(); return 0; }; diff --git a/lib/fnmatch/fnmatchtest.ww b/lib/fnmatch/fnmatchtest.ww index 4df0fa33..76961744 100644 --- a/lib/fnmatch/fnmatchtest.ww +++ b/lib/fnmatch/fnmatchtest.ww @@ -8,33 +8,19 @@ // byte-wise and those would match byte-identically but obscure the // ASCII semantics the test is asserting. // -// Failure path: each @test fn bumps `signalled` to its slot index, -// `check` does `exit(signalled + 10)` on miscompare so the harness -// reports `WEXITSTATUS = 11..N` pointing at the failing scenario. -// Same convention as lib/log/logtest. +// Failure path: a miscompare aborts via the assert/abort builtin +// (task #5 @test conversion). package fnmatch_test; import fnmatch; -// Direct rt_syscall binding rather than `use os;` — os exports -// read/write/close, which collide with io.read/write/close under the -// driver's flat-scope concat. Mirrors logtest / fmttest / bufiotest. -@symbol("rt_syscall") fn syscall1ww(num: i64, a: i64) i64; -fn doexit(code: i32) void = { - syscall1ww(60i64, code: i64); -}; - -let signalled: i32 = 0; - -fn fail() void = { doexit(signalled + 10); }; - // check — pin one row. `flags` is taken as i32 and cast to // fnmatch.flag so the test rows can read as integer bitmasks // without dragging enum literals into every line. fn check(pat: str, s: str, expected: bool, flags: i32) void = { let f: fnmatch.flag = flags as fnmatch.flag; - if (fnmatch.fnmatch(pat, s, f) != expected) { fail(); }; + assert(!(fnmatch.fnmatch(pat, s, f) != expected)); }; // ---- basic literal / wildcard cases --------------------------------- @@ -263,13 +249,13 @@ fn check(pat: str, s: str, expected: bool, flags: i32) void = { }; export fn main() i32 = { - signalled = 1; basic(); - signalled = 2; brackets(); - signalled = 3; ctype(); - signalled = 4; period(); - signalled = 5; noescape(); - signalled = 6; musl_basic(); - signalled = 7; pathname(); - signalled = 8; combined(); + basic(); + brackets(); + ctype(); + period(); + noescape(); + musl_basic(); + pathname(); + combined(); return 0; }; diff --git a/lib/shlex/shlextest.ww b/lib/shlex/shlextest.ww index 24adbe4e..e47d8a08 100644 --- a/lib/shlex/shlextest.ww +++ b/lib/shlex/shlextest.ww @@ -15,10 +15,8 @@ // shape against a memio.dynamic sink, mirroring Hare's escape.ha // testquote table. // -// Failure path: each @test fn bumps `signalled` to its slot index, -// the helpers do `exit(signalled + 10)` on miscompare so the harness -// reports `WEXITSTATUS = 11..N` pointing at the failing scenario. -// Same convention as fnmatchtest / logtest. +// Failure path: a miscompare aborts via the assert/abort builtin +// (task #5 @test conversion). package shlex_test; @@ -26,19 +24,6 @@ import shlex; import io; import memio; -// Direct rt_syscall binding rather than `use os;` — os exports -// read/write/close, which collide with io.read/write/close under the -// driver's flat-scope concat. Mirrors fnmatchtest / logtest / fmttest -// / bufiotest. -@symbol("rt_syscall") fn syscall1ww(num: i64, a: i64) i64; -fn doexit(code: i32) void = { - syscall1ww(60i64, code: i64); -}; - -let signalled: i32 = 0; - -fn fail() void = { doexit(signalled + 10); }; - fn streq(a: str, b: str) bool = { if (a.len != b.len) { return false; }; let i: i32 = 0; @@ -56,10 +41,10 @@ fn streq(a: str, b: str) bool = { fn check1(in: str, e0: str) void = { let r = shlex.split(in); match (r) { - case shlex.syntaxerr => { fail(); }; + case shlex.syntaxerr => { abort(); }; case let s: []str => { - if (s.len != 1) { fail(); }; - if (!streq(s[0], e0)) { fail(); }; + assert(!(s.len != 1)); + assert(!(!streq(s[0], e0))); }; }; }; @@ -67,11 +52,11 @@ fn check1(in: str, e0: str) void = { fn check2(in: str, e0: str, e1: str) void = { let r = shlex.split(in); match (r) { - case shlex.syntaxerr => { fail(); }; + case shlex.syntaxerr => { abort(); }; case let s: []str => { - if (s.len != 2) { fail(); }; - if (!streq(s[0], e0)) { fail(); }; - if (!streq(s[1], e1)) { fail(); }; + assert(!(s.len != 2)); + assert(!(!streq(s[0], e0))); + assert(!(!streq(s[1], e1))); }; }; }; @@ -79,12 +64,12 @@ fn check2(in: str, e0: str, e1: str) void = { fn check3(in: str, e0: str, e1: str, e2: str) void = { let r = shlex.split(in); match (r) { - case shlex.syntaxerr => { fail(); }; + case shlex.syntaxerr => { abort(); }; case let s: []str => { - if (s.len != 3) { fail(); }; - if (!streq(s[0], e0)) { fail(); }; - if (!streq(s[1], e1)) { fail(); }; - if (!streq(s[2], e2)) { fail(); }; + assert(!(s.len != 3)); + assert(!(!streq(s[0], e0))); + assert(!(!streq(s[1], e1))); + assert(!(!streq(s[2], e2))); }; }; }; @@ -93,16 +78,16 @@ fn checkerr(in: str) void = { let r = shlex.split(in); match (r) { case shlex.syntaxerr => {}; - case let s: []str => { fail(); }; + case let s: []str => { abort(); }; }; }; fn checkempty(in: str) void = { let r = shlex.split(in); match (r) { - case shlex.syntaxerr => { fail(); }; + case shlex.syntaxerr => { abort(); }; case let s: []str => { - if (s.len != 0) { fail(); }; + assert(!(s.len != 0)); }; }; }; @@ -115,12 +100,12 @@ fn checkquote(in: str, expected: str) void = { let n: size = 0; match (r) { case let v: size => { n = v; }; - case let _e: io.error => { fail(); }; + case let _e: io.error => { abort(); }; }; - if (n != expected.len: size) { fail(); }; + assert(!(n != expected.len: size)); let view: str = memio.string(&st); - if (!streq(view, expected)) { fail(); }; + assert(!(!streq(view, expected))); let _c: (void | io.error) = io.close(snk); }; @@ -182,7 +167,7 @@ fn checkquote(in: str, expected: str) void = { @test fn test_quotestr() void = { let r: str = shlex.quotestr("hello world"); - if (!streq(r, "'hello world'")) { fail(); }; + assert(!(!streq(r, "'hello world'"))); // leak r — short-lived test process, same precedent as fnmatchtest. }; @@ -191,13 +176,13 @@ fn checkquote(in: str, expected: str) void = { @test fn test_strerror() void = { let e: shlex.syntaxerr; let s: str = shlex.strerror(e); - if (!streq(s, "Invalid shell syntax")) { fail(); }; + assert(!(!streq(s, "Invalid shell syntax"))); }; export fn main() i32 = { - signalled = 1; test_split(); - signalled = 2; test_quote(); - signalled = 3; test_quotestr(); - signalled = 4; test_strerror(); + test_split(); + test_quote(); + test_quotestr(); + test_strerror(); return 0; }; diff --git a/lib/strings/stringstest.ww b/lib/strings/stringstest.ww index bb98bdb1..fbaa4077 100644 --- a/lib/strings/stringstest.ww +++ b/lib/strings/stringstest.ww @@ -1,7 +1,9 @@ // stringstest — exercises lib/strings. Run with // `out/bin/ww run lib/strings/stringstest.ww`. -// Same signalled-then-fail()-with-+10 shape as bytes / utf8 / hex / -// time tests: non-zero exit pinpoints the failing scenario. +// A failing row aborts via the assert/abort builtin (task #5 @test +// conversion). Per-row exit-code pinpoint is intentionally dropped: an +// abort reports the file, not the row (drew-t2-conversion-spec sec.5; +// Hare-equivalent, which reports file:line not loop index). // // Vectors mirror ref/hare/strings/{dup,concat,trim,contains,index, // suffix,compare}.ha where ww can express them. @@ -16,8 +18,6 @@ import strings; import encoding.utf8; import os; -let signalled: i32 = 0; -fn fail() void = { os.exit(signalled + 10); }; fn streq(a: str, b: str) bool = { if (a.len != b.len) { return false; }; @@ -34,44 +34,42 @@ fn streq(a: str, b: str) bool = { @test fn dup_cases() void = { let e: str = strings.dup(""); - if (!streq(e, "")) { fail(); }; - if (e.len != 0) { fail(); }; + assert(!(!streq(e, ""))); + assert(!(e.len != 0)); let h: str = strings.dup("hello"); - if (!streq(h, "hello")) { fail(); }; + assert(!(!streq(h, "hello"))); defer os.free(h.ptr: *void, h.len: u64); // multi-byte UTF-8: dup must copy raw bytes, not aliased view. let m: str = strings.dup("こんにちは"); - if (m.len != 15) { fail(); }; - if (!streq(m, "こんにちは")) { fail(); }; - if (m.ptr == "こんにちは".ptr) { fail(); }; // fresh alloc + assert(!(m.len != 15)); + assert(!(!streq(m, "こんにちは"))); + assert(!(m.ptr == "こんにちは".ptr)); // fresh alloc defer os.free(m.ptr: *void, m.len: u64); }; // ---- dupall ----------------------------------------------------------- -// ref/hare/strings/dup.ha:55 (#6). Per-row `signalled` bump narrows a -// failure exit code; element reads go through `&toks.ptr[i]: *str` +// ref/hare/strings/dup.ha:55 (#6). Element reads go through +// `&toks.ptr[i]: *str` // per the splitn cases (16B element copy gap, cgen.c:6515). @test fn dupall_cases() void = { // Empty input — empty result, mirrors Hare's `payload = []`. - signalled = 1800; let empty: []str; empty.ptr = nil: *str; empty.len = 0; empty.cap = 0; match (strings.dupall(empty)) { case let r: []str => { - if (r.len != 0) { fail(); }; + assert(!(r.len != 0)); strings.freeall(r); }; - case nomem => { fail(); }; + case nomem => { abort(); }; }; // Two-element ASCII — each output element is a fresh allocation // independent of the input (ptr differs from the borrowed source). - signalled = 1801; let in2: [2]str; in2[0] = "hello"; in2[1] = "world"; @@ -81,20 +79,19 @@ fn streq(a: str, b: str) bool = { src2.cap = 2; match (strings.dupall(src2)) { case let r: []str => { - if (r.len != 2) { fail(); }; + assert(!(r.len != 2)); expect_str(r, 0, "hello"); expect_str(r, 1, "world"); let p0: *str = &r.ptr[0]; - if (p0.ptr == in2[0].ptr) { fail(); }; + assert(!(p0.ptr == in2[0].ptr)); let p1: *str = &r.ptr[1]; - if (p1.ptr == in2[1].ptr) { fail(); }; + assert(!(p1.ptr == in2[1].ptr)); strings.freeall(r); }; - case nomem => { fail(); }; + case nomem => { abort(); }; }; // Singleton — `only` rune-equivalent of Hare's `["only"]`. - signalled = 1802; let in1: [1]str; in1[0] = "only"; let src1: []str; @@ -103,15 +100,14 @@ fn streq(a: str, b: str) bool = { src1.cap = 1; match (strings.dupall(src1)) { case let r: []str => { - if (r.len != 1) { fail(); }; + assert(!(r.len != 1)); expect_str(r, 0, "only"); strings.freeall(r); }; - case nomem => { fail(); }; + case nomem => { abort(); }; }; // Multibyte — UTF-8 bytes (5-byte and 15-byte) round-trip. - signalled = 1803; let inm: [2]str; inm[0] = "héllo"; inm[1] = "こんにちは"; @@ -121,26 +117,24 @@ fn streq(a: str, b: str) bool = { srcm.cap = 2; match (strings.dupall(srcm)) { case let r: []str => { - if (r.len != 2) { fail(); }; + assert(!(r.len != 2)); expect_str(r, 0, "héllo"); expect_str(r, 1, "こんにちは"); let p0: *str = &r.ptr[0]; - if (p0.len != 6) { fail(); }; // é is 2 bytes - if (p0.ptr == inm[0].ptr) { fail(); }; + assert(!(p0.len != 6)); // é is 2 bytes + assert(!(p0.ptr == inm[0].ptr)); let p1: *str = &r.ptr[1]; - if (p1.len != 15) { fail(); }; // each kana is 3 bytes - if (p1.ptr == inm[1].ptr) { fail(); }; + assert(!(p1.len != 15)); // each kana is 3 bytes + assert(!(p1.ptr == inm[1].ptr)); strings.freeall(r); }; - case nomem => { fail(); }; + case nomem => { abort(); }; }; }; // ---- concat ----------------------------------------------------------- // ref/hare/strings/concat.ha:18. Rows mirror Hare's vectors (0/1/2/3-arg, -// empty-mid, 2-empty) plus empty-first / empty-last / multibyte. The -// per-row `signalled` bump narrows a failure exit code to the offending -// row. +// empty-mid, 2-empty) plus empty-first / empty-last / multibyte. @test fn concat_cases() void = { let pool: [17]str; @@ -178,13 +172,12 @@ fn streq(a: str, b: str) bool = { let i: i32 = 0; for (i < 9) { - signalled = 200 + i; let argv: []str; argv.ptr = &pool[argo[i]]; argv.len = argn[i]; argv.cap = argn[i]; let got: str = strings.concat(argv...); - if (!streq(got, want[i])) { fail(); }; + assert(!(!streq(got, want[i]))); if (got.len > 0) { os.free(got.ptr: *void, got.len: u64); }; i += 1; }; @@ -194,7 +187,6 @@ fn streq(a: str, b: str) bool = { // ref/hare/strings/concat.ha:64. Rows mirror Hare's @test fn join // (0-arg, 1-arg, empty-sep, 3-arg.) plus 2-arg, all-empties, long sep, // multibyte, empty-mid (delim still inserted around the empty slot). -// Per-row `signalled` 1620+i narrows the failing row. @test fn join_cases() void = { let pool: [19]str; @@ -234,13 +226,12 @@ fn streq(a: str, b: str) bool = { let i: i32 = 0; for (i < 9) { - signalled = 1620 + i; let argv: []str; argv.ptr = &pool[argo[i]]; argv.len = argn[i]; argv.cap = argn[i]; let got: str = strings.join(seps[i], argv...); - if (!streq(got, want[i])) { fail(); }; + assert(!(!streq(got, want[i]))); if (got.len > 0) { os.free(got.ptr: *void, got.len: u64); }; i += 1; }; @@ -250,56 +241,50 @@ fn streq(a: str, b: str) bool = { // ref/hare/strings/suffix.ha:18. @test fn hasprefix_cases() void = { - if (!strings.hasprefix("hello world", "hello")) { fail(); }; - if (!strings.hasprefix("hello world", 'h')) { fail(); }; - if ( strings.hasprefix("hello world", "world")) { fail(); }; - if ( strings.hasprefix("hello world", 'q')) { fail(); }; - if (!strings.hasprefix("hello", "hello")) { fail(); }; // equal-len - if (!strings.hasprefix("anything", "")) { fail(); }; // empty prefix - if ( strings.hasprefix("", "x")) { fail(); }; + assert(!(!strings.hasprefix("hello world", "hello"))); + assert(!(!strings.hasprefix("hello world", 'h'))); + assert(!( strings.hasprefix("hello world", "world"))); + assert(!( strings.hasprefix("hello world", 'q'))); + assert(!(!strings.hasprefix("hello", "hello"))); // equal-len + assert(!(!strings.hasprefix("anything", ""))); // empty prefix + assert(!( strings.hasprefix("", "x"))); // multibyte rune prefix — '\'é\'' literal blocked by single-byte // lexrune (lib/ww/lex/lex.ww:659); pass codepoint directly. - if (!strings.hasprefix("éclat", 0xE9u32: rune)) { fail(); }; - if (!strings.hasprefix("🦀rust", 0x1F980u32: rune)) { fail(); }; + assert(!(!strings.hasprefix("éclat", 0xE9u32: rune))); + assert(!(!strings.hasprefix("🦀rust", 0x1F980u32: rune))); }; // ---- hassuffix -------------------------------------------------------- // ref/hare/strings/suffix.ha:36. @test fn hassuffix_cases() void = { - if (!strings.hassuffix("hello world", "world")) { fail(); }; - if (!strings.hassuffix("hello world", 'd')) { fail(); }; - if ( strings.hassuffix("hello world", "hello")) { fail(); }; - if ( strings.hassuffix("hello world", 'h')) { fail(); }; - if (!strings.hassuffix("café", 0xE9u32: rune)) { fail(); }; // multibyte + assert(!(!strings.hassuffix("hello world", "world"))); + assert(!(!strings.hassuffix("hello world", 'd'))); + assert(!( strings.hassuffix("hello world", "hello"))); + assert(!( strings.hassuffix("hello world", 'h'))); + assert(!(!strings.hassuffix("café", 0xE9u32: rune))); // multibyte }; // ---- contains --------------------------------------------------------- // ref/hare/strings/contains.ha:27. @test fn contains_cases() void = { - if (!strings.contains("hello world", "hello")) { fail(); }; - if (!strings.contains("hello world", 'h')) { fail(); }; - if ( strings.contains("hello world", 'x')) { fail(); }; - if (!strings.contains("hello world", "world")) { fail(); }; - if (!strings.contains("hello world", "")) { fail(); }; // empty hits at 0 - if ( strings.contains("hello world", "foobar")) { fail(); }; - if (!strings.contains("こんにちは", 0x306Bu32: rune)) { fail(); }; // 'に' - if (!strings.contains("こんにちは", "ちは")) { fail(); }; + assert(!(!strings.contains("hello world", "hello"))); + assert(!(!strings.contains("hello world", 'h'))); + assert(!( strings.contains("hello world", 'x'))); + assert(!(!strings.contains("hello world", "world"))); + assert(!(!strings.contains("hello world", ""))); // empty hits at 0 + assert(!( strings.contains("hello world", "foobar"))); + assert(!(!strings.contains("こんにちは", 0x306Bu32: rune))); // 'に' + assert(!(!strings.contains("こんにちは", "ちは"))); // Variadic rows. ref/hare/strings/contains.ha:27. - signalled = 1600; - if ( strings.contains("hello")) { fail(); }; - signalled = 1601; - if (!strings.contains("hello world", "world")) { fail(); }; - signalled = 1602; - if (!strings.contains("hello", 'l')) { fail(); }; - signalled = 1603; - if (!strings.contains("hello world", "foo", "world", 'x')) { fail(); }; - signalled = 1604; - if ( strings.contains("hello", "foo", 'z', "bar")) { fail(); }; - signalled = 1605; - if (!strings.contains("héllo", "x", 0xE9u32: rune)) { fail(); }; + assert(!( strings.contains("hello"))); + assert(!(!strings.contains("hello world", "world"))); + assert(!(!strings.contains("hello", 'l'))); + assert(!(!strings.contains("hello world", "foo", "world", 'x'))); + assert(!( strings.contains("hello", "foo", 'z', "bar"))); + assert(!(!strings.contains("héllo", "x", 0xE9u32: rune))); }; // ---- byteindex -------------------------------------------------------- @@ -307,62 +292,62 @@ fn streq(a: str, b: str) bool = { @test fn byteindex_str_cases() void = { match (strings.byteindex("hello", "hello")) { - case let i: i32 => { if (i != 0) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 0)); }; + case void => { abort(); }; }; match (strings.byteindex("hello world!", "world")) { - case let i: i32 => { if (i != 6) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 6)); }; + case void => { abort(); }; }; match (strings.byteindex("hello world!", "orld!")) { - case let i: i32 => { if (i != 7) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 7)); }; + case void => { abort(); }; }; match (strings.byteindex("hello world!", "word")) { - case let i: i32 => { fail(); }; + case let i: i32 => { abort(); }; case void => void; }; // empty needle hits at 0 (ref/hare/bytes/index.ha:63). match (strings.byteindex("hello", "")) { - case let i: i32 => { if (i != 0) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 0)); }; + case void => { abort(); }; }; // empty haystack, non-empty needle — absent. match (strings.byteindex("", "x")) { - case let i: i32 => { fail(); }; + case let i: i32 => { abort(); }; case void => void; }; // multibyte substring in multibyte haystack. match (strings.byteindex("こんにちは", "ちは")) { - case let i: i32 => { if (i != 9) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 9)); }; + case void => { abort(); }; }; }; @test fn byteindex_rune_cases() void = { // ASCII rune (1-byte encoding). match (strings.byteindex("hello world", 'w')) { - case let i: i32 => { if (i != 6) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 6)); }; + case void => { abort(); }; }; // 2-byte rune U+00E9 'é' inside "café". match (strings.byteindex("café", 0xE9u32: rune)) { - case let i: i32 => { if (i != 3) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 3)); }; + case void => { abort(); }; }; // 3-byte rune U+3061 'ち' inside "こんにちは". match (strings.byteindex("こんにちは", 0x3061u32: rune)) { - case let i: i32 => { if (i != 9) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 9)); }; + case void => { abort(); }; }; // 4-byte rune U+1F980 '🦀' inside "ab🦀cd". match (strings.byteindex("ab🦀cd", 0x1F980u32: rune)) { - case let i: i32 => { if (i != 2) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 2)); }; + case void => { abort(); }; }; // absent. match (strings.byteindex("こんにちは", 'q')) { - case let i: i32 => { fail(); }; + case let i: i32 => { abort(); }; case void => void; }; }; @@ -372,21 +357,21 @@ fn streq(a: str, b: str) bool = { @test fn rbyteindex_cases() void = { // Two 'た' in "またあったね" — ref/hare/strings/index.ha:160-161. match (strings.byteindex("またあったね", "た")) { - case let i: i32 => { if (i != 3) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 3)); }; + case void => { abort(); }; }; match (strings.rbyteindex("またあったね", "た")) { - case let i: i32 => { if (i != 12) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 12)); }; + case void => { abort(); }; }; // Rune arm, multi-byte 'に' U+306B. match (strings.rbyteindex("こんにちは", 0x306Bu32: rune)) { - case let i: i32 => { if (i != 6) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 6)); }; + case void => { abort(); }; }; // Absent. match (strings.rbyteindex("abc", 'z')) { - case let i: i32 => { fail(); }; + case let i: i32 => { abort(); }; case void => void; }; }; @@ -397,69 +382,58 @@ fn streq(a: str, b: str) bool = { @test fn index_cases() void = { // str-arm: ASCII haystack/needle, mid-string match. - signalled = 1400; match (strings.index("hello", "ll")) { - case let i: i32 => { if (i != 2) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 2)); }; + case void => { abort(); }; }; // str-arm: absent needle. - signalled = 1401; match (strings.index("hello", "world")) { - case let i: i32 => { fail(); }; + case let i: i32 => { abort(); }; case void => void; }; // Hare vectors at ref/hare/strings/index.ha:113-119. - signalled = 1402; match (strings.index("hello world!", "hello")) { - case let i: i32 => { if (i != 0) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 0)); }; + case void => { abort(); }; }; - signalled = 1403; match (strings.index("hello world!", "world")) { - case let i: i32 => { if (i != 6) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 6)); }; + case void => { abort(); }; }; - signalled = 1404; match (strings.index("hello world!", "orld!")) { - case let i: i32 => { if (i != 7) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 7)); }; + case void => { abort(); }; }; // Multibyte haystack + str needle: "ちは" at rune index 3 // in "こんにちは" (byteindex returns 9, rune-index is 3). - signalled = 1405; match (strings.index("こんにちは", "ちは")) { - case let i: i32 => { if (i != 3) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 3)); }; + case void => { abort(); }; }; // rune-arm. - signalled = 1406; match (strings.index("hello", 'l')) { - case let i: i32 => { if (i != 2) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 2)); }; + case void => { abort(); }; }; - signalled = 1407; match (strings.index("hello world", 'w')) { - case let i: i32 => { if (i != 6) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 6)); }; + case void => { abort(); }; }; // Multibyte rune: 'é' U+00E9 at rune 1 in "héllo" — pins // rune-index vs byte-index (byteindex returns 1; rune-index is // 1 also — but the str-arm's 1405 row covers the distinction). - signalled = 1408; match (strings.index("héllo", 0xE9u32: rune)) { - case let i: i32 => { if (i != 1) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 1)); }; + case void => { abort(); }; }; // Multibyte rune in multibyte haystack: 'ち' U+3061 at rune 3 // in "こんにちは" (byteindex returns 9, rune-index is 3). - signalled = 1409; match (strings.index("こんにちは", 0x3061u32: rune)) { - case let i: i32 => { if (i != 3) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 3)); }; + case void => { abort(); }; }; - signalled = 1410; match (strings.index("こんにちは", 'q')) { - case let i: i32 => { fail(); }; + case let i: i32 => { abort(); }; case void => void; }; // str-arm: rune-index ≠ byte-index again, mid-string match. @@ -467,50 +441,43 @@ fn streq(a: str, b: str) bool = { // (each kana is 3 bytes; ref/hare/strings/index.ha:60). Pins // the dual-iterator walk against the discarded byteindex-and-walk // shape (#10). - signalled = 1411; match (strings.index("またあったね", "あった")) { - case let i: i32 => { if (i != 2) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 2)); }; + case void => { abort(); }; }; // str-arm: tail-anchored multibyte needle. "は" is at rune 4 // (byte 12) in "こんにちは". - signalled = 1412; match (strings.index("こんにちは", "は")) { - case let i: i32 => { if (i != 4) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 4)); }; + case void => { abort(); }; }; // Empty needle hits at rune 0 — Hare's `index_string` falls into // the `needle_rune is done` branch on the very first inner step // (ref/hare/strings/index.ha:70). - signalled = 1413; match (strings.index("hello", "")) { - case let i: i32 => { if (i != 0) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 0)); }; + case void => { abort(); }; }; - signalled = 1414; match (strings.index("", "")) { - case let i: i32 => { if (i != 0) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 0)); }; + case void => { abort(); }; }; // Empty haystack, non-empty needle — absent. - signalled = 1415; match (strings.index("", "x")) { - case let i: i32 => { fail(); }; + case let i: i32 => { abort(); }; case void => void; }; // Multibyte haystack, multibyte absent needle — exercises the // inner-loop mismatch-break across runes (#10, Hare row // ref/hare/strings/index.ha:119). - signalled = 1416; match (strings.index("こんにちは", "きょうは")) { - case let i: i32 => { fail(); }; + case let i: i32 => { abort(); }; case void => void; }; // Self-match: haystack == needle, Hare row index.ha:113. - signalled = 1417; match (strings.index("hello", "hello")) { - case let i: i32 => { if (i != 0) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 0)); }; + case void => { abort(); }; }; }; @@ -519,49 +486,41 @@ fn streq(a: str, b: str) bool = { @test fn rindex_cases() void = { // str-arm. - signalled = 1500; match (strings.rindex("hello", "lo")) { - case let i: i32 => { if (i != 3) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 3)); }; + case void => { abort(); }; }; // Hare vector at ref/hare/strings/index.ha:122. - signalled = 1501; match (strings.rindex("hello world!", "o")) { - case let i: i32 => { if (i != 7) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 7)); }; + case void => { abort(); }; }; - signalled = 1502; match (strings.rindex("hello", "world")) { - case let i: i32 => { fail(); }; + case let i: i32 => { abort(); }; case void => void; }; // Multibyte: last "た" in "またあったね" — rbyteindex returns // 12, rune-index is 4 (ま=0 た=1 あ=2 っ=3 た=4 ね=5). - signalled = 1503; match (strings.rindex("またあったね", "た")) { - case let i: i32 => { if (i != 4) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 4)); }; + case void => { abort(); }; }; // rune-arm. - signalled = 1504; match (strings.rindex("hello", 'l')) { - case let i: i32 => { if (i != 3) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 3)); }; + case void => { abort(); }; }; - signalled = 1505; match (strings.rindex("aaaaa", 'a')) { - case let i: i32 => { if (i != 4) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 4)); }; + case void => { abort(); }; }; // Multibyte rune: 'に' U+306B at rune 2 in "こんにちは". - signalled = 1506; match (strings.rindex("こんにちは", 0x306Bu32: rune)) { - case let i: i32 => { if (i != 2) { fail(); }; }; - case void => { fail(); }; + case let i: i32 => { assert(!(i != 2)); }; + case void => { abort(); }; }; - signalled = 1507; match (strings.rindex("hello", 'z')) { - case let i: i32 => { fail(); }; + case let i: i32 => { abort(); }; case void => void; }; }; @@ -570,20 +529,20 @@ fn streq(a: str, b: str) bool = { // ref/hare/strings/trim.ha:99-107. @test fn trimprefix_cases() void = { - if (!streq(strings.trimprefix("", ""), "")) { fail(); }; - if (!streq(strings.trimprefix("", "blablabla"), "")) { fail(); }; - if (!streq(strings.trimprefix("hello, world", "hello"), ", world")) { fail(); }; - if (!streq(strings.trimprefix("blablabla", "bla"), "blabla")) { fail(); }; + assert(!(!streq(strings.trimprefix("", ""), ""))); + assert(!(!streq(strings.trimprefix("", "blablabla"), ""))); + assert(!(!streq(strings.trimprefix("hello, world", "hello"), ", world"))); + assert(!(!streq(strings.trimprefix("blablabla", "bla"), "blabla"))); // equal-length match strips to empty. - if (!streq(strings.trimprefix("hello", "hello"), "")) { fail(); }; + assert(!(!streq(strings.trimprefix("hello", "hello"), ""))); }; @test fn trimsuffix_cases() void = { - if (!streq(strings.trimsuffix("", ""), "")) { fail(); }; - if (!streq(strings.trimsuffix("", "blablabla"), "")) { fail(); }; - if (!streq(strings.trimsuffix("hello, world", "world"), "hello, ")) { fail(); }; - if (!streq(strings.trimsuffix("blablabla", "bla"), "blabla")) { fail(); }; - if (!streq(strings.trimsuffix("hello", "hello"), "")) { fail(); }; + assert(!(!streq(strings.trimsuffix("", ""), ""))); + assert(!(!streq(strings.trimsuffix("", "blablabla"), ""))); + assert(!(!streq(strings.trimsuffix("hello, world", "world"), "hello, "))); + assert(!(!streq(strings.trimsuffix("blablabla", "bla"), "blabla"))); + assert(!(!streq(strings.trimsuffix("hello", "hello"), ""))); }; // ---- ltrim / rtrim / trim --------------------------------------------- @@ -622,13 +581,12 @@ fn streq(a: str, b: str) bool = { let i: i32 = 0; for (i < 11) { - signalled = 1100 + i; let argv: []rune; argv.ptr = &runes[argo[i]]; argv.len = argn[i]; argv.cap = argn[i]; let got: str = strings.ltrim(inputs[i], argv...); - if (!streq(got, want[i])) { fail(); }; + assert(!(!streq(got, want[i]))); i += 1; }; }; @@ -678,13 +636,12 @@ fn streq(a: str, b: str) bool = { let i: i32 = 0; for (i < 11) { - signalled = 1200 + i; let argv: []rune; argv.ptr = &runes[argo[i]]; argv.len = argn[i]; argv.cap = argn[i]; let got: str = strings.rtrim(inputs[i], argv...); - if (!streq(got, want[i])) { fail(); }; + assert(!(!streq(got, want[i]))); i += 1; }; }; @@ -720,13 +677,12 @@ fn streq(a: str, b: str) bool = { let i: i32 = 0; for (i < 10) { - signalled = 1300 + i; let argv: []rune; argv.ptr = &runes[argo[i]]; argv.len = argn[i]; argv.cap = argn[i]; let got: str = strings.trim(inputs[i], argv...); - if (!streq(got, want[i])) { fail(); }; + assert(!(!streq(got, want[i]))); i += 1; }; }; @@ -735,12 +691,12 @@ fn streq(a: str, b: str) bool = { // ref/hare/strings/compare.ha:16. @test fn compare_cases() void = { - if (strings.compare("ABC", "ABC") != 0) { fail(); }; - if (strings.compare("ABC", "AB") <= 0) { fail(); }; - if (strings.compare("AB", "ABC") >= 0) { fail(); }; - if (strings.compare("BCD", "ABC") <= 0) { fail(); }; - if (strings.compare("ABC", "abc") >= 0) { fail(); }; - if (strings.compare("ABC", "こんにちは") >= 0) { fail(); }; + assert(!(strings.compare("ABC", "ABC") != 0)); + assert(!(strings.compare("ABC", "AB") <= 0)); + assert(!(strings.compare("AB", "ABC") >= 0)); + assert(!(strings.compare("BCD", "ABC") <= 0)); + assert(!(strings.compare("ABC", "abc") >= 0)); + assert(!(strings.compare("ABC", "こんにちは") >= 0)); }; // ---- sub / bytesub ---------------------------------------------------- @@ -749,28 +705,20 @@ fn streq(a: str, b: str) bool = { // syntax (filed as #37). bytesub now validates rune boundaries (#7). @test fn sub_cases() void = { - signalled = 1790; - if (!streq(strings.sub("a string", 0, 8), "a string")) { fail(); }; - signalled = 1791; - if (!streq(strings.sub("a string", 0, 1), "a")) { fail(); }; - signalled = 1792; - if (!streq(strings.sub("a string", 0, 3), "a s")) { fail(); }; - signalled = 1793; - if (!streq(strings.sub("a string", 2, 8), "string")) { fail(); }; + assert(!(!streq(strings.sub("a string", 0, 8), "a string"))); + assert(!(!streq(strings.sub("a string", 0, 1), "a"))); + assert(!(!streq(strings.sub("a string", 0, 3), "a s"))); + assert(!(!streq(strings.sub("a string", 2, 8), "string"))); // start == end yields an empty borrowed view. - signalled = 1794; - if (!streq(strings.sub("a string", 4, 4), "")) { fail(); }; - if (strings.sub("a string", 4, 4).len != 0) { fail(); }; + assert(!(!streq(strings.sub("a string", 4, 4), ""))); + assert(!(strings.sub("a string", 4, 4).len != 0)); // Hare vector — rune indices 1..3 over "こんにちは" select bytes // 3..9 ("んに"), not bytes 1..3. - signalled = 1795; - if (!streq(strings.sub("こんにちは", 1, 3), "んに")) { fail(); }; + assert(!(!streq(strings.sub("こんにちは", 1, 3), "んに"))); // 2-byte rune at rune index 1 in "héllo" — byte offsets 1..3. - signalled = 1796; - if (!streq(strings.sub("héllo", 1, 2), "é")) { fail(); }; + assert(!(!streq(strings.sub("héllo", 1, 2), "é"))); // start == 0, end == rune-len of full string. - signalled = 1797; - if (!streq(strings.sub("héllo", 0, 5), "héllo")) { fail(); }; + assert(!(!streq(strings.sub("héllo", 0, 5), "héllo"))); }; // Match-shape mirrors Hare's `bytesub(...)!` at ref/hare/strings/sub.ha: @@ -778,74 +726,63 @@ fn streq(a: str, b: str) bool = { // `(str | utf8.invalid)` by value) because the union-by-value path // crashes — same lift-on-pass-by-value family as #48. @test fn bytesub_cases() void = { - signalled = 1800; match (strings.bytesub("a string", 0, 8)) { - case let s: str => { if (!streq(s, "a string")) { fail(); }; }; - case let e: utf8.invalid => { fail(); }; + case let s: str => { assert(!(!streq(s, "a string"))); }; + case let e: utf8.invalid => { abort(); }; }; - signalled = 1801; match (strings.bytesub("a string", 0, 1)) { - case let s: str => { if (!streq(s, "a")) { fail(); }; }; - case let e: utf8.invalid => { fail(); }; + case let s: str => { assert(!(!streq(s, "a"))); }; + case let e: utf8.invalid => { abort(); }; }; - signalled = 1802; match (strings.bytesub("a string", 0, 3)) { - case let s: str => { if (!streq(s, "a s")) { fail(); }; }; - case let e: utf8.invalid => { fail(); }; + case let s: str => { assert(!(!streq(s, "a s"))); }; + case let e: utf8.invalid => { abort(); }; }; - signalled = 1803; match (strings.bytesub("a string", 2, 8)) { - case let s: str => { if (!streq(s, "string")) { fail(); }; }; - case let e: utf8.invalid => { fail(); }; + case let s: str => { assert(!(!streq(s, "string"))); }; + case let e: utf8.invalid => { abort(); }; }; - signalled = 1804; match (strings.bytesub("a string", 4, 4)) { - case let s: str => { if (!streq(s, "")) { fail(); }; }; - case let e: utf8.invalid => { fail(); }; + case let s: str => { assert(!(!streq(s, ""))); }; + case let e: utf8.invalid => { abort(); }; }; // Hare vector — byte indices 3..9 over "こんにちは" select "んに". - signalled = 1805; match (strings.bytesub("こんにちは", 3, 9)) { - case let s: str => { if (!streq(s, "んに")) { fail(); }; }; - case let e: utf8.invalid => { fail(); }; + case let s: str => { assert(!(!streq(s, "んに"))); }; + case let e: utf8.invalid => { abort(); }; }; // Rune/byte axis disagree on identical args (#3): sub(s,0,3) walks 3 // runes and yields 9 bytes; bytesub(s,0,3) yields the first 3 bytes // — one 3-byte codepoint. - signalled = 1806; - if (!streq(strings.sub("こんにちは", 0, 3), "こんに")) { fail(); }; + assert(!(!streq(strings.sub("こんにちは", 0, 3), "こんに"))); match (strings.bytesub("こんにちは", 0, 3)) { - case let s: str => { if (!streq(s, "こ")) { fail(); }; }; - case let e: utf8.invalid => { fail(); }; + case let s: str => { assert(!(!streq(s, "こ"))); }; + case let e: utf8.invalid => { abort(); }; }; // Borrowed view: ptr aliases input. - signalled = 1807; let s: str = "hello"; match (strings.bytesub(s, 1, 4)) { case let r: str => { - if (r.ptr != s.ptr + 1u64) { fail(); }; - if (r.len != 3) { fail(); }; + assert(!(r.ptr != s.ptr + 1u64)); + assert(!(r.len != 3)); }; - case let e: utf8.invalid => { fail(); }; + case let e: utf8.invalid => { abort(); }; }; // Hare's invalid row (ref/hare/strings/sub.ha:87) — start lands on // a continuation byte (2nd byte of "こ"), bytesub must reject (#7). - signalled = 1808; match (strings.bytesub("こんにちは", 1, 3)) { - case let r: str => { fail(); }; + case let r: str => { abort(); }; case let e: utf8.invalid => void; }; // Symmetric: end lands on a continuation byte (2nd byte of "ん"). - signalled = 1809; match (strings.bytesub("こんにちは", 0, 4)) { - case let r: str => { fail(); }; + case let r: str => { abort(); }; case let e: utf8.invalid => void; }; // end == s.len bypasses the continuation check (s[end] is OOB). - signalled = 1810; match (strings.bytesub("こんにちは", 0, 15)) { - case let s: str => { if (!streq(s, "こんにちは")) { fail(); }; }; - case let e: utf8.invalid => { fail(); }; + case let s: str => { assert(!(!streq(s, "こんにちは"))); }; + case let e: utf8.invalid => { abort(); }; }; }; @@ -856,11 +793,11 @@ fn streq(a: str, b: str) bool = { @test fn utf8_roundtrip_cases() void = { let s: str = "hello"; let b: []u8 = strings.toutf8(s); - if (b.len != 5) { fail(); }; - if (b[0] != 104u8) { fail(); }; // 'h' + assert(!(b.len != 5)); + assert(!(b[0] != 104u8)); // 'h' let r: str = strings.frombytes(b); - if (!streq(r, "hello")) { fail(); }; - if (r.ptr != s.ptr) { fail(); }; // borrowed, not copied + assert(!(!streq(r, "hello"))); + assert(!(r.ptr != s.ptr)); // borrowed, not copied }; // ---- iter / next ------------------------------------------------------ @@ -872,12 +809,12 @@ fn streq(a: str, b: str) bool = { @test fn iter_empty_cases() void = { let it: strings.iterator = strings.iter(""); match (strings.next(&it)) { - case let r: rune => { fail(); }; + case let r: rune => { abort(); }; case utf8.done => void; }; // Repeated next after done stays done. match (strings.next(&it)) { - case let r: rune => { fail(); }; + case let r: rune => { abort(); }; case utf8.done => void; }; }; @@ -885,19 +822,19 @@ fn streq(a: str, b: str) bool = { @test fn iter_ascii_cases() void = { let it: strings.iterator = strings.iter("hi!"); match (strings.next(&it)) { - case let r: rune => { if (r != 'h') { fail(); }; }; - case utf8.done => { fail(); }; + case let r: rune => { assert(!(r != 'h')); }; + case utf8.done => { abort(); }; }; match (strings.next(&it)) { - case let r: rune => { if (r != 'i') { fail(); }; }; - case utf8.done => { fail(); }; + case let r: rune => { assert(!(r != 'i')); }; + case utf8.done => { abort(); }; }; match (strings.next(&it)) { - case let r: rune => { if (r != '!') { fail(); }; }; - case utf8.done => { fail(); }; + case let r: rune => { assert(!(r != '!')); }; + case utf8.done => { abort(); }; }; match (strings.next(&it)) { - case let r: rune => { fail(); }; + case let r: rune => { abort(); }; case utf8.done => void; }; }; @@ -910,13 +847,13 @@ fn streq(a: str, b: str) bool = { expect[3] = 0xE9u32: rune; // 'é' U+00E9 for (i < 4) { match (strings.next(&it)) { - case let r: rune => { if (r != expect[i]) { fail(); }; }; - case utf8.done => { fail(); }; + case let r: rune => { assert(!(r != expect[i])); }; + case utf8.done => { abort(); }; }; i += 1; }; match (strings.next(&it)) { - case let r: rune => { fail(); }; + case let r: rune => { abort(); }; case utf8.done => void; }; }; @@ -932,13 +869,13 @@ fn streq(a: str, b: str) bool = { expect[4] = 0x306Fu32: rune; // 'は' for (i < 5) { match (strings.next(&it)) { - case let r: rune => { if (r != expect[i]) { fail(); }; }; - case utf8.done => { fail(); }; + case let r: rune => { assert(!(r != expect[i])); }; + case utf8.done => { abort(); }; }; i += 1; }; match (strings.next(&it)) { - case let r: rune => { fail(); }; + case let r: rune => { abort(); }; case utf8.done => void; }; }; @@ -951,13 +888,13 @@ fn streq(a: str, b: str) bool = { expect[1] = 'r'; expect[2] = 'u'; expect[3] = 's'; expect[4] = 't'; for (i < 5) { match (strings.next(&it)) { - case let r: rune => { if (r != expect[i]) { fail(); }; }; - case utf8.done => { fail(); }; + case let r: rune => { assert(!(r != expect[i])); }; + case utf8.done => { abort(); }; }; i += 1; }; match (strings.next(&it)) { - case let r: rune => { fail(); }; + case let r: rune => { abort(); }; case utf8.done => void; }; }; @@ -976,13 +913,13 @@ fn streq(a: str, b: str) bool = { expect[11] = 0x1F30Du32: rune; // '🌍' for (i < 12) { match (strings.next(&it)) { - case let r: rune => { if (r != expect[i]) { fail(); }; }; - case utf8.done => { fail(); }; + case let r: rune => { assert(!(r != expect[i])); }; + case utf8.done => { abort(); }; }; i += 1; }; match (strings.next(&it)) { - case let r: rune => { fail(); }; + case let r: rune => { abort(); }; case utf8.done => void; }; }; @@ -996,23 +933,23 @@ fn streq(a: str, b: str) bool = { let it: strings.iterator = strings.iter("hi"); match (strings.prev(&it)) { case utf8.done => void; - case let r: rune => { fail(); }; + case let r: rune => { abort(); }; }; }; @test fn iter_prev_ascii_cases() void = { let it: strings.iterator = strings.iter("abc"); match (strings.next(&it)) { - case let r: rune => { if (r != 'a') { fail(); }; }; - case utf8.done => { fail(); }; + case let r: rune => { assert(!(r != 'a')); }; + case utf8.done => { abort(); }; }; match (strings.prev(&it)) { - case let r: rune => { if (r != 'a') { fail(); }; }; - case utf8.done => { fail(); }; + case let r: rune => { assert(!(r != 'a')); }; + case utf8.done => { abort(); }; }; match (strings.prev(&it)) { case utf8.done => void; - case let r: rune => { fail(); }; + case let r: rune => { abort(); }; }; }; @@ -1022,7 +959,7 @@ fn streq(a: str, b: str) bool = { let s: strings.iterator = strings.iter("こんにちは"); match (strings.prev(&s)) { case utf8.done => void; - case let r: rune => { fail(); }; + case let r: rune => { abort(); }; }; let expect1: [2]rune; expect1[0] = 0x3053u32: rune; // 'こ' @@ -1030,15 +967,15 @@ fn streq(a: str, b: str) bool = { let i: i32 = 0; for (i < 2) { match (strings.next(&s)) { - case let r: rune => { if (r != expect1[i]) { fail(); }; }; - case utf8.done => { fail(); }; + case let r: rune => { assert(!(r != expect1[i])); }; + case utf8.done => { abort(); }; }; i += 1; }; - if (!streq(strings.iterstr(&s), "にちは")) { fail(); }; + assert(!(!streq(strings.iterstr(&s), "にちは"))); match (strings.prev(&s)) { - case let r: rune => { if (r != 0x3093u32: rune) { fail(); }; }; // 'ん' - case utf8.done => { fail(); }; + case let r: rune => { assert(!(r != 0x3093u32: rune)); }; // 'ん' + case utf8.done => { abort(); }; }; let expect2: [4]rune; expect2[0] = 0x3093u32: rune; // 'ん' @@ -1048,23 +985,23 @@ fn streq(a: str, b: str) bool = { i = 0; for (i < 4) { match (strings.next(&s)) { - case let r: rune => { if (r != expect2[i]) { fail(); }; }; - case utf8.done => { fail(); }; + case let r: rune => { assert(!(r != expect2[i])); }; + case utf8.done => { abort(); }; }; i += 1; }; match (strings.next(&s)) { case utf8.done => void; - case let r: rune => { fail(); }; + case let r: rune => { abort(); }; }; // Repeated next-after-done stays done. match (strings.next(&s)) { case utf8.done => void; - case let r: rune => { fail(); }; + case let r: rune => { abort(); }; }; match (strings.prev(&s)) { - case let r: rune => { if (r != 0x306Fu32: rune) { fail(); }; }; // 'は' - case utf8.done => { fail(); }; + case let r: rune => { assert(!(r != 0x306Fu32: rune)); }; // 'は' + case utf8.done => { abort(); }; }; // Swap to a reverse iterator. sret-into-existing-slot. @@ -1076,31 +1013,31 @@ fn streq(a: str, b: str) bool = { i = 0; for (i < 3) { match (strings.next(&s)) { - case let r: rune => { if (r != expect3[i]) { fail(); }; }; - case utf8.done => { fail(); }; + case let r: rune => { assert(!(r != expect3[i])); }; + case utf8.done => { abort(); }; }; i += 1; }; match (strings.next(&s)) { case utf8.done => void; - case let r: rune => { fail(); }; + case let r: rune => { abort(); }; }; match (strings.prev(&s)) { - case let r: rune => { if (r != 0x306Bu32: rune) { fail(); }; }; // 'に' - case utf8.done => { fail(); }; + case let r: rune => { assert(!(r != 0x306Bu32: rune)); }; // 'に' + case utf8.done => { abort(); }; }; }; @test fn iter_position_cases() void = { let it: strings.iterator = strings.iter("café"); // 5 bytes: c-a-f-é(2) - if (strings.position(&it) != 0) { fail(); }; - match (strings.next(&it)) { case let r: rune => void; case utf8.done => { fail(); }; }; - if (strings.position(&it) != 1) { fail(); }; - match (strings.next(&it)) { case let r: rune => void; case utf8.done => { fail(); }; }; - match (strings.next(&it)) { case let r: rune => void; case utf8.done => { fail(); }; }; - if (strings.position(&it) != 3) { fail(); }; - match (strings.next(&it)) { case let r: rune => void; case utf8.done => { fail(); }; }; - if (strings.position(&it) != 5) { fail(); }; + assert(!(strings.position(&it) != 0)); + match (strings.next(&it)) { case let r: rune => void; case utf8.done => { abort(); }; }; + assert(!(strings.position(&it) != 1)); + match (strings.next(&it)) { case let r: rune => void; case utf8.done => { abort(); }; }; + match (strings.next(&it)) { case let r: rune => void; case utf8.done => { abort(); }; }; + assert(!(strings.position(&it) != 3)); + match (strings.next(&it)) { case let r: rune => void; case utf8.done => { abort(); }; }; + assert(!(strings.position(&it) != 5)); }; // ref/hare/strings/iter.ha:110 @test fn slice. Hare uses `let t = s;` @@ -1110,53 +1047,53 @@ fn streq(a: str, b: str) bool = { @test fn iter_slice_cases() void = { let s: strings.iterator = strings.iter("こんにちは"); let t: strings.iterator = strings.iter("こんにちは"); - if (strings.slice(&s, &t).len != 0) { fail(); }; - if (strings.slice(&t, &s).len != 0) { fail(); }; + assert(!(strings.slice(&s, &t).len != 0)); + assert(!(strings.slice(&t, &s).len != 0)); let i: i32 = 0; for (i < 2) { match (strings.next(&s)) { case let r: rune => void; - case utf8.done => { fail(); }; + case utf8.done => { abort(); }; }; match (strings.next(&t)) { case let r: rune => void; - case utf8.done => { fail(); }; + case utf8.done => { abort(); }; }; i += 1; }; - if (strings.slice(&s, &t).len != 0) { fail(); }; - if (strings.slice(&t, &s).len != 0) { fail(); }; + assert(!(strings.slice(&s, &t).len != 0)); + assert(!(strings.slice(&t, &s).len != 0)); i = 0; for (i < 3) { match (strings.next(&t)) { case let r: rune => void; - case utf8.done => { fail(); }; + case utf8.done => { abort(); }; }; i += 1; }; - if (!streq(strings.slice(&s, &t), "にちは")) { fail(); }; + assert(!(!streq(strings.slice(&s, &t), "にちは"))); i = 0; for (i < 3) { match (strings.next(&s)) { case let r: rune => void; - case utf8.done => { fail(); }; + case utf8.done => { abort(); }; }; i += 1; }; - if (strings.slice(&s, &t).len != 0) { fail(); }; - if (strings.slice(&t, &s).len != 0) { fail(); }; + assert(!(strings.slice(&s, &t).len != 0)); + assert(!(strings.slice(&t, &s).len != 0)); }; @test fn iter_iterstr_reverse_cases() void = { // Reverse iter: iterstr is `src[0:offs]` — bytes BEFORE the cursor // (the still-to-be-walked region in reverse direction). let rit: strings.iterator = strings.riter("hello"); - if (!streq(strings.iterstr(&rit), "hello")) { fail(); }; - match (strings.next(&rit)) { case let r: rune => void; case utf8.done => { fail(); }; }; - if (!streq(strings.iterstr(&rit), "hell")) { fail(); }; - match (strings.next(&rit)) { case let r: rune => void; case utf8.done => { fail(); }; }; - match (strings.next(&rit)) { case let r: rune => void; case utf8.done => { fail(); }; }; - if (!streq(strings.iterstr(&rit), "he")) { fail(); }; + assert(!(!streq(strings.iterstr(&rit), "hello"))); + match (strings.next(&rit)) { case let r: rune => void; case utf8.done => { abort(); }; }; + assert(!(!streq(strings.iterstr(&rit), "hell"))); + match (strings.next(&rit)) { case let r: rune => void; case utf8.done => { abort(); }; }; + match (strings.next(&rit)) { case let r: rune => void; case utf8.done => { abort(); }; }; + assert(!(!streq(strings.iterstr(&rit), "he"))); }; // ---- tokenize / rtokenize / peek_token / remaining_tokens ----------- @@ -1166,29 +1103,28 @@ fn streq(a: str, b: str) bool = { fn expect_str_token(t: *strings.tokenizer, want: str) void = { match (strings.peek_token(t)) { - case let p: str => { if (!streq(p, want)) { fail(); }; }; - case bytes.done => { fail(); }; + case let p: str => { assert(!(!streq(p, want))); }; + case bytes.done => { abort(); }; }; match (strings.next_token(t)) { - case let n: str => { if (!streq(n, want)) { fail(); }; }; - case bytes.done => { fail(); }; + case let n: str => { assert(!(!streq(n, want))); }; + case bytes.done => { abort(); }; }; }; fn expect_str_done(t: *strings.tokenizer) void = { match (strings.peek_token(t)) { - case let p: str => { fail(); }; + case let p: str => { abort(); }; case bytes.done => void; }; match (strings.next_token(t)) { - case let n: str => { fail(); }; + case let n: str => { abort(); }; case bytes.done => void; }; }; @test fn tokenize_cases() void = { // Hare vector — single-space delim. ref/hare/strings/tokenize.ha:112. - signalled = 1700; let t: strings.tokenizer = strings.tokenize( "Hello world! My name is Harriet.", " "); expect_str_token(&t, "Hello"); @@ -1201,7 +1137,6 @@ fn expect_str_done(t: *strings.tokenizer) void = { // Multi-byte delim set — space + tab. // ref/hare/strings/tokenize.ha:124. - signalled = 1701; let t2: strings.tokenizer = strings.tokenize( "/dev/sda1\t/ ext4 rw,relatime\t0 0", " \t"); expect_str_token(&t2, "/dev/sda1"); @@ -1214,7 +1149,6 @@ fn expect_str_done(t: *strings.tokenizer) void = { // Consecutive delimiters — empty interior tokens. // ref/hare/strings/tokenize.ha:136. - signalled = 1702; let t3: strings.tokenizer = strings.tokenize("hello world", " "); expect_str_token(&t3, "hello"); expect_str_token(&t3, ""); @@ -1225,7 +1159,6 @@ fn expect_str_done(t: *strings.tokenizer) void = { // Leading + trailing delimiters yield empty tokens. // ref/hare/strings/tokenize.ha:147. - signalled = 1703; let t4: strings.tokenizer = strings.tokenize(" hello world ", " "); expect_str_token(&t4, ""); expect_str_token(&t4, "hello"); @@ -1234,20 +1167,17 @@ fn expect_str_done(t: *strings.tokenizer) void = { expect_str_done(&t4); // No delim hit — single full token. - signalled = 1704; let t5: strings.tokenizer = strings.tokenize("abc", " "); expect_str_token(&t5, "abc"); expect_str_done(&t5); // Empty input — done immediately. - signalled = 1705; let t6: strings.tokenizer = strings.tokenize("", " "); expect_str_done(&t6); }; @test fn rtokenize_cases() void = { // Reverse direction — first next_token is the last token. - signalled = 1710; let t: strings.tokenizer = strings.rtokenize( "Hello world! My name is Harriet.", " "); expect_str_token(&t, "Harriet."); @@ -1259,7 +1189,6 @@ fn expect_str_done(t: *strings.tokenizer) void = { expect_str_done(&t); // Multi-byte delim set, reverse direction. - signalled = 1711; let t2: strings.tokenizer = strings.rtokenize("a b\tc", " \t"); expect_str_token(&t2, "c"); expect_str_token(&t2, "b"); @@ -1267,39 +1196,35 @@ fn expect_str_done(t: *strings.tokenizer) void = { expect_str_done(&t2); // Empty input — done immediately. - signalled = 1712; let t3: strings.tokenizer = strings.rtokenize("", " "); expect_str_done(&t3); }; @test fn peek_token_cases() void = { // Two peeks without advancing return the same token. - signalled = 1720; let t: strings.tokenizer = strings.tokenize("a b c", " "); match (strings.peek_token(&t)) { - case let p: str => { if (!streq(p, "a")) { fail(); }; }; - case bytes.done => { fail(); }; + case let p: str => { assert(!(!streq(p, "a"))); }; + case bytes.done => { abort(); }; }; match (strings.peek_token(&t)) { - case let p: str => { if (!streq(p, "a")) { fail(); }; }; - case bytes.done => { fail(); }; + case let p: str => { assert(!(!streq(p, "a"))); }; + case bytes.done => { abort(); }; }; // Advance once — peek then returns "b". - signalled = 1721; match (strings.next_token(&t)) { - case let n: str => { if (!streq(n, "a")) { fail(); }; }; - case bytes.done => { fail(); }; + case let n: str => { assert(!(!streq(n, "a"))); }; + case bytes.done => { abort(); }; }; match (strings.peek_token(&t)) { - case let p: str => { if (!streq(p, "b")) { fail(); }; }; - case bytes.done => { fail(); }; + case let p: str => { assert(!(!streq(p, "b"))); }; + case bytes.done => { abort(); }; }; // Empty input — peek is done. - signalled = 1722; let t2: strings.tokenizer = strings.tokenize("", " "); match (strings.peek_token(&t2)) { - case let p: str => { fail(); }; + case let p: str => { abort(); }; case bytes.done => void; }; }; @@ -1307,25 +1232,23 @@ fn expect_str_done(t: *strings.tokenizer) void = { @test fn remaining_tokens_cases() void = { // ref/hare/strings/tokenize.ha:157. After 2 next_tokens, remaining // is "My name is Harriet.". - signalled = 1730; let t: strings.tokenizer = strings.tokenize( "Hello world! My name is Harriet.", " "); match (strings.next_token(&t)) { - case let n: str => { if (!streq(n, "Hello")) { fail(); }; }; - case bytes.done => { fail(); }; + case let n: str => { assert(!(!streq(n, "Hello"))); }; + case bytes.done => { abort(); }; }; match (strings.next_token(&t)) { - case let n: str => { if (!streq(n, "world!")) { fail(); }; }; - case bytes.done => { fail(); }; + case let n: str => { assert(!(!streq(n, "world!"))); }; + case bytes.done => { abort(); }; }; if (!streq(strings.remaining_tokens(&t), "My name is Harriet.")) { - fail(); + abort(); }; // Fresh tokenizer — remaining_tokens is the whole input. - signalled = 1731; let t2: strings.tokenizer = strings.tokenize("a b c", " "); - if (!streq(strings.remaining_tokens(&t2), "a b c")) { fail(); }; + assert(!(!streq(strings.remaining_tokens(&t2), "a b c"))); }; // ---- splitn / rsplitn / split ---------------------------------------- @@ -1335,12 +1258,12 @@ fn expect_str_done(t: *strings.tokenizer) void = { // multi-word-store gap noted at cmd/w6c/cgen.c:6515. fn expect_str(toks: []str, i: i32, want: str) void = { - if (i >= toks.len) { fail(); }; + assert(!(i >= toks.len)); let p: *str = &toks.ptr[i]; - if (p.len != want.len) { fail(); }; + assert(!(p.len != want.len)); let j: i32 = 0; for (j < want.len) { - if (p.ptr[j] != want[j]) { fail(); }; + assert(!(p.ptr[j] != want[j])); j += 1; }; }; @@ -1348,9 +1271,8 @@ fn expect_str(toks: []str, i: i32, want: str) void = { @test fn splitn_cases() void = { // ref/hare/strings/tokenize.ha:247 — n=4 buckets the trailing // "is Drew" as the remainder slot. - signalled = 1740; let t1: []str = strings.splitn("Hello, my name is Drew", " ", 4); - if (t1.len != 4) { fail(); }; + assert(!(t1.len != 4)); expect_str(t1, 0, "Hello,"); expect_str(t1, 1, "my"); expect_str(t1, 2, "name"); @@ -1359,32 +1281,28 @@ fn expect_str(toks: []str, i: i32, want: str) void = { // ref/hare/strings/tokenize.ha:263 — n > tokens leaves a single // slot holding the unchanged input (delim not found). - signalled = 1741; let t2: []str = strings.splitn("one", "=", 2); - if (t2.len != 1) { fail(); }; + assert(!(t2.len != 1)); expect_str(t2, 0, "one"); os.free(t2.ptr: *void, (t2.cap: u64) * size(str): u64); // n == 1 — single slot holding the whole input as remainder. - signalled = 1742; let t3: []str = strings.splitn("a b c", " ", 1); - if (t3.len != 1) { fail(); }; + assert(!(t3.len != 1)); expect_str(t3, 0, "a b c"); os.free(t3.ptr: *void, (t3.cap: u64) * size(str): u64); // Empty input — empty result. - signalled = 1743; let t4: []str = strings.splitn("", " ", 5); - if (t4.len != 0) { fail(); }; + assert(!(t4.len != 0)); if (t4.cap > 0) { os.free(t4.ptr: *void, (t4.cap: u64) * size(str): u64); }; // Multi-byte delim set (byte-set semantics per // ref/hare/strings/tokenize.ha:35) — split on ',' OR ':' OR ';'. - signalled = 1744; let t5: []str = strings.splitn("hello;world,foo:bar", ",:;", 10); - if (t5.len != 4) { fail(); }; + assert(!(t5.len != 4)); expect_str(t5, 0, "hello"); expect_str(t5, 1, "world"); expect_str(t5, 2, "foo"); @@ -1395,9 +1313,8 @@ fn expect_str(toks: []str, i: i32, want: str) void = { @test fn rsplitn_cases() void = { // ref/hare/strings/tokenize.ha:271 — reverse n=4 with the // "Hello, my" prefix as the remainder slot at index 0. - signalled = 1750; let t1: []str = strings.rsplitn("Hello, my name is Drew", " ", 4); - if (t1.len != 4) { fail(); }; + assert(!(t1.len != 4)); expect_str(t1, 0, "Hello, my"); expect_str(t1, 1, "name"); expect_str(t1, 2, "is"); @@ -1408,27 +1325,24 @@ fn expect_str(toks: []str, i: i32, want: str) void = { // (last-token-first order). Mirrors bytes.rsplitn (Hare's // ref/hare/strings/tokenize.ha:219-224 reverse step is gated // behind the n-1 loop completion). - signalled = 1751; let t2: []str = strings.rsplitn("a b c", " ", 10); - if (t2.len != 3) { fail(); }; + assert(!(t2.len != 3)); expect_str(t2, 0, "c"); expect_str(t2, 1, "b"); expect_str(t2, 2, "a"); os.free(t2.ptr: *void, (t2.cap: u64) * size(str): u64); // n == 1 — single slot holding the whole input as remainder. - signalled = 1752; let t3: []str = strings.rsplitn("a b c", " ", 1); - if (t3.len != 1) { fail(); }; + assert(!(t3.len != 1)); expect_str(t3, 0, "a b c"); os.free(t3.ptr: *void, (t3.cap: u64) * size(str): u64); // delim absent — first next_token yields the entire input as the // sole token; second iter sees done and short-circuits with the // 1-elem toks un-reversed (single element, reverse is a no-op). - signalled = 1753; let t4: []str = strings.rsplitn("abc", "=", 5); - if (t4.len != 1) { fail(); }; + assert(!(t4.len != 1)); expect_str(t4, 0, "abc"); os.free(t4.ptr: *void, (t4.cap: u64) * size(str): u64); }; @@ -1436,9 +1350,8 @@ fn expect_str(toks: []str, i: i32, want: str) void = { @test fn split_cases() void = { // ref/hare/strings/tokenize.ha:255 — full split, every delim hit // is a boundary. - signalled = 1760; let t1: []str = strings.split("Hello, my name is Drew", " "); - if (t1.len != 5) { fail(); }; + assert(!(t1.len != 5)); expect_str(t1, 0, "Hello,"); expect_str(t1, 1, "my"); expect_str(t1, 2, "name"); @@ -1447,9 +1360,8 @@ fn expect_str(toks: []str, i: i32, want: str) void = { os.free(t1.ptr: *void, (t1.cap: u64) * size(str): u64); // Leading + trailing delim — empty tokens at ends. - signalled = 1761; let t2: []str = strings.split(" a b ", " "); - if (t2.len != 4) { fail(); }; + assert(!(t2.len != 4)); expect_str(t2, 0, ""); expect_str(t2, 1, "a"); expect_str(t2, 2, "b"); @@ -1458,9 +1370,8 @@ fn expect_str(toks: []str, i: i32, want: str) void = { // Multi-byte delim set, byte-set semantics matching Hare's // strings::split example at ref/hare/strings/tokenize.ha:235. - signalled = 1762; let t3: []str = strings.split("hello;world,foo:bar", ",:;"); - if (t3.len != 4) { fail(); }; + assert(!(t3.len != 4)); expect_str(t3, 0, "hello"); expect_str(t3, 1, "world"); expect_str(t3, 2, "foo"); @@ -1479,102 +1390,90 @@ fn expect_str(toks: []str, i: i32, want: str) void = { @test fn lpad_cases() void = { // Hare row 1: shorter s, ASCII pad. - signalled = 1770; let r1: str = strings.lpad("2", '0', 5); - if (!streq(r1, "00002")) { fail(); }; - if (r1.len != 5) { fail(); }; + assert(!(!streq(r1, "00002"))); + assert(!(r1.len != 5)); os.free(r1.ptr: *void, r1.len: u64); // Hare row 2: s.len == maxlen — early dup path. - signalled = 1771; let r2: str = strings.lpad("12345", '0', 5); - if (!streq(r2, "12345")) { fail(); }; - if (r2.len != 5) { fail(); }; + assert(!(!streq(r2, "12345"))); + assert(!(r2.len != 5)); os.free(r2.ptr: *void, r2.len: u64); // Hare row 3: empty s, full-width pad. - signalled = 1772; let r3: str = strings.lpad("", '0', 5); - if (!streq(r3, "00000")) { fail(); }; - if (r3.len != 5) { fail(); }; + assert(!(!streq(r3, "00000"))); + assert(!(r3.len != 5)); os.free(r3.ptr: *void, r3.len: u64); // ww row 1: s.len > maxlen — early dup path returns input copy. - signalled = 1773; let r4: str = strings.lpad("abcdef", '_', 3); - if (!streq(r4, "abcdef")) { fail(); }; - if (r4.len != 6) { fail(); }; + assert(!(!streq(r4, "abcdef"))); + assert(!(r4.len != 6)); os.free(r4.ptr: *void, r4.len: u64); // ww row 2: multibyte s — byte-length contract (Hare `len(s)`). // "café" is 5 bytes; maxlen 7 → 2 pad bytes prepended. - signalled = 1774; let r5: str = strings.lpad("café", '_', 7); - if (!streq(r5, "__café")) { fail(); }; - if (r5.len != 7) { fail(); }; + assert(!(!streq(r5, "__café"))); + assert(!(r5.len != 7)); os.free(r5.ptr: *void, r5.len: u64); // ww row 3: multibyte pad rune. 'α' U+03B1 is 2 bytes (0xCE 0xB1). // s="x" (1 byte), maxlen=5 → npads = 5 - 1 = 4, pad_bytes=2, // total writes = 4*2 + 1 = 9 bytes; result `[..5]` = "αα" (4 bytes) // + 0xCE (truncated leading byte of next α) — exactly Hare's slice. - signalled = 1775; let r6: str = strings.lpad("x", 0x03B1u32: rune, 5); - if (r6.len != 5) { fail(); }; - if (r6.ptr[0] != 0xCEu8) { fail(); }; // α byte 0 - if (r6.ptr[1] != 0xB1u8) { fail(); }; // α byte 1 - if (r6.ptr[2] != 0xCEu8) { fail(); }; // α byte 0 - if (r6.ptr[3] != 0xB1u8) { fail(); }; // α byte 1 - if (r6.ptr[4] != 0xCEu8) { fail(); }; // truncated α byte 0 + assert(!(r6.len != 5)); + assert(!(r6.ptr[0] != 0xCEu8)); // α byte 0 + assert(!(r6.ptr[1] != 0xB1u8)); // α byte 1 + assert(!(r6.ptr[2] != 0xCEu8)); // α byte 0 + assert(!(r6.ptr[3] != 0xB1u8)); // α byte 1 + assert(!(r6.ptr[4] != 0xCEu8)); // truncated α byte 0 os.free(r6.ptr: *void, r6.len: u64); }; @test fn rpad_cases() void = { // Hare row 1: shorter s, ASCII pad. - signalled = 1780; let r1: str = strings.rpad("2", '0', 5); - if (!streq(r1, "20000")) { fail(); }; - if (r1.len != 5) { fail(); }; + assert(!(!streq(r1, "20000"))); + assert(!(r1.len != 5)); os.free(r1.ptr: *void, r1.len: u64); // Hare row 2: s.len == maxlen — early dup path. - signalled = 1781; let r2: str = strings.rpad("12345", '0', 5); - if (!streq(r2, "12345")) { fail(); }; - if (r2.len != 5) { fail(); }; + assert(!(!streq(r2, "12345"))); + assert(!(r2.len != 5)); os.free(r2.ptr: *void, r2.len: u64); // Hare row 3: empty s. - signalled = 1782; let r3: str = strings.rpad("", '0', 5); - if (!streq(r3, "00000")) { fail(); }; - if (r3.len != 5) { fail(); }; + assert(!(!streq(r3, "00000"))); + assert(!(r3.len != 5)); os.free(r3.ptr: *void, r3.len: u64); // ww row 1: s.len > maxlen — early dup path. - signalled = 1783; let r4: str = strings.rpad("abcdef", '_', 3); - if (!streq(r4, "abcdef")) { fail(); }; - if (r4.len != 6) { fail(); }; + assert(!(!streq(r4, "abcdef"))); + assert(!(r4.len != 6)); os.free(r4.ptr: *void, r4.len: u64); // ww row 2: multibyte s — byte-length contract. - signalled = 1784; let r5: str = strings.rpad("café", '_', 7); - if (!streq(r5, "café__")) { fail(); }; - if (r5.len != 7) { fail(); }; + assert(!(!streq(r5, "café__"))); + assert(!(r5.len != 7)); os.free(r5.ptr: *void, r5.len: u64); // ww row 3: multibyte pad rune (2-byte 'α'). s="x" (1 byte), // maxlen=5 → "xαα" appended is 1 + 4 = 5 bytes (no truncation). - signalled = 1785; let r6: str = strings.rpad("x", 0x03B1u32: rune, 5); - if (r6.len != 5) { fail(); }; - if (r6.ptr[0] != 'x') { fail(); }; - if (r6.ptr[1] != 0xCEu8) { fail(); }; // α byte 0 - if (r6.ptr[2] != 0xB1u8) { fail(); }; // α byte 1 - if (r6.ptr[3] != 0xCEu8) { fail(); }; // α byte 0 - if (r6.ptr[4] != 0xB1u8) { fail(); }; // α byte 1 + assert(!(r6.len != 5)); + assert(!(r6.ptr[0] != 'x')); + assert(!(r6.ptr[1] != 0xCEu8)); // α byte 0 + assert(!(r6.ptr[2] != 0xB1u8)); // α byte 1 + assert(!(r6.ptr[3] != 0xCEu8)); // α byte 0 + assert(!(r6.ptr[4] != 0xB1u8)); // α byte 1 os.free(r6.ptr: *void, r6.len: u64); }; @@ -1582,110 +1481,100 @@ fn expect_str(toks: []str, i: i32, want: str) void = { // ref/hare/strings/replace.ha:46 (#4). Hare rows 1-5 (target found // once / multiple times / shorter / longer / multibyte) plus ww rows // (no-match returns fresh copy, empty-replacement, result-shrinks-to- -// empty). Per-row `signalled` 1810+i narrows the failing row. +// empty). @test fn replace_cases() void = { // Hare row 1: target found once. - signalled = 1810; match (strings.replace("Hello world!", "world", "there")) { case let s: str => { - if (!streq(s, "Hello there!")) { fail(); }; + assert(!(!streq(s, "Hello there!"))); os.free(s.ptr: *void, s.len: u64); }; - case nomem => { fail(); }; + case nomem => { abort(); }; }; // Hare row 2: needle found four times (replacement same length). - signalled = 1811; match (strings.replace("I like dogs, dogs, birds, dogs", "dogs", "cats")) { case let s: str => { - if (!streq(s, "I like cats, cats, birds, cats")) { fail(); }; + assert(!(!streq(s, "I like cats, cats, birds, cats"))); os.free(s.ptr: *void, s.len: u64); }; - case nomem => { fail(); }; + case nomem => { abort(); }; }; // Hare row 3: replacement shorter than needle, non-overlapping // matches (Hare advances by needle.len, not 1). - signalled = 1812; match (strings.replace("aaaaaa", "aa", "a")) { case let s: str => { - if (!streq(s, "aaa")) { fail(); }; + assert(!(!streq(s, "aaa"))); os.free(s.ptr: *void, s.len: u64); }; - case nomem => { fail(); }; + case nomem => { abort(); }; }; // Hare row 4: replacement longer than needle. - signalled = 1813; match (strings.replace("aaa", "a", "aa")) { case let s: str => { - if (!streq(s, "aaaaaa")) { fail(); }; + assert(!(!streq(s, "aaaaaa"))); os.free(s.ptr: *void, s.len: u64); }; - case nomem => { fail(); }; + case nomem => { abort(); }; }; // Hare row 5: multibyte UTF-8 (3-byte runes). - signalled = 1814; match (strings.replace("こんにちは", "にち", "ばん")) { case let s: str => { - if (!streq(s, "こんばんは")) { fail(); }; + assert(!(!streq(s, "こんばんは"))); os.free(s.ptr: *void, s.len: u64); }; - case nomem => { fail(); }; + case nomem => { abort(); }; }; // ww row 1: needle not found — fresh copy (distinct from // borrowed input ptr). - signalled = 1815; let src: str = "hello world"; match (strings.replace(src, "xyz", "abc")) { case let s: str => { - if (!streq(s, "hello world")) { fail(); }; - if (s.ptr == src.ptr) { fail(); }; + assert(!(!streq(s, "hello world"))); + assert(!(s.ptr == src.ptr)); os.free(s.ptr: *void, s.len: u64); }; - case nomem => { fail(); }; + case nomem => { abort(); }; }; // ww row 2: empty replacement — needle is removed. - signalled = 1816; match (strings.replace("foo-bar-baz", "-", "")) { case let s: str => { - if (!streq(s, "foobarbaz")) { fail(); }; + assert(!(!streq(s, "foobarbaz"))); os.free(s.ptr: *void, s.len: u64); }; - case nomem => { fail(); }; + case nomem => { abort(); }; }; // ww row 3: result shrinks to empty — total==0 nil-alloc path. - signalled = 1817; match (strings.replace("aaaa", "aa", "")) { case let s: str => { - if (!streq(s, "")) { fail(); }; - if (s.len != 0) { fail(); }; + assert(!(!streq(s, ""))); + assert(!(s.len != 0)); }; - case nomem => { fail(); }; + case nomem => { abort(); }; }; // ww row 4: multibyte single-rune replace — every "に" → "X". - signalled = 1818; match (strings.replace("こんにちは", "に", "X")) { case let s: str => { - if (!streq(s, "こんXちは")) { fail(); }; + assert(!(!streq(s, "こんXちは"))); os.free(s.ptr: *void, s.len: u64); }; - case nomem => { fail(); }; + case nomem => { abort(); }; }; // ww row 5: empty input — returns empty. - signalled = 1819; match (strings.replace("", "x", "y")) { case let s: str => { - if (s.len != 0) { fail(); }; + assert(!(s.len != 0)); }; - case nomem => { fail(); }; + case nomem => { abort(); }; }; }; @@ -1696,155 +1585,138 @@ fn expect_str(toks: []str, i: i32, want: str) void = { @test fn cut_cases() void = { // both halves present. - signalled = 1830; let (a0, b0) = strings.cut("hello=world", "="); - if (!streq(a0, "hello")) { fail(); }; - if (!streq(b0, "world")) { fail(); }; + assert(!(!streq(a0, "hello"))); + assert(!(!streq(b0, "world"))); // only first instance is cut; rest stays in the second half. - signalled = 1831; let (a1, b1) = strings.cut("hello=world=foobar", "="); - if (!streq(a1, "hello")) { fail(); }; - if (!streq(b1, "world=foobar")) { fail(); }; + assert(!(!streq(a1, "hello"))); + assert(!(!streq(b1, "world=foobar"))); // delim absent -> (whole input, ""). - signalled = 1832; let (a2, b2) = strings.cut("hello world", "="); - if (!streq(a2, "hello world")) { fail(); }; - if (!streq(b2, "")) { fail(); }; + assert(!(!streq(a2, "hello world"))); + assert(!(!streq(b2, ""))); // delim at start -> empty before. - signalled = 1833; let (a3, b3) = strings.cut("=world", "="); - if (!streq(a3, "")) { fail(); }; - if (!streq(b3, "world")) { fail(); }; + assert(!(!streq(a3, ""))); + assert(!(!streq(b3, "world"))); // delim at end -> empty after. - signalled = 1834; let (a4, b4) = strings.cut("hello=", "="); - if (!streq(a4, "hello")) { fail(); }; - if (!streq(b4, "")) { fail(); }; + assert(!(!streq(a4, "hello"))); + assert(!(!streq(b4, ""))); // empty input -> ("", ""). - signalled = 1835; let (a5, b5) = strings.cut("", "="); - if (!streq(a5, "")) { fail(); }; - if (!streq(b5, "")) { fail(); }; + assert(!(!streq(a5, ""))); + assert(!(!streq(b5, ""))); // multi-byte delim present. - signalled = 1836; let (a6, b6) = strings.cut("aXYbXYc", "XY"); - if (!streq(a6, "a")) { fail(); }; - if (!streq(b6, "bXYc")) { fail(); }; + assert(!(!streq(a6, "a"))); + assert(!(!streq(b6, "bXYc"))); // multi-byte delim absent. - signalled = 1837; let (a7, b7) = strings.cut("abc", "XY"); - if (!streq(a7, "abc")) { fail(); }; - if (!streq(b7, "")) { fail(); }; + assert(!(!streq(a7, "abc"))); + assert(!(!streq(b7, ""))); // borrowed, not copied: first half aliases the input bytes. - signalled = 1838; let in: str = "hello=world"; let (a8, b8) = strings.cut(in, "="); - if (a8.ptr != in.ptr) { fail(); }; + assert(!(a8.ptr != in.ptr)); }; @test fn rcut_cases() void = { // rcut splits along the LAST instance. - signalled = 1840; let (a0, b0) = strings.rcut("hello=world=foobar", "="); - if (!streq(a0, "hello=world")) { fail(); }; - if (!streq(b0, "foobar")) { fail(); }; + assert(!(!streq(a0, "hello=world"))); + assert(!(!streq(b0, "foobar"))); // single instance == cut. - signalled = 1841; let (a1, b1) = strings.rcut("hello=world", "="); - if (!streq(a1, "hello")) { fail(); }; - if (!streq(b1, "world")) { fail(); }; + assert(!(!streq(a1, "hello"))); + assert(!(!streq(b1, "world"))); // delim absent -> (whole input, ""). - signalled = 1842; let (a2, b2) = strings.rcut("hello world", "="); - if (!streq(a2, "hello world")) { fail(); }; - if (!streq(b2, "")) { fail(); }; + assert(!(!streq(a2, "hello world"))); + assert(!(!streq(b2, ""))); // delim at end -> empty after. - signalled = 1843; let (a3, b3) = strings.rcut("hello=", "="); - if (!streq(a3, "hello")) { fail(); }; - if (!streq(b3, "")) { fail(); }; + assert(!(!streq(a3, "hello"))); + assert(!(!streq(b3, ""))); // delim at start -> empty before. - signalled = 1844; let (a4, b4) = strings.rcut("=world", "="); - if (!streq(a4, "")) { fail(); }; - if (!streq(b4, "world")) { fail(); }; + assert(!(!streq(a4, ""))); + assert(!(!streq(b4, "world"))); // empty input -> ("", ""). - signalled = 1845; let (a5, b5) = strings.rcut("", "="); - if (!streq(a5, "")) { fail(); }; - if (!streq(b5, "")) { fail(); }; + assert(!(!streq(a5, ""))); + assert(!(!streq(b5, ""))); // multi-byte delim, two instances -> cut at LAST. - signalled = 1846; let (a6, b6) = strings.rcut("XYaXYb", "XY"); - if (!streq(a6, "XYa")) { fail(); }; - if (!streq(b6, "b")) { fail(); }; + assert(!(!streq(a6, "XYa"))); + assert(!(!streq(b6, "b"))); // multi-byte delim absent. - signalled = 1847; let (a7, b7) = strings.rcut("abc", "XY"); - if (!streq(a7, "abc")) { fail(); }; - if (!streq(b7, "")) { fail(); }; + assert(!(!streq(a7, "abc"))); + assert(!(!streq(b7, ""))); }; export fn main() i32 = { - signalled = 1; dup_cases(); - signalled = 42; dupall_cases(); - signalled = 2; concat_cases(); - signalled = 30; join_cases(); - signalled = 3; hasprefix_cases(); - signalled = 4; hassuffix_cases(); - signalled = 5; contains_cases(); - signalled = 6; byteindex_str_cases(); - signalled = 7; byteindex_rune_cases(); - signalled = 8; rbyteindex_cases(); - signalled = 28; index_cases(); - signalled = 29; rindex_cases(); - signalled = 9; trimprefix_cases(); - signalled = 10; trimsuffix_cases(); - signalled = 11; ltrim_cases(); - signalled = 12; rtrim_cases(); - signalled = 13; trim_cases(); - signalled = 14; compare_cases(); - signalled = 40; sub_cases(); - signalled = 41; bytesub_cases(); - signalled = 15; utf8_roundtrip_cases(); - signalled = 16; iter_empty_cases(); - signalled = 17; iter_ascii_cases(); - signalled = 18; iter_twobyte_cases(); - signalled = 19; iter_threebyte_cases(); - signalled = 20; iter_fourbyte_cases(); - signalled = 21; iter_mixed_cases(); - signalled = 22; iter_prev_at_start_cases(); - signalled = 23; iter_prev_ascii_cases(); - signalled = 24; iter_full_cases(); - signalled = 25; iter_position_cases(); - signalled = 26; iter_iterstr_reverse_cases(); - signalled = 27; iter_slice_cases(); - signalled = 31; tokenize_cases(); - signalled = 32; rtokenize_cases(); - signalled = 33; peek_token_cases(); - signalled = 34; remaining_tokens_cases(); - signalled = 35; splitn_cases(); - signalled = 36; rsplitn_cases(); - signalled = 37; split_cases(); - signalled = 38; lpad_cases(); - signalled = 39; rpad_cases(); - signalled = 44; replace_cases(); - signalled = 45; cut_cases(); - signalled = 46; rcut_cases(); + dup_cases(); + dupall_cases(); + concat_cases(); + join_cases(); + hasprefix_cases(); + hassuffix_cases(); + contains_cases(); + byteindex_str_cases(); + byteindex_rune_cases(); + rbyteindex_cases(); + index_cases(); + rindex_cases(); + trimprefix_cases(); + trimsuffix_cases(); + ltrim_cases(); + rtrim_cases(); + trim_cases(); + compare_cases(); + sub_cases(); + bytesub_cases(); + utf8_roundtrip_cases(); + iter_empty_cases(); + iter_ascii_cases(); + iter_twobyte_cases(); + iter_threebyte_cases(); + iter_fourbyte_cases(); + iter_mixed_cases(); + iter_prev_at_start_cases(); + iter_prev_ascii_cases(); + iter_full_cases(); + iter_position_cases(); + iter_iterstr_reverse_cases(); + iter_slice_cases(); + tokenize_cases(); + rtokenize_cases(); + peek_token_cases(); + remaining_tokens_cases(); + splitn_cases(); + rsplitn_cases(); + split_cases(); + lpad_cases(); + rpad_cases(); + replace_cases(); + cut_cases(); + rcut_cases(); return 0; };