@rcrsr/rill 0.2.4 → 0.4.2

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 (59) hide show
  1. package/README.md +58 -30
  2. package/dist/check/visitor.d.ts.map +1 -1
  3. package/dist/check/visitor.js +3 -0
  4. package/dist/check/visitor.js.map +1 -1
  5. package/dist/lexer/tokenizer.d.ts.map +1 -1
  6. package/dist/lexer/tokenizer.js +3 -0
  7. package/dist/lexer/tokenizer.js.map +1 -1
  8. package/dist/parser/helpers.d.ts.map +1 -1
  9. package/dist/parser/helpers.js +16 -0
  10. package/dist/parser/helpers.js.map +1 -1
  11. package/dist/parser/parser-expr.d.ts.map +1 -1
  12. package/dist/parser/parser-expr.js +48 -13
  13. package/dist/parser/parser-expr.js.map +1 -1
  14. package/dist/parser/parser-literals.d.ts +2 -1
  15. package/dist/parser/parser-literals.d.ts.map +1 -1
  16. package/dist/parser/parser-literals.js +63 -6
  17. package/dist/parser/parser-literals.js.map +1 -1
  18. package/dist/parser/parser-variables.js +5 -0
  19. package/dist/parser/parser-variables.js.map +1 -1
  20. package/dist/runtime/core/callable.d.ts +22 -4
  21. package/dist/runtime/core/callable.d.ts.map +1 -1
  22. package/dist/runtime/core/callable.js +45 -3
  23. package/dist/runtime/core/callable.js.map +1 -1
  24. package/dist/runtime/core/context.d.ts.map +1 -1
  25. package/dist/runtime/core/context.js +1 -0
  26. package/dist/runtime/core/context.js.map +1 -1
  27. package/dist/runtime/core/equals.d.ts.map +1 -1
  28. package/dist/runtime/core/equals.js +32 -4
  29. package/dist/runtime/core/equals.js.map +1 -1
  30. package/dist/runtime/core/eval/base.d.ts.map +1 -1
  31. package/dist/runtime/core/eval/base.js +4 -1
  32. package/dist/runtime/core/eval/base.js.map +1 -1
  33. package/dist/runtime/core/eval/mixins/closures.d.ts.map +1 -1
  34. package/dist/runtime/core/eval/mixins/closures.js +71 -0
  35. package/dist/runtime/core/eval/mixins/closures.js.map +1 -1
  36. package/dist/runtime/core/eval/mixins/core.d.ts.map +1 -1
  37. package/dist/runtime/core/eval/mixins/core.js +293 -8
  38. package/dist/runtime/core/eval/mixins/core.js.map +1 -1
  39. package/dist/runtime/core/eval/mixins/literals.d.ts +2 -0
  40. package/dist/runtime/core/eval/mixins/literals.d.ts.map +1 -1
  41. package/dist/runtime/core/eval/mixins/literals.js +446 -39
  42. package/dist/runtime/core/eval/mixins/literals.js.map +1 -1
  43. package/dist/runtime/core/eval/mixins/variables.d.ts.map +1 -1
  44. package/dist/runtime/core/eval/mixins/variables.js +42 -2
  45. package/dist/runtime/core/eval/mixins/variables.js.map +1 -1
  46. package/dist/types.d.ts +20 -6
  47. package/dist/types.d.ts.map +1 -1
  48. package/dist/types.js +3 -0
  49. package/dist/types.js.map +1 -1
  50. package/docs/00_INDEX.md +4 -4
  51. package/docs/02_types.md +77 -2
  52. package/docs/04_operators.md +57 -0
  53. package/docs/06_closures.md +438 -4
  54. package/docs/11_reference.md +214 -13
  55. package/docs/15_grammar.ebnf +55 -5
  56. package/docs/18_design-principles.md +37 -0
  57. package/docs/19_cookbook.md +628 -0
  58. package/docs/99_llm-reference.txt +268 -14
  59. package/package.json +1 -1
@@ -2,10 +2,58 @@
2
2
 
3
3
  Closures are first-class values that capture their defining scope. This document covers closure semantics, binding behavior, and common patterns.
4
4
 
