@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,399 @@
1
+ # rill Type System
2
+
3
+ *Value types, type assertions, and type checking*
4
+
5
+ ## Overview
6
+
7
+ rill is dynamically typed and type-safe. Types are checked at runtime, but type errors are always caught—there are no implicit conversions or coercions.
8
+
9
+ | Type | Syntax | Example |
10
+ |------|--------|---------|
11
+ | String | `"text"` | `"hello"` |
12
+ | Number | `123`, `0.5` | `42`, `0.9` |
13
+ | Bool | `true`, `false` | `true` |
14
+ | List | `[a, b]` | `["file.ts", 42]` |
15
+ | Dict | `[k: v]` | `[output: "text", code: 0]` |
16
+ | Tuple | `*[...]` | `*[1, 2]`, `*[x: 1, y: 2]` |
17
+ | Closure | `\|\|{ }` | `\|x\|($x * 2)` |
18
+
19
+ **Key principles:**
20
+ - **Type-safe**: No implicit coercion—`"5" + 1` errors, not `"51"` or `6`
21
+ - **Type-locked variables**: A variable that holds a string always holds a string
22
+ - **Value-based**: All copies are deep, all comparisons by value
23
+ - **No null/undefined**: Empty values are valid (`""`, `[]`, `[:]`), but "no value" cannot exist
24
+ - **No truthiness**: Conditions require actual booleans, not "truthy" values
25
+
26
+ ---
27
+
28
+ ## Strings
29
+
30
+ Double-quoted text with variable interpolation using `{$var}`:
31
+
32
+ ```text
33
+ "hello world"
34
+ "Process {$filename} for review"
35
+ "Result: {$response}"
36
+ ```
37
+
38
+ Escape sequences: `\n`, `\t`, `\\`, `\"`, `{{` (literal `{`), `}}` (literal `}`)
39
+
40
+ ### Interpolation
41
+
42
+ Any valid expression works inside `{...}`:
43
+
44
+ ```rill
45
+ "alice" :> $name
46
+ 3 :> $a
47
+ 5 :> $b
48
+ true :> $ok
49
+ "Hello, {$name}!" # Variable
50
+ "sum: {$a + $b}" # Arithmetic
51
+ "valid: {$a > 0}" # Comparison
52
+ "status: {$ok ? \"yes\" ! \"no\"}" # Conditional
53
+ "upper: {$name -> .upper}" # Method chain
54
+ ```
55
+
56
+ ### Heredocs
57
+
58
+ Multiline strings use heredoc syntax:
59
+
60
+ ```rill
61
+ prompt(<<EOF
62
+ Review this code:
63
+ {$code}
64
+
65
+ Check for security issues.
66
+ EOF
67
+ )
68
+ ```
69
+
70
+ The delimiter (e.g., `EOF`) must not appear on its own line within the content.
71
+
72
+ See [Strings](09_strings.md) for string methods.
73
+
74
+ ---
75
+
76
+ ## Numbers
77
+
78
+ Used for arithmetic, exit codes, and loop limits:
79
+
80
+ ```rill
81
+ 42
82
+ 0
83
+ 3.14159
84
+ -7
85
+ ```
86
+
87
+ **Arithmetic operators:** `+`, `-`, `*`, `/`, `%` (modulo)
88
+
89
+ **Type constraint:** All arithmetic operands must be numbers. No implicit conversion:
90
+
91
+ ```rill
92
+ 5 + 3 # 8
93
+ "5" + 1 # ERROR: Arithmetic requires number, got string
94
+ ```
95
+
96
+ ---
97
+
98
+ ## Booleans
99
+
100
+ Literal `true` and `false`. Conditional expressions (`?`), loop conditions (`@`), and filter predicates require boolean values. Non-boolean values cause runtime errors.
101
+
102
+ ```rill
103
+ true ? "yes" ! "no" # "yes"
104
+ false ? "yes" ! "no" # "no"
105
+ ```
106
+
107
+ **No truthiness:** rill has no automatic boolean coercion. Empty strings, zero, and empty lists are not "falsy"—you must explicitly check:
108
+
109
+ ```rill
110
+ "" -> .empty ? "empty" ! "has content" # Use .empty method
111
+ 0 -> ($ == 0) ? "zero" ! "nonzero" # Use comparison
112
+ ```
113
+
114
+ ---
115
+
116
+ ## Lists
117
+
118
+ Ordered sequences of values:
119
+
120
+ ```rill
121
+ [1, 2, 3] :> $nums
122
+ $nums[0] # 1
123
+ $nums[-1] # 3 (last element)
124
+ $nums -> .len # 3
125
+ ```
126
+
127
+ **Access methods:**
128
+ - `[0]`, `[1]` — Index access (0-based)
129
+ - `[-1]`, `[-2]` — Negative index (from end)
130
+ - `.head` — First element (errors on empty)
131
+ - `.tail` — Last element (errors on empty)
132
+ - `.at(n)` — Element at index
133
+
134
+ **Out-of-bounds access** throws an error:
135
+
136
+ ```text
137
+ [] -> .at(0) # Error: List index out of bounds
138
+ ["a"] -> .at(5) # Error: List index out of bounds
139
+ ```
140
+
141
+ Use `??` for safe access with default:
142
+
143
+ ```rill
144
+ ["a"] :> $list
145
+ $list[0] ?? "default" # "a"
146
+ ```
147
+
148
+ See [Collections](07_collections.md) for iteration operators.
149
+
150
+ ---
151
+
152
+ ## Dicts
153
+
154
+ Key-value mappings with string keys:
155
+
156
+ ```rill
157
+ [name: "alice", age: 30] :> $person
158
+ $person.name # "alice"
159
+ $person.age # 30
160
+ ```
161
+
162
+ **Access patterns:**
163
+ - `.field` — Literal field access
164
+ - `.$key` — Variable as key
165
+ - `.($i + 1)` — Computed expression as key
166
+ - `.(a || b)` — Alternatives (try keys left-to-right)
167
+ - `.field ?? default` — Default value if missing
168
+ - `.?field` — Existence check (returns bool)
169
+ - `.?field&type` — Existence + type check
170
+
171
+ **Missing key access** throws an error. Use `??` for safe access:
172
+
173
+ ```rill
174
+ [:] :> $d
175
+ $d.missing ?? "" # "" (safe default)
176
+ ```
177
+
178
+ ### Dict Methods
179
+
180
+ | Method | Description |
181
+ |--------|-------------|
182
+ | `.keys` | All keys as list |
183
+ | `.values` | All values as list |
184
+ | `.entries` | List of `[key, value]` pairs |
185
+
186
+ ```rill
187
+ [name: "test", count: 42] -> .keys # ["count", "name"]
188
+ [name: "test", count: 42] -> .values # [42, "test"]
189
+ [a: 1, b: 2] -> .entries # [["a", 1], ["b", 2]]
190
+ ```
191
+
192
+ **Reserved methods** (`keys`, `values`, `entries`) cannot be used as dict keys.
193
+
194
+ ### Dict Closures
195
+
196
+ Closures in dicts have `$` late-bound to the containing dict. See [Closures](06_closures.md) for details.
197
+
198
+ ```rill
199
+ [
200
+ name: "toolkit",
201
+ count: 3,
202
+ str: ||"{$.name}: {$.count} items"
203
+ ] :> $obj
204
+
205
+ $obj.str # "toolkit: 3 items" (auto-invoked)
206
+ ```
207
+
208
+ ---
209
+
210
+ ## Tuples
211
+
212
+ Tuples package values for explicit argument unpacking at closure invocation. Created with the `*` spread operator:
213
+
214
+ ```rill
215
+ # From list (positional)
216
+ *[1, 2, 3] :> $t # tuple with positional values
217
+
218
+ # From dict (named)
219
+ *[x: 1, y: 2] :> $t # tuple with named values
220
+
221
+ # Via pipe target
222
+ [1, 2, 3] -> * :> $t # convert list to tuple
223
+ ```
224
+
225
+ ### Using Tuples at Invocation
226
+
227
+ ```rill
228
+ |a, b, c| { "{$a}-{$b}-{$c}" } :> $fmt
229
+
230
+ # Positional unpacking
231
+ *[1, 2, 3] -> $fmt() # "1-2-3"
232
+
233
+ # Named unpacking (order doesn't matter)
234
+ *[c: 3, a: 1, b: 2] -> $fmt() # "1-2-3"
235
+ ```
236
+
237
+ ### Strict Validation
238
+
239
+ When invoking with tuples, missing required parameters error, and extra arguments error:
240
+
241
+ ```rill
242
+ |x, y|($x + $y) :> $fn
243
+ *[1] -> $fn() # Error: missing argument 'y'
244
+ *[1, 2, 3] -> $fn() # Error: extra positional argument
245
+ *[x: 1, z: 3] -> $fn() # Error: unknown argument 'z'
246
+ ```
247
+
248
+ ### Parameter Defaults with Tuples
249
+
250
+ ```rill
251
+ |x, y = 10, z = 20|($x + $y + $z) :> $fn
252
+ *[5] -> $fn() # 35 (5 + 10 + 20)
253
+ *[x: 5, z: 30] -> $fn() # 45 (5 + 10 + 30)
254
+ ```
255
+
256
+ ### Auto-Unpacking with Parallel Spread
257
+
258
+ When a closure is invoked with a single tuple argument, the tuple auto-unpacks:
259
+
260
+ ```rill
261
+ # List of tuples with multi-arg closure
262
+ [*[1,2], *[3,4]] -> map |x,y|($x * $y) # [2, 12]
263
+
264
+ # Named tuples work too
265
+ [*[x:1, y:2], *[x:3, y:4]] -> map |x,y|($x + $y) # [3, 7]
266
+ ```
267
+
268
+ ---
269
+
270
+ ## Type Assertions
271
+
272
+ Use type assertions to validate values at runtime.
273
+
274
+ ### Assert Type (`:type`)
275
+
276
+ Error if type doesn't match, returns value unchanged:
277
+
278
+ ```rill
279
+ # Postfix form (binds tighter than method calls)
280
+ 42:number # passes, returns 42
281
+ (1 + 2):number # passes, returns 3
282
+ 42:number.str # "42" - assertion then method
283
+
284
+ # Pipe target form
285
+ "hello" -> :string # passes, returns "hello"
286
+ "hello" -> :number # ERROR: expected number, got string
287
+ $val -> :dict -> .keys # assert dict, then get keys
288
+ ```
289
+
290
+ ### Check Type (`:?type`)
291
+
292
+ Returns boolean, no error:
293
+
294
+ ```rill
295
+ # Postfix form
296
+ 42:?number # true
297
+ "hello":?number # false
298
+
299
+ # Pipe target form
300
+ "hello" -> :?string # true
301
+ ```
302
+
303
+ Type checks work in conditionals:
304
+
305
+ ```text
306
+ $val -> :?list ? process() ! skip() # branch on type
307
+ ```
308
+
309
+ **Supported types:** `string`, `number`, `bool`, `closure`, `list`, `dict`, `tuple`
310
+
311
+ ### In Pipe Chains
312
+
313
+ ```rill
314
+ # Assert type and continue processing
315
+ [1, 2, 3] -> :list -> each { $ * 2 }
316
+
317
+ # Multiple assertions in chain
318
+ "test" -> :string -> .len -> :number # 4
319
+ ```
320
+
321
+ ### Use Cases
322
+
323
+ ```rill
324
+ # Validate function input
325
+ |data| {
326
+ $data -> :list # assert input is list
327
+ $data -> each { $ * 2 }
328
+ } :> $process_items
329
+
330
+ # Type-safe branching
331
+ |val| {
332
+ $val -> :?number ? ($val * 2) ! ($val -> .len)
333
+ } :> $process
334
+ $process(5) # 10
335
+ $process("hello") # 5
336
+ ```
337
+
338
+ ---
339
+
340
+ ## Type-Locked Variables
341
+
342
+ Variables lock type on first assignment. The type is inferred from the value or declared explicitly:
343
+
344
+ ```rill
345
+ "hello" :> $name # implicit: locked as string
346
+ "world" :> $name # OK: same type
347
+ 5 :> $name # ERROR: cannot assign number to string
348
+
349
+ "hello" :> $name:string # explicit: declare and lock as string
350
+ 42 :> $count:number # explicit: declare and lock as number
351
+ ```
352
+
353
+ ### Inline Capture with Type
354
+
355
+ ```rill
356
+ "hello" :> $x:string -> .len # type annotation in mid-chain
357
+ ```
358
+
359
+ Type annotations validate on assignment and prevent accidental type changes:
360
+
361
+ ```rill
362
+ |x|$x :> $fn # locked as closure
363
+ "text" :> $fn # ERROR: cannot assign string to closure
364
+ ```
365
+
366
+ ---
367
+
368
+ ## Global Type Functions
369
+
370
+ | Function | Description |
371
+ |----------|-------------|
372
+ | `type` | Returns type name as string |
373
+ | `json` | Convert to JSON string |
374
+
375
+ ```rill
376
+ 42 -> type # "number"
377
+ "hello" -> type # "string"
378
+ [1, 2] -> type # "list"
379
+ *[1, 2] -> type # "tuple"
380
+ [a: 1] -> type # "dict"
381
+ ||{ $ } -> type # "closure"
382
+
383
+ [a: 1, b: 2] -> json # '{"a":1,"b":2}'
384
+ ```
385
+
386
+ **`json` closure handling:**
387
+ - Direct closure → error: `|x|{ $x } -> json` throws "Cannot serialize closure to JSON"
388
+ - Closures in dicts → skipped: `[a: 1, fn: ||{ 0 }] -> json` returns `'{"a":1}'`
389
+ - Closures in lists → skipped: `[1, ||{ 0 }, 2] -> json` returns `'[1,2]'`
390
+
391
+ ---
392
+
393
+ ## See Also
394
+
395
+ - [Variables](03_variables.md) — Declaration, scope, `$` binding
396
+ - [Closures](06_closures.md) — Closure semantics and patterns
397
+ - [Collections](07_collections.md) — List iteration operators
398
+ - [Strings](09_strings.md) — String methods reference
399
+ - [Reference](11_reference.md) — Quick reference tables
@@ -0,0 +1,314 @@
1
+ # rill Variables and Scope
2
+
3
+ *Variable declaration, type locking, and scope rules*
4
+
5
+ ## Overview
6
+
7
+ rill uses capture (`:>`) instead of assignment. Variables are type-locked after first assignment and follow strict scoping rules.
8
+
9
+ **Key principles:**
10
+ - **Capture, not assign**: Use `:>` to capture values into variables
11
+ - **Type-locked**: Variables lock type on first assignment
12
+ - **No shadowing**: Cannot redeclare a variable name from outer scope
13
+ - **No leakage**: Variables created inside blocks don't exist outside
14
+
15
+ ---
16
+
17
+ ## Variable Declaration
18
+
19
+ Variables are declared via capture (`:>`), not assignment:
20
+
21
+ ```rill
22
+ "hello" :> $greeting
23
+ 42 :> $count
24
+ [1, 2, 3] :> $items
25
+ ```
26
+
27
+ ### Capture and Continue
28
+
29
+ The `:>` operator captures the value AND continues the chain:
30
+
31
+ ```rill
32
+ "hello"
33
+ :> $greeting # capture "hello" into $greeting
34
+ -> "{$} world" # $ is still "hello"
35
+ :> $message # capture "hello world" into $message
36
+ -> .upper # result: "HELLO WORLD"
37
+ ```
38
+
39
+ ### Terminal Capture
40
+
41
+ Capture at end of expression stores and ends the chain:
42
+
43
+ ```rill
44
+ "hello" :> $result # capture and end chain (result: "hello")
45
+ ```
46
+
47
+ ---
48
+
49
+ ## The Pipe Variable `$`
50
+
51
+ `$` holds the current piped value in the current scope:
52
+
53
+ ```rill
54
+ "test value" -> {
55
+ .upper -> log # $ is "test value", logs "TEST VALUE"
56
+ }
57
+ ```
58
+
59
+ ### `$` Binding by Context
60
+
61
+ | Context | `$` contains |
62
+ |---------|--------------|
63
+ | Inline block `-> { }` | Piped value |
64
+ | Each loop `-> each { }` | Current iteration item |
65
+ | While-loop `(cond) @ { }` | Accumulated value |
66
+ | Do-while `@ { } ? cond` | Accumulated value |
67
+ | Conditional `cond ? { }` | Tested value |
68
+ | Piped conditional `-> ? { }` | Piped value (also used as condition) |
69
+ | Stored closure `\|x\|{ }` | N/A — use explicit params |
70
+ | Dict closure `\|\|{ $.x }` | Dict self (`this`) — late-bound |
71
+
72
+ ### Implied `$`
73
+
74
+ When certain constructs appear without explicit input, `$` is used implicitly:
75
+
76
+ | Written | Equivalent to | Context |
77
+ |---------|---------------|---------|
78
+ | `? { }` | `$ -> ? { }` | Piped conditional ($ as condition) |
79
+ | `.method()` | `$ -> .method()` | Method call without receiver |
80
+ | `$fn()` | `$fn($)` | Closure call with no explicit args* |
81
+
82
+ *Closure calls receive `$` only when: no explicit args, first param has no default, and `$` is not a closure.
83
+
84
+ ```rill
85
+ # Inside blocks, $ flows naturally
86
+ "test value" -> {
87
+ .upper -> log # $ is "test value"
88
+ }
89
+
90
+ # In each loops, $ is the current item
91
+ |x| { $x * 2 } :> $double
92
+ [1, 2, 3] -> each { $double() } # $double receives 1, 2, 3
93
+ ```
94
+
95
+ **When implied `$` does NOT apply:**
96
+
97
+ ```rill
98
+ # Explicit args override implied $
99
+ |x| { $x } :> $fn
100
+ $fn("explicit") # uses "explicit", not $
101
+
102
+ # Params with defaults use the default
103
+ |x: string = "default"| { $x } :> $fn2
104
+ $fn2() # uses "default", not $
105
+ ```
106
+
107
+ ---
108
+
109
+ ## Type-Locked Variables
110
+
111
+ Variables lock type after first assignment:
112
+
113
+ ```rill
114
+ "hello" :> $name # locked as string
115
+ "world" :> $name # OK: same type
116
+ ```
117
+
118
+ ```text
119
+ 5 :> $name # ERROR: cannot assign number to string
120
+ ```
121
+
122
+ ### Explicit Type Annotations
123
+
124
+ Declare type explicitly with `:type`:
125
+
126
+ ```rill
127
+ "hello" :> $name:string # declare and lock as string
128
+ 42 :> $count:number # declare and lock as number
129
+ ```
130
+
131
+ **Supported types:** `string`, `number`, `bool`, `closure`, `list`, `dict`, `tuple`
132
+
133
+ ### Inline Capture with Type
134
+
135
+ ```rill
136
+ "hello" :> $x:string -> .len # type annotation in mid-chain
137
+ ```
138
+
139
+ Type annotations validate on assignment and prevent accidental type changes:
140
+
141
+ ```rill
142
+ |x|$x :> $fn:closure # locked as closure
143
+ ```
144
+
145
+ ```text
146
+ "text" :> $fn # ERROR: cannot assign string to closure
147
+ ```
148
+
149
+ ---
150
+
151
+ ## Scope Rules
152
+
153
+ Blocks, loops, conditionals, and grouped expressions create child scopes.
154
+
155
+ ### Three Rules
156
+
157
+ 1. **Read from parent:** Variables from outer scopes are accessible (read-only)
158
+ 2. **No shadowing:** Cannot assign to a variable name that exists in an outer scope
159
+ 3. **No leakage:** Variables created inside don't exist outside
160
+
161
+ ```rill
162
+ "context" :> $ctx
163
+
164
+ "check" -> .contains("c") ? {
165
+ "process with {$ctx}" -> log # OK: read outer variable
166
+ "local" :> $temp # OK: new local variable
167
+ }
168
+ # $temp not accessible here
169
+ ```
170
+
171
+ ```text
172
+ "context" :> $ctx
173
+ "check" -> .contains("c") ? {
174
+ "new" :> $ctx # ERROR: cannot shadow outer $ctx
175
+ }
176
+ ```
177
+
178
+ ### While Loops and `$`
179
+
180
+ While loops use `$` as the accumulator since named variables in the body don't persist across iterations:
181
+
182
+ ```rill
183
+ # Use $ as accumulator (body result becomes next iteration's $)
184
+ 0 -> ($ < 5) @ { $ + 1 } # Result: 5
185
+
186
+ # Variables inside loop body are local to each iteration
187
+ 0 -> ($ < 3) @ {
188
+ ($ * 10) :> $temp # $temp exists only in this iteration
189
+ $ + 1
190
+ }
191
+ # $temp not accessible here
192
+ ```
193
+
194
+ ### Reading Outer Variables
195
+
196
+ ```rill
197
+ 10 :> $x
198
+ [1, 2, 3] -> each {
199
+ $x + $ # Reads outer $x = 10
200
+ }
201
+ # Result: [11, 12, 13]
202
+ ```
203
+
204
+ ---
205
+
206
+ ## Special Variables
207
+
208
+ | Variable | Contains | Source |
209
+ |----------|----------|--------|
210
+ | `$` | Piped value (current block scope) | Grammar |
211
+ | `$ARGS` | CLI positional args (list) | Runtime |
212
+ | `$ENV.NAME` | Environment variable | Runtime |
213
+ | `$name` | Named variable | Runtime |
214
+
215
+ `$` is a grammar-level construct. All other variables are runtime-provided with the same scoping rules as user-defined variables.
216
+
217
+ ### `$ARGS`
218
+
219
+ Access CLI positional arguments:
220
+
221
+ ```text
222
+ $ARGS[0] # first argument
223
+ $ARGS[1] # second argument
224
+ $ARGS -> each { log($) } # iterate all arguments
225
+ ```
226
+
227
+ ### `$ENV`
228
+
229
+ Access environment variables:
230
+
231
+ ```text
232
+ $ENV.HOME # /home/user
233
+ $ENV.PATH # /usr/bin:...
234
+ $ENV.DEPLOY_ENV ?? "dev" # with default
235
+ ```
236
+
237
+ ### Runtime-Provided Variables
238
+
239
+ Named variables like `$file` or `$config` are provided by the host runtime. rill treats them as any other variable in the outer scope:
240
+
241
+ ```text
242
+ ---
243
+ args: file: string, retries: number = 3
244
+ ---
245
+
246
+ # $file and $retries available because host parsed frontmatter
247
+ process($file, $retries)
248
+ ```
249
+
250
+ ---
251
+
252
+ ## Inline Capture Pattern
253
+
254
+ Captures can appear mid-chain for debugging or later reference. Semantically, `:> $a ->` stores the value and returns it unchanged (like `log`):
255
+
256
+ ```rill
257
+ "analyze this" :> $result -> .upper -> .len
258
+ # $result is "analyze this", final result is 12
259
+ ```
260
+
261
+ The value flows: `"analyze this"` → stored in `$result` → uppercased → length.
262
+
263
+ ### Debugging Pattern
264
+
265
+ ```rill
266
+ "test" :> $input -> log -> .upper :> $output -> log
267
+ # logs "test", then logs "TEST"
268
+ # $input is "test", $output is "TEST"
269
+ ```
270
+
271
+ ---
272
+
273
+ ## Common Patterns
274
+
275
+ ### Capture for Reuse
276
+
277
+ Capture when you need the value in multiple places:
278
+
279
+ ```rill
280
+ "hello" :> $greeting
281
+ "{$greeting} world" :> $message
282
+ "{$greeting} there" :> $alt
283
+ ```
284
+
285
+ ### Let Data Flow
286
+
287
+ Prefer implied `$` when the value flows directly to the next statement:
288
+
289
+ ```text
290
+ # Verbose — unnecessary capture
291
+ app::prompt("check status") :> $status
292
+ $status -> .empty ? app::error("No status")
293
+
294
+ # Idiomatic — data flows naturally
295
+ app::prompt("check status")
296
+ .empty ? app::error("No status")
297
+ ```
298
+
299
+ ### Accumulation in Loops
300
+
301
+ Use `$` for accumulation in while loops:
302
+
303
+ ```rill
304
+ "" -> (.len < 5) @ { "{$}x" } # "xxxxx"
305
+ ```
306
+
307
+ ---
308
+
309
+ ## See Also
310
+
311
+ - [Types](02_types.md) — Type system and type assertions
312
+ - [Control Flow](05_control-flow.md) — Conditionals and loops
313
+ - [Closures](06_closures.md) — Closure scope and late binding
314
+ - [Reference](11_reference.md) — Quick reference tables