@rcrsr/rill 0.1.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.
Files changed (295) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +187 -0
  3. package/dist/cli.d.ts +11 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +69 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/demo.d.ts +6 -0
  8. package/dist/demo.d.ts.map +1 -0
  9. package/dist/demo.js +121 -0
  10. package/dist/demo.js.map +1 -0
  11. package/dist/index.d.ts +10 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +9 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/lexer/errors.d.ts +9 -0
  16. package/dist/lexer/errors.d.ts.map +1 -0
  17. package/dist/lexer/errors.js +12 -0
  18. package/dist/lexer/errors.js.map +1 -0
  19. package/dist/lexer/helpers.d.ts +14 -0
  20. package/dist/lexer/helpers.d.ts.map +1 -0
  21. package/dist/lexer/helpers.js +30 -0
  22. package/dist/lexer/helpers.js.map +1 -0
  23. package/dist/lexer/index.d.ts +8 -0
  24. package/dist/lexer/index.d.ts.map +1 -0
  25. package/dist/lexer/index.js +8 -0
  26. package/dist/lexer/index.js.map +1 -0
  27. package/dist/lexer/operators.d.ts +11 -0
  28. package/dist/lexer/operators.d.ts.map +1 -0
  29. package/dist/lexer/operators.js +58 -0
  30. package/dist/lexer/operators.js.map +1 -0
  31. package/dist/lexer/readers.d.ts +12 -0
  32. package/dist/lexer/readers.d.ts.map +1 -0
  33. package/dist/lexer/readers.js +144 -0
  34. package/dist/lexer/readers.js.map +1 -0
  35. package/dist/lexer/state.d.ts +18 -0
  36. package/dist/lexer/state.d.ts.map +1 -0
  37. package/dist/lexer/state.js +37 -0
  38. package/dist/lexer/state.js.map +1 -0
  39. package/dist/lexer/tokenizer.d.ts +9 -0
  40. package/dist/lexer/tokenizer.d.ts.map +1 -0
  41. package/dist/lexer/tokenizer.js +100 -0
  42. package/dist/lexer/tokenizer.js.map +1 -0
  43. package/dist/lexer.d.ts +19 -0
  44. package/dist/lexer.d.ts.map +1 -0
  45. package/dist/lexer.js +344 -0
  46. package/dist/lexer.js.map +1 -0
  47. package/dist/parser/arithmetic.d.ts +16 -0
  48. package/dist/parser/arithmetic.d.ts.map +1 -0
  49. package/dist/parser/arithmetic.js +128 -0
  50. package/dist/parser/arithmetic.js.map +1 -0
  51. package/dist/parser/boolean.d.ts +15 -0
  52. package/dist/parser/boolean.d.ts.map +1 -0
  53. package/dist/parser/boolean.js +20 -0
  54. package/dist/parser/boolean.js.map +1 -0
  55. package/dist/parser/control-flow.d.ts +56 -0
  56. package/dist/parser/control-flow.d.ts.map +1 -0
  57. package/dist/parser/control-flow.js +167 -0
  58. package/dist/parser/control-flow.js.map +1 -0
  59. package/dist/parser/expressions.d.ts +23 -0
  60. package/dist/parser/expressions.d.ts.map +1 -0
  61. package/dist/parser/expressions.js +950 -0
  62. package/dist/parser/expressions.js.map +1 -0
  63. package/dist/parser/extraction.d.ts +48 -0
  64. package/dist/parser/extraction.d.ts.map +1 -0
  65. package/dist/parser/extraction.js +279 -0
  66. package/dist/parser/extraction.js.map +1 -0
  67. package/dist/parser/functions.d.ts +20 -0
  68. package/dist/parser/functions.d.ts.map +1 -0
  69. package/dist/parser/functions.js +96 -0
  70. package/dist/parser/functions.js.map +1 -0
  71. package/dist/parser/helpers.d.ts +94 -0
  72. package/dist/parser/helpers.d.ts.map +1 -0
  73. package/dist/parser/helpers.js +225 -0
  74. package/dist/parser/helpers.js.map +1 -0
  75. package/dist/parser/index.d.ts +49 -0
  76. package/dist/parser/index.d.ts.map +1 -0
  77. package/dist/parser/index.js +73 -0
  78. package/dist/parser/index.js.map +1 -0
  79. package/dist/parser/literals.d.ts +37 -0
  80. package/dist/parser/literals.d.ts.map +1 -0
  81. package/dist/parser/literals.js +373 -0
  82. package/dist/parser/literals.js.map +1 -0
  83. package/dist/parser/parser-collect.d.ts +16 -0
  84. package/dist/parser/parser-collect.d.ts.map +1 -0
  85. package/dist/parser/parser-collect.js +125 -0
  86. package/dist/parser/parser-collect.js.map +1 -0
  87. package/dist/parser/parser-control.d.ts +20 -0
  88. package/dist/parser/parser-control.d.ts.map +1 -0
  89. package/dist/parser/parser-control.js +120 -0
  90. package/dist/parser/parser-control.js.map +1 -0
  91. package/dist/parser/parser-expr.d.ts +37 -0
  92. package/dist/parser/parser-expr.d.ts.map +1 -0
  93. package/dist/parser/parser-expr.js +639 -0
  94. package/dist/parser/parser-expr.js.map +1 -0
  95. package/dist/parser/parser-extract.d.ts +17 -0
  96. package/dist/parser/parser-extract.d.ts.map +1 -0
  97. package/dist/parser/parser-extract.js +222 -0
  98. package/dist/parser/parser-extract.js.map +1 -0
  99. package/dist/parser/parser-functions.d.ts +21 -0
  100. package/dist/parser/parser-functions.d.ts.map +1 -0
  101. package/dist/parser/parser-functions.js +155 -0
  102. package/dist/parser/parser-functions.js.map +1 -0
  103. package/dist/parser/parser-literals.d.ts +22 -0
  104. package/dist/parser/parser-literals.d.ts.map +1 -0
  105. package/dist/parser/parser-literals.js +288 -0
  106. package/dist/parser/parser-literals.js.map +1 -0
  107. package/dist/parser/parser-script.d.ts +21 -0
  108. package/dist/parser/parser-script.d.ts.map +1 -0
  109. package/dist/parser/parser-script.js +174 -0
  110. package/dist/parser/parser-script.js.map +1 -0
  111. package/dist/parser/parser-variables.d.ts +20 -0
  112. package/dist/parser/parser-variables.d.ts.map +1 -0
  113. package/dist/parser/parser-variables.js +146 -0
  114. package/dist/parser/parser-variables.js.map +1 -0
  115. package/dist/parser/parser.d.ts +49 -0
  116. package/dist/parser/parser.d.ts.map +1 -0
  117. package/dist/parser/parser.js +54 -0
  118. package/dist/parser/parser.js.map +1 -0
  119. package/dist/parser/script.d.ts +14 -0
  120. package/dist/parser/script.d.ts.map +1 -0
  121. package/dist/parser/script.js +196 -0
  122. package/dist/parser/script.js.map +1 -0
  123. package/dist/parser/state.d.ts +40 -0
  124. package/dist/parser/state.d.ts.map +1 -0
  125. package/dist/parser/state.js +129 -0
  126. package/dist/parser/state.js.map +1 -0
  127. package/dist/parser/variables.d.ts +10 -0
  128. package/dist/parser/variables.d.ts.map +1 -0
  129. package/dist/parser/variables.js +215 -0
  130. package/dist/parser/variables.js.map +1 -0
  131. package/dist/runtime/ast-equals.d.ts +13 -0
  132. package/dist/runtime/ast-equals.d.ts.map +1 -0
  133. package/dist/runtime/ast-equals.js +447 -0
  134. package/dist/runtime/ast-equals.js.map +1 -0
  135. package/dist/runtime/builtins.d.ts +13 -0
  136. package/dist/runtime/builtins.d.ts.map +1 -0
  137. package/dist/runtime/builtins.js +180 -0
  138. package/dist/runtime/builtins.js.map +1 -0
  139. package/dist/runtime/callable.d.ts +88 -0
  140. package/dist/runtime/callable.d.ts.map +1 -0
  141. package/dist/runtime/callable.js +98 -0
  142. package/dist/runtime/callable.js.map +1 -0
  143. package/dist/runtime/context.d.ts +13 -0
  144. package/dist/runtime/context.d.ts.map +1 -0
  145. package/dist/runtime/context.js +73 -0
  146. package/dist/runtime/context.js.map +1 -0
  147. package/dist/runtime/core/callable.d.ts +171 -0
  148. package/dist/runtime/core/callable.d.ts.map +1 -0
  149. package/dist/runtime/core/callable.js +246 -0
  150. package/dist/runtime/core/callable.js.map +1 -0
  151. package/dist/runtime/core/context.d.ts +29 -0
  152. package/dist/runtime/core/context.d.ts.map +1 -0
  153. package/dist/runtime/core/context.js +154 -0
  154. package/dist/runtime/core/context.js.map +1 -0
  155. package/dist/runtime/core/equals.d.ts +9 -0
  156. package/dist/runtime/core/equals.d.ts.map +1 -0
  157. package/dist/runtime/core/equals.js +381 -0
  158. package/dist/runtime/core/equals.js.map +1 -0
  159. package/dist/runtime/core/eval/base.d.ts +65 -0
  160. package/dist/runtime/core/eval/base.d.ts.map +1 -0
  161. package/dist/runtime/core/eval/base.js +112 -0
  162. package/dist/runtime/core/eval/base.js.map +1 -0
  163. package/dist/runtime/core/eval/evaluator.d.ts +47 -0
  164. package/dist/runtime/core/eval/evaluator.d.ts.map +1 -0
  165. package/dist/runtime/core/eval/evaluator.js +73 -0
  166. package/dist/runtime/core/eval/evaluator.js.map +1 -0
  167. package/dist/runtime/core/eval/index.d.ts +57 -0
  168. package/dist/runtime/core/eval/index.d.ts.map +1 -0
  169. package/dist/runtime/core/eval/index.js +95 -0
  170. package/dist/runtime/core/eval/index.js.map +1 -0
  171. package/dist/runtime/core/eval/mixins/annotations.d.ts +19 -0
  172. package/dist/runtime/core/eval/mixins/annotations.d.ts.map +1 -0
  173. package/dist/runtime/core/eval/mixins/annotations.js +146 -0
  174. package/dist/runtime/core/eval/mixins/annotations.js.map +1 -0
  175. package/dist/runtime/core/eval/mixins/closures.d.ts +49 -0
  176. package/dist/runtime/core/eval/mixins/closures.d.ts.map +1 -0
  177. package/dist/runtime/core/eval/mixins/closures.js +479 -0
  178. package/dist/runtime/core/eval/mixins/closures.js.map +1 -0
  179. package/dist/runtime/core/eval/mixins/collections.d.ts +24 -0
  180. package/dist/runtime/core/eval/mixins/collections.d.ts.map +1 -0
  181. package/dist/runtime/core/eval/mixins/collections.js +466 -0
  182. package/dist/runtime/core/eval/mixins/collections.js.map +1 -0
  183. package/dist/runtime/core/eval/mixins/control-flow.d.ts +27 -0
  184. package/dist/runtime/core/eval/mixins/control-flow.d.ts.map +1 -0
  185. package/dist/runtime/core/eval/mixins/control-flow.js +369 -0
  186. package/dist/runtime/core/eval/mixins/control-flow.js.map +1 -0
  187. package/dist/runtime/core/eval/mixins/core.d.ts +24 -0
  188. package/dist/runtime/core/eval/mixins/core.d.ts.map +1 -0
  189. package/dist/runtime/core/eval/mixins/core.js +335 -0
  190. package/dist/runtime/core/eval/mixins/core.js.map +1 -0
  191. package/dist/runtime/core/eval/mixins/expressions.d.ts +19 -0
  192. package/dist/runtime/core/eval/mixins/expressions.d.ts.map +1 -0
  193. package/dist/runtime/core/eval/mixins/expressions.js +202 -0
  194. package/dist/runtime/core/eval/mixins/expressions.js.map +1 -0
  195. package/dist/runtime/core/eval/mixins/extraction.d.ts +10 -0
  196. package/dist/runtime/core/eval/mixins/extraction.d.ts.map +1 -0
  197. package/dist/runtime/core/eval/mixins/extraction.js +250 -0
  198. package/dist/runtime/core/eval/mixins/extraction.js.map +1 -0
  199. package/dist/runtime/core/eval/mixins/literals.d.ts +23 -0
  200. package/dist/runtime/core/eval/mixins/literals.d.ts.map +1 -0
  201. package/dist/runtime/core/eval/mixins/literals.js +180 -0
  202. package/dist/runtime/core/eval/mixins/literals.js.map +1 -0
  203. package/dist/runtime/core/eval/mixins/types.d.ts +20 -0
  204. package/dist/runtime/core/eval/mixins/types.d.ts.map +1 -0
  205. package/dist/runtime/core/eval/mixins/types.js +109 -0
  206. package/dist/runtime/core/eval/mixins/types.js.map +1 -0
  207. package/dist/runtime/core/eval/mixins/variables.d.ts +34 -0
  208. package/dist/runtime/core/eval/mixins/variables.d.ts.map +1 -0
  209. package/dist/runtime/core/eval/mixins/variables.js +247 -0
  210. package/dist/runtime/core/eval/mixins/variables.js.map +1 -0
  211. package/dist/runtime/core/eval/types.d.ts +41 -0
  212. package/dist/runtime/core/eval/types.d.ts.map +1 -0
  213. package/dist/runtime/core/eval/types.js +10 -0
  214. package/dist/runtime/core/eval/types.js.map +1 -0
  215. package/dist/runtime/core/evaluate.d.ts +42 -0
  216. package/dist/runtime/core/evaluate.d.ts.map +1 -0
  217. package/dist/runtime/core/evaluate.debug.js +1251 -0
  218. package/dist/runtime/core/evaluate.js +1913 -0
  219. package/dist/runtime/core/evaluate.js.map +1 -0
  220. package/dist/runtime/core/execute.d.ts +26 -0
  221. package/dist/runtime/core/execute.d.ts.map +1 -0
  222. package/dist/runtime/core/execute.js +177 -0
  223. package/dist/runtime/core/execute.js.map +1 -0
  224. package/dist/runtime/core/signals.d.ts +19 -0
  225. package/dist/runtime/core/signals.d.ts.map +1 -0
  226. package/dist/runtime/core/signals.js +26 -0
  227. package/dist/runtime/core/signals.js.map +1 -0
  228. package/dist/runtime/core/types.d.ts +177 -0
  229. package/dist/runtime/core/types.d.ts.map +1 -0
  230. package/dist/runtime/core/types.js +50 -0
  231. package/dist/runtime/core/types.js.map +1 -0
  232. package/dist/runtime/core/values.d.ts +66 -0
  233. package/dist/runtime/core/values.d.ts.map +1 -0
  234. package/dist/runtime/core/values.js +240 -0
  235. package/dist/runtime/core/values.js.map +1 -0
  236. package/dist/runtime/evaluate.d.ts +32 -0
  237. package/dist/runtime/evaluate.d.ts.map +1 -0
  238. package/dist/runtime/evaluate.js +1111 -0
  239. package/dist/runtime/evaluate.js.map +1 -0
  240. package/dist/runtime/execute.d.ts +26 -0
  241. package/dist/runtime/execute.d.ts.map +1 -0
  242. package/dist/runtime/execute.js +121 -0
  243. package/dist/runtime/execute.js.map +1 -0
  244. package/dist/runtime/ext/builtins.d.ts +16 -0
  245. package/dist/runtime/ext/builtins.d.ts.map +1 -0
  246. package/dist/runtime/ext/builtins.js +528 -0
  247. package/dist/runtime/ext/builtins.js.map +1 -0
  248. package/dist/runtime/ext/content-parser.d.ts +83 -0
  249. package/dist/runtime/ext/content-parser.d.ts.map +1 -0
  250. package/dist/runtime/ext/content-parser.js +536 -0
  251. package/dist/runtime/ext/content-parser.js.map +1 -0
  252. package/dist/runtime/index.d.ts +28 -0
  253. package/dist/runtime/index.d.ts.map +1 -0
  254. package/dist/runtime/index.js +34 -0
  255. package/dist/runtime/index.js.map +1 -0
  256. package/dist/runtime/signals.d.ts +19 -0
  257. package/dist/runtime/signals.d.ts.map +1 -0
  258. package/dist/runtime/signals.js +26 -0
  259. package/dist/runtime/signals.js.map +1 -0
  260. package/dist/runtime/types.d.ts +169 -0
  261. package/dist/runtime/types.d.ts.map +1 -0
  262. package/dist/runtime/types.js +50 -0
  263. package/dist/runtime/types.js.map +1 -0
  264. package/dist/runtime/values.d.ts +50 -0
  265. package/dist/runtime/values.d.ts.map +1 -0
  266. package/dist/runtime/values.js +209 -0
  267. package/dist/runtime/values.js.map +1 -0
  268. package/dist/runtime.d.ts +254 -0
  269. package/dist/runtime.d.ts.map +1 -0
  270. package/dist/runtime.js +2014 -0
  271. package/dist/runtime.js.map +1 -0
  272. package/dist/types.d.ts +752 -0
  273. package/dist/types.d.ts.map +1 -0
  274. package/dist/types.js +189 -0
  275. package/dist/types.js.map +1 -0
  276. package/docs/00_INDEX.md +65 -0
  277. package/docs/01_guide.md +390 -0
  278. package/docs/02_types.md +399 -0
  279. package/docs/03_variables.md +314 -0
  280. package/docs/04_operators.md +551 -0
  281. package/docs/05_control-flow.md +350 -0
  282. package/docs/06_closures.md +353 -0
  283. package/docs/07_collections.md +686 -0
  284. package/docs/08_iterators.md +330 -0
  285. package/docs/09_strings.md +205 -0
  286. package/docs/10_parsing.md +366 -0
  287. package/docs/11_reference.md +350 -0
  288. package/docs/12_examples.md +771 -0
  289. package/docs/13_modules.md +519 -0
  290. package/docs/14_host-integration.md +826 -0
  291. package/docs/15_grammar.ebnf +693 -0
  292. package/docs/16_conventions.md +696 -0
  293. package/docs/99_llm-reference.txt +300 -0
  294. package/docs/assets/logo.png +0 -0
  295. package/package.json +70 -0