5
+ ## Expression Delimiters
6
+
7
+ rill has two expression delimiters with deterministic behavior:
8
+
9
+ | Delimiter | Semantics | Produces |
10
+ |-----------|-----------|----------|
11
+ | `{ body }` | Deferred (closure creation) | `ScriptCallable` |
12
+ | `( expr )` | Eager (immediate evaluation) | Result value |
13
+
14
+ **Key distinction:**
15
+ - **Parentheses `( )`** evaluate immediately and return the result
16
+ - **Braces `{ }`** create a closure for later invocation (deferred execution)
17
+
18
+ ### Pipe Target Exception
19
+
20
+ When `{ }` appears as a pipe target, it creates a closure and **immediately invokes** it:
21
+
22
+ ```rill
23
+ 5 -> { $ + 1 } # 6 (same observable result as eager evaluation)
24
+ ```
25
+
26
+ This is conceptually two steps happening in sequence:
27
+
28
+ ```text
29
+ 5 -> { $ + 1 }
30
+
31
+ Step 1: Create closure with implicit $ parameter
32
+ Step 2: Invoke closure with piped value (5) as argument
33
+
34
+ Result: 6
35
+ ```
36
+
37
+ The observable result matches eager evaluation, but the mechanism differs. This matters when understanding error messages or debugging—the closure exists momentarily before invocation.
38
+
39
+ **Comparison:**
40
+
41
+ | Expression | Mechanism | Result |
42
+ |------------|-----------|--------|
43
+ | `5 -> { $ + 1 }` | Create closure, invoke with 5 | 6 |
44
+ | `5 -> ($ + 1)` | Evaluate expression with $ = 5 | 6 |
45
+ | `{ $ + 1 } :> $fn` | Create closure, store it | closure |
46
+ | `($ + 1) :> $x` | Error: $ undefined outside pipe | — |
47
+
48
+ The last row shows the key difference: `( )` requires `$` to already be defined, while `{ }` captures `$` as a parameter for later binding.
49
+
5
50
  ## Closure Syntax
6
51
 
7
52
  ```text
8
- # No parameters (property-style)
53
+ # Block-closure (implicit $ parameter)
54
+ { body }
55
+
56
+ # Zero-parameter closure (property-style, no parameters)
9
57
  || { body }
10
58
  ||body # shorthand for simple expressions
11
59
 
@@ -18,9 +66,107 @@ Closures are first-class values that capture their defining scope. This document
18
66
  |x: string = "hi"| { body } # default with explicit type
19
67
  ```
20
68
 
21
- **Key distinction:**
22
- - **Blocks `{ }`** execute immediately
23
- - **Closures `||{ }` or `|params|{ }`** are stored for later invocation
69
+ ## Block-Closures
70
+
71
+ Block syntax `{ body }` creates a closure with an implicit `$` parameter. This enables deferred execution and reusable transformations.
72
+
73
+ ### Basic Block-Closure
74
+
75
+ ```rill
76
+ { $ + 1 } :> $increment
77
+
78
+ 5 -> $increment # 6
79
+ 10 -> $increment # 11
80
+ $increment(7) # 8 (direct call also works)
81
+ ```
82
+
83
+ The block `{ $ + 1 }` produces a closure. When invoked, `$` is bound to the argument.
84
+
85
+ ### Block-Closures vs Zero-Param Closures
86
+
87
+ | Form | Params | `isProperty` | Behavior |
88
+ |------|--------|-------------|----------|
89
+ | `{ body }` (block-closure) | `[{ name: "$" }]` | `false` | Requires argument |
90
+ | `\|\|{ body }` (zero-param) | `[]` | `true` | Auto-invokes on dict access |
91
+
92
+ ```rill
93
+ { $ * 2 } :> $double
94
+ || { 42 } :> $constant
95
+
96
+ 5 -> $double # 10 (requires argument)
97
+ $constant() # 42 (no argument needed)
98
+
99
+ # In dicts
100
+ [
101
+ double: { $ * 2 },
102
+ constant: || { 42 }
103
+ ] :> $obj
104
+
105
+ $obj.double(5) # 10 (explicit call required)
106
+ $obj.constant # 42 (auto-invoked on access)
107
+ ```
108
+
109
+ Block-closures require an argument; zero-param closures do not.
110
+
111
+ ### Type Checking
112
+
113
+ Block-closures check type at runtime:
114
+
115
+ ```rill
116
+ { $ + 1 } :> $fn
117
+
118
+ type($fn) # "closure"
119
+ $fn(5) # 6
120
+ $fn("text") # Error: Cannot add string and number
121
+ ```
122
+
123
+ ### Multi-Statement Block-Closures
124
+
125
+ Block-closures can contain multiple statements:
126
+
127
+ ```rill
128
+ {
129
+ ($ * 2) :> $doubled
130
+ "{$}: doubled is {$doubled}"
131
+ } :> $describe
132
+
133
+ 5 -> $describe # "5: doubled is 10"
134
+ ```
135
+
136
+ ### Collection Operations
137
+
138
+ Block-closures integrate with collection operators:
139
+
140
+ ```rill
141
+ [1, 2, 3] -> map { $ * 2 } # [2, 4, 6]
142
+ [1, 2, 3] -> filter { $ > 1 } # [2, 3]
143
+ [1, 2, 3] -> fold(0) { $@ + $ } # 6 ($@ is accumulator)
144
+ ```
145
+
146
+ ### Eager vs Deferred Evaluation
147
+
148
+ The choice between `( )` and `{ }` determines when code executes:
149
+
150
+ ```rill
151
+ # Eager: parentheses evaluate immediately
152
+ 5 -> ($ + 1) :> $result
153
+ $result # 6 (number, already computed)
154
+
155
+ # Deferred: braces create closure
156
+ { $ + 1 } :> $addOne
157
+ type($addOne) # "closure"
158
+ 5 -> $addOne # 6
159
+ 10 -> $addOne # 11 (invoked later with different value)
160
+
161
+ # Practical difference
162
+ (5 + 1) :> $six # 6 (immediate)
163
+ { $ + 1 } :> $fn # closure (deferred)
164
+
165
+ $six # 6
166
+ 10 -> $fn # 11
167
+ ```
168
+
169
+ Use `( )` when you want the result now. Use `{ }` when you want reusable logic.
24
170
 
