@shd101wyy/yo 0.1.26 → 0.1.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/.github/skills/yo-async-effects/SKILL.md +4 -4
  2. package/.github/skills/yo-async-effects/async-effects-recipes.md +34 -34
  3. package/.github/skills/yo-core-patterns/SKILL.md +1 -1
  4. package/.github/skills/yo-core-patterns/core-patterns-cheatsheet.md +26 -26
  5. package/.github/skills/yo-project-workflow/SKILL.md +6 -3
  6. package/.github/skills/yo-project-workflow/workflow-cheatsheet.md +34 -11
  7. package/.github/skills/yo-syntax/SKILL.md +7 -6
  8. package/.github/skills/yo-syntax/syntax-cheatsheet.md +73 -60
  9. package/.github/skills/yo-wasm-integration/wasm-integration-cheatsheet.md +3 -3
  10. package/README.md +10 -8
  11. package/out/cjs/index.cjs +456 -438
  12. package/out/cjs/yo-cli.cjs +576 -543
  13. package/out/cjs/yo-lsp.cjs +559 -532
  14. package/out/esm/index.mjs +281 -263
  15. package/out/types/src/formatter.d.ts +11 -0
  16. package/out/types/src/lsp/formatting.d.ts +2 -0
  17. package/out/types/src/tests/formatter.test.d.ts +1 -0
  18. package/out/types/tsconfig.tsbuildinfo +1 -1
  19. package/package.json +1 -1
  20. package/std/alg/hash.yo +13 -21
  21. package/std/allocator.yo +25 -40
  22. package/std/async.yo +3 -7
  23. package/std/build.yo +105 -151
  24. package/std/cli/arg_parser.yo +184 -169
  25. package/std/collections/array_list.yo +350 -314
  26. package/std/collections/btree_map.yo +142 -131
  27. package/std/collections/deque.yo +132 -128
  28. package/std/collections/hash_map.yo +542 -566
  29. package/std/collections/hash_set.yo +623 -687
  30. package/std/collections/linked_list.yo +275 -293
  31. package/std/collections/ordered_map.yo +113 -85
  32. package/std/collections/priority_queue.yo +73 -73
  33. package/std/crypto/md5.yo +191 -95
  34. package/std/crypto/random.yo +56 -64
  35. package/std/crypto/sha256.yo +151 -107
  36. package/std/encoding/base64.yo +87 -81
  37. package/std/encoding/hex.yo +43 -50
  38. package/std/encoding/html.yo +56 -81
  39. package/std/encoding/html_char_utils.yo +7 -13
  40. package/std/encoding/html_entities.yo +2248 -2253
  41. package/std/encoding/json.yo +316 -224
  42. package/std/encoding/punycode.yo +86 -116
  43. package/std/encoding/toml.yo +67 -66
  44. package/std/encoding/utf16.yo +37 -44
  45. package/std/env.yo +62 -91
  46. package/std/error.yo +7 -15
  47. package/std/fmt/display.yo +5 -9
  48. package/std/fmt/index.yo +8 -14
  49. package/std/fmt/to_string.yo +330 -315
  50. package/std/fmt/writer.yo +58 -87
  51. package/std/fs/dir.yo +83 -102
  52. package/std/fs/file.yo +147 -180
  53. package/std/fs/metadata.yo +45 -78
  54. package/std/fs/temp.yo +55 -65
  55. package/std/fs/types.yo +27 -40
  56. package/std/fs/walker.yo +53 -68
  57. package/std/gc.yo +5 -8
  58. package/std/glob.yo +30 -43
  59. package/std/http/client.yo +107 -120
  60. package/std/http/http.yo +106 -96
  61. package/std/http/index.yo +4 -6
  62. package/std/imm/list.yo +88 -93
  63. package/std/imm/map.yo +528 -464
  64. package/std/imm/set.yo +52 -57
  65. package/std/imm/sorted_map.yo +340 -286
  66. package/std/imm/sorted_set.yo +57 -63
  67. package/std/imm/string.yo +404 -345
  68. package/std/imm/vec.yo +173 -181
  69. package/std/io/reader.yo +3 -6
  70. package/std/io/writer.yo +4 -8
  71. package/std/libc/assert.yo +5 -9
  72. package/std/libc/ctype.yo +32 -22
  73. package/std/libc/dirent.yo +26 -25
  74. package/std/libc/errno.yo +164 -90
  75. package/std/libc/fcntl.yo +52 -45
  76. package/std/libc/float.yo +66 -44
  77. package/std/libc/limits.yo +42 -33
  78. package/std/libc/math.yo +53 -82
  79. package/std/libc/signal.yo +72 -47
  80. package/std/libc/stdatomic.yo +217 -188
  81. package/std/libc/stdint.yo +5 -29
  82. package/std/libc/stdio.yo +5 -29
  83. package/std/libc/stdlib.yo +32 -39
  84. package/std/libc/string.yo +5 -23
  85. package/std/libc/sys/stat.yo +58 -56
  86. package/std/libc/time.yo +5 -19
  87. package/std/libc/unistd.yo +5 -20
  88. package/std/libc/wctype.yo +6 -9
  89. package/std/libc/windows.yo +26 -30
  90. package/std/log.yo +41 -55
  91. package/std/net/addr.yo +102 -97
  92. package/std/net/dns.yo +27 -28
  93. package/std/net/errors.yo +50 -49
  94. package/std/net/tcp.yo +113 -124
  95. package/std/net/udp.yo +55 -66
  96. package/std/os/env.yo +35 -33
  97. package/std/os/signal.yo +15 -25
  98. package/std/path.yo +276 -311
  99. package/std/prelude.yo +6304 -4315
  100. package/std/process/command.yo +87 -103
  101. package/std/process/index.yo +12 -31
  102. package/std/regex/compiler.yo +196 -95
  103. package/std/regex/flags.yo +58 -39
  104. package/std/regex/index.yo +157 -173
  105. package/std/regex/match.yo +20 -31
  106. package/std/regex/node.yo +134 -152
  107. package/std/regex/parser.yo +283 -259
  108. package/std/regex/unicode.yo +172 -202
  109. package/std/regex/vm.yo +155 -171
  110. package/std/string/index.yo +5 -7
  111. package/std/string/rune.yo +45 -55
  112. package/std/string/string.yo +937 -964
  113. package/std/string/string_builder.yo +94 -104
  114. package/std/string/unicode.yo +46 -64
  115. package/std/sync/channel.yo +72 -73
  116. package/std/sync/cond.yo +31 -36
  117. package/std/sync/mutex.yo +30 -32
  118. package/std/sync/once.yo +13 -16
  119. package/std/sync/rwlock.yo +26 -31
  120. package/std/sync/waitgroup.yo +20 -25
  121. package/std/sys/advise.yo +16 -24
  122. package/std/sys/bufio/buf_reader.yo +77 -93
  123. package/std/sys/bufio/buf_writer.yo +52 -65
  124. package/std/sys/clock.yo +4 -9
  125. package/std/sys/constants.yo +77 -61
  126. package/std/sys/copy.yo +4 -10
  127. package/std/sys/dir.yo +26 -43
  128. package/std/sys/dns.yo +41 -61
  129. package/std/sys/errors.yo +95 -103
  130. package/std/sys/events.yo +45 -57
  131. package/std/sys/externs.yo +319 -267
  132. package/std/sys/fallocate.yo +7 -11
  133. package/std/sys/fcntl.yo +14 -22
  134. package/std/sys/file.yo +26 -40
  135. package/std/sys/future.yo +5 -8
  136. package/std/sys/iov.yo +12 -25
  137. package/std/sys/lock.yo +12 -13
  138. package/std/sys/mmap.yo +38 -43
  139. package/std/sys/path.yo +3 -8
  140. package/std/sys/perm.yo +7 -21
  141. package/std/sys/pipe.yo +5 -12
  142. package/std/sys/process.yo +23 -29
  143. package/std/sys/seek.yo +10 -12
  144. package/std/sys/signal.yo +7 -13
  145. package/std/sys/signals.yo +52 -35
  146. package/std/sys/socket.yo +63 -58
  147. package/std/sys/socketpair.yo +3 -6
  148. package/std/sys/sockinfo.yo +11 -20
  149. package/std/sys/statfs.yo +11 -34
  150. package/std/sys/statx.yo +25 -52
  151. package/std/sys/sysinfo.yo +15 -20
  152. package/std/sys/tcp.yo +62 -92
  153. package/std/sys/temp.yo +5 -9
  154. package/std/sys/time.yo +5 -15
  155. package/std/sys/timer.yo +6 -11
  156. package/std/sys/tty.yo +10 -18
  157. package/std/sys/udp.yo +22 -39
  158. package/std/sys/umask.yo +3 -6
  159. package/std/sys/unix.yo +33 -52
  160. package/std/testing/bench.yo +49 -52
  161. package/std/thread.yo +10 -15
  162. package/std/time/datetime.yo +105 -89
  163. package/std/time/duration.yo +43 -56
  164. package/std/time/instant.yo +13 -18
  165. package/std/time/sleep.yo +5 -9
  166. package/std/url/index.yo +184 -209
  167. package/std/worker.yo +6 -10
