@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,1251 @@
1
+ /**
2
+ * Expression Evaluation
3
+ *
4
+ * Internal module for AST evaluation. Not part of public API.
5
+ * All evaluation functions are internal implementation details.
6
+ *
7
+ * @internal
8
+ */
9
+ import { AbortError, AutoExceptionError, RILL_ERROR_CODES, RuntimeError, TimeoutError, } from '../../types.js';
10
+ import { BUILTIN_METHODS } from '../ext/builtins.js';
11
+ import { isCallable, isDict, isScriptCallable } from './callable.js';
12
+ import { BreakSignal, ReturnSignal } from './signals.js';
13
+ import { createTupleFromDict, createTupleFromList, deepEquals, formatValue, inferType, isTuple, isReservedMethod, isTruthy, } from './values.js';
14
+ // ============================================================
15
+ // EXPORTED HELPERS (used by execute.ts)
16
+ // ============================================================
17
+ /** Helper to get location from an AST node */
18
+ function getNodeLocation(node) {
19
+ return node?.span.start;
20
+ }
21
+ /**
22
+ * Check if execution has been aborted via AbortSignal.
23
+ * Throws AbortError if signal is aborted.
24
+ */
25
+ export function checkAborted(ctx, node) {
26
+ if (ctx.signal?.aborted) {
27
+ throw new AbortError(getNodeLocation(node));
28
+ }
29
+ }
30
+ /**
31
+ * Check if the current pipe value matches any autoException pattern.
32
+ * Only checks string values. Throws AutoExceptionError on match.
33
+ */
34
+ export function checkAutoExceptions(value, ctx, node) {
35
+ if (typeof value !== 'string' || ctx.autoExceptions.length === 0) {
36
+ return;
37
+ }
38
+ for (const pattern of ctx.autoExceptions) {
39
+ if (pattern.test(value)) {
40
+ throw new AutoExceptionError(pattern.source, value, getNodeLocation(node));
41
+ }
42
+ }
43
+ }
44
+ /**
45
+ * Handle statement capture: set variable and fire observability event.
46
+ * Returns capture info if a capture occurred.
47
+ */
48
+ export function handleCapture(capture, value, ctx) {
49
+ if (!capture)
50
+ return undefined;
51
+ setVariable(ctx, capture.name, value, capture.typeName, capture.span.start);
52
+ const captureInfo = { name: capture.name, value };
53
+ ctx.observability.onCapture?.(captureInfo);
54
+ return captureInfo;
55
+ }
56
+ // ============================================================
57
+ // VARIABLE MANAGEMENT
58
+ // ============================================================
59
+ /**
60
+ * Set a variable with type checking.
61
+ * - First assignment locks the type (inferred or explicit)
62
+ * - Subsequent assignments must match the locked type
63
+ * - Explicit type annotation is validated against value type
64
+ */
65
+ function setVariable(ctx, name, value, explicitType, location) {
66
+ const valueType = inferType(value);
67
+ // Check explicit type annotation matches value
68
+ if (explicitType !== null && explicitType !== valueType) {
69
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, `Type mismatch: cannot assign ${valueType} to $${name}:${explicitType}`, location, { variableName: name, expectedType: explicitType, actualType: valueType });
70
+ }
71
+ // Check if variable already has a locked type
72
+ const lockedType = ctx.variableTypes.get(name);
73
+ if (lockedType !== undefined && lockedType !== valueType) {
74
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, `Type mismatch: cannot assign ${valueType} to $${name} (locked as ${lockedType})`, location, { variableName: name, expectedType: lockedType, actualType: valueType });
75
+ }
76
+ // Set the variable and lock its type
77
+ ctx.variables.set(name, value);
78
+ if (!ctx.variableTypes.has(name)) {
79
+ ctx.variableTypes.set(name, explicitType ?? valueType);
80
+ }
81
+ }
82
+ // ============================================================
83
+ // TIMEOUT WRAPPER
84
+ // ============================================================
85
+ /**
86
+ * Wrap a promise with a timeout. Returns original promise if no timeout configured.
87
+ */
88
+ function withTimeout(promise, timeoutMs, functionName, node) {
89
+ if (timeoutMs === undefined) {
90
+ return promise;
91
+ }
92
+ return Promise.race([
93
+ promise,
94
+ new Promise((_, reject) => {
95
+ setTimeout(() => {
96
+ reject(new TimeoutError(functionName, timeoutMs, getNodeLocation(node)));
97
+ }, timeoutMs);
98
+ }),
99
+ ]);
100
+ }
101
+ // ============================================================
102
+ // EXPRESSION EVALUATION
103
+ // ============================================================
104
+ /**
105
+ * Evaluate argument expressions while preserving the current pipeValue.
106
+ */
107
+ async function evaluateArgs(argExprs, ctx) {
108
+ const savedPipeValue = ctx.pipeValue;
109
+ const args = [];
110
+ for (const arg of argExprs) {
111
+ args.push(await evaluateExpression(arg, ctx));
112
+ }
113
+ ctx.pipeValue = savedPipeValue;
114
+ return args;
115
+ }
116
+ export async function evaluateExpression(expr, ctx) {
117
+ return evaluatePipeChain(expr, ctx);
118
+ }
119
+ async function evaluatePipeChain(chain, ctx) { console.log("evaluatePipeChain called, terminator:", chain.terminator?.type);
120
+ let value = await evaluatePostfixExpr(chain.head, ctx);
121
+ ctx.pipeValue = value;
122
+ for (const target of chain.pipes) {
123
+ value = await evaluatePipeTarget(target, value, ctx);
124
+ ctx.pipeValue = value;
125
+ }
126
+ // Handle chain terminator (capture, break, return)
127
+ if (chain.terminator) {
128
+ if (chain.terminator.type === 'Break') {
129
+ throw new BreakSignal(value);
130
+ }
131
+ if (chain.terminator.type === 'Return') {
132
+ throw new ReturnSignal(value);
133
+ }
134
+ // Capture
135
+ handleCapture(chain.terminator, value, ctx);
136
+ }
137
+ return value;
138
+ }
139
+ async function evaluatePostfixExpr(expr, ctx) {
140
+ let value = await evaluatePrimary(expr.primary, ctx);
141
+ for (const method of expr.methods) {
142
+ value = await evaluateMethod(method, value, ctx);
143
+ }
144
+ return value;
145
+ }
146
+ async function evaluatePrimary(primary, ctx) {
147
+ switch (primary.type) {
148
+ case 'StringLiteral':
149
+ return evaluateString(primary, ctx);
150
+ case 'NumberLiteral':
151
+ return primary.value;
152
+ case 'BoolLiteral':
153
+ return primary.value;
154
+ case 'Tuple':
155
+ return evaluateTuple(primary, ctx);
156
+ case 'Dict':
157
+ return evaluateDict(primary, ctx);
158
+ case 'FunctionLiteral':
159
+ return await createClosure(primary, ctx);
160
+ case 'Variable':
161
+ return evaluateVariableAsync(primary, ctx);
162
+ case 'FunctionCall':
163
+ return evaluateFunctionCall(primary, ctx);
164
+ case 'VariableCall':
165
+ return evaluateVariableCall(primary, ctx);
166
+ case 'MethodCall':
167
+ return evaluateMethod(primary, ctx.pipeValue, ctx);
168
+ case 'Conditional':
169
+ return evaluateConditional(primary, ctx);
170
+ case 'WhileLoop':
171
+ return evaluateWhileLoop(primary, ctx);
172
+ case 'ForLoop':
173
+ return evaluateForLoop(primary, ctx);
174
+ case 'DoWhileLoop':
175
+ return evaluateDoWhileLoop(primary, ctx);
176
+ case 'Block':
177
+ return evaluateBlockExpression(primary, ctx);
178
+ case 'GroupedExpr':
179
+ return evaluateGroupedExpr(primary, ctx);
180
+ case 'Spread':
181
+ return evaluateSpread(primary, ctx);
182
+ default:
183
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, `Unknown primary type: ${primary.type}`, getNodeLocation(primary));
184
+ }
185
+ }
186
+ async function evaluatePipeTarget(target, input, ctx) {
187
+ ctx.pipeValue = input;
188
+ switch (target.type) {
189
+ case 'Capture':
190
+ return evaluateCapture(target, input, ctx);
191
+ case 'FunctionCall':
192
+ return evaluateFunctionCall(target, ctx);
193
+ case 'VariableCall':
194
+ return evaluateVariableCallWithPipe(target, input, ctx);
195
+ case 'Invoke':
196
+ return evaluateInvoke(target, input, ctx);
197
+ case 'MethodCall':
198
+ return evaluateMethod(target, input, ctx);
199
+ case 'Conditional':
200
+ return evaluateConditional(target, ctx);
201
+ case 'WhileLoop':
202
+ return evaluateWhileLoop(target, ctx);
203
+ case 'ForLoop':
204
+ return evaluateForLoop(target, ctx);
205
+ case 'DoWhileLoop':
206
+ return evaluateDoWhileLoop(target, ctx);
207
+ case 'Block':
208
+ return evaluateBlockExpression(target, ctx);
209
+ case 'StringLiteral':
210
+ return evaluateString(target, ctx);
211
+ case 'GroupedExpr':
212
+ return evaluateGroupedExpr(target, ctx);
213
+ case 'ParallelSpread':
214
+ return evaluateParallelSpread(target, input, ctx);
215
+ case 'ParallelFilter':
216
+ return evaluateParallelFilter(target, input, ctx);
217
+ case 'SequentialSpread':
218
+ return evaluateSequentialSpread(target, input, ctx);
219
+ case 'Destructure':
220
+ return evaluateDestructure(target, input, ctx);
221
+ case 'Slice':
222
+ return evaluateSlice(target, input, ctx);
223
+ case 'Enumerate':
224
+ return evaluateEnumerate(target, input);
225
+ case 'Spread':
226
+ return evaluateSpread(target, ctx);
227
+ default:
228
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, `Unknown pipe target type: ${target.type}`, getNodeLocation(target));
229
+ }
230
+ }
231
+ // ============================================================
232
+ // STATEMENT EXECUTION
233
+ // ============================================================
234
+ export async function executeStatement(stmt, ctx) {
235
+ const value = await evaluateExpression(stmt.expression, ctx);
236
+ ctx.pipeValue = value;
237
+ checkAutoExceptions(value, ctx, stmt);
238
+ // Terminator handling is now inside PipeChainNode evaluation
239
+ // (evaluatePipeChain handles capture/break/return terminators)
240
+ return value;
241
+ }
242
+ // ============================================================
243
+ // SPREAD OPERATIONS
244
+ // ============================================================
245
+ async function evaluateParallelSpread(node, input, ctx) {
246
+ const target = await evaluateExpression(node.target, ctx);
247
+ const inputArray = Array.isArray(input) ? input : null;
248
+ const targetArray = Array.isArray(target) ? target : null;
249
+ if (inputArray && targetArray) {
250
+ if (inputArray.length !== targetArray.length) {
251
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, `Parallel zip requires equal lengths: got ${inputArray.length} args and ${targetArray.length} closures`, node.span.start);
252
+ }
253
+ const promises = inputArray.map((arg, i) => {
254
+ const closure = targetArray[i];
255
+ if (closure === undefined) {
256
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, `Missing closure at index ${i}`, node.span.start);
257
+ }
258
+ return invokeAsCallableOrFunction(closure, [arg], ctx, node.span.start);
259
+ });
260
+ return Promise.all(promises);
261
+ }
262
+ else if (inputArray && !targetArray) {
263
+ const promises = inputArray.map((arg) => invokeAsCallableOrFunction(target, [arg], ctx, node.span.start));
264
+ return Promise.all(promises);
265
+ }
266
+ else if (!inputArray && targetArray) {
267
+ const promises = targetArray.map((closure) => invokeAsCallableOrFunction(closure, [input], ctx, node.span.start));
268
+ return Promise.all(promises);
269
+ }
270
+ else {
271
+ const result = await invokeAsCallableOrFunction(target, [input], ctx, node.span.start);
272
+ return [result];
273
+ }
274
+ }
275
+ async function evaluateParallelFilter(node, input, ctx) {
276
+ if (!Array.isArray(input)) {
277
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, `Filter requires list, got ${isDict(input) ? 'dict' : typeof input}`, node.span.start);
278
+ }
279
+ const results = [];
280
+ for (const element of input) {
281
+ const savedPipeValue = ctx.pipeValue;
282
+ ctx.pipeValue = element;
283
+ let predicateResult;
284
+ if (node.predicate.type === 'Block') {
285
+ predicateResult = await evaluateBlockExpression(node.predicate, ctx);
286
+ }
287
+ else {
288
+ const closure = ctx.variables.get(node.predicate.name ?? '');
289
+ if (!closure) {
290
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_UNDEFINED_VARIABLE, `Undefined variable: $${node.predicate.name}`, node.predicate.span.start, { variableName: node.predicate.name });
291
+ }
292
+ if (!isCallable(closure)) {
293
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, `Filter predicate must be callable, got ${typeof closure}`, node.predicate.span.start);
294
+ }
295
+ predicateResult = await invokeCallable(closure, [element], ctx, node.predicate.span.start);
296
+ }
297
+ if (isTruthy(predicateResult)) {
298
+ results.push(element);
299
+ }
300
+ ctx.pipeValue = savedPipeValue;
301
+ }
302
+ return results;
303
+ }
304
+ async function evaluateSequentialSpread(node, input, ctx) {
305
+ const target = await evaluateExpression(node.target, ctx);
306
+ const closures = Array.isArray(target) ? target : [target];
307
+ let accumulated = input;
308
+ for (const closure of closures) {
309
+ accumulated = await invokeAsCallableOrFunction(closure, [accumulated], ctx, node.span.start);
310
+ }
311
+ return accumulated;
312
+ }
313
+ async function invokeAsCallableOrFunction(callableOrName, args, ctx, location) {
314
+ if (isCallable(callableOrName)) {
315
+ return invokeCallable(callableOrName, args, ctx, location);
316
+ }
317
+ if (typeof callableOrName === 'string') {
318
+ const fn = ctx.functions.get(callableOrName);
319
+ if (fn) {
320
+ const result = fn(args, ctx, location);
321
+ return result instanceof Promise ? result : result;
322
+ }
323
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_UNDEFINED_FUNCTION, `Unknown function: ${callableOrName}`, location, { functionName: callableOrName });
324
+ }
325
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, `Expected callable or function name, got ${typeof callableOrName}`, location);
326
+ }
327
+ function evaluateCapture(node, input, ctx) {
328
+ setVariable(ctx, node.name, input, node.typeName, node.span.start);
329
+ ctx.observability.onCapture?.({ name: node.name, value: input });
330
+ return input;
331
+ }
332
+ // ============================================================
333
+ // EXTRACTION OPERATORS
334
+ // ============================================================
335
+ function evaluateDestructure(node, input, ctx) {
336
+ const isList = Array.isArray(input);
337
+ const isDictInput = isDict(input);
338
+ const firstNonSkip = node.elements.find((e) => e.kind !== 'skip');
339
+ const isKeyPattern = firstNonSkip?.kind === 'keyValue';
340
+ if (isKeyPattern) {
341
+ if (!isDictInput) {
342
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, `Key destructure requires dict, got ${isList ? 'list' : typeof input}`, node.span.start);
343
+ }
344
+ for (const elem of node.elements) {
345
+ if (elem.kind === 'skip')
346
+ continue;
347
+ if (elem.kind === 'nested') {
348
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, 'Nested destructure not supported in dict patterns', elem.span.start);
349
+ }
350
+ if (elem.kind !== 'keyValue' || elem.key === null || elem.name === null) {
351
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, 'Dict destructure requires key: $var patterns', elem.span.start);
352
+ }
353
+ const dictInput = input;
354
+ if (!(elem.key in dictInput)) {
355
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, `Key '${elem.key}' not found in dict`, elem.span.start, { key: elem.key, availableKeys: Object.keys(dictInput) });
356
+ }
357
+ const dictValue = dictInput[elem.key];
358
+ if (dictValue === undefined) {
359
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, `Key '${elem.key}' has undefined value`, elem.span.start);
360
+ }
361
+ setVariable(ctx, elem.name, dictValue, elem.typeName, elem.span.start);
362
+ }
363
+ }
364
+ else {
365
+ if (!isList) {
366
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, `Positional destructure requires list, got ${isDictInput ? 'dict' : typeof input}`, node.span.start);
367
+ }
368
+ const listInput = input;
369
+ if (node.elements.length !== listInput.length) {
370
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, `Destructure pattern has ${node.elements.length} elements, list has ${listInput.length}`, node.span.start);
371
+ }
372
+ for (let i = 0; i < node.elements.length; i++) {
373
+ const elem = node.elements[i];
374
+ const value = listInput[i];
375
+ if (elem === undefined || value === undefined) {
376
+ continue;
377
+ }
378
+ if (elem.kind === 'skip')
379
+ continue;
380
+ if (elem.kind === 'nested' && elem.nested) {
381
+ evaluateDestructure(elem.nested, value, ctx);
382
+ continue;
383
+ }
384
+ if (elem.name === null) {
385
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, 'Invalid destructure element', elem.span.start);
386
+ }
387
+ setVariable(ctx, elem.name, value, elem.typeName, elem.span.start);
388
+ }
389
+ }
390
+ return input;
391
+ }
392
+ async function evaluateSlice(node, input, ctx) {
393
+ const isList = Array.isArray(input);
394
+ const isString = typeof input === 'string';
395
+ if (!isList && !isString) {
396
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, `Slice requires list or string, got ${isDict(input) ? 'dict' : typeof input}`, node.span.start);
397
+ }
398
+ const startBound = node.start
399
+ ? await evaluateSliceBound(node.start, ctx)
400
+ : null;
401
+ const stopBound = node.stop ? await evaluateSliceBound(node.stop, ctx) : null;
402
+ const stepBound = node.step ? await evaluateSliceBound(node.step, ctx) : null;
403
+ if (isList) {
404
+ return applySlice(input, input.length, startBound, stopBound, stepBound);
405
+ }
406
+ return applySlice(input, input.length, startBound, stopBound, stepBound);
407
+ }
408
+ async function evaluateSliceBound(bound, ctx) {
409
+ if (bound === null) {
410
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, 'Slice bound is null', undefined);
411
+ }
412
+ switch (bound.type) {
413
+ case 'NumberLiteral':
414
+ return bound.value;
415
+ case 'Variable': {
416
+ const value = evaluateVariable(bound, ctx);
417
+ if (typeof value !== 'number') {
418
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, `Slice bound must be number, got ${typeof value}`, bound.span.start);
419
+ }
420
+ return value;
421
+ }
422
+ case 'GroupedExpr': {
423
+ const value = await evaluateGroupedExpr(bound, ctx);
424
+ if (typeof value !== 'number') {
425
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, `Slice bound must be number, got ${typeof value}`, bound.span.start);
426
+ }
427
+ return value;
428
+ }
429
+ }
430
+ }
431
+ function applySlice(input, len, start, stop, step) {
432
+ const actualStep = step ?? 1;
433
+ if (actualStep === 0) {
434
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, 'Slice step cannot be zero', undefined);
435
+ }
436
+ const normalizeIndex = (idx, defaultVal, forStep) => {
437
+ if (idx === null)
438
+ return defaultVal;
439
+ let normalized = idx < 0 ? len + idx : idx;
440
+ if (forStep > 0) {
441
+ normalized = Math.max(0, Math.min(len, normalized));
442
+ }
443
+ else {
444
+ normalized = Math.max(-1, Math.min(len - 1, normalized));
445
+ }
446
+ return normalized;
447
+ };
448
+ const actualStart = normalizeIndex(start, actualStep > 0 ? 0 : len - 1, actualStep);
449
+ const actualStop = normalizeIndex(stop, actualStep > 0 ? len : -1, actualStep);
450
+ const indices = [];
451
+ if (actualStep > 0) {
452
+ for (let i = actualStart; i < actualStop; i += actualStep) {
453
+ indices.push(i);
454
+ }
455
+ }
456
+ else {
457
+ for (let i = actualStart; i > actualStop; i += actualStep) {
458
+ indices.push(i);
459
+ }
460
+ }
461
+ if (Array.isArray(input)) {
462
+ return indices.map((i) => input[i]);
463
+ }
464
+ else {
465
+ return indices.map((i) => input[i]).join('');
466
+ }
467
+ }
468
+ async function evaluateSpread(node, ctx) {
469
+ let value;
470
+ if (node.operand === null) {
471
+ value = ctx.pipeValue;
472
+ }
473
+ else {
474
+ value = await evaluateExpression(node.operand, ctx);
475
+ }
476
+ if (Array.isArray(value)) {
477
+ return createTupleFromList(value);
478
+ }
479
+ if (isDict(value)) {
480
+ return createTupleFromDict(value);
481
+ }
482
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, `Spread requires list or dict, got ${inferType(value)}`, node.span.start);
483
+ }
484
+ function evaluateEnumerate(node, input) {
485
+ if (Array.isArray(input)) {
486
+ return input.map((value, index) => ({
487
+ index,
488
+ value,
489
+ }));
490
+ }
491
+ if (isDict(input)) {
492
+ const keys = Object.keys(input).sort();
493
+ return keys.map((key, index) => ({
494
+ index,
495
+ key,
496
+ value: input[key],
497
+ }));
498
+ }
499
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, `Enumerate requires list or dict, got ${typeof input}`, node.span.start);
500
+ }
501
+ // ============================================================
502
+ // LITERAL EVALUATION
503
+ // ============================================================
504
+ async function evaluateString(node, ctx) {
505
+ let result = '';
506
+ for (const part of node.parts) {
507
+ if (typeof part === 'string') {
508
+ result += part;
509
+ }
510
+ else {
511
+ result += formatValue(ctx.pipeValue);
512
+ }
513
+ }
514
+ // Handle {$fn(args)} patterns
515
+ const varCallPattern = /\{\s*\$([a-zA-Z_][a-zA-Z0-9_]*)\(\s*([^)]*)\s*\)\s*\}/g;
516
+ const varCallMatches = [...result.matchAll(varCallPattern)];
517
+ for (const match of varCallMatches.reverse()) {
518
+ const fullMatch = match[0];
519
+ const fnName = match[1] ?? '';
520
+ const argsStr = match[2] ?? '';
521
+ const closure = ctx.variables.get(fnName);
522
+ if (closure && isCallable(closure)) {
523
+ const args = parseInterpolationArgs(argsStr, ctx);
524
+ const callResult = await invokeCallable(closure, args, ctx, node.span.start);
525
+ result =
526
+ result.slice(0, match.index) +
527
+ formatValue(callResult) +
528
+ result.slice(match.index + fullMatch.length);
529
+ }
530
+ }
531
+ // Handle {$} and {$.field} patterns
532
+ result = result.replace(/\{\s*\$(?![a-zA-Z_])([^}]*)\}/g, (_match, field) => {
533
+ let value = ctx.pipeValue;
534
+ const trimmed = field.trim();
535
+ if (trimmed) {
536
+ value = accessField(value, trimmed.slice(1));
537
+ }
538
+ return formatValue(value);
539
+ });
540
+ // Handle {$name} and {$name.field} patterns
541
+ const varPattern = /\{\s*\$([a-zA-Z_][a-zA-Z0-9_]*)([^}]*)\}/g;
542
+ const varMatches = [...result.matchAll(varPattern)];
543
+ for (const match of varMatches.reverse()) {
544
+ const fullMatch = match[0];
545
+ const name = match[1] ?? '';
546
+ const field = match[2] ?? '';
547
+ const idx = match.index;
548
+ if (!ctx.variables.has(name)) {
549
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_UNDEFINED_VARIABLE, `Undefined variable: $${name}`, getNodeLocation(node), { variableName: name });
550
+ }
551
+ let value = ctx.variables.get(name) ?? null;
552
+ if (isScriptCallable(value)) {
553
+ const args = value.params.length > 0 ? [ctx.pipeValue] : [];
554
+ value = await invokeScriptCallable(value, args, ctx, node.span.start);
555
+ }
556
+ const trimmed = field.trim();
557
+ if (trimmed) {
558
+ value = accessField(value, trimmed.slice(1));
559
+ }
560
+ result =
561
+ result.slice(0, idx) +
562
+ formatValue(value) +
563
+ result.slice(idx + fullMatch.length);
564
+ }
565
+ // Handle {.method} patterns
566
+ result = result.replace(/\{\s*\.([a-zA-Z_][a-zA-Z0-9_]*)\s*\}/g, (_match, methodName) => {
567
+ const method = BUILTIN_METHODS[methodName];
568
+ if (method) {
569
+ const methodResult = method(ctx.pipeValue, [], ctx);
570
+ return formatValue(methodResult);
571
+ }
572
+ return `{.${methodName}}`;
573
+ });
574
+ return result;
575
+ }
576
+ function parseInterpolationArgs(argsStr, ctx) {
577
+ const trimmed = argsStr.trim();
578
+ if (!trimmed)
579
+ return [];
580
+ const args = [];
581
+ const parts = trimmed.split(',').map((p) => p.trim());
582
+ for (const part of parts) {
583
+ if (part.startsWith('"') && part.endsWith('"')) {
584
+ args.push(part.slice(1, -1));
585
+ }
586
+ else if (/^-?\d+(\.\d+)?$/.test(part)) {
587
+ args.push(parseFloat(part));
588
+ }
589
+ else if (part === '$') {
590
+ args.push(ctx.pipeValue);
591
+ }
592
+ else if (part.startsWith('$')) {
593
+ const varName = part.slice(1);
594
+ args.push(ctx.variables.get(varName) ?? null);
595
+ }
596
+ else if (part === 'true') {
597
+ args.push(true);
598
+ }
599
+ else if (part === 'false') {
600
+ args.push(false);
601
+ }
602
+ else {
603
+ args.push(part);
604
+ }
605
+ }
606
+ return args;
607
+ }
608
+ async function evaluateTuple(node, ctx) {
609
+ const elements = [];
610
+ for (const elem of node.elements) {
611
+ elements.push(await evaluateExpression(elem, ctx));
612
+ }
613
+ return elements;
614
+ }
615
+ function isFunctionLiteralExpr(expr) {
616
+ if (expr.pipes.length > 0)
617
+ return false;
618
+ if (expr.head.methods.length > 0)
619
+ return false;
620
+ return expr.head.primary.type === 'FunctionLiteral';
621
+ }
622
+ async function evaluateDict(node, ctx) {
623
+ const result = {};
624
+ for (const entry of node.entries) {
625
+ if (isReservedMethod(entry.key)) {
626
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, `Cannot use reserved method name '${entry.key}' as dict key`, entry.span.start, { key: entry.key, reservedMethods: ['keys', 'values', 'entries'] });
627
+ }
628
+ if (isFunctionLiteralExpr(entry.value)) {
629
+ const fnLit = entry.value.head.primary;
630
+ const closure = await createClosure(fnLit, ctx);
631
+ result[entry.key] = closure;
632
+ }
633
+ else {
634
+ result[entry.key] = await evaluateExpression(entry.value, ctx);
635
+ }
636
+ }
637
+ for (const key of Object.keys(result)) {
638
+ const value = result[key];
639
+ if (value !== undefined && isCallable(value)) {
640
+ result[key] = {
641
+ ...value,
642
+ boundDict: result,
643
+ };
644
+ }
645
+ }
646
+ return result;
647
+ }
648
+ async function createClosure(node, ctx) {
649
+ const capturedVars = new Map(ctx.variables);
650
+ const params = [];
651
+ for (const param of node.params) {
652
+ let defaultValue = null;
653
+ if (param.defaultValue) {
654
+ defaultValue = await evaluatePrimary(param.defaultValue, ctx);
655
+ }
656
+ params.push({
657
+ name: param.name,
658
+ typeName: param.typeName,
659
+ defaultValue,
660
+ });
661
+ }
662
+ const isProperty = params.length === 0;
663
+ return {
664
+ __type: 'callable',
665
+ kind: 'script',
666
+ params,
667
+ body: node.body,
668
+ capturedVars,
669
+ isProperty,
670
+ };
671
+ }
672
+ // ============================================================
673
+ // VARIABLE EVALUATION
674
+ // ============================================================
675
+ function getBaseVariableValue(node, ctx) {
676
+ if (node.isPipeVar)
677
+ return ctx.pipeValue;
678
+ if (node.name)
679
+ return ctx.variables.get(node.name) ?? null;
680
+ return null;
681
+ }
682
+ function evaluateVariable(node, ctx) {
683
+ let value = getBaseVariableValue(node, ctx);
684
+ for (const access of node.fieldAccess) {
685
+ value = accessField(value, access.field);
686
+ }
687
+ return value;
688
+ }
689
+ async function evaluateVariableAsync(node, ctx) {
690
+ let value = getBaseVariableValue(node, ctx);
691
+ for (const access of node.fieldAccess) {
692
+ value = accessField(value, access.field);
693
+ if (isCallable(value) && value.isProperty && value.boundDict) {
694
+ value = await invokeCallable(value, [], ctx, node.span.start);
695
+ }
696
+ }
697
+ return value;
698
+ }
699
+ function accessField(value, field) {
700
+ if (value === null)
701
+ return null;
702
+ if (typeof field === 'number') {
703
+ if (Array.isArray(value))
704
+ return value[field] ?? null;
705
+ if (typeof value === 'string')
706
+ return value[field] ?? '';
707
+ return null;
708
+ }
709
+ if (typeof value === 'object' &&
710
+ !Array.isArray(value) &&
711
+ !isScriptCallable(value)) {
712
+ return value[field] ?? null;
713
+ }
714
+ return null;
715
+ }
716
+ // ============================================================
717
+ // FUNCTION & METHOD EVALUATION
718
+ // ============================================================
719
+ async function evaluateFunctionCall(node, ctx) {
720
+ checkAborted(ctx, node);
721
+ const fn = ctx.functions.get(node.name);
722
+ if (!fn) {
723
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_UNDEFINED_FUNCTION, `Unknown function: ${node.name}`, getNodeLocation(node), { functionName: node.name });
724
+ }
725
+ const args = await evaluateArgs(node.args, ctx);
726
+ if (args.length === 0 && ctx.pipeValue !== null) {
727
+ args.push(ctx.pipeValue);
728
+ }
729
+ ctx.observability.onFunctionCall?.({ name: node.name, args });
730
+ const startTime = Date.now();
731
+ const location = getNodeLocation(node);
732
+ const result = fn(args, ctx, location);
733
+ let value;
734
+ if (result instanceof Promise) {
735
+ value = await withTimeout(result, ctx.timeout, node.name, node);
736
+ }
737
+ else {
738
+ value = result;
739
+ }
740
+ ctx.observability.onFunctionReturn?.({
741
+ name: node.name,
742
+ value,
743
+ durationMs: Date.now() - startTime,
744
+ });
745
+ return value;
746
+ }
747
+ async function evaluateVariableCall(node, ctx) {
748
+ return evaluateVariableCallWithPipe(node, ctx.pipeValue, ctx);
749
+ }
750
+ async function evaluateVariableCallWithPipe(node, pipeInput, ctx) {
751
+ const closure = ctx.variables.get(node.name);
752
+ if (!closure) {
753
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_UNDEFINED_VARIABLE, `Unknown variable: $${node.name}`, getNodeLocation(node), { variableName: node.name });
754
+ }
755
+ if (!isCallable(closure)) {
756
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, `Variable $${node.name} is not a function (got ${typeof closure})`, getNodeLocation(node), { variableName: node.name, actualType: typeof closure });
757
+ }
758
+ const args = await evaluateArgs(node.args, ctx);
759
+ if (isScriptCallable(closure) &&
760
+ args.length === 0 &&
761
+ pipeInput !== null &&
762
+ closure.params.length > 0) {
763
+ const firstParam = closure.params[0];
764
+ if (firstParam?.defaultValue === null && !isCallable(pipeInput)) {
765
+ args.push(pipeInput);
766
+ }
767
+ }
768
+ return invokeCallable(closure, args, ctx, node.span.start);
769
+ }
770
+ async function invokeCallable(callable, args, ctx, callLocation) {
771
+ checkAborted(ctx, undefined);
772
+ if (callable.kind === 'script') {
773
+ return invokeScriptCallable(callable, args, ctx, callLocation);
774
+ }
775
+ else {
776
+ return invokeFnCallable(callable, args, ctx, callLocation);
777
+ }
778
+ }
779
+ async function invokeFnCallable(callable, args, ctx, callLocation) {
780
+ const effectiveArgs = callable.boundDict && args.length === 0 ? [callable.boundDict] : args;
781
+ const result = callable.fn(effectiveArgs, ctx, callLocation);
782
+ return result instanceof Promise ? await result : result;
783
+ }
784
+ // ============================================================
785
+ // CALLABLE INVOCATION HELPERS
786
+ // ============================================================
787
+ function createCallableContext(callable, ctx) {
788
+ const callableCtx = {
789
+ ...ctx,
790
+ variables: new Map(callable.capturedVars),
791
+ variableTypes: new Map(ctx.variableTypes),
792
+ };
793
+ if (callable.boundDict) {
794
+ callableCtx.pipeValue = callable.boundDict;
795
+ }
796
+ return callableCtx;
797
+ }
798
+ function inferTypeFromDefault(defaultValue) {
799
+ if (defaultValue === null)
800
+ return null;
801
+ const t = inferType(defaultValue);
802
+ return t === 'string' || t === 'number' || t === 'bool' ? t : null;
803
+ }
804
+ function validateParamType(param, value, callLocation) {
805
+ const expectedType = param.typeName ?? inferTypeFromDefault(param.defaultValue);
806
+ if (expectedType !== null) {
807
+ const valueType = inferType(value);
808
+ if (valueType !== expectedType) {
809
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, `Parameter type mismatch: ${param.name} expects ${expectedType}, got ${valueType}`, callLocation, { paramName: param.name, expectedType, actualType: valueType });
810
+ }
811
+ }
812
+ }
813
+ async function invokeScriptCallable(callable, args, ctx, callLocation) {
814
+ const firstArg = args[0];
815
+ if (args.length === 1 && firstArg !== undefined && isTuple(firstArg)) {
816
+ return invokeScriptCallableWithArgs(callable, firstArg, ctx, callLocation);
817
+ }
818
+ const callableCtx = createCallableContext(callable, ctx);
819
+ for (let i = 0; i < callable.params.length; i++) {
820
+ const param = callable.params[i];
821
+ let value;
822
+ if (i < args.length) {
823
+ value = args[i];
824
+ }
825
+ else if (param.defaultValue !== null) {
826
+ value = param.defaultValue;
827
+ }
828
+ else {
829
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, `Missing argument for parameter '${param.name}' at position ${i}`, callLocation, { paramName: param.name, position: i });
830
+ }
831
+ validateParamType(param, value, callLocation);
832
+ callableCtx.variables.set(param.name, value);
833
+ }
834
+ return evaluateSimpleBodyExpression(callable.body, callableCtx);
835
+ }
836
+ async function invokeScriptCallableWithArgs(closure, tupleValue, ctx, callLocation) {
837
+ const closureCtx = createCallableContext(closure, ctx);
838
+ const hasNumericKeys = [...tupleValue.entries.keys()].some((k) => typeof k === 'number');
839
+ const hasStringKeys = [...tupleValue.entries.keys()].some((k) => typeof k === 'string');
840
+ if (hasNumericKeys && hasStringKeys) {
841
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, 'Tuple cannot mix positional (numeric) and named (string) keys', callLocation);
842
+ }
843
+ const boundParams = new Set();
844
+ if (hasNumericKeys) {
845
+ for (const [key, value] of tupleValue.entries) {
846
+ const position = key;
847
+ const param = closure.params[position];
848
+ if (param === undefined) {
849
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, `Extra argument at position ${position} (closure has ${closure.params.length} params)`, callLocation, { position, paramCount: closure.params.length });
850
+ }
851
+ validateParamType(param, value, callLocation);
852
+ closureCtx.variables.set(param.name, value);
853
+ boundParams.add(param.name);
854
+ }
855
+ }
856
+ else if (hasStringKeys) {
857
+ const paramNames = new Set(closure.params.map((p) => p.name));
858
+ for (const [key, value] of tupleValue.entries) {
859
+ const name = key;
860
+ if (!paramNames.has(name)) {
861
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, `Unknown argument '${name}' (valid params: ${[...paramNames].join(', ')})`, callLocation, { argName: name, validParams: [...paramNames] });
862
+ }
863
+ const param = closure.params.find((p) => p.name === name);
864
+ validateParamType(param, value, callLocation);
865
+ closureCtx.variables.set(name, value);
866
+ boundParams.add(name);
867
+ }
868
+ }
869
+ for (const param of closure.params) {
870
+ if (!boundParams.has(param.name)) {
871
+ if (param.defaultValue !== null) {
872
+ closureCtx.variables.set(param.name, param.defaultValue);
873
+ }
874
+ else {
875
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, `Missing argument '${param.name}' (no default value)`, callLocation, { paramName: param.name });
876
+ }
877
+ }
878
+ }
879
+ return evaluateSimpleBodyExpression(closure.body, closureCtx);
880
+ }
881
+ async function evaluateInvoke(node, input, ctx) {
882
+ if (!isScriptCallable(input)) {
883
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, `Cannot invoke non-closure value (got ${typeof input})`, getNodeLocation(node));
884
+ }
885
+ const args = await evaluateArgs(node.args, ctx);
886
+ return invokeScriptCallable(input, args, ctx, node.span.start);
887
+ }
888
+ async function evaluateMethod(node, receiver, ctx) {
889
+ checkAborted(ctx, node);
890
+ if (isCallable(receiver)) {
891
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, `Method .${node.name} not available on callable (invoke with -> () first)`, getNodeLocation(node), { methodName: node.name, receiverType: 'callable' });
892
+ }
893
+ const args = await evaluateArgs(node.args, ctx);
894
+ if (isDict(receiver)) {
895
+ const dictValue = receiver[node.name];
896
+ if (dictValue !== undefined && isCallable(dictValue)) {
897
+ return invokeCallable(dictValue, args, ctx, getNodeLocation(node));
898
+ }
899
+ }
900
+ const method = ctx.methods.get(node.name);
901
+ if (!method) {
902
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_UNDEFINED_METHOD, `Unknown method: ${node.name}`, getNodeLocation(node), { methodName: node.name });
903
+ }
904
+ const result = method(receiver, args, ctx, getNodeLocation(node));
905
+ return result instanceof Promise ? await result : result;
906
+ }
907
+ // ============================================================
908
+ // CONTROL FLOW EVALUATION
909
+ // ============================================================
910
+ async function evaluateConditional(node, ctx) { console.log("evaluateConditional called, condition:", node.condition?.type, "thenBranch:", node.thenBranch?.type);
911
+ let conditionResult;
912
+ if (node.condition) {
913
+ conditionResult = await evaluateBoolExpr(node.condition, ctx);
914
+ }
915
+ else {
916
+ conditionResult = isTruthy(ctx.pipeValue);
917
+ }
918
+ if (conditionResult) {
919
+ // Use evaluateSimpleBody (not evaluateSimpleBodyExpression) so ReturnSignal
920
+ // propagates up to the containing block rather than being caught here
921
+ return evaluateSimpleBody(node.thenBranch, ctx);
922
+ }
923
+ else if (node.elseBranch) {
924
+ if (node.elseBranch.type === 'Conditional') {
925
+ return evaluateConditional(node.elseBranch, ctx);
926
+ }
927
+ return evaluateSimpleBody(node.elseBranch, ctx);
928
+ }
929
+ return ctx.pipeValue;
930
+ }
931
+ async function evaluateWhileLoop(node, ctx) {
932
+ const inputValue = ctx.pipeValue;
933
+ let maxIterations = Infinity;
934
+ if (node.maxIterations) {
935
+ const maxVal = await evaluateExpression(node.maxIterations, ctx);
936
+ if (typeof maxVal === 'number') {
937
+ maxIterations = maxVal;
938
+ }
939
+ }
940
+ ctx.pipeValue = inputValue;
941
+ let iterations = 0;
942
+ let value = ctx.pipeValue;
943
+ try {
944
+ while (iterations < maxIterations) {
945
+ checkAborted(ctx, node);
946
+ const conditionResult = await evaluateBoolExpr(node.condition, ctx);
947
+ if (!conditionResult)
948
+ break;
949
+ value = await evaluateSimpleBody(node.body, ctx);
950
+ ctx.pipeValue = value;
951
+ iterations++;
952
+ }
953
+ }
954
+ catch (e) {
955
+ if (e instanceof BreakSignal) {
956
+ return e.value;
957
+ }
958
+ throw e;
959
+ }
960
+ return value;
961
+ }
962
+ async function evaluateForLoop(node, ctx) {
963
+ const input = ctx.pipeValue;
964
+ const results = [];
965
+ try {
966
+ if (Array.isArray(input)) {
967
+ for (const item of input) {
968
+ checkAborted(ctx, node);
969
+ ctx.pipeValue = item;
970
+ results.push(await evaluateSimpleBody(node.body, ctx));
971
+ }
972
+ }
973
+ else if (typeof input === 'string') {
974
+ for (const char of input) {
975
+ checkAborted(ctx, node);
976
+ ctx.pipeValue = char;
977
+ results.push(await evaluateSimpleBody(node.body, ctx));
978
+ }
979
+ }
980
+ else {
981
+ checkAborted(ctx, node);
982
+ results.push(await evaluateSimpleBody(node.body, ctx));
983
+ }
984
+ }
985
+ catch (e) {
986
+ if (e instanceof BreakSignal) {
987
+ return e.value;
988
+ }
989
+ throw e;
990
+ }
991
+ return results;
992
+ }
993
+ async function evaluateDoWhileLoop(node, ctx) {
994
+ const inputValue = ctx.pipeValue;
995
+ let maxIterations = Infinity;
996
+ if (node.maxIterations) {
997
+ const maxVal = await evaluateExpression(node.maxIterations, ctx);
998
+ if (typeof maxVal === 'number') {
999
+ maxIterations = maxVal;
1000
+ }
1001
+ }
1002
+ ctx.pipeValue = inputValue;
1003
+ let iterations = 0;
1004
+ let value = ctx.pipeValue;
1005
+ try {
1006
+ // Do-while: body executes first, then condition is checked
1007
+ let shouldContinue = true;
1008
+ while (shouldContinue) {
1009
+ checkAborted(ctx, node);
1010
+ value = await evaluateSimpleBody(node.body, ctx);
1011
+ ctx.pipeValue = value;
1012
+ iterations++;
1013
+ if (iterations >= maxIterations) {
1014
+ shouldContinue = false;
1015
+ }
1016
+ else {
1017
+ shouldContinue = await evaluateBoolExpr(node.condition, ctx);
1018
+ }
1019
+ }
1020
+ }
1021
+ catch (e) {
1022
+ if (e instanceof BreakSignal) {
1023
+ return e.value;
1024
+ }
1025
+ throw e;
1026
+ }
1027
+ return value;
1028
+ }
1029
+ async function evaluateBlock(node, ctx) {
1030
+ let lastValue = ctx.pipeValue;
1031
+ for (const stmt of node.statements) {
1032
+ lastValue = await executeStatement(stmt, ctx);
1033
+ }
1034
+ return lastValue;
1035
+ }
1036
+ async function evaluateBlockExpression(node, ctx) {
1037
+ try {
1038
+ return await evaluateBlock(node, ctx);
1039
+ }
1040
+ catch (e) {
1041
+ if (e instanceof ReturnSignal) {
1042
+ return e.value;
1043
+ }
1044
+ throw e;
1045
+ }
1046
+ }
1047
+ /**
1048
+ * Evaluate a simple body (Block, GroupedExpr, or PostfixExpr).
1049
+ * Used by conditionals and loops.
1050
+ */
1051
+ async function evaluateSimpleBody(node, ctx) { console.log("evaluateSimpleBody called with type:", node.type);
1052
+ switch (node.type) {
1053
+ case 'Block':
1054
+ return evaluateBlock(node, ctx);
1055
+ case 'GroupedExpr':
1056
+ return evaluateGroupedExpr(node, ctx);
1057
+ case 'PostfixExpr':
1058
+ return evaluatePostfixExpr(node, ctx);
1059
+ case 'PipeChain':
1060
+ return evaluatePipeChain(node, ctx);
1061
+ }
1062
+ }
1063
+ /**
1064
+ * Evaluate a simple body as an expression (catches ReturnSignal).
1065
+ */
1066
+ async function evaluateSimpleBodyExpression(node, ctx) {
1067
+ try {
1068
+ return await evaluateSimpleBody(node, ctx);
1069
+ }
1070
+ catch (e) {
1071
+ if (e instanceof ReturnSignal) {
1072
+ return e.value;
1073
+ }
1074
+ throw e;
1075
+ }
1076
+ }
1077
+ // ============================================================
1078
+ // BOOLEAN EXPRESSION EVALUATION
1079
+ // ============================================================
1080
+ async function evaluateBoolExpr(expr, ctx) {
1081
+ if (expr.type === 'Comparison') {
1082
+ return evaluateComparison(expr, ctx);
1083
+ }
1084
+ switch (expr.op) {
1085
+ case 'or': {
1086
+ for (const operand of expr.operands) {
1087
+ if (await evaluateBoolExpr(operand, ctx))
1088
+ return true;
1089
+ }
1090
+ return false;
1091
+ }
1092
+ case 'and': {
1093
+ for (const operand of expr.operands) {
1094
+ if (!(await evaluateBoolExpr(operand, ctx)))
1095
+ return false;
1096
+ }
1097
+ return true;
1098
+ }
1099
+ case 'not': {
1100
+ return !(await evaluateBoolExpr(expr.operand, ctx));
1101
+ }
1102
+ }
1103
+ }
1104
+ async function evaluateComparison(node, ctx) {
1105
+ const left = await evaluateSimplePrimary(node.left, ctx);
1106
+ if (!node.op || !node.right) {
1107
+ return isTruthy(left);
1108
+ }
1109
+ const right = await evaluateSimplePrimary(node.right, ctx);
1110
+ switch (node.op) {
1111
+ case '==':
1112
+ return deepEquals(left, right);
1113
+ case '!=':
1114
+ return !deepEquals(left, right);
1115
+ case '<':
1116
+ return left < right;
1117
+ case '>':
1118
+ return left > right;
1119
+ case '<=':
1120
+ return left <= right;
1121
+ case '>=':
1122
+ return left >= right;
1123
+ default:
1124
+ return false;
1125
+ }
1126
+ }
1127
+ async function evaluateSimplePrimary(node, ctx) {
1128
+ switch (node.type) {
1129
+ case 'StringLiteral':
1130
+ return evaluateString(node, ctx);
1131
+ case 'NumberLiteral':
1132
+ return node.value;
1133
+ case 'BoolLiteral':
1134
+ return node.value;
1135
+ case 'Tuple':
1136
+ return evaluateTuple(node, ctx);
1137
+ case 'Dict':
1138
+ return evaluateDict(node, ctx);
1139
+ case 'Variable':
1140
+ return evaluateVariable(node, ctx);
1141
+ case 'FunctionCall':
1142
+ return evaluateFunctionCall(node, ctx);
1143
+ case 'MethodCall':
1144
+ return evaluateMethod(node, ctx.pipeValue, ctx);
1145
+ case 'Block':
1146
+ return evaluateBlockExpression(node, ctx);
1147
+ case 'BinaryExpr':
1148
+ return evaluateBinaryExpr(node, ctx);
1149
+ case 'UnaryExpr':
1150
+ return evaluateUnaryExpr(node, ctx);
1151
+ case 'GroupedExpr':
1152
+ return evaluateGroupedExpr(node, ctx);
1153
+ case 'PostfixExpr':
1154
+ return evaluatePostfixExpr(node, ctx);
1155
+ default:
1156
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, `Unknown simple primary type: ${node.type}`, getNodeLocation(node));
1157
+ }
1158
+ }
1159
+ // ============================================================
1160
+ // ARITHMETIC / GROUPED EXPRESSIONS
1161
+ // ============================================================
1162
+ async function evaluateBinaryExpr(node, ctx) {
1163
+ const left = await evaluateArithHead(node.left, ctx);
1164
+ const right = await evaluateArithHead(node.right, ctx);
1165
+ switch (node.op) {
1166
+ case '+':
1167
+ return left + right;
1168
+ case '-':
1169
+ return left - right;
1170
+ case '*':
1171
+ return left * right;
1172
+ case '/':
1173
+ if (right === 0) {
1174
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, 'Division by zero', node.span.start);
1175
+ }
1176
+ return left / right;
1177
+ case '%':
1178
+ if (right === 0) {
1179
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, 'Modulo by zero', node.span.start);
1180
+ }
1181
+ return left % right;
1182
+ }
1183
+ }
1184
+ async function evaluateUnaryExpr(node, ctx) {
1185
+ const operand = node.operand;
1186
+ if (operand.type === 'UnaryExpr') {
1187
+ return -(await evaluateUnaryExpr(operand, ctx));
1188
+ }
1189
+ const value = await evaluatePostfixExprNumber(operand, ctx);
1190
+ return -value;
1191
+ }
1192
+ async function evaluateArithHead(node, ctx) {
1193
+ switch (node.type) {
1194
+ case 'BinaryExpr':
1195
+ return evaluateBinaryExpr(node, ctx);
1196
+ case 'UnaryExpr':
1197
+ return evaluateUnaryExpr(node, ctx);
1198
+ case 'PostfixExpr':
1199
+ return evaluatePostfixExprNumber(node, ctx);
1200
+ }
1201
+ }
1202
+ async function evaluatePostfixExprNumber(node, ctx) {
1203
+ const value = await evaluatePostfixExpr(node, ctx);
1204
+ if (typeof value !== 'number') {
1205
+ throw new RuntimeError(RILL_ERROR_CODES.RUNTIME_TYPE_ERROR, `Arithmetic requires number, got ${inferType(value)}`, node.span.start);
1206
+ }
1207
+ return value;
1208
+ }
1209
+ async function evaluateGroupedExpr(node, ctx) {
1210
+ return evaluateInnerExpr(node.expression, ctx);
1211
+ }
1212
+ async function evaluateInnerExpr(node, ctx) {
1213
+ // Evaluate head (arithmetic or postfix)
1214
+ let value;
1215
+ switch (node.head.type) {
1216
+ case 'BinaryExpr':
1217
+ value = await evaluateBinaryExpr(node.head, ctx);
1218
+ break;
1219
+ case 'UnaryExpr':
1220
+ value = await evaluateUnaryExpr(node.head, ctx);
1221
+ break;
1222
+ case 'PostfixExpr':
1223
+ value = await evaluatePostfixExpr(node.head, ctx);
1224
+ break;
1225
+ }
1226
+ // Pipe through targets
1227
+ ctx.pipeValue = value;
1228
+ for (const target of node.pipes) {
1229
+ checkAborted(ctx, node);
1230
+ if (target.type === 'Capture') {
1231
+ value = evaluateCapture(target, value, ctx);
1232
+ }
1233
+ else {
1234
+ value = await evaluatePipeTarget(target, value, ctx);
1235
+ }
1236
+ ctx.pipeValue = value;
1237
+ }
1238
+ // Handle terminator
1239
+ if (node.terminator) {
1240
+ if (node.terminator.type === 'Break') {
1241
+ throw new BreakSignal(value);
1242
+ }
1243
+ if (node.terminator.type === 'Return') {
1244
+ throw new ReturnSignal(value);
1245
+ }
1246
+ // Capture
1247
+ handleCapture(node.terminator, value, ctx);
1248
+ }
1249
+ return value;
1250
+ }
1251
+ //# sourceMappingURL=evaluate.js.map