@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.
Files changed (250) hide show
  1. package/package.json +46 -0
  2. package/src/ast/index.ts +375 -0
  3. package/src/ast.ts +2 -0
  4. package/src/debug/advanced-features.ts +482 -0
  5. package/src/debug/bun-inspector.ts +424 -0
  6. package/src/debug/handoff-manager.ts +283 -0
  7. package/src/debug/index.ts +150 -0
  8. package/src/debug/runner.ts +365 -0
  9. package/src/debug/server.ts +565 -0
  10. package/src/debug/stack-merger.ts +267 -0
  11. package/src/debug/state.ts +581 -0
  12. package/src/debug/test/advanced-features.test.ts +300 -0
  13. package/src/debug/test/e2e.test.ts +218 -0
  14. package/src/debug/test/handoff-manager.test.ts +256 -0
  15. package/src/debug/test/runner.test.ts +256 -0
  16. package/src/debug/test/stack-merger.test.ts +163 -0
  17. package/src/debug/test/state.test.ts +400 -0
  18. package/src/debug/test/ts-debug-integration.test.ts +374 -0
  19. package/src/debug/test/ts-import-tracker.test.ts +125 -0
  20. package/src/debug/test/ts-source-map.test.ts +169 -0
  21. package/src/debug/ts-import-tracker.ts +151 -0
  22. package/src/debug/ts-source-map.ts +171 -0
  23. package/src/errors/index.ts +124 -0
  24. package/src/index.ts +358 -0
  25. package/src/lexer/index.ts +348 -0
  26. package/src/lexer.ts +2 -0
  27. package/src/parser/index.ts +792 -0
  28. package/src/parser/parse.ts +45 -0
  29. package/src/parser/test/async.test.ts +248 -0
  30. package/src/parser/test/destructuring.test.ts +167 -0
  31. package/src/parser/test/do-expression.test.ts +486 -0
  32. package/src/parser/test/errors/do-expression.test.ts +95 -0
  33. package/src/parser/test/errors/error-locations.test.ts +230 -0
  34. package/src/parser/test/errors/invalid-expressions.test.ts +144 -0
  35. package/src/parser/test/errors/missing-tokens.test.ts +126 -0
  36. package/src/parser/test/errors/model-declaration.test.ts +185 -0
  37. package/src/parser/test/errors/nested-blocks.test.ts +226 -0
  38. package/src/parser/test/errors/unclosed-delimiters.test.ts +122 -0
  39. package/src/parser/test/errors/unexpected-tokens.test.ts +120 -0
  40. package/src/parser/test/import-export.test.ts +143 -0
  41. package/src/parser/test/literals.test.ts +404 -0
  42. package/src/parser/test/model-declaration.test.ts +161 -0
  43. package/src/parser/test/nested-blocks.test.ts +402 -0
  44. package/src/parser/test/parser.test.ts +743 -0
  45. package/src/parser/test/private.test.ts +136 -0
  46. package/src/parser/test/template-literal.test.ts +127 -0
  47. package/src/parser/test/tool-declaration.test.ts +302 -0
  48. package/src/parser/test/ts-block.test.ts +252 -0
  49. package/src/parser/test/type-annotations.test.ts +254 -0
  50. package/src/parser/visitor/helpers.ts +330 -0
  51. package/src/parser/visitor.ts +794 -0
  52. package/src/parser.ts +2 -0
  53. package/src/runtime/ai/cache-chunking.test.ts +69 -0
  54. package/src/runtime/ai/cache-chunking.ts +73 -0
  55. package/src/runtime/ai/client.ts +109 -0
  56. package/src/runtime/ai/context.ts +168 -0
  57. package/src/runtime/ai/formatters.ts +316 -0
  58. package/src/runtime/ai/index.ts +38 -0
  59. package/src/runtime/ai/language-ref.ts +38 -0
  60. package/src/runtime/ai/providers/anthropic.ts +253 -0
  61. package/src/runtime/ai/providers/google.ts +201 -0
  62. package/src/runtime/ai/providers/openai.ts +156 -0
  63. package/src/runtime/ai/retry.ts +100 -0
  64. package/src/runtime/ai/return-tools.ts +301 -0
  65. package/src/runtime/ai/test/client.test.ts +83 -0
  66. package/src/runtime/ai/test/formatters.test.ts +485 -0
  67. package/src/runtime/ai/test/retry.test.ts +137 -0
  68. package/src/runtime/ai/test/return-tools.test.ts +450 -0
  69. package/src/runtime/ai/test/tool-loop.test.ts +319 -0
  70. package/src/runtime/ai/test/tool-schema.test.ts +241 -0
  71. package/src/runtime/ai/tool-loop.ts +203 -0
  72. package/src/runtime/ai/tool-schema.ts +151 -0
  73. package/src/runtime/ai/types.ts +113 -0
  74. package/src/runtime/ai-logger.ts +255 -0
  75. package/src/runtime/ai-provider.ts +347 -0
  76. package/src/runtime/async/dependencies.ts +276 -0
  77. package/src/runtime/async/executor.ts +293 -0
  78. package/src/runtime/async/index.ts +43 -0
  79. package/src/runtime/async/scheduling.ts +163 -0
  80. package/src/runtime/async/test/dependencies.test.ts +284 -0
  81. package/src/runtime/async/test/executor.test.ts +388 -0
  82. package/src/runtime/context.ts +357 -0
  83. package/src/runtime/exec/ai.ts +139 -0
  84. package/src/runtime/exec/expressions.ts +475 -0
  85. package/src/runtime/exec/frames.ts +26 -0
  86. package/src/runtime/exec/functions.ts +305 -0
  87. package/src/runtime/exec/interpolation.ts +312 -0
  88. package/src/runtime/exec/statements.ts +604 -0
  89. package/src/runtime/exec/tools.ts +129 -0
  90. package/src/runtime/exec/typescript.ts +215 -0
  91. package/src/runtime/exec/variables.ts +279 -0
  92. package/src/runtime/index.ts +975 -0
  93. package/src/runtime/modules.ts +452 -0
  94. package/src/runtime/serialize.ts +103 -0
  95. package/src/runtime/state.ts +489 -0
  96. package/src/runtime/stdlib/core.ts +45 -0
  97. package/src/runtime/stdlib/directory.test.ts +156 -0
  98. package/src/runtime/stdlib/edit.test.ts +154 -0
  99. package/src/runtime/stdlib/fastEdit.test.ts +201 -0
  100. package/src/runtime/stdlib/glob.test.ts +106 -0
  101. package/src/runtime/stdlib/grep.test.ts +144 -0
  102. package/src/runtime/stdlib/index.ts +16 -0
  103. package/src/runtime/stdlib/readFile.test.ts +123 -0
  104. package/src/runtime/stdlib/tools/index.ts +707 -0
  105. package/src/runtime/stdlib/writeFile.test.ts +157 -0
  106. package/src/runtime/step.ts +969 -0
  107. package/src/runtime/test/ai-context.test.ts +1086 -0
  108. package/src/runtime/test/ai-result-object.test.ts +419 -0
  109. package/src/runtime/test/ai-tool-flow.test.ts +859 -0
  110. package/src/runtime/test/async-execution-order.test.ts +618 -0
  111. package/src/runtime/test/async-execution.test.ts +344 -0
  112. package/src/runtime/test/async-nested.test.ts +660 -0
  113. package/src/runtime/test/async-parallel-timing.test.ts +546 -0
  114. package/src/runtime/test/basic1.test.ts +154 -0
  115. package/src/runtime/test/binary-operators.test.ts +431 -0
  116. package/src/runtime/test/break-statement.test.ts +257 -0
  117. package/src/runtime/test/context-modes.test.ts +650 -0
  118. package/src/runtime/test/context.test.ts +466 -0
  119. package/src/runtime/test/core-functions.test.ts +228 -0
  120. package/src/runtime/test/e2e.test.ts +88 -0
  121. package/src/runtime/test/error-locations/error-locations.test.ts +80 -0
  122. package/src/runtime/test/error-locations/main-error.vibe +4 -0
  123. package/src/runtime/test/error-locations/main-import-error.vibe +3 -0
  124. package/src/runtime/test/error-locations/utils/helper.vibe +5 -0
  125. package/src/runtime/test/for-in.test.ts +312 -0
  126. package/src/runtime/test/helpers.ts +69 -0
  127. package/src/runtime/test/imports.test.ts +334 -0
  128. package/src/runtime/test/json-expressions.test.ts +232 -0
  129. package/src/runtime/test/literals.test.ts +372 -0
  130. package/src/runtime/test/logical-indexing.test.ts +478 -0
  131. package/src/runtime/test/member-methods.test.ts +324 -0
  132. package/src/runtime/test/model-config.test.ts +338 -0
  133. package/src/runtime/test/null-handling.test.ts +342 -0
  134. package/src/runtime/test/private-visibility.test.ts +332 -0
  135. package/src/runtime/test/runtime-state.test.ts +514 -0
  136. package/src/runtime/test/scoping.test.ts +370 -0
  137. package/src/runtime/test/string-interpolation.test.ts +354 -0
  138. package/src/runtime/test/template-literal.test.ts +181 -0
  139. package/src/runtime/test/tool-execution.test.ts +467 -0
  140. package/src/runtime/test/tool-schema-generation.test.ts +477 -0
  141. package/src/runtime/test/tostring.test.ts +210 -0
  142. package/src/runtime/test/ts-block.test.ts +594 -0
  143. package/src/runtime/test/ts-error-location.test.ts +231 -0
  144. package/src/runtime/test/types.test.ts +732 -0
  145. package/src/runtime/test/verbose-logger.test.ts +710 -0
  146. package/src/runtime/test/vibe-expression.test.ts +54 -0
  147. package/src/runtime/test/vibe-value-errors.test.ts +541 -0
  148. package/src/runtime/test/while.test.ts +232 -0
  149. package/src/runtime/tools/builtin.ts +30 -0
  150. package/src/runtime/tools/directory-tools.ts +70 -0
  151. package/src/runtime/tools/file-tools.ts +228 -0
  152. package/src/runtime/tools/index.ts +5 -0
  153. package/src/runtime/tools/registry.ts +48 -0
  154. package/src/runtime/tools/search-tools.ts +134 -0
  155. package/src/runtime/tools/security.ts +36 -0
  156. package/src/runtime/tools/system-tools.ts +312 -0
  157. package/src/runtime/tools/test/fixtures/base-types.ts +40 -0
  158. package/src/runtime/tools/test/fixtures/test-types.ts +132 -0
  159. package/src/runtime/tools/test/registry.test.ts +713 -0
  160. package/src/runtime/tools/test/security.test.ts +86 -0
  161. package/src/runtime/tools/test/system-tools.test.ts +679 -0
  162. package/src/runtime/tools/test/ts-schema.test.ts +357 -0
  163. package/src/runtime/tools/ts-schema.ts +341 -0
  164. package/src/runtime/tools/types.ts +89 -0
  165. package/src/runtime/tools/utility-tools.ts +198 -0
  166. package/src/runtime/ts-eval.ts +126 -0
  167. package/src/runtime/types.ts +797 -0
  168. package/src/runtime/validation.ts +160 -0
  169. package/src/runtime/verbose-logger.ts +459 -0
  170. package/src/runtime.ts +2 -0
  171. package/src/semantic/analyzer-context.ts +62 -0
  172. package/src/semantic/analyzer-validators.ts +575 -0
  173. package/src/semantic/analyzer-visitors.ts +534 -0
  174. package/src/semantic/analyzer.ts +83 -0
  175. package/src/semantic/index.ts +11 -0
  176. package/src/semantic/symbol-table.ts +58 -0
  177. package/src/semantic/test/async-validation.test.ts +301 -0
  178. package/src/semantic/test/compress-validation.test.ts +179 -0
  179. package/src/semantic/test/const-reassignment.test.ts +111 -0
  180. package/src/semantic/test/control-flow.test.ts +346 -0
  181. package/src/semantic/test/destructuring.test.ts +185 -0
  182. package/src/semantic/test/duplicate-declarations.test.ts +168 -0
  183. package/src/semantic/test/export-validation.test.ts +111 -0
  184. package/src/semantic/test/fixtures/math.ts +31 -0
  185. package/src/semantic/test/imports.test.ts +148 -0
  186. package/src/semantic/test/json-type.test.ts +68 -0
  187. package/src/semantic/test/literals.test.ts +127 -0
  188. package/src/semantic/test/model-validation.test.ts +179 -0
  189. package/src/semantic/test/prompt-validation.test.ts +343 -0
  190. package/src/semantic/test/scoping.test.ts +312 -0
  191. package/src/semantic/test/tool-validation.test.ts +306 -0
  192. package/src/semantic/test/ts-type-checking.test.ts +563 -0
  193. package/src/semantic/test/type-constraints.test.ts +111 -0
  194. package/src/semantic/test/type-inference.test.ts +87 -0
  195. package/src/semantic/test/type-validation.test.ts +552 -0
  196. package/src/semantic/test/undefined-variables.test.ts +163 -0
  197. package/src/semantic/ts-block-checker.ts +204 -0
  198. package/src/semantic/ts-signatures.ts +194 -0
  199. package/src/semantic/ts-types.ts +170 -0
  200. package/src/semantic/types.ts +58 -0
  201. package/tests/fixtures/conditional-logic.vibe +14 -0
  202. package/tests/fixtures/function-call.vibe +16 -0
  203. package/tests/fixtures/imports/cycle-detection/a.vibe +6 -0
  204. package/tests/fixtures/imports/cycle-detection/b.vibe +5 -0
  205. package/tests/fixtures/imports/cycle-detection/main.vibe +3 -0
  206. package/tests/fixtures/imports/module-isolation/main-b.vibe +8 -0
  207. package/tests/fixtures/imports/module-isolation/main.vibe +9 -0
  208. package/tests/fixtures/imports/module-isolation/moduleA.vibe +6 -0
  209. package/tests/fixtures/imports/module-isolation/moduleB.vibe +6 -0
  210. package/tests/fixtures/imports/nested-import/helper.vibe +6 -0
  211. package/tests/fixtures/imports/nested-import/main.vibe +3 -0
  212. package/tests/fixtures/imports/nested-import/utils.ts +3 -0
  213. package/tests/fixtures/imports/nested-isolation/file2.vibe +15 -0
  214. package/tests/fixtures/imports/nested-isolation/file3.vibe +10 -0
  215. package/tests/fixtures/imports/nested-isolation/main.vibe +21 -0
  216. package/tests/fixtures/imports/pure-cycle/a.vibe +5 -0
  217. package/tests/fixtures/imports/pure-cycle/b.vibe +5 -0
  218. package/tests/fixtures/imports/pure-cycle/main.vibe +3 -0
  219. package/tests/fixtures/imports/ts-boolean/checks.ts +14 -0
  220. package/tests/fixtures/imports/ts-boolean/main.vibe +10 -0
  221. package/tests/fixtures/imports/ts-boolean/type-mismatch.vibe +5 -0
  222. package/tests/fixtures/imports/ts-boolean/use-constant.vibe +18 -0
  223. package/tests/fixtures/imports/ts-error-handling/helpers.ts +42 -0
  224. package/tests/fixtures/imports/ts-error-handling/main.vibe +5 -0
  225. package/tests/fixtures/imports/ts-import/main.vibe +4 -0
  226. package/tests/fixtures/imports/ts-import/math.ts +9 -0
  227. package/tests/fixtures/imports/ts-variables/call-non-function.vibe +5 -0
  228. package/tests/fixtures/imports/ts-variables/data.ts +10 -0
  229. package/tests/fixtures/imports/ts-variables/import-json.vibe +5 -0
  230. package/tests/fixtures/imports/ts-variables/import-type-mismatch.vibe +5 -0
  231. package/tests/fixtures/imports/ts-variables/import-variable.vibe +5 -0
  232. package/tests/fixtures/imports/vibe-import/greet.vibe +5 -0
  233. package/tests/fixtures/imports/vibe-import/main.vibe +3 -0
  234. package/tests/fixtures/multiple-ai-calls.vibe +10 -0
  235. package/tests/fixtures/simple-greeting.vibe +6 -0
  236. package/tests/fixtures/template-literals.vibe +11 -0
  237. package/tests/integration/basic-ai/basic-ai.integration.test.ts +166 -0
  238. package/tests/integration/basic-ai/basic-ai.vibe +12 -0
  239. package/tests/integration/bug-fix/bug-fix.integration.test.ts +201 -0
  240. package/tests/integration/bug-fix/buggy-code.ts +22 -0
  241. package/tests/integration/bug-fix/fix-bug.vibe +21 -0
  242. package/tests/integration/compress/compress.integration.test.ts +206 -0
  243. package/tests/integration/destructuring/destructuring.integration.test.ts +92 -0
  244. package/tests/integration/hello-world-translator/hello-world-translator.integration.test.ts +61 -0
  245. package/tests/integration/line-annotator/context-modes.integration.test.ts +261 -0
  246. package/tests/integration/line-annotator/line-annotator.integration.test.ts +148 -0
  247. package/tests/integration/multi-feature/cumulative-sum.integration.test.ts +75 -0
  248. package/tests/integration/multi-feature/number-analyzer.integration.test.ts +191 -0
  249. package/tests/integration/multi-feature/number-analyzer.vibe +59 -0
  250. 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
+ });