@phuetz/code-buddy 0.1.0 → 0.1.2

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 (305) hide show
  1. package/.codebuddy/skills/bundled/brave-search/SKILL.md +490 -0
  2. package/.codebuddy/skills/bundled/exa-search/SKILL.md +1122 -0
  3. package/.codebuddy/skills/bundled/perplexity/SKILL.md +748 -0
  4. package/.codebuddy/skills/bundled/playwright/SKILL.md +520 -0
  5. package/.codebuddy/skills/bundled/puppeteer/SKILL.md +708 -0
  6. package/.codebuddy/skills/bundled/web-fetch/SKILL.md +1003 -0
  7. package/README.md +56 -0
  8. package/dist/agent/agent-state.d.ts +3 -3
  9. package/dist/agent/agent-state.js +6 -6
  10. package/dist/agent/agent-state.js.map +1 -1
  11. package/dist/agent/base-agent.d.ts +4 -4
  12. package/dist/agent/base-agent.js +22 -9
  13. package/dist/agent/base-agent.js.map +1 -1
  14. package/dist/agent/cache-trace.d.ts +56 -0
  15. package/dist/agent/cache-trace.js +98 -0
  16. package/dist/agent/cache-trace.js.map +1 -0
  17. package/dist/agent/codebuddy-agent.js +4 -2
  18. package/dist/agent/codebuddy-agent.js.map +1 -1
  19. package/dist/agent/execution/agent-executor.d.ts +4 -4
  20. package/dist/agent/execution/agent-executor.js +46 -14
  21. package/dist/agent/execution/agent-executor.js.map +1 -1
  22. package/dist/agent/facades/agent-context-facade.js +1 -3
  23. package/dist/agent/facades/agent-context-facade.js.map +1 -1
  24. package/dist/agent/facades/message-history-manager.js +14 -12
  25. package/dist/agent/facades/message-history-manager.js.map +1 -1
  26. package/dist/agent/facades/session-facade.d.ts +3 -3
  27. package/dist/agent/facades/session-facade.js +6 -6
  28. package/dist/agent/facades/session-facade.js.map +1 -1
  29. package/dist/agent/history-repair.d.ts +37 -0
  30. package/dist/agent/history-repair.js +124 -0
  31. package/dist/agent/history-repair.js.map +1 -0
  32. package/dist/agent/index.d.ts +3 -3
  33. package/dist/agent/index.js +3 -3
  34. package/dist/agent/index.js.map +1 -1
  35. package/dist/agent/isolation/agent-workspace.d.ts +1 -0
  36. package/dist/agent/isolation/agent-workspace.js +10 -0
  37. package/dist/agent/isolation/agent-workspace.js.map +1 -1
  38. package/dist/agent/specialized/archive-agent.d.ts +3 -0
  39. package/dist/agent/specialized/archive-agent.js +71 -31
  40. package/dist/agent/specialized/archive-agent.js.map +1 -1
  41. package/dist/agent/specialized/index.d.ts +9 -8
  42. package/dist/agent/specialized/index.js +16 -8
  43. package/dist/agent/specialized/index.js.map +1 -1
  44. package/dist/agent/specialized/security-review/agent.js +19 -8
  45. package/dist/agent/specialized/security-review/agent.js.map +1 -1
  46. package/dist/agent/tool-executor.js +5 -0
  47. package/dist/agent/tool-executor.js.map +1 -1
  48. package/dist/agent/turn-diff-tracker.d.ts +79 -0
  49. package/dist/agent/turn-diff-tracker.js +195 -0
  50. package/dist/agent/turn-diff-tracker.js.map +1 -0
  51. package/dist/browser/controller.js +8 -4
  52. package/dist/browser/controller.js.map +1 -1
  53. package/dist/browser-automation/browser-manager.js +8 -1
  54. package/dist/browser-automation/browser-manager.js.map +1 -1
  55. package/dist/checkpoints/checkpoint-versioning.js +78 -20
  56. package/dist/checkpoints/checkpoint-versioning.js.map +1 -1
  57. package/dist/cli/config-loader.js +2 -4
  58. package/dist/cli/config-loader.js.map +1 -1
  59. package/dist/codebuddy/client.js +70 -11
  60. package/dist/codebuddy/client.js.map +1 -1
  61. package/dist/codebuddy/tools.d.ts +1 -7
  62. package/dist/codebuddy/tools.js +2 -30
  63. package/dist/codebuddy/tools.js.map +1 -1
  64. package/dist/commands/cli/daemon-commands.d.ts +14 -0
  65. package/dist/commands/cli/daemon-commands.js +166 -0
  66. package/dist/commands/cli/daemon-commands.js.map +1 -0
  67. package/dist/commands/cli/speak-command.d.ts +10 -0
  68. package/dist/commands/cli/speak-command.js +97 -0
  69. package/dist/commands/cli/speak-command.js.map +1 -0
  70. package/dist/commands/cli/utility-commands.d.ts +10 -0
  71. package/dist/commands/cli/utility-commands.js +88 -0
  72. package/dist/commands/cli/utility-commands.js.map +1 -0
  73. package/dist/commands/handlers/fcs-handlers.js +1 -1
  74. package/dist/commands/handlers/fcs-handlers.js.map +1 -1
  75. package/dist/commands/handlers/memory-handlers.js +2 -1
  76. package/dist/commands/handlers/memory-handlers.js.map +1 -1
  77. package/dist/commands/handlers/vibe-handlers.js +0 -1
  78. package/dist/commands/handlers/vibe-handlers.js.map +1 -1
  79. package/dist/commands/handlers/worktree-handlers.js +11 -0
  80. package/dist/commands/handlers/worktree-handlers.js.map +1 -1
  81. package/dist/commands/index.d.ts +8 -7
  82. package/dist/commands/index.js +10 -8
  83. package/dist/commands/index.js.map +1 -1
  84. package/dist/commands/mcp.d.ts +1 -0
  85. package/dist/commands/mcp.js +66 -7
  86. package/dist/commands/mcp.js.map +1 -1
  87. package/dist/commands/pipeline.js +25 -13
  88. package/dist/commands/pipeline.js.map +1 -1
  89. package/dist/config/hot-reload/watcher.js +4 -4
  90. package/dist/config/hot-reload/watcher.js.map +1 -1
  91. package/dist/config/model-tools.d.ts +41 -0
  92. package/dist/config/model-tools.js +194 -0
  93. package/dist/config/model-tools.js.map +1 -0
  94. package/dist/context/context-manager-v2.d.ts +2 -1
  95. package/dist/context/context-manager-v2.js +34 -5
  96. package/dist/context/context-manager-v2.js.map +1 -1
  97. package/dist/context/index.d.ts +12 -12
  98. package/dist/context/index.js +25 -12
  99. package/dist/context/index.js.map +1 -1
  100. package/dist/daemon/daemon-manager.js +23 -19
  101. package/dist/daemon/daemon-manager.js.map +1 -1
  102. package/dist/database/database-manager.d.ts +4 -0
  103. package/dist/database/database-manager.js +16 -7
  104. package/dist/database/database-manager.js.map +1 -1
  105. package/dist/desktop-automation/nutjs-provider.js +89 -0
  106. package/dist/desktop-automation/nutjs-provider.js.map +1 -1
  107. package/dist/errors/index.d.ts +4 -4
  108. package/dist/errors/index.js +8 -4
  109. package/dist/errors/index.js.map +1 -1
  110. package/dist/fcs/builtins.d.ts +2 -6
  111. package/dist/fcs/builtins.js +2 -568
  112. package/dist/fcs/builtins.js.map +1 -1
  113. package/dist/fcs/codebuddy-bindings.d.ts +3 -43
  114. package/dist/fcs/codebuddy-bindings.js +2 -606
  115. package/dist/fcs/codebuddy-bindings.js.map +1 -1
  116. package/dist/fcs/index.d.ts +2 -27
  117. package/dist/fcs/index.js +2 -53
  118. package/dist/fcs/index.js.map +1 -1
  119. package/dist/fcs/lexer.d.ts +2 -37
  120. package/dist/fcs/lexer.js +2 -459
  121. package/dist/fcs/lexer.js.map +1 -1
  122. package/dist/fcs/parser.d.ts +2 -68
  123. package/dist/fcs/parser.js +2 -893
  124. package/dist/fcs/parser.js.map +1 -1
  125. package/dist/fcs/runtime.d.ts +2 -59
  126. package/dist/fcs/runtime.js +2 -623
  127. package/dist/fcs/runtime.js.map +1 -1
  128. package/dist/fcs/script-registry.d.ts +3 -69
  129. package/dist/fcs/script-registry.js +2 -219
  130. package/dist/fcs/script-registry.js.map +1 -1
  131. package/dist/fcs/sync-bindings.d.ts +3 -101
  132. package/dist/fcs/sync-bindings.js +2 -410
  133. package/dist/fcs/sync-bindings.js.map +1 -1
  134. package/dist/fcs/types.d.ts +2 -285
  135. package/dist/fcs/types.js +2 -103
  136. package/dist/fcs/types.js.map +1 -1
  137. package/dist/hooks/index.d.ts +4 -4
  138. package/dist/hooks/index.js +4 -4
  139. package/dist/hooks/index.js.map +1 -1
  140. package/dist/hooks/use-input-handler.d.ts +1 -1
  141. package/dist/index.js +20 -330
  142. package/dist/index.js.map +1 -1
  143. package/dist/input/voice-control.js +11 -5
  144. package/dist/input/voice-control.js.map +1 -1
  145. package/dist/integrations/json-rpc/server.d.ts +9 -0
  146. package/dist/integrations/json-rpc/server.js +43 -13
  147. package/dist/integrations/json-rpc/server.js.map +1 -1
  148. package/dist/integrations/mcp/mcp-server.js +1 -1
  149. package/dist/integrations/mcp/mcp-server.js.map +1 -1
  150. package/dist/integrations/notification-integrations.d.ts +1 -0
  151. package/dist/integrations/notification-integrations.js +6 -1
  152. package/dist/integrations/notification-integrations.js.map +1 -1
  153. package/dist/mcp/client.js +2 -1
  154. package/dist/mcp/client.js.map +1 -1
  155. package/dist/mcp/config.js +89 -5
  156. package/dist/mcp/config.js.map +1 -1
  157. package/dist/mcp/mcp-client.js +65 -14
  158. package/dist/mcp/mcp-client.js.map +1 -1
  159. package/dist/mcp/transports.d.ts +0 -1
  160. package/dist/mcp/transports.js +1 -5
  161. package/dist/mcp/transports.js.map +1 -1
  162. package/dist/mcp/types.d.ts +2 -0
  163. package/dist/memory/index.d.ts +2 -2
  164. package/dist/memory/index.js +2 -2
  165. package/dist/memory/index.js.map +1 -1
  166. package/dist/persistence/session-lock.d.ts +42 -0
  167. package/dist/persistence/session-lock.js +165 -0
  168. package/dist/persistence/session-lock.js.map +1 -0
  169. package/dist/persistence/session-store.d.ts +18 -3
  170. package/dist/persistence/session-store.js +90 -21
  171. package/dist/persistence/session-store.js.map +1 -1
  172. package/dist/plugins/conflict-detection.js +2 -1
  173. package/dist/plugins/conflict-detection.js.map +1 -1
  174. package/dist/plugins/index.d.ts +3 -3
  175. package/dist/plugins/index.js +3 -3
  176. package/dist/plugins/index.js.map +1 -1
  177. package/dist/plugins/isolated-plugin-runner.d.ts +6 -0
  178. package/dist/plugins/isolated-plugin-runner.js +19 -1
  179. package/dist/plugins/isolated-plugin-runner.js.map +1 -1
  180. package/dist/providers/local-llm-provider.js +28 -8
  181. package/dist/providers/local-llm-provider.js.map +1 -1
  182. package/dist/sandbox/docker-sandbox.js +7 -4
  183. package/dist/sandbox/docker-sandbox.js.map +1 -1
  184. package/dist/scripting/builtins.d.ts +8 -3
  185. package/dist/scripting/builtins.js +506 -355
  186. package/dist/scripting/builtins.js.map +1 -1
  187. package/dist/scripting/codebuddy-bindings.d.ts +47 -0
  188. package/dist/scripting/codebuddy-bindings.js +488 -0
  189. package/dist/scripting/codebuddy-bindings.js.map +1 -0
  190. package/dist/scripting/index.d.ts +33 -30
  191. package/dist/scripting/index.js +41 -36
  192. package/dist/scripting/index.js.map +1 -1
  193. package/dist/scripting/lexer.d.ts +31 -13
  194. package/dist/scripting/lexer.js +379 -292
  195. package/dist/scripting/lexer.js.map +1 -1
  196. package/dist/scripting/parser.d.ts +63 -44
  197. package/dist/scripting/parser.js +700 -473
  198. package/dist/scripting/parser.js.map +1 -1
  199. package/dist/scripting/runtime.d.ts +55 -24
  200. package/dist/scripting/runtime.js +600 -288
  201. package/dist/scripting/runtime.js.map +1 -1
  202. package/dist/scripting/script-registry.d.ts +54 -0
  203. package/dist/scripting/script-registry.js +202 -0
  204. package/dist/scripting/script-registry.js.map +1 -0
  205. package/dist/scripting/sync-bindings.d.ts +105 -0
  206. package/dist/scripting/sync-bindings.js +353 -0
  207. package/dist/scripting/sync-bindings.js.map +1 -0
  208. package/dist/scripting/types.d.ts +297 -199
  209. package/dist/scripting/types.js +86 -60
  210. package/dist/scripting/types.js.map +1 -1
  211. package/dist/search/usearch-index.js +42 -7
  212. package/dist/search/usearch-index.js.map +1 -1
  213. package/dist/security/bash-parser.d.ts +51 -0
  214. package/dist/security/bash-parser.js +327 -0
  215. package/dist/security/bash-parser.js.map +1 -0
  216. package/dist/security/index.d.ts +7 -5
  217. package/dist/security/index.js +8 -7
  218. package/dist/security/index.js.map +1 -1
  219. package/dist/security/skill-scanner.d.ts +36 -0
  220. package/dist/security/skill-scanner.js +149 -0
  221. package/dist/security/skill-scanner.js.map +1 -0
  222. package/dist/security/trust-folders.d.ts +1 -0
  223. package/dist/security/trust-folders.js +19 -1
  224. package/dist/security/trust-folders.js.map +1 -1
  225. package/dist/server/auth/index.d.ts +2 -2
  226. package/dist/server/auth/index.js +2 -2
  227. package/dist/server/auth/index.js.map +1 -1
  228. package/dist/server/middleware/index.d.ts +5 -5
  229. package/dist/server/middleware/index.js +5 -5
  230. package/dist/server/middleware/index.js.map +1 -1
  231. package/dist/server/middleware/rate-limit.js +15 -3
  232. package/dist/server/middleware/rate-limit.js.map +1 -1
  233. package/dist/server/websocket/handler.js +54 -6
  234. package/dist/server/websocket/handler.js.map +1 -1
  235. package/dist/skills/eligibility.js +26 -4
  236. package/dist/skills/eligibility.js.map +1 -1
  237. package/dist/tasks/background-tasks.js +5 -1
  238. package/dist/tasks/background-tasks.js.map +1 -1
  239. package/dist/tools/apply-patch.d.ts +55 -0
  240. package/dist/tools/apply-patch.js +273 -0
  241. package/dist/tools/apply-patch.js.map +1 -0
  242. package/dist/tools/hooks/default-hooks.d.ts +1 -1
  243. package/dist/tools/hooks/default-hooks.js +2 -1
  244. package/dist/tools/hooks/default-hooks.js.map +1 -1
  245. package/dist/tools/index.d.ts +10 -10
  246. package/dist/tools/index.js +11 -11
  247. package/dist/tools/index.js.map +1 -1
  248. package/dist/tools/registry/bash-tools.js +6 -3
  249. package/dist/tools/registry/bash-tools.js.map +1 -1
  250. package/dist/tools/registry/misc-tools.js +1 -2
  251. package/dist/tools/registry/misc-tools.js.map +1 -1
  252. package/dist/tools/registry/search-tools.js +1 -1
  253. package/dist/tools/registry/search-tools.js.map +1 -1
  254. package/dist/tools/registry/text-editor-tools.js +1 -1
  255. package/dist/tools/registry/text-editor-tools.js.map +1 -1
  256. package/dist/tools/registry/todo-tools.js +37 -5
  257. package/dist/tools/registry/todo-tools.js.map +1 -1
  258. package/dist/tools/registry/tool-registry.js +5 -4
  259. package/dist/tools/registry/tool-registry.js.map +1 -1
  260. package/dist/tools/registry/web-tools.d.ts +1 -1
  261. package/dist/tools/registry/web-tools.js +28 -8
  262. package/dist/tools/registry/web-tools.js.map +1 -1
  263. package/dist/tools/text-editor.d.ts +1 -1
  264. package/dist/tools/text-editor.js +23 -5
  265. package/dist/tools/text-editor.js.map +1 -1
  266. package/dist/tools/web-search.d.ts +52 -37
  267. package/dist/tools/web-search.js +368 -163
  268. package/dist/tools/web-search.js.map +1 -1
  269. package/dist/types/errors.d.ts +1 -1
  270. package/dist/types/errors.js +2 -8
  271. package/dist/types/errors.js.map +1 -1
  272. package/dist/types/index.d.ts +2 -1
  273. package/dist/types/index.js +1 -2
  274. package/dist/types/index.js.map +1 -1
  275. package/dist/ui/components/ChatInterface.d.ts +1 -1
  276. package/dist/ui/index.d.ts +17 -21
  277. package/dist/ui/index.js +25 -22
  278. package/dist/ui/index.js.map +1 -1
  279. package/dist/utils/config-validation/schema.d.ts +15 -15
  280. package/dist/utils/head-tail-truncation.d.ts +34 -0
  281. package/dist/utils/head-tail-truncation.js +98 -0
  282. package/dist/utils/head-tail-truncation.js.map +1 -0
  283. package/dist/utils/logger.js +3 -9
  284. package/dist/utils/logger.js.map +1 -1
  285. package/dist/utils/sanitize.d.ts +5 -0
  286. package/dist/utils/sanitize.js +19 -0
  287. package/dist/utils/sanitize.js.map +1 -1
  288. package/dist/utils/settings-manager.js +4 -4
  289. package/dist/utils/settings-manager.js.map +1 -1
  290. package/dist/workflows/index.d.ts +4 -279
  291. package/dist/workflows/index.js +8 -822
  292. package/dist/workflows/index.js.map +1 -1
  293. package/dist/workflows/state-manager.d.ts +77 -0
  294. package/dist/workflows/state-manager.js +198 -0
  295. package/dist/workflows/state-manager.js.map +1 -0
  296. package/dist/workflows/step-manager.d.ts +39 -0
  297. package/dist/workflows/step-manager.js +196 -0
  298. package/dist/workflows/step-manager.js.map +1 -0
  299. package/dist/workflows/types.d.ts +87 -0
  300. package/dist/workflows/types.js +5 -0
  301. package/dist/workflows/types.js.map +1 -0
  302. package/dist/workflows/workflow-engine.d.ts +34 -0
  303. package/dist/workflows/workflow-engine.js +354 -0
  304. package/dist/workflows/workflow-engine.js.map +1 -0
  305. package/package.json +5 -1
