@rcrsr/rill 0.1.0

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 (295) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +187 -0
  3. package/dist/cli.d.ts +11 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +69 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/demo.d.ts +6 -0
  8. package/dist/demo.d.ts.map +1 -0
  9. package/dist/demo.js +121 -0
  10. package/dist/demo.js.map +1 -0
  11. package/dist/index.d.ts +10 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +9 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/lexer/errors.d.ts +9 -0
  16. package/dist/lexer/errors.d.ts.map +1 -0
  17. package/dist/lexer/errors.js +12 -0
  18. package/dist/lexer/errors.js.map +1 -0
  19. package/dist/lexer/helpers.d.ts +14 -0
  20. package/dist/lexer/helpers.d.ts.map +1 -0
  21. package/dist/lexer/helpers.js +30 -0
  22. package/dist/lexer/helpers.js.map +1 -0
  23. package/dist/lexer/index.d.ts +8 -0
  24. package/dist/lexer/index.d.ts.map +1 -0
  25. package/dist/lexer/index.js +8 -0
  26. package/dist/lexer/index.js.map +1 -0
  27. package/dist/lexer/operators.d.ts +11 -0
  28. package/dist/lexer/operators.d.ts.map +1 -0
  29. package/dist/lexer/operators.js +58 -0
  30. package/dist/lexer/operators.js.map +1 -0
  31. package/dist/lexer/readers.d.ts +12 -0
  32. package/dist/lexer/readers.d.ts.map +1 -0
  33. package/dist/lexer/readers.js +144 -0
  34. package/dist/lexer/readers.js.map +1 -0
  35. package/dist/lexer/state.d.ts +18 -0
  36. package/dist/lexer/state.d.ts.map +1 -0
  37. package/dist/lexer/state.js +37 -0
  38. package/dist/lexer/state.js.map +1 -0
  39. package/dist/lexer/tokenizer.d.ts +9 -0
  40. package/dist/lexer/tokenizer.d.ts.map +1 -0
  41. package/dist/lexer/tokenizer.js +100 -0
  42. package/dist/lexer/tokenizer.js.map +1 -0
  43. package/dist/lexer.d.ts +19 -0
  44. package/dist/lexer.d.ts.map +1 -0
  45. package/dist/lexer.js +344 -0
  46. package/dist/lexer.js.map +1 -0
  47. package/dist/parser/arithmetic.d.ts +16 -0
  48. package/dist/parser/arithmetic.d.ts.map +1 -0
  49. package/dist/parser/arithmetic.js +128 -0
  50. package/dist/parser/arithmetic.js.map +1 -0
  51. package/dist/parser/boolean.d.ts +15 -0
  52. package/dist/parser/boolean.d.ts.map +1 -0
  53. package/dist/parser/boolean.js +20 -0
  54. package/dist/parser/boolean.js.map +1 -0
  55. package/dist/parser/control-flow.d.ts +56 -0
  56. package/dist/parser/control-flow.d.ts.map +1 -0
  57. package/dist/parser/control-flow.js +167 -0
  58. package/dist/parser/control-flow.js.map +1 -0
  59. package/dist/parser/expressions.d.ts +23 -0
  60. package/dist/parser/expressions.d.ts.map +1 -0
  61. package/dist/parser/expressions.js +950 -0
  62. package/dist/parser/expressions.js.map +1 -0
  63. package/dist/parser/extraction.d.ts +48 -0
  64. package/dist/parser/extraction.d.ts.map +1 -0
  65. package/dist/parser/extraction.js +279 -0
  66. package/dist/parser/extraction.js.map +1 -0
  67. package/dist/parser/functions.d.ts +20 -0
  68. package/dist/parser/functions.d.ts.map +1 -0
  69. package/dist/parser/functions.js +96 -0
  70. package/dist/parser/functions.js.map +1 -0
  71. package/dist/parser/helpers.d.ts +94 -0
  72. package/dist/parser/helpers.d.ts.map +1 -0
  73. package/dist/parser/helpers.js +225 -0
  74. package/dist/parser/helpers.js.map +1 -0
  75. package/dist/parser/index.d.ts +49 -0
  76. package/dist/parser/index.d.ts.map +1 -0
  77. package/dist/parser/index.js +73 -0
  78. package/dist/parser/index.js.map +1 -0
  79. package/dist/parser/literals.d.ts +37 -0
  80. package/dist/parser/literals.d.ts.map +1 -0
  81. package/dist/parser/literals.js +373 -0
  82. package/dist/parser/literals.js.map +1 -0
  83. package/dist/parser/parser-collect.d.ts +16 -0
  84. package/dist/parser/parser-collect.d.ts.map +1 -0
  85. package/dist/parser/parser-collect.js +125 -0
  86. package/dist/parser/parser-collect.js.map +1 -0
  87. package/dist/parser/parser-control.d.ts +20 -0
  88. package/dist/parser/parser-control.d.ts.map +1 -0
  89. package/dist/parser/parser-control.js +120 -0
  90. package/dist/parser/parser-control.js.map +1 -0
  91. package/dist/parser/parser-expr.d.ts +37 -0
  92. package/dist/parser/parser-expr.d.ts.map +1 -0
  93. package/dist/parser/parser-expr.js +639 -0
  94. package/dist/parser/parser-expr.js.map +1 -0
  95. package/dist/parser/parser-extract.d.ts +17 -0
  96. package/dist/parser/parser-extract.d.ts.map +1 -0
  97. package/dist/parser/parser-extract.js +222 -0
  98. package/dist/parser/parser-extract.js.map +1 -0
  99. package/dist/parser/parser-functions.d.ts +21 -0
  100. package/dist/parser/parser-functions.d.ts.map +1 -0
  101. package/dist/parser/parser-functions.js +155 -0
  102. package/dist/parser/parser-functions.js.map +1 -0
  103. package/dist/parser/parser-literals.d.ts +22 -0
  104. package/dist/parser/parser-literals.d.ts.map +1 -0
  105. package/dist/parser/parser-literals.js +288 -0
  106. package/dist/parser/parser-literals.js.map +1 -0
  107. package/dist/parser/parser-script.d.ts +21 -0
  108. package/dist/parser/parser-script.d.ts.map +1 -0
  109. package/dist/parser/parser-script.js +174 -0
  110. package/dist/parser/parser-script.js.map +1 -0
  111. package/dist/parser/parser-variables.d.ts +20 -0
  112. package/dist/parser/parser-variables.d.ts.map +1 -0
  113. package/dist/parser/parser-variables.js +146 -0
  114. package/dist/parser/parser-variables.js.map +1 -0
  115. package/dist/parser/parser.d.ts +49 -0
  116. package/dist/parser/parser.d.ts.map +1 -0
  117. package/dist/parser/parser.js +54 -0
  118. package/dist/parser/parser.js.map +1 -0
  119. package/dist/parser/script.d.ts +14 -0
  120. package/dist/parser/script.d.ts.map +1 -0
  121. package/dist/parser/script.js +196 -0
  122. package/dist/parser/script.js.map +1 -0
  123. package/dist/parser/state.d.ts +40 -0
  124. package/dist/parser/state.d.ts.map +1 -0
  125. package/dist/parser/state.js +129 -0
  126. package/dist/parser/state.js.map +1 -0
  127. package/dist/parser/variables.d.ts +10 -0
  128. package/dist/parser/variables.d.ts.map +1 -0
  129. package/dist/parser/variables.js +215 -0
  130. package/dist/parser/variables.js.map +1 -0
  131. package/dist/runtime/ast-equals.d.ts +13 -0
  132. package/dist/runtime/ast-equals.d.ts.map +1 -0
  133. package/dist/runtime/ast-equals.js +447 -0
  134. package/dist/runtime/ast-equals.js.map +1 -0
  135. package/dist/runtime/builtins.d.ts +13 -0
  136. package/dist/runtime/builtins.d.ts.map +1 -0
  137. package/dist/runtime/builtins.js +180 -0
  138. package/dist/runtime/builtins.js.map +1 -0
  139. package/dist/runtime/callable.d.ts +88 -0
  140. package/dist/runtime/callable.d.ts.map +1 -0
  141. package/dist/runtime/callable.js +98 -0
  142. package/dist/runtime/callable.js.map +1 -0
  143. package/dist/runtime/context.d.ts +13 -0
  144. package/dist/runtime/context.d.ts.map +1 -0
  145. package/dist/runtime/context.js +73 -0
  146. package/dist/runtime/context.js.map +1 -0
  147. package/dist/runtime/core/callable.d.ts +171 -0
  148. package/dist/runtime/core/callable.d.ts.map +1 -0
  149. package/dist/runtime/core/callable.js +246 -0
  150. package/dist/runtime/core/callable.js.map +1 -0
  151. package/dist/runtime/core/context.d.ts +29 -0
  152. package/dist/runtime/core/context.d.ts.map +1 -0
  153. package/dist/runtime/core/context.js +154 -0
  154. package/dist/runtime/core/context.js.map +1 -0
  155. package/dist/runtime/core/equals.d.ts +9 -0
  156. package/dist/runtime/core/equals.d.ts.map +1 -0
  157. package/dist/runtime/core/equals.js +381 -0
  158. package/dist/runtime/core/equals.js.map +1 -0
  159. package/dist/runtime/core/eval/base.d.ts +65 -0
  160. package/dist/runtime/core/eval/base.d.ts.map +1 -0
  161. package/dist/runtime/core/eval/base.js +112 -0
  162. package/dist/runtime/core/eval/base.js.map +1 -0
  163. package/dist/runtime/core/eval/evaluator.d.ts +47 -0
  164. package/dist/runtime/core/eval/evaluator.d.ts.map +1 -0
  165. package/dist/runtime/core/eval/evaluator.js +73 -0
  166. package/dist/runtime/core/eval/evaluator.js.map +1 -0
  167. package/dist/runtime/core/eval/index.d.ts +57 -0
  168. package/dist/runtime/core/eval/index.d.ts.map +1 -0
  169. package/dist/runtime/core/eval/index.js +95 -0
  170. package/dist/runtime/core/eval/index.js.map +1 -0
  171. package/dist/runtime/core/eval/mixins/annotations.d.ts +19 -0
  172. package/dist/runtime/core/eval/mixins/annotations.d.ts.map +1 -0
  173. package/dist/runtime/core/eval/mixins/annotations.js +146 -0
  174. package/dist/runtime/core/eval/mixins/annotations.js.map +1 -0
  175. package/dist/runtime/core/eval/mixins/closures.d.ts +49 -0
  176. package/dist/runtime/core/eval/mixins/closures.d.ts.map +1 -0
  177. package/dist/runtime/core/eval/mixins/closures.js +479 -0
  178. package/dist/runtime/core/eval/mixins/closures.js.map +1 -0
  179. package/dist/runtime/core/eval/mixins/collections.d.ts +24 -0
  180. package/dist/runtime/core/eval/mixins/collections.d.ts.map +1 -0
  181. package/dist/runtime/core/eval/mixins/collections.js +466 -0
  182. package/dist/runtime/core/eval/mixins/collections.js.map +1 -0
  183. package/dist/runtime/core/eval/mixins/control-flow.d.ts +27 -0
  184. package/dist/runtime/core/eval/mixins/control-flow.d.ts.map +1 -0
  185. package/dist/runtime/core/eval/mixins/control-flow.js +369 -0
  186. package/dist/runtime/core/eval/mixins/control-flow.js.map +1 -0
  187. package/dist/runtime/core/eval/mixins/core.d.ts +24 -0
  188. package/dist/runtime/core/eval/mixins/core.d.ts.map +1 -0
  189. package/dist/runtime/core/eval/mixins/core.js +335 -0
  190. package/dist/runtime/core/eval/mixins/core.js.map +1 -0
  191. package/dist/runtime/core/eval/mixins/expressions.d.ts +19 -0
  192. package/dist/runtime/core/eval/mixins/expressions.d.ts.map +1 -0
  193. package/dist/runtime/core/eval/mixins/expressions.js +202 -0
  194. package/dist/runtime/core/eval/mixins/expressions.js.map +1 -0
  195. package/dist/runtime/core/eval/mixins/extraction.d.ts +10 -0
  196. package/dist/runtime/core/eval/mixins/extraction.d.ts.map +1 -0
  197. package/dist/runtime/core/eval/mixins/extraction.js +250 -0
  198. package/dist/runtime/core/eval/mixins/extraction.js.map +1 -0
  199. package/dist/runtime/core/eval/mixins/literals.d.ts +23 -0
  200. package/dist/runtime/core/eval/mixins/literals.d.ts.map +1 -0
  201. package/dist/runtime/core/eval/mixins/literals.js +180 -0
  202. package/dist/runtime/core/eval/mixins/literals.js.map +1 -0
  203. package/dist/runtime/core/eval/mixins/types.d.ts +20 -0
  204. package/dist/runtime/core/eval/mixins/types.d.ts.map +1 -0
  205. package/dist/runtime/core/eval/mixins/types.js +109 -0
  206. package/dist/runtime/core/eval/mixins/types.js.map +1 -0
  207. package/dist/runtime/core/eval/mixins/variables.d.ts +34 -0
  208. package/dist/runtime/core/eval/mixins/variables.d.ts.map +1 -0
  209. package/dist/runtime/core/eval/mixins/variables.js +247 -0
  210. package/dist/runtime/core/eval/mixins/variables.js.map +1 -0
  211. package/dist/runtime/core/eval/types.d.ts +41 -0
  212. package/dist/runtime/core/eval/types.d.ts.map +1 -0
  213. package/dist/runtime/core/eval/types.js +10 -0
  214. package/dist/runtime/core/eval/types.js.map +1 -0
  215. package/dist/runtime/core/evaluate.d.ts +42 -0
  216. package/dist/runtime/core/evaluate.d.ts.map +1 -0
  217. package/dist/runtime/core/evaluate.debug.js +1251 -0
  218. package/dist/runtime/core/evaluate.js +1913 -0
  219. package/dist/runtime/core/evaluate.js.map +1 -0
  220. package/dist/runtime/core/execute.d.ts +26 -0
  221. package/dist/runtime/core/execute.d.ts.map +1 -0
  222. package/dist/runtime/core/execute.js +177 -0
  223. package/dist/runtime/core/execute.js.map +1 -0
  224. package/dist/runtime/core/signals.d.ts +19 -0
  225. package/dist/runtime/core/signals.d.ts.map +1 -0
  226. package/dist/runtime/core/signals.js +26 -0
  227. package/dist/runtime/core/signals.js.map +1 -0
  228. package/dist/runtime/core/types.d.ts +177 -0
  229. package/dist/runtime/core/types.d.ts.map +1 -0
  230. package/dist/runtime/core/types.js +50 -0
  231. package/dist/runtime/core/types.js.map +1 -0
  232. package/dist/runtime/core/values.d.ts +66 -0
  233. package/dist/runtime/core/values.d.ts.map +1 -0
  234. package/dist/runtime/core/values.js +240 -0
  235. package/dist/runtime/core/values.js.map +1 -0
  236. package/dist/runtime/evaluate.d.ts +32 -0
  237. package/dist/runtime/evaluate.d.ts.map +1 -0
  238. package/dist/runtime/evaluate.js +1111 -0
  239. package/dist/runtime/evaluate.js.map +1 -0
  240. package/dist/runtime/execute.d.ts +26 -0
  241. package/dist/runtime/execute.d.ts.map +1 -0
  242. package/dist/runtime/execute.js +121 -0
  243. package/dist/runtime/execute.js.map +1 -0
  244. package/dist/runtime/ext/builtins.d.ts +16 -0
  245. package/dist/runtime/ext/builtins.d.ts.map +1 -0
  246. package/dist/runtime/ext/builtins.js +528 -0
  247. package/dist/runtime/ext/builtins.js.map +1 -0
  248. package/dist/runtime/ext/content-parser.d.ts +83 -0
  249. package/dist/runtime/ext/content-parser.d.ts.map +1 -0
  250. package/dist/runtime/ext/content-parser.js +536 -0
  251. package/dist/runtime/ext/content-parser.js.map +1 -0
  252. package/dist/runtime/index.d.ts +28 -0
  253. package/dist/runtime/index.d.ts.map +1 -0
  254. package/dist/runtime/index.js +34 -0
  255. package/dist/runtime/index.js.map +1 -0
  256. package/dist/runtime/signals.d.ts +19 -0
  257. package/dist/runtime/signals.d.ts.map +1 -0
  258. package/dist/runtime/signals.js +26 -0
  259. package/dist/runtime/signals.js.map +1 -0
  260. package/dist/runtime/types.d.ts +169 -0
  261. package/dist/runtime/types.d.ts.map +1 -0
  262. package/dist/runtime/types.js +50 -0
  263. package/dist/runtime/types.js.map +1 -0
  264. package/dist/runtime/values.d.ts +50 -0
  265. package/dist/runtime/values.d.ts.map +1 -0
  266. package/dist/runtime/values.js +209 -0
  267. package/dist/runtime/values.js.map +1 -0
  268. package/dist/runtime.d.ts +254 -0
  269. package/dist/runtime.d.ts.map +1 -0
  270. package/dist/runtime.js +2014 -0
  271. package/dist/runtime.js.map +1 -0
  272. package/dist/types.d.ts +752 -0
  273. package/dist/types.d.ts.map +1 -0
  274. package/dist/types.js +189 -0
  275. package/dist/types.js.map +1 -0
  276. package/docs/00_INDEX.md +65 -0
  277. package/docs/01_guide.md +390 -0
  278. package/docs/02_types.md +399 -0
  279. package/docs/03_variables.md +314 -0
  280. package/docs/04_operators.md +551 -0
  281. package/docs/05_control-flow.md +350 -0
  282. package/docs/06_closures.md +353 -0
  283. package/docs/07_collections.md +686 -0
  284. package/docs/08_iterators.md +330 -0
  285. package/docs/09_strings.md +205 -0
  286. package/docs/10_parsing.md +366 -0
  287. package/docs/11_reference.md +350 -0
  288. package/docs/12_examples.md +771 -0
  289. package/docs/13_modules.md +519 -0
  290. package/docs/14_host-integration.md +826 -0
  291. package/docs/15_grammar.ebnf +693 -0
  292. package/docs/16_conventions.md +696 -0
  293. package/docs/99_llm-reference.txt +300 -0
  294. package/docs/assets/logo.png +0 -0
  295. package/package.json +70 -0
