@shd101wyy/yo 0.1.14 → 0.1.16

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 (60) hide show
  1. package/README.md +43 -1
  2. package/out/cjs/index.cjs +581 -601
  3. package/out/cjs/yo-cli.cjs +739 -733
  4. package/out/cjs/yo-lsp.cjs +11615 -0
  5. package/out/esm/index.mjs +530 -550
  6. package/out/types/src/cache.d.ts +2 -0
  7. package/out/types/src/codegen/exprs/return.d.ts +1 -1
  8. package/out/types/src/codegen/types/generation.d.ts +0 -2
  9. package/out/types/src/codegen/utils/index.d.ts +2 -8
  10. package/out/types/src/doc/extractor.d.ts +4 -0
  11. package/out/types/src/evaluator/builtins/rc-fns.d.ts +0 -5
  12. package/out/types/src/evaluator/context.d.ts +7 -3
  13. package/out/types/src/evaluator/trait-checking.d.ts +2 -0
  14. package/out/types/src/evaluator/types/object.d.ts +2 -1
  15. package/out/types/src/evaluator/types/struct.d.ts +2 -1
  16. package/out/types/src/evaluator/types/utils.d.ts +3 -8
  17. package/out/types/src/evaluator/values/impl.d.ts +9 -1
  18. package/out/types/src/expr.d.ts +1 -2
  19. package/out/types/src/function-value.d.ts +1 -0
  20. package/out/types/src/lsp/completion.d.ts +3 -0
  21. package/out/types/src/lsp/definition.d.ts +3 -0
  22. package/out/types/src/lsp/diagnostics.d.ts +6 -0
  23. package/out/types/src/lsp/document-manager.d.ts +31 -0
  24. package/out/types/src/lsp/folding.d.ts +3 -0
  25. package/out/types/src/lsp/hover.d.ts +3 -0
  26. package/out/types/src/lsp/inlay-hints.d.ts +3 -0
  27. package/out/types/src/lsp/references.d.ts +3 -0
  28. package/out/types/src/lsp/rename.d.ts +16 -0
  29. package/out/types/src/lsp/server.d.ts +1 -0
  30. package/out/types/src/lsp/signature-help.d.ts +3 -0
  31. package/out/types/src/lsp/symbols.d.ts +3 -0
  32. package/out/types/src/lsp/utils.d.ts +11 -0
  33. package/out/types/src/tests/lsp.test.d.ts +1 -0
  34. package/out/types/src/tests/version.test.d.ts +1 -0
  35. package/out/types/src/types/creators.d.ts +2 -3
  36. package/out/types/src/types/definitions.d.ts +3 -6
  37. package/out/types/src/types/guards.d.ts +5 -2
  38. package/out/types/src/types/tags.d.ts +0 -1
  39. package/out/types/src/version-cache.d.ts +7 -0
  40. package/out/types/src/version.d.ts +5 -0
  41. package/out/types/tsconfig.tsbuildinfo +1 -1
  42. package/package.json +3 -1
  43. package/scripts/build-site.ts +32 -15
  44. package/scripts/check-liburing.js +2 -2
  45. package/std/imm/list.yo +254 -0
  46. package/std/imm/map.yo +917 -0
  47. package/std/imm/set.yo +179 -0
  48. package/std/imm/sorted_map.yo +595 -0
  49. package/std/imm/sorted_set.yo +202 -0
  50. package/std/imm/string.yo +667 -0
  51. package/std/imm/vec.yo +456 -0
  52. package/std/prelude.yo +22 -5
  53. package/std/sync/channel.yo +92 -44
  54. package/std/sync/cond.yo +5 -1
  55. package/std/sync/mutex.yo +5 -1
  56. package/std/sync/once.yo +2 -1
  57. package/std/sync/rwlock.yo +2 -1
  58. package/std/sync/waitgroup.yo +2 -1
  59. package/out/types/src/codegen/exprs/arc.d.ts +0 -5
  60. package/out/types/src/evaluator/calls/arc.d.ts +0 -15
