@grimoirelabs/core 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 (205) hide show
  1. package/dist/builders/expressions.d.ts +33 -0
  2. package/dist/builders/expressions.d.ts.map +1 -0
  3. package/dist/builders/expressions.js +57 -0
  4. package/dist/builders/expressions.js.map +1 -0
  5. package/dist/builders/index.d.ts +44 -0
  6. package/dist/builders/index.d.ts.map +1 -0
  7. package/dist/builders/index.js +32 -0
  8. package/dist/builders/index.js.map +1 -0
  9. package/dist/builders/spell-builder.d.ts +124 -0
  10. package/dist/builders/spell-builder.d.ts.map +1 -0
  11. package/dist/builders/spell-builder.js +299 -0
  12. package/dist/builders/spell-builder.js.map +1 -0
  13. package/dist/builders/step-builder.d.ts +212 -0
  14. package/dist/builders/step-builder.d.ts.map +1 -0
  15. package/dist/builders/step-builder.js +499 -0
  16. package/dist/builders/step-builder.js.map +1 -0
  17. package/dist/compiler/expression-parser.d.ts +14 -0
  18. package/dist/compiler/expression-parser.d.ts.map +1 -0
  19. package/dist/compiler/expression-parser.js +460 -0
  20. package/dist/compiler/expression-parser.js.map +1 -0
  21. package/dist/compiler/grimoire/ast.d.ts +450 -0
  22. package/dist/compiler/grimoire/ast.d.ts.map +1 -0
  23. package/dist/compiler/grimoire/ast.js +19 -0
  24. package/dist/compiler/grimoire/ast.js.map +1 -0
  25. package/dist/compiler/grimoire/errors.d.ts +65 -0
  26. package/dist/compiler/grimoire/errors.d.ts.map +1 -0
  27. package/dist/compiler/grimoire/errors.js +86 -0
  28. package/dist/compiler/grimoire/errors.js.map +1 -0
  29. package/dist/compiler/grimoire/index.d.ts +24 -0
  30. package/dist/compiler/grimoire/index.d.ts.map +1 -0
  31. package/dist/compiler/grimoire/index.js +63 -0
  32. package/dist/compiler/grimoire/index.js.map +1 -0
  33. package/dist/compiler/grimoire/parser.d.ts +135 -0
  34. package/dist/compiler/grimoire/parser.d.ts.map +1 -0
  35. package/dist/compiler/grimoire/parser.js +2148 -0
  36. package/dist/compiler/grimoire/parser.js.map +1 -0
  37. package/dist/compiler/grimoire/tokenizer.d.ts +59 -0
  38. package/dist/compiler/grimoire/tokenizer.d.ts.map +1 -0
  39. package/dist/compiler/grimoire/tokenizer.js +509 -0
  40. package/dist/compiler/grimoire/tokenizer.js.map +1 -0
  41. package/dist/compiler/grimoire/transformer.d.ts +71 -0
  42. package/dist/compiler/grimoire/transformer.d.ts.map +1 -0
  43. package/dist/compiler/grimoire/transformer.js +1011 -0
  44. package/dist/compiler/grimoire/transformer.js.map +1 -0
  45. package/dist/compiler/index.d.ts +45 -0
  46. package/dist/compiler/index.d.ts.map +1 -0
  47. package/dist/compiler/index.js +97 -0
  48. package/dist/compiler/index.js.map +1 -0
  49. package/dist/compiler/ir-generator.d.ts +16 -0
  50. package/dist/compiler/ir-generator.d.ts.map +1 -0
  51. package/dist/compiler/ir-generator.js +997 -0
  52. package/dist/compiler/ir-generator.js.map +1 -0
  53. package/dist/compiler/validator.d.ts +15 -0
  54. package/dist/compiler/validator.d.ts.map +1 -0
  55. package/dist/compiler/validator.js +401 -0
  56. package/dist/compiler/validator.js.map +1 -0
  57. package/dist/index.d.ts +16 -0
  58. package/dist/index.d.ts.map +1 -0
  59. package/dist/index.js +17 -0
  60. package/dist/index.js.map +1 -0
  61. package/dist/runtime/circuit-breaker.d.ts +59 -0
  62. package/dist/runtime/circuit-breaker.d.ts.map +1 -0
  63. package/dist/runtime/circuit-breaker.js +155 -0
  64. package/dist/runtime/circuit-breaker.js.map +1 -0
  65. package/dist/runtime/context.d.ts +92 -0
  66. package/dist/runtime/context.d.ts.map +1 -0
  67. package/dist/runtime/context.js +219 -0
  68. package/dist/runtime/context.js.map +1 -0
  69. package/dist/runtime/error-classifier.d.ts +16 -0
  70. package/dist/runtime/error-classifier.d.ts.map +1 -0
  71. package/dist/runtime/error-classifier.js +38 -0
  72. package/dist/runtime/error-classifier.js.map +1 -0
  73. package/dist/runtime/expression-evaluator.d.ts +36 -0
  74. package/dist/runtime/expression-evaluator.d.ts.map +1 -0
  75. package/dist/runtime/expression-evaluator.js +391 -0
  76. package/dist/runtime/expression-evaluator.js.map +1 -0
  77. package/dist/runtime/index.d.ts +12 -0
  78. package/dist/runtime/index.d.ts.map +1 -0
  79. package/dist/runtime/index.js +11 -0
  80. package/dist/runtime/index.js.map +1 -0
  81. package/dist/runtime/interpreter.d.ts +59 -0
  82. package/dist/runtime/interpreter.d.ts.map +1 -0
  83. package/dist/runtime/interpreter.js +414 -0
  84. package/dist/runtime/interpreter.js.map +1 -0
  85. package/dist/runtime/skills/registry.d.ts +11 -0
  86. package/dist/runtime/skills/registry.d.ts.map +1 -0
  87. package/dist/runtime/skills/registry.js +73 -0
  88. package/dist/runtime/skills/registry.js.map +1 -0
  89. package/dist/runtime/sqlite-state-store.d.ts +28 -0
  90. package/dist/runtime/sqlite-state-store.d.ts.map +1 -0
  91. package/dist/runtime/sqlite-state-store.js +180 -0
  92. package/dist/runtime/sqlite-state-store.js.map +1 -0
  93. package/dist/runtime/state-store.d.ts +52 -0
  94. package/dist/runtime/state-store.d.ts.map +1 -0
  95. package/dist/runtime/state-store.js +32 -0
  96. package/dist/runtime/state-store.js.map +1 -0
  97. package/dist/runtime/steps/action.d.ts +17 -0
  98. package/dist/runtime/steps/action.d.ts.map +1 -0
  99. package/dist/runtime/steps/action.js +430 -0
  100. package/dist/runtime/steps/action.js.map +1 -0
  101. package/dist/runtime/steps/advisory.d.ts +28 -0
  102. package/dist/runtime/steps/advisory.d.ts.map +1 -0
  103. package/dist/runtime/steps/advisory.js +209 -0
  104. package/dist/runtime/steps/advisory.js.map +1 -0
  105. package/dist/runtime/steps/compute.d.ts +9 -0
  106. package/dist/runtime/steps/compute.d.ts.map +1 -0
  107. package/dist/runtime/steps/compute.js +74 -0
  108. package/dist/runtime/steps/compute.js.map +1 -0
  109. package/dist/runtime/steps/conditional.d.ts +14 -0
  110. package/dist/runtime/steps/conditional.d.ts.map +1 -0
  111. package/dist/runtime/steps/conditional.js +58 -0
  112. package/dist/runtime/steps/conditional.js.map +1 -0
  113. package/dist/runtime/steps/emit.d.ts +9 -0
  114. package/dist/runtime/steps/emit.d.ts.map +1 -0
  115. package/dist/runtime/steps/emit.js +70 -0
  116. package/dist/runtime/steps/emit.js.map +1 -0
  117. package/dist/runtime/steps/halt.d.ts +9 -0
  118. package/dist/runtime/steps/halt.d.ts.map +1 -0
  119. package/dist/runtime/steps/halt.js +19 -0
  120. package/dist/runtime/steps/halt.js.map +1 -0
  121. package/dist/runtime/steps/loop.d.ts +14 -0
  122. package/dist/runtime/steps/loop.d.ts.map +1 -0
  123. package/dist/runtime/steps/loop.js +109 -0
  124. package/dist/runtime/steps/loop.js.map +1 -0
  125. package/dist/runtime/steps/parallel.d.ts +9 -0
  126. package/dist/runtime/steps/parallel.d.ts.map +1 -0
  127. package/dist/runtime/steps/parallel.js +87 -0
  128. package/dist/runtime/steps/parallel.js.map +1 -0
  129. package/dist/runtime/steps/pipeline.d.ts +9 -0
  130. package/dist/runtime/steps/pipeline.d.ts.map +1 -0
  131. package/dist/runtime/steps/pipeline.js +125 -0
  132. package/dist/runtime/steps/pipeline.js.map +1 -0
  133. package/dist/runtime/steps/try.d.ts +13 -0
  134. package/dist/runtime/steps/try.d.ts.map +1 -0
  135. package/dist/runtime/steps/try.js +222 -0
  136. package/dist/runtime/steps/try.js.map +1 -0
  137. package/dist/runtime/steps/wait.d.ts +9 -0
  138. package/dist/runtime/steps/wait.d.ts.map +1 -0
  139. package/dist/runtime/steps/wait.js +38 -0
  140. package/dist/runtime/steps/wait.js.map +1 -0
  141. package/dist/types/actions.d.ts +162 -0
  142. package/dist/types/actions.d.ts.map +1 -0
  143. package/dist/types/actions.js +5 -0
  144. package/dist/types/actions.js.map +1 -0
  145. package/dist/types/execution.d.ts +276 -0
  146. package/dist/types/execution.d.ts.map +1 -0
  147. package/dist/types/execution.js +5 -0
  148. package/dist/types/execution.js.map +1 -0
  149. package/dist/types/expressions.d.ts +100 -0
  150. package/dist/types/expressions.d.ts.map +1 -0
  151. package/dist/types/expressions.js +48 -0
  152. package/dist/types/expressions.js.map +1 -0
  153. package/dist/types/index.d.ts +14 -0
  154. package/dist/types/index.d.ts.map +1 -0
  155. package/dist/types/index.js +5 -0
  156. package/dist/types/index.js.map +1 -0
  157. package/dist/types/ir.d.ts +187 -0
  158. package/dist/types/ir.d.ts.map +1 -0
  159. package/dist/types/ir.js +5 -0
  160. package/dist/types/ir.js.map +1 -0
  161. package/dist/types/policy.d.ts +123 -0
  162. package/dist/types/policy.d.ts.map +1 -0
  163. package/dist/types/policy.js +5 -0
  164. package/dist/types/policy.js.map +1 -0
  165. package/dist/types/primitives.d.ts +76 -0
  166. package/dist/types/primitives.d.ts.map +1 -0
  167. package/dist/types/primitives.js +10 -0
  168. package/dist/types/primitives.js.map +1 -0
  169. package/dist/types/steps.d.ts +226 -0
  170. package/dist/types/steps.d.ts.map +1 -0
  171. package/dist/types/steps.js +5 -0
  172. package/dist/types/steps.js.map +1 -0
  173. package/dist/venues/index.d.ts +6 -0
  174. package/dist/venues/index.d.ts.map +1 -0
  175. package/dist/venues/index.js +26 -0
  176. package/dist/venues/index.js.map +1 -0
  177. package/dist/venues/types.d.ts +40 -0
  178. package/dist/venues/types.d.ts.map +1 -0
  179. package/dist/venues/types.js +5 -0
  180. package/dist/venues/types.js.map +1 -0
  181. package/dist/wallet/executor.d.ts +109 -0
  182. package/dist/wallet/executor.d.ts.map +1 -0
  183. package/dist/wallet/executor.js +354 -0
  184. package/dist/wallet/executor.js.map +1 -0
  185. package/dist/wallet/index.d.ts +14 -0
  186. package/dist/wallet/index.d.ts.map +1 -0
  187. package/dist/wallet/index.js +13 -0
  188. package/dist/wallet/index.js.map +1 -0
  189. package/dist/wallet/keystore.d.ts +44 -0
  190. package/dist/wallet/keystore.d.ts.map +1 -0
  191. package/dist/wallet/keystore.js +296 -0
  192. package/dist/wallet/keystore.js.map +1 -0
  193. package/dist/wallet/provider.d.ts +111 -0
  194. package/dist/wallet/provider.d.ts.map +1 -0
  195. package/dist/wallet/provider.js +309 -0
  196. package/dist/wallet/provider.js.map +1 -0
  197. package/dist/wallet/tx-builder.d.ts +85 -0
  198. package/dist/wallet/tx-builder.d.ts.map +1 -0
  199. package/dist/wallet/tx-builder.js +290 -0
  200. package/dist/wallet/tx-builder.js.map +1 -0
  201. package/dist/wallet/types.d.ts +116 -0
  202. package/dist/wallet/types.d.ts.map +1 -0
  203. package/dist/wallet/types.js +86 -0
  204. package/dist/wallet/types.js.map +1 -0
  205. package/package.json +39 -0
