@shd101wyy/yo 0.1.25 → 0.1.27

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.
Files changed (194) hide show
  1. package/.github/skills/yo-async-effects/SKILL.md +4 -4
  2. package/.github/skills/yo-async-effects/async-effects-recipes.md +40 -40
  3. package/.github/skills/yo-core-patterns/SKILL.md +1 -1
  4. package/.github/skills/yo-core-patterns/core-patterns-cheatsheet.md +30 -26
  5. package/.github/skills/yo-project-workflow/SKILL.md +6 -3
  6. package/.github/skills/yo-project-workflow/workflow-cheatsheet.md +34 -11
  7. package/.github/skills/yo-syntax/SKILL.md +7 -6
  8. package/.github/skills/yo-syntax/syntax-cheatsheet.md +78 -60
  9. package/.github/skills/yo-wasm-integration/wasm-integration-cheatsheet.md +3 -3
  10. package/README.md +10 -8
  11. package/out/cjs/index.cjs +583 -567
  12. package/out/cjs/yo-cli.cjs +664 -632
  13. package/out/cjs/yo-lsp.cjs +510 -485
  14. package/out/esm/index.mjs +538 -522
  15. package/out/types/src/codegen/codegen-c.d.ts +2 -2
  16. package/out/types/src/codegen/functions/collection.d.ts +2 -2
  17. package/out/types/src/codegen/functions/context.d.ts +3 -2
  18. package/out/types/src/codegen/types/collection.d.ts +2 -2
  19. package/out/types/src/codegen/utils/index.d.ts +3 -1
  20. package/out/types/src/doc/builder.d.ts +2 -2
  21. package/out/types/src/evaluator/calls/closure-type.d.ts +2 -2
  22. package/out/types/src/evaluator/calls/record-type.d.ts +11 -0
  23. package/out/types/src/evaluator/context.d.ts +8 -9
  24. package/out/types/src/evaluator/index.d.ts +3 -3
  25. package/out/types/src/evaluator/types/record.d.ts +14 -0
  26. package/out/types/src/evaluator/types/validation.d.ts +2 -2
  27. package/out/types/src/evaluator/values/anonymous-module.d.ts +5 -5
  28. package/out/types/src/evaluator/values/impl.d.ts +1 -1
  29. package/out/types/src/expr.d.ts +1 -4
  30. package/out/types/src/formatter.d.ts +11 -0
  31. package/out/types/src/function-value.d.ts +1 -1
  32. package/out/types/src/lsp/document-manager.d.ts +1 -1
  33. package/out/types/src/lsp/formatting.d.ts +2 -0
  34. package/out/types/src/module-manager.d.ts +3 -3
  35. package/out/types/src/tests/formatter.test.d.ts +1 -0
  36. package/out/types/src/types/creators.d.ts +3 -4
  37. package/out/types/src/types/definitions.d.ts +8 -19
  38. package/out/types/src/types/guards.d.ts +3 -3
  39. package/out/types/src/types/tags.d.ts +0 -1
  40. package/out/types/src/types/utils.d.ts +1 -1
  41. package/out/types/src/value-tag.d.ts +0 -1
  42. package/out/types/src/value.d.ts +6 -13
  43. package/out/types/tsconfig.tsbuildinfo +1 -1
  44. package/package.json +1 -1
  45. package/std/alg/hash.yo +13 -21
  46. package/std/allocator.yo +25 -40
  47. package/std/async.yo +3 -7
  48. package/std/build.yo +105 -151
  49. package/std/cli/arg_parser.yo +184 -169
  50. package/std/collections/array_list.yo +350 -314
  51. package/std/collections/btree_map.yo +142 -131
  52. package/std/collections/deque.yo +132 -128
  53. package/std/collections/hash_map.yo +542 -566
  54. package/std/collections/hash_set.yo +623 -687
  55. package/std/collections/linked_list.yo +275 -293
  56. package/std/collections/ordered_map.yo +113 -85
  57. package/std/collections/priority_queue.yo +73 -73
  58. package/std/crypto/md5.yo +191 -95
  59. package/std/crypto/random.yo +56 -64
  60. package/std/crypto/sha256.yo +151 -107
  61. package/std/encoding/base64.yo +87 -81
  62. package/std/encoding/hex.yo +43 -50
  63. package/std/encoding/html.yo +56 -81
  64. package/std/encoding/html_char_utils.yo +7 -13
  65. package/std/encoding/html_entities.yo +2248 -2253
  66. package/std/encoding/json.yo +316 -224
  67. package/std/encoding/punycode.yo +86 -116
  68. package/std/encoding/toml.yo +67 -66
  69. package/std/encoding/utf16.yo +37 -44
  70. package/std/env.yo +62 -91
  71. package/std/error.yo +12 -20
  72. package/std/fmt/display.yo +5 -9
  73. package/std/fmt/index.yo +8 -14
  74. package/std/fmt/to_string.yo +330 -315
  75. package/std/fmt/writer.yo +58 -87
  76. package/std/fs/dir.yo +83 -102
  77. package/std/fs/file.yo +147 -180
  78. package/std/fs/metadata.yo +45 -78
  79. package/std/fs/temp.yo +55 -65
  80. package/std/fs/types.yo +27 -40
  81. package/std/fs/walker.yo +53 -68
  82. package/std/gc.yo +5 -8
  83. package/std/glob.yo +30 -43
  84. package/std/http/client.yo +107 -120
  85. package/std/http/http.yo +106 -96
  86. package/std/http/index.yo +4 -6
  87. package/std/imm/list.yo +88 -93
  88. package/std/imm/map.yo +528 -464
  89. package/std/imm/set.yo +52 -57
  90. package/std/imm/sorted_map.yo +340 -286
  91. package/std/imm/sorted_set.yo +57 -63
  92. package/std/imm/string.yo +404 -345
  93. package/std/imm/vec.yo +173 -181
  94. package/std/io/reader.yo +3 -6
  95. package/std/io/writer.yo +4 -8
  96. package/std/libc/assert.yo +5 -9
  97. package/std/libc/ctype.yo +32 -22
  98. package/std/libc/dirent.yo +26 -25
  99. package/std/libc/errno.yo +164 -90
  100. package/std/libc/fcntl.yo +52 -45
  101. package/std/libc/float.yo +66 -44
  102. package/std/libc/limits.yo +42 -33
  103. package/std/libc/math.yo +53 -82
  104. package/std/libc/signal.yo +72 -47
  105. package/std/libc/stdatomic.yo +217 -188
  106. package/std/libc/stdint.yo +5 -29
  107. package/std/libc/stdio.yo +5 -29
  108. package/std/libc/stdlib.yo +32 -39
  109. package/std/libc/string.yo +5 -23
  110. package/std/libc/sys/stat.yo +58 -56
  111. package/std/libc/time.yo +5 -19
  112. package/std/libc/unistd.yo +5 -20
  113. package/std/libc/wctype.yo +6 -9
  114. package/std/libc/windows.yo +26 -30
  115. package/std/log.yo +41 -55
  116. package/std/net/addr.yo +102 -97
  117. package/std/net/dns.yo +27 -28
  118. package/std/net/errors.yo +50 -49
  119. package/std/net/tcp.yo +113 -124
  120. package/std/net/udp.yo +55 -66
  121. package/std/os/env.yo +35 -33
  122. package/std/os/signal.yo +15 -25
  123. package/std/path.yo +276 -311
  124. package/std/prelude.yo +6316 -4333
  125. package/std/process/command.yo +87 -103
  126. package/std/process/index.yo +12 -31
  127. package/std/regex/compiler.yo +196 -95
  128. package/std/regex/flags.yo +58 -39
  129. package/std/regex/index.yo +157 -173
  130. package/std/regex/match.yo +20 -31
  131. package/std/regex/node.yo +134 -152
  132. package/std/regex/parser.yo +283 -259
  133. package/std/regex/unicode.yo +172 -202
  134. package/std/regex/vm.yo +155 -171
  135. package/std/string/index.yo +5 -7
  136. package/std/string/rune.yo +45 -55
  137. package/std/string/string.yo +937 -964
  138. package/std/string/string_builder.yo +94 -104
  139. package/std/string/unicode.yo +46 -64
  140. package/std/sync/channel.yo +72 -73
  141. package/std/sync/cond.yo +31 -36
  142. package/std/sync/mutex.yo +30 -32
  143. package/std/sync/once.yo +13 -16
  144. package/std/sync/rwlock.yo +26 -31
  145. package/std/sync/waitgroup.yo +20 -25
  146. package/std/sys/advise.yo +16 -24
  147. package/std/sys/bufio/buf_reader.yo +77 -93
  148. package/std/sys/bufio/buf_writer.yo +52 -65
  149. package/std/sys/clock.yo +4 -9
  150. package/std/sys/constants.yo +77 -61
  151. package/std/sys/copy.yo +4 -10
  152. package/std/sys/dir.yo +26 -43
  153. package/std/sys/dns.yo +41 -61
  154. package/std/sys/errors.yo +95 -103
  155. package/std/sys/events.yo +45 -57
  156. package/std/sys/externs.yo +319 -267
  157. package/std/sys/fallocate.yo +7 -11
  158. package/std/sys/fcntl.yo +14 -22
  159. package/std/sys/file.yo +26 -40
  160. package/std/sys/future.yo +5 -8
  161. package/std/sys/iov.yo +12 -25
  162. package/std/sys/lock.yo +12 -13
  163. package/std/sys/mmap.yo +38 -43
  164. package/std/sys/path.yo +3 -8
  165. package/std/sys/perm.yo +7 -21
  166. package/std/sys/pipe.yo +5 -12
  167. package/std/sys/process.yo +23 -29
  168. package/std/sys/seek.yo +10 -12
  169. package/std/sys/signal.yo +7 -13
  170. package/std/sys/signals.yo +52 -35
  171. package/std/sys/socket.yo +63 -58
  172. package/std/sys/socketpair.yo +3 -6
  173. package/std/sys/sockinfo.yo +11 -20
  174. package/std/sys/statfs.yo +11 -34
  175. package/std/sys/statx.yo +25 -52
  176. package/std/sys/sysinfo.yo +15 -20
  177. package/std/sys/tcp.yo +62 -92
  178. package/std/sys/temp.yo +5 -9
  179. package/std/sys/time.yo +5 -15
  180. package/std/sys/timer.yo +6 -11
  181. package/std/sys/tty.yo +10 -18
  182. package/std/sys/udp.yo +22 -39
  183. package/std/sys/umask.yo +3 -6
  184. package/std/sys/unix.yo +33 -52
  185. package/std/testing/bench.yo +49 -52
  186. package/std/thread.yo +10 -15
  187. package/std/time/datetime.yo +105 -89
  188. package/std/time/duration.yo +43 -56
  189. package/std/time/instant.yo +13 -18
  190. package/std/time/sleep.yo +5 -9
  191. package/std/url/index.yo +184 -209
  192. package/std/worker.yo +6 -10
  193. package/out/types/src/evaluator/calls/module-type.d.ts +0 -11
  194. package/out/types/src/evaluator/types/module.d.ts +0 -19
