lib/strings+test: port replace per Hare
ref/hare/strings/replace.ha:46-66. Two-pass byte scan: pass 1 counts non-overlapping needle hits via bytes.hasprefix, pass 2 allocs the result []u8 at exact size and copies chunks + replacement. Single nomem propagation site at the alloc — ww's append builtin aborts on OOM (#11), so the per-chunk append(...)? form Hare uses is not available; the exact-size single alloc is equivalent in spec. total==0 returns {nil,0} to dodge rt_alloc(0) per #47. Empty needle is intentionally ungated and loops forever — that's Hare's behavior at ref/hare/strings/replace.ha:31 (i += len(needle) is 0; hasprefix("") always matches). Hare-faithful divergence, documented at the site. multireplace deferred to #49 — its (str, str) variadic gather hits #39 in variadic-param position. Filed and blocked accordingly.
This commit is contained in:
@@ -1664,6 +1664,117 @@ fn expect_str(toks: []str, i: i32, want: str) void = {
|
||||
os.free(r6.ptr: *void, r6.len: u64);
|
||||
};
|
||||
|
||||
// ---- replace ----------------------------------------------------------
|
||||
// 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.
|
||||
|
||||
@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(); };
|
||||
os.free(s.ptr: *void, s.len: u64);
|
||||
};
|
||||
case nomem => { fail(); };
|
||||
};
|
||||
|
||||
// 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(); };
|
||||
os.free(s.ptr: *void, s.len: u64);
|
||||
};
|
||||
case nomem => { fail(); };
|
||||
};
|
||||
|
||||
// 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(); };
|
||||
os.free(s.ptr: *void, s.len: u64);
|
||||
};
|
||||
case nomem => { fail(); };
|
||||
};
|
||||
|
||||
// Hare row 4: replacement longer than needle.
|
||||
signalled = 1813;
|
||||
match (strings.replace("aaa", "a", "aa")) {
|
||||
case let s: str => {
|
||||
if (!streq(s, "aaaaaa")) { fail(); };
|
||||
os.free(s.ptr: *void, s.len: u64);
|
||||
};
|
||||
case nomem => { fail(); };
|
||||
};
|
||||
|
||||
// Hare row 5: multibyte UTF-8 (3-byte runes).
|
||||
signalled = 1814;
|
||||
match (strings.replace("こんにちは", "にち", "ばん")) {
|
||||
case let s: str => {
|
||||
if (!streq(s, "こんばんは")) { fail(); };
|
||||
os.free(s.ptr: *void, s.len: u64);
|
||||
};
|
||||
case nomem => { fail(); };
|
||||
};
|
||||
|
||||
// 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(); };
|
||||
os.free(s.ptr: *void, s.len: u64);
|
||||
};
|
||||
case nomem => { fail(); };
|
||||
};
|
||||
|
||||
// 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(); };
|
||||
os.free(s.ptr: *void, s.len: u64);
|
||||
};
|
||||
case nomem => { fail(); };
|
||||
};
|
||||
|
||||
// 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(); };
|
||||
};
|
||||
case nomem => { fail(); };
|
||||
};
|
||||
|
||||
// ww row 4: multibyte single-rune replace — every "に" → "X".
|
||||
signalled = 1818;
|
||||
match (strings.replace("こんにちは", "に", "X")) {
|
||||
case let s: str => {
|
||||
if (!streq(s, "こんXちは")) { fail(); };
|
||||
os.free(s.ptr: *void, s.len: u64);
|
||||
};
|
||||
case nomem => { fail(); };
|
||||
};
|
||||
|
||||
// ww row 5: empty input — returns empty.
|
||||
signalled = 1819;
|
||||
match (strings.replace("", "x", "y")) {
|
||||
case let s: str => {
|
||||
if (s.len != 0) { fail(); };
|
||||
};
|
||||
case nomem => { fail(); };
|
||||
};
|
||||
};
|
||||
|
||||
export fn main() i32 = {
|
||||
signalled = 1; dup_cases();
|
||||
signalled = 42; dupall_cases();
|
||||
@@ -1708,5 +1819,6 @@ export fn main() i32 = {
|
||||
signalled = 37; split_cases();
|
||||
signalled = 38; lpad_cases();
|
||||
signalled = 39; rpad_cases();
|
||||
signalled = 44; replace_cases();
|
||||
return 0;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user