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