@@ -0,0 +1,826 @@
1
+ # Host Integration Guide
2
+
3
+ This guide covers embedding Rill in host applications. Rill is a vanilla language—all domain-specific functionality must be provided by the host.
4
+
5
+ ## Quick Start
6
+
7
+ ```typescript
8
+ import { parse, execute, createRuntimeContext } from '@rcrsr/rill';
9
+
10
+ const source = `
11
+ "Hello, World!" -> prompt() :> $response
12
+ $response
13
+ `;
14
+
15
+ const ast = parse(source);
16
+ const ctx = createRuntimeContext({
17
+ functions: {
18
+ prompt: async (args) => {
19
+ const text = String(args[0]);
20
+ return await callYourLLM(text);
21
+ },
22
+ },
23
+ });
24
+
25
+ const result = await execute(ast, ctx);
26
+ console.log(result.value);
27
+ ```
28
+
29
+ ## RuntimeOptions
30
+
31
+ The `createRuntimeContext()` function accepts these options:
32
+
33
+ | Option | Type | Description |
34
+ |--------|------|-------------|
35
+ | `variables` | `Record<string, RillValue>` | Initial variables accessible as `$name` |
36
+ | `functions` | `Record<string, CallableFn \| HostFunctionDefinition>` | Custom functions callable as `name()` |
37
+ | `callbacks` | `Partial<RuntimeCallbacks>` | I/O callbacks (e.g., `onLog`) |
38
+ | `observability` | `ObservabilityCallbacks` | Execution monitoring hooks |
39
+ | `timeout` | `number` | Timeout in ms for async functions |
40
+ | `autoExceptions` | `string[]` | Regex patterns that halt execution |
41
+ | `signal` | `AbortSignal` | Cancellation signal |
42
+
43
+ ## Host Function Contract
44
+
45
+ Host functions must follow these rules to ensure correct script behavior:
46
+
47
+ ### Immutability
48
+
49
+ **Host functions must not mutate input arguments.** rill values are immutable by design—modifying arguments breaks value semantics and causes unpredictable behavior.
50
+
51
+ ```typescript
52
+ // WRONG: Mutates input array
53
+ functions: {
54
+ addItem: (args) => {
55
+ const list = args[0] as unknown[];
56
+ list.push('new'); // DON'T DO THIS
57
+ return list;
58
+ },
59
+ }
60
+
61
+ // CORRECT: Return new value
62
+ functions: {
63
+ addItem: (args) => {
64
+ const list = args[0] as unknown[];
65
+ return [...list, 'new']; // Create new array
66
+ },
67
+ }
68
+ ```
69
+
70
+ ### Defensive Copies
71
+
72
+ For maximum safety, consider freezing values passed to host functions:
73
+
74
+ ```typescript
75
+ import { deepFreeze } from './utils'; // Your utility
76
+
77
+ functions: {
78
+ process: (args) => {
79
+ const frozen = deepFreeze(args[0]);
80
+ return transform(frozen); // Any mutation throws
81
+ },
82
+ }
83
+ ```
84
+
85
+ ### Return Values
86
+
87
+ - Return new values instead of modifying inputs
88
+ - Return `RillValue` types (string, number, boolean, array, object, or `RillCallable`)
89
+ - Avoid returning `null` or `undefined`—use empty string `''` or empty array `[]` instead
90
+
91
+ ## Custom Functions
92
+
93
+ Functions are called by name: `functionName(arg1, arg2)`.
94
+
95
+ ```typescript
96
+ const ctx = createRuntimeContext({
97
+ functions: {
98
+ // Sync function
99
+ add: (args) => {
100
+ const a = typeof args[0] === 'number' ? args[0] : 0;
101
+ const b = typeof args[1] === 'number' ? args[1] : 0;
102
+ return a + b;
103
+ },
104
+
105
+ // Async function
106
+ fetch: async (args, ctx, location) => {
107
+ const url = String(args[0]);
108
+ const response = await fetch(url);
109
+ return await response.text();
110
+ },
111
+
112
+ // Function with context access
113
+ getVar: (args, ctx) => {
114
+ const name = String(args[0]);
115
+ return ctx.variables.get(name) ?? null;
116
+ },
117
+
118
+ // Function with location for error reporting
119
+ validate: (args, ctx, location) => {
120
+ if (!args[0]) {
121
+ throw new Error(`Validation failed at line ${location?.line}`);
122
+ }
123
+ return args[0];
124
+ },
125
+ },
126
+ });
127
+ ```
128
+
129
+ ### Namespaced Functions
130
+
131
+ Use `::` to organize functions into namespaces:
132
+
133
+ ```typescript
134
+ const ctx = createRuntimeContext({
135
+ functions: {
136
+ // Namespaced functions use :: separator
137
+ 'math::add': (args) => (args[0] as number) + (args[1] as number),
138
+ 'math::multiply': (args) => (args[0] as number) * (args[1] as number),
139
+ 'str::upper': (args) => String(args[0]).toUpperCase(),
140
+ 'str::lower': (args) => String(args[0]).toLowerCase(),
141
+
142
+ // Multi-level namespaces
143
+ 'io::file::read': async (args) => fs.readFile(String(args[0]), 'utf-8'),
144
+ 'io::file::write': async (args) => fs.writeFile(String(args[0]), String(args[1])),
145
+ },
146
+ });
147
+ ```
148
+
149
+ Scripts call namespaced functions with the same syntax:
150
+
151
+ ```rill
152
+ math::add(1, 2) # 3
153
+ "hello" -> str::upper # "HELLO"
154
+ io::file::read("config.json") -> parse_json
155
+ ```
156
+
157
+ Namespaces help organize host APIs and avoid name collisions without requiring the `$` variable prefix.
158
+ ```
159
+
160
+ ### CallableFn Signature
161
+
162
+ ```typescript
163
+ type CallableFn = (
164
+ args: RillValue[],
165
+ ctx: RuntimeContext,
166
+ location?: SourceLocation
167
+ ) => RillValue | Promise<RillValue>;
168
+ ```
169
+
170
+ | Parameter | Description |
171
+ |-----------|-------------|
172
+ | `args` | Positional arguments passed to the function |
173
+ | `ctx` | Runtime context with variables, pipeValue, etc. |
174
+ | `location` | Source location of the call site (for error reporting) |
175
+
176
+ ## Typed Host Functions
177
+
178
+ Host functions can declare parameter types and defaults. The runtime validates arguments before calling your function, eliminating manual type checking.
179
+
180
+ ### Basic Type Declarations
181
+
182
+ Declare parameter types using the `HostFunctionDefinition` interface:
183
+
184
+ ```typescript
185
+ const ctx = createRuntimeContext({
186
+ functions: {
187
+ repeat: {
188
+ params: [
189
+ { name: 'str', type: 'string' },
190
+ { name: 'count', type: 'number', defaultValue: 1 },
191
+ ],
192
+ fn: (args) => {
193
+ // args[0] guaranteed to be string
194
+ // args[1] guaranteed to be number (or default)
195
+ return args[0].repeat(args[1]);
196
+ },
197
+ },
198
+ },
199
+ });
200
+ ```
201
+
202
+ Scripts call typed functions the same way:
203
+
204
+ ```text
205
+ repeat("hello", 3) # "hellohellohello"
206
+ repeat("hi") # "hi" (uses default count)
207
+ ```
208
+
209
+ ### Supported Types
210
+
211
+ | Type | Rill Value | Validation |
212
+ |------|------------|------------|
213
+ | `'string'` | String | `typeof value === 'string'` |
214
+ | `'number'` | Number | `typeof value === 'number'` |
215
+ | `'bool'` | Boolean | `typeof value === 'boolean'` |
216
+ | `'list'` | List | `Array.isArray(value)` |
217
+ | `'dict'` | Dict | `isDict(value)` |
218
+
219
+ ### Default Values
220
+
221
+ Parameters with default values are optional. The default applies when the argument is missing:
222
+
223
+ ```typescript
224
+ functions: {
225
+ greet: {
226
+ params: [
227
+ { name: 'name', type: 'string' },
228
+ { name: 'greeting', type: 'string', defaultValue: 'Hello' },
229
+ ],
230
+ fn: (args) => `${args[1]}, ${args[0]}!`,
231
+ },
232
+ }
233
+ ```
234
+
235
+ ```text
236
+ greet("Alice") # "Hello, Alice!"
237
+ greet("Bob", "Hi") # "Hi, Bob!"
238
+ ```
239
+
240
+ ### Type Mismatch Errors
241
+
242
+ When argument types don't match, the runtime throws `RuntimeError` with code `RUNTIME_TYPE_ERROR`:
243
+
244
+ ```typescript
245
+ // Script: repeat(42, 3)
246
+ // Error: Function 'repeat' expects parameter 'str' (position 0) to be string, got number
247
+ ```
248
+
249
+ Error details include:
250
+
251
+ - Function name
252
+ - Parameter name
253
+ - Parameter position
254
+ - Expected type
255
+ - Actual type received
256
+
257
+ ### Migration from Untyped Functions
258
+
259
+ Typed function declarations are optional. Existing untyped functions continue working without changes.
260
+
261
+ **Before (manual validation):**
262
+
263
+ ```typescript
264
+ functions: {
265
+ repeat: (args) => {
266
+ const str = typeof args[0] === 'string' ? args[0] : '';
267
+ const count = typeof args[1] === 'number' ? args[1] : 1;
268
+ return str.repeat(count);
269
+ },
270
+ }
271
+ ```
272
+
273
+ **After (declarative validation):**
274
+
275
+ ```typescript
276
+ functions: {
277
+ repeat: {
278
+ params: [
279
+ { name: 'str', type: 'string' },
280
+ { name: 'count', type: 'number', defaultValue: 1 },
281
+ ],
282
+ fn: (args) => args[0].repeat(args[1]), // Types guaranteed
283
+ },
284
+ }
285
+ ```
286
+
287
+ Benefits of migration:
288
+
289
+ - Eliminates manual type checking code
290
+ - Provides clear error messages to script authors
291
+ - Documents expected types in function signature
292
+ - Reduces bugs from incorrect type coercion
293
+
294
+ ### Mixed Function Definitions
295
+
296
+ You can mix typed and untyped functions in the same context:
297
+
298
+ ```typescript
299
+ const ctx = createRuntimeContext({
300
+ functions: {
301
+ // Untyped (legacy)
302
+ 'legacy::func': (args) => args[0],
303
+
304
+ // Typed (new)
305
+ 'typed::func': {
306
+ params: [{ name: 'x', type: 'string' }],
307
+ fn: (args) => args[0],
308
+ },
309
+ },
310
+ });
311
+ ```
312
+
313
+ ### HostFunctionDefinition Interface
314
+
315
+ ```typescript
316
+ interface HostFunctionDefinition {
317
+ params: HostFunctionParam[];
318
+ fn: CallableFn;
319
+ }
320
+
321
+ interface HostFunctionParam {
322
+ name: string;
323
+ type: 'string' | 'number' | 'bool' | 'list' | 'dict';
324
+ defaultValue?: RillValue;
325
+ }
326
+ ```
327
+
328
+ ## Application Callables
329
+
330
+ Hosts can create first-class callable values that scripts can store, pass, and invoke.
331
+
332
+ ```typescript
333
+ import { callable, isCallable, isApplicationCallable } from '@rcrsr/rill';
334
+
335
+ // Create a callable
336
+ const greet = callable((args) => `Hello, ${args[0]}!`);
337
+
338
+ // Use in variables
339
+ const ctx = createRuntimeContext({
340
+ variables: {
341
+ greet: greet,
342
+ },
343
+ });
344
+
345
+ // Script can invoke: $greet("World") -> "Hello, World!"
346
+ ```
347
+
348
+ ### callable() Function
349
+
350
+ ```typescript
351
+ function callable(fn: CallableFn, isProperty?: boolean): ApplicationCallable;
352
+ ```
353
+
354
+ | Parameter | Description |
355
+ |-----------|-------------|
356
+ | `fn` | The function to wrap |
357
+ | `isProperty` | If `true`, auto-invokes when accessed from dict |
358
+
359
+ ### Property-Style Callables
360
+
361
+ Property-style callables auto-invoke when accessed from a dict, enabling computed properties:
362
+
363
+ ```typescript
364
+ const ctx = createRuntimeContext({
365
+ variables: {
366
+ user: {
367
+ firstName: 'John',
368
+ lastName: 'Doe',
369
+ // Auto-invokes on access, receives bound dict
370
+ fullName: callable((args) => {
371
+ const dict = args[0] as Record<string, RillValue>;
372
+ return `${dict.firstName} ${dict.lastName}`;
373
+ }, true),
374
+ },
375
+ },
376
+ });
377
+
378
+ // Script: $user.fullName -> "John Doe"
379
+ ```
380
+
381
+ ### Dict Callables
382
+
383
+ Callables stored in dicts can be invoked using method syntax:
384
+
385
+ ```typescript
386
+ const ctx = createRuntimeContext({
387
+ variables: {
388
+ math: {
389
+ add: callable((args) => {
390
+ const a = typeof args[0] === 'number' ? args[0] : 0;
391
+ const b = typeof args[1] === 'number' ? args[1] : 0;
392
+ return a + b;
393
+ }),
394
+ },
395
+ },
396
+ });
397
+
398
+ // Script: $math.add(1, 2) -> 3
399
+ ```
400
+
401
+ ### Callable Kinds
402
+
403
+ | Kind | Type | Description |
404
+ |------|------|-------------|
405
+ | `script` | `ScriptCallable` | Closures from Rill source code |
406
+ | `runtime` | `RuntimeCallable` | Rill's built-in functions |
407
+ | `application` | `ApplicationCallable` | Host-provided callables |
408
+
409
+ ### Type Guards
410
+
411
+ ```typescript
412
+ import {
413
+ isCallable,
414
+ isScriptCallable,
415
+ isRuntimeCallable,
416
+ isApplicationCallable,
417
+ } from '@rcrsr/rill';
418
+
419
+ if (isCallable(value)) {
420
+ // value is RillCallable (any callable)
421
+ }
422
+
423
+ if (isApplicationCallable(value)) {
424
+ // value is ApplicationCallable (host-provided)
425
+ }
426
+
427
+ if (isScriptCallable(value)) {
428
+ // value is ScriptCallable (from Rill source)
429
+ }
430
+ ```
431
+
432
+ ## Cancellation
433
+
434
+ Use `AbortSignal` to cancel long-running scripts:
435
+
436
+ ```typescript
437
+ const controller = new AbortController();
438
+
439
+ const ctx = createRuntimeContext({
440
+ signal: controller.signal,
441
+ functions: {
442
+ longTask: async () => {
443
+ await new Promise((r) => setTimeout(r, 10000));
444
+ return 'done';
445
+ },
446
+ },
447
+ });
448
+
449
+ // Cancel after 1 second
450
+ setTimeout(() => controller.abort(), 1000);
451
+
452
+ try {
453
+ await execute(ast, ctx);
454
+ } catch (err) {
455
+ if (err instanceof AbortError) {
456
+ console.log('Execution cancelled');
457
+ }
458
+ }
459
+ ```
460
+
461
+ ### AbortError
462
+
463
+ ```typescript
464
+ import { AbortError } from '@rcrsr/rill';
465
+
466
+ try {
467
+ await execute(ast, ctx);
468
+ } catch (err) {
469
+ if (err instanceof AbortError) {
470
+ console.log(err.code); // 'RUNTIME_ABORTED'
471
+ console.log(err.message); // 'Execution aborted'
472
+ }
473
+ }
474
+ ```
475
+
476
+ Abort is checked at:
477
+ - Before each statement
478
+ - Before each function call
479
+ - Before each loop iteration
480
+ - In the stepper's `step()` method
481
+
482
+ ## Observability
483
+
484
+ Monitor execution with observability callbacks:
485
+
486
+ ```typescript
487
+ const ctx = createRuntimeContext({
488
+ observability: {
489
+ onStepStart: (event) => {
490
+ console.log(`Step ${event.index + 1}/${event.total}`);
491
+ },
492
+ onStepEnd: (event) => {
493
+ console.log(`Completed in ${event.durationMs}ms`);
494
+ },
495
+ onFunctionCall: (event) => {
496
+ console.log(`Calling ${event.name}(${event.args.join(', ')})`);
497
+ },
498
+ onFunctionReturn: (event) => {
499
+ console.log(`${event.name} returned: ${event.value}`);
500
+ },
501
+ onCapture: (event) => {
502
+ console.log(`Captured $${event.name} = ${event.value}`);
503
+ },
504
+ onError: (event) => {
505
+ console.error(`Error at step ${event.index}:`, event.error);
506
+ },
507
+ },
508
+ });
509
+ ```
510
+
511
+ ### Event Types
512
+
513
+ ```typescript
514
+ interface StepStartEvent {
515
+ index: number; // Statement index (0-based)
516
+ total: number; // Total statements
517
+ pipeValue: RillValue;
518
+ }
519
+
520
+ interface StepEndEvent {
521
+ index: number;
522
+ total: number;
523
+ value: RillValue;
524
+ durationMs: number;
525
+ }
526
+
527
+ interface FunctionCallEvent {
528
+ name: string;
529
+ args: RillValue[];
530
+ }
531
+
532
+ interface FunctionReturnEvent {
533
+ name: string;
534
+ value: RillValue;
535
+ durationMs: number;
536
+ }
537
+
538
+ interface CaptureEvent {
539
+ name: string;
540
+ value: RillValue;
541
+ }
542
+
543
+ interface ErrorEvent {
544
+ error: Error;
545
+ index?: number;
546
+ }
547
+ ```
548
+
549
+ ## Step-by-Step Execution
550
+
551
+ Use the stepper API for controlled execution:
552
+
553
+ ```typescript
554
+ import { parse, createRuntimeContext, createStepper } from '@rcrsr/rill';
555
+
556
+ const ast = parse(source);
557
+ const ctx = createRuntimeContext({ ... });
558
+ const stepper = createStepper(ast, ctx);
559
+
560
+ while (!stepper.done) {
561
+ const result = await stepper.step();
562
+ console.log(`Step ${result.index + 1}: ${result.value}`);
563
+
564
+ if (result.captured) {
565
+ console.log(`Captured: $${result.captured.name}`);
566
+ }
567
+ }
568
+
569
+ const final = stepper.getResult();
570
+ console.log('Final value:', final.value);
571
+ console.log('Variables:', final.variables);
572
+ ```
573
+
574
+ ### ExecutionStepper Interface
575
+
576
+ ```typescript
577
+ interface ExecutionStepper {
578
+ readonly done: boolean;
579
+ readonly index: number;
580
+ readonly total: number;
581
+ readonly context: RuntimeContext;
582
+ step(): Promise<StepResult>;
583
+ getResult(): ExecutionResult;
584
+ }
585
+
586
+ interface StepResult {
587
+ value: RillValue;
588
+ done: boolean;
589
+ index: number;
590
+ total: number;
591
+ captured?: { name: string; value: RillValue };
592
+ }
593
+
594
+ interface ExecutionResult {
595
+ value: RillValue;
596
+ variables: Record<string, RillValue>;
597
+ }
598
+ ```
599
+
600
+ ## I/O Callbacks
601
+
602
+ Handle script I/O through callbacks:
603
+
604
+ ```typescript
605
+ const ctx = createRuntimeContext({
606
+ callbacks: {
607
+ onLog: (value) => {
608
+ // Called when script uses .log method
609
+ console.log('[Rill]', value);
610
+ },
611
+ },
612
+ });
613
+ ```
614
+
615
+ ## Timeouts
616
+
617
+ Set a timeout for async operations:
618
+
619
+ ```typescript
620
+ const ctx = createRuntimeContext({
621
+ timeout: 30000, // 30 seconds
622
+ functions: {
623
+ slowOperation: async () => {
624
+ // Will throw TimeoutError if exceeds 30s
625
+ await longRunningTask();
626
+ return 'done';
627
+ },
628
+ },
629
+ });
630
+ ```
631
+
632
+ ## Auto-Exceptions
633
+
634
+ Halt execution when output matches patterns:
635
+
636
+ ```typescript
637
+ const ctx = createRuntimeContext({
638
+ autoExceptions: [
639
+ 'error:.*', // Matches "error: something"
640
+ 'FATAL', // Matches "FATAL" anywhere
641
+ ],
642
+ functions: {
643
+ process: (args) => {
644
+ // If this returns "error: invalid input",
645
+ // execution halts with AutoExceptionError
646
+ return externalProcess(args[0]);
647
+ },
648
+ },
649
+ });
650
+ ```
651
+
652
+ ## Initial Variables
653
+
654
+ Provide variables accessible in scripts:
655
+
656
+ ```typescript
657
+ const ctx = createRuntimeContext({
658
+ variables: {
659
+ config: {
660
+ apiUrl: 'https://api.example.com',
661
+ maxRetries: 3,
662
+ },
663
+ userId: 'user-123',
664
+ items: [1, 2, 3],
665
+ },
666
+ });
667
+
668
+ // Script can access: $config.apiUrl, $userId, $items
669
+ ```
670
+
671
+ ## Error Handling
672
+
673
+ All Rill errors extend `RillError` with structured information:
674
+
675
+ ```typescript
676
+ import { RuntimeError, ParseError, AbortError, TimeoutError } from '@rcrsr/rill';
677
+
678
+ try {
679
+ const ast = parse(source);
680
+ const result = await execute(ast, ctx);
681
+ } catch (err) {
682
+ if (err instanceof ParseError) {
683
+ console.log('Parse error:', err.message);
684
+ console.log('Location:', err.location);
685
+ } else if (err instanceof RuntimeError) {
686
+ console.log('Runtime error:', err.code);
687
+ console.log('Message:', err.message);
688
+ console.log('Location:', err.location);
689
+ console.log('Context:', err.context);
690
+ } else if (err instanceof AbortError) {
691
+ console.log('Execution cancelled');
692
+ } else if (err instanceof TimeoutError) {
693
+ console.log('Operation timed out');
694
+ }
695
+ }
696
+ ```
697
+
698
+ ### Error Codes
699
+
700
+ | Code | Description |
701
+ |------|-------------|
702
+ | `PARSE_UNEXPECTED_TOKEN` | Unexpected token in source |
703
+ | `PARSE_INVALID_SYNTAX` | Invalid syntax |
704
+ | `PARSE_INVALID_TYPE` | Invalid type annotation |
705
+ | `RUNTIME_UNDEFINED_VARIABLE` | Variable not defined |
706
+ | `RUNTIME_UNDEFINED_FUNCTION` | Function not defined |
707
+ | `RUNTIME_UNDEFINED_METHOD` | Method not defined (built-in only) |
708
+ | `RUNTIME_TYPE_ERROR` | Type mismatch (includes host function parameter validation) |
709
+ | `RUNTIME_TIMEOUT` | Operation timed out |
710
+ | `RUNTIME_ABORTED` | Execution cancelled |
711
+ | `RUNTIME_INVALID_PATTERN` | Invalid regex pattern |
712
+ | `RUNTIME_AUTO_EXCEPTION` | Auto-exception triggered |
713
+
714
+ ## Complete Example
715
+
716
+ ```typescript
717
+ import {
718
+ parse,
719
+ execute,
720
+ createRuntimeContext,
721
+ callable,
722
+ AbortError,
723
+ type RillValue,
724
+ } from '@rcrsr/rill';
725
+
726
+ const script = `
727
+ $config.greeting -> prompt() :> $response
728
+ $response
729
+ `;
730
+
731
+ const controller = new AbortController();
732
+
733
+ const ctx = createRuntimeContext({
734
+ variables: {
735
+ config: {
736
+ greeting: 'Say hello in French',
737
+ },
738
+ utils: {
739
+ // Property-style callable (computed property)
740
+ timestamp: callable(() => Date.now(), true),
741
+ // Regular callable
742
+ format: callable((args) => {
743
+ const [template, ...values] = args;
744
+ return String(template).replace(/\{\}/g, () =>
745
+ String(values.shift() ?? '')
746
+ );
747
+ }),
748
+ },
749
+ },
750
+
751
+ functions: {
752
+ prompt: async (args, ctx, location) => {
753
+ console.log(`[prompt at line ${location?.line}]`);
754
+ return await callLLM(String(args[0]));
755
+ },
756
+ },
757
+
758
+ callbacks: {
759
+ onLog: (value) => console.log('[log]', value),
760
+ },
761
+
762
+ observability: {
763
+ onStepStart: (e) => console.log(`Step ${e.index + 1}...`),
764
+ onStepEnd: (e) => console.log(`Done (${e.durationMs}ms)`),
765
+ },
766
+
767
+ timeout: 30000,
768
+ signal: controller.signal,
769
+ });
770
+
771
+ try {
772
+ const ast = parse(script);
773
+ const result = await execute(ast, ctx);
774
+ console.log('Result:', result.value);
775
+ console.log('Variables:', result.variables);
776
+ } catch (err) {
777
+ if (err instanceof AbortError) {
778
+ console.log('Cancelled');
779
+ } else {
780
+ throw err;
781
+ }
782
+ }
783
+ ```
784
+
785
+ ## API Reference
786
+
787
+ ### Exports
788
+
789
+ ```typescript
790
+ // Parsing
791
+ export { parse, ParseError, tokenize, LexerError };
792
+
793
+ // Execution
794
+ export { execute, createRuntimeContext, createStepper };
795
+ export type { RuntimeContext, RuntimeOptions, ExecutionResult };
796
+ export type { ExecutionStepper, StepResult };
797
+
798
+ // Callable types
799
+ export { callable, isCallable, isScriptCallable, isRuntimeCallable, isApplicationCallable };
800
+ export type { RillCallable, ScriptCallable, RuntimeCallable, ApplicationCallable, CallableFn };
801
+
802
+ // Host function types
803
+ export type { HostFunctionDefinition, HostFunctionParam };
804
+ export { validateHostFunctionArgs };
805
+
806
+ // Value types
807
+ export type { RillValue, RillArgs };
808
+
809
+ // Callbacks
810
+ export type { RuntimeCallbacks, ObservabilityCallbacks };
811
+ export type { StepStartEvent, StepEndEvent, FunctionCallEvent, FunctionReturnEvent };
812
+ export type { CaptureEvent, ErrorEvent };
813
+
814
+ // Errors
815
+ export { RillError, RuntimeError, ParseError, AbortError, TimeoutError, AutoExceptionError };
816
+ export { RILL_ERROR_CODES };
817
+ export type { RillErrorCode };
818
+
819
+ // Utilities
820
+ export { isArgs, isDict, isReservedMethod, RESERVED_DICT_METHODS };
821
+ export type { SourceLocation, SourceSpan };
822
+
823
+ // Control flow (for advanced use)
824
+ export { BreakSignal, ReturnSignal };
825
+ ```
826
+