File: replace.ha

package info (click to toggle)
hare 0.25.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,948 kB
  • sloc: asm: 1,264; makefile: 123; sh: 114; lisp: 101
file content (86 lines) | stat: -rw-r--r-- 2,530 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// SPDX-License-Identifier: MPL-2.0
// (c) Hare authors <https://harelang.org>

use bytes;

// Returns a new string duplicated from 's', but with all instances of 'needle'
// replaced with 'target'. The caller must free the return value.
export fn replace(s: str, needle: str, target: str) (str | nomem) = {
	return multireplace(s, (needle, target));
};

// Performs a replacement in 's' of each tuple given by 'repls'. Replacement
// occurs in a single pass of 's', and works like in [[replace]], except that
// replacement pairs found earlier in 'repls' will take precedence over later
// ones. For example:
//
// 	assert(multireplace("hello there", ("e", "a"), ("a", "x"), ("ell", "eww")) == "hallo thara");
// 	assert(multireplace("hello there", ("ell", "eww"), ("e", "a")) == "hewwo thara");
//
// The caller must free the return value.
export fn multireplace(s: str, repls: (str, str)...) (str | nomem) = {
	let b = toutf8(s);
	let res: []u8 = [];
	let ok = false;
	defer if (!ok) free(res);
	let i = 0z;
	let prev = 0z; // end of previous match, so we can append in chunks
	for :step (i < len(b)) {
		for (let (replace, with) .. repls) {
			const replb = (toutf8(replace), toutf8(with));
			if (bytes::hasprefix(b[i..], replb.0)) {
				append(res, b[prev..i]...)?;
				append(res, replb.1...)?;
				i += len(replb.0);
				prev = i;
				continue :step;
			};
		};
		i += 1;
	};
	append(res, b[prev..i]...)?;
	ok = true;
	return fromutf8_unsafe(res);
};

@test fn replace() void = {
	const s = replace("Hello world!", "world", "there")!;
	defer free(s);
	assert(s == "Hello there!");

	const s = replace("I like dogs, dogs, birds, dogs", "dogs", "cats")!;
	defer free(s);
	assert(s == "I like cats, cats, birds, cats");

	const s = replace("aaaaaa", "aa", "a")!;
	defer free(s);
	assert(s == "aaa");

	const s = replace("aaa", "a", "aa")!;
	defer free(s);
	assert(s == "aaaaaa");

	const s = replace("こんにちは", "にち", "ばん")!;
	defer free(s);
	assert(s == "こんばんは");
};

@test fn multireplace() void = {
	const s = multireplace("Hello world",
		("Hello", "Greetings"), ("world", "globe"))!;
	defer free(s);
	assert(s == "Greetings globe");

	const s = multireplace("ababa", ("a", "ba"), ("b", "a"), ("a", "c"))!;
	defer free(s);
	assert(s == "baabaaba");

	const s = multireplace("hello there", ("e", "a"),
		("a", "x"), ("ell", "eww"))!;
	defer free(s);
	assert(s == "hallo thara");

	const s = multireplace("hello there", ("ell", "eww"), ("e", "a"))!;
	defer free(s);
	assert(s == "hewwo thara");
};