@shd101wyy/yo 0.1.26 → 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-async-effects/SKILL.md +4 -4
- package/.github/skills/yo-async-effects/async-effects-recipes.md +34 -34
- package/.github/skills/yo-core-patterns/SKILL.md +1 -1
- package/.github/skills/yo-core-patterns/core-patterns-cheatsheet.md +29 -26
- package/.github/skills/yo-project-workflow/SKILL.md +6 -3
- package/.github/skills/yo-project-workflow/workflow-cheatsheet.md +36 -11
- package/.github/skills/yo-syntax/SKILL.md +7 -6
- package/.github/skills/yo-syntax/syntax-cheatsheet.md +225 -64
- package/.github/skills/yo-wasm-integration/wasm-integration-cheatsheet.md +3 -3
- package/README.md +10 -8
- package/out/cjs/index.cjs +553 -535
- package/out/cjs/yo-cli.cjs +685 -651
- package/out/cjs/yo-lsp.cjs +596 -569
- package/out/esm/index.mjs +396 -378
- 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/formatter.d.ts +11 -0
- package/out/types/src/lsp/formatting.d.ts +2 -0
- package/out/types/src/test-runner.d.ts +2 -0
- package/out/types/src/tests/formatter.test.d.ts +1 -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
- package/std/alg/hash.yo +13 -21
- package/std/allocator.yo +25 -40
- package/std/async.yo +3 -7
- package/std/build.yo +105 -151
- package/std/cli/arg_parser.yo +184 -169
- package/std/collections/array_list.yo +350 -314
- package/std/collections/btree_map.yo +142 -131
- package/std/collections/deque.yo +132 -128
- package/std/collections/hash_map.yo +542 -566
- package/std/collections/hash_set.yo +623 -687
- package/std/collections/linked_list.yo +275 -293
- package/std/collections/ordered_map.yo +113 -85
- package/std/collections/priority_queue.yo +73 -73
- package/std/crypto/md5.yo +191 -95
- package/std/crypto/random.yo +56 -64
- package/std/crypto/sha256.yo +151 -107
- package/std/encoding/base64.yo +87 -81
- package/std/encoding/hex.yo +43 -50
- package/std/encoding/html.yo +56 -81
- package/std/encoding/html_char_utils.yo +7 -13
- package/std/encoding/html_entities.yo +2248 -2253
- package/std/encoding/json.yo +316 -224
- package/std/encoding/punycode.yo +86 -116
- package/std/encoding/toml.yo +67 -66
- package/std/encoding/utf16.yo +37 -44
- package/std/env.yo +62 -91
- package/std/error.yo +7 -15
- package/std/fmt/display.yo +5 -9
- package/std/fmt/index.yo +8 -14
- package/std/fmt/to_string.yo +330 -315
- package/std/fmt/writer.yo +58 -87
- package/std/fs/dir.yo +83 -102
- package/std/fs/file.yo +147 -180
- package/std/fs/metadata.yo +45 -78
- package/std/fs/temp.yo +55 -65
- package/std/fs/types.yo +27 -40
- package/std/fs/walker.yo +53 -68
- package/std/gc.yo +5 -8
- package/std/glob.yo +30 -43
- package/std/http/client.yo +107 -120
- package/std/http/http.yo +106 -96
- package/std/http/index.yo +4 -6
- package/std/imm/list.yo +88 -93
- package/std/imm/map.yo +528 -464
- package/std/imm/set.yo +52 -57
- package/std/imm/sorted_map.yo +340 -286
- package/std/imm/sorted_set.yo +57 -63
- package/std/imm/string.yo +404 -345
- package/std/imm/vec.yo +173 -181
- package/std/io/reader.yo +3 -6
- package/std/io/writer.yo +4 -8
- package/std/libc/assert.yo +5 -9
- package/std/libc/ctype.yo +32 -22
- package/std/libc/dirent.yo +26 -25
- package/std/libc/errno.yo +164 -90
- package/std/libc/fcntl.yo +52 -45
- package/std/libc/float.yo +66 -44
- package/std/libc/limits.yo +42 -33
- package/std/libc/math.yo +53 -82
- package/std/libc/signal.yo +72 -47
- package/std/libc/stdatomic.yo +217 -188
- package/std/libc/stdint.yo +5 -29
- package/std/libc/stdio.yo +5 -29
- package/std/libc/stdlib.yo +32 -39
- package/std/libc/string.yo +5 -23
- package/std/libc/sys/stat.yo +58 -56
- package/std/libc/time.yo +5 -19
- package/std/libc/unistd.yo +5 -20
- package/std/libc/wctype.yo +6 -9
- package/std/libc/windows.yo +26 -30
- package/std/log.yo +41 -55
- package/std/net/addr.yo +102 -97
- package/std/net/dns.yo +27 -28
- package/std/net/errors.yo +50 -49
- package/std/net/tcp.yo +113 -124
- package/std/net/udp.yo +55 -66
- package/std/os/env.yo +35 -33
- package/std/os/signal.yo +15 -25
- package/std/path.yo +276 -311
- package/std/prelude.yo +6304 -4315
- package/std/process/command.yo +87 -103
- package/std/process/index.yo +12 -31
- package/std/regex/compiler.yo +196 -95
- package/std/regex/flags.yo +58 -39
- package/std/regex/index.yo +157 -173
- package/std/regex/match.yo +20 -31
- package/std/regex/node.yo +134 -152
- package/std/regex/parser.yo +283 -259
- package/std/regex/unicode.yo +172 -202
- package/std/regex/vm.yo +155 -171
- package/std/string/index.yo +5 -7
- package/std/string/rune.yo +45 -55
- package/std/string/string.yo +937 -964
- package/std/string/string_builder.yo +94 -104
- package/std/string/unicode.yo +46 -64
- package/std/sync/channel.yo +72 -73
- package/std/sync/cond.yo +31 -36
- package/std/sync/mutex.yo +30 -32
- package/std/sync/once.yo +13 -16
- package/std/sync/rwlock.yo +26 -31
- package/std/sync/waitgroup.yo +20 -25
- package/std/sys/advise.yo +16 -24
- package/std/sys/bufio/buf_reader.yo +77 -93
- package/std/sys/bufio/buf_writer.yo +52 -65
- package/std/sys/clock.yo +4 -9
- package/std/sys/constants.yo +77 -61
- package/std/sys/copy.yo +4 -10
- package/std/sys/dir.yo +26 -43
- package/std/sys/dns.yo +41 -61
- package/std/sys/errors.yo +95 -103
- package/std/sys/events.yo +45 -57
- package/std/sys/externs.yo +319 -267
- package/std/sys/fallocate.yo +7 -11
- package/std/sys/fcntl.yo +14 -22
- package/std/sys/file.yo +26 -40
- package/std/sys/future.yo +5 -8
- package/std/sys/iov.yo +12 -25
- package/std/sys/lock.yo +12 -13
- package/std/sys/mmap.yo +38 -43
- package/std/sys/path.yo +3 -8
- package/std/sys/perm.yo +7 -21
- package/std/sys/pipe.yo +5 -12
- package/std/sys/process.yo +23 -29
- package/std/sys/seek.yo +10 -12
- package/std/sys/signal.yo +7 -13
- package/std/sys/signals.yo +52 -35
- package/std/sys/socket.yo +63 -58
- package/std/sys/socketpair.yo +3 -6
- package/std/sys/sockinfo.yo +11 -20
- package/std/sys/statfs.yo +11 -34
- package/std/sys/statx.yo +25 -52
- package/std/sys/sysinfo.yo +15 -20
- package/std/sys/tcp.yo +62 -92
- package/std/sys/temp.yo +5 -9
- package/std/sys/time.yo +5 -15
- package/std/sys/timer.yo +6 -11
- package/std/sys/tty.yo +10 -18
- package/std/sys/udp.yo +22 -39
- package/std/sys/umask.yo +3 -6
- package/std/sys/unix.yo +33 -52
- package/std/testing/bench.yo +49 -52
- package/std/thread.yo +10 -15
- package/std/time/datetime.yo +105 -89
- package/std/time/duration.yo +43 -56
- package/std/time/instant.yo +13 -18
- package/std/time/sleep.yo +5 -9
- package/std/url/index.yo +184 -209
- package/std/worker.yo +6 -10
|
@@ -13,7 +13,7 @@ These are baseline syntax rules for portable Yo code.
|
|
|
13
13
|
## Common declaration forms
|
|
14
14
|
|
|
15
15
|
```rust
|
|
16
|
-
{ println } :: import
|
|
16
|
+
{ println } :: import("std/fmt");
|
|
17
17
|
|
|
18
18
|
app_name :: "yo-demo";
|
|
19
19
|
|
|
@@ -23,21 +23,22 @@ main :: (fn() -> unit)({
|
|
|
23
23
|
println(message);
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
-
export
|
|
26
|
+
export(main);
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
- Top-level binding: `name :: expr;`
|
|
30
30
|
- Local binding: `name := expr;`
|
|
31
31
|
- Typed binding: `(name : Type) = expr;`
|
|
32
32
|
- Function definition: `name :: (fn(args...) -> ReturnType)(body);`
|
|
33
|
+
- Export: `export(name);`
|
|
33
34
|
|
|
34
35
|
## Blocks and expressions
|
|
35
36
|
|
|
36
|
-
| Goal | Write
|
|
37
|
-
| ----------------- |
|
|
38
|
-
| Single expression | `cond(...)`
|
|
39
|
-
| Begin block | `{ x := i32(1); x }`
|
|
40
|
-
| Struct literal | `{ name: "yo", ok: true }` | `{ name: "yo"; ok: true }` |
|
|
37
|
+
| Goal | Write | Avoid |
|
|
38
|
+
| ----------------- | ---------------------------- | ---------------------------- |
|
|
39
|
+
| Single expression | `cond(...)` | `{ cond(...) }` |
|
|
40
|
+
| Begin block | `{ x := i32(1); x }` | `{ x := i32(1), x }` |
|
|
41
|
+
| Struct literal | `{ name : "yo", ok : true }` | `{ name : "yo"; ok : true }` |
|
|
41
42
|
|
|
42
43
|
```rust
|
|
43
44
|
result := cond(
|
|
@@ -53,6 +54,8 @@ total := {
|
|
|
53
54
|
|
|
54
55
|
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
56
|
|
|
57
|
+
In struct literals, keep spaces around `:` and parenthesize infix field values: `{ x : (1 + 2), y : 3 }`, not `{ x: 1 + 2, y: 3 }`.
|
|
58
|
+
|
|
56
59
|
## Control flow
|
|
57
60
|
|
|
58
61
|
```rust
|
|
@@ -74,6 +77,18 @@ if(done, println("done"), println("pending"));
|
|
|
74
77
|
- Always write `cond(...)`, never bare `cond ...`
|
|
75
78
|
- Always write `match(...)`, never bare `match ...`
|
|
76
79
|
- `if(a, b)` and `if(a, b, c)` are macro forms over `cond`
|
|
80
|
+
- Write `return(value)` or `return()`; `return value` is invalid.
|
|
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.
|
|
77
92
|
|
|
78
93
|
## String types
|
|
79
94
|
|
|
@@ -101,10 +116,18 @@ flag := ((a > b) && (b > c));
|
|
|
101
116
|
masked := ((A | B) | C);
|
|
102
117
|
```
|
|
103
118
|
|
|
104
|
-
-
|
|
105
|
-
- `func
|
|
119
|
+
- Calls require immediate parentheses: `func(arg1, arg2)`
|
|
120
|
+
- `func arg1, arg2` and `func (arg1, arg2)` are invalid
|
|
106
121
|
- Yo has no operator precedence; fully parenthesize binary expressions
|
|
107
|
-
-
|
|
122
|
+
- Preserve grouping around infix expressions on operator RHS positions: `true => (x / y)`, `value := (x + y)`, `(ptr &+ 1).*`
|
|
123
|
+
- Line breaks can disambiguate operator chains; keep line-leading operators like `(4\n| 5\n| 6)` and newlines after `:` before a lambda unless you add equivalent grouping
|
|
124
|
+
- When an operator ends a line, indent its RHS one level as a continuation: `(given(x) : T) =\n (v) -> { ... }`
|
|
125
|
+
- Prefix operators (`!`, `&`, `-`, `~`) require parenthesized operands: `func(&(s), a, b)`, `!(ready)`, `-(value)`.
|
|
126
|
+
- Tight special forms also require immediate parentheses: `#(expr)`, `?*(u8)`, `T <: !(Runtime)`
|
|
127
|
+
- Dynamic field access with unquote must keep grouping after the dot: `value.(#(field_expr))`, not `value.#(field_expr)`.
|
|
128
|
+
- Unquote splicing is the tight operator `...#(exprs)`; do not insert a space between `...` and `#`.
|
|
129
|
+
- Canonical pointer dereference is `ptr.*`; formatter should canonicalize legacy `ptr.(*)` to `ptr.*`.
|
|
130
|
+
- Keep single-line array and tuple literals compact during formatting: `[1, 2, 3]`, `(1, 2, 3)`.
|
|
108
131
|
- Parenthesize other unary operands too: `!(ready)`, `-(value)`
|
|
109
132
|
- **`!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
133
|
- **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), ...)`.
|
|
@@ -127,6 +150,8 @@ impl(Counter,
|
|
|
127
150
|
```
|
|
128
151
|
|
|
129
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;`
|
|
130
155
|
- Use `Self` in method signatures and in type definitions for recursive references (the type name is not available during its own definition)
|
|
131
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.
|
|
132
157
|
- Use `struct(...)` for record and effect-record types. The legacy `module(...)`,
|
|
@@ -169,7 +194,7 @@ safe_divide :: (fn(x : i32, y : i32, using(raise : Raise)) -> i32)(
|
|
|
169
194
|
|
|
170
195
|
caller :: (fn() -> i32)({
|
|
171
196
|
(given(raise) : Raise) = (fn(msg : String) -> i32)({
|
|
172
|
-
return
|
|
197
|
+
return(i32(0));
|
|
173
198
|
});
|
|
174
199
|
|
|
175
200
|
safe_divide(i32(10), i32(0))
|
|
@@ -189,9 +214,9 @@ caller :: (fn() -> i32)({
|
|
|
189
214
|
result := closure(i32(5));
|
|
190
215
|
|
|
191
216
|
transform :: (fn(list : ArrayList(i32), f : Impl(Fn(x : i32) -> i32)) -> unit)({
|
|
192
|
-
for
|
|
217
|
+
for(list.iter(), (ptr) => {
|
|
193
218
|
ptr.* = f(ptr.*);
|
|
194
|
-
};
|
|
219
|
+
});
|
|
195
220
|
});
|
|
196
221
|
```
|
|
197
222
|
|
|
@@ -203,15 +228,15 @@ transform :: (fn(list : ArrayList(i32), f : Impl(Fn(x : i32) -> i32)) -> unit)({
|
|
|
203
228
|
## Imports and modules
|
|
204
229
|
|
|
205
230
|
```rust
|
|
206
|
-
{ Parser } :: import
|
|
207
|
-
parser_module :: import
|
|
231
|
+
{ Parser } :: import("./parser.yo");
|
|
232
|
+
parser_module :: import("./parser.yo");
|
|
208
233
|
|
|
209
|
-
open
|
|
210
|
-
{ ArrayList } :: import
|
|
234
|
+
open(import("std/string"));
|
|
235
|
+
{ ArrayList } :: import("std/collections/array_list");
|
|
211
236
|
```
|
|
212
237
|
|
|
213
238
|
- Use relative imports for nearby `.yo` files
|
|
214
|
-
- Use `open
|
|
239
|
+
- Use `open(import("std/module"))` for standard-library modules you want fully in scope
|
|
215
240
|
- Do not write `import "./file.yo" as name`
|
|
216
241
|
- Do not import `std/prelude`
|
|
217
242
|
|
|
@@ -237,15 +262,28 @@ text := match(value,
|
|
|
237
262
|
Three destructuring shapes for arms (mix freely across arms):
|
|
238
263
|
|
|
239
264
|
```rust
|
|
240
|
-
Shape :: enum(
|
|
265
|
+
Shape :: enum(
|
|
266
|
+
Circle(radius : i32),
|
|
267
|
+
Rectangle(width : i32, height : i32),
|
|
268
|
+
Triangle(base : i32, height : i32, label : str)
|
|
269
|
+
);
|
|
241
270
|
|
|
242
271
|
match(s,
|
|
243
|
-
|
|
244
|
-
.
|
|
245
|
-
|
|
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)
|
|
246
282
|
)
|
|
247
283
|
```
|
|
248
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
|
+
|
|
249
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.
|
|
250
288
|
|
|
251
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.
|
|
@@ -273,17 +311,17 @@ show :: (fn(forall(T : Type), value : T, where(T <: ToString)) -> unit)(
|
|
|
273
311
|
|
|
274
312
|
```rust
|
|
275
313
|
main :: (fn() -> unit)(());
|
|
276
|
-
export
|
|
314
|
+
export(main);
|
|
277
315
|
|
|
278
|
-
export
|
|
316
|
+
export(
|
|
279
317
|
helper,
|
|
280
318
|
Config
|
|
281
|
-
;
|
|
319
|
+
);
|
|
282
320
|
```
|
|
283
321
|
|
|
284
|
-
- `export
|
|
322
|
+
- `export(name);` exports a single binding
|
|
285
323
|
- Block form exports multiple bindings separated by commas
|
|
286
|
-
- Every executable needs `export
|
|
324
|
+
- Every executable needs `export(main);`
|
|
287
325
|
|
|
288
326
|
## Static and dynamic dispatch types
|
|
289
327
|
|
|
@@ -321,15 +359,15 @@ factorial :: (fn(n : i32) -> i32)(
|
|
|
321
359
|
)
|
|
322
360
|
);
|
|
323
361
|
|
|
324
|
-
// Runtime infinite loop — `while
|
|
325
|
-
while
|
|
362
|
+
// Runtime infinite loop — `while(cond, body)` is ALWAYS runtime
|
|
363
|
+
while(true, {
|
|
326
364
|
work();
|
|
327
|
-
};
|
|
365
|
+
});
|
|
328
366
|
|
|
329
367
|
// Compile-time loop unrolling — requires comptime() modifier
|
|
330
|
-
while
|
|
368
|
+
while(comptime((i < 10)), {
|
|
331
369
|
// body evaluated/unrolled at compile time
|
|
332
|
-
};
|
|
370
|
+
});
|
|
333
371
|
|
|
334
372
|
// for loop — 2-arg prelude macro; first arg MUST be an iterator (has .next()):
|
|
335
373
|
for(list.iter(), x => { // ArrayList, array → call .iter() first
|
|
@@ -346,8 +384,8 @@ for(list.iter(), (ptr) => { // ptr is a mutable reference to each element
|
|
|
346
384
|
```
|
|
347
385
|
|
|
348
386
|
- Use `recur(...)` for self-recursion
|
|
349
|
-
- `while
|
|
350
|
-
- `while
|
|
387
|
+
- `while(cond, body)` is **always a runtime loop** — use this for open-ended loops (e.g., server accept loops, event loops)
|
|
388
|
+
- `while(comptime(cond), body)` explicitly unrolls at compile time — `cond` must be a compile-time-known value
|
|
351
389
|
- Using a comptime-only (`::`) variable in a bare `while` condition without `comptime()` is a **compile error** (would be an infinite loop at runtime)
|
|
352
390
|
- **`for(arr, item => { body })`** — correct 2-arg prelude macro form. The `item => { body }` is an anonymous closure.
|
|
353
391
|
- **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`)
|
|
@@ -355,19 +393,19 @@ for(list.iter(), (ptr) => { // ptr is a mutable reference to each element
|
|
|
355
393
|
## Return and branch safety
|
|
356
394
|
|
|
357
395
|
```rust
|
|
358
|
-
// WRONG — return
|
|
396
|
+
// WRONG — paren-less return is invalid:
|
|
359
397
|
match(opt,
|
|
360
|
-
.Some(v) => return v,
|
|
398
|
+
.Some(v) => return v,
|
|
361
399
|
.None => default_value()
|
|
362
400
|
);
|
|
363
401
|
|
|
364
|
-
// CORRECT —
|
|
402
|
+
// CORRECT — explicit return calls in begin blocks:
|
|
365
403
|
match(opt,
|
|
366
404
|
.Some(v) => {
|
|
367
|
-
return
|
|
405
|
+
return(v);
|
|
368
406
|
},
|
|
369
407
|
.None => {
|
|
370
|
-
return
|
|
408
|
+
return(default_value());
|
|
371
409
|
}
|
|
372
410
|
);
|
|
373
411
|
|
|
@@ -380,11 +418,11 @@ get_value :: (fn(opt : Option(i32)) -> i32)(
|
|
|
380
418
|
);
|
|
381
419
|
```
|
|
382
420
|
|
|
383
|
-
- `return
|
|
421
|
+
- `return expr` is invalid; write `return(expr)` or `return()` for unit
|
|
384
422
|
- In `cond` or `match` branches, **always use begin blocks** when you need `return`
|
|
385
|
-
- `return` must be the **last expression** in a begin block — dead code after `return` is rejected. Do NOT write `{ return
|
|
423
|
+
- `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.
|
|
386
424
|
- If the whole function is one expression, prefer expression-bodied style and skip `return` entirely
|
|
387
|
-
- The same
|
|
425
|
+
- The same rule applies to all calls in match branches: use immediate `(...)`
|
|
388
426
|
|
|
389
427
|
## String concatenation pitfall
|
|
390
428
|
|
|
@@ -406,43 +444,43 @@ content := String.from("line1\nline2\n");
|
|
|
406
444
|
## Iterator and for loop
|
|
407
445
|
|
|
408
446
|
```rust
|
|
409
|
-
{ ArrayList } :: import
|
|
447
|
+
{ ArrayList } :: import("std/collections/array_list");
|
|
410
448
|
|
|
411
449
|
list := ArrayList(i32).new();
|
|
412
450
|
list.push(i32(10));
|
|
413
451
|
list.push(i32(20));
|
|
414
452
|
|
|
415
|
-
for
|
|
453
|
+
for(list.iter(), (ptr) => {
|
|
416
454
|
println(ptr.*);
|
|
417
|
-
};
|
|
455
|
+
});
|
|
418
456
|
|
|
419
|
-
for
|
|
457
|
+
for(list.into_iter(), (value) => {
|
|
420
458
|
println(value);
|
|
421
|
-
};
|
|
459
|
+
});
|
|
422
460
|
```
|
|
423
461
|
|
|
424
|
-
- `for
|
|
462
|
+
- `for(collection, (variable) => { body })` iterates via the `Iterator` trait
|
|
425
463
|
- `.iter()` borrows the collection and yields pointers
|
|
426
464
|
- `.into_iter()` takes ownership and yields values
|
|
427
465
|
|
|
428
466
|
## Testing
|
|
429
467
|
|
|
430
468
|
```rust
|
|
431
|
-
test
|
|
469
|
+
test("Addition works", {
|
|
432
470
|
assert(((i32(1) + i32(1)) == i32(2)), "1+1 should be 2");
|
|
433
|
-
};
|
|
471
|
+
});
|
|
434
472
|
|
|
435
|
-
test
|
|
473
|
+
test("Compile-time check", {
|
|
436
474
|
comptime_assert((2 + 2) == 4);
|
|
437
475
|
comptime_expect_error({ x :: (1 / 0); });
|
|
438
|
-
};
|
|
476
|
+
});
|
|
439
477
|
|
|
440
|
-
test
|
|
478
|
+
test("Async test", {
|
|
441
479
|
io.await(yield());
|
|
442
|
-
};
|
|
480
|
+
});
|
|
443
481
|
```
|
|
444
482
|
|
|
445
|
-
- `test
|
|
483
|
+
- `test("description", { body })` defines a test — `io : IO` is automatically available
|
|
446
484
|
- All tests can use `io.async(...)`, `io.await(...)`, etc. without a `using` clause
|
|
447
485
|
- `assert(condition, "message")` — runtime assertion (always include a message)
|
|
448
486
|
- `comptime_assert(condition)` — compile-time assertion
|
|
@@ -540,6 +578,23 @@ Variable :: object(name : String, type : TypeValue);
|
|
|
540
578
|
Variable :: object(name : String, ty : TypeValue);
|
|
541
579
|
```
|
|
542
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
|
+
|
|
543
598
|
### ArrayList indexing uses call syntax
|
|
544
599
|
|
|
545
600
|
```rust
|
|
@@ -671,7 +726,7 @@ if((!cond), { do_thing(); });
|
|
|
671
726
|
|
|
672
727
|
### `escape` requires a nested-function context
|
|
673
728
|
|
|
674
|
-
`escape
|
|
729
|
+
`escape(value)` exits the **enclosing function** — the nearest `fn(...)` that
|
|
675
730
|
wraps the current code. It requires that the code is inside a nested function
|
|
676
731
|
(e.g., a closure or `given` handler lambda), NOT at the top level of a
|
|
677
732
|
standalone function definition.
|
|
@@ -679,20 +734,27 @@ standalone function definition.
|
|
|
679
734
|
```rust
|
|
680
735
|
// CORRECT — escape inside a given handler lambda (lambda has enclosing fn):
|
|
681
736
|
given(exn) := Exception(throw: ((err) -> {
|
|
682
|
-
escape
|
|
737
|
+
escape(result); // exits the outer function that contains this given()
|
|
683
738
|
}));
|
|
684
739
|
do_something(using(exn));
|
|
685
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
|
+
|
|
686
748
|
// CORRECT — escape inside a closure passed as argument:
|
|
687
749
|
result := match(opt, .Some(x) => x, .None => {
|
|
688
750
|
// This is NOT a nested function — use return:
|
|
689
|
-
// WRONG: escape
|
|
690
|
-
return
|
|
751
|
+
// WRONG: escape(default_val) // ERROR: no enclosing fn
|
|
752
|
+
return(default_val); // CORRECT: return exits the enclosing fn directly
|
|
691
753
|
});
|
|
692
754
|
|
|
693
755
|
// WRONG — escape inside a match arm (not a nested fn):
|
|
694
756
|
match(opt,
|
|
695
|
-
.Some(x) => { escape
|
|
757
|
+
.Some(x) => { escape(x); } // ERROR: "can only be used inside a function that has an enclosing function"
|
|
696
758
|
);
|
|
697
759
|
```
|
|
698
760
|
|
|
@@ -923,16 +985,16 @@ if (((is_tuple_type(ty) || is_struct_type(ty)) || is_union_type(ty)), ...)
|
|
|
923
985
|
|
|
924
986
|
### Duplicate imports from the same path must be merged
|
|
925
987
|
|
|
926
|
-
Having two `:: import
|
|
988
|
+
Having two `:: import("path")` lines importing from the same file causes a compile error.
|
|
927
989
|
Always merge them into a single destructuring import:
|
|
928
990
|
|
|
929
991
|
```rust
|
|
930
992
|
// ❌ Two imports from the same path
|
|
931
|
-
{ Foo } :: import
|
|
932
|
-
{ Bar } :: import
|
|
993
|
+
{ Foo } :: import("../../mod.yo");
|
|
994
|
+
{ Bar } :: import("../../mod.yo");
|
|
933
995
|
|
|
934
996
|
// ✅ Merged
|
|
935
|
-
{ Foo, Bar } :: import
|
|
997
|
+
{ Foo, Bar } :: import("../../mod.yo");
|
|
936
998
|
```
|
|
937
999
|
|
|
938
1000
|
### Implicit (`using`) parameters cannot be used with `:=` assignment
|
|
@@ -1023,3 +1085,102 @@ match(outer_val,
|
|
|
1023
1085
|
.None => { fallback() } // ← outer .None arm
|
|
1024
1086
|
) // ← closes outer match
|
|
1025
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").
|
|
@@ -16,7 +16,7 @@ Export C-compatible functions that operate on linear memory:
|
|
|
16
16
|
|
|
17
17
|
```rust
|
|
18
18
|
// src/wasm_api.yo
|
|
19
|
-
open
|
|
19
|
+
open(import("std/string"));
|
|
20
20
|
|
|
21
21
|
// Allocate WASM memory for the caller
|
|
22
22
|
wasm_alloc :: (fn(size : usize) -> *(u8))(
|
|
@@ -89,7 +89,7 @@ The `Executable` struct accepts: `name`, `root`, `target`, `optimize`, `allocato
|
|
|
89
89
|
Emscripten-specific flags go in `add_c_flags(...)` after creating the step.
|
|
90
90
|
|
|
91
91
|
```rust
|
|
92
|
-
build :: import
|
|
92
|
+
build :: import("std/build");
|
|
93
93
|
|
|
94
94
|
wasm_api :: build.executable({
|
|
95
95
|
name: "my_lib_wasm_api",
|
|
@@ -242,7 +242,7 @@ cd npm && node -e "const m = require('.'); m.createRenderer().render('hello').th
|
|
|
242
242
|
- **Memory leaks**: Always `_wasm_free` every `_wasm_alloc` on the JavaScript side.
|
|
243
243
|
- **String encoding**: WASM only sees bytes — use `TextEncoder`/`TextDecoder` for UTF-8.
|
|
244
244
|
- **Errno differences**: WASI errno values differ from POSIX. Use `std/libc/errno` constants.
|
|
245
|
-
- **No `main` needed**: Library WASM modules don't need `main` or `export
|
|
245
|
+
- **No `main` needed**: Library WASM modules don't need `main` or `export(main);` — just export the API functions.
|
|
246
246
|
- **Emscripten environment**: Set `-sENVIRONMENT='web,node'` to support both contexts, or `'node'` for Node.js only.
|
|
247
247
|
- **Module initialization is async**: Emscripten's `createModule()` returns a Promise. Initialize once and reuse.
|
|
248
248
|
- **WASM memory growth**: Always use `-sALLOW_MEMORY_GROWTH=1` for dynamic allocations.
|
package/README.md
CHANGED
|
@@ -202,14 +202,14 @@ my-project/
|
|
|
202
202
|
|
|
203
203
|
`src/main.yo`:
|
|
204
204
|
|
|
205
|
-
```
|
|
206
|
-
{ println } :: import
|
|
205
|
+
```rust
|
|
206
|
+
{ println } :: import("std/fmt");
|
|
207
207
|
|
|
208
208
|
main :: (fn() -> unit)({
|
|
209
209
|
println("Hello, world!");
|
|
210
210
|
});
|
|
211
211
|
|
|
212
|
-
export
|
|
212
|
+
export(main);
|
|
213
213
|
```
|
|
214
214
|
|
|
215
215
|
Common build commands:
|
|
@@ -220,6 +220,8 @@ $ yo build run # Build and run the executable
|
|
|
220
220
|
$ yo build test # Run tests
|
|
221
221
|
$ yo build --list-steps # List available build steps
|
|
222
222
|
$ yo build doc # Generate HTML documentation
|
|
223
|
+
$ yo fmt # Format Yo source files
|
|
224
|
+
$ yo fmt --check # Check formatting without writing changes
|
|
223
225
|
```
|
|
224
226
|
|
|
225
227
|
## Prelude
|
|
@@ -254,15 +256,15 @@ Check the [./tests](./tests/) and [./std](./std/) folders for more code examples
|
|
|
254
256
|
|
|
255
257
|
### Hello World
|
|
256
258
|
|
|
257
|
-
```
|
|
259
|
+
```rust
|
|
258
260
|
// main.yo
|
|
259
|
-
{ println } :: import
|
|
261
|
+
{ println } :: import("std/fmt");
|
|
260
262
|
|
|
261
|
-
main :: (fn() -> unit)
|
|
263
|
+
main :: (fn() -> unit)({
|
|
262
264
|
println("Hello, world!");
|
|
263
|
-
};
|
|
265
|
+
});
|
|
264
266
|
|
|
265
|
-
export
|
|
267
|
+
export(main);
|
|
266
268
|
|
|
267
269
|
// $ yo compile main.yo --release -o main
|
|
268
270
|
// $ ./main
|