25
171
  ## Late Binding
26
172
 
@@ -265,6 +411,9 @@ $fn() # 20 (late binding sees updated $x)
265
411
  ```rill
266
412
  |x| { $x + 1 } :> $inc
267
413
  $inc(5) # 6
414
+
415
+ { $ + 1 } :> $inc
416
+ $inc(5) # 6 (block-closure)
268
417
  ```
269
418
 
270
419
  ### Pipe Call
@@ -272,8 +421,13 @@ $inc(5) # 6
272
421
  ```rill
273
422
  |x| { $x + 1 } :> $inc
274
423
  5 -> $inc() # 6
424
+
425
+ { $ + 1 } :> $inc
426
+ 5 -> $inc # 6 (block-closure, no parens needed)
275
427
  ```
276
428
 
429
+ Block-closures work seamlessly with pipe syntax since `$` receives the piped value.
430
+
277
431
  ### Postfix Invocation
278
432
 
279
433
  Call closures from bracket access or expressions:
@@ -282,6 +436,9 @@ Call closures from bracket access or expressions:
282
436
  [|x| { $x * 2 }] :> $fns
283
437
  $fns[0](5) # 10
284
438
 
439
+ [{ $ * 2 }] :> $fns
440
+ $fns[0](5) # 10 (block-closure)
441
+
285
442
  || { |n| { $n * 2 } } :> $factory
286
443
  $factory()(5) # 10 (chained invocation)
