@shd101wyy/yo 0.1.28 → 0.1.30
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 +15 -15
- package/.github/skills/yo-async-effects/async-effects-recipes.md +118 -121
- package/.github/skills/yo-core-patterns/core-patterns-cheatsheet.md +33 -13
- package/.github/skills/yo-project-workflow/workflow-cheatsheet.md +1 -1
- package/.github/skills/yo-syntax/SKILL.md +2 -2
- package/.github/skills/yo-syntax/syntax-cheatsheet.md +108 -96
- package/README.md +6 -3
- package/out/cjs/index.cjs +812 -706
- package/out/cjs/yo-cli.cjs +1023 -907
- package/out/cjs/yo-lsp.cjs +836 -730
- package/out/esm/index.mjs +757 -651
- package/out/types/src/codegen/exprs/async.d.ts +2 -0
- package/out/types/src/codegen/exprs/await.d.ts +1 -0
- package/out/types/src/codegen/exprs/closures.d.ts +4 -0
- package/out/types/src/codegen/functions/context.d.ts +6 -0
- package/out/types/src/codegen/functions/declarations.d.ts +1 -1
- package/out/types/src/doc/model.d.ts +0 -1
- package/out/types/src/env.d.ts +2 -2
- package/out/types/src/evaluator/builtins/pragma.d.ts +9 -0
- package/out/types/src/evaluator/builtins/unsafe.d.ts +8 -0
- package/out/types/src/evaluator/context.d.ts +3 -1
- package/out/types/src/evaluator/exprs/{escape.d.ts → unwind.d.ts} +1 -1
- package/out/types/src/evaluator/index.d.ts +1 -1
- package/out/types/src/evaluator/memory-safety.d.ts +14 -0
- package/out/types/src/evaluator/types/flowability.d.ts +6 -0
- package/out/types/src/evaluator/types/function.d.ts +1 -2
- package/out/types/src/evaluator/utils.d.ts +0 -1
- package/out/types/src/expr-traversal.d.ts +1 -0
- package/out/types/src/expr.d.ts +9 -7
- package/out/types/src/public-safe-report.d.ts +19 -0
- package/out/types/src/tests/comptime-ref-gate.test.d.ts +1 -0
- package/out/types/src/tests/pragma-validation.test.d.ts +1 -0
- package/out/types/src/tests/public-safe-report.test.d.ts +1 -0
- package/out/types/src/tests/type-representation-pointer.test.d.ts +1 -0
- package/out/types/src/tests/unsafe-gate.test.d.ts +1 -0
- package/out/types/src/tests/unsafe-report-classify.test.d.ts +1 -0
- package/out/types/src/types/creators.d.ts +4 -6
- package/out/types/src/types/definitions.d.ts +9 -16
- package/out/types/src/types/guards.d.ts +1 -2
- package/out/types/src/types/tags.d.ts +0 -1
- package/out/types/src/types/utils.d.ts +5 -0
- package/out/types/src/unsafe-report.d.ts +29 -0
- package/out/types/src/value.d.ts +1 -0
- package/out/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/scripts/add-pragma-for-pointer-decls.ts +134 -0
- package/scripts/add-pragma.ts +58 -0
- package/scripts/migrate-amp-method-calls.ts +186 -0
- package/scripts/migrate-clone-calls.ts +93 -0
- package/scripts/migrate-get-unwrap.ts +166 -0
- package/scripts/migrate-index-patterns.ts +210 -0
- package/scripts/migrate-index-trait.ts +142 -0
- package/scripts/migrate-iterator.ts +150 -0
- package/scripts/migrate-self-ptr.ts +220 -0
- package/scripts/migrate-skip-pragmas.ts +109 -0
- package/scripts/migrate-tostring.ts +134 -0
- package/scripts/trim-pragma.ts +130 -0
- package/scripts/wrap-extern-calls.ts +161 -0
- package/std/alg/hash.yo +3 -2
- package/std/allocator.yo +6 -5
- package/std/async.yo +2 -2
- package/std/collections/array_list.yo +59 -40
- package/std/collections/btree_map.yo +19 -18
- package/std/collections/deque.yo +9 -8
- package/std/collections/hash_map.yo +101 -13
- package/std/collections/hash_set.yo +5 -4
- package/std/collections/linked_list.yo +39 -4
- package/std/collections/ordered_map.yo +3 -3
- package/std/collections/priority_queue.yo +14 -13
- package/std/crypto/md5.yo +2 -1
- package/std/crypto/random.yo +21 -20
- package/std/crypto/sha256.yo +2 -1
- package/std/encoding/base64.yo +18 -18
- package/std/encoding/hex.yo +5 -5
- package/std/encoding/json.yo +62 -13
- package/std/encoding/punycode.yo +24 -23
- package/std/encoding/toml.yo +4 -3
- package/std/encoding/utf16.yo +3 -3
- package/std/env.yo +43 -28
- package/std/error.yo +15 -3
- package/std/fmt/display.yo +2 -2
- package/std/fmt/index.yo +6 -5
- package/std/fmt/to_string.yo +39 -38
- package/std/fmt/writer.yo +9 -8
- package/std/fs/dir.yo +61 -66
- package/std/fs/file.yo +121 -126
- package/std/fs/metadata.yo +13 -18
- package/std/fs/temp.yo +35 -30
- package/std/fs/walker.yo +14 -19
- package/std/gc.yo +1 -0
- package/std/glob.yo +7 -7
- package/std/http/client.yo +33 -36
- package/std/http/http.yo +6 -6
- package/std/http/index.yo +4 -4
- package/std/imm/list.yo +33 -0
- package/std/imm/map.yo +2 -1
- package/std/imm/set.yo +1 -0
- package/std/imm/sorted_map.yo +1 -0
- package/std/imm/sorted_set.yo +1 -0
- package/std/imm/string.yo +27 -23
- package/std/imm/vec.yo +18 -2
- package/std/io/reader.yo +2 -1
- package/std/io/writer.yo +3 -2
- package/std/libc/assert.yo +1 -0
- package/std/libc/ctype.yo +1 -0
- package/std/libc/dirent.yo +1 -0
- package/std/libc/errno.yo +1 -0
- package/std/libc/fcntl.yo +1 -0
- package/std/libc/float.yo +1 -0
- package/std/libc/limits.yo +1 -0
- package/std/libc/math.yo +1 -0
- package/std/libc/signal.yo +1 -0
- package/std/libc/stdatomic.yo +1 -0
- package/std/libc/stdint.yo +1 -0
- package/std/libc/stdio.yo +1 -0
- package/std/libc/stdlib.yo +1 -0
- package/std/libc/string.yo +1 -0
- package/std/libc/sys/stat.yo +1 -0
- package/std/libc/time.yo +1 -0
- package/std/libc/unistd.yo +1 -0
- package/std/libc/wctype.yo +1 -0
- package/std/libc/windows.yo +2 -0
- package/std/log.yo +7 -6
- package/std/net/addr.yo +6 -5
- package/std/net/dns.yo +13 -16
- package/std/net/errors.yo +9 -9
- package/std/net/tcp.yo +71 -74
- package/std/net/udp.yo +40 -43
- package/std/os/signal.yo +5 -5
- package/std/path.yo +1 -0
- package/std/prelude.yo +377 -200
- package/std/process/command.yo +57 -46
- package/std/process/index.yo +2 -1
- package/std/regex/compiler.yo +10 -9
- package/std/regex/index.yo +41 -41
- package/std/regex/match.yo +2 -2
- package/std/regex/parser.yo +31 -31
- package/std/regex/vm.yo +42 -41
- package/std/string/string.yo +95 -40
- package/std/string/string_builder.yo +9 -9
- package/std/string/unicode.yo +50 -49
- package/std/sync/channel.yo +2 -1
- package/std/sync/cond.yo +5 -4
- package/std/sync/mutex.yo +4 -3
- package/std/sys/advise.yo +1 -0
- package/std/sys/bufio/buf_reader.yo +27 -26
- package/std/sys/bufio/buf_writer.yo +22 -21
- package/std/sys/clock.yo +1 -0
- package/std/sys/copy.yo +1 -0
- package/std/sys/dir.yo +10 -9
- package/std/sys/dns.yo +6 -5
- package/std/sys/errors.yo +12 -12
- package/std/sys/events.yo +1 -0
- package/std/sys/externs.yo +38 -37
- package/std/sys/file.yo +17 -16
- package/std/sys/future.yo +4 -3
- package/std/sys/iov.yo +1 -0
- package/std/sys/mmap.yo +1 -0
- package/std/sys/path.yo +1 -0
- package/std/sys/perm.yo +2 -1
- package/std/sys/pipe.yo +1 -0
- package/std/sys/process.yo +5 -4
- package/std/sys/signal.yo +1 -0
- package/std/sys/socketpair.yo +1 -0
- package/std/sys/sockinfo.yo +1 -0
- package/std/sys/statfs.yo +2 -1
- package/std/sys/statx.yo +1 -0
- package/std/sys/sysinfo.yo +1 -0
- package/std/sys/tcp.yo +15 -14
- package/std/sys/temp.yo +1 -0
- package/std/sys/time.yo +2 -1
- package/std/sys/timer.yo +6 -6
- package/std/sys/tty.yo +2 -1
- package/std/sys/udp.yo +13 -12
- package/std/sys/unix.yo +12 -11
- package/std/testing/bench.yo +4 -3
- package/std/thread.yo +7 -6
- package/std/time/datetime.yo +18 -15
- package/std/time/duration.yo +11 -10
- package/std/time/instant.yo +4 -4
- package/std/time/sleep.yo +1 -0
- package/std/url/index.yo +5 -5
- package/std/worker.yo +4 -3
|
@@ -78,7 +78,7 @@ if(done, println("done"), println("pending"));
|
|
|
78
78
|
- Always write `match(...)`, never bare `match ...`
|
|
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
|
-
- Write `
|
|
81
|
+
- Write `unwind(value)` or `unwind()`; `unwind value` is invalid.
|
|
82
82
|
- If a `match`/`cond` branch returns an enum variant and inference fails, qualify
|
|
83
83
|
the variant with its enum type: `TypeValue.Unit` instead of `.Unit`.
|
|
84
84
|
- Do not match enum payload literals directly, e.g. avoid `.Some(false)` and
|
|
@@ -121,12 +121,22 @@ masked := ((A | B) | C);
|
|
|
121
121
|
- Yo has no operator precedence; fully parenthesize binary expressions
|
|
122
122
|
- Preserve grouping around infix expressions on operator RHS positions: `true => (x / y)`, `value := (x + y)`, `(ptr &+ 1).*`
|
|
123
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: `(
|
|
124
|
+
- When an operator ends a line, indent its RHS one level as a continuation: `(x : T) =\n (v) -> { ... }`
|
|
125
125
|
- Prefix operators (`!`, `&`, `-`, `~`) require parenthesized operands: `func(&(s), a, b)`, `!(ready)`, `-(value)`.
|
|
126
126
|
- Tight special forms also require immediate parentheses: `#(expr)`, `?*(u8)`, `T <: !(Runtime)`
|
|
127
127
|
- Dynamic field access with unquote must keep grouping after the dot: `value.(#(field_expr))`, not `value.#(field_expr)`.
|
|
128
128
|
- Unquote splicing is the tight operator `...#(exprs)`; do not insert a space between `...` and `#`.
|
|
129
129
|
- Canonical pointer dereference is `ptr.*`; formatter should canonicalize legacy `ptr.(*)` to `ptr.*`.
|
|
130
|
+
- **Pointer deref (`p.*`), arithmetic (`&+`, `&-`, `&/`), and `consume(p.* = v)` require `unsafe(...)`, AND the file must declare `pragma(Pragma.AllowUnsafe);` at the top before `unsafe(...)` is usable.** Pointer comparison (`&==`, `&<`, etc.) and pointer-type casts (`*(u8)(p)`) stay safe. `unsafe(expr)` is a one-arg builtin call: `v := unsafe(p.*);`, `unsafe(p.* = i32(5));`, `unsafe(p &+ usize(1))`. Every file in `std/`, `yo-self/`, and `tests/` declares the pragma explicitly. User code (default) does not, so attempts to use `unsafe(...)` are rejected with a hint to add the pragma. See `plans/MEMORY_SAFETY.md`.
|
|
131
|
+
- **In-place mutation without raw pointers:** use the `ref(name) : T` parameter modifier (parallel to `own(name)`). `swap :: (fn(ref(a) : i32, ref(b) : i32) -> unit)({ tmp := a; a = b; b = tmp; });` — caller writes `swap(x, y)` with no `&()` syntax. The compiler lowers `ref(name) : T` to `T*` in C and inserts `&(arg)` at the call site automatically. Cannot combine with `own(...)` or with `forall`/`using` (those are erased at runtime — no binding to mutate). CAN combine with `comptime` as `comptime(ref(name)) : T` — the parameter is erased at runtime and mutations propagate via the evaluator's compile-time binding update path (used by prelude `ComptimeIndex`). See `plans/MEMORY_SAFETY.md` Phase B.
|
|
132
|
+
- **Object-type params:** use plain `name : Type`, NOT `*(Type)` or `ref(name) : Type`. Object types (`Environment`, `EvalContext`, `CodegenContext`, `Emitter`, `HashMap`, `ArrayList`, …) carry reference semantics — passing by name already shares the underlying RC state, so mutations through the param propagate to the caller. `*(Type)` requires `pragma(Pragma.AllowUnsafe);` for the `.* ` derefs and clutters the API; `ref(name) : Type` is redundant since object semantics already share state. Use the plain form: `foo :: (fn(ctx : EvalContext) -> unit)(ctx.method());`. The same applies at call sites — don't wrap object arguments with `&(obj)`; just pass `obj`. For receivers on object methods, plain `self : Self` is the idiom (`yo-self/env.yo`, `yo-self/codegen/context.yo`, `yo-self/emitter.yo` all follow this). `ref(self) : Self` is reserved for receivers on value-type methods (the form used by `Hash`, `Clone`, `ToString`, `Index`, `ComptimeIndex`, `Writer`, `Reader`).
|
|
133
|
+
- **Byte-buffer params:** prefer `Slice(u8)` over `*(u8) + usize` for public signatures (e.g. `random_bytes`, `fnv1a_hash_bytes`). `Slice` carries the length, eliminating the (`ptr`, `wrong-size`) footgun. Convert at the FFI seam with `slice.ptr()` and `slice.len()`; construct from existing storage with `Slice(u8).from_raw_parts(&(buf(0)), len)`. The `_cstr` family is the explicit raw-pointer variant — those names signal raw-pointer use by contract.
|
|
134
|
+
- **Audit public stdlib safety with `./yo-cli public-safe-report [path]`.** Flags every top-level public `fn(...)` whose params or return type expose `*(T)` outside an `extern(...)` block. Skips FFI-by-construction directories (`libc/`, `linux/`, `darwin/`, `cuda/`, `sys/`, `sync/`) and names that signal raw-pointer use by contract (`*_cstr`, `*_ptr`, `*_raw`, `raw_*`, `from_raw_parts`, `as_ptr`, `argv`, `argc`). Currently reports 0 findings on `./std` and `./yo-self`; keep it that way when adding new APIs.
|
|
135
|
+
- **Extern "c" call sites require `unsafe(...)` even in pragma'd files.** `unsafe(memcpy(dst, src, n))`, `unsafe(strlen(s))`, etc. The pragma authorizes DECLARING the FFI symbol via `extern(...)` / `c_include(...)`; the wrap is the per-call audit marker so `yo unsafe-report` lines up with UB-capable lines. `asm(...)` and `extern(...)` / `c_include(...)` declarations themselves do NOT need a wrap (the keyword / declaration syntax is its own marker). See `plans/EXTERN_UNSAFE_WRAP.md`.
|
|
136
|
+
- **Slice-flowability rule:** a function returning a slice-bearing type (`Slice(T)`, `str`, a struct wrapping a Slice, ...) must root the returned value in caller-owned storage (a `ref`-bound parameter, any non-`ref` parameter, a `comptime`/literal source, or a flowable projection chain). `(fn() -> Option(Slice(i32)))({ arr := ArrayList(i32).new(); arr.as_slice() })` is rejected; `(fn(ref(arr) : ArrayList(i32)) -> Option(Slice(i32)))(arr.as_slice())` is accepted. See `plans/SLICE_FLOWABILITY.md`.
|
|
137
|
+
- **Signed-integer overflow is defined (wrap-around).** Yo passes `-fwrapv` to clang/gcc/zig by default so `x + i32(1)` on `i32(MAX)` wraps to `i32(MIN)` instead of UB. Opt-out: `--cflags='-fno-wrapv'`.
|
|
138
|
+
- **`// SAFETY:` comment convention.** Every non-obvious `unsafe(...)` site in stdlib should have a `// SAFETY:` comment in the previous ~8 lines explaining the contract. `yo unsafe-report` picks them up and shows them inline under each finding.
|
|
139
|
+
- **User-facing memory-safety guide:** `docs/en-US/MEMORY_SAFETY.md` (English) and `docs/zh-CN/MEMORY_SAFETY.md` (Chinese). Refer users there instead of `plans/MEMORY_SAFETY.md` (which is the design document — not shipped via npm).
|
|
130
140
|
- Keep single-line array and tuple literals compact during formatting: `[1, 2, 3]`, `(1, 2, 3)`.
|
|
131
141
|
- Parenthesize other unary operands too: `!(ready)`, `-(value)`
|
|
132
142
|
- **`!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.
|
|
@@ -180,12 +190,12 @@ create_user(name: `Bob`, age: 30);
|
|
|
180
190
|
- Named arguments must keep the same order as the definition
|
|
181
191
|
- Default values use `?=` and must be compile-time known
|
|
182
192
|
|
|
183
|
-
###
|
|
193
|
+
### Effect parameters (explicit)
|
|
184
194
|
|
|
185
195
|
```rust
|
|
186
|
-
Raise :: (
|
|
196
|
+
Raise :: (ctl(msg : String) -> i32);
|
|
187
197
|
|
|
188
|
-
safe_divide :: (fn(x : i32, y : i32,
|
|
198
|
+
safe_divide :: (fn(x : i32, y : i32, raise : Raise) -> i32)(
|
|
189
199
|
cond(
|
|
190
200
|
(y == i32(0)) => raise(`divide by zero`),
|
|
191
201
|
true => (x / y)
|
|
@@ -193,18 +203,20 @@ safe_divide :: (fn(x : i32, y : i32, using(raise : Raise)) -> i32)(
|
|
|
193
203
|
);
|
|
194
204
|
|
|
195
205
|
caller :: (fn() -> i32)({
|
|
196
|
-
|
|
197
|
-
|
|
206
|
+
// Handler value bound to a local. Lambdas on the RHS of `=` need outer parens.
|
|
207
|
+
(raise : Raise) = ((msg) -> {
|
|
208
|
+
unwind(i32(0));
|
|
198
209
|
});
|
|
199
210
|
|
|
200
|
-
safe_divide(i32(10), i32(0))
|
|
211
|
+
safe_divide(i32(10), i32(0), raise)
|
|
201
212
|
});
|
|
202
213
|
```
|
|
203
214
|
|
|
204
|
-
-
|
|
205
|
-
- `
|
|
206
|
-
|
|
207
|
-
-
|
|
215
|
+
- Effect handlers are regular parameters — pass them explicitly at the call site.
|
|
216
|
+
- `ctl(args) -> R` types a handler that may `unwind` (discard the continuation).
|
|
217
|
+
Use plain `fn(args) -> R` for handlers that always resume.
|
|
218
|
+
- Bundle multiple effects into a struct (`Ctx :: struct(raise : Raise, log : Log)`)
|
|
219
|
+
and pass one parameter when there are many.
|
|
208
220
|
|
|
209
221
|
### Closures and anonymous functions
|
|
210
222
|
|
|
@@ -214,8 +226,8 @@ caller :: (fn() -> i32)({
|
|
|
214
226
|
result := closure(i32(5));
|
|
215
227
|
|
|
216
228
|
transform :: (fn(list : ArrayList(i32), f : Impl(Fn(x : i32) -> i32)) -> unit)({
|
|
217
|
-
for(list
|
|
218
|
-
|
|
229
|
+
for(list, ref(x) => {
|
|
230
|
+
x = f(x);
|
|
219
231
|
});
|
|
220
232
|
});
|
|
221
233
|
```
|
|
@@ -369,25 +381,29 @@ while(comptime((i < 10)), {
|
|
|
369
381
|
// body evaluated/unrolled at compile time
|
|
370
382
|
});
|
|
371
383
|
|
|
372
|
-
// for loop — 2-arg prelude macro
|
|
373
|
-
|
|
384
|
+
// for loop — 2-arg prelude macro. First arg is the collection
|
|
385
|
+
// directly; the macro dispatches on the body's binding shape to
|
|
386
|
+
// pick value-form vs borrow-form iteration:
|
|
387
|
+
for(list, (x) => { // value form: implicit .into_iter()
|
|
374
388
|
process(x);
|
|
375
389
|
});
|
|
376
390
|
|
|
377
|
-
for(
|
|
378
|
-
|
|
391
|
+
for(list, ref(x) => { // borrow form: iter() + project(pos)
|
|
392
|
+
x = transform(x); // writes propagate back into list
|
|
379
393
|
});
|
|
380
394
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
395
|
+
// Combinator chains (.map / .filter / .into_iter / etc.) yield
|
|
396
|
+
// computed values; pass them as the first arg in the value form:
|
|
397
|
+
for(list.iter().map((x) => (x + i32(1))), (y) => println(y));
|
|
384
398
|
```
|
|
385
399
|
|
|
386
400
|
- Use `recur(...)` for self-recursion
|
|
387
401
|
- `while(cond, body)` is **always a runtime loop** — use this for open-ended loops (e.g., server accept loops, event loops)
|
|
388
402
|
- `while(comptime(cond), body)` explicitly unrolls at compile time — `cond` must be a compile-time-known value
|
|
389
403
|
- Using a comptime-only (`::`) variable in a bare `while` condition without `comptime()` is a **compile error** (would be an infinite loop at runtime)
|
|
390
|
-
- **`for(
|
|
404
|
+
- **`for(coll, (x) => body)`** — value form; macro expands to `coll.into_iter()` then iterates by value (`x : T`).
|
|
405
|
+
- **`for(coll, ref(x) => body)`** — borrow form; macro expands to `coll.iter()` (position iterator) + `coll.project(pos)` (`Indexable.project` impl) so `x` is a writable binding. Writes propagate back into the collection.
|
|
406
|
+
- **Do NOT write `for(coll.iter(), (x) => …)` for the value form** — `.iter()` yields positions (usize), not the collection's elements. Use the bare collection or `.into_iter()`.
|
|
391
407
|
- **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`)
|
|
392
408
|
|
|
393
409
|
## Return and branch safety
|
|
@@ -450,18 +466,22 @@ list := ArrayList(i32).new();
|
|
|
450
466
|
list.push(i32(10));
|
|
451
467
|
list.push(i32(20));
|
|
452
468
|
|
|
453
|
-
|
|
454
|
-
|
|
469
|
+
// Value form — implicit .into_iter().
|
|
470
|
+
for(list, (value) => {
|
|
471
|
+
println(value);
|
|
455
472
|
});
|
|
456
473
|
|
|
457
|
-
|
|
458
|
-
|
|
474
|
+
// Borrow form — implicit .iter() + .project(pos). `x` is a writable
|
|
475
|
+
// binding into the collection; assignments propagate back.
|
|
476
|
+
for(list, ref(x) => {
|
|
477
|
+
x = (x + i32(1));
|
|
459
478
|
});
|
|
460
479
|
```
|
|
461
480
|
|
|
462
|
-
- `for(
|
|
463
|
-
-
|
|
464
|
-
-
|
|
481
|
+
- `for(coll, (x) => body)` — value form. Macro expands to `coll.into_iter()` and yields elements by value.
|
|
482
|
+
- `for(coll, ref(x) => body)` — borrow form. Macro expands to `coll.iter()` (a position iterator yielding `usize`) + `coll.project(pos)` (from the `Indexable` trait) so the body sees a writable binding.
|
|
483
|
+
- Combinator chains (`coll.iter().map(f).filter(g)`) only support the value form — pass the chain as the first arg with `(x) => body`.
|
|
484
|
+
- The for macro accepts the collection directly; do NOT call `.iter()` for the value form, that yields positions (usize), not elements.
|
|
465
485
|
|
|
466
486
|
## Testing
|
|
467
487
|
|
|
@@ -480,7 +500,7 @@ test("Async test", {
|
|
|
480
500
|
});
|
|
481
501
|
```
|
|
482
502
|
|
|
483
|
-
- `test("description", { body })` defines a test — `io :
|
|
503
|
+
- `test("description", { body })` defines a test — `io : Io` is automatically available
|
|
484
504
|
- All tests can use `io.async(...)`, `io.await(...)`, etc. without a `using` clause
|
|
485
505
|
- `assert(condition, "message")` — runtime assertion (always include a message)
|
|
486
506
|
- `comptime_assert(condition)` — compile-time assertion
|
|
@@ -602,11 +622,11 @@ list := ArrayList(i32).new();
|
|
|
602
622
|
list.push(i32(42));
|
|
603
623
|
|
|
604
624
|
val := list(usize(0)); // → i32 (value copy via Index trait)
|
|
605
|
-
list(usize(0)) = i32(99);
|
|
625
|
+
list(usize(0)) = i32(99); // mutate in place directly
|
|
606
626
|
|
|
607
627
|
// When you need the pointer explicitly:
|
|
608
|
-
ptr := &(list(usize(0)));
|
|
609
|
-
ptr.* = i32(99);
|
|
628
|
+
ptr := &(list(usize(0))); // → *(i32)
|
|
629
|
+
ptr.* = i32(99); // also works
|
|
610
630
|
|
|
611
631
|
// Safe access (returns Option(T)):
|
|
612
632
|
match(list.get(usize(0)),
|
|
@@ -620,6 +640,26 @@ match(list.get(usize(0)),
|
|
|
620
640
|
- `&(list(i))` returns `*(T)` if you need the pointer explicitly
|
|
621
641
|
- `list.get(i)` returns `Option(T)` for safe bounds-checked access
|
|
622
642
|
|
|
643
|
+
**Don't write `(&(X)).index(i).*` or `X.get(i).unwrap()` when you mean
|
|
644
|
+
`X(i)`.** Use the call-syntax form everywhere it works:
|
|
645
|
+
|
|
646
|
+
```rust
|
|
647
|
+
// ✗ Verbose, scans like raw-pointer code (and requires the file's
|
|
648
|
+
// pragma(Pragma.AllowUnsafe); because `.*` is gated):
|
|
649
|
+
(&(self.field)).index(i).* = value;
|
|
650
|
+
elem := (&(self.field)).index(i).*;
|
|
651
|
+
v := list.get(usize(0)).unwrap();
|
|
652
|
+
|
|
653
|
+
// ✓ Same semantics, no `.*`, no pragma needed:
|
|
654
|
+
self.field(i) = value;
|
|
655
|
+
elem := self.field(i);
|
|
656
|
+
v := list(usize(0));
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
Both forms call the same `Index` trait method. The call-syntax form
|
|
660
|
+
is shorter, doesn't need raw-pointer plumbing in user code, and
|
|
661
|
+
panics on out-of-bounds identically to `.unwrap()` on `.get(...)`.
|
|
662
|
+
|
|
623
663
|
### Named fields required for `struct`/`object` constructors
|
|
624
664
|
|
|
625
665
|
```rust
|
|
@@ -724,46 +764,38 @@ if(!cond, { do_thing(); });
|
|
|
724
764
|
if((!cond), { do_thing(); });
|
|
725
765
|
```
|
|
726
766
|
|
|
727
|
-
### `
|
|
767
|
+
### `unwind` requires a nested-function context
|
|
728
768
|
|
|
729
|
-
`
|
|
730
|
-
|
|
731
|
-
(
|
|
732
|
-
standalone function definition.
|
|
769
|
+
`unwind(value)` exits the **install frame** — the function that bound the
|
|
770
|
+
`ctl(...) -> R` value being called. It is only valid inside the body of a
|
|
771
|
+
`ctl(...) -> R` value (an effect handler).
|
|
733
772
|
|
|
734
773
|
```rust
|
|
735
|
-
|
|
736
|
-
given(exn) := Exception(throw: ((err) -> {
|
|
737
|
-
escape(result); // exits the outer function that contains this given()
|
|
738
|
-
}));
|
|
739
|
-
do_something(using(exn));
|
|
774
|
+
Raise :: (ctl(msg : String) -> i32);
|
|
740
775
|
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
})
|
|
776
|
+
caller :: (fn() -> i32)({
|
|
777
|
+
// The handler is a `ctl` value bound in `caller`. `unwind` exits `caller`.
|
|
778
|
+
(raise : Raise) = ((msg) -> {
|
|
779
|
+
eprintln(msg);
|
|
780
|
+
unwind(i32(-1));
|
|
781
|
+
});
|
|
782
|
+
safe_divide(i32(10), i32(0), raise) // call site: handler is passed explicitly
|
|
783
|
+
});
|
|
747
784
|
|
|
748
|
-
//
|
|
749
|
-
|
|
750
|
-
//
|
|
751
|
-
// WRONG: escape(default_val) // ERROR: no enclosing fn
|
|
752
|
-
return(default_val); // CORRECT: return exits the enclosing fn directly
|
|
785
|
+
// WRONG — `unwind` in a regular `fn` body (no install frame here) is rejected.
|
|
786
|
+
bad :: (fn() -> unit)({
|
|
787
|
+
unwind(()); // ERROR: unwind requires a ctl(...) body
|
|
753
788
|
});
|
|
754
789
|
|
|
755
|
-
// WRONG —
|
|
756
|
-
|
|
757
|
-
|
|
790
|
+
// WRONG — capturing a `ctl` value into a closure is rejected (closures escape).
|
|
791
|
+
make_closure :: (fn(raise : Raise) -> Impl(Fn() -> unit))(
|
|
792
|
+
() => { raise(`x`); } // ERROR: closure captures a control-bound value
|
|
758
793
|
);
|
|
759
794
|
```
|
|
760
795
|
|
|
761
|
-
**Rule of thumb**:
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
`return` inside a lambda exits that lambda only; `escape` exits the OUTER function
|
|
766
|
-
(the one that installed the `given` handler).
|
|
796
|
+
**Rule of thumb**: `unwind` belongs only inside the lambda bound to a
|
|
797
|
+
`ctl(...) -> R` handler. From any other position, use `return` to exit the
|
|
798
|
+
current `fn`.
|
|
767
799
|
|
|
768
800
|
### Parameter reassignment
|
|
769
801
|
|
|
@@ -818,19 +850,19 @@ fn_taking_str((`prefix_${value}`).as_str());
|
|
|
818
850
|
|
|
819
851
|
These features are powerful but less commonly used. Consult the linked docs for full details.
|
|
820
852
|
|
|
821
|
-
| Feature
|
|
822
|
-
|
|
|
823
|
-
| Higher-Kinded Types
|
|
824
|
-
| GADTs
|
|
825
|
-
| Derive traits
|
|
826
|
-
| Type reflection
|
|
827
|
-
| Inline assembly
|
|
828
|
-
| Metaprogramming
|
|
829
|
-
| Effect
|
|
830
|
-
| Custom derive rules
|
|
831
|
-
| Isolated types
|
|
832
|
-
| Arc (atomic ref count)
|
|
833
|
-
| Parallelism
|
|
853
|
+
| Feature | Syntax hint | Documentation |
|
|
854
|
+
| -------------------------- | -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
|
855
|
+
| Higher-Kinded Types | `forall(F : (fn(comptime(T) : Type) -> comptime(Type)))` | [DESIGN.md § HKT](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/DESIGN.md#higher-kinded-types-hkt) |
|
|
856
|
+
| GADTs | `enum(IntVal(i : i32) -> recur(i32))` | [GADTS.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/GADTS.md) |
|
|
857
|
+
| Derive traits | `derive(MyType, Eq, Hash, Clone, Ord, ToString)` | [DERIVE_TRAITS.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/DERIVE_TRAITS.md) |
|
|
858
|
+
| Type reflection | `Type.get_info(T)` returns `TypeInfo` | [TYPE_REFLECTION.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/TYPE_REFLECTION.md) |
|
|
859
|
+
| Inline assembly | `asm("mov {0}, #42", out(reg, i32))` | [INLINE_ASSEMBLY.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/INLINE_ASSEMBLY.md) |
|
|
860
|
+
| Metaprogramming | `quote(...)`, `unquote(...)`, `unquote_splicing(...)` | [DESIGN.md § Meta](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/DESIGN.md#meta-programming) |
|
|
861
|
+
| Effect bundle polymorphism | `forall(E : Type.Struct)` over a bundle struct | [ALGEBRAIC_EFFECTS.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/ALGEBRAIC_EFFECTS.md) |
|
|
862
|
+
| Custom derive rules | `derive_rule(MyTrait, (fn(...) -> unquote(Expr)){...})` | [DERIVE_TRAITS.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/DERIVE_TRAITS.md#user-defined-derive-rules) |
|
|
863
|
+
| Isolated types | `Iso(T)` for data-race-free parallelism | [ISOLATED.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/ISOLATED.md) |
|
|
864
|
+
| 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) |
|
|
865
|
+
| Parallelism | Thread pool, `io.spawn` for parallel work | [PARALLELISM.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/PARALLELISM.md) |
|
|
834
866
|
|
|
835
867
|
---
|
|
836
868
|
|
|
@@ -997,26 +1029,6 @@ Always merge them into a single destructuring import:
|
|
|
997
1029
|
{ Foo, Bar } :: import("../../mod.yo");
|
|
998
1030
|
```
|
|
999
1031
|
|
|
1000
|
-
### Implicit (`using`) parameters cannot be used with `:=` assignment
|
|
1001
|
-
|
|
1002
|
-
Implicit effect parameters introduced via `using(name : Type)` cannot be bound
|
|
1003
|
-
to a discarded variable with `:= name`. They can only be passed via `using()`.
|
|
1004
|
-
To suppress "unused parameter" warnings for an implicit param, simply omit the
|
|
1005
|
-
discard assignment — implicit params never trigger unused-variable errors.
|
|
1006
|
-
|
|
1007
|
-
```rust
|
|
1008
|
-
// WRONG — implicit variable cannot be used in assignment:
|
|
1009
|
-
foo :: (fn(using(exn : Exception)) -> unit)({
|
|
1010
|
-
_ := exn; // ERROR: Cannot use implicit variable "exn" in assignment
|
|
1011
|
-
()
|
|
1012
|
-
});
|
|
1013
|
-
|
|
1014
|
-
// CORRECT — just omit the discard line:
|
|
1015
|
-
foo :: (fn(using(exn : Exception)) -> unit)({
|
|
1016
|
-
()
|
|
1017
|
-
});
|
|
1018
|
-
```
|
|
1019
|
-
|
|
1020
1032
|
### Nested `Option` patterns require staging
|
|
1021
1033
|
|
|
1022
1034
|
`match` does not support nested destructuring patterns like `.Some(.TypeVal(x))`.
|
package/README.md
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
https://shd101wyy.github.io/Yo
|
|
10
10
|
|
|
11
|
+
**LLM-friendly to write, human-friendly to read.**
|
|
12
|
+
|
|
11
13
|
A multi-paradigm, general-purpose, compiled programming language.
|
|
12
14
|
Yo aims to be **Simple** and **Fast** (around 0% - 15% slower than C).
|
|
13
15
|
|
|
@@ -53,7 +55,8 @@ Below is a non-exhaustive list of features that Yo supports:
|
|
|
53
55
|
- Homoiconicity and metaprogramming (**Yo** syntax is inspired by the **Lisp** S expression. Simple syntax rule, Human & AI friendly).
|
|
54
56
|
- Closure.
|
|
55
57
|
- [Algebraic Effects and Handlers](./docs/en-US/ALGEBRAIC_EFFECTS.md) (One-shot delimited continuation. Tail-Resumptive. Implicit parameters via `using`/`given`, effect handlers with `return`/`escape`, by [Evidence Passing](https://xnning.github.io/papers/multip.pdf)).
|
|
56
|
-
- [Async/await](./docs/en-US/ASYNC_AWAIT.md) (Builtin `
|
|
58
|
+
- [Async/await](./docs/en-US/ASYNC_AWAIT.md) (Builtin `Io` effect. Stackless coroutine & Cooperative multi-tasking. Lazy Futures, multi-await, single-threaded concurrency via state machine transformation).
|
|
59
|
+
- [Memory safety by default](./docs/en-US/MEMORY_SAFETY.md) — user code can't write UB (no raw pointers, no FFI, no inline assembly) without an explicit `pragma(Pragma.AllowUnsafe);` opt-in. `ref(name)` for in-place mutation; `yo unsafe-report` for auditing the unsafe surface.
|
|
57
60
|
- `object` type with [Non-atomic Reference Counting and Thread-Local Cycle Collection](./docs/en-US/CYCLE_COLLECTION.md).
|
|
58
61
|
- [Compile-time Reference Counting with Ownership and Lifetime Analysis](./docs/en-US/COMPILE_TIME_RC_WITH_OWNERSHIP_ANALYSIS.md).
|
|
59
62
|
- Thread-per-core parallelism model (see [PARALLELISM.md](./docs/en-US/PARALLELISM.md)).
|
|
@@ -232,7 +235,7 @@ Every Yo file automatically imports **[std/prelude.yo](./std/prelude.yo)**, whic
|
|
|
232
235
|
- **C-compatible types**: `int`, `uint`, `short`, `long`, `longlong`, `char`, etc.
|
|
233
236
|
- **Core traits**: `Eq`, `Ord`, `Add`, `Sub`, `Mul`, `Div`, `Iterator`, `IntoIterator`, `TryFrom`, `TryInto`, `Dispose`, `Send`, `Rc`, `Acyclic`, etc.
|
|
234
237
|
- **Metaprogramming**: `Type`, `Expr`, `ExprList`, `Var`
|
|
235
|
-
- **Async**: `
|
|
238
|
+
- **Async**: `Io`, `FutureState`, `JoinHandle`
|
|
236
239
|
- **Utilities**: `assert`, `unsafe`, `try`, `for`, `not`, `arc`, `Box`, `box`
|
|
237
240
|
- etc.
|
|
238
241
|
|
|
@@ -376,7 +379,7 @@ This repository ships a set of **agent skill files** that teach AI agents how to
|
|
|
376
379
|
| -------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
|
|
377
380
|
| [`yo-syntax`](.github/skills/yo-syntax/SKILL.md) | Core language syntax: curly braces, cond/match, structs, enums, operators, modules |
|
|
378
381
|
| [`yo-core-patterns`](.github/skills/yo-core-patterns/SKILL.md) | Everyday patterns: types, generics, traits, error handling, collections, iterators |
|
|
379
|
-
| [`yo-async-effects`](.github/skills/yo-async-effects/SKILL.md) | Async/await, algebraic effects, Exception,
|
|
382
|
+
| [`yo-async-effects`](.github/skills/yo-async-effects/SKILL.md) | Async/await, algebraic effects, Exception, Io, spawning tasks |
|
|
380
383
|
| [`yo-project-workflow`](.github/skills/yo-project-workflow/SKILL.md) | `yo` CLI commands, `build.yo` project files, dependency management |
|
|
381
384
|
|
|
382
385
|
### Using in your own project
|