redscript-mc 2.6.2 → 3.0.0

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 (636) hide show
  1. package/.github/workflows/ci.yml +11 -0
  2. package/CHANGELOG.md +18 -9
  3. package/README-benchmarks.md +48 -0
  4. package/README-vscode-test.md +251 -0
  5. package/RELEASE_NOTES.md +74 -0
  6. package/ROADMAP.md +131 -167
  7. package/benchmarks/_shared.ts +468 -0
  8. package/benchmarks/baseline.json +2816 -0
  9. package/benchmarks/baseline.md +13 -0
  10. package/benchmarks/compiler-perf.report.json +207 -0
  11. package/benchmarks/compiler-perf.ts +76 -0
  12. package/benchmarks/results.md +13 -0
  13. package/benchmarks/stdlib-complexity.report.json +2606 -0
  14. package/benchmarks/stdlib-complexity.ts +54 -0
  15. package/benchmarks/stdlib-size.md +57 -0
  16. package/benchmarks/stdlib-size.ts +91 -0
  17. package/coverage-report.md +177 -0
  18. package/dist/src/__tests__/budget.test.js +4 -0
  19. package/dist/src/__tests__/cache/cache-behavior.test.d.ts +10 -0
  20. package/dist/src/__tests__/cache/cache-behavior.test.js +425 -0
  21. package/dist/src/__tests__/cache-extra.test.d.ts +5 -0
  22. package/dist/src/__tests__/cache-extra.test.js +211 -0
  23. package/dist/src/__tests__/cli-init.test.d.ts +1 -0
  24. package/dist/src/__tests__/cli-init.test.js +97 -0
  25. package/dist/src/__tests__/cli-publish.test.d.ts +9 -0
  26. package/dist/src/__tests__/cli-publish.test.js +189 -0
  27. package/dist/src/__tests__/cli.test.js +76 -0
  28. package/dist/src/__tests__/compile-preprocess.test.d.ts +11 -0
  29. package/dist/src/__tests__/compile-preprocess.test.js +328 -0
  30. package/dist/src/__tests__/compiler/break-stmt.test.d.ts +1 -0
  31. package/dist/src/__tests__/compiler/break-stmt.test.js +58 -0
  32. package/dist/src/__tests__/compiler/const-decl.test.d.ts +1 -0
  33. package/dist/src/__tests__/compiler/const-decl.test.js +123 -0
  34. package/dist/src/__tests__/compiler/continue-stmt.test.d.ts +1 -0
  35. package/dist/src/__tests__/compiler/continue-stmt.test.js +67 -0
  36. package/dist/src/__tests__/compiler/coroutine-extended.test.d.ts +17 -0
  37. package/dist/src/__tests__/compiler/coroutine-extended.test.js +565 -0
  38. package/dist/src/__tests__/compiler/deprecated.test.d.ts +4 -0
  39. package/dist/src/__tests__/compiler/deprecated.test.js +285 -0
  40. package/dist/src/__tests__/compiler/do-while.test.d.ts +1 -0
  41. package/dist/src/__tests__/compiler/do-while.test.js +120 -0
  42. package/dist/src/__tests__/compiler/enum-payload.test.d.ts +9 -0
  43. package/dist/src/__tests__/compiler/enum-payload.test.js +272 -0
  44. package/dist/src/__tests__/compiler/interface.test.d.ts +10 -0
  45. package/dist/src/__tests__/compiler/interface.test.js +258 -0
  46. package/dist/src/__tests__/compiler/labeled-loops.test.d.ts +1 -0
  47. package/dist/src/__tests__/compiler/labeled-loops.test.js +263 -0
  48. package/dist/src/__tests__/compiler/match-string.test.d.ts +1 -0
  49. package/dist/src/__tests__/compiler/match-string.test.js +43 -0
  50. package/dist/src/__tests__/compiler/memoize.test.d.ts +1 -0
  51. package/dist/src/__tests__/compiler/memoize.test.js +113 -0
  52. package/dist/src/__tests__/compiler/method-chain.test.d.ts +5 -0
  53. package/dist/src/__tests__/compiler/method-chain.test.js +115 -0
  54. package/dist/src/__tests__/compiler/module-import.test.d.ts +12 -0
  55. package/dist/src/__tests__/compiler/module-import.test.js +261 -0
  56. package/dist/src/__tests__/compiler/option-extensions.test.d.ts +6 -0
  57. package/dist/src/__tests__/compiler/option-extensions.test.js +191 -0
  58. package/dist/src/__tests__/compiler/profile-decorator.test.d.ts +1 -0
  59. package/dist/src/__tests__/compiler/profile-decorator.test.js +69 -0
  60. package/dist/src/__tests__/compiler/string-advanced.test.d.ts +7 -0
  61. package/dist/src/__tests__/compiler/string-advanced.test.js +281 -0
  62. package/dist/src/__tests__/compiler/struct-extends.test.d.ts +1 -0
  63. package/dist/src/__tests__/compiler/struct-extends.test.js +95 -0
  64. package/dist/src/__tests__/compiler/throttle-retry.test.d.ts +1 -0
  65. package/dist/src/__tests__/compiler/throttle-retry.test.js +166 -0
  66. package/dist/src/__tests__/compiler/tuple-type.test.d.ts +10 -0
  67. package/dist/src/__tests__/compiler/tuple-type.test.js +229 -0
  68. package/dist/src/__tests__/compiler/watch-decorator.test.d.ts +1 -0
  69. package/dist/src/__tests__/compiler/watch-decorator.test.js +65 -0
  70. package/dist/src/__tests__/config/project-config.test.d.ts +1 -0
  71. package/dist/src/__tests__/config/project-config.test.js +199 -0
  72. package/dist/src/__tests__/config-decorator.test.d.ts +8 -0
  73. package/dist/src/__tests__/config-decorator.test.js +142 -0
  74. package/dist/src/__tests__/diagnostics-extra.test.d.ts +6 -0
  75. package/dist/src/__tests__/diagnostics-extra.test.js +132 -0
  76. package/dist/src/__tests__/emit/compile-branches.test.d.ts +1 -0
  77. package/dist/src/__tests__/emit/compile-branches.test.js +123 -0
  78. package/dist/src/__tests__/emit/compile-coverage.test.d.ts +25 -0
  79. package/dist/src/__tests__/emit/compile-coverage.test.js +617 -0
  80. package/dist/src/__tests__/emit/compile-extra-branches.test.d.ts +12 -0
  81. package/dist/src/__tests__/emit/compile-extra-branches.test.js +225 -0
  82. package/dist/src/__tests__/emit/compile-mocked-branches.test.d.ts +0 -0
  83. package/dist/src/__tests__/emit/compile-mocked-branches.test.js +238 -0
  84. package/dist/src/__tests__/emit/execute-chain.test.d.ts +10 -0
  85. package/dist/src/__tests__/emit/execute-chain.test.js +94 -0
  86. package/dist/src/__tests__/emit/index.test.js +2 -1
  87. package/dist/src/__tests__/emit/modules-branches.test.d.ts +1 -0
  88. package/dist/src/__tests__/emit/modules-branches.test.js +88 -0
  89. package/dist/src/__tests__/emit/modules-coverage.test.d.ts +15 -0
  90. package/dist/src/__tests__/emit/modules-coverage.test.js +221 -0
  91. package/dist/src/__tests__/emit/modules-errors.test.d.ts +12 -0
  92. package/dist/src/__tests__/emit/modules-errors.test.js +169 -0
  93. package/dist/src/__tests__/emit/modules-rewrite.test.d.ts +17 -0
  94. package/dist/src/__tests__/emit/modules-rewrite.test.js +204 -0
  95. package/dist/src/__tests__/emit/source-map.test.d.ts +1 -0
  96. package/dist/src/__tests__/emit/source-map.test.js +167 -0
  97. package/dist/src/__tests__/enum.test.js +9 -4
  98. package/dist/src/__tests__/error-recovery.test.d.ts +7 -0
  99. package/dist/src/__tests__/error-recovery.test.js +217 -0
  100. package/dist/src/__tests__/events-types-extra.test.d.ts +10 -0
  101. package/dist/src/__tests__/events-types-extra.test.js +91 -0
  102. package/dist/src/__tests__/events-types.test.d.ts +4 -0
  103. package/dist/src/__tests__/events-types.test.js +56 -0
  104. package/dist/src/__tests__/formatter.test.js +13 -5
  105. package/dist/src/__tests__/hir/lower-extra.test.d.ts +9 -0
  106. package/dist/src/__tests__/hir/lower-extra.test.js +140 -0
  107. package/dist/src/__tests__/hir/monomorphize-extra.test.d.ts +15 -0
  108. package/dist/src/__tests__/hir/monomorphize-extra.test.js +200 -0
  109. package/dist/src/__tests__/hir/monomorphize-extra2.test.d.ts +16 -0
  110. package/dist/src/__tests__/hir/monomorphize-extra2.test.js +316 -0
  111. package/dist/src/__tests__/incremental.test.js +10 -2
  112. package/dist/src/__tests__/index-extra.test.d.ts +10 -0
  113. package/dist/src/__tests__/index-extra.test.js +71 -0
  114. package/dist/src/__tests__/lexer.test.js +2 -2
  115. package/dist/src/__tests__/lint/rules.test.d.ts +5 -0
  116. package/dist/src/__tests__/lint/rules.test.js +208 -0
  117. package/dist/src/__tests__/lir/lower.test.js +29 -0
  118. package/dist/src/__tests__/lir/verify.test.js +30 -0
  119. package/dist/src/__tests__/lsp/completion.test.d.ts +7 -0
  120. package/dist/src/__tests__/lsp/completion.test.js +583 -0
  121. package/dist/src/__tests__/lsp/definition.test.d.ts +7 -0
  122. package/dist/src/__tests__/lsp/definition.test.js +454 -0
  123. package/dist/src/__tests__/lsp/diagnostics.test.d.ts +10 -0
  124. package/dist/src/__tests__/lsp/diagnostics.test.js +98 -0
  125. package/dist/src/__tests__/lsp/hover-docs.test.d.ts +10 -0
  126. package/dist/src/__tests__/lsp/hover-docs.test.js +210 -0
  127. package/dist/src/__tests__/lsp.test.js +4 -1
  128. package/dist/src/__tests__/mc-integration/item-entity-events.test.js +4 -0
  129. package/dist/src/__tests__/mc-integration/stdlib-coverage-2.test.js +4 -0
  130. package/dist/src/__tests__/mc-integration/stdlib-coverage-3.test.d.ts +13 -0
  131. package/dist/src/__tests__/mc-integration/stdlib-coverage-3.test.js +1227 -0
  132. package/dist/src/__tests__/mc-integration/stdlib-coverage-4.test.d.ts +13 -0
  133. package/dist/src/__tests__/mc-integration/stdlib-coverage-4.test.js +1509 -0
  134. package/dist/src/__tests__/mc-integration/stdlib-coverage-5.test.d.ts +14 -0
  135. package/dist/src/__tests__/mc-integration/stdlib-coverage-5.test.js +1374 -0
  136. package/dist/src/__tests__/mc-integration/stdlib-coverage-6.test.d.ts +10 -0
  137. package/dist/src/__tests__/mc-integration/stdlib-coverage-6.test.js +759 -0
  138. package/dist/src/__tests__/mc-integration/stdlib-coverage-7.test.d.ts +13 -0
  139. package/dist/src/__tests__/mc-integration/stdlib-coverage-7.test.js +855 -0
  140. package/dist/src/__tests__/mc-integration/stdlib-coverage.test.js +4 -0
  141. package/dist/src/__tests__/mc-integration/syntax-coverage.test.js +4 -0
  142. package/dist/src/__tests__/mc-validator-coverage.test.d.ts +13 -0
  143. package/dist/src/__tests__/mc-validator-coverage.test.js +296 -0
  144. package/dist/src/__tests__/mc-validator-extra.test.d.ts +13 -0
  145. package/dist/src/__tests__/mc-validator-extra.test.js +245 -0
  146. package/dist/src/__tests__/mir/lower-extra.test.d.ts +20 -0
  147. package/dist/src/__tests__/mir/lower-extra.test.js +361 -0
  148. package/dist/src/__tests__/mir/lower-extra2.test.d.ts +17 -0
  149. package/dist/src/__tests__/mir/lower-extra2.test.js +317 -0
  150. package/dist/src/__tests__/mir/lower-extra3.test.d.ts +19 -0
  151. package/dist/src/__tests__/mir/lower-extra3.test.js +249 -0
  152. package/dist/src/__tests__/mir/lower-extra4.test.d.ts +23 -0
  153. package/dist/src/__tests__/mir/lower-extra4.test.js +606 -0
  154. package/dist/src/__tests__/mir/lower-extra5.test.d.ts +25 -0
  155. package/dist/src/__tests__/mir/lower-extra5.test.js +543 -0
  156. package/dist/src/__tests__/mir/lower-extra6.test.d.ts +16 -0
  157. package/dist/src/__tests__/mir/lower-extra6.test.js +471 -0
  158. package/dist/src/__tests__/mir/lower-extra7.test.d.ts +35 -0
  159. package/dist/src/__tests__/mir/lower-extra7.test.js +921 -0
  160. package/dist/src/__tests__/mir/lower-extra8.test.d.ts +19 -0
  161. package/dist/src/__tests__/mir/lower-extra8.test.js +626 -0
  162. package/dist/src/__tests__/mir/lower-extra9.test.d.ts +14 -0
  163. package/dist/src/__tests__/mir/lower-extra9.test.js +717 -0
  164. package/dist/src/__tests__/optimizer/auto-inline.test.d.ts +1 -0
  165. package/dist/src/__tests__/optimizer/auto-inline.test.js +176 -0
  166. package/dist/src/__tests__/optimizer/cse.test.d.ts +4 -0
  167. package/dist/src/__tests__/optimizer/cse.test.js +178 -0
  168. package/dist/src/__tests__/optimizer/inline_fn.test.d.ts +1 -0
  169. package/dist/src/__tests__/optimizer/inline_fn.test.js +221 -0
  170. package/dist/src/__tests__/optimizer/licm.test.d.ts +1 -0
  171. package/dist/src/__tests__/optimizer/licm.test.js +244 -0
  172. package/dist/src/__tests__/optimizer/optimizer-extended.test.d.ts +12 -0
  173. package/dist/src/__tests__/optimizer/optimizer-extended.test.js +993 -0
  174. package/dist/src/__tests__/optimizer/strength-reduction.test.d.ts +1 -0
  175. package/dist/src/__tests__/optimizer/strength-reduction.test.js +86 -0
  176. package/dist/src/__tests__/optimizer/tco.test.d.ts +14 -0
  177. package/dist/src/__tests__/optimizer/tco.test.js +203 -0
  178. package/dist/src/__tests__/parser-coverage.test.d.ts +25 -0
  179. package/dist/src/__tests__/parser-coverage.test.js +491 -0
  180. package/dist/src/__tests__/parser-extra.test.d.ts +6 -0
  181. package/dist/src/__tests__/parser-extra.test.js +451 -0
  182. package/dist/src/__tests__/parser.test.js +12 -0
  183. package/dist/src/__tests__/repl-extra.test.d.ts +13 -0
  184. package/dist/src/__tests__/repl-extra.test.js +174 -0
  185. package/dist/src/__tests__/repl-server-extra.test.d.ts +10 -0
  186. package/dist/src/__tests__/repl-server-extra.test.js +161 -0
  187. package/dist/src/__tests__/repl-server.test.d.ts +6 -0
  188. package/dist/src/__tests__/repl-server.test.js +146 -0
  189. package/dist/src/__tests__/runtime-extra.test.d.ts +15 -0
  190. package/dist/src/__tests__/runtime-extra.test.js +732 -0
  191. package/dist/src/__tests__/singleton-decorator.test.d.ts +11 -0
  192. package/dist/src/__tests__/singleton-decorator.test.js +260 -0
  193. package/dist/src/__tests__/sourcemap.test.js +1 -1
  194. package/dist/src/__tests__/stdlib/advanced.test.d.ts +5 -0
  195. package/dist/src/__tests__/stdlib/advanced.test.js +301 -0
  196. package/dist/src/__tests__/stdlib/bigint.test.d.ts +4 -0
  197. package/dist/src/__tests__/stdlib/bigint.test.js +83 -0
  198. package/dist/src/__tests__/stdlib/bits.test.d.ts +4 -0
  199. package/dist/src/__tests__/stdlib/bits.test.js +96 -0
  200. package/dist/src/__tests__/stdlib/bossbar.test.d.ts +4 -0
  201. package/dist/src/__tests__/stdlib/bossbar.test.js +72 -0
  202. package/dist/src/__tests__/stdlib/color.test.d.ts +4 -0
  203. package/dist/src/__tests__/stdlib/color.test.js +84 -0
  204. package/dist/src/__tests__/stdlib/combat.test.d.ts +4 -0
  205. package/dist/src/__tests__/stdlib/combat.test.js +64 -0
  206. package/dist/src/__tests__/stdlib/cooldown.test.d.ts +4 -0
  207. package/dist/src/__tests__/stdlib/cooldown.test.js +64 -0
  208. package/dist/src/__tests__/stdlib/dialog.test.js +15 -7
  209. package/dist/src/__tests__/stdlib/ecs.test.d.ts +4 -0
  210. package/dist/src/__tests__/stdlib/ecs.test.js +81 -0
  211. package/dist/src/__tests__/stdlib/effects.test.d.ts +4 -0
  212. package/dist/src/__tests__/stdlib/effects.test.js +72 -0
  213. package/dist/src/__tests__/stdlib/events.test.d.ts +4 -0
  214. package/dist/src/__tests__/stdlib/events.test.js +55 -0
  215. package/dist/src/__tests__/stdlib/expr.test.d.ts +4 -0
  216. package/dist/src/__tests__/stdlib/expr.test.js +77 -0
  217. package/dist/src/__tests__/stdlib/fft.test.d.ts +4 -0
  218. package/dist/src/__tests__/stdlib/fft.test.js +82 -0
  219. package/dist/src/__tests__/stdlib/graph.test.d.ts +4 -0
  220. package/dist/src/__tests__/stdlib/graph.test.js +102 -0
  221. package/dist/src/__tests__/stdlib/interactions.test.d.ts +4 -0
  222. package/dist/src/__tests__/stdlib/interactions.test.js +60 -0
  223. package/dist/src/__tests__/stdlib/inventory.test.d.ts +4 -0
  224. package/dist/src/__tests__/stdlib/inventory.test.js +68 -0
  225. package/dist/src/__tests__/stdlib/linalg.test.d.ts +5 -0
  226. package/dist/src/__tests__/stdlib/linalg.test.js +78 -0
  227. package/dist/src/__tests__/stdlib/map.test.d.ts +1 -0
  228. package/dist/src/__tests__/stdlib/map.test.js +84 -0
  229. package/dist/src/__tests__/stdlib/math.test.js +19 -6
  230. package/dist/src/__tests__/stdlib/math_hp.test.d.ts +4 -0
  231. package/dist/src/__tests__/stdlib/math_hp.test.js +80 -0
  232. package/dist/src/__tests__/stdlib/mobs.test.d.ts +4 -0
  233. package/dist/src/__tests__/stdlib/mobs.test.js +61 -0
  234. package/dist/src/__tests__/stdlib/noise.test.d.ts +4 -0
  235. package/dist/src/__tests__/stdlib/noise.test.js +73 -0
  236. package/dist/src/__tests__/stdlib/ode.test.d.ts +4 -0
  237. package/dist/src/__tests__/stdlib/ode.test.js +68 -0
  238. package/dist/src/__tests__/stdlib/parabola.test.d.ts +4 -0
  239. package/dist/src/__tests__/stdlib/parabola.test.js +77 -0
  240. package/dist/src/__tests__/stdlib/particles.test.d.ts +4 -0
  241. package/dist/src/__tests__/stdlib/particles.test.js +68 -0
  242. package/dist/src/__tests__/stdlib/physics.test.d.ts +4 -0
  243. package/dist/src/__tests__/stdlib/physics.test.js +76 -0
  244. package/dist/src/__tests__/stdlib/player.test.d.ts +4 -0
  245. package/dist/src/__tests__/stdlib/player.test.js +64 -0
  246. package/dist/src/__tests__/stdlib/quaternion.test.d.ts +4 -0
  247. package/dist/src/__tests__/stdlib/quaternion.test.js +73 -0
  248. package/dist/src/__tests__/stdlib/queue.test.d.ts +1 -0
  249. package/dist/src/__tests__/stdlib/queue.test.js +97 -0
  250. package/dist/src/__tests__/stdlib/random.test.d.ts +4 -0
  251. package/dist/src/__tests__/stdlib/random.test.js +76 -0
  252. package/dist/src/__tests__/stdlib/result.test.d.ts +12 -0
  253. package/dist/src/__tests__/stdlib/result.test.js +329 -0
  254. package/dist/src/__tests__/stdlib/scheduler.test.js +19 -8
  255. package/dist/src/__tests__/stdlib/set_int.test.d.ts +1 -0
  256. package/dist/src/__tests__/stdlib/set_int.test.js +88 -0
  257. package/dist/src/__tests__/stdlib/sets.test.d.ts +6 -0
  258. package/dist/src/__tests__/stdlib/sets.test.js +60 -0
  259. package/dist/src/__tests__/stdlib/signal.test.d.ts +4 -0
  260. package/dist/src/__tests__/stdlib/signal.test.js +84 -0
  261. package/dist/src/__tests__/stdlib/spawn.test.d.ts +4 -0
  262. package/dist/src/__tests__/stdlib/spawn.test.js +68 -0
  263. package/dist/src/__tests__/stdlib/string.test.d.ts +12 -0
  264. package/dist/src/__tests__/stdlib/string.test.js +231 -0
  265. package/dist/src/__tests__/stdlib/strings.test.d.ts +4 -0
  266. package/dist/src/__tests__/stdlib/strings.test.js +83 -0
  267. package/dist/src/__tests__/stdlib/tags.test.d.ts +4 -0
  268. package/dist/src/__tests__/stdlib/tags.test.js +57 -0
  269. package/dist/src/__tests__/stdlib/teams.test.d.ts +4 -0
  270. package/dist/src/__tests__/stdlib/teams.test.js +72 -0
  271. package/dist/src/__tests__/stdlib/timer.test.d.ts +4 -0
  272. package/dist/src/__tests__/stdlib/timer.test.js +79 -0
  273. package/dist/src/__tests__/stdlib/vec.test.d.ts +5 -0
  274. package/dist/src/__tests__/stdlib/vec.test.js +94 -0
  275. package/dist/src/__tests__/stdlib/world.test.d.ts +4 -0
  276. package/dist/src/__tests__/stdlib/world.test.js +72 -0
  277. package/dist/src/__tests__/struct-display.test.d.ts +1 -0
  278. package/dist/src/__tests__/struct-display.test.js +64 -0
  279. package/dist/src/__tests__/test-framework/runner.test.d.ts +10 -0
  280. package/dist/src/__tests__/test-framework/runner.test.js +193 -0
  281. package/dist/src/__tests__/tuner/adapters.test.d.ts +14 -0
  282. package/dist/src/__tests__/tuner/adapters.test.js +194 -0
  283. package/dist/src/__tests__/tuner/simulator-extra.test.d.ts +4 -0
  284. package/dist/src/__tests__/tuner/simulator-extra.test.js +193 -0
  285. package/dist/src/__tests__/typechecker-coverage.test.d.ts +30 -0
  286. package/dist/src/__tests__/typechecker-coverage.test.js +627 -0
  287. package/dist/src/__tests__/typechecker.test.js +3 -3
  288. package/dist/src/__tests__/watch-decorator.test.d.ts +1 -0
  289. package/dist/src/__tests__/watch-decorator.test.js +54 -0
  290. package/dist/src/ast/types.d.ts +102 -3
  291. package/dist/src/cache/incremental.d.ts +13 -14
  292. package/dist/src/cache/incremental.js +106 -89
  293. package/dist/src/cache/index.d.ts +8 -2
  294. package/dist/src/cache/index.js +18 -6
  295. package/dist/src/cli.d.ts +1 -0
  296. package/dist/src/cli.js +466 -17
  297. package/dist/src/config/project-config.d.ts +29 -0
  298. package/dist/src/config/project-config.js +180 -0
  299. package/dist/src/diagnostics/index.d.ts +9 -0
  300. package/dist/src/diagnostics/index.js +18 -1
  301. package/dist/src/emit/compile.d.ts +10 -0
  302. package/dist/src/emit/compile.js +395 -50
  303. package/dist/src/emit/index.d.ts +40 -0
  304. package/dist/src/emit/index.js +307 -14
  305. package/dist/src/emit/modules.js +21 -3
  306. package/dist/src/emit/sourcemap.d.ts +23 -27
  307. package/dist/src/emit/sourcemap.js +52 -30
  308. package/dist/src/formatter/index.js +33 -8
  309. package/dist/src/hir/deprecated.d.ts +13 -0
  310. package/dist/src/hir/deprecated.js +218 -0
  311. package/dist/src/hir/lower.js +114 -8
  312. package/dist/src/hir/monomorphize.js +22 -2
  313. package/dist/src/hir/types.d.ts +65 -1
  314. package/dist/src/index.d.ts +6 -0
  315. package/dist/src/index.js +18 -3
  316. package/dist/src/lexer/index.d.ts +2 -1
  317. package/dist/src/lexer/index.js +39 -3
  318. package/dist/src/lint/index.d.ts +45 -0
  319. package/dist/src/lint/index.js +930 -0
  320. package/dist/src/lir/lower.js +29 -2
  321. package/dist/src/lir/types.d.ts +2 -0
  322. package/dist/src/lsp/server.js +92 -5
  323. package/dist/src/mir/lower.js +775 -34
  324. package/dist/src/mir/macro.js +36 -2
  325. package/dist/src/mir/types.d.ts +12 -0
  326. package/dist/src/mir/verify.js +9 -0
  327. package/dist/src/optimizer/auto-inline.d.ts +2 -0
  328. package/dist/src/optimizer/auto-inline.js +67 -0
  329. package/dist/src/optimizer/cse.d.ts +20 -0
  330. package/dist/src/optimizer/cse.js +234 -0
  331. package/dist/src/optimizer/inline.d.ts +26 -0
  332. package/dist/src/optimizer/inline.js +286 -0
  333. package/dist/src/optimizer/interprocedural.js +4 -0
  334. package/dist/src/optimizer/licm.d.ts +32 -0
  335. package/dist/src/optimizer/licm.js +371 -0
  336. package/dist/src/optimizer/pipeline.js +12 -2
  337. package/dist/src/optimizer/strength_reduction.d.ts +15 -0
  338. package/dist/src/optimizer/strength_reduction.js +90 -0
  339. package/dist/src/optimizer/tco.d.ts +53 -0
  340. package/dist/src/optimizer/tco.js +238 -0
  341. package/dist/src/parser/index.d.ts +32 -0
  342. package/dist/src/parser/index.js +421 -59
  343. package/dist/src/repl-server.d.ts +13 -0
  344. package/dist/src/repl-server.js +127 -0
  345. package/dist/src/structs/expand.d.ts +15 -0
  346. package/dist/src/structs/expand.js +46 -0
  347. package/dist/src/testing/runner.d.ts +40 -0
  348. package/dist/src/testing/runner.js +237 -0
  349. package/dist/src/typechecker/index.d.ts +3 -0
  350. package/dist/src/typechecker/index.js +254 -9
  351. package/dist/tsconfig.tsbuildinfo +1 -1
  352. package/doc-drafts/redscript-docs/docs/en/stdlib/graph.md +104 -0
  353. package/doc-drafts/redscript-docs/docs/en/stdlib/parabola.md +113 -0
  354. package/doc-drafts/redscript-docs/docs/en/stdlib/pathfind.md +104 -0
  355. package/doc-drafts/redscript-docs/docs/en/stdlib/physics.md +134 -0
  356. package/doc-drafts/redscript-docs/docs/en/stdlib/quaternion.md +135 -0
  357. package/doc-drafts/redscript-docs/docs/zh/stdlib/graph.md +104 -0
  358. package/doc-drafts/redscript-docs/docs/zh/stdlib/parabola.md +113 -0
  359. package/doc-drafts/redscript-docs/docs/zh/stdlib/pathfind.md +104 -0
  360. package/doc-drafts/redscript-docs/docs/zh/stdlib/physics.md +134 -0
  361. package/doc-drafts/redscript-docs/docs/zh/stdlib/quaternion.md +135 -0
  362. package/docs/stdlib/result.md +156 -0
  363. package/docs/stdlib/result.zh.md +156 -0
  364. package/editors/vscode/fixtures/test.mcrs +7 -0
  365. package/editors/vscode/out/extension.js +2095 -225
  366. package/editors/vscode/out/lsp-server.js +519 -51
  367. package/editors/vscode/package-lock.json +9 -4
  368. package/editors/vscode/package.json +1 -1
  369. package/examples/display-demo.mcrs +64 -0
  370. package/examples/game/racing.mcrs +301 -0
  371. package/examples/game/tower_defense.mcrs +311 -0
  372. package/examples/math/physics_sim.mcrs +322 -0
  373. package/examples/rpg/boss_fight.mcrs +313 -0
  374. package/examples/rpg/health_system.mcrs +237 -0
  375. package/examples/rpg/inventory.mcrs +265 -0
  376. package/examples/util/debug_hud.mcrs +279 -0
  377. package/jest.config.js +10 -0
  378. package/package.json +12 -3
  379. package/playground/index.html +823 -0
  380. package/scripts/gen-docs.ts +533 -0
  381. package/scripts/update-redscript-docs-stdlib.sh +770 -0
  382. package/src/__tests__/budget.test.ts +5 -0
  383. package/src/__tests__/cache/cache-behavior.test.ts +480 -0
  384. package/src/__tests__/cache-extra.test.ts +199 -0
  385. package/src/__tests__/cli-docs.test.ts +77 -0
  386. package/src/__tests__/cli-init.test.ts +91 -0
  387. package/src/__tests__/cli-publish.test.ts +190 -0
  388. package/src/__tests__/cli.test.ts +117 -1
  389. package/src/__tests__/compile-preprocess.test.ts +366 -0
  390. package/src/__tests__/compiler/break-stmt.test.ts +66 -0
  391. package/src/__tests__/compiler/const-decl.test.ts +141 -0
  392. package/src/__tests__/compiler/continue-stmt.test.ts +81 -0
  393. package/src/__tests__/compiler/coroutine-extended.test.ts +723 -0
  394. package/src/__tests__/compiler/deprecated.test.ts +305 -0
  395. package/src/__tests__/compiler/do-while.test.ts +130 -0
  396. package/src/__tests__/compiler/enum-payload.test.ts +299 -0
  397. package/src/__tests__/compiler/interface.test.ts +287 -0
  398. package/src/__tests__/compiler/labeled-loops.test.ts +279 -0
  399. package/src/__tests__/compiler/match-string.test.ts +45 -0
  400. package/src/__tests__/compiler/memoize.test.ts +126 -0
  401. package/src/__tests__/compiler/method-chain.test.ts +121 -0
  402. package/src/__tests__/compiler/module-import.test.ts +240 -0
  403. package/src/__tests__/compiler/option-extensions.test.ts +207 -0
  404. package/src/__tests__/compiler/profile-decorator.test.ts +79 -0
  405. package/src/__tests__/compiler/string-advanced.test.ts +310 -0
  406. package/src/__tests__/compiler/struct-extends.test.ts +109 -0
  407. package/src/__tests__/compiler/throttle-retry.test.ts +191 -0
  408. package/src/__tests__/compiler/tuple-type.test.ts +263 -0
  409. package/src/__tests__/compiler/watch-decorator.test.ts +72 -0
  410. package/src/__tests__/config/project-config.test.ts +181 -0
  411. package/src/__tests__/config-decorator.test.ts +157 -0
  412. package/src/__tests__/diagnostics-extra.test.ts +155 -0
  413. package/src/__tests__/emit/compile-branches.test.ts +135 -0
  414. package/src/__tests__/emit/compile-coverage.test.ts +696 -0
  415. package/src/__tests__/emit/compile-extra-branches.test.ts +228 -0
  416. package/src/__tests__/emit/compile-mocked-branches.test.ts +249 -0
  417. package/src/__tests__/emit/compile.test.ts +6 -1
  418. package/src/__tests__/emit/execute-chain.test.ts +114 -0
  419. package/src/__tests__/emit/index.test.ts +2 -1
  420. package/src/__tests__/emit/modules-branches.test.ts +90 -0
  421. package/src/__tests__/emit/modules-coverage.test.ts +241 -0
  422. package/src/__tests__/emit/modules-errors.test.ts +192 -0
  423. package/src/__tests__/emit/modules-rewrite.test.ts +232 -0
  424. package/src/__tests__/emit/source-map.test.ts +152 -0
  425. package/src/__tests__/enum.test.ts +9 -4
  426. package/src/__tests__/error-recovery.test.ts +226 -0
  427. package/src/__tests__/events-types-extra.test.ts +110 -0
  428. package/src/__tests__/events-types.test.ts +66 -0
  429. package/src/__tests__/formatter.test.ts +15 -5
  430. package/src/__tests__/generics.test.ts +16 -9
  431. package/src/__tests__/hir/lower-extra.test.ts +151 -0
  432. package/src/__tests__/hir/monomorphize-coverage.test.ts +432 -0
  433. package/src/__tests__/hir/monomorphize-extra.test.ts +220 -0
  434. package/src/__tests__/hir/monomorphize-extra2.test.ts +350 -0
  435. package/src/__tests__/impl.test.ts +12 -8
  436. package/src/__tests__/incremental.test.ts +10 -2
  437. package/src/__tests__/index-extra.test.ts +79 -0
  438. package/src/__tests__/lexer.test.ts +2 -2
  439. package/src/__tests__/lint/hir-coverage.test.ts +1716 -0
  440. package/src/__tests__/lint/rules-coverage.test.ts +598 -0
  441. package/src/__tests__/lint/rules.test.ts +230 -0
  442. package/src/__tests__/lir/lower.test.ts +33 -0
  443. package/src/__tests__/lir/verify.test.ts +33 -0
  444. package/src/__tests__/lsp/completion.test.ts +687 -0
  445. package/src/__tests__/lsp/definition.test.ts +499 -0
  446. package/src/__tests__/lsp/diagnostics.test.ts +108 -0
  447. package/src/__tests__/lsp/hover-docs.test.ts +222 -0
  448. package/src/__tests__/lsp.test.ts +4 -1
  449. package/src/__tests__/mc-integration/item-entity-events.test.ts +5 -0
  450. package/src/__tests__/mc-integration/stdlib-coverage-2.test.ts +5 -0
  451. package/src/__tests__/mc-integration/stdlib-coverage-3.test.ts +1105 -0
  452. package/src/__tests__/mc-integration/stdlib-coverage-4.test.ts +1366 -0
  453. package/src/__tests__/mc-integration/stdlib-coverage-5.test.ts +1245 -0
  454. package/src/__tests__/mc-integration/stdlib-coverage-6.test.ts +755 -0
  455. package/src/__tests__/mc-integration/stdlib-coverage-7.test.ts +771 -0
  456. package/src/__tests__/mc-integration/stdlib-coverage.test.ts +5 -0
  457. package/src/__tests__/mc-integration/syntax-coverage.test.ts +5 -0
  458. package/src/__tests__/mc-validator-coverage.test.ts +325 -0
  459. package/src/__tests__/mc-validator-extra.test.ts +252 -0
  460. package/src/__tests__/mir/lower-extra.test.ts +402 -0
  461. package/src/__tests__/mir/lower-extra2.test.ts +348 -0
  462. package/src/__tests__/mir/lower-extra3.test.ts +277 -0
  463. package/src/__tests__/mir/lower-extra4.test.ts +636 -0
  464. package/src/__tests__/mir/lower-extra5.test.ts +612 -0
  465. package/src/__tests__/mir/lower-extra6.test.ts +520 -0
  466. package/src/__tests__/mir/lower-extra7.test.ts +1045 -0
  467. package/src/__tests__/mir/lower-extra8.test.ts +704 -0
  468. package/src/__tests__/mir/lower-extra9.test.ts +821 -0
  469. package/src/__tests__/optimizer/auto-inline.test.ts +206 -0
  470. package/src/__tests__/optimizer/cse.test.ts +195 -0
  471. package/src/__tests__/optimizer/inline_fn.test.ts +263 -0
  472. package/src/__tests__/optimizer/licm.test.ts +358 -0
  473. package/src/__tests__/optimizer/nbt-coalesce.test.ts +147 -0
  474. package/src/__tests__/optimizer/optimizer-extended.test.ts +1081 -0
  475. package/src/__tests__/optimizer/scoreboard-batch.test.ts +141 -0
  476. package/src/__tests__/optimizer/strength-reduction.test.ts +111 -0
  477. package/src/__tests__/optimizer/tco-coverage.test.ts +309 -0
  478. package/src/__tests__/optimizer/tco.test.ts +238 -0
  479. package/src/__tests__/option.test.ts +14 -7
  480. package/src/__tests__/parser-coverage.test.ts +576 -0
  481. package/src/__tests__/parser-extra.test.ts +531 -0
  482. package/src/__tests__/parser.test.ts +14 -0
  483. package/src/__tests__/repl-extra.test.ts +195 -0
  484. package/src/__tests__/repl-server-extra.test.ts +150 -0
  485. package/src/__tests__/repl-server.test.ts +122 -0
  486. package/src/__tests__/runtime-extra.test.ts +862 -0
  487. package/src/__tests__/singleton-decorator.test.ts +285 -0
  488. package/src/__tests__/sourcemap.test.ts +1 -1
  489. package/src/__tests__/stdlib/advanced.test.ts +312 -0
  490. package/src/__tests__/stdlib/bigint.test.ts +57 -0
  491. package/src/__tests__/stdlib/bits.test.ts +75 -0
  492. package/src/__tests__/stdlib/bossbar.test.ts +45 -0
  493. package/src/__tests__/stdlib/color.test.ts +60 -0
  494. package/src/__tests__/stdlib/combat.test.ts +35 -0
  495. package/src/__tests__/stdlib/cooldown.test.ts +35 -0
  496. package/src/__tests__/stdlib/dialog.test.ts +14 -6
  497. package/src/__tests__/stdlib/ecs.test.ts +54 -0
  498. package/src/__tests__/stdlib/effects.test.ts +45 -0
  499. package/src/__tests__/stdlib/events.test.ts +23 -0
  500. package/src/__tests__/stdlib/expr.test.ts +48 -0
  501. package/src/__tests__/stdlib/fft.test.ts +54 -0
  502. package/src/__tests__/stdlib/graph.test.ts +77 -0
  503. package/src/__tests__/stdlib/interactions.test.ts +30 -0
  504. package/src/__tests__/stdlib/inventory.test.ts +40 -0
  505. package/src/__tests__/stdlib/linalg.test.ts +52 -0
  506. package/src/__tests__/stdlib/map.test.ts +55 -0
  507. package/src/__tests__/stdlib/math.test.ts +19 -5
  508. package/src/__tests__/stdlib/math_hp.test.ts +55 -0
  509. package/src/__tests__/stdlib/mobs.test.ts +40 -0
  510. package/src/__tests__/stdlib/noise.test.ts +46 -0
  511. package/src/__tests__/stdlib/ode.test.ts +40 -0
  512. package/src/__tests__/stdlib/parabola.test.ts +51 -0
  513. package/src/__tests__/stdlib/particles.test.ts +40 -0
  514. package/src/__tests__/stdlib/physics.test.ts +50 -0
  515. package/src/__tests__/stdlib/player.test.ts +35 -0
  516. package/src/__tests__/stdlib/quaternion.test.ts +46 -0
  517. package/src/__tests__/stdlib/queue.test.ts +73 -0
  518. package/src/__tests__/stdlib/random.test.ts +50 -0
  519. package/src/__tests__/stdlib/result.test.ts +326 -0
  520. package/src/__tests__/stdlib/scheduler.test.ts +18 -7
  521. package/src/__tests__/stdlib/set_int.test.ts +62 -0
  522. package/src/__tests__/stdlib/sets.test.ts +28 -0
  523. package/src/__tests__/stdlib/signal.test.ts +60 -0
  524. package/src/__tests__/stdlib/spawn.test.ts +40 -0
  525. package/src/__tests__/stdlib/string.test.ts +224 -0
  526. package/src/__tests__/stdlib/strings.test.ts +55 -0
  527. package/src/__tests__/stdlib/tags.test.ts +32 -0
  528. package/src/__tests__/stdlib/teams.test.ts +45 -0
  529. package/src/__tests__/stdlib/timer.test.ts +53 -0
  530. package/src/__tests__/stdlib/vec.test.ts +72 -0
  531. package/src/__tests__/stdlib/world.test.ts +45 -0
  532. package/src/__tests__/struct-display.test.ts +69 -0
  533. package/src/__tests__/test-framework/runner.test.ts +208 -0
  534. package/src/__tests__/tuner/adapters.test.ts +232 -0
  535. package/src/__tests__/tuner/simulator-extra.test.ts +222 -0
  536. package/src/__tests__/tuple.test.ts +11 -4
  537. package/src/__tests__/typechecker-coverage.test.ts +671 -0
  538. package/src/__tests__/typechecker.test.ts +4 -3
  539. package/src/__tests__/watch-decorator.test.ts +59 -0
  540. package/src/ast/types.ts +65 -3
  541. package/src/cache/incremental.ts +128 -99
  542. package/src/cache/index.ts +35 -8
  543. package/src/cli.ts +538 -29
  544. package/src/config/project-config.ts +176 -0
  545. package/src/diagnostics/index.ts +22 -0
  546. package/src/docs.ts +98 -0
  547. package/src/emit/compile.ts +408 -51
  548. package/src/emit/index.ts +366 -18
  549. package/src/emit/modules.ts +19 -3
  550. package/src/emit/sourcemap.ts +64 -43
  551. package/src/formatter/index.ts +35 -8
  552. package/src/hir/deprecated.ts +212 -0
  553. package/src/hir/lower.ts +128 -8
  554. package/src/hir/monomorphize.ts +24 -2
  555. package/src/hir/types.ts +26 -1
  556. package/src/index.ts +23 -3
  557. package/src/lexer/index.ts +45 -6
  558. package/src/lint/index.ts +922 -0
  559. package/src/lir/lower.ts +30 -2
  560. package/src/lir/types.ts +4 -0
  561. package/src/lsp/server.ts +100 -1
  562. package/src/mir/lower.ts +785 -40
  563. package/src/mir/macro.ts +30 -2
  564. package/src/mir/types.ts +13 -0
  565. package/src/mir/verify.ts +10 -2
  566. package/src/optimizer/auto-inline.ts +86 -0
  567. package/src/optimizer/copy_prop.ts +2 -2
  568. package/src/optimizer/coroutine.ts +3 -3
  569. package/src/optimizer/cse.ts +205 -0
  570. package/src/optimizer/dce.ts +2 -2
  571. package/src/optimizer/inline.ts +335 -0
  572. package/src/optimizer/interprocedural.ts +5 -1
  573. package/src/optimizer/licm.ts +454 -0
  574. package/src/optimizer/nbt-coalesce.ts +109 -0
  575. package/src/optimizer/pipeline.ts +16 -2
  576. package/src/optimizer/scoreboard-batch.ts +52 -0
  577. package/src/optimizer/strength_reduction.ts +95 -0
  578. package/src/optimizer/tco.ts +267 -0
  579. package/src/optimizer/unroll.ts +2 -2
  580. package/src/parser/index.ts +426 -53
  581. package/src/repl-server.ts +102 -0
  582. package/src/stdlib/advanced.mcrs +271 -101
  583. package/src/stdlib/bigint.mcrs +97 -11
  584. package/src/stdlib/bits.mcrs +75 -12
  585. package/src/stdlib/bossbar.mcrs +37 -8
  586. package/src/stdlib/calculus.mcrs +82 -26
  587. package/src/stdlib/color.mcrs +98 -16
  588. package/src/stdlib/combat.mcrs +23 -5
  589. package/src/stdlib/cooldown.mcrs +19 -0
  590. package/src/stdlib/dialog.mcrs +45 -7
  591. package/src/stdlib/easing.mcrs +132 -12
  592. package/src/stdlib/ecs.mcrs +142 -25
  593. package/src/stdlib/effects.mcrs +88 -12
  594. package/src/stdlib/events.mcrs +21 -2
  595. package/src/stdlib/expr.mcrs +18 -3
  596. package/src/stdlib/fft.mcrs +66 -56
  597. package/src/stdlib/geometry.mcrs +137 -39
  598. package/src/stdlib/graph.mcrs +73 -0
  599. package/src/stdlib/heap.mcrs +49 -8
  600. package/src/stdlib/i18n/zh.yaml +2891 -0
  601. package/src/stdlib/interactions.mcrs +43 -20
  602. package/src/stdlib/inventory.mcrs +14 -3
  603. package/src/stdlib/linalg.mcrs +185 -30
  604. package/src/stdlib/list.mcrs +168 -18
  605. package/src/stdlib/map.mcrs +112 -0
  606. package/src/stdlib/math.mcrs +68 -18
  607. package/src/stdlib/math_hp.mcrs +124 -33
  608. package/src/stdlib/matrix.mcrs +133 -20
  609. package/src/stdlib/mobs.mcrs +87 -0
  610. package/src/stdlib/noise.mcrs +65 -21
  611. package/src/stdlib/ode.mcrs +96 -0
  612. package/src/stdlib/parabola.mcrs +104 -29
  613. package/src/stdlib/particles.mcrs +78 -21
  614. package/src/stdlib/pathfind.mcrs +89 -35
  615. package/src/stdlib/physics.mcrs +134 -26
  616. package/src/stdlib/player.mcrs +18 -0
  617. package/src/stdlib/quaternion.mcrs +213 -9
  618. package/src/stdlib/queue.mcrs +123 -0
  619. package/src/stdlib/random.mcrs +63 -18
  620. package/src/stdlib/result.mcrs +111 -0
  621. package/src/stdlib/scheduler.mcrs +59 -10
  622. package/src/stdlib/set_int.mcrs +240 -0
  623. package/src/stdlib/sets.mcrs +49 -19
  624. package/src/stdlib/signal.mcrs +151 -79
  625. package/src/stdlib/sort.mcrs +44 -24
  626. package/src/stdlib/spawn.mcrs +30 -7
  627. package/src/stdlib/state.mcrs +40 -5
  628. package/src/stdlib/strings.mcrs +131 -3
  629. package/src/stdlib/tags.mcrs +2 -2
  630. package/src/stdlib/teams.mcrs +22 -10
  631. package/src/stdlib/timer.mcrs +36 -6
  632. package/src/stdlib/vec.mcrs +44 -9
  633. package/src/stdlib/world.mcrs +57 -25
  634. package/src/structs/expand.ts +64 -0
  635. package/src/testing/runner.ts +271 -0
  636. package/src/typechecker/index.ts +273 -9