@@ -0,0 +1,667 @@
1
+ //! Immutable, thread-safe UTF-8 string type.
2
+ //!
3
+ //! Uses atomic reference counting for safe sharing across threads.
4
+ //! All "modification" operations return a new string; the original is unchanged.
5
+
6
+ { GlobalAllocator } :: import "../allocator.yo";
7
+ { malloc, free } :: GlobalAllocator;
8
+ { memcpy, memcmp } :: import "../libc/string.yo";
9
+ { rune } :: import "../string/rune.yo";
10
+ std_string :: import "../string";
11
+ { ToString } :: import "../fmt/to_string.yo";
12
+ imm_list :: import "./list.yo";
13
+
14
+ _min :: (fn(a: usize, b: usize) -> usize)(
15
+ cond((a < b) => a, true => b)
16
+ );
17
+
18
+ _alloc_bytes :: (fn(n: usize) -> *(u8))({
19
+ match(malloc(n),
20
+ .Some(p) => *(u8)(p),
21
+ .None => panic("imm.String: allocation failed")
22
+ )
23
+ });
24
+
25
+ /// Immutable, thread-safe UTF-8 string.
26
+ ///
27
+ /// Uses atomic RC — cheap to clone, safe to share across threads.
28
+ /// No mutable operations. All "modification" methods return a new String.
29
+ ///
30
+ /// Empty strings are zero-allocation (null pointer, zero length).
31
+ String :: atomic object(
32
+ _ptr : ?*(u8),
33
+ _len : usize,
34
+ _capacity : usize
35
+ );
36
+
37
+ impl(String, Dispose(
38
+ dispose : (fn(self: Self) -> unit)({
39
+ match(self._ptr,
40
+ .Some(p) => free(.Some(*(void)(p))),
41
+ .None => ()
42
+ );
43
+ })
44
+ ));
45
+
46
+ impl(String,
47
+ /// Create a new empty string (zero allocation).
48
+ new : (fn() -> Self)(
49
+ Self(_ptr: .None, _len: usize(0), _capacity: usize(0))
50
+ ),
51
+
52
+ /// Create a string from a `str` slice (copies the bytes).
53
+ from : (fn(s: str) -> Self)({
54
+ slen := s.len();
55
+ if((slen == usize(0)), {
56
+ return Self(_ptr: .None, _len: usize(0), _capacity: usize(0));
57
+ });
58
+ ptr := _alloc_bytes(slen);
59
+ memcpy(*(void)(ptr), *(void)(s.ptr()), slen);
60
+ return Self(_ptr: .Some(ptr), _len: slen, _capacity: slen);
61
+ }),
62
+
63
+ /// Create a string from a standard (mutable) String by copying its bytes.
64
+ from_string : (fn(s: std_string.String) -> Self)({
65
+ slen := s.bytes_len();
66
+ if((slen == usize(0)), {
67
+ return Self(_ptr: .None, _len: usize(0), _capacity: usize(0));
68
+ });
69
+ ptr := _alloc_bytes(slen);
70
+ (as_str : str) = s.as_str();
71
+ memcpy(*(void)(ptr), *(void)(as_str.ptr()), slen);
72
+ return Self(_ptr: .Some(ptr), _len: slen, _capacity: slen);
73
+ }),
74
+
75
+ /// Byte length of the string.
76
+ bytes_len : (fn(self: Self) -> usize)(
77
+ self._len
78
+ ),
79
+
80
+ /// Number of Unicode characters (runes) in the string.
81
+ len : (fn(self: Self) -> usize)({
82
+ if((self._len == usize(0)), {
83
+ return usize(0);
84
+ });
85
+ count := usize(0);
86
+ i := usize(0);
87
+ match(self._ptr,
88
+ .None => (),
89
+ .Some(p) => {
90
+ while (i < self._len), (i = (i + usize(1))), {
91
+ (b : u8) = (p &+ i).*;
92
+ if(((b & u8(0xC0)) != u8(0x80)), {
93
+ count = (count + usize(1));
94
+ });
95
+ };
96
+ }
97
+ );
98
+ count
99
+ }),
100
+
101
+ /// Check if the string is empty.
102
+ is_empty : (fn(self: Self) -> bool)(
103
+ (self._len == usize(0))
104
+ ),
105
+
106
+ /// Get a `str` view of the string's bytes (zero-copy borrow).
107
+ as_str : (fn(self: Self) -> str)(
108
+ match(self._ptr,
109
+ .Some(p) => str.from_raw_parts(p, self._len),
110
+ .None => str.from_raw_parts(*(u8)(""), usize(0))
111
+ )
112
+ ),
113
+
114
+ /// Get the byte at a given index, or `.None` if out of bounds.
115
+ byte_at : (fn(self: Self, index: usize) -> Option(u8))(
116
+ cond(
117
+ (index >= self._len) => .None,
118
+ true => match(self._ptr,
119
+ .Some(p) => .Some((p &+ index).*),
120
+ .None => .None
121
+ )
122
+ )
123
+ ),
124
+
125
+ /// Concatenate two strings, returning a new string.
126
+ /// Uses COW: if this string has a unique reference and enough capacity,
127
+ /// appends in-place.
128
+ concat : (fn(own(self): Self, other: Self) -> Self)({
129
+ if((self._len == usize(0)), {
130
+ unsafe.drop(self);
131
+ return other;
132
+ });
133
+ if((other._len == usize(0)), {
134
+ return self;
135
+ });
136
+ new_len := (self._len + other._len);
137
+ if((rc(self) == usize(1)), {
138
+ if((new_len <= self._capacity), {
139
+ match(other._ptr,
140
+ .Some(op) => match(self._ptr,
141
+ .Some(sp) => { memcpy(*(void)((sp &+ self._len)), *(void)(op), other._len); },
142
+ .None => ()
143
+ ),
144
+ .None => ()
145
+ );
146
+ self._len = new_len;
147
+ return self;
148
+ });
149
+ new_cap := cond(
150
+ (new_len > (self._capacity * usize(2))) => new_len,
151
+ true => (self._capacity * usize(2))
152
+ );
153
+ ptr := _alloc_bytes(new_cap);
154
+ match(self._ptr,
155
+ .Some(sp) => { memcpy(*(void)(ptr), *(void)(sp), self._len); free(.Some(*(void)(sp))); },
156
+ .None => ()
157
+ );
158
+ match(other._ptr,
159
+ .Some(op) => { memcpy(*(void)((ptr &+ self._len)), *(void)(op), other._len); },
160
+ .None => ()
161
+ );
162
+ self._ptr = .Some(ptr);
163
+ self._len = new_len;
164
+ self._capacity = new_cap;
165
+ return self;
166
+ });
167
+ ptr := _alloc_bytes(new_len);
168
+ match(self._ptr,
169
+ .Some(sp) => { memcpy(*(void)(ptr), *(void)(sp), self._len); },
170
+ .None => ()
171
+ );
172
+ match(other._ptr,
173
+ .Some(op) => { memcpy(*(void)((ptr &+ self._len)), *(void)(op), other._len); },
174
+ .None => ()
175
+ );
176
+ unsafe.drop(self);
177
+ return Self(_ptr: .Some(ptr), _len: new_len, _capacity: new_len);
178
+ }),
179
+
180
+ /// Extract a byte-range substring [start, end). Returns a new string.
181
+ /// If indices are out of bounds, clamps to valid range.
182
+ slice : (fn(self: Self, start: usize, end: usize) -> Self)({
183
+ (clamped_start : usize) = _min(start, self._len);
184
+ (clamped_end : usize) = _min(end, self._len);
185
+ if((clamped_start >= clamped_end), {
186
+ return Self.new();
187
+ });
188
+ new_len := (clamped_end - clamped_start);
189
+ ptr := _alloc_bytes(new_len);
190
+ match(self._ptr,
191
+ .Some(sp) => { memcpy(*(void)(ptr), *(void)((sp &+ clamped_start)), new_len); },
192
+ .None => ()
193
+ );
194
+ return Self(_ptr: .Some(ptr), _len: new_len, _capacity: new_len);
195
+ }),
196
+
197
+ /// Check if the string starts with a given prefix.
198
+ starts_with : (fn(self: Self, prefix: Self) -> bool)({
199
+ if((prefix._len > self._len), {
200
+ return false;
201
+ });
202
+ if((prefix._len == usize(0)), {
203
+ return true;
204
+ });
205
+ match(self._ptr,
206
+ .Some(sp) => match(prefix._ptr,
207
+ .Some(pp) => (memcmp(*(void)(sp), *(void)(pp), prefix._len) == int(0)),
208
+ .None => true
209
+ ),
210
+ .None => (prefix._len == usize(0))
211
+ )
212
+ }),
213
+
214
+ /// Check if the string ends with a given suffix.
215
+ ends_with : (fn(self: Self, suffix: Self) -> bool)({
216
+ if((suffix._len > self._len), {
217
+ return false;
218
+ });
219
+ if((suffix._len == usize(0)), {
220
+ return true;
221
+ });
222
+ offset := (self._len - suffix._len);
223
+ match(self._ptr,
224
+ .Some(sp) => match(suffix._ptr,
225
+ .Some(xp) => (memcmp(*(void)((sp &+ offset)), *(void)(xp), suffix._len) == int(0)),
226
+ .None => true
227
+ ),
228
+ .None => (suffix._len == usize(0))
229
+ )
230
+ }),
231
+
232
+ /// Find the first occurrence of `needle` starting at `from_index`.
233
+ /// Returns byte index or `.None`.
234
+ index_of : (fn(self: Self, needle: Self, from_index: usize) -> Option(usize))({
235
+ if((needle._len == usize(0)), {
236
+ return .Some(from_index);
237
+ });
238
+ if(((from_index + needle._len) > self._len), {
239
+ return .None;
240
+ });
241
+ match(self._ptr,
242
+ .None => { return .None; },
243
+ .Some(sp) => match(needle._ptr,
244
+ .None => { return .Some(from_index); },
245
+ .Some(np) => {
246
+ i := from_index;
247
+ limit := ((self._len - needle._len) + usize(1));
248
+ while (i < limit), (i = (i + usize(1))), {
249
+ if((memcmp(*(void)((sp &+ i)), *(void)(np), needle._len) == int(0)), {
250
+ return .Some(i);
251
+ });
252
+ };
253
+ return .None;
254
+ }
255
+ )
256
+ )
257
+ }),
258
+
259
+ /// Check if the string contains a given substring.
260
+ contains : (fn(self: Self, needle: Self) -> bool)(
261
+ self.index_of(needle, usize(0)).is_some()
262
+ ),
263
+
264
+ /// Split the string by a separator, returning an immutable List of Strings.
265
+ split : (fn(self: Self, sep: Self) -> imm_list.List(String))({
266
+ result := imm_list.List(String).new();
267
+ if((sep._len == usize(0)), {
268
+ return result.prepend(self);
269
+ });
270
+ pos := usize(0);
271
+ segments := imm_list.List(String).new();
272
+ while runtime(true), {
273
+ match(self.index_of(sep, pos),
274
+ .Some(idx) => {
275
+ segments = segments.prepend(self.slice(pos, idx));
276
+ pos = (idx + sep._len);
277
+ },
278
+ .None => {
279
+ segments = segments.prepend(self.slice(pos, self._len));
280
+ return segments.reverse();
281
+ }
282
+ );
283
+ };
284
+ segments.reverse()
285
+ }),
286
+
287
+ /// Check if a byte is ASCII whitespace.
288
+ _is_whitespace : (fn(b: u8) -> bool)(
289
+ ((((b == u8(0x20)) || (b == u8(0x09))) || (b == u8(0x0A))) || ((b == u8(0x0D)) || (b == u8(0x0C))))
290
+ ),
291
+
292
+ /// Trim leading and trailing ASCII whitespace.
293
+ trim : (fn(self: Self) -> Self)({
294
+ if((self._len == usize(0)), {
295
+ return self;
296
+ });
297
+ match(self._ptr,
298
+ .None => { return self; },
299
+ .Some(p) => {
300
+ start := usize(0);
301
+ while ((start < self._len) && (Self._is_whitespace((p &+ start).*))),
302
+ (start = (start + usize(1))),
303
+ ();
304
+ end := self._len;
305
+ while ((end > start) && (Self._is_whitespace((p &+ (end - usize(1))).*))),
306
+ (end = (end - usize(1))),
307
+ ();
308
+ if(((start == usize(0)) && (end == self._len)), {
309
+ return self;
310
+ });
311
+ return self.slice(start, end);
312
+ }
313
+ )
314
+ }),
315
+
316
+ /// Trim leading ASCII whitespace.
317
+ trim_start : (fn(self: Self) -> Self)({
318
+ if((self._len == usize(0)), {
319
+ return self;
320
+ });
321
+ match(self._ptr,
322
+ .None => { return self; },
323
+ .Some(p) => {
324
+ start := usize(0);
325
+ while ((start < self._len) && (Self._is_whitespace((p &+ start).*))),
326
+ (start = (start + usize(1))),
327
+ ();
328
+ if((start == usize(0)), {
329
+ return self;
330
+ });
331
+ return self.slice(start, self._len);
332
+ }
333
+ )
334
+ }),
335
+
336
+ /// Trim trailing ASCII whitespace.
337
+ trim_end : (fn(self: Self) -> Self)({
338
+ if((self._len == usize(0)), {
339
+ return self;
340
+ });
341
+ match(self._ptr,
342
+ .None => { return self; },
343
+ .Some(p) => {
344
+ end := self._len;
345
+ while ((end > usize(0)) && (Self._is_whitespace((p &+ (end - usize(1))).*))),
346
+ (end = (end - usize(1))),
347
+ ();
348
+ if((end == self._len), {
349
+ return self;
350
+ });
351
+ return self.slice(usize(0), end);
352
+ }
353
+ )
354
+ }),
355
+
356
+ /// Convert all ASCII uppercase letters to lowercase.
357
+ /// Uses COW: if this string has a unique reference, modifies bytes in-place.
358
+ to_lowercase : (fn(own(self): Self) -> Self)({
359
+ if((self._len == usize(0)), {
360
+ return self;
361
+ });
362
+ match(self._ptr,
363
+ .None => { return self; },
364
+ .Some(sp) => {
365
+ if((rc(self) == usize(1)), {
366
+ i := usize(0);
367
+ while (i < self._len), (i = (i + usize(1))), {
368
+ (b : u8) = (sp &+ i).*;
369
+ if(((b >= u8(0x41)) && (b <= u8(0x5A))), {
370
+ (sp &+ i).* = (b + u8(32));
371
+ });
372
+ };
373
+ return self;
374
+ });
375
+ ptr := _alloc_bytes(self._len);
376
+ memcpy(*(void)(ptr), *(void)(sp), self._len);
377
+ i := usize(0);
378
+ while (i < self._len), (i = (i + usize(1))), {
379
+ (b : u8) = (ptr &+ i).*;
380
+ if(((b >= u8(0x41)) && (b <= u8(0x5A))), {
381
+ (ptr &+ i).* = (b + u8(32));
382
+ });
383
+ };
384
+ saved_len := self._len;
385
+ unsafe.drop(self);
386
+ return Self(_ptr: .Some(ptr), _len: saved_len, _capacity: saved_len);
387
+ }
388
+ )
389
+ }),
390
+
391
+ /// Convert all ASCII lowercase letters to uppercase.
392
+ /// Uses COW: if this string has a unique reference, modifies bytes in-place.
393
+ to_uppercase : (fn(own(self): Self) -> Self)({
394
+ if((self._len == usize(0)), {
395
+ return self;
396
+ });
397
+ match(self._ptr,
398
+ .None => { return self; },
399
+ .Some(sp) => {
400
+ if((rc(self) == usize(1)), {
401
+ i := usize(0);
402
+ while (i < self._len), (i = (i + usize(1))), {
403
+ (b : u8) = (sp &+ i).*;
404
+ if(((b >= u8(0x61)) && (b <= u8(0x7A))), {
405
+ (sp &+ i).* = (b - u8(32));
406
+ });
407
+ };
408
+ return self;
409
+ });
410
+ ptr := _alloc_bytes(self._len);
411
+ memcpy(*(void)(ptr), *(void)(sp), self._len);
412
+ i := usize(0);
413
+ while (i < self._len), (i = (i + usize(1))), {
414
+ (b : u8) = (ptr &+ i).*;
415
+ if(((b >= u8(0x61)) && (b <= u8(0x7A))), {
416
+ (ptr &+ i).* = (b - u8(32));
417
+ });
418
+ };
419
+ saved_len := self._len;
420
+ unsafe.drop(self);
421
+ return Self(_ptr: .Some(ptr), _len: saved_len, _capacity: saved_len);
422
+ }
423
+ )
424
+ }),
425
+
426
+ /// Replace the first occurrence of `search` with `replacement`.
427
+ replace : (fn(self: Self, search: Self, replacement: Self) -> Self)({
428
+ match(self.index_of(search, usize(0)),
429
+ .None => { return self; },
430
+ .Some(idx) => {
431
+ before := self.slice(usize(0), idx);
432
+ after := self.slice((idx + search._len), self._len);
433
+ tmp := before.concat(replacement);
434
+ return tmp.concat(after);
435
+ }
436
+ )
437
+ }),
438
+
439
+ /// Replace all occurrences of `search` with `replacement`.
440
+ replace_all : (fn(self: Self, search: Self, replacement: Self) -> Self)({
441
+ if((search._len == usize(0)), {
442
+ return self;
443
+ });
444
+ result := Self.new();
445
+ pos := usize(0);
446
+ done := false;
447
+ while (!(done)), {
448
+ match(self.index_of(search, pos),
449
+ .Some(idx) => {
450
+ segment := self.slice(pos, idx);
451
+ result = result.concat(segment);
452
+ result = result.concat(replacement);
453
+ pos = (idx + search._len);
454
+ },
455
+ .None => {
456
+ result = result.concat(self.slice(pos, self._len));
457
+ done = true;
458
+ }
459
+ );
460
+ };
461
+ result
462
+ }),
463
+
464
+ /// Repeat the string `n` times.
465
+ repeat : (fn(self: Self, n: usize) -> Self)({
466
+ if(((n == usize(0)) || (self._len == usize(0))), {
467
+ return Self.new();
468
+ });
469
+ if((n == usize(1)), {
470
+ return self;
471
+ });
472
+ new_len := (self._len * n);
473
+ ptr := _alloc_bytes(new_len);
474
+ match(self._ptr,
475
+ .Some(sp) => {
476
+ i := usize(0);
477
+ while (i < n), (i = (i + usize(1))), {
478
+ memcpy(*(void)((ptr &+ (i * self._len))), *(void)(sp), self._len);
479
+ };
480
+ },
481
+ .None => ()
482
+ );
483
+ return Self(_ptr: .Some(ptr), _len: new_len, _capacity: new_len);
484
+ }),
485
+
486
+ /// Decode a single UTF-8 rune starting at byte index.
487
+ _decode_rune_at : (fn(self: Self, byte_index: usize) -> Option(rune))({
488
+ if((byte_index >= self._len), {
489
+ return .None;
490
+ });
491
+ match(self._ptr,
492
+ .None => { return .None; },
493
+ .Some(p) => {
494
+ (b0 : u8) = (p &+ byte_index).*;
495
+ if(((b0 & u8(0x80)) == u8(0)), {
496
+ return .Some(rune(char: u32(b0)));
497
+ });
498
+ if(((b0 & u8(0xE0)) == u8(0xC0)), {
499
+ if(((byte_index + usize(1)) >= self._len), {
500
+ return .None;
501
+ });
502
+ (b1 : u8) = (p &+ (byte_index + usize(1))).*;
503
+ (code : u32) = (((u32(b0) & u32(0x1F)) << u32(6)) | (u32(b1) & u32(0x3F)));
504
+ return .Some(rune(char: code));
505
+ });
506
+ if(((b0 & u8(0xF0)) == u8(0xE0)), {
507
+ if(((byte_index + usize(2)) >= self._len), {
508
+ return .None;
509
+ });
510
+ (b1 : u8) = (p &+ (byte_index + usize(1))).*;
511
+ (b2 : u8) = (p &+ (byte_index + usize(2))).*;
512
+ (code : u32) = ((((u32(b0) & u32(0x0F)) << u32(12)) | ((u32(b1) & u32(0x3F)) << u32(6))) | (u32(b2) & u32(0x3F)));
513
+ return .Some(rune(char: code));
514
+ });
515
+ if(((b0 & u8(0xF8)) == u8(0xF0)), {
516
+ if(((byte_index + usize(3)) >= self._len), {
517
+ return .None;
518
+ });
519
+ (b1 : u8) = (p &+ (byte_index + usize(1))).*;
520
+ (b2 : u8) = (p &+ (byte_index + usize(2))).*;
521
+ (b3 : u8) = (p &+ (byte_index + usize(3))).*;
522
+ (code : u32) = (((((u32(b0) & u32(0x07)) << u32(18)) | ((u32(b1) & u32(0x3F)) << u32(12))) | ((u32(b2) & u32(0x3F)) << u32(6))) | (u32(b3) & u32(0x3F)));
523
+ return .Some(rune(char: code));
524
+ });
525
+ return .None;
526
+ }
527
+ )
528
+ }),
529
+
530
+ /// Get the Unicode character (rune) at a given character index.
531
+ at : (fn(self: Self, index: usize) -> Option(rune))({
532
+ if((self._len == usize(0)), {
533
+ return .None;
534
+ });
535
+ match(self._ptr,
536
+ .None => { return .None; },
537
+ .Some(p) => {
538
+ char_count := usize(0);
539
+ byte_index := usize(0);
540
+ while (byte_index < self._len), (), {
541
+ if((char_count == index), {
542
+ return self._decode_rune_at(byte_index);
543
+ });
544
+ (b : u8) = (p &+ byte_index).*;
545
+ cond(
546
+ ((b & u8(0x80)) == u8(0)) => {
547
+ byte_index = (byte_index + usize(1));
548
+ },
549
+ ((b & u8(0xE0)) == u8(0xC0)) => {
550
+ byte_index = (byte_index + usize(2));
551
+ },
552
+ ((b & u8(0xF0)) == u8(0xE0)) => {
553
+ byte_index = (byte_index + usize(3));
554
+ },
555
+ true => {
556
+ byte_index = (byte_index + usize(4));
557
+ }
558
+ );
559
+ char_count = (char_count + usize(1));
560
+ };
561
+ return .None;
562
+ }
563
+ )
564
+ })
565
+ );
566
+
567
+ /// Equality by byte content.
568
+ impl(String, Eq(String)(
569
+ (==) : (fn(self: Self, other: Self) -> bool)({
570
+ if((self._len != other._len), {
571
+ return false;
572
+ });
573
+ if((self._len == usize(0)), {
574
+ return true;
575
+ });
576
+ match(self._ptr,
577
+ .Some(sp) => match(other._ptr,
578
+ .Some(op) => (memcmp(*(void)(sp), *(void)(op), self._len) == int(0)),
579
+ .None => false
580
+ ),
581
+ .None => match(other._ptr,
582
+ .Some(_) => false,
583
+ .None => true
584
+ )
585
+ )
586
+ }),
587
+ (!=) : (fn(self: Self, other: Self) -> bool)({
588
+ return !(Self.(==)(self, other));
589
+ })
590
+ ));
591
+
592
+ // TODO: Eq(str) impl — "Variable str not found" inside impl blocks for atomic object types.
593
+ // Provide eq_str method instead.
594
+ impl(String,
595
+ /// Compare with a `str` for equality.
596
+ eq_str : (fn(self: Self, other: str) -> bool)({
597
+ other_len := other.len();
598
+ if((self._len != other_len), {
599
+ return false;
600
+ });
601
+ if((self._len == usize(0)), {
602
+ return true;
603
+ });
604
+ match(self._ptr,
605
+ .Some(sp) => (memcmp(*(void)(sp), *(void)(other.ptr()), self._len) == int(0)),
606
+ .None => (other_len == usize(0))
607
+ )
608
+ })
609
+ );
610
+
611
+ /// Lexicographic ordering.
612
+ impl(String, Ord(String)(
613
+ (<) : (fn(self: Self, other: Self) -> bool)({
614
+ min_len := _min(self._len, other._len);
615
+ if((min_len > usize(0)), {
616
+ match(self._ptr,
617
+ .Some(sp) => match(other._ptr,
618
+ .Some(op) => {
619
+ (cmp_result : int) = memcmp(*(void)(sp), *(void)(op), min_len);
620
+ if((cmp_result != int(0)), {
621
+ return (cmp_result < int(0));
622
+ });
623
+ },
624
+ .None => ()
625
+ ),
626
+ .None => ()
627
+ );
628
+ });
629
+ (self._len < other._len)
630
+ }),
631
+ (<=) : (fn(self: Self, other: Self) -> bool)({
632
+ return !(other < self);
633
+ }),
634
+ (>) : (fn(self: Self, other: Self) -> bool)(
635
+ (other < self)
636
+ ),
637
+ (>=) : (fn(self: Self, other: Self) -> bool)({
638
+ return !(self < other);
639
+ })
640
+ ));
641
+
642
+ /// FNV-1a hash over the string's bytes.
643
+ impl(String, Hash(
644
+ (hash) : (fn(self: *(Self)) -> u64)({
645
+ h := u64(14695981039346656037);
646
+ match(self.*._ptr,
647
+ .None => (),
648
+ .Some(p) => {
649
+ i := usize(0);
650
+ while (i < self.*._len), (i = (i + usize(1))), {
651
+ h = (h ^ u64((p &+ i).*));
652
+ h = (h * u64(1099511628211));
653
+ };
654
+ }
655
+ );
656
+ h
657
+ })
658
+ ));
659
+
660
+ /// ToString returns a std.String for compatibility with println/print.
661
+ impl(String, ToString(
662
+ to_string : (fn(self: *(Self)) -> std_string.String)(
663
+ std_string.String.from(self.*.as_str())
664
+ )
665
+ ));
666
+
667
+ export String;