@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,346 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import { parse } from '../../parser/parse';
|
|
3
|
+
import { analyze } from '../index';
|
|
4
|
+
|
|
5
|
+
describe('Semantic Errors - Control Flow', () => {
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Return outside function
|
|
8
|
+
// ============================================================================
|
|
9
|
+
|
|
10
|
+
test('return at top level', () => {
|
|
11
|
+
const ast = parse(`
|
|
12
|
+
return "hello"
|
|
13
|
+
`);
|
|
14
|
+
const errors = analyze(ast);
|
|
15
|
+
expect(errors.length).toBe(1);
|
|
16
|
+
expect(errors[0].message).toBe('return outside of function');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('return in if block at top level', () => {
|
|
20
|
+
const ast = parse(`
|
|
21
|
+
if true {
|
|
22
|
+
return "hello"
|
|
23
|
+
}
|
|
24
|
+
`);
|
|
25
|
+
const errors = analyze(ast);
|
|
26
|
+
expect(errors.length).toBe(1);
|
|
27
|
+
expect(errors[0].message).toBe('return outside of function');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('return in else block at top level', () => {
|
|
31
|
+
const ast = parse(`
|
|
32
|
+
if false {
|
|
33
|
+
let x = "no"
|
|
34
|
+
} else {
|
|
35
|
+
return "hello"
|
|
36
|
+
}
|
|
37
|
+
`);
|
|
38
|
+
const errors = analyze(ast);
|
|
39
|
+
expect(errors.length).toBe(1);
|
|
40
|
+
expect(errors[0].message).toBe('return outside of function');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('return in nested blocks at top level', () => {
|
|
44
|
+
const ast = parse(`
|
|
45
|
+
if true {
|
|
46
|
+
if true {
|
|
47
|
+
return "hello"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
`);
|
|
51
|
+
const errors = analyze(ast);
|
|
52
|
+
expect(errors.length).toBe(1);
|
|
53
|
+
expect(errors[0].message).toBe('return outside of function');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// ============================================================================
|
|
57
|
+
// Valid return statements
|
|
58
|
+
// ============================================================================
|
|
59
|
+
|
|
60
|
+
test('return inside function', () => {
|
|
61
|
+
const ast = parse(`
|
|
62
|
+
function test() {
|
|
63
|
+
return "hello"
|
|
64
|
+
}
|
|
65
|
+
`);
|
|
66
|
+
const errors = analyze(ast);
|
|
67
|
+
expect(errors.length).toBe(0);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('return inside if block in function', () => {
|
|
71
|
+
const ast = parse(`
|
|
72
|
+
function test(x: boolean): text {
|
|
73
|
+
if x {
|
|
74
|
+
return "yes"
|
|
75
|
+
}
|
|
76
|
+
return "no"
|
|
77
|
+
}
|
|
78
|
+
`);
|
|
79
|
+
const errors = analyze(ast);
|
|
80
|
+
expect(errors.length).toBe(0);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('return inside else block in function', () => {
|
|
84
|
+
const ast = parse(`
|
|
85
|
+
function test(x: boolean): text {
|
|
86
|
+
if x {
|
|
87
|
+
return "yes"
|
|
88
|
+
} else {
|
|
89
|
+
return "no"
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
`);
|
|
93
|
+
const errors = analyze(ast);
|
|
94
|
+
expect(errors.length).toBe(0);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('return inside nested blocks in function', () => {
|
|
98
|
+
const ast = parse(`
|
|
99
|
+
function test(x: boolean): text {
|
|
100
|
+
if x {
|
|
101
|
+
if true {
|
|
102
|
+
return "deep"
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return "default"
|
|
106
|
+
}
|
|
107
|
+
`);
|
|
108
|
+
const errors = analyze(ast);
|
|
109
|
+
expect(errors.length).toBe(0);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// ============================================================================
|
|
113
|
+
// If condition type checking
|
|
114
|
+
// ============================================================================
|
|
115
|
+
|
|
116
|
+
test('if with non-boolean literal condition errors', () => {
|
|
117
|
+
const ast = parse(`
|
|
118
|
+
if "test" {
|
|
119
|
+
let y = 1
|
|
120
|
+
}
|
|
121
|
+
`);
|
|
122
|
+
const errors = analyze(ast);
|
|
123
|
+
expect(errors.length).toBe(1);
|
|
124
|
+
expect(errors[0].message).toBe('if condition must be boolean, got text');
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test('if with non-boolean typed variable condition errors', () => {
|
|
128
|
+
const ast = parse(`
|
|
129
|
+
const x: text = "test"
|
|
130
|
+
if x {
|
|
131
|
+
let y = 1
|
|
132
|
+
}
|
|
133
|
+
`);
|
|
134
|
+
const errors = analyze(ast);
|
|
135
|
+
expect(errors.length).toBe(1);
|
|
136
|
+
expect(errors[0].message).toBe('if condition must be boolean, got text');
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test('if with number literal condition errors', () => {
|
|
140
|
+
const ast = parse(`
|
|
141
|
+
if 5 {
|
|
142
|
+
let y = 1
|
|
143
|
+
}
|
|
144
|
+
`);
|
|
145
|
+
const errors = analyze(ast);
|
|
146
|
+
expect(errors.length).toBe(1);
|
|
147
|
+
expect(errors[0].message).toBe('if condition must be boolean, got number');
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test('if with boolean literal condition passes', () => {
|
|
151
|
+
const ast = parse(`
|
|
152
|
+
if true {
|
|
153
|
+
let y = 1
|
|
154
|
+
}
|
|
155
|
+
`);
|
|
156
|
+
const errors = analyze(ast);
|
|
157
|
+
expect(errors.length).toBe(0);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test('if with boolean typed variable condition passes', () => {
|
|
161
|
+
const ast = parse(`
|
|
162
|
+
let flag: boolean = true
|
|
163
|
+
if flag {
|
|
164
|
+
let y = 1
|
|
165
|
+
}
|
|
166
|
+
`);
|
|
167
|
+
const errors = analyze(ast);
|
|
168
|
+
expect(errors.length).toBe(0);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test('return with no value inside function', () => {
|
|
172
|
+
const ast = parse(`
|
|
173
|
+
function test() {
|
|
174
|
+
return
|
|
175
|
+
}
|
|
176
|
+
`);
|
|
177
|
+
const errors = analyze(ast);
|
|
178
|
+
expect(errors.length).toBe(0);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// ============================================================================
|
|
182
|
+
// Multiple return errors
|
|
183
|
+
// ============================================================================
|
|
184
|
+
|
|
185
|
+
test('multiple returns at top level', () => {
|
|
186
|
+
const ast = parse(`
|
|
187
|
+
return "first"
|
|
188
|
+
return "second"
|
|
189
|
+
`);
|
|
190
|
+
const errors = analyze(ast);
|
|
191
|
+
expect(errors.length).toBe(2);
|
|
192
|
+
expect(errors[0].message).toBe('return outside of function');
|
|
193
|
+
expect(errors[1].message).toBe('return outside of function');
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// ============================================================================
|
|
197
|
+
// Combined with other errors
|
|
198
|
+
// ============================================================================
|
|
199
|
+
|
|
200
|
+
test('return outside function with undefined variable', () => {
|
|
201
|
+
const ast = parse(`
|
|
202
|
+
return undefinedVar
|
|
203
|
+
`);
|
|
204
|
+
const errors = analyze(ast);
|
|
205
|
+
expect(errors.length).toBe(2);
|
|
206
|
+
expect(errors[0].message).toBe('return outside of function');
|
|
207
|
+
expect(errors[1].message).toBe("'undefinedVar' is not defined");
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// ============================================================================
|
|
211
|
+
// While loop semantic checks
|
|
212
|
+
// ============================================================================
|
|
213
|
+
|
|
214
|
+
test('while loop with undefined condition variable', () => {
|
|
215
|
+
const ast = parse(`
|
|
216
|
+
while undefinedVar {
|
|
217
|
+
let x = 1
|
|
218
|
+
}
|
|
219
|
+
`);
|
|
220
|
+
const errors = analyze(ast);
|
|
221
|
+
expect(errors.length).toBe(1);
|
|
222
|
+
expect(errors[0].message).toBe("'undefinedVar' is not defined");
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test('while loop with valid condition', () => {
|
|
226
|
+
const ast = parse(`
|
|
227
|
+
let keepGoing = true
|
|
228
|
+
while keepGoing {
|
|
229
|
+
keepGoing = false
|
|
230
|
+
}
|
|
231
|
+
`);
|
|
232
|
+
const errors = analyze(ast);
|
|
233
|
+
expect(errors.length).toBe(0);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test('while loop body variables not visible outside', () => {
|
|
237
|
+
const ast = parse(`
|
|
238
|
+
while true {
|
|
239
|
+
let innerVar = 1
|
|
240
|
+
}
|
|
241
|
+
let x = innerVar
|
|
242
|
+
`);
|
|
243
|
+
const errors = analyze(ast);
|
|
244
|
+
expect(errors.length).toBe(1);
|
|
245
|
+
expect(errors[0].message).toBe("'innerVar' is not defined");
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test('while loop can access outer variables', () => {
|
|
249
|
+
const ast = parse(`
|
|
250
|
+
let outerVar = true
|
|
251
|
+
while outerVar {
|
|
252
|
+
outerVar = false
|
|
253
|
+
}
|
|
254
|
+
`);
|
|
255
|
+
const errors = analyze(ast);
|
|
256
|
+
expect(errors.length).toBe(0);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test('nested while loops have proper scoping', () => {
|
|
260
|
+
const ast = parse(`
|
|
261
|
+
let outer = true
|
|
262
|
+
while outer {
|
|
263
|
+
let middle = true
|
|
264
|
+
while middle {
|
|
265
|
+
let inner = true
|
|
266
|
+
middle = false
|
|
267
|
+
}
|
|
268
|
+
outer = false
|
|
269
|
+
}
|
|
270
|
+
`);
|
|
271
|
+
const errors = analyze(ast);
|
|
272
|
+
expect(errors.length).toBe(0);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
test('return inside while loop in function is valid', () => {
|
|
276
|
+
const ast = parse(`
|
|
277
|
+
function test(): boolean {
|
|
278
|
+
while true {
|
|
279
|
+
return true
|
|
280
|
+
}
|
|
281
|
+
return false
|
|
282
|
+
}
|
|
283
|
+
`);
|
|
284
|
+
const errors = analyze(ast);
|
|
285
|
+
expect(errors.length).toBe(0);
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
test('return inside while loop at top level is invalid', () => {
|
|
289
|
+
const ast = parse(`
|
|
290
|
+
while true {
|
|
291
|
+
return true
|
|
292
|
+
}
|
|
293
|
+
`);
|
|
294
|
+
const errors = analyze(ast);
|
|
295
|
+
expect(errors.length).toBe(1);
|
|
296
|
+
expect(errors[0].message).toBe('return outside of function');
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
test('while with non-boolean literal condition errors', () => {
|
|
300
|
+
const ast = parse(`
|
|
301
|
+
while "test" {
|
|
302
|
+
let y = 1
|
|
303
|
+
}
|
|
304
|
+
`);
|
|
305
|
+
const errors = analyze(ast);
|
|
306
|
+
expect(errors.length).toBe(1);
|
|
307
|
+
expect(errors[0].message).toBe('while condition must be boolean, got text');
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
test('while with non-boolean typed variable condition errors', () => {
|
|
311
|
+
const ast = parse(`
|
|
312
|
+
const x: text = "test"
|
|
313
|
+
while x {
|
|
314
|
+
let y = 1
|
|
315
|
+
}
|
|
316
|
+
`);
|
|
317
|
+
const errors = analyze(ast);
|
|
318
|
+
expect(errors.length).toBe(1);
|
|
319
|
+
expect(errors[0].message).toBe('while condition must be boolean, got text');
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
test('while with number literal condition errors', () => {
|
|
323
|
+
const ast = parse(`
|
|
324
|
+
while 5 {
|
|
325
|
+
let y = 1
|
|
326
|
+
}
|
|
327
|
+
`);
|
|
328
|
+
const errors = analyze(ast);
|
|
329
|
+
expect(errors.length).toBe(1);
|
|
330
|
+
expect(errors[0].message).toBe('while condition must be boolean, got number');
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
test('while with inferred text variable condition errors', () => {
|
|
334
|
+
// With type inference, const x = "test" has inferred type 'text'
|
|
335
|
+
// Using text as while condition should error
|
|
336
|
+
const ast = parse(`
|
|
337
|
+
const x = "test"
|
|
338
|
+
while x {
|
|
339
|
+
let y = 1
|
|
340
|
+
}
|
|
341
|
+
`);
|
|
342
|
+
const errors = analyze(ast);
|
|
343
|
+
expect(errors.length).toBe(1);
|
|
344
|
+
expect(errors[0].message).toBe('while condition must be boolean, got text');
|
|
345
|
+
});
|
|
346
|
+
});
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import { parse } from '../../parser/parse';
|
|
3
|
+
import { analyze } from '../index';
|
|
4
|
+
|
|
5
|
+
describe('Semantic Validation - Destructuring Declarations', () => {
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Valid cases
|
|
8
|
+
// ============================================================================
|
|
9
|
+
|
|
10
|
+
test('valid const destructuring from do expression', () => {
|
|
11
|
+
const ast = parse(`
|
|
12
|
+
model m = { name: "test", apiKey: "key", url: "http://test" }
|
|
13
|
+
const {name: text, age: number} = do "get info" m
|
|
14
|
+
`);
|
|
15
|
+
const errors = analyze(ast);
|
|
16
|
+
expect(errors.length).toBe(0);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('valid let destructuring from do expression', () => {
|
|
20
|
+
const ast = parse(`
|
|
21
|
+
model m = { name: "test", apiKey: "key", url: "http://test" }
|
|
22
|
+
let {valid: boolean, reason: text} = do "validate" m
|
|
23
|
+
`);
|
|
24
|
+
const errors = analyze(ast);
|
|
25
|
+
expect(errors.length).toBe(0);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('valid destructuring with array types', () => {
|
|
29
|
+
const ast = parse(`
|
|
30
|
+
model m = { name: "test", apiKey: "key", url: "http://test" }
|
|
31
|
+
const {items: text[], counts: number[]} = do "get lists" m
|
|
32
|
+
`);
|
|
33
|
+
const errors = analyze(ast);
|
|
34
|
+
expect(errors.length).toBe(0);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// ============================================================================
|
|
38
|
+
// Duplicate field names within destructuring pattern
|
|
39
|
+
// ============================================================================
|
|
40
|
+
|
|
41
|
+
test('duplicate field name in destructuring pattern', () => {
|
|
42
|
+
const ast = parse(`
|
|
43
|
+
model m = { name: "test", apiKey: "key", url: "http://test" }
|
|
44
|
+
const {name: text, name: number} = do "get info" m
|
|
45
|
+
`);
|
|
46
|
+
const errors = analyze(ast);
|
|
47
|
+
expect(errors.length).toBe(1);
|
|
48
|
+
expect(errors[0].message).toContain("Duplicate field 'name'");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('multiple duplicate field names in pattern', () => {
|
|
52
|
+
const ast = parse(`
|
|
53
|
+
model m = { name: "test", apiKey: "key", url: "http://test" }
|
|
54
|
+
const {x: number, y: number, x: text, y: text} = do "get" m
|
|
55
|
+
`);
|
|
56
|
+
const errors = analyze(ast);
|
|
57
|
+
expect(errors.length).toBe(2);
|
|
58
|
+
expect(errors[0].message).toContain("Duplicate field 'x'");
|
|
59
|
+
expect(errors[1].message).toContain("Duplicate field 'y'");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// ============================================================================
|
|
63
|
+
// Destructuring creates variables that can conflict
|
|
64
|
+
// ============================================================================
|
|
65
|
+
|
|
66
|
+
test('destructuring field conflicts with existing variable', () => {
|
|
67
|
+
const ast = parse(`
|
|
68
|
+
model m = { name: "test", apiKey: "key", url: "http://test" }
|
|
69
|
+
let name = "existing"
|
|
70
|
+
const {name: text, age: number} = do "get info" m
|
|
71
|
+
`);
|
|
72
|
+
const errors = analyze(ast);
|
|
73
|
+
expect(errors.length).toBe(1);
|
|
74
|
+
expect(errors[0].message).toBe("'name' is already declared");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('destructuring field conflicts with function', () => {
|
|
78
|
+
const ast = parse(`
|
|
79
|
+
model m = { name: "test", apiKey: "key", url: "http://test" }
|
|
80
|
+
function greet() { return "hi" }
|
|
81
|
+
const {greet: text} = do "get" m
|
|
82
|
+
`);
|
|
83
|
+
const errors = analyze(ast);
|
|
84
|
+
expect(errors.length).toBe(1);
|
|
85
|
+
expect(errors[0].message).toBe("'greet' is already declared");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('two destructurings with conflicting fields', () => {
|
|
89
|
+
const ast = parse(`
|
|
90
|
+
model m = { name: "test", apiKey: "key", url: "http://test" }
|
|
91
|
+
const {name: text} = do "get name" m
|
|
92
|
+
const {name: number} = do "get another" m
|
|
93
|
+
`);
|
|
94
|
+
const errors = analyze(ast);
|
|
95
|
+
expect(errors.length).toBe(1);
|
|
96
|
+
expect(errors[0].message).toBe("'name' is already declared");
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// ============================================================================
|
|
100
|
+
// Invalid initializer (must be AI expression)
|
|
101
|
+
// ============================================================================
|
|
102
|
+
|
|
103
|
+
test('destructuring from string literal is invalid', () => {
|
|
104
|
+
const ast = parse(`
|
|
105
|
+
const {name: text} = "not an AI call"
|
|
106
|
+
`);
|
|
107
|
+
const errors = analyze(ast);
|
|
108
|
+
expect(errors.length).toBe(1);
|
|
109
|
+
expect(errors[0].message).toContain('Destructuring assignment requires a do or vibe expression');
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('destructuring from number is invalid', () => {
|
|
113
|
+
const ast = parse(`
|
|
114
|
+
const {value: number} = 42
|
|
115
|
+
`);
|
|
116
|
+
const errors = analyze(ast);
|
|
117
|
+
expect(errors.length).toBe(1);
|
|
118
|
+
expect(errors[0].message).toContain('Destructuring assignment requires a do or vibe expression');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test('destructuring from variable is invalid', () => {
|
|
122
|
+
const ast = parse(`
|
|
123
|
+
let data = "test"
|
|
124
|
+
const {name: text} = data
|
|
125
|
+
`);
|
|
126
|
+
const errors = analyze(ast);
|
|
127
|
+
expect(errors.length).toBe(1);
|
|
128
|
+
expect(errors[0].message).toContain('Destructuring assignment requires a do or vibe expression');
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// ============================================================================
|
|
132
|
+
// Shadowing in nested scopes (valid)
|
|
133
|
+
// ============================================================================
|
|
134
|
+
|
|
135
|
+
test('destructuring in block can shadow outer variable', () => {
|
|
136
|
+
const ast = parse(`
|
|
137
|
+
model m = { name: "test", apiKey: "key", url: "http://test" }
|
|
138
|
+
let name = "outer"
|
|
139
|
+
if true {
|
|
140
|
+
const {name: text} = do "get" m
|
|
141
|
+
}
|
|
142
|
+
`);
|
|
143
|
+
const errors = analyze(ast);
|
|
144
|
+
expect(errors.length).toBe(0);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test('destructuring in function can shadow outer variable', () => {
|
|
148
|
+
const ast = parse(`
|
|
149
|
+
model m = { name: "test", apiKey: "key", url: "http://test" }
|
|
150
|
+
let name = "outer"
|
|
151
|
+
function test() {
|
|
152
|
+
const {name: text} = do "get" m
|
|
153
|
+
return name
|
|
154
|
+
}
|
|
155
|
+
`);
|
|
156
|
+
const errors = analyze(ast);
|
|
157
|
+
expect(errors.length).toBe(0);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// ============================================================================
|
|
161
|
+
// Using destructured variables
|
|
162
|
+
// ============================================================================
|
|
163
|
+
|
|
164
|
+
test('can use destructured variables', () => {
|
|
165
|
+
const ast = parse(`
|
|
166
|
+
model m = { name: "test", apiKey: "key", url: "http://test" }
|
|
167
|
+
const {name: text, age: number} = do "get" m
|
|
168
|
+
let greeting = name
|
|
169
|
+
let years = age
|
|
170
|
+
`);
|
|
171
|
+
const errors = analyze(ast);
|
|
172
|
+
expect(errors.length).toBe(0);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test('cannot use destructured variable before declaration', () => {
|
|
176
|
+
const ast = parse(`
|
|
177
|
+
model m = { name: "test", apiKey: "key", url: "http://test" }
|
|
178
|
+
let x = name
|
|
179
|
+
const {name: text} = do "get" m
|
|
180
|
+
`);
|
|
181
|
+
const errors = analyze(ast);
|
|
182
|
+
expect(errors.length).toBe(1);
|
|
183
|
+
expect(errors[0].message).toContain("'name' is not defined");
|
|
184
|
+
});
|
|
185
|
+
});
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import { parse } from '../../parser/parse';
|
|
3
|
+
import { analyze } from '../index';
|
|
4
|
+
|
|
5
|
+
describe('Semantic Errors - Duplicate Declarations', () => {
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Basic duplicate declarations
|
|
8
|
+
// ============================================================================
|
|
9
|
+
|
|
10
|
+
test('duplicate let declaration', () => {
|
|
11
|
+
const ast = parse(`
|
|
12
|
+
let x = "hello"
|
|
13
|
+
let x = "world"
|
|
14
|
+
`);
|
|
15
|
+
const errors = analyze(ast);
|
|
16
|
+
expect(errors.length).toBe(1);
|
|
17
|
+
expect(errors[0].message).toBe("'x' is already declared");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('duplicate const declaration', () => {
|
|
21
|
+
const ast = parse(`
|
|
22
|
+
const x = "hello"
|
|
23
|
+
const x = "world"
|
|
24
|
+
`);
|
|
25
|
+
const errors = analyze(ast);
|
|
26
|
+
expect(errors.length).toBe(1);
|
|
27
|
+
expect(errors[0].message).toBe("'x' is already declared");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('let and const with same name', () => {
|
|
31
|
+
const ast = parse(`
|
|
32
|
+
let x = "hello"
|
|
33
|
+
const x = "world"
|
|
34
|
+
`);
|
|
35
|
+
const errors = analyze(ast);
|
|
36
|
+
expect(errors.length).toBe(1);
|
|
37
|
+
expect(errors[0].message).toBe("'x' is already declared");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('duplicate function declaration', () => {
|
|
41
|
+
const ast = parse(`
|
|
42
|
+
function greet(name: text): text {
|
|
43
|
+
return name
|
|
44
|
+
}
|
|
45
|
+
function greet(x: text): text {
|
|
46
|
+
return x
|
|
47
|
+
}
|
|
48
|
+
`);
|
|
49
|
+
const errors = analyze(ast);
|
|
50
|
+
expect(errors.length).toBe(1);
|
|
51
|
+
expect(errors[0].message).toBe("'greet' is already declared");
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('duplicate model declaration', () => {
|
|
55
|
+
const ast = parse(`
|
|
56
|
+
model myModel = { name: "test", apiKey: "key", url: "http://test" }
|
|
57
|
+
model myModel = { name: "test2", apiKey: "key2", url: "http://test2" }
|
|
58
|
+
`);
|
|
59
|
+
const errors = analyze(ast);
|
|
60
|
+
expect(errors.length).toBe(1);
|
|
61
|
+
expect(errors[0].message).toBe("'myModel' is already declared");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// ============================================================================
|
|
65
|
+
// Cross-type duplicates
|
|
66
|
+
// ============================================================================
|
|
67
|
+
|
|
68
|
+
test('variable and function with same name', () => {
|
|
69
|
+
const ast = parse(`
|
|
70
|
+
let greet = "hello"
|
|
71
|
+
function greet(name: text): text {
|
|
72
|
+
return name
|
|
73
|
+
}
|
|
74
|
+
`);
|
|
75
|
+
const errors = analyze(ast);
|
|
76
|
+
expect(errors.length).toBe(1);
|
|
77
|
+
expect(errors[0].message).toBe("'greet' is already declared");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('variable and model with same name', () => {
|
|
81
|
+
const ast = parse(`
|
|
82
|
+
let myModel = "test"
|
|
83
|
+
model myModel = { name: "test", apiKey: "key", url: "http://test" }
|
|
84
|
+
`);
|
|
85
|
+
const errors = analyze(ast);
|
|
86
|
+
expect(errors.length).toBe(1);
|
|
87
|
+
expect(errors[0].message).toBe("'myModel' is already declared");
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test('function and model with same name', () => {
|
|
91
|
+
const ast = parse(`
|
|
92
|
+
function myModel() {
|
|
93
|
+
return "test"
|
|
94
|
+
}
|
|
95
|
+
model myModel = { name: "test", apiKey: "key", url: "http://test" }
|
|
96
|
+
`);
|
|
97
|
+
const errors = analyze(ast);
|
|
98
|
+
expect(errors.length).toBe(1);
|
|
99
|
+
expect(errors[0].message).toBe("'myModel' is already declared");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// ============================================================================
|
|
103
|
+
// Shadowing in nested scopes (valid)
|
|
104
|
+
// ============================================================================
|
|
105
|
+
|
|
106
|
+
test('shadowing in block scope is valid', () => {
|
|
107
|
+
const ast = parse(`
|
|
108
|
+
let x = "outer"
|
|
109
|
+
if true {
|
|
110
|
+
let x = "inner"
|
|
111
|
+
}
|
|
112
|
+
`);
|
|
113
|
+
const errors = analyze(ast);
|
|
114
|
+
expect(errors.length).toBe(0);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test('shadowing in function scope is valid', () => {
|
|
118
|
+
const ast = parse(`
|
|
119
|
+
let x = "outer"
|
|
120
|
+
function test() {
|
|
121
|
+
let x = "inner"
|
|
122
|
+
return x
|
|
123
|
+
}
|
|
124
|
+
`);
|
|
125
|
+
const errors = analyze(ast);
|
|
126
|
+
expect(errors.length).toBe(0);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test('function parameter shadows outer variable is valid', () => {
|
|
130
|
+
const ast = parse(`
|
|
131
|
+
let name = "outer"
|
|
132
|
+
function greet(name: text): text {
|
|
133
|
+
return name
|
|
134
|
+
}
|
|
135
|
+
`);
|
|
136
|
+
const errors = analyze(ast);
|
|
137
|
+
expect(errors.length).toBe(0);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// ============================================================================
|
|
141
|
+
// Multiple duplicates
|
|
142
|
+
// ============================================================================
|
|
143
|
+
|
|
144
|
+
test('multiple duplicate declarations', () => {
|
|
145
|
+
const ast = parse(`
|
|
146
|
+
let x = "first"
|
|
147
|
+
let x = "second"
|
|
148
|
+
let x = "third"
|
|
149
|
+
`);
|
|
150
|
+
const errors = analyze(ast);
|
|
151
|
+
expect(errors.length).toBe(2);
|
|
152
|
+
expect(errors[0].message).toBe("'x' is already declared");
|
|
153
|
+
expect(errors[1].message).toBe("'x' is already declared");
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test('different duplicate declarations', () => {
|
|
157
|
+
const ast = parse(`
|
|
158
|
+
let x = "first"
|
|
159
|
+
let y = "first"
|
|
160
|
+
let x = "second"
|
|
161
|
+
let y = "second"
|
|
162
|
+
`);
|
|
163
|
+
const errors = analyze(ast);
|
|
164
|
+
expect(errors.length).toBe(2);
|
|
165
|
+
expect(errors[0].message).toBe("'x' is already declared");
|
|
166
|
+
expect(errors[1].message).toBe("'y' is already declared");
|
|
167
|
+
});
|
|
168
|
+
});
|