@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,431 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { parse } from '../../parser/parse';
3
+ import { Runtime, AIProvider } from '../index';
4
+
5
+ // Mock AI provider
6
+ function createMockProvider(): AIProvider {
7
+ return {
8
+ async execute() {
9
+ return { value: '' };
10
+ },
11
+ async generateCode() {
12
+ return { value: '' };
13
+ },
14
+ async askUser(): Promise<string> {
15
+ return '';
16
+ },
17
+ };
18
+ }
19
+
20
+ describe('Binary Operators - Arithmetic', () => {
21
+ test('addition: 1 + 2 = 3', async () => {
22
+ const ast = parse(`let result = 1 + 2`);
23
+ const runtime = new Runtime(ast, createMockProvider());
24
+ await runtime.run();
25
+ expect(runtime.getValue('result')).toBe(3);
26
+ });
27
+
28
+ test('subtraction: 10 - 3 = 7', async () => {
29
+ const ast = parse(`let result = 10 - 3`);
30
+ const runtime = new Runtime(ast, createMockProvider());
31
+ await runtime.run();
32
+ expect(runtime.getValue('result')).toBe(7);
33
+ });
34
+
35
+ test('multiplication: 4 * 5 = 20', async () => {
36
+ const ast = parse(`let result = 4 * 5`);
37
+ const runtime = new Runtime(ast, createMockProvider());
38
+ await runtime.run();
39
+ expect(runtime.getValue('result')).toBe(20);
40
+ });
41
+
42
+ test('division: 20 / 4 = 5', async () => {
43
+ const ast = parse(`let result = 20 / 4`);
44
+ const runtime = new Runtime(ast, createMockProvider());
45
+ await runtime.run();
46
+ expect(runtime.getValue('result')).toBe(5);
47
+ });
48
+
49
+ test('modulo: 7 % 3 = 1', async () => {
50
+ const ast = parse(`let result = 7 % 3`);
51
+ const runtime = new Runtime(ast, createMockProvider());
52
+ await runtime.run();
53
+ expect(runtime.getValue('result')).toBe(1);
54
+ });
55
+
56
+ test('chained addition: 1 + 2 + 3 = 6', async () => {
57
+ const ast = parse(`let result = 1 + 2 + 3`);
58
+ const runtime = new Runtime(ast, createMockProvider());
59
+ await runtime.run();
60
+ expect(runtime.getValue('result')).toBe(6);
61
+ });
62
+
63
+ test('chained subtraction: 10 - 3 - 2 = 5', async () => {
64
+ const ast = parse(`let result = 10 - 3 - 2`);
65
+ const runtime = new Runtime(ast, createMockProvider());
66
+ await runtime.run();
67
+ expect(runtime.getValue('result')).toBe(5);
68
+ });
69
+
70
+ test('mixed add/subtract: 10 + 5 - 3 = 12', async () => {
71
+ const ast = parse(`let result = 10 + 5 - 3`);
72
+ const runtime = new Runtime(ast, createMockProvider());
73
+ await runtime.run();
74
+ expect(runtime.getValue('result')).toBe(12);
75
+ });
76
+
77
+ test('chained multiplication: 2 * 3 * 4 = 24', async () => {
78
+ const ast = parse(`let result = 2 * 3 * 4`);
79
+ const runtime = new Runtime(ast, createMockProvider());
80
+ await runtime.run();
81
+ expect(runtime.getValue('result')).toBe(24);
82
+ });
83
+
84
+ test('chained division: 100 / 2 / 5 = 10', async () => {
85
+ const ast = parse(`let result = 100 / 2 / 5`);
86
+ const runtime = new Runtime(ast, createMockProvider());
87
+ await runtime.run();
88
+ expect(runtime.getValue('result')).toBe(10);
89
+ });
90
+
91
+ test('negative result: 3 - 10 = -7', async () => {
92
+ const ast = parse(`let result = 3 - 10`);
93
+ const runtime = new Runtime(ast, createMockProvider());
94
+ await runtime.run();
95
+ expect(runtime.getValue('result')).toBe(-7);
96
+ });
97
+
98
+ test('decimal division: 7 / 2 = 3.5', async () => {
99
+ const ast = parse(`let result = 7 / 2`);
100
+ const runtime = new Runtime(ast, createMockProvider());
101
+ await runtime.run();
102
+ expect(runtime.getValue('result')).toBe(3.5);
103
+ });
104
+ });
105
+
106
+ describe('Binary Operators - Precedence', () => {
107
+ test('multiplication before addition: 1 + 2 * 3 = 7', async () => {
108
+ const ast = parse(`let result = 1 + 2 * 3`);
109
+ const runtime = new Runtime(ast, createMockProvider());
110
+ await runtime.run();
111
+ expect(runtime.getValue('result')).toBe(7);
112
+ });
113
+
114
+ test('division before subtraction: 10 - 6 / 2 = 7', async () => {
115
+ const ast = parse(`let result = 10 - 6 / 2`);
116
+ const runtime = new Runtime(ast, createMockProvider());
117
+ await runtime.run();
118
+ expect(runtime.getValue('result')).toBe(7);
119
+ });
120
+
121
+ test('parentheses override: (1 + 2) * 3 = 9', async () => {
122
+ const ast = parse(`let result = (1 + 2) * 3`);
123
+ const runtime = new Runtime(ast, createMockProvider());
124
+ await runtime.run();
125
+ expect(runtime.getValue('result')).toBe(9);
126
+ });
127
+
128
+ test('nested parentheses: ((2 + 3) * 4) - 10 = 10', async () => {
129
+ const ast = parse(`let result = ((2 + 3) * 4) - 10`);
130
+ const runtime = new Runtime(ast, createMockProvider());
131
+ await runtime.run();
132
+ expect(runtime.getValue('result')).toBe(10);
133
+ });
134
+
135
+ test('complex expression: 2 + 3 * 4 - 6 / 2 = 11', async () => {
136
+ const ast = parse(`let result = 2 + 3 * 4 - 6 / 2`);
137
+ const runtime = new Runtime(ast, createMockProvider());
138
+ await runtime.run();
139
+ expect(runtime.getValue('result')).toBe(11);
140
+ });
141
+
142
+ test('modulo with addition: 10 % 3 + 5 = 6', async () => {
143
+ const ast = parse(`let result = 10 % 3 + 5`);
144
+ const runtime = new Runtime(ast, createMockProvider());
145
+ await runtime.run();
146
+ expect(runtime.getValue('result')).toBe(6);
147
+ });
148
+ });
149
+
150
+ describe('Binary Operators - Comparison', () => {
151
+ test('equality: 1 == 1 is true', async () => {
152
+ const ast = parse(`let result = 1 == 1`);
153
+ const runtime = new Runtime(ast, createMockProvider());
154
+ await runtime.run();
155
+ expect(runtime.getValue('result')).toBe(true);
156
+ });
157
+
158
+ test('equality: 1 == 2 is false', async () => {
159
+ const ast = parse(`let result = 1 == 2`);
160
+ const runtime = new Runtime(ast, createMockProvider());
161
+ await runtime.run();
162
+ expect(runtime.getValue('result')).toBe(false);
163
+ });
164
+
165
+ test('inequality: 1 != 2 is true', async () => {
166
+ const ast = parse(`let result = 1 != 2`);
167
+ const runtime = new Runtime(ast, createMockProvider());
168
+ await runtime.run();
169
+ expect(runtime.getValue('result')).toBe(true);
170
+ });
171
+
172
+ test('inequality: 1 != 1 is false', async () => {
173
+ const ast = parse(`let result = 1 != 1`);
174
+ const runtime = new Runtime(ast, createMockProvider());
175
+ await runtime.run();
176
+ expect(runtime.getValue('result')).toBe(false);
177
+ });
178
+
179
+ test('less than: 1 < 2 is true', async () => {
180
+ const ast = parse(`let result = 1 < 2`);
181
+ const runtime = new Runtime(ast, createMockProvider());
182
+ await runtime.run();
183
+ expect(runtime.getValue('result')).toBe(true);
184
+ });
185
+
186
+ test('less than: 2 < 1 is false', async () => {
187
+ const ast = parse(`let result = 2 < 1`);
188
+ const runtime = new Runtime(ast, createMockProvider());
189
+ await runtime.run();
190
+ expect(runtime.getValue('result')).toBe(false);
191
+ });
192
+
193
+ test('less than: 1 < 1 is false', async () => {
194
+ const ast = parse(`let result = 1 < 1`);
195
+ const runtime = new Runtime(ast, createMockProvider());
196
+ await runtime.run();
197
+ expect(runtime.getValue('result')).toBe(false);
198
+ });
199
+
200
+ test('greater than: 2 > 1 is true', async () => {
201
+ const ast = parse(`let result = 2 > 1`);
202
+ const runtime = new Runtime(ast, createMockProvider());
203
+ await runtime.run();
204
+ expect(runtime.getValue('result')).toBe(true);
205
+ });
206
+
207
+ test('greater than: 1 > 2 is false', async () => {
208
+ const ast = parse(`let result = 1 > 2`);
209
+ const runtime = new Runtime(ast, createMockProvider());
210
+ await runtime.run();
211
+ expect(runtime.getValue('result')).toBe(false);
212
+ });
213
+
214
+ test('less than or equal: 1 <= 2 is true', async () => {
215
+ const ast = parse(`let result = 1 <= 2`);
216
+ const runtime = new Runtime(ast, createMockProvider());
217
+ await runtime.run();
218
+ expect(runtime.getValue('result')).toBe(true);
219
+ });
220
+
221
+ test('less than or equal: 1 <= 1 is true', async () => {
222
+ const ast = parse(`let result = 1 <= 1`);
223
+ const runtime = new Runtime(ast, createMockProvider());
224
+ await runtime.run();
225
+ expect(runtime.getValue('result')).toBe(true);
226
+ });
227
+
228
+ test('less than or equal: 2 <= 1 is false', async () => {
229
+ const ast = parse(`let result = 2 <= 1`);
230
+ const runtime = new Runtime(ast, createMockProvider());
231
+ await runtime.run();
232
+ expect(runtime.getValue('result')).toBe(false);
233
+ });
234
+
235
+ test('greater than or equal: 2 >= 1 is true', async () => {
236
+ const ast = parse(`let result = 2 >= 1`);
237
+ const runtime = new Runtime(ast, createMockProvider());
238
+ await runtime.run();
239
+ expect(runtime.getValue('result')).toBe(true);
240
+ });
241
+
242
+ test('greater than or equal: 1 >= 1 is true', async () => {
243
+ const ast = parse(`let result = 1 >= 1`);
244
+ const runtime = new Runtime(ast, createMockProvider());
245
+ await runtime.run();
246
+ expect(runtime.getValue('result')).toBe(true);
247
+ });
248
+
249
+ test('greater than or equal: 1 >= 2 is false', async () => {
250
+ const ast = parse(`let result = 1 >= 2`);
251
+ const runtime = new Runtime(ast, createMockProvider());
252
+ await runtime.run();
253
+ expect(runtime.getValue('result')).toBe(false);
254
+ });
255
+ });
256
+
257
+ describe('Binary Operators - Comparison with Arithmetic', () => {
258
+ test('compare arithmetic results: 2 + 3 == 5 is true', async () => {
259
+ const ast = parse(`let result = 2 + 3 == 5`);
260
+ const runtime = new Runtime(ast, createMockProvider());
261
+ await runtime.run();
262
+ expect(runtime.getValue('result')).toBe(true);
263
+ });
264
+
265
+ test('compare arithmetic results: 2 * 3 > 5 is true', async () => {
266
+ const ast = parse(`let result = 2 * 3 > 5`);
267
+ const runtime = new Runtime(ast, createMockProvider());
268
+ await runtime.run();
269
+ expect(runtime.getValue('result')).toBe(true);
270
+ });
271
+
272
+ test('comparison in if condition', async () => {
273
+ const ast = parse(`
274
+ let x = 10
275
+ let result = "none"
276
+ if x > 5 {
277
+ result = "big"
278
+ }
279
+ `);
280
+ const runtime = new Runtime(ast, createMockProvider());
281
+ await runtime.run();
282
+ expect(runtime.getValue('result')).toBe('big');
283
+ });
284
+
285
+ test('arithmetic comparison in if condition', async () => {
286
+ const ast = parse(`
287
+ let a = 3
288
+ let b = 4
289
+ let result = "none"
290
+ if a + b == 7 {
291
+ result = "correct"
292
+ }
293
+ `);
294
+ const runtime = new Runtime(ast, createMockProvider());
295
+ await runtime.run();
296
+ expect(runtime.getValue('result')).toBe('correct');
297
+ });
298
+ });
299
+
300
+ describe('Binary Operators - With Variables', () => {
301
+ test('add two variables', async () => {
302
+ const ast = parse(`
303
+ let a = 5
304
+ let b = 3
305
+ let result = a + b
306
+ `);
307
+ const runtime = new Runtime(ast, createMockProvider());
308
+ await runtime.run();
309
+ expect(runtime.getValue('result')).toBe(8);
310
+ });
311
+
312
+ test('multiply variable by constant', async () => {
313
+ const ast = parse(`
314
+ let x = 7
315
+ let result = x * 3
316
+ `);
317
+ const runtime = new Runtime(ast, createMockProvider());
318
+ await runtime.run();
319
+ expect(runtime.getValue('result')).toBe(21);
320
+ });
321
+
322
+ test('complex expression with variables', async () => {
323
+ const ast = parse(`
324
+ let a = 10
325
+ let b = 5
326
+ let c = 2
327
+ let result = (a + b) * c - 10
328
+ `);
329
+ const runtime = new Runtime(ast, createMockProvider());
330
+ await runtime.run();
331
+ expect(runtime.getValue('result')).toBe(20);
332
+ });
333
+
334
+ test('compare two variables', async () => {
335
+ const ast = parse(`
336
+ let x = 10
337
+ let y = 20
338
+ let result = x < y
339
+ `);
340
+ const runtime = new Runtime(ast, createMockProvider());
341
+ await runtime.run();
342
+ expect(runtime.getValue('result')).toBe(true);
343
+ });
344
+ });
345
+
346
+ describe('Binary Operators - String Equality', () => {
347
+ test('string equality: same strings', async () => {
348
+ const ast = parse(`let result = "hello" == "hello"`);
349
+ const runtime = new Runtime(ast, createMockProvider());
350
+ await runtime.run();
351
+ expect(runtime.getValue('result')).toBe(true);
352
+ });
353
+
354
+ test('string equality: different strings', async () => {
355
+ const ast = parse(`let result = "hello" == "world"`);
356
+ const runtime = new Runtime(ast, createMockProvider());
357
+ await runtime.run();
358
+ expect(runtime.getValue('result')).toBe(false);
359
+ });
360
+
361
+ test('string inequality', async () => {
362
+ const ast = parse(`let result = "hello" != "world"`);
363
+ const runtime = new Runtime(ast, createMockProvider());
364
+ await runtime.run();
365
+ expect(runtime.getValue('result')).toBe(true);
366
+ });
367
+ });
368
+
369
+ describe('Binary Operators - Boolean Equality', () => {
370
+ test('boolean equality: true == true', async () => {
371
+ const ast = parse(`let result = true == true`);
372
+ const runtime = new Runtime(ast, createMockProvider());
373
+ await runtime.run();
374
+ expect(runtime.getValue('result')).toBe(true);
375
+ });
376
+
377
+ test('boolean equality: true == false', async () => {
378
+ const ast = parse(`let result = true == false`);
379
+ const runtime = new Runtime(ast, createMockProvider());
380
+ await runtime.run();
381
+ expect(runtime.getValue('result')).toBe(false);
382
+ });
383
+
384
+ test('boolean inequality: true != false', async () => {
385
+ const ast = parse(`let result = true != false`);
386
+ const runtime = new Runtime(ast, createMockProvider());
387
+ await runtime.run();
388
+ expect(runtime.getValue('result')).toBe(true);
389
+ });
390
+ });
391
+
392
+ describe('Binary Operators - In Loops', () => {
393
+ test('arithmetic in for loop', async () => {
394
+ const ast = parse(`
395
+ let sum = 0
396
+ for i in 5 {
397
+ sum = sum + i
398
+ }
399
+ `);
400
+ const runtime = new Runtime(ast, createMockProvider());
401
+ await runtime.run();
402
+ // 1 + 2 + 3 + 4 + 5 = 15
403
+ expect(runtime.getValue('sum')).toBe(15);
404
+ });
405
+
406
+ test('comparison in while loop', async () => {
407
+ const ast = parse(`
408
+ let count = 0
409
+ let max = 5
410
+ while count < max {
411
+ count = count + 1
412
+ }
413
+ `);
414
+ const runtime = new Runtime(ast, createMockProvider());
415
+ await runtime.run();
416
+ expect(runtime.getValue('count')).toBe(5);
417
+ });
418
+
419
+ test('multiplication in loop accumulator', async () => {
420
+ const ast = parse(`
421
+ let factorial = 1
422
+ for i in 5 {
423
+ factorial = factorial * i
424
+ }
425
+ `);
426
+ const runtime = new Runtime(ast, createMockProvider());
427
+ await runtime.run();
428
+ // 1 * 1 * 2 * 3 * 4 * 5 = 120
429
+ expect(runtime.getValue('factorial')).toBe(120);
430
+ });
431
+ });
@@ -0,0 +1,257 @@
1
+ /**
2
+ * Break Statement Tests
3
+ *
4
+ * Tests for the break statement which exits the innermost loop.
5
+ */
6
+ import { describe, test, expect } from 'bun:test';
7
+ import { parse } from '../../parser/parse';
8
+ import { createInitialState, runUntilPause, currentFrame } from '..';
9
+ import { resolveValue } from '../types';
10
+
11
+ describe('break statement', () => {
12
+ test('break exits for-in loop immediately', () => {
13
+ const source = `
14
+ let count = 0
15
+ for i in [1, 2, 3, 4, 5] {
16
+ if i == 3 {
17
+ break
18
+ }
19
+ count = count + 1
20
+ }
21
+ `;
22
+ const program = parse(source);
23
+ let state = createInitialState(program);
24
+ state = runUntilPause(state);
25
+
26
+ expect(state.status).toBe('completed');
27
+ const frame = currentFrame(state);
28
+ const count = resolveValue(frame.locals['count']);
29
+ // Loop should have run for i=1, i=2, then break at i=3
30
+ expect(count).toBe(2);
31
+ });
32
+
33
+ test('break exits while loop immediately', () => {
34
+ const source = `
35
+ let count = 0
36
+ let i = 0
37
+ while i < 10 {
38
+ i = i + 1
39
+ if i == 5 {
40
+ break
41
+ }
42
+ count = count + 1
43
+ }
44
+ `;
45
+ const program = parse(source);
46
+ let state = createInitialState(program);
47
+ state = runUntilPause(state);
48
+
49
+ expect(state.status).toBe('completed');
50
+ const frame = currentFrame(state);
51
+ const count = resolveValue(frame.locals['count']);
52
+ // Loop should have run for i=1,2,3,4, then break at i=5
53
+ expect(count).toBe(4);
54
+ const i = resolveValue(frame.locals['i']);
55
+ expect(i).toBe(5);
56
+ });
57
+
58
+ test('break only exits innermost loop (nested for-in)', () => {
59
+ const source = `
60
+ let outerCount = 0
61
+ let innerCount = 0
62
+ for i in [1, 2, 3] {
63
+ outerCount = outerCount + 1
64
+ for j in [1, 2, 3, 4, 5] {
65
+ if j == 2 {
66
+ break
67
+ }
68
+ innerCount = innerCount + 1
69
+ }
70
+ }
71
+ `;
72
+ const program = parse(source);
73
+ let state = createInitialState(program);
74
+ state = runUntilPause(state);
75
+
76
+ expect(state.status).toBe('completed');
77
+ const frame = currentFrame(state);
78
+ const outerCount = resolveValue(frame.locals['outerCount']);
79
+ const innerCount = resolveValue(frame.locals['innerCount']);
80
+ // Outer loop runs 3 times
81
+ expect(outerCount).toBe(3);
82
+ // Inner loop breaks at j=2 each time, so only j=1 increments count = 1 per outer iteration
83
+ expect(innerCount).toBe(3);
84
+ });
85
+
86
+ test('break only exits innermost loop (nested while)', () => {
87
+ const source = `
88
+ let outerCount = 0
89
+ let innerCount = 0
90
+ let i = 0
91
+ while i < 3 {
92
+ i = i + 1
93
+ outerCount = outerCount + 1
94
+ let j = 0
95
+ while j < 10 {
96
+ j = j + 1
97
+ if j == 3 {
98
+ break
99
+ }
100
+ innerCount = innerCount + 1
101
+ }
102
+ }
103
+ `;
104
+ const program = parse(source);
105
+ let state = createInitialState(program);
106
+ state = runUntilPause(state);
107
+
108
+ expect(state.status).toBe('completed');
109
+ const frame = currentFrame(state);
110
+ const outerCount = resolveValue(frame.locals['outerCount']);
111
+ const innerCount = resolveValue(frame.locals['innerCount']);
112
+ // Outer loop runs 3 times
113
+ expect(outerCount).toBe(3);
114
+ // Inner loop breaks at j=3 each time, so j=1,2 increment count = 2 per outer iteration
115
+ expect(innerCount).toBe(6);
116
+ });
117
+
118
+ test('break works in for-in inside while', () => {
119
+ const source = `
120
+ let result = 0
121
+ let i = 0
122
+ while i < 2 {
123
+ i = i + 1
124
+ for j in [10, 20, 30, 40] {
125
+ result = result + j
126
+ if j == 20 {
127
+ break
128
+ }
129
+ }
130
+ }
131
+ `;
132
+ const program = parse(source);
133
+ let state = createInitialState(program);
134
+ state = runUntilPause(state);
135
+
136
+ expect(state.status).toBe('completed');
137
+ const frame = currentFrame(state);
138
+ const result = resolveValue(frame.locals['result']);
139
+ // Each outer iteration: 10 + 20 = 30 (break at 20)
140
+ // 2 outer iterations: 30 * 2 = 60
141
+ expect(result).toBe(60);
142
+ });
143
+
144
+ test('break works in while inside for-in', () => {
145
+ const source = `
146
+ let result = 0
147
+ for i in [1, 2] {
148
+ let j = 0
149
+ while j < 100 {
150
+ j = j + 1
151
+ result = result + 1
152
+ if j == 5 {
153
+ break
154
+ }
155
+ }
156
+ }
157
+ `;
158
+ const program = parse(source);
159
+ let state = createInitialState(program);
160
+ state = runUntilPause(state);
161
+
162
+ expect(state.status).toBe('completed');
163
+ const frame = currentFrame(state);
164
+ const result = resolveValue(frame.locals['result']);
165
+ // Each outer iteration: while runs 5 times (j=1,2,3,4,5 then break)
166
+ // 2 outer iterations: 5 * 2 = 10
167
+ expect(result).toBe(10);
168
+ });
169
+
170
+ test('break as first statement in loop body', () => {
171
+ const source = `
172
+ let count = 0
173
+ for i in [1, 2, 3] {
174
+ break
175
+ count = count + 1
176
+ }
177
+ `;
178
+ const program = parse(source);
179
+ let state = createInitialState(program);
180
+ state = runUntilPause(state);
181
+
182
+ expect(state.status).toBe('completed');
183
+ const frame = currentFrame(state);
184
+ const count = resolveValue(frame.locals['count']);
185
+ // Break immediately, count never incremented
186
+ expect(count).toBe(0);
187
+ });
188
+
189
+ test('break preserves variables declared before loop', () => {
190
+ const source = `
191
+ let x = 100
192
+ for i in [1, 2, 3, 4, 5] {
193
+ x = x + i
194
+ if i == 3 {
195
+ break
196
+ }
197
+ }
198
+ `;
199
+ const program = parse(source);
200
+ let state = createInitialState(program);
201
+ state = runUntilPause(state);
202
+
203
+ expect(state.status).toBe('completed');
204
+ const frame = currentFrame(state);
205
+ const x = resolveValue(frame.locals['x']);
206
+ // x = 100 + 1 + 2 + 3 = 106 (break at i=3, after adding 3)
207
+ expect(x).toBe(106);
208
+ });
209
+
210
+ test('break cleans up loop variable scope', () => {
211
+ const source = `
212
+ let result = 0
213
+ for i in [1, 2, 3] {
214
+ let temp = i * 10
215
+ if i == 2 {
216
+ break
217
+ }
218
+ result = result + temp
219
+ }
220
+ `;
221
+ const program = parse(source);
222
+ let state = createInitialState(program);
223
+ state = runUntilPause(state);
224
+
225
+ expect(state.status).toBe('completed');
226
+ const frame = currentFrame(state);
227
+ const result = resolveValue(frame.locals['result']);
228
+ // i=1: temp=10, result=10
229
+ // i=2: temp=20, break before adding
230
+ expect(result).toBe(10);
231
+ // temp should be cleaned up (not in frame.locals at top level)
232
+ expect(frame.locals['temp']).toBeUndefined();
233
+ // i should be cleaned up too
234
+ expect(frame.locals['i']).toBeUndefined();
235
+ });
236
+
237
+ test('conditional break with complex condition', () => {
238
+ const source = `
239
+ let sum = 0
240
+ for n in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] {
241
+ sum = sum + n
242
+ if sum > 15 {
243
+ break
244
+ }
245
+ }
246
+ `;
247
+ const program = parse(source);
248
+ let state = createInitialState(program);
249
+ state = runUntilPause(state);
250
+
251
+ expect(state.status).toBe('completed');
252
+ const frame = currentFrame(state);
253
+ const sum = resolveValue(frame.locals['sum']);
254
+ // 1+2+3+4+5 = 15, then 1+2+3+4+5+6 = 21 > 15, break
255
+ expect(sum).toBe(21);
256
+ });
257
+ });