@@ -26,8 +26,8 @@ Use this skill when you need to:
26
26
  2. Add the necessary `using(...)` parameters to function signatures and call sites.
27
27
  3. Use the [async and effects recipes](./async-effects-recipes.md) for working patterns.
28
28
  4. Re-check handler semantics before finalizing:
29
- - `return value` resumes the continuation
30
- - `escape expr` discards it
29
+ - `return(value)` resumes the continuation
30
+ - `escape(expr)` discards it
31
31
 
32
32
  ## High-signal rules
33
33
 
@@ -38,8 +38,8 @@ Use this skill when you need to:
38
38
  - Future types include their effects: `Future(ResultType, IO, Effect...)`.
39
39
  - Effects are matched by type, not variable name.
40
40
  - `using(name : Type)` declares an implicit effect parameter; `given(name) := Type(...)` installs the handler.
41
- - `return value` inside a handler resumes the continuation; `escape expr` discards it.
42
- - `Exception` — non-resumable; handler calls `escape` to exit. `ResumableException(T)` — handler calls `return` to resume.
41
+ - `return(value)` inside a handler resumes the continuation; `escape(expr)` discards it.
42
+ - `Exception` — non-resumable; handler calls `escape(...)` to exit. `ResumableException(T)` — handler calls `return(...)` to resume.
43
43
  - Effect handlers are standalone, not closures; pass state explicitly.