@@ -8,34 +8,89 @@
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.lowerToMIR = lowerToMIR;
10
10
  const macro_1 = require("./macro");
11
+ function formatTypeNode(type) {
12
+ switch (type.kind) {
13
+ case 'named':
14
+ return type.name;
15
+ case 'array':
16
+ return `${formatTypeNode(type.elem)}[]`;
17
+ case 'struct':
18
+ case 'enum':
19
+ return type.name;
20
+ case 'function_type':
21
+ return `fn(${type.params.map(formatTypeNode).join(', ')}) -> ${formatTypeNode(type.return)}`;
22
+ case 'entity':
23
+ return type.entityType;
24
+ case 'selector':
25
+ return type.entityType ? `selector<${type.entityType}>` : 'selector';
26
+ case 'tuple':
27
+ return `(${type.elements.map(formatTypeNode).join(', ')})`;
28
+ case 'option':
29
+ return `Option<${formatTypeNode(type.inner)}>`;
30
+ }
31
+ }
32
+ function formatFunctionSignature(fn) {
33
+ const params = fn.params
34
+ .map(param => `${param.name}: ${formatTypeNode(param.type)}`)
35
+ .join(', ');
36
+ return `fn ${fn.name}(${params}) -> ${formatTypeNode(fn.returnType)}`;
37
+ }
11
38
  // ---------------------------------------------------------------------------
