@shd101wyy/yo 0.1.24 → 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-core-patterns/core-patterns-cheatsheet.md +30 -0
- package/.github/skills/yo-syntax/syntax-cheatsheet.md +436 -2
- package/out/cjs/index.cjs +551 -553
- package/out/cjs/yo-cli.cjs +638 -632
- package/out/cjs/yo-lsp.cjs +595 -597
- package/out/esm/index.mjs +487 -489
- package/out/types/src/codegen/utils/index.d.ts +1 -0
- package/out/types/src/expr.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/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
|
@@ -310,6 +310,21 @@ Yo's reference counting handles shallow copies automatically (no `Clone` trait c
|
|
|
310
310
|
the `Clone` trait is only for deep cloning and has the same circularity problem.
|
|
311
311
|
In practice, passing `EvalValue`-like types by value works fine without a `Clone` impl.
|
|
312
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
|
+
|
|
313
328
|
## Error handling
|
|
314
329
|
|
|
315
330
|
```rust
|
|
@@ -403,3 +418,18 @@ result := my_module.helper(i32(5));
|
|
|
403
418
|
|
|
404
419
|
- `impl { ... }` creates a module namespace
|
|
405
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
|
+
```
|
|
@@ -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
|
|
|
@@ -241,6 +243,8 @@ match(s,
|
|
|
241
243
|
|
|
242
244
|
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
245
|
|
|
246
|
+
> **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.
|
|
247
|
+
|
|
244
248
|
## Generics and compile-time
|
|
245
249
|
|
|
246
250
|
```rust
|
|
@@ -321,12 +325,27 @@ while true, {
|
|
|
321
325
|
while comptime(i < 10), {
|
|
322
326
|
// body evaluated/unrolled at compile time
|
|
323
327
|
};
|
|
328
|
+
|
|
329
|
+
// for loop — 2-arg prelude macro; first arg MUST be an iterator (has .next()):
|
|
330
|
+
for(list.iter(), x => { // ArrayList, array → call .iter() first
|
|
331
|
+
process(x);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
for(map.into_iter(), bucket => {
|
|
335
|
+
println(bucket.key);
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
for(list.iter(), (ptr) => { // ptr is a mutable reference to each element
|
|
339
|
+
ptr.* = transform(ptr.*);
|
|
340
|
+
});
|
|
324
341
|
```
|
|
325
342
|
|
|
326
343
|
- Use `recur(...)` for self-recursion
|
|
327
344
|
- `while cond` is **always a runtime loop** — use this for open-ended loops (e.g., server accept loops, event loops)
|
|
328
345
|
- `while comptime(cond)` explicitly unrolls at compile time — `cond` must be a compile-time-known value
|
|
329
346
|
- Using a comptime-only (`::`) variable in a bare `while` condition without `comptime()` is a **compile error** (would be an infinite loop at runtime)
|
|
347
|
+
- **`for(arr, item => { body })`** — correct 2-arg prelude macro form. The `item => { body }` is an anonymous closure.
|
|
348
|
+
- **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
349
|
|
|
331
350
|
## Return and branch safety
|
|
332
351
|
|
|
@@ -426,6 +445,35 @@ test "Async test", {
|
|
|
426
445
|
|
|
427
446
|
## Common pitfalls
|
|
428
447
|
|
|
448
|
+
### `&&` short-circuit with `match`/`cond` on RHS causes C codegen scope bug
|
|
449
|
+
|
|
450
|
+
Using `&&` where the right-hand side is a `match` or `cond` expression causes
|
|
451
|
+
a C codegen bug: the temp variable for the RHS is declared inside the short-circuit
|
|
452
|
+
`if` block but the cleanup drop is emitted outside it. This produces a C compile
|
|
453
|
+
error ("use of undeclared identifier").
|
|
454
|
+
|
|
455
|
+
```rust
|
|
456
|
+
// WRONG — triggers codegen scope bug:
|
|
457
|
+
is_ok := (av.is_compile_time_only && match(av.value,
|
|
458
|
+
.Some(v) => compute(v),
|
|
459
|
+
.None => false
|
|
460
|
+
));
|
|
461
|
+
|
|
462
|
+
// CORRECT — use an explicit if block to scope the match:
|
|
463
|
+
(is_ok : bool) = false;
|
|
464
|
+
if(av.is_compile_time_only, {
|
|
465
|
+
is_ok = match(av.value,
|
|
466
|
+
.Some(v) => compute(v),
|
|
467
|
+
.None => false
|
|
468
|
+
);
|
|
469
|
+
});
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
This only affects `&&`/`||` where the **right-hand side contains a `match`,
|
|
473
|
+
`cond`, or other expression that allocates heap-managed temporaries** (e.g.,
|
|
474
|
+
`String`, `ArrayList`, `Option(HeapType)`). Pure boolean expressions on both
|
|
475
|
+
sides are fine.
|
|
476
|
+
|
|
429
477
|
### `impl(...)` requires a trailing semicolon
|
|
430
478
|
|
|
431
479
|
```rust
|
|
@@ -455,6 +503,28 @@ foo();
|
|
|
455
503
|
bar();
|
|
456
504
|
```
|
|
457
505
|
|
|
506
|
+
### Enum pattern matching does NOT support literal values
|
|
507
|
+
|
|
508
|
+
Match patterns on enum variants only support **variable binding**, not literal comparison.
|
|
509
|
+
`.BoolVal(true)` binds the inner value to a variable named `true` — it does NOT check
|
|
510
|
+
if the value is `true`. The arm always matches any `BoolVal`.
|
|
511
|
+
|
|
512
|
+
```rust
|
|
513
|
+
// ❌ WRONG — always matches (true is a variable binding, not a comparison)
|
|
514
|
+
match(val,
|
|
515
|
+
.BoolVal(true) => handle_true(),
|
|
516
|
+
_ => ()
|
|
517
|
+
);
|
|
518
|
+
|
|
519
|
+
// ✅ CORRECT — bind to variable, then check with cond
|
|
520
|
+
match(val,
|
|
521
|
+
.BoolVal(b) => cond(b => handle_true(), true => ()),
|
|
522
|
+
_ => ()
|
|
523
|
+
);
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
Same applies to `.IntLit(42)`, `.StrLit("hello")`, etc.
|
|
527
|
+
|
|
458
528
|
### `type` is a reserved keyword — avoid as field/param name
|
|
459
529
|
|
|
460
530
|
```rust
|
|
@@ -545,7 +615,44 @@ This applies to ALL callee-before-caller relationships:
|
|
|
545
615
|
- `print_build_summary` before `execute_step`
|
|
546
616
|
- Exports section must come AFTER all definitions
|
|
547
617
|
|
|
548
|
-
###
|
|
618
|
+
### Named tuple fields in type syntax are not allowed
|
|
619
|
+
|
|
620
|
+
Yo does not support named tuple field types in the syntax `(name : Type, ...)`. Use an `object` struct instead:
|
|
621
|
+
|
|
622
|
+
```rust
|
|
623
|
+
// WRONG — "Labelled field is not allowed in tuple value":
|
|
624
|
+
get_range :: (fn(ty : TypeValue) -> Option((min : i64, max : u64)))(
|
|
625
|
+
.Some((min: i64(-128), max: u64(127)))
|
|
626
|
+
);
|
|
627
|
+
|
|
628
|
+
// CORRECT — define a named struct:
|
|
629
|
+
Range :: object(min : i64, max : u64);
|
|
630
|
+
get_range :: (fn(ty : TypeValue) -> Option(Range))(
|
|
631
|
+
.Some({min: i64(-128), max: u64(127)})
|
|
632
|
+
);
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
Named fields work in struct/object constructors `{min: ..., max: ...}`, just not in the type syntax for tuples.
|
|
636
|
+
|
|
637
|
+
### `Option` equality comparison limitations
|
|
638
|
+
|
|
639
|
+
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:
|
|
640
|
+
|
|
641
|
+
```rust
|
|
642
|
+
// WRONG — "No matching call found with arguments: r == .None":
|
|
643
|
+
assert((r == .None), "should be None");
|
|
644
|
+
|
|
645
|
+
// CORRECT — use .is_none() / .is_some() / .unwrap():
|
|
646
|
+
assert(r.is_none(), "should be None");
|
|
647
|
+
assert(r.is_some(), "should be Some");
|
|
648
|
+
assert((r.unwrap() == expected_value), "value check");
|
|
649
|
+
|
|
650
|
+
// Also fine in match:
|
|
651
|
+
match(r,
|
|
652
|
+
.Some(v) => assert((v == expected_value), "value check"),
|
|
653
|
+
.None => assert(false, "unexpected None")
|
|
654
|
+
);
|
|
655
|
+
```
|
|
549
656
|
|
|
550
657
|
The `!` operator is greedy and consumes all following args including the block. Always parenthesize:
|
|
551
658
|
|
|
@@ -557,6 +664,79 @@ if(!cond, { do_thing(); });
|
|
|
557
664
|
if((!cond), { do_thing(); });
|
|
558
665
|
```
|
|
559
666
|
|
|
667
|
+
### `escape` requires a nested-function context
|
|
668
|
+
|
|
669
|
+
`escape value` exits the **enclosing function** — the nearest `fn(...)` that
|
|
670
|
+
wraps the current code. It requires that the code is inside a nested function
|
|
671
|
+
(e.g., a closure or `given` handler lambda), NOT at the top level of a
|
|
672
|
+
standalone function definition.
|
|
673
|
+
|
|
674
|
+
```rust
|
|
675
|
+
// CORRECT — escape inside a given handler lambda (lambda has enclosing fn):
|
|
676
|
+
given(exn) := Exception(throw: ((err) -> {
|
|
677
|
+
escape result // exits the outer function that contains this given()
|
|
678
|
+
}));
|
|
679
|
+
do_something(using(exn));
|
|
680
|
+
|
|
681
|
+
// CORRECT — escape inside a closure passed as argument:
|
|
682
|
+
result := match(opt, .Some(x) => x, .None => {
|
|
683
|
+
// This is NOT a nested function — use return:
|
|
684
|
+
// WRONG: escape default_val // ERROR: no enclosing fn
|
|
685
|
+
return default_val // CORRECT: return exits the enclosing fn directly
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
// WRONG — escape inside a match arm (not a nested fn):
|
|
689
|
+
match(opt,
|
|
690
|
+
.Some(x) => { escape x } // ERROR: "can only be used inside a function that has an enclosing function"
|
|
691
|
+
);
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
**Rule of thumb**: Use `escape` only inside lambdas passed to `given()` handlers.
|
|
695
|
+
In all other contexts (match arms, if blocks, begin blocks, top-level fn bodies),
|
|
696
|
+
use `return` for early exit.
|
|
697
|
+
|
|
698
|
+
`return` inside a lambda exits that lambda only; `escape` exits the OUTER function
|
|
699
|
+
(the one that installed the `given` handler).
|
|
700
|
+
|
|
701
|
+
### Parameter reassignment
|
|
702
|
+
|
|
703
|
+
Function parameters are **NOT reassignable**. To reassign, declare a mutable local:
|
|
704
|
+
|
|
705
|
+
```rust
|
|
706
|
+
// WRONG — cannot reassign parameter 'env':
|
|
707
|
+
my_fn :: (fn(env : Environment) -> Environment)({
|
|
708
|
+
env = other_env; // ERROR: "cannot reassign itself"
|
|
709
|
+
env
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
// CORRECT — create a mutable local copy:
|
|
713
|
+
my_fn :: (fn(init_env : Environment) -> Environment)({
|
|
714
|
+
(env : Environment) = init_env;
|
|
715
|
+
env = other_env; // OK — reassigning local variable
|
|
716
|
+
env
|
|
717
|
+
});
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
This also applies to `object` types: you can mutate fields (`env.field = val`)
|
|
721
|
+
but cannot rebind the variable (`env = other_env`).
|
|
722
|
+
|
|
723
|
+
### String cloning
|
|
724
|
+
|
|
725
|
+
Calling `.clone()` on a `String` field from a struct/method chain requires a
|
|
726
|
+
reference — take `&` first:
|
|
727
|
+
|
|
728
|
+
```rust
|
|
729
|
+
// WRONG — .clone() requires *(Self) but gets Self value from field access:
|
|
730
|
+
name := token.value.clone(); // ERROR
|
|
731
|
+
|
|
732
|
+
// CORRECT — take reference first:
|
|
733
|
+
tok := some_fn_call();
|
|
734
|
+
name := (&tok.value).clone(); // OK
|
|
735
|
+
|
|
736
|
+
// ALSO CORRECT — use String.from on the str slice:
|
|
737
|
+
name := String.from(token.value.as_str());
|
|
738
|
+
```
|
|
739
|
+
|
|
560
740
|
### Template strings produce `String`, literals are `str`
|
|
561
741
|
|
|
562
742
|
```rust
|
|
@@ -584,3 +764,257 @@ These features are powerful but less commonly used. Consult the linked docs for
|
|
|
584
764
|
| Isolated types | `Iso(T)` for data-race-free parallelism | [ISOLATED.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/ISOLATED.md) |
|
|
585
765
|
| 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
766
|
| Parallelism | Thread pool, `io.spawn` for parallel work | [PARALLELISM.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/PARALLELISM.md) |
|
|
767
|
+
|
|
768
|
+
---
|
|
769
|
+
|
|
770
|
+
## Self-hosted evaluator (`yo-self/`) — known limitations and pitfalls
|
|
771
|
+
|
|
772
|
+
These constraints apply **only** to the self-hosted Yo evaluator (code inside `yo-self/`). The TypeScript-compiled Yo compiler does not have these restrictions.
|
|
773
|
+
|
|
774
|
+
### Match patterns: no bare identifier catch-all
|
|
775
|
+
|
|
776
|
+
The self-hosted parser only accepts three match arm forms:
|
|
777
|
+
|
|
778
|
+
- `.VariantName` — unit variant
|
|
779
|
+
- `.VariantName(p1, p2, ...)` — tuple variant
|
|
780
|
+
- `_` — wildcard
|
|
781
|
+
|
|
782
|
+
**Bare identifier catch-all `t => ...` is NOT supported.** Use `_` and access the outer binding directly:
|
|
783
|
+
|
|
784
|
+
```rust
|
|
785
|
+
// ❌ NOT supported in yo-self/
|
|
786
|
+
match(val, {
|
|
787
|
+
.SomeVariant(x) => x,
|
|
788
|
+
t => t, // ERROR: bare identifier not a valid pattern
|
|
789
|
+
})
|
|
790
|
+
|
|
791
|
+
// ✅ Correct — use outer binding
|
|
792
|
+
match(val, {
|
|
793
|
+
.SomeVariant(x) => x,
|
|
794
|
+
_ => val, // refer to outer binding
|
|
795
|
+
})
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
### String concatenation: no `String + str` operator
|
|
799
|
+
|
|
800
|
+
The `+` operator does not accept mixed `String`/`str` operands.
|
|
801
|
+
**Always use template strings** for concatenation:
|
|
802
|
+
|
|
803
|
+
```rust
|
|
804
|
+
// ❌ Type error
|
|
805
|
+
result := (parts + ", ");
|
|
806
|
+
result := (parts + item.as_str());
|
|
807
|
+
|
|
808
|
+
// ✅ Template strings
|
|
809
|
+
result := `${parts}, `;
|
|
810
|
+
result := `${parts}${item}`;
|
|
811
|
+
```
|
|
812
|
+
|
|
813
|
+
### `clone()` on extracted String fields is ambiguous
|
|
814
|
+
|
|
815
|
+
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))`).
|
|
816
|
+
|
|
817
|
+
```rust
|
|
818
|
+
// ❌ Ambiguous
|
|
819
|
+
.StructVal(name, fields) => name.clone()
|
|
820
|
+
|
|
821
|
+
// ✅ Use from + as_str
|
|
822
|
+
.StructVal(name, fields) => String.from(name.as_str())
|
|
823
|
+
```
|
|
824
|
+
|
|
825
|
+
### `box(val)` is a move — cannot box the same value twice
|
|
826
|
+
|
|
827
|
+
```rust
|
|
828
|
+
// ❌ Move error: target is moved by first box(target)
|
|
829
|
+
p1 := PtrVal(box(target), usize(0));
|
|
830
|
+
p2 := PtrVal(box(target), usize(0)); // ERROR: target already moved
|
|
831
|
+
|
|
832
|
+
// ✅ Create separate instances
|
|
833
|
+
p1 := PtrVal(box(EvalValue.IntLit(String.from("42"))), usize(0));
|
|
834
|
+
p2 := PtrVal(box(EvalValue.IntLit(String.from("42"))), usize(0));
|
|
835
|
+
```
|
|
836
|
+
|
|
837
|
+
### `recur(...)` for self-recursive lambdas
|
|
838
|
+
|
|
839
|
+
Lambdas defined as `name :: (fn(args) -> T)(body)` cannot call `name` inside `body`.
|
|
840
|
+
Use `recur(...)` instead:
|
|
841
|
+
|
|
842
|
+
```rust
|
|
843
|
+
// ❌ Would not find `my_fn` inside its own body
|
|
844
|
+
my_fn :: (fn(x : i32) -> i32)({
|
|
845
|
+
my_fn(x - 1) // ERROR: `my_fn` not in scope yet
|
|
846
|
+
});
|
|
847
|
+
|
|
848
|
+
// ✅ Use recur
|
|
849
|
+
my_fn :: (fn(x : i32) -> i32)({
|
|
850
|
+
recur(x - 1)
|
|
851
|
+
});
|
|
852
|
+
```
|
|
853
|
+
|
|
854
|
+
### `{ expr }` without semicolons is a struct literal, not a block
|
|
855
|
+
|
|
856
|
+
```rust
|
|
857
|
+
// ❌ Parsed as struct literal `{ match(...) }`
|
|
858
|
+
fn :: (fn() -> T)({ match(x, arms) })
|
|
859
|
+
|
|
860
|
+
// ✅ Remove braces or add semicolon
|
|
861
|
+
fn :: (fn() -> T)(match(x, arms))
|
|
862
|
+
fn :: (fn() -> T)({ match(x, arms); })
|
|
863
|
+
```
|
|
864
|
+
|
|
865
|
+
### Template strings cannot be nested inside `${...}` interpolations
|
|
866
|
+
|
|
867
|
+
A template string literal (`` ` `` ... `` ` ``) inside a `${...}` interpolation of another template string closes the outer string. The compiler gives confusing parse errors.
|
|
868
|
+
|
|
869
|
+
```rust
|
|
870
|
+
// ❌ Inner backtick closes the outer template string — parse error
|
|
871
|
+
lines.push(`**Implements:** ${`, `.join(names)}`);
|
|
872
|
+
|
|
873
|
+
// ✅ Assign the separator to a variable first
|
|
874
|
+
sep := `, `;
|
|
875
|
+
lines.push(`**Implements:** ${sep.join(names)}`);
|
|
876
|
+
```
|
|
877
|
+
|
|
878
|
+
### Pushing RC struct fields into ArrayList does not need `.clone()`
|
|
879
|
+
|
|
880
|
+
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.
|
|
881
|
+
|
|
882
|
+
```rust
|
|
883
|
+
// ❌ Ambiguous clone call
|
|
884
|
+
names.push(param.name.clone());
|
|
885
|
+
|
|
886
|
+
// ✅ Push directly — RC bump happens automatically
|
|
887
|
+
names.push(param.name);
|
|
888
|
+
```
|
|
889
|
+
|
|
890
|
+
If explicit clone is needed elsewhere, use `(&field).clone()` to select the pointer overload.
|
|
891
|
+
|
|
892
|
+
### `.Some(expr)` in expression position is parsed as a 2-arg property access
|
|
893
|
+
|
|
894
|
+
Using `.Some(x)` as an expression (not inside a match pattern) is parsed by the Yo parser
|
|
895
|
+
as a 2-arg dot property access: `obj.(prop, arg)`. This means `evaluate_property_access` is
|
|
896
|
+
invoked on it at compile time, causing confusing errors like "Failed to infer enum variant type".
|
|
897
|
+
|
|
898
|
+
```rust
|
|
899
|
+
// ❌ Parsed as 2-arg property access — NOT an Option::Some constructor call
|
|
900
|
+
val := .Some(oi.ty);
|
|
901
|
+
|
|
902
|
+
// ✅ Use the explicit fully-qualified form
|
|
903
|
+
val := Option(TypeValue).Some(oi.ty);
|
|
904
|
+
```
|
|
905
|
+
|
|
906
|
+
### `||` chaining requires explicit parentheses for 3+ operands
|
|
907
|
+
|
|
908
|
+
Chaining three or more `||` terms in a single expression is rejected with a precedence error.
|
|
909
|
+
Always add explicit parentheses around each pair:
|
|
910
|
+
|
|
911
|
+
```rust
|
|
912
|
+
// ❌ Rejected — ambiguous precedence
|
|
913
|
+
if ((is_tuple_type(ty) || is_struct_type(ty) || is_union_type(ty)), ...)
|
|
914
|
+
|
|
915
|
+
// ✅ Parenthesise each pair
|
|
916
|
+
if (((is_tuple_type(ty) || is_struct_type(ty)) || is_union_type(ty)), ...)
|
|
917
|
+
```
|
|
918
|
+
|
|
919
|
+
### Duplicate imports from the same path must be merged
|
|
920
|
+
|
|
921
|
+
Having two `:: import "path"` lines importing from the same file causes a compile error.
|
|
922
|
+
Always merge them into a single destructuring import:
|
|
923
|
+
|
|
924
|
+
```rust
|
|
925
|
+
// ❌ Two imports from the same path
|
|
926
|
+
{ Foo } :: import "../../mod.yo";
|
|
927
|
+
{ Bar } :: import "../../mod.yo";
|
|
928
|
+
|
|
929
|
+
// ✅ Merged
|
|
930
|
+
{ Foo, Bar } :: import "../../mod.yo";
|
|
931
|
+
```
|
|
932
|
+
|
|
933
|
+
### Implicit (`using`) parameters cannot be used with `:=` assignment
|
|
934
|
+
|
|
935
|
+
Implicit effect parameters introduced via `using(name : Type)` cannot be bound
|
|
936
|
+
to a discarded variable with `:= name`. They can only be passed via `using()`.
|
|
937
|
+
To suppress "unused parameter" warnings for an implicit param, simply omit the
|
|
938
|
+
discard assignment — implicit params never trigger unused-variable errors.
|
|
939
|
+
|
|
940
|
+
```rust
|
|
941
|
+
// WRONG — implicit variable cannot be used in assignment:
|
|
942
|
+
foo :: (fn(using(exn : Exception)) -> unit)({
|
|
943
|
+
_ := exn; // ERROR: Cannot use implicit variable "exn" in assignment
|
|
944
|
+
()
|
|
945
|
+
});
|
|
946
|
+
|
|
947
|
+
// CORRECT — just omit the discard line:
|
|
948
|
+
foo :: (fn(using(exn : Exception)) -> unit)({
|
|
949
|
+
()
|
|
950
|
+
});
|
|
951
|
+
```
|
|
952
|
+
|
|
953
|
+
### Nested `Option` patterns require staging
|
|
954
|
+
|
|
955
|
+
`match` does not support nested destructuring patterns like `.Some(.TypeVal(x))`.
|
|
956
|
+
Split into two separate `match` expressions.
|
|
957
|
+
|
|
958
|
+
```rust
|
|
959
|
+
// WRONG — nested option pattern:
|
|
960
|
+
match(opt_value,
|
|
961
|
+
.Some(.TypeVal(box)) => { ... }, // ERROR
|
|
962
|
+
_ => { ... }
|
|
963
|
+
);
|
|
964
|
+
|
|
965
|
+
// CORRECT — match in two stages:
|
|
966
|
+
match(opt_value,
|
|
967
|
+
.Some(v) => match(v,
|
|
968
|
+
.TypeVal(box) => { ... },
|
|
969
|
+
_ => { ... }
|
|
970
|
+
),
|
|
971
|
+
.None => { ... }
|
|
972
|
+
);
|
|
973
|
+
```
|
|
974
|
+
|
|
975
|
+
### Outer match on `Option` must have `.None` arm
|
|
976
|
+
|
|
977
|
+
When using `match(opt, .Some(x) => match(x, ...), ...)`, the outer match
|
|
978
|
+
needs its own `.None` arm. The inner match's `_ =>` wildcard does NOT cover
|
|
979
|
+
the outer match's `.None` variant.
|
|
980
|
+
|
|
981
|
+
```rust
|
|
982
|
+
// WRONG — outer match missing .None:
|
|
983
|
+
match(opt_callee_value,
|
|
984
|
+
.Some(cv) => match(cv,
|
|
985
|
+
.Foo(x) => { ... },
|
|
986
|
+
_ => { throw_phase3() } // inner wildcard, does NOT cover outer .None
|
|
987
|
+
) // outer match closes here — .None uncovered!
|
|
988
|
+
);
|
|
989
|
+
|
|
990
|
+
// CORRECT — add explicit .None arm to outer match:
|
|
991
|
+
match(opt_callee_value,
|
|
992
|
+
.Some(cv) => match(cv,
|
|
993
|
+
.Foo(x) => { ... },
|
|
994
|
+
_ => { throw_phase3() }
|
|
995
|
+
),
|
|
996
|
+
.None => { throw_phase3_none() }
|
|
997
|
+
);
|
|
998
|
+
```
|
|
999
|
+
|
|
1000
|
+
### Parenthesis balance in deeply nested matches
|
|
1001
|
+
|
|
1002
|
+
When using `match(outer, .Some(x) => match(inner, ...), .None => ...)`,
|
|
1003
|
+
count parentheses carefully:
|
|
1004
|
+
|
|
1005
|
+
- The inner `match(inner, ...)` closes with its own `)`
|
|
1006
|
+
- AFTER that `)`, add a `,` then the outer `.None =>` arm
|
|
1007
|
+
- The outer match closes with its own `)`
|
|
1008
|
+
- Only then does `});` close the function body
|
|
1009
|
+
|
|
1010
|
+
```rust
|
|
1011
|
+
// Correct structure:
|
|
1012
|
+
match(outer_val,
|
|
1013
|
+
.Some(x) => match(x,
|
|
1014
|
+
arm1,
|
|
1015
|
+
arm2,
|
|
1016
|
+
_ => { fallback() } // last inner arm, no trailing comma
|
|
1017
|
+
), // ← closes inner match; `,` continues outer
|
|
1018
|
+
.None => { fallback() } // ← outer .None arm
|
|
1019
|
+
) // ← closes outer match
|
|
1020
|
+
```
|