@shd101wyy/yo 0.1.23 → 0.1.25

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 (88) hide show
  1. package/.github/skills/yo-async-effects/async-effects-recipes.md +74 -1
  2. package/.github/skills/yo-core-patterns/core-patterns-cheatsheet.md +115 -0
  3. package/.github/skills/yo-syntax/syntax-cheatsheet.md +626 -6
  4. package/out/cjs/index.cjs +563 -564
  5. package/out/cjs/yo-cli.cjs +722 -715
  6. package/out/cjs/yo-lsp.cjs +601 -602
  7. package/out/esm/index.mjs +506 -507
  8. package/out/types/src/codegen/utils/index.d.ts +1 -0
  9. package/out/types/src/expr.d.ts +1 -0
  10. package/out/types/src/parser.d.ts +1 -0
  11. package/out/types/src/test-runner.d.ts +1 -0
  12. package/out/types/tsconfig.tsbuildinfo +1 -1
  13. package/package.json +1 -1
  14. package/std/build.yo +2 -2
  15. package/std/collections/array_list.yo +26 -0
  16. package/std/imm/map.yo +15 -15
  17. package/std/imm/sorted_map.yo +14 -14
  18. package/std/imm/string.yo +4 -4
  19. package/std/prelude.yo +18 -23
  20. package/std/process/command.yo +8 -8
  21. package/std/string/string.yo +76 -55
  22. package/std/string/unicode.yo +6 -6
  23. package/std/sys/signal.yo +6 -6
  24. package/vendor/mimalloc/.github/workflows/release.yaml +55 -0
  25. package/vendor/mimalloc/.github/workflows/stale.yaml +27 -0
  26. package/vendor/mimalloc/.github/workflows/test.yaml +163 -0
  27. package/vendor/mimalloc/CMakeLists.txt +52 -33
  28. package/vendor/mimalloc/azure-pipelines.yml +4 -3
  29. package/vendor/mimalloc/bin/bundle.bat +74 -0
  30. package/vendor/mimalloc/bin/bundle.sh +232 -0
  31. package/vendor/mimalloc/cmake/mimalloc-config-version.cmake +2 -2
  32. package/vendor/mimalloc/contrib/docker/alpine/Dockerfile +1 -1
  33. package/vendor/mimalloc/contrib/docker/alpine-arm32v7/Dockerfile +2 -2
  34. package/vendor/mimalloc/contrib/docker/alpine-x86/Dockerfile +1 -1
  35. package/vendor/mimalloc/contrib/docker/manylinux-x64/Dockerfile +1 -1
  36. package/vendor/mimalloc/contrib/vcpkg/portfile.cmake +4 -3
  37. package/vendor/mimalloc/contrib/vcpkg/vcpkg.json +1 -1
  38. package/vendor/mimalloc/doc/mimalloc-doc.h +42 -4
  39. package/vendor/mimalloc/doc/release-notes.md +15 -0
  40. package/vendor/mimalloc/ide/vs2022/mimalloc-lib.vcxproj +3 -3
  41. package/vendor/mimalloc/ide/vs2022/mimalloc-override-static-lib.vcxproj +511 -0
  42. package/vendor/mimalloc/ide/vs2022/mimalloc-override-static-lib.vcxproj.filters +117 -0
  43. package/vendor/mimalloc/ide/vs2022/mimalloc-test-dep.vcxproj +360 -0
  44. package/vendor/mimalloc/ide/vs2022/mimalloc-test-override-static.vcxproj +310 -0
  45. package/vendor/mimalloc/ide/vs2022/mimalloc.sln +92 -35
  46. package/vendor/mimalloc/include/mimalloc/atomic.h +178 -182
  47. package/vendor/mimalloc/include/mimalloc/bits.h +8 -10
  48. package/vendor/mimalloc/include/mimalloc/internal.h +76 -32
  49. package/vendor/mimalloc/include/mimalloc/prim.h +25 -18
  50. package/vendor/mimalloc/include/mimalloc/track.h +7 -2
  51. package/vendor/mimalloc/include/mimalloc/types.h +57 -29
  52. package/vendor/mimalloc/include/mimalloc-override.h +10 -10
  53. package/vendor/mimalloc/include/mimalloc-stats.h +18 -6
  54. package/vendor/mimalloc/include/mimalloc.h +22 -12
  55. package/vendor/mimalloc/readme.md +42 -17
  56. package/vendor/mimalloc/src/alloc-aligned.c +13 -11
  57. package/vendor/mimalloc/src/alloc-override.c +97 -17
  58. package/vendor/mimalloc/src/alloc-posix.c +44 -27
  59. package/vendor/mimalloc/src/alloc.c +73 -23
  60. package/vendor/mimalloc/src/arena-meta.c +3 -3
  61. package/vendor/mimalloc/src/arena.c +380 -192
  62. package/vendor/mimalloc/src/bitmap.c +68 -18
  63. package/vendor/mimalloc/src/bitmap.h +8 -4
  64. package/vendor/mimalloc/src/free.c +83 -47
  65. package/vendor/mimalloc/src/heap.c +94 -40
  66. package/vendor/mimalloc/src/init.c +273 -102
  67. package/vendor/mimalloc/src/libc.c +53 -8
  68. package/vendor/mimalloc/src/options.c +43 -40
  69. package/vendor/mimalloc/src/os.c +110 -45
  70. package/vendor/mimalloc/src/page-map.c +14 -8
  71. package/vendor/mimalloc/src/page-queue.c +9 -6
  72. package/vendor/mimalloc/src/page.c +26 -16
  73. package/vendor/mimalloc/src/prim/emscripten/prim.c +10 -1
  74. package/vendor/mimalloc/src/prim/osx/alloc-override-zone.c +35 -16
  75. package/vendor/mimalloc/src/prim/unix/prim.c +26 -22
  76. package/vendor/mimalloc/src/prim/wasi/prim.c +7 -4
  77. package/vendor/mimalloc/src/prim/windows/prim.c +247 -44
  78. package/vendor/mimalloc/src/random.c +8 -3
  79. package/vendor/mimalloc/src/stats.c +59 -48
  80. package/vendor/mimalloc/src/theap.c +85 -44
  81. package/vendor/mimalloc/src/threadlocal.c +102 -41
  82. package/vendor/mimalloc/test/main-override-static.c +31 -2
  83. package/vendor/mimalloc/test/main-override.c +27 -14
  84. package/vendor/mimalloc/test/main-static-dep.cpp +46 -0
  85. package/vendor/mimalloc/test/main-static-dep.h +11 -0
  86. package/vendor/mimalloc/test/test-api-fill.c +2 -2
  87. package/vendor/mimalloc/test/test-stress.c +3 -3
  88. package/vendor/mimalloc/test/test-wrong.c +11 -7