287
444
  ```
@@ -300,6 +457,283 @@ $list[0] -> .upper # "HELLO"
300
457
 
301
458
  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
459
 
460
+ ## Parameter Metadata
461
+
462
+ Closures expose parameter metadata via the `.params` property. This enables runtime introspection of function signatures.
463
+
464
+ ### Basic Usage
465
+
466
+ ```rill
467
+ |x, y| { $x + $y } :> $add
468
+ $add.params
469
+ # [
470
+ # x: [type: ""],
471
+ # y: [type: ""]
472
+ # ]
473
+ ```
474
+
475
+ ### Typed Parameters
476
+
477
+ ```rill
478
+ |name: string, age: number| { "{$name}: {$age}" } :> $format
479
+ $format.params
480
+ # [
481
+ # name: [type: "string"],
482
+ # age: [type: "number"]
483
+ # ]
484
+ ```
485
+
486
+ ### Block-Closures
487
+
488
+ Block-closures have an implicit `$` parameter:
489
+
490
+ ```rill
491
+ { $ * 2 } :> $double
492
+ $double.params
493
+ # [
494
+ # $: [type: ""]
495
+ # ]
496
+ ```
497
+
498
+ ### Zero-Parameter Closures
499
+
500
+ ```rill
501
+ || { 42 } :> $constant
502
+ $constant.params
503
+ # []
504
+ ```
505
+
506
+ ### Practical Use Cases
507
+
508
+ **Generic Function Wrapper:**
509
+
510
+ ```rill
511
+ |fn| {
512
+ $fn.params -> .keys -> .len :> $count
513
+ "Function has {$count} parameter(s)"
514
+ } :> $describe
515
+
516
+ |x, y| { $x + $y } :> $add
517
+ $describe($add) # "Function has 2 parameter(s)"
518
+ ```
519
+
520
+ **Validation:**
521
+
522
+ ```text
523
+ |fn| {
524
+ $fn.params -> .entries -> each {
525
+ $[1].type -> .empty ? "Missing type annotation: {$[0]}" ! ""
526
+ } -> filter { !$ -> .empty }
527
+ } :> $checkTypes
528
+
529
+ |x, y: number| { $x + $y } :> $partial
530
+ $checkTypes($partial) # ["Missing type annotation: x"]
531
+ ```
532
+
533
+ ## Parameter Annotations
534
+
535
+ Parameters can have their own annotations using `^(key: value)` syntax after the parameter name. These attach metadata to individual parameters for validation, configuration, or documentation purposes.
536
+
537
+ ### Syntax and Ordering
538
+
539
+ Parameter annotations appear in a specific order:
540
+
541
+ ```text
542
+ |paramName: type ^(annotations) = default| body
543
+ ```
544
+
545
+ **Ordering rules:**
546
+ 1. Parameter name (required)
547
+ 2. Type annotation with `:` (optional)
548
+ 3. Parameter annotations with `^()` (optional)
549
+ 4. Default value with `=` (optional)
550
+
551
+ ```rill
552
+ |x: number ^(min: 0, max: 100)|($x) :> $validate
553
+ |name: string ^(required: true) = "guest"|($name) :> $greet
554
+ |count ^(cache: true) = 0|($count) :> $process
555
+ ```
556
+
557
+ ### Access Pattern
558
+
559
+ Parameter annotations are accessed via `.params.paramName.__annotations.key`:
560
+
561
+ ```rill
562
+ |x: number ^(min: 0, max: 100), y: string|($x + $y) :> $fn
563
+
564
+ $fn.params
565
+ # Returns:
566
+ # [
567
+ # x: [type: "number", __annotations: [min: 0, max: 100]],
568
+ # y: [type: "string"]
569
+ # ]
570
+
571
+ $fn.params.x.__annotations.min # 0
572
+ $fn.params.x.__annotations.max # 100
573
+ $fn.params.y.?__annotations # false (no annotations on y)
574
+ ```
575
+
576
+ ### Validation Metadata
577
+
578
+ Use parameter annotations to specify constraints:
579
+
580
+ ```rill
581
+ |value: number ^(min: 0, max: 100)|($value) :> $bounded
582
+
583
+ $bounded.params.value.__annotations.min # 0
584
+ $bounded.params.value.__annotations.max # 100
585
+ ```
586
+
587
+ **Generic validator pattern:**
588
+
589
+ ```text
590
+ |fn, arg| {
591
+ $fn.params -> .entries -> .head -> *<$name, $meta>
592
+ $meta.?__annotations ? {
593
+ ($arg < $meta.__annotations.min) ? "Value {$arg} below min {$meta.__annotations.min}" !
594
+ ($arg > $meta.__annotations.max) ? "Value {$arg} above max {$meta.__annotations.max}" !
595
+ ""
596
+ } ! ""
597
+ } :> $validate
598
+
599
+ |x: number ^(min: 0, max: 10)|($x) :> $ranged
600
+ $validate($ranged, 15) # "Value 15 above max 10"
601
+ ```
602
+
603
+ ### Caching Hints
604
+
605
+ Mark parameters that should trigger caching behavior:
606
+
607
+ ```rill
608
+ |key: string ^(cache: true)|($key) :> $fetch
609
+
610
+ $fetch.params.key.__annotations.cache # true
611
+ ```
612
+
613
+ ### Format Specifications
614
+
615
+ Attach formatting metadata to parameters:
616
+
617
+ ```rill
618
+ |timestamp: string ^(format: "ISO8601")|($timestamp) :> $formatDate
619
+
620
+ $formatDate.params.timestamp.__annotations.format # "ISO8601"
621
+ ```
622
+
623
+ ### Multiple Annotations
624
+
625
+ Parameters can have multiple annotations:
626
+
627
+ ```rill
628
+ |email: string ^(required: true, pattern: ".*@.*", maxLength: 100)|($email) :> $validateEmail
629
+
630
+ $validateEmail.params.email.__annotations.required # true
631
+ $validateEmail.params.email.__annotations.pattern # ".*@.*"
632
+ $validateEmail.params.email.__annotations.maxLength # 100
633
+ ```
634
+
635
+ ### Annotation-Driven Logic
636
+
637
+ Use parameter annotations to drive runtime behavior:
638
+
639
+ ```text
640
+ |processor| {
641
+ $processor.params -> .entries -> each {
642
+ $[1].?__annotations ? {
643
+ $[1].__annotations.?required ? "Parameter {$[0]} is required" ! ""
644
+ } ! ""
645
+ } -> filter { !$ -> .empty }
646
+ } :> $getRequiredParams
647
+
648
+ |x, y: string ^(required: true), z|($x) :> $fn
649
+ $getRequiredParams($fn) # ["Parameter y is required"]
650
+ ```
651
+
652
+ ### Checking for Annotations
653
+
654
+ Use existence check `.?__annotations` to determine if a parameter has annotations:
655
+
656
+ ```rill
657
+ |x: number ^(min: 0), y: string|($x + $y) :> $fn
658
+
659
+ $fn.params.x.?__annotations # true
660
+ $fn.params.y.?__annotations # false
661
+ ```
662
+
663
+ ## Annotation Reflection
664
+
665
+ Closures support annotation reflection via `.^key` syntax. Annotations attach metadata to closures for runtime introspection.
666
+
667
+ **Type Restriction:** Only closures support annotation reflection. Accessing `.^key` on primitives throws `RUNTIME_TYPE_ERROR`.
668
+
669
+ ### Basic Annotation Access
670
+
671
+ ```rill
672
+ ^(min: 0, max: 100) |x|($x) :> $fn
673
+
674
+ $fn.^min # 0
675
+ $fn.^max # 100
676
+ ```
677
+
678
+ ### Complex Annotation Values
679
+
680
+ Annotations can hold any value type:
681
+
682
+ ```rill
683
+ ^(config: [timeout: 30, endpoints: ["a", "b"]]) |x|($x) :> $fn
684
+
685
+ $fn.^config.timeout # 30
686
+ $fn.^config.endpoints[0] # "a"
687
+ ```
688
+
689
+ ### Default Value Coalescing
690
+
691
+ Use the default value operator for optional annotations:
692
+
693
+ ```rill
694
+ |x|($x) :> $fn
695
+ $fn.^timeout ?? 30 # 30 (uses default when annotation missing)
696
+
697
+ ^(timeout: 60) |x|($x) :> $withTimeout
698
+ $withTimeout.^timeout ?? 30 # 60 (uses annotated value)
699
+ ```
700
+
701
+ ### Annotation-Driven Logic
702
+
703
+ ```rill
704
+ ^(enabled: true) |x|($x) :> $processor
705
+
706
+ $processor.^enabled ? "processing" ! "disabled" # "processing"
707
+ ```
708
+
709
+ ### Dynamic Annotations
710
+
711
+ Annotation values are evaluated at closure creation:
712
+
713
+ ```rill
714
+ 10 :> $base
715
+ ^(limit: $base * 10) |x|($x) :> $fn
716
+ $fn.^limit # 100
717
+ ```
718
+
719
+ ### Error Cases
720
+
721
+ **Undefined Annotation Key:**
722
+
723
+ ```rill
724
+ |x|($x) :> $fn
725
+ $fn.^missing # Error: RUNTIME_UNDEFINED_ANNOTATION
726
+ ```
727
+
728
+ **Non-Closure Type:**
729
+
730
+ ```text
731
+ "hello" :> $str
732
+ $str.^key # Error: RUNTIME_TYPE_ERROR
733
+ ```
734
+
735
+ All primitive types (string, number, boolean, list, dict) throw `RUNTIME_TYPE_ERROR` when accessing `.^key`.
736
+
303
737
  ## Error Behavior
304
738
 
305
739
  ### Undefined Variables