@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,431 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import { parse } from '../../parser/parse';
|
|
3
|
+
import { Runtime, AIProvider } from '../index';
|
|
4
|
+
|
|
5
|
+
// Mock AI provider
|
|
6
|
+
function createMockProvider(): AIProvider {
|
|
7
|
+
return {
|
|
8
|
+
async execute() {
|
|
9
|
+
return { value: '' };
|
|
10
|
+
},
|
|
11
|
+
async generateCode() {
|
|
12
|
+
return { value: '' };
|
|
13
|
+
},
|
|
14
|
+
async askUser(): Promise<string> {
|
|
15
|
+
return '';
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe('Binary Operators - Arithmetic', () => {
|
|
21
|
+
test('addition: 1 + 2 = 3', async () => {
|
|
22
|
+
const ast = parse(`let result = 1 + 2`);
|
|
23
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
24
|
+
await runtime.run();
|
|
25
|
+
expect(runtime.getValue('result')).toBe(3);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('subtraction: 10 - 3 = 7', async () => {
|
|
29
|
+
const ast = parse(`let result = 10 - 3`);
|
|
30
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
31
|
+
await runtime.run();
|
|
32
|
+
expect(runtime.getValue('result')).toBe(7);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('multiplication: 4 * 5 = 20', async () => {
|
|
36
|
+
const ast = parse(`let result = 4 * 5`);
|
|
37
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
38
|
+
await runtime.run();
|
|
39
|
+
expect(runtime.getValue('result')).toBe(20);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('division: 20 / 4 = 5', async () => {
|
|
43
|
+
const ast = parse(`let result = 20 / 4`);
|
|
44
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
45
|
+
await runtime.run();
|
|
46
|
+
expect(runtime.getValue('result')).toBe(5);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('modulo: 7 % 3 = 1', async () => {
|
|
50
|
+
const ast = parse(`let result = 7 % 3`);
|
|
51
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
52
|
+
await runtime.run();
|
|
53
|
+
expect(runtime.getValue('result')).toBe(1);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('chained addition: 1 + 2 + 3 = 6', async () => {
|
|
57
|
+
const ast = parse(`let result = 1 + 2 + 3`);
|
|
58
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
59
|
+
await runtime.run();
|
|
60
|
+
expect(runtime.getValue('result')).toBe(6);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('chained subtraction: 10 - 3 - 2 = 5', async () => {
|
|
64
|
+
const ast = parse(`let result = 10 - 3 - 2`);
|
|
65
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
66
|
+
await runtime.run();
|
|
67
|
+
expect(runtime.getValue('result')).toBe(5);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('mixed add/subtract: 10 + 5 - 3 = 12', async () => {
|
|
71
|
+
const ast = parse(`let result = 10 + 5 - 3`);
|
|
72
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
73
|
+
await runtime.run();
|
|
74
|
+
expect(runtime.getValue('result')).toBe(12);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('chained multiplication: 2 * 3 * 4 = 24', async () => {
|
|
78
|
+
const ast = parse(`let result = 2 * 3 * 4`);
|
|
79
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
80
|
+
await runtime.run();
|
|
81
|
+
expect(runtime.getValue('result')).toBe(24);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('chained division: 100 / 2 / 5 = 10', async () => {
|
|
85
|
+
const ast = parse(`let result = 100 / 2 / 5`);
|
|
86
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
87
|
+
await runtime.run();
|
|
88
|
+
expect(runtime.getValue('result')).toBe(10);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('negative result: 3 - 10 = -7', async () => {
|
|
92
|
+
const ast = parse(`let result = 3 - 10`);
|
|
93
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
94
|
+
await runtime.run();
|
|
95
|
+
expect(runtime.getValue('result')).toBe(-7);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('decimal division: 7 / 2 = 3.5', async () => {
|
|
99
|
+
const ast = parse(`let result = 7 / 2`);
|
|
100
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
101
|
+
await runtime.run();
|
|
102
|
+
expect(runtime.getValue('result')).toBe(3.5);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe('Binary Operators - Precedence', () => {
|
|
107
|
+
test('multiplication before addition: 1 + 2 * 3 = 7', async () => {
|
|
108
|
+
const ast = parse(`let result = 1 + 2 * 3`);
|
|
109
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
110
|
+
await runtime.run();
|
|
111
|
+
expect(runtime.getValue('result')).toBe(7);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test('division before subtraction: 10 - 6 / 2 = 7', async () => {
|
|
115
|
+
const ast = parse(`let result = 10 - 6 / 2`);
|
|
116
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
117
|
+
await runtime.run();
|
|
118
|
+
expect(runtime.getValue('result')).toBe(7);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test('parentheses override: (1 + 2) * 3 = 9', async () => {
|
|
122
|
+
const ast = parse(`let result = (1 + 2) * 3`);
|
|
123
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
124
|
+
await runtime.run();
|
|
125
|
+
expect(runtime.getValue('result')).toBe(9);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('nested parentheses: ((2 + 3) * 4) - 10 = 10', async () => {
|
|
129
|
+
const ast = parse(`let result = ((2 + 3) * 4) - 10`);
|
|
130
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
131
|
+
await runtime.run();
|
|
132
|
+
expect(runtime.getValue('result')).toBe(10);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test('complex expression: 2 + 3 * 4 - 6 / 2 = 11', async () => {
|
|
136
|
+
const ast = parse(`let result = 2 + 3 * 4 - 6 / 2`);
|
|
137
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
138
|
+
await runtime.run();
|
|
139
|
+
expect(runtime.getValue('result')).toBe(11);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test('modulo with addition: 10 % 3 + 5 = 6', async () => {
|
|
143
|
+
const ast = parse(`let result = 10 % 3 + 5`);
|
|
144
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
145
|
+
await runtime.run();
|
|
146
|
+
expect(runtime.getValue('result')).toBe(6);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
describe('Binary Operators - Comparison', () => {
|
|
151
|
+
test('equality: 1 == 1 is true', async () => {
|
|
152
|
+
const ast = parse(`let result = 1 == 1`);
|
|
153
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
154
|
+
await runtime.run();
|
|
155
|
+
expect(runtime.getValue('result')).toBe(true);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test('equality: 1 == 2 is false', async () => {
|
|
159
|
+
const ast = parse(`let result = 1 == 2`);
|
|
160
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
161
|
+
await runtime.run();
|
|
162
|
+
expect(runtime.getValue('result')).toBe(false);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test('inequality: 1 != 2 is true', async () => {
|
|
166
|
+
const ast = parse(`let result = 1 != 2`);
|
|
167
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
168
|
+
await runtime.run();
|
|
169
|
+
expect(runtime.getValue('result')).toBe(true);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test('inequality: 1 != 1 is false', async () => {
|
|
173
|
+
const ast = parse(`let result = 1 != 1`);
|
|
174
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
175
|
+
await runtime.run();
|
|
176
|
+
expect(runtime.getValue('result')).toBe(false);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test('less than: 1 < 2 is true', async () => {
|
|
180
|
+
const ast = parse(`let result = 1 < 2`);
|
|
181
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
182
|
+
await runtime.run();
|
|
183
|
+
expect(runtime.getValue('result')).toBe(true);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test('less than: 2 < 1 is false', async () => {
|
|
187
|
+
const ast = parse(`let result = 2 < 1`);
|
|
188
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
189
|
+
await runtime.run();
|
|
190
|
+
expect(runtime.getValue('result')).toBe(false);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test('less than: 1 < 1 is false', async () => {
|
|
194
|
+
const ast = parse(`let result = 1 < 1`);
|
|
195
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
196
|
+
await runtime.run();
|
|
197
|
+
expect(runtime.getValue('result')).toBe(false);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test('greater than: 2 > 1 is true', async () => {
|
|
201
|
+
const ast = parse(`let result = 2 > 1`);
|
|
202
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
203
|
+
await runtime.run();
|
|
204
|
+
expect(runtime.getValue('result')).toBe(true);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test('greater than: 1 > 2 is false', async () => {
|
|
208
|
+
const ast = parse(`let result = 1 > 2`);
|
|
209
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
210
|
+
await runtime.run();
|
|
211
|
+
expect(runtime.getValue('result')).toBe(false);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test('less than or equal: 1 <= 2 is true', async () => {
|
|
215
|
+
const ast = parse(`let result = 1 <= 2`);
|
|
216
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
217
|
+
await runtime.run();
|
|
218
|
+
expect(runtime.getValue('result')).toBe(true);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test('less than or equal: 1 <= 1 is true', async () => {
|
|
222
|
+
const ast = parse(`let result = 1 <= 1`);
|
|
223
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
224
|
+
await runtime.run();
|
|
225
|
+
expect(runtime.getValue('result')).toBe(true);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test('less than or equal: 2 <= 1 is false', async () => {
|
|
229
|
+
const ast = parse(`let result = 2 <= 1`);
|
|
230
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
231
|
+
await runtime.run();
|
|
232
|
+
expect(runtime.getValue('result')).toBe(false);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test('greater than or equal: 2 >= 1 is true', async () => {
|
|
236
|
+
const ast = parse(`let result = 2 >= 1`);
|
|
237
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
238
|
+
await runtime.run();
|
|
239
|
+
expect(runtime.getValue('result')).toBe(true);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
test('greater than or equal: 1 >= 1 is true', async () => {
|
|
243
|
+
const ast = parse(`let result = 1 >= 1`);
|
|
244
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
245
|
+
await runtime.run();
|
|
246
|
+
expect(runtime.getValue('result')).toBe(true);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
test('greater than or equal: 1 >= 2 is false', async () => {
|
|
250
|
+
const ast = parse(`let result = 1 >= 2`);
|
|
251
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
252
|
+
await runtime.run();
|
|
253
|
+
expect(runtime.getValue('result')).toBe(false);
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
describe('Binary Operators - Comparison with Arithmetic', () => {
|
|
258
|
+
test('compare arithmetic results: 2 + 3 == 5 is true', async () => {
|
|
259
|
+
const ast = parse(`let result = 2 + 3 == 5`);
|
|
260
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
261
|
+
await runtime.run();
|
|
262
|
+
expect(runtime.getValue('result')).toBe(true);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test('compare arithmetic results: 2 * 3 > 5 is true', async () => {
|
|
266
|
+
const ast = parse(`let result = 2 * 3 > 5`);
|
|
267
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
268
|
+
await runtime.run();
|
|
269
|
+
expect(runtime.getValue('result')).toBe(true);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
test('comparison in if condition', async () => {
|
|
273
|
+
const ast = parse(`
|
|
274
|
+
let x = 10
|
|
275
|
+
let result = "none"
|
|
276
|
+
if x > 5 {
|
|
277
|
+
result = "big"
|
|
278
|
+
}
|
|
279
|
+
`);
|
|
280
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
281
|
+
await runtime.run();
|
|
282
|
+
expect(runtime.getValue('result')).toBe('big');
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
test('arithmetic comparison in if condition', async () => {
|
|
286
|
+
const ast = parse(`
|
|
287
|
+
let a = 3
|
|
288
|
+
let b = 4
|
|
289
|
+
let result = "none"
|
|
290
|
+
if a + b == 7 {
|
|
291
|
+
result = "correct"
|
|
292
|
+
}
|
|
293
|
+
`);
|
|
294
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
295
|
+
await runtime.run();
|
|
296
|
+
expect(runtime.getValue('result')).toBe('correct');
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
describe('Binary Operators - With Variables', () => {
|
|
301
|
+
test('add two variables', async () => {
|
|
302
|
+
const ast = parse(`
|
|
303
|
+
let a = 5
|
|
304
|
+
let b = 3
|
|
305
|
+
let result = a + b
|
|
306
|
+
`);
|
|
307
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
308
|
+
await runtime.run();
|
|
309
|
+
expect(runtime.getValue('result')).toBe(8);
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
test('multiply variable by constant', async () => {
|
|
313
|
+
const ast = parse(`
|
|
314
|
+
let x = 7
|
|
315
|
+
let result = x * 3
|
|
316
|
+
`);
|
|
317
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
318
|
+
await runtime.run();
|
|
319
|
+
expect(runtime.getValue('result')).toBe(21);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
test('complex expression with variables', async () => {
|
|
323
|
+
const ast = parse(`
|
|
324
|
+
let a = 10
|
|
325
|
+
let b = 5
|
|
326
|
+
let c = 2
|
|
327
|
+
let result = (a + b) * c - 10
|
|
328
|
+
`);
|
|
329
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
330
|
+
await runtime.run();
|
|
331
|
+
expect(runtime.getValue('result')).toBe(20);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
test('compare two variables', async () => {
|
|
335
|
+
const ast = parse(`
|
|
336
|
+
let x = 10
|
|
337
|
+
let y = 20
|
|
338
|
+
let result = x < y
|
|
339
|
+
`);
|
|
340
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
341
|
+
await runtime.run();
|
|
342
|
+
expect(runtime.getValue('result')).toBe(true);
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
describe('Binary Operators - String Equality', () => {
|
|
347
|
+
test('string equality: same strings', async () => {
|
|
348
|
+
const ast = parse(`let result = "hello" == "hello"`);
|
|
349
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
350
|
+
await runtime.run();
|
|
351
|
+
expect(runtime.getValue('result')).toBe(true);
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
test('string equality: different strings', async () => {
|
|
355
|
+
const ast = parse(`let result = "hello" == "world"`);
|
|
356
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
357
|
+
await runtime.run();
|
|
358
|
+
expect(runtime.getValue('result')).toBe(false);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
test('string inequality', async () => {
|
|
362
|
+
const ast = parse(`let result = "hello" != "world"`);
|
|
363
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
364
|
+
await runtime.run();
|
|
365
|
+
expect(runtime.getValue('result')).toBe(true);
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
describe('Binary Operators - Boolean Equality', () => {
|
|
370
|
+
test('boolean equality: true == true', async () => {
|
|
371
|
+
const ast = parse(`let result = true == true`);
|
|
372
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
373
|
+
await runtime.run();
|
|
374
|
+
expect(runtime.getValue('result')).toBe(true);
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
test('boolean equality: true == false', async () => {
|
|
378
|
+
const ast = parse(`let result = true == false`);
|
|
379
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
380
|
+
await runtime.run();
|
|
381
|
+
expect(runtime.getValue('result')).toBe(false);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
test('boolean inequality: true != false', async () => {
|
|
385
|
+
const ast = parse(`let result = true != false`);
|
|
386
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
387
|
+
await runtime.run();
|
|
388
|
+
expect(runtime.getValue('result')).toBe(true);
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
describe('Binary Operators - In Loops', () => {
|
|
393
|
+
test('arithmetic in for loop', async () => {
|
|
394
|
+
const ast = parse(`
|
|
395
|
+
let sum = 0
|
|
396
|
+
for i in 5 {
|
|
397
|
+
sum = sum + i
|
|
398
|
+
}
|
|
399
|
+
`);
|
|
400
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
401
|
+
await runtime.run();
|
|
402
|
+
// 1 + 2 + 3 + 4 + 5 = 15
|
|
403
|
+
expect(runtime.getValue('sum')).toBe(15);
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
test('comparison in while loop', async () => {
|
|
407
|
+
const ast = parse(`
|
|
408
|
+
let count = 0
|
|
409
|
+
let max = 5
|
|
410
|
+
while count < max {
|
|
411
|
+
count = count + 1
|
|
412
|
+
}
|
|
413
|
+
`);
|
|
414
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
415
|
+
await runtime.run();
|
|
416
|
+
expect(runtime.getValue('count')).toBe(5);
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
test('multiplication in loop accumulator', async () => {
|
|
420
|
+
const ast = parse(`
|
|
421
|
+
let factorial = 1
|
|
422
|
+
for i in 5 {
|
|
423
|
+
factorial = factorial * i
|
|
424
|
+
}
|
|
425
|
+
`);
|
|
426
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
427
|
+
await runtime.run();
|
|
428
|
+
// 1 * 1 * 2 * 3 * 4 * 5 = 120
|
|
429
|
+
expect(runtime.getValue('factorial')).toBe(120);
|
|
430
|
+
});
|
|
431
|
+
});
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Break Statement Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for the break statement which exits the innermost loop.
|
|
5
|
+
*/
|
|
6
|
+
import { describe, test, expect } from 'bun:test';
|
|
7
|
+
import { parse } from '../../parser/parse';
|
|
8
|
+
import { createInitialState, runUntilPause, currentFrame } from '..';
|
|
9
|
+
import { resolveValue } from '../types';
|
|
10
|
+
|
|
11
|
+
describe('break statement', () => {
|
|
12
|
+
test('break exits for-in loop immediately', () => {
|
|
13
|
+
const source = `
|
|
14
|
+
let count = 0
|
|
15
|
+
for i in [1, 2, 3, 4, 5] {
|
|
16
|
+
if i == 3 {
|
|
17
|
+
break
|
|
18
|
+
}
|
|
19
|
+
count = count + 1
|
|
20
|
+
}
|
|
21
|
+
`;
|
|
22
|
+
const program = parse(source);
|
|
23
|
+
let state = createInitialState(program);
|
|
24
|
+
state = runUntilPause(state);
|
|
25
|
+
|
|
26
|
+
expect(state.status).toBe('completed');
|
|
27
|
+
const frame = currentFrame(state);
|
|
28
|
+
const count = resolveValue(frame.locals['count']);
|
|
29
|
+
// Loop should have run for i=1, i=2, then break at i=3
|
|
30
|
+
expect(count).toBe(2);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('break exits while loop immediately', () => {
|
|
34
|
+
const source = `
|
|
35
|
+
let count = 0
|
|
36
|
+
let i = 0
|
|
37
|
+
while i < 10 {
|
|
38
|
+
i = i + 1
|
|
39
|
+
if i == 5 {
|
|
40
|
+
break
|
|
41
|
+
}
|
|
42
|
+
count = count + 1
|
|
43
|
+
}
|
|
44
|
+
`;
|
|
45
|
+
const program = parse(source);
|
|
46
|
+
let state = createInitialState(program);
|
|
47
|
+
state = runUntilPause(state);
|
|
48
|
+
|
|
49
|
+
expect(state.status).toBe('completed');
|
|
50
|
+
const frame = currentFrame(state);
|
|
51
|
+
const count = resolveValue(frame.locals['count']);
|
|
52
|
+
// Loop should have run for i=1,2,3,4, then break at i=5
|
|
53
|
+
expect(count).toBe(4);
|
|
54
|
+
const i = resolveValue(frame.locals['i']);
|
|
55
|
+
expect(i).toBe(5);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('break only exits innermost loop (nested for-in)', () => {
|
|
59
|
+
const source = `
|
|
60
|
+
let outerCount = 0
|
|
61
|
+
let innerCount = 0
|
|
62
|
+
for i in [1, 2, 3] {
|
|
63
|
+
outerCount = outerCount + 1
|
|
64
|
+
for j in [1, 2, 3, 4, 5] {
|
|
65
|
+
if j == 2 {
|
|
66
|
+
break
|
|
67
|
+
}
|
|
68
|
+
innerCount = innerCount + 1
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
`;
|
|
72
|
+
const program = parse(source);
|
|
73
|
+
let state = createInitialState(program);
|
|
74
|
+
state = runUntilPause(state);
|
|
75
|
+
|
|
76
|
+
expect(state.status).toBe('completed');
|
|
77
|
+
const frame = currentFrame(state);
|
|
78
|
+
const outerCount = resolveValue(frame.locals['outerCount']);
|
|
79
|
+
const innerCount = resolveValue(frame.locals['innerCount']);
|
|
80
|
+
// Outer loop runs 3 times
|
|
81
|
+
expect(outerCount).toBe(3);
|
|
82
|
+
// Inner loop breaks at j=2 each time, so only j=1 increments count = 1 per outer iteration
|
|
83
|
+
expect(innerCount).toBe(3);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('break only exits innermost loop (nested while)', () => {
|
|
87
|
+
const source = `
|
|
88
|
+
let outerCount = 0
|
|
89
|
+
let innerCount = 0
|
|
90
|
+
let i = 0
|
|
91
|
+
while i < 3 {
|
|
92
|
+
i = i + 1
|
|
93
|
+
outerCount = outerCount + 1
|
|
94
|
+
let j = 0
|
|
95
|
+
while j < 10 {
|
|
96
|
+
j = j + 1
|
|
97
|
+
if j == 3 {
|
|
98
|
+
break
|
|
99
|
+
}
|
|
100
|
+
innerCount = innerCount + 1
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
`;
|
|
104
|
+
const program = parse(source);
|
|
105
|
+
let state = createInitialState(program);
|
|
106
|
+
state = runUntilPause(state);
|
|
107
|
+
|
|
108
|
+
expect(state.status).toBe('completed');
|
|
109
|
+
const frame = currentFrame(state);
|
|
110
|
+
const outerCount = resolveValue(frame.locals['outerCount']);
|
|
111
|
+
const innerCount = resolveValue(frame.locals['innerCount']);
|
|
112
|
+
// Outer loop runs 3 times
|
|
113
|
+
expect(outerCount).toBe(3);
|
|
114
|
+
// Inner loop breaks at j=3 each time, so j=1,2 increment count = 2 per outer iteration
|
|
115
|
+
expect(innerCount).toBe(6);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test('break works in for-in inside while', () => {
|
|
119
|
+
const source = `
|
|
120
|
+
let result = 0
|
|
121
|
+
let i = 0
|
|
122
|
+
while i < 2 {
|
|
123
|
+
i = i + 1
|
|
124
|
+
for j in [10, 20, 30, 40] {
|
|
125
|
+
result = result + j
|
|
126
|
+
if j == 20 {
|
|
127
|
+
break
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
`;
|
|
132
|
+
const program = parse(source);
|
|
133
|
+
let state = createInitialState(program);
|
|
134
|
+
state = runUntilPause(state);
|
|
135
|
+
|
|
136
|
+
expect(state.status).toBe('completed');
|
|
137
|
+
const frame = currentFrame(state);
|
|
138
|
+
const result = resolveValue(frame.locals['result']);
|
|
139
|
+
// Each outer iteration: 10 + 20 = 30 (break at 20)
|
|
140
|
+
// 2 outer iterations: 30 * 2 = 60
|
|
141
|
+
expect(result).toBe(60);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test('break works in while inside for-in', () => {
|
|
145
|
+
const source = `
|
|
146
|
+
let result = 0
|
|
147
|
+
for i in [1, 2] {
|
|
148
|
+
let j = 0
|
|
149
|
+
while j < 100 {
|
|
150
|
+
j = j + 1
|
|
151
|
+
result = result + 1
|
|
152
|
+
if j == 5 {
|
|
153
|
+
break
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
`;
|
|
158
|
+
const program = parse(source);
|
|
159
|
+
let state = createInitialState(program);
|
|
160
|
+
state = runUntilPause(state);
|
|
161
|
+
|
|
162
|
+
expect(state.status).toBe('completed');
|
|
163
|
+
const frame = currentFrame(state);
|
|
164
|
+
const result = resolveValue(frame.locals['result']);
|
|
165
|
+
// Each outer iteration: while runs 5 times (j=1,2,3,4,5 then break)
|
|
166
|
+
// 2 outer iterations: 5 * 2 = 10
|
|
167
|
+
expect(result).toBe(10);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test('break as first statement in loop body', () => {
|
|
171
|
+
const source = `
|
|
172
|
+
let count = 0
|
|
173
|
+
for i in [1, 2, 3] {
|
|
174
|
+
break
|
|
175
|
+
count = count + 1
|
|
176
|
+
}
|
|
177
|
+
`;
|
|
178
|
+
const program = parse(source);
|
|
179
|
+
let state = createInitialState(program);
|
|
180
|
+
state = runUntilPause(state);
|
|
181
|
+
|
|
182
|
+
expect(state.status).toBe('completed');
|
|
183
|
+
const frame = currentFrame(state);
|
|
184
|
+
const count = resolveValue(frame.locals['count']);
|
|
185
|
+
// Break immediately, count never incremented
|
|
186
|
+
expect(count).toBe(0);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test('break preserves variables declared before loop', () => {
|
|
190
|
+
const source = `
|
|
191
|
+
let x = 100
|
|
192
|
+
for i in [1, 2, 3, 4, 5] {
|
|
193
|
+
x = x + i
|
|
194
|
+
if i == 3 {
|
|
195
|
+
break
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
`;
|
|
199
|
+
const program = parse(source);
|
|
200
|
+
let state = createInitialState(program);
|
|
201
|
+
state = runUntilPause(state);
|
|
202
|
+
|
|
203
|
+
expect(state.status).toBe('completed');
|
|
204
|
+
const frame = currentFrame(state);
|
|
205
|
+
const x = resolveValue(frame.locals['x']);
|
|
206
|
+
// x = 100 + 1 + 2 + 3 = 106 (break at i=3, after adding 3)
|
|
207
|
+
expect(x).toBe(106);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test('break cleans up loop variable scope', () => {
|
|
211
|
+
const source = `
|
|
212
|
+
let result = 0
|
|
213
|
+
for i in [1, 2, 3] {
|
|
214
|
+
let temp = i * 10
|
|
215
|
+
if i == 2 {
|
|
216
|
+
break
|
|
217
|
+
}
|
|
218
|
+
result = result + temp
|
|
219
|
+
}
|
|
220
|
+
`;
|
|
221
|
+
const program = parse(source);
|
|
222
|
+
let state = createInitialState(program);
|
|
223
|
+
state = runUntilPause(state);
|
|
224
|
+
|
|
225
|
+
expect(state.status).toBe('completed');
|
|
226
|
+
const frame = currentFrame(state);
|
|
227
|
+
const result = resolveValue(frame.locals['result']);
|
|
228
|
+
// i=1: temp=10, result=10
|
|
229
|
+
// i=2: temp=20, break before adding
|
|
230
|
+
expect(result).toBe(10);
|
|
231
|
+
// temp should be cleaned up (not in frame.locals at top level)
|
|
232
|
+
expect(frame.locals['temp']).toBeUndefined();
|
|
233
|
+
// i should be cleaned up too
|
|
234
|
+
expect(frame.locals['i']).toBeUndefined();
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
test('conditional break with complex condition', () => {
|
|
238
|
+
const source = `
|
|
239
|
+
let sum = 0
|
|
240
|
+
for n in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] {
|
|
241
|
+
sum = sum + n
|
|
242
|
+
if sum > 15 {
|
|
243
|
+
break
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
`;
|
|
247
|
+
const program = parse(source);
|
|
248
|
+
let state = createInitialState(program);
|
|
249
|
+
state = runUntilPause(state);
|
|
250
|
+
|
|
251
|
+
expect(state.status).toBe('completed');
|
|
252
|
+
const frame = currentFrame(state);
|
|
253
|
+
const sum = resolveValue(frame.locals['sum']);
|
|
254
|
+
// 1+2+3+4+5 = 15, then 1+2+3+4+5+6 = 21 > 15, break
|
|
255
|
+
expect(sum).toBe(21);
|
|
256
|
+
});
|
|
257
|
+
});
|