@@ -26,6 +26,17 @@ pause_then_answer :: (fn(using(io : IO)) -> Impl(Future(i32, IO)))(
26
26
 
27
27
  - `io.async(...)` is lazy
28
28
  - If a function uses `using(io : IO)` and returns a future, include `IO` in the `Future(...)` type
29
+ - **Type annotations can be omitted** in the lambda's `using` params — the compiler infers types from the `Future(T, IO, Raise, ...)` return type. You can even rename the params:
30
+
31
+ ```rust
32
+ // Equivalent — types inferred from Future(i32, IO, Raise) in the return type
33
+ work :: (fn(using(io : IO, raise : Raise)) -> Impl(Future(i32, IO, Raise)))(
34
+ io.async((using(my_io, my_raise)) => { // ← any names, no type annotations needed
35
+ my_io.await(yield());
36
+ my_raise("error")
37
+ })
38
+ );
39
+ ```
29
40
 
30
41
  ## Sequential await
31
42
 
@@ -128,12 +139,50 @@ work :: (fn(using(io : IO, raise : Raise)) -> Impl(Future(i32, IO, Raise)))(
128
139
  - Every effect used by the future should appear in the `Future(...)` type
129
140
  - Effects propagate through `using(...)` just like other contextual parameters
130
141
 
142
+ ## Async recursion — use an iterative worklist instead
143
+
144
+ `recur` does **not** work inside an `io.async` lambda — it refers to the lambda's own signature, not the outer function, so the argument types will not match. Calling the outer function by name is also forbidden in Yo. Attempting either will produce a compile-time error.
145
+
146
+ **Solution**: replace async recursion with an iterative worklist using `ArrayList` as a stack:
147
+
148
+ ```rust
149
+ { read_dir, DirEntry } :: import "std/fs/dir";
150
+
151
+ process_dir :: (fn(root: Path, using(io: IO, exn: Exception)) -> Impl(Future(unit, IO, Exception)))(
152
+ io.async((using(io, exn)) => {
153
+ stack := ArrayList(Path).new();
154
+ { stack.push(root); };
155
+
156
+ while runtime((stack.len() > usize(0))), {
157
+ cur := match(stack.pop(), .Some(p) => p, .None => return ());
158
+ entries := io.await(read_dir(cur));
159
+ // process `entries`, push subdirectories to `stack`
160
+ n := entries.len();
161
+ i := usize(0);
162
+ while runtime((i < n)), {
163
+ match(entries.get(i),
164
+ .None => (),
165
+ .Some(e) => {
166
+ match(e.file_type,
167
+ .Directory => { stack.push(cur.join(Path.new(e.name))); },
168
+ _ => () // handle files here
169
+ );
170
+ }
171
+ );
172
+ i = (i + usize(1));
173
+ };
174
+ };
175
+ })
176
+ );
177
+ ```
178
+
131
179
  ## Common pitfalls
132
180
 
133
181
  - `io.async(...)` does not run immediately
134
182
  - `escape` inside async aborts the future instead of completing it normally
135
183
  - `io.await(...)` on an aborted future can panic; `JoinHandle.await(...)` converts abort into `.None`
136
184
  - Handler functions cannot capture outer variables like closures; pass required state explicitly
185
+ - **`recur` inside `io.async` calls the lambda, not the outer function** — use an iterative worklist for async recursion
137
186
 
138
187
  ## Exception (non-resumable)
139
188
 
@@ -176,7 +225,31 @@ export main;
176
225
  - Handler uses `escape` to discard the continuation and exit the enclosing function
177
226
  - Code after the escaped call is never reached
178
227
 
179
- ## ResumableException
228
+ ### Swallowing exceptions with a fallback value (return in Exception handler)
229
+
230
+ When an exception is thrown inside an async operation (e.g., `cmd.status()` or `cmd.output()`), you can **swallow the error and resume with a fallback value** by using `return` in the handler (not `escape`). The `ResumeType` is the return type of the operation that would have thrown.
231
+
232
+ ```rust
233
+ { Command, ExitStatus, Output } :: import "std/process/command";
234
+
235
+ // Check if a tool is available — returns false if it throws (e.g., not found)
236
+ given(try_exn) := Exception(throw: ((err) -> {
237
+ return ExitStatus(raw: i32(1)); // resume with "failed" exit status
238
+ }));
239
+ status := io.await(cmd.status(using(io, try_exn)));
240
+ available := status.success(); // false if exception was swallowed
241
+
242
+ // For cmd.output(), resume with a failed Output:
243
+ given(out_exn) := Exception(throw: ((err) -> {
244
+ return Output(status: ExitStatus(raw: i32(1)), stdout: ArrayList(u8).new(), stderr: ArrayList(u8).new());
245
+ }));
246
+ out := io.await(cmd.output(using(io, out_exn)));
247
+ if((!(out.status.success())), { return (); }); // handle failure
248
+ ```
249
+
250
+ Key: the `return` inside the handler resumes the _effect invocation site_ with the provided value. The calling code then sees the fallback as if the operation returned normally. Use `escape` only when the enclosing function returns `unit` (e.g., test bodies).
251
+
252
+ **`escape T_value` constraint**: `escape T_value` inside an `Exception` handler requires that the enclosing `io.async` closure's return type matches `T_value`. Due to forward type inference, the evaluator may not know the closure's return type at the point where `given` is declared. This causes a "Expected: unit" error when `escape non_unit` is used in a handler declared before the final return expression. Prefer `return fallback_value` (resume) when possible.
180
253
 
181
254
  `ResumableException(ResumeType)` is a module effect for resumable error handling. The handler uses `return` to resume with a recovery value:
182
255
 
@@ -33,6 +33,7 @@ Key rules:
33
33
  - In **runtime** code, `"hello"` is always `str`. Mixing literal and variable branches in `cond`/`match` works fine.
34
34
  - In **comptime** functions (return type `comptime(...)`), `"hello"` is `comptime_string`. It does NOT auto-convert to `str`. Use `str.from_raw_parts(*(u8)("..."), usize(N))` if a comptime function needs to return `str`.
35
35
  - For `String` constants, prefer `` `hello` `` over `String.from("hello")`.
36
+ - **PITFALL:** Never write `String.from(`hello`)` — backtick strings are already `String`, not `str`. `String.from` takes `str`, so wrapping a backtick in `String.from` causes a type error ("Cannot unify String and str"). Only use `String.from(str_expr)` for actual `str` values.
36
37
 
37
38
  ## Import patterns
38
39
 
@@ -88,6 +89,22 @@ numbers := ArrayList(i32).new();
88
89
  numbers.push(i32(1));
89
90
  numbers.push(i32(2));
90
91
 
92
+ // Index via call syntax (Index trait) — returns the value directly:
93
+ first := numbers(usize(0)); // → i32 (value)
94
+
95
+ // Mutate in place — direct assignment syntax:
96
+ numbers(usize(0)) = i32(99);
97
+
98
+ // When you need the pointer explicitly:
99
+ ptr := &(numbers(usize(0))); // → *(i32)
100
+ ptr.* = i32(100);
101
+
102
+ // Safe access:
103
+ match(numbers.get(usize(0)),
104
+ .Some(v) => println(`${v}`),
105
+ .None => ()
106
+ );
107
+
91
108
  counts := HashMap(String, i32).new();
92
109
  counts.set(`yo`, i32(1));
93
110
  ```
@@ -126,6 +143,20 @@ counter.* = (counter.* + i32(1));
126
143
  - Use `Box(T)` or `box(value)` for owned heap allocation
127
144
  - Use `*(T)` for raw pointers
128
145
  - Model nullable pointers as `Option(*(T))` or `?*(T)`, not sentinel integers
146
+ - Constructor syntax: `Box(T)(value)` — NOT `Box(T).new(value)`
147
+ - For self-referential `object` types, use `Box(Self)` to break the recursive cycle:
148
+
149
+ ```rust
150
+ Node :: object(
151
+ value : i32,
152
+ next : Option(Box(Self)) // Box(Self) breaks the recursive type cycle
153
+ );
154
+
155
+ n := Node(value: i32(1), next: Option(Box(Node)).None);
156
+ // Constructing a Box:
157
+ child := Box(Node)(Node(value: i32(2), next: Option(Box(Node)).None));
158
+ parent := Node(value: i32(1), next: Option(Box(Node)).Some(child));
159
+ ```
129
160
 
130
161
  ## Unicode and platform checks
131
162
 
@@ -225,6 +256,75 @@ println(p1.to_string());
225
256
  - Works for both structs and enums
226
257
  - Custom derives can be registered with `derive_rule`; see [DERIVE_TRAITS.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/DERIVE_TRAITS.md)
227
258
 
259
+ ### ⚠️ Circular derive trap: recursive enum with `ArrayList`
260
+
261
+ `derive(T, Eq)` and `derive(T, Clone)` fail when any field's type requires the derived trait to be already registered:
262
+
263
+ ```rust
264
+ // PROBLEM: derive expansion generates `fields_l == fields_r` (ArrayList(Node) needs
265
+ // Eq(Node)), but Eq(Node) isn't registered yet — compile error.
266
+ Node :: enum(Leaf, Branch(children : ArrayList(Self)));
267
+ derive(Node, Eq); // ← ERROR: "No matching call found for __lhs_children == __rhs_children"
268
+ ```
269
+
270
+ **Fix**: skip `derive`, write a manual recursive equality function with `recur`:
271
+
272
+ ```rust
273
+ node_eq :: (fn(a : Node, b : Node) -> bool)(
274
+ match(a,
275
+ .Leaf => match(b, .Leaf => true, _ => false),
276
+ .Branch(acs) =>
277
+ match(b,
278
+ .Branch(bcs) => {
279
+ cond(
280
+ (acs.len() != bcs.len()) => false,
281
+ true => {
282
+ (i : usize) = usize(0);
283
+ (ok : bool) = true;
284
+ while runtime(((i < acs.len()) && ok)), {
285
+ match(acs.get(i),
286
+ .Some(ac) => match(bcs.get(i),
287
+ .Some(bc) => { ok = recur(ac, bc); },
288
+ .None => { ok = false; }
289
+ ),
290
+ .None => { ok = false; }
291
+ );
292
+ i = (i + usize(1));
293
+ };
294
+ ok
295
+ }
296
+ )
297
+ },
298
+ _ => false
299
+ )
300
+ )
301
+ );
302
+
303
+ impl(Node, Eq(Node)(
304
+ (==) : (fn(a : Self, b : Self) -> bool)(node_eq(a, b))
305
+ ));
306
+ ```
307
+
308
+ Same issue applies to `Clone` when fields contain `ArrayList(Self)`.
309
+ Yo's reference counting handles shallow copies automatically (no `Clone` trait call needed);
310
+ the `Clone` trait is only for deep cloning and has the same circularity problem.
311
+ In practice, passing `EvalValue`-like types by value works fine without a `Clone` impl.
312
+
313
+ ### Comparing complex enum types
314
+
315
+ Complex enum types (structs/enums with nested fields) do not support `==`/`!=` unless `Eq` is
316
+ derived or implemented. For **tag-only equality** (checking which variant), use a tag function:
317
+
318
+ ```rust
319
+ // WRONG — TypeValue enum doesn't support !=
320
+ if(my_type != t_unit(), { ... });
321
+
322
+ // CORRECT — compare tags instead
323
+ { type_value_tag } :: import "../../types/type.yo";
324
+ { TypeTag } :: import "../../types/tags.yo";
325
+ if((type_value_tag(my_type) != TypeTag.TUnit), { ... });
326
+ ```
327
+
228
328
  ## Error handling
229
329
 
230
330
  ```rust
@@ -318,3 +418,18 @@ result := my_module.helper(i32(5));
318
418
 
319
419
  - `impl { ... }` creates a module namespace
320
420
  - Only `::` (compile-time) bindings are allowed inside
421
+
422
+ ## yo-self API: String vs str parameter gotchas
423
+
424
+ Several `yo-self/` APIs take `String` (not `str`) parameters even when the argument is conceptually a name:
425
+
426
+ - `get_variables_from_env(env, name: String)` — pass the `String` directly, do NOT call `.as_str()` first
427
+ - Most other env/value/type lookup functions follow the same convention
428
+
429
+ ```rust
430
+ // ❌ Wrong — .as_str() converts String → str but the param is String
431
+ vars := get_variables_from_env(env, prop_name_su.as_str());
432
+
433
+ // ✅ Correct — pass the String directly
434
+ vars := get_variables_from_env(env, prop_name_su);
435
+ ```