@shd101wyy/yo 0.1.5 → 0.1.7

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.
Files changed (50) hide show
  1. package/README.md +8 -6
  2. package/out/cjs/index.cjs +691 -636
  3. package/out/cjs/yo-cli.cjs +710 -653
  4. package/out/esm/index.mjs +649 -594
  5. package/out/types/src/build-runner.d.ts +1 -1
  6. package/out/types/src/codegen/async/runtime-io-common.d.ts +2 -1
  7. package/out/types/src/codegen/async/runtime.d.ts +5 -1
  8. package/out/types/src/codegen/codegen-c.d.ts +2 -0
  9. package/out/types/src/codegen/functions/collection.d.ts +1 -1
  10. package/out/types/src/codegen/functions/context.d.ts +1 -0
  11. package/out/types/src/codegen/functions/generation.d.ts +10 -0
  12. package/out/types/src/codegen/utils/index.d.ts +4 -0
  13. package/out/types/src/env.d.ts +1 -0
  14. package/out/types/src/evaluator/builtins/build.d.ts +1 -0
  15. package/out/types/src/evaluator/builtins/comptime-index-fns.d.ts +17 -0
  16. package/out/types/src/evaluator/calls/index-trait.d.ts +17 -0
  17. package/out/types/src/evaluator/context.d.ts +19 -14
  18. package/out/types/src/evaluator/index.d.ts +3 -1
  19. package/out/types/src/evaluator/trait-checking.d.ts +1 -0
  20. package/out/types/src/evaluator/values/anonymous-module.d.ts +3 -2
  21. package/out/types/src/expr.d.ts +22 -1
  22. package/out/types/src/module-manager.d.ts +1 -0
  23. package/out/types/src/target.d.ts +1 -0
  24. package/out/types/src/value.d.ts +4 -1
  25. package/out/types/tsconfig.tsbuildinfo +1 -1
  26. package/package.json +1 -1
  27. package/std/build.yo +2 -1
  28. package/std/collections/array_list.yo +114 -26
  29. package/std/collections/btree_map.yo +13 -3
  30. package/std/collections/deque.yo +10 -0
  31. package/std/collections/hash_map.yo +15 -0
  32. package/std/collections/priority_queue.yo +5 -5
  33. package/std/encoding/html.yo +283 -0
  34. package/std/encoding/html_char_utils.yo +36 -0
  35. package/std/encoding/html_entities.yo +2262 -0
  36. package/std/encoding/punycode.yo +366 -0
  37. package/std/encoding/toml.yo +1 -1
  38. package/std/fmt/to_string.yo +5 -4
  39. package/std/glob/index.yo +2 -2
  40. package/std/libc/wctype.yo +55 -0
  41. package/std/path.yo +6 -6
  42. package/std/prelude.yo +826 -205
  43. package/std/process.yo +1 -1
  44. package/std/regex/compiler.yo +11 -11
  45. package/std/regex/index.yo +2 -4
  46. package/std/regex/parser.yo +69 -4
  47. package/std/regex/vm.yo +53 -46
  48. package/std/string/string.yo +1424 -1339
  49. package/std/string/unicode.yo +242 -0
  50. package/out/types/src/evaluator/calls/array.d.ts +0 -14
@@ -1,5 +1,6 @@
1
1
  { ArrayList } :: import "../collections/array_list.yo";
2
2
  { rune } :: import "./rune.yo";
3
+ { memcpy, memcmp, strlen } :: import "../libc/string.yo";
3
4
 
