@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,370 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { parse } from '../../parser/parse';
3
+ import { createInitialState, runUntilPause } from '../index';
4
+
5
+ describe('Runtime Lexical Scoping', () => {
6
+ // ============================================================================
7
+ // Function access to global scope
8
+ // ============================================================================
9
+
10
+ test('function can read global variable', () => {
11
+ const ast = parse(`
12
+ let globalVar = "global_value"
13
+ function readGlobal() {
14
+ return globalVar
15
+ }
16
+ let result = readGlobal()
17
+ `);
18
+ let state = createInitialState(ast);
19
+ state = runUntilPause(state);
20
+
21
+ expect(state.status).toBe('completed');
22
+ expect(state.callStack[0].locals['result'].value).toBe('global_value');
23
+ });
24
+
25
+ test('function can modify global variable', () => {
26
+ const ast = parse(`
27
+ let counter = "before"
28
+ function updateCounter() {
29
+ counter = "after"
30
+ return counter
31
+ }
32
+ updateCounter()
33
+ `);
34
+ let state = createInitialState(ast);
35
+ state = runUntilPause(state);
36
+
37
+ expect(state.status).toBe('completed');
38
+ expect(state.callStack[0].locals['counter'].value).toBe('after');
39
+ });
40
+
41
+ test('function can access global constant', () => {
42
+ const ast = parse(`
43
+ const CONFIG = "config_value"
44
+ function useConfig() {
45
+ return CONFIG
46
+ }
47
+ let result = useConfig()
48
+ `);
49
+ let state = createInitialState(ast);
50
+ state = runUntilPause(state);
51
+
52
+ expect(state.status).toBe('completed');
53
+ expect(state.callStack[0].locals['result'].value).toBe('config_value');
54
+ });
55
+
56
+ test('function can call other global function', () => {
57
+ const ast = parse(`
58
+ function helper() {
59
+ return "helped"
60
+ }
61
+ function main() {
62
+ return helper()
63
+ }
64
+ let result = main()
65
+ `);
66
+ let state = createInitialState(ast);
67
+ state = runUntilPause(state);
68
+
69
+ expect(state.status).toBe('completed');
70
+ expect(state.callStack[0].locals['result'].value).toBe('helped');
71
+ });
72
+
73
+ test('recursion works', () => {
74
+ const ast = parse(`
75
+ let depth = "0"
76
+ function recurse(shouldRecurse: boolean): text {
77
+ if shouldRecurse {
78
+ depth = "reached"
79
+ return recurse(false)
80
+ }
81
+ return depth
82
+ }
83
+ let result = recurse(true)
84
+ `);
85
+ let state = createInitialState(ast);
86
+ state = runUntilPause(state);
87
+
88
+ expect(state.status).toBe('completed');
89
+ expect(state.callStack[0].locals['result'].value).toBe('reached');
90
+ });
91
+
92
+ // ============================================================================
93
+ // Shadowing
94
+ // ============================================================================
95
+
96
+ test('local variable shadows global', () => {
97
+ const ast = parse(`
98
+ let x = "global"
99
+ function shadow() {
100
+ let x = "local"
101
+ return x
102
+ }
103
+ let result = shadow()
104
+ `);
105
+ let state = createInitialState(ast);
106
+ state = runUntilPause(state);
107
+
108
+ expect(state.status).toBe('completed');
109
+ expect(state.callStack[0].locals['result'].value).toBe('local');
110
+ // Global x unchanged
111
+ expect(state.callStack[0].locals['x'].value).toBe('global');
112
+ });
113
+
114
+ test('parameter shadows global', () => {
115
+ const ast = parse(`
116
+ let name = "global_name"
117
+ function greet(name: text): text {
118
+ return name
119
+ }
120
+ let result = greet("param_name")
121
+ `);
122
+ let state = createInitialState(ast);
123
+ state = runUntilPause(state);
124
+
125
+ expect(state.status).toBe('completed');
126
+ expect(state.callStack[0].locals['result'].value).toBe('param_name');
127
+ // Global name unchanged
128
+ expect(state.callStack[0].locals['name'].value).toBe('global_name');
129
+ });
130
+
131
+ // ============================================================================
132
+ // String interpolation with scope chain
133
+ // ============================================================================
134
+
135
+ test('string interpolation can access global variables from function', () => {
136
+ const ast = parse(`
137
+ let name = "World"
138
+ function greet() {
139
+ return "Hello, {name}!"
140
+ }
141
+ let result = greet()
142
+ `);
143
+ let state = createInitialState(ast);
144
+ state = runUntilPause(state);
145
+
146
+ expect(state.status).toBe('completed');
147
+ expect(state.callStack[0].locals['result'].value).toBe('Hello, World!');
148
+ });
149
+
150
+ test('string interpolation prefers local over global (shadowing)', () => {
151
+ const ast = parse(`
152
+ let name = "Global"
153
+ function greet() {
154
+ let name = "Local"
155
+ return "Hello, {name}!"
156
+ }
157
+ let result = greet()
158
+ `);
159
+ let state = createInitialState(ast);
160
+ state = runUntilPause(state);
161
+
162
+ expect(state.status).toBe('completed');
163
+ expect(state.callStack[0].locals['result'].value).toBe('Hello, Local!');
164
+ });
165
+
166
+ // ============================================================================
167
+ // Model access from functions
168
+ // ============================================================================
169
+
170
+ test('function can access global model', () => {
171
+ const ast = parse(`
172
+ model myModel = { name: "test", apiKey: "key", url: "http://test" }
173
+ function useModel() {
174
+ return myModel
175
+ }
176
+ let result = useModel()
177
+ `);
178
+ let state = createInitialState(ast);
179
+ state = runUntilPause(state);
180
+
181
+ expect(state.status).toBe('completed');
182
+ const result = state.callStack[0].locals['result'].value as any;
183
+ // Model is accessible from function - just verify it's a model object
184
+ expect(result.__vibeModel).toBe(true);
185
+ });
186
+
187
+ test('cannot assign to global model from function', () => {
188
+ const ast = parse(`
189
+ model myModel = { name: "test", apiKey: "key", url: "http://test" }
190
+ function tryModify() {
191
+ myModel = "something else"
192
+ return myModel
193
+ }
194
+ tryModify()
195
+ `);
196
+ let state = createInitialState(ast);
197
+ state = runUntilPause(state);
198
+
199
+ expect(state.status).toBe('error');
200
+ expect(state.error).toContain("Cannot assign to constant 'myModel'");
201
+ });
202
+
203
+ test('local variable can shadow global model', () => {
204
+ const ast = parse(`
205
+ model myModel = { name: "test", apiKey: "key", url: "http://test" }
206
+ function shadowModel() {
207
+ let myModel = "shadowed"
208
+ return myModel
209
+ }
210
+ let result = shadowModel()
211
+ `);
212
+ let state = createInitialState(ast);
213
+ state = runUntilPause(state);
214
+
215
+ expect(state.status).toBe('completed');
216
+ expect(state.callStack[0].locals['result'].value).toBe('shadowed');
217
+ // Global model unchanged
218
+ const globalModel = state.callStack[0].locals['myModel'].value as any;
219
+ expect(globalModel.__vibeModel).toBe(true);
220
+ });
221
+
222
+ // ============================================================================
223
+ // Block scope within functions
224
+ // ============================================================================
225
+
226
+ test('block in function can access function locals', () => {
227
+ const ast = parse(`
228
+ function test() {
229
+ let funcVar = "func_value"
230
+ if true {
231
+ return funcVar
232
+ }
233
+ return "not reached"
234
+ }
235
+ let result = test()
236
+ `);
237
+ let state = createInitialState(ast);
238
+ state = runUntilPause(state);
239
+
240
+ expect(state.status).toBe('completed');
241
+ expect(state.callStack[0].locals['result'].value).toBe('func_value');
242
+ });
243
+
244
+ test('block in function can access function parameters', () => {
245
+ const ast = parse(`
246
+ function test(param: text): text {
247
+ if true {
248
+ return param
249
+ }
250
+ return "not reached"
251
+ }
252
+ let result = test("param_value")
253
+ `);
254
+ let state = createInitialState(ast);
255
+ state = runUntilPause(state);
256
+
257
+ expect(state.status).toBe('completed');
258
+ expect(state.callStack[0].locals['result'].value).toBe('param_value');
259
+ });
260
+
261
+ test('block in function can access global scope', () => {
262
+ const ast = parse(`
263
+ let globalVar = "global"
264
+ function test() {
265
+ if true {
266
+ return globalVar
267
+ }
268
+ return "not reached"
269
+ }
270
+ let result = test()
271
+ `);
272
+ let state = createInitialState(ast);
273
+ state = runUntilPause(state);
274
+
275
+ expect(state.status).toBe('completed');
276
+ expect(state.callStack[0].locals['result'].value).toBe('global');
277
+ });
278
+
279
+ // ============================================================================
280
+ // Nested blocks
281
+ // ============================================================================
282
+
283
+ test('nested blocks can access all outer scopes', () => {
284
+ const ast = parse(`
285
+ let a = "level0"
286
+ if true {
287
+ let b = "level1"
288
+ if true {
289
+ let c = "level2"
290
+ if true {
291
+ a = b
292
+ b = c
293
+ }
294
+ }
295
+ }
296
+ `);
297
+ let state = createInitialState(ast);
298
+ state = runUntilPause(state);
299
+
300
+ expect(state.status).toBe('completed');
301
+ expect(state.callStack[0].locals['a'].value).toBe('level1');
302
+ });
303
+
304
+ // ============================================================================
305
+ // Const protection across scopes
306
+ // ============================================================================
307
+
308
+ test('cannot assign to global const from function', () => {
309
+ const ast = parse(`
310
+ const CONFIG = "original"
311
+ function tryModify() {
312
+ CONFIG = "modified"
313
+ return CONFIG
314
+ }
315
+ tryModify()
316
+ `);
317
+ let state = createInitialState(ast);
318
+ state = runUntilPause(state);
319
+
320
+ expect(state.status).toBe('error');
321
+ expect(state.error).toContain("Cannot assign to constant 'CONFIG'");
322
+ });
323
+
324
+ // ============================================================================
325
+ // Complex scenarios
326
+ // ============================================================================
327
+
328
+ test('multiple functions sharing global state', () => {
329
+ const ast = parse(`
330
+ let state = "initial"
331
+ function setState(val: text): text {
332
+ state = val
333
+ return state
334
+ }
335
+ function getState() {
336
+ return state
337
+ }
338
+ setState("updated")
339
+ let result = getState()
340
+ `);
341
+ let state = createInitialState(ast);
342
+ state = runUntilPause(state);
343
+
344
+ expect(state.status).toBe('completed');
345
+ expect(state.callStack[0].locals['result'].value).toBe('updated');
346
+ });
347
+
348
+ test('function chain using global variables', () => {
349
+ const ast = parse(`
350
+ let prefix = "Hello"
351
+ let suffix = "!"
352
+ function addPrefix(msg: text): text {
353
+ return "{prefix}, {msg}"
354
+ }
355
+ function addSuffix(msg: text): text {
356
+ return "{msg}{suffix}"
357
+ }
358
+ function greet(name: text): text {
359
+ let withPrefix = addPrefix(name)
360
+ return addSuffix(withPrefix)
361
+ }
362
+ let result = greet("World")
363
+ `);
364
+ let state = createInitialState(ast);
365
+ state = runUntilPause(state);
366
+
367
+ expect(state.status).toBe('completed');
368
+ expect(state.callStack[0].locals['result'].value).toBe('Hello, World!');
369
+ });
370
+ });