@rcrsr/rill 0.5.0 → 0.6.1
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 +88 -162
- package/dist/generated/introspection-data.d.ts +1 -1
- package/dist/generated/introspection-data.d.ts.map +1 -1
- package/dist/generated/introspection-data.js +107 -186
- package/dist/generated/introspection-data.js.map +1 -1
- package/dist/generated/version-data.d.ts +1 -1
- package/dist/generated/version-data.js +4 -4
- package/dist/generated/version-data.js.map +1 -1
- package/dist/highlight-map.d.ts +4 -0
- package/dist/highlight-map.d.ts.map +1 -0
- package/dist/highlight-map.js +71 -0
- package/dist/highlight-map.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/lexer/errors.d.ts.map +1 -1
- package/dist/lexer/errors.js +9 -3
- package/dist/lexer/errors.js.map +1 -1
- package/dist/lexer/operators.js +1 -1
- package/dist/lexer/tokenizer.d.ts.map +1 -1
- package/dist/lexer/tokenizer.js +0 -14
- package/dist/lexer/tokenizer.js.map +1 -1
- package/dist/parser/helpers.d.ts +8 -0
- package/dist/parser/helpers.d.ts.map +1 -1
- package/dist/parser/helpers.js +4 -4
- package/dist/parser/helpers.js.map +1 -1
- package/dist/parser/index.d.ts.map +1 -1
- package/dist/parser/index.js +1 -1
- package/dist/parser/index.js.map +1 -1
- package/dist/parser/parser-collect.js +1 -1
- package/dist/parser/parser-collect.js.map +1 -1
- package/dist/parser/parser-control.js +4 -4
- package/dist/parser/parser-control.js.map +1 -1
- package/dist/parser/parser-expr.js +32 -10
- package/dist/parser/parser-expr.js.map +1 -1
- package/dist/parser/parser-extract.js +7 -3
- package/dist/parser/parser-extract.js.map +1 -1
- package/dist/parser/parser-functions.d.ts.map +1 -1
- package/dist/parser/parser-functions.js +7 -18
- package/dist/parser/parser-functions.js.map +1 -1
- package/dist/parser/parser-literals.js +15 -15
- package/dist/parser/parser-literals.js.map +1 -1
- package/dist/parser/parser-script.js +3 -3
- package/dist/parser/parser-script.js.map +1 -1
- package/dist/parser/parser-variables.js +4 -4
- package/dist/parser/parser-variables.js.map +1 -1
- package/dist/parser/state.d.ts +1 -1
- package/dist/parser/state.d.ts.map +1 -1
- package/dist/parser/state.js +2 -2
- package/dist/parser/state.js.map +1 -1
- package/dist/runtime/core/callable.d.ts +20 -0
- package/dist/runtime/core/callable.d.ts.map +1 -1
- package/dist/runtime/core/callable.js +30 -7
- package/dist/runtime/core/callable.js.map +1 -1
- package/dist/runtime/core/context.d.ts +21 -0
- package/dist/runtime/core/context.d.ts.map +1 -1
- package/dist/runtime/core/context.js +75 -4
- package/dist/runtime/core/context.js.map +1 -1
- package/dist/runtime/core/eval/base.d.ts.map +1 -1
- package/dist/runtime/core/eval/base.js +3 -3
- package/dist/runtime/core/eval/base.js.map +1 -1
- package/dist/runtime/core/eval/index.d.ts.map +1 -1
- package/dist/runtime/core/eval/index.js +2 -0
- package/dist/runtime/core/eval/index.js.map +1 -1
- package/dist/runtime/core/eval/mixins/annotations.js +3 -3
- package/dist/runtime/core/eval/mixins/annotations.js.map +1 -1
- package/dist/runtime/core/eval/mixins/closures.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/closures.js +69 -40
- package/dist/runtime/core/eval/mixins/closures.js.map +1 -1
- package/dist/runtime/core/eval/mixins/collections.js +15 -15
- package/dist/runtime/core/eval/mixins/collections.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 +12 -12
- 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 +12 -13
- package/dist/runtime/core/eval/mixins/core.js.map +1 -1
- package/dist/runtime/core/eval/mixins/expressions.js +9 -9
- package/dist/runtime/core/eval/mixins/expressions.js.map +1 -1
- package/dist/runtime/core/eval/mixins/extraction.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/extraction.js +15 -15
- package/dist/runtime/core/eval/mixins/extraction.js.map +1 -1
- package/dist/runtime/core/eval/mixins/literals.js +22 -22
- package/dist/runtime/core/eval/mixins/literals.js.map +1 -1
- package/dist/runtime/core/eval/mixins/types.js +4 -4
- package/dist/runtime/core/eval/mixins/types.js.map +1 -1
- package/dist/runtime/core/eval/mixins/variables.js +34 -34
- package/dist/runtime/core/eval/mixins/variables.js.map +1 -1
- package/dist/runtime/core/execute.js +3 -3
- package/dist/runtime/core/execute.js.map +1 -1
- package/dist/runtime/core/introspection.d.ts +30 -1
- package/dist/runtime/core/introspection.d.ts.map +1 -1
- package/dist/runtime/core/introspection.js +47 -1
- package/dist/runtime/core/introspection.js.map +1 -1
- package/dist/runtime/core/types.d.ts +11 -0
- package/dist/runtime/core/types.d.ts.map +1 -1
- package/dist/runtime/core/types.js.map +1 -1
- package/dist/runtime/ext/builtins.js +22 -22
- package/dist/runtime/ext/builtins.js.map +1 -1
- package/dist/runtime/ext/extensions.d.ts +1 -1
- package/dist/runtime/ext/extensions.d.ts.map +1 -1
- package/dist/runtime/ext/extensions.js +4 -5
- package/dist/runtime/ext/extensions.js.map +1 -1
- package/dist/runtime/index.d.ts +6 -4
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +3 -2
- package/dist/runtime/index.js.map +1 -1
- package/dist/types.d.ts +36 -37
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +440 -120
- package/dist/types.js.map +1 -1
- package/package.json +7 -66
- package/dist/check/config.d.ts +0 -20
- package/dist/check/config.d.ts.map +0 -1
- package/dist/check/config.js +0 -151
- package/dist/check/config.js.map +0 -1
- package/dist/check/fixer.d.ts +0 -39
- package/dist/check/fixer.d.ts.map +0 -1
- package/dist/check/fixer.js +0 -119
- package/dist/check/fixer.js.map +0 -1
- package/dist/check/index.d.ts +0 -10
- package/dist/check/index.d.ts.map +0 -1
- package/dist/check/index.js +0 -21
- package/dist/check/index.js.map +0 -1
- package/dist/check/rules/anti-patterns.d.ts +0 -65
- package/dist/check/rules/anti-patterns.d.ts.map +0 -1
- package/dist/check/rules/anti-patterns.js +0 -481
- package/dist/check/rules/anti-patterns.js.map +0 -1
- package/dist/check/rules/closures.d.ts +0 -66
- package/dist/check/rules/closures.d.ts.map +0 -1
- package/dist/check/rules/closures.js +0 -370
- package/dist/check/rules/closures.js.map +0 -1
- package/dist/check/rules/collections.d.ts +0 -90
- package/dist/check/rules/collections.d.ts.map +0 -1
- package/dist/check/rules/collections.js +0 -373
- package/dist/check/rules/collections.js.map +0 -1
- package/dist/check/rules/conditionals.d.ts +0 -41
- package/dist/check/rules/conditionals.d.ts.map +0 -1
- package/dist/check/rules/conditionals.js +0 -134
- package/dist/check/rules/conditionals.js.map +0 -1
- package/dist/check/rules/flow.d.ts +0 -46
- package/dist/check/rules/flow.d.ts.map +0 -1
- package/dist/check/rules/flow.js +0 -206
- package/dist/check/rules/flow.js.map +0 -1
- package/dist/check/rules/formatting.d.ts +0 -133
- package/dist/check/rules/formatting.d.ts.map +0 -1
- package/dist/check/rules/formatting.js +0 -648
- package/dist/check/rules/formatting.js.map +0 -1
- package/dist/check/rules/helpers.d.ts +0 -26
- package/dist/check/rules/helpers.d.ts.map +0 -1
- package/dist/check/rules/helpers.js +0 -66
- package/dist/check/rules/helpers.js.map +0 -1
- package/dist/check/rules/index.d.ts +0 -21
- package/dist/check/rules/index.d.ts.map +0 -1
- package/dist/check/rules/index.js +0 -78
- package/dist/check/rules/index.js.map +0 -1
- package/dist/check/rules/loops.d.ts +0 -77
- package/dist/check/rules/loops.d.ts.map +0 -1
- package/dist/check/rules/loops.js +0 -310
- package/dist/check/rules/loops.js.map +0 -1
- package/dist/check/rules/naming.d.ts +0 -21
- package/dist/check/rules/naming.d.ts.map +0 -1
- package/dist/check/rules/naming.js +0 -174
- package/dist/check/rules/naming.js.map +0 -1
- package/dist/check/rules/strings.d.ts +0 -28
- package/dist/check/rules/strings.d.ts.map +0 -1
- package/dist/check/rules/strings.js +0 -79
- package/dist/check/rules/strings.js.map +0 -1
- package/dist/check/rules/types.d.ts +0 -41
- package/dist/check/rules/types.d.ts.map +0 -1
- package/dist/check/rules/types.js +0 -167
- package/dist/check/rules/types.js.map +0 -1
- package/dist/check/types.d.ts +0 -112
- package/dist/check/types.d.ts.map +0 -1
- package/dist/check/types.js +0 -6
- package/dist/check/types.js.map +0 -1
- package/dist/check/validator.d.ts +0 -18
- package/dist/check/validator.d.ts.map +0 -1
- package/dist/check/validator.js +0 -110
- package/dist/check/validator.js.map +0 -1
- package/dist/check/visitor.d.ts +0 -33
- package/dist/check/visitor.d.ts.map +0 -1
- package/dist/check/visitor.js +0 -259
- package/dist/check/visitor.js.map +0 -1
- package/dist/cli-check.d.ts +0 -43
- package/dist/cli-check.d.ts.map +0 -1
- package/dist/cli-check.js +0 -368
- package/dist/cli-check.js.map +0 -1
- package/dist/cli-eval.d.ts +0 -15
- package/dist/cli-eval.d.ts.map +0 -1
- package/dist/cli-eval.js +0 -116
- package/dist/cli-eval.js.map +0 -1
- package/dist/cli-exec.d.ts +0 -49
- package/dist/cli-exec.d.ts.map +0 -1
- package/dist/cli-exec.js +0 -183
- package/dist/cli-exec.js.map +0 -1
- package/dist/cli-module-loader.d.ts +0 -19
- package/dist/cli-module-loader.d.ts.map +0 -1
- package/dist/cli-module-loader.js +0 -83
- package/dist/cli-module-loader.js.map +0 -1
- package/dist/cli-shared.d.ts +0 -44
- package/dist/cli-shared.d.ts.map +0 -1
- package/dist/cli-shared.js +0 -108
- package/dist/cli-shared.js.map +0 -1
- package/dist/cli.d.ts +0 -13
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -62
- package/dist/cli.js.map +0 -1
- package/dist/runtime/core/introspection-data.d.ts +0 -2
- package/dist/runtime/core/introspection-data.d.ts.map +0 -1
- package/dist/runtime/core/introspection-data.js +0 -618
- package/dist/runtime/core/introspection-data.js.map +0 -1
- package/dist/runtime/core/version-data.d.ts +0 -18
- package/dist/runtime/core/version-data.d.ts.map +0 -1
- package/dist/runtime/core/version-data.js +0 -16
- package/dist/runtime/core/version-data.js.map +0 -1
- package/docs/00_INDEX.md +0 -67
- package/docs/01_guide.md +0 -390
- package/docs/02_types.md +0 -504
- package/docs/03_variables.md +0 -324
- package/docs/04_operators.md +0 -629
- package/docs/05_control-flow.md +0 -692
- package/docs/06_closures.md +0 -787
- package/docs/07_collections.md +0 -688
- package/docs/08_iterators.md +0 -330
- package/docs/09_strings.md +0 -205
- package/docs/10_parsing.md +0 -366
- package/docs/11_reference.md +0 -600
- package/docs/12_examples.md +0 -748
- package/docs/13_modules.md +0 -519
- package/docs/14_host-integration.md +0 -985
- package/docs/15_grammar.ebnf +0 -773
- package/docs/16_conventions.md +0 -695
- package/docs/17_cli-tools.md +0 -184
- package/docs/18_design-principles.md +0 -247
- package/docs/19_cookbook.md +0 -628
- package/docs/88_errors.md +0 -902
- package/docs/99_llm-reference.txt +0 -614
- package/docs/assets/logo.png +0 -0
package/README.md
CHANGED
|
@@ -1,36 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
<img src="docs/assets/logo.png" alt="rill logo" width="280">
|
|
3
|
-
</p>
|
|
1
|
+
# @rcrsr/rill
|
|
4
2
|
|
|
5
|
-
|
|
3
|
+
Embeddable runtime for the [rill](https://github.com/rcrsr/rill) workflow language. Zero dependencies. Browser and Node.js compatible.
|
|
6
4
|
|
|
7
|
-
>
|
|
8
|
-
> **This language is experimental.** While usable, there may be significant bugs. Breaking changes will occur until v1.0.
|
|
5
|
+
> **Experimental.** Breaking changes will occur until v1.0.
|
|
9
6
|
|
|
10
|
-
##
|
|
7
|
+
## Install
|
|
11
8
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
Rill solves for AI platforms what Lua solves for game engines and Liquid solves for e-commerce: safe, user-authored logic — except the "user" is increasingly an LLM.
|
|
17
|
-
|
|
18
|
-
## Why Rill?
|
|
19
|
-
|
|
20
|
-
- **Embeddable.** Zero dependencies. [Integration](docs/14_host-integration.md) takes a few lines of code, browser or backend.
|
|
21
|
-
- **Sandboxed.** No filesystem, no network, no `eval()`. The host controls the entire function surface, not just what's blocked.
|
|
22
|
-
- **Bounded execution.** `^(limit: N)` annotations prevent runaway loops from exhausting LLM usage budgets.
|
|
23
|
-
- **LLM-optimized syntax.** Ships with [EBNF grammar](docs/15_grammar.ebnf) and [LLM reference](docs/99_llm-reference.txt). No ambiguity for codegen — one way to do each thing.
|
|
24
|
-
- **Intentionally constrained.** No null, no truthiness, sealed scopes, locked types. Removes the degrees of freedom where LLMs misgenerate.
|
|
25
|
-
- **Built-in LLM output parsing.** [Auto-detect](docs/10_parsing.md) and parse JSON, XML, YAML, checklists from model responses.
|
|
26
|
-
|
|
27
|
-
## Who Is This For?
|
|
28
|
-
|
|
29
|
-
**Agentic or Workflow Platform builders** who want safe, LLM-authored workflows inside their apps.
|
|
30
|
-
|
|
31
|
-
Rill is not a general-purpose language and it's intentionally constrained. For general application development, you'll want TypeScript, Python, or Go.
|
|
32
|
-
|
|
33
|
-
Rill powers [Claude Code Runner](https://github.com/rcrsr/claude-code-runner), a rich automation tool for Claude Code.
|
|
9
|
+
```bash
|
|
10
|
+
npm install @rcrsr/rill
|
|
11
|
+
```
|
|
34
12
|
|
|
35
13
|
## Quick Start
|
|
36
14
|
|
|
@@ -48,175 +26,123 @@ const ctx = createRuntimeContext({
|
|
|
48
26
|
params: [{ name: 'message', type: 'string' }],
|
|
49
27
|
fn: async (args) => await callYourLLM(args[0]),
|
|
50
28
|
},
|
|
51
|
-
error: {
|
|
52
|
-
params: [{ name: 'message', type: 'string' }],
|
|
53
|
-
fn: (args) => { throw new Error(String(args[0])); },
|
|
54
|
-
},
|
|
55
29
|
},
|
|
56
30
|
});
|
|
57
31
|
|
|
58
32
|
const result = await execute(parse(script), ctx);
|
|
33
|
+
console.log(result.value);
|
|
59
34
|
```
|
|
60
35
|
|
|
61
|
-
##
|
|
36
|
+
## API
|
|
62
37
|
|
|
63
|
-
###
|
|
38
|
+
### Core Pipeline
|
|
64
39
|
|
|
65
|
-
Data flows forward through pipes (`app::*` denotes an application-specific host function call).
|
|
66
|
-
|
|
67
|
-
```rill
|
|
68
|
-
app::prompt("analyze this code") -> .trim -> log
|
|
69
40
|
```
|
|
70
|
-
|
|
71
|
-
### Pattern-Driven Conditionals
|
|
72
|
-
|
|
73
|
-
Branch based on content patterns. Ideal for parsing LLM output.
|
|
74
|
-
|
|
75
|
-
```rill
|
|
76
|
-
app::prompt("analyze code")
|
|
77
|
-
-> .contains("ERROR") ? app::error() ! app::process()
|
|
41
|
+
Source Text → parse() → AST → execute() → Result
|
|
78
42
|
```
|
|
79
43
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
app::prompt("Generate a summary")
|
|
88
|
-
}
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
```rill
|
|
92
|
-
# Bounded iteration
|
|
93
|
-
^(limit: 100) $items -> each { "Processing: {$}" -> log }
|
|
94
|
-
```
|
|
44
|
+
| Export | Purpose |
|
|
45
|
+
|--------|---------|
|
|
46
|
+
| `parse(source)` | Parse rill source into an AST |
|
|
47
|
+
| `execute(ast, ctx)` | Execute an AST with a runtime context |
|
|
48
|
+
| `createRuntimeContext(opts)` | Create a configured runtime context |
|
|
49
|
+
| `callable(fn, isProperty?)` | Wrap a function as a rill-callable value |
|
|
50
|
+
| `prefixFunctions(prefix, fns)` | Namespace host functions (e.g., `app::`) |
|
|
95
51
|
|
|
96
|
-
###
|
|
52
|
+
### Runtime Options
|
|
97
53
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
54
|
+
```typescript
|
|
55
|
+
const ctx = createRuntimeContext({
|
|
56
|
+
// Host functions available to scripts
|
|
57
|
+
functions: {
|
|
58
|
+
prompt: {
|
|
59
|
+
params: [{ name: 'text', type: 'string' }],
|
|
60
|
+
fn: async (args, ctx, location) => { /* ... */ },
|
|
61
|
+
},
|
|
62
|
+
},
|
|
105
63
|
|
|
106
|
-
|
|
64
|
+
// Variables injected into script scope
|
|
65
|
+
variables: {
|
|
66
|
+
config: { greeting: 'hello' },
|
|
67
|
+
},
|
|
107
68
|
|
|
108
|
-
|
|
69
|
+
// Callbacks
|
|
70
|
+
callbacks: {
|
|
71
|
+
onLog: (value) => console.log(value),
|
|
72
|
+
},
|
|
109
73
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
```
|
|
74
|
+
// Observability hooks
|
|
75
|
+
observability: {
|
|
76
|
+
onStepStart: (e) => { /* ... */ },
|
|
77
|
+
onStepEnd: (e) => { /* ... */ },
|
|
78
|
+
},
|
|
116
79
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
80
|
+
// Execution limits
|
|
81
|
+
timeout: 30000,
|
|
82
|
+
signal: abortController.signal,
|
|
83
|
+
});
|
|
120
84
|
```
|
|
121
85
|
|
|
122
|
-
###
|
|
86
|
+
### Stepper API
|
|
123
87
|
|
|
124
|
-
|
|
88
|
+
Step through execution one statement at a time:
|
|
125
89
|
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
"[1, 2, 3]" -> parse_auto -> .data # [1, 2, 3]
|
|
129
|
-
```
|
|
90
|
+
```typescript
|
|
91
|
+
import { parse, createRuntimeContext, createStepper } from '@rcrsr/rill';
|
|
130
92
|
|
|
131
|
-
|
|
132
|
-
# Extract fenced code blocks
|
|
133
|
-
"```json\n[1,2]\n```" -> parse_fence("json") -> parse_json
|
|
134
|
-
```
|
|
93
|
+
const stepper = createStepper(parse(script), createRuntimeContext());
|
|
135
94
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
95
|
+
let step;
|
|
96
|
+
while (!(step = await stepper.next()).done) {
|
|
97
|
+
console.log(step.value);
|
|
98
|
+
}
|
|
139
99
|
```
|
|
140
100
|
|
|
141
|
-
###
|
|
101
|
+
### Additional Exports
|
|
142
102
|
|
|
143
|
-
|
|
103
|
+
| Export | Purpose |
|
|
104
|
+
|--------|---------|
|
|
105
|
+
| `parseWithRecovery(source)` | Parse with error recovery (for editors) |
|
|
106
|
+
| `tokenize(source)` | Tokenize source into a token stream |
|
|
107
|
+
| `TOKEN_HIGHLIGHT_MAP` | Syntax highlighting category map |
|
|
108
|
+
| `getLanguageReference()` | LLM-optimized language reference text |
|
|
109
|
+
| `getDocumentationCoverage()` | Coverage stats for doc examples |
|
|
110
|
+
| `getFunctions()` | List of built-in function metadata |
|
|
111
|
+
| `VERSION` / `VERSION_INFO` | Runtime version string and metadata |
|
|
144
112
|
|
|
145
|
-
|
|
146
|
-
"world" -> "Hello, {$}!" -> log # Hello, world!
|
|
147
|
-
```
|
|
113
|
+
### Error Handling
|
|
148
114
|
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
115
|
+
```typescript
|
|
116
|
+
import { parse, execute, createRuntimeContext, AbortError } from '@rcrsr/rill';
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
const result = await execute(parse(script), ctx);
|
|
120
|
+
} catch (err) {
|
|
121
|
+
if (err instanceof AbortError) {
|
|
122
|
+
// Execution was cancelled via signal
|
|
123
|
+
}
|
|
124
|
+
// Runtime errors include source location and error code
|
|
125
|
+
}
|
|
155
126
|
```
|
|
156
127
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
These aren't arbitrary constraints — they're guardrails for reliable codegen.
|
|
160
|
-
|
|
161
|
-
| Design choice | Codegen rationale |
|
|
162
|
-
|---------------|-------------------|
|
|
163
|
-
| **No null/undefined** | Eliminates the edge cases LLMs most frequently hallucinate |
|
|
164
|
-
| **No truthiness** | Forces explicit boolean checks — no silent type coercion bugs |
|
|
165
|
-
| **Value semantics** | Deep copy, deterministic equality. Safe for serialization, caching, replay |
|
|
166
|
-
| **Immutable scoping** | Parent variables are read-only in child scopes. Prevents state drift across iterations |
|
|
167
|
-
| **`$` prefix on variables** | Single-pass parsing, no symbol table. `name()` is a host function, `$name` is a variable — zero ambiguity |
|
|
168
|
-
| **Type locking** | Variables lock type on first assignment. Catches type hallucinations at the assignment site |
|
|
169
|
-
| **Linear error handling** | No try/catch, no unwinding. `assert` and `error` are terminal — easy for models to place correctly |
|
|
170
|
-
| **Loops as expressions** | `fold`, `each`, `(cond) @ {}` return state instead of mutating it. Aligns with step-by-step LLM reasoning |
|
|
171
|
-
|
|
172
|
-
## What Our Target Users Say
|
|
173
|
-
|
|
174
|
-
We asked LLMs to review Rill. They had opinions.
|
|
175
|
-
|
|
176
|
-
> "Disciplined to the point of stubbornness, but in a good way. It trades familiarity for predictability."
|
|
177
|
-
> — ChatGPT
|
|
178
|
-
|
|
179
|
-
> "You've basically banned the most common footguns in scripting languages."
|
|
180
|
-
> — Gemini
|
|
128
|
+
### Type Guards
|
|
181
129
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
|
188
|
-
|
|
189
|
-
| `->` | Pipe data forward |
|
|
190
|
-
| `:>` | Capture and continue |
|
|
191
|
-
| `$` | Current pipe value |
|
|
192
|
-
| `.field` | Property access on `$` |
|
|
193
|
-
| `cond ? a ! b` | Conditional |
|
|
194
|
-
| `cond @ { }` | While loop |
|
|
195
|
-
| `@ { } ? cond` | Do-while loop |
|
|
196
|
-
| `each`, `map`, `filter` | Collection operators |
|
|
197
|
-
| `fold(init)` | Reduction |
|
|
198
|
-
| `\|args\| { }` | Closure |
|
|
199
|
-
|
|
200
|
-
## Use Cases
|
|
201
|
-
|
|
202
|
-
- **User-defined workflows.** Let power users script automation without exposing arbitrary code execution.
|
|
203
|
-
- **Multi-phase pipelines.** Chain LLM calls with review gates between each step.
|
|
204
|
-
- **Parallel agent fan-out.** Launch specialist agents concurrently, collect structured results.
|
|
205
|
-
- **Edit-review loops.** Iterate until approval or `^(limit: N)` max attempts.
|
|
206
|
-
|
|
207
|
-
See [Examples](docs/12_examples.md) for complete workflow patterns.
|
|
130
|
+
| Export | Purpose |
|
|
131
|
+
|--------|---------|
|
|
132
|
+
| `isDict(value)` | Check if value is a rill dict |
|
|
133
|
+
| `isTuple(value)` | Check if value is a rill tuple |
|
|
134
|
+
| `isCallable(value)` | Check if value is any callable |
|
|
135
|
+
| `isScriptCallable(value)` | Check if value is a script-defined closure |
|
|
136
|
+
| `isApplicationCallable(value)` | Check if value is a host-provided callable |
|
|
208
137
|
|
|
209
138
|
## Documentation
|
|
210
139
|
|
|
211
|
-
See [docs/00_INDEX.md](docs/00_INDEX.md) for full navigation.
|
|
212
|
-
|
|
213
140
|
| Document | Description |
|
|
214
141
|
|----------|-------------|
|
|
215
|
-
| [
|
|
216
|
-
| [Reference](docs/
|
|
217
|
-
| [
|
|
218
|
-
| [
|
|
219
|
-
| [Design Principles](docs/18_design-principles.md) | Why Rill works the way it does |
|
|
142
|
+
| [Host Integration](https://github.com/rcrsr/rill/blob/main/docs/integration-host.md) | Embedding guide |
|
|
143
|
+
| [Host API Reference](https://github.com/rcrsr/rill/blob/main/docs/ref-host-api.md) | Complete TypeScript API |
|
|
144
|
+
| [Language Reference](https://github.com/rcrsr/rill/blob/main/docs/ref-language.md) | Language specification |
|
|
145
|
+
| [Extensions](https://github.com/rcrsr/rill/blob/main/docs/integration-extensions.md) | Reusable host function packages |
|
|
220
146
|
|
|
221
147
|
## License
|
|
222
148
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const LANGUAGE_REFERENCE = "RILL LANGUAGE REFERENCE\n=======================\n\nRill is designed to be generated by LLMs and understood by humans. The focus is on auditable LLM output, not ergonomic human authoring. It combines the flexibility of imperative languages with the strictness of declaritive specifications.\n\nSTRENGTHS AND USE CASES\n-----------------------\nStrengths:\n - Unambiguous syntax: $ prefix, explicit operators, no implicit coercion\n - Single-pass parseable: LLMs generate correct code without symbol tables\n - Readable by humans: pipe chains show data flow left-to-right\n - Safe defaults: immutable values, type locking, no null/undefined\n\nUse cases:\n - Workflow orchestration: chain LLM calls, API requests, transformations\n - State machines: (cond) @ { } loops with $ as state dict handle multi-state logic\n - Data pipelines: each/map/filter/fold process collections declaratively\n - Prompt engineering: parse LLM output with parse_json, parse_xml, parse_fence\n\nState machine pattern ($ carries state through iterations):\n [state: \"init\", data: $input]\n -> ($.state != \"done\") @ {\n $.state -> [\n init: { [state: \"process\", data: transform($.data)] },\n process: { [state: \"done\", data: finalize($.data)] }\n ]\n }\n\nNAMING CONVENTION: snake_case\n-----------------------------\nUse snake_case for all identifiers:\n $user_name, $item_list, $is_valid # variables\n $double_value, $cleanup_text # closures\n [first_name: \"x\", last_name: \"y\"] # dict keys\n\nWHY VARIABLES USE $ PREFIX\n--------------------------\nThe $ prefix enables single-pass parsing without a symbol table:\n\n name() -> host function call\n $name() -> closure invocation\n $name -> variable reference\n name -> dict key literal\n\nWithout $, \"process(data)\" is ambiguous: is process a host function or stored\nclosure? Is data a variable or key? This would require tracking all declarations.\n\nAdditional disambiguation:\n - Capture: :> $x requires $ for lookahead (avoids conflict with slice /<1:>)\n - Destructure: *<$a, $b> marks variables vs skip patterns\n - Dynamic access: $data.$key distinguishes variable-key from literal field\n - Visual clarity: $total is always a variable, no context needed\n\nThis follows rill's \"no magic\" principle: syntax communicates intent explicitly.\n\nSPACING RULES\n-------------\nOperators: space both sides 5 + 3, $x -> .upper, \"a\" :> $b\nParentheses: no inner space ($x + 1), ($ > 3) ? \"yes\"\nBraces: space inside { $x + 1 }, each { $ * 2 }\nBrackets: no inner space $list[0], $dict.items[1]\nLiterals: space after , and : [1, 2, 3], [name: \"x\", age: 30]\nClosures: space after params |x| ($x * 2), |a, b| { $a + $b }\nMethods: no space before . or ( $str.upper(), $list.join(\", \")\nPipes: space both sides \"x\" -> .upper -> .len\nContinuations: indent 2 spaces $data\n -> .filter { $.active }\n -> map { $.name }\n\nIMPLICIT $ SHORTHAND (always prefer)\n------------------------------------\n$.method() -> .method \"x\" -> .upper (not $.upper())\nfunc($) -> func \"x\" -> log (not log($))\n$fn($) -> $fn 5 -> $double (not $double($))\n\nNO THROWAWAY CAPTURES\n---------------------\nDon't capture just to continue - use line continuation instead:\n # avoid # good\n \"x\" :> $a \"x\"\n $a -> .upper :> $b -> .upper\n $b -> .len -> .len\n\nOnly capture when the variable is reused later in the code.\n\nCRITICAL DIFFERENCES FROM MAINSTREAM LANGUAGES\n----------------------------------------------\n\n1. NO ASSIGNMENT OPERATOR\n Wrong: x = 5\n Right: 5 :> $x\n\n Pipe (->): passes value to next operation\n Capture (:>): stores value AND continues chain\n Example: \"hello\" :> $x -> .upper :> $y # $x=\"hello\", $y=\"HELLO\"\n\n2. NO NULL/UNDEFINED\n Empty values (\"\", [], [:]) exist. \"No value\" cannot be represented.\n Use ?? for defaults: $dict.field ?? \"default\"\n Use .empty to check: $str -> .empty ? \"was empty\"\n\n3. NO TRUTHINESS\n Conditions MUST be boolean. No implicit coercion.\n Wrong: \"\" ? \"yes\" ! \"no\"\n Right: \"\" -> .empty ? \"yes\" ! \"no\"\n Wrong: 0 ? \"yes\" ! \"no\"\n Right: (0 == 0) ? \"yes\" ! \"no\"\n\n Negation (!) also requires boolean:\n Right: !true # false\n Right: \"hello\" -> .empty -> (!$) # true (negates boolean from .empty)\n Wrong: !\"hello\" # ERROR: Negation requires boolean, got string\n Wrong: !0 # ERROR: Negation requires boolean, got number\n\n4. VARIABLES LOCK TO FIRST TYPE\n \"hello\" :> $x\n 42 :> $x # ERROR: cannot assign number to string variable\n\n5. NO VARIABLE SHADOWING (CRITICAL FOR LOOPS)\n Child scopes can READ parent variables but cannot WRITE or redeclare them.\n Variables created inside blocks/loops do NOT leak out.\n\n WRONG - this pattern NEVER works:\n 0 :> $count\n [1, 2, 3] -> each { $count + 1 :> $count } # creates LOCAL $count\n $count # still 0!\n\n RIGHT - use $ or $@ as state carrier (see LOOP STATE PATTERNS below)\n\n6. NO EXCEPTIONS\n Errors halt execution. No try/catch. Use conditionals for error handling.\n Built-in: assert (validate condition), error (halt with message).\n\n7. VALUE SEMANTICS (no references)\n All copies are deep. All comparisons are by value. No object identity.\n [1, 2, 3] == [1, 2, 3] # true (content equality)\n [1, 2] :> $a\n $a :> $b # $b is an independent deep copy\n Mainstream habit to avoid: expecting two variables to share the same object.\n\nSYNTAX QUICK REFERENCE\n----------------------\n\nVariables: $name (always prefixed with $)\nStrings: \"hello {$var}\" # interpolation with {}\n \"\"\"...\"\"\" # multiline (also interpolates)\nNumbers: 42, 3.14, -7\nBooleans: true, false\nLists: [1, 2, 3]\n [...$list, 4] # spread: inline list elements\nDicts: [name: \"alice\", age: 30] # identifier keys\n [1: \"one\", 2: \"two\"] # number keys (incl. negative: [-1: \"neg\"])\n [true: \"yes\", false: \"no\"] # boolean keys\n [[\"a\", \"b\"]: 1] # multi-key: [a: 1, b: 1]\n [$keyVar: value] # variable key (must eval to string)\n [($expr): value] # computed key (must eval to string)\nTuples: *[1, 2, 3] # for argument unpacking\nClosures: |x|($x + 1) # like lambda/arrow functions\nType annot: \"hi\" :> $x:string # lock type on capture\nComments: # single line only\n\nPIPES AND $ BINDING\n-------------------\n\n$ is the current piped value. Its meaning depends on context:\n\n| Context | $ contains |\n|----------------------------|-------------------------|\n| -> { body } | piped value |\n| -> each { } | current item |\n| (cond) @ { } | accumulated value |\n| @ { } ? cond | accumulated value |\n| cond ? { } ! { } | tested value |\n| -> ? { } ! { } | piped value |\n| ||{ $.field } in dict | the containing dict |\n| |x|{ } stored closure | N/A - use parameters |\n\nImplied $: bare .method() means $ -> .method()\nExample: \"hello\" -> .upper # same as \"hello\" -> $.upper()\n\nCONTROL FLOW\n------------\n\nConditional (if-else):\n cond ? then_expr ! else_expr\n cond ? then_expr # else returns \"\"\n\nPiped conditional ($ becomes condition):\n value -> ? then_expr ! else_expr\n\nCondition loop (NO \"while\" keyword - use @ operator):\n init_value -> ($ < 10) @ { $ + 1 } # $ is accumulator\n\nDo-condition loop (body runs at least once):\n init_value -> @ { $ + 1 } ? ($ < 10)\n\nBreak (exits loop, returns collected results before break):\n [1,2,3,4,5] -> each { ($ == 3) ? break; $ } # returns [1, 2]\n\nReturn (exits block or script with value):\n { 5 :> $x; ($x > 3) ? (\"big\" -> return); \"small\" } # returns \"big\"\n \"done\" -> return # exits script with \"done\"\n\nAssert (validate condition, halt if false, pass through if true):\n 5 -> assert ($ > 0) # returns 5\n -1 -> assert ($ > 0) # ERROR: Assertion failed\n \"\" -> assert !.empty \"Input required\" # ERROR: Input required\n $val -> assert $:?list \"Expected list\" # type validation\n\nError (halt execution immediately with message):\n error \"Something went wrong\" # halt with message\n \"Operation failed\" -> error # piped form (must be string)\n error \"Status: {$code}\" # interpolation works\n\nPass (returns $ unchanged, explicit no-op):\n cond ? pass ! \"fallback\" # preserve $ when condition true\n cond ? \"value\" ! pass # preserve $ when condition false\n \"data\" -> { [status: pass] } # include $ in dict: [status: \"data\"]\n [1, -2, 3] -> map { ($ > 0) ? pass ! 0 } # [1, 0, 3]\n Note: pass requires pipe context. Using pass without $ throws error.\n\nLOOP STATE PATTERNS (CRITICAL)\n------------------------------\nRill loops CANNOT modify outer variables. All state must flow through $ or $@.\n\nWRONG - outer variable modification (NEVER works):\n 0 :> $sum\n [1, 2, 3] -> each { $sum + $ :> $sum } # $sum unchanged!\n\nWRONG - \"while\" keyword does not exist:\n while ($i < 10) { $i + 1 :> $i } # SYNTAX ERROR\n\nRIGHT - use fold for reduction:\n [1, 2, 3] -> fold(0) { $@ + $ } # 6 ($@ is accumulator)\n\nRIGHT - use each(init) when you need both results AND accumulator:\n [1, 2, 3] -> each(0) { $@ + $ } # [1, 3, 6] (running totals)\n\nRIGHT - use (cond) @ { } with $ as state dict for multiple values:\n [iter: 0, max: 3, text: $input, done: false]\n -> (!$.done && $.iter < $.max) @ {\n $.iter + 1 :> $i\n process($.text) :> $result\n $result.finished ? [iter: $i, max: $.max, text: $.text, done: true]\n ! [iter: $i, max: $.max, text: $result.text, done: false]\n }\n # Access final state: $.text, $.iter\n\nPattern summary:\n - Single value accumulation -> fold(init) { $@ + $ }\n - Per-item results + running -> each(init) { ... $@ ... }\n - Multiple state values / while -> (cond) @ { } with $ as state dict\n - \"while\" and \"for\" keywords -> DO NOT EXIST\n\nCOLLECTION OPERATORS\n--------------------\n\n| Operator | Execution | Returns | Break? |\n|--------------------|------------|----------------------|--------|\n| -> each { } | sequential | all body results | yes |\n| -> each(i) { $@+$} | sequential | all with accumulator | yes |\n| -> map { } | parallel | all body results | NO |\n| -> filter { } | parallel | matching elements | NO |\n| -> fold(i) { $@+$} | sequential | final result only | yes |\n\n$@ is the accumulator in each(init) and fold(init).\n\nMethod shorthand in collection operators:\n [\"a\", \"b\"] -> map .upper # [\"A\", \"B\"]\n [\"\", \"x\"] -> filter (!.empty) # [\"x\"]\n [\"a\", \"b\"] -> map .pad_start(3, \"0\") # [\"00a\", \"00b\"] (with args)\n [\" HI \"] -> map .trim.lower # [\"hi\"] (chained methods)\n\nBody forms (all operators accept these):\n -> each { $ * 2 } # block ($ is current element)\n -> each ($ + 10) # grouped expression\n -> each |x| ($x * 2) # inline closure\n -> each $double # variable closure\n -> each .upper # method shorthand\n\nDict iteration ($ contains key and value fields):\n [a: 1, b: 2] -> each { \"{$.key}={$.value}\" } # [\"a=1\", \"b=2\"]\n [a: 1, b: 5] -> filter { $.value > 2 } # entries where value > 2\n\nString iteration (iterates over characters):\n \"abc\" -> each { \"{$}!\" } # [\"a!\", \"b!\", \"c!\"]\n \"hello\" -> filter { $ != \"l\" } # [\"h\", \"e\", \"o\"]\n\nCLOSURES\n--------\n\nBLOCK-CLOSURES vs EXPLICIT CLOSURES:\n\nTwo ways to create closures:\n\n1. Block-closures: { body } in expression position\n { $ + 1 } :> $inc # implicit $ parameter\n $inc(5) # 6\n 5 -> $inc # 6 (pipe invocation)\n [x: { $ * 2 }] # dict value is closure\n type({ \"hi\" }) # \"closure\"\n\n2. Explicit closures: |params| body\n |x|($x + 1) :> $inc # named parameter\n |a, b|($a + $b) :> $add # multiple params\n |x = 0|($x + 1) :> $inc_or_one # default value\n |x: number|($x + 1) :> $typed # type annotation\n\nCRITICAL: { } vs ( ) distinction\n\n| Syntax | Semantics | Example |\n|--------------|------------------------|----------------------------|\n| { body } | Deferred (closure) | { $ + 1 } :> $fn # closure |\n| ( expr ) | Eager (immediate eval) | ( 5 + 1 ) :> $x # 6 |\n\nWhen to use:\n { body } :> $fn # store closure for later use\n ( expr ) :> $x # store result value immediately\n\nPIPE TARGET: { } creates closure then immediately invokes it:\n 5 -> { $ + 1 } # 6 (create closure, invoke with 5)\n 5 -> ($ + 1) # 6 (evaluate expression with $=5)\n Same observable result, different mechanism. Error messages differ.\n\nBlock-closure invocation:\n { $ + 1 } :> $inc\n $inc(5) # direct call: 6\n 5 -> $inc # pipe call: 6\n [1,2,3] -> map $inc # in collection op\n\nLATE BINDING: closures capture scope, not values. Variables resolve at call time.\n\n$ vs named params:\n Use $ in inline pipes and loops: \"hello\" -> { .upper }\n Use named params in stored closures: |x| ($x * 2) :> $double\n $ is undefined when a stored closure is called later \u2014 always use params.\n\nZero-param dict closures (methods):\n [count: 3, double: ||{ $.count * 2 }] :> $obj\n $obj.double # 6 ($ is bound to dict)\n\nPROPERTY ACCESS\n---------------\n\n$data.field # dict field\n$data[0], $data[-1] # list index (negative from end)\n$data.$key # variable as key\n$data.($i + 1) # computed key\n$data.(a || b) # try keys left-to-right\n$data.field ?? \"default\" # default if missing\n$data.?field # existence check (boolean)\n$data.?$keyVar # variable existence check\n$data.?($expr) # computed existence check\n$data.?field&string # existence AND type check\n$data.?$key&number # variable existence + type check\n$data.?($a -> \"{$}_b\")&list # computed existence + type check\n\nDISPATCH OPERATORS\n------------------\n\nDICT DISPATCH (single key):\nPipe a value to a dict to match keys and return associated values:\n $val -> [apple: \"fruit\", carrot: \"veg\"] # returns \"fruit\" if $val is \"apple\"\n $val -> [apple: \"fruit\"] ?? \"not found\" # default if no match\n $method -> [[\"GET\", \"HEAD\"]: \"safe\", [\"POST\", \"PUT\"]: \"unsafe\"] # multi-key dispatch\n\nMulti-key dispatch uses the same syntax as multi-key dict literals:\n [[\"GET\", \"HEAD\"]: \"safe\"] # dict with keys \"GET\" and \"HEAD\" both = \"safe\"\n \"GET\" -> [[\"GET\", \"HEAD\"]: \"safe\"] # \"safe\" (matches \"GET\" key)\n\nType-aware matching (keys matched by value AND type):\n 1 -> [1: \"number\", \"1\": \"string\"] # \"number\" (number key matches)\n \"1\" -> [1: \"number\", \"1\": \"string\"] # \"string\" (string key matches)\n true -> [true: \"bool\", \"true\": \"str\"] # \"bool\" (boolean key matches)\n\nLIST DISPATCH (index):\nPipe a number to a list to get element at index:\n 0 -> [\"first\", \"second\"] # \"first\"\n -1 -> [\"first\", \"second\"] # \"second\" (last)\n 5 -> [\"a\", \"b\"] ?? \"not found\" # default if out of bounds\n\nHIERARCHICAL DISPATCH (path navigation):\nPipe a list of keys/indexes to navigate nested structures:\n [\"name\", \"first\"] -> [name: [first: \"Alice\"]] # \"Alice\" (dict path)\n [0, 1] -> [[1, 2, 3], [4, 5, 6]] # 2 (list path)\n [\"users\", 0, \"name\"] -> [users: [[name: \"Alice\"]]] # \"Alice\" (mixed)\n [] -> [a: 1] # [a: 1] (empty path = unchanged)\n [\"a\", \"missing\"] -> [a: [x: 1]] ?? \"default\" # \"default\" (missing key)\n\nPath elements: strings for dict keys, numbers for list indexes (negative supported).\nTerminal closures receive $ bound to final path key:\n [\"req\", \"draft\"] -> [req: [draft: { \"key={$}\" }]] # \"key=draft\"\n\nTYPE OPERATIONS\n---------------\n\n:type - assert type (error if wrong): 42:number passes; \"x\":number errors\n:?type - check type (boolean): 42:?number is true; \"x\":?number is false\n\nTypes: string, number, boolean, list, dict, tuple, closure\n\nCOMPARISON METHODS\n------------------\n\nMethods for readable comparisons in conditionals:\n .eq(val) == $v -> .eq(\"A\") ? \"match\"\n .ne(val) != $v -> .ne(\"\") ? \"has value\"\n .lt(val) < $v -> .lt(10) ? \"small\"\n .gt(val) > 5 -> .gt(3) ? \"big\"\n .le(val) <= 10 -> .le(10) ? \"ok\"\n .ge(val) >= $age -> .ge(18) ? \"adult\"\n\nOPERATOR PRECEDENCE (highest to lowest)\n---------------------------------------\n\n1. Member access: .field, [index]\n2. Type operators: :type, :?type\n3. Unary: -, !\n4. Multiplicative: *, /, %\n5. Additive: +, -\n6. Comparison: ==, !=, <, >, <=, >=\n7. Logical AND: &&\n8. Logical OR: ||\n9. Default: ??\n10. Pipe: ->\n11. Capture: :>\n\nUse parentheses to override: (2 + 3) * 4\n\nEXTRACTION OPERATORS\n--------------------\n\nDestructure (*<>):\n [1, 2, 3] -> *<$a, $b, $c> # $a=1, $b=2, $c=3\n [x: 1, y: 2] -> *<x: $a, y: $b> # $a=1, $b=2\n [1, 2, 3] -> *<$first, _, $third> # _ skips element\n\nSlice (/<start:stop:step>):\n [0,1,2,3,4] -> /<1:3> # [1, 2]\n [0,1,2,3,4] -> /<-2:> # [3, 4]\n [0,1,2,3,4] -> /<::-1> # [4,3,2,1,0] (reverse)\n \"hello\" -> /<1:4> # \"ell\"\n\nLIST SPREAD IN LITERALS\n-----------------------\n\nInline list elements into a new list using ... (spread operator):\n [1, 2] :> $a\n [...$a, 3] # [1, 2, 3]\n [...$a, ...$b] # concatenate lists\n [...[], 1] # [1] (empty spread contributes nothing)\n [...($nums -> map {$ * 2})] # spread expression result\n [...$nested[0]] # spread from nested access\n\nError: spreading non-list throws RUNTIME_TYPE_ERROR\n \"hello\" :> $s\n [...$s] # ERROR: Spread requires list, got string\n\nMULTI-KEY DICT LITERALS\n-----------------------\n\nMap multiple keys to the same value using list syntax:\n [[\"a\", \"b\"]: 1] # [a: 1, b: 1]\n [[1, \"1\"]: \"x\"] # [1: \"x\", \"1\": \"x\"] (mixed types)\n [a: 0, [\"b\", \"c\"]: 1] # [a: 0, b: 1, c: 1] (mixed entries)\n [a: 0, [\"a\", \"b\"]: 1] # [a: 1, b: 1] (last-write-wins)\n\nError cases:\n [[]: 1] # ERROR: empty key list\n [[[1, 2], \"a\"]: 1] # ERROR: non-primitive key element\n\nTUPLES FOR ARGUMENT UNPACKING\n-----------------------------\n\n*[1, 2, 3] -> $fn() # positional: $fn(1, 2, 3)\n*[b: 2, a: 1] -> $fn() # named: $fn(a=1, b=2)\n*[...$list, 3] -> $fn() # spread in tuple: combines elements\n\nSPREAD OPERATOR (@)\n-------------------\n\nChains closures sequentially:\n 5 -> @[$inc, $double, $add10] # (5+1)*2+10 = 22\n\nSTRING METHODS\n--------------\n\n.len length\n.empty is empty string\n.trim remove whitespace\n.upper uppercase\n.lower lowercase\n.split(sep) split into list\n.lines split on newlines\n.join(sep) join list with separator (on list)\n.contains(s) substring check\n.starts_with(s) prefix check\n.ends_with(s) suffix check\n.replace(p,r) replace first match\n.replace_all(p,r) replace all matches\n.match(regex) first match info (dict with matched, index, groups)\n.is_match(regex) boolean regex check\n.head first character\n.tail last character\n.at(i) character at index\n.index_of(s) position of first match (-1 if none)\n.repeat(n) repeat string n times: \"ab\" -> .repeat(3) # \"ababab\"\n.pad_start(n,f) pad start to length: \"42\" -> .pad_start(5, \"0\") # \"00042\"\n.pad_end(n,f) pad end to length: \"42\" -> .pad_end(5, \"0\") # \"42000\"\n.str convert any value to string: 42 -> .str # \"42\"\n.num parse string to number: \"42\" -> .num # 42\n\nLIST/DICT METHODS\n-----------------\n\n.len length\n.empty is empty\n.head first element\n.tail last element\n.at(i) element at index\n.has(val) check if list contains value (deep equality)\n.has_any(list) check if list contains any value from candidates\n.has_all(list) check if list contains all values from candidates\n.keys dict keys as list\n.values dict values as list\n.entries dict as list of [key: k, value: v]\n\nPARSING FUNCTIONS (for LLM output)\n----------------------------------\n\nparse_auto(str) auto-detect format\nparse_json(str) parse JSON (repairs common errors)\nparse_xml(str, tag?) extract XML tag content\nparse_fence(str, lang?) extract fenced code block\nparse_fences(str) all fenced blocks as list\nparse_frontmatter(str) parse --- delimited YAML + body\nparse_checklist(str) parse markdown task lists\n\nGLOBAL FUNCTIONS\n----------------\n\ntype(val) returns type name\nlog(val) print and pass through\njson(val) convert to JSON string\nidentity(val) returns input unchanged\nrange(start, end, step?) number sequence\nrepeat(val, count) repeat value n times\nenumerate(coll) add index to elements\n\nITERATORS\n---------\n\nLazy sequence generation. Collection operators auto-expand iterators.\n\nBuilt-in iterators:\n range(0, 5) -> each { $ * 2 } # [0, 2, 4, 6, 8]\n repeat(\"x\", 3) -> each { $ } # [\"x\", \"x\", \"x\"]\n\n.first() method (returns iterator for any collection):\n [1, 2, 3] -> .first() # iterator at 1\n \"abc\" -> .first() # iterator at \"a\"\n [] -> .first() # [done: true, ...]\n\nIterator protocol (dict with value, done, next):\n $it.done # bool: is exhausted?\n $it.value # current element\n $it.next() # returns new iterator at next position\n\nITERATION LIMITS\n----------------\n\nDefault: 10,000 iterations max for loops.\nOverride: ^(limit: N) statement\n\nExample:\n ^(limit: 100) 0 -> ($ < 50) @ { $ + 1 }\n\nConcurrency limit for map (controls parallel concurrency):\n ^(limit: 3) $items -> map { slow_process($) }\n\nCOMMON MISTAKES\n---------------\n\n1. Using = for assignment -> use :> instead\n2. Using || for defaults -> use ?? instead\n3. Assuming truthiness -> explicit boolean checks required\n4. Breaking from map/filter -> only works in each/fold\n5. Modifying outer vars in loops -> use fold/$@ or $ as state dict (see LOOP STATE PATTERNS)\n6. Expecting variables to leak -> block scope is strict\n7. Forgetting () on methods -> .upper() not .upper (unless property)\n8. Reassigning different type -> variables lock to first type\n9. Using while/for keywords -> use (cond) @ { } or -> each { } instead\n\nSCRIPT RETURN VALUES\n--------------------\n\ntrue / non-empty string -> exit code 0\nfalse / empty string -> exit code 1\n[0, \"message\"] -> exit code 0 with message\n[1, \"message\"] -> exit code 1 with message\n\nMAINSTREAM TO RILL TRANSLATION\n------------------------------\n\n| Mainstream | Rill |\n|--------------------------|----------------------------------------- |\n| x = value | value :> $x |\n| null / undefined | ?? default, .? existence check |\n| if \"\" (truthiness) | .empty, == 0, :?type |\n| try { } catch { } | assert, conditionals, error |\n| for (i = 0; ...) | each, map, filter, fold |\n| count += 1 in loop | fold(0) { $@ + 1 } or $ accumulator |\n| a === b (reference) | == always compares by value |\n| a = b (shared ref) | :> always deep-copies |\n";
|
|
1
|
+
export declare const LANGUAGE_REFERENCE = "RILL LANGUAGE REFERENCE\n=======================\n\nRill is designed to be generated by LLMs and understood by humans. The focus is\non auditable LLM output, not ergonomic human authoring.\n\nESSENTIALS\n----------\n1. Variables use $ prefix ALWAYS: 5 => $x (no assignment operator =)\n2. Pipe with ->, capture with =>: \"hello\" => $x -> .upper => $y\n3. No null/undefined, no try/catch, no truthiness (conditions must be boolean)\n4. Variables lock to first type: \"hi\" => $x; 42 => $x # ERROR\n5. Loops cannot modify outer vars: use fold, each(init), or $ as state dict\n\nSTRENGTHS AND USE CASES\n-----------------------\nStrengths:\n - Unambiguous syntax: $ prefix, explicit operators, no implicit coercion\n - Single-pass parseable: LLMs generate correct code without symbol tables\n - Readable by humans: pipe chains show data flow left-to-right\n - Safe defaults: immutable values, type locking, no null/undefined\n\nUse cases:\n - Workflow orchestration: chain LLM calls, API requests, transformations\n - State machines: (cond) @ { } loops with $ as state dict\n - Data pipelines: each/map/filter/fold process collections declaratively\n - Prompt engineering: parse LLM output with parse_json, parse_xml, parse_fence\n\nState machine pattern ($ carries state through iterations):\n [state: \"init\", data: $input]\n -> ($.state != \"done\") @ {\n $.state -> [\n init: { [state: \"process\", data: transform($.data)] },\n process: { [state: \"done\", data: finalize($.data)] }\n ]\n }\n\nNAMING CONVENTION: snake_case\n-----------------------------\nUse snake_case for all identifiers:\n $user_name, $item_list, $is_valid # variables\n $double_value, $cleanup_text # closures\n [first_name: \"x\", last_name: \"y\"] # dict keys\n\nWHY VARIABLES USE $ PREFIX\n--------------------------\nThe $ prefix enables single-pass parsing without a symbol table:\n\n name() -> host function call\n $name() -> closure invocation\n $name -> variable reference\n name -> dict key literal\n\nWithout $, \"process(data)\" is ambiguous: is process a host function or stored\nclosure? Is data a variable or key? This would require tracking all declarations.\n\nSPACING RULES\n-------------\nOperators: space both sides 5 + 3, $x -> .upper, \"a\" => $b\nParentheses: no inner space ($x + 1), ($ > 3) ? \"yes\"\nBraces: space inside { $x + 1 }, each { $ * 2 }\nBrackets: no inner space $list[0], $dict.items[1]\nLiterals: space after , and : [1, 2, 3], [name: \"x\", age: 30]\nClosures: space after params |x| ($x * 2), |a, b| { $a + $b }\nMethods: no space before . or ( $str.upper(), $list.join(\", \")\nPipes: space both sides \"x\" -> .upper -> .len\nContinuations: indent 2 spaces $data\n -> .filter { $.active }\n -> map { $.name }\n\nIMPLICIT $ SHORTHAND (always prefer)\n------------------------------------\n$.method() -> .method \"x\" -> .upper (not $.upper())\nfunc($) -> func \"x\" -> log (not log($))\n$fn($) -> $fn 5 -> $double (not $double($))\n\nDon't capture just to continue - use line continuation instead:\n # avoid # good\n \"x\" => $a \"x\"\n $a -> .upper => $b -> .upper\n $b -> .len -> .len\n\nOnly capture when the variable is reused later in the code.\n\nCRITICAL DIFFERENCES FROM MAINSTREAM LANGUAGES\n----------------------------------------------\n\n1. NO ASSIGNMENT OPERATOR\n Wrong: x = 5 Mainstream: x = value\n Right: 5 => $x Rill: value => $x\n\n Pipe (->): passes value to next operation\n Capture (=>): stores value AND continues chain\n Example: \"hello\" => $x -> .upper => $y # $x=\"hello\", $y=\"HELLO\"\n\n2. NO NULL/UNDEFINED\n Empty values (\"\", [], [:]) exist. \"No value\" cannot be represented.\n Use ?? for defaults: $dict.field ?? \"default\"\n Use .empty to check: $str -> .empty ? \"was empty\"\n Mainstream: null/undefined Rill: ?? default, .? existence check\n\n3. NO TRUTHINESS\n Conditions MUST be boolean. No implicit coercion.\n Wrong: \"\" ? \"yes\" ! \"no\" Right: \"\" -> .empty ? \"yes\" ! \"no\"\n Wrong: 0 ? \"yes\" ! \"no\" Right: (0 == 0) ? \"yes\" ! \"no\"\n Negation (!) also requires boolean:\n Right: !true # false\n Right: \"hello\" -> .empty -> (!$) # true (negates boolean from .empty)\n Wrong: !\"hello\" # ERROR: Negation requires boolean\n Mainstream: if \"\" / if 0 Rill: .empty, == 0, :?type\n\n4. VARIABLES LOCK TO FIRST TYPE\n \"hello\" => $x\n 42 => $x # ERROR: cannot assign number to string variable\n\n5. NO VARIABLE SHADOWING (CRITICAL FOR LOOPS)\n Child scopes can READ parent variables but cannot WRITE or redeclare them.\n Variables created inside blocks/loops do NOT leak out.\n\n WRONG - outer variable modification (NEVER works):\n 0 => $count\n [1, 2, 3] -> each { $count + 1 => $count } # creates LOCAL $count\n $count # still 0!\n\n WRONG - \"while\" keyword does not exist:\n while ($i < 10) { $i + 1 => $i } # SYNTAX ERROR\n\n RIGHT - use fold for reduction:\n [1, 2, 3] -> fold(0) { $@ + $ } # 6 ($@ is accumulator)\n\n RIGHT - use each(init) for results AND accumulator:\n [1, 2, 3] -> each(0) { $@ + $ } # [1, 3, 6] (running totals)\n\n RIGHT - use (cond) @ { } with $ as state dict for multiple values:\n [iter: 0, max: 3, text: $input, done: false]\n -> (!$.done && $.iter < $.max) @ {\n $.iter + 1 => $i\n process($.text) => $result\n $result.finished ? [iter: $i, max: $.max, text: $.text, done: true]\n ! [iter: $i, max: $.max, text: $result.text, done: false]\n }\n\n Pattern summary:\n Single value accumulation -> fold(init) { $@ + $ }\n Per-item results + running -> each(init) { ... $@ ... }\n Multiple state values / while -> (cond) @ { } with $ as state dict\n \"while\" and \"for\" keywords -> DO NOT EXIST\n Mainstream: count += 1 in loop Rill: fold(0) { $@ + 1 } or $ accumulator\n\n6. NO EXCEPTIONS\n Errors halt execution. No try/catch. Use conditionals for error handling.\n Built-in: assert (validate condition), error (halt with message).\n Mainstream: try { } catch { } Rill: assert, conditionals, error\n\n7. VALUE SEMANTICS (no references)\n All copies are deep. All comparisons are by value. No object identity.\n [1, 2, 3] == [1, 2, 3] # true (content equality)\n [1, 2] => $a\n $a => $b # $b is an independent deep copy\n Mainstream: a === b (reference) Rill: == always compares by value\n Mainstream: a = b (shared ref) Rill: => always deep-copies\n\nSYNTAX QUICK REFERENCE\n----------------------\n\nVariables: $name (always prefixed with $)\nStrings: \"hello {$var}\" # interpolation with {}\n \"\"\"...\"\"\" # multiline (also interpolates)\nNumbers: 42, 3.14, -7\nBooleans: true, false\nLists: [1, 2, 3]\n [...$list, 4] # spread: inline list elements\nDicts: [name: \"alice\", age: 30] # identifier keys\n [1: \"one\", 2: \"two\"] # number keys (incl. negative: [-1: \"neg\"])\n [true: \"yes\", false: \"no\"] # boolean keys\n [[\"a\", \"b\"]: 1] # multi-key: [a: 1, b: 1]\n [$keyVar: value] # variable key (must eval to string)\n [($expr): value] # computed key (must eval to string)\nTuples: *[1, 2, 3] # for argument unpacking\nClosures: |x|($x + 1) # like lambda/arrow functions\nType annot: \"hi\" => $x:string # lock type on capture\nComments: # single line only\n\nPIPES AND $ BINDING\n-------------------\n\n$ is the current piped value. Its meaning depends on context:\n\n| Context | $ contains |\n|----------------------------|-------------------------|\n| -> { body } | piped value |\n| -> each { } | current item |\n| (cond) @ { } | accumulated value |\n| @ { } ? cond | accumulated value |\n| cond ? { } ! { } | tested value |\n| -> ? { } ! { } | piped value |\n| ||{ $.field } in dict | the containing dict |\n| |x|{ } stored closure | N/A - use parameters |\n\nImplied $: bare .method() means $ -> .method()\nExample: \"hello\" -> .upper # same as \"hello\" -> $.upper()\n\nCONTROL FLOW\n------------\n\nConditional (if-else):\n cond ? then_expr ! else_expr\n cond ? then_expr # else returns \"\"\n\nPiped conditional ($ becomes condition):\n value -> ? then_expr ! else_expr\n\nCondition loop (NO \"while\" keyword - use @ operator):\n init_value -> ($ < 10) @ { $ + 1 } # $ is accumulator\n\nDo-condition loop (body runs at least once):\n init_value -> @ { $ + 1 } ? ($ < 10)\n\nBreak (exits loop, returns collected results before break):\n [1,2,3,4,5] -> each { ($ == 3) ? break; $ } # returns [1, 2]\n\nReturn (exits block or script with value):\n { 5 => $x; ($x > 3) ? (\"big\" -> return); \"small\" } # returns \"big\"\n \"done\" -> return # exits script with \"done\"\n\nAssert (validate condition, halt if false, pass through if true):\n 5 -> assert ($ > 0) # returns 5\n -1 -> assert ($ > 0) # ERROR: Assertion failed\n \"\" -> assert !.empty \"Input required\" # ERROR: Input required\n $val -> assert $:?list \"Expected list\" # type validation\n\nError (halt execution immediately with message):\n error \"Something went wrong\" # halt with message\n \"Operation failed\" -> error # piped form (must be string)\n error \"Status: {$code}\" # interpolation works\n\nPass (returns $ unchanged, explicit no-op):\n cond ? pass ! \"fallback\" # preserve $ when condition true\n cond ? \"value\" ! pass # preserve $ when condition false\n \"data\" -> { [status: pass] } # include $ in dict: [status: \"data\"]\n [1, -2, 3] -> map { ($ > 0) ? pass ! 0 } # [1, 0, 3]\n Note: pass requires pipe context. Using pass without $ throws error.\n\nCOLLECTION OPERATORS\n--------------------\n\n| Operator | Execution | Returns | Break? |\n|--------------------|------------|----------------------|--------|\n| -> each { } | sequential | all body results | yes |\n| -> each(i) { $@+$} | sequential | all with accumulator | yes |\n| -> map { } | parallel | all body results | NO |\n| -> filter { } | parallel | matching elements | NO |\n| -> fold(i) { $@+$} | sequential | final result only | yes |\n\n$@ is the accumulator in each(init) and fold(init).\n\nMethod shorthand in collection operators:\n [\"a\", \"b\"] -> map .upper # [\"A\", \"B\"]\n [\"\", \"x\"] -> filter (!.empty) # [\"x\"]\n [\"a\", \"b\"] -> map .pad_start(3, \"0\") # [\"00a\", \"00b\"] (with args)\n [\" HI \"] -> map .trim.lower # [\"hi\"] (chained methods)\n\nBody forms (all operators accept these):\n -> each { $ * 2 } # block ($ is current element)\n -> each ($ + 10) # grouped expression\n -> each |x| ($x * 2) # inline closure\n -> each $double # variable closure\n -> each .upper # method shorthand\n -> each log # host function\n\nDict iteration ($ contains key and value fields):\n [a: 1, b: 2] -> each { \"{$.key}={$.value}\" } # [\"a=1\", \"b=2\"]\n [a: 1, b: 5] -> filter { $.value > 2 } # entries where value > 2\n\nString iteration (iterates over characters):\n \"abc\" -> each { \"{$}!\" } # [\"a!\", \"b!\", \"c!\"]\n \"hello\" -> filter { $ != \"l\" } # [\"h\", \"e\", \"o\"]\n\nCLOSURES\n--------\n\nBLOCK-CLOSURES vs EXPLICIT CLOSURES:\n\nTwo ways to create closures:\n\n1. Block-closures: { body } in expression position\n { $ + 1 } => $inc # implicit $ parameter\n $inc(5) # 6\n 5 -> $inc # 6 (pipe invocation)\n [x: { $ * 2 }] # dict value is closure\n type({ \"hi\" }) # \"closure\"\n\n2. Explicit closures: |params| body\n |x|($x + 1) => $inc # named parameter\n |a, b|($a + $b) => $add # multiple params\n |x = 0|($x + 1) => $inc_or_one # default value\n |x: number|($x + 1) => $typed # type annotation\n\nCRITICAL: { } vs ( ) distinction\n\n| Syntax | Semantics | Example |\n|--------------|------------------------|----------------------------|\n| { body } | Deferred (closure) | { $ + 1 } => $fn # closure |\n| ( expr ) | Eager (immediate eval) | ( 5 + 1 ) => $x # 6 |\n\nWhen to use:\n { body } => $fn # store closure for later use\n ( expr ) => $x # store result value immediately\n\nPIPE TARGET: { } creates closure then immediately invokes it:\n 5 -> { $ + 1 } # 6 (create closure, invoke with 5)\n 5 -> ($ + 1) # 6 (evaluate expression with $=5)\n Same observable result, different mechanism. Error messages differ.\n\nBlock-closure invocation:\n { $ + 1 } => $inc\n $inc(5) # direct call: 6\n 5 -> $inc # pipe call: 6\n [1,2,3] -> map $inc # in collection op\n\nLATE BINDING: closures capture scope, not values. Variables resolve at call time.\n\n$ vs named params:\n Use $ in inline pipes and loops: \"hello\" -> { .upper }\n Use named params in stored closures: |x| ($x * 2) => $double\n $ is undefined when a stored closure is called later \u2014 always use params.\n\nZero-param dict closures (methods):\n [count: 3, double: ||{ $.count * 2 }] => $obj\n $obj.double # 6 ($ is bound to dict)\n\nPROPERTY ACCESS\n---------------\n\n$data.field # dict field\n$data[0], $data[-1] # list index (negative from end)\n$data.$key # variable as key\n$data.($i + 1) # computed key\n$data.(a || b) # try keys left-to-right\n$data.field ?? \"default\" # default if missing\n$data.?field # existence check (boolean)\n$data.?$keyVar # variable existence check\n$data.?($expr) # computed existence check\n$data.?field&string # existence AND type check\n$data.?$key&number # variable existence + type check\n$data.?($a -> \"{$}_b\")&list # computed existence + type check\n\nDISPATCH OPERATORS\n------------------\n\nDICT DISPATCH (single key):\nPipe a value to a dict to match keys and return associated values:\n $val -> [apple: \"fruit\", carrot: \"veg\"] # returns \"fruit\" if $val is \"apple\"\n $val -> [apple: \"fruit\"] ?? \"not found\" # default if no match\n $method -> [[\"GET\", \"HEAD\"]: \"safe\", [\"POST\", \"PUT\"]: \"unsafe\"] # multi-key dispatch\n\nType-aware matching (keys matched by value AND type):\n 1 -> [1: \"number\", \"1\": \"string\"] # \"number\" (number key matches)\n \"1\" -> [1: \"number\", \"1\": \"string\"] # \"string\" (string key matches)\n true -> [true: \"bool\", \"true\": \"str\"] # \"bool\" (boolean key matches)\n\nLIST DISPATCH (index):\nPipe a number to a list to get element at index:\n 0 -> [\"first\", \"second\"] # \"first\"\n -1 -> [\"first\", \"second\"] # \"second\" (last)\n 5 -> [\"a\", \"b\"] ?? \"not found\" # default if out of bounds\n\nHIERARCHICAL DISPATCH (path navigation):\nPipe a list of keys/indexes to navigate nested structures:\n [\"name\", \"first\"] -> [name: [first: \"Alice\"]] # \"Alice\" (dict path)\n [0, 1] -> [[1, 2, 3], [4, 5, 6]] # 2 (list path)\n [\"users\", 0, \"name\"] -> [users: [[name: \"Alice\"]]] # \"Alice\" (mixed)\n [] -> [a: 1] # [a: 1] (empty path = unchanged)\n [\"a\", \"missing\"] -> [a: [x: 1]] ?? \"default\" # \"default\" (missing key)\n\nTYPE OPERATIONS\n---------------\n\n:type - assert type (error if wrong): 42:number passes; \"x\":number errors\n:?type - check type (boolean): 42:?number is true; \"x\":?number is false\n\nTypes: string, number, boolean, list, dict, tuple, closure\n\nComparison methods (for readable conditionals):\n .eq(val) == .ne(val) != .lt(val) < .gt(val) > .le(val) <= .ge(val) >=\n Example: $age -> .ge(18) ? \"adult\" ! \"minor\"\n\nOPERATOR PRECEDENCE (highest to lowest)\n---------------------------------------\n\n1. Member access: .field, [index]\n2. Type operators: :type, :?type\n3. Unary: -, !\n4. Multiplicative: *, /, %\n5. Additive: +, -\n6. Comparison: ==, !=, <, >, <=, >=\n7. Logical AND: &&\n8. Logical OR: ||\n9. Default: ??\n10. Pipe: ->\n11. Capture: =>\n\nUse parentheses to override: (2 + 3) * 4\n\nEXTRACTION OPERATORS\n--------------------\n\nDestructure (*<>):\n [1, 2, 3] -> *<$a, $b, $c> # $a=1, $b=2, $c=3\n [x: 1, y: 2] -> *<x: $a, y: $b> # $a=1, $b=2\n [1, 2, 3] -> *<$first, _, $third> # _ skips element\n\nSlice (/<start:stop:step>):\n [0,1,2,3,4] -> /<1:3> # [1, 2]\n [0,1,2,3,4] -> /<-2:> # [3, 4]\n [0,1,2,3,4] -> /<::-1> # [4,3,2,1,0] (reverse)\n \"hello\" -> /<1:4> # \"ell\"\n\nLIST SPREAD IN LITERALS\n-----------------------\n\nInline list elements into a new list using ... (spread operator):\n [1, 2] => $a\n [...$a, 3] # [1, 2, 3]\n [...$a, ...$b] # concatenate lists\n [...($nums -> map {$ * 2})] # spread expression result\n\nMULTI-KEY DICT LITERALS\n-----------------------\n\nMap multiple keys to the same value using list syntax:\n [[\"a\", \"b\"]: 1] # [a: 1, b: 1]\n [[1, \"1\"]: \"x\"] # [1: \"x\", \"1\": \"x\"] (mixed types)\n [a: 0, [\"b\", \"c\"]: 1] # [a: 0, b: 1, c: 1] (mixed entries)\n\nTUPLES FOR ARGUMENT UNPACKING\n-----------------------------\n\n*[1, 2, 3] -> $fn() # positional: $fn(1, 2, 3)\n*[b: 2, a: 1] -> $fn() # named: $fn(a=1, b=2)\n*[...$list, 3] -> $fn() # spread in tuple: combines elements\n\nCLOSURE CHAIN (@)\n-----------------\n\nChains closures sequentially (each receives previous result):\n 5 -> @[$inc, $double, $add10] # (5+1)*2+10 = 22\n\nSTRING METHODS\n--------------\n\n.len length .empty is empty string\n.trim remove whitespace .upper uppercase\n.lower lowercase .str convert to string\n.num parse to number .head first character\n.tail last character .at(i) character at index\n.split(sep) split into list (default: newline)\n.lines split on newlines .join(sep) join list with separator\n.contains(s) substring check .starts_with(s) prefix check\n.ends_with(s) suffix check .index_of(s) first match position (-1 if none)\n.replace(p,r) replace first regex match\n.replace_all(p,r) replace all regex matches\n.match(regex) first match info (dict with matched, index, groups)\n.is_match(regex) boolean regex check\n.repeat(n) repeat n times: \"ab\" -> .repeat(3) # \"ababab\"\n.pad_start(n,f) pad start: \"42\" -> .pad_start(5, \"0\") # \"00042\"\n.pad_end(n,f) pad end: \"42\" -> .pad_end(5, \"0\") # \"42000\"\n\nLIST/DICT METHODS\n-----------------\n\n.len length .empty is empty\n.head first element .tail last element\n.at(i) element at index .keys dict keys as list\n.values dict values as list .entries dict as list of [k, v] tuples\n.has(val) list contains value (deep equality)\n.has_any(list) list contains any value from candidates\n.has_all(list) list contains all values from candidates\n\nPARSING FUNCTIONS (for LLM output)\n----------------------------------\n\nparse_auto(str) auto-detect format\nparse_json(str) parse JSON (repairs common errors)\nparse_xml(str, tag?) extract XML tag content\nparse_fence(str, lang?) extract fenced code block\nparse_fences(str) all fenced blocks as list\nparse_frontmatter(str) parse --- delimited YAML + body\nparse_checklist(str) parse markdown task lists\n\nGLOBAL FUNCTIONS\n----------------\n\ntype(val) returns type name (string, number, boolean, list, dict, closure, tuple)\nlog(val) print and pass through\njson(val) convert to JSON string\nidentity(val) returns input unchanged\nrange(start, end, step?) number sequence (iterator)\nrepeat(val, count) repeat value n times (iterator)\nenumerate(coll) lists: [index, value]; dicts: [index, key, value]\n\nITERATORS\n---------\n\nLazy sequence generation. Collection operators auto-expand iterators.\n\nBuilt-in iterators:\n range(0, 5) -> each { $ * 2 } # [0, 2, 4, 6, 8]\n repeat(\"x\", 3) -> each { $ } # [\"x\", \"x\", \"x\"]\n\n.first() method (returns iterator for any collection):\n [1, 2, 3] -> .first() # iterator at 1\n \"abc\" -> .first() # iterator at \"a\"\n\nIterator protocol (dict with value, done, next):\n $it.done # bool: is exhausted?\n $it.value # current element\n $it.next() # returns new iterator at next position\n\nITERATION LIMITS\n----------------\n\nDefault: 10,000 iterations max for loops.\nOverride: ^(limit: N) statement\n\n ^(limit: 100) 0 -> ($ < 50) @ { $ + 1 }\n ^(limit: 3) $items -> map { slow_process($) } # concurrency limit\n\nSCRIPT RETURN VALUES\n--------------------\n\ntrue / non-empty string -> exit code 0\nfalse / empty string -> exit code 1\n[0, \"message\"] -> exit code 0 with message\n[1, \"message\"] -> exit code 1 with message\n";
|
|
2
2
|
//# sourceMappingURL=introspection-data.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"introspection-data.d.ts","sourceRoot":"","sources":["../../src/generated/introspection-data.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,kBAAkB,
|
|
1
|
+
{"version":3,"file":"introspection-data.d.ts","sourceRoot":"","sources":["../../src/generated/introspection-data.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,kBAAkB,q+sBAuhB9B,CAAC"}
|