@@ -0,0 +1,1011 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { basename, dirname, extname, resolve } from "node:path";
3
+ import { parse } from "./parser.js";
4
+ // =============================================================================
5
+ // TRANSFORMER CLASS
6
+ // =============================================================================
7
+ export class Transformer {
8
+ stepCounter = 0;
9
+ blockMap = new Map();
10
+ advisorDefaults = new Map();
11
+ assetDecimals = new Map();
12
+ venueLabelMap = new Map();
13
+ importStack = [];
14
+ /** Transform AST to SpellSource */
15
+ transform(ast, options) {
16
+ const source = {
17
+ spell: ast.name,
18
+ version: "1.0.0", // Default
19
+ };
20
+ // Pre-scan assets for unit conversion
21
+ for (const section of ast.sections) {
22
+ if (section.kind !== "assets")
23
+ continue;
24
+ for (const item of section.items) {
25
+ this.assetDecimals.set(item.symbol, item.decimals);
26
+ }
27
+ }
28
+ // Pre-scan venues to resolve skill adapters that reference venue labels
29
+ for (const section of ast.sections) {
30
+ if (section.kind !== "venues")
31
+ continue;
32
+ for (const group of section.groups) {
33
+ const set = this.venueLabelMap.get(group.name) ?? new Set();
34
+ for (const venue of group.venues) {
35
+ set.add(venue.name);
36
+ }
37
+ this.venueLabelMap.set(group.name, set);
38
+ }
39
+ }
40
+ // Resolve imports before local blocks
41
+ if (options?.filePath && ast.imports?.length) {
42
+ this.loadImports(ast.imports, dirname(options.filePath));
43
+ }
44
+ // Register blocks for do-invocations
45
+ for (const block of ast.blocks ?? []) {
46
+ if (this.blockMap.has(block.name)) {
47
+ throw new Error(`Duplicate block name '${block.name}'`);
48
+ }
49
+ this.blockMap.set(block.name, block);
50
+ }
51
+ // Process sections
52
+ for (const section of ast.sections) {
53
+ this.transformSection(section, source);
54
+ }
55
+ // Process triggers and their handlers
56
+ if (ast.triggers.length > 0) {
57
+ const trigger = ast.triggers[0];
58
+ if (trigger) {
59
+ source.trigger = this.transformTriggerType(trigger.trigger);
60
+ source.steps = this.transformStatements(trigger.body);
61
+ }
62
+ }
63
+ // Multiple triggers → any trigger
64
+ if (ast.triggers.length > 1) {
65
+ const triggers = ast.triggers.map((t) => this.transformTriggerType(t.trigger));
66
+ source.trigger = { any: triggers };
67
+ // Merge all steps (simplified - in practice we'd need more complex handling)
68
+ source.steps = ast.triggers.flatMap((t) => this.transformStatements(t.body));
69
+ }
70
+ return source;
71
+ }
72
+ loadImports(imports, baseDir, prefix = "") {
73
+ for (const imp of imports) {
74
+ const importPath = resolve(baseDir, imp.path);
75
+ if (this.importStack.includes(importPath)) {
76
+ const cycle = [...this.importStack, importPath].join(" -> ");
77
+ throw new Error(`Import cycle detected: ${cycle}`);
78
+ }
79
+ const source = readFileSync(importPath, "utf8");
80
+ const ast = parse(source);
81
+ const alias = imp.alias && imp.alias.length > 0 ? imp.alias : basename(importPath, extname(importPath));
82
+ const namespace = prefix ? `${prefix}.${alias}` : alias;
83
+ this.importStack.push(importPath);
84
+ // Register imported blocks with namespace
85
+ for (const block of ast.blocks ?? []) {
86
+ const name = namespace ? `${namespace}.${block.name}` : block.name;
87
+ if (this.blockMap.has(name)) {
88
+ throw new Error(`Duplicate block name '${name}' from import '${imp.path}'`);
89
+ }
90
+ this.blockMap.set(name, { ...block, name });
91
+ }
92
+ if (ast.imports?.length) {
93
+ this.loadImports(ast.imports, dirname(importPath), namespace);
94
+ }
95
+ this.importStack.pop();
96
+ }
97
+ }
98
+ /** Transform a section to SpellSource fields */
99
+ transformSection(section, source) {
100
+ switch (section.kind) {
101
+ case "version":
102
+ source.version = section.value;
103
+ break;
104
+ case "description":
105
+ source.description = section.value;
106
+ break;
107
+ case "assets":
108
+ source.assets = {};
109
+ for (const item of section.items) {
110
+ source.assets[item.symbol] = {
111
+ chain: item.chain ?? 1, // Default to mainnet
112
+ address: item.address ?? `0x${item.symbol.toLowerCase()}`, // Placeholder
113
+ decimals: item.decimals,
114
+ };
115
+ }
116
+ break;
117
+ case "params":
118
+ source.params = {};
119
+ for (const item of section.items) {
120
+ if (item.type || item.min !== undefined || item.max !== undefined || item.asset) {
121
+ source.params[item.name] = {
122
+ type: item.type,
123
+ asset: item.asset,
124
+ default: item.value !== undefined ? this.exprToValue(item.value) : undefined,
125
+ min: item.min,
126
+ max: item.max,
127
+ };
128
+ }
129
+ else if (item.value !== undefined) {
130
+ source.params[item.name] = this.exprToValue(item.value);
131
+ }
132
+ else {
133
+ source.params[item.name] = undefined;
134
+ }
135
+ }
136
+ break;
137
+ case "limits":
138
+ // Store limits as params
139
+ if (!source.params)
140
+ source.params = {};
141
+ for (const item of section.items) {
142
+ source.params[`limit_${item.name}`] = this.exprToValue(item.value);
143
+ }
144
+ break;
145
+ case "venues":
146
+ source.venues = {};
147
+ for (const group of section.groups) {
148
+ for (const venue of group.venues) {
149
+ source.venues[venue.name] = {
150
+ chain: venue.chain ?? 1,
151
+ address: venue.address ?? `0x${venue.name}`,
152
+ label: group.name,
153
+ };
154
+ }
155
+ }
156
+ break;
157
+ case "state": {
158
+ const state = {
159
+ persistent: source.state?.persistent ?? {},
160
+ ephemeral: source.state?.ephemeral ?? {},
161
+ };
162
+ source.state = state;
163
+ for (const item of section.persistent) {
164
+ state.persistent[item.name] = this.exprToValue(item.initialValue);
165
+ }
166
+ for (const item of section.ephemeral) {
167
+ state.ephemeral[item.name] = this.exprToValue(item.initialValue);
168
+ }
169
+ break;
170
+ }
171
+ case "skills": {
172
+ const skillsSection = section;
173
+ source.skills = source.skills ?? {};
174
+ for (const item of skillsSection.items) {
175
+ const resolvedAdapters = [];
176
+ for (const adapter of item.adapters) {
177
+ const byLabel = this.venueLabelMap.get(adapter);
178
+ if (byLabel && byLabel.size > 0) {
179
+ for (const alias of byLabel)
180
+ resolvedAdapters.push(alias);
181
+ continue;
182
+ }
183
+ resolvedAdapters.push(adapter);
184
+ }
185
+ source.skills[item.name] = {
186
+ type: item.type,
187
+ adapters: Array.from(new Set(resolvedAdapters)),
188
+ default_constraints: item.defaultConstraints?.maxSlippage
189
+ ? { max_slippage: this.exprToValue(item.defaultConstraints.maxSlippage) }
190
+ : undefined,
191
+ };
192
+ }
193
+ break;
194
+ }
195
+ case "advisors": {
196
+ const advisorsSection = section;
197
+ source.advisors = source.advisors ?? {};
198
+ for (const item of advisorsSection.items) {
199
+ source.advisors[item.name] = {
200
+ model: item.model,
201
+ system_prompt: item.systemPrompt,
202
+ skills: item.skills,
203
+ allowed_tools: item.allowedTools,
204
+ mcp: item.mcp,
205
+ timeout: item.timeout,
206
+ fallback: item.fallback,
207
+ rate_limit: item.maxPerRun || item.maxPerHour
208
+ ? { max_per_run: item.maxPerRun, max_per_hour: item.maxPerHour }
209
+ : undefined,
210
+ };
211
+ this.advisorDefaults.set(item.name, {
212
+ timeout: item.timeout,
213
+ fallback: item.fallback,
214
+ });
215
+ }
216
+ break;
217
+ }
218
+ case "guards": {
219
+ const guardsSection = section;
220
+ source.guards = guardsSection.items.map((item) => ({
221
+ id: item.id,
222
+ ...(item.check.kind === "advisory_expr"
223
+ ? {
224
+ advisory: item.check.advisor ?? "default",
225
+ check: item.check.prompt,
226
+ severity: item.severity ?? "warn",
227
+ fallback: item.fallback ?? true,
228
+ }
229
+ : {
230
+ check: this.exprToString(item.check),
231
+ severity: item.severity ?? "halt",
232
+ message: item.message,
233
+ }),
234
+ }));
235
+ break;
236
+ }
237
+ }
238
+ }
239
+ /** Transform trigger type */
240
+ transformTriggerType(trigger) {
241
+ switch (trigger.kind) {
242
+ case "manual":
243
+ return { manual: true };
244
+ case "hourly":
245
+ return { schedule: "0 * * * *" };
246
+ case "daily":
247
+ return { schedule: "0 0 * * *" };
248
+ case "schedule":
249
+ return { schedule: trigger.cron };
250
+ case "condition":
251
+ return {
252
+ condition: this.exprToString(trigger.expression),
253
+ poll_interval: trigger.pollInterval ?? 60,
254
+ };
255
+ case "event":
256
+ return {
257
+ event: trigger.event,
258
+ filter: trigger.filter ? this.exprToString(trigger.filter) : undefined,
259
+ };
260
+ }
261
+ }
262
+ /** Transform statements to step array */
263
+ transformStatements(statements) {
264
+ const steps = [];
265
+ for (const stmt of statements) {
266
+ const stmtSteps = this.transformStatement(stmt);
267
+ // Attach source location from AST span to the first step produced by this statement
268
+ if (stmt.span && stmtSteps.length > 0) {
269
+ const firstStep = stmtSteps[0];
270
+ if (firstStep) {
271
+ firstStep._sourceLocation = {
272
+ line: stmt.span.start.line,
273
+ column: stmt.span.start.column,
274
+ };
275
+ }
276
+ }
277
+ steps.push(...stmtSteps);
278
+ }
279
+ return steps;
280
+ }
281
+ /** Transform a single statement */
282
+ transformStatement(stmt) {
283
+ switch (stmt.kind) {
284
+ case "assignment":
285
+ return this.transformAssignment(stmt);
286
+ case "if":
287
+ return this.transformIf(stmt);
288
+ case "for":
289
+ return this.transformFor(stmt);
290
+ case "repeat":
291
+ return this.transformRepeat(stmt);
292
+ case "until":
293
+ return this.transformUntil(stmt);
294
+ case "try":
295
+ return this.transformTry(stmt);
296
+ case "parallel":
297
+ return this.transformParallel(stmt);
298
+ case "pipeline":
299
+ return this.transformPipeline(stmt);
300
+ case "advise":
301
+ return this.transformAdvise(stmt);
302
+ case "do":
303
+ return this.transformDo(stmt);
304
+ case "atomic":
305
+ return this.transformAtomic(stmt);
306
+ case "method_call":
307
+ return this.transformMethodCall(stmt);
308
+ case "emit":
309
+ return this.transformEmit(stmt);
310
+ case "halt":
311
+ return this.transformHalt(stmt);
312
+ case "wait":
313
+ return this.transformWait(stmt);
314
+ case "advisory":
315
+ return this.transformAdvisory(stmt);
316
+ case "pass":
317
+ return [];
318
+ default:
319
+ return [];
320
+ }
321
+ }
322
+ /** Transform assignment to compute step, or action step if RHS is a venue method call */
323
+ transformAssignment(stmt) {
324
+ // Check if RHS is a method call (venue action with output binding)
325
+ if (stmt.value.kind === "call") {
326
+ const callExpr = stmt.value;
327
+ if (callExpr.callee.kind === "property_access") {
328
+ const prop = callExpr.callee;
329
+ const methodCall = {
330
+ kind: "method_call",
331
+ object: prop.object,
332
+ method: prop.property,
333
+ args: callExpr.args,
334
+ outputBinding: stmt.target,
335
+ skill: stmt.skill,
336
+ };
337
+ if (stmt.constraints) {
338
+ methodCall.constraints = stmt.constraints;
339
+ }
340
+ return this.transformMethodCall(methodCall);
341
+ }
342
+ }
343
+ // Default: compute step
344
+ const id = this.nextStepId("compute");
345
+ return [
346
+ {
347
+ id,
348
+ compute: {
349
+ [stmt.target]: this.exprToString(stmt.value),
350
+ },
351
+ },
352
+ ];
353
+ }
354
+ /** Transform if statement to conditional step */
355
+ transformIf(stmt) {
356
+ const steps = [];
357
+ const id = this.nextStepId("cond");
358
+ // Check for advisory condition
359
+ if (stmt.condition.kind === "advisory_expr") {
360
+ const advisory = stmt.condition;
361
+ const thenSteps = this.transformStatements(stmt.thenBody);
362
+ const elseSteps = this.transformStatements(stmt.elseBody);
363
+ const advisorName = advisory.advisor ?? "default";
364
+ const defaults = this.advisorDefaults.get(advisorName);
365
+ // Advisory steps need special handling
366
+ steps.push({
367
+ id,
368
+ advisory: {
369
+ prompt: advisory.prompt,
370
+ advisor: advisorName,
371
+ output: `${id}_result`,
372
+ timeout: defaults?.timeout ?? 30,
373
+ fallback: defaults?.fallback ?? true,
374
+ },
375
+ });
376
+ // Add conditional based on advisory result
377
+ const condId = this.nextStepId("cond");
378
+ steps.push({
379
+ id: condId,
380
+ if: `${id}_result == true`,
381
+ then: thenSteps.map((s) => s.id),
382
+ else: elseSteps.map((s) => s.id),
383
+ });
384
+ steps.push(...thenSteps, ...elseSteps);
385
+ }
386
+ else {
387
+ // Regular conditional
388
+ const thenSteps = this.transformStatements(stmt.thenBody);
389
+ if (stmt.elifs && stmt.elifs.length > 0) {
390
+ // Build elif chain as nested conditionals (from last to first)
391
+ let elseChainSteps = this.transformStatements(stmt.elseBody);
392
+ let elseChainIds = elseChainSteps.map((s) => s.id);
393
+ for (let i = stmt.elifs.length - 1; i >= 0; i--) {
394
+ const elif = stmt.elifs[i];
395
+ if (!elif)
396
+ continue;
397
+ const elifId = this.nextStepId("cond");
398
+ const elifThenSteps = this.transformStatements(elif.body);
399
+ const elifCond = {
400
+ id: elifId,
401
+ if: this.exprToString(elif.condition),
402
+ then: elifThenSteps.map((s) => s.id),
403
+ else: elseChainIds,
404
+ };
405
+ elseChainSteps = [elifCond, ...elifThenSteps, ...elseChainSteps];
406
+ elseChainIds = [elifId];
407
+ }
408
+ steps.push({
409
+ id,
410
+ if: this.exprToString(stmt.condition),
411
+ then: thenSteps.map((s) => s.id),
412
+ else: elseChainIds,
413
+ });
414
+ steps.push(...thenSteps, ...elseChainSteps);
415
+ }
416
+ else {
417
+ // Simple if/else (no elif)
418
+ const elseSteps = this.transformStatements(stmt.elseBody);
419
+ steps.push({
420
+ id,
421
+ if: this.exprToString(stmt.condition),
422
+ then: thenSteps.map((s) => s.id),
423
+ else: elseSteps.map((s) => s.id),
424
+ });
425
+ steps.push(...thenSteps, ...elseSteps);
426
+ }
427
+ }
428
+ return steps;
429
+ }
430
+ /** Transform for loop to loop step */
431
+ transformFor(stmt) {
432
+ const id = this.nextStepId("loop");
433
+ const bodySteps = this.transformStatements(stmt.body);
434
+ return [
435
+ {
436
+ id,
437
+ for: `${stmt.variable} in ${this.exprToString(stmt.iterable)}`,
438
+ steps: bodySteps.map((s) => s.id),
439
+ max: stmt.maxIterations ?? 100,
440
+ },
441
+ ...bodySteps,
442
+ ];
443
+ }
444
+ /** Transform repeat loop to loop step */
445
+ transformRepeat(stmt) {
446
+ const id = this.nextStepId("loop");
447
+ const bodySteps = this.transformStatements(stmt.body);
448
+ const countValue = this.exprToValue(stmt.count);
449
+ const count = typeof countValue === "number" ? countValue : Number.parseFloat(String(countValue));
450
+ return [
451
+ {
452
+ id,
453
+ repeat: count,
454
+ steps: bodySteps.map((s) => s.id),
455
+ max: count,
456
+ },
457
+ ...bodySteps,
458
+ ];
459
+ }
460
+ /** Transform until loop to loop step */
461
+ transformUntil(stmt) {
462
+ const id = this.nextStepId("loop");
463
+ const bodySteps = this.transformStatements(stmt.body);
464
+ return [
465
+ {
466
+ id,
467
+ loop: {
468
+ until: this.exprToString(stmt.condition),
469
+ max: stmt.maxIterations ?? 100,
470
+ },
471
+ steps: bodySteps.map((s) => s.id),
472
+ max: stmt.maxIterations ?? 100,
473
+ },
474
+ ...bodySteps,
475
+ ];
476
+ }
477
+ /** Transform try/catch/finally to try step */
478
+ transformTry(stmt) {
479
+ const id = this.nextStepId("try");
480
+ const trySteps = this.transformStatements(stmt.tryBody);
481
+ const catchBlocks = [];
482
+ const catchSteps = [];
483
+ for (const c of stmt.catches) {
484
+ const steps = this.transformStatements(c.body);
485
+ catchSteps.push(...steps);
486
+ const block = {
487
+ error: c.error,
488
+ steps: steps.map((s) => s.id),
489
+ };
490
+ if (c.action)
491
+ block.action = c.action;
492
+ if (c.retry) {
493
+ block.retry = {
494
+ maxAttempts: c.retry.maxAttempts,
495
+ backoff: c.retry.backoff,
496
+ backoffBase: c.retry.backoffBase,
497
+ maxBackoff: c.retry.maxBackoff,
498
+ };
499
+ }
500
+ catchBlocks.push(block);
501
+ }
502
+ const finallySteps = stmt.finallyBody ? this.transformStatements(stmt.finallyBody) : [];
503
+ return [
504
+ {
505
+ id,
506
+ try: trySteps.map((s) => s.id),
507
+ catch: catchBlocks,
508
+ finally: finallySteps.map((s) => s.id),
509
+ },
510
+ ...trySteps,
511
+ ...catchSteps,
512
+ ...finallySteps,
513
+ ];
514
+ }
515
+ /** Transform parallel block */
516
+ transformParallel(stmt) {
517
+ const id = this.nextStepId("parallel");
518
+ const branches = [];
519
+ const branchSteps = [];
520
+ for (const branch of stmt.branches) {
521
+ const steps = this.transformStatements(branch.body);
522
+ branchSteps.push(...steps);
523
+ branches.push({ name: branch.name, steps: steps.map((s) => s.id) });
524
+ }
525
+ const join = stmt.join
526
+ ? {
527
+ type: stmt.join.type,
528
+ metric: stmt.join.metric ? this.exprToString(stmt.join.metric) : undefined,
529
+ order: stmt.join.order,
530
+ count: stmt.join.count,
531
+ }
532
+ : undefined;
533
+ return [
534
+ {
535
+ id,
536
+ parallel: {
537
+ branches,
538
+ join,
539
+ on_fail: stmt.onFail ?? "abort",
540
+ },
541
+ },
542
+ ...branchSteps,
543
+ ];
544
+ }
545
+ /** Transform pipeline statement */
546
+ transformPipeline(stmt) {
547
+ const id = this.nextStepId("pipeline");
548
+ const stages = [];
549
+ const stageSteps = [];
550
+ let parallel = false;
551
+ for (const stage of stmt.stages) {
552
+ let op = stage.op === "pmap" ? "map" : stage.op;
553
+ if (op === "where")
554
+ op = "filter";
555
+ if (stage.op === "pmap")
556
+ parallel = true;
557
+ // Stages without step bodies
558
+ if (op === "take" || op === "skip" || op === "sort") {
559
+ const entry = { op };
560
+ if (stage.count !== undefined)
561
+ entry.count = stage.count;
562
+ if (stage.order)
563
+ entry.order = stage.order;
564
+ if (stage.by)
565
+ entry.by = this.exprToString(stage.by);
566
+ stages.push(entry);
567
+ continue;
568
+ }
569
+ const steps = this.transformStatement(stage.step);
570
+ if (steps.length === 0)
571
+ continue;
572
+ stageSteps.push(...steps);
573
+ const stepId = steps[0]?.id;
574
+ const entry = { op, step: stepId };
575
+ if (stage.initial) {
576
+ entry.initial = this.exprToString(stage.initial);
577
+ }
578
+ stages.push(entry);
579
+ }
580
+ return [
581
+ {
582
+ id,
583
+ pipeline: {
584
+ source: this.exprToString(stmt.source),
585
+ stages,
586
+ parallel,
587
+ },
588
+ output: stmt.outputBinding,
589
+ },
590
+ ...stageSteps,
591
+ ];
592
+ }
593
+ /** Transform advise statement */
594
+ transformAdvise(stmt) {
595
+ const id = this.nextStepId("advisory");
596
+ return [
597
+ {
598
+ id,
599
+ advisory: {
600
+ prompt: stmt.prompt,
601
+ advisor: stmt.advisor,
602
+ output: stmt.target,
603
+ timeout: stmt.timeout,
604
+ fallback: this.exprToFallback(stmt.fallback),
605
+ output_schema: this.serializeOutputSchema(stmt.outputSchema),
606
+ },
607
+ },
608
+ ];
609
+ }
610
+ exprToFallback(expr) {
611
+ switch (expr.kind) {
612
+ case "literal":
613
+ case "array_literal":
614
+ case "object_literal":
615
+ case "percentage":
616
+ case "unit_literal":
617
+ return { __literal: this.exprToValue(expr) };
618
+ default:
619
+ return { __expr: this.exprToString(expr) };
620
+ }
621
+ }
622
+ /** Transform block invocation */
623
+ transformDo(stmt) {
624
+ const block = this.blockMap.get(stmt.name);
625
+ if (!block) {
626
+ return [];
627
+ }
628
+ if (block.params.length !== stmt.args.length) {
629
+ return [];
630
+ }
631
+ const assignments = block.params.map((param, i) => ({
632
+ kind: "assignment",
633
+ target: param,
634
+ value: stmt.args[i],
635
+ }));
636
+ return this.transformStatements([...assignments, ...block.body]);
637
+ }
638
+ /** Transform atomic block to try step */
639
+ transformAtomic(stmt) {
640
+ const id = this.nextStepId("atomic");
641
+ const bodySteps = this.transformStatements(stmt.body);
642
+ return [
643
+ {
644
+ id,
645
+ try: bodySteps.map((s) => s.id),
646
+ catch: [{ error: "*", action: stmt.onFailure ?? "revert" }],
647
+ },
648
+ ...bodySteps,
649
+ ];
650
+ }
651
+ /** Transform method call to action step */
652
+ transformMethodCall(stmt) {
653
+ const id = this.nextStepId("action");
654
+ // Determine action type from method name
655
+ const method = stmt.method.toLowerCase();
656
+ let actionType = method;
657
+ // Map common method names to action types
658
+ const methodMap = {
659
+ deposit: "lend",
660
+ supply: "lend",
661
+ borrow: "borrow",
662
+ repay: "repay",
663
+ withdraw: "withdraw",
664
+ stake: "stake",
665
+ unstake: "unstake",
666
+ claim: "claim",
667
+ swap: "swap",
668
+ bridge: "bridge",
669
+ transfer: "transfer",
670
+ get_supply_rates: "query", // Query operations
671
+ get_rates: "query",
672
+ };
673
+ actionType = methodMap[method] ?? method;
674
+ // Build action object
675
+ const action = {
676
+ type: actionType,
677
+ };
678
+ // Extract venue from object
679
+ if (stmt.object.kind === "identifier") {
680
+ action.venue = stmt.object.name;
681
+ }
682
+ else if (stmt.object.kind === "venue_ref_expr") {
683
+ action.venue = stmt.object.name;
684
+ }
685
+ else if (stmt.object.kind === "property_access") {
686
+ // e.g., venues.lending
687
+ const prop = stmt.object;
688
+ if (prop.object.kind === "identifier" && prop.object.name === "venues") {
689
+ action.venue = prop.property;
690
+ }
691
+ }
692
+ // Map arguments based on action type
693
+ if (actionType === "lend" ||
694
+ actionType === "withdraw" ||
695
+ actionType === "repay" ||
696
+ actionType === "stake" ||
697
+ actionType === "unstake") {
698
+ const assetArg = stmt.args[0];
699
+ const amountArg = stmt.args[1];
700
+ if (assetArg) {
701
+ action.asset = this.exprToString(assetArg);
702
+ }
703
+ if (amountArg) {
704
+ action.amount = this.exprToString(amountArg);
705
+ }
706
+ }
707
+ else if (actionType === "borrow") {
708
+ const assetArg = stmt.args[0];
709
+ const amountArg = stmt.args[1];
710
+ const collateralArg = stmt.args[2];
711
+ if (assetArg) {
712
+ action.asset = this.exprToString(assetArg);
713
+ }
714
+ if (amountArg) {
715
+ action.amount = this.exprToString(amountArg);
716
+ }
717
+ if (collateralArg) {
718
+ action.collateral = this.exprToString(collateralArg);
719
+ }
720
+ }
721
+ else if (actionType === "claim") {
722
+ const assetArg = stmt.args[0];
723
+ if (assetArg) {
724
+ action.asset = this.exprToString(assetArg);
725
+ }
726
+ }
727
+ else if (actionType === "bridge") {
728
+ const assetArg = stmt.args[0];
729
+ const amountArg = stmt.args[1];
730
+ const chainArg = stmt.args[2];
731
+ if (assetArg) {
732
+ action.asset = this.exprToString(assetArg);
733
+ }
734
+ if (amountArg) {
735
+ action.amount = this.exprToString(amountArg);
736
+ }
737
+ if (chainArg) {
738
+ action.to_chain = this.exprToString(chainArg);
739
+ }
740
+ }
741
+ else if (actionType === "swap") {
742
+ const assetInArg = stmt.args[0];
743
+ const assetOutArg = stmt.args[1];
744
+ const amountArg = stmt.args[2];
745
+ if (assetInArg && assetOutArg) {
746
+ action.asset_in = this.exprToString(assetInArg);
747
+ action.asset_out = this.exprToString(assetOutArg);
748
+ }
749
+ if (amountArg) {
750
+ action.amount = this.exprToString(amountArg);
751
+ }
752
+ }
753
+ else if (actionType === "transfer") {
754
+ const assetArg = stmt.args[0];
755
+ const amountArg = stmt.args[1];
756
+ const toArg = stmt.args[2];
757
+ if (assetArg) {
758
+ action.asset = this.exprToString(assetArg);
759
+ }
760
+ if (amountArg) {
761
+ action.amount = this.exprToString(amountArg);
762
+ }
763
+ if (toArg) {
764
+ action.to = this.exprToString(toArg);
765
+ }
766
+ }
767
+ else if (actionType === "query") {
768
+ // Query operations become compute steps
769
+ return [
770
+ {
771
+ id,
772
+ compute: {
773
+ [`${id}_result`]: `${this.exprToString(stmt.object)}.${stmt.method}(${stmt.args.map((a) => this.exprToString(a)).join(", ")})`,
774
+ },
775
+ },
776
+ ];
777
+ }
778
+ else {
779
+ // Generic mapping
780
+ stmt.args.forEach((arg, i) => {
781
+ action[`arg${i}`] = this.exprToString(arg);
782
+ });
783
+ }
784
+ const step = {
785
+ id,
786
+ action,
787
+ };
788
+ if (stmt.skill) {
789
+ step.skill = stmt.skill;
790
+ }
791
+ if (stmt.outputBinding) {
792
+ step.output = stmt.outputBinding;
793
+ }
794
+ if (stmt.constraints) {
795
+ const constraints = {};
796
+ for (const { key, value } of stmt.constraints.constraints) {
797
+ const constraintKey = key === "slippage"
798
+ ? "max_slippage"
799
+ : key === "min_out"
800
+ ? "min_output"
801
+ : key === "max_in"
802
+ ? "max_input"
803
+ : key;
804
+ constraints[constraintKey] = this.exprToValue(value);
805
+ }
806
+ step.constraints = constraints;
807
+ }
808
+ return [step];
809
+ }
810
+ /** Transform emit to emit step */
811
+ transformEmit(stmt) {
812
+ const id = this.nextStepId("emit");
813
+ const data = {};
814
+ for (const { key, value } of stmt.data) {
815
+ data[key] = this.exprToString(value);
816
+ }
817
+ return [
818
+ {
819
+ id,
820
+ emit: {
821
+ event: stmt.event,
822
+ data,
823
+ },
824
+ },
825
+ ];
826
+ }
827
+ /** Transform halt to halt step */
828
+ transformHalt(stmt) {
829
+ const id = this.nextStepId("halt");
830
+ return [{ id, halt: stmt.reason }];
831
+ }
832
+ /** Transform wait to wait step */
833
+ transformWait(stmt) {
834
+ const id = this.nextStepId("wait");
835
+ return [{ id, wait: stmt.duration }];
836
+ }
837
+ /** Transform advisory */
838
+ transformAdvisory(stmt) {
839
+ const id = this.nextStepId("advisory");
840
+ const thenSteps = this.transformStatements(stmt.thenBody);
841
+ const elseSteps = this.transformStatements(stmt.elseBody);
842
+ const advisorName = stmt.advisor ?? "default";
843
+ const defaults = this.advisorDefaults.get(advisorName);
844
+ return [
845
+ {
846
+ id,
847
+ advisory: {
848
+ prompt: stmt.prompt,
849
+ advisor: advisorName,
850
+ timeout: stmt.timeout ?? defaults?.timeout ?? 30,
851
+ fallback: stmt.fallback ?? defaults?.fallback ?? true,
852
+ },
853
+ },
854
+ ...thenSteps,
855
+ ...elseSteps,
856
+ ];
857
+ }
858
+ /** Convert expression to string representation */
859
+ exprToString(expr) {
860
+ switch (expr.kind) {
861
+ case "literal": {
862
+ const lit = expr;
863
+ if (lit.literalType === "string") {
864
+ return `"${lit.value}"`;
865
+ }
866
+ return String(lit.value);
867
+ }
868
+ case "identifier":
869
+ return expr.name;
870
+ case "venue_ref_expr":
871
+ return `@${expr.name}`;
872
+ case "advisory_expr":
873
+ return `**${expr.prompt}**`;
874
+ case "percentage":
875
+ return String(expr.value);
876
+ case "unit_literal": {
877
+ const raw = this.unitLiteralToRaw(expr);
878
+ return String(raw);
879
+ }
880
+ case "binary": {
881
+ const bin = expr;
882
+ const op = bin.op === "and" ? "AND" : bin.op === "or" ? "OR" : bin.op;
883
+ return `(${this.exprToString(bin.left)} ${op} ${this.exprToString(bin.right)})`;
884
+ }
885
+ case "unary": {
886
+ const un = expr;
887
+ const op = un.op === "not" ? "NOT " : un.op;
888
+ return `${op}${this.exprToString(un.arg)}`;
889
+ }
890
+ case "call": {
891
+ const call = expr;
892
+ const callee = this.exprToString(call.callee);
893
+ const args = call.args.map((a) => this.exprToString(a)).join(", ");
894
+ return `${callee}(${args})`;
895
+ }
896
+ case "property_access": {
897
+ const prop = expr;
898
+ return `${this.exprToString(prop.object)}.${prop.property}`;
899
+ }
900
+ case "array_access": {
901
+ const arr = expr;
902
+ return `${this.exprToString(arr.array)}[${this.exprToString(arr.index)}]`;
903
+ }
904
+ case "array_literal": {
905
+ const arrLit = expr;
906
+ return `[${arrLit.elements.map((e) => this.exprToString(e)).join(", ")}]`;
907
+ }
908
+ case "object_literal": {
909
+ const objLit = expr;
910
+ const entries = objLit.entries.map((e) => `${e.key}: ${this.exprToString(e.value)}`);
911
+ return `{${entries.join(", ")}}`;
912
+ }
913
+ case "ternary": {
914
+ const tern = expr;
915
+ return `(${this.exprToString(tern.condition)} ? ${this.exprToString(tern.thenExpr)} : ${this.exprToString(tern.elseExpr)})`;
916
+ }
917
+ default:
918
+ return "";
919
+ }
920
+ }
921
+ /** Convert expression to runtime value */
922
+ exprToValue(expr) {
923
+ switch (expr.kind) {
924
+ case "literal":
925
+ return expr.value;
926
+ case "percentage":
927
+ return expr.value;
928
+ case "unit_literal":
929
+ return this.unitLiteralToRaw(expr);
930
+ case "array_literal":
931
+ return expr.elements.map((e) => this.exprToValue(e));
932
+ case "object_literal": {
933
+ const obj = {};
934
+ for (const entry of expr.entries) {
935
+ obj[entry.key] = this.exprToValue(entry.value);
936
+ }
937
+ return obj;
938
+ }
939
+ case "unary": {
940
+ const un = expr;
941
+ if (un.arg.kind === "literal") {
942
+ const lit = un.arg;
943
+ if (un.op === "-" && typeof lit.value === "number") {
944
+ return -lit.value;
945
+ }
946
+ if (un.op === "not") {
947
+ return !lit.value;
948
+ }
949
+ }
950
+ if (un.arg.kind === "unit_literal") {
951
+ const raw = this.unitLiteralToRaw(un.arg);
952
+ if (un.op === "-") {
953
+ return -raw;
954
+ }
955
+ }
956
+ return this.exprToString(expr);
957
+ }
958
+ default:
959
+ // For expressions, return string representation
960
+ return this.exprToString(expr);
961
+ }
962
+ }
963
+ /** Generate next step ID */
964
+ nextStepId(prefix) {
965
+ return `${prefix}_${++this.stepCounter}`;
966
+ }
967
+ unitLiteralToRaw(expr) {
968
+ const unit = expr.unit;
969
+ if (unit === "bps" || unit === "bp") {
970
+ return expr.value;
971
+ }
972
+ if (!this.assetDecimals.has(unit)) {
973
+ throw new Error(`Unknown asset '${unit}' for unit literal`);
974
+ }
975
+ const decimals = this.assetDecimals.get(unit);
976
+ if (decimals === undefined) {
977
+ throw new Error(`Asset '${unit}' is missing decimals for unit literal conversion`);
978
+ }
979
+ const multiplier = 10 ** decimals;
980
+ return Math.floor(expr.value * multiplier);
981
+ }
982
+ serializeOutputSchema(schema) {
983
+ const fields = schema.fields &&
984
+ Object.fromEntries(Object.entries(schema.fields).map(([key, value]) => [
985
+ key,
986
+ this.serializeOutputSchema(value),
987
+ ]));
988
+ return {
989
+ type: schema.type,
990
+ values: schema.values,
991
+ min: schema.min,
992
+ max: schema.max,
993
+ min_length: schema.minLength,
994
+ max_length: schema.maxLength,
995
+ pattern: schema.pattern,
996
+ fields,
997
+ items: schema.items ? this.serializeOutputSchema(schema.items) : undefined,
998
+ };
999
+ }
1000
+ }
1001
+ // =============================================================================
1002
+ // PUBLIC API
1003
+ // =============================================================================
1004
+ /**
1005
+ * Transform Grimoire AST to SpellSource
1006
+ */
1007
+ export function transform(ast, options) {
1008
+ const transformer = new Transformer();
1009
+ return transformer.transform(ast, options);
1010
+ }
1011
+ //# sourceMappingURL=transformer.js.map