package/std/path.yo CHANGED
@@ -1,55 +1,50 @@
1
1
  //! Cross-platform filesystem path manipulation.
2
-
3
- open import "./string";
4
- open import "./fmt";
5
- open import "./collections/array_list";
6
-
2
+ open(import("./string"));
3
+ open(import("./fmt"));
4
+ open(import("./collections/array_list"));
7
5
  /// Path parsing error variants.
8
6
  PathError :: enum(
9
7
  /// The path string was empty.
10
8
  EmptyPath,
11
9
  /// The path string was malformed.
12
- InvalidPath(message: String)
10
+ InvalidPath(message : String)
13
11
  );
14
- export PathError;
15
-
12
+ export(PathError);
16
13
  /// Platform-specific path separator: `'/'` on Unix, `'\\'` on Windows.
17
14
  PATH_SEPARATOR :: cond(
18
- (__yo_process_platform() == "windows") => u8(92), // '\' for Windows
19
- true => u8(47) // '/' for Unix
15
+ (__yo_process_platform() == "windows") => u8(92),
16
+ // '\' for Windows
17
+ true => u8(47) // '/' for Unix
20
18
  );
21
- export PATH_SEPARATOR;
22
-
19
+ export(PATH_SEPARATOR);
23
20
  /// Platform-specific path list delimiter: `':'` on Unix, `';'` on Windows.
