@rcrsr/rill 0.2.2 → 0.2.4
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/dist/check/rules/anti-patterns.d.ts.map +1 -1
- package/dist/check/rules/anti-patterns.js +1 -6
- package/dist/check/rules/anti-patterns.js.map +1 -1
- package/dist/check/rules/closures.d.ts.map +1 -1
- package/dist/check/rules/closures.js +2 -5
- package/dist/check/rules/closures.js.map +1 -1
- package/dist/check/rules/conditionals.d.ts.map +1 -1
- package/dist/check/rules/conditionals.js +36 -8
- package/dist/check/rules/conditionals.js.map +1 -1
- package/dist/check/rules/formatting.d.ts.map +1 -1
- package/dist/check/rules/formatting.js +16 -6
- package/dist/check/rules/formatting.js.map +1 -1
- package/dist/check/rules/naming.d.ts.map +1 -1
- package/dist/check/rules/naming.js +9 -2
- package/dist/check/rules/naming.js.map +1 -1
- package/dist/check/rules/strings.d.ts.map +1 -1
- package/dist/check/rules/strings.js +1 -2
- package/dist/check/rules/strings.js.map +1 -1
- package/dist/check/rules/types.d.ts.map +1 -1
- package/dist/check/rules/types.js +4 -3
- package/dist/check/rules/types.js.map +1 -1
- package/dist/check/types.d.ts +2 -0
- package/dist/check/types.d.ts.map +1 -1
- package/dist/check/validator.d.ts.map +1 -1
- package/dist/check/validator.js +8 -0
- package/dist/check/validator.js.map +1 -1
- package/dist/check/visitor.d.ts.map +1 -1
- package/dist/check/visitor.js +13 -1
- package/dist/check/visitor.js.map +1 -1
- package/dist/cli-check.js +1 -0
- package/dist/cli-check.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/lexer/operators.d.ts.map +1 -1
- package/dist/lexer/operators.js +2 -0
- package/dist/lexer/operators.js.map +1 -1
- package/dist/parser/helpers.d.ts +2 -1
- package/dist/parser/helpers.d.ts.map +1 -1
- package/dist/parser/helpers.js +62 -10
- package/dist/parser/helpers.js.map +1 -1
- package/dist/parser/parser-control.d.ts +3 -1
- package/dist/parser/parser-control.d.ts.map +1 -1
- package/dist/parser/parser-control.js +93 -0
- package/dist/parser/parser-control.js.map +1 -1
- package/dist/parser/parser-expr.d.ts.map +1 -1
- package/dist/parser/parser-expr.js +43 -1
- package/dist/parser/parser-expr.js.map +1 -1
- package/dist/parser/parser-functions.js +20 -3
- package/dist/parser/parser-functions.js.map +1 -1
- package/dist/parser/parser-literals.js +18 -2
- package/dist/parser/parser-literals.js.map +1 -1
- package/dist/parser/parser-script.d.ts +2 -2
- package/dist/parser/parser-script.d.ts.map +1 -1
- package/dist/parser/parser-script.js +43 -3
- package/dist/parser/parser-script.js.map +1 -1
- package/dist/runtime/core/equals.js +14 -2
- package/dist/runtime/core/equals.js.map +1 -1
- package/dist/runtime/core/eval/mixins/control-flow.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/control-flow.js +83 -0
- package/dist/runtime/core/eval/mixins/control-flow.js.map +1 -1
- package/dist/runtime/core/eval/mixins/core.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/core.js +16 -0
- package/dist/runtime/core/eval/mixins/core.js.map +1 -1
- package/dist/runtime/core/eval/mixins/literals.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/literals.js +82 -0
- package/dist/runtime/core/eval/mixins/literals.js.map +1 -1
- package/dist/runtime/core/execute.js +6 -6
- package/dist/runtime/core/execute.js.map +1 -1
- package/dist/runtime/ext/builtins.d.ts.map +1 -1
- package/dist/runtime/ext/builtins.js +66 -0
- package/dist/runtime/ext/builtins.js.map +1 -1
- package/dist/types.d.ts +37 -13
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -1
- package/docs/00_INDEX.md +1 -0
- package/docs/05_control-flow.md +261 -2
- package/docs/09_strings.md +1 -1
- package/docs/11_reference.md +45 -0
- package/docs/12_examples.md +58 -55
- package/docs/14_host-integration.md +2 -0
- package/docs/15_grammar.ebnf +8 -1
- package/docs/18_design-principles.md +210 -0
- package/package.json +1 -1
package/docs/12_examples.md
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
Examples demonstrating core language features for workflow orchestration.
|
|
4
4
|
|
|
5
|
-
> **Note:** These examples
|
|
5
|
+
> **Note:** These examples use `app::` prefix for host-provided functions (`app::prompt()`, `app::fetch()`, etc.). Built-in functions (`log`, `parse_json`, `range`) need no prefix. Frontmatter is opaque to rill; the host parses it and provides named variables to the script context.
|
|
6
6
|
|
|
7
7
|
## Feature Implementation Workflow
|
|
8
8
|
|
|
9
9
|
Validates requirements, creates spec, iterates on review, then implements.
|
|
10
10
|
|
|
11
|
-
```
|
|
11
|
+
```text
|
|
12
12
|
---
|
|
13
13
|
timeout: 00:10:00
|
|
14
14
|
args: requirements: string
|
|
@@ -19,17 +19,17 @@ args: requirements: string
|
|
|
19
19
|
Review the requirements document at {$requirements}.
|
|
20
20
|
Check for completeness and clarity.
|
|
21
21
|
Output READY if complete, or list missing elements.
|
|
22
|
-
""" -> prompt() :> $validation
|
|
22
|
+
""" -> app::prompt() :> $validation
|
|
23
23
|
|
|
24
|
-
$validation ->
|
|
25
|
-
error
|
|
24
|
+
$validation -> .contains("READY") -> !$ ? {
|
|
25
|
+
error "Requirements incomplete: {$validation}"
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
# Phase 2: Create specification
|
|
29
29
|
"""
|
|
30
30
|
Create a technical specification from {$requirements}.
|
|
31
31
|
Include API design, data models, and component structure.
|
|
32
|
-
""" -> prompt() :> $spec
|
|
32
|
+
""" -> app::prompt() :> $spec
|
|
33
33
|
|
|
34
34
|
"Specification created" -> log
|
|
35
35
|
|
|
@@ -40,7 +40,7 @@ Review this specification for issues:
|
|
|
40
40
|
{$}
|
|
41
41
|
|
|
42
42
|
Output APPROVED if ready, or REVISION REQUIRED with feedback.
|
|
43
|
-
""" -> prompt() :> $review
|
|
43
|
+
""" -> app::prompt() :> $review
|
|
44
44
|
|
|
45
45
|
$review -> ?(.contains("APPROVED")) { break }
|
|
46
46
|
|
|
@@ -51,7 +51,7 @@ Update the specification based on this feedback:
|
|
|
51
51
|
|
|
52
52
|
Original spec:
|
|
53
53
|
{$}
|
|
54
|
-
""" -> prompt()} :> $approved_spec
|
|
54
|
+
""" -> app::prompt()} :> $approved_spec
|
|
55
55
|
|
|
56
56
|
# Phase 4: Implementation
|
|
57
57
|
"""
|
|
@@ -59,10 +59,10 @@ Implement the approved specification:
|
|
|
59
59
|
{$approved_spec}
|
|
60
60
|
|
|
61
61
|
Create the necessary files and tests.
|
|
62
|
-
""" -> prompt() :> $implementation
|
|
62
|
+
""" -> app::prompt() :> $implementation
|
|
63
63
|
|
|
64
64
|
# Phase 5: Verify
|
|
65
|
-
prompt("Run tests and verify implementation") :> $verification
|
|
65
|
+
app::prompt("Run tests and verify implementation") :> $verification
|
|
66
66
|
|
|
67
67
|
$verification -> ?(.contains("PASS")) {
|
|
68
68
|
[0, "Workflow complete"]
|
|
@@ -81,10 +81,10 @@ args: plan: string
|
|
|
81
81
|
---
|
|
82
82
|
|
|
83
83
|
# Initial check
|
|
84
|
-
prompt("Read {$plan} and find the first unchecked item (- [ ])") :> $status
|
|
84
|
+
app::prompt("Read {$plan} and find the first unchecked item (- [ ])") :> $status
|
|
85
85
|
|
|
86
86
|
# Work loop
|
|
87
|
-
|
|
87
|
+
(!$status -> .contains("ALL COMPLETE")) @ {
|
|
88
88
|
"""
|
|
89
89
|
Based on this status:
|
|
90
90
|
{$}
|
|
@@ -93,7 +93,8 @@ Based on this status:
|
|
|
93
93
|
2. Mark it complete in {$plan}
|
|
94
94
|
3. Check if any unchecked items remain
|
|
95
95
|
4. Output ALL COMPLETE if done, or describe next item
|
|
96
|
-
""" -> prompt()
|
|
96
|
+
""" -> app::prompt()
|
|
97
|
+
} :> $final
|
|
97
98
|
|
|
98
99
|
"Plan complete: {$final}" -> log
|
|
99
100
|
```
|
|
@@ -108,7 +109,7 @@ args: target: string
|
|
|
108
109
|
---
|
|
109
110
|
|
|
110
111
|
# Run tests
|
|
111
|
-
prompt("Run tests for {$target} and report results") :> $result
|
|
112
|
+
app::prompt("Run tests for {$target} and report results") :> $result
|
|
112
113
|
|
|
113
114
|
# Fix loop
|
|
114
115
|
$result -> @(.contains("FAIL")) {
|
|
@@ -119,12 +120,12 @@ Fix these test failures:
|
|
|
119
120
|
{$}
|
|
120
121
|
|
|
121
122
|
Make minimal changes. Then run tests again and report results.
|
|
122
|
-
""" -> prompt()} :> $final
|
|
123
|
+
""" -> app::prompt()} :> $final
|
|
123
124
|
|
|
124
125
|
$final -> ?(.contains("PASS")) {
|
|
125
126
|
"All tests passing"
|
|
126
127
|
} ! {
|
|
127
|
-
error
|
|
128
|
+
error "Could not fix all tests"
|
|
128
129
|
}
|
|
129
130
|
```
|
|
130
131
|
|
|
@@ -138,7 +139,7 @@ args: file: string
|
|
|
138
139
|
---
|
|
139
140
|
|
|
140
141
|
# Get file summary
|
|
141
|
-
prompt("Read and summarize {$file}") :> $summary
|
|
142
|
+
app::prompt("Read and summarize {$file}") :> $summary
|
|
142
143
|
|
|
143
144
|
# Security check
|
|
144
145
|
"""
|
|
@@ -146,7 +147,7 @@ Evaluate for SECURITY issues:
|
|
|
146
147
|
{$summary}
|
|
147
148
|
|
|
148
149
|
Output PASS, WARN, or FAIL with explanation.
|
|
149
|
-
""" -> prompt() :> $security
|
|
150
|
+
""" -> app::prompt() :> $security
|
|
150
151
|
|
|
151
152
|
# Performance check
|
|
152
153
|
"""
|
|
@@ -154,15 +155,15 @@ Evaluate for PERFORMANCE issues:
|
|
|
154
155
|
{$summary}
|
|
155
156
|
|
|
156
157
|
Output PASS, WARN, or FAIL with explanation.
|
|
157
|
-
""" -> prompt() :> $performance
|
|
158
|
+
""" -> app::prompt() :> $performance
|
|
158
159
|
|
|
159
160
|
# Check results
|
|
160
|
-
$security ->
|
|
161
|
-
error
|
|
161
|
+
$security -> .contains("FAIL") -> ? {
|
|
162
|
+
error "Security review failed: {$security}"
|
|
162
163
|
}
|
|
163
164
|
|
|
164
|
-
$performance ->
|
|
165
|
-
error
|
|
165
|
+
$performance -> .contains("FAIL") -> ? {
|
|
166
|
+
error "Performance review failed: {$performance}"
|
|
166
167
|
}
|
|
167
168
|
|
|
168
169
|
"Code review passed"
|
|
@@ -172,14 +173,14 @@ $performance -> ?(.contains("FAIL")) {
|
|
|
172
173
|
|
|
173
174
|
Deploys based on environment configuration.
|
|
174
175
|
|
|
175
|
-
```
|
|
176
|
+
```text
|
|
176
177
|
---
|
|
177
178
|
args: service: string
|
|
178
179
|
---
|
|
179
180
|
|
|
180
181
|
# Validate environment
|
|
181
182
|
$ENV.DEPLOY_ENV -> ?(.empty()) {
|
|
182
|
-
error
|
|
183
|
+
error "DEPLOY_ENV not set"
|
|
183
184
|
}
|
|
184
185
|
|
|
185
186
|
# Environment-specific deployment
|
|
@@ -189,13 +190,13 @@ Deploy {$service} to production.
|
|
|
189
190
|
- Run full test suite first
|
|
190
191
|
- Enable monitoring
|
|
191
192
|
- Use blue-green deployment
|
|
192
|
-
""" -> prompt()} ! ($ENV.DEPLOY_ENV == "staging") ? {
|
|
193
|
+
""" -> app::prompt()} ! ($ENV.DEPLOY_ENV == "staging") ? {
|
|
193
194
|
"""
|
|
194
195
|
Deploy {$service} to staging.
|
|
195
196
|
- Run smoke tests
|
|
196
197
|
- Enable debug logging
|
|
197
|
-
""" -> prompt()} ! {
|
|
198
|
-
prompt("Deploy {$service} to development environment")
|
|
198
|
+
""" -> app::prompt()} ! {
|
|
199
|
+
app::prompt("Deploy {$service} to development environment")
|
|
199
200
|
} :> $result
|
|
200
201
|
|
|
201
202
|
"Deployment complete" -> log
|
|
@@ -217,10 +218,11 @@ args: operation: string
|
|
|
217
218
|
Perform: {$operation}
|
|
218
219
|
|
|
219
220
|
Output SUCCESS, RETRY, or FAILED.
|
|
220
|
-
""" -> prompt()
|
|
221
|
+
""" -> app::prompt()
|
|
222
|
+
} ? (.contains("RETRY")) :> $result
|
|
221
223
|
|
|
222
224
|
# Loop exits when result doesn't contain RETRY
|
|
223
|
-
.contains("SUCCESS") ? [0, "Succeeded"] ! [1, "Failed: {$}"]
|
|
225
|
+
$result -> .contains("SUCCESS") ? [0, "Succeeded"] ! [1, "Failed: {$result}"]
|
|
224
226
|
```
|
|
225
227
|
|
|
226
228
|
The do-while form eliminates the separate first-attempt code since the body always executes at least once.
|
|
@@ -235,13 +237,13 @@ args: file: string
|
|
|
235
237
|
---
|
|
236
238
|
|
|
237
239
|
# Inline capture: value flows through $raw to log to conditional
|
|
238
|
-
prompt("Read {$file}") :> $raw -> log ->
|
|
239
|
-
error
|
|
240
|
+
app::prompt("Read {$file}") :> $raw -> log -> .contains("ERROR") -> ? {
|
|
241
|
+
error "Failed to read: {$raw}"
|
|
240
242
|
}
|
|
241
243
|
|
|
242
244
|
# Continue with $raw available for later use
|
|
243
|
-
prompt("Analyze this content:\n{$raw}") :> $analysis -> log ->
|
|
244
|
-
error
|
|
245
|
+
app::prompt("Analyze this content:\n{$raw}") :> $analysis -> log -> .empty -> ? {
|
|
246
|
+
error "Analysis produced no output"
|
|
245
247
|
}
|
|
246
248
|
|
|
247
249
|
# Both $raw and $analysis available
|
|
@@ -251,7 +253,7 @@ Compare the original:
|
|
|
251
253
|
|
|
252
254
|
With the analysis:
|
|
253
255
|
{$analysis}
|
|
254
|
-
""" -> prompt()
|
|
256
|
+
""" -> app::prompt()
|
|
255
257
|
```
|
|
256
258
|
|
|
257
259
|
Semantically, `:> $var ->` is `:> $var.set($) ->` — the capture acts like `log`, storing the value while passing it through unchanged.
|
|
@@ -267,7 +269,7 @@ args: file: string
|
|
|
267
269
|
|
|
268
270
|
# Define a typed helper closure
|
|
269
271
|
|input: string| {
|
|
270
|
-
prompt("Validate: {$input}") -> ?(.contains("VALID")) { true } ! { false }
|
|
272
|
+
app::prompt("Validate: {$input}") -> ?(.contains("VALID")) { true } ! { false }
|
|
271
273
|
} :> $validate:closure
|
|
272
274
|
|
|
273
275
|
# Capture with explicit type locks the variable
|
|
@@ -279,15 +281,15 @@ args: file: string
|
|
|
279
281
|
# "oops" :> $validate # ERROR: cannot assign string to closure
|
|
280
282
|
|
|
281
283
|
# Inline type annotation in pipe chain
|
|
282
|
-
prompt("Check {$file}") :> $result:string -> log -> ?(.contains("ERROR")) {
|
|
283
|
-
error
|
|
284
|
+
app::prompt("Check {$file}") :> $result:string -> log -> ?(.contains("ERROR")) {
|
|
285
|
+
error $result
|
|
284
286
|
}
|
|
285
287
|
|
|
286
288
|
# Type annotations catch mistakes early
|
|
287
|
-
prompt("Analyze {$file}") :> $analysis:string
|
|
289
|
+
app::prompt("Analyze {$file}") :> $analysis:string
|
|
288
290
|
|
|
289
291
|
?(.contains("FAIL")) {
|
|
290
|
-
error
|
|
292
|
+
error "Analysis failed: {$analysis}"
|
|
291
293
|
}
|
|
292
294
|
|
|
293
295
|
[0, "Processing complete"]
|
|
@@ -302,9 +304,9 @@ Extracts specific information from responses.
|
|
|
302
304
|
args: logfile: string
|
|
303
305
|
---
|
|
304
306
|
|
|
305
|
-
prompt("Read {$logfile} and find all ERROR lines") :> $errors
|
|
307
|
+
app::prompt("Read {$logfile} and find all ERROR lines") :> $errors
|
|
306
308
|
|
|
307
|
-
$errors ->
|
|
309
|
+
$errors -> .empty -> ? {
|
|
308
310
|
"No errors found"
|
|
309
311
|
} ! {
|
|
310
312
|
"""
|
|
@@ -312,7 +314,7 @@ Analyze these errors and categorize them:
|
|
|
312
314
|
{$errors}
|
|
313
315
|
|
|
314
316
|
For each unique error type, suggest a fix.
|
|
315
|
-
""" -> prompt() :> $analysis
|
|
317
|
+
""" -> app::prompt() :> $analysis
|
|
316
318
|
|
|
317
319
|
"Error analysis complete" -> log
|
|
318
320
|
$analysis
|
|
@@ -348,16 +350,16 @@ $analysis -> .contains("FAIL") ? {
|
|
|
348
350
|
|
|
349
351
|
Uses bar-delimited arithmetic for calculations within workflow logic.
|
|
350
352
|
|
|
351
|
-
```
|
|
353
|
+
```text
|
|
352
354
|
---
|
|
353
355
|
args: items: string
|
|
354
356
|
---
|
|
355
357
|
|
|
356
358
|
# Count items and calculate batch sizes
|
|
357
|
-
prompt("Count items in {$items}") -> .match("(\\d+) items") :> $m
|
|
359
|
+
app::prompt("Count items in {$items}") -> .match("(\\d+) items") :> $m
|
|
358
360
|
|
|
359
|
-
$m ->
|
|
360
|
-
error
|
|
361
|
+
$m -> .empty -> ? {
|
|
362
|
+
error "Could not parse item count"
|
|
361
363
|
}
|
|
362
364
|
|
|
363
365
|
$m.groups[0] -> .num :> $count
|
|
@@ -376,7 +378,7 @@ range(1, $batches + 1) -> each {
|
|
|
376
378
|
"""
|
|
377
379
|
Process batch {$batch_num} of {$batches}
|
|
378
380
|
Items {$start} through {$end}
|
|
379
|
-
""" -> prompt()}
|
|
381
|
+
""" -> app::prompt()}
|
|
380
382
|
|
|
381
383
|
[0, "Processed all batches"]
|
|
382
384
|
```
|
|
@@ -400,9 +402,9 @@ Rules:
|
|
|
400
402
|
- Output :::BLOCKED::: if you need information you don't have
|
|
401
403
|
- Output :::NEEDS_HUMAN::: if human judgment is required
|
|
402
404
|
- Output :::DONE::: when complete
|
|
403
|
-
""" -> prompt() :> $result
|
|
405
|
+
""" -> app::prompt() :> $result
|
|
404
406
|
|
|
405
|
-
|
|
407
|
+
(!$result -> .contains(":::DONE:::")) @ {
|
|
406
408
|
"""
|
|
407
409
|
Continue working on: {$task}
|
|
408
410
|
|
|
@@ -410,7 +412,8 @@ Previous progress:
|
|
|
410
412
|
{$}
|
|
411
413
|
|
|
412
414
|
Remember the signal rules.
|
|
413
|
-
""" -> prompt()
|
|
415
|
+
""" -> app::prompt()
|
|
416
|
+
} :> $final
|
|
414
417
|
|
|
415
418
|
"Task complete: {$final}" -> log
|
|
416
419
|
```
|
|
@@ -452,7 +455,7 @@ $code -> .gt(0) ? {
|
|
|
452
455
|
# ...
|
|
453
456
|
# enumerate($tasks) -> each {
|
|
454
457
|
# "[{$.index + 1}/{$tasks.len}] Processing: {$.value}" -> log()
|
|
455
|
-
# prompt("Complete task: {$.value}")
|
|
458
|
+
# app::prompt("Complete task: {$.value}")
|
|
456
459
|
# }
|
|
457
460
|
```
|
|
458
461
|
|
|
@@ -559,7 +562,7 @@ For XML content, use `parse_xml` for more precise extraction:
|
|
|
559
562
|
|
|
560
563
|
```rill
|
|
561
564
|
# LLM returns: "Here's the config:\n```json\n{...}\n```"
|
|
562
|
-
prompt("Generate JSON config") -> parse_fence("json") -> parse_json :> $config
|
|
565
|
+
app::prompt("Generate JSON config") -> parse_fence("json") -> parse_json :> $config
|
|
563
566
|
|
|
564
567
|
# Access parsed data
|
|
565
568
|
$config.host -> log
|
|
@@ -570,7 +573,7 @@ $config.port -> log
|
|
|
570
573
|
|
|
571
574
|
```rill
|
|
572
575
|
# LLM returns: "<thinking>...</thinking><answer>...</answer>"
|
|
573
|
-
prompt("Analyze step by step") :> $response
|
|
576
|
+
app::prompt("Analyze step by step") :> $response
|
|
574
577
|
|
|
575
578
|
# Extract thinking for logging
|
|
576
579
|
$response -> parse_xml("thinking") -> log
|
|
@@ -585,7 +588,7 @@ $response -> parse_xml("answer") -> parse_json :> $answer
|
|
|
585
588
|
"""
|
|
586
589
|
What function should I call?
|
|
587
590
|
Return in format: <tool><name>func</name><args>{...}</args></tool>
|
|
588
|
-
""" -> prompt() :> $response
|
|
591
|
+
""" -> app::prompt() :> $response
|
|
589
592
|
|
|
590
593
|
$response -> parse_xml("tool") :> $tool
|
|
591
594
|
$tool -> parse_xml("name") :> $fn_name
|
|
@@ -712,6 +712,8 @@ try {
|
|
|
712
712
|
| `RUNTIME_ABORTED` | Execution cancelled |
|
|
713
713
|
| `RUNTIME_INVALID_PATTERN` | Invalid regex pattern |
|
|
714
714
|
| `RUNTIME_AUTO_EXCEPTION` | Auto-exception triggered |
|
|
715
|
+
| `RUNTIME_ASSERTION_FAILED` | Assertion failed (condition false) |
|
|
716
|
+
| `RUNTIME_ERROR_RAISED` | Error statement executed |
|
|
715
717
|
|
|
716
718
|
## Complete Example
|
|
717
719
|
|
package/docs/15_grammar.ebnf
CHANGED
|
@@ -88,7 +88,7 @@ yaml-char = letter | digit | ":" | " " | "-" | "_" | "." | "[" | "]" | "," |
|
|
|
88
88
|
[1,2,3] -> each { break } -- break terminates (exits loop)
|
|
89
89
|
"result" -> return -- return terminates (exits function) *)
|
|
90
90
|
|
|
91
|
-
statement = [ annotation ] , pipe-chain ;
|
|
91
|
+
statement = [ annotation ] , ( pipe-chain | assert-statement | error-statement ) ;
|
|
92
92
|
(* pipe-chain includes optional terminator: capture | "break" | "return" *)
|
|
93
93
|
|
|
94
94
|
(* Annotations: ^(key: value, ...) prefix modifying statement behavior.
|
|
@@ -134,6 +134,13 @@ comparison-op = "==" | "!=" | "<" | ">" | "<=" | ">=" ;
|
|
|
134
134
|
chain-terminator = "break" | "return" ;
|
|
135
135
|
(* Note: capture can appear mid-chain or at end; handled in pipe-chain *)
|
|
136
136
|
|
|
137
|
+
assert-statement = "assert" , expression , [ string ] ;
|
|
138
|
+
(* Assert statement: validates boolean expression, halts on false.
|
|
139
|
+
Optional string provides custom error message.
|
|
140
|
+
Constraint: expression must evaluate to boolean, else RUNTIME_TYPE_ERROR. *)
|
|
141
|
+
|
|
142
|
+
error-statement = "error" , string ;
|
|
143
|
+
|
|
137
144
|
postfix-expr = primary , { "." , method-name , [ "(" , [ arguments ] , ")" ] } ;
|
|
138
145
|
|
|
139
146
|
primary = literal
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# rill Design Principles
|
|
2
|
+
|
|
3
|
+
**rillistic**: code that embraces pipes, explicit booleans, sealed scopes, and value semantics instead of fighting them. This document defines the rillistic mental model for code generators and developers transitioning from Python, TypeScript, or C-like languages.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Core Model: Data Flows, Not State Mutates
|
|
8
|
+
|
|
9
|
+
Mainstream languages center on variables that hold and mutate state. rill centers on data flowing left-to-right through transformations.
|
|
10
|
+
|
|
11
|
+
### Mainstream mental model
|
|
12
|
+
|
|
13
|
+
1. Read variable
|
|
14
|
+
2. Modify variable
|
|
15
|
+
3. Write variable
|
|
16
|
+
|
|
17
|
+
### rill mental model
|
|
18
|
+
|
|
19
|
+
1. Data enters pipeline
|
|
20
|
+
2. Transformations shape it
|
|
21
|
+
3. Result emerges at the end
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
Every rill program answers one question: "What happens to this data as it moves through the pipe?"
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Six Principles That Break Mainstream Habits
|
|
29
|
+
|
|
30
|
+
### 1. Pipes Replace Assignment
|
|
31
|
+
|
|
32
|
+
No `=` operator exists. Data moves via `->`, values captured via `:>`.
|
|
33
|
+
|
|
34
|
+
```rill
|
|
35
|
+
# Data flows through transformations
|
|
36
|
+
" hello world " -> .trim -> .upper -> .split(" ")
|
|
37
|
+
# Result: ["HELLO", "WORLD"]
|
|
38
|
+
|
|
39
|
+
# Capture only when a value appears more than once
|
|
40
|
+
app::prompt("analyze") :> $result
|
|
41
|
+
$result -> log
|
|
42
|
+
$result -> .contains("ERROR") ? { error "Analysis failed: {$result}" }
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Mainstream habit to break:** Creating intermediate variables for each step. In rill, let data flow. Capture only for reuse.
|
|
46
|
+
|
|
47
|
+
### 2. Null Does Not Exist
|
|
48
|
+
|
|
49
|
+
No null, undefined, nil, or None. Missing values produce errors. Use `??` for defaults and `.?` for existence checks.
|
|
50
|
+
|
|
51
|
+
```rill
|
|
52
|
+
[name: "alice"] :> $user
|
|
53
|
+
$user.name ?? "anonymous" # Default if missing
|
|
54
|
+
$user.?email # Returns true or false
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Mainstream habit to break:** Checking for null before access. In rill, decide upfront what the default is, or let the error surface.
|
|
58
|
+
|
|
59
|
+
### 3. No Truthiness
|
|
60
|
+
|
|
61
|
+
Conditions must evaluate to boolean. Empty strings, zero, and empty lists are not "falsy."
|
|
62
|
+
|
|
63
|
+
```rill
|
|
64
|
+
# These are errors — conditions must be boolean:
|
|
65
|
+
# "" ? "yes" ERROR
|
|
66
|
+
# 0 ? "yes" ERROR
|
|
67
|
+
# [] ? "yes" ERROR
|
|
68
|
+
|
|
69
|
+
# Explicit boolean conversion required:
|
|
70
|
+
"" -> .empty ? "empty"
|
|
71
|
+
0 -> ($ == 0) ? "zero"
|
|
72
|
+
[] -> .empty ? "no items"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Mainstream habit to break:** Using values directly as conditions. In rill, convert to boolean explicitly with `.empty`, comparisons, or `:?type`.
|
|
76
|
+
|
|
77
|
+
### 4. No Exceptions
|
|
78
|
+
|
|
79
|
+
Errors halt execution. No try/catch, no error recovery stack. Validate before acting.
|
|
80
|
+
|
|
81
|
+
```text
|
|
82
|
+
# Guard early
|
|
83
|
+
$input -> .empty ? error("Input required")
|
|
84
|
+
|
|
85
|
+
# Assert constraints
|
|
86
|
+
$data -> assert :?list "Expected list"
|
|
87
|
+
|
|
88
|
+
# Check patterns in results
|
|
89
|
+
$response -> .contains("ERROR") ? error("Failed: {$response}")
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Mainstream habit to break:** Wrapping operations in try/catch. In rill, validate inputs and check outputs with conditionals.
|
|
93
|
+
|
|
94
|
+
### 5. Scopes Are Sealed
|
|
95
|
+
|
|
96
|
+
Inner scopes cannot read or modify outer variables created after the scope opens. Loop bodies cannot mutate variables from the enclosing scope.
|
|
97
|
+
|
|
98
|
+
```text
|
|
99
|
+
# This does NOT work — inner :> creates a local:
|
|
100
|
+
0 :> $count
|
|
101
|
+
[1, 2, 3] -> each { $count + 1 :> $count }
|
|
102
|
+
$count # Still 0
|
|
103
|
+
|
|
104
|
+
# Use accumulators instead:
|
|
105
|
+
[1, 2, 3] -> fold(0) { $@ + 1 } # Final: 3
|
|
106
|
+
[1, 2, 3] -> each(0) { $@ + $ } # Running: [1, 3, 6]
|
|
107
|
+
0 -> ($ < 5) @ { $ + 1 } # While: 5
|
|
108
|
+
[result: "", done: false] -> (!.done) @ { # While: "aaaaa"
|
|
109
|
+
[result: "a{.result}", .result.len == 5]
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Mainstream habit to break:** Mutating a counter or accumulator variable from inside a loop. In rill, `$` carries state forward through iterations, and `$@` holds the accumulator in fold/each(init).
|
|
114
|
+
|
|
115
|
+
### 6. Everything Is a Value
|
|
116
|
+
|
|
117
|
+
No references. All copies are deep. All comparisons are by value. Types lock on first assignment.
|
|
118
|
+
|
|
119
|
+
```rill
|
|
120
|
+
[1, 2, 3] == [1, 2, 3] # true — content equality
|
|
121
|
+
[1, 2] :> $a
|
|
122
|
+
$a :> $b # $b is an independent deep copy
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**Mainstream habit to break:** Expecting two variables to point at the same object. In rill, every binding holds its own copy.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Rillistic Idioms
|
|
130
|
+
|
|
131
|
+
### Flow, Don't Store
|
|
132
|
+
|
|
133
|
+
```text
|
|
134
|
+
# Not rillistic: unnecessary intermediates
|
|
135
|
+
"hello" :> $step1
|
|
136
|
+
$step1 -> .upper :> $step2
|
|
137
|
+
$step2 -> .len :> $step3
|
|
138
|
+
$step3
|
|
139
|
+
|
|
140
|
+
# Rillistic: let data flow
|
|
141
|
+
"hello" -> .upper -> .len
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Shorthand in Collection Operators
|
|
145
|
+
|
|
146
|
+
```rill
|
|
147
|
+
# Not rillistic: verbose closure
|
|
148
|
+
["hello", "world"] -> map |x| { $x.upper() }
|
|
149
|
+
|
|
150
|
+
# Rillistic: method shorthand
|
|
151
|
+
["hello", "world"] -> map .upper
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Defaults Over Conditionals
|
|
155
|
+
|
|
156
|
+
```text
|
|
157
|
+
# Not rillistic: verbose existence check
|
|
158
|
+
$dict.?field ? $dict.field ! "default"
|
|
159
|
+
|
|
160
|
+
# Rillistic: default operator
|
|
161
|
+
$dict.field ?? "default"
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Accumulators Over Mutation
|
|
165
|
+
|
|
166
|
+
```text
|
|
167
|
+
# Not rillistic: trying to mutate outer scope
|
|
168
|
+
"" :> $result
|
|
169
|
+
["a", "b", "c"] -> each { $result + $ :> $result }
|
|
170
|
+
|
|
171
|
+
# Rillistic: fold produces the value
|
|
172
|
+
["a", "b", "c"] -> fold("") { $@ + $ }
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Explicit Booleans Over Coercion
|
|
176
|
+
|
|
177
|
+
```rill
|
|
178
|
+
"hello" :> $str
|
|
179
|
+
$str -> .empty ? "no" ! "yes"
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### `$` in Context, Parameters in Closures
|
|
183
|
+
|
|
184
|
+
```text
|
|
185
|
+
# Rillistic: $ in inline pipes and loops
|
|
186
|
+
"hello" -> { .upper }
|
|
187
|
+
[1, 2, 3] -> each { $ * 2 }
|
|
188
|
+
0 -> ($ < 5) @ { $ + 1 }
|
|
189
|
+
|
|
190
|
+
# Rillistic: named params in stored closures
|
|
191
|
+
|x| ($x * 2) :> $double
|
|
192
|
+
5 -> $double
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
`$` binds to the current pipe context. Stored closures use named parameters because `$` is undefined when called later.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Summary Table
|
|
200
|
+
|
|
201
|
+
| Mainstream concept | rill replacement |
|
|
202
|
+
|---|---|
|
|
203
|
+
| `x = value` | `value :> $x` or `value -> transform` |
|
|
204
|
+
| `null` / `undefined` | `??` default, `.?` existence check |
|
|
205
|
+
| Truthiness (`if ""`) | `.empty`, `== 0`, `:?type` |
|
|
206
|
+
| `try { } catch { }` | `assert`, conditionals, `error()` |
|
|
207
|
+
| `for (i = 0; ...)` | `each`, `map`, `filter`, `fold` |
|
|
208
|
+
| `count += 1` in loop | `fold(0) { $@ + 1 }` or `$` accumulator |
|
|
209
|
+
| `a === b` (reference) | `==` always compares by value |
|
|
210
|
+
| `a = b` (shared ref) | `:>` always deep-copies |
|