redscript-mc 1.2.25 → 1.2.27

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 (265) hide show
  1. package/README.md +67 -9
  2. package/README.zh.md +61 -4
  3. package/dist/__tests__/cli.test.js +1 -1
  4. package/dist/__tests__/codegen.test.js +12 -6
  5. package/dist/__tests__/e2e.test.js +6 -6
  6. package/dist/__tests__/lowering.test.js +8 -8
  7. package/dist/__tests__/optimizer.test.js +31 -0
  8. package/dist/__tests__/stdlib-advanced.test.d.ts +4 -0
  9. package/dist/__tests__/stdlib-advanced.test.js +378 -0
  10. package/dist/__tests__/stdlib-bigint.test.d.ts +7 -0
  11. package/dist/__tests__/stdlib-bigint.test.js +428 -0
  12. package/dist/__tests__/stdlib-math.test.d.ts +7 -0
  13. package/dist/__tests__/stdlib-math.test.js +352 -0
  14. package/dist/__tests__/stdlib-vec.test.d.ts +4 -0
  15. package/dist/__tests__/stdlib-vec.test.js +264 -0
  16. package/dist/ast/types.d.ts +17 -1
  17. package/dist/codegen/mcfunction/index.js +154 -18
  18. package/dist/codegen/var-allocator.d.ts +17 -0
  19. package/dist/codegen/var-allocator.js +26 -0
  20. package/dist/compile.d.ts +14 -0
  21. package/dist/compile.js +62 -5
  22. package/dist/data/arena/function/__load.mcfunction +6 -0
  23. package/dist/data/arena/function/__tick.mcfunction +2 -0
  24. package/dist/data/arena/function/announce_leaders/else_1.mcfunction +3 -0
  25. package/dist/data/arena/function/announce_leaders/foreach_0/merge_2.mcfunction +1 -0
  26. package/dist/data/arena/function/announce_leaders/foreach_0/then_0.mcfunction +3 -0
  27. package/dist/data/arena/function/announce_leaders/foreach_0.mcfunction +7 -0
  28. package/dist/data/arena/function/announce_leaders/foreach_1/merge_2.mcfunction +1 -0
  29. package/dist/data/arena/function/announce_leaders/foreach_1/then_0.mcfunction +4 -0
  30. package/dist/data/arena/function/announce_leaders/foreach_1.mcfunction +6 -0
  31. package/dist/data/arena/function/announce_leaders/merge_2.mcfunction +1 -0
  32. package/dist/data/arena/function/announce_leaders/then_0.mcfunction +4 -0
  33. package/dist/data/arena/function/announce_leaders.mcfunction +6 -0
  34. package/dist/data/arena/function/arena_tick/merge_2.mcfunction +1 -0
  35. package/dist/data/arena/function/arena_tick/then_0.mcfunction +4 -0
  36. package/dist/data/arena/function/arena_tick.mcfunction +11 -0
  37. package/dist/data/counter/function/__load.mcfunction +5 -0
  38. package/dist/data/counter/function/__tick.mcfunction +2 -0
  39. package/dist/data/counter/function/counter_tick/merge_2.mcfunction +1 -0
  40. package/dist/data/counter/function/counter_tick/then_0.mcfunction +3 -0
  41. package/dist/data/counter/function/counter_tick.mcfunction +11 -0
  42. package/dist/data/gcd2/function/__load.mcfunction +3 -0
  43. package/dist/data/gcd2/function/abs/merge_2.mcfunction +3 -0
  44. package/dist/data/gcd2/function/abs/then_0.mcfunction +5 -0
  45. package/dist/data/gcd2/function/abs.mcfunction +7 -0
  46. package/dist/data/gcd2/function/gcd/loop_body_1.mcfunction +7 -0
  47. package/dist/data/gcd2/function/gcd/loop_check_0.mcfunction +5 -0
  48. package/dist/data/gcd2/function/gcd/loop_exit_2.mcfunction +3 -0
  49. package/dist/data/gcd2/function/gcd.mcfunction +14 -0
  50. package/dist/data/gcd3/function/__load.mcfunction +3 -0
  51. package/dist/data/gcd3/function/abs/merge_2.mcfunction +3 -0
  52. package/dist/data/gcd3/function/abs/then_0.mcfunction +5 -0
  53. package/dist/data/gcd3/function/abs.mcfunction +7 -0
  54. package/dist/data/gcd3/function/gcd/loop_body_1.mcfunction +7 -0
  55. package/dist/data/gcd3/function/gcd/loop_check_0.mcfunction +5 -0
  56. package/dist/data/gcd3/function/gcd/loop_exit_2.mcfunction +3 -0
  57. package/dist/data/gcd3/function/gcd.mcfunction +14 -0
  58. package/dist/data/gcd3/function/test.mcfunction +7 -0
  59. package/dist/data/gcd3nm/function/__load.mcfunction +3 -0
  60. package/dist/data/gcd3nm/function/abs/merge_2.mcfunction +3 -0
  61. package/dist/data/gcd3nm/function/abs/then_0.mcfunction +5 -0
  62. package/dist/data/gcd3nm/function/abs.mcfunction +7 -0
  63. package/dist/data/gcd3nm/function/gcd/loop_body_1.mcfunction +7 -0
  64. package/dist/data/gcd3nm/function/gcd/loop_check_0.mcfunction +5 -0
  65. package/dist/data/gcd3nm/function/gcd/loop_exit_2.mcfunction +3 -0
  66. package/dist/data/gcd3nm/function/gcd.mcfunction +14 -0
  67. package/dist/data/gcd3nm/function/test.mcfunction +7 -0
  68. package/dist/data/gcd_test/function/__load.mcfunction +3 -0
  69. package/dist/data/gcd_test/function/abs/merge_2.mcfunction +3 -0
  70. package/dist/data/gcd_test/function/abs/then_0.mcfunction +5 -0
  71. package/dist/data/gcd_test/function/abs.mcfunction +7 -0
  72. package/dist/data/gcd_test/function/gcd/loop_body_1.mcfunction +7 -0
  73. package/dist/data/gcd_test/function/gcd/loop_check_0.mcfunction +5 -0
  74. package/dist/data/gcd_test/function/gcd/loop_exit_2.mcfunction +3 -0
  75. package/dist/data/gcd_test/function/gcd.mcfunction +14 -0
  76. package/dist/data/isqrttest/function/__load.mcfunction +6 -0
  77. package/dist/data/isqrttest/function/isqrt/loop_body_4.mcfunction +12 -0
  78. package/dist/data/isqrttest/function/isqrt/loop_check_3.mcfunction +5 -0
  79. package/dist/data/isqrttest/function/isqrt/loop_exit_5.mcfunction +3 -0
  80. package/dist/data/isqrttest/function/isqrt/merge_2.mcfunction +4 -0
  81. package/dist/data/isqrttest/function/isqrt/merge_8.mcfunction +6 -0
  82. package/dist/data/isqrttest/function/isqrt/then_0.mcfunction +3 -0
  83. package/dist/data/isqrttest/function/isqrt/then_6.mcfunction +3 -0
  84. package/dist/data/isqrttest/function/isqrt.mcfunction +7 -0
  85. package/dist/data/isqrttest/function/test.mcfunction +6 -0
  86. package/dist/data/mathtest/function/__load.mcfunction +3 -0
  87. package/dist/data/mathtest/function/abs/merge_2.mcfunction +3 -0
  88. package/dist/data/mathtest/function/abs/then_0.mcfunction +5 -0
  89. package/dist/data/mathtest/function/abs.mcfunction +6 -0
  90. package/dist/data/mathtest/function/test.mcfunction +5 -0
  91. package/dist/data/minecraft/tags/function/load.json +5 -0
  92. package/dist/data/minecraft/tags/function/tick.json +5 -0
  93. package/dist/data/mypack/function/__load.mcfunction +13 -0
  94. package/dist/data/mypack/function/_atan_init.mcfunction +2 -0
  95. package/dist/data/mypack/function/abs/merge_2.mcfunction +3 -0
  96. package/dist/data/mypack/function/abs/then_0.mcfunction +5 -0
  97. package/dist/data/mypack/function/abs.mcfunction +6 -0
  98. package/dist/data/mypack/function/atan2_fixed/__sgi_1.mcfunction +2 -0
  99. package/dist/data/mypack/function/atan2_fixed/else_34.mcfunction +3 -0
  100. package/dist/data/mypack/function/atan2_fixed/loop_body_31.mcfunction +19 -0
  101. package/dist/data/mypack/function/atan2_fixed/loop_check_30.mcfunction +5 -0
  102. package/dist/data/mypack/function/atan2_fixed/loop_exit_32.mcfunction +6 -0
  103. package/dist/data/mypack/function/atan2_fixed/merge_11.mcfunction +6 -0
  104. package/dist/data/mypack/function/atan2_fixed/merge_14.mcfunction +3 -0
  105. package/dist/data/mypack/function/atan2_fixed/merge_17.mcfunction +6 -0
  106. package/dist/data/mypack/function/atan2_fixed/merge_2.mcfunction +5 -0
  107. package/dist/data/mypack/function/atan2_fixed/merge_20.mcfunction +5 -0
  108. package/dist/data/mypack/function/atan2_fixed/merge_23.mcfunction +5 -0
  109. package/dist/data/mypack/function/atan2_fixed/merge_26.mcfunction +6 -0
  110. package/dist/data/mypack/function/atan2_fixed/merge_29.mcfunction +4 -0
  111. package/dist/data/mypack/function/atan2_fixed/merge_38.mcfunction +5 -0
  112. package/dist/data/mypack/function/atan2_fixed/merge_41.mcfunction +5 -0
  113. package/dist/data/mypack/function/atan2_fixed/merge_44.mcfunction +5 -0
  114. package/dist/data/mypack/function/atan2_fixed/merge_47.mcfunction +5 -0
  115. package/dist/data/mypack/function/atan2_fixed/merge_5.mcfunction +5 -0
  116. package/dist/data/mypack/function/atan2_fixed/merge_8.mcfunction +3 -0
  117. package/dist/data/mypack/function/atan2_fixed/then_0.mcfunction +5 -0
  118. package/dist/data/mypack/function/atan2_fixed/then_12.mcfunction +3 -0
  119. package/dist/data/mypack/function/atan2_fixed/then_15.mcfunction +5 -0
  120. package/dist/data/mypack/function/atan2_fixed/then_18.mcfunction +5 -0
  121. package/dist/data/mypack/function/atan2_fixed/then_21.mcfunction +3 -0
  122. package/dist/data/mypack/function/atan2_fixed/then_24.mcfunction +3 -0
  123. package/dist/data/mypack/function/atan2_fixed/then_27.mcfunction +6 -0
  124. package/dist/data/mypack/function/atan2_fixed/then_3.mcfunction +3 -0
  125. package/dist/data/mypack/function/atan2_fixed/then_33.mcfunction +5 -0
  126. package/dist/data/mypack/function/atan2_fixed/then_36.mcfunction +5 -0
  127. package/dist/data/mypack/function/atan2_fixed/then_39.mcfunction +5 -0
  128. package/dist/data/mypack/function/atan2_fixed/then_42.mcfunction +3 -0
  129. package/dist/data/mypack/function/atan2_fixed/then_45.mcfunction +5 -0
  130. package/dist/data/mypack/function/atan2_fixed/then_6.mcfunction +3 -0
  131. package/dist/data/mypack/function/atan2_fixed/then_9.mcfunction +5 -0
  132. package/dist/data/mypack/function/atan2_fixed.mcfunction +7 -0
  133. package/dist/data/mypack/function/my_game.mcfunction +10 -0
  134. package/dist/data/quiz/function/__load.mcfunction +16 -0
  135. package/dist/data/quiz/function/__tick.mcfunction +6 -0
  136. package/dist/data/quiz/function/__trigger_quiz_a_dispatch.mcfunction +4 -0
  137. package/dist/data/quiz/function/__trigger_quiz_b_dispatch.mcfunction +4 -0
  138. package/dist/data/quiz/function/__trigger_quiz_c_dispatch.mcfunction +4 -0
  139. package/dist/data/quiz/function/__trigger_quiz_start_dispatch.mcfunction +4 -0
  140. package/dist/data/quiz/function/answer_a.mcfunction +4 -0
  141. package/dist/data/quiz/function/answer_b.mcfunction +4 -0
  142. package/dist/data/quiz/function/answer_c.mcfunction +4 -0
  143. package/dist/data/quiz/function/ask_question/else_1.mcfunction +5 -0
  144. package/dist/data/quiz/function/ask_question/else_4.mcfunction +5 -0
  145. package/dist/data/quiz/function/ask_question/else_7.mcfunction +4 -0
  146. package/dist/data/quiz/function/ask_question/merge_2.mcfunction +1 -0
  147. package/dist/data/quiz/function/ask_question/merge_5.mcfunction +2 -0
  148. package/dist/data/quiz/function/ask_question/merge_8.mcfunction +2 -0
  149. package/dist/data/quiz/function/ask_question/then_0.mcfunction +4 -0
  150. package/dist/data/quiz/function/ask_question/then_3.mcfunction +4 -0
  151. package/dist/data/quiz/function/ask_question/then_6.mcfunction +4 -0
  152. package/dist/data/quiz/function/ask_question.mcfunction +7 -0
  153. package/dist/data/quiz/function/finish_quiz.mcfunction +6 -0
  154. package/dist/data/quiz/function/handle_answer/else_1.mcfunction +5 -0
  155. package/dist/data/quiz/function/handle_answer/else_10.mcfunction +3 -0
  156. package/dist/data/quiz/function/handle_answer/else_16.mcfunction +3 -0
  157. package/dist/data/quiz/function/handle_answer/else_4.mcfunction +3 -0
  158. package/dist/data/quiz/function/handle_answer/else_7.mcfunction +5 -0
  159. package/dist/data/quiz/function/handle_answer/merge_11.mcfunction +2 -0
  160. package/dist/data/quiz/function/handle_answer/merge_14.mcfunction +2 -0
  161. package/dist/data/quiz/function/handle_answer/merge_17.mcfunction +2 -0
  162. package/dist/data/quiz/function/handle_answer/merge_2.mcfunction +8 -0
  163. package/dist/data/quiz/function/handle_answer/merge_5.mcfunction +2 -0
  164. package/dist/data/quiz/function/handle_answer/merge_8.mcfunction +2 -0
  165. package/dist/data/quiz/function/handle_answer/then_0.mcfunction +5 -0
  166. package/dist/data/quiz/function/handle_answer/then_12.mcfunction +5 -0
  167. package/dist/data/quiz/function/handle_answer/then_15.mcfunction +6 -0
  168. package/dist/data/quiz/function/handle_answer/then_3.mcfunction +6 -0
  169. package/dist/data/quiz/function/handle_answer/then_6.mcfunction +5 -0
  170. package/dist/data/quiz/function/handle_answer/then_9.mcfunction +6 -0
  171. package/dist/data/quiz/function/handle_answer.mcfunction +11 -0
  172. package/dist/data/quiz/function/start_quiz.mcfunction +5 -0
  173. package/dist/data/reqtest/function/__load.mcfunction +4 -0
  174. package/dist/data/reqtest/function/_table_init.mcfunction +2 -0
  175. package/dist/data/reqtest/function/no_trig.mcfunction +3 -0
  176. package/dist/data/reqtest/function/use_table.mcfunction +4 -0
  177. package/dist/data/reqtest2/function/__load.mcfunction +3 -0
  178. package/dist/data/reqtest2/function/no_trig.mcfunction +3 -0
  179. package/dist/data/runtime/function/__load.mcfunction +5 -0
  180. package/dist/data/runtime/function/__tick.mcfunction +2 -0
  181. package/dist/data/runtime/function/counter_tick/then_0.mcfunction +3 -0
  182. package/dist/data/runtime/function/counter_tick.mcfunction +13 -0
  183. package/dist/data/shop/function/__load.mcfunction +7 -0
  184. package/dist/data/shop/function/__tick.mcfunction +3 -0
  185. package/dist/data/shop/function/__trigger_shop_buy_dispatch.mcfunction +4 -0
  186. package/dist/data/shop/function/complete_purchase/else_1.mcfunction +5 -0
  187. package/dist/data/shop/function/complete_purchase/else_4.mcfunction +5 -0
  188. package/dist/data/shop/function/complete_purchase/else_7.mcfunction +3 -0
  189. package/dist/data/shop/function/complete_purchase/merge_2.mcfunction +2 -0
  190. package/dist/data/shop/function/complete_purchase/merge_5.mcfunction +2 -0
  191. package/dist/data/shop/function/complete_purchase/merge_8.mcfunction +2 -0
  192. package/dist/data/shop/function/complete_purchase/then_0.mcfunction +4 -0
  193. package/dist/data/shop/function/complete_purchase/then_3.mcfunction +4 -0
  194. package/dist/data/shop/function/complete_purchase/then_6.mcfunction +4 -0
  195. package/dist/data/shop/function/complete_purchase.mcfunction +7 -0
  196. package/dist/data/shop/function/handle_shop_trigger.mcfunction +3 -0
  197. package/dist/data/swap_test/function/__load.mcfunction +3 -0
  198. package/dist/data/swap_test/function/gcd_old/loop_body_1.mcfunction +7 -0
  199. package/dist/data/swap_test/function/gcd_old/loop_check_0.mcfunction +5 -0
  200. package/dist/data/swap_test/function/gcd_old/loop_exit_2.mcfunction +3 -0
  201. package/dist/data/swap_test/function/gcd_old.mcfunction +8 -0
  202. package/dist/data/turret/function/__load.mcfunction +5 -0
  203. package/dist/data/turret/function/__tick.mcfunction +4 -0
  204. package/dist/data/turret/function/__trigger_deploy_turret_dispatch.mcfunction +4 -0
  205. package/dist/data/turret/function/deploy_turret.mcfunction +8 -0
  206. package/dist/data/turret/function/turret_tick/at_1.mcfunction +2 -0
  207. package/dist/data/turret/function/turret_tick/foreach_0.mcfunction +2 -0
  208. package/dist/data/turret/function/turret_tick/foreach_2.mcfunction +2 -0
  209. package/dist/data/turret/function/turret_tick/tick_body.mcfunction +3 -0
  210. package/dist/data/turret/function/turret_tick/tick_skip.mcfunction +1 -0
  211. package/dist/data/turret/function/turret_tick.mcfunction +5 -0
  212. package/dist/gcd2.map.json +15 -0
  213. package/dist/gcd3.map.json +17 -0
  214. package/dist/gcd_test.map.json +15 -0
  215. package/dist/index.js +20 -1
  216. package/dist/ir/types.d.ts +4 -0
  217. package/dist/isqrttest.map.json +15 -0
  218. package/dist/lexer/index.d.ts +1 -1
  219. package/dist/lexer/index.js +1 -0
  220. package/dist/lowering/index.d.ts +5 -0
  221. package/dist/lowering/index.js +154 -14
  222. package/dist/mathtest.map.json +6 -0
  223. package/dist/mypack.map.json +27 -0
  224. package/dist/optimizer/dce.js +21 -5
  225. package/dist/optimizer/passes.js +18 -6
  226. package/dist/optimizer/structure.js +7 -0
  227. package/dist/pack.mcmeta +6 -0
  228. package/dist/parser/index.d.ts +5 -0
  229. package/dist/parser/index.js +43 -2
  230. package/dist/reqtest.map.json +4 -0
  231. package/dist/reqtest2.map.json +4 -0
  232. package/dist/runtime/index.d.ts +6 -0
  233. package/dist/runtime/index.js +130 -9
  234. package/dist/runtime.map.json +7 -0
  235. package/dist/swap_test.map.json +14 -0
  236. package/editors/vscode/package-lock.json +3 -3
  237. package/editors/vscode/package.json +1 -1
  238. package/examples/showcase.mcrs +505 -0
  239. package/package.json +1 -1
  240. package/src/__tests__/cli.test.ts +1 -1
  241. package/src/__tests__/codegen.test.ts +12 -6
  242. package/src/__tests__/e2e.test.ts +6 -6
  243. package/src/__tests__/lowering.test.ts +8 -8
  244. package/src/__tests__/optimizer.test.ts +33 -0
  245. package/src/__tests__/stdlib-advanced.test.ts +379 -0
  246. package/src/__tests__/stdlib-bigint.test.ts +427 -0
  247. package/src/__tests__/stdlib-math.test.ts +374 -0
  248. package/src/__tests__/stdlib-vec.test.ts +259 -0
  249. package/src/ast/types.ts +11 -1
  250. package/src/codegen/mcfunction/index.ts +143 -19
  251. package/src/codegen/var-allocator.ts +29 -0
  252. package/src/compile.ts +72 -5
  253. package/src/index.ts +21 -1
  254. package/src/ir/types.ts +2 -0
  255. package/src/lexer/index.ts +2 -1
  256. package/src/lowering/index.ts +171 -14
  257. package/src/optimizer/dce.ts +22 -5
  258. package/src/optimizer/passes.ts +18 -5
  259. package/src/optimizer/structure.ts +6 -1
  260. package/src/parser/index.ts +47 -2
  261. package/src/runtime/index.ts +130 -10
  262. package/src/stdlib/advanced.mcrs +330 -0
  263. package/src/stdlib/bigint.mcrs +205 -0
  264. package/src/stdlib/math.mcrs +274 -21
  265. package/src/stdlib/vec.mcrs +246 -0
