@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.
Files changed (84) hide show
  1. package/dist/check/rules/anti-patterns.d.ts.map +1 -1
  2. package/dist/check/rules/anti-patterns.js +1 -6
  3. package/dist/check/rules/anti-patterns.js.map +1 -1
  4. package/dist/check/rules/closures.d.ts.map +1 -1
  5. package/dist/check/rules/closures.js +2 -5
  6. package/dist/check/rules/closures.js.map +1 -1
  7. package/dist/check/rules/conditionals.d.ts.map +1 -1
  8. package/dist/check/rules/conditionals.js +36 -8
  9. package/dist/check/rules/conditionals.js.map +1 -1
  10. package/dist/check/rules/formatting.d.ts.map +1 -1
  11. package/dist/check/rules/formatting.js +16 -6
  12. package/dist/check/rules/formatting.js.map +1 -1
  13. package/dist/check/rules/naming.d.ts.map +1 -1
  14. package/dist/check/rules/naming.js +9 -2
  15. package/dist/check/rules/naming.js.map +1 -1
  16. package/dist/check/rules/strings.d.ts.map +1 -1
  17. package/dist/check/rules/strings.js +1 -2
  18. package/dist/check/rules/strings.js.map +1 -1
  19. package/dist/check/rules/types.d.ts.map +1 -1
  20. package/dist/check/rules/types.js +4 -3
  21. package/dist/check/rules/types.js.map +1 -1
  22. package/dist/check/types.d.ts +2 -0
  23. package/dist/check/types.d.ts.map +1 -1
  24. package/dist/check/validator.d.ts.map +1 -1
  25. package/dist/check/validator.js +8 -0
  26. package/dist/check/validator.js.map +1 -1
  27. package/dist/check/visitor.d.ts.map +1 -1
  28. package/dist/check/visitor.js +13 -1
  29. package/dist/check/visitor.js.map +1 -1
  30. package/dist/cli-check.js +1 -0
  31. package/dist/cli-check.js.map +1 -1
  32. package/dist/index.d.ts +1 -1
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/lexer/operators.d.ts.map +1 -1
  35. package/dist/lexer/operators.js +2 -0
  36. package/dist/lexer/operators.js.map +1 -1
  37. package/dist/parser/helpers.d.ts +2 -1
  38. package/dist/parser/helpers.d.ts.map +1 -1
  39. package/dist/parser/helpers.js +62 -10
  40. package/dist/parser/helpers.js.map +1 -1
  41. package/dist/parser/parser-control.d.ts +3 -1
  42. package/dist/parser/parser-control.d.ts.map +1 -1
  43. package/dist/parser/parser-control.js +93 -0
  44. package/dist/parser/parser-control.js.map +1 -1
  45. package/dist/parser/parser-expr.d.ts.map +1 -1
  46. package/dist/parser/parser-expr.js +43 -1
  47. package/dist/parser/parser-expr.js.map +1 -1
  48. package/dist/parser/parser-functions.js +20 -3
  49. package/dist/parser/parser-functions.js.map +1 -1
  50. package/dist/parser/parser-literals.js +18 -2
  51. package/dist/parser/parser-literals.js.map +1 -1
  52. package/dist/parser/parser-script.d.ts +2 -2
  53. package/dist/parser/parser-script.d.ts.map +1 -1
  54. package/dist/parser/parser-script.js +43 -3
  55. package/dist/parser/parser-script.js.map +1 -1
  56. package/dist/runtime/core/equals.js +14 -2
  57. package/dist/runtime/core/equals.js.map +1 -1
  58. package/dist/runtime/core/eval/mixins/control-flow.d.ts.map +1 -1
  59. package/dist/runtime/core/eval/mixins/control-flow.js +83 -0
  60. package/dist/runtime/core/eval/mixins/control-flow.js.map +1 -1
  61. package/dist/runtime/core/eval/mixins/core.d.ts.map +1 -1
  62. package/dist/runtime/core/eval/mixins/core.js +16 -0
  63. package/dist/runtime/core/eval/mixins/core.js.map +1 -1
  64. package/dist/runtime/core/eval/mixins/literals.d.ts.map +1 -1
  65. package/dist/runtime/core/eval/mixins/literals.js +82 -0
  66. package/dist/runtime/core/eval/mixins/literals.js.map +1 -1
  67. package/dist/runtime/core/execute.js +6 -6
  68. package/dist/runtime/core/execute.js.map +1 -1
  69. package/dist/runtime/ext/builtins.d.ts.map +1 -1
  70. package/dist/runtime/ext/builtins.js +66 -0
  71. package/dist/runtime/ext/builtins.js.map +1 -1
  72. package/dist/types.d.ts +37 -13
  73. package/dist/types.d.ts.map +1 -1
  74. package/dist/types.js +4 -0
  75. package/dist/types.js.map +1 -1
  76. package/docs/00_INDEX.md +1 -0
  77. package/docs/05_control-flow.md +261 -2
  78. package/docs/09_strings.md +1 -1
  79. package/docs/11_reference.md +45 -0
  80. package/docs/12_examples.md +58 -55
  81. package/docs/14_host-integration.md +2 -0
  82. package/docs/15_grammar.ebnf +8 -1
  83. package/docs/18_design-principles.md +210 -0
  84. package/package.json +1 -1
@@ -2,13 +2,13 @@
2
2
 
3
3
  Examples demonstrating core language features for workflow orchestration.
4
4
 
5
- > **Note:** These examples assume the host provides `prompt()`, `error()`, and other domain functions. rill is a vanilla language—all integrations come from the host runtime. Frontmatter is opaque to rill; the host parses it and provides named variables to the script context.
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
- ```rill
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 -> ?(!.contains("READY")) {
25
- error("Requirements incomplete: {$validation}")
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
- $status -> @(!.contains("ALL COMPLETE")) {
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()} :> $final
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("Could not fix all tests")
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 -> ?(.contains("FAIL")) {
161
- error("Security review failed: {$security}")
161
+ $security -> .contains("FAIL") -> ? {
162
+ error "Security review failed: {$security}"
162
163
  }
163
164
 
164
- $performance -> ?(.contains("FAIL")) {
165
- error("Performance review failed: {$performance}")
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
- ```rill
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("DEPLOY_ENV not set")
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()} ? (.contains("RETRY"))
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 -> ?(.contains("ERROR")) {
239
- error("Failed to read: {$raw}")
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 -> ?(.empty()) {
244
- error("Analysis produced no output")
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($result)
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("Analysis failed: {$analysis}")
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 -> ?(.empty()) {
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
- ```rill
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 -> ?(.empty()) {
360
- error("Could not parse item count")
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
- $result -> @(!.contains(":::DONE:::")) {
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()} :> $final
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
 
@@ -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 |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rcrsr/rill",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Rill - An embeddable scripting language for orchestrating LLM workflows",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",