@@ -1,12 +1,22 @@
1
1
  /**
2
- * Buddy Script Runtime
2
+ * Unified Script Runtime
3
3
  *
4
- * Executes parsed AST nodes
4
+ * Based on FCS runtime (class/test/assert/dict/lambda/interpolation/pipeline/ternary/index)
5
+ * with Buddy Script additions:
6
+ * - Async/await native support
7
+ * - ForCStyle statement execution
8
+ * - AwaitExpression handling
9
+ * - Export statement handling
10
+ * - Timeout enforcement per-statement
11
+ * - ScriptResult with testResults tracking
12
+ *
13
+ * Extensions: .bs (primary), .fcs (backward-compatible alias)
5
14
  */
6
- import { DEFAULT_SCRIPT_CONFIG, } from './types.js';
15
+ import { TokenType, DEFAULT_SCRIPT_CONFIG, } from './types.js';
7
16
  import { createBuiltins } from './builtins.js';
8
- // Special return value for control flow
9
- class ReturnValue {
17
+ import { createGrokBindings } from './codebuddy-bindings.js';
18
+ // Control flow signals
19
+ class ReturnSignal {
10
20
  value;
11
21
  constructor(value) {
12
22
  this.value = value;
@@ -16,26 +26,36 @@ class BreakSignal {
16
26
  }
17
27
  class ContinueSignal {
18
28
  }
19
- export class Runtime {
29
+ export class FCSRuntime {
20
30
  config;
21
31
  globalContext;
22
32
  output = [];
23
33
  startTime = 0;
34
+ testResults = [];
24
35
  constructor(config = {}) {
25
36
  this.config = { ...DEFAULT_SCRIPT_CONFIG, ...config };
26
37
  this.globalContext = {
27
38
  variables: new Map(),
28
39
  functions: new Map(),
29
40
  };
30
- // Initialize builtins
31
41
  this.initializeBuiltins();
32
42
  }
33
43
  initializeBuiltins() {
34
- const builtins = createBuiltins(this.config, (msg) => {
44
+ const printFn = (msg) => {
35
45
  this.output.push(msg);
36
- });
37
- for (const [name, fn] of Object.entries(builtins)) {
38
- this.globalContext.functions.set(name, fn);
46
+ };
47
+ // Load unified builtins (merged FCS + BS)
48
+ const builtins = createBuiltins(this.config, printFn);
49
+ for (const [name, value] of Object.entries(builtins)) {
50
+ this.globalContext.variables.set(name, value);
51
+ if (typeof value === 'function') {
52
+ this.globalContext.functions.set(name, value);
53
+ }
54
+ }
55
+ // Load codebuddy bindings (grok.*, tool.*, context.*, agent.*, mcp.*, git.*, session.*)
56
+ const grokBindings = createGrokBindings(this.config, printFn);
57
+ for (const [name, value] of Object.entries(grokBindings)) {
58
+ this.globalContext.variables.set(name, value);
39
59
  }
40
60
  // Inject user variables
41
61
  if (this.config.variables) {
@@ -47,124 +67,360 @@ export class Runtime {
47
67
  async execute(program) {
48
68
  this.startTime = Date.now();
49
69
  this.output = [];
70
+ this.testResults = [];
50
71
  let returnValue = null;
72
+ let error;
51
73
  try {
52
- for (const statement of program.body) {
53
- const result = await this.executeStatement(statement, this.globalContext);
54
- if (result instanceof ReturnValue) {
74
+ for (const statement of (program.statements || program.body || [])) {
75
+ const result = await this.executeNode(statement, this.globalContext);
76
+ if (result instanceof ReturnSignal) {
55
77
  returnValue = result.value;
56
78
  break;
57
79
  }
58
80
  }
59
81
  }
60
- catch (error) {
61
- if (error instanceof ReturnValue) {
62
- returnValue = error.value;
82
+ catch (err) {
83
+ if (err instanceof ReturnSignal) {
84
+ returnValue = err.value;
63
85
  }
64
86
  else {
65
- throw error;
87
+ error = err instanceof Error ? err.message : String(err);
66
88
  }
67
89
  }
68
90
  return {
91
+ success: !error,
69
92
  output: this.output,
93
+ error,
70
94
  returnValue,
71
95
  duration: Date.now() - this.startTime,
96
+ testResults: this.testResults.length > 0 ? this.testResults : undefined,
72
97
  };
73
98
  }
74
- async executeStatement(stmt, ctx) {
99
+ // Normalize old Buddy Script AST nodes to FCS format
100
+ normalizeNode(node) {
101
+ const n = node;
102
+ // Map old BS type names to FCS type names
103
+ const typeMap = {
104
+ 'VariableDeclaration': 'VarDeclaration',
105
+ 'BlockStatement': 'Block',
106
+ 'IfStatement': 'If',
107
+ 'WhileStatement': 'While',
108
+ 'ForInStatement': 'For',
109
+ 'ReturnStatement': 'Return',
110
+ 'BreakStatement': 'Break',
111
+ 'ContinueStatement': 'Continue',
112
+ 'TryStatement': 'Try',
113
+ 'ThrowStatement': 'Throw',
114
+ 'ExpressionStatement': 'ExpressionStmt',
115
+ 'BinaryExpression': 'Binary',
116
+ 'UnaryExpression': 'Unary',
117
+ 'LogicalExpression': 'Binary',
118
+ 'AssignmentExpression': 'Assignment',
119
+ 'CallExpression': 'Call',
120
+ 'MemberExpression': 'Member',
121
+ 'ArrayExpression': 'Array',
122
+ 'ObjectExpression': 'Dict',
123
+ 'ConditionalExpression': 'Ternary',
124
+ 'ArrowFunctionExpression': 'Lambda',
125
+ 'ArrowFunction': 'Lambda',
126
+ 'AwaitExpression': 'Await',
127
+ };
128
+ if (n.type === 'ForStatement') {
129
+ // Detect C-style vs for-in
130
+ n.type = (n.init !== undefined || n.test !== undefined || n.update !== undefined) ? 'ForCStyle' : 'For';
131
+ }
132
+ else if (typeMap[n.type]) {
133
+ n.type = typeMap[n.type];
134
+ }
135
+ // Map old BS field names to FCS field names
136
+ if (n.type === 'VarDeclaration' && n.init !== undefined && n.initializer === undefined) {
137
+ n.initializer = n.init;
138
+ if (n.kind)
139
+ n.isConst = n.kind === 'const';
140
+ }
141
+ if (n.type === 'Block' && n.body && !n.statements) {
142
+ n.statements = n.body;
143
+ }
144
+ if (n.type === 'If') {
145
+ if (n.test && !n.condition)
146
+ n.condition = n.test;
147
+ if (n.consequent && !n.thenBranch)
148
+ n.thenBranch = n.consequent;
149
+ if (n.alternate !== undefined && n.elseBranch === undefined)
150
+ n.elseBranch = n.alternate;
151
+ if (n.body && !n.thenBranch)
152
+ n.thenBranch = n.body;
153
+ }
154
+ if (n.type === 'While' && n.test && !n.condition) {
155
+ n.condition = n.test;
156
+ }
157
+ // Map For (for-in): old BS uses left/right, FCS uses variable/iterable
158
+ if (n.type === 'For') {
159
+ if (n.left && !n.variable)
160
+ n.variable = typeof n.left === 'object' ? n.left.name : n.left;
161
+ if (n.right && !n.iterable)
162
+ n.iterable = n.right;
163
+ }
164
+ if (n.type === 'Try') {
165
+ if (n.block && !n.tryBlock)
166
+ n.tryBlock = n.block;
167
+ if (n.handler && !n.catchClauses) {
168
+ n.catchClauses = [{
169
+ variable: n.handler.param || n.handler.variable,
170
+ body: n.handler.body || n.handler,
171
+ }];
172
+ }
173
+ if (n.finalizer && !n.finallyBlock)
174
+ n.finallyBlock = n.finalizer;
175
+ }
176
+ if (n.type === 'Ternary') {
177
+ if (n.test && !n.condition)
178
+ n.condition = n.test;
179
+ }
180
+ if (n.type === 'Lambda' && n.params && !n.parameters) {
181
+ n.parameters = n.params.map((p) => typeof p === 'string' ? p : p.name);
182
+ }
183
+ if (n.type === 'Return' && n.argument !== undefined && n.value === undefined) {
184
+ n.value = n.argument;
185
+ }
186
+ if (n.type === 'Throw' && n.argument !== undefined && n.expression === undefined) {
187
+ n.expression = n.argument;
188
+ }
189
+ // Map old-style operators ('+', '-', etc.) to TokenType names ('Plus', 'Minus', etc.)
190
+ if ((n.type === 'Binary' || n.type === 'Unary' || n.type === 'Assignment') && n.operator) {
191
+ const opMap = {
192
+ '+': 'Plus', '-': 'Minus', '*': 'Multiply', '/': 'Divide', '%': 'Modulo', '**': 'Power',
193
+ '==': 'Equal', '===': 'Equal', '!=': 'NotEqual', '!==': 'NotEqual', '<': 'Less', '>': 'Greater', '<=': 'LessEqual', '>=': 'GreaterEqual',
194
+ '&&': 'And', '||': 'Or', '!': 'Not',
195
+ '=': 'Assign', '+=': 'PlusAssign', '-=': 'MinusAssign', '*=': 'MultiplyAssign', '/=': 'DivideAssign',
196
+ };
197
+ if (opMap[n.operator])
198
+ n.operator = opMap[n.operator];
199
+ }
200
+ // Map Unary: old BS uses 'argument', FCS uses 'operand'
201
+ if (n.type === 'Unary' && n.argument && !n.operand) {
202
+ n.operand = n.argument;
203
+ }
204
+ // Map FunctionDeclaration: old BS uses 'params', FCS uses 'parameters'
205
+ if (n.type === 'FunctionDeclaration' && n.params && !n.parameters) {
206
+ n.parameters = n.params.map((p) => {
207
+ if (typeof p === 'string')
208
+ return { name: p };
209
+ return { name: p.name, defaultValue: p.default || p.defaultValue };
210
+ });
211
+ }
212
+ // Map Assignment fields: old BS uses left/right, FCS uses target/value
213
+ if (n.type === 'Assignment') {
214
+ if (n.left && !n.target)
215
+ n.target = n.left;
216
+ if (n.right !== undefined && n.value === undefined)
217
+ n.value = n.right;
218
+ }
219
+ // Call: callee and arguments are the same in both formats
220
+ // Map old 'object'/'property' to FCS 'object'/'member' for MemberExpression
221
+ if (n.type === 'Member' && n.property !== undefined && n.member === undefined) {
222
+ // Old BS property can be an AstNode (e.g., {type: 'Identifier', name: 'foo'}) or a string
223
+ n.member = typeof n.property === 'object' ? (n.property.name || n.property.value) : n.property;
224
+ }
225
+ // Map 'elements' to 'elements' (same for arrays)
226
+ // Map 'properties' to 'elements' Map for objects/dicts
227
+ if (n.type === 'Dict' && n.properties && !(n.elements instanceof Map)) {
228
+ const map = new Map();
229
+ for (const p of n.properties) {
230
+ const key = typeof p.key === 'object' ? (p.key.name || p.key.value) : p.key;
231
+ map.set(String(key), p.value);
232
+ }
233
+ n.elements = map;
234
+ }
235
+ return n;
236
+ }
237
+ async executeNode(node, ctx) {
75
238
  // Check timeout
76
239
  if (Date.now() - this.startTime > this.config.timeout) {
77
- throw new Error(`Script execution timed out after ${this.config.timeout}ms. Consider breaking the script into smaller parts or increasing the timeout limit.`);
78
- }
79
- switch (stmt.type) {
80
- case 'VariableDeclaration':
81
- return this.executeVariableDeclaration(stmt, ctx);
240
+ throw new Error(`Script timeout after ${this.config.timeout}ms`);
241
+ }
242
+ // Normalize old BS AST format
243
+ node = this.normalizeNode(node);
244
+ switch (node.type) {
245
+ // Declarations
246
+ case 'VarDeclaration':
247
+ return this.executeVarDeclaration(node, ctx);
82
248
  case 'FunctionDeclaration':
83
- return this.executeFunctionDeclaration(stmt, ctx);
84
- case 'ExpressionStatement':
85
- return this.evaluateExpression(stmt.expression, ctx);
86
- case 'IfStatement':
87
- return this.executeIfStatement(stmt, ctx);
88
- case 'WhileStatement':
89
- return this.executeWhileStatement(stmt, ctx);
90
- case 'ForStatement':
91
- return this.executeForStatement(stmt, ctx);
92
- case 'ForInStatement':
93
- return this.executeForInStatement(stmt, ctx);
94
- case 'ReturnStatement':
95
- return this.executeReturnStatement(stmt, ctx);
96
- case 'TryStatement':
97
- return this.executeTryStatement(stmt, ctx);
98
- case 'ThrowStatement':
99
- return this.executeThrowStatement(stmt, ctx);
100
- case 'BreakStatement':
249
+ return this.executeFunctionDeclaration(node, ctx);
250
+ case 'ClassDeclaration':
251
+ return this.executeClassDeclaration(node, ctx);
252
+ case 'TestDeclaration':
253
+ return this.executeTestDeclaration(node, ctx);
254
+ // Statements
255
+ case 'Block':
256
+ return this.executeBlock(node, ctx);
257
+ case 'ExpressionStmt':
258
+ return this.evaluate(node.expression, ctx);
259
+ case 'If':
260
+ return this.executeIfStatement(node, ctx);
261
+ case 'While':
262
+ return this.executeWhileStatement(node, ctx);
263
+ case 'For':
264
+ return this.executeForStatement(node, ctx);
265
+ case 'ForCStyle':
266
+ return this.executeForCStyleStatement(node, ctx);
267
+ case 'Return':
268
+ return this.executeReturnStatement(node, ctx);
269
+ case 'Break':
101
270
  return new BreakSignal();
102
- case 'ContinueStatement':
271
+ case 'Continue':
103
272
  return new ContinueSignal();
104
- case 'BlockStatement':
105
- return this.executeBlock(stmt, ctx);
106
- case 'ImportStatement':
107
- // Imports are handled at initialization
108
- return null;
273
+ case 'Try':
274
+ return this.executeTryStatement(node, ctx);
275
+ case 'Throw':
276
+ return this.executeThrowStatement(node, ctx);
277
+ case 'Import':
278
+ return this.executeImportStatement(node, ctx);
279
+ case 'Export':
280
+ return this.executeExportStatement(node, ctx);
281
+ case 'Assert':
282
+ return this.executeAssertStatement(node, ctx);
283
+ // Expressions (delegated to evaluate)
109
284
  default:
110
- throw new Error(`Unsupported statement type "${stmt.type}". This may be a syntax error or an unsupported feature.`);
285
+ return this.evaluate(node, ctx);
111
286
  }
112
287
  }
113
- async executeVariableDeclaration(stmt, ctx) {
114
- const value = stmt.init ? await this.evaluateExpression(stmt.init, ctx) : null;
115
- ctx.variables.set(stmt.name, value);
288
+ // ============================================
289
+ // Statement Execution
290
+ // ============================================
291
+ async executeVarDeclaration(node, ctx) {
292
+ const value = node.initializer
293
+ ? await this.evaluate(node.initializer, ctx)
294
+ : null;
295
+ ctx.variables.set(node.name, value);
116
296
  return null;
117
297
  }
118
- async executeFunctionDeclaration(stmt, ctx) {
298
+ async executeFunctionDeclaration(node, ctx) {
119
299
  const fn = async (...args) => {
120
300
  const localCtx = {
121
301
  variables: new Map(),
122
302
  functions: new Map(ctx.functions),
123
303
  parent: ctx,
124
304
  };
125
- // Bind parameters
126
- for (let i = 0; i < stmt.params.length; i++) {
127
- const param = stmt.params[i];
128
- const value = args[i] !== undefined ? args[i] : (param.defaultValue ? await this.evaluateExpression(param.defaultValue, ctx) : null);
305
+ // Bind parameters (with named args support)
306
+ const lastArg = args.length > 0 ? args[args.length - 1] : null;
307
+ const hasNamedArgs = lastArg !== null && typeof lastArg === 'object' && lastArg !== null && '__namedArgs' in lastArg;
308
+ const namedArgs = hasNamedArgs
309
+ ? args.pop().__namedArgs
310
+ : {};
311
+ for (let i = 0; i < node.parameters.length; i++) {
312
+ const param = node.parameters[i];
313
+ let value;
314
+ if (namedArgs[param.name] !== undefined) {
315
+ value = namedArgs[param.name];
316
+ }
317
+ else if (args[i] !== undefined) {
318
+ value = args[i];
319
+ }
320
+ else if (param.defaultValue) {
321
+ value = await this.evaluate(param.defaultValue, ctx);
322
+ }
323
+ else {
324
+ value = null;
325
+ }
129
326
  localCtx.variables.set(param.name, value);
130
327
  }
131
328
  try {
132
- const result = await this.executeBlock(stmt.body, localCtx);
133
- if (result instanceof ReturnValue) {
329
+ const result = await this.executeBlock(node.body, localCtx);
330
+ if (result instanceof ReturnSignal) {
134
331
  return result.value;
135
332
  }
136
333
  return null;
137
334
  }
138
- catch (error) {
139
- if (error instanceof ReturnValue) {
140
- return error.value;
335
+ catch (err) {
336
+ if (err instanceof ReturnSignal) {
337
+ return err.value;
141
338
  }
142
- throw error;
339
+ throw err;
143
340
  }
144
341
  };
145
- ctx.functions.set(stmt.name, fn);
146
- ctx.variables.set(stmt.name, fn);
342
+ ctx.functions.set(node.name, fn);
343
+ ctx.variables.set(node.name, fn);
147
344
  return null;
148
345
  }
149
- async executeIfStatement(stmt, ctx) {
150
- const condition = await this.evaluateExpression(stmt.condition, ctx);
151
- if (this.isTruthy(condition)) {
152
- return this.executeBlock(stmt.consequent, ctx);
153
- }
154
- else if (stmt.alternate) {
155
- if (stmt.alternate.type === 'IfStatement') {
156
- return this.executeIfStatement(stmt.alternate, ctx);
346
+ async executeClassDeclaration(node, ctx) {
347
+ const classConstructor = async (...args) => {
348
+ const instance = {};
349
+ for (const member of node.members) {
350
+ if (member.type === 'VarDeclaration') {
351
+ const varDecl = member;
352
+ instance[varDecl.name] = varDecl.initializer
353
+ ? await this.evaluate(varDecl.initializer, ctx)
354
+ : null;
355
+ }
356
+ else if (member.type === 'FunctionDeclaration') {
357
+ const fnDecl = member;
358
+ instance[fnDecl.name] = async (...methodArgs) => {
359
+ const methodCtx = {
360
+ variables: new Map([['this', instance]]),
361
+ functions: new Map(ctx.functions),
362
+ parent: ctx,
363
+ };
364
+ for (let i = 0; i < fnDecl.parameters.length; i++) {
365
+ methodCtx.variables.set(fnDecl.parameters[i].name, methodArgs[i]);
366
+ }
367
+ const result = await this.executeBlock(fnDecl.body, methodCtx);
368
+ if (result instanceof ReturnSignal) {
369
+ return result.value;
370
+ }
371
+ return null;
372
+ };
373
+ }
157
374
  }
158
- else {
159
- return this.executeBlock(stmt.alternate, ctx);
375
+ if (instance.constructor && typeof instance.constructor === 'function') {
376
+ await instance.constructor(...args);
160
377
  }
378
+ return instance;
379
+ };
380
+ ctx.variables.set(node.name, classConstructor);
381
+ return null;
382
+ }
383
+ async executeTestDeclaration(node, ctx) {
384
+ if (!this.config.verbose) {
385
+ return null;
386
+ }
387
+ try {
388
+ await this.executeBlock(node.body, ctx);
389
+ this.testResults.push({ name: node.name, passed: true });
390
+ this.output.push(`✓ ${node.name}`);
391
+ }
392
+ catch (err) {
393
+ const errorMsg = err instanceof Error ? err.message : String(err);
394
+ this.testResults.push({ name: node.name, passed: false, error: errorMsg });
395
+ this.output.push(`✗ ${node.name}: ${errorMsg}`);
396
+ }
397
+ return null;
398
+ }
399
+ async executeBlock(node, ctx) {
400
+ for (const stmt of (node.statements || node.body || [])) {
401
+ const result = await this.executeNode(stmt, ctx);
402
+ if (result instanceof ReturnSignal ||
403
+ result instanceof BreakSignal ||
404
+ result instanceof ContinueSignal) {
405
+ return result;
406
+ }
407
+ }
408
+ return null;
409
+ }
410
+ async executeIfStatement(node, ctx) {
411
+ const condition = await this.evaluate(node.condition, ctx);
412
+ if (this.isTruthy(condition)) {
413
+ return this.executeNode(node.thenBranch, ctx);
414
+ }
415
+ else if (node.elseBranch) {
416
+ return this.executeNode(node.elseBranch, ctx);
161
417
  }
162
418
  return null;
163
419
  }
164
- async executeWhileStatement(stmt, ctx) {
165
- while (this.isTruthy(await this.evaluateExpression(stmt.condition, ctx))) {
166
- const result = await this.executeBlock(stmt.body, ctx);
167
- if (result instanceof ReturnValue)
420
+ async executeWhileStatement(node, ctx) {
421
+ while (this.isTruthy(await this.evaluate(node.condition, ctx))) {
422
+ const result = await this.executeNode(node.body, ctx);
423
+ if (result instanceof ReturnSignal)
168
424
  return result;
169
425
  if (result instanceof BreakSignal)
170
426
  break;
@@ -173,330 +429,386 @@ export class Runtime {
173
429
  }
174
430
  return null;
175
431
  }
176
- async executeForStatement(stmt, ctx) {
177
- // Create local context for loop-scoped variables (like the init var)
178
- // Use empty map - only loop variables go here, parent lookup for outer vars
432
+ async executeForStatement(node, ctx) {
433
+ const iterable = await this.evaluate(node.iterable, ctx);
434
+ if (!Array.isArray(iterable) && typeof iterable !== 'object') {
435
+ throw new Error('for-in requires an iterable (array or object)');
436
+ }
437
+ const items = Array.isArray(iterable)
438
+ ? iterable
439
+ : Object.entries(iterable);
179
440
  const localCtx = {
180
441
  variables: new Map(),
181
442
  functions: ctx.functions,
182
443
  parent: ctx,
183
444
  };
184
- // Execute init
185
- if (stmt.init) {
186
- if (stmt.init.type === 'VariableDeclaration') {
187
- await this.executeVariableDeclaration(stmt.init, localCtx);
188
- }
189
- else {
190
- await this.evaluateExpression(stmt.init, localCtx);
191
- }
192
- }
193
- // Loop while test is true
194
- while (stmt.test === null || this.isTruthy(await this.evaluateExpression(stmt.test, localCtx))) {
195
- const result = await this.executeBlock(stmt.body, localCtx);
196
- if (result instanceof ReturnValue)
445
+ for (const item of items) {
446
+ localCtx.variables.set(node.variable, item);
447
+ const result = await this.executeNode(node.body, localCtx);
448
+ if (result instanceof ReturnSignal)
197
449
  return result;
198
450
  if (result instanceof BreakSignal)
199
451
  break;
200
- if (result instanceof ContinueSignal) {
201
- // Still run the update on continue
202
- if (stmt.update) {
203
- await this.evaluateExpression(stmt.update, localCtx);
204
- }
452
+ if (result instanceof ContinueSignal)
205
453
  continue;
206
- }
207
- // Execute update
208
- if (stmt.update) {
209
- await this.evaluateExpression(stmt.update, localCtx);
210
- }
211
454
  }
212
455
  return null;
213
456
  }
214
- async executeForInStatement(stmt, ctx) {
215
- const iterable = await this.evaluateExpression(stmt.iterable, ctx);
216
- if (!Array.isArray(iterable) && typeof iterable !== 'object') {
217
- throw new Error('for-in loop requires an array or object to iterate over. Got: ' + (typeof iterable));
218
- }
219
- const items = Array.isArray(iterable) ? iterable : Object.entries(iterable);
220
- // Create local context with only the loop variable - parent lookup for outer vars
457
+ async executeForCStyleStatement(node, ctx) {
221
458
  const localCtx = {
222
459
  variables: new Map(),
223
460
  functions: ctx.functions,
224
461
  parent: ctx,
225
462
  };
226
- for (const item of items) {
227
- localCtx.variables.set(stmt.variable, item);
228
- const result = await this.executeBlock(stmt.body, localCtx);
229
- if (result instanceof ReturnValue)
463
+ // Execute init (normalize in case of old BS format)
464
+ if (node.init) {
465
+ const initNode = this.normalizeNode(node.init);
466
+ if (initNode.type === 'VarDeclaration') {
467
+ await this.executeVarDeclaration(initNode, localCtx);
468
+ }
469
+ else {
470
+ await this.evaluate(initNode, localCtx);
471
+ }
472
+ }
473
+ // Loop while test is true
474
+ while (node.test === null || this.isTruthy(await this.evaluate(node.test, localCtx))) {
475
+ const result = await this.executeNode(node.body, localCtx);
476
+ if (result instanceof ReturnSignal)
230
477
  return result;
231
478
  if (result instanceof BreakSignal)
232
479
  break;
233
- if (result instanceof ContinueSignal)
480
+ if (result instanceof ContinueSignal) {
481
+ if (node.update) {
482
+ await this.evaluate(node.update, localCtx);
483
+ }
234
484
  continue;
485
+ }
486
+ if (node.update) {
487
+ await this.evaluate(node.update, localCtx);
488
+ }
235
489
  }
236
490
  return null;
237
491
  }
238
- async executeReturnStatement(stmt, ctx) {
239
- const value = stmt.argument ? await this.evaluateExpression(stmt.argument, ctx) : null;
240
- return new ReturnValue(value);
492
+ async executeReturnStatement(node, ctx) {
493
+ const value = node.value ? await this.evaluate(node.value, ctx) : null;
494
+ return new ReturnSignal(value);
241
495
  }
242
- async executeTryStatement(stmt, ctx) {
496
+ async executeTryStatement(node, ctx) {
243
497
  try {
244
- return await this.executeBlock(stmt.block, ctx);
498
+ return await this.executeBlock(node.tryBlock, ctx);
245
499
  }
246
- catch (error) {
247
- if (stmt.handler) {
500
+ catch (err) {
501
+ for (const clause of node.catchClauses) {
248
502
  const localCtx = {
249
503
  variables: new Map(ctx.variables),
250
504
  functions: ctx.functions,
251
505
  parent: ctx,
252
506
  };
253
- const errorValue = error instanceof Error ? {
254
- message: error.message,
255
- stack: error.stack,
256
- } : error;
257
- localCtx.variables.set(stmt.handler.param, errorValue);
258
- return this.executeBlock(stmt.handler.body, localCtx);
507
+ if (clause.variable) {
508
+ const errorValue = err instanceof Error
509
+ ? { message: err.message, stack: err.stack }
510
+ : err;
511
+ localCtx.variables.set(clause.variable, errorValue);
512
+ }
513
+ return this.executeBlock(clause.body, localCtx);
514
+ }
515
+ throw err;
516
+ }
517
+ finally {
518
+ if (node.finallyBlock) {
519
+ await this.executeBlock(node.finallyBlock, ctx);
259
520
  }
260
- throw error;
261
521
  }
262
522
  }
263
- async executeThrowStatement(stmt, ctx) {
264
- const value = await this.evaluateExpression(stmt.argument, ctx);
523
+ async executeThrowStatement(node, ctx) {
524
+ const value = await this.evaluate(node.expression, ctx);
265
525
  if (typeof value === 'string') {
266
526
  throw new Error(value);
267
527
  }
268
528
  throw value;
269
529
  }
270
- async executeBlock(block, ctx) {
271
- // Use the same context - don't create a new scope for blocks
272
- // New variables declared inside will still be added to this context
273
- // but assignments to existing variables will propagate up via setVariable
274
- for (const stmt of block.body) {
275
- const result = await this.executeStatement(stmt, ctx);
276
- if (result instanceof ReturnValue || result instanceof BreakSignal || result instanceof ContinueSignal) {
277
- return result;
278
- }
530
+ async executeImportStatement(_node, _ctx) {
531
+ // Imports are handled at initialization or by module system
532
+ return null;
533
+ }
534
+ async executeExportStatement(node, ctx) {
535
+ // Execute the declaration normally - exports don't change runtime behavior
536
+ return this.executeNode(node.declaration, ctx);
537
+ }
538
+ async executeAssertStatement(node, ctx) {
539
+ const condition = await this.evaluate(node.condition, ctx);
540
+ if (!this.isTruthy(condition)) {
541
+ const message = node.message || 'Assertion failed';
542
+ throw new Error(message);
279
543
  }
280
544
  return null;
281
545
  }
282
546
  // ============================================
283
547
  // Expression Evaluation
284
548
  // ============================================
285
- async evaluateExpression(expr, ctx) {
286
- switch (expr.type) {
549
+ async evaluate(node, ctx) {
550
+ node = this.normalizeNode(node);
551
+ switch (node.type) {
287
552
  case 'Literal':
288
- return expr.value;
553
+ return node.value;
289
554
  case 'Identifier':
290
- return this.lookupVariable(expr.name, ctx);
291
- case 'BinaryExpression':
292
- return this.evaluateBinaryExpression(expr, ctx);
293
- case 'UnaryExpression':
294
- return this.evaluateUnaryExpression(expr, ctx);
295
- case 'LogicalExpression':
296
- return this.evaluateLogicalExpression(expr, ctx);
297
- case 'AssignmentExpression':
298
- return this.evaluateAssignmentExpression(expr, ctx);
299
- case 'CallExpression':
300
- return this.evaluateCallExpression(expr, ctx);
301
- case 'MemberExpression':
302
- return this.evaluateMemberExpression(expr, ctx);
303
- case 'ArrayExpression':
304
- return this.evaluateArrayExpression(expr, ctx);
305
- case 'ObjectExpression':
306
- return this.evaluateObjectExpression(expr, ctx);
307
- case 'ConditionalExpression':
308
- return this.evaluateConditionalExpression(expr, ctx);
309
- case 'AwaitExpression':
310
- return this.evaluateExpression(expr.argument, ctx);
311
- case 'ArrowFunction':
312
- return this.createArrowFunction(expr, ctx);
555
+ return this.lookupVariable(node.name, ctx);
556
+ case 'Binary':
557
+ return this.evaluateBinary(node, ctx);
558
+ case 'Unary':
559
+ return this.evaluateUnary(node, ctx);
560
+ case 'Assignment':
561
+ return this.evaluateAssignment(node, ctx);
562
+ case 'Call':
563
+ return this.evaluateCall(node, ctx);
564
+ case 'Member':
565
+ return this.evaluateMember(node, ctx);
566
+ case 'Index':
567
+ return this.evaluateIndex(node, ctx);
568
+ case 'Array':
569
+ return this.evaluateArray(node, ctx);
570
+ case 'Dict':
571
+ return this.evaluateDict(node, ctx);
572
+ case 'Lambda':
573
+ return this.evaluateLambda(node, ctx);
574
+ case 'Interpolation':
575
+ return this.evaluateInterpolation(node, ctx);
576
+ case 'Ternary':
577
+ return this.evaluateTernary(node, ctx);
578
+ case 'Await':
579
+ // Await just evaluates the argument (our runtime is already async)
580
+ return this.evaluate(node.argument, ctx);
313
581
  default:
314
- throw new Error(`Unsupported expression type "${expr.type}". Check syntax or upgrade to a newer version.`);
582
+ throw new Error(`Unknown expression type: ${node.type}`);
315
583
  }
316
584
  }
317
585
  lookupVariable(name, ctx) {
318
- if (ctx.variables.has(name)) {
319
- return ctx.variables.get(name);
586
+ // Walk up scope chain checking variables first
587
+ let current = ctx;
588
+ while (current) {
589
+ if (current.variables.has(name)) {
590
+ return current.variables.get(name);
591
+ }
592
+ current = current.parent;
320
593
  }
594
+ // Then check functions (builtins)
321
595
  if (ctx.functions.has(name)) {
322
596
  return ctx.functions.get(name);
323
597
  }
324
- if (ctx.parent) {
325
- return this.lookupVariable(name, ctx.parent);
598
+ throw new Error(`Undefined variable: ${name}`);
599
+ }
600
+ setVariable(name, value, ctx) {
601
+ let current = ctx;
602
+ while (current) {
603
+ if (current.variables.has(name)) {
604
+ current.variables.set(name, value);
605
+ return;
606
+ }
607
+ current = current.parent;
326
608
  }
327
- throw new Error(`Variable "${name}" is not defined. Check the variable name or define it before use.`);
609
+ ctx.variables.set(name, value);
328
610
  }
329
- async evaluateBinaryExpression(expr, ctx) {
330
- const left = await this.evaluateExpression(expr.left, ctx);
331
- const right = await this.evaluateExpression(expr.right, ctx);
332
- switch (expr.operator) {
333
- case '+':
611
+ async evaluateBinary(node, ctx) {
612
+ const left = await this.evaluate(node.left, ctx);
613
+ const right = await this.evaluate(node.right, ctx);
614
+ switch (node.operator) {
615
+ case TokenType.Plus:
334
616
  if (typeof left === 'string' || typeof right === 'string') {
335
617
  return String(left) + String(right);
336
618
  }
337
619
  return left + right;
338
- case '-': return left - right;
339
- case '*':
340
- // Support string repetition: "x" * 3 = "xxx"
620
+ case TokenType.Minus:
621
+ return left - right;
622
+ case TokenType.Multiply:
341
623
  if (typeof left === 'string' && typeof right === 'number') {
342
624
  return left.repeat(right);
343
625
  }
344
626
  return left * right;
345
- case '/': return left / right;
346
- case '%': return left % right;
347
- case '**': return Math.pow(left, right);
348
- case '==':
349
- case '===': return left === right;
350
- case '!=':
351
- case '!==': return left !== right;
352
- case '<': return left < right;
353
- case '<=': return left <= right;
354
- case '>': return left > right;
355
- case '>=': return left >= right;
627
+ case TokenType.Divide:
628
+ return left / right;
629
+ case TokenType.Modulo:
630
+ return left % right;
631
+ case TokenType.Power:
632
+ return Math.pow(left, right);
633
+ case TokenType.Equal:
634
+ return left === right;
635
+ case TokenType.NotEqual:
636
+ return left !== right;
637
+ case TokenType.Less:
638
+ return left < right;
639
+ case TokenType.LessEqual:
640
+ return left <= right;
641
+ case TokenType.Greater:
642
+ return left > right;
643
+ case TokenType.GreaterEqual:
644
+ return left >= right;
645
+ case TokenType.And:
646
+ return this.isTruthy(left) && this.isTruthy(right);
647
+ case TokenType.Or:
648
+ return this.isTruthy(left) || this.isTruthy(right);
356
649
  default:
357
- throw new Error(`Unsupported binary operator "${expr.operator}". Valid operators: +, -, *, /, %, **, ==, !=, <, <=, >, >=`);
650
+ throw new Error(`Unknown binary operator: ${node.operator}`);
358
651
  }
359
652
  }
360
- async evaluateUnaryExpression(expr, ctx) {
361
- const argument = await this.evaluateExpression(expr.argument, ctx);
362
- switch (expr.operator) {
363
- case '-': return -argument;
364
- case '!': return !this.isTruthy(argument);
653
+ async evaluateUnary(node, ctx) {
654
+ const operand = await this.evaluate(node.operand, ctx);
655
+ switch (node.operator) {
656
+ case TokenType.Minus:
657
+ return -operand;
658
+ case TokenType.Not:
659
+ return !this.isTruthy(operand);
365
660
  default:
366
- throw new Error(`Unsupported unary operator "${expr.operator}". Valid operators: -, !`);
367
- }
368
- }
369
- async evaluateLogicalExpression(expr, ctx) {
370
- const left = await this.evaluateExpression(expr.left, ctx);
371
- if (expr.operator === '&&') {
372
- if (!this.isTruthy(left))
373
- return left;
374
- return this.evaluateExpression(expr.right, ctx);
375
- }
376
- else {
377
- if (this.isTruthy(left))
378
- return left;
379
- return this.evaluateExpression(expr.right, ctx);
661
+ throw new Error(`Unknown unary operator: ${node.operator}`);
380
662
  }
381
663
  }
382
- async evaluateAssignmentExpression(expr, ctx) {
383
- let value = await this.evaluateExpression(expr.right, ctx);
384
- if (expr.operator === '+=') {
385
- const current = await this.evaluateExpression(expr.left, ctx);
386
- if (typeof current === 'string' || typeof value === 'string') {
387
- value = String(current) + String(value);
388
- }
389
- else {
390
- value = current + value;
664
+ async evaluateAssignment(node, ctx) {
665
+ let value = await this.evaluate(node.value, ctx);
666
+ if (node.operator !== TokenType.Assign) {
667
+ const current = await this.evaluate(node.target, ctx);
668
+ switch (node.operator) {
669
+ case TokenType.PlusAssign:
670
+ if (typeof current === 'string' || typeof value === 'string') {
671
+ value = String(current) + String(value);
672
+ }
673
+ else {
674
+ value = current + value;
675
+ }
676
+ break;
677
+ case TokenType.MinusAssign:
678
+ value = current - value;
679
+ break;
680
+ case TokenType.MultiplyAssign:
681
+ value = current * value;
682
+ break;
683
+ case TokenType.DivideAssign:
684
+ value = current / value;
685
+ break;
391
686
  }
392
687
  }
393
- else if (expr.operator === '-=') {
394
- const current = await this.evaluateExpression(expr.left, ctx);
395
- value = current - value;
688
+ if (node.target.type === 'Identifier') {
689
+ this.setVariable(node.target.name, value, ctx);
396
690
  }
397
- if (expr.left.type === 'Identifier') {
398
- this.setVariable(expr.left.name, value, ctx);
691
+ else if (node.target.type === 'Member') {
692
+ const member = node.target;
693
+ const object = await this.evaluate(member.object, ctx);
694
+ object[member.member] = value;
399
695
  }
400
- else if (expr.left.type === 'MemberExpression') {
401
- const member = expr.left;
402
- const object = await this.evaluateExpression(member.object, ctx);
403
- const property = member.computed
404
- ? await this.evaluateExpression(member.property, ctx)
405
- : member.property.name;
406
- object[property] = value;
696
+ else if (node.target.type === 'Index') {
697
+ const index = node.target;
698
+ const object = await this.evaluate(index.object, ctx);
699
+ const key = await this.evaluate(index.index, ctx);
700
+ object[key] = value;
407
701
  }
408
702
  return value;
409
703
  }
410
- setVariable(name, value, ctx) {
411
- // Find where the variable is defined
412
- let current = ctx;
413
- while (current) {
414
- if (current.variables.has(name)) {
415
- current.variables.set(name, value);
416
- return;
417
- }
418
- current = current.parent;
419
- }
420
- // If not found, create in current context
421
- ctx.variables.set(name, value);
422
- }
423
- async evaluateCallExpression(expr, ctx) {
424
- const callee = await this.evaluateExpression(expr.callee, ctx);
704
+ async evaluateCall(node, ctx) {
705
+ const callee = await this.evaluate(node.callee, ctx);
425
706
  if (typeof callee !== 'function') {
426
- throw new Error(`Cannot call ${JSON.stringify(callee)}: it is not a function. Check if the function name is correct.`);
707
+ throw new Error(`${JSON.stringify(callee)} is not a function`);
427
708
  }
428
709
  const args = [];
429
- for (const arg of expr.arguments) {
430
- args.push(await this.evaluateExpression(arg, ctx));
710
+ for (const arg of node.arguments) {
711
+ args.push(await this.evaluate(arg, ctx));
712
+ }
713
+ // Handle named arguments
714
+ if (node.namedArgs && Object.keys(node.namedArgs).length > 0) {
715
+ const namedValues = {};
716
+ for (const [name, expr] of Object.entries(node.namedArgs)) {
717
+ namedValues[name] = await this.evaluate(expr, ctx);
718
+ }
719
+ args.push({ __namedArgs: namedValues });
431
720
  }
432
- return callee(...args);
721
+ const result = callee(...args);
722
+ return result instanceof Promise ? await result : result;
433
723
  }
434
- async evaluateMemberExpression(expr, ctx) {
435
- const object = await this.evaluateExpression(expr.object, ctx);
724
+ async evaluateMember(node, ctx) {
725
+ const object = await this.evaluate(node.object, ctx);
436
726
  if (object === null || object === undefined) {
437
- throw new Error('Cannot read property of null or undefined. Ensure the object exists before accessing its properties.');
727
+ throw new Error('Cannot read property of null or undefined');
438
728
  }
439
- const property = expr.computed
440
- ? await this.evaluateExpression(expr.property, ctx)
441
- : expr.property.name;
442
729
  const obj = object;
443
- const value = obj[property];
444
- // Bind methods
730
+ const value = obj[node.member];
445
731
  if (typeof value === 'function') {
446
732
  return value.bind(obj);
447
733
  }
448
734
  return value;
449
735
  }
450
- async evaluateArrayExpression(expr, ctx) {
451
- const elements = [];
452
- for (const element of expr.elements) {
453
- elements.push(await this.evaluateExpression(element, ctx));
736
+ async evaluateIndex(node, ctx) {
737
+ const object = await this.evaluate(node.object, ctx);
738
+ const index = await this.evaluate(node.index, ctx);
739
+ if (object === null || object === undefined) {
740
+ throw new Error('Cannot index null or undefined');
454
741
  }
455
- return elements;
742
+ return object[index];
456
743
  }
457
- async evaluateObjectExpression(expr, ctx) {
458
- const obj = {};
459
- for (const prop of expr.properties) {
460
- const key = typeof prop.key === 'string'
461
- ? prop.key
462
- : await this.evaluateExpression(prop.key, ctx);
463
- obj[key] = await this.evaluateExpression(prop.value, ctx);
744
+ async evaluateArray(node, ctx) {
745
+ const elements = [];
746
+ for (const element of node.elements) {
747
+ elements.push(await this.evaluate(element, ctx));
464
748
  }
465
- return obj;
749
+ return elements;
466
750
  }
467
- async evaluateConditionalExpression(expr, ctx) {
468
- const test = await this.evaluateExpression(expr.test, ctx);
469
- if (this.isTruthy(test)) {
470
- return this.evaluateExpression(expr.consequent, ctx);
751
+ async evaluateDict(node, ctx) {
752
+ const dict = {};
753
+ for (const [key, valueNode] of node.elements) {
754
+ dict[key] = await this.evaluate(valueNode, ctx);
471
755
  }
472
- return this.evaluateExpression(expr.alternate, ctx);
756
+ return dict;
473
757
  }
474
- createArrowFunction(expr, ctx) {
475
- const arrow = expr;
758
+ evaluateLambda(node, ctx) {
476
759
  return async (...args) => {
477
760
  const localCtx = {
478
- variables: new Map(ctx.variables),
761
+ variables: new Map(),
479
762
  functions: ctx.functions,
480
763
  parent: ctx,
481
764
  };
482
- for (let i = 0; i < arrow.params.length; i++) {
483
- localCtx.variables.set(arrow.params[i].name, args[i]);
765
+ for (let i = 0; i < node.parameters.length; i++) {
766
+ localCtx.variables.set(node.parameters[i], args[i]);
484
767
  }
485
- if (arrow.body.type === 'BlockStatement') {
486
- const result = await this.executeBlock(arrow.body, localCtx);
487
- if (result instanceof ReturnValue) {
768
+ if (node.body.type === 'Block') {
769
+ const result = await this.executeBlock(node.body, localCtx);
770
+ if (result instanceof ReturnSignal) {
488
771
  return result.value;
489
772
  }
490
773
  return null;
491
774
  }
492
- return this.evaluateExpression(arrow.body, localCtx);
775
+ return this.evaluate(node.body, localCtx);
493
776
  };
494
777
  }
778
+ async evaluateInterpolation(node, ctx) {
779
+ let result = '';
780
+ for (const part of node.parts) {
781
+ const value = await this.evaluate(part, ctx);
782
+ result += String(value);
783
+ }
784
+ return result;
785
+ }
786
+ async evaluateTernary(node, ctx) {
787
+ const condition = await this.evaluate(node.condition, ctx);
788
+ if (this.isTruthy(condition)) {
789
+ return this.evaluate(node.consequent, ctx);
790
+ }
791
+ return this.evaluate(node.alternate, ctx);
792
+ }
793
+ // ============================================
794
+ // Helpers
795
+ // ============================================
495
796
  isTruthy(value) {
496
797
  if (value === null || value === undefined || value === false || value === 0 || value === '') {
497
798
  return false;
498
799
  }
499
800
  return true;
500
801
  }
802
+ getTestResults() {
803
+ return this.testResults;
804
+ }
805
+ getOutput() {
806
+ return this.output;
807
+ }
808
+ }
809
+ /** @deprecated Use FCSRuntime instead */
810
+ export const Runtime = FCSRuntime;
811
+ export function createRuntime(config = {}) {
812
+ return new FCSRuntime(config);
501
813
  }
502
814
  //# sourceMappingURL=runtime.js.map