@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.
- package/.codebuddy/skills/bundled/brave-search/SKILL.md +490 -0
- package/.codebuddy/skills/bundled/exa-search/SKILL.md +1122 -0
- package/.codebuddy/skills/bundled/perplexity/SKILL.md +748 -0
- package/.codebuddy/skills/bundled/playwright/SKILL.md +520 -0
- package/.codebuddy/skills/bundled/puppeteer/SKILL.md +708 -0
- package/.codebuddy/skills/bundled/web-fetch/SKILL.md +1003 -0
- package/README.md +56 -0
- package/dist/agent/agent-state.d.ts +3 -3
- package/dist/agent/agent-state.js +6 -6
- package/dist/agent/agent-state.js.map +1 -1
- package/dist/agent/base-agent.d.ts +4 -4
- package/dist/agent/base-agent.js +22 -9
- package/dist/agent/base-agent.js.map +1 -1
- package/dist/agent/cache-trace.d.ts +56 -0
- package/dist/agent/cache-trace.js +98 -0
- package/dist/agent/cache-trace.js.map +1 -0
- package/dist/agent/codebuddy-agent.js +4 -2
- package/dist/agent/codebuddy-agent.js.map +1 -1
- package/dist/agent/execution/agent-executor.d.ts +4 -4
- package/dist/agent/execution/agent-executor.js +46 -14
- package/dist/agent/execution/agent-executor.js.map +1 -1
- package/dist/agent/facades/agent-context-facade.js +1 -3
- package/dist/agent/facades/agent-context-facade.js.map +1 -1
- package/dist/agent/facades/message-history-manager.js +14 -12
- package/dist/agent/facades/message-history-manager.js.map +1 -1
- package/dist/agent/facades/session-facade.d.ts +3 -3
- package/dist/agent/facades/session-facade.js +6 -6
- package/dist/agent/facades/session-facade.js.map +1 -1
- package/dist/agent/history-repair.d.ts +37 -0
- package/dist/agent/history-repair.js +124 -0
- package/dist/agent/history-repair.js.map +1 -0
- package/dist/agent/index.d.ts +3 -3
- package/dist/agent/index.js +3 -3
- package/dist/agent/index.js.map +1 -1
- package/dist/agent/isolation/agent-workspace.d.ts +1 -0
- package/dist/agent/isolation/agent-workspace.js +10 -0
- package/dist/agent/isolation/agent-workspace.js.map +1 -1
- package/dist/agent/specialized/archive-agent.d.ts +3 -0
- package/dist/agent/specialized/archive-agent.js +71 -31
- package/dist/agent/specialized/archive-agent.js.map +1 -1
- package/dist/agent/specialized/index.d.ts +9 -8
- package/dist/agent/specialized/index.js +16 -8
- package/dist/agent/specialized/index.js.map +1 -1
- package/dist/agent/specialized/security-review/agent.js +19 -8
- package/dist/agent/specialized/security-review/agent.js.map +1 -1
- package/dist/agent/tool-executor.js +5 -0
- package/dist/agent/tool-executor.js.map +1 -1
- package/dist/agent/turn-diff-tracker.d.ts +79 -0
- package/dist/agent/turn-diff-tracker.js +195 -0
- package/dist/agent/turn-diff-tracker.js.map +1 -0
- package/dist/browser/controller.js +8 -4
- package/dist/browser/controller.js.map +1 -1
- package/dist/browser-automation/browser-manager.js +8 -1
- package/dist/browser-automation/browser-manager.js.map +1 -1
- package/dist/checkpoints/checkpoint-versioning.js +78 -20
- package/dist/checkpoints/checkpoint-versioning.js.map +1 -1
- package/dist/cli/config-loader.js +2 -4
- package/dist/cli/config-loader.js.map +1 -1
- package/dist/codebuddy/client.js +70 -11
- package/dist/codebuddy/client.js.map +1 -1
- package/dist/codebuddy/tools.d.ts +1 -7
- package/dist/codebuddy/tools.js +2 -30
- package/dist/codebuddy/tools.js.map +1 -1
- package/dist/commands/cli/daemon-commands.d.ts +14 -0
- package/dist/commands/cli/daemon-commands.js +166 -0
- package/dist/commands/cli/daemon-commands.js.map +1 -0
- package/dist/commands/cli/speak-command.d.ts +10 -0
- package/dist/commands/cli/speak-command.js +97 -0
- package/dist/commands/cli/speak-command.js.map +1 -0
- package/dist/commands/cli/utility-commands.d.ts +10 -0
- package/dist/commands/cli/utility-commands.js +88 -0
- package/dist/commands/cli/utility-commands.js.map +1 -0
- package/dist/commands/handlers/fcs-handlers.js +1 -1
- package/dist/commands/handlers/fcs-handlers.js.map +1 -1
- package/dist/commands/handlers/memory-handlers.js +2 -1
- package/dist/commands/handlers/memory-handlers.js.map +1 -1
- package/dist/commands/handlers/vibe-handlers.js +0 -1
- package/dist/commands/handlers/vibe-handlers.js.map +1 -1
- package/dist/commands/handlers/worktree-handlers.js +11 -0
- package/dist/commands/handlers/worktree-handlers.js.map +1 -1
- package/dist/commands/index.d.ts +8 -7
- package/dist/commands/index.js +10 -8
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/mcp.d.ts +1 -0
- package/dist/commands/mcp.js +66 -7
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/pipeline.js +25 -13
- package/dist/commands/pipeline.js.map +1 -1
- package/dist/config/hot-reload/watcher.js +4 -4
- package/dist/config/hot-reload/watcher.js.map +1 -1
- package/dist/config/model-tools.d.ts +41 -0
- package/dist/config/model-tools.js +194 -0
- package/dist/config/model-tools.js.map +1 -0
- package/dist/context/context-manager-v2.d.ts +2 -1
- package/dist/context/context-manager-v2.js +34 -5
- package/dist/context/context-manager-v2.js.map +1 -1
- package/dist/context/index.d.ts +12 -12
- package/dist/context/index.js +25 -12
- package/dist/context/index.js.map +1 -1
- package/dist/daemon/daemon-manager.js +23 -19
- package/dist/daemon/daemon-manager.js.map +1 -1
- package/dist/database/database-manager.d.ts +4 -0
- package/dist/database/database-manager.js +16 -7
- package/dist/database/database-manager.js.map +1 -1
- package/dist/desktop-automation/nutjs-provider.js +89 -0
- package/dist/desktop-automation/nutjs-provider.js.map +1 -1
- package/dist/errors/index.d.ts +4 -4
- package/dist/errors/index.js +8 -4
- package/dist/errors/index.js.map +1 -1
- package/dist/fcs/builtins.d.ts +2 -6
- package/dist/fcs/builtins.js +2 -568
- package/dist/fcs/builtins.js.map +1 -1
- package/dist/fcs/codebuddy-bindings.d.ts +3 -43
- package/dist/fcs/codebuddy-bindings.js +2 -606
- package/dist/fcs/codebuddy-bindings.js.map +1 -1
- package/dist/fcs/index.d.ts +2 -27
- package/dist/fcs/index.js +2 -53
- package/dist/fcs/index.js.map +1 -1
- package/dist/fcs/lexer.d.ts +2 -37
- package/dist/fcs/lexer.js +2 -459
- package/dist/fcs/lexer.js.map +1 -1
- package/dist/fcs/parser.d.ts +2 -68
- package/dist/fcs/parser.js +2 -893
- package/dist/fcs/parser.js.map +1 -1
- package/dist/fcs/runtime.d.ts +2 -59
- package/dist/fcs/runtime.js +2 -623
- package/dist/fcs/runtime.js.map +1 -1
- package/dist/fcs/script-registry.d.ts +3 -69
- package/dist/fcs/script-registry.js +2 -219
- package/dist/fcs/script-registry.js.map +1 -1
- package/dist/fcs/sync-bindings.d.ts +3 -101
- package/dist/fcs/sync-bindings.js +2 -410
- package/dist/fcs/sync-bindings.js.map +1 -1
- package/dist/fcs/types.d.ts +2 -285
- package/dist/fcs/types.js +2 -103
- package/dist/fcs/types.js.map +1 -1
- package/dist/hooks/index.d.ts +4 -4
- package/dist/hooks/index.js +4 -4
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/use-input-handler.d.ts +1 -1
- package/dist/index.js +20 -330
- package/dist/index.js.map +1 -1
- package/dist/input/voice-control.js +11 -5
- package/dist/input/voice-control.js.map +1 -1
- package/dist/integrations/json-rpc/server.d.ts +9 -0
- package/dist/integrations/json-rpc/server.js +43 -13
- package/dist/integrations/json-rpc/server.js.map +1 -1
- package/dist/integrations/mcp/mcp-server.js +1 -1
- package/dist/integrations/mcp/mcp-server.js.map +1 -1
- package/dist/integrations/notification-integrations.d.ts +1 -0
- package/dist/integrations/notification-integrations.js +6 -1
- package/dist/integrations/notification-integrations.js.map +1 -1
- package/dist/mcp/client.js +2 -1
- package/dist/mcp/client.js.map +1 -1
- package/dist/mcp/config.js +89 -5
- package/dist/mcp/config.js.map +1 -1
- package/dist/mcp/mcp-client.js +65 -14
- package/dist/mcp/mcp-client.js.map +1 -1
- package/dist/mcp/transports.d.ts +0 -1
- package/dist/mcp/transports.js +1 -5
- package/dist/mcp/transports.js.map +1 -1
- package/dist/mcp/types.d.ts +2 -0
- package/dist/memory/index.d.ts +2 -2
- package/dist/memory/index.js +2 -2
- package/dist/memory/index.js.map +1 -1
- package/dist/persistence/session-lock.d.ts +42 -0
- package/dist/persistence/session-lock.js +165 -0
- package/dist/persistence/session-lock.js.map +1 -0
- package/dist/persistence/session-store.d.ts +18 -3
- package/dist/persistence/session-store.js +90 -21
- package/dist/persistence/session-store.js.map +1 -1
- package/dist/plugins/conflict-detection.js +2 -1
- package/dist/plugins/conflict-detection.js.map +1 -1
- package/dist/plugins/index.d.ts +3 -3
- package/dist/plugins/index.js +3 -3
- package/dist/plugins/index.js.map +1 -1
- package/dist/plugins/isolated-plugin-runner.d.ts +6 -0
- package/dist/plugins/isolated-plugin-runner.js +19 -1
- package/dist/plugins/isolated-plugin-runner.js.map +1 -1
- package/dist/providers/local-llm-provider.js +28 -8
- package/dist/providers/local-llm-provider.js.map +1 -1
- package/dist/sandbox/docker-sandbox.js +7 -4
- package/dist/sandbox/docker-sandbox.js.map +1 -1
- package/dist/scripting/builtins.d.ts +8 -3
- package/dist/scripting/builtins.js +506 -355
- package/dist/scripting/builtins.js.map +1 -1
- package/dist/scripting/codebuddy-bindings.d.ts +47 -0
- package/dist/scripting/codebuddy-bindings.js +488 -0
- package/dist/scripting/codebuddy-bindings.js.map +1 -0
- package/dist/scripting/index.d.ts +33 -30
- package/dist/scripting/index.js +41 -36
- package/dist/scripting/index.js.map +1 -1
- package/dist/scripting/lexer.d.ts +31 -13
- package/dist/scripting/lexer.js +379 -292
- package/dist/scripting/lexer.js.map +1 -1
- package/dist/scripting/parser.d.ts +63 -44
- package/dist/scripting/parser.js +700 -473
- package/dist/scripting/parser.js.map +1 -1
- package/dist/scripting/runtime.d.ts +55 -24
- package/dist/scripting/runtime.js +600 -288
- package/dist/scripting/runtime.js.map +1 -1
- package/dist/scripting/script-registry.d.ts +54 -0
- package/dist/scripting/script-registry.js +202 -0
- package/dist/scripting/script-registry.js.map +1 -0
- package/dist/scripting/sync-bindings.d.ts +105 -0
- package/dist/scripting/sync-bindings.js +353 -0
- package/dist/scripting/sync-bindings.js.map +1 -0
- package/dist/scripting/types.d.ts +297 -199
- package/dist/scripting/types.js +86 -60
- package/dist/scripting/types.js.map +1 -1
- package/dist/search/usearch-index.js +42 -7
- package/dist/search/usearch-index.js.map +1 -1
- package/dist/security/bash-parser.d.ts +51 -0
- package/dist/security/bash-parser.js +327 -0
- package/dist/security/bash-parser.js.map +1 -0
- package/dist/security/index.d.ts +7 -5
- package/dist/security/index.js +8 -7
- package/dist/security/index.js.map +1 -1
- package/dist/security/skill-scanner.d.ts +36 -0
- package/dist/security/skill-scanner.js +149 -0
- package/dist/security/skill-scanner.js.map +1 -0
- package/dist/security/trust-folders.d.ts +1 -0
- package/dist/security/trust-folders.js +19 -1
- package/dist/security/trust-folders.js.map +1 -1
- package/dist/server/auth/index.d.ts +2 -2
- package/dist/server/auth/index.js +2 -2
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/middleware/index.d.ts +5 -5
- package/dist/server/middleware/index.js +5 -5
- package/dist/server/middleware/index.js.map +1 -1
- package/dist/server/middleware/rate-limit.js +15 -3
- package/dist/server/middleware/rate-limit.js.map +1 -1
- package/dist/server/websocket/handler.js +54 -6
- package/dist/server/websocket/handler.js.map +1 -1
- package/dist/skills/eligibility.js +26 -4
- package/dist/skills/eligibility.js.map +1 -1
- package/dist/tasks/background-tasks.js +5 -1
- package/dist/tasks/background-tasks.js.map +1 -1
- package/dist/tools/apply-patch.d.ts +55 -0
- package/dist/tools/apply-patch.js +273 -0
- package/dist/tools/apply-patch.js.map +1 -0
- package/dist/tools/hooks/default-hooks.d.ts +1 -1
- package/dist/tools/hooks/default-hooks.js +2 -1
- package/dist/tools/hooks/default-hooks.js.map +1 -1
- package/dist/tools/index.d.ts +10 -10
- package/dist/tools/index.js +11 -11
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/registry/bash-tools.js +6 -3
- package/dist/tools/registry/bash-tools.js.map +1 -1
- package/dist/tools/registry/misc-tools.js +1 -2
- package/dist/tools/registry/misc-tools.js.map +1 -1
- package/dist/tools/registry/search-tools.js +1 -1
- package/dist/tools/registry/search-tools.js.map +1 -1
- package/dist/tools/registry/text-editor-tools.js +1 -1
- package/dist/tools/registry/text-editor-tools.js.map +1 -1
- package/dist/tools/registry/todo-tools.js +37 -5
- package/dist/tools/registry/todo-tools.js.map +1 -1
- package/dist/tools/registry/tool-registry.js +5 -4
- package/dist/tools/registry/tool-registry.js.map +1 -1
- package/dist/tools/registry/web-tools.d.ts +1 -1
- package/dist/tools/registry/web-tools.js +28 -8
- package/dist/tools/registry/web-tools.js.map +1 -1
- package/dist/tools/text-editor.d.ts +1 -1
- package/dist/tools/text-editor.js +23 -5
- package/dist/tools/text-editor.js.map +1 -1
- package/dist/tools/web-search.d.ts +52 -37
- package/dist/tools/web-search.js +368 -163
- package/dist/tools/web-search.js.map +1 -1
- package/dist/types/errors.d.ts +1 -1
- package/dist/types/errors.js +2 -8
- package/dist/types/errors.js.map +1 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.js +1 -2
- package/dist/types/index.js.map +1 -1
- package/dist/ui/components/ChatInterface.d.ts +1 -1
- package/dist/ui/index.d.ts +17 -21
- package/dist/ui/index.js +25 -22
- package/dist/ui/index.js.map +1 -1
- package/dist/utils/config-validation/schema.d.ts +15 -15
- package/dist/utils/head-tail-truncation.d.ts +34 -0
- package/dist/utils/head-tail-truncation.js +98 -0
- package/dist/utils/head-tail-truncation.js.map +1 -0
- package/dist/utils/logger.js +3 -9
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/sanitize.d.ts +5 -0
- package/dist/utils/sanitize.js +19 -0
- package/dist/utils/sanitize.js.map +1 -1
- package/dist/utils/settings-manager.js +4 -4
- package/dist/utils/settings-manager.js.map +1 -1
- package/dist/workflows/index.d.ts +4 -279
- package/dist/workflows/index.js +8 -822
- package/dist/workflows/index.js.map +1 -1
- package/dist/workflows/state-manager.d.ts +77 -0
- package/dist/workflows/state-manager.js +198 -0
- package/dist/workflows/state-manager.js.map +1 -0
- package/dist/workflows/step-manager.d.ts +39 -0
- package/dist/workflows/step-manager.js +196 -0
- package/dist/workflows/step-manager.js.map +1 -0
- package/dist/workflows/types.d.ts +87 -0
- package/dist/workflows/types.js +5 -0
- package/dist/workflows/types.js.map +1 -0
- package/dist/workflows/workflow-engine.d.ts +34 -0
- package/dist/workflows/workflow-engine.js +354 -0
- package/dist/workflows/workflow-engine.js.map +1 -0
- package/package.json +5 -1
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Unified Script Runtime
|
|
3
3
|
*
|
|
4
|
-
*
|
|
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
|
-
|
|
9
|
-
|
|
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
|
|
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
|
|
44
|
+
const printFn = (msg) => {
|
|
35
45
|
this.output.push(msg);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
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.
|
|
54
|
-
if (result instanceof
|
|
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 (
|
|
61
|
-
if (
|
|
62
|
-
returnValue =
|
|
82
|
+
catch (err) {
|
|
83
|
+
if (err instanceof ReturnSignal) {
|
|
84
|
+
returnValue = err.value;
|
|
63
85
|
}
|
|
64
86
|
else {
|
|
65
|
-
|
|
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
|
-
|
|
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
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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(
|
|
84
|
-
case '
|
|
85
|
-
return this.
|
|
86
|
-
case '
|
|
87
|
-
return this.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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 '
|
|
271
|
+
case 'Continue':
|
|
103
272
|
return new ContinueSignal();
|
|
104
|
-
case '
|
|
105
|
-
return this.
|
|
106
|
-
case '
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
285
|
+
return this.evaluate(node, ctx);
|
|
111
286
|
}
|
|
112
287
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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(
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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(
|
|
133
|
-
if (result instanceof
|
|
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 (
|
|
139
|
-
if (
|
|
140
|
-
return
|
|
335
|
+
catch (err) {
|
|
336
|
+
if (err instanceof ReturnSignal) {
|
|
337
|
+
return err.value;
|
|
141
338
|
}
|
|
142
|
-
throw
|
|
339
|
+
throw err;
|
|
143
340
|
}
|
|
144
341
|
};
|
|
145
|
-
ctx.functions.set(
|
|
146
|
-
ctx.variables.set(
|
|
342
|
+
ctx.functions.set(node.name, fn);
|
|
343
|
+
ctx.variables.set(node.name, fn);
|
|
147
344
|
return null;
|
|
148
345
|
}
|
|
149
|
-
async
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
-
|
|
159
|
-
|
|
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(
|
|
165
|
-
while (this.isTruthy(await this.
|
|
166
|
-
const result = await this.
|
|
167
|
-
if (result instanceof
|
|
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(
|
|
177
|
-
|
|
178
|
-
|
|
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
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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
|
|
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
|
-
|
|
227
|
-
|
|
228
|
-
const
|
|
229
|
-
if (
|
|
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(
|
|
239
|
-
const value =
|
|
240
|
-
return new
|
|
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(
|
|
496
|
+
async executeTryStatement(node, ctx) {
|
|
243
497
|
try {
|
|
244
|
-
return await this.executeBlock(
|
|
498
|
+
return await this.executeBlock(node.tryBlock, ctx);
|
|
245
499
|
}
|
|
246
|
-
catch (
|
|
247
|
-
|
|
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
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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(
|
|
264
|
-
const value = await this.
|
|
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
|
|
271
|
-
//
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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
|
|
286
|
-
|
|
549
|
+
async evaluate(node, ctx) {
|
|
550
|
+
node = this.normalizeNode(node);
|
|
551
|
+
switch (node.type) {
|
|
287
552
|
case 'Literal':
|
|
288
|
-
return
|
|
553
|
+
return node.value;
|
|
289
554
|
case 'Identifier':
|
|
290
|
-
return this.lookupVariable(
|
|
291
|
-
case '
|
|
292
|
-
return this.
|
|
293
|
-
case '
|
|
294
|
-
return this.
|
|
295
|
-
case '
|
|
296
|
-
return this.
|
|
297
|
-
case '
|
|
298
|
-
return this.
|
|
299
|
-
case '
|
|
300
|
-
return this.
|
|
301
|
-
case '
|
|
302
|
-
return this.
|
|
303
|
-
case '
|
|
304
|
-
return this.
|
|
305
|
-
case '
|
|
306
|
-
return this.
|
|
307
|
-
case '
|
|
308
|
-
return this.
|
|
309
|
-
case '
|
|
310
|
-
return this.
|
|
311
|
-
case '
|
|
312
|
-
return this.
|
|
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(`
|
|
582
|
+
throw new Error(`Unknown expression type: ${node.type}`);
|
|
315
583
|
}
|
|
316
584
|
}
|
|
317
585
|
lookupVariable(name, ctx) {
|
|
318
|
-
|
|
319
|
-
|
|
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
|
-
|
|
325
|
-
|
|
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
|
-
|
|
609
|
+
ctx.variables.set(name, value);
|
|
328
610
|
}
|
|
329
|
-
async
|
|
330
|
-
const left = await this.
|
|
331
|
-
const right = await this.
|
|
332
|
-
switch (
|
|
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
|
|
339
|
-
|
|
340
|
-
|
|
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
|
|
346
|
-
|
|
347
|
-
case
|
|
348
|
-
|
|
349
|
-
case
|
|
350
|
-
|
|
351
|
-
case
|
|
352
|
-
|
|
353
|
-
case
|
|
354
|
-
|
|
355
|
-
case
|
|
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(`
|
|
650
|
+
throw new Error(`Unknown binary operator: ${node.operator}`);
|
|
358
651
|
}
|
|
359
652
|
}
|
|
360
|
-
async
|
|
361
|
-
const
|
|
362
|
-
switch (
|
|
363
|
-
case
|
|
364
|
-
|
|
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(`
|
|
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
|
|
383
|
-
let value = await this.
|
|
384
|
-
if (
|
|
385
|
-
const current = await this.
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
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
|
-
|
|
394
|
-
|
|
395
|
-
value = current - value;
|
|
688
|
+
if (node.target.type === 'Identifier') {
|
|
689
|
+
this.setVariable(node.target.name, value, ctx);
|
|
396
690
|
}
|
|
397
|
-
if (
|
|
398
|
-
|
|
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 (
|
|
401
|
-
const
|
|
402
|
-
const object = await this.
|
|
403
|
-
const
|
|
404
|
-
|
|
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
|
-
|
|
411
|
-
|
|
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(
|
|
707
|
+
throw new Error(`${JSON.stringify(callee)} is not a function`);
|
|
427
708
|
}
|
|
428
709
|
const args = [];
|
|
429
|
-
for (const arg of
|
|
430
|
-
args.push(await this.
|
|
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
|
-
|
|
721
|
+
const result = callee(...args);
|
|
722
|
+
return result instanceof Promise ? await result : result;
|
|
433
723
|
}
|
|
434
|
-
async
|
|
435
|
-
const object = await this.
|
|
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
|
|
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[
|
|
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
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
|
|
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
|
|
742
|
+
return object[index];
|
|
456
743
|
}
|
|
457
|
-
async
|
|
458
|
-
const
|
|
459
|
-
for (const
|
|
460
|
-
|
|
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
|
|
749
|
+
return elements;
|
|
466
750
|
}
|
|
467
|
-
async
|
|
468
|
-
const
|
|
469
|
-
|
|
470
|
-
|
|
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
|
|
756
|
+
return dict;
|
|
473
757
|
}
|
|
474
|
-
|
|
475
|
-
const arrow = expr;
|
|
758
|
+
evaluateLambda(node, ctx) {
|
|
476
759
|
return async (...args) => {
|
|
477
760
|
const localCtx = {
|
|
478
|
-
variables: new Map(
|
|
761
|
+
variables: new Map(),
|
|
479
762
|
functions: ctx.functions,
|
|
480
763
|
parent: ctx,
|
|
481
764
|
};
|
|
482
|
-
for (let i = 0; i <
|
|
483
|
-
localCtx.variables.set(
|
|
765
|
+
for (let i = 0; i < node.parameters.length; i++) {
|
|
766
|
+
localCtx.variables.set(node.parameters[i], args[i]);
|
|
484
767
|
}
|
|
485
|
-
if (
|
|
486
|
-
const result = await this.executeBlock(
|
|
487
|
-
if (result instanceof
|
|
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.
|
|
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
|