@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,136 @@
|
|
|
1
|
+
import { describe, it, expect } from 'bun:test';
|
|
2
|
+
import { parse } from '../parse';
|
|
3
|
+
import type * as AST from '../../ast';
|
|
4
|
+
|
|
5
|
+
describe('private keyword parsing', () => {
|
|
6
|
+
describe('let private', () => {
|
|
7
|
+
it('parses let private with type annotation', () => {
|
|
8
|
+
const code = 'let private apiKey: text = "secret"';
|
|
9
|
+
const ast = parse(code);
|
|
10
|
+
|
|
11
|
+
expect(ast.body.length).toBe(1);
|
|
12
|
+
const decl = ast.body[0] as AST.LetDeclaration;
|
|
13
|
+
expect(decl.type).toBe('LetDeclaration');
|
|
14
|
+
expect(decl.name).toBe('apiKey');
|
|
15
|
+
expect(decl.typeAnnotation).toBe('text');
|
|
16
|
+
expect(decl.isPrivate).toBe(true);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('parses let private without type annotation', () => {
|
|
20
|
+
const code = 'let private counter = 0';
|
|
21
|
+
const ast = parse(code);
|
|
22
|
+
|
|
23
|
+
const decl = ast.body[0] as AST.LetDeclaration;
|
|
24
|
+
expect(decl.type).toBe('LetDeclaration');
|
|
25
|
+
expect(decl.name).toBe('counter');
|
|
26
|
+
expect(decl.typeAnnotation).toBeNull();
|
|
27
|
+
expect(decl.isPrivate).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('parses regular let without private', () => {
|
|
31
|
+
const code = 'let publicVar: text = "visible"';
|
|
32
|
+
const ast = parse(code);
|
|
33
|
+
|
|
34
|
+
const decl = ast.body[0] as AST.LetDeclaration;
|
|
35
|
+
expect(decl.type).toBe('LetDeclaration');
|
|
36
|
+
expect(decl.name).toBe('publicVar');
|
|
37
|
+
expect(decl.isPrivate).toBeUndefined();
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe('const private', () => {
|
|
42
|
+
it('parses const private with type annotation', () => {
|
|
43
|
+
const code = 'const private SECRET: text = "password"';
|
|
44
|
+
const ast = parse(code);
|
|
45
|
+
|
|
46
|
+
expect(ast.body.length).toBe(1);
|
|
47
|
+
const decl = ast.body[0] as AST.ConstDeclaration;
|
|
48
|
+
expect(decl.type).toBe('ConstDeclaration');
|
|
49
|
+
expect(decl.name).toBe('SECRET');
|
|
50
|
+
expect(decl.typeAnnotation).toBe('text');
|
|
51
|
+
expect(decl.isPrivate).toBe(true);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('parses const private without type annotation', () => {
|
|
55
|
+
const code = 'const private MAX_RETRIES = 3';
|
|
56
|
+
const ast = parse(code);
|
|
57
|
+
|
|
58
|
+
const decl = ast.body[0] as AST.ConstDeclaration;
|
|
59
|
+
expect(decl.type).toBe('ConstDeclaration');
|
|
60
|
+
expect(decl.name).toBe('MAX_RETRIES');
|
|
61
|
+
expect(decl.isPrivate).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('parses regular const without private', () => {
|
|
65
|
+
const code = 'const publicConst = "visible"';
|
|
66
|
+
const ast = parse(code);
|
|
67
|
+
|
|
68
|
+
const decl = ast.body[0] as AST.ConstDeclaration;
|
|
69
|
+
expect(decl.type).toBe('ConstDeclaration');
|
|
70
|
+
expect(decl.name).toBe('publicConst');
|
|
71
|
+
expect(decl.isPrivate).toBeUndefined();
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('destructuring with private fields', () => {
|
|
76
|
+
it('parses destructuring with private field', () => {
|
|
77
|
+
const code = 'let {private x: text, y: number} = someExpr';
|
|
78
|
+
const ast = parse(code);
|
|
79
|
+
|
|
80
|
+
const decl = ast.body[0] as AST.DestructuringDeclaration;
|
|
81
|
+
expect(decl.type).toBe('DestructuringDeclaration');
|
|
82
|
+
expect(decl.fields.length).toBe(2);
|
|
83
|
+
|
|
84
|
+
expect(decl.fields[0].name).toBe('x');
|
|
85
|
+
expect(decl.fields[0].type).toBe('text');
|
|
86
|
+
expect(decl.fields[0].isPrivate).toBe(true);
|
|
87
|
+
|
|
88
|
+
expect(decl.fields[1].name).toBe('y');
|
|
89
|
+
expect(decl.fields[1].type).toBe('number');
|
|
90
|
+
expect(decl.fields[1].isPrivate).toBeUndefined();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('parses destructuring with all private fields', () => {
|
|
94
|
+
const code = 'const {private a: text, private b: number} = someExpr';
|
|
95
|
+
const ast = parse(code);
|
|
96
|
+
|
|
97
|
+
const decl = ast.body[0] as AST.DestructuringDeclaration;
|
|
98
|
+
expect(decl.isConst).toBe(true);
|
|
99
|
+
expect(decl.fields.length).toBe(2);
|
|
100
|
+
|
|
101
|
+
expect(decl.fields[0].name).toBe('a');
|
|
102
|
+
expect(decl.fields[0].isPrivate).toBe(true);
|
|
103
|
+
|
|
104
|
+
expect(decl.fields[1].name).toBe('b');
|
|
105
|
+
expect(decl.fields[1].isPrivate).toBe(true);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('parses destructuring with no private fields', () => {
|
|
109
|
+
const code = 'let {x: text, y: number} = someExpr';
|
|
110
|
+
const ast = parse(code);
|
|
111
|
+
|
|
112
|
+
const decl = ast.body[0] as AST.DestructuringDeclaration;
|
|
113
|
+
expect(decl.fields[0].isPrivate).toBeUndefined();
|
|
114
|
+
expect(decl.fields[1].isPrivate).toBeUndefined();
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe('mixed declarations', () => {
|
|
119
|
+
it('parses multiple declarations with mixed visibility', () => {
|
|
120
|
+
const code = `
|
|
121
|
+
let private secret: text = "hidden"
|
|
122
|
+
let visible: text = "shown"
|
|
123
|
+
const private PASSWORD: text = "***"
|
|
124
|
+
const PUBLIC_KEY: text = "key"
|
|
125
|
+
`;
|
|
126
|
+
const ast = parse(code);
|
|
127
|
+
|
|
128
|
+
expect(ast.body.length).toBe(4);
|
|
129
|
+
|
|
130
|
+
expect((ast.body[0] as AST.LetDeclaration).isPrivate).toBe(true);
|
|
131
|
+
expect((ast.body[1] as AST.LetDeclaration).isPrivate).toBeUndefined();
|
|
132
|
+
expect((ast.body[2] as AST.ConstDeclaration).isPrivate).toBe(true);
|
|
133
|
+
expect((ast.body[3] as AST.ConstDeclaration).isPrivate).toBeUndefined();
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
});
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import { parse } from '../parse';
|
|
3
|
+
|
|
4
|
+
describe('Parser - Template Literals', () => {
|
|
5
|
+
test('basic template literal', () => {
|
|
6
|
+
const ast = parse('let x = `hello`');
|
|
7
|
+
expect(ast.body).toHaveLength(1);
|
|
8
|
+
expect(ast.body[0]).toMatchObject({
|
|
9
|
+
type: 'LetDeclaration',
|
|
10
|
+
name: 'x',
|
|
11
|
+
initializer: {
|
|
12
|
+
type: 'TemplateLiteral',
|
|
13
|
+
value: 'hello',
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('template literal with interpolation syntax preserved', () => {
|
|
19
|
+
const ast = parse('let x = `hello ${name}`');
|
|
20
|
+
expect(ast.body).toHaveLength(1);
|
|
21
|
+
expect(ast.body[0]).toMatchObject({
|
|
22
|
+
type: 'LetDeclaration',
|
|
23
|
+
name: 'x',
|
|
24
|
+
initializer: {
|
|
25
|
+
type: 'TemplateLiteral',
|
|
26
|
+
value: 'hello ${name}',
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('template literal multiline', () => {
|
|
32
|
+
const ast = parse(`let x = \`line1
|
|
33
|
+
line2\``);
|
|
34
|
+
expect(ast.body).toHaveLength(1);
|
|
35
|
+
const init = (ast.body[0] as any).initializer;
|
|
36
|
+
expect(init.type).toBe('TemplateLiteral');
|
|
37
|
+
expect(init.value).toBe('line1\nline2');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('template literal with escape sequences', () => {
|
|
41
|
+
// Note: Current escape handling just removes the backslash
|
|
42
|
+
// Full JS-style escape handling (\\n → newline) could be added later
|
|
43
|
+
const ast = parse('let x = `hello\\nworld`');
|
|
44
|
+
expect(ast.body).toHaveLength(1);
|
|
45
|
+
expect(ast.body[0]).toMatchObject({
|
|
46
|
+
type: 'LetDeclaration',
|
|
47
|
+
name: 'x',
|
|
48
|
+
initializer: {
|
|
49
|
+
type: 'TemplateLiteral',
|
|
50
|
+
value: 'hellonworld',
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('template literal with escaped backtick', () => {
|
|
56
|
+
const ast = parse('let x = `hello\\`world`');
|
|
57
|
+
expect(ast.body).toHaveLength(1);
|
|
58
|
+
expect(ast.body[0]).toMatchObject({
|
|
59
|
+
type: 'LetDeclaration',
|
|
60
|
+
name: 'x',
|
|
61
|
+
initializer: {
|
|
62
|
+
type: 'TemplateLiteral',
|
|
63
|
+
value: 'hello`world',
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('template literal in const declaration', () => {
|
|
69
|
+
const ast = parse('const msg = `hello world`');
|
|
70
|
+
expect(ast.body).toHaveLength(1);
|
|
71
|
+
expect(ast.body[0]).toMatchObject({
|
|
72
|
+
type: 'ConstDeclaration',
|
|
73
|
+
name: 'msg',
|
|
74
|
+
initializer: {
|
|
75
|
+
type: 'TemplateLiteral',
|
|
76
|
+
value: 'hello world',
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('template literal in function return', () => {
|
|
82
|
+
const ast = parse(`
|
|
83
|
+
function greet() {
|
|
84
|
+
return \`hello\`
|
|
85
|
+
}
|
|
86
|
+
`);
|
|
87
|
+
const func = ast.body[0] as any;
|
|
88
|
+
const returnStmt = func.body.body[0];
|
|
89
|
+
expect(returnStmt.type).toBe('ReturnStatement');
|
|
90
|
+
expect(returnStmt.value.type).toBe('TemplateLiteral');
|
|
91
|
+
expect(returnStmt.value.value).toBe('hello');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('template literal as function argument', () => {
|
|
95
|
+
const ast = parse('myFunc(`hello ${name}`)');
|
|
96
|
+
const call = (ast.body[0] as any).expression;
|
|
97
|
+
expect(call.type).toBe('CallExpression');
|
|
98
|
+
expect(call.arguments[0].type).toBe('TemplateLiteral');
|
|
99
|
+
expect(call.arguments[0].value).toBe('hello ${name}');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('template literal with multiple interpolations', () => {
|
|
103
|
+
const ast = parse('let x = `${greeting}, ${name}!`');
|
|
104
|
+
expect(ast.body).toHaveLength(1);
|
|
105
|
+
expect(ast.body[0]).toMatchObject({
|
|
106
|
+
type: 'LetDeclaration',
|
|
107
|
+
name: 'x',
|
|
108
|
+
initializer: {
|
|
109
|
+
type: 'TemplateLiteral',
|
|
110
|
+
value: '${greeting}, ${name}!',
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('empty template literal', () => {
|
|
116
|
+
const ast = parse('let x = ``');
|
|
117
|
+
expect(ast.body).toHaveLength(1);
|
|
118
|
+
expect(ast.body[0]).toMatchObject({
|
|
119
|
+
type: 'LetDeclaration',
|
|
120
|
+
name: 'x',
|
|
121
|
+
initializer: {
|
|
122
|
+
type: 'TemplateLiteral',
|
|
123
|
+
value: '',
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
});
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import { parse } from '../parse';
|
|
3
|
+
|
|
4
|
+
describe('Parser - Tool Declaration', () => {
|
|
5
|
+
// ============================================================================
|
|
6
|
+
// Basic tool declarations
|
|
7
|
+
// ============================================================================
|
|
8
|
+
|
|
9
|
+
test('simple tool with no parameters', () => {
|
|
10
|
+
const ast = parse(`
|
|
11
|
+
tool getCurrentTime(): text
|
|
12
|
+
@description "Get the current time"
|
|
13
|
+
{
|
|
14
|
+
ts() {
|
|
15
|
+
return new Date().toISOString()
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
`);
|
|
19
|
+
expect(ast.body).toHaveLength(1);
|
|
20
|
+
expect(ast.body[0]).toMatchObject({
|
|
21
|
+
type: 'ToolDeclaration',
|
|
22
|
+
name: 'getCurrentTime',
|
|
23
|
+
params: [],
|
|
24
|
+
returnType: 'text',
|
|
25
|
+
description: 'Get the current time',
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('tool with single parameter', () => {
|
|
30
|
+
const ast = parse(`
|
|
31
|
+
tool greet(name: text): text
|
|
32
|
+
@description "Greet someone"
|
|
33
|
+
{
|
|
34
|
+
ts(name) {
|
|
35
|
+
return "Hello, " + name
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
`);
|
|
39
|
+
expect(ast.body).toHaveLength(1);
|
|
40
|
+
expect(ast.body[0]).toMatchObject({
|
|
41
|
+
type: 'ToolDeclaration',
|
|
42
|
+
name: 'greet',
|
|
43
|
+
params: [
|
|
44
|
+
{ name: 'name', typeAnnotation: 'text' },
|
|
45
|
+
],
|
|
46
|
+
returnType: 'text',
|
|
47
|
+
description: 'Greet someone',
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('tool with multiple parameters', () => {
|
|
52
|
+
const ast = parse(`
|
|
53
|
+
tool calculate(x: number, y: number, op: text): number
|
|
54
|
+
@description "Perform a calculation"
|
|
55
|
+
{
|
|
56
|
+
ts(x, y, op) {
|
|
57
|
+
if (op === "add") return x + y
|
|
58
|
+
if (op === "mul") return x * y
|
|
59
|
+
return 0
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
`);
|
|
63
|
+
expect(ast.body).toHaveLength(1);
|
|
64
|
+
expect(ast.body[0]).toMatchObject({
|
|
65
|
+
type: 'ToolDeclaration',
|
|
66
|
+
name: 'calculate',
|
|
67
|
+
params: [
|
|
68
|
+
{ name: 'x', typeAnnotation: 'number' },
|
|
69
|
+
{ name: 'y', typeAnnotation: 'number' },
|
|
70
|
+
{ name: 'op', typeAnnotation: 'text' },
|
|
71
|
+
],
|
|
72
|
+
returnType: 'number',
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// ============================================================================
|
|
77
|
+
// Tool with @param descriptions
|
|
78
|
+
// ============================================================================
|
|
79
|
+
|
|
80
|
+
test('tool with @param descriptions', () => {
|
|
81
|
+
const ast = parse(`
|
|
82
|
+
tool fetchUrl(url: text): json
|
|
83
|
+
@description "Fetch data from a URL"
|
|
84
|
+
@param url "The URL to fetch from"
|
|
85
|
+
{
|
|
86
|
+
ts(url) {
|
|
87
|
+
const response = await fetch(url)
|
|
88
|
+
return await response.json()
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
`);
|
|
92
|
+
expect(ast.body).toHaveLength(1);
|
|
93
|
+
expect(ast.body[0]).toMatchObject({
|
|
94
|
+
type: 'ToolDeclaration',
|
|
95
|
+
name: 'fetchUrl',
|
|
96
|
+
params: [
|
|
97
|
+
{ name: 'url', typeAnnotation: 'text', description: 'The URL to fetch from' },
|
|
98
|
+
],
|
|
99
|
+
description: 'Fetch data from a URL',
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('tool with multiple @param descriptions', () => {
|
|
104
|
+
const ast = parse(`
|
|
105
|
+
tool sendEmail(to: text, subject: text, body: text): boolean
|
|
106
|
+
@description "Send an email"
|
|
107
|
+
@param to "The recipient email address"
|
|
108
|
+
@param subject "The email subject line"
|
|
109
|
+
@param body "The email body content"
|
|
110
|
+
{
|
|
111
|
+
ts(to, subject, body) {
|
|
112
|
+
return true
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
`);
|
|
116
|
+
expect(ast.body).toHaveLength(1);
|
|
117
|
+
const toolDecl = ast.body[0];
|
|
118
|
+
expect(toolDecl.type).toBe('ToolDeclaration');
|
|
119
|
+
if (toolDecl.type === 'ToolDeclaration') {
|
|
120
|
+
expect(toolDecl.params).toHaveLength(3);
|
|
121
|
+
expect(toolDecl.params[0].description).toBe('The recipient email address');
|
|
122
|
+
expect(toolDecl.params[1].description).toBe('The email subject line');
|
|
123
|
+
expect(toolDecl.params[2].description).toBe('The email body content');
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// ============================================================================
|
|
128
|
+
// Tool type annotations
|
|
129
|
+
// ============================================================================
|
|
130
|
+
|
|
131
|
+
test('tool with json return type', () => {
|
|
132
|
+
const ast = parse(`
|
|
133
|
+
tool getData(): json
|
|
134
|
+
@description "Get some data"
|
|
135
|
+
{
|
|
136
|
+
ts() {
|
|
137
|
+
return { foo: "bar" }
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
`);
|
|
141
|
+
expect(ast.body).toHaveLength(1);
|
|
142
|
+
expect(ast.body[0]).toMatchObject({
|
|
143
|
+
type: 'ToolDeclaration',
|
|
144
|
+
name: 'getData',
|
|
145
|
+
returnType: 'json',
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test('tool with boolean return type', () => {
|
|
150
|
+
const ast = parse(`
|
|
151
|
+
tool isValid(value: text): boolean
|
|
152
|
+
@description "Check if value is valid"
|
|
153
|
+
{
|
|
154
|
+
ts(value) {
|
|
155
|
+
return value.length > 0
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
`);
|
|
159
|
+
expect(ast.body).toHaveLength(1);
|
|
160
|
+
expect(ast.body[0]).toMatchObject({
|
|
161
|
+
type: 'ToolDeclaration',
|
|
162
|
+
returnType: 'boolean',
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test('tool with array type parameter', () => {
|
|
167
|
+
const ast = parse(`
|
|
168
|
+
tool sum(numbers: number[]): number
|
|
169
|
+
@description "Sum all numbers"
|
|
170
|
+
{
|
|
171
|
+
ts(numbers) {
|
|
172
|
+
return numbers.reduce((a, b) => a + b, 0)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
`);
|
|
176
|
+
expect(ast.body).toHaveLength(1);
|
|
177
|
+
expect(ast.body[0]).toMatchObject({
|
|
178
|
+
type: 'ToolDeclaration',
|
|
179
|
+
params: [
|
|
180
|
+
{ name: 'numbers', typeAnnotation: 'number[]' },
|
|
181
|
+
],
|
|
182
|
+
returnType: 'number',
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// ============================================================================
|
|
187
|
+
// Tool with imported TypeScript types
|
|
188
|
+
// ============================================================================
|
|
189
|
+
|
|
190
|
+
test('tool with imported type as parameter', () => {
|
|
191
|
+
// Note: Currently uses regular import syntax. Type imports would be:
|
|
192
|
+
// import type { CustomerInfo } from "./types.ts"
|
|
193
|
+
const ast = parse(`
|
|
194
|
+
import { CustomerInfo } from "./types.ts"
|
|
195
|
+
|
|
196
|
+
tool processCustomer(info: CustomerInfo): json
|
|
197
|
+
@description "Process customer information"
|
|
198
|
+
{
|
|
199
|
+
ts(info) {
|
|
200
|
+
return { processed: true }
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
`);
|
|
204
|
+
expect(ast.body).toHaveLength(2);
|
|
205
|
+
expect(ast.body[0].type).toBe('ImportDeclaration');
|
|
206
|
+
expect(ast.body[1]).toMatchObject({
|
|
207
|
+
type: 'ToolDeclaration',
|
|
208
|
+
name: 'processCustomer',
|
|
209
|
+
params: [
|
|
210
|
+
{ name: 'info', typeAnnotation: 'CustomerInfo' },
|
|
211
|
+
],
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// ============================================================================
|
|
216
|
+
// Multiple tools
|
|
217
|
+
// ============================================================================
|
|
218
|
+
|
|
219
|
+
test('multiple tool declarations', () => {
|
|
220
|
+
const ast = parse(`
|
|
221
|
+
tool add(a: number, b: number): number
|
|
222
|
+
@description "Add two numbers"
|
|
223
|
+
{
|
|
224
|
+
ts(a, b) { return a + b }
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
tool multiply(a: number, b: number): number
|
|
228
|
+
@description "Multiply two numbers"
|
|
229
|
+
{
|
|
230
|
+
ts(a, b) { return a * b }
|
|
231
|
+
}
|
|
232
|
+
`);
|
|
233
|
+
expect(ast.body).toHaveLength(2);
|
|
234
|
+
expect(ast.body[0]).toMatchObject({
|
|
235
|
+
type: 'ToolDeclaration',
|
|
236
|
+
name: 'add',
|
|
237
|
+
});
|
|
238
|
+
expect(ast.body[1]).toMatchObject({
|
|
239
|
+
type: 'ToolDeclaration',
|
|
240
|
+
name: 'multiply',
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// ============================================================================
|
|
245
|
+
// Tool with no return type
|
|
246
|
+
// ============================================================================
|
|
247
|
+
|
|
248
|
+
test('tool without return type annotation', () => {
|
|
249
|
+
const ast = parse(`
|
|
250
|
+
tool logMessage(message: text)
|
|
251
|
+
@description "Log a message"
|
|
252
|
+
{
|
|
253
|
+
ts(message) {
|
|
254
|
+
console.log(message)
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
`);
|
|
258
|
+
expect(ast.body).toHaveLength(1);
|
|
259
|
+
expect(ast.body[0]).toMatchObject({
|
|
260
|
+
type: 'ToolDeclaration',
|
|
261
|
+
name: 'logMessage',
|
|
262
|
+
returnType: null,
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
describe('Syntax Errors - Tool Declaration', () => {
|
|
268
|
+
test('tool missing name', () => {
|
|
269
|
+
expect(() => parse(`
|
|
270
|
+
tool (x: text): text
|
|
271
|
+
@description "test"
|
|
272
|
+
{
|
|
273
|
+
ts(x) { return x }
|
|
274
|
+
}
|
|
275
|
+
`)).toThrow();
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
test('tool missing body', () => {
|
|
279
|
+
expect(() => parse(`
|
|
280
|
+
tool test(): text
|
|
281
|
+
@description "test"
|
|
282
|
+
`)).toThrow();
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// Note: Type validation happens at semantic analysis, not parsing.
|
|
286
|
+
// The parser accepts any identifier as a type, including custom types
|
|
287
|
+
// from imports. So "invalidType" is valid syntax that would fail later.
|
|
288
|
+
test('tool with custom type parameter parses successfully', () => {
|
|
289
|
+
// This is valid syntax - type validation happens later
|
|
290
|
+
const ast = parse(`
|
|
291
|
+
tool test(x: CustomType): text
|
|
292
|
+
@description "test"
|
|
293
|
+
{
|
|
294
|
+
ts(x) { return x }
|
|
295
|
+
}
|
|
296
|
+
`);
|
|
297
|
+
expect(ast.body[0]).toMatchObject({
|
|
298
|
+
type: 'ToolDeclaration',
|
|
299
|
+
params: [{ name: 'x', typeAnnotation: 'CustomType' }],
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
});
|