@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,330 @@
1
+ # rill Iterators
2
+
3
+ *Lazy sequence generation with the iterator protocol*
4
+
5
+ ## Overview
6
+
7
+ Iterators provide lazy sequence generation in rill. They produce values on demand rather than materializing entire collections upfront.
8
+
9
+ **Built-in iterators:**
10
+
11
+ | Function | Description |
12
+ |----------|-------------|
13
+ | `range(start, end, step?)` | Generate number sequence |
14
+ | `repeat(value, count)` | Repeat value n times |
15
+ | `.first()` | Get iterator for any collection |
16
+
17
+ **Key characteristics:**
18
+
19
+ - Value-based: `.next()` returns a new iterator, original unchanged
20
+ - Lazy: Elements generated on demand
21
+ - Composable: Work with all collection operators (`each`, `map`, `filter`, `fold`)
22
+
23
+ ```rill
24
+ range(0, 5) -> each { $ * 2 } # [0, 2, 4, 6, 8]
25
+ repeat("x", 3) -> each { $ } # ["x", "x", "x"]
26
+ [1, 2, 3] -> .first() -> each { $ } # [1, 2, 3]
27
+ ```
28
+
29
+ ---
30
+
31
+ ## Iterator Protocol
32
+
33
+ Iterators are dicts with three fields:
34
+
35
+ | Field | Type | Description |
36
+ |-------|------|-------------|
37
+ | `value` | any | Current element (absent when done) |
38
+ | `done` | bool | True if exhausted |
39
+ | `next` | closure | Returns new iterator at next position |
40
+
41
+ ```rill
42
+ # Iterator structure
43
+ [
44
+ value: 0,
45
+ done: false,
46
+ next: || { ... } # returns new iterator
47
+ ]
48
+ ```
49
+
50
+ Collection operators automatically recognize and expand iterators:
51
+
52
+ ```rill
53
+ range(1, 4) -> map { $ * 10 } # [10, 20, 30]
54
+ range(0, 10) -> filter { $ > 5 } # [6, 7, 8, 9]
55
+ range(1, 6) -> fold(0) { $@ + $ } # 15
56
+ ```
57
+
58
+ ---
59
+
60
+ ## Built-in Iterators
61
+
62
+ ### `range(start, end, step?)`
63
+
64
+ Generate a sequence of numbers from `start` (inclusive) to `end` (exclusive).
65
+
66
+ | Parameter | Type | Default | Description |
67
+ |-----------|------|---------|-------------|
68
+ | `start` | number | required | First value |
69
+ | `end` | number | required | Stop value (exclusive) |
70
+ | `step` | number | 1 | Increment (can be negative) |
71
+
72
+ ```rill
73
+ range(0, 5) # 0, 1, 2, 3, 4
74
+ range(1, 6) # 1, 2, 3, 4, 5
75
+ range(0, 10, 2) # 0, 2, 4, 6, 8
76
+ range(5, 0, -1) # 5, 4, 3, 2, 1
77
+ range(-3, 2) # -3, -2, -1, 0, 1
78
+ range(0, 1, 0.25) # 0, 0.25, 0.5, 0.75
79
+ ```
80
+
81
+ **Edge cases:**
82
+
83
+ ```rill
84
+ range(5, 5) # empty (start == end)
85
+ range(5, 3) # empty (start > end with positive step)
86
+ range(0, 5, -1) # empty (wrong direction)
87
+ range(0, 5, 0) # ERROR: step cannot be zero
88
+ ```
89
+
90
+ ### `repeat(value, count)`
91
+
92
+ Generate a value repeated n times.
93
+
94
+ | Parameter | Type | Description |
95
+ |-----------|------|-------------|
96
+ | `value` | any | Value to repeat |
97
+ | `count` | number | Number of repetitions |
98
+
99
+ ```rill
100
+ repeat("x", 3) # "x", "x", "x"
101
+ repeat(0, 5) # 0, 0, 0, 0, 0
102
+ repeat([a: 1], 2) # [a: 1], [a: 1]
103
+ ```
104
+
105
+ **Edge cases:**
106
+
107
+ ```rill
108
+ repeat("x", 0) # empty
109
+ repeat("x", -1) # ERROR: count cannot be negative
110
+ ```
111
+
112
+ ---
113
+
114
+ ## The `.first()` Method
115
+
116
+ Returns an iterator for any collection. Provides a consistent interface for manual iteration.
117
+
118
+ | Input Type | `.first()` Returns |
119
+ |------------|-------------------|
120
+ | list | Iterator over elements |
121
+ | string | Iterator over characters |
122
+ | dict | Iterator over `[key: k, value: v]` entries |
123
+ | iterator | Returns itself (identity) |
124
+
125
+ ```rill
126
+ [1, 2, 3] -> .first() # iterator at 1
127
+ "abc" -> .first() # iterator at "a"
128
+ [a: 1, b: 2] -> .first() # iterator at [key: "a", value: 1]
129
+ range(0, 5) -> .first() # iterator at 0 (identity)
130
+ ```
131
+
132
+ **Empty collections** return a done iterator:
133
+
134
+ ```rill
135
+ [] -> .first() # [done: true, next: ...]
136
+ "" -> .first() # [done: true, next: ...]
137
+ ```
138
+
139
+ **Using `.first()` with collection operators:**
140
+
141
+ ```rill
142
+ [1, 2, 3] -> .first() -> each { $ * 2 } # [2, 4, 6]
143
+ "hello" -> .first() -> each { $ } # ["h", "e", "l", "l", "o"]
144
+ ```
145
+
146
+ ---
147
+
148
+ ## Manual Iteration
149
+
150
+ Traverse an iterator by accessing `.value`, `.done`, and calling `.next()`:
151
+
152
+ ```rill
153
+ [1, 2, 3] -> .first() :> $it
154
+
155
+ # Check if done
156
+ $it.done # false
157
+
158
+ # Get current value
159
+ $it.value # 1
160
+
161
+ # Advance to next position
162
+ $it.next() :> $it
163
+ $it.value # 2
164
+ ```
165
+
166
+ **Loop pattern (using $ as accumulator):**
167
+
168
+ ```text
169
+ "hello" -> .first() -> !$.done @ {
170
+ $.value -> log
171
+ $.next()
172
+ }
173
+ # logs: h, e, l, l, o
174
+ ```
175
+
176
+ **Preferred: use `each` for iteration:**
177
+
178
+ ```rill
179
+ "hello" -> each { log($) }
180
+ # logs: h, e, l, l, o
181
+ ```
182
+
183
+ **Check before access:**
184
+
185
+ ```rill
186
+ $list -> .first() :> $it
187
+ $it.done ? "empty" ! $it.value
188
+ ```
189
+
190
+ ---
191
+
192
+ ## Custom Iterators
193
+
194
+ Create custom iterators by implementing the protocol:
195
+
196
+ ```rill
197
+ # Counter from start to max
198
+ |start, max| [
199
+ value: $start,
200
+ done: ($start > $max),
201
+ next: || { $counter($.value + 1, $max) }
202
+ ] :> $counter
203
+
204
+ $counter(1, 5) -> each { $ } # [1, 2, 3, 4, 5]
205
+ ```
206
+
207
+ **Fibonacci sequence:**
208
+
209
+ ```text
210
+ |a, b, max| [
211
+ value: $a,
212
+ done: ($a > $max),
213
+ next: || { $fib($.b, $.a + $.b, $max) }
214
+ ] :> $fib
215
+
216
+ $fib(0, 1, 50) -> each { $ } # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
217
+ ```
218
+
219
+ **Infinite iterator (use with limit):**
220
+
221
+ ```text
222
+ |n| [
223
+ value: $n,
224
+ done: false,
225
+ next: || { $naturals($.value + 1) }
226
+ ] :> $naturals
227
+
228
+ # Take first 5 using fold with compound accumulator
229
+ $naturals(1) -> .first() -> fold([list: [], it: $]) {
230
+ ($@.list -> .len >= 5) ? $@ -> break ! [
231
+ list: [...$@.list, $@.it.value],
232
+ it: $@.it.next()
233
+ ]
234
+ } -> $.list # [1, 2, 3, 4, 5]
235
+ ```
236
+
237
+ ---
238
+
239
+ ## Element Access: `.head` and `.tail`
240
+
241
+ For direct element access (not iteration), use `.head` and `.tail`:
242
+
243
+ | Method | Description |
244
+ |--------|-------------|
245
+ | `.head` | First element (errors on empty) |
246
+ | `.tail` | Last element (errors on empty) |
247
+
248
+ ```rill
249
+ [1, 2, 3] -> .head # 1
250
+ [1, 2, 3] -> .tail # 3
251
+ "hello" -> .head # "h"
252
+ "hello" -> .tail # "o"
253
+ ```
254
+
255
+ **Empty collections error** (no null in rill):
256
+
257
+ ```rill
258
+ [] -> .head # ERROR: Cannot get head of empty list
259
+ "" -> .tail # ERROR: Cannot get tail of empty string
260
+ ```
261
+
262
+ **Comparison with `.first()`:**
263
+
264
+ | Method | Returns | On Empty |
265
+ |--------|---------|----------|
266
+ | `.head` | Element directly | Error |
267
+ | `.first()` | Iterator | Done iterator |
268
+
269
+ ---
270
+
271
+ ## Examples
272
+
273
+ ### Sum of squares
274
+
275
+ ```rill
276
+ range(1, 11) -> map { $ * $ } -> fold(0) { $@ + $ }
277
+ # 385 (1 + 4 + 9 + ... + 100)
278
+ ```
279
+
280
+ ### Generate index markers
281
+
282
+ ```rill
283
+ range(0, 5) -> each { "Item {$}" }
284
+ # ["Item 0", "Item 1", "Item 2", "Item 3", "Item 4"]
285
+ ```
286
+
287
+ ### Retry pattern
288
+
289
+ ```text
290
+ repeat(1, 3) -> each {
291
+ attempt() :> $result
292
+ ($result.success == true) ? ($result -> break)
293
+ pause("00:00:01")
294
+ $result
295
+ }
296
+ ```
297
+
298
+ ### Filter even numbers
299
+
300
+ ```rill
301
+ range(0, 20) -> filter { ($ % 2) == 0 }
302
+ # [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
303
+ ```
304
+
305
+ ### Nested iteration
306
+
307
+ ```rill
308
+ range(1, 4) -> each { $ :> $row -> range(1, 4) -> each { $row * $ } }
309
+ # [[1, 2, 3], [2, 4, 6], [3, 6, 9]]
310
+ ```
311
+
312
+ ---
313
+
314
+ ## Limits
315
+
316
+ Iterators are expanded eagerly when passed to collection operators. A default limit of 10000 elements prevents infinite loops:
317
+
318
+ ```rill
319
+ # This would error after 10000 elements
320
+ |n| [value: $n, done: false, next: || { $inf($.value + 1) }] :> $inf
321
+ $inf(0) -> each { $ } # ERROR: Iterator exceeded 10000 elements
322
+ ```
323
+
324
+ ---
325
+
326
+ ## See Also
327
+
328
+ - [Collections](07_collections.md) — `each`, `map`, `filter`, `fold` operators
329
+ - [Closures](06_closures.md) — Closure semantics for custom iterators
330
+ - [Reference](11_reference.md) — Complete language specification
@@ -0,0 +1,205 @@
1
+ # rill String Methods Reference
2
+
3
+ String methods for text manipulation, pattern matching, and formatting.
4
+
5
+ ## Case Conversion
6
+
7
+ | Method | Signature | Description |
8
+ |----------|-------------|-----------------------|
9
+ | `.lower` | `() -> string` | Convert to lowercase |
10
+ | `.upper` | `() -> string` | Convert to uppercase |
11
+
12
+ ```rill
13
+ "Hello World" -> .lower # "hello world"
14
+ "Hello World" -> .upper # "HELLO WORLD"
15
+ ```
16
+
17
+ ## Prefix and Suffix
18
+
19
+ | Method | Signature | Description |
20
+ |----------------|------------------------|--------------------------------|
21
+ | `.starts_with` | `(prefix: string) -> bool` | True if string starts with prefix |
22
+ | `.ends_with` | `(suffix: string) -> bool` | True if string ends with suffix |
23
+
24
+ ```rill
25
+ "hello" -> .starts_with("he") # true
26
+ "file.txt" -> .ends_with(".txt") # true
27
+ "Hello" -> .starts_with("hello") # false (case sensitive)
28
+ ```
29
+
30
+ ## Search and Position
31
+
32
+ | Method | Signature | Description |
33
+ |-------------|------------------------|-------------------------------------|
34
+ | `.contains` | `(substr: string) -> bool` | True if string contains substring |
35
+ | `.index_of` | `(substr: string) -> number` | Position of first match (-1 if none) |
36
+
37
+ ```rill
38
+ "hello world" -> .contains("world") # true
39
+ "hello world" -> .index_of("o") # 4
40
+ "hello" -> .index_of("x") # -1
41
+ ```
42
+
43
+ ## Pattern Matching
44
+
45
+ | Method | Signature | Description |
46
+ |-------------|------------------------|----------------------------------|
47
+ | `.match` | `(pattern: string) -> dict` | First regex match info, or `[:]` if none |
48
+ | `.is_match` | `(pattern: string) -> bool` | True if regex matches anywhere |
49
+
50
+ ### `.match` Return Value
51
+
52
+ Returns a dict with three fields:
53
+ - `matched`: The matched text
54
+ - `index`: Position of match in string
55
+ - `groups`: Capture groups as list
56
+
57
+ ```rill
58
+ "hello123" -> .match("[0-9]+")
59
+ # [matched: "123", index: 5, groups: []]
60
+
61
+ "v1.2.3" -> .match("v(\\d+)\\.(\\d+)\\.(\\d+)")
62
+ # [matched: "v1.2.3", index: 0, groups: ["1", "2", "3"]]
63
+
64
+ "hello" -> .match("[0-9]+")
65
+ # [:] (empty dict = no match)
66
+ ```
67
+
68
+ ### `.is_match` for Boolean Checks
69
+
70
+ ```rill
71
+ "hello123" -> .is_match("[0-9]+") # true
72
+ "hello" -> .is_match("[0-9]+") # false
73
+ ```
74
+
75
+ ### Pattern Matching in Conditionals
76
+
77
+ ```rill
78
+ $response -> .is_match("ERROR") ? handle_error()
79
+
80
+ $response -> .match("code: (\\d+)") :> $m
81
+ $m -> !.empty ? process($m.groups[0])
82
+ ```
83
+
84
+ ## Replacement
85
+
86
+ | Method | Signature | Description |
87
+ |----------------|-----------------------------------------|--------------------------|
88
+ | `.replace` | `(pattern: string, replacement: string) -> string` | Replace first regex match |
89
+ | `.replace_all` | `(pattern: string, replacement: string) -> string` | Replace all regex matches |
90
+
91
+ ```rill
92
+ "a-b-c" -> .replace("-", "_") # "a_b-c"
93
+ "a-b-c" -> .replace_all("-", "_") # "a_b_c"
94
+
95
+ "a1b2c3" -> .replace("[0-9]", "X") # "aXb2c3"
96
+ "a1b2c3" -> .replace_all("[0-9]", "X") # "aXbXcX"
97
+
98
+ "hello" -> .replace_all("l", "") # "heo"
99
+ ```
100
+
101
+ ## Formatting
102
+
103
+ | Method | Signature | Description |
104
+ |--------------|----------------------------------------|--------------------------------|
105
+ | `.trim` | `() -> string` | Remove leading/trailing whitespace |
106
+ | `.repeat` | `(n: number) -> string` | Repeat string n times |
107
+ | `.pad_start` | `(length: number, fill: string = " ") -> string` | Pad start to length |
108
+ | `.pad_end` | `(length: number, fill: string = " ") -> string` | Pad end to length |
109
+
110
+ ```rill
111
+ " hello " -> .trim # "hello"
112
+
113
+ "ab" -> .repeat(3) # "ababab"
114
+ "ab" -> .repeat(0) # ""
115
+
116
+ "42" -> .pad_start(5) # " 42"
117
+ "42" -> .pad_start(5, "0") # "00042"
118
+
119
+ "42" -> .pad_end(5) # "42 "
120
+ "42" -> .pad_end(5, "0") # "42000"
121
+ ```
122
+
123
+ ## Splitting and Joining
124
+
125
+ | Method | Signature | Description |
126
+ |----------|------------------------------|------------------------------------|
127
+ | `.split` | `(sep: string = "\n") -> list` | Split by separator |
128
+ | `.join` | `(sep: string = ",") -> string` | Join list with separator |
129
+ | `.lines` | `() -> list` | Split on newlines (same as .split) |
130
+
131
+ ```rill
132
+ "a,b,c" -> .split(",") # ["a", "b", "c"]
133
+ "a\nb\nc" -> .lines # ["a", "b", "c"]
134
+
135
+ ["a", "b", "c"] -> .join("-") # "a-b-c"
136
+ ["a", "b", "c"] -> .join("\n") # "a\nb\nc"
137
+ ```
138
+
139
+ ## Conversion and Length
140
+
141
+ | Method | Signature | Description |
142
+ |--------|----------------|-----------------------------|
143
+ | `.str` | `() -> string` | Convert any value to string |
144
+ | `.num` | `() -> number` | Parse string to number |
145
+ | `.len` | `() -> number` | String length |
146
+
147
+ ```rill
148
+ 42 -> .str # "42"
149
+ "42" -> .num # 42
150
+ "hello" -> .len # 5
151
+ ```
152
+
153
+ ## Element Access
154
+
155
+ | Method | Signature | Description |
156
+ |----------|------------------------|--------------------------|
157
+ | `.head` | `-> string` | First character (errors on empty) |
158
+ | `.tail` | `-> string` | Last character (errors on empty) |
159
+ | `.at` | `(index: number) -> string` | Character at index |
160
+
161
+ ```rill
162
+ "hello" -> .head # "h"
163
+ "hello" -> .tail # "o"
164
+ "hello" -> .at(1) # "e"
165
+ ```
166
+
167
+ ## Common Patterns
168
+
169
+ ### Normalize and Compare
170
+
171
+ ```rill
172
+ $input -> .trim -> .lower -> .eq("yes")
173
+ ```
174
+
175
+ ### Extract and Validate
176
+
177
+ ```rill
178
+ $email -> .is_match("^[^@]+@[^@]+$") ? process($email) ! error("Invalid email")
179
+ ```
180
+
181
+ ### Format Output
182
+
183
+ ```rill
184
+ [[name: "Alice", value: 100], [name: "Bob", value: 42]] :> $items
185
+ $items -> each {
186
+ $.name -> .pad_end(20) :> $name
187
+ $.value -> .str -> .pad_start(10) :> $val
188
+ "{$name}{$val}"
189
+ } -> .join("\n")
190
+ ```
191
+
192
+ ### Replace Patterns
193
+
194
+ ```rill
195
+ "hello world" :> $text
196
+ $text -> .replace_all("\\s+", " ") -> .trim
197
+ ```
198
+
199
+ ### Parse Structured Text
200
+
201
+ ```rill
202
+ "key: value" :> $line
203
+ $line -> .match("(\\w+):\\s*(.+)") :> $m
204
+ $m -> !.empty ? [key: $m.groups[0], value: $m.groups[1]]
205
+ ```