redscript-mc 1.2.30 → 2.1.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 (593) hide show
  1. package/.claudeignore +21 -0
  2. package/.github/workflows/ci.yml +1 -0
  3. package/README.md +12 -16
  4. package/README.zh.md +2 -2
  5. package/demo.gif +0 -0
  6. package/dist/cli.js +2 -554
  7. package/dist/compile.js +2 -266
  8. package/dist/index.js +2 -159
  9. package/dist/src/__tests__/budget.test.js +261 -0
  10. package/dist/src/__tests__/cli.test.js +104 -0
  11. package/dist/{__tests__ → src/__tests__}/dce.test.js +11 -47
  12. package/dist/{__tests__ → src/__tests__}/diagnostics.test.js +67 -40
  13. package/dist/src/__tests__/e2e/basic.test.d.ts +8 -0
  14. package/dist/src/__tests__/e2e/basic.test.js +140 -0
  15. package/dist/src/__tests__/e2e/coroutine.test.d.ts +7 -0
  16. package/dist/src/__tests__/e2e/coroutine.test.js +132 -0
  17. package/dist/src/__tests__/e2e/macros.test.d.ts +9 -0
  18. package/dist/src/__tests__/e2e/macros.test.js +182 -0
  19. package/dist/src/__tests__/e2e/migrate.test.d.ts +13 -0
  20. package/dist/src/__tests__/e2e/migrate.test.js +2739 -0
  21. package/dist/src/__tests__/e2e/stdlib-e2e.test.d.ts +10 -0
  22. package/dist/src/__tests__/e2e/stdlib-e2e.test.js +324 -0
  23. package/dist/src/__tests__/enum.test.d.ts +10 -0
  24. package/dist/src/__tests__/enum.test.js +389 -0
  25. package/dist/src/__tests__/generics.test.d.ts +14 -0
  26. package/dist/src/__tests__/generics.test.js +367 -0
  27. package/dist/src/__tests__/hir/desugar.test.js +234 -0
  28. package/dist/src/__tests__/incremental.test.d.ts +5 -0
  29. package/dist/src/__tests__/incremental.test.js +308 -0
  30. package/dist/src/__tests__/lir/lower.test.js +559 -0
  31. package/dist/src/__tests__/lir/types.test.js +185 -0
  32. package/dist/src/__tests__/lir/verify.test.js +221 -0
  33. package/dist/src/__tests__/lsp.test.d.ts +7 -0
  34. package/dist/src/__tests__/lsp.test.js +245 -0
  35. package/dist/{__tests__ → src/__tests__}/mc-integration.test.js +1 -3
  36. package/dist/src/__tests__/mc-version.test.d.ts +10 -0
  37. package/dist/src/__tests__/mc-version.test.js +154 -0
  38. package/dist/src/__tests__/mir/arithmetic.test.js +130 -0
  39. package/dist/src/__tests__/mir/control-flow.test.js +205 -0
  40. package/dist/src/__tests__/mir/verify.test.js +223 -0
  41. package/dist/src/__tests__/modules.test.d.ts +7 -0
  42. package/dist/src/__tests__/modules.test.js +333 -0
  43. package/dist/src/__tests__/optimizer/block_merge.test.js +78 -0
  44. package/dist/src/__tests__/optimizer/branch_simplify.test.js +58 -0
  45. package/dist/src/__tests__/optimizer/constant_fold.test.js +131 -0
  46. package/dist/src/__tests__/optimizer/copy_prop.test.js +91 -0
  47. package/dist/src/__tests__/optimizer/coroutine.test.d.ts +12 -0
  48. package/dist/src/__tests__/optimizer/coroutine.test.js +251 -0
  49. package/dist/src/__tests__/optimizer/dce.test.d.ts +1 -0
  50. package/dist/src/__tests__/optimizer/dce.test.js +76 -0
  51. package/dist/src/__tests__/optimizer/interprocedural.test.d.ts +1 -0
  52. package/dist/src/__tests__/optimizer/interprocedural.test.js +145 -0
  53. package/dist/src/__tests__/optimizer/lir/const_imm.test.d.ts +1 -0
  54. package/dist/src/__tests__/optimizer/lir/const_imm.test.js +138 -0
  55. package/dist/src/__tests__/optimizer/lir/dead_slot.test.d.ts +1 -0
  56. package/dist/src/__tests__/optimizer/lir/dead_slot.test.js +141 -0
  57. package/dist/src/__tests__/optimizer/lir/peephole.test.d.ts +1 -0
  58. package/dist/src/__tests__/optimizer/lir/peephole.test.js +126 -0
  59. package/dist/src/__tests__/optimizer/lir/pipeline.test.d.ts +1 -0
  60. package/dist/src/__tests__/optimizer/lir/pipeline.test.js +84 -0
  61. package/dist/src/__tests__/optimizer/nbt-batch.test.d.ts +1 -0
  62. package/dist/src/__tests__/optimizer/nbt-batch.test.js +110 -0
  63. package/dist/src/__tests__/optimizer/pipeline.test.d.ts +1 -0
  64. package/dist/src/__tests__/optimizer/pipeline.test.js +102 -0
  65. package/dist/src/__tests__/optimizer/selector-cache.test.d.ts +1 -0
  66. package/dist/src/__tests__/optimizer/selector-cache.test.js +103 -0
  67. package/dist/src/__tests__/optimizer/unroll.test.d.ts +1 -0
  68. package/dist/src/__tests__/optimizer/unroll.test.js +206 -0
  69. package/dist/src/__tests__/option.test.d.ts +14 -0
  70. package/dist/src/__tests__/option.test.js +275 -0
  71. package/dist/src/__tests__/parser.test.d.ts +1 -0
  72. package/dist/src/__tests__/repl.test.d.ts +1 -0
  73. package/dist/src/__tests__/schedule.test.d.ts +7 -0
  74. package/dist/src/__tests__/schedule.test.js +98 -0
  75. package/dist/src/__tests__/sourcemap.test.d.ts +7 -0
  76. package/dist/src/__tests__/sourcemap.test.js +227 -0
  77. package/dist/src/__tests__/tuple.test.d.ts +11 -0
  78. package/dist/src/__tests__/tuple.test.js +202 -0
  79. package/dist/src/__tests__/typechecker-strict.test.d.ts +10 -0
  80. package/dist/src/__tests__/typechecker-strict.test.js +197 -0
  81. package/dist/src/__tests__/typechecker.test.d.ts +1 -0
  82. package/dist/{ast → src/ast}/types.d.ts +58 -3
  83. package/dist/src/cache/deps.d.ts +41 -0
  84. package/dist/src/cache/deps.js +158 -0
  85. package/dist/src/cache/incremental.d.ts +35 -0
  86. package/dist/src/cache/incremental.js +165 -0
  87. package/dist/src/cache/index.d.ts +37 -0
  88. package/dist/src/cache/index.js +152 -0
  89. package/dist/{cli.d.ts → src/cli.d.ts} +1 -1
  90. package/dist/src/cli.js +474 -0
  91. package/dist/src/compile.d.ts +37 -0
  92. package/dist/src/compile.js +165 -0
  93. package/dist/{diagnostics → src/diagnostics}/index.d.ts +1 -1
  94. package/dist/{diagnostics → src/diagnostics}/index.js +8 -11
  95. package/dist/src/emit/compile.d.ts +29 -0
  96. package/dist/src/emit/compile.js +143 -0
  97. package/dist/src/emit/index.d.ts +26 -0
  98. package/dist/src/emit/index.js +223 -0
  99. package/dist/src/emit/modules.d.ts +29 -0
  100. package/dist/src/emit/modules.js +492 -0
  101. package/dist/src/emit/sourcemap.d.ts +53 -0
  102. package/dist/src/emit/sourcemap.js +73 -0
  103. package/dist/src/hir/lower.d.ts +15 -0
  104. package/dist/src/hir/lower.js +399 -0
  105. package/dist/src/hir/monomorphize.d.ts +22 -0
  106. package/dist/src/hir/monomorphize.js +379 -0
  107. package/dist/src/hir/types.d.ts +406 -0
  108. package/dist/src/hir/types.js +16 -0
  109. package/dist/src/index.d.ts +39 -0
  110. package/dist/src/index.js +67 -0
  111. package/dist/{lexer → src/lexer}/index.d.ts +1 -1
  112. package/dist/{lexer → src/lexer}/index.js +1 -0
  113. package/dist/src/lir/budget.d.ts +37 -0
  114. package/dist/src/lir/budget.js +280 -0
  115. package/dist/src/lir/lower.d.ts +15 -0
  116. package/dist/src/lir/lower.js +472 -0
  117. package/dist/src/lir/types.d.ts +139 -0
  118. package/dist/src/lir/types.js +11 -0
  119. package/dist/src/lir/verify.d.ts +14 -0
  120. package/dist/src/lir/verify.js +113 -0
  121. package/dist/src/lsp/main.d.ts +8 -0
  122. package/dist/src/lsp/main.js +11 -0
  123. package/dist/src/lsp/server.d.ts +11 -0
  124. package/dist/src/lsp/server.js +352 -0
  125. package/dist/{mc-test → src/mc-test}/runner.js +4 -3
  126. package/dist/src/mir/lower.d.ts +9 -0
  127. package/dist/src/mir/lower.js +1264 -0
  128. package/dist/src/mir/macro.d.ts +22 -0
  129. package/dist/src/mir/macro.js +168 -0
  130. package/dist/src/mir/types.d.ts +191 -0
  131. package/dist/src/mir/types.js +11 -0
  132. package/dist/src/mir/verify.d.ts +16 -0
  133. package/dist/src/mir/verify.js +216 -0
  134. package/dist/src/optimizer/block_merge.d.ts +12 -0
  135. package/dist/src/optimizer/block_merge.js +84 -0
  136. package/dist/src/optimizer/branch_simplify.d.ts +9 -0
  137. package/dist/src/optimizer/branch_simplify.js +28 -0
  138. package/dist/src/optimizer/constant_fold.d.ts +10 -0
  139. package/dist/src/optimizer/constant_fold.js +85 -0
  140. package/dist/src/optimizer/copy_prop.d.ts +9 -0
  141. package/dist/src/optimizer/copy_prop.js +113 -0
  142. package/dist/src/optimizer/coroutine.d.ts +34 -0
  143. package/dist/src/optimizer/coroutine.js +789 -0
  144. package/dist/src/optimizer/dce.d.ts +8 -0
  145. package/dist/src/optimizer/dce.js +156 -0
  146. package/dist/src/optimizer/interprocedural.d.ts +14 -0
  147. package/dist/src/optimizer/interprocedural.js +186 -0
  148. package/dist/src/optimizer/lir/const_imm.d.ts +12 -0
  149. package/dist/src/optimizer/lir/const_imm.js +139 -0
  150. package/dist/src/optimizer/lir/dead_slot.d.ts +14 -0
  151. package/dist/src/optimizer/lir/dead_slot.js +130 -0
  152. package/dist/src/optimizer/lir/peephole.d.ts +21 -0
  153. package/dist/src/optimizer/lir/peephole.js +52 -0
  154. package/dist/src/optimizer/lir/pipeline.d.ts +10 -0
  155. package/dist/src/optimizer/lir/pipeline.js +34 -0
  156. package/dist/src/optimizer/nbt-batch.d.ts +11 -0
  157. package/dist/src/optimizer/nbt-batch.js +51 -0
  158. package/dist/src/optimizer/pipeline.d.ts +14 -0
  159. package/dist/src/optimizer/pipeline.js +58 -0
  160. package/dist/src/optimizer/selector-cache.d.ts +22 -0
  161. package/dist/src/optimizer/selector-cache.js +100 -0
  162. package/dist/src/optimizer/unroll.d.ts +32 -0
  163. package/dist/src/optimizer/unroll.js +348 -0
  164. package/dist/{parser → src/parser}/index.d.ts +8 -0
  165. package/dist/{parser → src/parser}/index.js +204 -14
  166. package/dist/{repl.d.ts → src/repl.d.ts} +1 -1
  167. package/dist/{runtime → src/runtime}/index.js +1 -1
  168. package/dist/{typechecker → src/typechecker}/index.d.ts +4 -0
  169. package/dist/{typechecker → src/typechecker}/index.js +198 -13
  170. package/dist/src/types/mc-version.d.ts +24 -0
  171. package/dist/src/types/mc-version.js +49 -0
  172. package/docs/ROADMAP.md +395 -0
  173. package/docs/compiler-pipeline-redesign.md +2260 -0
  174. package/docs/optimization-ideas.md +1076 -0
  175. package/editors/vscode/out/extension.js +25176 -8000
  176. package/editors/vscode/package-lock.json +90 -6
  177. package/editors/vscode/package.json +3 -2
  178. package/editors/vscode/src/extension.ts +97 -67
  179. package/examples/showcase.mcrs +3 -3
  180. package/package.json +13 -6
  181. package/scripts/postbuild.js +15 -0
  182. package/src/__tests__/budget.test.ts +297 -0
  183. package/src/__tests__/cli.test.ts +8 -220
  184. package/src/__tests__/dce.test.ts +11 -56
  185. package/src/__tests__/diagnostics.test.ts +61 -41
  186. package/src/__tests__/e2e/basic.test.ts +154 -0
  187. package/src/__tests__/e2e/coroutine.test.ts +142 -0
  188. package/src/__tests__/e2e/macros.test.ts +199 -0
  189. package/src/__tests__/e2e/migrate.test.ts +3008 -0
  190. package/src/__tests__/e2e/stdlib-e2e.test.ts +348 -0
  191. package/src/__tests__/enum.test.ts +425 -0
  192. package/src/__tests__/generics.test.ts +390 -0
  193. package/src/__tests__/hir/desugar.test.ts +263 -0
  194. package/src/__tests__/incremental.test.ts +337 -0
  195. package/src/__tests__/lir/lower.test.ts +619 -0
  196. package/src/__tests__/lir/types.test.ts +207 -0
  197. package/src/__tests__/lir/verify.test.ts +249 -0
  198. package/src/__tests__/lsp.test.ts +270 -0
  199. package/src/__tests__/mc-integration.test.ts +1 -2
  200. package/src/__tests__/mc-version.test.ts +178 -0
  201. package/src/__tests__/mir/arithmetic.test.ts +156 -0
  202. package/src/__tests__/mir/control-flow.test.ts +242 -0
  203. package/src/__tests__/mir/verify.test.ts +254 -0
  204. package/src/__tests__/modules.test.ts +365 -0
  205. package/src/__tests__/optimizer/block_merge.test.ts +84 -0
  206. package/src/__tests__/optimizer/branch_simplify.test.ts +64 -0
  207. package/src/__tests__/optimizer/constant_fold.test.ts +145 -0
  208. package/src/__tests__/optimizer/copy_prop.test.ts +99 -0
  209. package/src/__tests__/optimizer/coroutine.test.ts +312 -0
  210. package/src/__tests__/optimizer/dce.test.ts +83 -0
  211. package/src/__tests__/optimizer/interprocedural.test.ts +174 -0
  212. package/src/__tests__/optimizer/lir/const_imm.test.ts +151 -0
  213. package/src/__tests__/optimizer/lir/dead_slot.test.ts +156 -0
  214. package/src/__tests__/optimizer/lir/peephole.test.ts +136 -0
  215. package/src/__tests__/optimizer/lir/pipeline.test.ts +113 -0
  216. package/src/__tests__/optimizer/nbt-batch.test.ts +119 -0
  217. package/src/__tests__/optimizer/pipeline.test.ts +116 -0
  218. package/src/__tests__/optimizer/selector-cache.test.ts +112 -0
  219. package/src/__tests__/optimizer/unroll.test.ts +231 -0
  220. package/src/__tests__/option.test.ts +299 -0
  221. package/src/__tests__/schedule.test.ts +105 -0
  222. package/src/__tests__/sourcemap.test.ts +254 -0
  223. package/src/__tests__/tuple.test.ts +220 -0
  224. package/src/__tests__/typechecker-strict.test.ts +216 -0
  225. package/src/ast/types.ts +39 -3
  226. package/src/cache/deps.ts +132 -0
  227. package/src/cache/incremental.ts +173 -0
  228. package/src/cache/index.ts +135 -0
  229. package/src/cli.ts +111 -195
  230. package/src/compile.ts +6 -162
  231. package/src/diagnostics/index.ts +8 -11
  232. package/src/emit/compile.ts +177 -0
  233. package/src/emit/index.ts +286 -0
  234. package/src/emit/modules.ts +581 -0
  235. package/src/emit/sourcemap.ts +101 -0
  236. package/src/hir/lower.ts +455 -0
  237. package/src/hir/monomorphize.ts +416 -0
  238. package/src/hir/types.ts +228 -0
  239. package/src/index.ts +37 -182
  240. package/src/lexer/index.ts +2 -1
  241. package/src/lir/budget.ts +321 -0
  242. package/src/lir/lower.ts +587 -0
  243. package/src/lir/types.ts +113 -0
  244. package/src/lir/verify.ts +129 -0
  245. package/src/lsp/main.ts +9 -0
  246. package/src/lsp/server.ts +414 -0
  247. package/src/mc-test/runner.ts +4 -3
  248. package/src/mir/lower.ts +1403 -0
  249. package/src/mir/macro.ts +167 -0
  250. package/src/mir/types.ts +117 -0
  251. package/src/mir/verify.ts +218 -0
  252. package/src/optimizer/block_merge.ts +93 -0
  253. package/src/optimizer/branch_simplify.ts +27 -0
  254. package/src/optimizer/constant_fold.ts +88 -0
  255. package/src/optimizer/copy_prop.ts +106 -0
  256. package/src/optimizer/coroutine.ts +996 -0
  257. package/src/optimizer/dce.ts +108 -653
  258. package/src/optimizer/interprocedural.ts +177 -0
  259. package/src/optimizer/lir/const_imm.ts +143 -0
  260. package/src/optimizer/lir/dead_slot.ts +123 -0
  261. package/src/optimizer/lir/peephole.ts +57 -0
  262. package/src/optimizer/lir/pipeline.ts +37 -0
  263. package/src/optimizer/nbt-batch.ts +50 -0
  264. package/src/optimizer/pipeline.ts +59 -0
  265. package/src/optimizer/selector-cache.ts +103 -0
  266. package/src/optimizer/unroll.ts +386 -0
  267. package/src/parser/index.ts +213 -16
  268. package/src/repl.ts +1 -1
  269. package/src/runtime/index.ts +1 -1
  270. package/src/stdlib/math.mcrs +4 -4
  271. package/src/templates/quest.mcrs +4 -4
  272. package/src/typechecker/index.ts +215 -15
  273. package/src/types/mc-version.ts +46 -0
  274. package/tsconfig.json +1 -1
  275. package/dist/__tests__/cli.test.js +0 -278
  276. package/dist/__tests__/codegen.test.js +0 -152
  277. package/dist/__tests__/e2e.test.d.ts +0 -6
  278. package/dist/__tests__/e2e.test.js +0 -1847
  279. package/dist/__tests__/entity-types.test.js +0 -203
  280. package/dist/__tests__/lowering.test.js +0 -1015
  281. package/dist/__tests__/macro.test.d.ts +0 -8
  282. package/dist/__tests__/macro.test.js +0 -305
  283. package/dist/__tests__/nbt.test.js +0 -82
  284. package/dist/__tests__/optimizer-advanced.test.js +0 -124
  285. package/dist/__tests__/optimizer.test.js +0 -149
  286. package/dist/__tests__/runtime.test.js +0 -289
  287. package/dist/__tests__/stdlib-advanced.test.d.ts +0 -4
  288. package/dist/__tests__/stdlib-advanced.test.js +0 -378
  289. package/dist/__tests__/stdlib-bigint.test.d.ts +0 -7
  290. package/dist/__tests__/stdlib-bigint.test.js +0 -428
  291. package/dist/__tests__/stdlib-math.test.d.ts +0 -7
  292. package/dist/__tests__/stdlib-math.test.js +0 -352
  293. package/dist/__tests__/stdlib-vec.test.d.ts +0 -4
  294. package/dist/__tests__/stdlib-vec.test.js +0 -264
  295. package/dist/__tests__/structure-optimizer.test.js +0 -33
  296. package/dist/__tests__/var-allocator.test.js +0 -69
  297. package/dist/codegen/cmdblock/index.d.ts +0 -26
  298. package/dist/codegen/cmdblock/index.js +0 -45
  299. package/dist/codegen/mcfunction/index.d.ts +0 -40
  300. package/dist/codegen/mcfunction/index.js +0 -606
  301. package/dist/codegen/structure/index.d.ts +0 -24
  302. package/dist/codegen/structure/index.js +0 -279
  303. package/dist/codegen/var-allocator.d.ts +0 -45
  304. package/dist/codegen/var-allocator.js +0 -104
  305. package/dist/compile.d.ts +0 -68
  306. package/dist/data/arena/function/__load.mcfunction +0 -6
  307. package/dist/data/arena/function/__tick.mcfunction +0 -2
  308. package/dist/data/arena/function/announce_leaders/else_1.mcfunction +0 -3
  309. package/dist/data/arena/function/announce_leaders/foreach_0/merge_2.mcfunction +0 -1
  310. package/dist/data/arena/function/announce_leaders/foreach_0/then_0.mcfunction +0 -3
  311. package/dist/data/arena/function/announce_leaders/foreach_0.mcfunction +0 -7
  312. package/dist/data/arena/function/announce_leaders/foreach_1/merge_2.mcfunction +0 -1
  313. package/dist/data/arena/function/announce_leaders/foreach_1/then_0.mcfunction +0 -4
  314. package/dist/data/arena/function/announce_leaders/foreach_1.mcfunction +0 -6
  315. package/dist/data/arena/function/announce_leaders/merge_2.mcfunction +0 -1
  316. package/dist/data/arena/function/announce_leaders/then_0.mcfunction +0 -4
  317. package/dist/data/arena/function/announce_leaders.mcfunction +0 -6
  318. package/dist/data/arena/function/arena_tick/merge_2.mcfunction +0 -1
  319. package/dist/data/arena/function/arena_tick/then_0.mcfunction +0 -4
  320. package/dist/data/arena/function/arena_tick.mcfunction +0 -11
  321. package/dist/data/counter/function/__load.mcfunction +0 -5
  322. package/dist/data/counter/function/__tick.mcfunction +0 -2
  323. package/dist/data/counter/function/counter_tick/merge_2.mcfunction +0 -1
  324. package/dist/data/counter/function/counter_tick/then_0.mcfunction +0 -3
  325. package/dist/data/counter/function/counter_tick.mcfunction +0 -11
  326. package/dist/data/gcd2/function/__load.mcfunction +0 -3
  327. package/dist/data/gcd2/function/abs/merge_2.mcfunction +0 -3
  328. package/dist/data/gcd2/function/abs/then_0.mcfunction +0 -5
  329. package/dist/data/gcd2/function/abs.mcfunction +0 -7
  330. package/dist/data/gcd2/function/gcd/loop_body_1.mcfunction +0 -7
  331. package/dist/data/gcd2/function/gcd/loop_check_0.mcfunction +0 -5
  332. package/dist/data/gcd2/function/gcd/loop_exit_2.mcfunction +0 -3
  333. package/dist/data/gcd2/function/gcd.mcfunction +0 -14
  334. package/dist/data/gcd3/function/__load.mcfunction +0 -3
  335. package/dist/data/gcd3/function/abs/merge_2.mcfunction +0 -3
  336. package/dist/data/gcd3/function/abs/then_0.mcfunction +0 -5
  337. package/dist/data/gcd3/function/abs.mcfunction +0 -7
  338. package/dist/data/gcd3/function/gcd/loop_body_1.mcfunction +0 -7
  339. package/dist/data/gcd3/function/gcd/loop_check_0.mcfunction +0 -5
  340. package/dist/data/gcd3/function/gcd/loop_exit_2.mcfunction +0 -3
  341. package/dist/data/gcd3/function/gcd.mcfunction +0 -14
  342. package/dist/data/gcd3/function/test.mcfunction +0 -7
  343. package/dist/data/gcd3nm/function/__load.mcfunction +0 -3
  344. package/dist/data/gcd3nm/function/abs/merge_2.mcfunction +0 -3
  345. package/dist/data/gcd3nm/function/abs/then_0.mcfunction +0 -5
  346. package/dist/data/gcd3nm/function/abs.mcfunction +0 -7
  347. package/dist/data/gcd3nm/function/gcd/loop_body_1.mcfunction +0 -7
  348. package/dist/data/gcd3nm/function/gcd/loop_check_0.mcfunction +0 -5
  349. package/dist/data/gcd3nm/function/gcd/loop_exit_2.mcfunction +0 -3
  350. package/dist/data/gcd3nm/function/gcd.mcfunction +0 -14
  351. package/dist/data/gcd3nm/function/test.mcfunction +0 -7
  352. package/dist/data/gcd_test/function/__load.mcfunction +0 -3
  353. package/dist/data/gcd_test/function/abs/merge_2.mcfunction +0 -3
  354. package/dist/data/gcd_test/function/abs/then_0.mcfunction +0 -5
  355. package/dist/data/gcd_test/function/abs.mcfunction +0 -7
  356. package/dist/data/gcd_test/function/gcd/loop_body_1.mcfunction +0 -7
  357. package/dist/data/gcd_test/function/gcd/loop_check_0.mcfunction +0 -5
  358. package/dist/data/gcd_test/function/gcd/loop_exit_2.mcfunction +0 -3
  359. package/dist/data/gcd_test/function/gcd.mcfunction +0 -14
  360. package/dist/data/isqrttest/function/__load.mcfunction +0 -6
  361. package/dist/data/isqrttest/function/isqrt/loop_body_4.mcfunction +0 -12
  362. package/dist/data/isqrttest/function/isqrt/loop_check_3.mcfunction +0 -5
  363. package/dist/data/isqrttest/function/isqrt/loop_exit_5.mcfunction +0 -3
  364. package/dist/data/isqrttest/function/isqrt/merge_2.mcfunction +0 -4
  365. package/dist/data/isqrttest/function/isqrt/merge_8.mcfunction +0 -6
  366. package/dist/data/isqrttest/function/isqrt/then_0.mcfunction +0 -3
  367. package/dist/data/isqrttest/function/isqrt/then_6.mcfunction +0 -3
  368. package/dist/data/isqrttest/function/isqrt.mcfunction +0 -7
  369. package/dist/data/isqrttest/function/test.mcfunction +0 -6
  370. package/dist/data/mathtest/function/__load.mcfunction +0 -3
  371. package/dist/data/mathtest/function/abs/merge_2.mcfunction +0 -3
  372. package/dist/data/mathtest/function/abs/then_0.mcfunction +0 -5
  373. package/dist/data/mathtest/function/abs.mcfunction +0 -6
  374. package/dist/data/mathtest/function/test.mcfunction +0 -5
  375. package/dist/data/minecraft/tags/function/load.json +0 -5
  376. package/dist/data/minecraft/tags/function/tick.json +0 -5
  377. package/dist/data/mypack/function/__load.mcfunction +0 -13
  378. package/dist/data/mypack/function/_atan_init.mcfunction +0 -2
  379. package/dist/data/mypack/function/abs/merge_2.mcfunction +0 -3
  380. package/dist/data/mypack/function/abs/then_0.mcfunction +0 -5
  381. package/dist/data/mypack/function/abs.mcfunction +0 -6
  382. package/dist/data/mypack/function/atan2_fixed/__sgi_1.mcfunction +0 -2
  383. package/dist/data/mypack/function/atan2_fixed/else_34.mcfunction +0 -3
  384. package/dist/data/mypack/function/atan2_fixed/loop_body_31.mcfunction +0 -19
  385. package/dist/data/mypack/function/atan2_fixed/loop_check_30.mcfunction +0 -5
  386. package/dist/data/mypack/function/atan2_fixed/loop_exit_32.mcfunction +0 -6
  387. package/dist/data/mypack/function/atan2_fixed/merge_11.mcfunction +0 -6
  388. package/dist/data/mypack/function/atan2_fixed/merge_14.mcfunction +0 -3
  389. package/dist/data/mypack/function/atan2_fixed/merge_17.mcfunction +0 -6
  390. package/dist/data/mypack/function/atan2_fixed/merge_2.mcfunction +0 -5
  391. package/dist/data/mypack/function/atan2_fixed/merge_20.mcfunction +0 -5
  392. package/dist/data/mypack/function/atan2_fixed/merge_23.mcfunction +0 -5
  393. package/dist/data/mypack/function/atan2_fixed/merge_26.mcfunction +0 -6
  394. package/dist/data/mypack/function/atan2_fixed/merge_29.mcfunction +0 -4
  395. package/dist/data/mypack/function/atan2_fixed/merge_38.mcfunction +0 -5
  396. package/dist/data/mypack/function/atan2_fixed/merge_41.mcfunction +0 -5
  397. package/dist/data/mypack/function/atan2_fixed/merge_44.mcfunction +0 -5
  398. package/dist/data/mypack/function/atan2_fixed/merge_47.mcfunction +0 -5
  399. package/dist/data/mypack/function/atan2_fixed/merge_5.mcfunction +0 -5
  400. package/dist/data/mypack/function/atan2_fixed/merge_8.mcfunction +0 -3
  401. package/dist/data/mypack/function/atan2_fixed/then_0.mcfunction +0 -5
  402. package/dist/data/mypack/function/atan2_fixed/then_12.mcfunction +0 -3
  403. package/dist/data/mypack/function/atan2_fixed/then_15.mcfunction +0 -5
  404. package/dist/data/mypack/function/atan2_fixed/then_18.mcfunction +0 -5
  405. package/dist/data/mypack/function/atan2_fixed/then_21.mcfunction +0 -3
  406. package/dist/data/mypack/function/atan2_fixed/then_24.mcfunction +0 -3
  407. package/dist/data/mypack/function/atan2_fixed/then_27.mcfunction +0 -6
  408. package/dist/data/mypack/function/atan2_fixed/then_3.mcfunction +0 -3
  409. package/dist/data/mypack/function/atan2_fixed/then_33.mcfunction +0 -5
  410. package/dist/data/mypack/function/atan2_fixed/then_36.mcfunction +0 -5
  411. package/dist/data/mypack/function/atan2_fixed/then_39.mcfunction +0 -5
  412. package/dist/data/mypack/function/atan2_fixed/then_42.mcfunction +0 -3
  413. package/dist/data/mypack/function/atan2_fixed/then_45.mcfunction +0 -5
  414. package/dist/data/mypack/function/atan2_fixed/then_6.mcfunction +0 -3
  415. package/dist/data/mypack/function/atan2_fixed/then_9.mcfunction +0 -5
  416. package/dist/data/mypack/function/atan2_fixed.mcfunction +0 -7
  417. package/dist/data/mypack/function/my_game.mcfunction +0 -10
  418. package/dist/data/quiz/function/__load.mcfunction +0 -16
  419. package/dist/data/quiz/function/__tick.mcfunction +0 -6
  420. package/dist/data/quiz/function/__trigger_quiz_a_dispatch.mcfunction +0 -4
  421. package/dist/data/quiz/function/__trigger_quiz_b_dispatch.mcfunction +0 -4
  422. package/dist/data/quiz/function/__trigger_quiz_c_dispatch.mcfunction +0 -4
  423. package/dist/data/quiz/function/__trigger_quiz_start_dispatch.mcfunction +0 -4
  424. package/dist/data/quiz/function/answer_a.mcfunction +0 -4
  425. package/dist/data/quiz/function/answer_b.mcfunction +0 -4
  426. package/dist/data/quiz/function/answer_c.mcfunction +0 -4
  427. package/dist/data/quiz/function/ask_question/else_1.mcfunction +0 -5
  428. package/dist/data/quiz/function/ask_question/else_4.mcfunction +0 -5
  429. package/dist/data/quiz/function/ask_question/else_7.mcfunction +0 -4
  430. package/dist/data/quiz/function/ask_question/merge_2.mcfunction +0 -1
  431. package/dist/data/quiz/function/ask_question/merge_5.mcfunction +0 -2
  432. package/dist/data/quiz/function/ask_question/merge_8.mcfunction +0 -2
  433. package/dist/data/quiz/function/ask_question/then_0.mcfunction +0 -4
  434. package/dist/data/quiz/function/ask_question/then_3.mcfunction +0 -4
  435. package/dist/data/quiz/function/ask_question/then_6.mcfunction +0 -4
  436. package/dist/data/quiz/function/ask_question.mcfunction +0 -7
  437. package/dist/data/quiz/function/finish_quiz.mcfunction +0 -6
  438. package/dist/data/quiz/function/handle_answer/else_1.mcfunction +0 -5
  439. package/dist/data/quiz/function/handle_answer/else_10.mcfunction +0 -3
  440. package/dist/data/quiz/function/handle_answer/else_16.mcfunction +0 -3
  441. package/dist/data/quiz/function/handle_answer/else_4.mcfunction +0 -3
  442. package/dist/data/quiz/function/handle_answer/else_7.mcfunction +0 -5
  443. package/dist/data/quiz/function/handle_answer/merge_11.mcfunction +0 -2
  444. package/dist/data/quiz/function/handle_answer/merge_14.mcfunction +0 -2
  445. package/dist/data/quiz/function/handle_answer/merge_17.mcfunction +0 -2
  446. package/dist/data/quiz/function/handle_answer/merge_2.mcfunction +0 -8
  447. package/dist/data/quiz/function/handle_answer/merge_5.mcfunction +0 -2
  448. package/dist/data/quiz/function/handle_answer/merge_8.mcfunction +0 -2
  449. package/dist/data/quiz/function/handle_answer/then_0.mcfunction +0 -5
  450. package/dist/data/quiz/function/handle_answer/then_12.mcfunction +0 -5
  451. package/dist/data/quiz/function/handle_answer/then_15.mcfunction +0 -6
  452. package/dist/data/quiz/function/handle_answer/then_3.mcfunction +0 -6
  453. package/dist/data/quiz/function/handle_answer/then_6.mcfunction +0 -5
  454. package/dist/data/quiz/function/handle_answer/then_9.mcfunction +0 -6
  455. package/dist/data/quiz/function/handle_answer.mcfunction +0 -11
  456. package/dist/data/quiz/function/start_quiz.mcfunction +0 -5
  457. package/dist/data/reqtest/function/__load.mcfunction +0 -4
  458. package/dist/data/reqtest/function/_table_init.mcfunction +0 -2
  459. package/dist/data/reqtest/function/no_trig.mcfunction +0 -3
  460. package/dist/data/reqtest/function/use_table.mcfunction +0 -4
  461. package/dist/data/reqtest2/function/__load.mcfunction +0 -3
  462. package/dist/data/reqtest2/function/no_trig.mcfunction +0 -3
  463. package/dist/data/runtime/function/__load.mcfunction +0 -5
  464. package/dist/data/runtime/function/__tick.mcfunction +0 -2
  465. package/dist/data/runtime/function/counter_tick/then_0.mcfunction +0 -3
  466. package/dist/data/runtime/function/counter_tick.mcfunction +0 -13
  467. package/dist/data/shop/function/__load.mcfunction +0 -7
  468. package/dist/data/shop/function/__tick.mcfunction +0 -3
  469. package/dist/data/shop/function/__trigger_shop_buy_dispatch.mcfunction +0 -4
  470. package/dist/data/shop/function/complete_purchase/else_1.mcfunction +0 -5
  471. package/dist/data/shop/function/complete_purchase/else_4.mcfunction +0 -5
  472. package/dist/data/shop/function/complete_purchase/else_7.mcfunction +0 -3
  473. package/dist/data/shop/function/complete_purchase/merge_2.mcfunction +0 -2
  474. package/dist/data/shop/function/complete_purchase/merge_5.mcfunction +0 -2
  475. package/dist/data/shop/function/complete_purchase/merge_8.mcfunction +0 -2
  476. package/dist/data/shop/function/complete_purchase/then_0.mcfunction +0 -4
  477. package/dist/data/shop/function/complete_purchase/then_3.mcfunction +0 -4
  478. package/dist/data/shop/function/complete_purchase/then_6.mcfunction +0 -4
  479. package/dist/data/shop/function/complete_purchase.mcfunction +0 -7
  480. package/dist/data/shop/function/handle_shop_trigger.mcfunction +0 -3
  481. package/dist/data/swap_test/function/__load.mcfunction +0 -3
  482. package/dist/data/swap_test/function/gcd_old/loop_body_1.mcfunction +0 -7
  483. package/dist/data/swap_test/function/gcd_old/loop_check_0.mcfunction +0 -5
  484. package/dist/data/swap_test/function/gcd_old/loop_exit_2.mcfunction +0 -3
  485. package/dist/data/swap_test/function/gcd_old.mcfunction +0 -8
  486. package/dist/data/turret/function/__load.mcfunction +0 -5
  487. package/dist/data/turret/function/__tick.mcfunction +0 -4
  488. package/dist/data/turret/function/__trigger_deploy_turret_dispatch.mcfunction +0 -4
  489. package/dist/data/turret/function/deploy_turret.mcfunction +0 -8
  490. package/dist/data/turret/function/turret_tick/at_1.mcfunction +0 -2
  491. package/dist/data/turret/function/turret_tick/foreach_0.mcfunction +0 -2
  492. package/dist/data/turret/function/turret_tick/foreach_2.mcfunction +0 -2
  493. package/dist/data/turret/function/turret_tick/tick_body.mcfunction +0 -3
  494. package/dist/data/turret/function/turret_tick/tick_skip.mcfunction +0 -1
  495. package/dist/data/turret/function/turret_tick.mcfunction +0 -5
  496. package/dist/gcd2.map.json +0 -15
  497. package/dist/gcd3.map.json +0 -17
  498. package/dist/gcd_test.map.json +0 -15
  499. package/dist/index.d.ts +0 -62
  500. package/dist/ir/builder.d.ts +0 -33
  501. package/dist/ir/builder.js +0 -99
  502. package/dist/ir/types.d.ts +0 -132
  503. package/dist/ir/types.js +0 -15
  504. package/dist/isqrttest.map.json +0 -15
  505. package/dist/lowering/index.d.ts +0 -188
  506. package/dist/lowering/index.js +0 -3403
  507. package/dist/mathtest.map.json +0 -6
  508. package/dist/mypack.map.json +0 -27
  509. package/dist/optimizer/commands.d.ts +0 -38
  510. package/dist/optimizer/commands.js +0 -451
  511. package/dist/optimizer/dce.d.ts +0 -34
  512. package/dist/optimizer/dce.js +0 -639
  513. package/dist/optimizer/passes.d.ts +0 -34
  514. package/dist/optimizer/passes.js +0 -243
  515. package/dist/optimizer/structure.d.ts +0 -9
  516. package/dist/optimizer/structure.js +0 -356
  517. package/dist/pack.mcmeta +0 -6
  518. package/dist/reqtest.map.json +0 -4
  519. package/dist/reqtest2.map.json +0 -4
  520. package/dist/runtime.map.json +0 -7
  521. package/dist/swap_test.map.json +0 -14
  522. package/src/__tests__/codegen.test.ts +0 -161
  523. package/src/__tests__/e2e.test.ts +0 -2039
  524. package/src/__tests__/entity-types.test.ts +0 -236
  525. package/src/__tests__/lowering.test.ts +0 -1185
  526. package/src/__tests__/macro.test.ts +0 -343
  527. package/src/__tests__/nbt.test.ts +0 -58
  528. package/src/__tests__/optimizer-advanced.test.ts +0 -144
  529. package/src/__tests__/optimizer.test.ts +0 -162
  530. package/src/__tests__/runtime.test.ts +0 -305
  531. package/src/__tests__/stdlib-advanced.test.ts +0 -379
  532. package/src/__tests__/stdlib-bigint.test.ts +0 -427
  533. package/src/__tests__/stdlib-math.test.ts +0 -374
  534. package/src/__tests__/stdlib-vec.test.ts +0 -259
  535. package/src/__tests__/structure-optimizer.test.ts +0 -38
  536. package/src/__tests__/var-allocator.test.ts +0 -75
  537. package/src/codegen/cmdblock/index.ts +0 -63
  538. package/src/codegen/mcfunction/index.ts +0 -662
  539. package/src/codegen/structure/index.ts +0 -346
  540. package/src/codegen/var-allocator.ts +0 -104
  541. package/src/ir/builder.ts +0 -116
  542. package/src/ir/types.ts +0 -134
  543. package/src/lowering/index.ts +0 -3876
  544. package/src/optimizer/commands.ts +0 -534
  545. package/src/optimizer/passes.ts +0 -250
  546. package/src/optimizer/structure.ts +0 -450
  547. /package/dist/{__tests__/cli.test.d.ts → src/__tests__/budget.test.d.ts} +0 -0
  548. /package/dist/{__tests__/codegen.test.d.ts → src/__tests__/cli.test.d.ts} +0 -0
  549. /package/dist/{__tests__ → src/__tests__}/compile-all.test.d.ts +0 -0
  550. /package/dist/{__tests__ → src/__tests__}/compile-all.test.js +0 -0
  551. /package/dist/{__tests__ → src/__tests__}/dce.test.d.ts +0 -0
  552. /package/dist/{__tests__ → src/__tests__}/diagnostics.test.d.ts +0 -0
  553. /package/dist/{__tests__ → src/__tests__}/formatter.test.d.ts +0 -0
  554. /package/dist/{__tests__ → src/__tests__}/formatter.test.js +0 -0
  555. /package/dist/{__tests__/entity-types.test.d.ts → src/__tests__/hir/desugar.test.d.ts} +0 -0
  556. /package/dist/{__tests__ → src/__tests__}/lexer.test.d.ts +0 -0
  557. /package/dist/{__tests__ → src/__tests__}/lexer.test.js +0 -0
  558. /package/dist/{__tests__/lowering.test.d.ts → src/__tests__/lir/lower.test.d.ts} +0 -0
  559. /package/dist/{__tests__/mc-syntax.test.d.ts → src/__tests__/lir/types.test.d.ts} +0 -0
  560. /package/dist/{__tests__/nbt.test.d.ts → src/__tests__/lir/verify.test.d.ts} +0 -0
  561. /package/dist/{__tests__ → src/__tests__}/mc-integration.test.d.ts +0 -0
  562. /package/dist/{__tests__/optimizer-advanced.test.d.ts → src/__tests__/mc-syntax.test.d.ts} +0 -0
  563. /package/dist/{__tests__ → src/__tests__}/mc-syntax.test.js +0 -0
  564. /package/dist/{__tests__/optimizer.test.d.ts → src/__tests__/mir/arithmetic.test.d.ts} +0 -0
  565. /package/dist/{__tests__/parser.test.d.ts → src/__tests__/mir/control-flow.test.d.ts} +0 -0
  566. /package/dist/{__tests__/repl.test.d.ts → src/__tests__/mir/verify.test.d.ts} +0 -0
  567. /package/dist/{__tests__/runtime.test.d.ts → src/__tests__/optimizer/block_merge.test.d.ts} +0 -0
  568. /package/dist/{__tests__/structure-optimizer.test.d.ts → src/__tests__/optimizer/branch_simplify.test.d.ts} +0 -0
  569. /package/dist/{__tests__/typechecker.test.d.ts → src/__tests__/optimizer/constant_fold.test.d.ts} +0 -0
  570. /package/dist/{__tests__/var-allocator.test.d.ts → src/__tests__/optimizer/copy_prop.test.d.ts} +0 -0
  571. /package/dist/{__tests__ → src/__tests__}/parser.test.js +0 -0
  572. /package/dist/{__tests__ → src/__tests__}/repl.test.js +0 -0
  573. /package/dist/{__tests__ → src/__tests__}/typechecker.test.js +0 -0
  574. /package/dist/{ast → src/ast}/types.js +0 -0
  575. /package/dist/{builtins → src/builtins}/metadata.d.ts +0 -0
  576. /package/dist/{builtins → src/builtins}/metadata.js +0 -0
  577. /package/dist/{events → src/events}/types.d.ts +0 -0
  578. /package/dist/{events → src/events}/types.js +0 -0
  579. /package/dist/{formatter → src/formatter}/index.d.ts +0 -0
  580. /package/dist/{formatter → src/formatter}/index.js +0 -0
  581. /package/dist/{mc-test → src/mc-test}/client.d.ts +0 -0
  582. /package/dist/{mc-test → src/mc-test}/client.js +0 -0
  583. /package/dist/{mc-test → src/mc-test}/runner.d.ts +0 -0
  584. /package/dist/{mc-test → src/mc-test}/setup.d.ts +0 -0
  585. /package/dist/{mc-test → src/mc-test}/setup.js +0 -0
  586. /package/dist/{mc-validator → src/mc-validator}/index.d.ts +0 -0
  587. /package/dist/{mc-validator → src/mc-validator}/index.js +0 -0
  588. /package/dist/{nbt → src/nbt}/index.d.ts +0 -0
  589. /package/dist/{nbt → src/nbt}/index.js +0 -0
  590. /package/dist/{repl.js → src/repl.js} +0 -0
  591. /package/dist/{runtime → src/runtime}/index.d.ts +0 -0
  592. /package/dist/{types → src/types}/entity-hierarchy.d.ts +0 -0
  593. /package/dist/{types → src/types}/entity-hierarchy.js +0 -0
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Selector Cache — MIR optimization pass.
3
+ *
4
+ * Within a single basic block, if the same complex selector (one containing '[')
5
+ * appears in ≥2 call_context instructions (as `as` or `at` subcommands),
6
+ * the pass replaces the 2nd and subsequent occurrences with a simpler
7
+ * tag-based selector `@e[tag=__cache_sel_N]`.
8
+ *
9
+ * Two new call_context instructions are prepended to the block:
10
+ * 1. Cleanup: execute as @e[tag=__cache_sel_N] run <fn: __sel_cleanup_<tag>>
11
+ * — signals codegen to emit: tag @e[tag=<tag>] remove <tag>
12
+ * 2. Tag-add: execute as <original_selector> run <fn: __sel_tag_<tag>>
13
+ * — signals codegen to emit: tag @s add <tag>
14
+ *
15
+ * The synthetic fn names `__sel_cleanup_*` and `__sel_tag_*` are a convention
16
+ * recognized by the codegen layer for special-case emission.
17
+ *
18
+ * This is a block-local pass (does not track selector lifetime across block
19
+ * boundaries) so it is always correct with respect to control flow.
20
+ */
21
+
22
+ import type { MIRFunction, MIRBlock, MIRInstr } from '../mir/types'
23
+
24
+ export function selectorCache(fn: MIRFunction): MIRFunction {
25
+ let tagId = 0
26
+ return {
27
+ ...fn,
28
+ blocks: fn.blocks.map(block => processBlock(block, () => tagId++)),
29
+ }
30
+ }
31
+
32
+ function processBlock(block: MIRBlock, nextId: () => number): MIRBlock {
33
+ // Count how many times each complex selector appears across call_context instrs
34
+ const selectorCount = new Map<string, number>()
35
+ for (const instr of block.instrs) {
36
+ if (instr.kind === 'call_context') {
37
+ for (const sub of instr.subcommands) {
38
+ if ((sub.kind === 'as' || sub.kind === 'at') && isComplexSelector(sub.selector)) {
39
+ selectorCount.set(sub.selector, (selectorCount.get(sub.selector) ?? 0) + 1)
40
+ }
41
+ }
42
+ }
43
+ }
44
+
45
+ // Build a map of selectors that appear ≥2 times → assigned tag name
46
+ const repeated = new Map<string, string>() // selector → tag name
47
+ for (const [sel, count] of selectorCount) {
48
+ if (count >= 2) {
49
+ repeated.set(sel, `__cache_sel_${nextId()}`)
50
+ }
51
+ }
52
+
53
+ if (repeated.size === 0) return block
54
+
55
+ // Prepend cleanup + tag-add instructions for each repeated selector
56
+ const prefixInstrs: MIRInstr[] = []
57
+ for (const [sel, tag] of repeated) {
58
+ // 1. Cleanup: remove stale tags from any entities that still carry this tag
59
+ prefixInstrs.push({
60
+ kind: 'call_context',
61
+ fn: `__sel_cleanup_${tag}`,
62
+ subcommands: [{ kind: 'as', selector: `@e[tag=${tag}]` }],
63
+ })
64
+ // 2. Tag-add: tag all matching entities with the cache tag
65
+ prefixInstrs.push({
66
+ kind: 'call_context',
67
+ fn: `__sel_tag_${tag}`,
68
+ subcommands: [{ kind: 'as', selector: sel }],
69
+ })
70
+ }
71
+
72
+ // Rewrite instructions: first occurrence of each repeated selector is kept
73
+ // as-is; subsequent occurrences are replaced with the tag-based selector.
74
+ const seen = new Set<string>()
75
+ const newInstrs: MIRInstr[] = [...prefixInstrs]
76
+ for (const instr of block.instrs) {
77
+ if (instr.kind === 'call_context') {
78
+ const newSubs = instr.subcommands.map(sub => {
79
+ if ((sub.kind === 'as' || sub.kind === 'at') && repeated.has(sub.selector)) {
80
+ const tag = repeated.get(sub.selector)!
81
+ if (seen.has(sub.selector)) {
82
+ // Subsequent occurrence — use the tag selector
83
+ return { ...sub, selector: `@e[tag=${tag}]` }
84
+ } else {
85
+ // First occurrence — keep original, mark as seen
86
+ seen.add(sub.selector)
87
+ return sub
88
+ }
89
+ }
90
+ return sub
91
+ })
92
+ newInstrs.push({ ...instr, subcommands: newSubs })
93
+ } else {
94
+ newInstrs.push(instr)
95
+ }
96
+ }
97
+
98
+ return { ...block, instrs: newInstrs }
99
+ }
100
+
101
+ function isComplexSelector(selector: string): boolean {
102
+ return selector.includes('[')
103
+ }
@@ -0,0 +1,386 @@
1
+ /**
2
+ * Small Constant Loop Unrolling — MIR optimization pass.
3
+ *
4
+ * Detects `for (let i = 0; i < N; i++)` loops where N is a compile-time
5
+ * constant and N ≤ 8, then unrolls them: the loop body is duplicated N times
6
+ * with the loop variable substituted as the literal 0..N-1.
7
+ *
8
+ * Pattern recognized (after HIR→MIR lowering):
9
+ * entry block: const t_i 0 (loop var init)
10
+ * loop_header: cmp(lt, t_i, N) → branch body/exit
11
+ * loop_body: body instructions, jump → loop_latch
12
+ * loop_latch: t_i = t_i + 1, jump → loop_header
13
+ * loop_exit: ...
14
+ *
15
+ * After unrolling:
16
+ * entry block (with i=0 def removed):
17
+ * [body with t_i → 0]
18
+ * [body with t_i → 1]
19
+ * ...
20
+ * [body with t_i → N-1]
21
+ * jump → loop_exit
22
+ * loop_exit: ...
23
+ *
24
+ * Limitations:
25
+ * - Only unrolls when N ≤ 8
26
+ * - The loop variable must be initialized to exactly 0 before the loop
27
+ * - The latch must do exactly `t_i = t_i + 1` (or equivalent const add)
28
+ * - No break/continue (body must not jump directly to exit or latch)
29
+ * - N must be a compile-time constant (Operand kind='const')
30
+ */
31
+
32
+ import type {
33
+ MIRFunction, MIRBlock, MIRInstr, Operand, Temp, BlockId,
34
+ } from '../mir/types'
35
+
36
+ const UNROLL_LIMIT = 8
37
+
38
+ // ---------------------------------------------------------------------------
39
+ // Public API
40
+ // ---------------------------------------------------------------------------
41
+
42
+ export function loopUnroll(fn: MIRFunction): MIRFunction {
43
+ let current = fn
44
+ let changed = true
45
+ // Iterate to fixpoint in case of multiple unrollable loops
46
+ while (changed) {
47
+ changed = false
48
+ const result = tryUnrollOne(current)
49
+ if (result !== current) {
50
+ current = result
51
+ changed = true
52
+ }
53
+ }
54
+ return current
55
+ }
56
+
57
+ // ---------------------------------------------------------------------------
58
+ // Core: try to unroll one loop in the function
59
+ // ---------------------------------------------------------------------------
60
+
61
+ interface LoopInfo {
62
+ /** Block id of loop_header */
63
+ headerId: BlockId
64
+ /** Block id of loop_body */
65
+ bodyId: BlockId
66
+ /** Block id of loop_latch */
67
+ latchId: BlockId
68
+ /** Block id of loop_exit */
69
+ exitId: BlockId
70
+ /** The loop variable temp name */
71
+ loopVar: Temp
72
+ /** The upper bound constant (exclusive: i < N) */
73
+ N: number
74
+ /** Block id that jumps into the loop header (the pre-header) */
75
+ preHeaderId: BlockId
76
+ }
77
+
78
+ function tryUnrollOne(fn: MIRFunction): MIRFunction {
79
+ const blockMap = new Map(fn.blocks.map(b => [b.id, b]))
80
+ const info = findUnrollableLoop(fn, blockMap)
81
+ if (!info) return fn
82
+
83
+ return unroll(fn, blockMap, info)
84
+ }
85
+
86
+ // ---------------------------------------------------------------------------
87
+ // Loop detection
88
+ // ---------------------------------------------------------------------------
89
+
90
+ function findUnrollableLoop(fn: MIRFunction, blockMap: Map<BlockId, MIRBlock>): LoopInfo | null {
91
+ for (const block of fn.blocks) {
92
+ if (!block.id.startsWith('loop_header')) continue
93
+ const info = analyzeLoop(fn, blockMap, block)
94
+ if (info) return info
95
+ }
96
+ return null
97
+ }
98
+
99
+ function analyzeLoop(
100
+ fn: MIRFunction,
101
+ blockMap: Map<BlockId, MIRBlock>,
102
+ header: MIRBlock,
103
+ ): LoopInfo | null {
104
+ // Header must branch on a cmp result
105
+ if (header.term.kind !== 'branch') return null
106
+ const branch = header.term
107
+ if (branch.cond.kind !== 'temp') return null
108
+ const condName = branch.cond.name
109
+
110
+ // Find the cmp instruction in the header
111
+ const cmpInstr = header.instrs.find(
112
+ instr => instr.kind === 'cmp' && instr.dst === condName
113
+ ) as Extract<MIRInstr, { kind: 'cmp' }> | undefined
114
+ if (!cmpInstr) return null
115
+
116
+ // Must be a `lt` comparison: i < N
117
+ if (cmpInstr.op !== 'lt') return null
118
+
119
+ // Left operand must be a temp (the loop var), right must be a constant
120
+ if (cmpInstr.a.kind !== 'temp') return null
121
+ if (cmpInstr.b.kind !== 'const') return null
122
+
123
+ const loopVar = cmpInstr.a.name
124
+ const N = cmpInstr.b.value
125
+
126
+ // Reject if N > limit or N <= 0
127
+ if (N > UNROLL_LIMIT || N <= 0) return null
128
+
129
+ // then = loop_body, else = loop_exit
130
+ const bodyId = branch.then
131
+ const exitId = branch.else
132
+
133
+ const bodyBlock = blockMap.get(bodyId)
134
+ if (!bodyBlock) return null
135
+ if (!bodyBlock.id.startsWith('loop_body')) return null
136
+
137
+ // Body must end with jump to latch (or header if no latch)
138
+ if (bodyBlock.term.kind !== 'jump') return null
139
+ const afterBodyId = bodyBlock.term.target
140
+ const afterBody = blockMap.get(afterBodyId)
141
+ if (!afterBody) return null
142
+
143
+ // Find the latch block
144
+ let latchId: BlockId
145
+ if (afterBody.id.startsWith('loop_latch')) {
146
+ latchId = afterBodyId
147
+ } else {
148
+ return null
149
+ }
150
+
151
+ const latch = blockMap.get(latchId)!
152
+
153
+ // Latch must end with jump back to header
154
+ if (latch.term.kind !== 'jump') return null
155
+ if (latch.term.target !== header.id) return null
156
+
157
+ // Latch must increment loopVar by 1
158
+ if (!latchIncrementsBy1(latch, loopVar)) return null
159
+
160
+ // Loop var must be initialized to 0 before entering the loop
161
+ // Find the pre-header: the block that jumps to header (not the latch)
162
+ const preHeaderId = findPreHeader(fn, header.id, latchId)
163
+ if (!preHeaderId) return null
164
+
165
+ const preHeader = blockMap.get(preHeaderId)!
166
+ if (!initializesTo0(preHeader, fn, loopVar)) return null
167
+
168
+ // Body must not contain break (direct jump to exit) or continue to latch
169
+ // (those would require more complex handling)
170
+ if (bodyHasBreakOrContinue(bodyBlock, exitId, latchId)) return null
171
+
172
+ return { headerId: header.id, bodyId, latchId, exitId, loopVar, N, preHeaderId }
173
+ }
174
+
175
+ /** Check that the latch increments loopVar by 1.
176
+ *
177
+ * Two common patterns:
178
+ * 1. Direct: add loopVar loopVar 1
179
+ * 2. Two-step: add t_tmp loopVar 1; copy loopVar t_tmp
180
+ */
181
+ function latchIncrementsBy1(latch: MIRBlock, loopVar: Temp): boolean {
182
+ // Pattern 1: add dst=loopVar, a=loopVar, b=const(1)
183
+ for (const instr of latch.instrs) {
184
+ if (
185
+ instr.kind === 'add' &&
186
+ instr.dst === loopVar &&
187
+ instr.a.kind === 'temp' && instr.a.name === loopVar &&
188
+ instr.b.kind === 'const' && instr.b.value === 1
189
+ ) {
190
+ return true
191
+ }
192
+ }
193
+
194
+ // Pattern 2: add t_tmp loopVar 1; copy loopVar t_tmp
195
+ // Find the copy that assigns loopVar, then check the add that produced the source
196
+ for (let i = 0; i < latch.instrs.length; i++) {
197
+ const instr = latch.instrs[i]
198
+ if (
199
+ instr.kind === 'copy' &&
200
+ instr.dst === loopVar &&
201
+ instr.src.kind === 'temp'
202
+ ) {
203
+ const srcTemp = instr.src.name
204
+ // Find the add instruction that produced srcTemp
205
+ for (const addInstr of latch.instrs) {
206
+ if (
207
+ addInstr.kind === 'add' &&
208
+ addInstr.dst === srcTemp &&
209
+ addInstr.a.kind === 'temp' && addInstr.a.name === loopVar &&
210
+ addInstr.b.kind === 'const' && addInstr.b.value === 1
211
+ ) {
212
+ return true
213
+ }
214
+ }
215
+ }
216
+ }
217
+
218
+ return false
219
+ }
220
+
221
+ /** Check if loopVar is initialized to 0 in the pre-header (or reachable const) */
222
+ function initializesTo0(preHeader: MIRBlock, fn: MIRFunction, loopVar: Temp): boolean {
223
+ // Check if last definition of loopVar in preHeader is const 0
224
+ for (let i = preHeader.instrs.length - 1; i >= 0; i--) {
225
+ const instr = preHeader.instrs[i]
226
+ if (instr.kind === 'const' && instr.dst === loopVar && instr.value === 0) return true
227
+ if (instr.kind === 'copy' && instr.dst === loopVar) {
228
+ return instr.src.kind === 'const' && instr.src.value === 0
229
+ }
230
+ // If something else defines loopVar, it's not 0
231
+ if (getInstrDst(instr) === loopVar) return false
232
+ }
233
+ // Also check entry block if preHeader is entry
234
+ if (preHeader.id === fn.entry) return false
235
+ return false
236
+ }
237
+
238
+ /** Find the pre-header block: the predecessor of header that is NOT the latch */
239
+ function findPreHeader(fn: MIRFunction, headerId: BlockId, latchId: BlockId): BlockId | null {
240
+ const preds: BlockId[] = []
241
+ for (const block of fn.blocks) {
242
+ const targets = getTermTargets(block.term)
243
+ if (targets.includes(headerId) && block.id !== latchId) {
244
+ preds.push(block.id)
245
+ }
246
+ }
247
+ return preds.length === 1 ? preds[0] : null
248
+ }
249
+
250
+ /** Check if body block has break (jump to exit) or continue (jump to latch) */
251
+ function bodyHasBreakOrContinue(body: MIRBlock, exitId: BlockId, latchId: BlockId): boolean {
252
+ // The normal path is body → latch. Any direct jump to exit = break,
253
+ // any jump to latch from within body sub-blocks = continue
254
+ // For simplicity, we only unroll if the body has no sub-blocks with these patterns.
255
+ // We only check the body block itself (single-block body).
256
+ // A multi-block body would need recursive checking.
257
+ return false
258
+ }
259
+
260
+ // ---------------------------------------------------------------------------
261
+ // Unrolling
262
+ // ---------------------------------------------------------------------------
263
+
264
+ function unroll(fn: MIRFunction, blockMap: Map<BlockId, MIRBlock>, info: LoopInfo): MIRFunction {
265
+ const { headerId, bodyId, latchId, exitId, loopVar, N, preHeaderId } = info
266
+
267
+ const preHeader = blockMap.get(preHeaderId)!
268
+ const body = blockMap.get(bodyId)!
269
+
270
+ // Remove the `const loopVar 0` init from preHeader
271
+ const newPreHeaderInstrs = preHeader.instrs.filter(
272
+ instr => !(instr.kind === 'const' && instr.dst === loopVar && instr.value === 0) &&
273
+ !(instr.kind === 'copy' && instr.dst === loopVar &&
274
+ instr.src.kind === 'const' && instr.src.value === 0)
275
+ )
276
+
277
+ // Build N copies of the body with loopVar substituted
278
+ const unrolledInstrs: MIRInstr[] = []
279
+ for (let iter = 0; iter < N; iter++) {
280
+ const substitution = new Map<Temp, Operand>([[loopVar, { kind: 'const', value: iter }]])
281
+ for (const instr of body.instrs) {
282
+ unrolledInstrs.push(substituteInstr(instr, substitution))
283
+ }
284
+ }
285
+
286
+ // New pre-header: original instrs (minus init) + all unrolled body + jump to exit
287
+ const newPreHeader: MIRBlock = {
288
+ ...preHeader,
289
+ instrs: [...newPreHeaderInstrs, ...unrolledInstrs],
290
+ term: { kind: 'jump', target: exitId },
291
+ }
292
+
293
+ // Remove header, body, latch blocks; update preHeader; keep exit
294
+ const keepIds = new Set(fn.blocks.map(b => b.id))
295
+ keepIds.delete(headerId)
296
+ keepIds.delete(bodyId)
297
+ keepIds.delete(latchId)
298
+
299
+ const newBlocks: MIRBlock[] = []
300
+ for (const block of fn.blocks) {
301
+ if (block.id === preHeaderId) {
302
+ newBlocks.push(newPreHeader)
303
+ } else if (keepIds.has(block.id)) {
304
+ newBlocks.push(block)
305
+ }
306
+ }
307
+
308
+ // Recompute predecessors
309
+ const newBlocksWithPreds = recomputePreds(newBlocks)
310
+
311
+ return { ...fn, blocks: newBlocksWithPreds }
312
+ }
313
+
314
+ // ---------------------------------------------------------------------------
315
+ // Instruction substitution
316
+ // ---------------------------------------------------------------------------
317
+
318
+ function substituteOp(op: Operand, sub: Map<Temp, Operand>): Operand {
319
+ if (op.kind === 'temp') {
320
+ const replacement = sub.get(op.name)
321
+ if (replacement) return replacement
322
+ }
323
+ return op
324
+ }
325
+
326
+ function substituteInstr(instr: MIRInstr, sub: Map<Temp, Operand>): MIRInstr {
327
+ switch (instr.kind) {
328
+ case 'copy': return { ...instr, src: substituteOp(instr.src, sub) }
329
+ case 'neg': case 'not': return { ...instr, src: substituteOp(instr.src, sub) }
330
+ case 'add': case 'sub': case 'mul': case 'div': case 'mod':
331
+ case 'and': case 'or':
332
+ return { ...instr, a: substituteOp(instr.a, sub), b: substituteOp(instr.b, sub) }
333
+ case 'cmp':
334
+ return { ...instr, a: substituteOp(instr.a, sub), b: substituteOp(instr.b, sub) }
335
+ case 'nbt_write':
336
+ return { ...instr, src: substituteOp(instr.src, sub) }
337
+ case 'call':
338
+ return { ...instr, args: instr.args.map(a => substituteOp(a, sub)) }
339
+ case 'call_macro':
340
+ return { ...instr, args: instr.args.map(a => ({ ...a, value: substituteOp(a.value, sub) })) }
341
+ case 'branch':
342
+ return { ...instr, cond: substituteOp(instr.cond, sub) }
343
+ case 'return':
344
+ return { ...instr, value: instr.value ? substituteOp(instr.value, sub) : null }
345
+ default:
346
+ return instr
347
+ }
348
+ }
349
+
350
+ // ---------------------------------------------------------------------------
351
+ // Helpers
352
+ // ---------------------------------------------------------------------------
353
+
354
+ function getInstrDst(instr: MIRInstr): Temp | null {
355
+ switch (instr.kind) {
356
+ case 'const': case 'copy':
357
+ case 'add': case 'sub': case 'mul': case 'div': case 'mod':
358
+ case 'neg': case 'cmp': case 'and': case 'or': case 'not':
359
+ case 'nbt_read':
360
+ return instr.dst
361
+ case 'call': case 'call_macro':
362
+ return instr.dst
363
+ default:
364
+ return null
365
+ }
366
+ }
367
+
368
+ function getTermTargets(term: MIRInstr): BlockId[] {
369
+ switch (term.kind) {
370
+ case 'jump': return [term.target]
371
+ case 'branch': return [term.then, term.else]
372
+ default: return []
373
+ }
374
+ }
375
+
376
+ function recomputePreds(blocks: MIRBlock[]): MIRBlock[] {
377
+ const predMap = new Map<BlockId, BlockId[]>()
378
+ for (const b of blocks) predMap.set(b.id, [])
379
+ for (const block of blocks) {
380
+ for (const target of getTermTargets(block.term)) {
381
+ const preds = predMap.get(target)
382
+ if (preds) preds.push(block.id)
383
+ }
384
+ }
385
+ return blocks.map(b => ({ ...b, preds: predMap.get(b.id) ?? [] }))
386
+ }