@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
|
@@ -51,7 +51,7 @@ total := {
|
|
|
51
51
|
};
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
Remember: `{ expr }` without semicolons is a struct literal, not a block.
|
|
54
|
+
Remember: `{ expr }` without semicolons is a struct literal, not a block. The parser now detects this mistake and emits a clear error if the single expression is not a valid struct field.
|
|
55
55
|
|
|
56
56
|
## Control flow
|
|
57
57
|
|
|
@@ -90,6 +90,8 @@ 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.
|
|
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.
|
|
93
95
|
|
|
94
96
|
## Calls, operators, and whitespace
|
|
95
97
|
|
|
@@ -102,7 +104,10 @@ masked := ((A | B) | C);
|
|
|
102
104
|
- Prefer parenthesized calls: `func(arg1, arg2)`
|
|
103
105
|
- `func (a, b)` is a different parse shape than `func(a, b)`
|
|
104
106
|
- Yo has no operator precedence; fully parenthesize binary expressions
|
|
105
|
-
-
|
|
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.
|
|
108
|
+
- Parenthesize other unary operands too: `!(ready)`, `-(value)`
|
|
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), ...)`.
|
|
106
111
|
|
|
107
112
|
## Functions and methods
|
|
108
113
|
|
|
@@ -126,6 +131,7 @@ impl(Counter,
|
|
|
126
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.
|
|
127
132
|
- Wrap `fn` types in parentheses when they appear after `:`
|
|
128
133
|
- **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.
|
|
134
|
+
- **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`.
|
|
129
135
|
|
|
130
136
|
### Named arguments and default values
|
|
131
137
|
|
|
@@ -223,6 +229,22 @@ text := match(value,
|
|
|
223
229
|
- Construction and match branches use the leading `.`
|
|
224
230
|
- Nested destructuring is not supported; match one layer at a time
|
|
225
231
|
|
|
232
|
+
Three destructuring shapes for arms (mix freely across arms):
|
|
233
|
+
|
|
234
|
+
```rust
|
|
235
|
+
Shape :: enum(Circle(radius : i32), Rectangle(width : i32, height : i32));
|
|
236
|
+
|
|
237
|
+
match(s,
|
|
238
|
+
.Circle(r) => (r * r), // positional
|
|
239
|
+
.Rectangle(width: w, height: h) => (w * h), // labeled
|
|
240
|
+
.Rectangle({width, height: h}) => (width * h) // curly shorthand
|
|
241
|
+
)
|
|
242
|
+
```
|
|
243
|
+
|
|
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.
|
|
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
|
+
|
|
226
248
|
## Generics and compile-time
|
|
227
249
|
|
|
228
250
|
```rust
|
|
@@ -294,14 +316,36 @@ factorial :: (fn(n : i32) -> i32)(
|
|
|
294
316
|
)
|
|
295
317
|
);
|
|
296
318
|
|
|
297
|
-
while runtime
|
|
319
|
+
// Runtime infinite loop — `while cond` is ALWAYS runtime
|
|
320
|
+
while true, {
|
|
298
321
|
work();
|
|
299
322
|
};
|
|
323
|
+
|
|
324
|
+
// Compile-time loop unrolling — requires comptime() modifier
|
|
325
|
+
while comptime(i < 10), {
|
|
326
|
+
// body evaluated/unrolled at compile time
|
|
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
|
+
});
|
|
300
341
|
```
|
|
301
342
|
|
|
302
343
|
- Use `recur(...)` for self-recursion
|
|
303
|
-
- `while
|
|
304
|
-
-
|
|
344
|
+
- `while cond` is **always a runtime loop** — use this for open-ended loops (e.g., server accept loops, event loops)
|
|
345
|
+
- `while comptime(cond)` explicitly unrolls at compile time — `cond` must be a compile-time-known value
|
|
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`)
|
|
305
349
|
|
|
306
350
|
## Return and branch safety
|
|
307
351
|
|
|
@@ -333,9 +377,27 @@ get_value :: (fn(opt : Option(i32)) -> i32)(
|
|
|
333
377
|
|
|
334
378
|
- `return expr1, expr2` parses as a single function call: `return(expr1, expr2)`
|
|
335
379
|
- In `cond` or `match` branches, **always use begin blocks** when you need `return`
|
|
380
|
+
- `return` must be the **last expression** in a begin block — dead code after `return` is rejected. Do NOT write `{ return x; fallback_val }`. Write `{ return x; }` only.
|
|
336
381
|
- If the whole function is one expression, prefer expression-bodied style and skip `return` entirely
|
|
337
382
|
- The same trap applies to any function call without parens in match branches
|
|
338
383
|
|
|
384
|
+
## String concatenation pitfall
|
|
385
|
+
|
|
386
|
+
```rust
|
|
387
|
+
// WRONG — str + str causes "comptime_string vs str" type unification error:
|
|
388
|
+
content := String.from("line1\n" + "line2\n");
|
|
389
|
+
|
|
390
|
+
// CORRECT — use .concat() on String objects:
|
|
391
|
+
content := String.from("line1\n").concat(String.from("line2\n"));
|
|
392
|
+
|
|
393
|
+
// Also CORRECT — single long string literal:
|
|
394
|
+
content := String.from("line1\nline2\n");
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
- `"hello" + "world"` at runtime uses `+` on `str` values, which can cause type mismatches
|
|
398
|
+
- The `str + str` operator can produce a `comptime_string` in some contexts, which is not always compatible with `str`
|
|
399
|
+
- Prefer `.concat()` method on `String` objects when building multi-part strings at runtime
|
|
400
|
+
|
|
339
401
|
## Iterator and for loop
|
|
340
402
|
|
|
341
403
|
```rust
|
|
@@ -381,7 +443,311 @@ test "Async test", {
|
|
|
381
443
|
- `comptime_assert(condition)` — compile-time assertion
|
|
382
444
|
- `comptime_expect_error(expr)` — verify code produces a compile error
|
|
383
445
|
|
|
384
|
-
##
|
|
446
|
+
## Common pitfalls
|
|
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
|
+
|
|
477
|
+
### `impl(...)` requires a trailing semicolon
|
|
478
|
+
|
|
479
|
+
```rust
|
|
480
|
+
// WRONG — "Invalid function call on type" at runtime:
|
|
481
|
+
impl(MyType,
|
|
482
|
+
get : (fn(self : Self) -> i32)(self.x)
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
// CORRECT:
|
|
486
|
+
impl(MyType,
|
|
487
|
+
get : (fn(self : Self) -> i32)(self.x)
|
|
488
|
+
);
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
### `___` discard variable cannot appear twice in the same scope
|
|
492
|
+
|
|
493
|
+
```rust
|
|
494
|
+
// WRONG — shadowing of ___ is not allowed:
|
|
495
|
+
___ := foo();
|
|
496
|
+
___ := bar();
|
|
497
|
+
|
|
498
|
+
// CORRECT — use unique names or bare calls:
|
|
499
|
+
_a := foo();
|
|
500
|
+
_b := bar();
|
|
501
|
+
// or simply:
|
|
502
|
+
foo();
|
|
503
|
+
bar();
|
|
504
|
+
```
|
|
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
|
+
|
|
528
|
+
### `type` is a reserved keyword — avoid as field/param name
|
|
529
|
+
|
|
530
|
+
```rust
|
|
531
|
+
// WRONG:
|
|
532
|
+
Variable :: object(name : String, type : TypeValue);
|
|
533
|
+
|
|
534
|
+
// CORRECT:
|
|
535
|
+
Variable :: object(name : String, ty : TypeValue);
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
### ArrayList indexing uses call syntax
|
|
539
|
+
|
|
540
|
+
```rust
|
|
541
|
+
list := ArrayList(i32).new();
|
|
542
|
+
list.push(i32(42));
|
|
543
|
+
|
|
544
|
+
val := list(usize(0)); // → i32 (value copy via Index trait)
|
|
545
|
+
list(usize(0)) = i32(99); // mutate in place directly
|
|
546
|
+
|
|
547
|
+
// When you need the pointer explicitly:
|
|
548
|
+
ptr := &(list(usize(0))); // → *(i32)
|
|
549
|
+
ptr.* = i32(99); // also works
|
|
550
|
+
|
|
551
|
+
// Safe access (returns Option(T)):
|
|
552
|
+
match(list.get(usize(0)),
|
|
553
|
+
.Some(v) => println(`${v}`),
|
|
554
|
+
.None => ()
|
|
555
|
+
);
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
- `list(i)` returns the value `T` (not a pointer)
|
|
559
|
+
- `list(i) = val` mutates in place directly (preferred)
|
|
560
|
+
- `&(list(i))` returns `*(T)` if you need the pointer explicitly
|
|
561
|
+
- `list.get(i)` returns `Option(T)` for safe bounds-checked access
|
|
562
|
+
|
|
563
|
+
### Named fields required for `struct`/`object` constructors
|
|
564
|
+
|
|
565
|
+
```rust
|
|
566
|
+
Point :: struct(x : i32, y : i32);
|
|
567
|
+
|
|
568
|
+
// CORRECT:
|
|
569
|
+
p := Point(x: i32(1), y: i32(2));
|
|
570
|
+
|
|
571
|
+
// WRONG — positional not supported for struct/object:
|
|
572
|
+
p := Point(i32(1), i32(2));
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
Enum variant construction is positional (no field names needed).
|
|
576
|
+
|
|
577
|
+
### Object types (RC) are passed by value
|
|
578
|
+
|
|
579
|
+
`HashMap`, `ArrayList`, and other `object(...)` types are reference-counted. Passing them by value shares the underlying data — mutations are visible to all holders.
|
|
580
|
+
|
|
581
|
+
```rust
|
|
582
|
+
// DO NOT use pointer params for RC objects:
|
|
583
|
+
// WRONG: fn(m : *(HashMap(String, V))) — will cause greedy & issues at call site
|
|
584
|
+
// CORRECT: fn(m : HashMap(String, V)) — pass by value, mutations propagate via RC
|
|
585
|
+
|
|
586
|
+
process_map :: (fn(m : HashMap(String, i32)) -> unit)({
|
|
587
|
+
m.set(String.from("key"), i32(42)); // mutation visible to caller
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
counts := HashMap(String, i32).new();
|
|
591
|
+
process_map(counts);
|
|
592
|
+
// counts now has "key" => 42
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
### Forward references are NOT allowed
|
|
596
|
+
|
|
597
|
+
Top-level bindings are evaluated strictly in order. A function must be defined BEFORE it is called (even inside closures that are called later).
|
|
598
|
+
|
|
599
|
+
```rust
|
|
600
|
+
// WRONG — forward reference:
|
|
601
|
+
caller :: (fn() -> unit)({ helper(); });
|
|
602
|
+
helper :: (fn() -> unit)({ println("hi"); });
|
|
603
|
+
|
|
604
|
+
// CORRECT — helper before caller:
|
|
605
|
+
helper :: (fn() -> unit)({ println("hi"); });
|
|
606
|
+
caller :: (fn() -> unit)({ helper(); });
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
This applies to ALL callee-before-caller relationships:
|
|
610
|
+
|
|
611
|
+
- `_walk_dag` before `build_dag`
|
|
612
|
+
- `compile_artifact`, `run_executable`, `run_test_suite` before `execute_node`
|
|
613
|
+
- `execute_node` before `execute_dag`
|
|
614
|
+
- `_print_summary_node` before `print_build_summary`
|
|
615
|
+
- `print_build_summary` before `execute_step`
|
|
616
|
+
- Exports section must come AFTER all definitions
|
|
617
|
+
|
|
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
|
+
```
|
|
656
|
+
|
|
657
|
+
The `!` operator is greedy and consumes all following args including the block. Always parenthesize:
|
|
658
|
+
|
|
659
|
+
```rust
|
|
660
|
+
// WRONG — ! consumes "cond, block" as one arg:
|
|
661
|
+
if(!cond, { do_thing(); });
|
|
662
|
+
|
|
663
|
+
// CORRECT — ! only consumes (cond):
|
|
664
|
+
if((!cond), { do_thing(); });
|
|
665
|
+
```
|
|
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
|
+
|
|
740
|
+
### Template strings produce `String`, literals are `str`
|
|
741
|
+
|
|
742
|
+
```rust
|
|
743
|
+
// Template string `` `...` `` → String
|
|
744
|
+
// String literal "..." → str
|
|
745
|
+
|
|
746
|
+
// If a function takes `str`, call .as_str() on a template string:
|
|
747
|
+
fn_taking_str((`prefix_${value}`).as_str());
|
|
748
|
+
|
|
749
|
+
// Or change the function to take String
|
|
750
|
+
```
|
|
385
751
|
|
|
386
752
|
These features are powerful but less commonly used. Consult the linked docs for full details.
|
|
387
753
|
|
|
@@ -398,3 +764,257 @@ These features are powerful but less commonly used. Consult the linked docs for
|
|
|
398
764
|
| Isolated types | `Iso(T)` for data-race-free parallelism | [ISOLATED.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/ISOLATED.md) |
|
|
399
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) |
|
|
400
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
|
+
```
|