44
44
  - Yo async is single-threaded concurrency, not multithreaded parallelism.
45
45
 
@@ -14,7 +14,7 @@ These patterns cover normal Yo async code and algebraic effects.
14
14
  ## Minimal async function
15
15
 
16
16
  ```rust
17
- { yield } :: import "std/async";
17
+ { yield } :: import("std/async");
18
18
 
19
19
  pause_then_answer :: (fn(using(io : IO)) -> Impl(Future(i32, IO)))(
20
20
  io.async((using(io : IO)) => {
@@ -41,7 +41,7 @@ work :: (fn(using(io : IO, raise : Raise)) -> Impl(Future(i32, IO, Raise)))(
41
41
  ## Sequential await
42
42
 
43
43
  ```rust
44
- { yield } :: import "std/async";
44
+ { yield } :: import("std/async");
45
45
 
46
46
  main :: (fn(using(io : IO)) -> unit)({
47
47
  task := io.async((using(io : IO)) => {
@@ -53,13 +53,13 @@ main :: (fn(using(io : IO)) -> unit)({
53
53
  assert((result == i32(1)), "unexpected result");
54
54
  });
55
55
 
56
- export main;
56
+ export(main);
57
57
  ```
58
58
 
59
59
  ## Concurrent tasks on the same thread
60
60
 
61
61
  ```rust
62
- { yield } :: import "std/async";
62
+ { yield } :: import("std/async");
63
63
 
64
64
  main :: (fn(using(io : IO)) -> unit)({
65
65
  task1 := io.async((using(io : IO)) => {
@@ -78,7 +78,7 @@ main :: (fn(using(io : IO)) -> unit)({
78
78
  result2 := handle2.await(using(io));
79
79
  });
80
80
 
81
- export main;
81
+ export(main);
82
82
  ```
83
83
 
84
84
  - `io.spawn(...)` begins execution without waiting
@@ -87,8 +87,8 @@ export main;
87
87
  ## Propagating and handling effects
88
88
 
89
89
  ```rust
90
- open import "std/fmt";
91
- open import "std/string";
90
+ open(import("std/fmt"));
91
+ open(import("std/string"));
92
92
 
93
93
  Raise :: (fn(msg : String) -> i32);
94
94
 
@@ -102,7 +102,7 @@ safe_divide :: (fn(x : i32, y : i32, using(raise : Raise)) -> i32)(
102
102
  resume_example :: (fn() -> i32)({
103
103
  (given(raise) : Raise) = (fn(msg : String) -> i32)({
104
104
  println(msg);
105
- return i32(0);
105
+ return(i32(0));
106
106
  });
107
107
 
108
108
  safe_divide(i32(8), i32(0))
@@ -111,22 +111,22 @@ resume_example :: (fn() -> i32)({
111
111
  escape_example :: (fn() -> i32)({
112
112
  (given(raise) : Raise) = (fn(msg : String) -> i32)({
113
113
  println(msg);
114
- escape i32(-1);
114
+ escape(i32(-1));
115
115
  });
116
116
 
117
117
  safe_divide(i32(8), i32(0))
118
118
  });
119
119
  ```
120
120
 
121
- | Handler action | Meaning |
122
- | -------------- | -------------------------------------------- |
123
- | `return value` | Resume the continuation with `value` |
124
- | `escape expr` | Exit the function that installed the handler |
121
+ | Handler action | Meaning |
122
+ | --------------- | -------------------------------------------- |
123
+ | `return(value)` | Resume the continuation with `value` |
124
+ | `escape(expr)` | Exit the function that installed the handler |
125
125
 
126
126
  ## Futures with multiple effects
127
127
 
128
128
  ```rust
129
- { yield } :: import "std/async";
129
+ { yield } :: import("std/async");
130
130
 
131
131
  work :: (fn(using(io : IO, raise : Raise)) -> Impl(Future(i32, IO, Raise)))(
132
132
  io.async((using(io : IO, raise : Raise)) => {
@@ -146,20 +146,20 @@ work :: (fn(using(io : IO, raise : Raise)) -> Impl(Future(i32, IO, Raise)))(
146
146
  **Solution**: replace async recursion with an iterative worklist using `ArrayList` as a stack:
147
147
 
148
148
  ```rust
149
- { read_dir, DirEntry } :: import "std/fs/dir";
149
+ { read_dir, DirEntry } :: import("std/fs/dir");
150
150
 
151
151
  process_dir :: (fn(root: Path, using(io: IO, exn: Exception)) -> Impl(Future(unit, IO, Exception)))(
152
152
  io.async((using(io, exn)) => {
153
153
  stack := ArrayList(Path).new();
154
154
  { stack.push(root); };
155
155
 
156
- while runtime((stack.len() > usize(0))), {
157
- cur := match(stack.pop(), .Some(p) => p, .None => return ());
156
+ while(runtime((stack.len() > usize(0))), {
157
+ cur := match(stack.pop(), .Some(p) => p, .None => return());
158
158
  entries := io.await(read_dir(cur));
159
159
  // process `entries`, push subdirectories to `stack`
160
160
  n := entries.len();
161
161
  i := usize(0);
162
- while runtime((i < n)), {
162
+ while(runtime((i < n)), {
163
163
  match(entries.get(i),
164
164
  .None => (),
165
165
  .Some(e) => {
@@ -170,8 +170,8 @@ process_dir :: (fn(root: Path, using(io: IO, exn: Exception)) -> Impl(Future(uni
170
170
  }
171
171
  );
172
172
  i = (i + usize(1));
173
- };
174
- };
173
+ });
174
+ });
175
175
  })
176
176
  );
177
177
  ```
@@ -186,11 +186,11 @@ process_dir :: (fn(root: Path, using(io: IO, exn: Exception)) -> Impl(Future(uni
186
186
 
187
187
  ## Exception (non-resumable)
188
188
 
189
- `Exception` is a built-in module effect for non-resumable error handling. When the handler calls `escape`, the continuation is discarded:
189
+ `Exception` is a built-in struct-record effect for non-resumable error handling. When the handler calls `escape`, the continuation is discarded:
190
190
 
191
191
  ```rust
192
- open import "std/error";
193
- open import "std/fmt";
192
+ open(import("std/error"));
193
+ open(import("std/fmt"));
194
194
 
195
195
  DivError :: enum(DivByZero);
196
196
  impl(DivError, ToString(to_string : ((self) -> `division by zero`)));
@@ -207,7 +207,7 @@ main :: (fn() -> unit)({
207
207
  given(exn) := Exception(
208
208
  throw : ((err) -> {
209
209
  println(`Error: ${err}`);
210
- escape ();
210
+ escape();
211
211
  })
212
212
  );
213
213
 
@@ -217,7 +217,7 @@ main :: (fn() -> unit)({
217
217
  safe_divide(i32(10), i32(0));
218
218
  });
219
219
 
220
- export main;
220
+ export(main);
221
221
  ```
222
222
 
223
223
  - `Exception` has a single field `throw : (fn(error : AnyError) -> T)`
@@ -230,32 +230,32 @@ export main;
230
230
  When an exception is thrown inside an async operation (e.g., `cmd.status()` or `cmd.output()`), you can **swallow the error and resume with a fallback value** by using `return` in the handler (not `escape`). The `ResumeType` is the return type of the operation that would have thrown.
231
231
 
232
232
  ```rust
233
- { Command, ExitStatus, Output } :: import "std/process/command";
233
+ { Command, ExitStatus, Output } :: import("std/process/command");
234
234
 
235
235
  // Check if a tool is available — returns false if it throws (e.g., not found)
236
236
  given(try_exn) := Exception(throw: ((err) -> {
237
- return ExitStatus(raw: i32(1)); // resume with "failed" exit status
237
+ return(ExitStatus(raw: i32(1))); // resume with "failed" exit status
238
238
  }));
239
239
  status := io.await(cmd.status(using(io, try_exn)));
240
240
  available := status.success(); // false if exception was swallowed
241
241
 
242
242
  // For cmd.output(), resume with a failed Output:
243
243
  given(out_exn) := Exception(throw: ((err) -> {
244
- return Output(status: ExitStatus(raw: i32(1)), stdout: ArrayList(u8).new(), stderr: ArrayList(u8).new());
244
+ return(Output(status: ExitStatus(raw: i32(1)), stdout: ArrayList(u8).new(), stderr: ArrayList(u8).new()));
245
245
  }));
246
246
  out := io.await(cmd.output(using(io, out_exn)));
247
- if((!(out.status.success())), { return (); }); // handle failure
247
+ if((!(out.status.success())), { return(); }); // handle failure
248
248
  ```
249
249
 
250
250
  Key: the `return` inside the handler resumes the _effect invocation site_ with the provided value. The calling code then sees the fallback as if the operation returned normally. Use `escape` only when the enclosing function returns `unit` (e.g., test bodies).
251
251
 
252
- **`escape T_value` constraint**: `escape T_value` inside an `Exception` handler requires that the enclosing `io.async` closure's return type matches `T_value`. Due to forward type inference, the evaluator may not know the closure's return type at the point where `given` is declared. This causes a "Expected: unit" error when `escape non_unit` is used in a handler declared before the final return expression. Prefer `return fallback_value` (resume) when possible.
252
+ **`escape(T_value)` constraint**: `escape(T_value)` inside an `Exception` handler requires that the enclosing `io.async` closure's return type matches `T_value`. Due to forward type inference, the evaluator may not know the closure's return type at the point where `given` is declared. This causes a "Expected: unit" error when `escape(non_unit)` is used in a handler declared before the final return expression. Prefer `return(fallback_value)` (resume) when possible.
253
253
 
254
- `ResumableException(ResumeType)` is a module effect for resumable error handling. The handler uses `return` to resume with a recovery value:
254
+ `ResumableException(ResumeType)` is a struct-record effect for resumable error handling. The handler uses `return` to resume with a recovery value:
255
255
 
256
256
  ```rust
257
- open import "std/error";
258
- open import "std/fmt";
257
+ open(import("std/error"));
258
+ open(import("std/fmt"));
259
259
 
260
260
  safe_divide :: (fn(x : i32, y : i32, using(exn : ResumableException(i32))) -> i32)(
261
261
  cond(
@@ -268,7 +268,7 @@ main :: (fn() -> unit)({
268
268
  given(exn) := ResumableException(i32)(
269
269
  throw : ((err) -> {
270
270
  println(`Recovering from: ${err}`);
271
- return i32(0);
271
+ return(i32(0));
272
272
  })
273
273
  );
274
274
 
@@ -276,25 +276,25 @@ main :: (fn() -> unit)({
276
276
  assert((result == i32(0)), "recovered with 0");
277
277
  });
278
278
 
279
- export main;
279
+ export(main);
280
280
  ```
281
281
 
282
- - Handler uses `return value` to resume the continuation with the recovery value
282
+ - Handler uses `return(value)` to resume the continuation with the recovery value
283
283
  - The call site receives the returned value and continues normally
284
284
 
285
- ## Module effects vs function-type effects
285
+ ## Struct-record effects vs function-type effects
286
286
 
287
- Effects in Yo can be plain function types or module types:
287
+ Effects in Yo can be plain function types or struct-record types:
288
288
 
289
289
  ```rust
290
290
  Raise :: (fn(msg : String) -> i32);
291
291
 
292
- Logger :: module(
292
+ Logger :: struct(
293
293
  log : (fn(level : i32, msg : String) -> unit)
294
294
  );
295
295
  ```
296
296
 
297
- Both kinds use `using(...)` / `given(...)` with the same semantics — they compile to evidence passing (function pointers as implicit C parameters). Module effects group related operations under a single name.
297
+ Both kinds use `using(...)` / `given(...)` with the same semantics — they compile to evidence passing (function pointers as implicit C parameters). Struct-record effects group related operations under a single nominal type.
298
298
 
299
299
  ## Async with effects
300
300
 
@@ -40,7 +40,7 @@ Use this skill when you need to:
40
40
  - Use `derive(Type, Eq, Hash, Clone, Ord, ToString)` to auto-generate common trait impls.
41
41
  - Custom error types implement `ToString` + `Error`; wrap with `dyn(...)` into `AnyError`.
42
42
  - Use `(params) => expr` for closures; `Impl(Fn(...) -> T)` for the closure type.
43
- - Use `for collection.iter(), (item) => { ... }` for iteration.
43
+ - Use `for(collection.iter(), (item) => { ... })` for iteration.
44
44
  - Indexed modules import cleanly as `std/url`, `std/regex`, `std/http`, `std/log`, and `std/glob`; multi-module families use explicit submodules.
45
45
 
46
46
  ## Resource
@@ -5,8 +5,8 @@ These patterns are aimed at everyday Yo application and library code.
5
5
  ## Strings and output
6
6
 
7
7
  ```rust
8
- open import "std/fmt";
9
- open import "std/string";
8
+ open(import("std/fmt"));
9
+ open(import("std/string"));
10
10
 
11
11
  (name : str) = "yo";
12
12
  greeting := `Hello ${name}`;
@@ -38,13 +38,13 @@ Key rules:
38
38
  ## Import patterns
39
39
 
40
40
  ```rust
41
- { LocalType } :: import "./local_type.yo";
42
- open import "std/string";
43
- { ArrayList } :: import "std/collections/array_list";
44
- { HashMap } :: import "std/collections/hash_map";
45
- { Url } :: import "std/url";
46
- { Regex } :: import "std/regex";
47
- { fetch, HttpRequest } :: import "std/http";
41
+ { LocalType } :: import("./local_type.yo");
42
+ open(import("std/string"));
43
+ { ArrayList } :: import("std/collections/array_list");
44
+ { HashMap } :: import("std/collections/hash_map");
45
+ { Url } :: import("std/url");
46
+ { Regex } :: import("std/regex");
47
+ { fetch, HttpRequest } :: import("std/http");
48
48
  ```
49
49
 
50
50
  | Need | Import pattern |
@@ -60,7 +60,7 @@ Do not import `std/prelude`; it is already available.
60
60
  ## Option and Result
61
61
 
62
62
  ```rust
63
- open import "std/string";
63
+ open(import("std/string"));
64
64
 
65
65
  (value : Option(i32)) = .Some(i32(21));
66
66
  doubled := value.map((x) => (x * i32(2)));
@@ -81,9 +81,9 @@ text := match(parsed,
81
81
  ## Collections
82
82
 
83
83
  ```rust
84
- { ArrayList } :: import "std/collections/array_list";
85
- { HashMap } :: import "std/collections/hash_map";
86
- open import "std/string";
84
+ { ArrayList } :: import("std/collections/array_list");
85
+ { HashMap } :: import("std/collections/hash_map");
86
+ open(import("std/string"));
87
87
 
88
88
  numbers := ArrayList(i32).new();
89
89
  numbers.push(i32(1));
@@ -161,7 +161,7 @@ parent := Node(value: i32(1), next: Option(Box(Node)).Some(child));
161
161
  ## Unicode and platform checks
162
162
 
163
163
  ```rust
164
- { Platform, platform } :: import "std/process";
164
+ { Platform, platform } :: import("std/process");
165
165
 
166
166
  separator := cond(
167
167
  (platform == Platform.Windows) => `\\`,
@@ -190,6 +190,10 @@ TcpStream :: object(fd : i32, buffer : ArrayList(u8));
190
190
 
191
191
  - Use `newtype(...)` when the type has exactly one field
192
192
  - Use `object(...)` for types that need shared ownership
193
+ - Source-file imports are namespace structs. The old `module(...)`, `Module`,
194
+ and `SelfModule` syntax is gone; use `struct(...)`, `Type`, and normal `Self`.
195
+ - Fields written as `name :: value` or `comptime(name) : Type` are compile-time-only static fields/methods and have no runtime layout
196
+ - A normal field with a compile-time-only type, such as `x : comptime_int`, is still a data field and makes the struct comptime-only
193
197
 
194
198
  ## Impl blocks and generics
195
199
 
@@ -281,7 +285,7 @@ node_eq :: (fn(a : Node, b : Node) -> bool)(
281
285
  true => {
282
286
  (i : usize) = usize(0);
283
287
  (ok : bool) = true;
284
- while runtime(((i < acs.len()) && ok)), {
288
+ while(runtime(((i < acs.len()) && ok)), {
285
289
  match(acs.get(i),
286
290
  .Some(ac) => match(bcs.get(i),
287
291
  .Some(bc) => { ok = recur(ac, bc); },
@@ -290,7 +294,7 @@ node_eq :: (fn(a : Node, b : Node) -> bool)(
290
294
  .None => { ok = false; }
291
295
  );
292
296
  i = (i + usize(1));
293
- };
297
+ });
294
298
  ok
295
299
  }
296
300
  )
@@ -320,15 +324,15 @@ derived or implemented. For **tag-only equality** (checking which variant), use
320
324
  if(my_type != t_unit(), { ... });
321
325
 
322
326
  // CORRECT — compare tags instead
323
- { type_value_tag } :: import "../../types/type.yo";
324
- { TypeTag } :: import "../../types/tags.yo";
327
+ { type_value_tag } :: import("../../types/type.yo");
328
+ { TypeTag } :: import("../../types/tags.yo");
325
329
  if((type_value_tag(my_type) != TypeTag.TUnit), { ... });
326
330
  ```
327
331
 
328
332
  ## Error handling
329
333
 
330
334
  ```rust
331
- open import "std/error";
335
+ open(import("std/error"));
332
336
 
333
337
  DivError :: enum(DivByZero);
334
338
  impl(DivError, ToString(to_string : ((self) -> `division by zero`)));
@@ -354,9 +358,9 @@ safe_div :: (fn(a : i32, b : i32) -> Result(i32, DivError))(
354
358
  result := inc(i32(5));
355
359
 
356
360
  transform :: (fn(values : ArrayList(i32), f : Impl(Fn(x : i32) -> i32)) -> unit)({
357
- for values.iter(), (ptr) => {
361
+ for(values.iter(), (ptr) => {
358
362
  ptr.* = f(ptr.*);
359
- };
363
+ });
360
364
  });
361
365
  ```
362
366
 
@@ -368,19 +372,19 @@ transform :: (fn(values : ArrayList(i32), f : Impl(Fn(x : i32) -> i32)) -> unit)
368
372
  ## Iterator and for loop
369
373
 
370
374
  ```rust
371
- { ArrayList } :: import "std/collections/array_list";
375
+ { ArrayList } :: import("std/collections/array_list");
372
376
 
373
377
  list := ArrayList(i32).new();
374
378
  list.push(i32(1));
375
379
  list.push(i32(2));
376
380
 
377
- for list.iter(), (ptr) => {
381
+ for(list.iter(), (ptr) => {
378
382
  println(ptr.*);
379
- };
383
+ });
380
384
 
381
- for list.into_iter(), (value) => {
385
+ for(list.into_iter(), (value) => {
382
386
  println(value);
383
- };
387
+ });
384
388
  ```
385
389
 
386
390
  | Method | Yields | Semantics |
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: yo-project-workflow
3
- description: Build, test, scaffold, and manage Yo projects. Use this when working with yo init, build.yo, yo build, yo compile, yo test, yo install, yo fetch, yo version, or cross-platform Yo project setup.
3
+ description: Build, test, scaffold, format, and manage Yo projects. Use this when working with yo init, build.yo, yo build, yo compile, yo test, yo fmt, yo install, yo fetch, yo version, or cross-platform Yo project setup.
4
4
  argument-hint: "[project task or command]"
5
5
  ---
6
6
 
@@ -17,6 +17,7 @@ Use this skill when you need to:
17
17
  - scaffold a new Yo project
18
18
  - understand or edit `build.yo`
19
19
  - choose between `yo build`, `yo compile`, and `yo test`
20
+ - format source with `yo fmt`
20
21
  - manage dependencies with `yo install` or `yo fetch`
21
22
  - pin or manage Yo versions with `yo version`
22
23
  - install AI agent skill files with `yo skills install`
@@ -28,8 +29,9 @@ Use this skill when you need to:
28
29
  1. If the repository has `build.yo`, prefer `yo build` and named steps for whole-project work.
29
30
  2. For single-file experiments or reproductions, use `yo compile`.
30
31
  3. For tests, use `yo test [path]` and narrow to a file or pattern before broad runs.
31
- 4. To pin the project to a specific Yo version, use `yo version pin`.
32
- 5. Consult the [workflow cheatsheet](./workflow-cheatsheet.md) for command shapes, project layout, and a minimal `build.yo`.
32
+ 4. For source formatting, use `yo fmt`; use `yo fmt --check` in CI-style verification.
33
+ 5. To pin the project to a specific Yo version, use `yo version pin`.
34
+ 6. Consult the [workflow cheatsheet](./workflow-cheatsheet.md) for command shapes, project layout, and a minimal `build.yo`.
33
35
 
34
36
  ## High-signal rules
35
37
 
@@ -37,6 +39,7 @@ Use this skill when you need to:
37
39
  - `build.yo` is Yo code that imports `std/build`; build functions register compile-time steps.
38
40
  - `yo build run` and `yo build test` are the standard project entry points.
39
41
  - `yo test ./tests/some.test.yo --parallel 1` is the focused single-file test pattern.
42
+ - `yo fmt` applies the fixed Yo style with 2-space indentation; there are no formatter options.
40
43
  - Use `yo install` and `yo fetch` for git or path dependencies.
41
44
  - Use `yo version pin` to create a `.yo-version` file for reproducible builds.
42
45
  - Use `yo skills install` to copy Yo skill files into all agent config directories in the project.
@@ -18,6 +18,8 @@ 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
+ | Format Yo source | `yo fmt ./src ./tests` |
22
+ | Check Yo formatting | `yo fmt --check` |
21
23
  | Generate docs for project | `yo doc ./src` |
22
24
  | Generate docs (custom) | `yo doc ./src -o docs --title "My Project"` |
23
25
  | Install AI agent skills | `yo skills install` |
@@ -48,7 +50,7 @@ my-project/
48
50
  ## Minimal `build.yo`
49
51
 
50
52
  ```rust
51
- build :: import "std/build";
53
+ build :: import("std/build");
52
54
 
53
55
  mod :: build.module({ name: "my-project", root: "./src/lib.yo" });
54
56
 
@@ -89,6 +91,7 @@ doc_step.depend_on(docs);
89
91
  | Whole project with `build.yo` | `yo build ...` |
90
92
  | One standalone file | `yo compile ...` |
91
93
  | One test file or test directory | `yo test ...` |
94
+ | Formatting Yo source | `yo fmt ...` |
92
95
  | Dependency changes | `yo install ...` then `yo fetch` if needed |
93
96
 
94
97
  ## Testing patterns
@@ -103,25 +106,45 @@ yo test ./tests/main.test.yo --bail --verbose --parallel 1
103
106
  - Use `--test-name-pattern` when a file contains many tests
104
107
  - Use `yo build test` when the repository's main test workflow is defined in `build.yo`
105
108
 
109
+ ## Formatting
110
+
111
+ ```bash
112
+ yo fmt
113
+ yo fmt ./src ./tests
114
+ yo fmt --check
115
+ ```
116
+
117
+ - `yo fmt` recursively formats `.yo` files under the current directory by default
118
+ - Pass files or directories to limit the scope
119
+ - `yo fmt --check` reports files that need formatting without writing changes
120
+ - Formatting is intentionally fixed: 2-space indentation and no configuration
121
+ - Formatting must be idempotent: a second `yo fmt` run on the same files should report no changes and must not produce parser errors
122
+ - The formatter removes redundant grouping parentheses, e.g. `return((1 + 2))` → `return(1 + 2)`
123
+ - Infix-like separators keep spaces on both sides, including `{ x : value }` fields and `[T ; N]` array type sugar
124
+ - It preserves delimiter syntax whose meaning is not just grouping: tuples, `{...}` struct/begin forms, `[T ; N]` arrays, `[T]` slices, call parentheses, function body calls, and prefix-operator operands
125
+ - It preserves grouping or operator line breaks where removing them would expose ambiguous infix syntax, e.g. `{ x : (1 + 2), y : 3 }`, `true => (x / y)`, `(ptr &+ 1).*`, and line-leading `|` chains
126
+ - When an operator ends a line, `yo fmt` indents the RHS one extra level as a continuation
127
+ - `yo fmt` canonicalizes legacy deref spelling `ptr.(*)` to `ptr.*` and keeps single-line array/tuple literals compact
128
+
106
129
  ### Writing tests in Yo
107
130
 
108
131
  ```rust
109
- test "Basic assertion", {
132
+ test("Basic assertion", {
110
133
  assert(((i32(1) + i32(1)) == i32(2)), "1+1 should be 2");
111
- };
134
+ });
112
135
 
113
- test "Compile-time check", {
136
+ test("Compile-time check", {
114
137
  comptime_assert((2 + 2) == 4);
115
138
  comptime_expect_error({ x :: (1 / 0); });
116
- };
139
+ });
117
140
 
118
- test "Async test", {
119
- { yield } :: import "std/async";
141
+ test("Async test", {
142
+ { yield } :: import("std/async");
120
143
  io.await(yield());
121
- };
144
+ });
122
145
  ```
123
146
 
124
- - `test "name", { body }` defines a test — `io : IO` is automatically available
147
+ - `test("name", { body })` defines a test — `io : IO` is automatically available
125
148
  - All tests can use `io.async(...)`, `io.await(...)`, etc. without a `using` clause
126
149
  - `assert(condition, "message")` — always include a message string
127
150
  - `comptime_assert(expr)` — verified at compile time
@@ -147,7 +170,7 @@ yo test ./tests/main.test.yo --target wasm-wasi
147
170
  For projects that compile to WASM npm packages, use `target: build.CompilationTarget.Wasm32_Emscripten` and `add_c_flags(...)` for Emscripten settings:
148
171
 
149
172
  ```rust
150
- build :: import "std/build";
173
+ build :: import("std/build");
151
174
 
152
175
  wasm_api :: build.executable({
153
176
  name: "my_lib_wasm_api",
@@ -191,7 +214,7 @@ yo doc --document-private # Include non-exported items
191
214
  In `build.yo`:
192
215
 
193
216
  ```rust
194
- build :: import "std/build";
217
+ build :: import("std/build");
195
218
 
196
219
  docs :: build.doc({ name: "docs", root: "./src" });
197
220
  doc_step :: build.step("doc", "Generate documentation");
@@ -31,26 +31,27 @@ Use this skill when you need to:
31
31
  - `cond(...)` and `match(...)` always require parentheses.
32
32
  - `{ expr }` is a struct literal; `{ expr; }` is a begin block.
33
33
  - Yo has no operator precedence. Parenthesize every binary operation.
34
- - Use `func(arg)` with no space before `(` for normal calls.
34
+ - Use `func(arg)` with no space before `(` for every call; `func arg` and `func (arg)` are invalid.
35
+ - Use `return(value)` / `return()` and `escape(value)` / `escape()`; bare control-flow arguments are invalid.
35
36
  - Use `recur(...)` for self-recursion instead of the function name.
36
37
  - Use `forall(T : Type)` for generic type parameters, `comptime(x) : T` for compile-time parameters.
37
38
  - Use `where(T <: Trait)` to constrain type parameters.
38
39
  - Use `using(name : Type)` for implicit/effect parameters and `given(name) := Type(...)` to install handlers.
39
40
  - Use `(params) => expr` for closures; `Impl(Fn(...) -> T)` for the closure type.
40
- - Every executable needs `export main;`.
41
+ - Every executable needs `export(main);`.
41
42
  - Import sibling modules with relative paths like `./file.yo`.
42
43
  - Do not import `std/prelude`; it is loaded automatically.
43
44
  - Use `snake_case` for names, `PascalCase` for types/traits, 2-space indentation.
44
45
 
45
46
  ## Common traps
46
47
 
47
- - `return expr1, expr2` is parsed as one call; use a begin block or return an expression directly.
48
+ - `return expr` is invalid; use `return(expr)` or `return()` for unit.
48
49
  - Nested patterns like `.Ok(.Some(x))` are not supported; match in stages.
49
- - Unary operators need parenthesized operands: `!(ready)`.
50
- - `while true` is compile-time. Use `while runtime(true), { ... }` for infinite runtime loops.
50
+ - Unary operators need parenthesized operands: `!(ready)`, `&(value)`.
51
+ - Use `while(true, { ... })` for infinite runtime loops; use `while(comptime(cond), { ... })` only for compile-time unrolling.
51
52
  - A single-expression lambda body should not be wrapped in `{ ... }` unless semicolons make it a begin block.
52
53
  - `"hello"` is `comptime_string` inside `comptime` functions, not `str`. In runtime code, `"hello"` is always `str`.
53
- - Functions called without `()` in match/cond branches consume trailing commas. Always add `()` or use begin blocks.
54
+ - Calls in match/cond branches must use immediate `(...)`; this avoids trailing-comma ambiguity.
54
55
 
55
56
  ## Resource
56
57