@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.
- package/.github/skills/yo-async-effects/async-effects-recipes.md +74 -1
- package/.github/skills/yo-core-patterns/core-patterns-cheatsheet.md +115 -0
- package/.github/skills/yo-syntax/syntax-cheatsheet.md +626 -6
- package/out/cjs/index.cjs +563 -564
- package/out/cjs/yo-cli.cjs +722 -715
- package/out/cjs/yo-lsp.cjs +601 -602
- package/out/esm/index.mjs +506 -507
- package/out/types/src/codegen/utils/index.d.ts +1 -0
- package/out/types/src/expr.d.ts +1 -0
- package/out/types/src/parser.d.ts +1 -0
- package/out/types/src/test-runner.d.ts +1 -0
- package/out/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/std/build.yo +2 -2
- package/std/collections/array_list.yo +26 -0
- package/std/imm/map.yo +15 -15
- package/std/imm/sorted_map.yo +14 -14
- package/std/imm/string.yo +4 -4
- package/std/prelude.yo +18 -23
- package/std/process/command.yo +8 -8
- package/std/string/string.yo +76 -55
- package/std/string/unicode.yo +6 -6
- package/std/sys/signal.yo +6 -6
- package/vendor/mimalloc/.github/workflows/release.yaml +55 -0
- package/vendor/mimalloc/.github/workflows/stale.yaml +27 -0
- package/vendor/mimalloc/.github/workflows/test.yaml +163 -0
- package/vendor/mimalloc/CMakeLists.txt +52 -33
- package/vendor/mimalloc/azure-pipelines.yml +4 -3
- package/vendor/mimalloc/bin/bundle.bat +74 -0
- package/vendor/mimalloc/bin/bundle.sh +232 -0
- package/vendor/mimalloc/cmake/mimalloc-config-version.cmake +2 -2
- package/vendor/mimalloc/contrib/docker/alpine/Dockerfile +1 -1
- package/vendor/mimalloc/contrib/docker/alpine-arm32v7/Dockerfile +2 -2
- package/vendor/mimalloc/contrib/docker/alpine-x86/Dockerfile +1 -1
- package/vendor/mimalloc/contrib/docker/manylinux-x64/Dockerfile +1 -1
- package/vendor/mimalloc/contrib/vcpkg/portfile.cmake +4 -3
- package/vendor/mimalloc/contrib/vcpkg/vcpkg.json +1 -1
- package/vendor/mimalloc/doc/mimalloc-doc.h +42 -4
- package/vendor/mimalloc/doc/release-notes.md +15 -0
- package/vendor/mimalloc/ide/vs2022/mimalloc-lib.vcxproj +3 -3
- package/vendor/mimalloc/ide/vs2022/mimalloc-override-static-lib.vcxproj +511 -0
- package/vendor/mimalloc/ide/vs2022/mimalloc-override-static-lib.vcxproj.filters +117 -0
- package/vendor/mimalloc/ide/vs2022/mimalloc-test-dep.vcxproj +360 -0
- package/vendor/mimalloc/ide/vs2022/mimalloc-test-override-static.vcxproj +310 -0
- package/vendor/mimalloc/ide/vs2022/mimalloc.sln +92 -35
- package/vendor/mimalloc/include/mimalloc/atomic.h +178 -182
- package/vendor/mimalloc/include/mimalloc/bits.h +8 -10
- package/vendor/mimalloc/include/mimalloc/internal.h +76 -32
- package/vendor/mimalloc/include/mimalloc/prim.h +25 -18
- package/vendor/mimalloc/include/mimalloc/track.h +7 -2
- package/vendor/mimalloc/include/mimalloc/types.h +57 -29
- package/vendor/mimalloc/include/mimalloc-override.h +10 -10
- package/vendor/mimalloc/include/mimalloc-stats.h +18 -6
- package/vendor/mimalloc/include/mimalloc.h +22 -12
- package/vendor/mimalloc/readme.md +42 -17
- package/vendor/mimalloc/src/alloc-aligned.c +13 -11
- package/vendor/mimalloc/src/alloc-override.c +97 -17
- package/vendor/mimalloc/src/alloc-posix.c +44 -27
- package/vendor/mimalloc/src/alloc.c +73 -23
- package/vendor/mimalloc/src/arena-meta.c +3 -3
- package/vendor/mimalloc/src/arena.c +380 -192
- package/vendor/mimalloc/src/bitmap.c +68 -18
- package/vendor/mimalloc/src/bitmap.h +8 -4
- package/vendor/mimalloc/src/free.c +83 -47
- package/vendor/mimalloc/src/heap.c +94 -40
- package/vendor/mimalloc/src/init.c +273 -102
- package/vendor/mimalloc/src/libc.c +53 -8
- package/vendor/mimalloc/src/options.c +43 -40
- package/vendor/mimalloc/src/os.c +110 -45
- package/vendor/mimalloc/src/page-map.c +14 -8
- package/vendor/mimalloc/src/page-queue.c +9 -6
- package/vendor/mimalloc/src/page.c +26 -16
- package/vendor/mimalloc/src/prim/emscripten/prim.c +10 -1
- package/vendor/mimalloc/src/prim/osx/alloc-override-zone.c +35 -16
- package/vendor/mimalloc/src/prim/unix/prim.c +26 -22
- package/vendor/mimalloc/src/prim/wasi/prim.c +7 -4
- package/vendor/mimalloc/src/prim/windows/prim.c +247 -44
- package/vendor/mimalloc/src/random.c +8 -3
- package/vendor/mimalloc/src/stats.c +59 -48
- package/vendor/mimalloc/src/theap.c +85 -44
- package/vendor/mimalloc/src/threadlocal.c +102 -41
- package/vendor/mimalloc/test/main-override-static.c +31 -2
- package/vendor/mimalloc/test/main-override.c +27 -14
- package/vendor/mimalloc/test/main-static-dep.cpp +46 -0
- package/vendor/mimalloc/test/main-static-dep.h +11 -0
- package/vendor/mimalloc/test/test-api-fill.c +2 -2
- package/vendor/mimalloc/test/test-stress.c +3 -3
- 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
|
-
|
|
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
|
+
```
|