@rcrsr/rill 0.16.0 → 0.18.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/README.md +37 -21
- package/dist/ast-nodes.d.ts +14 -4
- package/dist/ast-unions.d.ts +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -0
- package/dist/error-registry.js +228 -0
- package/dist/ext/crypto/index.d.ts +3 -3
- package/dist/ext/crypto/index.js +62 -59
- package/dist/ext/exec/index.d.ts +3 -3
- package/dist/ext/exec/index.js +15 -9
- package/dist/ext/fetch/index.d.ts +3 -3
- package/dist/ext/fetch/index.js +17 -12
- package/dist/ext/fetch/request.js +1 -1
- package/dist/ext/fs/index.d.ts +3 -3
- package/dist/ext/fs/index.js +256 -266
- package/dist/ext/fs/sandbox.d.ts +18 -0
- package/dist/ext/fs/sandbox.js +33 -0
- package/dist/ext/kv/index.d.ts +3 -3
- package/dist/ext/kv/index.js +198 -196
- package/dist/ext/kv/store.d.ts +1 -1
- package/dist/ext/kv/store.js +2 -1
- package/dist/ext-parse-bridge.d.ts +10 -0
- package/dist/ext-parse-bridge.js +10 -0
- package/dist/generated/introspection-data.d.ts +1 -1
- package/dist/generated/introspection-data.js +385 -296
- package/dist/generated/version-data.d.ts +1 -1
- package/dist/generated/version-data.js +2 -2
- package/dist/highlight-map.js +1 -0
- package/dist/index.d.ts +1 -4
- package/dist/index.js +1 -5
- package/dist/lexer/operators.js +1 -0
- package/dist/parser/helpers.js +1 -0
- package/dist/parser/parser-expr.js +44 -5
- package/dist/parser/parser-literals.js +111 -4
- package/dist/parser/parser-shape.js +2 -2
- package/dist/parser/parser-types.js +12 -0
- package/dist/parser/parser-use.js +26 -3
- package/dist/parser/parser.d.ts +2 -0
- package/dist/parser/parser.js +2 -0
- package/dist/runtime/core/callable.d.ts +24 -13
- package/dist/runtime/core/callable.js +71 -38
- package/dist/runtime/core/context.d.ts +2 -13
- package/dist/runtime/core/context.js +80 -79
- package/dist/runtime/core/eval/base.d.ts +2 -2
- package/dist/runtime/core/eval/base.js +2 -0
- package/dist/runtime/core/eval/evaluator.d.ts +1 -1
- package/dist/runtime/core/eval/index.d.ts +3 -3
- package/dist/runtime/core/eval/index.js +11 -0
- package/dist/runtime/core/eval/mixins/closures.js +381 -41
- package/dist/runtime/core/eval/mixins/collections.js +81 -6
- package/dist/runtime/core/eval/mixins/control-flow.js +1 -1
- package/dist/runtime/core/eval/mixins/conversion.js +61 -115
- package/dist/runtime/core/eval/mixins/core.js +17 -4
- package/dist/runtime/core/eval/mixins/expressions.js +36 -27
- package/dist/runtime/core/eval/mixins/extraction.js +2 -3
- package/dist/runtime/core/eval/mixins/list-dispatch.js +1 -1
- package/dist/runtime/core/eval/mixins/literals.js +17 -6
- package/dist/runtime/core/eval/mixins/types.js +73 -54
- package/dist/runtime/core/eval/mixins/variables.js +12 -8
- package/dist/runtime/core/execute.d.ts +1 -1
- package/dist/runtime/core/field-descriptor.d.ts +3 -3
- package/dist/runtime/core/field-descriptor.js +2 -1
- package/dist/runtime/core/introspection.d.ts +2 -2
- package/dist/runtime/core/introspection.js +7 -6
- package/dist/runtime/core/resolvers.d.ts +1 -1
- package/dist/runtime/core/signals.d.ts +6 -1
- package/dist/runtime/core/signals.js +9 -0
- package/dist/runtime/core/types/constructors.d.ts +54 -0
- package/dist/runtime/core/types/constructors.js +201 -0
- package/dist/runtime/core/types/guards.d.ts +42 -0
- package/dist/runtime/core/types/guards.js +88 -0
- package/dist/runtime/core/types/index.d.ts +18 -0
- package/dist/runtime/core/types/index.js +19 -0
- package/dist/runtime/core/types/markers.d.ts +12 -0
- package/dist/runtime/core/types/markers.js +7 -0
- package/dist/runtime/core/types/operations.d.ts +98 -0
- package/dist/runtime/core/types/operations.js +804 -0
- package/dist/runtime/core/types/registrations.d.ts +126 -0
- package/dist/runtime/core/types/registrations.js +751 -0
- package/dist/runtime/core/{types.d.ts → types/runtime.d.ts} +22 -10
- package/dist/runtime/core/types/structures.d.ts +146 -0
- package/dist/runtime/core/types/structures.js +12 -0
- package/dist/runtime/core/values.d.ts +29 -209
- package/dist/runtime/core/values.js +56 -968
- package/dist/runtime/ext/builtins.js +88 -68
- package/dist/runtime/ext/extensions.d.ts +31 -125
- package/dist/runtime/ext/extensions.js +2 -94
- package/dist/runtime/ext/test-context.d.ts +28 -0
- package/dist/runtime/ext/test-context.js +155 -0
- package/dist/runtime/index.d.ts +12 -12
- package/dist/runtime/index.js +13 -5
- package/dist/signature-parser.d.ts +2 -2
- package/dist/signature-parser.js +14 -14
- package/dist/token-types.d.ts +1 -0
- package/dist/token-types.js +1 -0
- package/package.json +1 -1
- /package/dist/runtime/core/{types.js → types/runtime.js} +0 -0
|
@@ -85,7 +85,7 @@ Pattern summary:
|
|
|
85
85
|
### 6. No Exceptions
|
|
86
86
|
|
|
87
87
|
Errors halt execution. No try/catch. Use conditionals for error handling.
|
|
88
|
-
|
|
88
|
+
Keywords: \`assert\` (validate condition), \`error\` (halt with message).
|
|
89
89
|
|
|
90
90
|
### 7. Value Semantics (No References)
|
|
91
91
|
|
|
@@ -95,45 +95,6 @@ All copies are deep. All comparisons are by value. No object identity.
|
|
|
95
95
|
list[1, 2] => \$a
|
|
96
96
|
\$a => \$b # \$b is an independent deep copy
|
|
97
97
|
|
|
98
|
-
## Grammar Patterns
|
|
99
|
-
|
|
100
|
-
rill uses two bracket forms as grammar constructs (not function calls):
|
|
101
|
-
|
|
102
|
-
**\`keyword[...]\`** — collection literals. Keyword and \`[\` must be adjacent (no space):
|
|
103
|
-
|
|
104
|
-
list[1, 2, 3] # list
|
|
105
|
-
dict[name: "alice", age: 30] # dict (key: value pairs)
|
|
106
|
-
tuple[1, 2, 3] # positional argument unpacking
|
|
107
|
-
ordered[a: 1, b: 2] # named argument unpacking
|
|
108
|
-
|
|
109
|
-
list [1, 2] # ERROR: RILL-P007 (space before bracket)
|
|
110
|
-
|
|
111
|
-
**\`keyword<...>\`** — extraction operators. Custom grammar with \`,\` or \`:\` separators:
|
|
112
|
-
|
|
113
|
-
list[1, 2, 3] -> destruct<\$a, \$b, \$c> # destructure into variables
|
|
114
|
-
list[0,1,2,3] -> slice<1:3> # slice with start:stop:step
|
|
115
|
-
|
|
116
|
-
These are NOT function calls. They are parsed as special syntax by the compiler.
|
|
117
|
-
|
|
118
|
-
## Callable Types
|
|
119
|
-
|
|
120
|
-
rill has three callable types:
|
|
121
|
-
|
|
122
|
-
| Type | Source | Examples |
|
|
123
|
-
|------|--------|---------|
|
|
124
|
-
| Closure | Script-defined | \`\\|x\\| { \$x * 2 }\`, \`{ \$ + 1 }\` |
|
|
125
|
-
| Built-in function | Shipped with runtime | \`log\`, \`json\`, \`range\`, \`chain\`, \`error\` |
|
|
126
|
-
| Host function | Registered by host app | \`app::fetch()\`, \`app::prompt()\` |
|
|
127
|
-
|
|
128
|
-
The \`\$\` prefix disambiguates callables from variables and keys:
|
|
129
|
-
|
|
130
|
-
name() -> built-in or host function call
|
|
131
|
-
\$name() -> closure invocation
|
|
132
|
-
\$name -> variable reference
|
|
133
|
-
name -> dict key literal
|
|
134
|
-
|
|
135
|
-
Without \`\$\`, \`process(data)\` is ambiguous: is \`process\` a function or stored closure? Is \`data\` a variable or key? This would require tracking all declarations.
|
|
136
|
-
|
|
137
98
|
## Syntax Quick Reference
|
|
138
99
|
|
|
139
100
|
Variables: \$name (always prefixed with \$)
|
|
@@ -153,8 +114,28 @@ Without \`\$\`, \`process(data)\` is ambiguous: is \`process\` a function or sto
|
|
|
153
114
|
Ordered: ordered[a: 1, b: 2] # named argument unpacking (preserves order)
|
|
154
115
|
Closures: |x|(\$x + 1) # like lambda/arrow functions
|
|
155
116
|
Type annot: "hi" => \$x:string # lock type on capture
|
|
117
|
+
Modules: use<module:greetings> => \$g # host-provided imports
|
|
156
118
|
Comments: # single line only
|
|
157
119
|
|
|
120
|
+
CRITICAL: \`keyword[\` must have NO space before \`[\`. \`list [1]\` is ERROR (RILL-P007).
|
|
121
|
+
|
|
122
|
+
## Callable Types
|
|
123
|
+
|
|
124
|
+
rill has three callable types:
|
|
125
|
+
|
|
126
|
+
| Type | Source | Examples |
|
|
127
|
+
|------|--------|---------|
|
|
128
|
+
| Closure | Script-defined | \`\\|x\\| { \$x * 2 }\`, \`{ \$ + 1 }\` |
|
|
129
|
+
| Built-in function | Shipped with runtime | \`log\`, \`json\`, \`range\`, \`repeat\`, \`chain\` |
|
|
130
|
+
| Host function | Registered by host app | \`app::fetch()\`, \`app::prompt()\` |
|
|
131
|
+
|
|
132
|
+
The \`\$\` prefix disambiguates callables from variables and keys:
|
|
133
|
+
|
|
134
|
+
name() -> built-in or host function call
|
|
135
|
+
\$name() -> closure invocation
|
|
136
|
+
\$name -> variable reference
|
|
137
|
+
name -> dict key literal
|
|
138
|
+
|
|
158
139
|
## Pipes and \$ Binding
|
|
159
140
|
|
|
160
141
|
\`\$\` is the current piped value. Its meaning depends on context:
|
|
@@ -173,12 +154,27 @@ Without \`\$\`, \`process(data)\` is ambiguous: is \`process\` a function or sto
|
|
|
173
154
|
Implied \`\$\`: bare \`.method()\` means \`\$ -> .method()\`.
|
|
174
155
|
Example: \`"hello" -> .upper\` is the same as \`"hello" -> \$.upper()\`.
|
|
175
156
|
|
|
157
|
+
Always prefer implicit \`\$\` shorthand:
|
|
158
|
+
|
|
159
|
+
\$.method() -> .method "x" -> .upper (not \$.upper())
|
|
160
|
+
func(\$) -> func "x" -> log (not log(\$))
|
|
161
|
+
\$fn(\$) -> \$fn 5 -> \$double (not \$double(\$))
|
|
162
|
+
|
|
163
|
+
Don't capture just to continue — use line continuation instead:
|
|
164
|
+
|
|
165
|
+
# avoid # good
|
|
166
|
+
"x" => \$a "x"
|
|
167
|
+
\$a -> .upper => \$b -> .upper
|
|
168
|
+
\$b -> .len -> .len
|
|
169
|
+
|
|
170
|
+
Only capture when the variable is reused later in the code.
|
|
171
|
+
|
|
176
172
|
## Control Flow
|
|
177
173
|
|
|
178
174
|
Conditional (if-else):
|
|
179
175
|
|
|
180
176
|
cond ? then_expr ! else_expr
|
|
181
|
-
cond ? then_expr # else returns
|
|
177
|
+
cond ? then_expr # else returns empty string
|
|
182
178
|
|
|
183
179
|
Piped conditional (\`\$\` becomes condition):
|
|
184
180
|
|
|
@@ -240,8 +236,52 @@ Pass (returns \`\$\` unchanged, explicit no-op):
|
|
|
240
236
|
|
|
241
237
|
Note: \`pass\` requires pipe context. Using \`pass\` without \`\$\` throws error.
|
|
242
238
|
|
|
239
|
+
## Closures
|
|
240
|
+
|
|
241
|
+
**Block-closures:** \`{ body }\` in expression position
|
|
242
|
+
|
|
243
|
+
{ \$ + 1 } => \$inc # implicit \$ parameter
|
|
244
|
+
\$inc(5) # 6
|
|
245
|
+
5 -> \$inc # 6 (pipe invocation)
|
|
246
|
+
dict[x: { \$ * 2 }] # dict value is closure
|
|
247
|
+
|
|
248
|
+
**Explicit closures:** \`|params| body\`
|
|
249
|
+
|
|
250
|
+
|x|(\$x + 1) => \$inc # named parameter
|
|
251
|
+
|a, b|(\$a + \$b) => \$add # multiple params
|
|
252
|
+
|x = 0|(\$x + 1) => \$inc_or_one # default value
|
|
253
|
+
|x: number|(\$x + 1) => \$typed # type annotation
|
|
254
|
+
|
|
255
|
+
\`{ body }\` vs \`( expr )\` distinction:
|
|
256
|
+
|
|
257
|
+
| Syntax | Semantics | Example |
|
|
258
|
+
|---|---|---|
|
|
259
|
+
| \`{ body }\` | Deferred (closure) | \`{ \$ + 1 } => \$fn\` |
|
|
260
|
+
| \`( expr )\` | Eager (immediate eval) | \`( 5 + 1 ) => \$x\` gives 6 |
|
|
261
|
+
|
|
262
|
+
LATE BINDING: closures capture scope, not values. Variables resolve at call time.
|
|
263
|
+
|
|
264
|
+
\`\$\` vs named params:
|
|
265
|
+
- Use \`\$\` in inline pipes and loops: \`"hello" -> { .upper }\`
|
|
266
|
+
- Use named params in stored closures: \`|x| (\$x * 2) => \$double\`
|
|
267
|
+
- \`\$\` is undefined when a stored closure is called later — always use params.
|
|
268
|
+
|
|
269
|
+
Zero-param dict closures (methods):
|
|
270
|
+
|
|
271
|
+
dict[count: 3, double: ||{ \$.count * 2 }] => \$obj
|
|
272
|
+
\$obj.double # 6 (\$ is bound to dict)
|
|
273
|
+
|
|
243
274
|
## Collection Operators
|
|
244
275
|
|
|
276
|
+
Choose the right operator:
|
|
277
|
+
|
|
278
|
+
Transform each item? -> map { }
|
|
279
|
+
Side effects per item? -> each { }
|
|
280
|
+
Single result from list? -> fold(init) { \$@ + \$ }
|
|
281
|
+
Filter items? -> filter { }
|
|
282
|
+
Results + running total? -> each(init) { ... \$@ ... }
|
|
283
|
+
Loop with state? -> (cond) @ { } (see Control Flow)
|
|
284
|
+
|
|
245
285
|
| Operator | Execution | Returns | Break? |
|
|
246
286
|
|---|---|---|---|
|
|
247
287
|
| \`-> each { }\` | sequential | all body results | yes |
|
|
@@ -278,24 +318,232 @@ String iteration (iterates over characters):
|
|
|
278
318
|
"abc" -> each { "{\$}!" } # list["a!", "b!", "c!"]
|
|
279
319
|
"hello" -> filter { \$ != "l" } # list["h", "e", "o"]
|
|
280
320
|
|
|
281
|
-
##
|
|
321
|
+
## Property Access
|
|
282
322
|
|
|
283
|
-
|
|
323
|
+
\$data.field # dict field
|
|
324
|
+
\$data[0], \$data[-1] # list index (negative from end)
|
|
325
|
+
\$data.\$key # variable as key
|
|
326
|
+
\$data.(\$i + 1) # computed key
|
|
327
|
+
\$data.(a || b) # try keys left-to-right
|
|
328
|
+
\$data.field ?? "default" # default if missing
|
|
329
|
+
\$data.?field # existence check (boolean)
|
|
330
|
+
\$data.?\$keyVar # variable existence check
|
|
331
|
+
\$data.?(\$expr) # computed existence check
|
|
332
|
+
\$data.?field&string # existence AND type check
|
|
333
|
+
\$data.?\$key&number # variable existence + type check
|
|
334
|
+
\$data.?(\$a -> "{\$}_b")&list # computed existence + type check
|
|
284
335
|
|
|
285
|
-
|
|
336
|
+
Dict keys take priority over built-in methods. A dict with key \`len\` returns that key's value, not the built-in \`.len\` method.
|
|
286
337
|
|
|
287
|
-
|
|
288
|
-
\$inc(5) # 6
|
|
289
|
-
5 -> \$inc # 6 (pipe invocation)
|
|
290
|
-
dict[x: { \$ * 2 }] # dict value is closure
|
|
291
|
-
{ "hi" }:?closure # true
|
|
338
|
+
## Types and Assertions
|
|
292
339
|
|
|
293
|
-
**
|
|
340
|
+
**Type names:**
|
|
294
341
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
342
|
+
string number bool list dict ordered
|
|
343
|
+
tuple closure vector iterator any type
|
|
344
|
+
|
|
345
|
+
**Type assertion** \`:type\` — errors if value does not match:
|
|
346
|
+
|
|
347
|
+
42:number # passes
|
|
348
|
+
"x":number # RILL-R002: expected number, got string
|
|
349
|
+
\$val:list(string) # assert parameterized type
|
|
350
|
+
|
|
351
|
+
**Type check** \`:?type\` — returns boolean, never errors:
|
|
352
|
+
|
|
353
|
+
42:?number # true
|
|
354
|
+
"x":?number # false
|
|
355
|
+
|
|
356
|
+
Both forms work as pipe targets:
|
|
357
|
+
|
|
358
|
+
\$val -> :number # assert in pipe
|
|
359
|
+
\$val -> :?number # check in pipe
|
|
360
|
+
|
|
361
|
+
**Union types** (\`T1|T2\`) — match any member type:
|
|
362
|
+
|
|
363
|
+
42 -> :string|number # passes (number matches)
|
|
364
|
+
42:?string|number # true
|
|
365
|
+
"hello" => \$x:string|number # capture with union type
|
|
366
|
+
|
|
367
|
+
**Enforcing dict shapes** with \`dict(field: type, ...)\`:
|
|
368
|
+
|
|
369
|
+
\$data -> :dict(name: string, age: number) # assert exact fields and types
|
|
370
|
+
\$data:?dict(name: string, age: number) # check without error
|
|
371
|
+
|
|
372
|
+
Use in closure params to validate input structure:
|
|
373
|
+
|
|
374
|
+
|d: dict(name: string, age: number)| {
|
|
375
|
+
"{\$d.name} is {\$d.age}"
|
|
376
|
+
} => \$format_person
|
|
377
|
+
|
|
378
|
+
**Parameterized type constructors** (structural types):
|
|
379
|
+
|
|
380
|
+
list(string) # list of strings
|
|
381
|
+
dict(name: string, age: number) # dict with named fields
|
|
382
|
+
tuple(number, string) # positional tuple
|
|
383
|
+
ordered(x: number, y: number) # named ordered args
|
|
384
|
+
|
|
385
|
+
**Type conversion** \`:>type\` — explicit conversion between compatible types:
|
|
386
|
+
|
|
387
|
+
42 -> :>string # "42"
|
|
388
|
+
"3.14" -> :>number # 3.14
|
|
389
|
+
list[1, 2] -> :>tuple # tuple[1, 2]
|
|
390
|
+
tuple[1, 2] -> :>list # list[1, 2]
|
|
391
|
+
ordered[a: 1] -> :>dict # dict[a: 1]
|
|
392
|
+
|
|
393
|
+
## Common Patterns
|
|
394
|
+
|
|
395
|
+
**Process an API response:**
|
|
396
|
+
|
|
397
|
+
app::fetch("https://api.example.com/users") => \$response
|
|
398
|
+
\$response.data
|
|
399
|
+
-> filter { \$.active }
|
|
400
|
+
-> map { dict[name: \$.name, email: \$.email] }
|
|
401
|
+
-> each {
|
|
402
|
+
"Processing {\$.name}" -> log
|
|
403
|
+
app::send_welcome(\$.email)
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
**Build a result from a list:**
|
|
407
|
+
|
|
408
|
+
list["alice", "bob", "charlie"]
|
|
409
|
+
-> map { dict[name: \$, greeting: "Hello, {\$}!"] }
|
|
410
|
+
-> fold(dict[]) {
|
|
411
|
+
dict[...\$@, \$.name: \$.greeting]
|
|
412
|
+
}
|
|
413
|
+
# dict[alice: "Hello, alice!", bob: "Hello, bob!", charlie: "Hello, charlie!"]
|
|
414
|
+
|
|
415
|
+
**Validate and transform input:**
|
|
416
|
+
|
|
417
|
+
\$input -> assert \$:?string "Expected string input"
|
|
418
|
+
\$input
|
|
419
|
+
-> .trim
|
|
420
|
+
-> assert !.empty "Input cannot be empty"
|
|
421
|
+
-> .split(",")
|
|
422
|
+
-> map .trim
|
|
423
|
+
-> filter (!.empty)
|
|
424
|
+
|
|
425
|
+
**Stateful retry loop:**
|
|
426
|
+
|
|
427
|
+
dict[attempts: 0, max: 3, result: "", done: false]
|
|
428
|
+
-> (!\$.done && \$.attempts < \$.max) @ {
|
|
429
|
+
\$.attempts + 1 => \$try
|
|
430
|
+
app::fetch(\$url) => \$res
|
|
431
|
+
\$res.?status&number
|
|
432
|
+
? (\$res.status == 200)
|
|
433
|
+
? dict[attempts: \$try, max: \$.max, result: \$res.body, done: true]
|
|
434
|
+
! dict[attempts: \$try, max: \$.max, result: "", done: false]
|
|
435
|
+
! dict[attempts: \$try, max: \$.max, result: "", done: false]
|
|
436
|
+
}
|
|
437
|
+
-> \$.done ? \$.result ! error "Failed after {\$.max} attempts"
|
|
438
|
+
|
|
439
|
+
**Reduce with running state:**
|
|
440
|
+
|
|
441
|
+
list[10, 25, 5, 40, 15]
|
|
442
|
+
-> each(dict[sum: 0, max: 0]) {
|
|
443
|
+
\$.sum + \$@ => \$new_sum
|
|
444
|
+
(\$ > \$@.max) ? \$ ! \$@.max => \$new_max
|
|
445
|
+
dict[sum: \$new_sum, max: \$new_max]
|
|
446
|
+
}
|
|
447
|
+
# each item produces the running state; last element is final
|
|
448
|
+
|
|
449
|
+
## Method Reference
|
|
450
|
+
|
|
451
|
+
### String Methods
|
|
452
|
+
|
|
453
|
+
| Method | Description |
|
|
454
|
+
|---|---|
|
|
455
|
+
| \`.len\` | length |
|
|
456
|
+
| \`.empty\` | is empty string |
|
|
457
|
+
| \`.trim\` | remove whitespace |
|
|
458
|
+
| \`.upper\` | uppercase |
|
|
459
|
+
| \`.lower\` | lowercase |
|
|
460
|
+
| \`.head\` | first character |
|
|
461
|
+
| \`.tail\` | last character |
|
|
462
|
+
| \`.at(i)\` | character at index |
|
|
463
|
+
| \`.first\` | iterator at first character |
|
|
464
|
+
| \`.split(sep)\` | split into list (default: newline) |
|
|
465
|
+
| \`.lines\` | split on newlines |
|
|
466
|
+
| \`.contains(s)\` | substring check |
|
|
467
|
+
| \`.starts_with(s)\` | prefix check |
|
|
468
|
+
| \`.ends_with(s)\` | suffix check |
|
|
469
|
+
| \`.index_of(s)\` | first match position (-1 if none) |
|
|
470
|
+
| \`.replace(p, r)\` | replace first regex match |
|
|
471
|
+
| \`.replace_all(p, r)\` | replace all regex matches |
|
|
472
|
+
| \`.match(regex)\` | first match info (dict with matched, index, groups) |
|
|
473
|
+
| \`.is_match(regex)\` | boolean regex check |
|
|
474
|
+
| \`.repeat(n)\` | repeat n times |
|
|
475
|
+
| \`.pad_start(n, f)\` | pad start |
|
|
476
|
+
| \`.pad_end(n, f)\` | pad end |
|
|
477
|
+
|
|
478
|
+
### List Methods
|
|
479
|
+
|
|
480
|
+
| Method | Description |
|
|
481
|
+
|---|---|
|
|
482
|
+
| \`.len\` | length |
|
|
483
|
+
| \`.empty\` | is empty |
|
|
484
|
+
| \`.head\` | first element |
|
|
485
|
+
| \`.tail\` | last element |
|
|
486
|
+
| \`.first\` | iterator at first element |
|
|
487
|
+
| \`.at(i)\` | element at index |
|
|
488
|
+
| \`.join(sep)\` | join elements with separator |
|
|
489
|
+
| \`.has(val)\` | contains value (deep equality) |
|
|
490
|
+
| \`.has_any(list)\` | contains any value from candidates |
|
|
491
|
+
| \`.has_all(list)\` | contains all values from candidates |
|
|
492
|
+
|
|
493
|
+
### Dict Methods
|
|
494
|
+
|
|
495
|
+
| Method | Description |
|
|
496
|
+
|---|---|
|
|
497
|
+
| \`.len\` | number of entries |
|
|
498
|
+
| \`.empty\` | is empty |
|
|
499
|
+
| \`.first\` | iterator at first entry |
|
|
500
|
+
| \`.keys\` | keys as list |
|
|
501
|
+
| \`.values\` | values as list |
|
|
502
|
+
| \`.entries\` | list of \`tuple[k, v]\` pairs |
|
|
503
|
+
|
|
504
|
+
### Comparison Methods
|
|
505
|
+
|
|
506
|
+
.eq(val) == .ne(val) != .lt(val) < .gt(val) > .le(val) <= .ge(val) >=
|
|
507
|
+
Example: \$age -> .ge(18) ? "adult" ! "minor"
|
|
508
|
+
|
|
509
|
+
### Built-in Functions
|
|
510
|
+
|
|
511
|
+
| Function | Description |
|
|
512
|
+
|---|---|
|
|
513
|
+
| \`log(val)\` | print and pass through |
|
|
514
|
+
| \`json(val)\` | convert to JSON string |
|
|
515
|
+
| \`identity(val)\` | returns input unchanged |
|
|
516
|
+
| \`range(start, end, step?)\` | number sequence (iterator) |
|
|
517
|
+
| \`repeat(val, count)\` | repeat value n times (iterator) |
|
|
518
|
+
| \`enumerate(coll)\` | lists: \`tuple[index, value]\`; dicts: \`tuple[index, key, value]\` |
|
|
519
|
+
| \`chain(\$fn)\` | apply single closure; chain(val, list[\$f, \$g]) applies in sequence |
|
|
520
|
+
|
|
521
|
+
## Style
|
|
522
|
+
|
|
523
|
+
### Naming: snake_case
|
|
524
|
+
|
|
525
|
+
\$user_name, \$item_list, \$is_valid # variables
|
|
526
|
+
\$double_value, \$cleanup_text # closures
|
|
527
|
+
dict[first_name: "x", last_name: "y"] # dict keys
|
|
528
|
+
|
|
529
|
+
### Spacing
|
|
530
|
+
|
|
531
|
+
Operators: space both sides 5 + 3, \$x -> .upper, "a" => \$b
|
|
532
|
+
Parentheses: no inner space (\$x + 1), (\$ > 3) ? "yes"
|
|
533
|
+
Braces: space inside { \$x + 1 }, each { \$ * 2 }
|
|
534
|
+
Brackets: no inner space \$list[0], \$dict.items[1]
|
|
535
|
+
Literals: space after , and : list[1, 2, 3], dict[name: "x", age: 30]
|
|
536
|
+
Keyword+[ NO space between list[1, 2] (not list [1, 2] — RILL-P007)
|
|
537
|
+
Closures: space after params |x| (\$x * 2), |a, b| { \$a + \$b }
|
|
538
|
+
Methods: no space before . or ( \$str.upper(), \$list.join(", ")
|
|
539
|
+
Pipes: space both sides "x" -> .upper -> .len
|
|
540
|
+
Continuations: indent 2 spaces \$data
|
|
541
|
+
-> .filter { \$.active }
|
|
542
|
+
-> map { \$.name }
|
|
543
|
+
|
|
544
|
+
## Extended Reference
|
|
545
|
+
|
|
546
|
+
### Typed Closures
|
|
299
547
|
|
|
300
548
|
**Anonymous typed closures:** \`|type|{ body }\` — reserved type keyword as param, \`\$\` is type-checked piped input
|
|
301
549
|
|
|
@@ -305,7 +553,7 @@ Two ways to create closures:
|
|
|
305
553
|
5 -> \$double # 10
|
|
306
554
|
"hi" -> \$double # RILL-R001: expected number, got string
|
|
307
555
|
|
|
308
|
-
All
|
|
556
|
+
All 12 type names valid: string, number, bool, list, dict, ordered, tuple, closure, vector, iterator, any, type
|
|
309
557
|
Parameterized: |list(number)|{ \$ -> each { \$ * 2 } }
|
|
310
558
|
|
|
311
559
|
**Return type assertions** (enforced at runtime — RILL-R004 on mismatch):
|
|
@@ -316,10 +564,20 @@ Parameterized: |list(number)|{ \$ -> each { \$ * 2 } }
|
|
|
316
564
|
|x: number| { \$x * 2 }:string => \$fn # mismatch halts with RILL-R004
|
|
317
565
|
\$fn(5) # RILL-R004: expected string, got number
|
|
318
566
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
567
|
+
### Callable Reflection
|
|
568
|
+
|
|
569
|
+
\`.^input\` and \`.^output\` work on ALL callable types:
|
|
570
|
+
|
|
571
|
+
|x: number, y: string|(\$x) => \$fn
|
|
572
|
+
\$fn.^input # ordered(x: number, y: string)
|
|
573
|
+
\$fn.^output # any (no return type declared)
|
|
574
|
+
|
|
575
|
+
|x: number|{ \$x * 2 }:number => \$fn2
|
|
576
|
+
\$fn2.^output # number
|
|
577
|
+
|
|
578
|
+
\`.^input\` returns an ordered dict mapping parameter names to type values.
|
|
579
|
+
\`.^output\` returns the declared return type (\`any\` if undeclared).
|
|
580
|
+
Both work on script closures, built-in functions, and host functions.
|
|
323
581
|
|
|
324
582
|
**Description shorthand** (bare string in \`^(...)\` expands to \`description: <string>\`):
|
|
325
583
|
|
|
@@ -330,41 +588,7 @@ Declared return type accessible via \$fn.^output annotation.
|
|
|
330
588
|
\$get_user.^description # "Fetch profile"
|
|
331
589
|
\$get_user.^cache # true
|
|
332
590
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
| Syntax | Semantics | Example |
|
|
336
|
-
|---|---|---|
|
|
337
|
-
| \`{ body }\` | Deferred (closure) | \`{ \$ + 1 } => \$fn\` |
|
|
338
|
-
| \`( expr )\` | Eager (immediate eval) | \`( 5 + 1 ) => \$x\` gives 6 |
|
|
339
|
-
|
|
340
|
-
LATE BINDING: closures capture scope, not values. Variables resolve at call time.
|
|
341
|
-
|
|
342
|
-
\`\$\` vs named params:
|
|
343
|
-
- Use \`\$\` in inline pipes and loops: \`"hello" -> { .upper }\`
|
|
344
|
-
- Use named params in stored closures: \`|x| (\$x * 2) => \$double\`
|
|
345
|
-
- \`\$\` is undefined when a stored closure is called later — always use params.
|
|
346
|
-
|
|
347
|
-
Zero-param dict closures (methods):
|
|
348
|
-
|
|
349
|
-
dict[count: 3, double: ||{ \$.count * 2 }] => \$obj
|
|
350
|
-
\$obj.double # 6 (\$ is bound to dict)
|
|
351
|
-
|
|
352
|
-
## Property Access
|
|
353
|
-
|
|
354
|
-
\$data.field # dict field
|
|
355
|
-
\$data[0], \$data[-1] # list index (negative from end)
|
|
356
|
-
\$data.\$key # variable as key
|
|
357
|
-
\$data.(\$i + 1) # computed key
|
|
358
|
-
\$data.(a || b) # try keys left-to-right
|
|
359
|
-
\$data.field ?? "default" # default if missing
|
|
360
|
-
\$data.?field # existence check (boolean)
|
|
361
|
-
\$data.?\$keyVar # variable existence check
|
|
362
|
-
\$data.?(\$expr) # computed existence check
|
|
363
|
-
\$data.?field&string # existence AND type check
|
|
364
|
-
\$data.?\$key&number # variable existence + type check
|
|
365
|
-
\$data.?(\$a -> "{\$}_b")&list # computed existence + type check
|
|
366
|
-
|
|
367
|
-
## Dispatch Operators
|
|
591
|
+
### Dispatch Operators
|
|
368
592
|
|
|
369
593
|
**Dict dispatch** (single key) — pipe a value to a dict to match keys:
|
|
370
594
|
|
|
@@ -396,82 +620,41 @@ Note: lists require same-type elements. For mixed paths (strings and numbers), u
|
|
|
396
620
|
dict[users: list[dict[name: "Alice"]]] => \$d
|
|
397
621
|
\$d.users[0].name # "Alice"
|
|
398
622
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
**Type names:**
|
|
402
|
-
|
|
403
|
-
string number bool list dict ordered
|
|
404
|
-
tuple closure vector any type
|
|
405
|
-
|
|
406
|
-
**Type names are first-class values** — assign to variables, use in dynamic expressions:
|
|
407
|
-
|
|
408
|
-
number => \$t # store a type value
|
|
409
|
-
42 -> :\$t # dynamic assertion (same as :number)
|
|
410
|
-
42 -> :?\$t # dynamic check: true
|
|
411
|
-
42 -> :>\$t # dynamic conversion (same as :>number)
|
|
412
|
-
\$val.^type == \$t # compare against stored type
|
|
413
|
-
|
|
414
|
-
**Parameterized type constructors** (structural types):
|
|
415
|
-
|
|
416
|
-
list(string) # list of strings
|
|
417
|
-
dict(name: string, age: number) # dict with named fields
|
|
418
|
-
tuple(number, string) # positional tuple
|
|
419
|
-
ordered(x: number, y: number) # named ordered args
|
|
420
|
-
|x: number, y: string| :any # closure signature type
|
|
421
|
-
|
|
422
|
-
Closure signatures include parameter names — \`|x: number|\` and \`|y: number|\` are different types.
|
|
423
|
-
|
|
424
|
-
**Type assertion** \`:type\` — errors if value does not match:
|
|
425
|
-
|
|
426
|
-
42:number # passes
|
|
427
|
-
"x":number # RILL-R002: expected number, got string
|
|
428
|
-
\$val:list(string) # assert parameterized type
|
|
429
|
-
|
|
430
|
-
**Enforcing dict shapes** with \`dict(field: type, ...)\`:
|
|
431
|
-
|
|
432
|
-
\$data -> :dict(name: string, age: number) # assert exact fields and types
|
|
433
|
-
\$data:?dict(name: string, age: number) # check without error
|
|
434
|
-
\$data:dict(name: string) -> .name # assert then access
|
|
435
|
-
|
|
436
|
-
Use in closure params to validate input structure:
|
|
437
|
-
|
|
438
|
-
|d: dict(name: string, age: number)| {
|
|
439
|
-
"{\$d.name} is {\$d.age}"
|
|
440
|
-
} => \$format_person
|
|
623
|
+
### Extraction Operators
|
|
441
624
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
42:?number # true
|
|
445
|
-
"x":?number # false
|
|
446
|
-
\$val:?list(string) # structural check
|
|
625
|
+
Destructure (\`destruct<>\`):
|
|
447
626
|
|
|
448
|
-
|
|
627
|
+
list[1, 2, 3] -> destruct<\$a, \$b, \$c> # \$a=1, \$b=2, \$c=3
|
|
628
|
+
dict[x: 1, y: 2] -> destruct<x: \$a, y: \$b> # \$a=1, \$b=2
|
|
629
|
+
list[1, 2, 3] -> destruct<\$first, _, \$third> # _ skips element
|
|
449
630
|
|
|
450
|
-
|
|
451
|
-
\$val -> :?number # check in pipe
|
|
631
|
+
Slice (\`slice<start:stop:step>\`):
|
|
452
632
|
|
|
453
|
-
|
|
633
|
+
list[0,1,2,3,4] -> slice<1:3> # list[1, 2]
|
|
634
|
+
list[0,1,2,3,4] -> slice<-2:> # list[3, 4]
|
|
635
|
+
list[0,1,2,3,4] -> slice<::-1> # list[4,3,2,1,0] (reverse)
|
|
636
|
+
"hello" -> slice<1:4> # "ell"
|
|
454
637
|
|
|
455
|
-
|
|
456
|
-
42.^type.name # "number"
|
|
457
|
-
list[1, 2].^type.name # "list"
|
|
458
|
-
list[1, 2].^type.signature # "list(number)"
|
|
638
|
+
### List Spread
|
|
459
639
|
|
|
460
|
-
|
|
640
|
+
list[1, 2] => \$a
|
|
641
|
+
list[...\$a, 3] # list[1, 2, 3]
|
|
642
|
+
list[...\$a, ...\$b] # concatenate lists
|
|
643
|
+
list[...(\$nums -> map {\$ * 2})] # spread expression result
|
|
461
644
|
|
|
462
|
-
|
|
463
|
-
\$val.^type == \$other.^type # compare structural types
|
|
645
|
+
### Argument Unpacking
|
|
464
646
|
|
|
465
|
-
|
|
647
|
+
tuple[1, 2, 3] -> \$fn(...) # positional spread: \$fn(1, 2, 3)
|
|
648
|
+
ordered[a: 10, b: 2] -> \$fn(...) # named spread: keys must match parameter order
|
|
649
|
+
tuple[...\$list, 3] -> \$fn(...) # spread in tuple then spread into call
|
|
650
|
+
tuple[1, 2, 3] => \$args
|
|
651
|
+
\$args -> \$fn(...) # spread stored tuple
|
|
652
|
+
ordered[a: 1, b: 2] => \$named
|
|
653
|
+
\$named -> \$fn(...) # spread stored ordered
|
|
466
654
|
|
|
467
|
-
|
|
468
|
-
"3.14" -> :>number # 3.14
|
|
469
|
-
list[1, 2] -> :>tuple # tuple[1, 2]
|
|
470
|
-
tuple[1, 2] -> :>list # list[1, 2]
|
|
471
|
-
ordered[a: 1] -> :>dict # dict[a: 1]
|
|
472
|
-
dict[x: 1, y: 2] -> :>ordered(x: number, y: number) # structural
|
|
655
|
+
### Type Conversion Matrix
|
|
473
656
|
|
|
474
|
-
|
|
657
|
+
\`:>type\` compatibility (error = halts with RILL-R036, no-op = same-type identity):
|
|
475
658
|
|
|
476
659
|
Source | :>list | :>dict | :>tuple | :>ordered(sig) | :>number | :>string | :>bool
|
|
477
660
|
---------|---------|---------|---------|----------------|----------|----------|-------
|
|
@@ -489,115 +672,43 @@ Compatibility matrix (error = halts with RILL-R036, no-op = same-type identity):
|
|
|
489
672
|
⁴ true maps to 1, false maps to 0
|
|
490
673
|
⁵ 0 maps to false, 1 maps to true; all other values halt with RILL-R036
|
|
491
674
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
.eq(val) == .ne(val) != .lt(val) < .gt(val) > .le(val) <= .ge(val) >=
|
|
495
|
-
Example: \$age -> .ge(18) ? "adult" ! "minor"
|
|
496
|
-
|
|
497
|
-
## Operator Precedence (Highest to Lowest)
|
|
498
|
-
|
|
499
|
-
1. Member access: \`.field\`, \`[index]\`
|
|
500
|
-
2. Type operators: \`:type\`, \`:?type\`
|
|
501
|
-
3. Unary: \`-\`, \`!\`
|
|
502
|
-
4. Multiplicative: \`*\`, \`/\`, \`%\`
|
|
503
|
-
5. Additive: \`+\`, \`-\`
|
|
504
|
-
6. Comparison: \`==\`, \`!=\`, \`<\`, \`>\`, \`<=\`, \`>=\`
|
|
505
|
-
7. Logical AND: \`&&\`
|
|
506
|
-
8. Logical OR: \`||\`
|
|
507
|
-
9. Default: \`??\`
|
|
508
|
-
10. Pipe: \`->\`
|
|
509
|
-
11. Capture: \`=>\`
|
|
510
|
-
|
|
511
|
-
Use parentheses to override: \`(2 + 3) * 4\`
|
|
512
|
-
|
|
513
|
-
## Extraction Operators
|
|
514
|
-
|
|
515
|
-
Destructure (\`destruct<>\`):
|
|
516
|
-
|
|
517
|
-
list[1, 2, 3] -> destruct<\$a, \$b, \$c> # \$a=1, \$b=2, \$c=3
|
|
518
|
-
dict[x: 1, y: 2] -> destruct<x: \$a, y: \$b> # \$a=1, \$b=2
|
|
519
|
-
list[1, 2, 3] -> destruct<\$first, _, \$third> # _ skips element
|
|
675
|
+
**Default values in type constructors** (used with \`:>\` conversion):
|
|
520
676
|
|
|
521
|
-
|
|
677
|
+
dict(a: string, b: string = "x") # field b defaults to "x"
|
|
678
|
+
ordered(x: number, y: number = 0) # field y defaults to 0
|
|
679
|
+
tuple(string, number = 0) # trailing element defaults to 0
|
|
522
680
|
|
|
523
|
-
|
|
524
|
-
list[0,1,2,3,4] -> slice<-2:> # list[3, 4]
|
|
525
|
-
list[0,1,2,3,4] -> slice<::-1> # list[4,3,2,1,0] (reverse)
|
|
526
|
-
"hello" -> slice<1:4> # "ell"
|
|
681
|
+
dict[b: "b"] -> :>dict(b: string, a: string = "a") # dict[a: "a", b: "b"]
|
|
527
682
|
|
|
528
|
-
|
|
683
|
+
Only \`:>\` conversion fills defaults. \`:\` assertion does NOT hydrate defaults.
|
|
684
|
+
\`:>dict(...)\` and \`:>ordered(...)\` fill missing fields from type constructor defaults.
|
|
685
|
+
Nested fields are recursively hydrated.
|
|
529
686
|
|
|
530
|
-
|
|
531
|
-
list[...\$a, 3] # list[1, 2, 3]
|
|
532
|
-
list[...\$a, ...\$b] # concatenate lists
|
|
533
|
-
list[...(\$nums -> map {\$ * 2})] # spread expression result
|
|
687
|
+
### Type Inspection
|
|
534
688
|
|
|
535
|
-
|
|
689
|
+
**\`^type\` operator** — returns a type value for inspection:
|
|
536
690
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
\$args -> \$fn(...) # spread stored tuple
|
|
542
|
-
ordered[a: 1, b: 2] => \$named
|
|
543
|
-
\$named -> \$fn(...) # spread stored ordered
|
|
691
|
+
42.^type # type value for number
|
|
692
|
+
42.^type.name # "number"
|
|
693
|
+
list[1, 2].^type.name # "list"
|
|
694
|
+
list[1, 2].^type.signature # "list(number)"
|
|
544
695
|
|
|
545
|
-
|
|
696
|
+
**Type value equality:**
|
|
546
697
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
| \`.len\` | length |
|
|
550
|
-
| \`.empty\` | is empty string |
|
|
551
|
-
| \`.trim\` | remove whitespace |
|
|
552
|
-
| \`.upper\` | uppercase |
|
|
553
|
-
| \`.lower\` | lowercase |
|
|
554
|
-
| \`.head\` | first character |
|
|
555
|
-
| \`.tail\` | last character |
|
|
556
|
-
| \`.at(i)\` | character at index |
|
|
557
|
-
| \`.split(sep)\` | split into list (default: newline) |
|
|
558
|
-
| \`.lines\` | split on newlines |
|
|
559
|
-
| \`.join(sep)\` | join list with separator |
|
|
560
|
-
| \`.contains(s)\` | substring check |
|
|
561
|
-
| \`.starts_with(s)\` | prefix check |
|
|
562
|
-
| \`.ends_with(s)\` | suffix check |
|
|
563
|
-
| \`.index_of(s)\` | first match position (-1 if none) |
|
|
564
|
-
| \`.replace(p, r)\` | replace first regex match |
|
|
565
|
-
| \`.replace_all(p, r)\` | replace all regex matches |
|
|
566
|
-
| \`.match(regex)\` | first match info (dict with matched, index, groups) |
|
|
567
|
-
| \`.is_match(regex)\` | boolean regex check |
|
|
568
|
-
| \`.repeat(n)\` | repeat n times |
|
|
569
|
-
| \`.pad_start(n, f)\` | pad start |
|
|
570
|
-
| \`.pad_end(n, f)\` | pad end |
|
|
698
|
+
\$val.^type == list(number) # true if \$val is list(number)
|
|
699
|
+
\$val.^type == \$other.^type # compare structural types
|
|
571
700
|
|
|
572
|
-
|
|
701
|
+
### Modules
|
|
573
702
|
|
|
574
|
-
|
|
575
|
-
|---|---|
|
|
576
|
-
| \`.len\` | length |
|
|
577
|
-
| \`.empty\` | is empty |
|
|
578
|
-
| \`.head\` | first element |
|
|
579
|
-
| \`.tail\` | last element |
|
|
580
|
-
| \`.at(i)\` | element at index |
|
|
581
|
-
| \`.keys\` | dict keys as list |
|
|
582
|
-
| \`.values\` | dict values as list |
|
|
583
|
-
| \`.entries\` | dict as list of \`tuple[k, v]\` pairs |
|
|
584
|
-
| \`.has(val)\` | list contains value (deep equality) |
|
|
585
|
-
| \`.has_any(list)\` | list contains any value from candidates |
|
|
586
|
-
| \`.has_all(list)\` | list contains all values from candidates |
|
|
703
|
+
\`use<scheme:resource>\` resolves a named resource through a host-registered scheme resolver:
|
|
587
704
|
|
|
588
|
-
|
|
705
|
+
use<module:greetings> => \$g # load module, capture as \$g
|
|
706
|
+
\$g.hello("World") # call module member
|
|
707
|
+
use<ext:qdrant.search> # load extension member
|
|
589
708
|
|
|
590
|
-
|
|
591
|
-
|---|---|
|
|
592
|
-
| \`log(val)\` | print and pass through |
|
|
593
|
-
| \`json(val)\` | convert to JSON string |
|
|
594
|
-
| \`identity(val)\` | returns input unchanged |
|
|
595
|
-
| \`range(start, end, step?)\` | number sequence (iterator) |
|
|
596
|
-
| \`repeat(val, count)\` | repeat value n times (iterator) |
|
|
597
|
-
| \`enumerate(coll)\` | lists: \`tuple[index, value]\`; dicts: \`tuple[index, key, value]\` |
|
|
598
|
-
| \`chain(\$fn)\` | apply single closure; chain(val, list[\$f, \$g]) applies in sequence |
|
|
709
|
+
\`use<>\` returns the resolved value (dict, closure, or any rill type). The host registers resolvers for each scheme. Scripts cannot define resolvers.
|
|
599
710
|
|
|
600
|
-
|
|
711
|
+
### Iterators
|
|
601
712
|
|
|
602
713
|
Lazy sequence generation. Collection operators auto-expand iterators.
|
|
603
714
|
|
|
@@ -615,7 +726,7 @@ Iterator protocol (dict with \`value\`, \`done\`, \`next\`):
|
|
|
615
726
|
\$it.value # current element
|
|
616
727
|
\$it.next() # returns new iterator at next position
|
|
617
728
|
|
|
618
|
-
|
|
729
|
+
### Iteration Limits
|
|
619
730
|
|
|
620
731
|
Default: 10,000 iterations max for loops.
|
|
621
732
|
Override: \`^(limit: N)\` at operator level (between operator name and body).
|
|
@@ -626,7 +737,23 @@ Override: \`^(limit: N)\` at operator level (between operator name and body).
|
|
|
626
737
|
|
|
627
738
|
Invalid annotation keys produce a runtime error.
|
|
628
739
|
|
|
629
|
-
|
|
740
|
+
### Operator Precedence (Highest to Lowest)
|
|
741
|
+
|
|
742
|
+
1. Member access: \`.field\`, \`[index]\`
|
|
743
|
+
2. Type operators: \`:type\`, \`:?type\`
|
|
744
|
+
3. Unary: \`-\`, \`!\`
|
|
745
|
+
4. Multiplicative: \`*\`, \`/\`, \`%\`
|
|
746
|
+
5. Additive: \`+\`, \`-\`
|
|
747
|
+
6. Comparison: \`==\`, \`!=\`, \`<\`, \`>\`, \`<=\`, \`>=\`
|
|
748
|
+
7. Logical AND: \`&&\`
|
|
749
|
+
8. Logical OR: \`||\`
|
|
750
|
+
9. Default: \`??\`
|
|
751
|
+
10. Pipe: \`->\`
|
|
752
|
+
11. Capture: \`=>\`
|
|
753
|
+
|
|
754
|
+
Use parentheses to override: \`(2 + 3) * 4\`
|
|
755
|
+
|
|
756
|
+
### Script Return Values
|
|
630
757
|
|
|
631
758
|
| Return value | Exit code |
|
|
632
759
|
|---|---|
|
|
@@ -634,42 +761,4 @@ Invalid annotation keys produce a runtime error.
|
|
|
634
761
|
| \`false\` / empty string | 1 |
|
|
635
762
|
| \`list[0, "message"]\` | 0 with message |
|
|
636
763
|
| \`list[1, "message"]\` | 1 with message |
|
|
637
|
-
|
|
638
|
-
## Style
|
|
639
|
-
|
|
640
|
-
### Naming: snake_case
|
|
641
|
-
|
|
642
|
-
\$user_name, \$item_list, \$is_valid # variables
|
|
643
|
-
\$double_value, \$cleanup_text # closures
|
|
644
|
-
dict[first_name: "x", last_name: "y"] # dict keys
|
|
645
|
-
|
|
646
|
-
### Spacing
|
|
647
|
-
|
|
648
|
-
Operators: space both sides 5 + 3, \$x -> .upper, "a" => \$b
|
|
649
|
-
Parentheses: no inner space (\$x + 1), (\$ > 3) ? "yes"
|
|
650
|
-
Braces: space inside { \$x + 1 }, each { \$ * 2 }
|
|
651
|
-
Brackets: no inner space \$list[0], \$dict.items[1]
|
|
652
|
-
Literals: space after , and : list[1, 2, 3], dict[name: "x", age: 30]
|
|
653
|
-
Keyword+[ NO space between list[1, 2] (not list [1, 2] — RILL-P007)
|
|
654
|
-
Closures: space after params |x| (\$x * 2), |a, b| { \$a + \$b }
|
|
655
|
-
Methods: no space before . or ( \$str.upper(), \$list.join(", ")
|
|
656
|
-
Pipes: space both sides "x" -> .upper -> .len
|
|
657
|
-
Continuations: indent 2 spaces \$data
|
|
658
|
-
-> .filter { \$.active }
|
|
659
|
-
-> map { \$.name }
|
|
660
|
-
|
|
661
|
-
### Implicit \$ Shorthand (Always Prefer)
|
|
662
|
-
|
|
663
|
-
\$.method() -> .method "x" -> .upper (not \$.upper())
|
|
664
|
-
func(\$) -> func "x" -> log (not log(\$))
|
|
665
|
-
\$fn(\$) -> \$fn 5 -> \$double (not \$double(\$))
|
|
666
|
-
|
|
667
|
-
Don't capture just to continue — use line continuation instead:
|
|
668
|
-
|
|
669
|
-
# avoid # good
|
|
670
|
-
"x" => \$a "x"
|
|
671
|
-
\$a -> .upper => \$b -> .upper
|
|
672
|
-
\$b -> .len -> .len
|
|
673
|
-
|
|
674
|
-
Only capture when the variable is reused later in the code.
|
|
675
764
|
`;
|