@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.
- package/LICENSE +21 -0
- package/README.md +187 -0
- package/dist/cli.d.ts +11 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +69 -0
- package/dist/cli.js.map +1 -0
- package/dist/demo.d.ts +6 -0
- package/dist/demo.d.ts.map +1 -0
- package/dist/demo.js +121 -0
- package/dist/demo.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/lexer/errors.d.ts +9 -0
- package/dist/lexer/errors.d.ts.map +1 -0
- package/dist/lexer/errors.js +12 -0
- package/dist/lexer/errors.js.map +1 -0
- package/dist/lexer/helpers.d.ts +14 -0
- package/dist/lexer/helpers.d.ts.map +1 -0
- package/dist/lexer/helpers.js +30 -0
- package/dist/lexer/helpers.js.map +1 -0
- package/dist/lexer/index.d.ts +8 -0
- package/dist/lexer/index.d.ts.map +1 -0
- package/dist/lexer/index.js +8 -0
- package/dist/lexer/index.js.map +1 -0
- package/dist/lexer/operators.d.ts +11 -0
- package/dist/lexer/operators.d.ts.map +1 -0
- package/dist/lexer/operators.js +58 -0
- package/dist/lexer/operators.js.map +1 -0
- package/dist/lexer/readers.d.ts +12 -0
- package/dist/lexer/readers.d.ts.map +1 -0
- package/dist/lexer/readers.js +144 -0
- package/dist/lexer/readers.js.map +1 -0
- package/dist/lexer/state.d.ts +18 -0
- package/dist/lexer/state.d.ts.map +1 -0
- package/dist/lexer/state.js +37 -0
- package/dist/lexer/state.js.map +1 -0
- package/dist/lexer/tokenizer.d.ts +9 -0
- package/dist/lexer/tokenizer.d.ts.map +1 -0
- package/dist/lexer/tokenizer.js +100 -0
- package/dist/lexer/tokenizer.js.map +1 -0
- package/dist/lexer.d.ts +19 -0
- package/dist/lexer.d.ts.map +1 -0
- package/dist/lexer.js +344 -0
- package/dist/lexer.js.map +1 -0
- package/dist/parser/arithmetic.d.ts +16 -0
- package/dist/parser/arithmetic.d.ts.map +1 -0
- package/dist/parser/arithmetic.js +128 -0
- package/dist/parser/arithmetic.js.map +1 -0
- package/dist/parser/boolean.d.ts +15 -0
- package/dist/parser/boolean.d.ts.map +1 -0
- package/dist/parser/boolean.js +20 -0
- package/dist/parser/boolean.js.map +1 -0
- package/dist/parser/control-flow.d.ts +56 -0
- package/dist/parser/control-flow.d.ts.map +1 -0
- package/dist/parser/control-flow.js +167 -0
- package/dist/parser/control-flow.js.map +1 -0
- package/dist/parser/expressions.d.ts +23 -0
- package/dist/parser/expressions.d.ts.map +1 -0
- package/dist/parser/expressions.js +950 -0
- package/dist/parser/expressions.js.map +1 -0
- package/dist/parser/extraction.d.ts +48 -0
- package/dist/parser/extraction.d.ts.map +1 -0
- package/dist/parser/extraction.js +279 -0
- package/dist/parser/extraction.js.map +1 -0
- package/dist/parser/functions.d.ts +20 -0
- package/dist/parser/functions.d.ts.map +1 -0
- package/dist/parser/functions.js +96 -0
- package/dist/parser/functions.js.map +1 -0
- package/dist/parser/helpers.d.ts +94 -0
- package/dist/parser/helpers.d.ts.map +1 -0
- package/dist/parser/helpers.js +225 -0
- package/dist/parser/helpers.js.map +1 -0
- package/dist/parser/index.d.ts +49 -0
- package/dist/parser/index.d.ts.map +1 -0
- package/dist/parser/index.js +73 -0
- package/dist/parser/index.js.map +1 -0
- package/dist/parser/literals.d.ts +37 -0
- package/dist/parser/literals.d.ts.map +1 -0
- package/dist/parser/literals.js +373 -0
- package/dist/parser/literals.js.map +1 -0
- package/dist/parser/parser-collect.d.ts +16 -0
- package/dist/parser/parser-collect.d.ts.map +1 -0
- package/dist/parser/parser-collect.js +125 -0
- package/dist/parser/parser-collect.js.map +1 -0
- package/dist/parser/parser-control.d.ts +20 -0
- package/dist/parser/parser-control.d.ts.map +1 -0
- package/dist/parser/parser-control.js +120 -0
- package/dist/parser/parser-control.js.map +1 -0
- package/dist/parser/parser-expr.d.ts +37 -0
- package/dist/parser/parser-expr.d.ts.map +1 -0
- package/dist/parser/parser-expr.js +639 -0
- package/dist/parser/parser-expr.js.map +1 -0
- package/dist/parser/parser-extract.d.ts +17 -0
- package/dist/parser/parser-extract.d.ts.map +1 -0
- package/dist/parser/parser-extract.js +222 -0
- package/dist/parser/parser-extract.js.map +1 -0
- package/dist/parser/parser-functions.d.ts +21 -0
- package/dist/parser/parser-functions.d.ts.map +1 -0
- package/dist/parser/parser-functions.js +155 -0
- package/dist/parser/parser-functions.js.map +1 -0
- package/dist/parser/parser-literals.d.ts +22 -0
- package/dist/parser/parser-literals.d.ts.map +1 -0
- package/dist/parser/parser-literals.js +288 -0
- package/dist/parser/parser-literals.js.map +1 -0
- package/dist/parser/parser-script.d.ts +21 -0
- package/dist/parser/parser-script.d.ts.map +1 -0
- package/dist/parser/parser-script.js +174 -0
- package/dist/parser/parser-script.js.map +1 -0
- package/dist/parser/parser-variables.d.ts +20 -0
- package/dist/parser/parser-variables.d.ts.map +1 -0
- package/dist/parser/parser-variables.js +146 -0
- package/dist/parser/parser-variables.js.map +1 -0
- package/dist/parser/parser.d.ts +49 -0
- package/dist/parser/parser.d.ts.map +1 -0
- package/dist/parser/parser.js +54 -0
- package/dist/parser/parser.js.map +1 -0
- package/dist/parser/script.d.ts +14 -0
- package/dist/parser/script.d.ts.map +1 -0
- package/dist/parser/script.js +196 -0
- package/dist/parser/script.js.map +1 -0
- package/dist/parser/state.d.ts +40 -0
- package/dist/parser/state.d.ts.map +1 -0
- package/dist/parser/state.js +129 -0
- package/dist/parser/state.js.map +1 -0
- package/dist/parser/variables.d.ts +10 -0
- package/dist/parser/variables.d.ts.map +1 -0
- package/dist/parser/variables.js +215 -0
- package/dist/parser/variables.js.map +1 -0
- package/dist/runtime/ast-equals.d.ts +13 -0
- package/dist/runtime/ast-equals.d.ts.map +1 -0
- package/dist/runtime/ast-equals.js +447 -0
- package/dist/runtime/ast-equals.js.map +1 -0
- package/dist/runtime/builtins.d.ts +13 -0
- package/dist/runtime/builtins.d.ts.map +1 -0
- package/dist/runtime/builtins.js +180 -0
- package/dist/runtime/builtins.js.map +1 -0
- package/dist/runtime/callable.d.ts +88 -0
- package/dist/runtime/callable.d.ts.map +1 -0
- package/dist/runtime/callable.js +98 -0
- package/dist/runtime/callable.js.map +1 -0
- package/dist/runtime/context.d.ts +13 -0
- package/dist/runtime/context.d.ts.map +1 -0
- package/dist/runtime/context.js +73 -0
- package/dist/runtime/context.js.map +1 -0
- package/dist/runtime/core/callable.d.ts +171 -0
- package/dist/runtime/core/callable.d.ts.map +1 -0
- package/dist/runtime/core/callable.js +246 -0
- package/dist/runtime/core/callable.js.map +1 -0
- package/dist/runtime/core/context.d.ts +29 -0
- package/dist/runtime/core/context.d.ts.map +1 -0
- package/dist/runtime/core/context.js +154 -0
- package/dist/runtime/core/context.js.map +1 -0
- package/dist/runtime/core/equals.d.ts +9 -0
- package/dist/runtime/core/equals.d.ts.map +1 -0
- package/dist/runtime/core/equals.js +381 -0
- package/dist/runtime/core/equals.js.map +1 -0
- package/dist/runtime/core/eval/base.d.ts +65 -0
- package/dist/runtime/core/eval/base.d.ts.map +1 -0
- package/dist/runtime/core/eval/base.js +112 -0
- package/dist/runtime/core/eval/base.js.map +1 -0
- package/dist/runtime/core/eval/evaluator.d.ts +47 -0
- package/dist/runtime/core/eval/evaluator.d.ts.map +1 -0
- package/dist/runtime/core/eval/evaluator.js +73 -0
- package/dist/runtime/core/eval/evaluator.js.map +1 -0
- package/dist/runtime/core/eval/index.d.ts +57 -0
- package/dist/runtime/core/eval/index.d.ts.map +1 -0
- package/dist/runtime/core/eval/index.js +95 -0
- package/dist/runtime/core/eval/index.js.map +1 -0
- package/dist/runtime/core/eval/mixins/annotations.d.ts +19 -0
- package/dist/runtime/core/eval/mixins/annotations.d.ts.map +1 -0
- package/dist/runtime/core/eval/mixins/annotations.js +146 -0
- package/dist/runtime/core/eval/mixins/annotations.js.map +1 -0
- package/dist/runtime/core/eval/mixins/closures.d.ts +49 -0
- package/dist/runtime/core/eval/mixins/closures.d.ts.map +1 -0
- package/dist/runtime/core/eval/mixins/closures.js +479 -0
- package/dist/runtime/core/eval/mixins/closures.js.map +1 -0
- package/dist/runtime/core/eval/mixins/collections.d.ts +24 -0
- package/dist/runtime/core/eval/mixins/collections.d.ts.map +1 -0
- package/dist/runtime/core/eval/mixins/collections.js +466 -0
- package/dist/runtime/core/eval/mixins/collections.js.map +1 -0
- package/dist/runtime/core/eval/mixins/control-flow.d.ts +27 -0
- package/dist/runtime/core/eval/mixins/control-flow.d.ts.map +1 -0
- package/dist/runtime/core/eval/mixins/control-flow.js +369 -0
- package/dist/runtime/core/eval/mixins/control-flow.js.map +1 -0
- package/dist/runtime/core/eval/mixins/core.d.ts +24 -0
- package/dist/runtime/core/eval/mixins/core.d.ts.map +1 -0
- package/dist/runtime/core/eval/mixins/core.js +335 -0
- package/dist/runtime/core/eval/mixins/core.js.map +1 -0
- package/dist/runtime/core/eval/mixins/expressions.d.ts +19 -0
- package/dist/runtime/core/eval/mixins/expressions.d.ts.map +1 -0
- package/dist/runtime/core/eval/mixins/expressions.js +202 -0
- package/dist/runtime/core/eval/mixins/expressions.js.map +1 -0
- package/dist/runtime/core/eval/mixins/extraction.d.ts +10 -0
- package/dist/runtime/core/eval/mixins/extraction.d.ts.map +1 -0
- package/dist/runtime/core/eval/mixins/extraction.js +250 -0
- package/dist/runtime/core/eval/mixins/extraction.js.map +1 -0
- package/dist/runtime/core/eval/mixins/literals.d.ts +23 -0
- package/dist/runtime/core/eval/mixins/literals.d.ts.map +1 -0
- package/dist/runtime/core/eval/mixins/literals.js +180 -0
- package/dist/runtime/core/eval/mixins/literals.js.map +1 -0
- package/dist/runtime/core/eval/mixins/types.d.ts +20 -0
- package/dist/runtime/core/eval/mixins/types.d.ts.map +1 -0
- package/dist/runtime/core/eval/mixins/types.js +109 -0
- package/dist/runtime/core/eval/mixins/types.js.map +1 -0
- package/dist/runtime/core/eval/mixins/variables.d.ts +34 -0
- package/dist/runtime/core/eval/mixins/variables.d.ts.map +1 -0
- package/dist/runtime/core/eval/mixins/variables.js +247 -0
- package/dist/runtime/core/eval/mixins/variables.js.map +1 -0
- package/dist/runtime/core/eval/types.d.ts +41 -0
- package/dist/runtime/core/eval/types.d.ts.map +1 -0
- package/dist/runtime/core/eval/types.js +10 -0
- package/dist/runtime/core/eval/types.js.map +1 -0
- package/dist/runtime/core/evaluate.d.ts +42 -0
- package/dist/runtime/core/evaluate.d.ts.map +1 -0
- package/dist/runtime/core/evaluate.debug.js +1251 -0
- package/dist/runtime/core/evaluate.js +1913 -0
- package/dist/runtime/core/evaluate.js.map +1 -0
- package/dist/runtime/core/execute.d.ts +26 -0
- package/dist/runtime/core/execute.d.ts.map +1 -0
- package/dist/runtime/core/execute.js +177 -0
- package/dist/runtime/core/execute.js.map +1 -0
- package/dist/runtime/core/signals.d.ts +19 -0
- package/dist/runtime/core/signals.d.ts.map +1 -0
- package/dist/runtime/core/signals.js +26 -0
- package/dist/runtime/core/signals.js.map +1 -0
- package/dist/runtime/core/types.d.ts +177 -0
- package/dist/runtime/core/types.d.ts.map +1 -0
- package/dist/runtime/core/types.js +50 -0
- package/dist/runtime/core/types.js.map +1 -0
- package/dist/runtime/core/values.d.ts +66 -0
- package/dist/runtime/core/values.d.ts.map +1 -0
- package/dist/runtime/core/values.js +240 -0
- package/dist/runtime/core/values.js.map +1 -0
- package/dist/runtime/evaluate.d.ts +32 -0
- package/dist/runtime/evaluate.d.ts.map +1 -0
- package/dist/runtime/evaluate.js +1111 -0
- package/dist/runtime/evaluate.js.map +1 -0
- package/dist/runtime/execute.d.ts +26 -0
- package/dist/runtime/execute.d.ts.map +1 -0
- package/dist/runtime/execute.js +121 -0
- package/dist/runtime/execute.js.map +1 -0
- package/dist/runtime/ext/builtins.d.ts +16 -0
- package/dist/runtime/ext/builtins.d.ts.map +1 -0
- package/dist/runtime/ext/builtins.js +528 -0
- package/dist/runtime/ext/builtins.js.map +1 -0
- package/dist/runtime/ext/content-parser.d.ts +83 -0
- package/dist/runtime/ext/content-parser.d.ts.map +1 -0
- package/dist/runtime/ext/content-parser.js +536 -0
- package/dist/runtime/ext/content-parser.js.map +1 -0
- package/dist/runtime/index.d.ts +28 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +34 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/signals.d.ts +19 -0
- package/dist/runtime/signals.d.ts.map +1 -0
- package/dist/runtime/signals.js +26 -0
- package/dist/runtime/signals.js.map +1 -0
- package/dist/runtime/types.d.ts +169 -0
- package/dist/runtime/types.d.ts.map +1 -0
- package/dist/runtime/types.js +50 -0
- package/dist/runtime/types.js.map +1 -0
- package/dist/runtime/values.d.ts +50 -0
- package/dist/runtime/values.d.ts.map +1 -0
- package/dist/runtime/values.js +209 -0
- package/dist/runtime/values.js.map +1 -0
- package/dist/runtime.d.ts +254 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +2014 -0
- package/dist/runtime.js.map +1 -0
- package/dist/types.d.ts +752 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +189 -0
- package/dist/types.js.map +1 -0
- package/docs/00_INDEX.md +65 -0
- package/docs/01_guide.md +390 -0
- package/docs/02_types.md +399 -0
- package/docs/03_variables.md +314 -0
- package/docs/04_operators.md +551 -0
- package/docs/05_control-flow.md +350 -0
- package/docs/06_closures.md +353 -0
- package/docs/07_collections.md +686 -0
- package/docs/08_iterators.md +330 -0
- package/docs/09_strings.md +205 -0
- package/docs/10_parsing.md +366 -0
- package/docs/11_reference.md +350 -0
- package/docs/12_examples.md +771 -0
- package/docs/13_modules.md +519 -0
- package/docs/14_host-integration.md +826 -0
- package/docs/15_grammar.ebnf +693 -0
- package/docs/16_conventions.md +696 -0
- package/docs/99_llm-reference.txt +300 -0
- package/docs/assets/logo.png +0 -0
- 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
|