@@ -0,0 +1,15 @@
1
+ {
2
+ "$e": "n",
3
+ "$g": "p0",
4
+ "$j": "i",
5
+ "$k": "x",
6
+ "$p": "next",
7
+ "$s": "f",
8
+ "$u": "result",
9
+ "$a": "const:0",
10
+ "$b": "const:1",
11
+ "$c": "const:2",
12
+ "$d": "const:16",
13
+ "$f": "internal:p0",
14
+ "$i": "internal:ret"
15
+ }
@@ -5,7 +5,7 @@
5
5
  * Handles special cases like entity selectors vs decorators,
6
6
  * range literals, and raw commands.
7
7
  */
8
- export type TokenKind = 'fn' | 'let' | 'const' | 'if' | 'else' | 'while' | 'for' | 'foreach' | 'match' | 'return' | 'break' | 'continue' | 'as' | 'at' | 'in' | 'is' | 'struct' | 'impl' | 'enum' | 'trigger' | 'namespace' | 'execute' | 'run' | 'unless' | 'declare' | 'int' | 'bool' | 'float' | 'string' | 'void' | 'BlockPos' | 'true' | 'false' | 'selector' | 'decorator' | 'int_lit' | 'float_lit' | 'byte_lit' | 'short_lit' | 'long_lit' | 'double_lit' | 'string_lit' | 'f_string' | 'range_lit' | 'rel_coord' | 'local_coord' | '+' | '-' | '*' | '/' | '%' | '~' | '^' | '==' | '!=' | '<' | '<=' | '>' | '>=' | '&&' | '||' | '!' | '=' | '+=' | '-=' | '*=' | '/=' | '%=' | '{' | '}' | '(' | ')' | '[' | ']' | ',' | ';' | ':' | '::' | '->' | '=>' | '.' | 'ident' | 'mc_name' | 'raw_cmd' | 'eof';
8
+ export type TokenKind = 'fn' | 'let' | 'const' | 'if' | 'else' | 'while' | 'for' | 'foreach' | 'match' | 'return' | 'break' | 'continue' | 'as' | 'at' | 'in' | 'is' | 'struct' | 'impl' | 'enum' | 'trigger' | 'namespace' | 'module' | 'execute' | 'run' | 'unless' | 'declare' | 'int' | 'bool' | 'float' | 'string' | 'void' | 'BlockPos' | 'true' | 'false' | 'selector' | 'decorator' | 'int_lit' | 'float_lit' | 'byte_lit' | 'short_lit' | 'long_lit' | 'double_lit' | 'string_lit' | 'f_string' | 'range_lit' | 'rel_coord' | 'local_coord' | '+' | '-' | '*' | '/' | '%' | '~' | '^' | '==' | '!=' | '<' | '<=' | '>' | '>=' | '&&' | '||' | '!' | '=' | '+=' | '-=' | '*=' | '/=' | '%=' | '{' | '}' | '(' | ')' | '[' | ']' | ',' | ';' | ':' | '::' | '->' | '=>' | '.' | 'ident' | 'mc_name' | 'raw_cmd' | 'eof';
9
9
  export interface Token {
10
10
  kind: TokenKind;
11
11
  value: string;
@@ -34,6 +34,7 @@ const KEYWORDS = {
34
34
  enum: 'enum',
35
35
  trigger: 'trigger',
36
36
  namespace: 'namespace',
37
+ module: 'module',
37
38
  execute: 'execute',
38
39
  run: 'run',
39
40
  unless: 'unless',
@@ -23,6 +23,11 @@ export declare class Lowering {
23
23
  private implMethods;
24
24
  private specializedFunctions;
25
25
  private currentFn;
26
+ /** Unique IR variable name for a local variable, scoped to the current function.
27
+ * Prevents cross-function scoreboard slot collisions: $fn_x ≠ $gn_x.
28
+ * Only applies to user-defined locals/params; internal slots ($p0, $ret) are
29
+ * intentionally global (calling convention). */
30
+ private fnVar;
26
31
  private currentStdlibCallSite?;
27
32
  private foreachCounter;
28
33
  private lambdaCounter;
@@ -128,6 +128,9 @@ const BUILTINS = {
128
128
  setTimeout: () => null, // Special handling
129
129
  setInterval: () => null, // Special handling
130
130
  clearInterval: () => null, // Special handling
131
+ storage_get_int: () => null, // Special handling (dynamic NBT array read via macro)
132
+ storage_set_array: () => null, // Special handling (write literal NBT array to storage)
133
+ storage_set_int: () => null, // Special handling (dynamic NBT array write via macro)
131
134
  };
132
135
  function getSpan(node) {
133
136
  return node?.span;
@@ -196,6 +199,13 @@ function emitBlockPos(pos) {
196
199
  // Lowering Class
197
200
  // ---------------------------------------------------------------------------
198
201
  class Lowering {
202
+ /** Unique IR variable name for a local variable, scoped to the current function.
203
+ * Prevents cross-function scoreboard slot collisions: $fn_x ≠ $gn_x.
204
+ * Only applies to user-defined locals/params; internal slots ($p0, $ret) are
205
+ * intentionally global (calling convention). */
206
+ fnVar(name) {
207
+ return `$${this.currentFn}_${name}`;
208
+ }
199
209
  currentEntityContext() {
200
210
  return this.entityContextStack.length > 0
201
211
  ? this.entityContextStack[this.entityContextStack.length - 1]
@@ -330,12 +340,30 @@ class Lowering {
330
340
  }
331
341
  preScanExpr(expr, paramNames, macroParams) {
332
342
  if (expr.kind === 'call' && BUILTINS[expr.fn] !== undefined) {
333
- // All ident args to macro-aware builtins that are params → macro params
334
- for (const arg of expr.args) {
335
- if (arg.kind === 'ident' && paramNames.has(arg.name)) {
336
- macroParams.add(arg.name);
343
+ // Only trigger macro param detection for builtins that actually emit
344
+ // MC commands with $(param) inline in the current function body.
345
+ // Special-handled builtins (storage_get_int / storage_set_int / etc.) are
346
+ // declared as `() => null` — they create their own sub-functions for macro
347
+ // indirection and do NOT require the surrounding function to be a macro.
348
+ const handler = BUILTINS[expr.fn];
349
+ const isSpecialHandled = (() => {
350
+ try {
351
+ return handler() === null;
352
+ }
353
+ catch {
354
+ return false;
355
+ }
356
+ })();
357
+ if (!isSpecialHandled) {
358
+ for (const arg of expr.args) {
359
+ if (arg.kind === 'ident' && paramNames.has(arg.name)) {
360
+ macroParams.add(arg.name);
361
+ }
337
362
  }
338
363
  }
364
+ // Always recurse into args for nested calls/expressions
365
+ for (const arg of expr.args)
366
+ this.preScanExpr(arg, paramNames, macroParams);
339
367
  return;
340
368
  }
341
369
  // Recurse into sub-expressions for other call types
@@ -564,13 +592,13 @@ class Lowering {
564
592
  this.stringValues.set(param.name, '');
565
593
  continue;
566
594
  }
567
- this.varMap.set(param.name, `$${param.name}`);
595
+ this.varMap.set(param.name, this.fnVar(param.name));
568
596
  }
569
597
  }
570
598
  else {
571
599
  for (const param of runtimeParams) {
572
600
  const paramName = param.name;
573
- this.varMap.set(paramName, `$${paramName}`);
601
+ this.varMap.set(paramName, this.fnVar(paramName));
574
602
  this.varTypes.set(paramName, this.normalizeType(param.type));
575
603
  }
576
604
  }
@@ -581,18 +609,22 @@ class Lowering {
581
609
  }
582
610
  // Start entry block
583
611
  this.builder.startBlock('entry');
584
- // Copy params from $p0, $p1, ... to named variables
612
+ // Copy params from the parameter-passing slots to named local variables.
613
+ // Use { kind: 'param', index: i } so the codegen resolves to
614
+ // alloc.internal('p{i}') consistently in both mangle and no-mangle modes,
615
+ // avoiding the slot-collision between the internal register and a user variable
616
+ // named 'p0'/'p1' that occurred with { kind: 'var', name: '$p0' }.
585
617
  for (let i = 0; i < runtimeParams.length; i++) {
586
618
  const paramName = runtimeParams[i].name;
587
- const varName = `$${paramName}`;
588
- this.builder.emitAssign(varName, { kind: 'var', name: `$p${i}` });
619
+ const varName = this.fnVar(paramName);
620
+ this.builder.emitAssign(varName, { kind: 'param', index: i });
589
621
  }
590
622
  if (staticEventDec) {
591
623
  for (let i = 0; i < fn.params.length; i++) {
592
624
  const param = fn.params[i];
593
625
  const expected = eventParamSpecs[i];
594
626
  if (expected?.type.kind === 'named' && expected.type.name !== 'string') {
595
- this.builder.emitAssign(`$${param.name}`, { kind: 'const', value: 0 });
627
+ this.builder.emitAssign(this.fnVar(param.name), { kind: 'const', value: 0 });
596
628
  }
597
629
  }
598
630
  }
@@ -649,6 +681,22 @@ class Lowering {
649
681
  if (fn.decorators.some(d => d.name === 'load')) {
650
682
  irFn.isLoadInit = true;
651
683
  }
684
+ // @requires("dep_fn") — when this function is compiled in, dep_fn is also
685
+ // called from __load. The dep_fn itself does NOT need @load; it can be a
686
+ // private (_) function that only runs at load time when this fn is used.
687
+ const requiredLoads = [];
688
+ for (const d of fn.decorators) {
689
+ if (d.name === 'require_on_load') {
690
+ for (const arg of d.rawArgs ?? []) {
691
+ if (arg.kind === 'string') {
692
+ requiredLoads.push(arg.value);
693
+ }
694
+ }
695
+ }
696
+ }
697
+ if (requiredLoads.length > 0) {
698
+ irFn.requiredLoads = requiredLoads;
699
+ }
652
700
  // Handle tick rate counter if needed
653
701
  if (tickRate && tickRate > 1) {
654
702
  this.wrapWithTickRate(irFn, tickRate);
@@ -773,7 +821,7 @@ class Lowering {
773
821
  if (this.currentContext.binding === stmt.name) {
774
822
  throw new diagnostics_1.DiagnosticError('LoweringError', `Cannot redeclare foreach binding '${stmt.name}'`, stmt.span ?? { line: 0, col: 0 });
775
823
  }
776
- const varName = `$${stmt.name}`;
824
+ const varName = this.fnVar(stmt.name);
777
825
  this.varMap.set(stmt.name, varName);
778
826
  // Track variable type
779
827
  const declaredType = stmt.type ? this.normalizeType(stmt.type) : this.inferExprType(stmt.init);
@@ -1049,7 +1097,7 @@ class Lowering {
1049
1097
  this.builder.startBlock(exitLabel);
1050
1098
  }
1051
1099
  lowerForRangeStmt(stmt) {
1052
- const loopVar = `$${stmt.varName}`;
1100
+ const loopVar = this.fnVar(stmt.varName);
1053
1101
  const subFnName = `${this.currentFn}/__for_${this.foreachCounter++}`;
1054
1102
  // Initialize loop variable
1055
1103
  this.varMap.set(stmt.varName, loopVar);
@@ -1208,7 +1256,7 @@ class Lowering {
1208
1256
  return;
1209
1257
  }
1210
1258
  const arrayType = this.inferExprType(stmt.iterable);
1211
- const bindingVar = `$${stmt.binding}`;
1259
+ const bindingVar = this.fnVar(stmt.binding);
1212
1260
  const indexVar = this.builder.freshTemp();
1213
1261
  const lengthVar = this.builder.freshTemp();
1214
1262
  const condVar = this.builder.freshTemp();
@@ -2216,6 +2264,98 @@ class Lowering {
2216
2264
  this.builder.emitRaw(`execute store result score ${dst} rs run data get ${targetType} ${target} ${path} ${scale}`);
2217
2265
  return { kind: 'var', name: dst };
2218
2266
  }
2267
+ // storage_get_int(storage_ns, array_key, index) -> int
2268
+ // Reads one element from an NBT int-array stored in data storage.
2269
+ // storage_ns : e.g. "math:tables"
2270
+ // array_key : e.g. "sin"
2271
+ // index : integer index (const or runtime)
2272
+ //
2273
+ // Const index: execute store result score $dst rs run data get storage math:tables sin[N] 1
2274
+ // Runtime index: macro sub-function via rs:heap, mirrors readArrayElement.
2275
+ if (name === 'storage_get_int') {
2276
+ const storageNs = this.exprToString(args[0]); // "math:tables"
2277
+ const arrayKey = this.exprToString(args[1]); // "sin"
2278
+ const indexOperand = this.lowerExpr(args[2]);
2279
+ const dst = this.builder.freshTemp();
2280
+ if (indexOperand.kind === 'const') {
2281
+ this.builder.emitRaw(`execute store result score ${dst} rs run data get storage ${storageNs} ${arrayKey}[${indexOperand.value}] 1`);
2282
+ }
2283
+ else {
2284
+ // Runtime index: store the index into rs:heap under a unique key,
2285
+ // then call a macro sub-function that uses $(key) to index the array.
2286
+ const macroKey = `__sgi_${this.foreachCounter++}`;
2287
+ const subFnName = `${this.currentFn}/__sgi_${this.foreachCounter++}`;
2288
+ const indexVar = indexOperand.kind === 'var'
2289
+ ? indexOperand.name
2290
+ : this.operandToVar(indexOperand);
2291
+ this.builder.emitRaw(`execute store result storage rs:heap ${macroKey} int 1 run scoreboard players get ${indexVar} rs`);
2292
+ this.builder.emitRaw(`function ${this.namespace}:${subFnName} with storage rs:heap`);
2293
+ // Prefix \x01 is a sentinel for the MC macro '$' line-start marker.
2294
+ // We avoid using literal '$execute' here so the pre-alloc pass
2295
+ // doesn't mistakenly register 'execute' as a scoreboard variable.
2296
+ // Codegen replaces \x01 → '$' when emitting the mc function file.
2297
+ this.emitRawSubFunction(subFnName, `\x01execute store result score ${dst} rs run data get storage ${storageNs} ${arrayKey}[$(${macroKey})] 1`);
2298
+ }
2299
+ return { kind: 'var', name: dst };
2300
+ }
2301
+ // storage_set_array(storage_ns, array_key, nbt_array_literal)
2302
+ // Writes a literal NBT int array to data storage (used in @load for tables).
2303
+ // storage_set_array("math:tables", "sin", "[0, 17, 35, ...]")
2304
+ if (name === 'storage_set_array') {
2305
+ const storageNs = this.exprToString(args[0]);
2306
+ const arrayKey = this.exprToString(args[1]);
2307
+ const nbtLiteral = this.exprToString(args[2]);
2308
+ this.builder.emitRaw(`data modify storage ${storageNs} ${arrayKey} set value ${nbtLiteral}`);
2309
+ return { kind: 'const', value: 0 };
2310
+ }
2311
+ // storage_set_int(storage_ns, array_key, index, value) -> void
2312
+ // Writes one integer element into an NBT int-array stored in data storage.
2313
+ // storage_ns : e.g. "rs:bigint"
2314
+ // array_key : e.g. "a"
2315
+ // index : element index (const or runtime)
2316
+ // value : integer value to write (const or runtime)
2317
+ //
2318
+ // Const index + const value:
2319
+ // execute store result storage <ns> <key>[N] int 1 run scoreboard players set $const_V rs V
2320
+ // Runtime index or value: macro sub-function via rs:heap
2321
+ if (name === 'storage_set_int') {
2322
+ const storageNs = this.exprToString(args[0]);
2323
+ const arrayKey = this.exprToString(args[1]);
2324
+ const indexOperand = this.lowerExpr(args[2]);
2325
+ const valueOperand = this.lowerExpr(args[3]);
2326
+ if (indexOperand.kind === 'const') {
2327
+ // Static index — use execute store result to write to the fixed slot
2328
+ const valVar = valueOperand.kind === 'var'
2329
+ ? valueOperand.name
2330
+ : this.operandToVar(valueOperand);
2331
+ this.builder.emitRaw(`execute store result storage ${storageNs} ${arrayKey}[${indexOperand.value}] int 1 run scoreboard players get ${valVar} rs`);
2332
+ }
2333
+ else {
2334
+ // Runtime index: we need a macro sub-function.
2335
+ // Store index + value into rs:heap, call macro that does:
2336
+ // $data modify storage <ns> <key>[$(idx_key)] set value $(val_key)
2337
+ const macroIdxKey = `__ssi_i_${this.foreachCounter++}`;
2338
+ const macroValKey = `__ssi_v_${this.foreachCounter++}`; // kept to pin valVar in optimizer
2339
+ const subFnName = `${this.currentFn}/__ssi_${this.foreachCounter++}`;
2340
+ const indexVar = indexOperand.kind === 'var'
2341
+ ? indexOperand.name
2342
+ : this.operandToVar(indexOperand);
2343
+ const valVar = valueOperand.kind === 'var'
2344
+ ? valueOperand.name
2345
+ : this.operandToVar(valueOperand);
2346
+ this.builder.emitRaw(`execute store result storage rs:heap ${macroIdxKey} int 1 run scoreboard players get ${indexVar} rs`);
2347
+ // Pin valVar in the optimizer's read-set so the assignment is not dead-code-eliminated.
2348
+ // The value is stored to rs:heap but NOT used by the macro (the macro reads the scoreboard
2349
+ // slot directly to avoid the MC 'data modify set value $(n)' macro substitution bug).
2350
+ this.builder.emitRaw(`execute store result storage rs:heap ${macroValKey} int 1 run scoreboard players get ${valVar} rs`);
2351
+ this.builder.emitRaw(`function ${this.namespace}:${subFnName} with storage rs:heap`);
2352
+ // Use execute store result (not 'data modify set value $(val)') to avoid MC macro
2353
+ // substitution bugs with numeric values. The scoreboard slot ${valVar} is hardcoded
2354
+ // into the macro sub-function at compile time — only the array index is macro-substituted.
2355
+ this.emitRawSubFunction(subFnName, `\x01execute store result storage ${storageNs} ${arrayKey}[$(${macroIdxKey})] int 1 run scoreboard players get ${valVar} rs`);
2356
+ }
2357
+ return { kind: 'const', value: 0 };
2358
+ }
2219
2359
  // data_merge(target, nbt) — merge NBT into entity/block/storage
2220
2360
  // data_merge(@s, { Invisible: 1b, Silent: 1b })
2221
2361
  if (name === 'data_merge') {
@@ -3006,7 +3146,7 @@ class Lowering {
3006
3146
  const indexVar = index.kind === 'var' ? index.name : this.operandToVar(index);
3007
3147
  this.builder.emitRaw(`execute store result storage rs:heap ${macroKey} int 1 run scoreboard players get ${indexVar} rs`);
3008
3148
  this.builder.emitRaw(`function ${this.namespace}:${subFnName} with storage rs:heap`);
3009
- this.emitRawSubFunction(subFnName, `$execute store result score ${dst} rs run data get storage rs:heap ${arrayName}[$(${macroKey})]`);
3149
+ this.emitRawSubFunction(subFnName, `\x01execute store result score ${dst} rs run data get storage rs:heap ${arrayName}[$(${macroKey})]`);
3010
3150
  return { kind: 'var', name: dst };
3011
3151
  }
3012
3152
  emitRawSubFunction(name, ...commands) {
@@ -0,0 +1,6 @@
1
+ {
2
+ "$b": "x",
3
+ "$a": "const:0",
4
+ "$c": "internal:p0",
5
+ "$f": "internal:ret"
6
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "$o": "x",
3
+ "$r": "execute",
4
+ "$u": "y",
5
+ "$aa": "ax",
6
+ "$ad": "ay",
7
+ "$ai": "swapped",
8
+ "$ak": "tmp",
9
+ "$al": "lo",
10
+ "$am": "hi",
11
+ "$ar": "mid",
12
+ "$as": "t",
13
+ "$ax": "theta",
14
+ "$a": "const:0",
15
+ "$b": "const:1",
16
+ "$c": "const:2",
17
+ "$d": "const:45",
18
+ "$e": "const:90",
19
+ "$f": "const:180",
20
+ "$g": "const:270",
21
+ "$h": "const:360",
22
+ "$i": "const:1000",
23
+ "$j": "const:46340",
24
+ "$k": "internal:ret",
25
+ "$n": "internal:p0",
26
+ "$t": "internal:p1"
27
+ }
@@ -115,12 +115,17 @@ class DeadCodeEliminator {
115
115
  findEntryPoints(program) {
116
116
  const entries = new Set();
117
117
  for (const fn of program.declarations) {
118
- // All top-level functions are entry points (callable via /function)
119
- // Exception: functions starting with _ are considered private/internal
120
- if (!fn.name.startsWith('_')) {
121
- entries.add(fn.name);
118
+ // Library functions (from `module library;` or `librarySources`) are
119
+ // NOT MC entry points they're only kept if reachable from user code.
120
+ // Exception: decorators like @tick / @load / @on / @keep always force inclusion.
121
+ if (!fn.isLibraryFn) {
122
+ // All top-level non-library functions are entry points (callable via /function)
123
+ // Exception: functions starting with _ are considered private/internal
124
+ if (!fn.name.startsWith('_')) {
125
+ entries.add(fn.name);
126
+ }
122
127
  }
123
- // Decorated functions are always entry points (even if prefixed with _)
128
+ // Decorated functions are always entry points regardless of library mode or _ prefix
124
129
  if (fn.decorators.some(decorator => [
125
130
  'tick',
126
131
  'load',
@@ -148,6 +153,17 @@ class DeadCodeEliminator {
148
153
  }
149
154
  this.reachableFunctions.add(fnName);
150
155
  this.collectFunctionRefs(fn);
156
+ // @requires("dep") — when fn is reachable, its required dependencies are
157
+ // also pulled into the reachable set so they survive DCE.
158
+ for (const decorator of fn.decorators) {
159
+ if (decorator.name === 'require_on_load') {
160
+ for (const arg of decorator.rawArgs ?? []) {
161
+ if (arg.kind === 'string') {
162
+ this.markReachable(arg.value);
163
+ }
164
+ }
165
+ }
166
+ }
151
167
  }
152
168
  collectFunctionRefs(fn) {
153
169
  const scope = [fn.params.map(param => ({ id: `param:${fn.name}:${param.name}`, name: param.name }))];
@@ -93,32 +93,44 @@ function copyPropagation(fn) {
93
93
  return op;
94
94
  return copies.get(op.name) ?? op;
95
95
  }
96
+ /**
97
+ * Invalidate all copies that became stale because `written` was modified.
98
+ * When $y is overwritten, any mapping copies[$tmp] = $y is now stale:
99
+ * reading $tmp would return the OLD $y value via the copy, but $y now holds
100
+ * a different value. Remove both the direct entry (copies[$y]) and every
101
+ * reverse entry that points at $y.
102
+ */
103
+ function invalidate(written) {
104
+ copies.delete(written);
105
+ for (const [k, v] of copies) {
106
+ if (v.kind === 'var' && v.name === written)
107
+ copies.delete(k);
108
+ }
109
+ }
96
110
  const newInstrs = [];
97
111
  for (const instr of block.instrs) {
98
112
  switch (instr.op) {
99
113
  case 'assign': {
100
114
  const src = resolve(instr.src);
115
+ invalidate(instr.dst);
101
116
  // Only propagate scalars (var or const), not storage
102
117
  if (src.kind === 'var' || src.kind === 'const') {
103
118
  copies.set(instr.dst, src);
104
119
  }
105
- else {
106
- copies.delete(instr.dst);
107
- }
108
120
  newInstrs.push({ ...instr, src });
109
121
  break;
110
122
  }
111
123
  case 'binop':
112
- copies.delete(instr.dst);
124
+ invalidate(instr.dst);
113
125
  newInstrs.push({ ...instr, lhs: resolve(instr.lhs), rhs: resolve(instr.rhs) });
114
126
  break;
115
127
  case 'cmp':
116
- copies.delete(instr.dst);
128
+ invalidate(instr.dst);
117
129
  newInstrs.push({ ...instr, lhs: resolve(instr.lhs), rhs: resolve(instr.rhs) });
118
130
  break;
119
131
  case 'call':
120
132
  if (instr.dst)
121
- copies.delete(instr.dst);
133
+ invalidate(instr.dst);
122
134
  newInstrs.push({ ...instr, args: instr.args.map(resolve) });
123
135
  break;
124
136
  default:
@@ -21,6 +21,8 @@ function operandToScore(op) {
21
21
  return `${varRef(op.name)} ${OBJ}`;
22
22
  if (op.kind === 'const')
23
23
  return `$const_${op.value} ${OBJ}`;
24
+ if (op.kind === 'param')
25
+ return `$p${op.index} ${OBJ}`;
24
26
  throw new Error(`Cannot convert storage operand to score: ${op.path}`);
25
27
  }
26
28
  function emitInstr(instr, namespace) {
@@ -35,6 +37,11 @@ function emitInstr(instr, namespace) {
35
37
  cmd: `scoreboard players operation ${varRef(instr.dst)} ${OBJ} = ${varRef(instr.src.name)} ${OBJ}`,
36
38
  });
37
39
  }
40
+ else if (instr.src.kind === 'param') {
41
+ commands.push({
42
+ cmd: `scoreboard players operation ${varRef(instr.dst)} ${OBJ} = $p${instr.src.index} ${OBJ}`,
43
+ });
44
+ }
38
45
  else {
39
46
  commands.push({
40
47
  cmd: `execute store result score ${varRef(instr.dst)} ${OBJ} run data get storage ${instr.src.path}`,
@@ -0,0 +1,6 @@
1
+ {
2
+ "pack": {
3
+ "pack_format": 26,
4
+ "description": "mypack datapack — compiled by redscript"
5
+ }
6
+ }
@@ -11,6 +11,11 @@ export declare class Parser {
11
11
  private pos;
12
12
  private sourceLines;
13
13
  private filePath?;
14
+ /** Set to true once `module library;` is seen — all subsequent fn declarations
15
+ * will be marked isLibraryFn=true. When library sources are parsed via the
16
+ * `librarySources` compile option, each source is parsed by its own fresh
17
+ * Parser instance, so this flag never bleeds into user code. */
18
+ private inLibraryMode;
14
19
  constructor(tokens: Token[], source?: string, filePath?: string);
15
20
  private peek;
16
21
  private advance;
@@ -64,6 +64,11 @@ function computeIsSingle(raw) {
64
64
  class Parser {
65
65
  constructor(tokens, source, filePath) {
66
66
  this.pos = 0;
67
+ /** Set to true once `module library;` is seen — all subsequent fn declarations
68
+ * will be marked isLibraryFn=true. When library sources are parsed via the
69
+ * `librarySources` compile option, each source is parsed by its own fresh
70
+ * Parser instance, so this flag never bleeds into user code. */
71
+ this.inLibraryMode = false;
67
72
  this.tokens = tokens;
68
73
  this.sourceLines = source?.split('\n') ?? [];
69
74
  this.filePath = filePath;
@@ -135,6 +140,7 @@ class Parser {
135
140
  const implBlocks = [];
136
141
  const enums = [];
137
142
  const consts = [];
143
+ let isLibrary = false;
138
144
  // Check for namespace declaration
139
145
  if (this.check('namespace')) {
140
146
  this.advance();
@@ -142,6 +148,19 @@ class Parser {
142
148
  namespace = name.value;
143
149
  this.expect(';');
144
150
  }
151
+ // Check for module declaration: `module library;`
152
+ // Library-mode: all functions parsed from this point are marked isLibraryFn=true.
153
+ // When using the `librarySources` compile option, each library source is parsed
154
+ // by its own fresh Parser — so this flag never bleeds into user code.
155
+ if (this.check('module')) {
156
+ this.advance();
157
+ const modKind = this.expect('ident');
158
+ if (modKind.value === 'library') {
159
+ isLibrary = true;
160
+ this.inLibraryMode = true;
161
+ }
162
+ this.expect(';');
163
+ }
145
164
  // Parse struct and function declarations
146
165
  while (!this.check('eof')) {
147
166
  if (this.check('let')) {
@@ -168,7 +187,7 @@ class Parser {
168
187
  declarations.push(this.parseFnDecl());
169
188
  }
170
189
  }
171
- return { namespace, globals, declarations, structs, implBlocks, enums, consts };
190
+ return { namespace, globals, declarations, structs, implBlocks, enums, consts, isLibrary };
172
191
  }
173
192
  // -------------------------------------------------------------------------
174
193
  // Struct Declaration
@@ -267,7 +286,8 @@ class Parser {
267
286
  returnType = this.parseType();
268
287
  }
269
288
  const body = this.parseBlock();
270
- return this.withLoc({ name, params, returnType, decorators, body }, fnToken);
289
+ const fn = this.withLoc({ name, params, returnType, decorators, body, isLibraryFn: this.inLibraryMode || undefined }, fnToken);
290
+ return fn;
271
291
  }
272
292
  /** Parse a `declare fn name(params): returnType;` stub — no body, just discard. */
273
293
  parseDeclareStub() {
@@ -336,6 +356,27 @@ class Parser {
336
356
  return { name, args };
337
357
  }
338
358
  }
359
+ // @require_on_load(fn_name) — when this fn is used, fn_name is called from __load.
360
+ // Accepts bare identifiers (with optional leading _) or quoted strings.
361
+ if (name === 'require_on_load') {
362
+ const rawArgs = [];
363
+ for (const part of argsStr.split(',')) {
364
+ const trimmed = part.trim();
365
+ // Bare identifier: @require_on_load(_math_init)
366
+ const identMatch = trimmed.match(/^([A-Za-z_][A-Za-z0-9_]*)$/);
367
+ if (identMatch) {
368
+ rawArgs.push({ kind: 'string', value: identMatch[1] });
369
+ }
370
+ else {
371
+ // Quoted string fallback: @require_on_load("_math_init")
372
+ const strMatch = trimmed.match(/^"([^"]*)"$/);
373
+ if (strMatch) {
374
+ rawArgs.push({ kind: 'string', value: strMatch[1] });
375
+ }
376
+ }
377
+ }
378
+ return { name, rawArgs };
379
+ }
339
380
  // Handle key=value format (e.g., rate=20)
340
381
  for (const part of argsStr.split(',')) {
341
382
  const [key, val] = part.split('=').map(s => s.trim());
@@ -0,0 +1,4 @@
1
+ {
2
+ "$a": "const:42",
3
+ "$b": "internal:ret"
4
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "$a": "const:42",
3
+ "$b": "internal:ret"
4
+ }
@@ -43,6 +43,7 @@ export declare class MCRuntime {
43
43
  private entityIdCounter;
44
44
  private returnValue;
45
45
  private shouldReturn;
46
+ private currentMacroContext;
46
47
  constructor(namespace: string);
47
48
  loadDatapack(dir: string): void;
48
49
  loadFunction(name: string, lines: string[]): void;
@@ -56,8 +57,13 @@ export declare class MCRuntime {
56
57
  private execExecute;
57
58
  private parseNextSelector;
58
59
  private execFunctionCmd;
60
+ /** Expand MC macro placeholders: $(key) → value from currentMacroContext */
61
+ private expandMacro;
59
62
  private execData;
60
63
  private parseDataValue;
64
+ /** Return the whole storage compound at storagePath as a flat key→value map.
65
+ * Used by 'function ... with storage' to provide macro context. */
66
+ private getStorageCompound;
61
67
  private getStorageField;
62
68
  private setStorageField;
63
69
  private removeStorageField;