24
21
  PATH_DELIMITER :: cond(
25
- (__yo_process_platform() == "windows") => u8(59), // ';' for Windows
26
- true => u8(58) // ':' for Unix
22
+ (__yo_process_platform() == "windows") => u8(59),
23
+ // ';' for Windows
24
+ true => u8(58) // ':' for Unix
27
25
  );
28
- export PATH_DELIMITER;
29
-
26
+ export(PATH_DELIMITER);
30
27
  /// Filesystem path with segment-based operations.
31
28
  /// Paths are normalized on construction: `.` segments are removed, `..` pops the parent.
32
29
  Path :: object(
33
- _segments: ArrayList(String),
34
- _is_absolute: bool
30
+ _segments : ArrayList(String),
31
+ _is_absolute : bool
35
32
  );
36
- impl(Path,
33
+ impl(
34
+ Path,
37
35
  /// Create a new path from a string. Normalizes separators and resolves `.`/`..`.
38
- new : (fn(path_str: String) -> Self)({
36
+ new : (fn(path_str : String) -> Self)({
39
37
  segments := ArrayList(String).new();
40
38
  is_abs := false;
41
-
42
39
  // Handle empty path early
43
40
  cond(
44
41
  (path_str.is_empty()) => {
45
- return Self(_segments: segments, _is_absolute: is_abs);
42
+ return(Self(_segments : segments, _is_absolute : is_abs));
46
43
  },
47
44
  true => ()
48
45
  );
49
-
50
46
  // Normalize backslashes to forward slashes for cross-platform support
51
47
  normalized := path_str.replace_all(String.from("\\"), String.from("/"));
52
-
53
48
  // Check if path is absolute
54
49
  // Unix: starts with '/'
55
50
  // Windows: starts with drive letter like 'C:' or UNC path '\\'
@@ -57,7 +52,8 @@ impl(Path,
57
52
  cond(
58
53
  (bytes.len() > usize(0)) => {
59
54
  first_byte := bytes.get(usize(0));
60
- match(first_byte,
55
+ match(
56
+ first_byte,
61
57
  .Some(b) => cond(
62
58
  // Unix-style absolute path (starts with /)
63
59
  (b == u8(47)) => {
@@ -68,7 +64,8 @@ impl(Path,
68
64
  cond(
69
65
  (bytes.len() >= usize(2)) => {
70
66
  second_byte_opt := bytes.get(usize(1));
71
- match(second_byte_opt,
67
+ match(
68
+ second_byte_opt,
72
69
  .Some(second_byte) => cond(
73
70
  // Check if second byte is ':' (colon)
74
71
  (second_byte == u8(58)) => {
@@ -91,51 +88,54 @@ impl(Path,
91
88
  }
92
89
  ),
93
90
  .None => ()
94
- );
91
+ );
95
92
  },
96
93
  true => ()
97
94
  );
98
-
99
95
  // Split path by "/" separator (backslashes already normalized)
100
96
  sep := String.from("/");
101
97
  parts := normalized.split(sep);
102
-
103
98
  // Process each part
104
99
  i := usize(0);
105
- while ((i < parts.len())),
106
- (i = (i + usize(1))),
107
- {
100
+ while(i < parts.len(), i = (i + usize(1)), {
108
101
  part_opt := parts.get(i);
109
- match(part_opt,
102
+ match(
103
+ part_opt,
110
104
  .Some(part) => {
111
105
  // Skip empty segments (from leading/trailing slashes)
112
106
  cond(
113
107
  (part.is_empty()) => (),
114
108
  true => {
115
109
  // Check if it's "." (current directory)
116
- is_dot := ((part.len() == usize(1)) && {
117
- byte_opt := part.as_bytes().get(usize(0));
118
- match(byte_opt,
119
- .Some(b) => (b == u8(46)),
120
- .None => false
121
- )
122
- });
123
-
110
+ is_dot := (
111
+ (part.len() == usize(1)) && {
112
+ byte_opt := part.as_bytes().get(usize(0));
113
+ match(
114
+ byte_opt,
115
+ .Some(b) => (b == u8(46)),
116
+ .None => false
117
+ )
118
+ }
119
+ );
124
120
  // Check if it's ".." (parent directory)
125
- is_dotdot := ((part.len() == usize(2)) && {
126
- b0_opt := part.as_bytes().get(usize(0));
127
- b1_opt := part.as_bytes().get(usize(1));
128
- match(b0_opt,
129
- .Some(b0) => match(b1_opt,
130
- .Some(b1) => ((b0 == u8(46)) && (b1 == u8(46))),
121
+ is_dotdot := (
122
+ (part.len() == usize(2)) && {
123
+ b0_opt := part.as_bytes().get(usize(0));
124
+ b1_opt := part.as_bytes().get(usize(1));
125
+ match(
126
+ b0_opt,
127
+ .Some(b0) => match(
128
+ b1_opt,
129
+ .Some(b1) => ((b0 == u8(46)) && (b1 == u8(46))),
130
+ .None => false
131
+ ),
131
132
  .None => false
132
- ),
133
- .None => false
134
- )
135
- });
136
-
133
+ )
134
+ }
135
+ );
137
136
  cond(
138
- (is_dot) => (), // Skip "."
137
+ (is_dot) => (),
138
+ // Skip "."
139
139
  (is_dotdot) => {
140
140
  // Pop the last segment if exists
141
141
  cond(
@@ -154,355 +154,322 @@ impl(Path,
154
154
  },
155
155
  .None => ()
156
156
  );
157
- };
158
-
159
- return Self(_segments: segments, _is_absolute: is_abs);
157
+ });
158
+ return(Self(_segments : segments, _is_absolute : is_abs));
160
159
  }),
161
-
162
160
  /// Create a path from a C string pointer.
163
- from_cstr : (fn(cstr: *(u8)) -> Self)({
161
+ from_cstr : (fn(cstr : *(u8)) -> Self)({
164
162
  str_result := String.from_cstr(cstr);
165
- match(str_result,
163
+ match(
164
+ str_result,
166
165
  .Ok(s) => {
167
- return Self.new(s);
166
+ return(Self.new(s));
168
167
  },
169
168
  .Err(_) => {
170
169
  // Return empty path on error
171
- return Self(_segments: ArrayList(String).new(), _is_absolute: false);
170
+ return(Self(_segments : ArrayList(String).new(), _is_absolute : false));
172
171
  }
173
172
  );
174
173
  }),
175
-
176
174
  /// Join this path with another. If `other` is absolute, returns `other`.
177
- join : (fn(self: Self, other: Self) -> Self)({
175
+ join : (fn(self : Self, other : Self) -> Self)({
178
176
  // If other is absolute, return other
179
177
  cond(
180
178
  (other._is_absolute) => {
181
- return other;
179
+ return(other);
182
180
  },
183
181
  true => ()
184
182
  );
185
-
186
183
  // Otherwise, concatenate segments
187
- new_segments := ArrayList(String).with_capacity((self._segments.len() + other._segments.len()));
188
-
184
+ new_segments := ArrayList(String).with_capacity(self._segments.len() + other._segments.len());
189
185
  // Copy self segments
190
186
  i := usize(0);
191
- while ((i < self._segments.len())),
192
- (i = (i + usize(1))),
193
- {
187
+ while(i < self._segments.len(), i = (i + usize(1)), {
194
188
  seg := self._segments.get(i);
195
- match(seg,
189
+ match(
190
+ seg,
196
191
  .Some(s) => {
197
192
  new_segments.push(s);
198
193
  },
199
194
  .None => ()
200
195
  );
201
- };
202
-
196
+ });
203
197
  // Copy other segments
204
198
  j := usize(0);
205
- while ((j < other._segments.len())),
206
- (j = (j + usize(1))),
207
- {
199
+ while(j < other._segments.len(), j = (j + usize(1)), {
208
200
  seg := other._segments.get(j);
209
- match(seg,
201
+ match(
202
+ seg,
210
203
  .Some(s) => {
211
204
  new_segments.push(s);
212
205
  },
213
206
  .None => ()
214
207
  );
215
- };
216
-
217
- return Self(_segments: new_segments, _is_absolute: self._is_absolute);
208
+ });
209
+ return(Self(_segments : new_segments, _is_absolute : self._is_absolute));
218
210
  }),
219
-
220
211
  /// Get the parent directory path, or `.None` if there are no segments.
221
- parent : (fn(self: Self) -> Option(Self))({
212
+ parent : (fn(self : Self) -> Option(Self))({
222
213
  cond(
223
214
  (self._segments.len() == usize(0)) => {
224
- return .None;
215
+ return(.None);
225
216
  },
226
217
  true => ()
227
218
  );
228
-
229
- new_segments := ArrayList(String).with_capacity((self._segments.len() - usize(1)));
219
+ new_segments := ArrayList(String).with_capacity(self._segments.len() - usize(1));
230
220
  i := usize(0);
231
- while ((i < (self._segments.len() - usize(1)))),
232
- (i = (i + usize(1))),
233
- {
221
+ while(i < (self._segments.len() - usize(1)), i = (i + usize(1)), {
234
222
  seg := self._segments.get(i);
235
- match(seg,
223
+ match(
224
+ seg,
236
225
  .Some(s) => {
237
226
  new_segments.push(s);
238
227
  },
239
228
  .None => ()
240
229
  );
241
- };
242
-
243
- return .Some(Self(_segments: new_segments, _is_absolute: self._is_absolute));
230
+ });
231
+ return(.Some(Self(_segments : new_segments, _is_absolute : self._is_absolute)));
244
232
  }),
245
-
246
233
  /// Get the last path segment (filename), or `.None` for empty paths.
247
- file_name : (fn(self: Self) -> Option(String))({
234
+ file_name : (fn(self : Self) -> Option(String))({
248
235
  cond(
249
236
  (self._segments.len() == usize(0)) => {
250
- return .None;
237
+ return(.None);
251
238
  },
252
239
  true => ()
253
240
  );
254
-
255
- return self._segments.get((self._segments.len() - usize(1)));
241
+ return(self._segments.get(self._segments.len() - usize(1)));
256
242
  }),
257
-
258
243
  /// Get the filename without its extension, or `.None` for empty paths.
259
- file_stem : (fn(self: Self) -> Option(String))({
244
+ file_stem : (fn(self : Self) -> Option(String))({
260
245
  name_opt := self.file_name();
261
- match(name_opt,
246
+ match(
247
+ name_opt,
262
248
  .Some(name) => {
263
249
  // Find the last dot using String methods
264
250
  dot := String.from(".");
265
251
  last_dot_result := name.last_index_of(dot);
266
- match(last_dot_result,
252
+ match(
253
+ last_dot_result,
267
254
  .Some(idx) => {
268
255
  // Return substring before the dot
269
256
  stem := name.substring(usize(0), idx);
270
- return .Some(stem);
257
+ return(.Some(stem));
271
258
  },
272
259
  .None => {
273
260
  // No dot found, return the whole name
274
- return .Some(name);
261
+ return(.Some(name));
275
262
  }
276
263
  );
277
264
  },
278
265
  .None => {
279
- return .None;
266
+ return(.None);
280
267
  }
281
268
  );
282
269
  }),
283
-
284
270
  /// Get the file extension (without the dot), or `.None` if there is none.
285
- extension : (fn(self: Self) -> Option(String))({
271
+ extension : (fn(self : Self) -> Option(String))({
286
272
  name_opt := self.file_name();
287
- match(name_opt,
273
+ match(
274
+ name_opt,
288
275
  .Some(name) => {
289
276
  // Find the last dot using String methods
290
277
  dot := String.from(".");
291
278
  last_dot_result := name.last_index_of(dot);
292
- match(last_dot_result,
279
+ match(
280
+ last_dot_result,
293
281
  .Some(idx) => {
294
282
  // Return substring after the dot
295
- ext := name.substring((idx + usize(1)), name.len());
283
+ ext := name.substring(idx + usize(1), name.len());
296
284
  cond(
297
285
  (ext.is_empty()) => {
298
- return .None;
286
+ return(.None);
299
287
  },
300
288
  true => {
301
- return .Some(ext);
289
+ return(.Some(ext));
302
290
  }
303
291
  );
304
292
  },
305
293
  .None => {
306
294
  // No dot found, no extension
307
- return .None;
295
+ return(.None);
308
296
  }
309
297
  );
310
298
  },
311
299
  .None => {
312
- return .None;
300
+ return(.None);
313
301
  }
314
302
  );
315
303
  }),
316
-
317
304
  /// Return a new path with the extension replaced by `ext`.
318
- with_extension : (fn(self: Self, ext: String) -> Self)({
305
+ with_extension : (fn(self : Self, ext : String) -> Self)({
319
306
  cond(
320
307
  (self._segments.len() == usize(0)) => {
321
- return self;
308
+ return(self);
322
309
  },
323
310
  true => ()
324
311
  );
325
-
326
312
  // Get the file stem
327
313
  stem_opt := self.file_stem();
328
- match(stem_opt,
314
+ match(
315
+ stem_opt,
329
316
  .Some(stem) => {
330
317
  // Build new file name with extension
331
318
  dot := String.from(".");
332
319
  new_name := stem.concat(dot).concat(ext);
333
-
334
320
  // Replace the last segment
335
321
  new_segments := ArrayList(String).with_capacity(self._segments.len());
336
322
  i := usize(0);
337
- while ((i < (self._segments.len() - usize(1)))),
338
- (i = (i + usize(1))),
339
- {
323
+ while(i < (self._segments.len() - usize(1)), i = (i + usize(1)), {
340
324
  seg := self._segments.get(i);
341
- match(seg,
325
+ match(
326
+ seg,
342
327
  .Some(s) => {
343
328
  new_segments.push(s);
344
329
  },
345
330
  .None => ()
346
331
  );
347
- };
332
+ });
348
333
  new_segments.push(new_name);
349
-
350
- return Self(_segments: new_segments, _is_absolute: self._is_absolute);
334
+ return(Self(_segments : new_segments, _is_absolute : self._is_absolute));
351
335
  },
352
336
  .None => {
353
- return self;
337
+ return(self);
354
338
  }
355
339
  );
356
340
  }),
357
-
358
341
  /// Return a new path with the last segment replaced by `name`.
359
- with_file_name : (fn(self: Self, name: String) -> Self)({
342
+ with_file_name : (fn(self : Self, name : String) -> Self)({
360
343
  cond(
361
344
  (self._segments.len() == usize(0)) => {
362
345
  // Empty path, just return a new path with the name
363
346
  segments := ArrayList(String).new();
364
347
  segments.push(name);
365
- return Self(_segments: segments, _is_absolute: self._is_absolute);
348
+ return(Self(_segments : segments, _is_absolute : self._is_absolute));
366
349
  },
367
350
  true => ()
368
351
  );
369
-
370
352
  // Replace the last segment
371
353
  new_segments := ArrayList(String).with_capacity(self._segments.len());
372
354
  i := usize(0);
373
- while ((i < (self._segments.len() - usize(1)))),
374
- (i = (i + usize(1))),
375
- {
355
+ while(i < (self._segments.len() - usize(1)), i = (i + usize(1)), {
376
356
  seg := self._segments.get(i);
377
- match(seg,
357
+ match(
358
+ seg,
378
359
  .Some(s) => {
379
360
  new_segments.push(s);
380
361
  },
381
362
  .None => ()
382
363
  );
383
- };
364
+ });
384
365
  new_segments.push(name);
385
-
386
- return Self(_segments: new_segments, _is_absolute: self._is_absolute);
366
+ return(Self(_segments : new_segments, _is_absolute : self._is_absolute));
387
367
  }),
388
-
389
368
  /// Check if this path starts with the given base path (segment-wise).
390
- starts_with : (fn(self: Self, base: Self) -> bool)({
369
+ starts_with : (fn(self : Self, base : Self) -> bool)({
391
370
  // Absolute vs relative mismatch
392
371
  cond(
393
372
  (self._is_absolute != base._is_absolute) => {
394
- return false;
373
+ return(false);
395
374
  },
396
375
  true => ()
397
376
  );
398
-
399
377
  // Base has more segments than self
400
378
  cond(
401
379
  (base._segments.len() > self._segments.len()) => {
402
- return false;
380
+ return(false);
403
381
  },
404
382
  true => ()
405
383
  );
406
-
407
384
  // Check if all base segments match
408
385
  i := usize(0);
409
- while ((i < base._segments.len())),
410
- (i = (i + usize(1))),
411
- {
386
+ while(i < base._segments.len(), i = (i + usize(1)), {
412
387
  self_seg_opt := self._segments.get(i);
413
388
  base_seg_opt := base._segments.get(i);
414
-
415
- match(self_seg_opt,
416
- .Some(self_seg) => match(base_seg_opt,
389
+ match(
390
+ self_seg_opt,
391
+ .Some(self_seg) => match(
392
+ base_seg_opt,
417
393
  .Some(base_seg) => {
418
394
  cond(
419
395
  (self_seg == base_seg) => (),
420
396
  true => {
421
- return false;
397
+ return(false);
422
398
  }
423
399
  );
424
400
  },
425
401
  .None => {
426
- return false;
402
+ return(false);
427
403
  }
428
404
  ),
429
405
  .None => {
430
- return false;
406
+ return(false);
431
407
  }
432
408
  );
433
- };
434
-
435
- return true;
409
+ });
410
+ return(true);
436
411
  }),
437
-
438
412
  /// Check if this path ends with the given suffix path (segment-wise).
439
- ends_with : (fn(self: Self, suffix: Self) -> bool)({
413
+ ends_with : (fn(self : Self, suffix : Self) -> bool)({
440
414
  // Suffix has more segments than self
441
415
  cond(
442
416
  (suffix._segments.len() > self._segments.len()) => {
443
- return false;
417
+ return(false);
444
418
  },
445
419
  true => ()
446
420
  );
447
-
448
421
  // If suffix is absolute, it must match exactly
449
422
  cond(
450
423
  (suffix._is_absolute) => {
451
424
  cond(
452
425
  (self._is_absolute != suffix._is_absolute) => {
453
- return false;
426
+ return(false);
454
427
  },
455
428
  true => ()
456
429
  );
457
430
  cond(
458
431
  (self._segments.len() != suffix._segments.len()) => {
459
- return false;
432
+ return(false);
460
433
  },
461
434
  true => ()
462
435
  );
463
436
  },
464
437
  true => ()
465
438
  );
466
-
467
439
  // Check if suffix segments match the end of self
468
440
  suffix_len := suffix._segments.len();
469
441
  self_len := self._segments.len();
470
442
  offset := (self_len - suffix_len);
471
-
472
443
  i := usize(0);
473
- while ((i < suffix_len)),
474
- (i = (i + usize(1))),
475
- {
476
- self_seg_opt := self._segments.get((offset + i));
444
+ while(i < suffix_len, i = (i + usize(1)), {
445
+ self_seg_opt := self._segments.get(offset + i);
477
446
  suffix_seg_opt := suffix._segments.get(i);
478
-
479
- match(self_seg_opt,
480
- .Some(self_seg) => match(suffix_seg_opt,
447
+ match(
448
+ self_seg_opt,
449
+ .Some(self_seg) => match(
450
+ suffix_seg_opt,
481
451
  .Some(suffix_seg) => {
482
452
  cond(
483
453
  (self_seg == suffix_seg) => (),
484
454
  true => {
485
- return false;
455
+ return(false);
486
456
  }
487
457
  );
488
458
  },
489
459
  .None => {
490
- return false;
460
+ return(false);
491
461
  }
492
462
  ),
493
463
  .None => {
494
- return false;
464
+ return(false);
495
465
  }
496
466
  );
497
- };
498
-
499
- return true;
467
+ });
468
+ return(true);
500
469
  }),
501
-
502
470
  /// Get all path components as a list. Absolute paths start with `"/"`.
503
- components : (fn(self: Self) -> ArrayList(String))({
504
- result := ArrayList(String).with_capacity((self._segments.len() + usize(1)));
505
-
471
+ components : (fn(self : Self) -> ArrayList(String))({
472
+ result := ArrayList(String).with_capacity(self._segments.len() + usize(1));
506
473
  // Add root component if absolute
507
474
  cond(
508
475
  (self._is_absolute) => {
@@ -510,151 +477,149 @@ impl(Path,
510
477
  },
511
478
  true => ()
512
479
  );
513
-
514
480
  // Add all segments
515
481
  i := usize(0);
516
- while ((i < self._segments.len())),
517
- (i = (i + usize(1))),
518
- {
482
+ while(i < self._segments.len(), i = (i + usize(1)), {
519
483
  seg := self._segments.get(i);
520
- match(seg,
484
+ match(
485
+ seg,
521
486
  .Some(s) => {
522
487
  result.push(s);
523
488
  },
524
489
  .None => ()
525
490
  );
526
- };
527
-
528
- return result;
491
+ });
492
+ return(result);
529
493
  }),
530
-
531
494
  /// Check if the path is absolute.
532
- is_absolute : (fn(self: Self) -> bool)(
495
+ is_absolute : (fn(self : Self) -> bool)(
533
496
  self._is_absolute
534
497
  ),
535
-
536
498
  /// Check if the path is relative (not absolute).
537
- is_relative : (fn(self: Self) -> bool)(
538
- !self._is_absolute
499
+ is_relative : (fn(self : Self) -> bool)(
500
+ !(self._is_absolute)
539
501
  )
540
502
  );
541
-
542
- impl(Path, ToString(
543
- to_string : (self -> {
544
- segments := self._segments;
545
- sep := String.from("/");
546
- result := String.new();
547
-
548
- // Check if first segment is a Windows drive letter (e.g., "C:")
549
- has_drive_letter := cond(
550
- (segments.len() > usize(0)) => {
551
- first_seg_opt := segments.get(usize(0));
552
- match(first_seg_opt,
553
- .Some(first_seg) =>
554
- // Check if it's a drive letter like "C:"
555
- cond(
556
- (first_seg.len() == usize(2)) => {
557
- b0_opt := first_seg.as_bytes().get(usize(0));
558
- b1_opt := first_seg.as_bytes().get(usize(1));
559
- match(b0_opt,
560
- .Some(b0) => match(b1_opt,
561
- .Some(b1) => {
562
- is_letter := (((b0 >= u8(65)) && (b0 <= u8(90))) || ((b0 >= u8(97)) && (b0 <= u8(122))));
563
- is_colon := (b1 == u8(58));
564
- (is_letter && is_colon)
565
- },
566
- .None => false
567
- ),
568
- .None => false
569
- )
570
- },
571
- true => false
572
- ),
573
- .None => false
574
- )
575
- },
576
- true => false
577
- );
578
-
579
- // For Unix absolute paths, prepend /
580
- // For Windows paths with drive letter, don't prepend /
581
- cond(
582
- (self._is_absolute && !has_drive_letter) => {
583
- result = result.concat(sep);
584
- },
585
- true => ()
586
- );
587
- i := usize(0);
588
- segments_len := segments.len();
589
- while ((i < segments_len)),
590
- (i = (i + usize(1))),
591
- {
592
- seg := segments.get(i);
593
- match(seg,
594
- .Some(s) => {
595
- result = result.concat(s);
596
- cond(
597
- ((i < (segments_len - usize(1)))) => {
598
- result = result.concat(sep);
503
+ impl(
504
+ Path,
505
+ ToString(
506
+ to_string : (
507
+ self -> {
508
+ segments := self._segments;
509
+ sep := String.from("/");
510
+ result := String.new();
511
+ // Check if first segment is a Windows drive letter (e.g., "C:")
512
+ has_drive_letter := cond(
513
+ (segments.len() > usize(0)) => {
514
+ first_seg_opt := segments.get(usize(0));
515
+ match(
516
+ first_seg_opt,
517
+ .Some(first_seg) =>
518
+ // Check if it's a drive letter like "C:"
519
+ cond(
520
+ (first_seg.len() == usize(2)) => {
521
+ b0_opt := first_seg.as_bytes().get(usize(0));
522
+ b1_opt := first_seg.as_bytes().get(usize(1));
523
+ match(
524
+ b0_opt,
525
+ .Some(b0) => match(
526
+ b1_opt,
527
+ .Some(b1) => {
528
+ is_letter := (((b0 >= u8(65)) && (b0 <= u8(90))) || ((b0 >= u8(97)) && (b0 <= u8(122))));
529
+ is_colon := (b1 == u8(58));
530
+ is_letter && is_colon
531
+ },
532
+ .None => false
533
+ ),
534
+ .None => false
535
+ )
536
+ },
537
+ true => false
538
+ ),
539
+ .None => false
540
+ )
541
+ },
542
+ true => false
543
+ );
544
+ // For Unix absolute paths, prepend /
545
+ // For Windows paths with drive letter, don't prepend /
546
+ cond(
547
+ (self._is_absolute && !(has_drive_letter)) => {
548
+ result = result.concat(sep);
549
+ },
550
+ true => ()
551
+ );
552
+ i := usize(0);
553
+ segments_len := segments.len();
554
+ while(i < segments_len, i = (i + usize(1)), {
555
+ seg := segments.get(i);
556
+ match(
557
+ seg,
558
+ .Some(s) => {
559
+ result = result.concat(s);
560
+ cond(
561
+ (i < (segments_len - usize(1))) => {
562
+ result = result.concat(sep);
563
+ },
564
+ true => ()
565
+ );
599
566
  },
600
- true => ()
567
+ .None => ()
601
568
  );
602
- },
603
- .None => ()
604
- );
605
- };
606
- return result;
607
- })
608
- ));
609
-
610
- impl(Path, Eq(Path)(
611
- (==) : ((self, other)-> {
612
- // Compare absolute vs relative
613
- cond(
614
- (self._is_absolute != other._is_absolute) => {
615
- return false;
616
- },
617
- true => ()
618
- );
619
-
620
- // Compare segment lengths
621
- cond(
622
- (self._segments.len() != other._segments.len()) => {
623
- return false;
624
- },
625
- true => ()
626
- );
627
-
628
- // Compare each segment
629
- i := usize(0);
630
- while ((i < self._segments.len())),
631
- (i = (i + usize(1))),
632
- {
633
- self_seg_opt := self._segments.get(i);
634
- other_seg_opt := other._segments.get(i);
635
-
636
- match(self_seg_opt,
637
- .Some(self_seg) => match(other_seg_opt,
638
- .Some(other_seg) => {
639
- cond(
640
- (self_seg == other_seg) => (),
641
- true => {
642
- return false;
643
- }
644
- );
569
+ });
570
+ return(result);
571
+ }
572
+ )
573
+ )
574
+ );
575
+ impl(
576
+ Path,
577
+ Eq(Path)(
578
+ (==) : (
579
+ (self, other) -> {
580
+ // Compare absolute vs relative
581
+ cond(
582
+ (self._is_absolute != other._is_absolute) => {
583
+ return(false);
645
584
  },
646
- .None => {
647
- return false;
648
- }
649
- ),
650
- .None => {
651
- return false;
652
- }
653
- );
654
- };
655
-
656
- return true;
657
- })
658
- ));
659
-
660
- export Path;
585
+ true => ()
586
+ );
587
+ // Compare segment lengths
588
+ cond(
589
+ (self._segments.len() != other._segments.len()) => {
590
+ return(false);
591
+ },
592
+ true => ()
593
+ );
594
+ // Compare each segment
595
+ i := usize(0);
596
+ while(i < self._segments.len(), i = (i + usize(1)), {
597
+ self_seg_opt := self._segments.get(i);
598
+ other_seg_opt := other._segments.get(i);
599
+ match(
600
+ self_seg_opt,
601
+ .Some(self_seg) => match(
602
+ other_seg_opt,
603
+ .Some(other_seg) => {
604
+ cond(
605
+ (self_seg == other_seg) => (),
606
+ true => {
607
+ return(false);
608
+ }
609
+ );
610
+ },
611
+ .None => {
612
+ return(false);
613
+ }
614
+ ),
615
+ .None => {
616
+ return(false);
617
+ }
618
+ );
619
+ });
620
+ return(true);
621
+ }
622
+ )
623
+ )
624
+ );
625
+ export(Path);