@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,301 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { parse } from '../../parser/parse';
3
+ import { analyze } from '../index';
4
+
5
+ describe('Semantic Validation - Async Declarations', () => {
6
+ // ============================================================================
7
+ // Valid async let declarations
8
+ // ============================================================================
9
+
10
+ test('valid async let with do expression', () => {
11
+ const ast = parse(`
12
+ model m = { name: "test", apiKey: "key", url: "http://test" }
13
+ async let x: text = do "prompt" m
14
+ `);
15
+ const errors = analyze(ast);
16
+ expect(errors.length).toBe(0);
17
+ });
18
+
19
+ test('valid async let with vibe expression', () => {
20
+ const ast = parse(`
21
+ model m = { name: "test", apiKey: "key", url: "http://test" }
22
+ async let x: text = vibe "prompt" m
23
+ `);
24
+ const errors = analyze(ast);
25
+ expect(errors.length).toBe(0);
26
+ });
27
+
28
+ test('valid async let with ts block', () => {
29
+ const ast = parse(`
30
+ async let x = ts() { return fetchData(); }
31
+ `);
32
+ const errors = analyze(ast);
33
+ expect(errors.length).toBe(0);
34
+ });
35
+
36
+ test('valid async let with function call', () => {
37
+ const ast = parse(`
38
+ function getData(): text { return "data" }
39
+ async let x = getData()
40
+ `);
41
+ const errors = analyze(ast);
42
+ expect(errors.length).toBe(0);
43
+ });
44
+
45
+ // ============================================================================
46
+ // Valid async const declarations
47
+ // ============================================================================
48
+
49
+ test('valid async const with do expression', () => {
50
+ const ast = parse(`
51
+ model m = { name: "test", apiKey: "key", url: "http://test" }
52
+ async const x: text = do "prompt" m
53
+ `);
54
+ const errors = analyze(ast);
55
+ expect(errors.length).toBe(0);
56
+ });
57
+
58
+ test('valid async const with vibe expression', () => {
59
+ const ast = parse(`
60
+ model m = { name: "test", apiKey: "key", url: "http://test" }
61
+ async const x: text = vibe "prompt" m
62
+ `);
63
+ const errors = analyze(ast);
64
+ expect(errors.length).toBe(0);
65
+ });
66
+
67
+ test('valid async const with ts block', () => {
68
+ const ast = parse(`
69
+ async const x = ts() { return fetchData(); }
70
+ `);
71
+ const errors = analyze(ast);
72
+ expect(errors.length).toBe(0);
73
+ });
74
+
75
+ test('valid async const with function call', () => {
76
+ const ast = parse(`
77
+ function getData(): text { return "data" }
78
+ async const x = getData()
79
+ `);
80
+ const errors = analyze(ast);
81
+ expect(errors.length).toBe(0);
82
+ });
83
+
84
+ // ============================================================================
85
+ // Valid async destructuring declarations
86
+ // ============================================================================
87
+
88
+ test('valid async let destructuring with do expression', () => {
89
+ const ast = parse(`
90
+ model m = { name: "test", apiKey: "key", url: "http://test" }
91
+ async let {name: text, age: number} = do "get info" m
92
+ `);
93
+ const errors = analyze(ast);
94
+ expect(errors.length).toBe(0);
95
+ });
96
+
97
+ test('valid async const destructuring with do expression', () => {
98
+ const ast = parse(`
99
+ model m = { name: "test", apiKey: "key", url: "http://test" }
100
+ async const {name: text, age: number} = do "get info" m
101
+ `);
102
+ const errors = analyze(ast);
103
+ expect(errors.length).toBe(0);
104
+ });
105
+
106
+ // ============================================================================
107
+ // Valid async standalone statements (fire-and-forget)
108
+ // ============================================================================
109
+
110
+ test('valid async do statement', () => {
111
+ const ast = parse(`
112
+ model m = { name: "test", apiKey: "key", url: "http://test" }
113
+ async do "log something" m
114
+ `);
115
+ const errors = analyze(ast);
116
+ expect(errors.length).toBe(0);
117
+ });
118
+
119
+ test('valid async vibe statement', () => {
120
+ const ast = parse(`
121
+ model m = { name: "test", apiKey: "key", url: "http://test" }
122
+ async vibe "process data" m
123
+ `);
124
+ const errors = analyze(ast);
125
+ expect(errors.length).toBe(0);
126
+ });
127
+
128
+ test('valid async ts block statement', () => {
129
+ const ast = parse(`
130
+ async ts() { console.log("fire and forget"); }
131
+ `);
132
+ const errors = analyze(ast);
133
+ expect(errors.length).toBe(0);
134
+ });
135
+
136
+ test('valid async function call statement', () => {
137
+ const ast = parse(`
138
+ function logEvent(msg: text): text { return msg }
139
+ async logEvent("event")
140
+ `);
141
+ const errors = analyze(ast);
142
+ expect(errors.length).toBe(0);
143
+ });
144
+
145
+ // ============================================================================
146
+ // Invalid async declarations - caught at parse time
147
+ // Note: Invalid async expressions (literals, identifiers, etc.) are caught
148
+ // by the parser grammar, not semantic analysis. These tests verify parse errors.
149
+ // ============================================================================
150
+
151
+ test('async let with string literal fails at parse time', () => {
152
+ expect(() => parse(`async let x = "hello"`)).toThrow();
153
+ });
154
+
155
+ test('async let with number literal fails at parse time', () => {
156
+ expect(() => parse(`async let x = 42`)).toThrow();
157
+ });
158
+
159
+ test('async let with identifier is invalid (semantic error)', () => {
160
+ const ast = parse(`
161
+ let data = "test"
162
+ async let x = data
163
+ `);
164
+ const errors = analyze(ast);
165
+ expect(errors.length).toBe(1);
166
+ expect(errors[0].message).toContain('async declarations require a single do, vibe, ts block, or function call');
167
+ });
168
+
169
+ test('untyped let with do expression requires type annotation', () => {
170
+ const ast = parse(`
171
+ model m = { name: "test", apiKey: "key", url: "http://test" }
172
+ let x = do "prompt" m
173
+ `);
174
+ const errors = analyze(ast);
175
+ expect(errors.length).toBe(1);
176
+ expect(errors[0].message).toContain('Type cannot be inferred from AI call');
177
+ });
178
+
179
+ test('untyped let with vibe expression requires type annotation', () => {
180
+ const ast = parse(`
181
+ model m = { name: "test", apiKey: "key", url: "http://test" }
182
+ let x = vibe "prompt" m
183
+ `);
184
+ const errors = analyze(ast);
185
+ expect(errors.length).toBe(1);
186
+ expect(errors[0].message).toContain('Type cannot be inferred from AI call');
187
+ });
188
+
189
+ test('async const with literal fails at parse time', () => {
190
+ expect(() => parse(`async const x = "hello"`)).toThrow();
191
+ });
192
+
193
+ // ============================================================================
194
+ // Async declarations with type annotations (valid)
195
+ // ============================================================================
196
+
197
+ test('valid async let with type annotation', () => {
198
+ const ast = parse(`
199
+ model m = { name: "test", apiKey: "key", url: "http://test" }
200
+ async let x: text = do "prompt" m
201
+ `);
202
+ const errors = analyze(ast);
203
+ expect(errors.length).toBe(0);
204
+ });
205
+
206
+ test('valid async const with type annotation', () => {
207
+ const ast = parse(`
208
+ model m = { name: "test", apiKey: "key", url: "http://test" }
209
+ async const x: number = do "give number" m
210
+ `);
211
+ const errors = analyze(ast);
212
+ expect(errors.length).toBe(0);
213
+ });
214
+
215
+ // ============================================================================
216
+ // Async declarations with private modifier (valid)
217
+ // ============================================================================
218
+
219
+ test('valid async let with private modifier', () => {
220
+ const ast = parse(`
221
+ model m = { name: "test", apiKey: "key", url: "http://test" }
222
+ async let private secret: text = do "get secret" m
223
+ `);
224
+ const errors = analyze(ast);
225
+ expect(errors.length).toBe(0);
226
+ });
227
+
228
+ test('valid async const with private modifier', () => {
229
+ const ast = parse(`
230
+ function getKey(): text { return "key" }
231
+ async const private API_KEY = getKey()
232
+ `);
233
+ const errors = analyze(ast);
234
+ expect(errors.length).toBe(0);
235
+ });
236
+
237
+ // ============================================================================
238
+ // Multiple async declarations (valid)
239
+ // ============================================================================
240
+
241
+ test('multiple async let declarations are valid', () => {
242
+ const ast = parse(`
243
+ model m = { name: "test", apiKey: "key", url: "http://test" }
244
+ async let a: text = do "1" m
245
+ async let b: text = do "2" m
246
+ async let c: text = do "3" m
247
+ `);
248
+ const errors = analyze(ast);
249
+ expect(errors.length).toBe(0);
250
+ });
251
+
252
+ test('mixed async and sync declarations are valid', () => {
253
+ const ast = parse(`
254
+ model m = { name: "test", apiKey: "key", url: "http://test" }
255
+ let sync1 = "hello"
256
+ async let async1: text = do "prompt" m
257
+ const sync2 = 42
258
+ async const async2 = ts() { return fetchData(); }
259
+ `);
260
+ const errors = analyze(ast);
261
+ expect(errors.length).toBe(0);
262
+ });
263
+
264
+ // ============================================================================
265
+ // Async with method calls (valid)
266
+ // ============================================================================
267
+
268
+ test('valid async method call statement', () => {
269
+ const ast = parse(`
270
+ let api = "api"
271
+ async api.sendNotification("done")
272
+ `);
273
+ const errors = analyze(ast);
274
+ expect(errors.length).toBe(0);
275
+ });
276
+
277
+ // ============================================================================
278
+ // Using async declared variables (valid)
279
+ // ============================================================================
280
+
281
+ test('can use async declared variables in expressions', () => {
282
+ const ast = parse(`
283
+ model m = { name: "test", apiKey: "key", url: "http://test" }
284
+ async let x: text = do "1" m
285
+ async let y: text = do "2" m
286
+ let result = x + y
287
+ `);
288
+ const errors = analyze(ast);
289
+ expect(errors.length).toBe(0);
290
+ });
291
+
292
+ test('async declared variable can be used as dependency', () => {
293
+ const ast = parse(`
294
+ model m = { name: "test", apiKey: "key", url: "http://test" }
295
+ async let x: text = do "get data" m
296
+ async let y: text = do "use {x}" m
297
+ `);
298
+ const errors = analyze(ast);
299
+ expect(errors.length).toBe(0);
300
+ });
301
+ });
@@ -0,0 +1,179 @@
1
+ import { describe, test, expect } from 'bun:test';
2
+ import { parse } from '../../parser/parse';
3
+ import { SemanticAnalyzer } from '../analyzer';
4
+
5
+ function getErrors(source: string): string[] {
6
+ const ast = parse(source);
7
+ const analyzer = new SemanticAnalyzer();
8
+ return analyzer.analyze(ast, source).map((e) => e.message);
9
+ }
10
+
11
+ describe('Compress Context Mode Validation', () => {
12
+ describe('valid compress usage', () => {
13
+ test('compress with no arguments is valid', () => {
14
+ const errors = getErrors(`
15
+ for i in [1, 2, 3] {
16
+ let x = i
17
+ } compress
18
+ `);
19
+ expect(errors).toEqual([]);
20
+ });
21
+
22
+ test('compress with string literal is valid', () => {
23
+ const errors = getErrors(`
24
+ for i in [1, 2, 3] {
25
+ let x = i
26
+ } compress("summarize")
27
+ `);
28
+ expect(errors).toEqual([]);
29
+ });
30
+
31
+ test('compress with model identifier is valid', () => {
32
+ const errors = getErrors(`
33
+ model m = { name: "test", apiKey: "key", url: "http://test" }
34
+ for i in [1, 2, 3] {
35
+ let x = i
36
+ } compress(m)
37
+ `);
38
+ expect(errors).toEqual([]);
39
+ });
40
+
41
+ test('compress with prompt variable is valid', () => {
42
+ const errors = getErrors(`
43
+ const SUMMARY: prompt = "summarize the loop"
44
+ for i in [1, 2, 3] {
45
+ let x = i
46
+ } compress(SUMMARY)
47
+ `);
48
+ expect(errors).toEqual([]);
49
+ });
50
+
51
+ test('compress with text variable is valid (treated as prompt)', () => {
52
+ const errors = getErrors(`
53
+ const summary: text = "summarize the loop"
54
+ for i in [1, 2, 3] {
55
+ let x = i
56
+ } compress(summary)
57
+ `);
58
+ expect(errors).toEqual([]);
59
+ });
60
+
61
+ test('compress with string literal and model is valid', () => {
62
+ const errors = getErrors(`
63
+ model m = { name: "test", apiKey: "key", url: "http://test" }
64
+ for i in [1, 2, 3] {
65
+ let x = i
66
+ } compress("summarize", m)
67
+ `);
68
+ expect(errors).toEqual([]);
69
+ });
70
+
71
+ test('compress with prompt variable and model is valid', () => {
72
+ const errors = getErrors(`
73
+ model m = { name: "test", apiKey: "key", url: "http://test" }
74
+ const SUMMARY: prompt = "summarize the loop"
75
+ for i in [1, 2, 3] {
76
+ let x = i
77
+ } compress(SUMMARY, m)
78
+ `);
79
+ expect(errors).toEqual([]);
80
+ });
81
+ });
82
+
83
+ describe('invalid compress usage', () => {
84
+ test('compress with undeclared identifier reports error', () => {
85
+ const errors = getErrors(`
86
+ for i in [1, 2, 3] {
87
+ let x = i
88
+ } compress(undeclared)
89
+ `);
90
+ expect(errors).toContain("compress argument 'undeclared' is not declared");
91
+ });
92
+
93
+ test('compress with undeclared model identifier reports error', () => {
94
+ const errors = getErrors(`
95
+ const SUMMARY: prompt = "summarize"
96
+ for i in [1, 2, 3] {
97
+ let x = i
98
+ } compress(SUMMARY, undeclaredModel)
99
+ `);
100
+ expect(errors).toContain("compress model 'undeclaredModel' is not declared");
101
+ });
102
+
103
+ test('compress with json variable reports error', () => {
104
+ const errors = getErrors(`
105
+ const config: json = { key: "value" }
106
+ for i in [1, 2, 3] {
107
+ let x = i
108
+ } compress(config)
109
+ `);
110
+ expect(errors).toContain("compress argument 'config' must be prompt or model type, got json");
111
+ });
112
+
113
+ test('compress with number variable reports error', () => {
114
+ const errors = getErrors(`
115
+ const num: number = 42
116
+ for i in [1, 2, 3] {
117
+ let x = i
118
+ } compress(num)
119
+ `);
120
+ expect(errors).toContain("compress argument 'num' must be prompt or model type, got number");
121
+ });
122
+
123
+ test('compress with function as argument reports error', () => {
124
+ const errors = getErrors(`
125
+ function myFunc(x: text): text { return x }
126
+ for i in [1, 2, 3] {
127
+ let x = i
128
+ } compress(myFunc)
129
+ `);
130
+ expect(errors).toContain("compress argument 'myFunc' must be prompt or model type, got function");
131
+ });
132
+
133
+ test('compress second arg must be model type', () => {
134
+ const errors = getErrors(`
135
+ const SUMMARY: prompt = "summarize"
136
+ const notModel: text = "not a model"
137
+ for i in [1, 2, 3] {
138
+ let x = i
139
+ } compress(SUMMARY, notModel)
140
+ `);
141
+ expect(errors).toContain("compress second argument 'notModel' must be model type, got text");
142
+ });
143
+
144
+ test('compress first arg must be prompt type when two args provided', () => {
145
+ const errors = getErrors(`
146
+ model m = { name: "test", apiKey: "key", url: "http://test" }
147
+ const config: json = { key: "value" }
148
+ for i in [1, 2, 3] {
149
+ let x = i
150
+ } compress(config, m)
151
+ `);
152
+ expect(errors).toContain("compress first argument 'config' must be prompt type when two arguments provided, got json");
153
+ });
154
+ });
155
+
156
+ describe('while loop compress validation', () => {
157
+ test('while loop compress with model is valid', () => {
158
+ const errors = getErrors(`
159
+ model m = { name: "test", apiKey: "key", url: "http://test" }
160
+ let i = 0
161
+ while (i < 3) {
162
+ i = i + 1
163
+ } compress(m)
164
+ `);
165
+ expect(errors).toEqual([]);
166
+ });
167
+
168
+ test('while loop compress with invalid type reports error', () => {
169
+ const errors = getErrors(`
170
+ const num: number = 42
171
+ let i = 0
172
+ while (i < 3) {
173
+ i = i + 1
174
+ } compress(num)
175
+ `);
176
+ expect(errors).toContain("compress argument 'num' must be prompt or model type, got number");
177
+ });
178
+ });
179
+ });
@@ -0,0 +1,111 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { parse } from '../../parser/parse';
3
+ import { analyze } from '../index';
4
+
5
+ describe('Semantic Errors - Const Reassignment', () => {
6
+ // ============================================================================
7
+ // Basic const reassignment
8
+ // ============================================================================
9
+
10
+ test('cannot reassign const variable', () => {
11
+ const ast = parse(`
12
+ const x = "hello"
13
+ x = "world"
14
+ `);
15
+ const errors = analyze(ast);
16
+ expect(errors.length).toBe(1);
17
+ expect(errors[0].message).toBe("Cannot reassign constant 'x'");
18
+ });
19
+
20
+ test('can reassign let variable', () => {
21
+ const ast = parse(`
22
+ let x = "hello"
23
+ x = "world"
24
+ `);
25
+ const errors = analyze(ast);
26
+ expect(errors.length).toBe(0);
27
+ });
28
+
29
+ // ============================================================================
30
+ // Const reassignment in different scopes
31
+ // ============================================================================
32
+
33
+ test('cannot reassign const in nested block', () => {
34
+ const ast = parse(`
35
+ const x = "hello"
36
+ if true {
37
+ x = "world"
38
+ }
39
+ `);
40
+ const errors = analyze(ast);
41
+ expect(errors.length).toBe(1);
42
+ expect(errors[0].message).toBe("Cannot reassign constant 'x'");
43
+ });
44
+
45
+ test('cannot reassign const in function', () => {
46
+ const ast = parse(`
47
+ const x = "hello"
48
+ function test() {
49
+ x = "world"
50
+ return x
51
+ }
52
+ `);
53
+ const errors = analyze(ast);
54
+ expect(errors.length).toBe(1);
55
+ expect(errors[0].message).toBe("Cannot reassign constant 'x'");
56
+ });
57
+
58
+ // ============================================================================
59
+ // Shadowing vs reassignment
60
+ // ============================================================================
61
+
62
+ test('shadowing const with let in nested scope is valid', () => {
63
+ const ast = parse(`
64
+ const x = "outer"
65
+ if true {
66
+ let x = "inner"
67
+ }
68
+ `);
69
+ const errors = analyze(ast);
70
+ expect(errors.length).toBe(0);
71
+ });
72
+
73
+ test('shadowing const with const in nested scope is valid', () => {
74
+ const ast = parse(`
75
+ const x = "outer"
76
+ function test() {
77
+ const x = "inner"
78
+ return x
79
+ }
80
+ `);
81
+ const errors = analyze(ast);
82
+ expect(errors.length).toBe(0);
83
+ });
84
+
85
+ // ============================================================================
86
+ // Multiple reassignments
87
+ // ============================================================================
88
+
89
+ test('multiple const reassignments report multiple errors', () => {
90
+ const ast = parse(`
91
+ const x = "hello"
92
+ x = "world"
93
+ x = "again"
94
+ `);
95
+ const errors = analyze(ast);
96
+ expect(errors.length).toBe(2);
97
+ expect(errors[0].message).toBe("Cannot reassign constant 'x'");
98
+ expect(errors[1].message).toBe("Cannot reassign constant 'x'");
99
+ });
100
+
101
+ test('reassigning different consts reports separate errors', () => {
102
+ const ast = parse(`
103
+ const x = "hello"
104
+ const y = "world"
105
+ x = "new x"
106
+ y = "new y"
107
+ `);
108
+ const errors = analyze(ast);
109
+ expect(errors.length).toBe(2);
110
+ });
111
+ });