@@ -0,0 +1,350 @@
1
+ # rill Control Flow
2
+
3
+ *Conditionals, loops, break, and return*
4
+
5
+ ## Overview
6
+
7
+ rill provides singular control flow—no exceptions, no try/catch. Errors halt execution. Recovery requires explicit conditionals.
8
+
9
+ | Syntax | Description |
10
+ |--------|-------------|
11
+ | `cond ? then ! else` | Conditional (if-else) |
12
+ | `$val -> ? then ! else` | Piped conditional (uses $ as cond) |
13
+ | `(cond) @ body` | While loop (cond is bool) |
14
+ | `@ body ? cond` | Do-while (body first) |
15
+ | `break` / `$val -> break` | Exit loop |
16
+ | `return` / `$val -> return` | Exit block |
17
+
18
+ ---
19
+
20
+ ## Conditionals
21
+
22
+ `?` is the conditional operator. The condition precedes `?`, and `!` introduces the else clause.
23
+
24
+ ### Syntax Forms
25
+
26
+ ```text
27
+ condition ? then-body
28
+ condition ? then-body ! else-body
29
+ $val -> ? then-body ! else-body # piped form: $ is the condition
30
+ ```
31
+
32
+ ### Standalone Form
33
+
34
+ Condition precedes `?`:
35
+
36
+ ```rill
37
+ true ? "yes" ! "no" # "yes"
38
+ false ? "yes" ! "no" # "no"
39
+ (5 > 3) ? "big" ! "small" # grouped comparison as condition
40
+ ```
41
+
42
+ ### Piped Form
43
+
44
+ Use `$` as condition:
45
+
46
+ ```rill
47
+ true -> ? "yes" ! "no" # "yes" (pipe value must be bool)
48
+ 5 -> ($ > 3) ? "big" ! "small" # "big"
49
+ ```
50
+
51
+ ### Method Conditions
52
+
53
+ Methods that return booleans work directly as conditions:
54
+
55
+ ```rill
56
+ "hello" -> .contains("ell") ? "found" ! "missing" # "found"
57
+ "abc" -> !.empty ? "has content" ! "empty" # "has content"
58
+ ```
59
+
60
+ ### Condition Forms
61
+
62
+ ```rill
63
+ "test" -> ($ == "test") ? "match" ! "no" # grouped comparison
64
+ "test" -> .eq("test") ? "match" ! "no" # comparison method
65
+ "xyz" -> .contains("x") ? "found" ! "no" # method as condition
66
+ ```
67
+
68
+ ### Optional Else
69
+
70
+ The else branch (`! ...`) is optional:
71
+
72
+ ```rill
73
+ true ? "executed" # only runs if true
74
+ false ? "skipped" # returns empty string
75
+ ```
76
+
77
+ ### Else-If Chains
78
+
79
+ ```rill
80
+ "B" :> $val
81
+ $val -> .eq("A") ? "a" ! .eq("B") ? "b" ! "other" # "b"
82
+ ```
83
+
84
+ ### Return Value
85
+
86
+ Conditionals return the last expression of the executed branch:
87
+
88
+ ```rill
89
+ true -> ? "yes" ! "no" :> $result # "yes"
90
+ false -> ? "yes" ! "no" :> $result # "no"
91
+ ```
92
+
93
+ ### Block Bodies
94
+
95
+ Use braces for multi-statement branches:
96
+
97
+ ```rill
98
+ true -> ? {
99
+ "step 1" -> log
100
+ "step 2" -> log
101
+ "done"
102
+ } ! {
103
+ "skipped"
104
+ }
105
+ ```
106
+
107
+ ---
108
+
109
+ ## While Loop
110
+
111
+ Pre-condition loop. Condition is evaluated before each iteration. The body result becomes the next iteration's `$`.
112
+
113
+ ### Syntax
114
+
115
+ ```text
116
+ initial -> (condition) @ { body }
117
+ ```
118
+
119
+ ### Basic Usage
120
+
121
+ ```rill
122
+ # Count to 5
123
+ 0 -> ($ < 5) @ { $ + 1 } # Result: 5
124
+
125
+ # String accumulation
126
+ "" -> (.len < 5) @ { "{$}x" } # Result: "xxxxx"
127
+ ```
128
+
129
+ ### Condition Forms
130
+
131
+ ```rill
132
+ 0 -> ($ < 10) @ { $ + 1 } # comparison condition
133
+ "" -> (.len < 5) @ { "{$}x" } # method call condition
134
+ ```
135
+
136
+ ### Infinite Loop with Break
137
+
138
+ ```rill
139
+ 0 -> (true) @ {
140
+ $ + 1 -> ($ > 5) ? break ! $
141
+ } # Result: 6
142
+ ```
143
+
144
+ ### Loop Limits
145
+
146
+ Use `^(limit: N)` annotation to set maximum iterations (default: 10,000):
147
+
148
+ ```rill
149
+ ^(limit: 100) 0 -> ($ < 10) @ { $ + 1 } # Runs 10 iterations, returns 10
150
+ ```
151
+
152
+ Exceeding the limit throws `RuntimeError` with code `RUNTIME_LIMIT_EXCEEDED`.
153
+
154
+ ---
155
+
156
+ ## Do-While Loop
157
+
158
+ Post-condition loop. Body executes first, then condition is checked. Use when you want at least one execution.
159
+
160
+ ### Syntax
161
+
162
+ ```text
163
+ initial -> @ { body } ? (condition)
164
+ ```
165
+
166
+ ### Basic Usage
167
+
168
+ ```rill
169
+ # Execute at least once, continue while condition holds
170
+ 0 -> @ { $ + 1 } ? ($ < 5) # Returns 5
171
+
172
+ # String accumulation
173
+ "" -> @ { "{$}x" } ? (.len < 3) # Returns "xxx"
174
+ ```
175
+
176
+ ### When to Use
177
+
178
+ - **While** `(condition) @ { body }`: condition checked BEFORE body (may execute 0 times)
179
+ - **Do-while** `@ { body } ? (condition)`: condition checked AFTER body (executes at least once)
180
+
181
+ ### Retry Pattern
182
+
183
+ Do-while is ideal for retry patterns:
184
+
185
+ ```text
186
+ ^(limit: 5) @ {
187
+ app::prompt("Perform operation")
188
+ } ? (.contains("RETRY"))
189
+ # Loop exits when result doesn't contain RETRY
190
+ ```
191
+
192
+ ### Loop Limit
193
+
194
+ ```rill
195
+ ^(limit: 100) 0 -> @ { $ + 1 } ? ($ < 10) # Returns 10
196
+ ```
197
+
198
+ ---
199
+
200
+ ## Break
201
+
202
+ Exit a loop early. Returns the value piped to `break`, or current `$` if bare.
203
+
204
+ ### Syntax
205
+
206
+ ```text
207
+ break # exit with current $
208
+ $value -> break # exit with value
209
+ ```
210
+
211
+ ### In Each Loop
212
+
213
+ ```rill
214
+ [1, 2, 3, 4, 5] -> each {
215
+ ($ > 3) ? ("found {$}" -> break)
216
+ $
217
+ }
218
+ # Returns "found 4"
219
+ ```
220
+
221
+ ### In While Loop
222
+
223
+ ```rill
224
+ 0 -> (true) @ {
225
+ ($ + 1) -> ($ > 3) ? break ! $
226
+ }
227
+ # Returns 4
228
+ ```
229
+
230
+ ### Break Value
231
+
232
+ In `each`, break returns partial results collected before the break:
233
+
234
+ ```rill
235
+ ["a", "b", "STOP", "c"] -> each {
236
+ ($ == "STOP") ? break
237
+ $
238
+ }
239
+ # Returns ["a", "b"] (partial results before break)
240
+ ```
241
+
242
+ ### Break Not Allowed
243
+
244
+ `break` is not supported in `map`, `filter`, or `fold` (parallel operations):
245
+
246
+ ```text
247
+ [1, 2, 3] -> map { break } # ERROR: break not supported in map
248
+ ```
249
+
250
+ ---
251
+
252
+ ## Return
253
+
254
+ Exit a block early. Returns the value piped to `return`, or current `$` if bare.
255
+
256
+ ### Syntax
257
+
258
+ ```text
259
+ return # exit with current $
260
+ $value -> return # exit with value
261
+ ```
262
+
263
+ ### In Blocks
264
+
265
+ ```rill
266
+ {
267
+ 5 :> $x
268
+ ($x > 3) ? ("big" -> return)
269
+ "small"
270
+ }
271
+ # Returns "big"
272
+ ```
273
+
274
+ ### Multi-Phase Pipeline
275
+
276
+ ```text
277
+ {
278
+ "content" :> $data
279
+ $data -> .contains("ERROR") ? ("Read failed" -> return)
280
+ "processed: {$data}"
281
+ }
282
+ # Returns "processed: content" or "Read failed"
283
+ ```
284
+
285
+ ---
286
+
287
+ ## Control Flow Summary
288
+
289
+ | Statement | Scope | Effect |
290
+ |-----------|-------|--------|
291
+ | `break` | Loop | Exit loop with current `$` |
292
+ | `$val -> break` | Loop | Exit loop with value |
293
+ | `return` | Block/Script | Exit block or script with current `$` |
294
+ | `$val -> return` | Block/Script | Exit block or script with value |
295
+
296
+ > **Note:** Script-level exit functions like `error()` or `stop()` must be provided by the host application. See [Host Integration](14_host-integration.md).
297
+
298
+ ---
299
+
300
+ ## Patterns
301
+
302
+ ### Guard Clauses
303
+
304
+ Exit early on invalid conditions (assumes host provides `error()`):
305
+
306
+ ```text
307
+ |data| {
308
+ $data -> .empty ? app::error("Empty input")
309
+ $data -> :?list ? $ ! app::error("Expected list")
310
+ $data -> each { $ * 2 }
311
+ } :> $process
312
+ ```
313
+
314
+ ### Retry with Limit
315
+
316
+ ```text
317
+ ^(limit: 3) @ {
318
+ app::prompt("Try operation")
319
+ } ? (.contains("RETRY"))
320
+
321
+ .contains("SUCCESS") ? [0, "Done"] ! [1, "Failed"]
322
+ ```
323
+
324
+ ### State Machine
325
+
326
+ ```rill
327
+ "start" -> ($ != "done") @ {
328
+ ($ == "start") ? "processing" ! ($ == "processing") ? "validating" ! ($ == "validating") ? "done" ! $
329
+ }
330
+ # Walks through states: start -> processing -> validating -> done
331
+ ```
332
+
333
+ ### Find First Match
334
+
335
+ ```rill
336
+ [1, 2, 3, 4, 5] -> each {
337
+ ($ > 3) ? ($ -> break)
338
+ $
339
+ }
340
+ # Returns 4 (first element > 3)
341
+ ```
342
+
343
+ ---
344
+
345
+ ## See Also
346
+
347
+ - [Variables](03_variables.md) — Scope rules and `$` binding
348
+ - [Collections](07_collections.md) — `each`, `map`, `filter`, `fold` iteration
349
+ - [Operators](04_operators.md) — Comparison and logical operators
350
+ - [Reference](11_reference.md) — Quick reference tables
@@ -0,0 +1,353 @@
1
+ # Closures
2
+
3
+ Closures are first-class values that capture their defining scope. This document covers closure semantics, binding behavior, and common patterns.
4
+
5
+ ## Closure Syntax
6
+
7
+ ```text
8
+ # No parameters (property-style)
9
+ || { body }
10
+ ||body # shorthand for simple expressions
11
+
12
+ # With parameters
13
+ |x| { body }
14
+ |x|body # shorthand
15
+ |x, y| { body } # multiple parameters
16
+ |x: string| { body } # typed parameter
17
+ |x: number = 10| { body } # default value (type inferred)
18
+ |x: string = "hi"| { body } # default with explicit type
19
+ ```
20
+
21
+ **Key distinction:**
22
+ - **Blocks `{ }`** execute immediately
23
+ - **Closures `||{ }` or `|params|{ }`** are stored for later invocation
24
+
25
+ ## Late Binding
26
+
27
+ Closures resolve captured variables at **call time**, not definition time. This enables recursive patterns and forward references.
28
+
29
+ ### Basic Example
30
+
31
+ ```rill
32
+ 10 :> $x
33
+ ||($x + 5) :> $fn
34
+ 20 :> $x
35
+ $fn() # 25 (sees current $x=20, not $x=10 at definition)
36
+ ```
37
+
38
+ ### Recursive Closures
39
+
40
+ ```rill
41
+ |n| { ($n < 1) ? 1 ! ($n * $factorial($n - 1)) } :> $factorial
42
+ $factorial(5) # 120
43
+ ```
44
+
45
+ The closure references `$factorial` before it exists. Late binding resolves `$factorial` when the closure executes.
46
+
47
+ ### Mutual Recursion
48
+
49
+ ```rill
50
+ |n| { ($n == 0) ? true ! $odd($n - 1) } :> $even
51
+ |n| { ($n == 0) ? false ! $even($n - 1) } :> $odd
52
+ $even(4) # true
53
+ ```
54
+
55
+ ### Forward References
56
+
57
+ ```rill
58
+ [
59
+ || { $helper(1) },
60
+ || { $helper(2) }
61
+ ] :> $handlers
62
+
63
+ |n| { $n * 10 } :> $helper # defined after closures
64
+
65
+ $handlers[0]() # 10
66
+ $handlers[1]() # 20
67
+ ```
68
+
69
+ ### Variable Mutation Visibility
70
+
71
+ Closures see the current value of captured variables:
72
+
73
+ ```rill
74
+ 0 :> $counter
75
+ || { $counter } :> $get
76
+ || { $counter + 1 } :> $getPlus1
77
+
78
+ 5 :> $counter
79
+
80
+ [$get(), $getPlus1()] # [5, 6]
81
+ ```
82
+
83
+ ## Dict-Bound Closures
84
+
85
+ Closures stored in dicts have `$` late-bound to the containing dict at invocation (like `this` in other languages).
86
+
87
+ ### Zero-Arg Closures Auto-Invoke
88
+
89
+ ```rill
90
+ [
91
+ name: "toolkit",
92
+ count: 3,
93
+ summary: || { "{$.name}: {$.count} items" }
94
+ ] :> $obj
95
+
96
+ $obj.summary # "toolkit: 3 items" (auto-invoked on access)
97
+ ```
98
+
99
+ ### Accessing Sibling Fields
100
+
101
+ ```rill
102
+ [
103
+ width: 10,
104
+ height: 5,
105
+ area: || { $.width * $.height }
106
+ ] :> $rect
107
+
108
+ $rect.area # 50
109
+ ```
110
+
111
+ ### Parameterized Dict Closures
112
+
113
+ ```rill
114
+ [
115
+ name: "tools",
116
+ greet: |x| { "{$.name} says: {$x}" }
117
+ ] :> $obj
118
+
119
+ $obj.greet("hello") # "tools says: hello"
120
+ ```
121
+
122
+ ### Reusable Closures Across Dicts
123
+
124
+ ```rill
125
+ || { "{$.name}: {$.count} items" } :> $describer
126
+
127
+ [name: "tools", count: 3, str: $describer] :> $obj1
128
+ [name: "actions", count: 5, str: $describer] :> $obj2
129
+
130
+ $obj1.str # "tools: 3 items"
131
+ $obj2.str # "actions: 5 items"
132
+ ```
133
+
134
+ ### Calling Sibling Methods
135
+
136
+ ```rill
137
+ [
138
+ double: |n| { $n * 2 },
139
+ quad: |n| { $.double($.double($n)) }
140
+ ] :> $math
141
+
142
+ $math.quad(3) # 12
143
+ ```
144
+
145
+ ## List-Stored Closures
146
+
147
+ Closures in lists maintain their defining scope. Invoke via bracket access:
148
+
149
+ ```rill
150
+ [
151
+ |x| { $x + 1 },
152
+ |x| { $x * 2 },
153
+ |x| { $x * $x }
154
+ ] :> $transforms
155
+
156
+ $transforms[0](5) # 6
157
+ $transforms[1](5) # 10
158
+ $transforms[2](5) # 25
159
+ ```
160
+
161
+ ### Chaining List Closures
162
+
163
+ ```rill
164
+ |n| { $n + 1 } :> $inc
165
+ |n| { $n * 2 } :> $double
166
+
167
+ 5 -> @[$inc, $double, $inc] # 13: (5+1)*2+1
168
+ ```
169
+
170
+ ## Inline Closures
171
+
172
+ Closures can appear inline in expressions:
173
+
174
+ ```rill
175
+ [1, 2, 3] -> map |x| { $x * 2 } # [2, 4, 6]
176
+
177
+ [1, 2, 3] -> filter |x| { $x > 1 } # [2, 3]
178
+
179
+ [1, 2, 3] -> fold(0) |acc, x| { $acc + $x } # 6
180
+ ```
181
+
182
+ ### Inline with Block Bodies
183
+
184
+ ```rill
185
+ [1, 2, 3] -> map |x| {
186
+ ($x * 10) :> $scaled
187
+ "{$x} -> {$scaled}"
188
+ }
189
+ # ["1 -> 10", "2 -> 20", "3 -> 30"]
190
+ ```
191
+
192
+ ## Nested Closures
193
+
194
+ Closures can contain closures. Each captures its defining scope:
195
+
196
+ ```rill
197
+ |n| { || { $n } } :> $makeGetter
198
+ $makeGetter(42)() # 42
199
+ ```
200
+
201
+ ### Closure Factory Pattern
202
+
203
+ ```rill
204
+ |multiplier| {
205
+ |x| { $x * $multiplier }
206
+ } :> $makeMultiplier
207
+
208
+ $makeMultiplier(3) :> $triple
209
+ $makeMultiplier(10) :> $tenX
210
+
211
+ $triple(5) # 15
212
+ $tenX(5) # 50
213
+ ```
214
+
215
+ ### Nested Late Binding
216
+
217
+ ```rill
218
+ 1 :> $x
219
+ || { || { $x } } :> $outer
220
+ 5 :> $x
221
+ $outer()() # 5 (inner closure sees updated $x)
222
+ ```
223
+
224
+ ## Parameter Shadowing
225
+
226
+ Closure parameters shadow captured variables of the same name:
227
+
228
+ ```rill
229
+ 100 :> $x
230
+ |x| { $x * 2 } :> $double
231
+ $double(5) # 10 (parameter $x=5 shadows captured $x=100)
232
+ ```
233
+
234
+ ## Scope Isolation
235
+
236
+ ### Loop Closures
237
+
238
+ Each loop iteration creates a new child scope. Capture variables explicitly to preserve per-iteration values:
239
+
240
+ ```rill
241
+ # Capture $ into named variable for each iteration
242
+ [1, 2, 3] -> each {
243
+ $ :> $item
244
+ || { $item }
245
+ } :> $closures
246
+
247
+ [$closures[0](), $closures[1](), $closures[2]()] # [1, 2, 3]
248
+ ```
249
+
250
+ **Note:** `$` (pipeValue) is a context property, not a variable. Use explicit capture for closure access.
251
+
252
+ ### Conditional Branch Closures
253
+
254
+ ```rill
255
+ 10 :> $x
256
+ true ? { || { $x } } ! { || { 0 } } :> $fn
257
+ 20 :> $x
258
+ $fn() # 20 (late binding sees updated $x)
259
+ ```
260
+
261
+ ## Invocation Patterns
262
+
263
+ ### Direct Call
264
+
265
+ ```rill
266
+ |x| { $x + 1 } :> $inc
267
+ $inc(5) # 6
268
+ ```
269
+
270
+ ### Pipe Call
271
+
272
+ ```rill
273
+ |x| { $x + 1 } :> $inc
274
+ 5 -> $inc() # 6
275
+ ```
276
+
277
+ ### Postfix Invocation
278
+
279
+ Call closures from bracket access or expressions:
280
+
281
+ ```rill
282
+ [|x| { $x * 2 }] :> $fns
283
+ $fns[0](5) # 10
284
+
285
+ || { |n| { $n * 2 } } :> $factory
286
+ $factory()(5) # 10 (chained invocation)
287
+ ```
288
+
289
+ ### Method Access After Bracket (Requires Grouping)
290
+
291
+ ```rill
292
+ ["hello", "world"] :> $list
293
+
294
+ # Use grouping to call method on bracket result
295
+ ($list[0]).upper # "HELLO"
296
+
297
+ # Or use pipe syntax
298
+ $list[0] -> .upper # "HELLO"
299
+ ```
300
+
301
+ Note: `$list[0].upper` parses `.upper` as field access on `$list`, not as a method call on the element. This throws an error since lists don't have an `upper` field.
302
+
303
+ ## Error Behavior
304
+
305
+ ### Undefined Variables
306
+
307
+ Undefined variables throw an error at call time (rill has no null):
308
+
309
+ ```text
310
+ || { $undefined } :> $fn
311
+ $fn() # Error: Undefined variable: $undefined
312
+ ```
313
+
314
+ ### Invoking Non-Callable
315
+
316
+ ```rill
317
+ [1, 2, 3] :> $list
318
+ $list[0]() # Error: Cannot invoke non-callable value (got number)
319
+ ```
320
+
321
+ ### Type Errors
322
+
323
+ ```rill
324
+ |x: string| { $x } :> $fn
325
+ $fn(42) # Error: Parameter type mismatch: x expects string, got number
326
+ ```
327
+
328
+ ## Implementation Notes
329
+
330
+ ### Scope Chain
331
+
332
+ Closures store a reference to their defining scope (`definingScope`). At invocation:
333
+ 1. A child context is created with `definingScope` as parent
334
+ 2. Parameters are bound in the child context
335
+ 3. Variable lookups traverse: local → definingScope → parent chain
336
+
337
+ ### Memory Considerations
338
+
339
+ - Closures hold references to their defining scope
340
+ - Scopes form a tree structure (no circular references)
341
+ - Scopes remain live while referenced by closures
342
+
343
+ ### Performance
344
+
345
+ - Variable lookup traverses the scope chain at each access
346
+ - No caching (ensures mutation visibility)
347
+ - Closure creation is lightweight (stores reference, not copy)
348
+
349
+ ## Related Documentation
350
+
351
+ - [Reference](11_reference.md) — Language specification
352
+ - [Collections](07_collections.md) — `each`, `map`, `filter`, `fold` with closures
353
+ - [Guide](01_guide.md) — Getting started tutorial