@shd101wyy/yo 0.1.26 → 0.1.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/skills/yo-async-effects/SKILL.md +4 -4
- package/.github/skills/yo-async-effects/async-effects-recipes.md +34 -34
- package/.github/skills/yo-core-patterns/SKILL.md +1 -1
- package/.github/skills/yo-core-patterns/core-patterns-cheatsheet.md +26 -26
- package/.github/skills/yo-project-workflow/SKILL.md +6 -3
- package/.github/skills/yo-project-workflow/workflow-cheatsheet.md +34 -11
- package/.github/skills/yo-syntax/SKILL.md +7 -6
- package/.github/skills/yo-syntax/syntax-cheatsheet.md +73 -60
- package/.github/skills/yo-wasm-integration/wasm-integration-cheatsheet.md +3 -3
- package/README.md +10 -8
- package/out/cjs/index.cjs +456 -438
- package/out/cjs/yo-cli.cjs +576 -543
- package/out/cjs/yo-lsp.cjs +559 -532
- package/out/esm/index.mjs +281 -263
- package/out/types/src/formatter.d.ts +11 -0
- package/out/types/src/lsp/formatting.d.ts +2 -0
- package/out/types/src/tests/formatter.test.d.ts +1 -0
- package/out/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/std/alg/hash.yo +13 -21
- package/std/allocator.yo +25 -40
- package/std/async.yo +3 -7
- package/std/build.yo +105 -151
- package/std/cli/arg_parser.yo +184 -169
- package/std/collections/array_list.yo +350 -314
- package/std/collections/btree_map.yo +142 -131
- package/std/collections/deque.yo +132 -128
- package/std/collections/hash_map.yo +542 -566
- package/std/collections/hash_set.yo +623 -687
- package/std/collections/linked_list.yo +275 -293
- package/std/collections/ordered_map.yo +113 -85
- package/std/collections/priority_queue.yo +73 -73
- package/std/crypto/md5.yo +191 -95
- package/std/crypto/random.yo +56 -64
- package/std/crypto/sha256.yo +151 -107
- package/std/encoding/base64.yo +87 -81
- package/std/encoding/hex.yo +43 -50
- package/std/encoding/html.yo +56 -81
- package/std/encoding/html_char_utils.yo +7 -13
- package/std/encoding/html_entities.yo +2248 -2253
- package/std/encoding/json.yo +316 -224
- package/std/encoding/punycode.yo +86 -116
- package/std/encoding/toml.yo +67 -66
- package/std/encoding/utf16.yo +37 -44
- package/std/env.yo +62 -91
- package/std/error.yo +7 -15
- package/std/fmt/display.yo +5 -9
- package/std/fmt/index.yo +8 -14
- package/std/fmt/to_string.yo +330 -315
- package/std/fmt/writer.yo +58 -87
- package/std/fs/dir.yo +83 -102
- package/std/fs/file.yo +147 -180
- package/std/fs/metadata.yo +45 -78
- package/std/fs/temp.yo +55 -65
- package/std/fs/types.yo +27 -40
- package/std/fs/walker.yo +53 -68
- package/std/gc.yo +5 -8
- package/std/glob.yo +30 -43
- package/std/http/client.yo +107 -120
- package/std/http/http.yo +106 -96
- package/std/http/index.yo +4 -6
- package/std/imm/list.yo +88 -93
- package/std/imm/map.yo +528 -464
- package/std/imm/set.yo +52 -57
- package/std/imm/sorted_map.yo +340 -286
- package/std/imm/sorted_set.yo +57 -63
- package/std/imm/string.yo +404 -345
- package/std/imm/vec.yo +173 -181
- package/std/io/reader.yo +3 -6
- package/std/io/writer.yo +4 -8
- package/std/libc/assert.yo +5 -9
- package/std/libc/ctype.yo +32 -22
- package/std/libc/dirent.yo +26 -25
- package/std/libc/errno.yo +164 -90
- package/std/libc/fcntl.yo +52 -45
- package/std/libc/float.yo +66 -44
- package/std/libc/limits.yo +42 -33
- package/std/libc/math.yo +53 -82
- package/std/libc/signal.yo +72 -47
- package/std/libc/stdatomic.yo +217 -188
- package/std/libc/stdint.yo +5 -29
- package/std/libc/stdio.yo +5 -29
- package/std/libc/stdlib.yo +32 -39
- package/std/libc/string.yo +5 -23
- package/std/libc/sys/stat.yo +58 -56
- package/std/libc/time.yo +5 -19
- package/std/libc/unistd.yo +5 -20
- package/std/libc/wctype.yo +6 -9
- package/std/libc/windows.yo +26 -30
- package/std/log.yo +41 -55
- package/std/net/addr.yo +102 -97
- package/std/net/dns.yo +27 -28
- package/std/net/errors.yo +50 -49
- package/std/net/tcp.yo +113 -124
- package/std/net/udp.yo +55 -66
- package/std/os/env.yo +35 -33
- package/std/os/signal.yo +15 -25
- package/std/path.yo +276 -311
- package/std/prelude.yo +6304 -4315
- package/std/process/command.yo +87 -103
- package/std/process/index.yo +12 -31
- package/std/regex/compiler.yo +196 -95
- package/std/regex/flags.yo +58 -39
- package/std/regex/index.yo +157 -173
- package/std/regex/match.yo +20 -31
- package/std/regex/node.yo +134 -152
- package/std/regex/parser.yo +283 -259
- package/std/regex/unicode.yo +172 -202
- package/std/regex/vm.yo +155 -171
- package/std/string/index.yo +5 -7
- package/std/string/rune.yo +45 -55
- package/std/string/string.yo +937 -964
- package/std/string/string_builder.yo +94 -104
- package/std/string/unicode.yo +46 -64
- package/std/sync/channel.yo +72 -73
- package/std/sync/cond.yo +31 -36
- package/std/sync/mutex.yo +30 -32
- package/std/sync/once.yo +13 -16
- package/std/sync/rwlock.yo +26 -31
- package/std/sync/waitgroup.yo +20 -25
- package/std/sys/advise.yo +16 -24
- package/std/sys/bufio/buf_reader.yo +77 -93
- package/std/sys/bufio/buf_writer.yo +52 -65
- package/std/sys/clock.yo +4 -9
- package/std/sys/constants.yo +77 -61
- package/std/sys/copy.yo +4 -10
- package/std/sys/dir.yo +26 -43
- package/std/sys/dns.yo +41 -61
- package/std/sys/errors.yo +95 -103
- package/std/sys/events.yo +45 -57
- package/std/sys/externs.yo +319 -267
- package/std/sys/fallocate.yo +7 -11
- package/std/sys/fcntl.yo +14 -22
- package/std/sys/file.yo +26 -40
- package/std/sys/future.yo +5 -8
- package/std/sys/iov.yo +12 -25
- package/std/sys/lock.yo +12 -13
- package/std/sys/mmap.yo +38 -43
- package/std/sys/path.yo +3 -8
- package/std/sys/perm.yo +7 -21
- package/std/sys/pipe.yo +5 -12
- package/std/sys/process.yo +23 -29
- package/std/sys/seek.yo +10 -12
- package/std/sys/signal.yo +7 -13
- package/std/sys/signals.yo +52 -35
- package/std/sys/socket.yo +63 -58
- package/std/sys/socketpair.yo +3 -6
- package/std/sys/sockinfo.yo +11 -20
- package/std/sys/statfs.yo +11 -34
- package/std/sys/statx.yo +25 -52
- package/std/sys/sysinfo.yo +15 -20
- package/std/sys/tcp.yo +62 -92
- package/std/sys/temp.yo +5 -9
- package/std/sys/time.yo +5 -15
- package/std/sys/timer.yo +6 -11
- package/std/sys/tty.yo +10 -18
- package/std/sys/udp.yo +22 -39
- package/std/sys/umask.yo +3 -6
- package/std/sys/unix.yo +33 -52
- package/std/testing/bench.yo +49 -52
- package/std/thread.yo +10 -15
- package/std/time/datetime.yo +105 -89
- package/std/time/duration.yo +43 -56
- package/std/time/instant.yo +13 -18
- package/std/time/sleep.yo +5 -9
- package/std/url/index.yo +184 -209
- package/std/worker.yo +6 -10
package/std/string/string.yo
CHANGED
|
@@ -1,123 +1,118 @@
|
|
|
1
1
|
//! Immutable UTF-8 string type with comprehensive operations.
|
|
2
|
-
|
|
3
|
-
{
|
|
4
|
-
{
|
|
5
|
-
{ memcpy, memcmp, strlen } :: import "../libc/string.yo";
|
|
6
|
-
|
|
2
|
+
{ ArrayList } :: import("../collections/array_list.yo");
|
|
3
|
+
{ rune } :: import("./rune.yo");
|
|
4
|
+
{ memcpy, memcmp, strlen } :: import("../libc/string.yo");
|
|
7
5
|
/// String operation error variants.
|
|
8
6
|
StringError :: enum(
|
|
9
7
|
/// The input bytes are not valid UTF-8.
|
|
10
8
|
InvalidUtf8,
|
|
11
9
|
/// The index is out of bounds for the string's byte length.
|
|
12
|
-
IndexOutOfBounds(index: usize, length: usize)
|
|
10
|
+
IndexOutOfBounds(index : usize, length : usize)
|
|
13
11
|
);
|
|
14
|
-
|
|
15
12
|
/// Immutable UTF-8 encoded string.
|
|
16
13
|
/// All operations return new strings; mutable operations require a pointer to self.
|
|
17
14
|
/// Empty strings use zero allocation (represented as `Option.None` internally).
|
|
18
15
|
String :: newtype(
|
|
19
|
-
_bytes: Option(ArrayList(u8))
|
|
16
|
+
_bytes : Option(ArrayList(u8))
|
|
20
17
|
);
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
impl(
|
|
19
|
+
String,
|
|
23
20
|
/**
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
* Create a new empty string (zero allocation)
|
|
22
|
+
*/
|
|
26
23
|
new : (fn() -> Self)(
|
|
27
|
-
Self(_bytes
|
|
24
|
+
Self(_bytes :.None)
|
|
28
25
|
),
|
|
29
|
-
|
|
30
26
|
/**
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
with_capacity : (fn(capacity: usize) -> Self)(
|
|
27
|
+
* Create a new empty string with pre-allocated capacity for the given number of bytes.
|
|
28
|
+
* The string starts empty but can hold `capacity` bytes without reallocating.
|
|
29
|
+
*/
|
|
30
|
+
with_capacity : (fn(capacity : usize) -> Self)(
|
|
35
31
|
cond(
|
|
36
|
-
(capacity == usize(0)) => Self(_bytes
|
|
37
|
-
true => Self(_bytes
|
|
32
|
+
(capacity == usize(0)) => Self(_bytes :.None),
|
|
33
|
+
true => Self(_bytes :.Some(ArrayList(u8).with_capacity(capacity)))
|
|
38
34
|
)
|
|
39
35
|
),
|
|
40
|
-
|
|
41
36
|
/**
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
from_bytes : (fn(bytes: ArrayList(u8)) -> Self)(
|
|
37
|
+
* Create a string from raw bytes (assumes valid UTF-8)
|
|
38
|
+
* No validation is performed - the caller must ensure bytes are valid UTF-8
|
|
39
|
+
*/
|
|
40
|
+
from_bytes : (fn(bytes : ArrayList(u8)) -> Self)(
|
|
46
41
|
cond(
|
|
47
|
-
(bytes.len() == usize(0)) => Self(_bytes
|
|
48
|
-
true => Self(_bytes
|
|
42
|
+
(bytes.len() == usize(0)) => Self(_bytes :.None),
|
|
43
|
+
true => Self(_bytes :.Some(bytes))
|
|
49
44
|
)
|
|
50
45
|
),
|
|
51
|
-
|
|
52
46
|
/**
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
from : (fn(slice: str) -> Self)({
|
|
47
|
+
* Create a string from a slice of bytes str
|
|
48
|
+
* The slice is a fat pointer containing data pointer and length
|
|
49
|
+
* Example: String.from(slice) where slice is str
|
|
50
|
+
*/
|
|
51
|
+
from : (fn(slice : str) -> Self)({
|
|
58
52
|
slen := slice.len();
|
|
59
|
-
if(
|
|
60
|
-
return
|
|
53
|
+
if(slen == usize(0), {
|
|
54
|
+
return(Self(_bytes :.None));
|
|
61
55
|
});
|
|
62
56
|
bytes := ArrayList(u8).with_capacity(slen);
|
|
63
57
|
bytes.extend_from_ptr(slice.ptr(), slen);
|
|
64
|
-
return
|
|
58
|
+
return(Self(_bytes :.Some(bytes)));
|
|
65
59
|
}),
|
|
66
|
-
|
|
67
60
|
/**
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
from_cstr : (fn(cstr: *(u8)) -> Result(Self, StringError))({
|
|
61
|
+
* Create a string from a C null-terminated string *(u8)
|
|
62
|
+
* Copies bytes until it finds a null byte (0)
|
|
63
|
+
* Example: String.from_cstr(c_string_ptr)
|
|
64
|
+
*
|
|
65
|
+
* Note: We manually search for the null terminator by iterating
|
|
66
|
+
*/
|
|
67
|
+
from_cstr : (fn(cstr : *(u8)) -> Result(Self, StringError))({
|
|
75
68
|
len := strlen((*(char))(cstr));
|
|
76
|
-
if(
|
|
77
|
-
return
|
|
69
|
+
if(len == usize(0), {
|
|
70
|
+
return(.Ok(Self(_bytes :.None)));
|
|
78
71
|
});
|
|
79
72
|
bytes := ArrayList(u8).with_capacity(len);
|
|
80
73
|
bytes.extend_from_ptr(cstr, len);
|
|
81
|
-
return
|
|
74
|
+
return(.Ok(Self(_bytes :.Some(bytes))));
|
|
82
75
|
}),
|
|
83
|
-
|
|
84
76
|
/// Convert the string to a null-terminated C string.
|
|
85
77
|
/// Returns an `ArrayList(u8)` with the string bytes followed by a `\0`.
|
|
86
|
-
to_cstr : (fn(self: Self) -> ArrayList(u8))({
|
|
87
|
-
(bytes_len : usize) = match(
|
|
78
|
+
to_cstr : (fn(self : Self) -> ArrayList(u8))({
|
|
79
|
+
(bytes_len : usize) = match(
|
|
80
|
+
self._bytes,
|
|
88
81
|
.Some(b) => b.len(),
|
|
89
82
|
.None => usize(0)
|
|
90
83
|
);
|
|
91
|
-
bytes_with_null := ArrayList(u8).with_capacity(
|
|
92
|
-
match(
|
|
93
|
-
.
|
|
84
|
+
bytes_with_null := ArrayList(u8).with_capacity(bytes_len + usize(1));
|
|
85
|
+
match(
|
|
86
|
+
self._bytes,
|
|
87
|
+
.Some(b) => match(
|
|
88
|
+
b.ptr(),
|
|
94
89
|
.Some(src) => bytes_with_null.extend_from_ptr(src, bytes_len),
|
|
95
90
|
.None => ()
|
|
96
91
|
),
|
|
97
92
|
.None => ()
|
|
98
93
|
);
|
|
99
94
|
bytes_with_null.push(u8(0));
|
|
100
|
-
return
|
|
95
|
+
return(bytes_with_null);
|
|
101
96
|
}),
|
|
102
|
-
|
|
103
97
|
/**
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
len : (fn(self: Self) -> usize)({
|
|
98
|
+
* Get the length of the string in Unicode characters (runes)
|
|
99
|
+
* For "你好世界", this returns 4
|
|
100
|
+
*/
|
|
101
|
+
len : (fn(self : Self) -> usize)({
|
|
108
102
|
(inner : Option(ArrayList(u8))) = self._bytes;
|
|
109
|
-
match(
|
|
110
|
-
|
|
103
|
+
match(
|
|
104
|
+
inner,
|
|
105
|
+
.None => {
|
|
106
|
+
return(usize(0));
|
|
107
|
+
},
|
|
111
108
|
.Some(al) => {
|
|
112
109
|
count := usize(0);
|
|
113
110
|
byte_index := usize(0);
|
|
114
111
|
total_bytes := al.len();
|
|
115
|
-
|
|
116
|
-
while ((byte_index < total_bytes)),
|
|
117
|
-
(byte_index = (byte_index + usize(1))),
|
|
118
|
-
{
|
|
112
|
+
while(byte_index < total_bytes, byte_index = (byte_index + usize(1)), {
|
|
119
113
|
byte_opt := al.get(byte_index);
|
|
120
|
-
match(
|
|
114
|
+
match(
|
|
115
|
+
byte_opt,
|
|
121
116
|
.Some(byte) => {
|
|
122
117
|
cond(
|
|
123
118
|
((byte < u8(0x80)) || (byte >= u8(0xC0))) => {
|
|
@@ -128,63 +123,65 @@ impl(String,
|
|
|
128
123
|
},
|
|
129
124
|
.None => ()
|
|
130
125
|
);
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
return count;
|
|
126
|
+
});
|
|
127
|
+
return(count);
|
|
134
128
|
}
|
|
135
129
|
)
|
|
136
130
|
}),
|
|
137
|
-
|
|
138
131
|
/**
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
is_empty : (fn(self: Self) -> bool)(
|
|
142
|
-
match(
|
|
132
|
+
* Check if the string is empty
|
|
133
|
+
*/
|
|
134
|
+
is_empty : (fn(self : Self) -> bool)(
|
|
135
|
+
match(
|
|
136
|
+
self._bytes,
|
|
143
137
|
.None => true,
|
|
144
138
|
.Some(al) => (al.len() == usize(0))
|
|
145
139
|
)
|
|
146
140
|
),
|
|
147
|
-
|
|
148
141
|
/**
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
as_bytes : (fn(self: Self) -> ArrayList(u8))(
|
|
153
|
-
match(
|
|
142
|
+
* Get a reference to the internal byte array
|
|
143
|
+
* Returns an empty ArrayList if the string is empty
|
|
144
|
+
*/
|
|
145
|
+
as_bytes : (fn(self : Self) -> ArrayList(u8))(
|
|
146
|
+
match(
|
|
147
|
+
self._bytes,
|
|
154
148
|
.None => ArrayList(u8).new(),
|
|
155
149
|
.Some(al) => al
|
|
156
150
|
)
|
|
157
151
|
),
|
|
158
|
-
|
|
159
152
|
// as_bytes : (fn(self: Self) -> ArrayList(u8))({
|
|
160
153
|
// return self._bytes;
|
|
161
154
|
// }),
|
|
162
|
-
|
|
163
155
|
/**
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
as_str : (fn(self: Self) -> str)(
|
|
168
|
-
match(
|
|
156
|
+
* Get a str view into the string's UTF-8 bytes
|
|
157
|
+
* Returns a fat pointer (pointer + length) without copying
|
|
158
|
+
*/
|
|
159
|
+
as_str : (fn(self : Self) -> str)(
|
|
160
|
+
match(
|
|
161
|
+
self._bytes,
|
|
169
162
|
.None => str.from_raw_parts(*(u8)(""), usize(0)),
|
|
170
|
-
.Some(al) => match(
|
|
163
|
+
.Some(al) => match(
|
|
164
|
+
al._ptr,
|
|
171
165
|
.Some(p) => str.from_raw_parts(p, al._length),
|
|
172
166
|
.None => str.from_raw_parts(*(u8)(""), usize(0))
|
|
173
167
|
)
|
|
174
168
|
)
|
|
175
169
|
),
|
|
176
|
-
|
|
177
170
|
/**
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
_decode_rune_at : (fn(self: Self, byte_index: usize) -> Option(rune))({
|
|
171
|
+
* Decode a UTF-8 encoded rune starting at the given byte index
|
|
172
|
+
* Internal helper method
|
|
173
|
+
*/
|
|
174
|
+
_decode_rune_at : (fn(self : Self, byte_index : usize) -> Option(rune))({
|
|
182
175
|
(al : Option(ArrayList(u8))) = self._bytes;
|
|
183
|
-
match(
|
|
184
|
-
|
|
176
|
+
match(
|
|
177
|
+
al,
|
|
178
|
+
.None => {
|
|
179
|
+
return(.None);
|
|
180
|
+
},
|
|
185
181
|
.Some(bytes) => {
|
|
186
182
|
first_byte_opt := bytes.get(byte_index);
|
|
187
|
-
match(
|
|
183
|
+
match(
|
|
184
|
+
first_byte_opt,
|
|
188
185
|
.Some(first_byte) => {
|
|
189
186
|
(res : Option(rune)) = cond(
|
|
190
187
|
(first_byte < u8(0x80)) => {
|
|
@@ -192,85 +189,91 @@ impl(String,
|
|
|
192
189
|
rune.from_u32(codepoint)
|
|
193
190
|
},
|
|
194
191
|
((first_byte >= u8(0xC0)) && (first_byte < u8(0xE0))) => {
|
|
195
|
-
second_opt := bytes.get(
|
|
196
|
-
match(
|
|
192
|
+
second_opt := bytes.get(byte_index + usize(1));
|
|
193
|
+
match(
|
|
194
|
+
second_opt,
|
|
197
195
|
.Some(second) => {
|
|
198
196
|
codepoint := (((u32(first_byte) & u32(0x1F)) << u32(6)) | (u32(second) & u32(0x3F)));
|
|
199
197
|
rune.from_u32(codepoint)
|
|
200
198
|
},
|
|
201
|
-
.None
|
|
199
|
+
.None =>.None
|
|
202
200
|
)
|
|
203
201
|
},
|
|
204
202
|
((first_byte >= u8(0xE0)) && (first_byte < u8(0xF0))) => {
|
|
205
|
-
second_opt := bytes.get(
|
|
206
|
-
third_opt := bytes.get(
|
|
207
|
-
match(
|
|
203
|
+
second_opt := bytes.get(byte_index + usize(1));
|
|
204
|
+
third_opt := bytes.get(byte_index + usize(2));
|
|
205
|
+
match(
|
|
206
|
+
second_opt,
|
|
208
207
|
.Some(second) =>
|
|
209
|
-
match(
|
|
208
|
+
match(
|
|
209
|
+
third_opt,
|
|
210
210
|
.Some(third) => {
|
|
211
211
|
codepoint := ((((u32(first_byte) & u32(0x0F)) << u32(12)) | ((u32(second) & u32(0x3F)) << u32(6))) | (u32(third) & u32(0x3F)));
|
|
212
212
|
rune.from_u32(codepoint)
|
|
213
213
|
},
|
|
214
|
-
.None
|
|
214
|
+
.None =>.None
|
|
215
215
|
),
|
|
216
|
-
.None
|
|
216
|
+
.None =>.None
|
|
217
217
|
)
|
|
218
218
|
},
|
|
219
219
|
((first_byte >= u8(0xF0)) && (first_byte < u8(0xF8))) => {
|
|
220
|
-
second_opt := bytes.get(
|
|
221
|
-
third_opt := bytes.get(
|
|
222
|
-
fourth_opt := bytes.get(
|
|
223
|
-
match(
|
|
220
|
+
second_opt := bytes.get(byte_index + usize(1));
|
|
221
|
+
third_opt := bytes.get(byte_index + usize(2));
|
|
222
|
+
fourth_opt := bytes.get(byte_index + usize(3));
|
|
223
|
+
match(
|
|
224
|
+
second_opt,
|
|
224
225
|
.Some(second) =>
|
|
225
|
-
match(
|
|
226
|
+
match(
|
|
227
|
+
third_opt,
|
|
226
228
|
.Some(third) =>
|
|
227
|
-
match(
|
|
229
|
+
match(
|
|
230
|
+
fourth_opt,
|
|
228
231
|
.Some(fourth) => {
|
|
229
232
|
codepoint := (((((u32(first_byte) & u32(0x07)) << u32(18)) | ((u32(second) & u32(0x3F)) << u32(12))) | ((u32(third) & u32(0x3F)) << u32(6))) | (u32(fourth) & u32(0x3F)));
|
|
230
233
|
rune.from_u32(codepoint)
|
|
231
234
|
},
|
|
232
|
-
.None
|
|
235
|
+
.None =>.None
|
|
233
236
|
),
|
|
234
|
-
.None
|
|
237
|
+
.None =>.None
|
|
235
238
|
),
|
|
236
|
-
.None
|
|
239
|
+
.None =>.None
|
|
237
240
|
)
|
|
238
241
|
},
|
|
239
|
-
true
|
|
242
|
+
true =>.None
|
|
240
243
|
);
|
|
241
|
-
return
|
|
244
|
+
return(res);
|
|
242
245
|
},
|
|
243
|
-
.None
|
|
246
|
+
.None =>.None
|
|
244
247
|
)
|
|
245
248
|
}
|
|
246
249
|
)
|
|
247
250
|
}),
|
|
248
|
-
|
|
249
251
|
/**
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
at : (fn(self: Self, index: usize) -> Option(rune))(
|
|
255
|
-
match(
|
|
256
|
-
.
|
|
252
|
+
* Get the rune at the specified character index (0-based)
|
|
253
|
+
* Returns None if index is out of bounds
|
|
254
|
+
* For "你好世界", at(0) returns '你', at(1) returns '好', etc.
|
|
255
|
+
*/
|
|
256
|
+
at : (fn(self : Self, index : usize) -> Option(rune))(
|
|
257
|
+
match(
|
|
258
|
+
self._bytes,
|
|
259
|
+
.None => {
|
|
260
|
+
return(.None);
|
|
261
|
+
},
|
|
257
262
|
.Some(al) => {
|
|
258
263
|
char_count := usize(0);
|
|
259
264
|
byte_index := usize(0);
|
|
260
265
|
total_bytes := al.len();
|
|
261
|
-
|
|
262
|
-
while ((byte_index < total_bytes)),
|
|
263
|
-
(byte_index = (byte_index + usize(1))),
|
|
264
|
-
{
|
|
266
|
+
while(byte_index < total_bytes, byte_index = (byte_index + usize(1)), {
|
|
265
267
|
byte_opt := al.get(byte_index);
|
|
266
|
-
match(
|
|
268
|
+
match(
|
|
269
|
+
byte_opt,
|
|
267
270
|
.Some(byte) => {
|
|
268
271
|
is_start := ((byte < u8(0x80)) || (byte >= u8(0xC0)));
|
|
269
272
|
cond(
|
|
270
273
|
is_start => {
|
|
271
274
|
cond(
|
|
272
275
|
(char_count == index) => {
|
|
273
|
-
return
|
|
276
|
+
return(Self._decode_rune_at(self, byte_index));
|
|
274
277
|
},
|
|
275
278
|
true => {
|
|
276
279
|
char_count = (char_count + usize(1));
|
|
@@ -282,55 +285,56 @@ impl(String,
|
|
|
282
285
|
},
|
|
283
286
|
.None => ()
|
|
284
287
|
);
|
|
285
|
-
};
|
|
286
|
-
|
|
287
|
-
return .None;
|
|
288
|
+
});
|
|
289
|
+
return(.None);
|
|
288
290
|
}
|
|
289
291
|
)
|
|
290
292
|
),
|
|
291
|
-
|
|
292
293
|
/**
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
concat : (fn(self: Self, other: Self) -> Self)({
|
|
296
|
-
(self_len : usize) = match(self._bytes
|
|
297
|
-
(other_len : usize) = match(other._bytes
|
|
298
|
-
if((
|
|
299
|
-
return
|
|
294
|
+
* Concatenate two strings (like JavaScript +)
|
|
295
|
+
*/
|
|
296
|
+
concat : (fn(self : Self, other : Self) -> Self)({
|
|
297
|
+
(self_len : usize) = match(self._bytes,.Some(b) => b.len(),.None => usize(0));
|
|
298
|
+
(other_len : usize) = match(other._bytes,.Some(b) => b.len(),.None => usize(0));
|
|
299
|
+
if((self_len == usize(0)) && (other_len == usize(0)), {
|
|
300
|
+
return(Self(_bytes :.None));
|
|
300
301
|
});
|
|
301
|
-
new_bytes := ArrayList(u8).with_capacity(
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
.Some(b) => match(
|
|
302
|
+
new_bytes := ArrayList(u8).with_capacity(self_len + other_len);
|
|
303
|
+
match(
|
|
304
|
+
self._bytes,
|
|
305
|
+
.Some(b) => match(
|
|
306
|
+
b.ptr(),
|
|
305
307
|
.Some(p) => new_bytes.extend_from_ptr(p, self_len),
|
|
306
308
|
.None => ()
|
|
307
309
|
),
|
|
308
310
|
.None => ()
|
|
309
311
|
);
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
.Some(b) => match(
|
|
312
|
+
match(
|
|
313
|
+
other._bytes,
|
|
314
|
+
.Some(b) => match(
|
|
315
|
+
b.ptr(),
|
|
313
316
|
.Some(p) => new_bytes.extend_from_ptr(p, other_len),
|
|
314
317
|
.None => ()
|
|
315
318
|
),
|
|
316
319
|
.None => ()
|
|
317
320
|
);
|
|
318
|
-
|
|
319
|
-
return Self(_bytes: .Some(new_bytes));
|
|
321
|
+
return(Self(_bytes :.Some(new_bytes)));
|
|
320
322
|
}),
|
|
321
|
-
|
|
322
323
|
/// Append another `String` to this string in-place (mutates `self`).
|
|
323
|
-
push_string : (fn(self: *(Self), other: Self) -> unit)({
|
|
324
|
-
(olen : usize) = match(other._bytes
|
|
325
|
-
if(
|
|
326
|
-
match(
|
|
327
|
-
.
|
|
324
|
+
push_string : (fn(self : *(Self), other : Self) -> unit)({
|
|
325
|
+
(olen : usize) = match(other._bytes,.Some(b) => b.len(),.None => usize(0));
|
|
326
|
+
if(olen > usize(0), {
|
|
327
|
+
match(
|
|
328
|
+
other._bytes,
|
|
329
|
+
.Some(ob) => match(
|
|
330
|
+
ob.ptr(),
|
|
328
331
|
.Some(p) => {
|
|
329
|
-
match(
|
|
332
|
+
match(
|
|
333
|
+
self.*._bytes,
|
|
330
334
|
.None => {
|
|
331
335
|
(new_al : ArrayList(u8)) = ArrayList(u8).with_capacity(olen);
|
|
332
336
|
new_al.extend_from_ptr(p, olen);
|
|
333
|
-
self.*._bytes
|
|
337
|
+
self.*._bytes =.Some(new_al);
|
|
334
338
|
},
|
|
335
339
|
.Some(al) => {
|
|
336
340
|
al.extend_from_ptr(p, olen);
|
|
@@ -343,16 +347,16 @@ impl(String,
|
|
|
343
347
|
);
|
|
344
348
|
});
|
|
345
349
|
}),
|
|
346
|
-
|
|
347
350
|
/// Append a `str` slice to this string in-place (mutates `self`).
|
|
348
|
-
push_str : (fn(self: *(Self), s: str) -> unit)({
|
|
351
|
+
push_str : (fn(self : *(Self), s : str) -> unit)({
|
|
349
352
|
(slen : usize) = s.len();
|
|
350
|
-
if(
|
|
351
|
-
match(
|
|
353
|
+
if(slen > usize(0), {
|
|
354
|
+
match(
|
|
355
|
+
self.*._bytes,
|
|
352
356
|
.None => {
|
|
353
357
|
(new_al : ArrayList(u8)) = ArrayList(u8).with_capacity(slen);
|
|
354
358
|
new_al.extend_from_ptr(s.ptr(), slen);
|
|
355
|
-
self.*._bytes
|
|
359
|
+
self.*._bytes =.Some(new_al);
|
|
356
360
|
},
|
|
357
361
|
.Some(al) => {
|
|
358
362
|
al.extend_from_ptr(s.ptr(), slen);
|
|
@@ -360,88 +364,89 @@ impl(String,
|
|
|
360
364
|
);
|
|
361
365
|
});
|
|
362
366
|
}),
|
|
363
|
-
|
|
364
367
|
/// Append a single byte to this string in-place (mutates `self`).
|
|
365
368
|
/// The caller must ensure the byte maintains valid UTF-8.
|
|
366
|
-
push_byte : (fn(self: *(Self), b: u8) -> unit)({
|
|
367
|
-
match(
|
|
369
|
+
push_byte : (fn(self : *(Self), b : u8) -> unit)({
|
|
370
|
+
match(
|
|
371
|
+
self.*._bytes,
|
|
368
372
|
.None => {
|
|
369
373
|
(new_al : ArrayList(u8)) = ArrayList(u8).with_capacity(usize(8));
|
|
370
374
|
new_al.push(b);
|
|
371
|
-
self.*._bytes
|
|
375
|
+
self.*._bytes =.Some(new_al);
|
|
372
376
|
},
|
|
373
377
|
.Some(al) => {
|
|
374
378
|
al.push(b);
|
|
375
379
|
}
|
|
376
380
|
);
|
|
377
381
|
}),
|
|
378
|
-
|
|
379
382
|
/// Reserve capacity for at least `additional` more bytes.
|
|
380
383
|
/// If the string is empty, creates a new buffer with the given capacity.
|
|
381
|
-
reserve : (fn(self: *(Self), additional: usize) -> unit)({
|
|
382
|
-
match(
|
|
384
|
+
reserve : (fn(self : *(Self), additional : usize) -> unit)({
|
|
385
|
+
match(
|
|
386
|
+
self.*._bytes,
|
|
383
387
|
.None => {
|
|
384
|
-
self.*._bytes
|
|
388
|
+
self.*._bytes =.Some(ArrayList(u8).with_capacity(additional));
|
|
385
389
|
},
|
|
386
390
|
.Some(al) => {
|
|
387
391
|
(current_len : usize) = al.len();
|
|
388
|
-
al.ensure_total_capacity(
|
|
392
|
+
al.ensure_total_capacity(current_len + additional);
|
|
389
393
|
}
|
|
390
394
|
);
|
|
391
395
|
}),
|
|
392
|
-
|
|
393
396
|
/// Clear the string content but keep the allocated buffer for reuse.
|
|
394
|
-
clear : (fn(self: *(Self)) -> unit)(
|
|
395
|
-
match(
|
|
397
|
+
clear : (fn(self : *(Self)) -> unit)(
|
|
398
|
+
match(
|
|
399
|
+
self.*._bytes,
|
|
396
400
|
.Some(al) => al.clear(),
|
|
397
401
|
.None => ()
|
|
398
402
|
)
|
|
399
403
|
),
|
|
400
|
-
|
|
401
404
|
/// Create a deep copy of this string with its own buffer.
|
|
402
|
-
clone : (fn(self: Self) -> Self)(
|
|
403
|
-
match(
|
|
404
|
-
.
|
|
405
|
+
clone : (fn(self : Self) -> Self)(
|
|
406
|
+
match(
|
|
407
|
+
self._bytes,
|
|
408
|
+
.None => Self(_bytes :.None),
|
|
405
409
|
.Some(al) => {
|
|
406
410
|
(new_al : ArrayList(u8)) = ArrayList(u8).with_capacity(al.len());
|
|
407
|
-
match(
|
|
411
|
+
match(
|
|
412
|
+
al.ptr(),
|
|
408
413
|
.Some(p) => new_al.extend_from_ptr(p, al.len()),
|
|
409
414
|
.None => ()
|
|
410
415
|
);
|
|
411
|
-
Self(_bytes
|
|
416
|
+
Self(_bytes :.Some(new_al))
|
|
412
417
|
}
|
|
413
418
|
)
|
|
414
419
|
),
|
|
415
|
-
|
|
416
420
|
/// Get the number of bytes in the string (not Unicode characters).
|
|
417
421
|
/// Use `len()` for Unicode character count.
|
|
418
|
-
bytes_len : (fn(self: Self) -> usize)(
|
|
419
|
-
match(self._bytes
|
|
422
|
+
bytes_len : (fn(self : Self) -> usize)(
|
|
423
|
+
match(self._bytes,.Some(b) => b.len(),.None => usize(0))
|
|
420
424
|
),
|
|
421
|
-
|
|
422
425
|
/**
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
substring : (fn(self: Self, start: usize, end: usize) -> Self)(
|
|
428
|
-
match(
|
|
429
|
-
.
|
|
426
|
+
* Extract a substring from start to end character indices (like JavaScript substring)
|
|
427
|
+
* Returns a new string containing characters from start (inclusive) to end (exclusive)
|
|
428
|
+
* If end is greater than length, it will substring to the end of the string
|
|
429
|
+
*/
|
|
430
|
+
substring : (fn(self : Self, start : usize, end : usize) -> Self)(
|
|
431
|
+
match(
|
|
432
|
+
self._bytes,
|
|
433
|
+
.None => {
|
|
434
|
+
return(Self(_bytes :.None));
|
|
435
|
+
},
|
|
430
436
|
.Some(al) => {
|
|
431
437
|
total_bytes := al.len();
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
return Self(_bytes: .None);
|
|
438
|
+
if(start >= end, {
|
|
439
|
+
return(Self(_bytes :.None));
|
|
435
440
|
});
|
|
436
|
-
|
|
437
441
|
(char_index : usize) = usize(0);
|
|
438
442
|
(byte_index : usize) = usize(0);
|
|
439
443
|
(start_byte : usize) = total_bytes;
|
|
440
444
|
(end_byte : usize) = total_bytes;
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
+
while(byte_index < total_bytes, {
|
|
446
|
+
if(char_index == start, {
|
|
447
|
+
start_byte = byte_index;
|
|
448
|
+
});
|
|
449
|
+
if(char_index == end, {
|
|
445
450
|
end_byte = byte_index;
|
|
446
451
|
break;
|
|
447
452
|
});
|
|
@@ -454,52 +459,53 @@ impl(String,
|
|
|
454
459
|
);
|
|
455
460
|
byte_index = (byte_index + byte_len);
|
|
456
461
|
char_index = (char_index + usize(1));
|
|
457
|
-
};
|
|
458
|
-
if((char_index == start), { start_byte = byte_index; });
|
|
459
|
-
if((char_index == end), { end_byte = byte_index; });
|
|
460
|
-
|
|
461
|
-
if((start_byte >= end_byte), {
|
|
462
|
-
return Self(_bytes: .None);
|
|
463
462
|
});
|
|
464
|
-
|
|
463
|
+
if(char_index == start, {
|
|
464
|
+
start_byte = byte_index;
|
|
465
|
+
});
|
|
466
|
+
if(char_index == end, {
|
|
467
|
+
end_byte = byte_index;
|
|
468
|
+
});
|
|
469
|
+
if(start_byte >= end_byte, {
|
|
470
|
+
return(Self(_bytes :.None));
|
|
471
|
+
});
|
|
465
472
|
(count : usize) = (end_byte - start_byte);
|
|
466
473
|
new_bytes := ArrayList(u8).with_capacity(count);
|
|
467
|
-
match(
|
|
468
|
-
.
|
|
474
|
+
match(
|
|
475
|
+
al.ptr(),
|
|
476
|
+
.Some(src_p) => new_bytes.extend_from_ptr(src_p &+ start_byte, count),
|
|
469
477
|
.None => ()
|
|
470
478
|
);
|
|
471
|
-
Self(_bytes
|
|
479
|
+
Self(_bytes :.Some(new_bytes))
|
|
472
480
|
}
|
|
473
481
|
)
|
|
474
482
|
),
|
|
475
|
-
|
|
476
483
|
/**
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
index_of : (fn(self: Self, substr: Self, (from_index : usize) ?= 0) -> Option(usize))(
|
|
484
|
+
* Find the first occurrence of a substring (like JavaScript indexOf)
|
|
485
|
+
* Returns the character index of the first match, or None if not found
|
|
486
|
+
* from_index specifies the character index to start searching from
|
|
487
|
+
*/
|
|
488
|
+
index_of : (fn(self : Self, substr : Self, (from_index : usize) ?= 0) -> Option(usize))(
|
|
482
489
|
cond(
|
|
483
|
-
substr.is_empty()
|
|
490
|
+
substr.is_empty() =>.Some(from_index),
|
|
484
491
|
true => {
|
|
485
|
-
(self_bytes : usize) = match(self._bytes
|
|
486
|
-
(sub_bytes : usize) = match(substr._bytes
|
|
487
|
-
|
|
492
|
+
(self_bytes : usize) = match(self._bytes,.Some(b) => b.len(),.None => usize(0));
|
|
493
|
+
(sub_bytes : usize) = match(substr._bytes,.Some(b) => b.len(),.None => usize(0));
|
|
488
494
|
cond(
|
|
489
|
-
(sub_bytes > self_bytes)
|
|
490
|
-
true => match(
|
|
491
|
-
.
|
|
492
|
-
.
|
|
493
|
-
|
|
495
|
+
(sub_bytes > self_bytes) =>.None,
|
|
496
|
+
true => match(
|
|
497
|
+
self._bytes,
|
|
498
|
+
.None =>.None,
|
|
499
|
+
.Some(self_al) => match(
|
|
500
|
+
substr._bytes,
|
|
501
|
+
.None =>.None,
|
|
494
502
|
.Some(sub_al) => {
|
|
495
503
|
char_index := usize(0);
|
|
496
504
|
byte_index := usize(0);
|
|
497
|
-
|
|
498
|
-
while (((byte_index < self_bytes) && (char_index < from_index))),
|
|
499
|
-
(byte_index = (byte_index + usize(1))),
|
|
500
|
-
{
|
|
505
|
+
while((byte_index < self_bytes) && (char_index < from_index), byte_index = (byte_index + usize(1)), {
|
|
501
506
|
first_byte_opt := self_al.get(byte_index);
|
|
502
|
-
match(
|
|
507
|
+
match(
|
|
508
|
+
first_byte_opt,
|
|
503
509
|
.Some(first_byte) => {
|
|
504
510
|
is_start := ((first_byte < u8(0x80)) || (first_byte >= u8(0xC0)));
|
|
505
511
|
cond(
|
|
@@ -511,21 +517,18 @@ impl(String,
|
|
|
511
517
|
},
|
|
512
518
|
.None => ()
|
|
513
519
|
);
|
|
514
|
-
};
|
|
515
|
-
|
|
516
|
-
while ((byte_index <= (self_bytes - sub_bytes))),
|
|
517
|
-
(byte_index = (byte_index + usize(1))),
|
|
518
|
-
{
|
|
520
|
+
});
|
|
521
|
+
while(byte_index <= (self_bytes - sub_bytes), byte_index = (byte_index + usize(1)), {
|
|
519
522
|
matches := true;
|
|
520
523
|
j := usize(0);
|
|
521
|
-
while
|
|
522
|
-
|
|
523
|
-
{
|
|
524
|
-
self_byte_opt := self_al.get((byte_index + j));
|
|
524
|
+
while(j < sub_bytes, j = (j + usize(1)), {
|
|
525
|
+
self_byte_opt := self_al.get(byte_index + j);
|
|
525
526
|
sub_byte_opt := sub_al.get(j);
|
|
526
|
-
match(
|
|
527
|
+
match(
|
|
528
|
+
self_byte_opt,
|
|
527
529
|
.Some(self_byte) =>
|
|
528
|
-
match(
|
|
530
|
+
match(
|
|
531
|
+
sub_byte_opt,
|
|
529
532
|
.Some(sub_byte) => {
|
|
530
533
|
cond(
|
|
531
534
|
(self_byte != sub_byte) => {
|
|
@@ -545,17 +548,16 @@ impl(String,
|
|
|
545
548
|
break;
|
|
546
549
|
}
|
|
547
550
|
);
|
|
548
|
-
};
|
|
549
|
-
|
|
551
|
+
});
|
|
550
552
|
cond(
|
|
551
553
|
matches => {
|
|
552
|
-
return
|
|
554
|
+
return(.Some(char_index));
|
|
553
555
|
},
|
|
554
556
|
true => ()
|
|
555
557
|
);
|
|
556
|
-
|
|
557
558
|
first_byte_opt := self_al.get(byte_index);
|
|
558
|
-
match(
|
|
559
|
+
match(
|
|
560
|
+
first_byte_opt,
|
|
559
561
|
.Some(first_byte) => {
|
|
560
562
|
is_start := ((first_byte < u8(0x80)) || (first_byte >= u8(0xC0)));
|
|
561
563
|
cond(
|
|
@@ -567,9 +569,8 @@ impl(String,
|
|
|
567
569
|
},
|
|
568
570
|
.None => ()
|
|
569
571
|
);
|
|
570
|
-
};
|
|
571
|
-
|
|
572
|
-
return .None;
|
|
572
|
+
});
|
|
573
|
+
return(.None);
|
|
573
574
|
}
|
|
574
575
|
)
|
|
575
576
|
)
|
|
@@ -577,72 +578,66 @@ impl(String,
|
|
|
577
578
|
}
|
|
578
579
|
)
|
|
579
580
|
),
|
|
580
|
-
|
|
581
581
|
/**
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
contains : (fn(self: Self, substr: Self, (from_index : usize) ?= 0) -> bool)(
|
|
585
|
-
match(
|
|
582
|
+
* Check if the string contains a substring
|
|
583
|
+
*/
|
|
584
|
+
contains : (fn(self : Self, substr : Self, (from_index : usize) ?= 0) -> bool)(
|
|
585
|
+
match(
|
|
586
|
+
self.index_of(substr, from_index),
|
|
586
587
|
.Some(_) => true,
|
|
587
588
|
.None => false
|
|
588
589
|
)
|
|
589
590
|
),
|
|
590
|
-
|
|
591
591
|
/**
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
split : (fn(self: Self, separator: Self) -> ArrayList(Self))({
|
|
592
|
+
* Split the string by a separator into an array of strings (like JavaScript split)
|
|
593
|
+
* Returns an ArrayList of String
|
|
594
|
+
*/
|
|
595
|
+
split : (fn(self : Self, separator : Self) -> ArrayList(Self))({
|
|
596
596
|
result := ArrayList(Self).new();
|
|
597
|
-
|
|
598
597
|
cond(
|
|
599
598
|
separator.is_empty() => {
|
|
600
599
|
char_count := self.len();
|
|
601
600
|
i := usize(0);
|
|
602
|
-
while
|
|
603
|
-
|
|
604
|
-
{
|
|
605
|
-
char_str := self.substring(i, (i + usize(1)));
|
|
601
|
+
while(i < char_count, i = (i + usize(1)), {
|
|
602
|
+
char_str := self.substring(i, i + usize(1));
|
|
606
603
|
result.push(char_str);
|
|
607
|
-
};
|
|
608
|
-
return
|
|
604
|
+
});
|
|
605
|
+
return(result);
|
|
609
606
|
},
|
|
610
607
|
self.is_empty() => {
|
|
611
608
|
result.push(new());
|
|
612
|
-
return
|
|
609
|
+
return(result);
|
|
613
610
|
},
|
|
614
|
-
true => match(
|
|
611
|
+
true => match(
|
|
612
|
+
self._bytes,
|
|
615
613
|
.None => {
|
|
616
614
|
result.push(new());
|
|
617
|
-
return
|
|
615
|
+
return(result);
|
|
618
616
|
},
|
|
619
|
-
.Some(self_al) => match(
|
|
617
|
+
.Some(self_al) => match(
|
|
618
|
+
separator._bytes,
|
|
620
619
|
.None => {
|
|
621
620
|
result.push(self);
|
|
622
|
-
return
|
|
621
|
+
return(result);
|
|
623
622
|
},
|
|
624
623
|
.Some(sep_al) => {
|
|
625
624
|
self_bytes := self_al.len();
|
|
626
625
|
sep_bytes := sep_al.len();
|
|
627
|
-
|
|
628
626
|
current_bytes := ArrayList(u8).new();
|
|
629
627
|
byte_index := usize(0);
|
|
630
|
-
|
|
631
|
-
while ((byte_index < self_bytes)),
|
|
632
|
-
(byte_index = (byte_index + usize(1))),
|
|
633
|
-
{
|
|
628
|
+
while(byte_index < self_bytes, byte_index = (byte_index + usize(1)), {
|
|
634
629
|
matches := true;
|
|
635
630
|
cond(
|
|
636
631
|
((byte_index + sep_bytes) <= self_bytes) => {
|
|
637
632
|
j := usize(0);
|
|
638
|
-
while
|
|
639
|
-
|
|
640
|
-
{
|
|
641
|
-
self_byte_opt := self_al.get((byte_index + j));
|
|
633
|
+
while(j < sep_bytes, j = (j + usize(1)), {
|
|
634
|
+
self_byte_opt := self_al.get(byte_index + j);
|
|
642
635
|
sep_byte_opt := sep_al.get(j);
|
|
643
|
-
match(
|
|
636
|
+
match(
|
|
637
|
+
self_byte_opt,
|
|
644
638
|
.Some(self_byte) =>
|
|
645
|
-
match(
|
|
639
|
+
match(
|
|
640
|
+
sep_byte_opt,
|
|
646
641
|
.Some(sep_byte) => {
|
|
647
642
|
cond(
|
|
648
643
|
(self_byte != sep_byte) => {
|
|
@@ -662,25 +657,25 @@ impl(String,
|
|
|
662
657
|
break;
|
|
663
658
|
}
|
|
664
659
|
);
|
|
665
|
-
};
|
|
660
|
+
});
|
|
666
661
|
},
|
|
667
662
|
true => {
|
|
668
663
|
matches = false;
|
|
669
664
|
}
|
|
670
665
|
);
|
|
671
|
-
|
|
672
666
|
cond(
|
|
673
667
|
matches => {
|
|
674
668
|
cond(
|
|
675
|
-
(current_bytes.len() == usize(0)) => result.push(Self(_bytes
|
|
676
|
-
true => result.push(Self(_bytes
|
|
669
|
+
(current_bytes.len() == usize(0)) => result.push(Self(_bytes :.None)),
|
|
670
|
+
true => result.push(Self(_bytes :.Some(current_bytes)))
|
|
677
671
|
);
|
|
678
672
|
current_bytes = ArrayList(u8).new();
|
|
679
673
|
byte_index = ((byte_index + sep_bytes) - usize(1));
|
|
680
674
|
},
|
|
681
675
|
true => {
|
|
682
676
|
byte_opt := self_al.get(byte_index);
|
|
683
|
-
match(
|
|
677
|
+
match(
|
|
678
|
+
byte_opt,
|
|
684
679
|
.Some(byte) => {
|
|
685
680
|
current_bytes.push(byte);
|
|
686
681
|
},
|
|
@@ -688,48 +683,45 @@ impl(String,
|
|
|
688
683
|
);
|
|
689
684
|
}
|
|
690
685
|
);
|
|
691
|
-
};
|
|
692
|
-
|
|
686
|
+
});
|
|
693
687
|
cond(
|
|
694
|
-
(current_bytes.len() == usize(0)) => result.push(Self(_bytes
|
|
695
|
-
true => result.push(Self(_bytes
|
|
688
|
+
(current_bytes.len() == usize(0)) => result.push(Self(_bytes :.None)),
|
|
689
|
+
true => result.push(Self(_bytes :.Some(current_bytes)))
|
|
696
690
|
);
|
|
697
|
-
return
|
|
691
|
+
return(result);
|
|
698
692
|
}
|
|
699
693
|
)
|
|
700
694
|
)
|
|
701
695
|
)
|
|
702
696
|
}),
|
|
703
|
-
|
|
704
697
|
/**
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
last_index_of : (fn(self: Self, substr: Self, (from_index : usize) ?= usize.MAX) -> Option(usize))(
|
|
698
|
+
* Find the last occurrence of a substring (like JavaScript lastIndexOf)
|
|
699
|
+
* Returns the character index of the last match, or None if not found
|
|
700
|
+
* from_index specifies the character index to start searching backwards from
|
|
701
|
+
*/
|
|
702
|
+
last_index_of : (fn(self : Self, substr : Self, (from_index : usize) ?= usize.MAX) -> Option(usize))(
|
|
710
703
|
cond(
|
|
711
|
-
substr.is_empty()
|
|
712
|
-
true => match(
|
|
713
|
-
.
|
|
714
|
-
.
|
|
715
|
-
|
|
704
|
+
substr.is_empty() =>.Some(self.len()),
|
|
705
|
+
true => match(
|
|
706
|
+
self._bytes,
|
|
707
|
+
.None =>.None,
|
|
708
|
+
.Some(self_al) => match(
|
|
709
|
+
substr._bytes,
|
|
710
|
+
.None =>.None,
|
|
716
711
|
.Some(sub_al) => {
|
|
717
712
|
self_bytes := self_al.len();
|
|
718
713
|
sub_bytes := sub_al.len();
|
|
719
|
-
|
|
720
714
|
cond(
|
|
721
|
-
(sub_bytes > self_bytes)
|
|
715
|
+
(sub_bytes > self_bytes) =>.None,
|
|
722
716
|
true => {
|
|
723
717
|
char_positions := ArrayList(usize).new();
|
|
724
718
|
byte_positions := ArrayList(usize).new();
|
|
725
|
-
|
|
726
719
|
byte_idx := usize(0);
|
|
727
720
|
char_idx := usize(0);
|
|
728
|
-
while
|
|
729
|
-
(byte_idx = (byte_idx + usize(1))),
|
|
730
|
-
{
|
|
721
|
+
while(byte_idx < self_bytes, byte_idx = (byte_idx + usize(1)), {
|
|
731
722
|
byte_opt := self_al.get(byte_idx);
|
|
732
|
-
match(
|
|
723
|
+
match(
|
|
724
|
+
byte_opt,
|
|
733
725
|
.Some(byte) => {
|
|
734
726
|
is_start := ((byte < u8(0x80)) || (byte >= u8(0xC0)));
|
|
735
727
|
cond(
|
|
@@ -743,37 +735,35 @@ impl(String,
|
|
|
743
735
|
},
|
|
744
736
|
.None => ()
|
|
745
737
|
);
|
|
746
|
-
};
|
|
747
|
-
|
|
738
|
+
});
|
|
748
739
|
max_char_index := cond(
|
|
749
740
|
(from_index >= char_idx) => char_idx,
|
|
750
741
|
true => (from_index + usize(1))
|
|
751
742
|
);
|
|
752
|
-
|
|
753
|
-
(last_match : Option(usize)) = .None;
|
|
743
|
+
(last_match : Option(usize)) =.None;
|
|
754
744
|
i := usize(0);
|
|
755
|
-
while
|
|
756
|
-
(i = (i + usize(1))),
|
|
757
|
-
{
|
|
745
|
+
while(i < char_positions.len(), i = (i + usize(1)), {
|
|
758
746
|
char_pos_opt := char_positions.get(i);
|
|
759
747
|
byte_pos_opt := byte_positions.get(i);
|
|
760
|
-
match(
|
|
748
|
+
match(
|
|
749
|
+
char_pos_opt,
|
|
761
750
|
.Some(char_pos) =>
|
|
762
|
-
match(
|
|
751
|
+
match(
|
|
752
|
+
byte_pos_opt,
|
|
763
753
|
.Some(byte_pos) => {
|
|
764
754
|
cond(
|
|
765
755
|
(char_pos >= max_char_index) => break,
|
|
766
756
|
((byte_pos + sub_bytes) <= self_bytes) => {
|
|
767
757
|
matches := true;
|
|
768
758
|
j := usize(0);
|
|
769
|
-
while
|
|
770
|
-
|
|
771
|
-
{
|
|
772
|
-
self_byte_opt := self_al.get((byte_pos + j));
|
|
759
|
+
while(j < sub_bytes, j = (j + usize(1)), {
|
|
760
|
+
self_byte_opt := self_al.get(byte_pos + j);
|
|
773
761
|
sub_byte_opt := sub_al.get(j);
|
|
774
|
-
match(
|
|
762
|
+
match(
|
|
763
|
+
self_byte_opt,
|
|
775
764
|
.Some(self_byte) =>
|
|
776
|
-
match(
|
|
765
|
+
match(
|
|
766
|
+
sub_byte_opt,
|
|
777
767
|
.Some(sub_byte) => {
|
|
778
768
|
cond(
|
|
779
769
|
(self_byte != sub_byte) => {
|
|
@@ -793,10 +783,10 @@ impl(String,
|
|
|
793
783
|
break;
|
|
794
784
|
}
|
|
795
785
|
);
|
|
796
|
-
};
|
|
786
|
+
});
|
|
797
787
|
cond(
|
|
798
788
|
matches => {
|
|
799
|
-
last_match
|
|
789
|
+
last_match =.Some(char_pos);
|
|
800
790
|
},
|
|
801
791
|
true => ()
|
|
802
792
|
);
|
|
@@ -808,9 +798,8 @@ impl(String,
|
|
|
808
798
|
),
|
|
809
799
|
.None => ()
|
|
810
800
|
);
|
|
811
|
-
};
|
|
812
|
-
|
|
813
|
-
return last_match;
|
|
801
|
+
});
|
|
802
|
+
return(last_match);
|
|
814
803
|
}
|
|
815
804
|
)
|
|
816
805
|
}
|
|
@@ -818,30 +807,28 @@ impl(String,
|
|
|
818
807
|
)
|
|
819
808
|
)
|
|
820
809
|
),
|
|
821
|
-
|
|
822
810
|
/**
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
starts_with : (fn(self: Self, prefix: Self, (position : usize) ?= 0) -> bool)({
|
|
827
|
-
(prefix_bytes : usize) = match(prefix._bytes
|
|
828
|
-
(self_bytes : usize) = match(self._bytes
|
|
829
|
-
|
|
811
|
+
* Check if the string starts with the specified substring (like JavaScript startsWith)
|
|
812
|
+
* position: The character index to start checking from (default 0)
|
|
813
|
+
*/
|
|
814
|
+
starts_with : (fn(self : Self, prefix : Self, (position : usize) ?= 0) -> bool)({
|
|
815
|
+
(prefix_bytes : usize) = match(prefix._bytes,.Some(b) => b.len(),.None => usize(0));
|
|
816
|
+
(self_bytes : usize) = match(self._bytes,.Some(b) => b.len(),.None => usize(0));
|
|
830
817
|
cond(
|
|
831
818
|
(prefix_bytes == usize(0)) => true,
|
|
832
|
-
true => match(
|
|
819
|
+
true => match(
|
|
820
|
+
self._bytes,
|
|
833
821
|
.None => false,
|
|
834
|
-
.Some(self_al) => match(
|
|
822
|
+
.Some(self_al) => match(
|
|
823
|
+
prefix._bytes,
|
|
835
824
|
.None => true,
|
|
836
825
|
.Some(prefix_al) => {
|
|
837
826
|
char_index := usize(0);
|
|
838
827
|
byte_index := usize(0);
|
|
839
|
-
|
|
840
|
-
while (((byte_index < self_bytes) && (char_index < position))),
|
|
841
|
-
(byte_index = (byte_index + usize(1))),
|
|
842
|
-
{
|
|
828
|
+
while((byte_index < self_bytes) && (char_index < position), byte_index = (byte_index + usize(1)), {
|
|
843
829
|
byte_opt := self_al.get(byte_index);
|
|
844
|
-
match(
|
|
830
|
+
match(
|
|
831
|
+
byte_opt,
|
|
845
832
|
.Some(byte) => {
|
|
846
833
|
is_start := ((byte < u8(0x80)) || (byte >= u8(0xC0)));
|
|
847
834
|
cond(
|
|
@@ -861,21 +848,20 @@ impl(String,
|
|
|
861
848
|
},
|
|
862
849
|
.None => ()
|
|
863
850
|
);
|
|
864
|
-
};
|
|
865
|
-
|
|
851
|
+
});
|
|
866
852
|
cond(
|
|
867
853
|
((byte_index + prefix_bytes) > self_bytes) => false,
|
|
868
854
|
true => {
|
|
869
855
|
i := usize(0);
|
|
870
856
|
matches := true;
|
|
871
|
-
while
|
|
872
|
-
|
|
873
|
-
{
|
|
874
|
-
self_byte_opt := self_al.get((byte_index + i));
|
|
857
|
+
while(i < prefix_bytes, i = (i + usize(1)), {
|
|
858
|
+
self_byte_opt := self_al.get(byte_index + i);
|
|
875
859
|
prefix_byte_opt := prefix_al.get(i);
|
|
876
|
-
match(
|
|
860
|
+
match(
|
|
861
|
+
self_byte_opt,
|
|
877
862
|
.Some(self_byte) =>
|
|
878
|
-
match(
|
|
863
|
+
match(
|
|
864
|
+
prefix_byte_opt,
|
|
879
865
|
.Some(prefix_byte) => {
|
|
880
866
|
cond(
|
|
881
867
|
(self_byte != prefix_byte) => {
|
|
@@ -895,8 +881,8 @@ impl(String,
|
|
|
895
881
|
break;
|
|
896
882
|
}
|
|
897
883
|
);
|
|
898
|
-
};
|
|
899
|
-
return
|
|
884
|
+
});
|
|
885
|
+
return(matches);
|
|
900
886
|
}
|
|
901
887
|
)
|
|
902
888
|
}
|
|
@@ -904,20 +890,20 @@ impl(String,
|
|
|
904
890
|
)
|
|
905
891
|
)
|
|
906
892
|
}),
|
|
907
|
-
|
|
908
893
|
/**
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
ends_with : (fn(self: Self, suffix: Self, (end_position : usize) ?= usize.MAX) -> bool)({
|
|
913
|
-
(suffix_bytes : usize) = match(suffix._bytes
|
|
914
|
-
(self_bytes : usize) = match(self._bytes
|
|
915
|
-
|
|
894
|
+
* Check if the string ends with the specified substring (like JavaScript endsWith)
|
|
895
|
+
* end_position: The character length to consider (default is string length)
|
|
896
|
+
*/
|
|
897
|
+
ends_with : (fn(self : Self, suffix : Self, (end_position : usize) ?= usize.MAX) -> bool)({
|
|
898
|
+
(suffix_bytes : usize) = match(suffix._bytes,.Some(b) => b.len(),.None => usize(0));
|
|
899
|
+
(self_bytes : usize) = match(self._bytes,.Some(b) => b.len(),.None => usize(0));
|
|
916
900
|
cond(
|
|
917
901
|
(suffix_bytes == usize(0)) => true,
|
|
918
|
-
true => match(
|
|
902
|
+
true => match(
|
|
903
|
+
self._bytes,
|
|
919
904
|
.None => false,
|
|
920
|
-
.Some(self_al) => match(
|
|
905
|
+
.Some(self_al) => match(
|
|
906
|
+
suffix._bytes,
|
|
921
907
|
.None => true,
|
|
922
908
|
.Some(suffix_al) => {
|
|
923
909
|
string_char_len := self.len();
|
|
@@ -926,16 +912,13 @@ impl(String,
|
|
|
926
912
|
(end_position > string_char_len) => string_char_len,
|
|
927
913
|
true => end_position
|
|
928
914
|
);
|
|
929
|
-
|
|
930
915
|
char_index := usize(0);
|
|
931
916
|
byte_index := usize(0);
|
|
932
917
|
end_byte_index := self_bytes;
|
|
933
|
-
|
|
934
|
-
while ((byte_index < self_bytes)),
|
|
935
|
-
(byte_index = (byte_index + usize(1))),
|
|
936
|
-
{
|
|
918
|
+
while(byte_index < self_bytes, byte_index = (byte_index + usize(1)), {
|
|
937
919
|
byte_opt := self_al.get(byte_index);
|
|
938
|
-
match(
|
|
920
|
+
match(
|
|
921
|
+
byte_opt,
|
|
939
922
|
.Some(byte) => {
|
|
940
923
|
is_start := ((byte < u8(0x80)) || (byte >= u8(0xC0)));
|
|
941
924
|
cond(
|
|
@@ -955,22 +938,21 @@ impl(String,
|
|
|
955
938
|
},
|
|
956
939
|
.None => ()
|
|
957
940
|
);
|
|
958
|
-
};
|
|
959
|
-
|
|
941
|
+
});
|
|
960
942
|
cond(
|
|
961
943
|
(suffix_bytes > end_byte_index) => false,
|
|
962
944
|
true => {
|
|
963
945
|
offset := (end_byte_index - suffix_bytes);
|
|
964
946
|
i := usize(0);
|
|
965
947
|
matches := true;
|
|
966
|
-
while
|
|
967
|
-
|
|
968
|
-
{
|
|
969
|
-
self_byte_opt := self_al.get((offset + i));
|
|
948
|
+
while(i < suffix_bytes, i = (i + usize(1)), {
|
|
949
|
+
self_byte_opt := self_al.get(offset + i);
|
|
970
950
|
suffix_byte_opt := suffix_al.get(i);
|
|
971
|
-
match(
|
|
951
|
+
match(
|
|
952
|
+
self_byte_opt,
|
|
972
953
|
.Some(self_byte) =>
|
|
973
|
-
match(
|
|
954
|
+
match(
|
|
955
|
+
suffix_byte_opt,
|
|
974
956
|
.Some(suffix_byte) => {
|
|
975
957
|
cond(
|
|
976
958
|
(self_byte != suffix_byte) => {
|
|
@@ -990,8 +972,8 @@ impl(String,
|
|
|
990
972
|
break;
|
|
991
973
|
}
|
|
992
974
|
);
|
|
993
|
-
};
|
|
994
|
-
return
|
|
975
|
+
});
|
|
976
|
+
return(matches);
|
|
995
977
|
}
|
|
996
978
|
)
|
|
997
979
|
}
|
|
@@ -999,39 +981,36 @@ impl(String,
|
|
|
999
981
|
)
|
|
1000
982
|
)
|
|
1001
983
|
}),
|
|
1002
|
-
|
|
1003
984
|
/**
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
replace : (fn(self: Self, search_value: Self, new_value: Self) -> Self)(
|
|
985
|
+
* Replace the first occurrence of search_value with new_value (like JavaScript replace)
|
|
986
|
+
*/
|
|
987
|
+
replace : (fn(self : Self, search_value : Self, new_value : Self) -> Self)(
|
|
1007
988
|
cond(
|
|
1008
989
|
search_value.is_empty() => {
|
|
1009
|
-
return
|
|
990
|
+
return(new_value.concat(self));
|
|
1010
991
|
},
|
|
1011
|
-
true => match(
|
|
992
|
+
true => match(
|
|
993
|
+
self._bytes,
|
|
1012
994
|
.None => self,
|
|
1013
|
-
.Some(self_al) => match(
|
|
995
|
+
.Some(self_al) => match(
|
|
996
|
+
search_value._bytes,
|
|
1014
997
|
.None => self,
|
|
1015
998
|
.Some(search_al) => {
|
|
1016
999
|
self_bytes := self_al.len();
|
|
1017
1000
|
search_bytes := search_al.len();
|
|
1018
|
-
|
|
1019
1001
|
byte_index := usize(0);
|
|
1020
|
-
(found_at : Option(usize))
|
|
1021
|
-
|
|
1022
|
-
while ((byte_index <= (self_bytes - search_bytes))),
|
|
1023
|
-
(byte_index = (byte_index + usize(1))),
|
|
1024
|
-
{
|
|
1002
|
+
(found_at : Option(usize)) =.None;
|
|
1003
|
+
while(byte_index <= (self_bytes - search_bytes), byte_index = (byte_index + usize(1)), {
|
|
1025
1004
|
matches := true;
|
|
1026
1005
|
j := usize(0);
|
|
1027
|
-
while
|
|
1028
|
-
|
|
1029
|
-
{
|
|
1030
|
-
self_byte_opt := self_al.get((byte_index + j));
|
|
1006
|
+
while(j < search_bytes, j = (j + usize(1)), {
|
|
1007
|
+
self_byte_opt := self_al.get(byte_index + j);
|
|
1031
1008
|
search_byte_opt := search_al.get(j);
|
|
1032
|
-
match(
|
|
1009
|
+
match(
|
|
1010
|
+
self_byte_opt,
|
|
1033
1011
|
.Some(self_byte) =>
|
|
1034
|
-
match(
|
|
1012
|
+
match(
|
|
1013
|
+
search_byte_opt,
|
|
1035
1014
|
.Some(search_byte) => {
|
|
1036
1015
|
cond(
|
|
1037
1016
|
(self_byte != search_byte) => {
|
|
@@ -1051,48 +1030,47 @@ impl(String,
|
|
|
1051
1030
|
break;
|
|
1052
1031
|
}
|
|
1053
1032
|
);
|
|
1054
|
-
};
|
|
1055
|
-
|
|
1033
|
+
});
|
|
1056
1034
|
cond(
|
|
1057
1035
|
matches => {
|
|
1058
|
-
found_at
|
|
1036
|
+
found_at =.Some(byte_index);
|
|
1059
1037
|
break;
|
|
1060
1038
|
},
|
|
1061
1039
|
true => ()
|
|
1062
1040
|
);
|
|
1063
|
-
};
|
|
1064
|
-
|
|
1065
|
-
|
|
1041
|
+
});
|
|
1042
|
+
match(
|
|
1043
|
+
found_at,
|
|
1066
1044
|
.Some(pos) => {
|
|
1067
|
-
(nv_bytes : usize) = match(new_value._bytes
|
|
1068
|
-
new_bytes := ArrayList(u8).with_capacity((
|
|
1069
|
-
|
|
1070
|
-
|
|
1045
|
+
(nv_bytes : usize) = match(new_value._bytes,.Some(b) => b.len(),.None => usize(0));
|
|
1046
|
+
new_bytes := ArrayList(u8).with_capacity((self_bytes - search_bytes) + nv_bytes);
|
|
1047
|
+
match(
|
|
1048
|
+
self_al.ptr(),
|
|
1071
1049
|
.Some(p) => {
|
|
1072
|
-
if(
|
|
1050
|
+
if(pos > usize(0), {
|
|
1073
1051
|
new_bytes.extend_from_ptr(p, pos);
|
|
1074
1052
|
});
|
|
1075
1053
|
},
|
|
1076
1054
|
.None => ()
|
|
1077
1055
|
);
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
.Some(nv_al) => match(
|
|
1056
|
+
match(
|
|
1057
|
+
new_value._bytes,
|
|
1058
|
+
.Some(nv_al) => match(
|
|
1059
|
+
nv_al.ptr(),
|
|
1081
1060
|
.Some(p) => new_bytes.extend_from_ptr(p, nv_bytes),
|
|
1082
1061
|
.None => ()
|
|
1083
1062
|
),
|
|
1084
1063
|
.None => ()
|
|
1085
1064
|
);
|
|
1086
|
-
|
|
1087
1065
|
(after_pos : usize) = (pos + search_bytes);
|
|
1088
|
-
if(
|
|
1089
|
-
match(
|
|
1090
|
-
.
|
|
1066
|
+
if(after_pos < self_bytes, {
|
|
1067
|
+
match(
|
|
1068
|
+
self_al.ptr(),
|
|
1069
|
+
.Some(p) => new_bytes.extend_from_ptr(p &+ after_pos, self_bytes - after_pos),
|
|
1091
1070
|
.None => ()
|
|
1092
1071
|
);
|
|
1093
1072
|
});
|
|
1094
|
-
|
|
1095
|
-
return Self(_bytes: .Some(new_bytes));
|
|
1073
|
+
return(Self(_bytes :.Some(new_bytes)));
|
|
1096
1074
|
},
|
|
1097
1075
|
.None => self
|
|
1098
1076
|
)
|
|
@@ -1101,38 +1079,36 @@ impl(String,
|
|
|
1101
1079
|
)
|
|
1102
1080
|
)
|
|
1103
1081
|
),
|
|
1104
|
-
|
|
1105
1082
|
/**
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
replace_all : (fn(self: Self, search_value: Self, new_value: Self) -> Self)(
|
|
1083
|
+
* Replace all occurrences of search_value with new_value (like JavaScript replaceAll)
|
|
1084
|
+
*/
|
|
1085
|
+
replace_all : (fn(self : Self, search_value : Self, new_value : Self) -> Self)(
|
|
1109
1086
|
cond(
|
|
1110
1087
|
search_value.is_empty() => self,
|
|
1111
|
-
true => match(
|
|
1088
|
+
true => match(
|
|
1089
|
+
self._bytes,
|
|
1112
1090
|
.None => self,
|
|
1113
|
-
.Some(self_al) => match(
|
|
1091
|
+
.Some(self_al) => match(
|
|
1092
|
+
search_value._bytes,
|
|
1114
1093
|
.None => self,
|
|
1115
1094
|
.Some(search_al) => {
|
|
1116
1095
|
self_bytes := self_al.len();
|
|
1117
1096
|
search_bytes := search_al.len();
|
|
1118
1097
|
new_bytes := ArrayList(u8).new();
|
|
1119
|
-
|
|
1120
1098
|
byte_index := usize(0);
|
|
1121
|
-
while
|
|
1122
|
-
(byte_index = (byte_index + usize(1))),
|
|
1123
|
-
{
|
|
1099
|
+
while(byte_index < self_bytes, byte_index = (byte_index + usize(1)), {
|
|
1124
1100
|
matches := true;
|
|
1125
1101
|
cond(
|
|
1126
1102
|
((byte_index + search_bytes) <= self_bytes) => {
|
|
1127
1103
|
j := usize(0);
|
|
1128
|
-
while
|
|
1129
|
-
|
|
1130
|
-
{
|
|
1131
|
-
self_byte_opt := self_al.get((byte_index + j));
|
|
1104
|
+
while(j < search_bytes, j = (j + usize(1)), {
|
|
1105
|
+
self_byte_opt := self_al.get(byte_index + j);
|
|
1132
1106
|
search_byte_opt := search_al.get(j);
|
|
1133
|
-
match(
|
|
1107
|
+
match(
|
|
1108
|
+
self_byte_opt,
|
|
1134
1109
|
.Some(self_byte) =>
|
|
1135
|
-
match(
|
|
1110
|
+
match(
|
|
1111
|
+
search_byte_opt,
|
|
1136
1112
|
.Some(search_byte) => {
|
|
1137
1113
|
cond(
|
|
1138
1114
|
(self_byte != search_byte) => {
|
|
@@ -1152,17 +1128,18 @@ impl(String,
|
|
|
1152
1128
|
break;
|
|
1153
1129
|
}
|
|
1154
1130
|
);
|
|
1155
|
-
};
|
|
1131
|
+
});
|
|
1156
1132
|
},
|
|
1157
1133
|
true => {
|
|
1158
1134
|
matches = false;
|
|
1159
1135
|
}
|
|
1160
1136
|
);
|
|
1161
|
-
|
|
1162
1137
|
cond(
|
|
1163
1138
|
matches => {
|
|
1164
|
-
match(
|
|
1165
|
-
.
|
|
1139
|
+
match(
|
|
1140
|
+
new_value._bytes,
|
|
1141
|
+
.Some(nv_al) => match(
|
|
1142
|
+
nv_al.ptr(),
|
|
1166
1143
|
.Some(p) => new_bytes.extend_from_ptr(p, nv_al.len()),
|
|
1167
1144
|
.None => ()
|
|
1168
1145
|
),
|
|
@@ -1172,41 +1149,41 @@ impl(String,
|
|
|
1172
1149
|
},
|
|
1173
1150
|
true => {
|
|
1174
1151
|
byte_opt := self_al.get(byte_index);
|
|
1175
|
-
match(
|
|
1176
|
-
|
|
1152
|
+
match(
|
|
1153
|
+
byte_opt,
|
|
1154
|
+
.Some(byte) => {
|
|
1155
|
+
new_bytes.push(byte);
|
|
1156
|
+
},
|
|
1177
1157
|
.None => ()
|
|
1178
1158
|
);
|
|
1179
1159
|
}
|
|
1180
1160
|
);
|
|
1181
|
-
};
|
|
1182
|
-
|
|
1161
|
+
});
|
|
1183
1162
|
cond(
|
|
1184
|
-
(new_bytes.len() == usize(0)) => Self(_bytes
|
|
1185
|
-
true => Self(_bytes
|
|
1163
|
+
(new_bytes.len() == usize(0)) => Self(_bytes :.None),
|
|
1164
|
+
true => Self(_bytes :.Some(new_bytes))
|
|
1186
1165
|
)
|
|
1187
1166
|
}
|
|
1188
1167
|
)
|
|
1189
1168
|
)
|
|
1190
1169
|
)
|
|
1191
1170
|
),
|
|
1192
|
-
|
|
1193
1171
|
/**
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
to_uppercase : (fn(self: Self) -> Self)(
|
|
1198
|
-
match(
|
|
1199
|
-
.
|
|
1172
|
+
* Convert ASCII lowercase letters to uppercase (like JavaScript toUpperCase)
|
|
1173
|
+
* Note: Only handles ASCII a-z, not full Unicode case mapping
|
|
1174
|
+
*/
|
|
1175
|
+
to_uppercase : (fn(self : Self) -> Self)(
|
|
1176
|
+
match(
|
|
1177
|
+
self._bytes,
|
|
1178
|
+
.None => Self(_bytes :.None),
|
|
1200
1179
|
.Some(al) => {
|
|
1201
1180
|
(total : usize) = al.len();
|
|
1202
1181
|
new_bytes := ArrayList(u8).with_capacity(total);
|
|
1203
|
-
|
|
1204
1182
|
i := usize(0);
|
|
1205
|
-
while
|
|
1206
|
-
(i = (i + usize(1))),
|
|
1207
|
-
{
|
|
1183
|
+
while(i < total, i = (i + usize(1)), {
|
|
1208
1184
|
byte_opt := al.get(i);
|
|
1209
|
-
match(
|
|
1185
|
+
match(
|
|
1186
|
+
byte_opt,
|
|
1210
1187
|
.Some(byte) => {
|
|
1211
1188
|
new_byte := cond(
|
|
1212
1189
|
((byte >= u8(0x61)) && (byte <= u8(0x7A))) => (byte - u8(0x20)),
|
|
@@ -1216,30 +1193,27 @@ impl(String,
|
|
|
1216
1193
|
},
|
|
1217
1194
|
.None => ()
|
|
1218
1195
|
);
|
|
1219
|
-
};
|
|
1220
|
-
|
|
1221
|
-
Self(_bytes: .Some(new_bytes))
|
|
1196
|
+
});
|
|
1197
|
+
Self(_bytes :.Some(new_bytes))
|
|
1222
1198
|
}
|
|
1223
1199
|
)
|
|
1224
1200
|
),
|
|
1225
|
-
|
|
1226
1201
|
/**
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
to_lowercase : (fn(self: Self) -> Self)(
|
|
1231
|
-
match(
|
|
1232
|
-
.
|
|
1202
|
+
* Convert ASCII uppercase letters to lowercase (like JavaScript toLowerCase)
|
|
1203
|
+
* Note: Only handles ASCII A-Z, not full Unicode case mapping
|
|
1204
|
+
*/
|
|
1205
|
+
to_lowercase : (fn(self : Self) -> Self)(
|
|
1206
|
+
match(
|
|
1207
|
+
self._bytes,
|
|
1208
|
+
.None => Self(_bytes :.None),
|
|
1233
1209
|
.Some(al) => {
|
|
1234
1210
|
(total : usize) = al.len();
|
|
1235
1211
|
new_bytes := ArrayList(u8).with_capacity(total);
|
|
1236
|
-
|
|
1237
1212
|
i := usize(0);
|
|
1238
|
-
while
|
|
1239
|
-
(i = (i + usize(1))),
|
|
1240
|
-
{
|
|
1213
|
+
while(i < total, i = (i + usize(1)), {
|
|
1241
1214
|
byte_opt := al.get(i);
|
|
1242
|
-
match(
|
|
1215
|
+
match(
|
|
1216
|
+
byte_opt,
|
|
1243
1217
|
.Some(byte) => {
|
|
1244
1218
|
new_byte := cond(
|
|
1245
1219
|
((byte >= u8(0x41)) && (byte <= u8(0x5A))) => (byte + u8(0x20)),
|
|
@@ -1249,81 +1223,77 @@ impl(String,
|
|
|
1249
1223
|
},
|
|
1250
1224
|
.None => ()
|
|
1251
1225
|
);
|
|
1252
|
-
};
|
|
1253
|
-
|
|
1254
|
-
Self(_bytes: .Some(new_bytes))
|
|
1226
|
+
});
|
|
1227
|
+
Self(_bytes :.Some(new_bytes))
|
|
1255
1228
|
}
|
|
1256
1229
|
)
|
|
1257
1230
|
),
|
|
1258
|
-
|
|
1259
1231
|
/**
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
_is_whitespace_byte : (fn(byte: u8) -> bool)(
|
|
1264
|
-
(
|
|
1232
|
+
* Helper to check if a byte is ASCII whitespace
|
|
1233
|
+
* Space (0x20), Tab (0x09), Newline (0x0A), Carriage Return (0x0D), Form Feed (0x0C), Vertical Tab (0x0B)
|
|
1234
|
+
*/
|
|
1235
|
+
_is_whitespace_byte : (fn(byte : u8) -> bool)(
|
|
1236
|
+
(byte == u8(0x20)) || ((byte == u8(0x09)) || ((byte == u8(0x0A)) || ((byte == u8(0x0D)) || ((byte == u8(0x0C)) || (byte == u8(0x0B))))))
|
|
1265
1237
|
),
|
|
1266
|
-
|
|
1267
1238
|
/**
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
trim : (fn(self: Self) -> Self)(
|
|
1271
|
-
match(
|
|
1272
|
-
.
|
|
1239
|
+
* Remove whitespace from both ends of the string (like JavaScript trim)
|
|
1240
|
+
*/
|
|
1241
|
+
trim : (fn(self : Self) -> Self)(
|
|
1242
|
+
match(
|
|
1243
|
+
self._bytes,
|
|
1244
|
+
.None => Self(_bytes :.None),
|
|
1273
1245
|
.Some(al) => {
|
|
1274
1246
|
total_bytes := al.len();
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
return Self(_bytes: .None);
|
|
1247
|
+
if(total_bytes == usize(0), {
|
|
1248
|
+
return(Self(_bytes :.None));
|
|
1278
1249
|
});
|
|
1279
|
-
|
|
1280
1250
|
(start_idx : usize) = usize(0);
|
|
1281
|
-
while
|
|
1251
|
+
while(start_idx < total_bytes, {
|
|
1282
1252
|
(b : u8) = al(start_idx);
|
|
1283
|
-
if(!(Self._is_whitespace_byte(b)), {
|
|
1253
|
+
if(!(Self._is_whitespace_byte(b)), {
|
|
1254
|
+
break;
|
|
1255
|
+
});
|
|
1284
1256
|
start_idx = (start_idx + usize(1));
|
|
1285
|
-
};
|
|
1286
|
-
|
|
1257
|
+
});
|
|
1287
1258
|
(end_idx : usize) = total_bytes;
|
|
1288
|
-
while
|
|
1289
|
-
(b : u8) = al(
|
|
1290
|
-
if(!(Self._is_whitespace_byte(b)), {
|
|
1259
|
+
while(end_idx > start_idx, {
|
|
1260
|
+
(b : u8) = al(end_idx - usize(1));
|
|
1261
|
+
if(!(Self._is_whitespace_byte(b)), {
|
|
1262
|
+
break;
|
|
1263
|
+
});
|
|
1291
1264
|
end_idx = (end_idx - usize(1));
|
|
1292
|
-
};
|
|
1293
|
-
|
|
1294
|
-
if((start_idx >= end_idx), {
|
|
1295
|
-
return Self(_bytes: .None);
|
|
1296
1265
|
});
|
|
1297
|
-
|
|
1266
|
+
if(start_idx >= end_idx, {
|
|
1267
|
+
return(Self(_bytes :.None));
|
|
1268
|
+
});
|
|
1298
1269
|
(count : usize) = (end_idx - start_idx);
|
|
1299
1270
|
new_bytes := ArrayList(u8).with_capacity(count);
|
|
1300
|
-
match(
|
|
1301
|
-
.
|
|
1271
|
+
match(
|
|
1272
|
+
al.ptr(),
|
|
1273
|
+
.Some(p) => new_bytes.extend_from_ptr(p &+ start_idx, count),
|
|
1302
1274
|
.None => ()
|
|
1303
1275
|
);
|
|
1304
|
-
Self(_bytes
|
|
1276
|
+
Self(_bytes :.Some(new_bytes))
|
|
1305
1277
|
}
|
|
1306
1278
|
)
|
|
1307
1279
|
),
|
|
1308
|
-
|
|
1309
1280
|
/**
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
trim_start : (fn(self: Self) -> Self)(
|
|
1313
|
-
match(
|
|
1314
|
-
.
|
|
1281
|
+
* Remove whitespace from the start of the string (like JavaScript trimStart)
|
|
1282
|
+
*/
|
|
1283
|
+
trim_start : (fn(self : Self) -> Self)(
|
|
1284
|
+
match(
|
|
1285
|
+
self._bytes,
|
|
1286
|
+
.None => Self(_bytes :.None),
|
|
1315
1287
|
.Some(al) => {
|
|
1316
1288
|
total_bytes := al.len();
|
|
1317
|
-
|
|
1318
1289
|
cond(
|
|
1319
|
-
(total_bytes == usize(0)) => Self(_bytes
|
|
1290
|
+
(total_bytes == usize(0)) => Self(_bytes :.None),
|
|
1320
1291
|
true => {
|
|
1321
1292
|
start_idx := usize(0);
|
|
1322
|
-
while
|
|
1323
|
-
(start_idx = (start_idx + usize(1))),
|
|
1324
|
-
{
|
|
1293
|
+
while(start_idx < total_bytes, start_idx = (start_idx + usize(1)), {
|
|
1325
1294
|
byte_opt := al.get(start_idx);
|
|
1326
|
-
match(
|
|
1295
|
+
match(
|
|
1296
|
+
byte_opt,
|
|
1327
1297
|
.Some(byte) => {
|
|
1328
1298
|
is_ws := Self._is_whitespace_byte(byte);
|
|
1329
1299
|
cond(
|
|
@@ -1333,18 +1303,18 @@ impl(String,
|
|
|
1333
1303
|
},
|
|
1334
1304
|
.None => break
|
|
1335
1305
|
);
|
|
1336
|
-
};
|
|
1337
|
-
|
|
1306
|
+
});
|
|
1338
1307
|
cond(
|
|
1339
|
-
(start_idx >= total_bytes) => Self(_bytes
|
|
1308
|
+
(start_idx >= total_bytes) => Self(_bytes :.None),
|
|
1340
1309
|
true => {
|
|
1341
1310
|
(count : usize) = (total_bytes - start_idx);
|
|
1342
1311
|
new_bytes := ArrayList(u8).with_capacity(count);
|
|
1343
|
-
match(
|
|
1344
|
-
.
|
|
1312
|
+
match(
|
|
1313
|
+
al.ptr(),
|
|
1314
|
+
.Some(p) => new_bytes.extend_from_ptr(p &+ start_idx, count),
|
|
1345
1315
|
.None => ()
|
|
1346
1316
|
);
|
|
1347
|
-
Self(_bytes
|
|
1317
|
+
Self(_bytes :.Some(new_bytes))
|
|
1348
1318
|
}
|
|
1349
1319
|
)
|
|
1350
1320
|
}
|
|
@@ -1352,25 +1322,23 @@ impl(String,
|
|
|
1352
1322
|
}
|
|
1353
1323
|
)
|
|
1354
1324
|
),
|
|
1355
|
-
|
|
1356
1325
|
/**
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
trim_end : (fn(self: Self) -> Self)(
|
|
1360
|
-
match(
|
|
1361
|
-
.
|
|
1326
|
+
* Remove whitespace from the end of the string (like JavaScript trimEnd)
|
|
1327
|
+
*/
|
|
1328
|
+
trim_end : (fn(self : Self) -> Self)(
|
|
1329
|
+
match(
|
|
1330
|
+
self._bytes,
|
|
1331
|
+
.None => Self(_bytes :.None),
|
|
1362
1332
|
.Some(al) => {
|
|
1363
1333
|
total_bytes := al.len();
|
|
1364
|
-
|
|
1365
1334
|
cond(
|
|
1366
|
-
(total_bytes == usize(0)) => Self(_bytes
|
|
1335
|
+
(total_bytes == usize(0)) => Self(_bytes :.None),
|
|
1367
1336
|
true => {
|
|
1368
1337
|
end_idx := total_bytes;
|
|
1369
|
-
while
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
match(byte_opt,
|
|
1338
|
+
while(end_idx > usize(0), end_idx = (end_idx - usize(1)), {
|
|
1339
|
+
byte_opt := al.get(end_idx - usize(1));
|
|
1340
|
+
match(
|
|
1341
|
+
byte_opt,
|
|
1374
1342
|
.Some(byte) => {
|
|
1375
1343
|
is_ws := Self._is_whitespace_byte(byte);
|
|
1376
1344
|
cond(
|
|
@@ -1380,17 +1348,17 @@ impl(String,
|
|
|
1380
1348
|
},
|
|
1381
1349
|
.None => break
|
|
1382
1350
|
);
|
|
1383
|
-
};
|
|
1384
|
-
|
|
1351
|
+
});
|
|
1385
1352
|
cond(
|
|
1386
|
-
(end_idx == usize(0)) => Self(_bytes
|
|
1353
|
+
(end_idx == usize(0)) => Self(_bytes :.None),
|
|
1387
1354
|
true => {
|
|
1388
1355
|
new_bytes := ArrayList(u8).with_capacity(end_idx);
|
|
1389
|
-
match(
|
|
1356
|
+
match(
|
|
1357
|
+
al.ptr(),
|
|
1390
1358
|
.Some(p) => new_bytes.extend_from_ptr(p, end_idx),
|
|
1391
1359
|
.None => ()
|
|
1392
1360
|
);
|
|
1393
|
-
Self(_bytes
|
|
1361
|
+
Self(_bytes :.Some(new_bytes))
|
|
1394
1362
|
}
|
|
1395
1363
|
)
|
|
1396
1364
|
}
|
|
@@ -1399,323 +1367,334 @@ impl(String,
|
|
|
1399
1367
|
)
|
|
1400
1368
|
)
|
|
1401
1369
|
);
|
|
1402
|
-
|
|
1403
1370
|
/// Add trait implementation — concatenate two strings with `+`.
|
|
1404
|
-
impl(
|
|
1405
|
-
|
|
1406
|
-
(
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
));
|
|
1410
|
-
|
|
1371
|
+
impl(
|
|
1372
|
+
String,
|
|
1373
|
+
Add(String)(
|
|
1374
|
+
Output : String,
|
|
1375
|
+
(+) : (fn(self : Self, other : Self) -> Self.Output)({
|
|
1376
|
+
return(self.concat(other));
|
|
1377
|
+
})
|
|
1378
|
+
)
|
|
1379
|
+
);
|
|
1411
1380
|
/// Eq trait implementation — byte-level comparison of two strings.
|
|
1412
|
-
impl(
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
(
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
(
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
)
|
|
1444
|
-
|
|
1381
|
+
impl(
|
|
1382
|
+
String,
|
|
1383
|
+
Eq(String)(
|
|
1384
|
+
(==) : (fn(self : Self, other : Self) -> bool)({
|
|
1385
|
+
(self_len : usize) = match(self._bytes,.Some(b) => b.len(),.None => usize(0));
|
|
1386
|
+
(other_len : usize) = match(other._bytes,.Some(b) => b.len(),.None => usize(0));
|
|
1387
|
+
cond(
|
|
1388
|
+
(self_len != other_len) => false,
|
|
1389
|
+
(self_len == usize(0)) => true,
|
|
1390
|
+
true =>
|
|
1391
|
+
match(
|
|
1392
|
+
self._bytes,
|
|
1393
|
+
.Some(self_al) =>
|
|
1394
|
+
match(
|
|
1395
|
+
other._bytes,
|
|
1396
|
+
.Some(other_al) =>
|
|
1397
|
+
match(
|
|
1398
|
+
self_al.ptr(),
|
|
1399
|
+
.Some(self_ptr) =>
|
|
1400
|
+
match(
|
|
1401
|
+
other_al.ptr(),
|
|
1402
|
+
.Some(other_ptr) =>
|
|
1403
|
+
(memcmp((*(void))(self_ptr), (*(void))(other_ptr), self_len) == int(0)),
|
|
1404
|
+
.None => false
|
|
1405
|
+
),
|
|
1406
|
+
.None => false
|
|
1407
|
+
),
|
|
1408
|
+
.None => false
|
|
1409
|
+
),
|
|
1410
|
+
.None => false
|
|
1411
|
+
)
|
|
1412
|
+
)
|
|
1413
|
+
}),
|
|
1414
|
+
(!=) : (fn(self : Self, other : Self) -> bool)({
|
|
1415
|
+
return(!(Self.(==)(self, other)));
|
|
1416
|
+
})
|
|
1417
|
+
)
|
|
1418
|
+
);
|
|
1445
1419
|
/// Hash trait implementation — FNV-1a hash over the string's bytes.
|
|
1446
|
-
impl(
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1420
|
+
impl(
|
|
1421
|
+
String,
|
|
1422
|
+
Hash(
|
|
1423
|
+
(hash) : (fn(self : *(Self)) -> u64)({
|
|
1424
|
+
h := u64(14695981039346656037);
|
|
1425
|
+
match(
|
|
1426
|
+
self.*._bytes,
|
|
1427
|
+
.None => (),
|
|
1428
|
+
.Some(al) => {
|
|
1429
|
+
i := usize(0);
|
|
1430
|
+
len := al.len();
|
|
1431
|
+
while(i < len, i = (i + usize(1)), {
|
|
1432
|
+
match(
|
|
1433
|
+
al.get(i),
|
|
1434
|
+
.Some(b) => {
|
|
1435
|
+
h = (h ^ u64(b));
|
|
1436
|
+
h = (h * u64(1099511628211));
|
|
1437
|
+
},
|
|
1438
|
+
.None => ()
|
|
1439
|
+
);
|
|
1440
|
+
});
|
|
1441
|
+
}
|
|
1442
|
+
);
|
|
1443
|
+
h
|
|
1444
|
+
})
|
|
1445
|
+
)
|
|
1446
|
+
);
|
|
1471
1447
|
/// === Iterator support ===
|
|
1472
|
-
|
|
1473
1448
|
/**
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1449
|
+
* Rune iterator for `String` — yields decoded Unicode runes.
|
|
1450
|
+
* Used by `chars()` and `into_iter()`.
|
|
1451
|
+
*/
|
|
1477
1452
|
StringChars :: struct(
|
|
1478
|
-
_string
|
|
1453
|
+
_string : String,
|
|
1479
1454
|
_byte_index : usize
|
|
1480
1455
|
);
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
.
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
(
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1456
|
+
impl(
|
|
1457
|
+
StringChars,
|
|
1458
|
+
Iterator(
|
|
1459
|
+
Item : rune,
|
|
1460
|
+
next : (fn(self : *(Self)) -> Option(rune))(
|
|
1461
|
+
match(
|
|
1462
|
+
self._string._bytes,
|
|
1463
|
+
.None =>.None,
|
|
1464
|
+
.Some(al) => cond(
|
|
1465
|
+
(self._byte_index >= al.len()) =>.None,
|
|
1466
|
+
true => {
|
|
1467
|
+
first_byte_opt := al.get(self._byte_index);
|
|
1468
|
+
match(
|
|
1469
|
+
first_byte_opt,
|
|
1470
|
+
.Some(first_byte) => {
|
|
1471
|
+
(byte_len : usize) = cond(
|
|
1472
|
+
(first_byte < u8(0x80)) => usize(1),
|
|
1473
|
+
((first_byte >= u8(0xC0)) && (first_byte < u8(0xE0))) => usize(2),
|
|
1474
|
+
((first_byte >= u8(0xE0)) && (first_byte < u8(0xF0))) => usize(3),
|
|
1475
|
+
((first_byte >= u8(0xF0)) && (first_byte < u8(0xF8))) => usize(4),
|
|
1476
|
+
true => usize(1)
|
|
1477
|
+
);
|
|
1478
|
+
r := self._string._decode_rune_at(self._byte_index);
|
|
1479
|
+
self._byte_index = (self._byte_index + byte_len);
|
|
1480
|
+
r
|
|
1481
|
+
},
|
|
1482
|
+
.None =>.None
|
|
1483
|
+
)
|
|
1484
|
+
}
|
|
1485
|
+
)
|
|
1507
1486
|
)
|
|
1508
1487
|
)
|
|
1509
1488
|
)
|
|
1510
|
-
)
|
|
1511
|
-
|
|
1489
|
+
);
|
|
1512
1490
|
/**
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1491
|
+
* Byte iterator for `String` — yields raw UTF-8 bytes.
|
|
1492
|
+
* Used by `bytes()`.
|
|
1493
|
+
*/
|
|
1516
1494
|
StringBytes :: struct(
|
|
1517
1495
|
_string : String,
|
|
1518
|
-
_index
|
|
1496
|
+
_index : usize
|
|
1519
1497
|
);
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1498
|
+
impl(
|
|
1499
|
+
StringBytes,
|
|
1500
|
+
Iterator(
|
|
1501
|
+
Item : u8,
|
|
1502
|
+
next : (fn(self : *(Self)) -> Option(u8))(
|
|
1503
|
+
match(
|
|
1504
|
+
self._string._bytes,
|
|
1505
|
+
.None =>.None,
|
|
1506
|
+
.Some(al) => cond(
|
|
1507
|
+
(self._index >= al.len()) =>.None,
|
|
1508
|
+
true => {
|
|
1509
|
+
byte_opt := al.get(self._index);
|
|
1510
|
+
self._index = (self._index + usize(1));
|
|
1511
|
+
byte_opt
|
|
1512
|
+
}
|
|
1513
|
+
)
|
|
1533
1514
|
)
|
|
1534
1515
|
)
|
|
1535
1516
|
)
|
|
1536
|
-
)
|
|
1537
|
-
|
|
1538
|
-
|
|
1517
|
+
);
|
|
1518
|
+
impl(
|
|
1519
|
+
String,
|
|
1539
1520
|
/**
|
|
1540
|
-
|
|
1541
|
-
|
|
1521
|
+
* Returns a rune iterator over the string's Unicode characters
|
|
1522
|
+
*/
|
|
1542
1523
|
chars : (fn(self : Self) -> StringChars)(
|
|
1543
|
-
StringChars(_string: self, _byte_index: usize(0))
|
|
1524
|
+
StringChars(_string : self, _byte_index : usize(0))
|
|
1544
1525
|
),
|
|
1545
|
-
|
|
1546
1526
|
/**
|
|
1547
|
-
|
|
1548
|
-
|
|
1527
|
+
* Returns a byte iterator over the string's raw UTF-8 bytes
|
|
1528
|
+
*/
|
|
1549
1529
|
bytes : (fn(self : Self) -> StringBytes)(
|
|
1550
|
-
StringBytes(_string: self, _index: usize(0))
|
|
1530
|
+
StringBytes(_string : self, _index : usize(0))
|
|
1551
1531
|
),
|
|
1552
|
-
|
|
1553
1532
|
/**
|
|
1554
|
-
|
|
1555
|
-
|
|
1533
|
+
* Consume the string and return a rune iterator (default iteration)
|
|
1534
|
+
*/
|
|
1556
1535
|
into_iter : (fn(self : Self) -> StringChars)(
|
|
1557
|
-
StringChars(_string: self, _byte_index: usize(0))
|
|
1536
|
+
StringChars(_string : self, _byte_index : usize(0))
|
|
1558
1537
|
)
|
|
1559
1538
|
);
|
|
1560
|
-
|
|
1561
1539
|
/// === String utility methods ===
|
|
1562
|
-
|
|
1563
1540
|
/**
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1541
|
+
* Line iterator for `String` — yields one line at a time (split on `\n`).
|
|
1542
|
+
* The trailing newline is not included in each yielded line.
|
|
1543
|
+
* Used by `lines()`.
|
|
1544
|
+
*/
|
|
1568
1545
|
StringLines :: struct(
|
|
1569
|
-
_string
|
|
1546
|
+
_string : String,
|
|
1570
1547
|
_byte_index : usize
|
|
1571
1548
|
);
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
(
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1549
|
+
impl(
|
|
1550
|
+
StringLines,
|
|
1551
|
+
Iterator(
|
|
1552
|
+
Item : String,
|
|
1553
|
+
next : (fn(self : *(Self)) -> Option(String))(
|
|
1554
|
+
match(
|
|
1555
|
+
self._string._bytes,
|
|
1556
|
+
.None =>.None,
|
|
1557
|
+
.Some(al) => {
|
|
1558
|
+
total := al.len();
|
|
1559
|
+
cond(
|
|
1560
|
+
(self._byte_index >= total) =>.None,
|
|
1561
|
+
true => {
|
|
1562
|
+
start := self._byte_index;
|
|
1563
|
+
i := start;
|
|
1564
|
+
while(i < total, i = (i + usize(1)), {
|
|
1565
|
+
byte_opt := al.get(i);
|
|
1566
|
+
match(
|
|
1567
|
+
byte_opt,
|
|
1568
|
+
.Some(b) => {
|
|
1569
|
+
cond(
|
|
1570
|
+
(b == u8(10)) => {
|
|
1571
|
+
break;
|
|
1572
|
+
},
|
|
1573
|
+
true => ()
|
|
1574
|
+
);
|
|
1575
|
+
},
|
|
1576
|
+
.None => ()
|
|
1577
|
+
);
|
|
1578
|
+
});
|
|
1579
|
+
line_buf := ArrayList(u8).with_capacity(i - start);
|
|
1580
|
+
j := start;
|
|
1581
|
+
while(j < i, j = (j + usize(1)), {
|
|
1582
|
+
byte_opt := al.get(j);
|
|
1583
|
+
match(
|
|
1584
|
+
byte_opt,
|
|
1585
|
+
.Some(b) => {
|
|
1586
|
+
line_buf.push(b);
|
|
1587
|
+
},
|
|
1588
|
+
.None => ()
|
|
1589
|
+
);
|
|
1590
|
+
});
|
|
1591
|
+
self._byte_index = cond(
|
|
1592
|
+
(i < total) => (i + usize(1)),
|
|
1593
|
+
true => total
|
|
1608
1594
|
);
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
)
|
|
1618
|
-
}
|
|
1619
|
-
)
|
|
1620
|
-
}
|
|
1595
|
+
cond(
|
|
1596
|
+
(line_buf.len() == usize(0)) =>.Some(String(_bytes :.None)),
|
|
1597
|
+
true =>.Some(String(_bytes :.Some(line_buf)))
|
|
1598
|
+
)
|
|
1599
|
+
}
|
|
1600
|
+
)
|
|
1601
|
+
}
|
|
1602
|
+
)
|
|
1621
1603
|
)
|
|
1622
1604
|
)
|
|
1623
|
-
)
|
|
1624
|
-
|
|
1625
|
-
|
|
1605
|
+
);
|
|
1606
|
+
impl(
|
|
1607
|
+
String,
|
|
1626
1608
|
/**
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1609
|
+
* Returns a line iterator over the string.
|
|
1610
|
+
* Each call to `next()` yields the next line (without the trailing `\n`).
|
|
1611
|
+
*
|
|
1612
|
+
* ## Example
|
|
1613
|
+
* ```rust
|
|
1614
|
+
* s := `hello\nworld`;
|
|
1615
|
+
* iter := s.lines();
|
|
1616
|
+
* assert(iter.next() == .Some(`hello`), "first line");
|
|
1617
|
+
* assert(iter.next() == .Some(`world`), "second line");
|
|
1618
|
+
* assert(iter.next() == .None, "done");
|
|
1619
|
+
* ```
|
|
1620
|
+
*/
|
|
1639
1621
|
lines : (fn(self : Self) -> StringLines)(
|
|
1640
|
-
StringLines(_string: self, _byte_index: usize(0))
|
|
1622
|
+
StringLines(_string : self, _byte_index : usize(0))
|
|
1641
1623
|
),
|
|
1642
|
-
|
|
1643
1624
|
/**
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
repeat : (fn(self: Self, n: usize) -> Self)(
|
|
1625
|
+
* Returns a new string containing `n` copies of `self`.
|
|
1626
|
+
* Returns an empty string when `n == 0` or `self` is empty.
|
|
1627
|
+
*
|
|
1628
|
+
* ## Example
|
|
1629
|
+
* ```rust
|
|
1630
|
+
* assert(`ab`.repeat(usize(3)) == `ababab`, "repeat");
|
|
1631
|
+
* assert(`x`.repeat(usize(0)) == ``, "repeat 0");
|
|
1632
|
+
* ```
|
|
1633
|
+
*/
|
|
1634
|
+
repeat : (fn(self : Self, n : usize) -> Self)(
|
|
1654
1635
|
cond(
|
|
1655
|
-
(n == usize(0)) => Self(_bytes
|
|
1656
|
-
self.is_empty() => Self(_bytes
|
|
1636
|
+
(n == usize(0)) => Self(_bytes :.None),
|
|
1637
|
+
self.is_empty() => Self(_bytes :.None),
|
|
1657
1638
|
true => {
|
|
1658
1639
|
self_byte_len := self.bytes_len();
|
|
1659
1640
|
total := (self_byte_len * n);
|
|
1660
1641
|
buf := ArrayList(u8).with_capacity(total);
|
|
1661
1642
|
i := usize(0);
|
|
1662
|
-
while
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
match(self._bytes,
|
|
1643
|
+
while(i < n, i = (i + usize(1)), {
|
|
1644
|
+
match(
|
|
1645
|
+
self._bytes,
|
|
1666
1646
|
.Some(al) => {
|
|
1667
1647
|
j := usize(0);
|
|
1668
|
-
while
|
|
1669
|
-
(j = (j + usize(1))),
|
|
1670
|
-
{
|
|
1648
|
+
while(j < self_byte_len, j = (j + usize(1)), {
|
|
1671
1649
|
byte_opt := al.get(j);
|
|
1672
|
-
match(
|
|
1673
|
-
|
|
1650
|
+
match(
|
|
1651
|
+
byte_opt,
|
|
1652
|
+
.Some(b) => {
|
|
1653
|
+
buf.push(b);
|
|
1654
|
+
},
|
|
1674
1655
|
.None => ()
|
|
1675
1656
|
);
|
|
1676
|
-
};
|
|
1657
|
+
});
|
|
1677
1658
|
},
|
|
1678
1659
|
.None => ()
|
|
1679
1660
|
);
|
|
1680
|
-
};
|
|
1681
|
-
Self(_bytes
|
|
1661
|
+
});
|
|
1662
|
+
Self(_bytes :.Some(buf))
|
|
1682
1663
|
}
|
|
1683
1664
|
)
|
|
1684
1665
|
),
|
|
1685
|
-
|
|
1686
1666
|
/**
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
join : (fn(self: Self, items: ArrayList(Self)) -> Self)({
|
|
1667
|
+
* Join an `ArrayList(String)` with this string as separator.
|
|
1668
|
+
* Returns an empty string when `items` is empty.
|
|
1669
|
+
*
|
|
1670
|
+
* ## Example
|
|
1671
|
+
* ```rust
|
|
1672
|
+
* items := ArrayList(String).new();
|
|
1673
|
+
* items.push(`a`);
|
|
1674
|
+
* items.push(`b`);
|
|
1675
|
+
* items.push(`c`);
|
|
1676
|
+
* result := `, `.join(items);
|
|
1677
|
+
* assert(result == `a, b, c`, "join");
|
|
1678
|
+
* ```
|
|
1679
|
+
*/
|
|
1680
|
+
join : (fn(self : Self, items : ArrayList(Self)) -> Self)({
|
|
1701
1681
|
count := items.len();
|
|
1702
1682
|
cond(
|
|
1703
|
-
(count == usize(0)) => Self(_bytes
|
|
1683
|
+
(count == usize(0)) => Self(_bytes :.None),
|
|
1704
1684
|
true => {
|
|
1705
1685
|
sep_byte_len := self.bytes_len();
|
|
1706
1686
|
total_bytes := usize(0);
|
|
1707
1687
|
k := usize(0);
|
|
1708
|
-
while
|
|
1709
|
-
(k = (k + usize(1))),
|
|
1710
|
-
{
|
|
1688
|
+
while(k < count, k = (k + usize(1)), {
|
|
1711
1689
|
item_opt := items.get(k);
|
|
1712
|
-
match(
|
|
1690
|
+
match(
|
|
1691
|
+
item_opt,
|
|
1713
1692
|
.Some(item) => {
|
|
1714
1693
|
total_bytes = (total_bytes + item.bytes_len());
|
|
1715
1694
|
},
|
|
1716
1695
|
.None => ()
|
|
1717
1696
|
);
|
|
1718
|
-
};
|
|
1697
|
+
});
|
|
1719
1698
|
cond(
|
|
1720
1699
|
(count > usize(1)) => {
|
|
1721
1700
|
total_bytes = (total_bytes + (sep_byte_len * (count - usize(1))));
|
|
@@ -1724,41 +1703,44 @@ impl(String,
|
|
|
1724
1703
|
);
|
|
1725
1704
|
buf := ArrayList(u8).with_capacity(total_bytes);
|
|
1726
1705
|
m := usize(0);
|
|
1727
|
-
while
|
|
1728
|
-
(m = (m + usize(1))),
|
|
1729
|
-
{
|
|
1706
|
+
while(m < count, m = (m + usize(1)), {
|
|
1730
1707
|
item_opt := items.get(m);
|
|
1731
|
-
match(
|
|
1708
|
+
match(
|
|
1709
|
+
item_opt,
|
|
1732
1710
|
.Some(item) => {
|
|
1733
|
-
match(
|
|
1711
|
+
match(
|
|
1712
|
+
item._bytes,
|
|
1734
1713
|
.Some(al) => {
|
|
1735
1714
|
p := usize(0);
|
|
1736
|
-
while
|
|
1737
|
-
(p = (p + usize(1))),
|
|
1738
|
-
{
|
|
1715
|
+
while(p < al.len(), p = (p + usize(1)), {
|
|
1739
1716
|
byte_opt := al.get(p);
|
|
1740
|
-
match(
|
|
1741
|
-
|
|
1717
|
+
match(
|
|
1718
|
+
byte_opt,
|
|
1719
|
+
.Some(b) => {
|
|
1720
|
+
buf.push(b);
|
|
1721
|
+
},
|
|
1742
1722
|
.None => ()
|
|
1743
1723
|
);
|
|
1744
|
-
};
|
|
1724
|
+
});
|
|
1745
1725
|
},
|
|
1746
1726
|
.None => ()
|
|
1747
1727
|
);
|
|
1748
1728
|
cond(
|
|
1749
1729
|
(m < (count - usize(1))) => {
|
|
1750
|
-
match(
|
|
1730
|
+
match(
|
|
1731
|
+
self._bytes,
|
|
1751
1732
|
.Some(sep_al) => {
|
|
1752
1733
|
q := usize(0);
|
|
1753
|
-
while
|
|
1754
|
-
(q = (q + usize(1))),
|
|
1755
|
-
{
|
|
1734
|
+
while(q < sep_byte_len, q = (q + usize(1)), {
|
|
1756
1735
|
byte_opt := sep_al.get(q);
|
|
1757
|
-
match(
|
|
1758
|
-
|
|
1736
|
+
match(
|
|
1737
|
+
byte_opt,
|
|
1738
|
+
.Some(b) => {
|
|
1739
|
+
buf.push(b);
|
|
1740
|
+
},
|
|
1759
1741
|
.None => ()
|
|
1760
1742
|
);
|
|
1761
|
-
};
|
|
1743
|
+
});
|
|
1762
1744
|
},
|
|
1763
1745
|
.None => ()
|
|
1764
1746
|
);
|
|
@@ -1768,42 +1750,42 @@ impl(String,
|
|
|
1768
1750
|
},
|
|
1769
1751
|
.None => ()
|
|
1770
1752
|
);
|
|
1771
|
-
};
|
|
1753
|
+
});
|
|
1772
1754
|
cond(
|
|
1773
|
-
(buf.len() == usize(0)) => Self(_bytes
|
|
1774
|
-
true => Self(_bytes
|
|
1755
|
+
(buf.len() == usize(0)) => Self(_bytes :.None),
|
|
1756
|
+
true => Self(_bytes :.Some(buf))
|
|
1775
1757
|
)
|
|
1776
1758
|
}
|
|
1777
1759
|
)
|
|
1778
1760
|
})
|
|
1779
1761
|
);
|
|
1780
|
-
|
|
1781
1762
|
/// === Numeric parsing methods ===
|
|
1782
|
-
|
|
1783
|
-
|
|
1763
|
+
impl(
|
|
1764
|
+
String,
|
|
1784
1765
|
/**
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
_is_digit_byte : (fn(byte: u8) -> bool)(
|
|
1788
|
-
(
|
|
1766
|
+
* Helper: check if a byte is an ASCII digit '0'-'9'
|
|
1767
|
+
*/
|
|
1768
|
+
_is_digit_byte : (fn(byte : u8) -> bool)(
|
|
1769
|
+
(byte >= u8(48)) && (byte <= u8(57))
|
|
1789
1770
|
),
|
|
1790
|
-
|
|
1791
1771
|
/**
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
parse_i32 : (fn(self: Self) -> Option(i32))(
|
|
1796
|
-
match(
|
|
1797
|
-
.
|
|
1772
|
+
* Parse the string as a signed 32-bit integer.
|
|
1773
|
+
* Returns .Some(value) on success, .None on failure (empty, invalid chars, overflow).
|
|
1774
|
+
*/
|
|
1775
|
+
parse_i32 : (fn(self : Self) -> Option(i32))(
|
|
1776
|
+
match(
|
|
1777
|
+
self._bytes,
|
|
1778
|
+
.None =>.None,
|
|
1798
1779
|
.Some(al) => {
|
|
1799
1780
|
total_bytes := al.len();
|
|
1800
1781
|
cond(
|
|
1801
|
-
(total_bytes == usize(0))
|
|
1782
|
+
(total_bytes == usize(0)) =>.None,
|
|
1802
1783
|
true => {
|
|
1803
1784
|
idx := usize(0);
|
|
1804
1785
|
is_negative := false;
|
|
1805
1786
|
first_byte_opt := al.get(usize(0));
|
|
1806
|
-
match(
|
|
1787
|
+
match(
|
|
1788
|
+
first_byte_opt,
|
|
1807
1789
|
.Some(first_byte) => {
|
|
1808
1790
|
cond(
|
|
1809
1791
|
(first_byte == u8(45)) => {
|
|
@@ -1817,49 +1799,45 @@ impl(String,
|
|
|
1817
1799
|
);
|
|
1818
1800
|
},
|
|
1819
1801
|
.None => {
|
|
1820
|
-
return
|
|
1802
|
+
return(.None);
|
|
1821
1803
|
}
|
|
1822
1804
|
);
|
|
1823
|
-
|
|
1824
1805
|
cond(
|
|
1825
|
-
(idx >= total_bytes)
|
|
1806
|
+
(idx >= total_bytes) =>.None,
|
|
1826
1807
|
true => {
|
|
1827
1808
|
result := i64(0);
|
|
1828
1809
|
has_digit := false;
|
|
1829
|
-
|
|
1830
|
-
while ((idx < total_bytes)),
|
|
1831
|
-
(idx = (idx + usize(1))),
|
|
1832
|
-
{
|
|
1810
|
+
while(idx < total_bytes, idx = (idx + usize(1)), {
|
|
1833
1811
|
byte_opt := al.get(idx);
|
|
1834
|
-
match(
|
|
1812
|
+
match(
|
|
1813
|
+
byte_opt,
|
|
1835
1814
|
.Some(byte) => {
|
|
1836
1815
|
cond(
|
|
1837
1816
|
Self._is_digit_byte(byte) => {
|
|
1838
|
-
digit := i64(
|
|
1817
|
+
digit := i64(byte - u8(48));
|
|
1839
1818
|
result = ((result * i64(10)) + digit);
|
|
1840
1819
|
has_digit = true;
|
|
1841
1820
|
},
|
|
1842
1821
|
true => {
|
|
1843
|
-
return
|
|
1822
|
+
return(.None);
|
|
1844
1823
|
}
|
|
1845
1824
|
);
|
|
1846
1825
|
},
|
|
1847
1826
|
.None => {
|
|
1848
|
-
return
|
|
1827
|
+
return(.None);
|
|
1849
1828
|
}
|
|
1850
1829
|
);
|
|
1851
|
-
};
|
|
1852
|
-
|
|
1830
|
+
});
|
|
1853
1831
|
cond(
|
|
1854
|
-
(!(has_digit))
|
|
1832
|
+
(!(has_digit)) =>.None,
|
|
1855
1833
|
true => {
|
|
1856
1834
|
final_val := cond(
|
|
1857
1835
|
is_negative => (i64(0) - result),
|
|
1858
1836
|
true => result
|
|
1859
1837
|
);
|
|
1860
1838
|
cond(
|
|
1861
|
-
((final_val < i64(-2147483648)) || (final_val > i64(2147483647)))
|
|
1862
|
-
true
|
|
1839
|
+
((final_val < i64(-(2147483648))) || (final_val > i64(2147483647))) =>.None,
|
|
1840
|
+
true =>.Some(i32(final_val))
|
|
1863
1841
|
)
|
|
1864
1842
|
}
|
|
1865
1843
|
)
|
|
@@ -1870,24 +1848,25 @@ impl(String,
|
|
|
1870
1848
|
}
|
|
1871
1849
|
)
|
|
1872
1850
|
),
|
|
1873
|
-
|
|
1874
1851
|
/**
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
parse_i64 : (fn(self: Self) -> Option(i64))(
|
|
1880
|
-
match(
|
|
1881
|
-
.
|
|
1852
|
+
* Parse the string as a signed 64-bit integer.
|
|
1853
|
+
* Returns .Some(value) on success, .None on failure.
|
|
1854
|
+
* Note: does not detect i64 overflow during accumulation.
|
|
1855
|
+
*/
|
|
1856
|
+
parse_i64 : (fn(self : Self) -> Option(i64))(
|
|
1857
|
+
match(
|
|
1858
|
+
self._bytes,
|
|
1859
|
+
.None =>.None,
|
|
1882
1860
|
.Some(al) => {
|
|
1883
1861
|
total_bytes := al.len();
|
|
1884
1862
|
cond(
|
|
1885
|
-
(total_bytes == usize(0))
|
|
1863
|
+
(total_bytes == usize(0)) =>.None,
|
|
1886
1864
|
true => {
|
|
1887
1865
|
idx := usize(0);
|
|
1888
1866
|
is_negative := false;
|
|
1889
1867
|
first_byte_opt := al.get(usize(0));
|
|
1890
|
-
match(
|
|
1868
|
+
match(
|
|
1869
|
+
first_byte_opt,
|
|
1891
1870
|
.Some(first_byte) => {
|
|
1892
1871
|
cond(
|
|
1893
1872
|
(first_byte == u8(45)) => {
|
|
@@ -1901,47 +1880,43 @@ impl(String,
|
|
|
1901
1880
|
);
|
|
1902
1881
|
},
|
|
1903
1882
|
.None => {
|
|
1904
|
-
return
|
|
1883
|
+
return(.None);
|
|
1905
1884
|
}
|
|
1906
1885
|
);
|
|
1907
|
-
|
|
1908
1886
|
cond(
|
|
1909
|
-
(idx >= total_bytes)
|
|
1887
|
+
(idx >= total_bytes) =>.None,
|
|
1910
1888
|
true => {
|
|
1911
1889
|
result := i64(0);
|
|
1912
1890
|
has_digit := false;
|
|
1913
|
-
|
|
1914
|
-
while ((idx < total_bytes)),
|
|
1915
|
-
(idx = (idx + usize(1))),
|
|
1916
|
-
{
|
|
1891
|
+
while(idx < total_bytes, idx = (idx + usize(1)), {
|
|
1917
1892
|
byte_opt := al.get(idx);
|
|
1918
|
-
match(
|
|
1893
|
+
match(
|
|
1894
|
+
byte_opt,
|
|
1919
1895
|
.Some(byte) => {
|
|
1920
1896
|
cond(
|
|
1921
1897
|
Self._is_digit_byte(byte) => {
|
|
1922
|
-
digit := i64(
|
|
1898
|
+
digit := i64(byte - u8(48));
|
|
1923
1899
|
result = ((result * i64(10)) + digit);
|
|
1924
1900
|
has_digit = true;
|
|
1925
1901
|
},
|
|
1926
1902
|
true => {
|
|
1927
|
-
return
|
|
1903
|
+
return(.None);
|
|
1928
1904
|
}
|
|
1929
1905
|
);
|
|
1930
1906
|
},
|
|
1931
1907
|
.None => {
|
|
1932
|
-
return
|
|
1908
|
+
return(.None);
|
|
1933
1909
|
}
|
|
1934
1910
|
);
|
|
1935
|
-
};
|
|
1936
|
-
|
|
1911
|
+
});
|
|
1937
1912
|
cond(
|
|
1938
|
-
(!(has_digit))
|
|
1913
|
+
(!(has_digit)) =>.None,
|
|
1939
1914
|
true => {
|
|
1940
1915
|
final_val := cond(
|
|
1941
1916
|
is_negative => (i64(0) - result),
|
|
1942
1917
|
true => result
|
|
1943
1918
|
);
|
|
1944
|
-
return
|
|
1919
|
+
return(.Some(final_val));
|
|
1945
1920
|
}
|
|
1946
1921
|
)
|
|
1947
1922
|
}
|
|
@@ -1951,26 +1926,27 @@ impl(String,
|
|
|
1951
1926
|
}
|
|
1952
1927
|
)
|
|
1953
1928
|
),
|
|
1954
|
-
|
|
1955
1929
|
/**
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
parse_u32 : (fn(self: Self) -> Option(u32))(
|
|
1960
|
-
match(
|
|
1961
|
-
.
|
|
1930
|
+
* Parse the string as an unsigned 32-bit integer.
|
|
1931
|
+
* Returns .Some(value) on success, .None on failure (empty, invalid chars, overflow, negative sign).
|
|
1932
|
+
*/
|
|
1933
|
+
parse_u32 : (fn(self : Self) -> Option(u32))(
|
|
1934
|
+
match(
|
|
1935
|
+
self._bytes,
|
|
1936
|
+
.None =>.None,
|
|
1962
1937
|
.Some(al) => {
|
|
1963
1938
|
total_bytes := al.len();
|
|
1964
1939
|
cond(
|
|
1965
|
-
(total_bytes == usize(0))
|
|
1940
|
+
(total_bytes == usize(0)) =>.None,
|
|
1966
1941
|
true => {
|
|
1967
1942
|
idx := usize(0);
|
|
1968
1943
|
first_byte_opt := al.get(usize(0));
|
|
1969
|
-
match(
|
|
1944
|
+
match(
|
|
1945
|
+
first_byte_opt,
|
|
1970
1946
|
.Some(first_byte) => {
|
|
1971
1947
|
cond(
|
|
1972
1948
|
(first_byte == u8(45)) => {
|
|
1973
|
-
return
|
|
1949
|
+
return(.None);
|
|
1974
1950
|
},
|
|
1975
1951
|
(first_byte == u8(43)) => {
|
|
1976
1952
|
idx = usize(1);
|
|
@@ -1979,44 +1955,40 @@ impl(String,
|
|
|
1979
1955
|
);
|
|
1980
1956
|
},
|
|
1981
1957
|
.None => {
|
|
1982
|
-
return
|
|
1958
|
+
return(.None);
|
|
1983
1959
|
}
|
|
1984
1960
|
);
|
|
1985
|
-
|
|
1986
1961
|
cond(
|
|
1987
|
-
(idx >= total_bytes)
|
|
1962
|
+
(idx >= total_bytes) =>.None,
|
|
1988
1963
|
true => {
|
|
1989
1964
|
result := u64(0);
|
|
1990
1965
|
has_digit := false;
|
|
1991
|
-
|
|
1992
|
-
while ((idx < total_bytes)),
|
|
1993
|
-
(idx = (idx + usize(1))),
|
|
1994
|
-
{
|
|
1966
|
+
while(idx < total_bytes, idx = (idx + usize(1)), {
|
|
1995
1967
|
byte_opt := al.get(idx);
|
|
1996
|
-
match(
|
|
1968
|
+
match(
|
|
1969
|
+
byte_opt,
|
|
1997
1970
|
.Some(byte) => {
|
|
1998
1971
|
cond(
|
|
1999
1972
|
Self._is_digit_byte(byte) => {
|
|
2000
|
-
digit := u64(
|
|
1973
|
+
digit := u64(byte - u8(48));
|
|
2001
1974
|
result = ((result * u64(10)) + digit);
|
|
2002
1975
|
has_digit = true;
|
|
2003
1976
|
},
|
|
2004
1977
|
true => {
|
|
2005
|
-
return
|
|
1978
|
+
return(.None);
|
|
2006
1979
|
}
|
|
2007
1980
|
);
|
|
2008
1981
|
},
|
|
2009
1982
|
.None => {
|
|
2010
|
-
return
|
|
1983
|
+
return(.None);
|
|
2011
1984
|
}
|
|
2012
1985
|
);
|
|
2013
|
-
};
|
|
2014
|
-
|
|
1986
|
+
});
|
|
2015
1987
|
cond(
|
|
2016
|
-
(!(has_digit))
|
|
1988
|
+
(!(has_digit)) =>.None,
|
|
2017
1989
|
true => cond(
|
|
2018
|
-
(result > u64(4294967295))
|
|
2019
|
-
true
|
|
1990
|
+
(result > u64(4294967295)) =>.None,
|
|
1991
|
+
true =>.Some(u32(result))
|
|
2020
1992
|
)
|
|
2021
1993
|
)
|
|
2022
1994
|
}
|
|
@@ -2026,27 +1998,28 @@ impl(String,
|
|
|
2026
1998
|
}
|
|
2027
1999
|
)
|
|
2028
2000
|
),
|
|
2029
|
-
|
|
2030
2001
|
/**
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
parse_u64 : (fn(self: Self) -> Option(u64))(
|
|
2036
|
-
match(
|
|
2037
|
-
.
|
|
2002
|
+
* Parse the string as an unsigned 64-bit integer.
|
|
2003
|
+
* Returns .Some(value) on success, .None on failure.
|
|
2004
|
+
* Note: does not detect u64 overflow during accumulation.
|
|
2005
|
+
*/
|
|
2006
|
+
parse_u64 : (fn(self : Self) -> Option(u64))(
|
|
2007
|
+
match(
|
|
2008
|
+
self._bytes,
|
|
2009
|
+
.None =>.None,
|
|
2038
2010
|
.Some(al) => {
|
|
2039
2011
|
total_bytes := al.len();
|
|
2040
2012
|
cond(
|
|
2041
|
-
(total_bytes == usize(0))
|
|
2013
|
+
(total_bytes == usize(0)) =>.None,
|
|
2042
2014
|
true => {
|
|
2043
2015
|
idx := usize(0);
|
|
2044
2016
|
first_byte_opt := al.get(usize(0));
|
|
2045
|
-
match(
|
|
2017
|
+
match(
|
|
2018
|
+
first_byte_opt,
|
|
2046
2019
|
.Some(first_byte) => {
|
|
2047
2020
|
cond(
|
|
2048
2021
|
(first_byte == u8(45)) => {
|
|
2049
|
-
return
|
|
2022
|
+
return(.None);
|
|
2050
2023
|
},
|
|
2051
2024
|
(first_byte == u8(43)) => {
|
|
2052
2025
|
idx = usize(1);
|
|
@@ -2055,42 +2028,38 @@ impl(String,
|
|
|
2055
2028
|
);
|
|
2056
2029
|
},
|
|
2057
2030
|
.None => {
|
|
2058
|
-
return
|
|
2031
|
+
return(.None);
|
|
2059
2032
|
}
|
|
2060
2033
|
);
|
|
2061
|
-
|
|
2062
2034
|
cond(
|
|
2063
|
-
(idx >= total_bytes)
|
|
2035
|
+
(idx >= total_bytes) =>.None,
|
|
2064
2036
|
true => {
|
|
2065
2037
|
result := u64(0);
|
|
2066
2038
|
has_digit := false;
|
|
2067
|
-
|
|
2068
|
-
while ((idx < total_bytes)),
|
|
2069
|
-
(idx = (idx + usize(1))),
|
|
2070
|
-
{
|
|
2039
|
+
while(idx < total_bytes, idx = (idx + usize(1)), {
|
|
2071
2040
|
byte_opt := al.get(idx);
|
|
2072
|
-
match(
|
|
2041
|
+
match(
|
|
2042
|
+
byte_opt,
|
|
2073
2043
|
.Some(byte) => {
|
|
2074
2044
|
cond(
|
|
2075
2045
|
Self._is_digit_byte(byte) => {
|
|
2076
|
-
digit := u64(
|
|
2046
|
+
digit := u64(byte - u8(48));
|
|
2077
2047
|
result = ((result * u64(10)) + digit);
|
|
2078
2048
|
has_digit = true;
|
|
2079
2049
|
},
|
|
2080
2050
|
true => {
|
|
2081
|
-
return
|
|
2051
|
+
return(.None);
|
|
2082
2052
|
}
|
|
2083
2053
|
);
|
|
2084
2054
|
},
|
|
2085
2055
|
.None => {
|
|
2086
|
-
return
|
|
2056
|
+
return(.None);
|
|
2087
2057
|
}
|
|
2088
2058
|
);
|
|
2089
|
-
};
|
|
2090
|
-
|
|
2059
|
+
});
|
|
2091
2060
|
cond(
|
|
2092
|
-
(!(has_digit))
|
|
2093
|
-
true
|
|
2061
|
+
(!(has_digit)) =>.None,
|
|
2062
|
+
true =>.Some(result)
|
|
2094
2063
|
)
|
|
2095
2064
|
}
|
|
2096
2065
|
)
|
|
@@ -2099,14 +2068,14 @@ impl(String,
|
|
|
2099
2068
|
}
|
|
2100
2069
|
)
|
|
2101
2070
|
),
|
|
2102
|
-
|
|
2103
2071
|
/**
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
parse_bool : (fn(self: Self) -> Option(bool))(
|
|
2108
|
-
match(
|
|
2109
|
-
.
|
|
2072
|
+
* Parse the string as a boolean.
|
|
2073
|
+
* Returns .Some(true) for "true", .Some(false) for "false", .None for anything else.
|
|
2074
|
+
*/
|
|
2075
|
+
parse_bool : (fn(self : Self) -> Option(bool))(
|
|
2076
|
+
match(
|
|
2077
|
+
self._bytes,
|
|
2078
|
+
.None =>.None,
|
|
2110
2079
|
.Some(al) => {
|
|
2111
2080
|
total_bytes := al.len();
|
|
2112
2081
|
cond(
|
|
@@ -2118,7 +2087,7 @@ impl(String,
|
|
|
2118
2087
|
cond(
|
|
2119
2088
|
((b0 == u8(116)) && ((b1 == u8(114)) && ((b2 == u8(117)) && (b3 == u8(101))))) =>
|
|
2120
2089
|
.Some(true),
|
|
2121
|
-
true
|
|
2090
|
+
true =>.None
|
|
2122
2091
|
)
|
|
2123
2092
|
},
|
|
2124
2093
|
(total_bytes == usize(5)) => {
|
|
@@ -2130,51 +2099,57 @@ impl(String,
|
|
|
2130
2099
|
cond(
|
|
2131
2100
|
((b0 == u8(102)) && ((b1 == u8(97)) && ((b2 == u8(108)) && ((b3 == u8(115)) && (b4 == u8(101)))))) =>
|
|
2132
2101
|
.Some(false),
|
|
2133
|
-
true
|
|
2102
|
+
true =>.None
|
|
2134
2103
|
)
|
|
2135
2104
|
},
|
|
2136
|
-
true
|
|
2105
|
+
true =>.None
|
|
2137
2106
|
)
|
|
2138
2107
|
}
|
|
2139
2108
|
)
|
|
2140
2109
|
)
|
|
2141
2110
|
);
|
|
2142
|
-
|
|
2143
2111
|
/// Index trait implementation — access a single byte by index.
|
|
2144
2112
|
/// Panics if the string is empty.
|
|
2145
|
-
impl(
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2113
|
+
impl(
|
|
2114
|
+
String,
|
|
2115
|
+
Index(usize)(
|
|
2116
|
+
Output : u8,
|
|
2117
|
+
index : (fn(self : *(Self), idx : usize) -> *(Self.Output))(
|
|
2118
|
+
match(
|
|
2119
|
+
self.*._bytes,
|
|
2120
|
+
.Some(bytes) => &(bytes(idx)),
|
|
2121
|
+
.None => panic("String: index on empty string")
|
|
2122
|
+
)
|
|
2151
2123
|
)
|
|
2152
2124
|
)
|
|
2153
|
-
)
|
|
2154
|
-
|
|
2125
|
+
);
|
|
2155
2126
|
/// Clone implementation — produces an independent byte-level copy.
|
|
2156
|
-
impl(
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2127
|
+
impl(
|
|
2128
|
+
String,
|
|
2129
|
+
Clone(
|
|
2130
|
+
clone : (fn(self : *(Self)) -> Self)(
|
|
2131
|
+
match(
|
|
2132
|
+
self._bytes,
|
|
2133
|
+
.None => Self(_bytes :.None),
|
|
2134
|
+
.Some(al) => {
|
|
2135
|
+
n := al.len();
|
|
2136
|
+
cond(
|
|
2137
|
+
(n == usize(0)) => Self(_bytes :.None),
|
|
2138
|
+
true => {
|
|
2139
|
+
buf := ArrayList(u8).with_capacity(n);
|
|
2140
|
+
match(
|
|
2141
|
+
al._ptr,
|
|
2142
|
+
.Some(ptr) => buf.extend_from_ptr(ptr, n),
|
|
2143
|
+
.None => ()
|
|
2144
|
+
);
|
|
2145
|
+
Self(_bytes :.Some(buf))
|
|
2146
|
+
}
|
|
2147
|
+
)
|
|
2148
|
+
}
|
|
2149
|
+
)
|
|
2174
2150
|
)
|
|
2175
2151
|
)
|
|
2176
|
-
)
|
|
2177
|
-
|
|
2152
|
+
);
|
|
2178
2153
|
/// Like `assert`, but accepts a runtime `String` message (e.g. a template
|
|
2179
2154
|
/// string with interpolation: `` `error: ${value}` ``).
|
|
2180
2155
|
///
|
|
@@ -2184,7 +2159,6 @@ impl(String, Clone(
|
|
|
2184
2159
|
assert_dyn :: (fn(flag : bool, msg : String) -> unit)({
|
|
2185
2160
|
assert(flag, msg.as_str());
|
|
2186
2161
|
});
|
|
2187
|
-
|
|
2188
2162
|
/// Like `panic`, but accepts a runtime `String` message (e.g. a template
|
|
2189
2163
|
/// string with interpolation: `` `error: ${value}` ``).
|
|
2190
2164
|
///
|
|
@@ -2193,8 +2167,7 @@ assert_dyn :: (fn(flag : bool, msg : String) -> unit)({
|
|
|
2193
2167
|
panic_dyn :: (fn(msg : String) -> unit)({
|
|
2194
2168
|
panic(msg.as_str());
|
|
2195
2169
|
});
|
|
2196
|
-
|
|
2197
|
-
export
|
|
2170
|
+
export(
|
|
2198
2171
|
String,
|
|
2199
2172
|
StringError,
|
|
2200
2173
|
StringChars,
|
|
@@ -2202,4 +2175,4 @@ export
|
|
|
2202
2175
|
StringLines,
|
|
2203
2176
|
assert_dyn,
|
|
2204
2177
|
panic_dyn
|
|
2205
|
-
;
|
|
2178
|
+
);
|