@shd101wyy/yo 0.1.27 → 0.1.28
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 +3 -0
- package/.github/skills/yo-project-workflow/workflow-cheatsheet.md +2 -0
- package/.github/skills/yo-syntax/syntax-cheatsheet.md +152 -4
- package/out/cjs/index.cjs +446 -446
- package/out/cjs/yo-cli.cjs +619 -618
- package/out/cjs/yo-lsp.cjs +559 -559
- package/out/esm/index.mjs +373 -373
- package/out/types/src/env.d.ts +1 -0
- package/out/types/src/evaluator/calls/helper.d.ts +4 -2
- package/out/types/src/evaluator/types/synthesizer.d.ts +1 -0
- package/out/types/src/test-runner.d.ts +2 -0
- package/out/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/scripts/probe-parser-parity.ts +61 -0
- package/scripts/probe-yo-self-parser.sh +33 -0
- package/scripts/validate-yo-self-fmt.ts +184 -0
|
@@ -144,6 +144,9 @@ counter.* = (counter.* + i32(1));
|
|
|
144
144
|
- Use `*(T)` for raw pointers
|
|
145
145
|
- Model nullable pointers as `Option(*(T))` or `?*(T)`, not sentinel integers
|
|
146
146
|
- Constructor syntax: `Box(T)(value)` — NOT `Box(T).new(value)`
|
|
147
|
+
- Single-payload objects may use `(*) : T`; access the payload with `value.*`.
|
|
148
|
+
This is a value payload accessor for object values, while pointer dereference
|
|
149
|
+
still applies when the receiver has pointer type.
|
|
147
150
|
- For self-referential `object` types, use `Box(Self)` to break the recursive cycle:
|
|
148
151
|
|
|
149
152
|
```rust
|
|
@@ -18,6 +18,7 @@ These commands and patterns are aimed at normal Yo projects that use the public
|
|
|
18
18
|
| Inspect generated C | `yo compile main.yo --emit-c --skip-c-compiler` |
|
|
19
19
|
| Run tests in one file | `yo test ./tests/main.test.yo --parallel 1` |
|
|
20
20
|
| Filter tests by name | `yo test ./tests/main.test.yo --test-name-pattern "Name"` |
|
|
21
|
+
| Tune test batch size | `yo test ./tests/main.test.yo --test-batch-size 100` |
|
|
21
22
|
| Format Yo source | `yo fmt ./src ./tests` |
|
|
22
23
|
| Check Yo formatting | `yo fmt --check` |
|
|
23
24
|
| Generate docs for project | `yo doc ./src` |
|
|
@@ -104,6 +105,7 @@ yo test ./tests/main.test.yo --bail --verbose --parallel 1
|
|
|
104
105
|
|
|
105
106
|
- Use `--parallel 1` for focused, readable single-file runs
|
|
106
107
|
- Use `--test-name-pattern` when a file contains many tests
|
|
108
|
+
- Use `--test-batch-size N` if a large `.test.yo` file generates C that compiles slowly or looks stuck
|
|
107
109
|
- Use `yo build test` when the repository's main test workflow is defined in `build.yo`
|
|
108
110
|
|
|
109
111
|
## Formatting
|
|
@@ -79,6 +79,16 @@ if(done, println("done"), println("pending"));
|
|
|
79
79
|
- `if(a, b)` and `if(a, b, c)` are macro forms over `cond`
|
|
80
80
|
- Write `return(value)` or `return()`; `return value` is invalid.
|
|
81
81
|
- Write `escape(value)` or `escape()`; `escape value` is invalid.
|
|
82
|
+
- If a `match`/`cond` branch returns an enum variant and inference fails, qualify
|
|
83
|
+
the variant with its enum type: `TypeValue.Unit` instead of `.Unit`.
|
|
84
|
+
- Do not match enum payload literals directly, e.g. avoid `.Some(false)` and
|
|
85
|
+
`.Some(true)` as sibling branches. Match `.Some(value)` once, then branch with
|
|
86
|
+
`if(value, ...)` or `cond(...)` inside the arm; otherwise generated C can
|
|
87
|
+
contain duplicate enum `case` labels.
|
|
88
|
+
- In large enum matches, avoid binding a pattern variable with the same name as a
|
|
89
|
+
variant field (for example, prefer `struct_field_types` over `field_types`).
|
|
90
|
+
This can currently produce invalid generated C in some self-hosted codegen
|
|
91
|
+
paths.
|
|
82
92
|
|
|
83
93
|
## String types
|
|
84
94
|
|
|
@@ -140,6 +150,8 @@ impl(Counter,
|
|
|
140
150
|
```
|
|
141
151
|
|
|
142
152
|
- No space between a function type and its body: `(fn(...) -> T)(...)`
|
|
153
|
+
- Top-level aliases for function types need parentheses too:
|
|
154
|
+
`Callback :: (fn(x : i32) -> i32);`, not `Callback :: fn(x : i32) -> i32;`
|
|
143
155
|
- Use `Self` in method signatures and in type definitions for recursive references (the type name is not available during its own definition)
|
|
144
156
|
- `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.
|
|
145
157
|
- Use `struct(...)` for record and effect-record types. The legacy `module(...)`,
|
|
@@ -250,15 +262,28 @@ text := match(value,
|
|
|
250
262
|
Three destructuring shapes for arms (mix freely across arms):
|
|
251
263
|
|
|
252
264
|
```rust
|
|
253
|
-
Shape :: enum(
|
|
265
|
+
Shape :: enum(
|
|
266
|
+
Circle(radius : i32),
|
|
267
|
+
Rectangle(width : i32, height : i32),
|
|
268
|
+
Triangle(base : i32, height : i32, label : str)
|
|
269
|
+
);
|
|
254
270
|
|
|
255
271
|
match(s,
|
|
256
|
-
|
|
257
|
-
.
|
|
258
|
-
|
|
272
|
+
// ✅ Preferred — curly shorthand names only the fields you use.
|
|
273
|
+
.Triangle({base, height: h}) => (base * h),
|
|
274
|
+
|
|
275
|
+
// Also OK — labeled (label : var) pairs; order-free, partial matches OK.
|
|
276
|
+
.Circle(radius: r) => (r * r),
|
|
277
|
+
|
|
278
|
+
// ⚠️ Avoid for 2+ field variants — positional with `_` is brittle when
|
|
279
|
+
// a field is added and harder to read (each `_` requires counting).
|
|
280
|
+
// OK when the variant has one field, or when every field is named.
|
|
281
|
+
.Rectangle(w, h) => (w * h)
|
|
259
282
|
)
|
|
260
283
|
```
|
|
261
284
|
|
|
285
|
+
**Preferred form**: `.Variant({label, label: alias})`. Names only the fields the arm binds, so adding a field to the variant later doesn't silently break every arm. `tests/match_curly.test.yo` is the spec.
|
|
286
|
+
|
|
262
287
|
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.
|
|
263
288
|
|
|
264
289
|
> **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.
|
|
@@ -553,6 +578,23 @@ Variable :: object(name : String, type : TypeValue);
|
|
|
553
578
|
Variable :: object(name : String, ty : TypeValue);
|
|
554
579
|
```
|
|
555
580
|
|
|
581
|
+
### 1-element array literals require a trailing comma
|
|
582
|
+
|
|
583
|
+
`[expr]` without a trailing comma is **parsed as a Slice type** `Slice(expr)`, not an array literal. To create a 1-element array value, add a trailing comma:
|
|
584
|
+
|
|
585
|
+
```rust
|
|
586
|
+
// WRONG — parsed as Slice type, not array literal:
|
|
587
|
+
arr := [i32(42)];
|
|
588
|
+
|
|
589
|
+
// CORRECT — trailing comma makes it an array literal:
|
|
590
|
+
arr := [i32(42),];
|
|
591
|
+
|
|
592
|
+
// Multi-element arrays work fine (comma separator detected):
|
|
593
|
+
arr2 := [i32(1), i32(2), i32(3)]; // ✓
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
This also applies inside source strings in proto-evaluator tests.
|
|
597
|
+
|
|
556
598
|
### ArrayList indexing uses call syntax
|
|
557
599
|
|
|
558
600
|
```rust
|
|
@@ -696,6 +738,13 @@ given(exn) := Exception(throw: ((err) -> {
|
|
|
696
738
|
}));
|
|
697
739
|
do_something(using(exn));
|
|
698
740
|
|
|
741
|
+
// CORRECT — even after process-exit helpers, satisfy the handler's resume type:
|
|
742
|
+
given(exn2) := Exception(throw: ((err) -> {
|
|
743
|
+
eprintln(err.to_string());
|
|
744
|
+
exit(int(1));
|
|
745
|
+
escape(); // required because exit() returns unit, not the handler ResumeType
|
|
746
|
+
}));
|
|
747
|
+
|
|
699
748
|
// CORRECT — escape inside a closure passed as argument:
|
|
700
749
|
result := match(opt, .Some(x) => x, .None => {
|
|
701
750
|
// This is NOT a nested function — use return:
|
|
@@ -1036,3 +1085,102 @@ match(outer_val,
|
|
|
1036
1085
|
.None => { fallback() } // ← outer .None arm
|
|
1037
1086
|
) // ← closes outer match
|
|
1038
1087
|
```
|
|
1088
|
+
|
|
1089
|
+
### Nested enum patterns in match are NOT supported
|
|
1090
|
+
|
|
1091
|
+
Yo does **not** support nested enum patterns inside a single match arm.
|
|
1092
|
+
You cannot write `.Some(.IntLit(n))` — this is a parser error.
|
|
1093
|
+
|
|
1094
|
+
```rust
|
|
1095
|
+
// ❌ WRONG — nested enum pattern, parser error:
|
|
1096
|
+
match(v.get(usize(0)),
|
|
1097
|
+
.Some(.IntLit(n)) => assert(n.as_str() == "3", "ok"),
|
|
1098
|
+
_ => assert(false, "err")
|
|
1099
|
+
)
|
|
1100
|
+
|
|
1101
|
+
// ✅ CORRECT — two-level match:
|
|
1102
|
+
match(v.get(usize(0)),
|
|
1103
|
+
.Some(x) => match(x, .IntLit(n) => assert(n.as_str() == "3", "ok"), _ => assert(false, "err")),
|
|
1104
|
+
.None => assert(false, "err")
|
|
1105
|
+
)
|
|
1106
|
+
```
|
|
1107
|
+
|
|
1108
|
+
This applies to ALL nested enum patterns: `.Some(.BoolVal(b))`, `.Some(.ArrayVal(arr))`, etc. — always use a two-level match.
|
|
1109
|
+
|
|
1110
|
+
### `get_callee()` returns ExprVal directly, not an Option-wrapped EnumVal
|
|
1111
|
+
|
|
1112
|
+
In the proto-evaluator source strings (`evaluate_module_body`), `ExprVal.get_callee()` on a FnCall returns the callee `ExprVal` directly — NOT wrapped in an `Option` EnumVal. Chaining `.is_some()` fails with SIGABRT because `is_some()` requires an `EnumVal` receiver.
|
|
1113
|
+
|
|
1114
|
+
```rust
|
|
1115
|
+
// ❌ SIGABRT — get_callee() returns ExprVal, not Option(EnumVal)
|
|
1116
|
+
result := quote(foo(i64(1))).get_callee().is_some();
|
|
1117
|
+
|
|
1118
|
+
// ✅ Chain .is_atom() or .is_fn_call() on the returned ExprVal
|
|
1119
|
+
result := quote(foo(i64(1))).get_callee().is_atom(); // true: callee "foo" is an atom
|
|
1120
|
+
result := quote(foo(i64(1))).get_callee().is_fn_call(); // false: callee "foo" is not a fn call
|
|
1121
|
+
```
|
|
1122
|
+
|
|
1123
|
+
Similarly, calling `get_callee()` on an Atom causes the overall evaluation to fail — do not test the Atom case via `get_callee()` in source strings.
|
|
1124
|
+
|
|
1125
|
+
### Source-string evaluation pitfalls (proto-evaluator tests)
|
|
1126
|
+
|
|
1127
|
+
When writing source strings passed to `evaluate_module_body` in proto-evaluator tests:
|
|
1128
|
+
|
|
1129
|
+
**`cond` form**: Always use the `cond(condition => value, true => fallback)` form, NOT `cond(condition, value, fallback)`. The 3-arg form does NOT work inside lambdas or recursive functions in source strings.
|
|
1130
|
+
|
|
1131
|
+
```
|
|
1132
|
+
// ❌ WRONG — crashes inside lambdas and recursive functions
|
|
1133
|
+
cond((n <= i32(1)), i32(1), (n * recur((n - i32(1)))))
|
|
1134
|
+
|
|
1135
|
+
// ✅ CORRECT
|
|
1136
|
+
cond((n <= i32(1)) => i32(1), true => (n * recur((n - i32(1)))))
|
|
1137
|
+
```
|
|
1138
|
+
|
|
1139
|
+
**Recursive functions**: Use `recur(...)` for self-recursion inside named `::` functions. Never call the function by name from inside its own body.
|
|
1140
|
+
|
|
1141
|
+
**Chaining function calls with operators**: `f(a) + f(b) + f(c)` throws an exception. Use fold over an array instead:
|
|
1142
|
+
|
|
1143
|
+
```
|
|
1144
|
+
// ❌ WRONG — exception in source strings
|
|
1145
|
+
result := abs_val(i32(3)) + abs_val(i32(1)) + abs_val(i32(4));
|
|
1146
|
+
|
|
1147
|
+
// ✅ CORRECT
|
|
1148
|
+
arr := [i32(3), i32(1), i32(4)];
|
|
1149
|
+
result := arr.fold(i32(0), (fn(acc : i32, x : i32) -> i32)((acc + abs_val(x))));
|
|
1150
|
+
```
|
|
1151
|
+
|
|
1152
|
+
**Empty array `[]` in cond branches**: `cond(condition => [x], true => [])` crashes because the empty array type is unknown. Avoid empty array literals in conditional branches inside `flat_map` lambdas.
|
|
1153
|
+
|
|
1154
|
+
**Option types**: Must use `Option(T).Some(val)` not `Option.Some(val)`. `Option(T).None` with a type annotation crashes — use `r := Option(i32).None` without annotation. `.is_none()` is not supported; use `!(r.is_some())`. `and_then(f)` returns the raw value (not wrapped in Option), so calling `.unwrap_or()` on the result crashes.
|
|
1155
|
+
|
|
1156
|
+
**Number literals**: `i32(-3)` crashes — use `(i32(0) - i32(3))`. `i32.as_usize()` / `usize.as_i32()` not supported.
|
|
1157
|
+
|
|
1158
|
+
**Fibonacci without tmp variable**: `b = (a + b); a = (b - a)` computes fib correctly without a temp variable. After N iterations, `a` holds fib(N) and `b` holds fib(N+1).
|
|
1159
|
+
|
|
1160
|
+
**3-term multiplication in source strings**: `(x * x * x)` causes an exception in evaluated source strings. Break it into a block:
|
|
1161
|
+
|
|
1162
|
+
```
|
|
1163
|
+
// ❌ WRONG — causes exception
|
|
1164
|
+
cubes := arr.map((fn(x : i32) -> i32)((x * x * x)));
|
|
1165
|
+
|
|
1166
|
+
// ✅ CORRECT — use a block with a local binding
|
|
1167
|
+
cubes := arr.map((fn(x : i32) -> i32)({
|
|
1168
|
+
sq := (x * x);
|
|
1169
|
+
(sq * x)
|
|
1170
|
+
}));
|
|
1171
|
+
```
|
|
1172
|
+
|
|
1173
|
+
**3-term sum in fold on tuples**: `(acc + p.0 + p.1)` inside a fold lambda on tuple pairs crashes. Always map pairs to scalars first, then fold:
|
|
1174
|
+
|
|
1175
|
+
```
|
|
1176
|
+
// ❌ WRONG — crashes in fold on (i32, i32) tuples
|
|
1177
|
+
total := pairs.fold(i32(0), (fn(acc : i32, p : (i32, i32)) -> i32)((acc + p.0 + p.1)));
|
|
1178
|
+
|
|
1179
|
+
// ✅ CORRECT — map to scalars first, then fold
|
|
1180
|
+
sums := pairs.map((fn(p : (i32, i32)) -> i32)((p.0 + p.1)));
|
|
1181
|
+
total := sums.fold(i32(0), (fn(acc : i32, x : i32) -> i32)((acc + x)));
|
|
1182
|
+
```
|
|
1183
|
+
|
|
1184
|
+
**`&&` in `cond` conditions inside `while` body**: Crashes. Avoid by restructuring (e.g., start loop at 1 instead of 0 to eliminate the `&& (i > 0)` guard).
|
|
1185
|
+
|
|
1186
|
+
**Test API format**: Use `evaluate_module_body(exprs, &(env))` (reference syntax, returns `Option`). Match with function-style `match(result, .None => ..., .Some(m) => ...)`. Do NOT use block-style `match(result) { ... }` — it causes a parse error ("Paren-less function and operator calls are not supported").
|