lib/strings+test: port join from Hare str... variadic (#19)

This commit is contained in:
2026-05-19 12:22:16 +09:00
parent 3a85db0f3f
commit 02ada29624
5 changed files with 221 additions and 0 deletions

View File

@@ -129,6 +129,47 @@ export fn concat(strs: str...) str = {
return r;
};
// join — fresh allocation with `delim` placed between each element of
// `strs`. Caller releases with `os.free(r.ptr, r.len: u64)`.
// ref/hare/strings/concat.ha:46. Hare's `nomem` return is dropped:
// `os.alloc` aborts on OOM.
export fn join(delim: str, strs: str...) str = {
let total: i32 = 0;
let i: i32 = 0;
for (i < strs.len) {
total += strs[i].len;
if (i + 1 < strs.len) { total += delim.len; };
i += 1;
};
let r: str;
r.ptr = nil;
r.len = 0;
if (total == 0) { return r; };
let buf: *u8 = os.alloc(total: u64): *u8;
let off: i32 = 0;
i = 0;
for (i < strs.len) {
let j: i32 = 0;
for (j < strs[i].len) {
buf[off + j] = strs[i][j];
j += 1;
};
off += strs[i].len;
if (i + 1 < strs.len) {
j = 0;
for (j < delim.len) {
buf[off + j] = delim[j];
j += 1;
};
off += delim.len;
};
i += 1;
};
r.ptr = buf;
r.len = total;
return r;
};
// sub — borrowed `s[start..end]`. ref/hare/strings/sub.ha:30 is
// rune-wise; this ww form is byte-wise (no rune iterator yet, planned
// for commit 2). Clamps out-of-range silently where Hare aborts —

View File

@@ -99,6 +99,62 @@ fn streq(a: str, b: str) bool = {
};
};
// ---- join -------------------------------------------------------------
// 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;
pool[0] = "hello";
pool[1] = "a";
pool[2] = "b";
pool[3] = "a";
pool[4] = "b";
pool[5] = "c";
pool[6] = "a";
pool[7] = "b";
pool[8] = "c";
pool[9] = "";
pool[10] = "";
pool[11] = "a";
pool[12] = "b";
pool[13] = "c";
pool[14] = "こん";
pool[15] = "にちは";
pool[16] = "a";
pool[17] = "";
pool[18] = "b";
let argo: [9]i32;
let argn: [9]i32;
let seps: [9]str;
let want: [9]str;
argo[0]=0; argn[0]=0; seps[0]="."; want[0]="";
argo[1]=0; argn[1]=1; seps[1]="."; want[1]="hello";
argo[2]=1; argn[2]=2; seps[2]=", "; want[2]="a, b";
argo[3]=3; argn[3]=3; seps[3]="."; want[3]="a.b.c";
argo[4]=6; argn[4]=3; seps[4]=""; want[4]="abc";
argo[5]=9; argn[5]=2; seps[5]=","; want[5]=",";
argo[6]=11; argn[6]=3; seps[6]=" :: "; want[6]="a :: b :: c";
argo[7]=14; argn[7]=2; seps[7]="・"; want[7]="こん・にちは";
argo[8]=16; argn[8]=3; seps[8]="-"; want[8]="a--b";
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(); };
if (got.len > 0) { os.free(got.ptr: *void, got.len: u64); };
i += 1;
};
};
// ---- hasprefix --------------------------------------------------------
// ref/hare/strings/suffix.ha:18.
@@ -842,6 +898,7 @@ fn streq(a: str, b: str) bool = {
export fn main() i32 = {
signalled = 1; dup_cases();
signalled = 2; concat_cases();
signalled = 30; join_cases();
signalled = 3; hasprefix_cases();
signalled = 4; hassuffix_cases();
signalled = 5; contains_cases();