@vibe-lang/runtime 0.2.5
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/package.json +46 -0
- package/src/ast/index.ts +375 -0
- package/src/ast.ts +2 -0
- package/src/debug/advanced-features.ts +482 -0
- package/src/debug/bun-inspector.ts +424 -0
- package/src/debug/handoff-manager.ts +283 -0
- package/src/debug/index.ts +150 -0
- package/src/debug/runner.ts +365 -0
- package/src/debug/server.ts +565 -0
- package/src/debug/stack-merger.ts +267 -0
- package/src/debug/state.ts +581 -0
- package/src/debug/test/advanced-features.test.ts +300 -0
- package/src/debug/test/e2e.test.ts +218 -0
- package/src/debug/test/handoff-manager.test.ts +256 -0
- package/src/debug/test/runner.test.ts +256 -0
- package/src/debug/test/stack-merger.test.ts +163 -0
- package/src/debug/test/state.test.ts +400 -0
- package/src/debug/test/ts-debug-integration.test.ts +374 -0
- package/src/debug/test/ts-import-tracker.test.ts +125 -0
- package/src/debug/test/ts-source-map.test.ts +169 -0
- package/src/debug/ts-import-tracker.ts +151 -0
- package/src/debug/ts-source-map.ts +171 -0
- package/src/errors/index.ts +124 -0
- package/src/index.ts +358 -0
- package/src/lexer/index.ts +348 -0
- package/src/lexer.ts +2 -0
- package/src/parser/index.ts +792 -0
- package/src/parser/parse.ts +45 -0
- package/src/parser/test/async.test.ts +248 -0
- package/src/parser/test/destructuring.test.ts +167 -0
- package/src/parser/test/do-expression.test.ts +486 -0
- package/src/parser/test/errors/do-expression.test.ts +95 -0
- package/src/parser/test/errors/error-locations.test.ts +230 -0
- package/src/parser/test/errors/invalid-expressions.test.ts +144 -0
- package/src/parser/test/errors/missing-tokens.test.ts +126 -0
- package/src/parser/test/errors/model-declaration.test.ts +185 -0
- package/src/parser/test/errors/nested-blocks.test.ts +226 -0
- package/src/parser/test/errors/unclosed-delimiters.test.ts +122 -0
- package/src/parser/test/errors/unexpected-tokens.test.ts +120 -0
- package/src/parser/test/import-export.test.ts +143 -0
- package/src/parser/test/literals.test.ts +404 -0
- package/src/parser/test/model-declaration.test.ts +161 -0
- package/src/parser/test/nested-blocks.test.ts +402 -0
- package/src/parser/test/parser.test.ts +743 -0
- package/src/parser/test/private.test.ts +136 -0
- package/src/parser/test/template-literal.test.ts +127 -0
- package/src/parser/test/tool-declaration.test.ts +302 -0
- package/src/parser/test/ts-block.test.ts +252 -0
- package/src/parser/test/type-annotations.test.ts +254 -0
- package/src/parser/visitor/helpers.ts +330 -0
- package/src/parser/visitor.ts +794 -0
- package/src/parser.ts +2 -0
- package/src/runtime/ai/cache-chunking.test.ts +69 -0
- package/src/runtime/ai/cache-chunking.ts +73 -0
- package/src/runtime/ai/client.ts +109 -0
- package/src/runtime/ai/context.ts +168 -0
- package/src/runtime/ai/formatters.ts +316 -0
- package/src/runtime/ai/index.ts +38 -0
- package/src/runtime/ai/language-ref.ts +38 -0
- package/src/runtime/ai/providers/anthropic.ts +253 -0
- package/src/runtime/ai/providers/google.ts +201 -0
- package/src/runtime/ai/providers/openai.ts +156 -0
- package/src/runtime/ai/retry.ts +100 -0
- package/src/runtime/ai/return-tools.ts +301 -0
- package/src/runtime/ai/test/client.test.ts +83 -0
- package/src/runtime/ai/test/formatters.test.ts +485 -0
- package/src/runtime/ai/test/retry.test.ts +137 -0
- package/src/runtime/ai/test/return-tools.test.ts +450 -0
- package/src/runtime/ai/test/tool-loop.test.ts +319 -0
- package/src/runtime/ai/test/tool-schema.test.ts +241 -0
- package/src/runtime/ai/tool-loop.ts +203 -0
- package/src/runtime/ai/tool-schema.ts +151 -0
- package/src/runtime/ai/types.ts +113 -0
- package/src/runtime/ai-logger.ts +255 -0
- package/src/runtime/ai-provider.ts +347 -0
- package/src/runtime/async/dependencies.ts +276 -0
- package/src/runtime/async/executor.ts +293 -0
- package/src/runtime/async/index.ts +43 -0
- package/src/runtime/async/scheduling.ts +163 -0
- package/src/runtime/async/test/dependencies.test.ts +284 -0
- package/src/runtime/async/test/executor.test.ts +388 -0
- package/src/runtime/context.ts +357 -0
- package/src/runtime/exec/ai.ts +139 -0
- package/src/runtime/exec/expressions.ts +475 -0
- package/src/runtime/exec/frames.ts +26 -0
- package/src/runtime/exec/functions.ts +305 -0
- package/src/runtime/exec/interpolation.ts +312 -0
- package/src/runtime/exec/statements.ts +604 -0
- package/src/runtime/exec/tools.ts +129 -0
- package/src/runtime/exec/typescript.ts +215 -0
- package/src/runtime/exec/variables.ts +279 -0
- package/src/runtime/index.ts +975 -0
- package/src/runtime/modules.ts +452 -0
- package/src/runtime/serialize.ts +103 -0
- package/src/runtime/state.ts +489 -0
- package/src/runtime/stdlib/core.ts +45 -0
- package/src/runtime/stdlib/directory.test.ts +156 -0
- package/src/runtime/stdlib/edit.test.ts +154 -0
- package/src/runtime/stdlib/fastEdit.test.ts +201 -0
- package/src/runtime/stdlib/glob.test.ts +106 -0
- package/src/runtime/stdlib/grep.test.ts +144 -0
- package/src/runtime/stdlib/index.ts +16 -0
- package/src/runtime/stdlib/readFile.test.ts +123 -0
- package/src/runtime/stdlib/tools/index.ts +707 -0
- package/src/runtime/stdlib/writeFile.test.ts +157 -0
- package/src/runtime/step.ts +969 -0
- package/src/runtime/test/ai-context.test.ts +1086 -0
- package/src/runtime/test/ai-result-object.test.ts +419 -0
- package/src/runtime/test/ai-tool-flow.test.ts +859 -0
- package/src/runtime/test/async-execution-order.test.ts +618 -0
- package/src/runtime/test/async-execution.test.ts +344 -0
- package/src/runtime/test/async-nested.test.ts +660 -0
- package/src/runtime/test/async-parallel-timing.test.ts +546 -0
- package/src/runtime/test/basic1.test.ts +154 -0
- package/src/runtime/test/binary-operators.test.ts +431 -0
- package/src/runtime/test/break-statement.test.ts +257 -0
- package/src/runtime/test/context-modes.test.ts +650 -0
- package/src/runtime/test/context.test.ts +466 -0
- package/src/runtime/test/core-functions.test.ts +228 -0
- package/src/runtime/test/e2e.test.ts +88 -0
- package/src/runtime/test/error-locations/error-locations.test.ts +80 -0
- package/src/runtime/test/error-locations/main-error.vibe +4 -0
- package/src/runtime/test/error-locations/main-import-error.vibe +3 -0
- package/src/runtime/test/error-locations/utils/helper.vibe +5 -0
- package/src/runtime/test/for-in.test.ts +312 -0
- package/src/runtime/test/helpers.ts +69 -0
- package/src/runtime/test/imports.test.ts +334 -0
- package/src/runtime/test/json-expressions.test.ts +232 -0
- package/src/runtime/test/literals.test.ts +372 -0
- package/src/runtime/test/logical-indexing.test.ts +478 -0
- package/src/runtime/test/member-methods.test.ts +324 -0
- package/src/runtime/test/model-config.test.ts +338 -0
- package/src/runtime/test/null-handling.test.ts +342 -0
- package/src/runtime/test/private-visibility.test.ts +332 -0
- package/src/runtime/test/runtime-state.test.ts +514 -0
- package/src/runtime/test/scoping.test.ts +370 -0
- package/src/runtime/test/string-interpolation.test.ts +354 -0
- package/src/runtime/test/template-literal.test.ts +181 -0
- package/src/runtime/test/tool-execution.test.ts +467 -0
- package/src/runtime/test/tool-schema-generation.test.ts +477 -0
- package/src/runtime/test/tostring.test.ts +210 -0
- package/src/runtime/test/ts-block.test.ts +594 -0
- package/src/runtime/test/ts-error-location.test.ts +231 -0
- package/src/runtime/test/types.test.ts +732 -0
- package/src/runtime/test/verbose-logger.test.ts +710 -0
- package/src/runtime/test/vibe-expression.test.ts +54 -0
- package/src/runtime/test/vibe-value-errors.test.ts +541 -0
- package/src/runtime/test/while.test.ts +232 -0
- package/src/runtime/tools/builtin.ts +30 -0
- package/src/runtime/tools/directory-tools.ts +70 -0
- package/src/runtime/tools/file-tools.ts +228 -0
- package/src/runtime/tools/index.ts +5 -0
- package/src/runtime/tools/registry.ts +48 -0
- package/src/runtime/tools/search-tools.ts +134 -0
- package/src/runtime/tools/security.ts +36 -0
- package/src/runtime/tools/system-tools.ts +312 -0
- package/src/runtime/tools/test/fixtures/base-types.ts +40 -0
- package/src/runtime/tools/test/fixtures/test-types.ts +132 -0
- package/src/runtime/tools/test/registry.test.ts +713 -0
- package/src/runtime/tools/test/security.test.ts +86 -0
- package/src/runtime/tools/test/system-tools.test.ts +679 -0
- package/src/runtime/tools/test/ts-schema.test.ts +357 -0
- package/src/runtime/tools/ts-schema.ts +341 -0
- package/src/runtime/tools/types.ts +89 -0
- package/src/runtime/tools/utility-tools.ts +198 -0
- package/src/runtime/ts-eval.ts +126 -0
- package/src/runtime/types.ts +797 -0
- package/src/runtime/validation.ts +160 -0
- package/src/runtime/verbose-logger.ts +459 -0
- package/src/runtime.ts +2 -0
- package/src/semantic/analyzer-context.ts +62 -0
- package/src/semantic/analyzer-validators.ts +575 -0
- package/src/semantic/analyzer-visitors.ts +534 -0
- package/src/semantic/analyzer.ts +83 -0
- package/src/semantic/index.ts +11 -0
- package/src/semantic/symbol-table.ts +58 -0
- package/src/semantic/test/async-validation.test.ts +301 -0
- package/src/semantic/test/compress-validation.test.ts +179 -0
- package/src/semantic/test/const-reassignment.test.ts +111 -0
- package/src/semantic/test/control-flow.test.ts +346 -0
- package/src/semantic/test/destructuring.test.ts +185 -0
- package/src/semantic/test/duplicate-declarations.test.ts +168 -0
- package/src/semantic/test/export-validation.test.ts +111 -0
- package/src/semantic/test/fixtures/math.ts +31 -0
- package/src/semantic/test/imports.test.ts +148 -0
- package/src/semantic/test/json-type.test.ts +68 -0
- package/src/semantic/test/literals.test.ts +127 -0
- package/src/semantic/test/model-validation.test.ts +179 -0
- package/src/semantic/test/prompt-validation.test.ts +343 -0
- package/src/semantic/test/scoping.test.ts +312 -0
- package/src/semantic/test/tool-validation.test.ts +306 -0
- package/src/semantic/test/ts-type-checking.test.ts +563 -0
- package/src/semantic/test/type-constraints.test.ts +111 -0
- package/src/semantic/test/type-inference.test.ts +87 -0
- package/src/semantic/test/type-validation.test.ts +552 -0
- package/src/semantic/test/undefined-variables.test.ts +163 -0
- package/src/semantic/ts-block-checker.ts +204 -0
- package/src/semantic/ts-signatures.ts +194 -0
- package/src/semantic/ts-types.ts +170 -0
- package/src/semantic/types.ts +58 -0
- package/tests/fixtures/conditional-logic.vibe +14 -0
- package/tests/fixtures/function-call.vibe +16 -0
- package/tests/fixtures/imports/cycle-detection/a.vibe +6 -0
- package/tests/fixtures/imports/cycle-detection/b.vibe +5 -0
- package/tests/fixtures/imports/cycle-detection/main.vibe +3 -0
- package/tests/fixtures/imports/module-isolation/main-b.vibe +8 -0
- package/tests/fixtures/imports/module-isolation/main.vibe +9 -0
- package/tests/fixtures/imports/module-isolation/moduleA.vibe +6 -0
- package/tests/fixtures/imports/module-isolation/moduleB.vibe +6 -0
- package/tests/fixtures/imports/nested-import/helper.vibe +6 -0
- package/tests/fixtures/imports/nested-import/main.vibe +3 -0
- package/tests/fixtures/imports/nested-import/utils.ts +3 -0
- package/tests/fixtures/imports/nested-isolation/file2.vibe +15 -0
- package/tests/fixtures/imports/nested-isolation/file3.vibe +10 -0
- package/tests/fixtures/imports/nested-isolation/main.vibe +21 -0
- package/tests/fixtures/imports/pure-cycle/a.vibe +5 -0
- package/tests/fixtures/imports/pure-cycle/b.vibe +5 -0
- package/tests/fixtures/imports/pure-cycle/main.vibe +3 -0
- package/tests/fixtures/imports/ts-boolean/checks.ts +14 -0
- package/tests/fixtures/imports/ts-boolean/main.vibe +10 -0
- package/tests/fixtures/imports/ts-boolean/type-mismatch.vibe +5 -0
- package/tests/fixtures/imports/ts-boolean/use-constant.vibe +18 -0
- package/tests/fixtures/imports/ts-error-handling/helpers.ts +42 -0
- package/tests/fixtures/imports/ts-error-handling/main.vibe +5 -0
- package/tests/fixtures/imports/ts-import/main.vibe +4 -0
- package/tests/fixtures/imports/ts-import/math.ts +9 -0
- package/tests/fixtures/imports/ts-variables/call-non-function.vibe +5 -0
- package/tests/fixtures/imports/ts-variables/data.ts +10 -0
- package/tests/fixtures/imports/ts-variables/import-json.vibe +5 -0
- package/tests/fixtures/imports/ts-variables/import-type-mismatch.vibe +5 -0
- package/tests/fixtures/imports/ts-variables/import-variable.vibe +5 -0
- package/tests/fixtures/imports/vibe-import/greet.vibe +5 -0
- package/tests/fixtures/imports/vibe-import/main.vibe +3 -0
- package/tests/fixtures/multiple-ai-calls.vibe +10 -0
- package/tests/fixtures/simple-greeting.vibe +6 -0
- package/tests/fixtures/template-literals.vibe +11 -0
- package/tests/integration/basic-ai/basic-ai.integration.test.ts +166 -0
- package/tests/integration/basic-ai/basic-ai.vibe +12 -0
- package/tests/integration/bug-fix/bug-fix.integration.test.ts +201 -0
- package/tests/integration/bug-fix/buggy-code.ts +22 -0
- package/tests/integration/bug-fix/fix-bug.vibe +21 -0
- package/tests/integration/compress/compress.integration.test.ts +206 -0
- package/tests/integration/destructuring/destructuring.integration.test.ts +92 -0
- package/tests/integration/hello-world-translator/hello-world-translator.integration.test.ts +61 -0
- package/tests/integration/line-annotator/context-modes.integration.test.ts +261 -0
- package/tests/integration/line-annotator/line-annotator.integration.test.ts +148 -0
- package/tests/integration/multi-feature/cumulative-sum.integration.test.ts +75 -0
- package/tests/integration/multi-feature/number-analyzer.integration.test.ts +191 -0
- package/tests/integration/multi-feature/number-analyzer.vibe +59 -0
- package/tests/integration/tool-calls/tool-calls.integration.test.ts +93 -0
|
@@ -0,0 +1,604 @@
|
|
|
1
|
+
// Statement execution helpers: declarations, control flow
|
|
2
|
+
|
|
3
|
+
import * as AST from '../../ast';
|
|
4
|
+
import type { SourceLocation } from '../../errors';
|
|
5
|
+
import type { RuntimeState, VibeValue } from '../types';
|
|
6
|
+
import { createVibeValue, resolveValue, isVibeValue } from '../types';
|
|
7
|
+
import { currentFrame } from '../state';
|
|
8
|
+
import { requireBoolean, validateAndCoerce } from '../validation';
|
|
9
|
+
import { execDeclareVar } from './variables';
|
|
10
|
+
import { getImportedVibeFunction } from '../modules';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Let declaration - push instructions for initializer.
|
|
14
|
+
* For async declarations, sets context flag so AI/TS handlers use non-blocking mode.
|
|
15
|
+
* For prompt-typed variables, sets inPromptContext for string interpolation semantics.
|
|
16
|
+
*/
|
|
17
|
+
export function execLetDeclaration(state: RuntimeState, stmt: AST.LetDeclaration): RuntimeState {
|
|
18
|
+
if (stmt.initializer) {
|
|
19
|
+
// For async declarations, set context flag before evaluating
|
|
20
|
+
let baseState = stmt.isAsync ? {
|
|
21
|
+
...state,
|
|
22
|
+
currentAsyncVarName: stmt.name,
|
|
23
|
+
currentAsyncIsConst: false,
|
|
24
|
+
currentAsyncType: stmt.typeAnnotation,
|
|
25
|
+
currentAsyncIsPrivate: stmt.isPrivate ?? false,
|
|
26
|
+
} : state;
|
|
27
|
+
|
|
28
|
+
// For prompt-typed variables, set inPromptContext for string interpolation
|
|
29
|
+
const isPromptType = stmt.typeAnnotation === 'prompt';
|
|
30
|
+
if (isPromptType) {
|
|
31
|
+
baseState = { ...baseState, inPromptContext: true };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Build instruction stack with optional clear_prompt_context
|
|
35
|
+
const instructions: RuntimeState['instructionStack'] = [
|
|
36
|
+
{ op: 'exec_expression', expr: stmt.initializer, location: stmt.initializer.location },
|
|
37
|
+
];
|
|
38
|
+
if (isPromptType) {
|
|
39
|
+
instructions.push({ op: 'clear_prompt_context', location: stmt.location });
|
|
40
|
+
}
|
|
41
|
+
instructions.push(
|
|
42
|
+
{ op: 'declare_var', name: stmt.name, isConst: false, type: stmt.typeAnnotation, isPrivate: stmt.isPrivate, location: stmt.location },
|
|
43
|
+
...state.instructionStack
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
...baseState,
|
|
48
|
+
instructionStack: instructions,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// No initializer, declare with null
|
|
53
|
+
return execDeclareVar(state, stmt.name, false, stmt.typeAnnotation, null, stmt.isPrivate);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Const declaration - push instructions for initializer.
|
|
58
|
+
* For async declarations, sets context flag so AI/TS handlers use non-blocking mode.
|
|
59
|
+
* For prompt-typed variables, sets inPromptContext for string interpolation semantics.
|
|
60
|
+
*/
|
|
61
|
+
export function execConstDeclaration(state: RuntimeState, stmt: AST.ConstDeclaration): RuntimeState {
|
|
62
|
+
// For async declarations, set context flag before evaluating
|
|
63
|
+
let baseState = stmt.isAsync ? {
|
|
64
|
+
...state,
|
|
65
|
+
currentAsyncVarName: stmt.name,
|
|
66
|
+
currentAsyncIsConst: true,
|
|
67
|
+
currentAsyncType: stmt.typeAnnotation,
|
|
68
|
+
currentAsyncIsPrivate: stmt.isPrivate ?? false,
|
|
69
|
+
} : state;
|
|
70
|
+
|
|
71
|
+
// For prompt-typed variables, set inPromptContext for string interpolation
|
|
72
|
+
const isPromptType = stmt.typeAnnotation === 'prompt';
|
|
73
|
+
if (isPromptType) {
|
|
74
|
+
baseState = { ...baseState, inPromptContext: true };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Build instruction stack with optional clear_prompt_context
|
|
78
|
+
const instructions: RuntimeState['instructionStack'] = [
|
|
79
|
+
{ op: 'exec_expression', expr: stmt.initializer, location: stmt.initializer.location },
|
|
80
|
+
];
|
|
81
|
+
if (isPromptType) {
|
|
82
|
+
instructions.push({ op: 'clear_prompt_context', location: stmt.location });
|
|
83
|
+
}
|
|
84
|
+
instructions.push(
|
|
85
|
+
{ op: 'declare_var', name: stmt.name, isConst: true, type: stmt.typeAnnotation, isPrivate: stmt.isPrivate, location: stmt.location },
|
|
86
|
+
...state.instructionStack
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
...baseState,
|
|
91
|
+
instructionStack: instructions,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Destructuring declaration - evaluate initializer (AI expression) and assign fields.
|
|
97
|
+
* const {name: text, age: number} = do "..." model default
|
|
98
|
+
* For async declarations, sets context flag so AI/TS handlers use non-blocking mode.
|
|
99
|
+
* For destructuring, currentAsyncIsDestructure=true tells the async system to NOT update
|
|
100
|
+
* any variable on completion - the actual variables are created by destructure_assign.
|
|
101
|
+
*/
|
|
102
|
+
export function execDestructuringDeclaration(
|
|
103
|
+
state: RuntimeState,
|
|
104
|
+
stmt: AST.DestructuringDeclaration
|
|
105
|
+
): RuntimeState {
|
|
106
|
+
// Convert AST fields to ExpectedField format for runtime (including isPrivate)
|
|
107
|
+
const expectedFields = stmt.fields.map((f) => ({
|
|
108
|
+
name: f.name,
|
|
109
|
+
type: f.type,
|
|
110
|
+
...(f.isPrivate ? { isPrivate: true } : {}),
|
|
111
|
+
}));
|
|
112
|
+
|
|
113
|
+
// For async declarations, set isDestructure flag to enable async path
|
|
114
|
+
// variableName stays null - destructure_assign creates the actual variables
|
|
115
|
+
const baseState = stmt.isAsync ? {
|
|
116
|
+
...state,
|
|
117
|
+
currentAsyncVarName: null, // No single variable - destructure_assign handles it
|
|
118
|
+
currentAsyncIsConst: stmt.isConst,
|
|
119
|
+
currentAsyncType: null, // Destructuring doesn't have single type
|
|
120
|
+
currentAsyncIsPrivate: false,
|
|
121
|
+
currentAsyncIsDestructure: true, // Signals async path without variable tracking
|
|
122
|
+
} : state;
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
...baseState,
|
|
126
|
+
// Set pendingDestructuring so AI provider knows what fields to expect
|
|
127
|
+
pendingDestructuring: expectedFields,
|
|
128
|
+
instructionStack: [
|
|
129
|
+
{ op: 'exec_expression', expr: stmt.initializer, location: stmt.initializer.location },
|
|
130
|
+
{ op: 'destructure_assign', fields: expectedFields, isConst: stmt.isConst, location: stmt.location },
|
|
131
|
+
...state.instructionStack,
|
|
132
|
+
],
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Model config fields in evaluation order
|
|
137
|
+
const MODEL_CONFIG_FIELDS = ['modelName', 'apiKey', 'url', 'provider', 'maxRetriesOnError', 'thinkingLevel', 'tools'] as const;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Model declaration - evaluate all config expressions through instruction stack.
|
|
141
|
+
* This allows CallExpressions (like env(), ts blocks, function calls) to work in model config.
|
|
142
|
+
*/
|
|
143
|
+
export function execModelDeclaration(state: RuntimeState, stmt: AST.ModelDeclaration): RuntimeState {
|
|
144
|
+
const instructions: typeof state.instructionStack = [];
|
|
145
|
+
|
|
146
|
+
// Push evaluation instructions for each config field
|
|
147
|
+
// Fields are evaluated in order and pushed to valueStack
|
|
148
|
+
for (const field of MODEL_CONFIG_FIELDS) {
|
|
149
|
+
const expr = stmt.config[field];
|
|
150
|
+
if (expr) {
|
|
151
|
+
instructions.push({ op: 'exec_expression', expr, location: expr.location });
|
|
152
|
+
} else {
|
|
153
|
+
// Use undefined for missing fields to preserve backward compatibility
|
|
154
|
+
instructions.push({ op: 'literal', value: undefined, location: stmt.location });
|
|
155
|
+
}
|
|
156
|
+
instructions.push({ op: 'push_value', location: stmt.location });
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Finally, declare the model (will pop values from stack)
|
|
160
|
+
instructions.push({ op: 'declare_model', stmt, location: stmt.location });
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
...state,
|
|
164
|
+
instructionStack: [...instructions, ...state.instructionStack],
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Finalize model declaration by popping evaluated config values from valueStack.
|
|
170
|
+
*/
|
|
171
|
+
export function finalizeModelDeclaration(
|
|
172
|
+
state: RuntimeState,
|
|
173
|
+
stmt: AST.ModelDeclaration
|
|
174
|
+
): RuntimeState {
|
|
175
|
+
// Pop values from stack in reverse order (LIFO)
|
|
176
|
+
// Fields were pushed in order: modelName, apiKey, url, provider, maxRetriesOnError, thinkingLevel, tools
|
|
177
|
+
const fieldCount = MODEL_CONFIG_FIELDS.length;
|
|
178
|
+
const rawValues = state.valueStack.slice(-fieldCount);
|
|
179
|
+
const newValueStack = state.valueStack.slice(0, -fieldCount);
|
|
180
|
+
|
|
181
|
+
// Unwrap VibeValues to get raw values
|
|
182
|
+
const [modelName, apiKey, url, provider, maxRetriesOnError, thinkingLevel, tools] = rawValues.map(
|
|
183
|
+
v => resolveValue(v)
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
const modelValue = {
|
|
187
|
+
__vibeModel: true,
|
|
188
|
+
name: modelName as string | null,
|
|
189
|
+
apiKey: apiKey as string | null,
|
|
190
|
+
url: url as string | null,
|
|
191
|
+
provider: provider as string | null,
|
|
192
|
+
maxRetriesOnError: maxRetriesOnError as number | null,
|
|
193
|
+
thinkingLevel: thinkingLevel as string | null,
|
|
194
|
+
tools: tools as unknown[] | undefined,
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const frame = currentFrame(state);
|
|
198
|
+
const newLocals = {
|
|
199
|
+
...frame.locals,
|
|
200
|
+
[stmt.name]: createVibeValue(modelValue, { isConst: true, typeAnnotation: null }),
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
...state,
|
|
205
|
+
valueStack: newValueStack,
|
|
206
|
+
// Set lastUsedModel if not already set (first model declaration)
|
|
207
|
+
lastUsedModel: state.lastUsedModel ?? stmt.name,
|
|
208
|
+
callStack: [
|
|
209
|
+
...state.callStack.slice(0, -1),
|
|
210
|
+
{ ...frame, locals: newLocals },
|
|
211
|
+
],
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* If statement - push condition and branch instruction.
|
|
217
|
+
*/
|
|
218
|
+
export function execIfStatement(state: RuntimeState, stmt: AST.IfStatement): RuntimeState {
|
|
219
|
+
return {
|
|
220
|
+
...state,
|
|
221
|
+
instructionStack: [
|
|
222
|
+
{ op: 'exec_expression', expr: stmt.condition, location: stmt.condition.location },
|
|
223
|
+
{ op: 'if_branch', consequent: stmt.consequent, alternate: stmt.alternate, location: stmt.location },
|
|
224
|
+
...state.instructionStack,
|
|
225
|
+
],
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* For-in statement - push iterable evaluation and for_in_init.
|
|
231
|
+
*/
|
|
232
|
+
export function execForInStatement(state: RuntimeState, stmt: AST.ForInStatement): RuntimeState {
|
|
233
|
+
return {
|
|
234
|
+
...state,
|
|
235
|
+
instructionStack: [
|
|
236
|
+
{ op: 'exec_expression', expr: stmt.iterable, location: stmt.iterable.location },
|
|
237
|
+
{ op: 'for_in_init', stmt, location: stmt.location },
|
|
238
|
+
...state.instructionStack,
|
|
239
|
+
],
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* While statement - evaluate condition and loop.
|
|
245
|
+
*/
|
|
246
|
+
export function execWhileStatement(state: RuntimeState, stmt: AST.WhileStatement): RuntimeState {
|
|
247
|
+
const frame = currentFrame(state);
|
|
248
|
+
const savedKeys = Object.keys(frame.locals);
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
...state,
|
|
252
|
+
instructionStack: [
|
|
253
|
+
{ op: 'exec_expression', expr: stmt.condition, location: stmt.condition.location },
|
|
254
|
+
{ op: 'while_init', stmt, savedKeys, location: stmt.location },
|
|
255
|
+
...state.instructionStack,
|
|
256
|
+
],
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* If branch - decide based on lastResult.
|
|
262
|
+
*/
|
|
263
|
+
export function execIfBranch(
|
|
264
|
+
state: RuntimeState,
|
|
265
|
+
consequent: AST.BlockStatement,
|
|
266
|
+
alternate?: AST.Statement | null
|
|
267
|
+
): RuntimeState {
|
|
268
|
+
const condition = state.lastResult;
|
|
269
|
+
|
|
270
|
+
if (requireBoolean(condition, 'if condition')) {
|
|
271
|
+
return {
|
|
272
|
+
...state,
|
|
273
|
+
instructionStack: [
|
|
274
|
+
{ op: 'exec_statement', stmt: consequent, location: consequent.location },
|
|
275
|
+
...state.instructionStack,
|
|
276
|
+
],
|
|
277
|
+
};
|
|
278
|
+
} else if (alternate) {
|
|
279
|
+
return {
|
|
280
|
+
...state,
|
|
281
|
+
instructionStack: [
|
|
282
|
+
{ op: 'exec_statement', stmt: alternate, location: alternate.location },
|
|
283
|
+
...state.instructionStack,
|
|
284
|
+
],
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return state;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Block statement - push statements with exit_block cleanup.
|
|
293
|
+
*/
|
|
294
|
+
export function execBlockStatement(state: RuntimeState, stmt: AST.BlockStatement): RuntimeState {
|
|
295
|
+
const frame = currentFrame(state);
|
|
296
|
+
const savedKeys = Object.keys(frame.locals);
|
|
297
|
+
|
|
298
|
+
// Push statements in order (we pop from front, so first statement first)
|
|
299
|
+
const stmtInstructions = stmt.body
|
|
300
|
+
.map((s) => ({ op: 'exec_statement' as const, stmt: s, location: s.location }));
|
|
301
|
+
|
|
302
|
+
return {
|
|
303
|
+
...state,
|
|
304
|
+
instructionStack: [
|
|
305
|
+
...stmtInstructions,
|
|
306
|
+
{ op: 'exit_block', savedKeys, location: stmt.location },
|
|
307
|
+
...state.instructionStack,
|
|
308
|
+
],
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Enter block scope (placeholder for symmetry).
|
|
314
|
+
*/
|
|
315
|
+
export function execEnterBlock(state: RuntimeState, _savedKeys: string[]): RuntimeState {
|
|
316
|
+
return state;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Exit block scope - remove variables declared in block.
|
|
321
|
+
* Before removing, await any pending async operations for those variables.
|
|
322
|
+
*/
|
|
323
|
+
export function execExitBlock(state: RuntimeState, savedKeys: string[], location?: SourceLocation): RuntimeState {
|
|
324
|
+
const frame = currentFrame(state);
|
|
325
|
+
const savedKeySet = new Set(savedKeys);
|
|
326
|
+
|
|
327
|
+
// Await ALL pending async operations at block boundaries
|
|
328
|
+
// This ensures operations started in loops are properly awaited even if their
|
|
329
|
+
// variables are overwritten by subsequent iterations
|
|
330
|
+
const pendingAsyncIds: string[] = [];
|
|
331
|
+
for (const opId of state.pendingAsyncIds) {
|
|
332
|
+
const operation = state.asyncOperations.get(opId);
|
|
333
|
+
if (operation && (operation.status === 'pending' || operation.status === 'running')) {
|
|
334
|
+
pendingAsyncIds.push(opId);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// If there are pending async operations, await them before cleaning up
|
|
339
|
+
if (pendingAsyncIds.length > 0) {
|
|
340
|
+
return {
|
|
341
|
+
...state,
|
|
342
|
+
status: 'awaiting_async',
|
|
343
|
+
awaitingAsyncIds: pendingAsyncIds,
|
|
344
|
+
instructionStack: [
|
|
345
|
+
{ op: 'exit_block', savedKeys, location: location ?? { line: 0, column: 0 } },
|
|
346
|
+
...state.instructionStack,
|
|
347
|
+
],
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// No pending async - proceed with cleanup
|
|
352
|
+
const newLocals: Record<string, VibeValue> = {};
|
|
353
|
+
for (const key of Object.keys(frame.locals)) {
|
|
354
|
+
if (savedKeySet.has(key)) {
|
|
355
|
+
newLocals[key] = frame.locals[key];
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return {
|
|
360
|
+
...state,
|
|
361
|
+
callStack: [
|
|
362
|
+
...state.callStack.slice(0, -1),
|
|
363
|
+
{ ...frame, locals: newLocals },
|
|
364
|
+
],
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Return statement - evaluate value and return.
|
|
370
|
+
*/
|
|
371
|
+
export function execReturnStatement(state: RuntimeState, stmt: AST.ReturnStatement): RuntimeState {
|
|
372
|
+
if (stmt.value) {
|
|
373
|
+
return {
|
|
374
|
+
...state,
|
|
375
|
+
instructionStack: [
|
|
376
|
+
{ op: 'exec_expression', expr: stmt.value, location: stmt.value.location },
|
|
377
|
+
{ op: 'return_value', location: stmt.location },
|
|
378
|
+
...state.instructionStack,
|
|
379
|
+
],
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return execReturnValue({ ...state, lastResult: null });
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Return value - pop frame and skip to after pop_frame instruction.
|
|
388
|
+
*/
|
|
389
|
+
export function execReturnValue(state: RuntimeState): RuntimeState {
|
|
390
|
+
const currentFrameRef = state.callStack[state.callStack.length - 1];
|
|
391
|
+
const funcName = currentFrameRef?.name;
|
|
392
|
+
|
|
393
|
+
// Check if return value is a pending async operation - need to await it first
|
|
394
|
+
const returnValue = state.lastResult;
|
|
395
|
+
if (isVibeValue(returnValue) && returnValue.asyncOperationId) {
|
|
396
|
+
const opId = returnValue.asyncOperationId;
|
|
397
|
+
const operation = state.asyncOperations.get(opId);
|
|
398
|
+
if (operation && (operation.status === 'pending' || operation.status === 'running')) {
|
|
399
|
+
// Need to await this operation before returning
|
|
400
|
+
return {
|
|
401
|
+
...state,
|
|
402
|
+
status: 'awaiting_async',
|
|
403
|
+
awaitingAsyncIds: [opId],
|
|
404
|
+
// Re-queue the return_value instruction to run after await completes
|
|
405
|
+
instructionStack: [
|
|
406
|
+
{ op: 'return_value', location: { line: 0, column: 0 } },
|
|
407
|
+
...state.instructionStack,
|
|
408
|
+
],
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Validate return type if function has one
|
|
414
|
+
let validatedReturnValue = returnValue;
|
|
415
|
+
if (funcName && funcName !== 'main') {
|
|
416
|
+
const func = state.functions[funcName] ?? getImportedVibeFunction(state, funcName);
|
|
417
|
+
if (func?.returnType) {
|
|
418
|
+
const { value: validatedValue } = validateAndCoerce(
|
|
419
|
+
validatedReturnValue,
|
|
420
|
+
func.returnType,
|
|
421
|
+
`return value of ${funcName}`
|
|
422
|
+
);
|
|
423
|
+
validatedReturnValue = validatedValue;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Pop frame
|
|
428
|
+
const newCallStack = state.callStack.slice(0, -1);
|
|
429
|
+
|
|
430
|
+
if (newCallStack.length === 0) {
|
|
431
|
+
return { ...state, status: 'completed', callStack: newCallStack, lastResult: validatedReturnValue };
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// Find and skip past the pop_frame instruction
|
|
435
|
+
let newInstructionStack = state.instructionStack;
|
|
436
|
+
const popFrameIndex = newInstructionStack.findIndex((i) => i.op === 'pop_frame');
|
|
437
|
+
if (popFrameIndex !== -1) {
|
|
438
|
+
newInstructionStack = newInstructionStack.slice(popFrameIndex + 1);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
return { ...state, callStack: newCallStack, instructionStack: newInstructionStack, lastResult: validatedReturnValue };
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Execute statements at index - sequential statement execution.
|
|
446
|
+
*/
|
|
447
|
+
export function execStatements(state: RuntimeState, stmts: AST.Statement[], index: number, location: SourceLocation): RuntimeState {
|
|
448
|
+
if (index >= stmts.length) {
|
|
449
|
+
return state;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const stmt = stmts[index];
|
|
453
|
+
return {
|
|
454
|
+
...state,
|
|
455
|
+
instructionStack: [
|
|
456
|
+
{ op: 'exec_statement', stmt, location: stmt.location },
|
|
457
|
+
{ op: 'exec_statements', stmts, index: index + 1, location },
|
|
458
|
+
...state.instructionStack,
|
|
459
|
+
],
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Break statement - exit the innermost loop.
|
|
465
|
+
* Awaits pending async operations and triggers compress if needed.
|
|
466
|
+
*/
|
|
467
|
+
export function execBreakStatement(state: RuntimeState, stmt: AST.BreakStatement): RuntimeState {
|
|
468
|
+
// Find the innermost loop instruction in the instruction stack
|
|
469
|
+
const loopIndex = state.instructionStack.findIndex(
|
|
470
|
+
(instr) => instr.op === 'for_in_iterate' || instr.op === 'while_iterate' || instr.op === 'while_check'
|
|
471
|
+
);
|
|
472
|
+
|
|
473
|
+
if (loopIndex === -1) {
|
|
474
|
+
// This shouldn't happen if semantic analysis is correct
|
|
475
|
+
throw new Error('break statement outside of loop');
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
const loopInstr = state.instructionStack[loopIndex];
|
|
479
|
+
|
|
480
|
+
// Extract loop info based on instruction type
|
|
481
|
+
let savedKeys: string[];
|
|
482
|
+
let contextMode: AST.ContextMode | undefined;
|
|
483
|
+
let label: string | undefined;
|
|
484
|
+
let entryIndex: number;
|
|
485
|
+
let scopeType: 'for' | 'while';
|
|
486
|
+
|
|
487
|
+
if (loopInstr.op === 'for_in_iterate') {
|
|
488
|
+
savedKeys = loopInstr.savedKeys;
|
|
489
|
+
contextMode = loopInstr.contextMode;
|
|
490
|
+
label = loopInstr.label;
|
|
491
|
+
entryIndex = loopInstr.entryIndex;
|
|
492
|
+
scopeType = 'for';
|
|
493
|
+
} else {
|
|
494
|
+
// while_iterate or while_check
|
|
495
|
+
savedKeys = loopInstr.savedKeys;
|
|
496
|
+
contextMode = loopInstr.contextMode;
|
|
497
|
+
label = loopInstr.label;
|
|
498
|
+
entryIndex = loopInstr.entryIndex;
|
|
499
|
+
scopeType = 'while';
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// Remove all instructions up to and including the loop instruction
|
|
503
|
+
const newInstructionStack = state.instructionStack.slice(loopIndex + 1);
|
|
504
|
+
|
|
505
|
+
// Push break_loop instruction to handle async await and context mode
|
|
506
|
+
return {
|
|
507
|
+
...state,
|
|
508
|
+
instructionStack: [
|
|
509
|
+
{
|
|
510
|
+
op: 'break_loop',
|
|
511
|
+
savedKeys,
|
|
512
|
+
contextMode,
|
|
513
|
+
label,
|
|
514
|
+
entryIndex,
|
|
515
|
+
scopeType,
|
|
516
|
+
location: stmt.location,
|
|
517
|
+
},
|
|
518
|
+
...newInstructionStack,
|
|
519
|
+
],
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Statement dispatcher - routes to appropriate statement handler.
|
|
525
|
+
*/
|
|
526
|
+
export function execStatement(state: RuntimeState, stmt: AST.Statement): RuntimeState {
|
|
527
|
+
switch (stmt.type) {
|
|
528
|
+
case 'ImportDeclaration':
|
|
529
|
+
// Imports are processed during module loading, skip at runtime
|
|
530
|
+
return state;
|
|
531
|
+
|
|
532
|
+
case 'ExportDeclaration':
|
|
533
|
+
// Execute the underlying declaration
|
|
534
|
+
return execStatement(state, stmt.declaration);
|
|
535
|
+
|
|
536
|
+
case 'LetDeclaration':
|
|
537
|
+
return execLetDeclaration(state, stmt);
|
|
538
|
+
|
|
539
|
+
case 'ConstDeclaration':
|
|
540
|
+
return execConstDeclaration(state, stmt);
|
|
541
|
+
|
|
542
|
+
case 'DestructuringDeclaration':
|
|
543
|
+
return execDestructuringDeclaration(state, stmt);
|
|
544
|
+
|
|
545
|
+
case 'FunctionDeclaration':
|
|
546
|
+
// Functions are already collected at init, nothing to do
|
|
547
|
+
return state;
|
|
548
|
+
|
|
549
|
+
case 'ToolDeclaration':
|
|
550
|
+
// Register the tool at runtime
|
|
551
|
+
return {
|
|
552
|
+
...state,
|
|
553
|
+
instructionStack: [
|
|
554
|
+
{ op: 'exec_tool_declaration', decl: stmt, location: stmt.location },
|
|
555
|
+
...state.instructionStack,
|
|
556
|
+
],
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
case 'ModelDeclaration':
|
|
560
|
+
return execModelDeclaration(state, stmt);
|
|
561
|
+
|
|
562
|
+
case 'ReturnStatement':
|
|
563
|
+
return execReturnStatement(state, stmt);
|
|
564
|
+
|
|
565
|
+
case 'IfStatement':
|
|
566
|
+
return execIfStatement(state, stmt);
|
|
567
|
+
|
|
568
|
+
case 'ForInStatement':
|
|
569
|
+
return execForInStatement(state, stmt);
|
|
570
|
+
|
|
571
|
+
case 'WhileStatement':
|
|
572
|
+
return execWhileStatement(state, stmt);
|
|
573
|
+
|
|
574
|
+
case 'BlockStatement':
|
|
575
|
+
return execBlockStatement(state, stmt);
|
|
576
|
+
|
|
577
|
+
case 'ExpressionStatement':
|
|
578
|
+
return {
|
|
579
|
+
...state,
|
|
580
|
+
instructionStack: [
|
|
581
|
+
{ op: 'exec_expression', expr: stmt.expression, location: stmt.expression.location },
|
|
582
|
+
...state.instructionStack,
|
|
583
|
+
],
|
|
584
|
+
};
|
|
585
|
+
|
|
586
|
+
case 'AsyncStatement':
|
|
587
|
+
// Fire-and-forget async - set flag so handlers schedule async execution
|
|
588
|
+
return {
|
|
589
|
+
...state,
|
|
590
|
+
currentAsyncIsFireAndForget: true,
|
|
591
|
+
instructionStack: [
|
|
592
|
+
{ op: 'exec_expression', expr: stmt.expression, location: stmt.expression.location },
|
|
593
|
+
{ op: 'clear_async_context', location: stmt.location },
|
|
594
|
+
...state.instructionStack,
|
|
595
|
+
],
|
|
596
|
+
};
|
|
597
|
+
|
|
598
|
+
case 'BreakStatement':
|
|
599
|
+
return execBreakStatement(state, stmt);
|
|
600
|
+
|
|
601
|
+
default:
|
|
602
|
+
throw new Error(`Unknown statement type: ${(stmt as AST.Statement).type}`);
|
|
603
|
+
}
|
|
604
|
+
}
|