@shd101wyy/yo 0.1.24 → 0.1.26
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 +6 -6
- package/.github/skills/yo-core-patterns/core-patterns-cheatsheet.md +34 -0
- package/.github/skills/yo-syntax/syntax-cheatsheet.md +441 -2
- package/out/cjs/index.cjs +563 -567
- package/out/cjs/yo-cli.cjs +656 -651
- package/out/cjs/yo-lsp.cjs +614 -618
- package/out/esm/index.mjs +582 -586
- package/out/types/src/codegen/codegen-c.d.ts +2 -2
- package/out/types/src/codegen/functions/collection.d.ts +2 -2
- package/out/types/src/codegen/functions/context.d.ts +3 -2
- package/out/types/src/codegen/types/collection.d.ts +2 -2
- package/out/types/src/codegen/utils/index.d.ts +4 -1
- package/out/types/src/doc/builder.d.ts +2 -2
- package/out/types/src/evaluator/calls/closure-type.d.ts +2 -2
- package/out/types/src/evaluator/calls/record-type.d.ts +11 -0
- package/out/types/src/evaluator/context.d.ts +8 -9
- package/out/types/src/evaluator/index.d.ts +3 -3
- package/out/types/src/evaluator/types/record.d.ts +14 -0
- package/out/types/src/evaluator/types/validation.d.ts +2 -2
- package/out/types/src/evaluator/values/anonymous-module.d.ts +5 -5
- package/out/types/src/evaluator/values/impl.d.ts +1 -1
- package/out/types/src/expr.d.ts +2 -4
- package/out/types/src/function-value.d.ts +1 -1
- package/out/types/src/lsp/document-manager.d.ts +1 -1
- package/out/types/src/module-manager.d.ts +3 -3
- package/out/types/src/test-runner.d.ts +1 -0
- package/out/types/src/types/creators.d.ts +3 -4
- package/out/types/src/types/definitions.d.ts +8 -19
- package/out/types/src/types/guards.d.ts +3 -3
- package/out/types/src/types/tags.d.ts +0 -1
- package/out/types/src/types/utils.d.ts +1 -1
- package/out/types/src/value-tag.d.ts +0 -1
- package/out/types/src/value.d.ts +6 -13
- package/out/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/std/error.yo +6 -6
- package/std/prelude.yo +1 -7
- 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
- package/out/types/src/evaluator/calls/module-type.d.ts +0 -11
- package/out/types/src/evaluator/types/module.d.ts +0 -19
|
@@ -186,7 +186,7 @@ process_dir :: (fn(root: Path, using(io: IO, exn: Exception)) -> Impl(Future(uni
|
|
|
186
186
|
|
|
187
187
|
## Exception (non-resumable)
|
|
188
188
|
|
|
189
|
-
`Exception` is a built-in
|
|
189
|
+
`Exception` is a built-in struct-record effect for non-resumable error handling. When the handler calls `escape`, the continuation is discarded:
|
|
190
190
|
|
|
191
191
|
```rust
|
|
192
192
|
open import "std/error";
|
|
@@ -251,7 +251,7 @@ Key: the `return` inside the handler resumes the _effect invocation site_ with t
|
|
|
251
251
|
|
|
252
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.
|
|
253
253
|
|
|
254
|
-
`ResumableException(ResumeType)` is a
|
|
254
|
+
`ResumableException(ResumeType)` is a struct-record effect for resumable error handling. The handler uses `return` to resume with a recovery value:
|
|
255
255
|
|
|
256
256
|
```rust
|
|
257
257
|
open import "std/error";
|
|
@@ -282,19 +282,19 @@ export main;
|
|
|
282
282
|
- Handler uses `return value` to resume the continuation with the recovery value
|
|
283
283
|
- The call site receives the returned value and continues normally
|
|
284
284
|
|
|
285
|
-
##
|
|
285
|
+
## Struct-record effects vs function-type effects
|
|
286
286
|
|
|
287
|
-
Effects in Yo can be plain function types or
|
|
287
|
+
Effects in Yo can be plain function types or struct-record types:
|
|
288
288
|
|
|
289
289
|
```rust
|
|
290
290
|
Raise :: (fn(msg : String) -> i32);
|
|
291
291
|
|
|
292
|
-
Logger ::
|
|
292
|
+
Logger :: struct(
|
|
293
293
|
log : (fn(level : i32, msg : String) -> unit)
|
|
294
294
|
);
|
|
295
295
|
```
|
|
296
296
|
|
|
297
|
-
Both kinds use `using(...)` / `given(...)` with the same semantics — they compile to evidence passing (function pointers as implicit C parameters).
|
|
297
|
+
Both kinds use `using(...)` / `given(...)` with the same semantics — they compile to evidence passing (function pointers as implicit C parameters). Struct-record effects group related operations under a single nominal type.
|
|
298
298
|
|
|
299
299
|
## Async with effects
|
|
300
300
|
|
|
@@ -190,6 +190,10 @@ TcpStream :: object(fd : i32, buffer : ArrayList(u8));
|
|
|
190
190
|
|
|
191
191
|
- Use `newtype(...)` when the type has exactly one field
|
|
192
192
|
- Use `object(...)` for types that need shared ownership
|
|
193
|
+
- Source-file imports are namespace structs. The old `module(...)`, `Module`,
|
|
194
|
+
and `SelfModule` syntax is gone; use `struct(...)`, `Type`, and normal `Self`.
|
|
195
|
+
- Fields written as `name :: value` or `comptime(name) : Type` are compile-time-only static fields/methods and have no runtime layout
|
|
196
|
+
- A normal field with a compile-time-only type, such as `x : comptime_int`, is still a data field and makes the struct comptime-only
|
|
193
197
|
|
|
194
198
|
## Impl blocks and generics
|
|
195
199
|
|
|
@@ -310,6 +314,21 @@ Yo's reference counting handles shallow copies automatically (no `Clone` trait c
|
|
|
310
314
|
the `Clone` trait is only for deep cloning and has the same circularity problem.
|
|
311
315
|
In practice, passing `EvalValue`-like types by value works fine without a `Clone` impl.
|
|
312
316
|
|
|
317
|
+
### Comparing complex enum types
|
|
318
|
+
|
|
319
|
+
Complex enum types (structs/enums with nested fields) do not support `==`/`!=` unless `Eq` is
|
|
320
|
+
derived or implemented. For **tag-only equality** (checking which variant), use a tag function:
|
|
321
|
+
|
|
322
|
+
```rust
|
|
323
|
+
// WRONG — TypeValue enum doesn't support !=
|
|
324
|
+
if(my_type != t_unit(), { ... });
|
|
325
|
+
|
|
326
|
+
// CORRECT — compare tags instead
|
|
327
|
+
{ type_value_tag } :: import "../../types/type.yo";
|
|
328
|
+
{ TypeTag } :: import "../../types/tags.yo";
|
|
329
|
+
if((type_value_tag(my_type) != TypeTag.TUnit), { ... });
|
|
330
|
+
```
|
|
331
|
+
|
|
313
332
|
## Error handling
|
|
314
333
|
|
|
315
334
|
```rust
|
|
@@ -403,3 +422,18 @@ result := my_module.helper(i32(5));
|
|
|
403
422
|
|
|
404
423
|
- `impl { ... }` creates a module namespace
|
|
405
424
|
- Only `::` (compile-time) bindings are allowed inside
|
|
425
|
+
|
|
426
|
+
## yo-self API: String vs str parameter gotchas
|
|
427
|
+
|
|
428
|
+
Several `yo-self/` APIs take `String` (not `str`) parameters even when the argument is conceptually a name:
|
|
429
|
+
|
|
430
|
+
- `get_variables_from_env(env, name: String)` — pass the `String` directly, do NOT call `.as_str()` first
|
|
431
|
+
- Most other env/value/type lookup functions follow the same convention
|
|
432
|
+
|
|
433
|
+
```rust
|
|
434
|
+
// ❌ Wrong — .as_str() converts String → str but the param is String
|
|
435
|
+
vars := get_variables_from_env(env, prop_name_su.as_str());
|
|
436
|
+
|
|
437
|
+
// ✅ Correct — pass the String directly
|
|
438
|
+
vars := get_variables_from_env(env, prop_name_su);
|
|
439
|
+
```
|
|
@@ -90,6 +90,7 @@ Key rules:
|
|
|
90
90
|
- In **runtime** code, `"hello"` is `str`. Mixing literals and variables in `cond`/`match` branches is fine.
|
|
91
91
|
- In **comptime** functions (return type `comptime(...)`), `"hello"` is `comptime_string` — it does NOT auto-convert to `str`.
|
|
92
92
|
- For `String` constants, prefer `` `hello` `` over `String.from("hello")`.
|
|
93
|
+
- **`String.from(`` `...` ``)` is WRONG**: `` `...` `` is already `String`; `String.from` takes `str`. Use `` `...` `` directly or `String.from("...")` with double quotes.
|
|
93
94
|
- **`assert` takes `str`, not `String`**: `assert(cond, "message")` — always use `""`. Passing a template string `` `...` `` causes a type mismatch. Use a custom `check_str` helper when you need `String` diagnostics.
|
|
94
95
|
|
|
95
96
|
## Calls, operators, and whitespace
|
|
@@ -103,9 +104,10 @@ masked := ((A | B) | C);
|
|
|
103
104
|
- Prefer parenthesized calls: `func(arg1, arg2)`
|
|
104
105
|
- `func (a, b)` is a different parse shape than `func(a, b)`
|
|
105
106
|
- Yo has no operator precedence; fully parenthesize binary expressions
|
|
106
|
-
- **All unary operators (`!`, `&`, `-`, `~`) greedily consume everything that follows, including comma-separated args.** `func(&s, a, b)` is parsed as `func(&(s, a, b))` — ONE tuple argument!
|
|
107
|
+
- **All unary operators (`!`, `&`, `-`, `~`) greedily consume everything that follows, including comma-separated args.** `func(&s, a, b)` is parsed as `func(&(s, a, b))` — ONE tuple argument! Preferred fix: parenthesize the operand: `func(&(s), a, b)`. The outer-parens form `func((&s), a, b)` also works. Either is fine; the operand-parens form `&(x)` matches how the parser thinks about it.
|
|
107
108
|
- Parenthesize other unary operands too: `!(ready)`, `-(value)`
|
|
108
109
|
- **`!x && y` is parsed as `!(x && y)`**, not `(!x) && y`. Prefix `!` greedily consumes the full right-hand expression. To get `(!x) && y`, write `((!x) && y)` with explicit inner parens.
|
|
110
|
+
- **Nested `&&` / `||` in a single compound condition causes "Ambiguous operator precedence"** even with explicit parentheses: `((A && B) && (C && D))` on one line triggers the error. Fix: extract sub-conditions into named booleans first: `_c1 := (A && B); _c2 := (C && D); if((_c1 && _c2), ...)`.
|
|
109
111
|
|
|
110
112
|
## Functions and methods
|
|
111
113
|
|
|
@@ -127,6 +129,11 @@ impl(Counter,
|
|
|
127
129
|
- No space between a function type and its body: `(fn(...) -> T)(...)`
|
|
128
130
|
- Use `Self` in method signatures and in type definitions for recursive references (the type name is not available during its own definition)
|
|
129
131
|
- `Self` also works inside generic type constructors — it refers to the current instantiation (e.g., `Tree(T)` inside `Tree`). Use `recur(args)` only when type arguments differ from the current instantiation.
|
|
132
|
+
- Use `struct(...)` for record and effect-record types. The legacy `module(...)`,
|
|
133
|
+
`Module`, and `SelfModule` syntax has been removed; imported files are
|
|
134
|
+
represented as namespace structs, and recursive references use normal `Self`.
|
|
135
|
+
- Bare `Module` is not a type alias. Use `Type` for comptime type values; type
|
|
136
|
+
reflection reports source-module namespaces as `TypeInfo.Struct(...)`.
|
|
130
137
|
- Wrap `fn` types in parentheses when they appear after `:`
|
|
131
138
|
- **Forward references between methods in the same `impl` block are supported.** A method defined later in the block can be called by a method defined earlier. Both `self.method()` and `Self.method(...)` dispatch work. Only the canonical `name : (fn(...) -> R)(body)` method shape participates; bare lambdas do not get forward-ref shells.
|
|
132
139
|
- **Module-level `::` function definitions are processed in order.** A function body that calls another function declared later in the same file will fail with "Variable not found". Always define leaf helpers first (bottom-up order): `eval_identifier` → `eval_atom` → `evaluate`.
|
|
@@ -241,6 +248,8 @@ match(s,
|
|
|
241
248
|
|
|
242
249
|
Curly `{a, b: c}` is sugar for `(a: a, b: c)` — order-free, supports partial matches (omit fields). Use `{label: _}` to ignore a specific field. Bare `{_}` and empty `{}` are rejected.
|
|
243
250
|
|
|
251
|
+
> **Critical**: Within a single match arm, you must use **either all positional or all named** field patterns. Mixing positional and named fields in the same arm (e.g., `.Foo(x, y: z, w)`) causes C codegen to emit undeclared identifiers for the named fields. This is a parser/codegen limitation — do not mix.
|
|
252
|
+
|
|
244
253
|
## Generics and compile-time
|
|
245
254
|
|
|
246
255
|
```rust
|
|
@@ -321,12 +330,27 @@ while true, {
|
|
|
321
330
|
while comptime(i < 10), {
|
|
322
331
|
// body evaluated/unrolled at compile time
|
|
323
332
|
};
|
|
333
|
+
|
|
334
|
+
// for loop — 2-arg prelude macro; first arg MUST be an iterator (has .next()):
|
|
335
|
+
for(list.iter(), x => { // ArrayList, array → call .iter() first
|
|
336
|
+
process(x);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
for(map.into_iter(), bucket => {
|
|
340
|
+
println(bucket.key);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
for(list.iter(), (ptr) => { // ptr is a mutable reference to each element
|
|
344
|
+
ptr.* = transform(ptr.*);
|
|
345
|
+
});
|
|
324
346
|
```
|
|
325
347
|
|
|
326
348
|
- Use `recur(...)` for self-recursion
|
|
327
349
|
- `while cond` is **always a runtime loop** — use this for open-ended loops (e.g., server accept loops, event loops)
|
|
328
350
|
- `while comptime(cond)` explicitly unrolls at compile time — `cond` must be a compile-time-known value
|
|
329
351
|
- Using a comptime-only (`::`) variable in a bare `while` condition without `comptime()` is a **compile error** (would be an infinite loop at runtime)
|
|
352
|
+
- **`for(arr, item => { body })`** — correct 2-arg prelude macro form. The `item => { body }` is an anonymous closure.
|
|
353
|
+
- **Do NOT use `for(x, arr, { body })`** — this older 3-arg form is an evaluator-internal representation, not valid top-level Yo syntax. (The self-hosted evaluator currently only understands the 3-arg form in its internal for-loop handler; track issue: `issues/eval-for-loop-3arg-vs-2arg.md`)
|
|
330
354
|
|
|
331
355
|
## Return and branch safety
|
|
332
356
|
|
|
@@ -426,6 +450,35 @@ test "Async test", {
|
|
|
426
450
|
|
|
427
451
|
## Common pitfalls
|
|
428
452
|
|
|
453
|
+
### `&&` short-circuit with `match`/`cond` on RHS causes C codegen scope bug
|
|
454
|
+
|
|
455
|
+
Using `&&` where the right-hand side is a `match` or `cond` expression causes
|
|
456
|
+
a C codegen bug: the temp variable for the RHS is declared inside the short-circuit
|
|
457
|
+
`if` block but the cleanup drop is emitted outside it. This produces a C compile
|
|
458
|
+
error ("use of undeclared identifier").
|
|
459
|
+
|
|
460
|
+
```rust
|
|
461
|
+
// WRONG — triggers codegen scope bug:
|
|
462
|
+
is_ok := (av.is_compile_time_only && match(av.value,
|
|
463
|
+
.Some(v) => compute(v),
|
|
464
|
+
.None => false
|
|
465
|
+
));
|
|
466
|
+
|
|
467
|
+
// CORRECT — use an explicit if block to scope the match:
|
|
468
|
+
(is_ok : bool) = false;
|
|
469
|
+
if(av.is_compile_time_only, {
|
|
470
|
+
is_ok = match(av.value,
|
|
471
|
+
.Some(v) => compute(v),
|
|
472
|
+
.None => false
|
|
473
|
+
);
|
|
474
|
+
});
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
This only affects `&&`/`||` where the **right-hand side contains a `match`,
|
|
478
|
+
`cond`, or other expression that allocates heap-managed temporaries** (e.g.,
|
|
479
|
+
`String`, `ArrayList`, `Option(HeapType)`). Pure boolean expressions on both
|
|
480
|
+
sides are fine.
|
|
481
|
+
|
|
429
482
|
### `impl(...)` requires a trailing semicolon
|
|
430
483
|
|
|
431
484
|
```rust
|
|
@@ -455,6 +508,28 @@ foo();
|
|
|
455
508
|
bar();
|
|
456
509
|
```
|
|
457
510
|
|
|
511
|
+
### Enum pattern matching does NOT support literal values
|
|
512
|
+
|
|
513
|
+
Match patterns on enum variants only support **variable binding**, not literal comparison.
|
|
514
|
+
`.BoolVal(true)` binds the inner value to a variable named `true` — it does NOT check
|
|
515
|
+
if the value is `true`. The arm always matches any `BoolVal`.
|
|
516
|
+
|
|
517
|
+
```rust
|
|
518
|
+
// ❌ WRONG — always matches (true is a variable binding, not a comparison)
|
|
519
|
+
match(val,
|
|
520
|
+
.BoolVal(true) => handle_true(),
|
|
521
|
+
_ => ()
|
|
522
|
+
);
|
|
523
|
+
|
|
524
|
+
// ✅ CORRECT — bind to variable, then check with cond
|
|
525
|
+
match(val,
|
|
526
|
+
.BoolVal(b) => cond(b => handle_true(), true => ()),
|
|
527
|
+
_ => ()
|
|
528
|
+
);
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
Same applies to `.IntLit(42)`, `.StrLit("hello")`, etc.
|
|
532
|
+
|
|
458
533
|
### `type` is a reserved keyword — avoid as field/param name
|
|
459
534
|
|
|
460
535
|
```rust
|
|
@@ -545,7 +620,44 @@ This applies to ALL callee-before-caller relationships:
|
|
|
545
620
|
- `print_build_summary` before `execute_step`
|
|
546
621
|
- Exports section must come AFTER all definitions
|
|
547
622
|
|
|
548
|
-
###
|
|
623
|
+
### Named tuple fields in type syntax are not allowed
|
|
624
|
+
|
|
625
|
+
Yo does not support named tuple field types in the syntax `(name : Type, ...)`. Use an `object` struct instead:
|
|
626
|
+
|
|
627
|
+
```rust
|
|
628
|
+
// WRONG — "Labelled field is not allowed in tuple value":
|
|
629
|
+
get_range :: (fn(ty : TypeValue) -> Option((min : i64, max : u64)))(
|
|
630
|
+
.Some((min: i64(-128), max: u64(127)))
|
|
631
|
+
);
|
|
632
|
+
|
|
633
|
+
// CORRECT — define a named struct:
|
|
634
|
+
Range :: object(min : i64, max : u64);
|
|
635
|
+
get_range :: (fn(ty : TypeValue) -> Option(Range))(
|
|
636
|
+
.Some({min: i64(-128), max: u64(127)})
|
|
637
|
+
);
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
Named fields work in struct/object constructors `{min: ..., max: ...}`, just not in the type syntax for tuples.
|
|
641
|
+
|
|
642
|
+
### `Option` equality comparison limitations
|
|
643
|
+
|
|
644
|
+
Comparing `Option(T)` with `== .None` or `== .Some(...)` fails when the inner type `T` lacks a derived `Eq` implementation or when `.None` is type-ambiguous. Always use the method API:
|
|
645
|
+
|
|
646
|
+
```rust
|
|
647
|
+
// WRONG — "No matching call found with arguments: r == .None":
|
|
648
|
+
assert((r == .None), "should be None");
|
|
649
|
+
|
|
650
|
+
// CORRECT — use .is_none() / .is_some() / .unwrap():
|
|
651
|
+
assert(r.is_none(), "should be None");
|
|
652
|
+
assert(r.is_some(), "should be Some");
|
|
653
|
+
assert((r.unwrap() == expected_value), "value check");
|
|
654
|
+
|
|
655
|
+
// Also fine in match:
|
|
656
|
+
match(r,
|
|
657
|
+
.Some(v) => assert((v == expected_value), "value check"),
|
|
658
|
+
.None => assert(false, "unexpected None")
|
|
659
|
+
);
|
|
660
|
+
```
|
|
549
661
|
|
|
550
662
|
The `!` operator is greedy and consumes all following args including the block. Always parenthesize:
|
|
551
663
|
|
|
@@ -557,6 +669,79 @@ if(!cond, { do_thing(); });
|
|
|
557
669
|
if((!cond), { do_thing(); });
|
|
558
670
|
```
|
|
559
671
|
|
|
672
|
+
### `escape` requires a nested-function context
|
|
673
|
+
|
|
674
|
+
`escape value` exits the **enclosing function** — the nearest `fn(...)` that
|
|
675
|
+
wraps the current code. It requires that the code is inside a nested function
|
|
676
|
+
(e.g., a closure or `given` handler lambda), NOT at the top level of a
|
|
677
|
+
standalone function definition.
|
|
678
|
+
|
|
679
|
+
```rust
|
|
680
|
+
// CORRECT — escape inside a given handler lambda (lambda has enclosing fn):
|
|
681
|
+
given(exn) := Exception(throw: ((err) -> {
|
|
682
|
+
escape result // exits the outer function that contains this given()
|
|
683
|
+
}));
|
|
684
|
+
do_something(using(exn));
|
|
685
|
+
|
|
686
|
+
// CORRECT — escape inside a closure passed as argument:
|
|
687
|
+
result := match(opt, .Some(x) => x, .None => {
|
|
688
|
+
// This is NOT a nested function — use return:
|
|
689
|
+
// WRONG: escape default_val // ERROR: no enclosing fn
|
|
690
|
+
return default_val // CORRECT: return exits the enclosing fn directly
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
// WRONG — escape inside a match arm (not a nested fn):
|
|
694
|
+
match(opt,
|
|
695
|
+
.Some(x) => { escape x } // ERROR: "can only be used inside a function that has an enclosing function"
|
|
696
|
+
);
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
**Rule of thumb**: Use `escape` only inside lambdas passed to `given()` handlers.
|
|
700
|
+
In all other contexts (match arms, if blocks, begin blocks, top-level fn bodies),
|
|
701
|
+
use `return` for early exit.
|
|
702
|
+
|
|
703
|
+
`return` inside a lambda exits that lambda only; `escape` exits the OUTER function
|
|
704
|
+
(the one that installed the `given` handler).
|
|
705
|
+
|
|
706
|
+
### Parameter reassignment
|
|
707
|
+
|
|
708
|
+
Function parameters are **NOT reassignable**. To reassign, declare a mutable local:
|
|
709
|
+
|
|
710
|
+
```rust
|
|
711
|
+
// WRONG — cannot reassign parameter 'env':
|
|
712
|
+
my_fn :: (fn(env : Environment) -> Environment)({
|
|
713
|
+
env = other_env; // ERROR: "cannot reassign itself"
|
|
714
|
+
env
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
// CORRECT — create a mutable local copy:
|
|
718
|
+
my_fn :: (fn(init_env : Environment) -> Environment)({
|
|
719
|
+
(env : Environment) = init_env;
|
|
720
|
+
env = other_env; // OK — reassigning local variable
|
|
721
|
+
env
|
|
722
|
+
});
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
This also applies to `object` types: you can mutate fields (`env.field = val`)
|
|
726
|
+
but cannot rebind the variable (`env = other_env`).
|
|
727
|
+
|
|
728
|
+
### String cloning
|
|
729
|
+
|
|
730
|
+
Calling `.clone()` on a `String` field from a struct/method chain requires a
|
|
731
|
+
reference — take `&` first:
|
|
732
|
+
|
|
733
|
+
```rust
|
|
734
|
+
// WRONG — .clone() requires *(Self) but gets Self value from field access:
|
|
735
|
+
name := token.value.clone(); // ERROR
|
|
736
|
+
|
|
737
|
+
// CORRECT — take reference first:
|
|
738
|
+
tok := some_fn_call();
|
|
739
|
+
name := (&tok.value).clone(); // OK
|
|
740
|
+
|
|
741
|
+
// ALSO CORRECT — use String.from on the str slice:
|
|
742
|
+
name := String.from(token.value.as_str());
|
|
743
|
+
```
|
|
744
|
+
|
|
560
745
|
### Template strings produce `String`, literals are `str`
|
|
561
746
|
|
|
562
747
|
```rust
|
|
@@ -584,3 +769,257 @@ These features are powerful but less commonly used. Consult the linked docs for
|
|
|
584
769
|
| Isolated types | `Iso(T)` for data-race-free parallelism | [ISOLATED.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/ISOLATED.md) |
|
|
585
770
|
| Arc (atomic ref count) | `arc(value)`, `shared.(*)` for cross-thread sharing | [ARC.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/ARC.md) |
|
|
586
771
|
| Parallelism | Thread pool, `io.spawn` for parallel work | [PARALLELISM.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/PARALLELISM.md) |
|
|
772
|
+
|
|
773
|
+
---
|
|
774
|
+
|
|
775
|
+
## Self-hosted evaluator (`yo-self/`) — known limitations and pitfalls
|
|
776
|
+
|
|
777
|
+
These constraints apply **only** to the self-hosted Yo evaluator (code inside `yo-self/`). The TypeScript-compiled Yo compiler does not have these restrictions.
|
|
778
|
+
|
|
779
|
+
### Match patterns: no bare identifier catch-all
|
|
780
|
+
|
|
781
|
+
The self-hosted parser only accepts three match arm forms:
|
|
782
|
+
|
|
783
|
+
- `.VariantName` — unit variant
|
|
784
|
+
- `.VariantName(p1, p2, ...)` — tuple variant
|
|
785
|
+
- `_` — wildcard
|
|
786
|
+
|
|
787
|
+
**Bare identifier catch-all `t => ...` is NOT supported.** Use `_` and access the outer binding directly:
|
|
788
|
+
|
|
789
|
+
```rust
|
|
790
|
+
// ❌ NOT supported in yo-self/
|
|
791
|
+
match(val, {
|
|
792
|
+
.SomeVariant(x) => x,
|
|
793
|
+
t => t, // ERROR: bare identifier not a valid pattern
|
|
794
|
+
})
|
|
795
|
+
|
|
796
|
+
// ✅ Correct — use outer binding
|
|
797
|
+
match(val, {
|
|
798
|
+
.SomeVariant(x) => x,
|
|
799
|
+
_ => val, // refer to outer binding
|
|
800
|
+
})
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
### String concatenation: no `String + str` operator
|
|
804
|
+
|
|
805
|
+
The `+` operator does not accept mixed `String`/`str` operands.
|
|
806
|
+
**Always use template strings** for concatenation:
|
|
807
|
+
|
|
808
|
+
```rust
|
|
809
|
+
// ❌ Type error
|
|
810
|
+
result := (parts + ", ");
|
|
811
|
+
result := (parts + item.as_str());
|
|
812
|
+
|
|
813
|
+
// ✅ Template strings
|
|
814
|
+
result := `${parts}, `;
|
|
815
|
+
result := `${parts}${item}`;
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
### `clone()` on extracted String fields is ambiguous
|
|
819
|
+
|
|
820
|
+
When a `String` field is bound in a match arm, calling `.clone()` triggers an ambiguity error (two impls: `fn(self: String)` and `fn(self: *(String))`).
|
|
821
|
+
|
|
822
|
+
```rust
|
|
823
|
+
// ❌ Ambiguous
|
|
824
|
+
.StructVal(name, fields) => name.clone()
|
|
825
|
+
|
|
826
|
+
// ✅ Use from + as_str
|
|
827
|
+
.StructVal(name, fields) => String.from(name.as_str())
|
|
828
|
+
```
|
|
829
|
+
|
|
830
|
+
### `box(val)` is a move — cannot box the same value twice
|
|
831
|
+
|
|
832
|
+
```rust
|
|
833
|
+
// ❌ Move error: target is moved by first box(target)
|
|
834
|
+
p1 := PtrVal(box(target), usize(0));
|
|
835
|
+
p2 := PtrVal(box(target), usize(0)); // ERROR: target already moved
|
|
836
|
+
|
|
837
|
+
// ✅ Create separate instances
|
|
838
|
+
p1 := PtrVal(box(EvalValue.IntLit(String.from("42"))), usize(0));
|
|
839
|
+
p2 := PtrVal(box(EvalValue.IntLit(String.from("42"))), usize(0));
|
|
840
|
+
```
|
|
841
|
+
|
|
842
|
+
### `recur(...)` for self-recursive lambdas
|
|
843
|
+
|
|
844
|
+
Lambdas defined as `name :: (fn(args) -> T)(body)` cannot call `name` inside `body`.
|
|
845
|
+
Use `recur(...)` instead:
|
|
846
|
+
|
|
847
|
+
```rust
|
|
848
|
+
// ❌ Would not find `my_fn` inside its own body
|
|
849
|
+
my_fn :: (fn(x : i32) -> i32)({
|
|
850
|
+
my_fn(x - 1) // ERROR: `my_fn` not in scope yet
|
|
851
|
+
});
|
|
852
|
+
|
|
853
|
+
// ✅ Use recur
|
|
854
|
+
my_fn :: (fn(x : i32) -> i32)({
|
|
855
|
+
recur(x - 1)
|
|
856
|
+
});
|
|
857
|
+
```
|
|
858
|
+
|
|
859
|
+
### `{ expr }` without semicolons is a struct literal, not a block
|
|
860
|
+
|
|
861
|
+
```rust
|
|
862
|
+
// ❌ Parsed as struct literal `{ match(...) }`
|
|
863
|
+
fn :: (fn() -> T)({ match(x, arms) })
|
|
864
|
+
|
|
865
|
+
// ✅ Remove braces or add semicolon
|
|
866
|
+
fn :: (fn() -> T)(match(x, arms))
|
|
867
|
+
fn :: (fn() -> T)({ match(x, arms); })
|
|
868
|
+
```
|
|
869
|
+
|
|
870
|
+
### Template strings cannot be nested inside `${...}` interpolations
|
|
871
|
+
|
|
872
|
+
A template string literal (`` ` `` ... `` ` ``) inside a `${...}` interpolation of another template string closes the outer string. The compiler gives confusing parse errors.
|
|
873
|
+
|
|
874
|
+
```rust
|
|
875
|
+
// ❌ Inner backtick closes the outer template string — parse error
|
|
876
|
+
lines.push(`**Implements:** ${`, `.join(names)}`);
|
|
877
|
+
|
|
878
|
+
// ✅ Assign the separator to a variable first
|
|
879
|
+
sep := `, `;
|
|
880
|
+
lines.push(`**Implements:** ${sep.join(names)}`);
|
|
881
|
+
```
|
|
882
|
+
|
|
883
|
+
### Pushing RC struct fields into ArrayList does not need `.clone()`
|
|
884
|
+
|
|
885
|
+
String (and other RC object) fields of structs can be passed directly to `ArrayList.push()`. Calling `.clone()` triggers an ambiguity error between `fn(self: String)` and `fn(self: *(String))` overloads.
|
|
886
|
+
|
|
887
|
+
```rust
|
|
888
|
+
// ❌ Ambiguous clone call
|
|
889
|
+
names.push(param.name.clone());
|
|
890
|
+
|
|
891
|
+
// ✅ Push directly — RC bump happens automatically
|
|
892
|
+
names.push(param.name);
|
|
893
|
+
```
|
|
894
|
+
|
|
895
|
+
If explicit clone is needed elsewhere, use `(&field).clone()` to select the pointer overload.
|
|
896
|
+
|
|
897
|
+
### `.Some(expr)` in expression position is parsed as a 2-arg property access
|
|
898
|
+
|
|
899
|
+
Using `.Some(x)` as an expression (not inside a match pattern) is parsed by the Yo parser
|
|
900
|
+
as a 2-arg dot property access: `obj.(prop, arg)`. This means `evaluate_property_access` is
|
|
901
|
+
invoked on it at compile time, causing confusing errors like "Failed to infer enum variant type".
|
|
902
|
+
|
|
903
|
+
```rust
|
|
904
|
+
// ❌ Parsed as 2-arg property access — NOT an Option::Some constructor call
|
|
905
|
+
val := .Some(oi.ty);
|
|
906
|
+
|
|
907
|
+
// ✅ Use the explicit fully-qualified form
|
|
908
|
+
val := Option(TypeValue).Some(oi.ty);
|
|
909
|
+
```
|
|
910
|
+
|
|
911
|
+
### `||` chaining requires explicit parentheses for 3+ operands
|
|
912
|
+
|
|
913
|
+
Chaining three or more `||` terms in a single expression is rejected with a precedence error.
|
|
914
|
+
Always add explicit parentheses around each pair:
|
|
915
|
+
|
|
916
|
+
```rust
|
|
917
|
+
// ❌ Rejected — ambiguous precedence
|
|
918
|
+
if ((is_tuple_type(ty) || is_struct_type(ty) || is_union_type(ty)), ...)
|
|
919
|
+
|
|
920
|
+
// ✅ Parenthesise each pair
|
|
921
|
+
if (((is_tuple_type(ty) || is_struct_type(ty)) || is_union_type(ty)), ...)
|
|
922
|
+
```
|
|
923
|
+
|
|
924
|
+
### Duplicate imports from the same path must be merged
|
|
925
|
+
|
|
926
|
+
Having two `:: import "path"` lines importing from the same file causes a compile error.
|
|
927
|
+
Always merge them into a single destructuring import:
|
|
928
|
+
|
|
929
|
+
```rust
|
|
930
|
+
// ❌ Two imports from the same path
|
|
931
|
+
{ Foo } :: import "../../mod.yo";
|
|
932
|
+
{ Bar } :: import "../../mod.yo";
|
|
933
|
+
|
|
934
|
+
// ✅ Merged
|
|
935
|
+
{ Foo, Bar } :: import "../../mod.yo";
|
|
936
|
+
```
|
|
937
|
+
|
|
938
|
+
### Implicit (`using`) parameters cannot be used with `:=` assignment
|
|
939
|
+
|
|
940
|
+
Implicit effect parameters introduced via `using(name : Type)` cannot be bound
|
|
941
|
+
to a discarded variable with `:= name`. They can only be passed via `using()`.
|
|
942
|
+
To suppress "unused parameter" warnings for an implicit param, simply omit the
|
|
943
|
+
discard assignment — implicit params never trigger unused-variable errors.
|
|
944
|
+
|
|
945
|
+
```rust
|
|
946
|
+
// WRONG — implicit variable cannot be used in assignment:
|
|
947
|
+
foo :: (fn(using(exn : Exception)) -> unit)({
|
|
948
|
+
_ := exn; // ERROR: Cannot use implicit variable "exn" in assignment
|
|
949
|
+
()
|
|
950
|
+
});
|
|
951
|
+
|
|
952
|
+
// CORRECT — just omit the discard line:
|
|
953
|
+
foo :: (fn(using(exn : Exception)) -> unit)({
|
|
954
|
+
()
|
|
955
|
+
});
|
|
956
|
+
```
|
|
957
|
+
|
|
958
|
+
### Nested `Option` patterns require staging
|
|
959
|
+
|
|
960
|
+
`match` does not support nested destructuring patterns like `.Some(.TypeVal(x))`.
|
|
961
|
+
Split into two separate `match` expressions.
|
|
962
|
+
|
|
963
|
+
```rust
|
|
964
|
+
// WRONG — nested option pattern:
|
|
965
|
+
match(opt_value,
|
|
966
|
+
.Some(.TypeVal(box)) => { ... }, // ERROR
|
|
967
|
+
_ => { ... }
|
|
968
|
+
);
|
|
969
|
+
|
|
970
|
+
// CORRECT — match in two stages:
|
|
971
|
+
match(opt_value,
|
|
972
|
+
.Some(v) => match(v,
|
|
973
|
+
.TypeVal(box) => { ... },
|
|
974
|
+
_ => { ... }
|
|
975
|
+
),
|
|
976
|
+
.None => { ... }
|
|
977
|
+
);
|
|
978
|
+
```
|
|
979
|
+
|
|
980
|
+
### Outer match on `Option` must have `.None` arm
|
|
981
|
+
|
|
982
|
+
When using `match(opt, .Some(x) => match(x, ...), ...)`, the outer match
|
|
983
|
+
needs its own `.None` arm. The inner match's `_ =>` wildcard does NOT cover
|
|
984
|
+
the outer match's `.None` variant.
|
|
985
|
+
|
|
986
|
+
```rust
|
|
987
|
+
// WRONG — outer match missing .None:
|
|
988
|
+
match(opt_callee_value,
|
|
989
|
+
.Some(cv) => match(cv,
|
|
990
|
+
.Foo(x) => { ... },
|
|
991
|
+
_ => { throw_phase3() } // inner wildcard, does NOT cover outer .None
|
|
992
|
+
) // outer match closes here — .None uncovered!
|
|
993
|
+
);
|
|
994
|
+
|
|
995
|
+
// CORRECT — add explicit .None arm to outer match:
|
|
996
|
+
match(opt_callee_value,
|
|
997
|
+
.Some(cv) => match(cv,
|
|
998
|
+
.Foo(x) => { ... },
|
|
999
|
+
_ => { throw_phase3() }
|
|
1000
|
+
),
|
|
1001
|
+
.None => { throw_phase3_none() }
|
|
1002
|
+
);
|
|
1003
|
+
```
|
|
1004
|
+
|
|
1005
|
+
### Parenthesis balance in deeply nested matches
|
|
1006
|
+
|
|
1007
|
+
When using `match(outer, .Some(x) => match(inner, ...), .None => ...)`,
|
|
1008
|
+
count parentheses carefully:
|
|
1009
|
+
|
|
1010
|
+
- The inner `match(inner, ...)` closes with its own `)`
|
|
1011
|
+
- AFTER that `)`, add a `,` then the outer `.None =>` arm
|
|
1012
|
+
- The outer match closes with its own `)`
|
|
1013
|
+
- Only then does `});` close the function body
|
|
1014
|
+
|
|
1015
|
+
```rust
|
|
1016
|
+
// Correct structure:
|
|
1017
|
+
match(outer_val,
|
|
1018
|
+
.Some(x) => match(x,
|
|
1019
|
+
arm1,
|
|
1020
|
+
arm2,
|
|
1021
|
+
_ => { fallback() } // last inner arm, no trailing comma
|
|
1022
|
+
), // ← closes inner match; `,` continues outer
|
|
1023
|
+
.None => { fallback() } // ← outer .None arm
|
|
1024
|
+
) // ← closes outer match
|
|
1025
|
+
```
|