4
5
  StringError :: enum(
5
6
  InvalidUtf8,
@@ -12,18 +13,30 @@ StringError :: enum(
12
13
  * Similar to JavaScript String, Python str, and Rust String.
13
14
  * Strings are immutable - all operations return new strings.
14
15
  *
15
- * Internal storage uses ArrayList(u8) for UTF-8 bytes.
16
+ * Internal storage uses Option(ArrayList(u8)) for UTF-8 bytes.
17
+ * An empty string is represented as .None (zero allocation).
16
18
  */
17
19
  String :: newtype(
18
- _bytes: ArrayList(u8)
20
+ _bytes: Option(ArrayList(u8))
19
21
  );
20
22
 
21
23
  impl(String,
22
24
  /**
23
- * Create a new empty string
25
+ * Create a new empty string (zero allocation)
24
26
  */
25
27
  new : (fn() -> Self)(
26
- Self(_bytes: ArrayList(u8).new())
28
+ Self(_bytes: .None)
29
+ ),
30
+
31
+ /**
32
+ * Create a new empty string with pre-allocated capacity for the given number of bytes.
33
+ * The string starts empty but can hold `capacity` bytes without reallocating.
34
+ */
35
+ with_capacity : (fn(capacity: usize) -> Self)(
36
+ cond(
37
+ (capacity == usize(0)) => Self(_bytes: .None),
38
+ true => Self(_bytes: .Some(ArrayList(u8).with_capacity(capacity)))
39
+ )
27
40
  ),
28
41
 
29
42
  /**
@@ -31,7 +44,10 @@ impl(String,
31
44
  * No validation is performed - the caller must ensure bytes are valid UTF-8
32
45
  */
33
46
  from_bytes : (fn(bytes: ArrayList(u8)) -> Self)(
34
- Self(_bytes: bytes)
47
+ cond(
48
+ (bytes.len() == usize(0)) => Self(_bytes: .None),
49
+ true => Self(_bytes: .Some(bytes))
50
+ )
35
51
  ),
36
52
 
37
53
  /**
@@ -40,18 +56,13 @@ impl(String,
40
56
  * Example: String.from(slice) where slice is str
41
57
  */
42
58
  from : (fn(slice: str) -> Self)({
43
- // Create a new ArrayList and copy bytes from the slice
44
- bytes := ArrayList(u8).with_capacity(slice.len());
45
-
46
- i := usize(0);
47
- while ((i < slice.len())),
48
- (i = (i + usize(1))),
49
- {
50
- byte := slice.bytes(i);
51
- bytes.push(byte);
52
- };
53
-
54
- return Self(_bytes: bytes);
59
+ slen := slice.len();
60
+ if((slen == usize(0)), {
61
+ return Self(_bytes: .None);
62
+ });
63
+ bytes := ArrayList(u8).with_capacity(slen);
64
+ bytes.extend_from_ptr(slice.ptr(), slen);
65
+ return Self(_bytes: .Some(bytes));
55
66
  }),
56
67
 
57
68
  /**
@@ -62,49 +73,29 @@ impl(String,
62
73
  * Note: We manually search for the null terminator by iterating
63
74
  */
64
75
  from_cstr : (fn(cstr: *(u8)) -> Result(Self, StringError))({
65
- // Create a new ArrayList to store the bytes
66
- bytes := ArrayList(u8).new();
67
-
68
- // Iterate through the C string until we find the null terminator
69
- i := usize(0);
70
- // FIXME: This is causing infinite loop during compile-time evaluation.
71
- // We need to check if the loop body has runtime expr, then we shouldn't consider it as infinite loop.
72
- while {f := true; f}, (i = (i + usize(1))), {
73
- // Access byte at index i using pointer arithmetic/
74
- byte_ptr := (cstr &+ i);
75
- byte := byte_ptr.*;
76
-
77
- // Stop when we find the null terminator
78
- cond(
79
- (byte == u8(0)) => break,
80
- true => bytes.push(byte)
81
- );
82
- };
83
-
84
- return .Ok(Self(_bytes: bytes));
76
+ len := strlen((*(char))(cstr));
77
+ if((len == usize(0)), {
78
+ return .Ok(Self(_bytes: .None));
79
+ });
80
+ bytes := ArrayList(u8).with_capacity(len);
81
+ bytes.extend_from_ptr(cstr, len);
82
+ return .Ok(Self(_bytes: .Some(bytes)));
85
83
  }),
86
84
 
87
85
  to_cstr : (fn(self: Self) -> ArrayList(u8))({
88
- bytes_len := self._bytes.len();
89
- bytes_with_null := ArrayList(u8).with_capacity(bytes_len + usize(1));
90
-
91
- // Copy existing bytes
92
- i := usize(0);
93
- while (i < bytes_len),
94
- (i = (i + usize(1))),
95
- {
96
- byte_opt := self._bytes.get(i);
97
- match(byte_opt,
98
- .Some(byte) => {
99
- bytes_with_null.push(byte);
100
- },
86
+ (bytes_len : usize) = match(self._bytes,
87
+ .Some(b) => b.len(),
88
+ .None => usize(0)
89
+ );
90
+ bytes_with_null := ArrayList(u8).with_capacity((bytes_len + usize(1)));
91
+ match(self._bytes,
92
+ .Some(b) => match(b.ptr(),
93
+ .Some(src) => bytes_with_null.extend_from_ptr(src, bytes_len),
101
94
  .None => ()
102
- );
103
- };
104
-
105
- // Add null terminator
95
+ ),
96
+ .None => ()
97
+ );
106
98
  bytes_with_null.push(u8(0));
107
-
108
99
  return bytes_with_null;
109
100
  }),
110
101
 
@@ -113,44 +104,55 @@ impl(String,
113
104
  * For "你好世界", this returns 4
114
105
  */
115
106
  len : (fn(self: Self) -> usize)({
116
- count := usize(0);
117
- byte_index := usize(0);
118
- total_bytes := self._bytes.len();
119
-
120
- while ((byte_index < total_bytes)),
121
- (byte_index = (byte_index + usize(1))),
122
- {
123
- byte_opt := self._bytes.get(byte_index);
124
- match(byte_opt,
125
- .Some(byte) => {
126
- // Count UTF-8 character start bytes (not continuation bytes)
127
- // Continuation bytes have pattern 10xxxxxx (0x80-0xBF)
128
- cond(
129
- ((byte < u8(0x80)) || (byte >= u8(0xC0))) => {
130
- count = (count + usize(1));
107
+ (inner : Option(ArrayList(u8))) = self._bytes;
108
+ match(inner,
109
+ .None => { return usize(0); },
110
+ .Some(al) => {
111
+ count := usize(0);
112
+ byte_index := usize(0);
113
+ total_bytes := al.len();
114
+
115
+ while ((byte_index < total_bytes)),
116
+ (byte_index = (byte_index + usize(1))),
117
+ {
118
+ byte_opt := al.get(byte_index);
119
+ match(byte_opt,
120
+ .Some(byte) => {
121
+ cond(
122
+ ((byte < u8(0x80)) || (byte >= u8(0xC0))) => {
123
+ count = (count + usize(1));
124
+ },
125
+ true => ()
126
+ );
131
127
  },
132
- true => ()
128
+ .None => ()
133
129
  );
134
- },
135
- .None => ()
136
- );
137
- };
138
-
139
- return count;
130
+ };
131
+
132
+ return count;
133
+ }
134
+ )
140
135
  }),
141
136
 
142
137
  /**
143
138
  * Check if the string is empty
144
139
  */
145
140
  is_empty : (fn(self: Self) -> bool)(
146
- (self._bytes.len() == usize(0))
141
+ match(self._bytes,
142
+ .None => true,
143
+ .Some(al) => (al.len() == usize(0))
144
+ )
147
145
  ),
148
146
 
149
147
  /**
150
148
  * Get a reference to the internal byte array
149
+ * Returns an empty ArrayList if the string is empty
151
150
  */
152
151
  as_bytes : (fn(self: Self) -> ArrayList(u8))(
153
- self._bytes
152
+ match(self._bytes,
153
+ .None => ArrayList(u8).new(),
154
+ .Some(al) => al
155
+ )
154
156
  ),
155
157
 
156
158
  // as_bytes : (fn(self: Self) -> ArrayList(u8))({
@@ -162,9 +164,12 @@ impl(String,
162
164
  * Returns a fat pointer (pointer + length) without copying
163
165
  */
164
166
  as_str : (fn(self: Self) -> str)(
165
- match(self._bytes._ptr,
166
- .Some(p) => str.from_raw_parts(p, self._bytes._length),
167
- .None => str.from_raw_parts(*(u8)(""), usize(0))
167
+ match(self._bytes,
168
+ .None => str.from_raw_parts(*(u8)(""), usize(0)),
169
+ .Some(al) => match(al._ptr,
170
+ .Some(p) => str.from_raw_parts(p, al._length),
171
+ .None => str.from_raw_parts(*(u8)(""), usize(0))
172
+ )
168
173
  )
169
174
  ),
170
175
 
@@ -173,70 +178,70 @@ impl(String,
173
178
  * Internal helper method
174
179
  */
175
180
  _decode_rune_at : (fn(self: Self, byte_index: usize) -> Option(rune))({
176
- first_byte_opt := self._bytes.get(byte_index);
177
- match(first_byte_opt,
178
- .Some(first_byte) => {
179
- // Determine how many bytes this UTF-8 character uses
180
- (res : Option(rune)) = cond(
181
- // 1-byte character: 0xxxxxxx (ASCII)
182
- (first_byte < u8(0x80)) => {
183
- codepoint := u32(first_byte);
184
- rune.from_u32(codepoint)
185
- },
186
- // 2-byte character: 110xxxxx 10xxxxxx
187
- ((first_byte >= u8(0xC0)) && (first_byte < u8(0xE0))) => {
188
- second_opt := self._bytes.get((byte_index + usize(1)));
189
- match(second_opt,
190
- .Some(second) => {
191
- codepoint := (((u32(first_byte) & u32(0x1F)) << u32(6)) | (u32(second) & u32(0x3F)));
181
+ (al : Option(ArrayList(u8))) = self._bytes;
182
+ match(al,
183
+ .None => { return .None; },
184
+ .Some(bytes) => {
185
+ first_byte_opt := bytes.get(byte_index);
186
+ match(first_byte_opt,
187
+ .Some(first_byte) => {
188
+ (res : Option(rune)) = cond(
189
+ (first_byte < u8(0x80)) => {
190
+ codepoint := u32(first_byte);
192
191
  rune.from_u32(codepoint)
193
192
  },
194
- .None => .None
195
- )
196
- },
197
- // 3-byte character: 1110xxxx 10xxxxxx 10xxxxxx
198
- ((first_byte >= u8(0xE0)) && (first_byte < u8(0xF0))) => {
199
- second_opt := self._bytes.get((byte_index + usize(1)));
200
- third_opt := self._bytes.get((byte_index + usize(2)));
201
- match(second_opt,
202
- .Some(second) =>
203
- match(third_opt,
204
- .Some(third) => {
205
- codepoint := ((((u32(first_byte) & u32(0x0F)) << u32(12)) | ((u32(second) & u32(0x3F)) << u32(6))) | (u32(third) & u32(0x3F)));
193
+ ((first_byte >= u8(0xC0)) && (first_byte < u8(0xE0))) => {
194
+ second_opt := bytes.get((byte_index + usize(1)));
195
+ match(second_opt,
196
+ .Some(second) => {
197
+ codepoint := (((u32(first_byte) & u32(0x1F)) << u32(6)) | (u32(second) & u32(0x3F)));
206
198
  rune.from_u32(codepoint)
207
199
  },
208
200
  .None => .None
209
- ),
210
- .None => .None
211
- )
212
- },
213
- // 4-byte character: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
214
- ((first_byte >= u8(0xF0)) && (first_byte < u8(0xF8))) => {
215
- second_opt := self._bytes.get((byte_index + usize(1)));
216
- third_opt := self._bytes.get((byte_index + usize(2)));
217
- fourth_opt := self._bytes.get((byte_index + usize(3)));
218
- match(second_opt,
219
- .Some(second) =>
220
- match(third_opt,
221
- .Some(third) =>
222
- match(fourth_opt,
223
- .Some(fourth) => {
224
- codepoint := (((((u32(first_byte) & u32(0x07)) << u32(18)) | ((u32(second) & u32(0x3F)) << u32(12))) | ((u32(third) & u32(0x3F)) << u32(6))) | (u32(fourth) & u32(0x3F)));
201
+ )
202
+ },
203
+ ((first_byte >= u8(0xE0)) && (first_byte < u8(0xF0))) => {
204
+ second_opt := bytes.get((byte_index + usize(1)));
205
+ third_opt := bytes.get((byte_index + usize(2)));
206
+ match(second_opt,
207
+ .Some(second) =>
208
+ match(third_opt,
209
+ .Some(third) => {
210
+ codepoint := ((((u32(first_byte) & u32(0x0F)) << u32(12)) | ((u32(second) & u32(0x3F)) << u32(6))) | (u32(third) & u32(0x3F)));
225
211
  rune.from_u32(codepoint)
226
212
  },
227
213
  .None => .None
228
214
  ),
229
215
  .None => .None
230
- ),
231
- .None => .None
232
- )
216
+ )
217
+ },
218
+ ((first_byte >= u8(0xF0)) && (first_byte < u8(0xF8))) => {
219
+ second_opt := bytes.get((byte_index + usize(1)));
220
+ third_opt := bytes.get((byte_index + usize(2)));
221
+ fourth_opt := bytes.get((byte_index + usize(3)));
222
+ match(second_opt,
223
+ .Some(second) =>
224
+ match(third_opt,
225
+ .Some(third) =>
226
+ match(fourth_opt,
227
+ .Some(fourth) => {
228
+ codepoint := (((((u32(first_byte) & u32(0x07)) << u32(18)) | ((u32(second) & u32(0x3F)) << u32(12))) | ((u32(third) & u32(0x3F)) << u32(6))) | (u32(fourth) & u32(0x3F)));
229
+ rune.from_u32(codepoint)
230
+ },
231
+ .None => .None
232
+ ),
233
+ .None => .None
234
+ ),
235
+ .None => .None
236
+ )
237
+ },
238
+ true => .None
239
+ );
240
+ return res;
233
241
  },
234
- // Invalid UTF-8 start byte
235
- true => .None
236
- );
237
- return res;
238
- },
239
- .None => .None
242
+ .None => .None
243
+ )
244
+ }
240
245
  )
241
246
  }),
242
247
 
@@ -246,135 +251,219 @@ impl(String,
246
251
  * For "你好世界", at(0) returns '你', at(1) returns '好', etc.
247
252
  */
248
253
  at : (fn(self: Self, index: usize) -> Option(rune))({
249
- char_count := usize(0);
250
- byte_index := usize(0);
251
- total_bytes := self._bytes.len();
252
-
253
- // Find the byte position of the character at the given index
254
- while ((byte_index < total_bytes)),
255
- (byte_index = (byte_index + usize(1))),
256
- {
257
- byte_opt := self._bytes.get(byte_index);
258
- match(byte_opt,
259
- .Some(byte) => {
260
- // Check if this is a UTF-8 character start byte
261
- is_start := ((byte < u8(0x80)) || (byte >= u8(0xC0)));
262
- cond(
263
- is_start => {
264
- // Found a character start byte
254
+ match(self._bytes,
255
+ .None => { return .None; },
256
+ .Some(al) => {
257
+ char_count := usize(0);
258
+ byte_index := usize(0);
259
+ total_bytes := al.len();
260
+
261
+ while ((byte_index < total_bytes)),
262
+ (byte_index = (byte_index + usize(1))),
263
+ {
264
+ byte_opt := al.get(byte_index);
265
+ match(byte_opt,
266
+ .Some(byte) => {
267
+ is_start := ((byte < u8(0x80)) || (byte >= u8(0xC0)));
265
268
  cond(
266
- (char_count == index) => {
267
- // This is the character we want, decode it
268
- return Self._decode_rune_at(self, byte_index);
269
+ is_start => {
270
+ cond(
271
+ (char_count == index) => {
272
+ return Self._decode_rune_at(self, byte_index);
273
+ },
274
+ true => {
275
+ char_count = (char_count + usize(1));
276
+ }
277
+ );
269
278
  },
270
- true => {
271
- // Not the right character yet, keep counting
272
- char_count = (char_count + usize(1));
273
- }
279
+ true => ()
274
280
  );
275
281
  },
276
- true => ()
282
+ .None => ()
277
283
  );
278
- },
279
- .None => ()
280
- );
281
- };
282
-
283
- // Index out of bounds
284
- return .None;
284
+ };
285
+
286
+ return .None;
287
+ }
288
+ )
285
289
  }),
286
290
 
287
291
  /**
288
292
  * Concatenate two strings (like JavaScript +)
289
293
  */
290
294
  concat : (fn(self: Self, other: Self) -> Self)({
291
- self_len := self._bytes.len();
292
- other_len := other._bytes.len();
295
+ (self_len : usize) = match(self._bytes, .Some(b) => b.len(), .None => usize(0));
296
+ (other_len : usize) = match(other._bytes, .Some(b) => b.len(), .None => usize(0));
297
+ if(((self_len == usize(0)) && (other_len == usize(0))), {
298
+ return Self(_bytes: .None);
299
+ });
293
300
  new_bytes := ArrayList(u8).with_capacity((self_len + other_len));
294
301
 
295
- // Copy bytes from self
296
- i := usize(0);
297
- while ((i < self_len)),
298
- (i = (i + usize(1))),
299
- {
300
- byte_opt := self._bytes.get(i);
301
- match(byte_opt,
302
- .Some(byte) => { new_bytes.push(byte); },
303
- .None => { panic("String.concat: failed to get byte from self"); }
304
- );
305
- };
302
+ match(self._bytes,
303
+ .Some(b) => match(b.ptr(),
304
+ .Some(p) => new_bytes.extend_from_ptr(p, self_len),
305
+ .None => ()
306
+ ),
307
+ .None => ()
308
+ );
306
309
 
307
- // Copy bytes from other
308
- j := usize(0);
309
- while ((j < other_len)),
310
- (j = (j + usize(1))),
311
- {
312
- byte_opt := other._bytes.get(j);
313
- match(byte_opt,
314
- .Some(byte) => { new_bytes.push(byte); },
315
- .None => { panic("String.concat: failed to get byte from other"); }
316
- );
317
- };
310
+ match(other._bytes,
311
+ .Some(b) => match(b.ptr(),
312
+ .Some(p) => new_bytes.extend_from_ptr(p, other_len),
313
+ .None => ()
314
+ ),
315
+ .None => ()
316
+ );
318
317
 
319
- return Self(_bytes: new_bytes);
318
+ return Self(_bytes: .Some(new_bytes));
319
+ }),
320
+
321
+ // Append another String to this String in-place (mutates self)
322
+ push_string : (fn(self: *(Self), other: Self) -> unit)({
323
+ (olen : usize) = match(other._bytes, .Some(b) => b.len(), .None => usize(0));
324
+ if((olen > usize(0)), {
325
+ match(other._bytes,
326
+ .Some(ob) => match(ob.ptr(),
327
+ .Some(p) => {
328
+ match(self.*._bytes,
329
+ .None => {
330
+ (new_al : ArrayList(u8)) = ArrayList(u8).with_capacity(olen);
331
+ new_al.extend_from_ptr(p, olen);
332
+ self.*._bytes = .Some(new_al);
333
+ },
334
+ .Some(al) => {
335
+ al.extend_from_ptr(p, olen);
336
+ }
337
+ );
338
+ },
339
+ .None => ()
340
+ ),
341
+ .None => ()
342
+ );
343
+ });
344
+ }),
345
+
346
+ push_str : (fn(self: *(Self), s: str) -> unit)({
347
+ (slen : usize) = s.len();
348
+ if((slen > usize(0)), {
349
+ match(self.*._bytes,
350
+ .None => {
351
+ (new_al : ArrayList(u8)) = ArrayList(u8).with_capacity(slen);
352
+ new_al.extend_from_ptr(s.ptr(), slen);
353
+ self.*._bytes = .Some(new_al);
354
+ },
355
+ .Some(al) => {
356
+ al.extend_from_ptr(s.ptr(), slen);
357
+ }
358
+ );
359
+ });
360
+ }),
361
+
362
+ push_byte : (fn(self: *(Self), b: u8) -> unit)({
363
+ match(self.*._bytes,
364
+ .None => {
365
+ (new_al : ArrayList(u8)) = ArrayList(u8).with_capacity(usize(8));
366
+ new_al.push(b);
367
+ self.*._bytes = .Some(new_al);
368
+ },
369
+ .Some(al) => {
370
+ al.push(b);
371
+ }
372
+ );
373
+ }),
374
+
375
+ reserve : (fn(self: *(Self), additional: usize) -> unit)({
376
+ match(self.*._bytes,
377
+ .None => {
378
+ self.*._bytes = .Some(ArrayList(u8).with_capacity(additional));
379
+ },
380
+ .Some(al) => {
381
+ (current_len : usize) = al.len();
382
+ al.ensure_total_capacity((current_len + additional));
383
+ }
384
+ );
320
385
  }),
321
386
 
387
+ // Clear the string content but keep the allocated buffer for reuse
388
+ clear : (fn(self: *(Self)) -> unit)(
389
+ match(self.*._bytes,
390
+ .Some(al) => al.clear(),
391
+ .None => ()
392
+ )
393
+ ),
394
+
395
+ // Create a deep copy of this string with its own buffer
396
+ clone : (fn(self: Self) -> Self)(
397
+ match(self._bytes,
398
+ .None => Self(_bytes: .None),
399
+ .Some(al) => {
400
+ (new_al : ArrayList(u8)) = ArrayList(u8).with_capacity(al.len());
401
+ match(al.ptr(),
402
+ .Some(p) => new_al.extend_from_ptr(p, al.len()),
403
+ .None => ()
404
+ );
405
+ Self(_bytes: .Some(new_al))
406
+ }
407
+ )
408
+ ),
409
+
410
+ // Get the number of bytes in the string (not Unicode characters)
411
+ bytes_len : (fn(self: Self) -> usize)(
412
+ match(self._bytes, .Some(b) => b.len(), .None => usize(0))
413
+ ),
414
+
322
415
  /**
323
416
  * Extract a substring from start to end character indices (like JavaScript substring)
324
417
  * Returns a new string containing characters from start (inclusive) to end (exclusive)
325
418
  * If end is greater than length, it will substring to the end of the string
326
419
  */
327
420
  substring : (fn(self: Self, start: usize, end: usize) -> Self)({
328
- total_bytes := self._bytes.len();
329
- new_bytes := ArrayList(u8).new();
330
-
331
- char_index := usize(0);
332
- byte_index := usize(0);
333
-
334
- while (byte_index < total_bytes),
335
- (byte_index = (byte_index + usize(1))),
336
- {
337
- byte_opt := self._bytes.get(byte_index);
338
- match(byte_opt,
339
- .Some(byte) => {
340
- is_start := ((byte < u8(0x80)) || (byte >= u8(0xC0)));
341
- cond(
342
- is_start => {
343
- cond(
344
- (char_index >= end) => break,
345
- ((char_index >= start) && (char_index < end)) => {
346
- new_bytes.push(byte);
347
- byte_len := cond(
348
- (byte < u8(0x80)) => usize(1),
349
- ((byte >= u8(0xC0)) && (byte < u8(0xE0))) => usize(2),
350
- ((byte >= u8(0xE0)) && (byte < u8(0xF0))) => usize(3),
351
- ((byte >= u8(0xF0)) && (byte < u8(0xF8))) => usize(4),
352
- true => usize(1)
353
- );
354
- k := usize(1);
355
- while ((k < byte_len)),
356
- (k = (k + usize(1))),
357
- {
358
- next_byte_opt := self._bytes.get((byte_index + k));
359
- match(next_byte_opt,
360
- .Some(next_byte) => { new_bytes.push(next_byte); },
361
- .None => ()
362
- );
363
- };
364
- byte_index = ((byte_index + byte_len) - usize(1));
365
- },
366
- true => ()
367
- );
368
- char_index = (char_index + usize(1));
369
- },
370
- true => ()
421
+ match(self._bytes,
422
+ .None => { return Self(_bytes: .None); },
423
+ .Some(al) => {
424
+ total_bytes := al.len();
425
+
426
+ if((start >= end), {
427
+ return Self(_bytes: .None);
428
+ });
429
+
430
+ (char_index : usize) = usize(0);
431
+ (byte_index : usize) = usize(0);
432
+ (start_byte : usize) = total_bytes;
433
+ (end_byte : usize) = total_bytes;
434
+
435
+ while (byte_index < total_bytes), {
436
+ if((char_index == start), { start_byte = byte_index; });
437
+ if((char_index == end), {
438
+ end_byte = byte_index;
439
+ break;
440
+ });
441
+ (b : u8) = al(byte_index);
442
+ (byte_len : usize) = cond(
443
+ (b < u8(0x80)) => usize(1),
444
+ (b < u8(0xE0)) => usize(2),
445
+ (b < u8(0xF0)) => usize(3),
446
+ true => usize(4)
371
447
  );
372
- },
373
- .None => ()
374
- );
375
- };
376
-
377
- return Self(_bytes: new_bytes);
448
+ byte_index = (byte_index + byte_len);
449
+ char_index = (char_index + usize(1));
450
+ };
451
+ if((char_index == start), { start_byte = byte_index; });
452
+ if((char_index == end), { end_byte = byte_index; });
453
+
454
+ if((start_byte >= end_byte), {
455
+ return Self(_bytes: .None);
456
+ });
457
+
458
+ (count : usize) = (end_byte - start_byte);
459
+ new_bytes := ArrayList(u8).with_capacity(count);
460
+ match(al.ptr(),
461
+ .Some(src_p) => new_bytes.extend_from_ptr((src_p &+ start_byte), count),
462
+ .None => ()
463
+ );
464
+ Self(_bytes: .Some(new_bytes))
465
+ }
466
+ )
378
467
  }),
379
468
 
380
469
  /**
@@ -386,92 +475,97 @@ impl(String,
386
475
  cond(
387
476
  substr.is_empty() => .Some(from_index),
388
477
  true => {
389
- self_bytes := self._bytes.len();
390
- sub_bytes := substr._bytes.len();
478
+ (self_bytes : usize) = match(self._bytes, .Some(b) => b.len(), .None => usize(0));
479
+ (sub_bytes : usize) = match(substr._bytes, .Some(b) => b.len(), .None => usize(0));
391
480
 
392
481
  cond(
393
482
  (sub_bytes > self_bytes) => .None,
394
- true => {
395
- char_index := usize(0);
396
- byte_index := usize(0);
397
-
398
- // Skip to from_index character position
399
- while (((byte_index < self_bytes) && (char_index < from_index))),
400
- (byte_index = (byte_index + usize(1))),
401
- {
402
- first_byte_opt := self._bytes.get(byte_index);
403
- match(first_byte_opt,
404
- .Some(first_byte) => {
405
- is_start := ((first_byte < u8(0x80)) || (first_byte >= u8(0xC0)));
406
- cond(
407
- is_start => {
408
- char_index = (char_index + usize(1));
483
+ true => match(self._bytes,
484
+ .None => .None,
485
+ .Some(self_al) => match(substr._bytes,
486
+ .None => .None,
487
+ .Some(sub_al) => {
488
+ char_index := usize(0);
489
+ byte_index := usize(0);
490
+
491
+ while (((byte_index < self_bytes) && (char_index < from_index))),
492
+ (byte_index = (byte_index + usize(1))),
493
+ {
494
+ first_byte_opt := self_al.get(byte_index);
495
+ match(first_byte_opt,
496
+ .Some(first_byte) => {
497
+ is_start := ((first_byte < u8(0x80)) || (first_byte >= u8(0xC0)));
498
+ cond(
499
+ is_start => {
500
+ char_index = (char_index + usize(1));
501
+ },
502
+ true => ()
503
+ );
409
504
  },
410
- true => ()
505
+ .None => ()
411
506
  );
412
- },
413
- .None => ()
414
- );
415
- };
416
-
417
- while ((byte_index <= (self_bytes - sub_bytes))),
418
- (byte_index = (byte_index + usize(1))),
419
- {
420
- matches := true;
421
- j := usize(0);
422
- while ((j < sub_bytes)),
423
- (j = (j + usize(1))),
424
- {
425
- self_byte_opt := self._bytes.get((byte_index + j));
426
- sub_byte_opt := substr._bytes.get(j);
427
- match(self_byte_opt,
428
- .Some(self_byte) =>
429
- match(sub_byte_opt,
430
- .Some(sub_byte) => {
431
- cond(
432
- (self_byte != sub_byte) => {
507
+ };
508
+
509
+ while ((byte_index <= (self_bytes - sub_bytes))),
510
+ (byte_index = (byte_index + usize(1))),
511
+ {
512
+ matches := true;
513
+ j := usize(0);
514
+ while ((j < sub_bytes)),
515
+ (j = (j + usize(1))),
516
+ {
517
+ self_byte_opt := self_al.get((byte_index + j));
518
+ sub_byte_opt := sub_al.get(j);
519
+ match(self_byte_opt,
520
+ .Some(self_byte) =>
521
+ match(sub_byte_opt,
522
+ .Some(sub_byte) => {
523
+ cond(
524
+ (self_byte != sub_byte) => {
525
+ matches = false;
526
+ break;
527
+ },
528
+ true => ()
529
+ );
530
+ },
531
+ .None => {
433
532
  matches = false;
434
533
  break;
435
- },
436
- true => ()
437
- );
438
- },
534
+ }
535
+ ),
439
536
  .None => {
440
537
  matches = false;
441
538
  break;
442
539
  }
443
- ),
444
- .None => {
445
- matches = false;
446
- break;
447
- }
448
- );
449
- };
450
-
451
- cond(
452
- matches => {
453
- return .Some(char_index);
454
- },
455
- true => ()
456
- );
457
-
458
- first_byte_opt := self._bytes.get(byte_index);
459
- match(first_byte_opt,
460
- .Some(first_byte) => {
461
- is_start := ((first_byte < u8(0x80)) || (first_byte >= u8(0xC0)));
540
+ );
541
+ };
542
+
462
543
  cond(
463
- is_start => {
464
- char_index = (char_index + usize(1));
544
+ matches => {
545
+ return .Some(char_index);
465
546
  },
466
547
  true => ()
467
548
  );
468
- },
469
- .None => ()
470
- );
471
- };
472
-
473
- return .None;
474
- }
549
+
550
+ first_byte_opt := self_al.get(byte_index);
551
+ match(first_byte_opt,
552
+ .Some(first_byte) => {
553
+ is_start := ((first_byte < u8(0x80)) || (first_byte >= u8(0xC0)));
554
+ cond(
555
+ is_start => {
556
+ char_index = (char_index + usize(1));
557
+ },
558
+ true => ()
559
+ );
560
+ },
561
+ .None => ()
562
+ );
563
+ };
564
+
565
+ return .None;
566
+ }
567
+ )
568
+ )
475
569
  )
476
570
  }
477
571
  )
@@ -510,75 +604,93 @@ impl(String,
510
604
  result.push(new());
511
605
  return result;
512
606
  },
513
- true => {
514
- self_bytes := self._bytes.len();
515
- sep_bytes := separator._bytes.len();
516
-
517
- current_bytes := ArrayList(u8).new();
518
- byte_index := usize(0);
519
-
520
- while ((byte_index < self_bytes)),
521
- (byte_index = (byte_index + usize(1))),
522
- {
523
- matches := true;
524
- cond(
525
- ((byte_index + sep_bytes) <= self_bytes) => {
526
- j := usize(0);
527
- while ((j < sep_bytes)),
528
- (j = (j + usize(1))),
529
- {
530
- self_byte_opt := self._bytes.get((byte_index + j));
531
- sep_byte_opt := separator._bytes.get(j);
532
- match(self_byte_opt,
533
- .Some(self_byte) =>
534
- match(sep_byte_opt,
535
- .Some(sep_byte) => {
536
- cond(
537
- (self_byte != sep_byte) => {
607
+ true => match(self._bytes,
608
+ .None => {
609
+ result.push(new());
610
+ return result;
611
+ },
612
+ .Some(self_al) => match(separator._bytes,
613
+ .None => {
614
+ result.push(self);
615
+ return result;
616
+ },
617
+ .Some(sep_al) => {
618
+ self_bytes := self_al.len();
619
+ sep_bytes := sep_al.len();
620
+
621
+ current_bytes := ArrayList(u8).new();
622
+ byte_index := usize(0);
623
+
624
+ while ((byte_index < self_bytes)),
625
+ (byte_index = (byte_index + usize(1))),
626
+ {
627
+ matches := true;
628
+ cond(
629
+ ((byte_index + sep_bytes) <= self_bytes) => {
630
+ j := usize(0);
631
+ while ((j < sep_bytes)),
632
+ (j = (j + usize(1))),
633
+ {
634
+ self_byte_opt := self_al.get((byte_index + j));
635
+ sep_byte_opt := sep_al.get(j);
636
+ match(self_byte_opt,
637
+ .Some(self_byte) =>
638
+ match(sep_byte_opt,
639
+ .Some(sep_byte) => {
640
+ cond(
641
+ (self_byte != sep_byte) => {
642
+ matches = false;
643
+ break;
644
+ },
645
+ true => ()
646
+ );
647
+ },
648
+ .None => {
538
649
  matches = false;
539
650
  break;
540
- },
541
- true => ()
542
- );
543
- },
651
+ }
652
+ ),
544
653
  .None => {
545
654
  matches = false;
546
655
  break;
547
656
  }
548
- ),
549
- .None => {
550
- matches = false;
551
- break;
552
- }
553
- );
554
- };
555
- },
556
- true => {
557
- matches = false;
558
- }
559
- );
560
-
561
- cond(
562
- matches => {
563
- result.push(Self(_bytes: current_bytes));
564
- current_bytes = ArrayList(u8).new();
565
- byte_index = ((byte_index + sep_bytes) - usize(1));
566
- },
567
- true => {
568
- byte_opt := self._bytes.get(byte_index);
569
- match(byte_opt,
570
- .Some(byte) => {
571
- current_bytes.push(byte);
657
+ );
658
+ };
572
659
  },
573
- .None => ()
660
+ true => {
661
+ matches = false;
662
+ }
574
663
  );
575
- }
576
- );
577
- };
578
-
579
- result.push(Self(_bytes: current_bytes));
580
- return result;
581
- }
664
+
665
+ cond(
666
+ matches => {
667
+ cond(
668
+ (current_bytes.len() == usize(0)) => result.push(Self(_bytes: .None)),
669
+ true => result.push(Self(_bytes: .Some(current_bytes)))
670
+ );
671
+ current_bytes = ArrayList(u8).new();
672
+ byte_index = ((byte_index + sep_bytes) - usize(1));
673
+ },
674
+ true => {
675
+ byte_opt := self_al.get(byte_index);
676
+ match(byte_opt,
677
+ .Some(byte) => {
678
+ current_bytes.push(byte);
679
+ },
680
+ .None => ()
681
+ );
682
+ }
683
+ );
684
+ };
685
+
686
+ cond(
687
+ (current_bytes.len() == usize(0)) => result.push(Self(_bytes: .None)),
688
+ true => result.push(Self(_bytes: .Some(current_bytes)))
689
+ );
690
+ return result;
691
+ }
692
+ )
693
+ )
582
694
  )
583
695
  }),
584
696
 
@@ -590,110 +702,113 @@ impl(String,
590
702
  last_index_of : (fn(self: Self, substr: Self, (from_index : usize) ?= usize.MAX) -> Option(usize))(
591
703
  cond(
592
704
  substr.is_empty() => .Some(self.len()),
593
- true => {
594
- self_bytes := self._bytes.len();
595
- sub_bytes := substr._bytes.len();
596
-
597
- cond(
598
- (sub_bytes > self_bytes) => .None,
599
- true => {
600
- // Build a mapping of byte_index -> char_index for character start bytes
601
- char_positions := ArrayList(usize).new();
602
- byte_positions := ArrayList(usize).new();
705
+ true => match(self._bytes,
706
+ .None => .None,
707
+ .Some(self_al) => match(substr._bytes,
708
+ .None => .None,
709
+ .Some(sub_al) => {
710
+ self_bytes := self_al.len();
711
+ sub_bytes := sub_al.len();
603
712
 
604
- byte_idx := usize(0);
605
- char_idx := usize(0);
606
- while ((byte_idx < self_bytes)),
607
- (byte_idx = (byte_idx + usize(1))),
608
- {
609
- byte_opt := self._bytes.get(byte_idx);
610
- match(byte_opt,
611
- .Some(byte) => {
612
- is_start := ((byte < u8(0x80)) || (byte >= u8(0xC0)));
613
- cond(
614
- is_start => {
615
- char_positions.push(char_idx);
616
- byte_positions.push(byte_idx);
617
- char_idx = (char_idx + usize(1));
713
+ cond(
714
+ (sub_bytes > self_bytes) => .None,
715
+ true => {
716
+ char_positions := ArrayList(usize).new();
717
+ byte_positions := ArrayList(usize).new();
718
+
719
+ byte_idx := usize(0);
720
+ char_idx := usize(0);
721
+ while ((byte_idx < self_bytes)),
722
+ (byte_idx = (byte_idx + usize(1))),
723
+ {
724
+ byte_opt := self_al.get(byte_idx);
725
+ match(byte_opt,
726
+ .Some(byte) => {
727
+ is_start := ((byte < u8(0x80)) || (byte >= u8(0xC0)));
728
+ cond(
729
+ is_start => {
730
+ char_positions.push(char_idx);
731
+ byte_positions.push(byte_idx);
732
+ char_idx = (char_idx + usize(1));
733
+ },
734
+ true => ()
735
+ );
618
736
  },
619
- true => ()
737
+ .None => ()
620
738
  );
621
- },
622
- .None => ()
623
- );
624
- };
625
-
626
- // Determine max_char_index based on from_index
627
- max_char_index := cond(
628
- (from_index >= char_idx) => char_idx,
629
- true => (from_index + usize(1))
630
- );
631
-
632
- // Search backwards from the end
633
- (last_match : Option(usize)) = .None;
634
- i := usize(0);
635
- while ((i < char_positions.len())),
636
- (i = (i + usize(1))),
637
- {
638
- char_pos_opt := char_positions.get(i);
639
- byte_pos_opt := byte_positions.get(i);
640
- match(char_pos_opt,
641
- .Some(char_pos) =>
642
- match(byte_pos_opt,
643
- .Some(byte_pos) => {
644
- cond(
645
- (char_pos >= max_char_index) => break,
646
- ((byte_pos + sub_bytes) <= self_bytes) => {
647
- matches := true;
648
- j := usize(0);
649
- while ((j < sub_bytes)),
650
- (j = (j + usize(1))),
651
- {
652
- self_byte_opt := self._bytes.get((byte_pos + j));
653
- sub_byte_opt := substr._bytes.get(j);
654
- match(self_byte_opt,
655
- .Some(self_byte) =>
656
- match(sub_byte_opt,
657
- .Some(sub_byte) => {
658
- cond(
659
- (self_byte != sub_byte) => {
739
+ };
740
+
741
+ max_char_index := cond(
742
+ (from_index >= char_idx) => char_idx,
743
+ true => (from_index + usize(1))
744
+ );
745
+
746
+ (last_match : Option(usize)) = .None;
747
+ i := usize(0);
748
+ while ((i < char_positions.len())),
749
+ (i = (i + usize(1))),
750
+ {
751
+ char_pos_opt := char_positions.get(i);
752
+ byte_pos_opt := byte_positions.get(i);
753
+ match(char_pos_opt,
754
+ .Some(char_pos) =>
755
+ match(byte_pos_opt,
756
+ .Some(byte_pos) => {
757
+ cond(
758
+ (char_pos >= max_char_index) => break,
759
+ ((byte_pos + sub_bytes) <= self_bytes) => {
760
+ matches := true;
761
+ j := usize(0);
762
+ while ((j < sub_bytes)),
763
+ (j = (j + usize(1))),
764
+ {
765
+ self_byte_opt := self_al.get((byte_pos + j));
766
+ sub_byte_opt := sub_al.get(j);
767
+ match(self_byte_opt,
768
+ .Some(self_byte) =>
769
+ match(sub_byte_opt,
770
+ .Some(sub_byte) => {
771
+ cond(
772
+ (self_byte != sub_byte) => {
773
+ matches = false;
774
+ break;
775
+ },
776
+ true => ()
777
+ );
778
+ },
779
+ .None => {
660
780
  matches = false;
661
781
  break;
662
- },
663
- true => ()
664
- );
665
- },
782
+ }
783
+ ),
666
784
  .None => {
667
785
  matches = false;
668
786
  break;
669
787
  }
670
- ),
671
- .None => {
672
- matches = false;
673
- break;
674
- }
675
- );
676
- };
677
- cond(
678
- matches => {
679
- last_match = .Some(char_pos);
788
+ );
789
+ };
790
+ cond(
791
+ matches => {
792
+ last_match = .Some(char_pos);
793
+ },
794
+ true => ()
795
+ );
680
796
  },
681
797
  true => ()
682
798
  );
683
799
  },
684
- true => ()
685
- );
686
- },
800
+ .None => ()
801
+ ),
687
802
  .None => ()
688
- ),
689
- .None => ()
690
- );
691
- };
692
-
693
- return last_match;
803
+ );
804
+ };
805
+
806
+ return last_match;
807
+ }
808
+ )
694
809
  }
695
810
  )
696
- }
811
+ )
697
812
  )
698
813
  ),
699
814
 
@@ -702,83 +817,84 @@ impl(String,
702
817
  * position: The character index to start checking from (default 0)
703
818
  */
704
819
  starts_with : (fn(self: Self, prefix: Self, (position : usize) ?= 0) -> bool)({
705
- prefix_bytes := prefix._bytes.len();
706
- self_bytes := self._bytes.len();
820
+ (prefix_bytes : usize) = match(prefix._bytes, .Some(b) => b.len(), .None => usize(0));
821
+ (self_bytes : usize) = match(self._bytes, .Some(b) => b.len(), .None => usize(0));
707
822
 
708
823
  cond(
709
824
  (prefix_bytes == usize(0)) => true,
710
- true => {
711
- // Find the byte index corresponding to the position-th character
712
- char_index := usize(0);
713
- byte_index := usize(0);
714
-
715
- // Skip to the position-th character
716
- while (((byte_index < self_bytes) && (char_index < position))),
717
- (byte_index = (byte_index + usize(1))),
718
- {
719
- byte_opt := self._bytes.get(byte_index);
720
- match(byte_opt,
721
- .Some(byte) => {
722
- is_start := ((byte < u8(0x80)) || (byte >= u8(0xC0)));
723
- cond(
724
- is_start => {
725
- // Determine byte length of this UTF-8 character
726
- byte_len := cond(
727
- (byte < u8(0x80)) => usize(1),
728
- ((byte >= u8(0xC0)) && (byte < u8(0xE0))) => usize(2),
729
- ((byte >= u8(0xE0)) && (byte < u8(0xF0))) => usize(3),
730
- ((byte >= u8(0xF0)) && (byte < u8(0xF8))) => usize(4),
731
- true => usize(1)
825
+ true => match(self._bytes,
826
+ .None => false,
827
+ .Some(self_al) => match(prefix._bytes,
828
+ .None => true,
829
+ .Some(prefix_al) => {
830
+ char_index := usize(0);
831
+ byte_index := usize(0);
832
+
833
+ while (((byte_index < self_bytes) && (char_index < position))),
834
+ (byte_index = (byte_index + usize(1))),
835
+ {
836
+ byte_opt := self_al.get(byte_index);
837
+ match(byte_opt,
838
+ .Some(byte) => {
839
+ is_start := ((byte < u8(0x80)) || (byte >= u8(0xC0)));
840
+ cond(
841
+ is_start => {
842
+ byte_len := cond(
843
+ (byte < u8(0x80)) => usize(1),
844
+ ((byte >= u8(0xC0)) && (byte < u8(0xE0))) => usize(2),
845
+ ((byte >= u8(0xE0)) && (byte < u8(0xF0))) => usize(3),
846
+ ((byte >= u8(0xF0)) && (byte < u8(0xF8))) => usize(4),
847
+ true => usize(1)
848
+ );
849
+ byte_index = ((byte_index + byte_len) - usize(1));
850
+ char_index = (char_index + usize(1));
851
+ },
852
+ true => ()
732
853
  );
733
- byte_index = ((byte_index + byte_len) - usize(1));
734
- char_index = (char_index + usize(1));
735
854
  },
736
- true => ()
855
+ .None => ()
737
856
  );
738
- },
739
- .None => ()
740
- );
741
- };
742
-
743
- // Check if we have enough bytes left for the prefix
744
- cond(
745
- ((byte_index + prefix_bytes) > self_bytes) => false,
746
- true => {
747
- // Compare bytes starting at byte_index
748
- i := usize(0);
749
- matches := true;
750
- while ((i < prefix_bytes)),
751
- (i = (i + usize(1))),
752
- {
753
- self_byte_opt := self._bytes.get((byte_index + i));
754
- prefix_byte_opt := prefix._bytes.get(i);
755
- match(self_byte_opt,
756
- .Some(self_byte) =>
757
- match(prefix_byte_opt,
758
- .Some(prefix_byte) => {
759
- cond(
760
- (self_byte != prefix_byte) => {
857
+ };
858
+
859
+ cond(
860
+ ((byte_index + prefix_bytes) > self_bytes) => false,
861
+ true => {
862
+ i := usize(0);
863
+ matches := true;
864
+ while ((i < prefix_bytes)),
865
+ (i = (i + usize(1))),
866
+ {
867
+ self_byte_opt := self_al.get((byte_index + i));
868
+ prefix_byte_opt := prefix_al.get(i);
869
+ match(self_byte_opt,
870
+ .Some(self_byte) =>
871
+ match(prefix_byte_opt,
872
+ .Some(prefix_byte) => {
873
+ cond(
874
+ (self_byte != prefix_byte) => {
875
+ matches = false;
876
+ break;
877
+ },
878
+ true => ()
879
+ );
880
+ },
881
+ .None => {
761
882
  matches = false;
762
883
  break;
763
- },
764
- true => ()
765
- );
766
- },
884
+ }
885
+ ),
767
886
  .None => {
768
887
  matches = false;
769
888
  break;
770
889
  }
771
- ),
772
- .None => {
773
- matches = false;
774
- break;
775
- }
776
- );
777
- };
778
- return matches;
890
+ );
891
+ };
892
+ return matches;
893
+ }
894
+ )
779
895
  }
780
896
  )
781
- }
897
+ )
782
898
  )
783
899
  }),
784
900
 
@@ -787,91 +903,93 @@ impl(String,
787
903
  * end_position: The character length to consider (default is string length)
788
904
  */
789
905
  ends_with : (fn(self: Self, suffix: Self, (end_position : usize) ?= usize.MAX) -> bool)({
790
- suffix_bytes := suffix._bytes.len();
791
- self_bytes := self._bytes.len();
906
+ (suffix_bytes : usize) = match(suffix._bytes, .Some(b) => b.len(), .None => usize(0));
907
+ (self_bytes : usize) = match(self._bytes, .Some(b) => b.len(), .None => usize(0));
792
908
 
793
909
  cond(
794
910
  (suffix_bytes == usize(0)) => true,
795
- true => {
796
- // Determine the effective end position in characters
797
- string_char_len := self.len();
798
- effective_end := cond(
799
- (end_position == usize.MAX) => string_char_len,
800
- (end_position > string_char_len) => string_char_len,
801
- true => end_position
802
- );
803
-
804
- // Find the byte index corresponding to effective_end character position
805
- char_index := usize(0);
806
- byte_index := usize(0);
807
- end_byte_index := self_bytes;
808
-
809
- while ((byte_index < self_bytes)),
810
- (byte_index = (byte_index + usize(1))),
811
- {
812
- byte_opt := self._bytes.get(byte_index);
813
- match(byte_opt,
814
- .Some(byte) => {
815
- is_start := ((byte < u8(0x80)) || (byte >= u8(0xC0)));
816
- cond(
817
- is_start => {
911
+ true => match(self._bytes,
912
+ .None => false,
913
+ .Some(self_al) => match(suffix._bytes,
914
+ .None => true,
915
+ .Some(suffix_al) => {
916
+ string_char_len := self.len();
917
+ effective_end := cond(
918
+ (end_position == usize.MAX) => string_char_len,
919
+ (end_position > string_char_len) => string_char_len,
920
+ true => end_position
921
+ );
922
+
923
+ char_index := usize(0);
924
+ byte_index := usize(0);
925
+ end_byte_index := self_bytes;
926
+
927
+ while ((byte_index < self_bytes)),
928
+ (byte_index = (byte_index + usize(1))),
929
+ {
930
+ byte_opt := self_al.get(byte_index);
931
+ match(byte_opt,
932
+ .Some(byte) => {
933
+ is_start := ((byte < u8(0x80)) || (byte >= u8(0xC0)));
818
934
  cond(
819
- (char_index == effective_end) => {
820
- end_byte_index = byte_index;
821
- break;
935
+ is_start => {
936
+ cond(
937
+ (char_index == effective_end) => {
938
+ end_byte_index = byte_index;
939
+ break;
940
+ },
941
+ true => {
942
+ char_index = (char_index + usize(1));
943
+ }
944
+ );
822
945
  },
823
- true => {
824
- char_index = (char_index + usize(1));
825
- }
946
+ true => ()
826
947
  );
827
948
  },
828
- true => ()
949
+ .None => ()
829
950
  );
830
- },
831
- .None => ()
832
- );
833
- };
834
-
835
- // Check if suffix fits within the effective range
836
- cond(
837
- (suffix_bytes > end_byte_index) => false,
838
- true => {
839
- // Compare bytes: check if bytes before end_byte_index end with suffix
840
- offset := (end_byte_index - suffix_bytes);
841
- i := usize(0);
842
- matches := true;
843
- while ((i < suffix_bytes)),
844
- (i = (i + usize(1))),
845
- {
846
- self_byte_opt := self._bytes.get((offset + i));
847
- suffix_byte_opt := suffix._bytes.get(i);
848
- match(self_byte_opt,
849
- .Some(self_byte) =>
850
- match(suffix_byte_opt,
851
- .Some(suffix_byte) => {
852
- cond(
853
- (self_byte != suffix_byte) => {
951
+ };
952
+
953
+ cond(
954
+ (suffix_bytes > end_byte_index) => false,
955
+ true => {
956
+ offset := (end_byte_index - suffix_bytes);
957
+ i := usize(0);
958
+ matches := true;
959
+ while ((i < suffix_bytes)),
960
+ (i = (i + usize(1))),
961
+ {
962
+ self_byte_opt := self_al.get((offset + i));
963
+ suffix_byte_opt := suffix_al.get(i);
964
+ match(self_byte_opt,
965
+ .Some(self_byte) =>
966
+ match(suffix_byte_opt,
967
+ .Some(suffix_byte) => {
968
+ cond(
969
+ (self_byte != suffix_byte) => {
970
+ matches = false;
971
+ break;
972
+ },
973
+ true => ()
974
+ );
975
+ },
976
+ .None => {
854
977
  matches = false;
855
978
  break;
856
- },
857
- true => ()
858
- );
859
- },
979
+ }
980
+ ),
860
981
  .None => {
861
982
  matches = false;
862
983
  break;
863
984
  }
864
- ),
865
- .None => {
866
- matches = false;
867
- break;
868
- }
869
- );
870
- };
871
- return matches;
985
+ );
986
+ };
987
+ return matches;
988
+ }
989
+ )
872
990
  }
873
991
  )
874
- }
992
+ )
875
993
  )
876
994
  }),
877
995
 
@@ -881,132 +999,29 @@ impl(String,
881
999
  replace : (fn(self: Self, search_value: Self, new_value: Self) -> Self)(
882
1000
  cond(
883
1001
  search_value.is_empty() => {
884
- // If search is empty, prepend new_value
885
1002
  return new_value.concat(self);
886
1003
  },
887
- true => {
888
- self_bytes := self._bytes.len();
889
- search_bytes := search_value._bytes.len();
890
-
891
- // Find first occurrence at byte level
892
- byte_index := usize(0);
893
- (found_at : Option(usize)) = .None;
894
-
895
- while ((byte_index <= (self_bytes - search_bytes))),
896
- (byte_index = (byte_index + usize(1))),
897
- {
898
- matches := true;
899
- j := usize(0);
900
- while ((j < search_bytes)),
901
- (j = (j + usize(1))),
902
- {
903
- self_byte_opt := self._bytes.get((byte_index + j));
904
- search_byte_opt := search_value._bytes.get(j);
905
- match(self_byte_opt,
906
- .Some(self_byte) =>
907
- match(search_byte_opt,
908
- .Some(search_byte) => {
909
- cond(
910
- (self_byte != search_byte) => {
911
- matches = false;
912
- break;
913
- },
914
- true => ()
915
- );
916
- },
917
- .None => {
918
- matches = false;
919
- break;
920
- }
921
- ),
922
- .None => {
923
- matches = false;
924
- break;
925
- }
926
- );
927
- };
928
-
929
- cond(
930
- matches => {
931
- found_at = .Some(byte_index);
932
- break;
933
- },
934
- true => ()
935
- );
936
- };
937
-
938
- match(found_at,
939
- .Some(pos) => {
940
- new_bytes := ArrayList(u8).with_capacity(((self_bytes - search_bytes) + new_value._bytes.len()));
1004
+ true => match(self._bytes,
1005
+ .None => self,
1006
+ .Some(self_al) => match(search_value._bytes,
1007
+ .None => self,
1008
+ .Some(search_al) => {
1009
+ self_bytes := self_al.len();
1010
+ search_bytes := search_al.len();
941
1011
 
942
- // Copy bytes before match
943
- i := usize(0);
944
- while ((i < pos)),
945
- (i = (i + usize(1))),
946
- {
947
- byte_opt := self._bytes.get(i);
948
- match(byte_opt,
949
- .Some(byte) => { new_bytes.push(byte); },
950
- .None => ()
951
- );
952
- };
953
-
954
- // Copy replacement bytes
955
- k := usize(0);
956
- while ((k < new_value._bytes.len())),
957
- (k = (k + usize(1))),
958
- {
959
- byte_opt := new_value._bytes.get(k);
960
- match(byte_opt,
961
- .Some(byte) => { new_bytes.push(byte); },
962
- .None => ()
963
- );
964
- };
1012
+ byte_index := usize(0);
1013
+ (found_at : Option(usize)) = .None;
965
1014
 
966
- // Copy bytes after match
967
- m := (pos + search_bytes);
968
- while ((m < self_bytes)),
969
- (m = (m + usize(1))),
1015
+ while ((byte_index <= (self_bytes - search_bytes))),
1016
+ (byte_index = (byte_index + usize(1))),
970
1017
  {
971
- byte_opt := self._bytes.get(m);
972
- match(byte_opt,
973
- .Some(byte) => { new_bytes.push(byte); },
974
- .None => ()
975
- );
976
- };
977
-
978
- return Self(_bytes: new_bytes);
979
- },
980
- .None => self
981
- )
982
- }
983
- )
984
- ),
985
-
986
- /**
987
- * Replace all occurrences of search_value with new_value (like JavaScript replaceAll)
988
- */
989
- replace_all : (fn(self: Self, search_value: Self, new_value: Self) -> Self)(
990
- cond(
991
- search_value.is_empty() => self,
992
- true => {
993
- self_bytes := self._bytes.len();
994
- search_bytes := search_value._bytes.len();
995
- new_bytes := ArrayList(u8).new();
996
-
997
- byte_index := usize(0);
998
- while ((byte_index < self_bytes)),
999
- (byte_index = (byte_index + usize(1))),
1000
- {
1001
- matches := true;
1002
- cond(
1003
- ((byte_index + search_bytes) <= self_bytes) => {
1018
+ matches := true;
1004
1019
  j := usize(0);
1005
1020
  while ((j < search_bytes)),
1006
1021
  (j = (j + usize(1))),
1007
1022
  {
1008
- self_byte_opt := self._bytes.get((byte_index + j));
1009
- search_byte_opt := search_value._bytes.get(j);
1023
+ self_byte_opt := self_al.get((byte_index + j));
1024
+ search_byte_opt := search_al.get(j);
1010
1025
  match(self_byte_opt,
1011
1026
  .Some(self_byte) =>
1012
1027
  match(search_byte_opt,
@@ -1030,39 +1045,141 @@ impl(String,
1030
1045
  }
1031
1046
  );
1032
1047
  };
1033
- },
1034
- true => {
1035
- matches = false;
1036
- }
1037
- );
1038
-
1039
- cond(
1040
- matches => {
1041
- // Copy replacement bytes
1042
- k := usize(0);
1043
- while ((k < new_value._bytes.len())),
1044
- (k = (k + usize(1))),
1045
- {
1046
- byte_opt := new_value._bytes.get(k);
1047
- match(byte_opt,
1048
- .Some(byte) => { new_bytes.push(byte); },
1048
+
1049
+ cond(
1050
+ matches => {
1051
+ found_at = .Some(byte_index);
1052
+ break;
1053
+ },
1054
+ true => ()
1055
+ );
1056
+ };
1057
+
1058
+ match(found_at,
1059
+ .Some(pos) => {
1060
+ (nv_bytes : usize) = match(new_value._bytes, .Some(b) => b.len(), .None => usize(0));
1061
+ new_bytes := ArrayList(u8).with_capacity(((self_bytes - search_bytes) + nv_bytes));
1062
+
1063
+ match(self_al.ptr(),
1064
+ .Some(p) => {
1065
+ if((pos > usize(0)), {
1066
+ new_bytes.extend_from_ptr(p, pos);
1067
+ });
1068
+ },
1049
1069
  .None => ()
1050
1070
  );
1051
- };
1052
- byte_index = ((byte_index + search_bytes) - usize(1));
1053
- },
1054
- true => {
1055
- byte_opt := self._bytes.get(byte_index);
1056
- match(byte_opt,
1057
- .Some(byte) => { new_bytes.push(byte); },
1058
- .None => ()
1071
+
1072
+ match(new_value._bytes,
1073
+ .Some(nv_al) => match(nv_al.ptr(),
1074
+ .Some(p) => new_bytes.extend_from_ptr(p, nv_bytes),
1075
+ .None => ()
1076
+ ),
1077
+ .None => ()
1078
+ );
1079
+
1080
+ (after_pos : usize) = (pos + search_bytes);
1081
+ if((after_pos < self_bytes), {
1082
+ match(self_al.ptr(),
1083
+ .Some(p) => new_bytes.extend_from_ptr((p &+ after_pos), (self_bytes - after_pos)),
1084
+ .None => ()
1085
+ );
1086
+ });
1087
+
1088
+ return Self(_bytes: .Some(new_bytes));
1089
+ },
1090
+ .None => self
1091
+ )
1092
+ }
1093
+ )
1094
+ )
1095
+ )
1096
+ ),
1097
+
1098
+ /**
1099
+ * Replace all occurrences of search_value with new_value (like JavaScript replaceAll)
1100
+ */
1101
+ replace_all : (fn(self: Self, search_value: Self, new_value: Self) -> Self)(
1102
+ cond(
1103
+ search_value.is_empty() => self,
1104
+ true => match(self._bytes,
1105
+ .None => self,
1106
+ .Some(self_al) => match(search_value._bytes,
1107
+ .None => self,
1108
+ .Some(search_al) => {
1109
+ self_bytes := self_al.len();
1110
+ search_bytes := search_al.len();
1111
+ new_bytes := ArrayList(u8).new();
1112
+
1113
+ byte_index := usize(0);
1114
+ while ((byte_index < self_bytes)),
1115
+ (byte_index = (byte_index + usize(1))),
1116
+ {
1117
+ matches := true;
1118
+ cond(
1119
+ ((byte_index + search_bytes) <= self_bytes) => {
1120
+ j := usize(0);
1121
+ while ((j < search_bytes)),
1122
+ (j = (j + usize(1))),
1123
+ {
1124
+ self_byte_opt := self_al.get((byte_index + j));
1125
+ search_byte_opt := search_al.get(j);
1126
+ match(self_byte_opt,
1127
+ .Some(self_byte) =>
1128
+ match(search_byte_opt,
1129
+ .Some(search_byte) => {
1130
+ cond(
1131
+ (self_byte != search_byte) => {
1132
+ matches = false;
1133
+ break;
1134
+ },
1135
+ true => ()
1136
+ );
1137
+ },
1138
+ .None => {
1139
+ matches = false;
1140
+ break;
1141
+ }
1142
+ ),
1143
+ .None => {
1144
+ matches = false;
1145
+ break;
1146
+ }
1147
+ );
1148
+ };
1149
+ },
1150
+ true => {
1151
+ matches = false;
1152
+ }
1059
1153
  );
1060
- }
1061
- );
1062
- };
1063
-
1064
- return Self(_bytes: new_bytes);
1065
- }
1154
+
1155
+ cond(
1156
+ matches => {
1157
+ match(new_value._bytes,
1158
+ .Some(nv_al) => match(nv_al.ptr(),
1159
+ .Some(p) => new_bytes.extend_from_ptr(p, nv_al.len()),
1160
+ .None => ()
1161
+ ),
1162
+ .None => ()
1163
+ );
1164
+ byte_index = ((byte_index + search_bytes) - usize(1));
1165
+ },
1166
+ true => {
1167
+ byte_opt := self_al.get(byte_index);
1168
+ match(byte_opt,
1169
+ .Some(byte) => { new_bytes.push(byte); },
1170
+ .None => ()
1171
+ );
1172
+ }
1173
+ );
1174
+ };
1175
+
1176
+ cond(
1177
+ (new_bytes.len() == usize(0)) => Self(_bytes: .None),
1178
+ true => Self(_bytes: .Some(new_bytes))
1179
+ )
1180
+ }
1181
+ )
1182
+ )
1066
1183
  )
1067
1184
  ),
1068
1185
 
@@ -1070,57 +1187,67 @@ impl(String,
1070
1187
  * Convert ASCII lowercase letters to uppercase (like JavaScript toUpperCase)
1071
1188
  * Note: Only handles ASCII a-z, not full Unicode case mapping
1072
1189
  */
1073
- to_uppercase : (fn(self: Self) -> Self)({
1074
- new_bytes := ArrayList(u8).with_capacity(self._bytes.len());
1075
-
1076
- i := usize(0);
1077
- while ((i < self._bytes.len())),
1078
- (i = (i + usize(1))),
1079
- {
1080
- byte_opt := self._bytes.get(i);
1081
- match(byte_opt,
1082
- .Some(byte) => {
1083
- // ASCII lowercase a-z is 0x61-0x7A, uppercase A-Z is 0x41-0x5A
1084
- new_byte := cond(
1085
- ((byte >= u8(0x61)) && (byte <= u8(0x7A))) => (byte - u8(0x20)),
1086
- true => byte
1190
+ to_uppercase : (fn(self: Self) -> Self)(
1191
+ match(self._bytes,
1192
+ .None => Self(_bytes: .None),
1193
+ .Some(al) => {
1194
+ (total : usize) = al.len();
1195
+ new_bytes := ArrayList(u8).with_capacity(total);
1196
+
1197
+ i := usize(0);
1198
+ while ((i < total)),
1199
+ (i = (i + usize(1))),
1200
+ {
1201
+ byte_opt := al.get(i);
1202
+ match(byte_opt,
1203
+ .Some(byte) => {
1204
+ new_byte := cond(
1205
+ ((byte >= u8(0x61)) && (byte <= u8(0x7A))) => (byte - u8(0x20)),
1206
+ true => byte
1207
+ );
1208
+ new_bytes.push(new_byte);
1209
+ },
1210
+ .None => ()
1087
1211
  );
1088
- new_bytes.push(new_byte);
1089
- },
1090
- .None => ()
1091
- );
1092
- };
1093
-
1094
- return Self(_bytes: new_bytes);
1095
- }),
1212
+ };
1213
+
1214
+ Self(_bytes: .Some(new_bytes))
1215
+ }
1216
+ )
1217
+ ),
1096
1218
 
1097
1219
  /**
1098
1220
  * Convert ASCII uppercase letters to lowercase (like JavaScript toLowerCase)
1099
1221
  * Note: Only handles ASCII A-Z, not full Unicode case mapping
1100
1222
  */
1101
- to_lowercase : (fn(self: Self) -> Self)({
1102
- new_bytes := ArrayList(u8).with_capacity(self._bytes.len());
1103
-
1104
- i := usize(0);
1105
- while ((i < self._bytes.len())),
1106
- (i = (i + usize(1))),
1107
- {
1108
- byte_opt := self._bytes.get(i);
1109
- match(byte_opt,
1110
- .Some(byte) => {
1111
- // ASCII uppercase A-Z is 0x41-0x5A, lowercase a-z is 0x61-0x7A
1112
- new_byte := cond(
1113
- ((byte >= u8(0x41)) && (byte <= u8(0x5A))) => (byte + u8(0x20)),
1114
- true => byte
1223
+ to_lowercase : (fn(self: Self) -> Self)(
1224
+ match(self._bytes,
1225
+ .None => Self(_bytes: .None),
1226
+ .Some(al) => {
1227
+ (total : usize) = al.len();
1228
+ new_bytes := ArrayList(u8).with_capacity(total);
1229
+
1230
+ i := usize(0);
1231
+ while ((i < total)),
1232
+ (i = (i + usize(1))),
1233
+ {
1234
+ byte_opt := al.get(i);
1235
+ match(byte_opt,
1236
+ .Some(byte) => {
1237
+ new_byte := cond(
1238
+ ((byte >= u8(0x41)) && (byte <= u8(0x5A))) => (byte + u8(0x20)),
1239
+ true => byte
1240
+ );
1241
+ new_bytes.push(new_byte);
1242
+ },
1243
+ .None => ()
1115
1244
  );
1116
- new_bytes.push(new_byte);
1117
- },
1118
- .None => ()
1119
- );
1120
- };
1121
-
1122
- return Self(_bytes: new_bytes);
1123
- }),
1245
+ };
1246
+
1247
+ Self(_bytes: .Some(new_bytes))
1248
+ }
1249
+ )
1250
+ ),
1124
1251
 
1125
1252
  /**
1126
1253
  * Helper to check if a byte is ASCII whitespace
@@ -1133,223 +1260,173 @@ impl(String,
1133
1260
  /**
1134
1261
  * Remove whitespace from both ends of the string (like JavaScript trim)
1135
1262
  */
1136
- trim : (fn(self: Self) -> Self)({
1137
- total_bytes := self._bytes.len();
1138
-
1139
- cond(
1140
- (total_bytes == usize(0)) => {
1141
- return new();
1142
- },
1143
- true => ()
1144
- );
1145
-
1146
- // Find start index (skip leading whitespace)
1147
- start_idx := usize(0);
1148
- while ((start_idx < total_bytes)),
1149
- (start_idx = (start_idx + usize(1))),
1150
- {
1151
- byte_opt := self._bytes.get(start_idx);
1152
- match(byte_opt,
1153
- .Some(byte) => {
1154
- is_ws := Self._is_whitespace_byte(byte);
1155
- cond(
1156
- (!(is_ws)) => break,
1157
- true => ()
1158
- );
1159
- },
1160
- .None => break
1161
- );
1162
- };
1163
-
1164
- // Find end index (skip trailing whitespace)
1165
- end_idx := total_bytes;
1166
- while ((end_idx > start_idx)),
1167
- (end_idx = (end_idx - usize(1))),
1168
- {
1169
- byte_opt := self._bytes.get((end_idx - usize(1)));
1170
- match(byte_opt,
1171
- .Some(byte) => {
1172
- is_ws := Self._is_whitespace_byte(byte);
1173
- cond(
1174
- (!(is_ws)) => break,
1175
- true => ()
1176
- );
1177
- },
1178
- .None => break
1179
- );
1180
- };
1181
-
1182
- cond(
1183
- (start_idx >= end_idx) => {
1184
- return new();
1185
- },
1186
- true => ()
1187
- );
1188
-
1189
- new_bytes := ArrayList(u8).with_capacity((end_idx - start_idx));
1190
- i := start_idx;
1191
- while ((i < end_idx)),
1192
- (i = (i + usize(1))),
1193
- {
1194
- byte_opt := self._bytes.get(i);
1195
- match(byte_opt,
1196
- .Some(byte) => { new_bytes.push(byte); },
1197
- .None => ()
1198
- );
1199
- };
1200
- return Self(_bytes: new_bytes);
1201
- }),
1263
+ trim : (fn(self: Self) -> Self)(
1264
+ match(self._bytes,
1265
+ .None => Self(_bytes: .None),
1266
+ .Some(al) => {
1267
+ total_bytes := al.len();
1268
+
1269
+ if((total_bytes == usize(0)), {
1270
+ return Self(_bytes: .None);
1271
+ });
1272
+
1273
+ (start_idx : usize) = usize(0);
1274
+ while (start_idx < total_bytes), {
1275
+ (b : u8) = al(start_idx);
1276
+ if(!(Self._is_whitespace_byte(b)), { break; });
1277
+ start_idx = (start_idx + usize(1));
1278
+ };
1279
+
1280
+ (end_idx : usize) = total_bytes;
1281
+ while (end_idx > start_idx), {
1282
+ (b : u8) = al((end_idx - usize(1)));
1283
+ if(!(Self._is_whitespace_byte(b)), { break; });
1284
+ end_idx = (end_idx - usize(1));
1285
+ };
1286
+
1287
+ if((start_idx >= end_idx), {
1288
+ return Self(_bytes: .None);
1289
+ });
1290
+
1291
+ (count : usize) = (end_idx - start_idx);
1292
+ new_bytes := ArrayList(u8).with_capacity(count);
1293
+ match(al.ptr(),
1294
+ .Some(p) => new_bytes.extend_from_ptr((p &+ start_idx), count),
1295
+ .None => ()
1296
+ );
1297
+ Self(_bytes: .Some(new_bytes))
1298
+ }
1299
+ )
1300
+ ),
1202
1301
 
1203
1302
  /**
1204
1303
  * Remove whitespace from the start of the string (like JavaScript trimStart)
1205
1304
  */
1206
- trim_start : (fn(self: Self) -> Self)({
1207
- total_bytes := self._bytes.len();
1208
-
1209
- cond(
1210
- (total_bytes == usize(0)) => {
1211
- return new();
1212
- },
1213
- true => ()
1214
- );
1215
-
1216
- // Find start index (skip leading whitespace)
1217
- start_idx := usize(0);
1218
- while ((start_idx < total_bytes)),
1219
- (start_idx = (start_idx + usize(1))),
1220
- {
1221
- byte_opt := self._bytes.get(start_idx);
1222
- match(byte_opt,
1223
- .Some(byte) => {
1224
- is_ws := Self._is_whitespace_byte(byte);
1225
- cond(
1226
- (!(is_ws)) => break,
1227
- true => ()
1228
- );
1229
- },
1230
- .None => break
1231
- );
1232
- };
1233
-
1234
- cond(
1235
- (start_idx >= total_bytes) => {
1236
- return new();
1237
- },
1238
- true => ()
1239
- );
1240
-
1241
- new_bytes := ArrayList(u8).with_capacity((total_bytes - start_idx));
1242
- i := start_idx;
1243
- while ((i < total_bytes)),
1244
- (i = (i + usize(1))),
1245
- {
1246
- byte_opt := self._bytes.get(i);
1247
- match(byte_opt,
1248
- .Some(byte) => { new_bytes.push(byte); },
1249
- .None => ()
1250
- );
1251
- };
1252
- return Self(_bytes: new_bytes);
1253
- }),
1305
+ trim_start : (fn(self: Self) -> Self)(
1306
+ match(self._bytes,
1307
+ .None => Self(_bytes: .None),
1308
+ .Some(al) => {
1309
+ total_bytes := al.len();
1310
+
1311
+ cond(
1312
+ (total_bytes == usize(0)) => Self(_bytes: .None),
1313
+ true => {
1314
+ start_idx := usize(0);
1315
+ while ((start_idx < total_bytes)),
1316
+ (start_idx = (start_idx + usize(1))),
1317
+ {
1318
+ byte_opt := al.get(start_idx);
1319
+ match(byte_opt,
1320
+ .Some(byte) => {
1321
+ is_ws := Self._is_whitespace_byte(byte);
1322
+ cond(
1323
+ (!(is_ws)) => break,
1324
+ true => ()
1325
+ );
1326
+ },
1327
+ .None => break
1328
+ );
1329
+ };
1330
+
1331
+ cond(
1332
+ (start_idx >= total_bytes) => Self(_bytes: .None),
1333
+ true => {
1334
+ (count : usize) = (total_bytes - start_idx);
1335
+ new_bytes := ArrayList(u8).with_capacity(count);
1336
+ match(al.ptr(),
1337
+ .Some(p) => new_bytes.extend_from_ptr((p &+ start_idx), count),
1338
+ .None => ()
1339
+ );
1340
+ Self(_bytes: .Some(new_bytes))
1341
+ }
1342
+ )
1343
+ }
1344
+ )
1345
+ }
1346
+ )
1347
+ ),
1254
1348
 
1255
1349
  /**
1256
1350
  * Remove whitespace from the end of the string (like JavaScript trimEnd)
1257
1351
  */
1258
- trim_end : (fn(self: Self) -> Self)({
1259
- total_bytes := self._bytes.len();
1260
-
1261
- cond(
1262
- (total_bytes == usize(0)) => {
1263
- return new();
1264
- },
1265
- true => ()
1266
- );
1267
-
1268
- // Find end index (skip trailing whitespace)
1269
- end_idx := total_bytes;
1270
- while ((end_idx > usize(0))),
1271
- (end_idx = (end_idx - usize(1))),
1272
- {
1273
- byte_opt := self._bytes.get((end_idx - usize(1)));
1274
- match(byte_opt,
1275
- .Some(byte) => {
1276
- is_ws := Self._is_whitespace_byte(byte);
1277
- cond(
1278
- (!(is_ws)) => break,
1279
- true => ()
1280
- );
1281
- },
1282
- .None => break
1283
- );
1284
- };
1285
-
1286
- cond(
1287
- (end_idx == usize(0)) => {
1288
- return new();
1289
- },
1290
- true => ()
1291
- );
1292
-
1293
- new_bytes := ArrayList(u8).with_capacity(end_idx);
1294
- i := usize(0);
1295
- while ((i < end_idx)),
1296
- (i = (i + usize(1))),
1297
- {
1298
- byte_opt := self._bytes.get(i);
1299
- match(byte_opt,
1300
- .Some(byte) => { new_bytes.push(byte); },
1301
- .None => ()
1302
- );
1303
- };
1304
- return Self(_bytes: new_bytes);
1305
- })
1352
+ trim_end : (fn(self: Self) -> Self)(
1353
+ match(self._bytes,
1354
+ .None => Self(_bytes: .None),
1355
+ .Some(al) => {
1356
+ total_bytes := al.len();
1357
+
1358
+ cond(
1359
+ (total_bytes == usize(0)) => Self(_bytes: .None),
1360
+ true => {
1361
+ end_idx := total_bytes;
1362
+ while ((end_idx > usize(0))),
1363
+ (end_idx = (end_idx - usize(1))),
1364
+ {
1365
+ byte_opt := al.get((end_idx - usize(1)));
1366
+ match(byte_opt,
1367
+ .Some(byte) => {
1368
+ is_ws := Self._is_whitespace_byte(byte);
1369
+ cond(
1370
+ (!(is_ws)) => break,
1371
+ true => ()
1372
+ );
1373
+ },
1374
+ .None => break
1375
+ );
1376
+ };
1377
+
1378
+ cond(
1379
+ (end_idx == usize(0)) => Self(_bytes: .None),
1380
+ true => {
1381
+ new_bytes := ArrayList(u8).with_capacity(end_idx);
1382
+ match(al.ptr(),
1383
+ .Some(p) => new_bytes.extend_from_ptr(p, end_idx),
1384
+ .None => ()
1385
+ );
1386
+ Self(_bytes: .Some(new_bytes))
1387
+ }
1388
+ )
1389
+ }
1390
+ )
1391
+ }
1392
+ )
1393
+ )
1306
1394
  );
1307
1395
 
1308
- impl(String, Add(String, String)(
1309
- (+) : (fn(self: Self, other: Self) -> Self)({
1396
+ impl(String, Add(String)(
1397
+ Output : String,
1398
+ (+) : (fn(self: Self, other: Self) -> Self.Output)({
1310
1399
  return self.concat(other);
1311
1400
  })
1312
1401
  ));
1313
1402
 
1314
1403
  impl(String, Eq(String)(
1315
1404
  (==) : (fn(self: Self, other: Self) -> bool)({
1316
- // First compare lengths
1317
- self_len := self._bytes.len();
1318
- other_len := other._bytes.len();
1405
+ (self_len : usize) = match(self._bytes, .Some(b) => b.len(), .None => usize(0));
1406
+ (other_len : usize) = match(other._bytes, .Some(b) => b.len(), .None => usize(0));
1319
1407
 
1320
1408
  cond(
1321
- (self_len != other_len) => {
1322
- return false;
1323
- },
1324
- true => ()
1325
- );
1326
-
1327
- // Compare each byte
1328
- i := usize(0);
1329
- while ((i < self_len)),
1330
- (i = (i + usize(1))),
1331
- {
1332
- self_byte_opt := self._bytes.get(i);
1333
- other_byte_opt := other._bytes.get(i);
1334
-
1335
- match(self_byte_opt,
1336
- .Some(self_byte) =>
1337
- match(other_byte_opt,
1338
- .Some(other_byte) => {
1339
- cond(
1340
- (self_byte != other_byte) => {
1341
- return false;
1342
- },
1343
- true => ()
1344
- );
1345
- },
1346
- .None => return false
1347
- ),
1348
- .None => return false
1349
- );
1350
- };
1351
-
1352
- return true;
1409
+ (self_len != other_len) => false,
1410
+ (self_len == usize(0)) => true,
1411
+ true =>
1412
+ match(self._bytes,
1413
+ .Some(self_al) =>
1414
+ match(other._bytes,
1415
+ .Some(other_al) =>
1416
+ match(self_al.ptr(),
1417
+ .Some(self_ptr) =>
1418
+ match(other_al.ptr(),
1419
+ .Some(other_ptr) =>
1420
+ (memcmp((*(void))(self_ptr), (*(void))(other_ptr), self_len) == int(0)),
1421
+ .None => false
1422
+ ),
1423
+ .None => false
1424
+ ),
1425
+ .None => false
1426
+ ),
1427
+ .None => false
1428
+ )
1429
+ )
1353
1430
  }),
1354
1431
  (!=) : (fn(self: Self, other: Self) -> bool)({
1355
1432
  return !(Self.(==)(self, other));
@@ -1358,21 +1435,25 @@ impl(String, Eq(String)(
1358
1435
 
1359
1436
  impl(String, Hash(
1360
1437
  (hash): (fn(self: *(Self)) -> u64)({
1361
- // FNV-1a hash over the bytes
1362
1438
  h := u64(14695981039346656037);
1363
- i := usize(0);
1364
- len := self.*._bytes.len();
1365
- while ((i < len)),
1366
- (i = (i + usize(1))),
1367
- {
1368
- match(self.*._bytes.get(i),
1369
- .Some(b) => {
1370
- h = (h ^ u64(b));
1371
- h = (h * u64(1099511628211));
1372
- },
1373
- .None => ()
1374
- );
1375
- };
1439
+ match(self.*._bytes,
1440
+ .None => (),
1441
+ .Some(al) => {
1442
+ i := usize(0);
1443
+ len := al.len();
1444
+ while ((i < len)),
1445
+ (i = (i + usize(1))),
1446
+ {
1447
+ match(al.get(i),
1448
+ .Some(b) => {
1449
+ h = (h ^ u64(b));
1450
+ h = (h * u64(1099511628211));
1451
+ },
1452
+ .None => ()
1453
+ );
1454
+ };
1455
+ }
1456
+ );
1376
1457
  h
1377
1458
  })
1378
1459
  ));
@@ -1391,27 +1472,29 @@ StringChars :: struct(
1391
1472
  impl(StringChars, Iterator(
1392
1473
  Item : rune,
1393
1474
  next : (fn(self : *(Self)) -> Option(rune))(
1394
- cond(
1395
- (self._byte_index >= self._string._bytes.len()) => .None,
1396
- true => {
1397
- first_byte_opt := self._string._bytes.get(self._byte_index);
1398
- match(first_byte_opt,
1399
- .Some(first_byte) => {
1400
- // Determine byte length of this UTF-8 character
1401
- (byte_len : usize) = cond(
1402
- (first_byte < u8(0x80)) => usize(1),
1403
- ((first_byte >= u8(0xC0)) && (first_byte < u8(0xE0))) => usize(2),
1404
- ((first_byte >= u8(0xE0)) && (first_byte < u8(0xF0))) => usize(3),
1405
- ((first_byte >= u8(0xF0)) && (first_byte < u8(0xF8))) => usize(4),
1406
- true => usize(1)
1407
- );
1408
- r := self._string._decode_rune_at(self._byte_index);
1409
- self._byte_index = (self._byte_index + byte_len);
1410
- r
1411
- },
1412
- .None => .None
1413
- )
1414
- }
1475
+ match(self._string._bytes,
1476
+ .None => .None,
1477
+ .Some(al) => cond(
1478
+ (self._byte_index >= al.len()) => .None,
1479
+ true => {
1480
+ first_byte_opt := al.get(self._byte_index);
1481
+ match(first_byte_opt,
1482
+ .Some(first_byte) => {
1483
+ (byte_len : usize) = cond(
1484
+ (first_byte < u8(0x80)) => usize(1),
1485
+ ((first_byte >= u8(0xC0)) && (first_byte < u8(0xE0))) => usize(2),
1486
+ ((first_byte >= u8(0xE0)) && (first_byte < u8(0xF0))) => usize(3),
1487
+ ((first_byte >= u8(0xF0)) && (first_byte < u8(0xF8))) => usize(4),
1488
+ true => usize(1)
1489
+ );
1490
+ r := self._string._decode_rune_at(self._byte_index);
1491
+ self._byte_index = (self._byte_index + byte_len);
1492
+ r
1493
+ },
1494
+ .None => .None
1495
+ )
1496
+ }
1497
+ )
1415
1498
  )
1416
1499
  )
1417
1500
  ));
@@ -1428,13 +1511,16 @@ StringBytes :: struct(
1428
1511
  impl(StringBytes, Iterator(
1429
1512
  Item : u8,
1430
1513
  next : (fn(self : *(Self)) -> Option(u8))(
1431
- cond(
1432
- (self._index >= self._string._bytes.len()) => .None,
1433
- true => {
1434
- byte_opt := self._string._bytes.get(self._index);
1435
- self._index = (self._index + usize(1));
1436
- byte_opt
1437
- }
1514
+ match(self._string._bytes,
1515
+ .None => .None,
1516
+ .Some(al) => cond(
1517
+ (self._index >= al.len()) => .None,
1518
+ true => {
1519
+ byte_opt := al.get(self._index);
1520
+ self._index = (self._index + usize(1));
1521
+ byte_opt
1522
+ }
1523
+ )
1438
1524
  )
1439
1525
  )
1440
1526
  ));
@@ -1476,365 +1562,364 @@ impl(String,
1476
1562
  * Parse the string as a signed 32-bit integer.
1477
1563
  * Returns .Some(value) on success, .None on failure (empty, invalid chars, overflow).
1478
1564
  */
1479
- parse_i32 : (fn(self: Self) -> Option(i32))({
1480
- total_bytes := self._bytes.len();
1481
- cond(
1482
- (total_bytes == usize(0)) => {
1483
- return .None;
1484
- },
1485
- true => ()
1486
- );
1487
-
1488
- idx := usize(0);
1489
-
1490
- // Check for sign
1491
- is_negative := false;
1492
- first_byte_opt := self._bytes.get(usize(0));
1493
- match(first_byte_opt,
1494
- .Some(first_byte) => {
1565
+ parse_i32 : (fn(self: Self) -> Option(i32))(
1566
+ match(self._bytes,
1567
+ .None => .None,
1568
+ .Some(al) => {
1569
+ total_bytes := al.len();
1495
1570
  cond(
1496
- (first_byte == u8(45)) => {
1497
- is_negative = true;
1498
- idx = usize(1);
1499
- },
1500
- (first_byte == u8(43)) => {
1501
- idx = usize(1);
1502
- },
1503
- true => ()
1504
- );
1505
- },
1506
- .None => {
1507
- return .None;
1508
- }
1509
- );
1510
-
1511
- // Must have at least one digit after sign
1512
- cond(
1513
- (idx >= total_bytes) => {
1514
- return .None;
1515
- },
1516
- true => ()
1517
- );
1518
-
1519
- result := i64(0);
1520
- has_digit := false;
1521
-
1522
- while ((idx < total_bytes)),
1523
- (idx = (idx + usize(1))),
1524
- {
1525
- byte_opt := self._bytes.get(idx);
1526
- match(byte_opt,
1527
- .Some(byte) => {
1528
- cond(
1529
- Self._is_digit_byte(byte) => {
1530
- digit := i64((byte - u8(48)));
1531
- result = ((result * i64(10)) + digit);
1532
- has_digit = true;
1533
- },
1534
- true => {
1535
- return .None;
1536
- }
1537
- );
1538
- },
1539
- .None => {
1540
- return .None;
1541
- }
1542
- );
1543
- };
1544
-
1545
- cond(
1546
- (!(has_digit)) => {
1547
- return .None;
1548
- },
1549
- true => ()
1550
- );
1551
-
1552
- final_val := cond(
1553
- is_negative => (i64(0) - result),
1554
- true => result
1555
- );
1571
+ (total_bytes == usize(0)) => .None,
1572
+ true => {
1573
+ idx := usize(0);
1574
+ is_negative := false;
1575
+ first_byte_opt := al.get(usize(0));
1576
+ match(first_byte_opt,
1577
+ .Some(first_byte) => {
1578
+ cond(
1579
+ (first_byte == u8(45)) => {
1580
+ is_negative = true;
1581
+ idx = usize(1);
1582
+ },
1583
+ (first_byte == u8(43)) => {
1584
+ idx = usize(1);
1585
+ },
1586
+ true => ()
1587
+ );
1588
+ },
1589
+ .None => {
1590
+ return .None;
1591
+ }
1592
+ );
1556
1593
 
1557
- // Check i32 range: -2147483648 to 2147483647
1558
- cond(
1559
- ((final_val < i64(-2147483648)) || (final_val > i64(2147483647))) => .None,
1560
- true => .Some(i32(final_val))
1594
+ cond(
1595
+ (idx >= total_bytes) => .None,
1596
+ true => {
1597
+ result := i64(0);
1598
+ has_digit := false;
1599
+
1600
+ while ((idx < total_bytes)),
1601
+ (idx = (idx + usize(1))),
1602
+ {
1603
+ byte_opt := al.get(idx);
1604
+ match(byte_opt,
1605
+ .Some(byte) => {
1606
+ cond(
1607
+ Self._is_digit_byte(byte) => {
1608
+ digit := i64((byte - u8(48)));
1609
+ result = ((result * i64(10)) + digit);
1610
+ has_digit = true;
1611
+ },
1612
+ true => {
1613
+ return .None;
1614
+ }
1615
+ );
1616
+ },
1617
+ .None => {
1618
+ return .None;
1619
+ }
1620
+ );
1621
+ };
1622
+
1623
+ cond(
1624
+ (!(has_digit)) => .None,
1625
+ true => {
1626
+ final_val := cond(
1627
+ is_negative => (i64(0) - result),
1628
+ true => result
1629
+ );
1630
+ cond(
1631
+ ((final_val < i64(-2147483648)) || (final_val > i64(2147483647))) => .None,
1632
+ true => .Some(i32(final_val))
1633
+ )
1634
+ }
1635
+ )
1636
+ }
1637
+ )
1638
+ }
1639
+ )
1640
+ }
1561
1641
  )
1562
- }),
1642
+ ),
1563
1643
 
1564
1644
  /**
1565
1645
  * Parse the string as a signed 64-bit integer.
1566
1646
  * Returns .Some(value) on success, .None on failure.
1567
1647
  * Note: does not detect i64 overflow during accumulation.
1568
1648
  */
1569
- parse_i64 : (fn(self: Self) -> Option(i64))({
1570
- total_bytes := self._bytes.len();
1571
- cond(
1572
- (total_bytes == usize(0)) => {
1573
- return .None;
1574
- },
1575
- true => ()
1576
- );
1577
-
1578
- idx := usize(0);
1579
-
1580
- is_negative := false;
1581
- first_byte_opt := self._bytes.get(usize(0));
1582
- match(first_byte_opt,
1583
- .Some(first_byte) => {
1649
+ parse_i64 : (fn(self: Self) -> Option(i64))(
1650
+ match(self._bytes,
1651
+ .None => .None,
1652
+ .Some(al) => {
1653
+ total_bytes := al.len();
1584
1654
  cond(
1585
- (first_byte == u8(45)) => {
1586
- is_negative = true;
1587
- idx = usize(1);
1588
- },
1589
- (first_byte == u8(43)) => {
1590
- idx = usize(1);
1591
- },
1592
- true => ()
1593
- );
1594
- },
1595
- .None => {
1596
- return .None;
1597
- }
1598
- );
1599
-
1600
- cond(
1601
- (idx >= total_bytes) => {
1602
- return .None;
1603
- },
1604
- true => ()
1605
- );
1606
-
1607
- result := i64(0);
1608
- has_digit := false;
1609
-
1610
- while ((idx < total_bytes)),
1611
- (idx = (idx + usize(1))),
1612
- {
1613
- byte_opt := self._bytes.get(idx);
1614
- match(byte_opt,
1615
- .Some(byte) => {
1616
- cond(
1617
- Self._is_digit_byte(byte) => {
1618
- digit := i64((byte - u8(48)));
1619
- result = ((result * i64(10)) + digit);
1620
- has_digit = true;
1621
- },
1622
- true => {
1623
- return .None;
1624
- }
1625
- );
1626
- },
1627
- .None => {
1628
- return .None;
1629
- }
1630
- );
1631
- };
1655
+ (total_bytes == usize(0)) => .None,
1656
+ true => {
1657
+ idx := usize(0);
1658
+ is_negative := false;
1659
+ first_byte_opt := al.get(usize(0));
1660
+ match(first_byte_opt,
1661
+ .Some(first_byte) => {
1662
+ cond(
1663
+ (first_byte == u8(45)) => {
1664
+ is_negative = true;
1665
+ idx = usize(1);
1666
+ },
1667
+ (first_byte == u8(43)) => {
1668
+ idx = usize(1);
1669
+ },
1670
+ true => ()
1671
+ );
1672
+ },
1673
+ .None => {
1674
+ return .None;
1675
+ }
1676
+ );
1632
1677
 
1633
- cond(
1634
- (!(has_digit)) => .None,
1635
- true => {
1636
- final_val := cond(
1637
- is_negative => (i64(0) - result),
1638
- true => result
1639
- );
1640
- return .Some(final_val);
1678
+ cond(
1679
+ (idx >= total_bytes) => .None,
1680
+ true => {
1681
+ result := i64(0);
1682
+ has_digit := false;
1683
+
1684
+ while ((idx < total_bytes)),
1685
+ (idx = (idx + usize(1))),
1686
+ {
1687
+ byte_opt := al.get(idx);
1688
+ match(byte_opt,
1689
+ .Some(byte) => {
1690
+ cond(
1691
+ Self._is_digit_byte(byte) => {
1692
+ digit := i64((byte - u8(48)));
1693
+ result = ((result * i64(10)) + digit);
1694
+ has_digit = true;
1695
+ },
1696
+ true => {
1697
+ return .None;
1698
+ }
1699
+ );
1700
+ },
1701
+ .None => {
1702
+ return .None;
1703
+ }
1704
+ );
1705
+ };
1706
+
1707
+ cond(
1708
+ (!(has_digit)) => .None,
1709
+ true => {
1710
+ final_val := cond(
1711
+ is_negative => (i64(0) - result),
1712
+ true => result
1713
+ );
1714
+ return .Some(final_val);
1715
+ }
1716
+ )
1717
+ }
1718
+ )
1719
+ }
1720
+ )
1641
1721
  }
1642
1722
  )
1643
- }),
1723
+ ),
1644
1724
 
1645
1725
  /**
1646
1726
  * Parse the string as an unsigned 32-bit integer.
1647
1727
  * Returns .Some(value) on success, .None on failure (empty, invalid chars, overflow, negative sign).
1648
1728
  */
1649
- parse_u32 : (fn(self: Self) -> Option(u32))({
1650
- total_bytes := self._bytes.len();
1651
- cond(
1652
- (total_bytes == usize(0)) => {
1653
- return .None;
1654
- },
1655
- true => ()
1656
- );
1657
-
1658
- idx := usize(0);
1659
-
1660
- // Optional '+' sign, but '-' is invalid for unsigned
1661
- first_byte_opt := self._bytes.get(usize(0));
1662
- match(first_byte_opt,
1663
- .Some(first_byte) => {
1664
- cond(
1665
- (first_byte == u8(45)) => {
1666
- return .None;
1667
- },
1668
- (first_byte == u8(43)) => {
1669
- idx = usize(1);
1670
- },
1671
- true => ()
1672
- );
1673
- },
1674
- .None => {
1675
- return .None;
1676
- }
1677
- );
1678
-
1679
- cond(
1680
- (idx >= total_bytes) => {
1681
- return .None;
1682
- },
1683
- true => ()
1684
- );
1685
-
1686
- result := u64(0);
1687
- has_digit := false;
1688
-
1689
- while ((idx < total_bytes)),
1690
- (idx = (idx + usize(1))),
1691
- {
1692
- byte_opt := self._bytes.get(idx);
1693
- match(byte_opt,
1694
- .Some(byte) => {
1695
- cond(
1696
- Self._is_digit_byte(byte) => {
1697
- digit := u64((byte - u8(48)));
1698
- result = ((result * u64(10)) + digit);
1699
- has_digit = true;
1700
- },
1701
- true => {
1702
- return .None;
1703
- }
1704
- );
1705
- },
1706
- .None => {
1707
- return .None;
1708
- }
1709
- );
1710
- };
1711
-
1712
- cond(
1713
- (!(has_digit)) => .None,
1714
- true => {
1715
- // Check u32 range: 0 to 4294967295
1729
+ parse_u32 : (fn(self: Self) -> Option(u32))(
1730
+ match(self._bytes,
1731
+ .None => .None,
1732
+ .Some(al) => {
1733
+ total_bytes := al.len();
1716
1734
  cond(
1717
- (result > u64(4294967295)) => {
1718
- return .None;
1719
- },
1735
+ (total_bytes == usize(0)) => .None,
1720
1736
  true => {
1721
- return .Some(u32(result));
1737
+ idx := usize(0);
1738
+ first_byte_opt := al.get(usize(0));
1739
+ match(first_byte_opt,
1740
+ .Some(first_byte) => {
1741
+ cond(
1742
+ (first_byte == u8(45)) => {
1743
+ return .None;
1744
+ },
1745
+ (first_byte == u8(43)) => {
1746
+ idx = usize(1);
1747
+ },
1748
+ true => ()
1749
+ );
1750
+ },
1751
+ .None => {
1752
+ return .None;
1753
+ }
1754
+ );
1755
+
1756
+ cond(
1757
+ (idx >= total_bytes) => .None,
1758
+ true => {
1759
+ result := u64(0);
1760
+ has_digit := false;
1761
+
1762
+ while ((idx < total_bytes)),
1763
+ (idx = (idx + usize(1))),
1764
+ {
1765
+ byte_opt := al.get(idx);
1766
+ match(byte_opt,
1767
+ .Some(byte) => {
1768
+ cond(
1769
+ Self._is_digit_byte(byte) => {
1770
+ digit := u64((byte - u8(48)));
1771
+ result = ((result * u64(10)) + digit);
1772
+ has_digit = true;
1773
+ },
1774
+ true => {
1775
+ return .None;
1776
+ }
1777
+ );
1778
+ },
1779
+ .None => {
1780
+ return .None;
1781
+ }
1782
+ );
1783
+ };
1784
+
1785
+ cond(
1786
+ (!(has_digit)) => .None,
1787
+ true => cond(
1788
+ (result > u64(4294967295)) => .None,
1789
+ true => .Some(u32(result))
1790
+ )
1791
+ )
1792
+ }
1793
+ )
1722
1794
  }
1723
- );
1795
+ )
1724
1796
  }
1725
1797
  )
1726
- }),
1798
+ ),
1727
1799
 
1728
1800
  /**
1729
1801
  * Parse the string as an unsigned 64-bit integer.
1730
1802
  * Returns .Some(value) on success, .None on failure.
1731
1803
  * Note: does not detect u64 overflow during accumulation.
1732
1804
  */
1733
- parse_u64 : (fn(self: Self) -> Option(u64))({
1734
- total_bytes := self._bytes.len();
1735
- cond(
1736
- (total_bytes == usize(0)) => {
1737
- return .None;
1738
- },
1739
- true => ()
1740
- );
1741
-
1742
- idx := usize(0);
1743
-
1744
- first_byte_opt := self._bytes.get(usize(0));
1745
- match(first_byte_opt,
1746
- .Some(first_byte) => {
1805
+ parse_u64 : (fn(self: Self) -> Option(u64))(
1806
+ match(self._bytes,
1807
+ .None => .None,
1808
+ .Some(al) => {
1809
+ total_bytes := al.len();
1747
1810
  cond(
1748
- (first_byte == u8(45)) => {
1749
- return .None;
1750
- },
1751
- (first_byte == u8(43)) => {
1752
- idx = usize(1);
1753
- },
1754
- true => ()
1755
- );
1756
- },
1757
- .None => {
1758
- return .None;
1759
- }
1760
- );
1761
-
1762
- cond(
1763
- (idx >= total_bytes) => {
1764
- return .None;
1765
- },
1766
- true => ()
1767
- );
1811
+ (total_bytes == usize(0)) => .None,
1812
+ true => {
1813
+ idx := usize(0);
1814
+ first_byte_opt := al.get(usize(0));
1815
+ match(first_byte_opt,
1816
+ .Some(first_byte) => {
1817
+ cond(
1818
+ (first_byte == u8(45)) => {
1819
+ return .None;
1820
+ },
1821
+ (first_byte == u8(43)) => {
1822
+ idx = usize(1);
1823
+ },
1824
+ true => ()
1825
+ );
1826
+ },
1827
+ .None => {
1828
+ return .None;
1829
+ }
1830
+ );
1768
1831
 
1769
- result := u64(0);
1770
- has_digit := false;
1771
-
1772
- while ((idx < total_bytes)),
1773
- (idx = (idx + usize(1))),
1774
- {
1775
- byte_opt := self._bytes.get(idx);
1776
- match(byte_opt,
1777
- .Some(byte) => {
1778
- cond(
1779
- Self._is_digit_byte(byte) => {
1780
- digit := u64((byte - u8(48)));
1781
- result = ((result * u64(10)) + digit);
1782
- has_digit = true;
1783
- },
1784
- true => {
1785
- return .None;
1786
- }
1787
- );
1788
- },
1789
- .None => {
1790
- return .None;
1791
- }
1792
- );
1793
- };
1832
+ cond(
1833
+ (idx >= total_bytes) => .None,
1834
+ true => {
1835
+ result := u64(0);
1836
+ has_digit := false;
1837
+
1838
+ while ((idx < total_bytes)),
1839
+ (idx = (idx + usize(1))),
1840
+ {
1841
+ byte_opt := al.get(idx);
1842
+ match(byte_opt,
1843
+ .Some(byte) => {
1844
+ cond(
1845
+ Self._is_digit_byte(byte) => {
1846
+ digit := u64((byte - u8(48)));
1847
+ result = ((result * u64(10)) + digit);
1848
+ has_digit = true;
1849
+ },
1850
+ true => {
1851
+ return .None;
1852
+ }
1853
+ );
1854
+ },
1855
+ .None => {
1856
+ return .None;
1857
+ }
1858
+ );
1859
+ };
1794
1860
 
1795
- cond(
1796
- (!(has_digit)) => .None,
1797
- true => .Some(result)
1861
+ cond(
1862
+ (!(has_digit)) => .None,
1863
+ true => .Some(result)
1864
+ )
1865
+ }
1866
+ )
1867
+ }
1868
+ )
1869
+ }
1798
1870
  )
1799
- }),
1871
+ ),
1800
1872
 
1801
1873
  /**
1802
1874
  * Parse the string as a boolean.
1803
1875
  * Returns .Some(true) for "true", .Some(false) for "false", .None for anything else.
1804
1876
  */
1805
- parse_bool : (fn(self: Self) -> Option(bool))({
1806
- total_bytes := self._bytes.len();
1807
- // "true" = 4 bytes: 116(t), 114(r), 117(u), 101(e)
1808
- // "false" = 5 bytes: 102(f), 97(a), 108(l), 115(s), 101(e)
1809
- cond(
1810
- (total_bytes == usize(4)) => {
1811
- b0 := self._bytes.get(usize(0)).unwrap_or(u8(0));
1812
- b1 := self._bytes.get(usize(1)).unwrap_or(u8(0));
1813
- b2 := self._bytes.get(usize(2)).unwrap_or(u8(0));
1814
- b3 := self._bytes.get(usize(3)).unwrap_or(u8(0));
1877
+ parse_bool : (fn(self: Self) -> Option(bool))(
1878
+ match(self._bytes,
1879
+ .None => .None,
1880
+ .Some(al) => {
1881
+ total_bytes := al.len();
1815
1882
  cond(
1816
- ((b0 == u8(116)) && ((b1 == u8(114)) && ((b2 == u8(117)) && (b3 == u8(101))))) =>
1817
- .Some(true),
1818
- true => .None
1819
- )
1820
- },
1821
- (total_bytes == usize(5)) => {
1822
- b0 := self._bytes.get(usize(0)).unwrap_or(u8(0));
1823
- b1 := self._bytes.get(usize(1)).unwrap_or(u8(0));
1824
- b2 := self._bytes.get(usize(2)).unwrap_or(u8(0));
1825
- b3 := self._bytes.get(usize(3)).unwrap_or(u8(0));
1826
- b4 := self._bytes.get(usize(4)).unwrap_or(u8(0));
1827
- cond(
1828
- ((b0 == u8(102)) && ((b1 == u8(97)) && ((b2 == u8(108)) && ((b3 == u8(115)) && (b4 == u8(101)))))) =>
1829
- .Some(false),
1883
+ (total_bytes == usize(4)) => {
1884
+ b0 := al.get(usize(0)).unwrap_or(u8(0));
1885
+ b1 := al.get(usize(1)).unwrap_or(u8(0));
1886
+ b2 := al.get(usize(2)).unwrap_or(u8(0));
1887
+ b3 := al.get(usize(3)).unwrap_or(u8(0));
1888
+ cond(
1889
+ ((b0 == u8(116)) && ((b1 == u8(114)) && ((b2 == u8(117)) && (b3 == u8(101))))) =>
1890
+ .Some(true),
1891
+ true => .None
1892
+ )
1893
+ },
1894
+ (total_bytes == usize(5)) => {
1895
+ b0 := al.get(usize(0)).unwrap_or(u8(0));
1896
+ b1 := al.get(usize(1)).unwrap_or(u8(0));
1897
+ b2 := al.get(usize(2)).unwrap_or(u8(0));
1898
+ b3 := al.get(usize(3)).unwrap_or(u8(0));
1899
+ b4 := al.get(usize(4)).unwrap_or(u8(0));
1900
+ cond(
1901
+ ((b0 == u8(102)) && ((b1 == u8(97)) && ((b2 == u8(108)) && ((b3 == u8(115)) && (b4 == u8(101)))))) =>
1902
+ .Some(false),
1903
+ true => .None
1904
+ )
1905
+ },
1830
1906
  true => .None
1831
1907
  )
1832
- },
1833
- true => .None
1908
+ }
1834
1909
  )
1835
- })
1910
+ )
1836
1911
  );
1837
1912
 
1913
+ impl(String, Index(usize)(
1914
+ Output : u8,
1915
+ index : (fn(self: *(Self), idx: usize) -> *(Self.Output))(
1916
+ match(self.*._bytes,
1917
+ .Some(bytes) => &(bytes(idx)),
1918
+ .None => panic("String: index on empty string")
1919
+ )
1920
+ )
1921
+ ));
1922
+
1838
1923
  export
1839
1924
  String,
1840
1925
  StringError,