12
39
  // Public API
13
40
  // ---------------------------------------------------------------------------
14
41
  function lowerToMIR(hir, sourceFile) {
15
42
  // Build struct definitions: name → field names
16
43
  const structDefs = new Map();
44
+ // Track @singleton struct names for special expansion of GameState::set(gs) calls
45
+ const singletonStructs = new Set();
17
46
  for (const s of hir.structs) {
18
47
  structDefs.set(s.name, s.fields.map(f => f.name));
48
+ if (s.isSingleton)
49
+ singletonStructs.add(s.name);
19
50
  }
20
51
  // Build enum definitions: enumName → variantName → integer value
21
52
  const enumDefs = new Map();
53
+ // Build enum payload info: enumName → variantName → field list
54
+ const enumPayloads = new Map();
22
55
  for (const e of hir.enums) {
23
56
  const variants = new Map();
57
+ const payloads = new Map();
24
58
  for (const v of e.variants) {
25
59
  variants.set(v.name, v.value ?? 0);
60
+ if (v.fields && v.fields.length > 0) {
61
+ payloads.set(v.name, v.fields);
62
+ }
26
63
  }
27
64
  enumDefs.set(e.name, variants);
65
+ if (payloads.size > 0) {
66
+ enumPayloads.set(e.name, payloads);
67
+ }
28
68
  }
29
- // Build impl method info: typeName → methodName → { hasSelf }
69
+ // Build impl method info: typeName → methodName → { hasSelf, returnStructName? }
30
70
  const implMethods = new Map();
31
71
  for (const ib of hir.implBlocks) {
32
72
  const methods = new Map();
33
73
  for (const m of ib.methods) {
34
74
  const hasSelf = m.params.length > 0 && m.params[0].name === 'self';
35
- methods.set(m.name, { hasSelf });
75
+ const returnStructName = m.returnType.kind === 'struct' ? m.returnType.name : undefined;
76
+ methods.set(m.name, { hasSelf, returnStructName });
36
77
  }
37
78
  implMethods.set(ib.typeName, methods);
38
79
  }
80
+ // Build Display impl registry: typeName → f-string parts from to_string body
81
+ // Used to inline Display::to_string() calls at call sites instead of generating a function.
82
+ const displayImpls = new Map();
83
+ for (const ib of hir.implBlocks) {
84
+ if (ib.traitName === 'Display') {
85
+ const toStringMethod = ib.methods.find(m => m.name === 'to_string');
86
+ if (toStringMethod && toStringMethod.body.length > 0) {
87
+ const firstStmt = toStringMethod.body[0];
88
+ if (firstStmt.kind === 'return' && firstStmt.value && firstStmt.value.kind === 'f_string') {
89
+ displayImpls.set(ib.typeName, firstStmt.value.parts);
90
+ }
91
+ }
92
+ }
93
+ }
39
94
  // Pre-scan for macro functions
40
95
  const macroInfo = (0, macro_1.detectMacroFunctions)(hir);
41
96
  // Build function param info for call_macro generation at call sites
@@ -56,15 +111,27 @@ function lowerToMIR(hir, sourceFile) {
56
111
  }
57
112
  // Shared registry: specializedName → [mirFn, ...helpers]
58
113
  const specializedFnsRegistry = new Map();
114
+ // Build module-level const map: name → integer value (for inlining at use sites)
115
+ const constValues = new Map();
116
+ for (const c of hir.consts) {
117
+ if (c.value.kind === 'int_lit')
118
+ constValues.set(c.name, c.value.value);
119
+ else if (c.value.kind === 'bool_lit')
120
+ constValues.set(c.name, c.value.value ? 1 : 0);
121
+ else if (c.value.kind === 'float_lit')
122
+ constValues.set(c.name, Math.round(c.value.value * 10000));
123
+ }
59
124
  const allFunctions = [];
60
125
  for (const f of hir.functions) {
61
- const { fn, helpers } = lowerFunction(f, hir.namespace, structDefs, implMethods, macroInfo, fnParamInfo, enumDefs, sourceFile, timerCounter, undefined, hirFnMap, specializedFnsRegistry);
126
+ const { fn, helpers } = lowerFunction(f, hir.namespace, structDefs, implMethods, macroInfo, fnParamInfo, enumDefs, sourceFile, timerCounter, undefined, hirFnMap, specializedFnsRegistry, undefined, enumPayloads, constValues, singletonStructs, displayImpls);
62
127
  allFunctions.push(fn, ...helpers);
63
128
  }
64
- // Lower impl block methods
129
+ // Lower impl block methods (skip Display::to_string — inlined at call sites instead)
65
130
  for (const ib of hir.implBlocks) {
131
+ if (ib.traitName === 'Display')
132
+ continue; // Display impls are inlined, not emitted as functions
66
133
  for (const m of ib.methods) {
67
- const { fn, helpers } = lowerImplMethod(m, ib.typeName, hir.namespace, structDefs, implMethods, macroInfo, fnParamInfo, enumDefs, sourceFile, timerCounter);
134
+ const { fn, helpers } = lowerImplMethod(m, ib.typeName, hir.namespace, structDefs, implMethods, macroInfo, fnParamInfo, enumDefs, sourceFile, timerCounter, enumPayloads, constValues);
68
135
  allFunctions.push(fn, ...helpers);
69
136
  }
70
137
  }
@@ -82,12 +149,14 @@ function lowerToMIR(hir, sourceFile) {
82
149
  // Function lowering context
83
150
  // ---------------------------------------------------------------------------
84
151
  class FnContext {
85
- constructor(namespace, fnName, structDefs = new Map(), implMethods = new Map(), macroInfo = new Map(), fnParamInfo = new Map(), enumDefs = new Map(), timerCounter = { count: 0, timerId: 0 }) {
152
+ constructor(namespace, fnName, structDefs = new Map(), implMethods = new Map(), macroInfo = new Map(), fnParamInfo = new Map(), enumDefs = new Map(), timerCounter = { count: 0, timerId: 0 }, enumPayloads = new Map()) {
86
153
  this.tempCounter = 0;
87
154
  this.blockCounter = 0;
88
155
  this.blocks = [];
89
- /** Stack of (loopHeader, loopExit, continueTo) for break/continue */
156
+ /** Stack of (loopHeader, loopExit, continueTo, label?) for break/continue */
90
157
  this.loopStack = [];
158
+ /** Pending label to attach to the next pushLoop call (set by labeled_loop lowering) */
159
+ this.pendingLoopLabel = undefined;
91
160
  /** Extracted helper functions for execute blocks */
92
161
  this.helperFunctions = [];
93
162
  /** Struct variable tracking: varName → { typeName, fields: fieldName → temp } */
@@ -96,6 +165,8 @@ class FnContext {
96
165
  this.tupleVars = new Map();
97
166
  /** Array variable tracking: varName → { ns, pathPrefix } for NBT-backed int[] */
98
167
  this.arrayVars = new Map();
168
+ /** String variable tracking: varName → storage path within rs:strings */
169
+ this.stringVars = new Map();
99
170
  /** Current source location (set during statement lowering) */
100
171
  this.currentSourceLoc = undefined;
101
172
  /** Source file path for the module being compiled */
@@ -107,10 +178,17 @@ class FnContext {
107
178
  /** Tracks double variables: varName → NBT storage path (without "rs:d " prefix) */
108
179
  this.doubleVars = new Map();
109
180
  this.doubleVarCount = 0;
181
+ this.stringVarCount = 0;
110
182
  /** HIR function definitions for array-arg monomorphization */
111
183
  this.hirFunctions = new Map();
112
184
  /** Shared registry of already-generated specialized (monomorphized) MIR functions */
113
185
  this.specializedFnsRegistry = new Map();
186
+ /** Module-level const values: name → integer value (inlined at use sites) */
187
+ this.constValues = new Map();
188
+ /** @singleton struct names — static_call GameState::set(gs) expands struct fields */
189
+ this.singletonStructs = new Set();
190
+ /** Display trait impls: typeName → f-string parts from to_string body (inlined at call sites) */
191
+ this.displayImpls = new Map();
114
192
  this.namespace = namespace;
115
193
  this.fnName = fnName;
116
194
  this.structDefs = structDefs;
@@ -119,6 +197,7 @@ class FnContext {
119
197
  this.fnParamInfo = fnParamInfo;
120
198
  this.currentMacroParams = macroInfo.get(fnName)?.macroParams ?? new Set();
121
199
  this.enumDefs = enumDefs;
200
+ this.enumPayloads = enumPayloads;
122
201
  this.timerCounter = timerCounter;
123
202
  const entry = this.makeBlock('entry');
124
203
  this.currentBlock = entry;
@@ -157,8 +236,13 @@ class FnContext {
157
236
  current() {
158
237
  return this.currentBlock;
159
238
  }
160
- pushLoop(header, exit, continueTo) {
161
- this.loopStack.push({ header, exit, continueTo: continueTo ?? header });
239
+ setPendingLoopLabel(label) {
240
+ this.pendingLoopLabel = label;
241
+ }
242
+ pushLoop(header, exit, continueTo, label) {
243
+ const effectiveLabel = label ?? this.pendingLoopLabel;
244
+ this.pendingLoopLabel = undefined;
245
+ this.loopStack.push({ header, exit, continueTo: continueTo ?? header, label: effectiveLabel });
162
246
  }
163
247
  popLoop() {
164
248
  this.loopStack.pop();
@@ -166,6 +250,14 @@ class FnContext {
166
250
  currentLoop() {
167
251
  return this.loopStack[this.loopStack.length - 1];
168
252
  }
253
+ /** Find loop by label — searches from innermost to outermost */
254
+ findLoopByLabel(label) {
255
+ for (let i = this.loopStack.length - 1; i >= 0; i--) {
256
+ if (this.loopStack[i].label === label)
257
+ return this.loopStack[i];
258
+ }
259
+ return undefined;
260
+ }
169
261
  getNamespace() {
170
262
  return this.namespace;
171
263
  }
@@ -178,6 +270,10 @@ class FnContext {
178
270
  this.doubleVars.set(varName, path);
179
271
  return path;
180
272
  }
273
+ /** Allocate a unique NBT storage path for a string value */
274
+ freshStringVar(varName) {
275
+ return `${this.namespace}_${this.fnName}_${varName}_${this.stringVarCount++}`;
276
+ }
181
277
  }
182
278
  // ---------------------------------------------------------------------------
183
279
  // Function lowering
@@ -190,10 +286,13 @@ hirFnMap,
190
286
  /** Shared registry of already-generated specialized MIR functions */
191
287
  specializedFnsRegistry,
192
288
  /** Override the MIR function name (used when generating specialized versions) */
193
- overrideName) {
289
+ overrideName, enumPayloads = new Map(), constValues = new Map(), singletonStructs = new Set(), displayImpls = new Map()) {
194
290
  const mirFnName = overrideName ?? fn.name;
195
- const ctx = new FnContext(namespace, mirFnName, structDefs, implMethods, macroInfo, fnParamInfo, enumDefs, timerCounter);
196
- ctx.sourceFile = sourceFile;
291
+ const ctx = new FnContext(namespace, mirFnName, structDefs, implMethods, macroInfo, fnParamInfo, enumDefs, timerCounter, enumPayloads);
292
+ ctx.sourceFile = fn.sourceFile ?? sourceFile;
293
+ ctx.constValues = constValues;
294
+ ctx.singletonStructs = singletonStructs;
295
+ ctx.displayImpls = displayImpls;
197
296
  if (hirFnMap)
198
297
  ctx.hirFunctions = hirFnMap;
199
298
  if (specializedFnsRegistry)
@@ -210,6 +309,7 @@ overrideName) {
210
309
  const params = [];
211
310
  const scope = new Map();
212
311
  let doubleParamSlot = 0;
312
+ let stringParamSlot = 0;
213
313
  fn.params.forEach((p) => {
214
314
  if (p.type.kind === 'array' && arrayArgBindings?.has(p.name)) {
215
315
  // Array param already bound via arrayVars; no scoreboard slot needed
@@ -222,6 +322,10 @@ overrideName) {
222
322
  // No scoreboard param slot; callee reads from rs:d __dp<i> via doubleVars
223
323
  return;
224
324
  }
325
+ if (p.type.kind === 'named' && (p.type.name === 'string' || p.type.name === 'format_string')) {
326
+ ctx.stringVars.set(p.name, `__sp${stringParamSlot++}`);
327
+ return;
328
+ }
225
329
  const t = ctx.freshTemp();
226
330
  params.push({ name: t, isMacroParam: fnMacroInfo?.macroParams.has(p.name) ?? false });
227
331
  scope.set(p.name, t);
@@ -243,13 +347,16 @@ overrideName) {
243
347
  blocks: liveBlocks,
244
348
  entry: 'entry',
245
349
  isMacro: fnMacroInfo != null,
350
+ sourceLoc: fn.span && (fn.sourceFile ?? sourceFile) ? { file: fn.sourceFile ?? sourceFile, line: fn.span.line, col: fn.span.col } : undefined,
351
+ sourceSnippet: formatFunctionSignature(fn),
246
352
  };
247
353
  return { fn: result, helpers: ctx.helperFunctions };
248
354
  }
249
- function lowerImplMethod(method, typeName, namespace, structDefs, implMethods, macroInfo = new Map(), fnParamInfo = new Map(), enumDefs = new Map(), sourceFile, timerCounter = { count: 0, timerId: 0 }) {
355
+ function lowerImplMethod(method, typeName, namespace, structDefs, implMethods, macroInfo = new Map(), fnParamInfo = new Map(), enumDefs = new Map(), sourceFile, timerCounter = { count: 0, timerId: 0 }, enumPayloads = new Map(), constValues = new Map()) {
250
356
  const fnName = `${typeName}::${method.name}`;
251
- const ctx = new FnContext(namespace, fnName, structDefs, implMethods, macroInfo, fnParamInfo, enumDefs, timerCounter);
252
- ctx.sourceFile = sourceFile;
357
+ const ctx = new FnContext(namespace, fnName, structDefs, implMethods, macroInfo, fnParamInfo, enumDefs, timerCounter, enumPayloads);
358
+ ctx.sourceFile = method.sourceFile ?? sourceFile;
359
+ ctx.constValues = constValues;
253
360
  const fields = structDefs.get(typeName) ?? [];
254
361
  const hasSelf = method.params.length > 0 && method.params[0].name === 'self';
255
362
  const params = [];
@@ -322,6 +429,8 @@ function lowerImplMethod(method, typeName, namespace, structDefs, implMethods, m
322
429
  blocks: liveBlocks,
323
430
  entry: 'entry',
324
431
  isMacro: macroInfo.has(fnName),
432
+ sourceLoc: method.span && (method.sourceFile ?? sourceFile) ? { file: method.sourceFile ?? sourceFile, line: method.span.line, col: method.span.col } : undefined,
433
+ sourceSnippet: formatFunctionSignature(method),
325
434
  };
326
435
  return { fn: result, helpers: ctx.helperFunctions };
327
436
  }
@@ -384,7 +493,14 @@ function lowerStmt(stmt, ctx, scope) {
384
493
  }
385
494
  switch (stmt.kind) {
386
495
  case 'let': {
387
- if (stmt.init.kind === 'some_lit') {
496
+ if (stmt.type?.kind === 'named' && (stmt.type.name === 'string' || stmt.type.name === 'format_string')) {
497
+ const path = lowerStringExprToPath(stmt.init, ctx, scope, stmt.name) ?? ctx.freshStringVar(stmt.name);
498
+ ctx.stringVars.set(stmt.name, path);
499
+ const t = ctx.freshTemp();
500
+ ctx.emit({ kind: 'const', dst: t, value: 0 });
501
+ scope.set(stmt.name, t);
502
+ }
503
+ else if (stmt.init.kind === 'some_lit') {
388
504
  // Some(expr) — create option struct vars: has=1, val=expr
389
505
  // Use __opt_ prefix so DCE treats these as side-effectful scoreboard writes
390
506
  const hasTemp = `__opt_${stmt.name}_has`;
@@ -446,9 +562,17 @@ function lowerStmt(stmt, ctx, scope) {
446
562
  const fieldTemps = new Map([['has', hasTemp], ['val', valTemp]]);
447
563
  ctx.structVars.set(stmt.name, { typeName: '__option', fields: fieldTemps });
448
564
  }
449
- else if (stmt.type?.kind === 'struct') {
565
+ else if (
566
+ // Struct-typed let: explicit annotation OR inferred from @singleton::get() return
567
+ stmt.type?.kind === 'struct' ||
568
+ (stmt.init.kind === 'static_call' &&
569
+ ctx.singletonStructs.has(stmt.init.type) &&
570
+ stmt.init.method === 'get')) {
450
571
  // Struct-typed let with non-literal init (e.g., call returning struct)
451
- const fields = ctx.structDefs.get(stmt.type.name);
572
+ const inferredStructName = stmt.type?.kind === 'struct'
573
+ ? stmt.type.name
574
+ : stmt.init.type;
575
+ const fields = ctx.structDefs.get(inferredStructName);
452
576
  if (fields) {
453
577
  lowerExpr(stmt.init, ctx, scope);
454
578
  // Copy from return field slots into struct variable temps
@@ -464,7 +588,7 @@ function lowerStmt(stmt, ctx, scope) {
464
588
  ctx.constTemps.set(t, constVal);
465
589
  }
466
590
  }
467
- ctx.structVars.set(stmt.name, { typeName: stmt.type.name, fields: fieldTemps });
591
+ ctx.structVars.set(stmt.name, { typeName: inferredStructName, fields: fieldTemps });
468
592
  }
469
593
  else {
470
594
  const valOp = lowerExpr(stmt.init, ctx, scope);
@@ -606,6 +730,13 @@ function lowerStmt(stmt, ctx, scope) {
606
730
  }
607
731
  break;
608
732
  }
733
+ case 'const_decl': {
734
+ // Evaluate the literal at compile time and store in constValues for inlining at use sites
735
+ const op = lowerExpr(stmt.value, ctx, scope);
736
+ const numericValue = op.kind === 'const' ? op.value : 0;
737
+ ctx.constValues.set(stmt.name, numericValue);
738
+ break;
739
+ }
609
740
  case 'expr': {
610
741
  lowerExpr(stmt.expr, ctx, scope);
611
742
  break;
@@ -682,6 +813,32 @@ function lowerStmt(stmt, ctx, scope) {
682
813
  ctx.switchTo(dead);
683
814
  break;
684
815
  }
816
+ case 'break_label': {
817
+ const loop = ctx.findLoopByLabel(stmt.label);
818
+ if (!loop)
819
+ throw new Error(`break: label '${stmt.label}' not found`);
820
+ ctx.terminate({ kind: 'jump', target: loop.exit });
821
+ const dead = ctx.newBlock('post_break_label');
822
+ ctx.switchTo(dead);
823
+ break;
824
+ }
825
+ case 'continue_label': {
826
+ const loop = ctx.findLoopByLabel(stmt.label);
827
+ if (!loop)
828
+ throw new Error(`continue: label '${stmt.label}' not found`);
829
+ ctx.terminate({ kind: 'jump', target: loop.continueTo });
830
+ const dead = ctx.newBlock('post_continue_label');
831
+ ctx.switchTo(dead);
832
+ break;
833
+ }
834
+ case 'labeled_loop': {
835
+ // The body is a while/foreach stmt; we need to push the label into the loop stack.
836
+ // We do this by injecting the label into the next pushLoop call by temporarily
837
+ // storing the pending label in ctx, then letting the inner loop case handle it.
838
+ ctx.setPendingLoopLabel(stmt.label);
839
+ lowerStmt(stmt.body, ctx, scope);
840
+ break;
841
+ }
685
842
  case 'if': {
686
843
  const condOp = lowerExpr(stmt.cond, ctx, scope);
687
844
  const thenBlock = ctx.newBlock('then');
@@ -770,6 +927,8 @@ function lowerStmt(stmt, ctx, scope) {
770
927
  blocks: helperBlocks,
771
928
  entry: 'entry',
772
929
  isMacro: false,
930
+ sourceLoc: stmt.span && ctx.sourceFile ? { file: ctx.sourceFile, line: stmt.span.line, col: stmt.span.col } : undefined,
931
+ sourceSnippet: 'foreach helper',
773
932
  });
774
933
  ctx.emit({ kind: 'call_context', fn: helperName, subcommands });
775
934
  break;
@@ -793,11 +952,52 @@ function lowerStmt(stmt, ctx, scope) {
793
952
  blocks: execBlocks,
794
953
  entry: 'entry',
795
954
  isMacro: false,
955
+ sourceLoc: stmt.span && ctx.sourceFile ? { file: ctx.sourceFile, line: stmt.span.line, col: stmt.span.col } : undefined,
956
+ sourceSnippet: 'execute helper',
796
957
  });
797
958
  ctx.emit({ kind: 'call_context', fn: helperName, subcommands });
798
959
  break;
799
960
  }
800
961
  case 'match': {
962
+ const hasStringPats = stmt.arms.some(a => a.pattern.kind === 'PatExpr' && a.pattern.expr.kind === 'str_lit');
963
+ if (hasStringPats) {
964
+ const matchPath = lowerStringExprToPath(stmt.expr, ctx, scope, 'match');
965
+ if (!matchPath) {
966
+ throw new Error('String match requires a string literal or tracked string variable');
967
+ }
968
+ const mergeBlock = ctx.newBlock('match_merge');
969
+ for (let i = 0; i < stmt.arms.length; i++) {
970
+ const arm = stmt.arms[i];
971
+ const pat = arm.pattern;
972
+ if (pat.kind === 'PatWild') {
973
+ lowerBlock(arm.body, ctx, new Map(scope));
974
+ if (isPlaceholderTerm(ctx.current().term)) {
975
+ ctx.terminate({ kind: 'jump', target: mergeBlock.id });
976
+ }
977
+ continue;
978
+ }
979
+ if (pat.kind === 'PatExpr' && pat.expr.kind === 'str_lit') {
980
+ const cmpTemp = ctx.freshTemp();
981
+ ctx.emit({ kind: 'string_match', dst: cmpTemp, ns: 'rs:strings', path: matchPath, value: pat.expr.value });
982
+ const armBody = ctx.newBlock('match_arm');
983
+ const nextArm = ctx.newBlock('match_next');
984
+ ctx.terminate({ kind: 'branch', cond: { kind: 'temp', name: cmpTemp }, then: armBody.id, else: nextArm.id });
985
+ ctx.switchTo(armBody);
986
+ lowerBlock(arm.body, ctx, new Map(scope));
987
+ if (isPlaceholderTerm(ctx.current().term)) {
988
+ ctx.terminate({ kind: 'jump', target: mergeBlock.id });
989
+ }
990
+ ctx.switchTo(nextArm);
991
+ continue;
992
+ }
993
+ throw new Error(`Unsupported string match pattern: ${pat.kind}`);
994
+ }
995
+ if (isPlaceholderTerm(ctx.current().term)) {
996
+ ctx.terminate({ kind: 'jump', target: mergeBlock.id });
997
+ }
998
+ ctx.switchTo(mergeBlock);
999
+ break;
1000
+ }
801
1001
  // Lower match as chained if/else
802
1002
  const mergeBlock = ctx.newBlock('match_merge');
803
1003
  // Determine if any arm uses Option patterns (PatSome / PatNone).
@@ -893,6 +1093,38 @@ function lowerStmt(stmt, ctx, scope) {
893
1093
  }
894
1094
  ctx.switchTo(nextArm);
895
1095
  }
1096
+ else if (pat.kind === 'PatEnum') {
1097
+ // Enum pattern: check tag value matches, then bind payload fields via NBT reads
1098
+ const tagValue = ctx.enumDefs.get(pat.enumName)?.get(pat.variant) ?? 0;
1099
+ const cmpTemp = ctx.freshTemp();
1100
+ ctx.emit({ kind: 'cmp', dst: cmpTemp, op: 'eq', a: matchVal, b: { kind: 'const', value: tagValue } });
1101
+ const armBody = ctx.newBlock('match_arm');
1102
+ const nextArm = ctx.newBlock('match_next');
1103
+ ctx.terminate({ kind: 'branch', cond: { kind: 'temp', name: cmpTemp }, then: armBody.id, else: nextArm.id });
1104
+ ctx.switchTo(armBody);
1105
+ const armScope = new Map(scope);
1106
+ // Bind each pattern variable by reading the corresponding NBT payload field
1107
+ const payloadFields = ctx.enumPayloads.get(pat.enumName)?.get(pat.variant) ?? [];
1108
+ for (let bi = 0; bi < pat.bindings.length; bi++) {
1109
+ const binding = pat.bindings[bi];
1110
+ const fieldDef = payloadFields[bi];
1111
+ const fieldName = fieldDef ? fieldDef.name : binding;
1112
+ const bindTemp = ctx.freshTemp();
1113
+ ctx.emit({
1114
+ kind: 'nbt_read',
1115
+ dst: bindTemp,
1116
+ ns: 'rs:enums',
1117
+ path: `${pat.enumName}_${fieldName}`,
1118
+ scale: 1,
1119
+ });
1120
+ armScope.set(binding, bindTemp);
1121
+ }
1122
+ lowerBlock(arm.body, ctx, armScope);
1123
+ if (isPlaceholderTerm(ctx.current().term)) {
1124
+ ctx.terminate({ kind: 'jump', target: mergeBlock.id });
1125
+ }
1126
+ ctx.switchTo(nextArm);
1127
+ }
896
1128
  else if (pat.kind === 'PatExpr') {
897
1129
  // Legacy: range_lit or other expression
898
1130
  const expr = pat.expr;
@@ -1010,6 +1242,52 @@ function lowerStmt(stmt, ctx, scope) {
1010
1242
  ctx.switchTo(mergeBlock);
1011
1243
  break;
1012
1244
  }
1245
+ case 'while_let_some': {
1246
+ // while let Some(x) = init { body }
1247
+ // Compiles to: loop { check opt.has; if 0 break; bind x = opt.val; body }
1248
+ const headerBlock = ctx.newBlock('whl_header');
1249
+ const bodyBlock = ctx.newBlock('whl_body');
1250
+ const exitBlock = ctx.newBlock('whl_exit');
1251
+ ctx.terminate({ kind: 'jump', target: headerBlock.id });
1252
+ // Header: re-evaluate init and check has
1253
+ ctx.switchTo(headerBlock);
1254
+ let hasOp;
1255
+ let valTemp;
1256
+ const sv = (() => {
1257
+ if (stmt.init.kind === 'ident')
1258
+ return ctx.structVars.get(stmt.init.name);
1259
+ return undefined;
1260
+ })();
1261
+ if (sv && sv.typeName === '__option') {
1262
+ const hasT = sv.fields.get('has');
1263
+ const valT = sv.fields.get('val');
1264
+ hasOp = { kind: 'temp', name: hasT };
1265
+ valTemp = valT;
1266
+ }
1267
+ else {
1268
+ lowerExpr(stmt.init, ctx, scope);
1269
+ const hasT = ctx.freshTemp();
1270
+ const valT = ctx.freshTemp();
1271
+ ctx.emit({ kind: 'copy', dst: hasT, src: { kind: 'temp', name: '__rf_has' } });
1272
+ ctx.emit({ kind: 'copy', dst: valT, src: { kind: 'temp', name: '__rf_val' } });
1273
+ hasOp = { kind: 'temp', name: hasT };
1274
+ valTemp = valT;
1275
+ }
1276
+ ctx.terminate({ kind: 'branch', cond: hasOp, then: bodyBlock.id, else: exitBlock.id });
1277
+ // Body: bind x = val, run body
1278
+ ctx.switchTo(bodyBlock);
1279
+ const bodyScope = new Map(scope);
1280
+ if (valTemp)
1281
+ bodyScope.set(stmt.binding, valTemp);
1282
+ ctx.pushLoop(headerBlock.id, exitBlock.id, headerBlock.id);
1283
+ lowerBlock(stmt.body, ctx, bodyScope);
1284
+ ctx.popLoop();
1285
+ if (isPlaceholderTerm(ctx.current().term)) {
1286
+ ctx.terminate({ kind: 'jump', target: headerBlock.id });
1287
+ }
1288
+ ctx.switchTo(exitBlock);
1289
+ break;
1290
+ }
1013
1291
  default: {
1014
1292
  const _exhaustive = stmt;
1015
1293
  throw new Error(`Unknown HIR statement kind: ${_exhaustive.kind}`);
@@ -1085,6 +1363,10 @@ function lowerExpr(expr, ctx, scope) {
1085
1363
  const temp = scope.get(expr.name);
1086
1364
  if (temp)
1087
1365
  return { kind: 'temp', name: temp };
1366
+ // Module-level const: inline the literal value directly (no scoreboard slot)
1367
+ if (ctx.constValues.has(expr.name)) {
1368
+ return { kind: 'const', value: ctx.constValues.get(expr.name) };
1369
+ }
1088
1370
  // Unresolved ident — could be a global or external reference
1089
1371
  const t = ctx.freshTemp();
1090
1372
  ctx.emit({ kind: 'copy', dst: t, src: { kind: 'const', value: 0 } });
@@ -1213,6 +1495,35 @@ function lowerExpr(expr, ctx, scope) {
1213
1495
  const value = variants?.get(expr.variant) ?? 0;
1214
1496
  return { kind: 'const', value };
1215
1497
  }
1498
+ case 'enum_construct': {
1499
+ // Enum variant construction with payload:
1500
+ // Color::RGB(r: 10, g: 20, b: 30)
1501
+ // → scoreboard set tag = variant int value
1502
+ // → nbt_write rs:enums Color_r = 10, Color_g = 20, Color_b = 30
1503
+ const variants = ctx.enumDefs.get(expr.enumName);
1504
+ const tagValue = variants?.get(expr.variant) ?? 0;
1505
+ // Write tag to a temp (the result of the expression is the integer tag)
1506
+ const tagTemp = ctx.freshTemp();
1507
+ ctx.emit({ kind: 'const', dst: tagTemp, value: tagValue });
1508
+ // Write payload fields to NBT storage rs:enums
1509
+ const payloadFields = ctx.enumPayloads.get(expr.enumName)?.get(expr.variant) ?? [];
1510
+ for (const arg of expr.args) {
1511
+ const fieldDef = payloadFields.find(f => f.name === arg.name);
1512
+ const argOp = lowerExpr(arg.value, ctx, scope);
1513
+ // Determine NBT type from field definition
1514
+ const nbtType = fieldDef && (fieldDef.type.kind === 'named') &&
1515
+ (fieldDef.type.name === 'float' || fieldDef.type.name === 'fixed') ? 'float' : 'int';
1516
+ ctx.emit({
1517
+ kind: 'nbt_write',
1518
+ ns: 'rs:enums',
1519
+ path: `${expr.enumName}_${arg.name}`,
1520
+ type: nbtType,
1521
+ scale: 1,
1522
+ src: argOp,
1523
+ });
1524
+ }
1525
+ return { kind: 'temp', name: tagTemp };
1526
+ }
1216
1527
  case 'member': {
1217
1528
  // Enum variant access via dot syntax: Phase.Idle → integer constant
1218
1529
  if (expr.obj.kind === 'ident') {
@@ -1535,18 +1846,78 @@ function lowerExpr(expr, ctx, scope) {
1535
1846
  ctx.emit({ kind: 'const', dst: t, value: 0 });
1536
1847
  return { kind: 'temp', name: t };
1537
1848
  }
1849
+ // Handle int_to_str / bool_to_str — identity functions for scoreboard int/bool → string
1850
+ // In f-string context these are handled by precomputeFStringParts; outside that, just
1851
+ // evaluate the argument and return it (integer stays as integer for scoreboard purposes).
1852
+ if (expr.fn === 'int_to_str' || expr.fn === 'bool_to_str') {
1853
+ if (expr.args.length === 1) {
1854
+ return lowerExpr(expr.args[0], ctx, scope);
1855
+ }
1856
+ const t = ctx.freshTemp();
1857
+ ctx.emit({ kind: 'const', dst: t, value: 0 });
1858
+ return { kind: 'temp', name: t };
1859
+ }
1860
+ // Handle assert(cond[, message]) — test framework builtin
1861
+ if (expr.fn === 'assert') {
1862
+ const condArg = expr.args[0];
1863
+ const msgArg = expr.args[1];
1864
+ const condOp = condArg ? lowerExpr(condArg, ctx, scope) : { kind: 'const', value: 0 };
1865
+ // Evaluate condition to a temp
1866
+ let condTemp;
1867
+ if (condOp.kind === 'temp') {
1868
+ condTemp = condOp.name;
1869
+ }
1870
+ else {
1871
+ condTemp = ctx.freshTemp();
1872
+ ctx.emit({ kind: 'const', dst: condTemp, value: condOp.value });
1873
+ }
1874
+ // Get message string
1875
+ let msgStr = 'assert failed';
1876
+ if (msgArg && msgArg.kind === 'str_lit') {
1877
+ msgStr = msgArg.value;
1878
+ }
1879
+ const obj = `__${ctx.getNamespace()}`;
1880
+ // emit: execute unless score $condTemp <obj> matches 1 run tellraw @a "FAIL: <msg>"
1881
+ const failMsg = JSON.stringify({ text: `FAIL: ${msgStr}`, color: 'red' });
1882
+ ctx.emit({ kind: 'call', dst: null, fn: `__raw:execute unless score $${ctx.getFnName()}_${condTemp} ${obj} matches 1 run tellraw @a ${failMsg}`, args: [] });
1883
+ ctx.emit({ kind: 'call', dst: null, fn: `__raw:execute unless score $${ctx.getFnName()}_${condTemp} ${obj} matches 1 run scoreboard players add rs.test_failed rs.meta 1`, args: [] });
1884
+ const t = ctx.freshTemp();
1885
+ ctx.emit({ kind: 'const', dst: t, value: 0 });
1886
+ return { kind: 'temp', name: t };
1887
+ }
1538
1888
  // Handle builtin calls → raw MC commands
1539
1889
  if (macro_1.BUILTIN_SET.has(expr.fn)) {
1540
- const cmd = formatBuiltinCall(expr.fn, expr.args, ctx.currentMacroParams, ctx.getNamespace());
1890
+ // For text builtins with f-string args, precompute complex expressions to temp vars
1891
+ const TEXT_BUILTINS_SET = new Set(['tell', 'tellraw', 'title', 'subtitle', 'actionbar', 'announce']);
1892
+ let resolvedArgs = expr.args;
1893
+ if (TEXT_BUILTINS_SET.has(expr.fn)) {
1894
+ resolvedArgs = expr.args.map(arg => arg.kind === 'f_string' ? precomputeFStringParts(arg, ctx, scope) : arg);
1895
+ }
1896
+ const cmd = formatBuiltinCall(expr.fn, resolvedArgs, ctx.currentMacroParams, ctx.getNamespace());
1541
1897
  ctx.emit({ kind: 'call', dst: null, fn: `__raw:${cmd}`, args: [] });
1542
1898
  const t = ctx.freshTemp();
1543
1899
  ctx.emit({ kind: 'const', dst: t, value: 0 });
1544
1900
  return { kind: 'temp', name: t };
1545
1901
  }
1546
1902
  // Check for struct instance method call: parser desugars v.method() → call('method', [v, ...])
1903
+ // Some method names are remapped by the parser (e.g. add→set_add for set builtins).
1904
+ // We reverse-map them here to support impl methods with those names.
1905
+ const PARSER_METHOD_REMAP = {
1906
+ 'set_add': 'add', 'set_contains': 'contains', 'set_remove': 'remove', 'set_clear': 'clear',
1907
+ '__array_push': 'push', '__array_pop': 'pop',
1908
+ '__entity_tag': 'tag', '__entity_untag': 'untag', '__entity_has_tag': 'has_tag',
1909
+ };
1547
1910
  if (expr.args.length > 0 && expr.args[0].kind === 'ident') {
1548
1911
  const sv = ctx.structVars.get(expr.args[0].name);
1549
1912
  if (sv) {
1913
+ // Intercept Display::to_string() calls — inlined at f-string call sites; return 0 otherwise
1914
+ if (expr.fn === 'to_string' && ctx.displayImpls.has(sv.typeName)) {
1915
+ // Display::to_string() is expanded inline in f-string context (precomputeFStringParts).
1916
+ // Outside of that context, return a dummy 0 (the call is not valid standalone).
1917
+ const t = ctx.freshTemp();
1918
+ ctx.emit({ kind: 'const', dst: t, value: 0 });
1919
+ return { kind: 'temp', name: t };
1920
+ }
1550
1921
  // Intercept Timer method calls when _id is a known compile-time constant
1551
1922
  if (sv.typeName === 'Timer') {
1552
1923
  const idTemp = sv.fields.get('_id');
@@ -1555,7 +1926,10 @@ function lowerExpr(expr, ctx, scope) {
1555
1926
  return lowerTimerMethod(expr.fn, timerId, sv, ctx, scope, expr.args.slice(1));
1556
1927
  }
1557
1928
  }
1558
- const methodInfo = ctx.implMethods.get(sv.typeName)?.get(expr.fn);
1929
+ // Try direct name, then try reverse-mapped name (for parser-remapped builtins)
1930
+ const originalMethodName = PARSER_METHOD_REMAP[expr.fn] ?? expr.fn;
1931
+ const methodInfo = ctx.implMethods.get(sv.typeName)?.get(expr.fn)
1932
+ ?? ctx.implMethods.get(sv.typeName)?.get(originalMethodName);
1559
1933
  if (methodInfo?.hasSelf) {
1560
1934
  // Build args: self fields first, then remaining explicit args
1561
1935
  const fields = ctx.structDefs.get(sv.typeName) ?? [];
@@ -1584,7 +1958,7 @@ function lowerExpr(expr, ctx, scope) {
1584
1958
  }
1585
1959
  const allArgs = [...selfArgs, ...explicitArgs];
1586
1960
  const t = ctx.freshTemp();
1587
- ctx.emit({ kind: 'call', dst: t, fn: `${sv.typeName}::${expr.fn}`, args: allArgs });
1961
+ ctx.emit({ kind: 'call', dst: t, fn: `${sv.typeName}::${originalMethodName}`, args: allArgs });
1588
1962
  return { kind: 'temp', name: t };
1589
1963
  }
1590
1964
  }
@@ -1592,21 +1966,47 @@ function lowerExpr(expr, ctx, scope) {
1592
1966
  // Check if calling a macro function → emit call_macro
1593
1967
  const targetMacro = ctx.macroInfo.get(expr.fn);
1594
1968
  if (targetMacro) {
1595
- const args = expr.args.map(a => lowerExpr(a, ctx, scope));
1596
1969
  const targetParams = ctx.fnParamInfo.get(expr.fn) ?? [];
1597
1970
  const macroArgs = [];
1598
- for (let i = 0; i < targetParams.length && i < args.length; i++) {
1971
+ for (let i = 0; i < targetParams.length && i < expr.args.length; i++) {
1599
1972
  const paramName = targetParams[i].name;
1600
1973
  if (targetMacro.macroParams.has(paramName)) {
1601
1974
  const paramTypeName = targetMacro.paramTypes.get(paramName) ?? 'int';
1602
- const isFloat = paramTypeName === 'float';
1603
- const isFixed = paramTypeName === 'fixed';
1604
- macroArgs.push({
1605
- name: paramName,
1606
- value: args[i],
1607
- type: (isFloat || isFixed) ? 'double' : 'int',
1608
- scale: isFloat ? 0.01 : isFixed ? 0.0001 : 1,
1609
- });
1975
+ const isString = paramTypeName === 'string' || paramTypeName === 'format_string';
1976
+ const isSelector = paramTypeName === 'selector';
1977
+ if (isString) {
1978
+ // String macro params: store directly to rs:macro_args as NBT string
1979
+ const srcPath = lowerStringExprToPath(expr.args[i], ctx, scope, paramName);
1980
+ if (srcPath) {
1981
+ ctx.emit({
1982
+ kind: 'call',
1983
+ dst: null,
1984
+ fn: `__raw:data modify storage rs:macro_args ${paramName} set from storage rs:strings ${srcPath}`,
1985
+ args: [],
1986
+ });
1987
+ }
1988
+ }
1989
+ else if (isSelector) {
1990
+ // Selector macro params: store the selector string as an NBT string in rs:macro_args
1991
+ const arg = expr.args[i];
1992
+ const selStr = arg.kind === 'selector' ? arg.raw : '@s';
1993
+ ctx.emit({
1994
+ kind: 'call',
1995
+ dst: null,
1996
+ fn: `__raw:data modify storage rs:macro_args ${paramName} set value ${JSON.stringify(selStr)}`,
1997
+ args: [],
1998
+ });
1999
+ }
2000
+ else {
2001
+ const isFloat = paramTypeName === 'float';
2002
+ const isFixed = paramTypeName === 'fixed';
2003
+ macroArgs.push({
2004
+ name: paramName,
2005
+ value: lowerExpr(expr.args[i], ctx, scope),
2006
+ type: (isFloat || isFixed) ? 'double' : 'int',
2007
+ scale: isFloat ? 0.01 : isFixed ? 0.0001 : 1,
2008
+ });
2009
+ }
1610
2010
  }
1611
2011
  }
1612
2012
  const t = ctx.freshTemp();
@@ -1640,7 +2040,7 @@ function lowerExpr(expr, ctx, scope) {
1640
2040
  if (!ctx.specializedFnsRegistry.has(specializedName)) {
1641
2041
  // Placeholder to prevent re-entry
1642
2042
  ctx.specializedFnsRegistry.set(specializedName, []);
1643
- const { fn: specFn, helpers: specHelpers } = lowerFunction(targetHirFn, ctx.getNamespace(), ctx.structDefs, ctx.implMethods, ctx.macroInfo, ctx.fnParamInfo, ctx.enumDefs, ctx.sourceFile, ctx.timerCounter, arrayArgBindings, ctx.hirFunctions, ctx.specializedFnsRegistry, specializedName);
2043
+ const { fn: specFn, helpers: specHelpers } = lowerFunction(targetHirFn, ctx.getNamespace(), ctx.structDefs, ctx.implMethods, ctx.macroInfo, ctx.fnParamInfo, ctx.enumDefs, ctx.sourceFile, ctx.timerCounter, arrayArgBindings, ctx.hirFunctions, ctx.specializedFnsRegistry, specializedName, ctx.enumPayloads, ctx.constValues, ctx.singletonStructs, ctx.displayImpls);
1644
2044
  ctx.specializedFnsRegistry.set(specializedName, [specFn, ...specHelpers]);
1645
2045
  }
1646
2046
  // Emit call to the specialized function, passing only non-array args
@@ -1662,6 +2062,35 @@ function lowerExpr(expr, ctx, scope) {
1662
2062
  {
1663
2063
  const targetParams = ctx.fnParamInfo.get(expr.fn);
1664
2064
  if (targetParams) {
2065
+ const hasStringParam = targetParams.some(p => p.type.kind === 'named' && (p.type.name === 'string' || p.type.name === 'format_string'));
2066
+ if (hasStringParam) {
2067
+ const nonStringArgs = [];
2068
+ let stringSlot = 0;
2069
+ for (let i = 0; i < targetParams.length && i < expr.args.length; i++) {
2070
+ const p = targetParams[i];
2071
+ if (p.type.kind === 'named' && (p.type.name === 'string' || p.type.name === 'format_string')) {
2072
+ const srcPath = lowerStringExprToPath(expr.args[i], ctx, scope, `arg${stringSlot}`);
2073
+ if (srcPath) {
2074
+ ctx.emit({
2075
+ kind: 'call',
2076
+ dst: null,
2077
+ fn: `__raw:data modify storage rs:strings __sp${stringSlot} set from storage rs:strings ${srcPath}`,
2078
+ args: [],
2079
+ });
2080
+ }
2081
+ stringSlot++;
2082
+ }
2083
+ else {
2084
+ nonStringArgs.push(lowerExpr(expr.args[i], ctx, scope));
2085
+ }
2086
+ }
2087
+ for (let i = targetParams.length; i < expr.args.length; i++) {
2088
+ nonStringArgs.push(lowerExpr(expr.args[i], ctx, scope));
2089
+ }
2090
+ const t = ctx.freshTemp();
2091
+ ctx.emit({ kind: 'call', dst: t, fn: expr.fn, args: nonStringArgs });
2092
+ return { kind: 'temp', name: t };
2093
+ }
1665
2094
  const hasDoubleParam = targetParams.some(p => p.type.kind === 'named' && p.type.name === 'double');
1666
2095
  if (hasDoubleParam) {
1667
2096
  const ns = ctx.getNamespace();
@@ -1732,6 +2161,12 @@ function lowerExpr(expr, ctx, scope) {
1732
2161
  if (expr.callee.kind === 'member' && expr.callee.obj.kind === 'ident') {
1733
2162
  const sv = ctx.structVars.get(expr.callee.obj.name);
1734
2163
  if (sv) {
2164
+ // Intercept Display::to_string() — inlined in f-string context; return 0 otherwise
2165
+ if (expr.callee.field === 'to_string' && ctx.displayImpls.has(sv.typeName)) {
2166
+ const t = ctx.freshTemp();
2167
+ ctx.emit({ kind: 'const', dst: t, value: 0 });
2168
+ return { kind: 'temp', name: t };
2169
+ }
1735
2170
  // Intercept Timer method calls when _id is a known compile-time constant
1736
2171
  if (sv.typeName === 'Timer') {
1737
2172
  const idTemp = sv.fields.get('_id');
@@ -1756,6 +2191,35 @@ function lowerExpr(expr, ctx, scope) {
1756
2191
  }
1757
2192
  }
1758
2193
  }
2194
+ // Method chaining: callee obj is not a simple ident (e.g. v.scale(2).add(...))
2195
+ // Determine if the callee obj expression returns a struct via __rf_ slots
2196
+ if (expr.callee.kind === 'member' && expr.callee.obj.kind !== 'ident') {
2197
+ const returnedStructType = inferInvokeReturnStructType(expr.callee.obj, ctx);
2198
+ if (returnedStructType) {
2199
+ // Lower the inner call — result goes into __rf_ slots
2200
+ lowerExpr(expr.callee.obj, ctx, scope);
2201
+ // Read __rf_ slots into temps for this chained call
2202
+ const chainFields = ctx.structDefs.get(returnedStructType) ?? [];
2203
+ const chainFieldTemps = new Map();
2204
+ for (const fieldName of chainFields) {
2205
+ const ft = ctx.freshTemp();
2206
+ ctx.emit({ kind: 'copy', dst: ft, src: { kind: 'temp', name: `__rf_${fieldName}` } });
2207
+ chainFieldTemps.set(fieldName, ft);
2208
+ }
2209
+ const methodInfo = ctx.implMethods.get(returnedStructType)?.get(expr.callee.field);
2210
+ if (methodInfo?.hasSelf) {
2211
+ const selfArgs = chainFields.map(f => {
2212
+ const temp = chainFieldTemps.get(f);
2213
+ return temp ? { kind: 'temp', name: temp } : { kind: 'const', value: 0 };
2214
+ });
2215
+ const explicitArgs = expr.args.map(a => lowerExpr(a, ctx, scope));
2216
+ const allArgs = [...selfArgs, ...explicitArgs];
2217
+ const ct = ctx.freshTemp();
2218
+ ctx.emit({ kind: 'call', dst: ct, fn: `${returnedStructType}::${expr.callee.field}`, args: allArgs });
2219
+ return { kind: 'temp', name: ct };
2220
+ }
2221
+ }
2222
+ }
1759
2223
  // Fallback: generic invoke
1760
2224
  const calleeOp = lowerExpr(expr.callee, ctx, scope);
1761
2225
  const args = expr.args.map(a => lowerExpr(a, ctx, scope));
@@ -1782,6 +2246,29 @@ function lowerExpr(expr, ctx, scope) {
1782
2246
  ctx.emit({ kind: 'const', dst: t, value: 0 });
1783
2247
  return { kind: 'temp', name: t };
1784
2248
  }
2249
+ // @singleton struct static calls: expand struct arg field-by-field for ::set
2250
+ if (ctx.singletonStructs.has(expr.type)) {
2251
+ if (expr.method === 'get') {
2252
+ // GameState::get() — no struct args, our synthetic LIR fn writes to $__rf_<field> slots
2253
+ const t = ctx.freshTemp();
2254
+ ctx.emit({ kind: 'call', dst: t, fn: `${expr.type}::${expr.method}`, args: [] });
2255
+ return { kind: 'temp', name: t };
2256
+ }
2257
+ else if (expr.method === 'set' && expr.args.length === 1 && expr.args[0].kind === 'ident') {
2258
+ // GameState::set(gs) — flatten struct arg into individual field args ($p0, $p1, ...)
2259
+ const sv = ctx.structVars.get(expr.args[0].name);
2260
+ if (sv) {
2261
+ const fields = ctx.structDefs.get(sv.typeName) ?? [];
2262
+ const fieldArgs = fields.map(f => {
2263
+ const temp = sv.fields.get(f);
2264
+ return temp ? { kind: 'temp', name: temp } : { kind: 'const', value: 0 };
2265
+ });
2266
+ const t = ctx.freshTemp();
2267
+ ctx.emit({ kind: 'call', dst: t, fn: `${expr.type}::${expr.method}`, args: fieldArgs });
2268
+ return { kind: 'temp', name: t };
2269
+ }
2270
+ }
2271
+ }
1785
2272
  const args = expr.args.map(a => lowerExpr(a, ctx, scope));
1786
2273
  const t = ctx.freshTemp();
1787
2274
  ctx.emit({ kind: 'call', dst: t, fn: `${expr.type}::${expr.method}`, args });
@@ -1815,6 +2302,43 @@ function lowerExpr(expr, ctx, scope) {
1815
2302
  ctx.emit({ kind: 'const', dst: t, value: 0 });
1816
2303
  return { kind: 'temp', name: t };
1817
2304
  }
2305
+ case 'unwrap_or': {
2306
+ // opt.unwrap_or(default) → evaluate opt, if has=1 return val else return default
2307
+ const resultTemp = ctx.freshTemp();
2308
+ const defaultOp = lowerExpr(expr.default_, ctx, scope);
2309
+ ctx.emit({ kind: 'copy', dst: resultTemp, src: defaultOp });
2310
+ const sv = (() => {
2311
+ if (expr.opt.kind === 'ident')
2312
+ return ctx.structVars.get(expr.opt.name);
2313
+ return undefined;
2314
+ })();
2315
+ let hasOp;
2316
+ let valTemp;
2317
+ if (sv && sv.typeName === '__option') {
2318
+ const hasT = sv.fields.get('has');
2319
+ const valT = sv.fields.get('val');
2320
+ hasOp = { kind: 'temp', name: hasT };
2321
+ valTemp = valT;
2322
+ }
2323
+ else {
2324
+ lowerExpr(expr.opt, ctx, scope);
2325
+ const hasT = ctx.freshTemp();
2326
+ const valT = ctx.freshTemp();
2327
+ ctx.emit({ kind: 'copy', dst: hasT, src: { kind: 'temp', name: '__rf_has' } });
2328
+ ctx.emit({ kind: 'copy', dst: valT, src: { kind: 'temp', name: '__rf_val' } });
2329
+ hasOp = { kind: 'temp', name: hasT };
2330
+ valTemp = valT;
2331
+ }
2332
+ const someBlock = ctx.newBlock('unwrap_some');
2333
+ const mergeBlock = ctx.newBlock('unwrap_merge');
2334
+ ctx.terminate({ kind: 'branch', cond: hasOp, then: someBlock.id, else: mergeBlock.id });
2335
+ ctx.switchTo(someBlock);
2336
+ if (valTemp)
2337
+ ctx.emit({ kind: 'copy', dst: resultTemp, src: { kind: 'temp', name: valTemp } });
2338
+ ctx.terminate({ kind: 'jump', target: mergeBlock.id });
2339
+ ctx.switchTo(mergeBlock);
2340
+ return { kind: 'temp', name: resultTemp };
2341
+ }
1818
2342
  case 'type_cast': {
1819
2343
  const ns = ctx.getNamespace();
1820
2344
  const targetName = expr.targetType.kind === 'named' ? expr.targetType.name : null;
@@ -1942,6 +2466,31 @@ function lowerShortCircuitOr(expr, ctx, scope) {
1942
2466
  // ---------------------------------------------------------------------------
1943
2467
  // Timer method inlining
1944
2468
  // ---------------------------------------------------------------------------
2469
+ /**
2470
+ * Infer the struct type name returned by a chained invoke/call expression.
2471
+ * Used to support method chaining: v.scale(2).add(...) where scale() returns Vec2.
2472
+ * Returns the struct type name if determinable, otherwise undefined.
2473
+ */
2474
+ function inferInvokeReturnStructType(expr, ctx) {
2475
+ if (expr.kind === 'invoke' && expr.callee.kind === 'member') {
2476
+ // Find the receiver type via structVars
2477
+ let receiverTypeName;
2478
+ if (expr.callee.obj.kind === 'ident') {
2479
+ receiverTypeName = ctx.structVars.get(expr.callee.obj.name)?.typeName;
2480
+ }
2481
+ else {
2482
+ // Recursively infer the type for deeper chains
2483
+ receiverTypeName = inferInvokeReturnStructType(expr.callee.obj, ctx);
2484
+ }
2485
+ if (receiverTypeName) {
2486
+ const methodInfo = ctx.implMethods.get(receiverTypeName)?.get(expr.callee.field);
2487
+ if (methodInfo?.returnStructName) {
2488
+ return methodInfo.returnStructName;
2489
+ }
2490
+ }
2491
+ }
2492
+ return undefined;
2493
+ }
1945
2494
  /**
1946
2495
  * Inline a Timer instance method call using the statically-assigned timer ID.
1947
2496
  * Emits scoreboard operations directly, bypassing the Timer::* function calls.
@@ -2080,6 +2629,39 @@ function lowerExecuteSubcmd(sub) {
2080
2629
  }
2081
2630
  }
2082
2631
  }
2632
+ function lowerStringExprToPath(expr, ctx, scope, hint = 'str') {
2633
+ switch (expr.kind) {
2634
+ case 'str_lit': {
2635
+ const path = ctx.freshStringVar(hint);
2636
+ ctx.emit({
2637
+ kind: 'call',
2638
+ dst: null,
2639
+ fn: `__raw:data modify storage rs:strings ${path} set value ${JSON.stringify(expr.value)}`,
2640
+ args: [],
2641
+ });
2642
+ return path;
2643
+ }
2644
+ case 'ident':
2645
+ return ctx.stringVars.get(expr.name) ?? null;
2646
+ case 'assign': {
2647
+ if (!ctx.stringVars.has(expr.target))
2648
+ return null;
2649
+ const dstPath = ctx.stringVars.get(expr.target);
2650
+ const srcPath = lowerStringExprToPath(expr.value, ctx, scope, expr.target);
2651
+ if (!srcPath || srcPath === dstPath)
2652
+ return dstPath;
2653
+ ctx.emit({
2654
+ kind: 'call',
2655
+ dst: null,
2656
+ fn: `__raw:data modify storage rs:strings ${dstPath} set from storage rs:strings ${srcPath}`,
2657
+ args: [],
2658
+ });
2659
+ return dstPath;
2660
+ }
2661
+ default:
2662
+ return null;
2663
+ }
2664
+ }
2083
2665
  function selectorToString(sel) {
2084
2666
  // EntitySelector has kind like '@a', '@e', '@s', etc.
2085
2667
  // Filters are key=value pairs that become [key=value,key=value]
@@ -2120,6 +2702,102 @@ const MACRO_SENTINEL = '\x01';
2120
2702
  * Convert an f_string HIRExpr to a Minecraft JSON text component string.
2121
2703
  * Each interpolated variable becomes a {"score":{"name":"$var","objective":"__ns"}} component.
2122
2704
  */
2705
+ /**
2706
+ * Pre-evaluate complex f-string expression parts to MIR temp vars,
2707
+ * returning a rewritten HIRExpr where each complex part is replaced by a simple ident.
2708
+ */
2709
+ function precomputeFStringParts(expr, ctx, scope) {
2710
+ if (expr.kind !== 'f_string')
2711
+ return expr;
2712
+ const newParts = [];
2713
+ for (const part of expr.parts) {
2714
+ if (part.kind === 'text') {
2715
+ newParts.push(part);
2716
+ continue;
2717
+ }
2718
+ const inner = part.expr;
2719
+ // Simple cases that fStringToJsonText already handles
2720
+ if (inner.kind === 'ident' || inner.kind === 'int_lit' || inner.kind === 'bool_lit') {
2721
+ newParts.push({ kind: 'expr', expr: inner });
2722
+ continue;
2723
+ }
2724
+ // Display::to_string() inline expansion in f-string context:
2725
+ // v.to_string() (desugared as call{fn:'to_string', args:[v]}) → inline the Display impl's f-string
2726
+ if (inner.kind === 'call' && inner.fn === 'to_string' && inner.args.length === 1 && inner.args[0].kind === 'ident') {
2727
+ const argName = inner.args[0].name;
2728
+ const sv = ctx.structVars.get(argName);
2729
+ if (sv && ctx.displayImpls.has(sv.typeName)) {
2730
+ const displayParts = ctx.displayImpls.get(sv.typeName);
2731
+ // Expand Display impl parts, substituting self.<field> with the actual struct field temps
2732
+ for (const dp of displayParts) {
2733
+ if (dp.kind === 'text') {
2734
+ newParts.push(dp);
2735
+ }
2736
+ else {
2737
+ const dpExpr = dp.expr;
2738
+ // member(ident('self'), field) → lookup struct field temp
2739
+ if (dpExpr.kind === 'member' && dpExpr.obj.kind === 'ident' && dpExpr.obj.name === 'self') {
2740
+ const fieldTemp = sv.fields.get(dpExpr.field);
2741
+ if (fieldTemp) {
2742
+ newParts.push({ kind: 'expr', expr: { kind: 'ident', name: fieldTemp } });
2743
+ }
2744
+ else {
2745
+ newParts.push({ kind: 'expr', expr: { kind: 'int_lit', value: 0 } });
2746
+ }
2747
+ }
2748
+ else {
2749
+ // Other expressions: lower them with self fields in scope
2750
+ const selfScope = new Map(scope);
2751
+ for (const [fieldName, fieldTemp] of sv.fields) {
2752
+ selfScope.set(`self.${fieldName}`, fieldTemp);
2753
+ }
2754
+ const tempOp = lowerExpr(dpExpr, ctx, selfScope);
2755
+ if (tempOp.kind === 'temp') {
2756
+ newParts.push({ kind: 'expr', expr: { kind: 'ident', name: tempOp.name } });
2757
+ }
2758
+ else if (tempOp.kind === 'const') {
2759
+ newParts.push({ kind: 'expr', expr: { kind: 'int_lit', value: tempOp.value } });
2760
+ }
2761
+ }
2762
+ }
2763
+ }
2764
+ continue;
2765
+ }
2766
+ }
2767
+ // int_to_str(x) / bool_to_str(x) in f-string: pass through the inner arg as a scoreboard score ref
2768
+ if (inner.kind === 'call' && (inner.fn === 'int_to_str' || inner.fn === 'bool_to_str') && inner.args.length === 1) {
2769
+ const arg = inner.args[0];
2770
+ if (arg.kind === 'ident') {
2771
+ newParts.push({ kind: 'expr', expr: arg });
2772
+ continue;
2773
+ }
2774
+ // If arg is complex, lower it first
2775
+ const argOp = lowerExpr(arg, ctx, scope);
2776
+ if (argOp.kind === 'temp') {
2777
+ newParts.push({ kind: 'expr', expr: { kind: 'ident', name: argOp.name } });
2778
+ }
2779
+ else if (argOp.kind === 'const') {
2780
+ newParts.push({ kind: 'expr', expr: { kind: 'int_lit', value: argOp.value } });
2781
+ }
2782
+ else {
2783
+ newParts.push(part);
2784
+ }
2785
+ continue;
2786
+ }
2787
+ // Complex expression: lower to a temp var, then reference it as ident
2788
+ const tempOp = lowerExpr(inner, ctx, scope);
2789
+ if (tempOp.kind === 'temp') {
2790
+ newParts.push({ kind: 'expr', expr: { kind: 'ident', name: tempOp.name } });
2791
+ }
2792
+ else if (tempOp.kind === 'const') {
2793
+ newParts.push({ kind: 'expr', expr: { kind: 'int_lit', value: tempOp.value } });
2794
+ }
2795
+ else {
2796
+ newParts.push(part);
2797
+ }
2798
+ }
2799
+ return { kind: 'f_string', parts: newParts };
2800
+ }
2123
2801
  function fStringToJsonText(expr, namespace) {
2124
2802
  if (expr.kind !== 'f_string')
2125
2803
  return JSON.stringify(expr.kind === 'str_lit' ? { text: expr.value } : { text: '~' });
@@ -2131,7 +2809,7 @@ function fStringToJsonText(expr, namespace) {
2131
2809
  extra.push({ text: part.value });
2132
2810
  }
2133
2811
  else {
2134
- // expr part — must be a scoreboard variable (ident)
2812
+ // expr part — must be a scoreboard variable (ident) or literal
2135
2813
  const inner = part.expr;
2136
2814
  if (inner.kind === 'ident') {
2137
2815
  extra.push({ score: { name: `$${inner.name}`, objective } });
@@ -2139,6 +2817,9 @@ function fStringToJsonText(expr, namespace) {
2139
2817
  else if (inner.kind === 'int_lit') {
2140
2818
  extra.push({ text: String(inner.value) });
2141
2819
  }
2820
+ else if (inner.kind === 'bool_lit') {
2821
+ extra.push({ text: inner.value ? 'true' : 'false' });
2822
+ }
2142
2823
  else {
2143
2824
  extra.push({ text: '?' });
2144
2825
  }
@@ -2303,6 +2984,66 @@ function formatBuiltinCall(fn, args, macroParams, namespace = '') {
2303
2984
  case 'xp_set':
2304
2985
  cmd = `xp set ${strs[0]} ${strs[1]} ${strs[2] ?? 'points'}`;
2305
2986
  break;
2987
+ case 'scoreboard_add_objective':
2988
+ cmd = strs[2] ? `scoreboard objectives add ${strs[0]} ${strs[1]} ${strs[2]}` : `scoreboard objectives add ${strs[0]} ${strs[1]}`;
2989
+ break;
2990
+ case 'scoreboard_remove_objective':
2991
+ cmd = `scoreboard objectives remove ${strs[0]}`;
2992
+ break;
2993
+ case 'scoreboard_display':
2994
+ cmd = `scoreboard objectives setdisplay ${strs[0]} ${strs[1] ?? ''}`;
2995
+ break;
2996
+ case 'scoreboard_hide':
2997
+ cmd = `scoreboard objectives setdisplay ${strs[0]}`;
2998
+ break;
2999
+ case 'team_add':
3000
+ cmd = strs[1] ? `team add ${strs[0]} ${strs[1]}` : `team add ${strs[0]}`;
3001
+ break;
3002
+ case 'team_remove':
3003
+ cmd = `team remove ${strs[0]}`;
3004
+ break;
3005
+ case 'team_join':
3006
+ cmd = `team join ${strs[0]} ${strs[1]}`;
3007
+ break;
3008
+ case 'team_leave':
3009
+ cmd = `team leave ${strs[0]}`;
3010
+ break;
3011
+ case 'team_option':
3012
+ cmd = `team modify ${strs[0]} ${strs[1]} ${strs[2]}`;
3013
+ break;
3014
+ case 'bossbar_add':
3015
+ cmd = `bossbar add ${strs[0]} ${strs[1] ? JSON.stringify({ text: strs[1] }) : '""'}`;
3016
+ break;
3017
+ case 'bossbar_remove':
3018
+ cmd = `bossbar remove ${strs[0]}`;
3019
+ break;
3020
+ case 'bossbar_set_value':
3021
+ cmd = `bossbar set ${strs[0]} value ${strs[1]}`;
3022
+ break;
3023
+ case 'bossbar_get_value':
3024
+ cmd = `bossbar get ${strs[0]} value`;
3025
+ break;
3026
+ case 'bossbar_set_max':
3027
+ cmd = `bossbar set ${strs[0]} max ${strs[1]}`;
3028
+ break;
3029
+ case 'bossbar_set_color':
3030
+ cmd = `bossbar set ${strs[0]} color ${strs[1]}`;
3031
+ break;
3032
+ case 'bossbar_set_style':
3033
+ cmd = `bossbar set ${strs[0]} style ${strs[1]}`;
3034
+ break;
3035
+ case 'bossbar_set_visible':
3036
+ cmd = `bossbar set ${strs[0]} visible ${strs[1]}`;
3037
+ break;
3038
+ case 'bossbar_set_players':
3039
+ cmd = `bossbar set ${strs[0]} players ${strs[1]}`;
3040
+ break;
3041
+ case 'data_get':
3042
+ cmd = `data get ${strs[0]} ${strs[1]} ${strs[2] ?? ''}`.trimEnd();
3043
+ break;
3044
+ case 'data_merge':
3045
+ cmd = `data merge ${strs[0]} ${strs[1]} ${strs[2]}`;
3046
+ break;
2306
3047
  default: cmd = `${fn} ${strs.join(' ')}`;
2307
3048
  }
2308
3049
  return hasMacro ? `${MACRO_SENTINEL}${cmd}` : cmd;