@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,230 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import { parse } from '../../parse';
|
|
3
|
+
import { ParserError, LexerError } from '../../../errors';
|
|
4
|
+
|
|
5
|
+
describe('Error Locations', () => {
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Parser error locations
|
|
8
|
+
// ============================================================================
|
|
9
|
+
|
|
10
|
+
test('missing closing brace reports error', () => {
|
|
11
|
+
try {
|
|
12
|
+
parse(`
|
|
13
|
+
let x = "first"
|
|
14
|
+
let y = "second"
|
|
15
|
+
if condition {
|
|
16
|
+
let z = "inside"
|
|
17
|
+
`);
|
|
18
|
+
expect.unreachable('Should have thrown');
|
|
19
|
+
} catch (e) {
|
|
20
|
+
expect(e).toBeInstanceOf(ParserError);
|
|
21
|
+
const err = e as ParserError;
|
|
22
|
+
// EOF token may not have line info, but error should exist
|
|
23
|
+
expect(err.location).toBeDefined();
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('unexpected token reports correct line and column', () => {
|
|
28
|
+
try {
|
|
29
|
+
parse(`
|
|
30
|
+
let a = "one"
|
|
31
|
+
let b = "two"
|
|
32
|
+
let = "missing identifier"
|
|
33
|
+
`);
|
|
34
|
+
expect.unreachable('Should have thrown');
|
|
35
|
+
} catch (e) {
|
|
36
|
+
expect(e).toBeInstanceOf(ParserError);
|
|
37
|
+
const err = e as ParserError;
|
|
38
|
+
expect(err.location?.line).toBe(4);
|
|
39
|
+
expect(err.location?.column).toBe(5); // position of '='
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('missing function body reports correct line', () => {
|
|
44
|
+
try {
|
|
45
|
+
parse(`
|
|
46
|
+
let setup = "before"
|
|
47
|
+
|
|
48
|
+
function broken()
|
|
49
|
+
|
|
50
|
+
let after = "this line"
|
|
51
|
+
`);
|
|
52
|
+
expect.unreachable('Should have thrown');
|
|
53
|
+
} catch (e) {
|
|
54
|
+
expect(e).toBeInstanceOf(ParserError);
|
|
55
|
+
const err = e as ParserError;
|
|
56
|
+
expect(err.location?.line).toBe(6); // 'let' on line 6 instead of '{'
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('invalid expression in nested block reports correct line', () => {
|
|
61
|
+
try {
|
|
62
|
+
parse(`
|
|
63
|
+
function outer() {
|
|
64
|
+
let a = "first"
|
|
65
|
+
if condition {
|
|
66
|
+
let b = = "double equals"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
`);
|
|
70
|
+
expect.unreachable('Should have thrown');
|
|
71
|
+
} catch (e) {
|
|
72
|
+
expect(e).toBeInstanceOf(ParserError);
|
|
73
|
+
const err = e as ParserError;
|
|
74
|
+
expect(err.location?.line).toBe(5);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('unclosed string in call reports correct line', () => {
|
|
79
|
+
try {
|
|
80
|
+
parse(`
|
|
81
|
+
let x = "before"
|
|
82
|
+
let y = "middle"
|
|
83
|
+
foo("unclosed
|
|
84
|
+
let z = "after"
|
|
85
|
+
`);
|
|
86
|
+
expect.unreachable('Should have thrown');
|
|
87
|
+
} catch (e) {
|
|
88
|
+
// Unclosed string is a lexer error
|
|
89
|
+
expect(e).toBeInstanceOf(LexerError);
|
|
90
|
+
const err = e as LexerError;
|
|
91
|
+
// Lexer reports error on the line after the unclosed string
|
|
92
|
+
expect(err.location?.line).toBe(5);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('missing closing brace in function reports error', () => {
|
|
97
|
+
try {
|
|
98
|
+
parse(`
|
|
99
|
+
function test() {
|
|
100
|
+
let a = "one"
|
|
101
|
+
let b = "two"
|
|
102
|
+
let c = "three"
|
|
103
|
+
`);
|
|
104
|
+
expect.unreachable('Should have thrown');
|
|
105
|
+
} catch (e) {
|
|
106
|
+
expect(e).toBeInstanceOf(ParserError);
|
|
107
|
+
const err = e as ParserError;
|
|
108
|
+
// EOF token may not have line info, but error should exist
|
|
109
|
+
expect(err.location).toBeDefined();
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// ============================================================================
|
|
114
|
+
// Lexer error locations
|
|
115
|
+
// ============================================================================
|
|
116
|
+
|
|
117
|
+
test('unclosed string reports correct line', () => {
|
|
118
|
+
try {
|
|
119
|
+
parse(`
|
|
120
|
+
let a = "valid"
|
|
121
|
+
let b = "also valid"
|
|
122
|
+
let c = "unclosed
|
|
123
|
+
let d = "never reached"
|
|
124
|
+
`);
|
|
125
|
+
expect.unreachable('Should have thrown');
|
|
126
|
+
} catch (e) {
|
|
127
|
+
expect(e).toBeInstanceOf(LexerError);
|
|
128
|
+
const err = e as LexerError;
|
|
129
|
+
// Lexer reports error on the line after the unclosed string
|
|
130
|
+
expect(err.location?.line).toBe(5);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test('unclosed string with single quotes reports correct line', () => {
|
|
135
|
+
try {
|
|
136
|
+
parse(`
|
|
137
|
+
let first = 'ok'
|
|
138
|
+
let second = 'also ok'
|
|
139
|
+
let third = 'not closed
|
|
140
|
+
let fourth = 'never seen'
|
|
141
|
+
`);
|
|
142
|
+
expect.unreachable('Should have thrown');
|
|
143
|
+
} catch (e) {
|
|
144
|
+
expect(e).toBeInstanceOf(LexerError);
|
|
145
|
+
const err = e as LexerError;
|
|
146
|
+
// Lexer reports error on the line after the unclosed string
|
|
147
|
+
expect(err.location?.line).toBe(5);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// ============================================================================
|
|
152
|
+
// Error formatting
|
|
153
|
+
// ============================================================================
|
|
154
|
+
|
|
155
|
+
test('format() includes source context', () => {
|
|
156
|
+
try {
|
|
157
|
+
parse(`
|
|
158
|
+
let valid = "ok"
|
|
159
|
+
let broken =
|
|
160
|
+
let after = "never"
|
|
161
|
+
`);
|
|
162
|
+
expect.unreachable('Should have thrown');
|
|
163
|
+
} catch (e) {
|
|
164
|
+
expect(e).toBeInstanceOf(ParserError);
|
|
165
|
+
const err = e as ParserError;
|
|
166
|
+
const formatted = err.format();
|
|
167
|
+
expect(formatted).toContain('[vibe:');
|
|
168
|
+
expect(formatted).toContain('4:'); // line 4 (where 'let after' is)
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test('format() shows caret pointing to error', () => {
|
|
173
|
+
try {
|
|
174
|
+
parse(`
|
|
175
|
+
let x = "hello"
|
|
176
|
+
let y = "world"
|
|
177
|
+
function () {
|
|
178
|
+
return "missing name"
|
|
179
|
+
}
|
|
180
|
+
`);
|
|
181
|
+
expect.unreachable('Should have thrown');
|
|
182
|
+
} catch (e) {
|
|
183
|
+
expect(e).toBeInstanceOf(ParserError);
|
|
184
|
+
const err = e as ParserError;
|
|
185
|
+
const formatted = err.format();
|
|
186
|
+
expect(formatted).toContain('^'); // caret indicator
|
|
187
|
+
expect(formatted).toContain('|'); // line separator
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// ============================================================================
|
|
192
|
+
// Column accuracy
|
|
193
|
+
// ============================================================================
|
|
194
|
+
|
|
195
|
+
test('column points to exact token position', () => {
|
|
196
|
+
try {
|
|
197
|
+
parse(`
|
|
198
|
+
let validName = "ok"
|
|
199
|
+
let = "missing identifier"
|
|
200
|
+
`);
|
|
201
|
+
expect.unreachable('Should have thrown');
|
|
202
|
+
} catch (e) {
|
|
203
|
+
expect(e).toBeInstanceOf(ParserError);
|
|
204
|
+
const err = e as ParserError;
|
|
205
|
+
expect(err.location?.line).toBe(3);
|
|
206
|
+
expect(err.location?.column).toBe(8); // position of '=' after spaces
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test('deeply nested error has correct location', () => {
|
|
211
|
+
try {
|
|
212
|
+
parse(`
|
|
213
|
+
function level1() {
|
|
214
|
+
function level2() {
|
|
215
|
+
if a {
|
|
216
|
+
if b {
|
|
217
|
+
let x = ,
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
`);
|
|
223
|
+
expect.unreachable('Should have thrown');
|
|
224
|
+
} catch (e) {
|
|
225
|
+
expect(e).toBeInstanceOf(ParserError);
|
|
226
|
+
const err = e as ParserError;
|
|
227
|
+
expect(err.location?.line).toBe(6);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
});
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import { parse } from '../../parse';
|
|
3
|
+
|
|
4
|
+
describe('Syntax Errors - Invalid Expressions', () => {
|
|
5
|
+
// ============================================================================
|
|
6
|
+
// Invalid call expressions
|
|
7
|
+
// ============================================================================
|
|
8
|
+
|
|
9
|
+
test('call with leading comma', () => {
|
|
10
|
+
expect(() => parse(`
|
|
11
|
+
foo(, "hello")
|
|
12
|
+
`)).toThrow();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('call with trailing comma', () => {
|
|
16
|
+
expect(() => parse(`
|
|
17
|
+
foo("hello",)
|
|
18
|
+
`)).toThrow();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('call with double comma', () => {
|
|
22
|
+
expect(() => parse(`
|
|
23
|
+
foo("a",, "b")
|
|
24
|
+
`)).toThrow();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('call with only comma', () => {
|
|
28
|
+
expect(() => parse(`
|
|
29
|
+
foo(,)
|
|
30
|
+
`)).toThrow();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// ============================================================================
|
|
34
|
+
// Invalid function parameters
|
|
35
|
+
// ============================================================================
|
|
36
|
+
|
|
37
|
+
test('function params with leading comma', () => {
|
|
38
|
+
expect(() => parse(`
|
|
39
|
+
function test(, a) {
|
|
40
|
+
return a
|
|
41
|
+
}
|
|
42
|
+
`)).toThrow();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('function params with trailing comma', () => {
|
|
46
|
+
expect(() => parse(`
|
|
47
|
+
function test(a,) {
|
|
48
|
+
return a
|
|
49
|
+
}
|
|
50
|
+
`)).toThrow();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('function params with double comma', () => {
|
|
54
|
+
expect(() => parse(`
|
|
55
|
+
function test(a,, b) {
|
|
56
|
+
return a
|
|
57
|
+
}
|
|
58
|
+
`)).toThrow();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// ============================================================================
|
|
62
|
+
// Invalid primary expressions
|
|
63
|
+
// ============================================================================
|
|
64
|
+
|
|
65
|
+
test('empty parentheses as expression', () => {
|
|
66
|
+
expect(() => parse(`
|
|
67
|
+
let x = ()
|
|
68
|
+
`)).toThrow();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('just equals sign', () => {
|
|
72
|
+
expect(() => parse(`
|
|
73
|
+
=
|
|
74
|
+
`)).toThrow();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('just comma', () => {
|
|
78
|
+
expect(() => parse(`
|
|
79
|
+
,
|
|
80
|
+
`)).toThrow();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// ============================================================================
|
|
84
|
+
// Invalid do/vibe expressions
|
|
85
|
+
// ============================================================================
|
|
86
|
+
|
|
87
|
+
test('vibe with equals instead of expression', () => {
|
|
88
|
+
expect(() => parse(`
|
|
89
|
+
let x = vibe =
|
|
90
|
+
`)).toThrow();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('vibe with comma instead of expression', () => {
|
|
94
|
+
expect(() => parse(`
|
|
95
|
+
let x = vibe ,
|
|
96
|
+
`)).toThrow();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('vibe with closing paren', () => {
|
|
100
|
+
expect(() => parse(`
|
|
101
|
+
let x = vibe )
|
|
102
|
+
`)).toThrow();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// ============================================================================
|
|
106
|
+
// Invalid if conditions
|
|
107
|
+
// ============================================================================
|
|
108
|
+
|
|
109
|
+
test('if with empty condition', () => {
|
|
110
|
+
expect(() => parse(`
|
|
111
|
+
if {
|
|
112
|
+
let x = "yes"
|
|
113
|
+
}
|
|
114
|
+
`)).toThrow();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test('if with equals as condition', () => {
|
|
118
|
+
expect(() => parse(`
|
|
119
|
+
if = {
|
|
120
|
+
let x = "yes"
|
|
121
|
+
}
|
|
122
|
+
`)).toThrow();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// ============================================================================
|
|
126
|
+
// Invalid return expressions
|
|
127
|
+
// ============================================================================
|
|
128
|
+
|
|
129
|
+
test('return with comma', () => {
|
|
130
|
+
expect(() => parse(`
|
|
131
|
+
function test() {
|
|
132
|
+
return ,
|
|
133
|
+
}
|
|
134
|
+
`)).toThrow();
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test('return with only equals', () => {
|
|
138
|
+
expect(() => parse(`
|
|
139
|
+
function test() {
|
|
140
|
+
return =
|
|
141
|
+
}
|
|
142
|
+
`)).toThrow();
|
|
143
|
+
});
|
|
144
|
+
});
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import { parse } from '../../parse';
|
|
3
|
+
|
|
4
|
+
describe('Syntax Errors - Missing Tokens', () => {
|
|
5
|
+
// ============================================================================
|
|
6
|
+
// const declaration
|
|
7
|
+
// ============================================================================
|
|
8
|
+
|
|
9
|
+
test('const missing equals and initializer', () => {
|
|
10
|
+
expect(() => parse(`
|
|
11
|
+
const x
|
|
12
|
+
`)).toThrow();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('const missing initializer after equals', () => {
|
|
16
|
+
expect(() => parse(`
|
|
17
|
+
const x =
|
|
18
|
+
`)).toThrow();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// let declaration
|
|
23
|
+
// ============================================================================
|
|
24
|
+
|
|
25
|
+
test('let missing initializer after equals', () => {
|
|
26
|
+
expect(() => parse(`
|
|
27
|
+
let x =
|
|
28
|
+
`)).toThrow();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// ============================================================================
|
|
32
|
+
// function declaration
|
|
33
|
+
// ============================================================================
|
|
34
|
+
|
|
35
|
+
test('function missing closing paren', () => {
|
|
36
|
+
expect(() => parse(`
|
|
37
|
+
function foo(
|
|
38
|
+
`)).toThrow();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('function missing body', () => {
|
|
42
|
+
expect(() => parse(`
|
|
43
|
+
function foo()
|
|
44
|
+
`)).toThrow();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('function with params missing closing paren', () => {
|
|
48
|
+
expect(() => parse(`
|
|
49
|
+
function greet(name
|
|
50
|
+
`)).toThrow();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('function missing opening paren', () => {
|
|
54
|
+
expect(() => parse(`
|
|
55
|
+
function foo {
|
|
56
|
+
return "hello"
|
|
57
|
+
}
|
|
58
|
+
`)).toThrow();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// ============================================================================
|
|
62
|
+
// if statement
|
|
63
|
+
// ============================================================================
|
|
64
|
+
|
|
65
|
+
test('if missing block', () => {
|
|
66
|
+
expect(() => parse(`
|
|
67
|
+
if true
|
|
68
|
+
`)).toThrow();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('if with else missing block', () => {
|
|
72
|
+
expect(() => parse(`
|
|
73
|
+
if true {
|
|
74
|
+
let x = "yes"
|
|
75
|
+
} else
|
|
76
|
+
`)).toThrow();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// ============================================================================
|
|
80
|
+
// return statement
|
|
81
|
+
// ============================================================================
|
|
82
|
+
|
|
83
|
+
test('return with incomplete expression', () => {
|
|
84
|
+
expect(() => parse(`
|
|
85
|
+
function test() {
|
|
86
|
+
return =
|
|
87
|
+
}
|
|
88
|
+
`)).toThrow();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// ============================================================================
|
|
92
|
+
// vibe expression
|
|
93
|
+
// ============================================================================
|
|
94
|
+
|
|
95
|
+
test('vibe missing prompt', () => {
|
|
96
|
+
expect(() => parse(`
|
|
97
|
+
let x = do
|
|
98
|
+
`)).toThrow();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// ============================================================================
|
|
102
|
+
// vibe expression
|
|
103
|
+
// ============================================================================
|
|
104
|
+
|
|
105
|
+
test('vibe missing prompt', () => {
|
|
106
|
+
expect(() => parse(`
|
|
107
|
+
let x = vibe
|
|
108
|
+
`)).toThrow();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// ============================================================================
|
|
112
|
+
// call expression
|
|
113
|
+
// ============================================================================
|
|
114
|
+
|
|
115
|
+
test('call missing closing paren', () => {
|
|
116
|
+
expect(() => parse(`
|
|
117
|
+
foo(
|
|
118
|
+
`)).toThrow();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test('call with args missing closing paren', () => {
|
|
122
|
+
expect(() => parse(`
|
|
123
|
+
greet("hello"
|
|
124
|
+
`)).toThrow();
|
|
125
|
+
});
|
|
126
|
+
});
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import { parse } from '../../parse';
|
|
3
|
+
|
|
4
|
+
describe('Syntax Errors - Model Declaration', () => {
|
|
5
|
+
// ============================================================================
|
|
6
|
+
// Missing model components
|
|
7
|
+
// ============================================================================
|
|
8
|
+
|
|
9
|
+
test('model missing name', () => {
|
|
10
|
+
expect(() => parse(`
|
|
11
|
+
model = {
|
|
12
|
+
name: "test"
|
|
13
|
+
}
|
|
14
|
+
`)).toThrow();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('model missing equals sign', () => {
|
|
18
|
+
expect(() => parse(`
|
|
19
|
+
model myModel {
|
|
20
|
+
name: "test"
|
|
21
|
+
}
|
|
22
|
+
`)).toThrow();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('model missing object literal', () => {
|
|
26
|
+
expect(() => parse(`
|
|
27
|
+
model myModel =
|
|
28
|
+
`)).toThrow();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('model with string instead of object', () => {
|
|
32
|
+
expect(() => parse(`
|
|
33
|
+
model myModel = "not an object"
|
|
34
|
+
`)).toThrow();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('model with identifier instead of object', () => {
|
|
38
|
+
expect(() => parse(`
|
|
39
|
+
model myModel = otherModel
|
|
40
|
+
`)).toThrow();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// ============================================================================
|
|
44
|
+
// Object literal errors
|
|
45
|
+
// ============================================================================
|
|
46
|
+
|
|
47
|
+
test('model with unclosed brace', () => {
|
|
48
|
+
expect(() => parse(`
|
|
49
|
+
model myModel = {
|
|
50
|
+
name: "test"
|
|
51
|
+
`)).toThrow();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('model with only opening brace', () => {
|
|
55
|
+
expect(() => parse(`
|
|
56
|
+
model myModel = {
|
|
57
|
+
`)).toThrow();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('model missing opening brace', () => {
|
|
61
|
+
expect(() => parse(`
|
|
62
|
+
model myModel = name: "test" }
|
|
63
|
+
`)).toThrow();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// ============================================================================
|
|
67
|
+
// Property errors
|
|
68
|
+
// ============================================================================
|
|
69
|
+
|
|
70
|
+
test('property missing colon', () => {
|
|
71
|
+
expect(() => parse(`
|
|
72
|
+
model myModel = {
|
|
73
|
+
name "test"
|
|
74
|
+
}
|
|
75
|
+
`)).toThrow();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('property missing value', () => {
|
|
79
|
+
expect(() => parse(`
|
|
80
|
+
model myModel = {
|
|
81
|
+
name:
|
|
82
|
+
}
|
|
83
|
+
`)).toThrow();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('property with trailing comma only', () => {
|
|
87
|
+
expect(() => parse(`
|
|
88
|
+
model myModel = {
|
|
89
|
+
,
|
|
90
|
+
}
|
|
91
|
+
`)).toThrow();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('property with double colon', () => {
|
|
95
|
+
expect(() => parse(`
|
|
96
|
+
model myModel = {
|
|
97
|
+
name:: "test"
|
|
98
|
+
}
|
|
99
|
+
`)).toThrow();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('property with equals instead of colon', () => {
|
|
103
|
+
expect(() => parse(`
|
|
104
|
+
model myModel = {
|
|
105
|
+
name = "test"
|
|
106
|
+
}
|
|
107
|
+
`)).toThrow();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test('property key as string literal', () => {
|
|
111
|
+
expect(() => parse(`
|
|
112
|
+
model myModel = {
|
|
113
|
+
"name": "test"
|
|
114
|
+
}
|
|
115
|
+
`)).toThrow();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test('property with missing key', () => {
|
|
119
|
+
expect(() => parse(`
|
|
120
|
+
model myModel = {
|
|
121
|
+
: "test"
|
|
122
|
+
}
|
|
123
|
+
`)).toThrow();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// ============================================================================
|
|
127
|
+
// Multiple properties errors
|
|
128
|
+
// ============================================================================
|
|
129
|
+
|
|
130
|
+
test('properties missing comma separator', () => {
|
|
131
|
+
expect(() => parse(`
|
|
132
|
+
model myModel = {
|
|
133
|
+
name: "test"
|
|
134
|
+
apiKey: "key"
|
|
135
|
+
}
|
|
136
|
+
`)).toThrow();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test('properties with double comma', () => {
|
|
140
|
+
expect(() => parse(`
|
|
141
|
+
model myModel = {
|
|
142
|
+
name: "test",,
|
|
143
|
+
apiKey: "key"
|
|
144
|
+
}
|
|
145
|
+
`)).toThrow();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test('trailing comma without next property', () => {
|
|
149
|
+
expect(() => parse(`
|
|
150
|
+
model myModel = {
|
|
151
|
+
name: "test",
|
|
152
|
+
}
|
|
153
|
+
`)).toThrow();
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// ============================================================================
|
|
157
|
+
// Model keyword misuse
|
|
158
|
+
// ============================================================================
|
|
159
|
+
|
|
160
|
+
test('model as variable name', () => {
|
|
161
|
+
expect(() => parse(`
|
|
162
|
+
let model = "test"
|
|
163
|
+
`)).toThrow();
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test('nested model in property', () => {
|
|
167
|
+
expect(() => parse(`
|
|
168
|
+
model outer = {
|
|
169
|
+
inner: model nested = {}
|
|
170
|
+
}
|
|
171
|
+
`)).toThrow();
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// ============================================================================
|
|
175
|
+
// Model with invalid property values
|
|
176
|
+
// ============================================================================
|
|
177
|
+
|
|
178
|
+
test('model property with unclosed string', () => {
|
|
179
|
+
expect(() => parse(`
|
|
180
|
+
model myModel = {
|
|
181
|
+
name: "unclosed
|
|
182
|
+
}
|
|
183
|
+
`)).toThrow();
|
|
184
|
+
});
|
|
185
|
+
});
|