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
@@ -32,6 +32,8 @@ function operandToScore(op, alloc) {
32
32
  return `${alloc.alloc(op.name)} ${OBJ}`;
33
33
  if (op.kind === 'const')
34
34
  return `${alloc.constant(op.value)} ${OBJ}`;
35
+ if (op.kind === 'param')
36
+ return `${alloc.internal(`p${op.index}`)} ${OBJ}`;
35
37
  throw new Error(`Cannot convert storage operand to score: ${op.path}`);
36
38
  }
37
39
  // Collect all constants used in a function for pre-setup
@@ -79,6 +81,9 @@ function emitInstr(instr, ns, alloc) {
79
81
  else if (src.kind === 'var') {
80
82
  lines.push(`scoreboard players operation ${dst} ${OBJ} = ${alloc.alloc(src.name)} ${OBJ}`);
81
83
  }
84
+ else if (src.kind === 'param') {
85
+ lines.push(`scoreboard players operation ${dst} ${OBJ} = ${alloc.internal(`p${src.index}`)} ${OBJ}`);
86
+ }
82
87
  else {
83
88
  lines.push(`execute store result score ${dst} ${OBJ} run data get storage ${src.path}`);
84
89
  }
@@ -121,21 +126,42 @@ function emitInstr(instr, ns, alloc) {
121
126
  break;
122
127
  }
123
128
  case 'call': {
124
- // Push args as param fake players
129
+ // Push args into the internal parameter slots ($p0, $p1, ...).
130
+ // We emit the copy commands directly (not via emitInstr/alloc.alloc) to
131
+ // ensure the destination resolves to alloc.internal('p{i}') rather than
132
+ // alloc.alloc('p{i}') which would create a *different* user-var slot.
125
133
  for (let i = 0; i < instr.args.length; i++) {
126
- const paramName = alloc.internal(`p${i}`);
127
- lines.push(...emitInstr({ op: 'assign', dst: paramName, src: instr.args[i] }, ns, alloc));
134
+ const paramSlot = alloc.internal(`p${i}`);
135
+ const arg = instr.args[i];
136
+ if (arg.kind === 'const') {
137
+ lines.push(`scoreboard players set ${paramSlot} ${OBJ} ${arg.value}`);
138
+ }
139
+ else if (arg.kind === 'var') {
140
+ lines.push(`scoreboard players operation ${paramSlot} ${OBJ} = ${alloc.alloc(arg.name)} ${OBJ}`);
141
+ }
142
+ else if (arg.kind === 'param') {
143
+ lines.push(`scoreboard players operation ${paramSlot} ${OBJ} = ${alloc.internal(`p${arg.index}`)} ${OBJ}`);
144
+ }
145
+ // storage args are rare for call sites; fall through to no-op
128
146
  }
129
147
  lines.push(`function ${ns}:${instr.fn}`);
130
148
  if (instr.dst) {
131
- const retName = alloc.internal('ret');
132
- lines.push(`scoreboard players operation ${alloc.alloc(instr.dst)} ${OBJ} = ${retName} ${OBJ}`);
149
+ const retSlot = alloc.internal('ret');
150
+ lines.push(`scoreboard players operation ${alloc.alloc(instr.dst)} ${OBJ} = ${retSlot} ${OBJ}`);
133
151
  }
134
152
  break;
135
153
  }
136
- case 'raw':
137
- lines.push(instr.cmd);
154
+ case 'raw': {
155
+ // resolveRaw rewrites $var tokens that are registered in the allocator
156
+ // so that mangle=true mode produces correct mangled names instead of
157
+ // the raw IR names embedded by the lowering phase.
158
+ // \x01 is a sentinel for the MC macro line-start '$' (used by
159
+ // storage_get_int sub-functions). Replace it last, after resolveRaw,
160
+ // so '$execute' is never treated as a variable reference.
161
+ const rawResolved = alloc.resolveRaw(instr.cmd).replace(/^\x01/, '$');
162
+ lines.push(rawResolved);
138
163
  break;
164
+ }
139
165
  }
140
166
  return lines;
141
167
  }
@@ -157,17 +183,32 @@ function emitTerm(term, ns, fnName, alloc) {
157
183
  lines.push(`execute if score ${alloc.alloc(term.cond)} ${OBJ} matches 1.. run function ${ns}:${fnName}/${term.else_}`);
158
184
  break;
159
185
  case 'return': {
160
- const retName = alloc.internal('ret');
186
+ // Emit the copy to the shared return slot directly — do NOT go through
187
+ // emitInstr/alloc.alloc(retSlot) which would allocate a *user* var slot
188
+ // (different from the internal slot) and break mangle mode.
189
+ const retSlot = alloc.internal('ret');
161
190
  if (term.value) {
162
- lines.push(...emitInstr({ op: 'assign', dst: retName, src: term.value }, ns, alloc));
191
+ if (term.value.kind === 'const') {
192
+ lines.push(`scoreboard players set ${retSlot} ${OBJ} ${term.value.value}`);
193
+ }
194
+ else if (term.value.kind === 'var') {
195
+ lines.push(`scoreboard players operation ${retSlot} ${OBJ} = ${alloc.alloc(term.value.name)} ${OBJ}`);
196
+ }
197
+ else if (term.value.kind === 'param') {
198
+ lines.push(`scoreboard players operation ${retSlot} ${OBJ} = ${alloc.internal(`p${term.value.index}`)} ${OBJ}`);
199
+ }
163
200
  }
164
- // In MC 1.20+, use `return` command
201
+ // MC 1.20+: use `return` to propagate the value back to the caller's
202
+ // `execute store result … run function …` without an extra scoreboard read.
165
203
  if (term.value?.kind === 'const') {
166
204
  lines.push(`return ${term.value.value}`);
167
205
  }
168
206
  else if (term.value?.kind === 'var') {
169
207
  lines.push(`return run scoreboard players get ${alloc.alloc(term.value.name)} ${OBJ}`);
170
208
  }
209
+ else if (term.value?.kind === 'param') {
210
+ lines.push(`return run scoreboard players get ${alloc.internal(`p${term.value.index}`)} ${OBJ}`);
211
+ }
171
212
  break;
172
213
  }
173
214
  case 'tick_yield':
@@ -237,6 +278,62 @@ function countMcfunctionCommands(files) {
237
278
  .length;
238
279
  }, 0);
239
280
  }
281
+ // ---------------------------------------------------------------------------
282
+ // Pre-allocation helpers for the two-pass mangle strategy
283
+ // ---------------------------------------------------------------------------
284
+ /** Register every variable referenced in an instruction with the allocator. */
285
+ function preAllocInstr(instr, alloc) {
286
+ switch (instr.op) {
287
+ case 'assign':
288
+ alloc.alloc(instr.dst);
289
+ if (instr.src.kind === 'var')
290
+ alloc.alloc(instr.src.name);
291
+ break;
292
+ case 'binop':
293
+ alloc.alloc(instr.dst);
294
+ if (instr.lhs.kind === 'var')
295
+ alloc.alloc(instr.lhs.name);
296
+ if (instr.rhs.kind === 'var')
297
+ alloc.alloc(instr.rhs.name);
298
+ break;
299
+ case 'cmp':
300
+ alloc.alloc(instr.dst);
301
+ if (instr.lhs.kind === 'var')
302
+ alloc.alloc(instr.lhs.name);
303
+ if (instr.rhs.kind === 'var')
304
+ alloc.alloc(instr.rhs.name);
305
+ break;
306
+ case 'call':
307
+ for (const arg of instr.args) {
308
+ if (arg.kind === 'var')
309
+ alloc.alloc(arg.name);
310
+ }
311
+ if (instr.dst)
312
+ alloc.alloc(instr.dst);
313
+ break;
314
+ case 'raw':
315
+ // Scan for $varname tokens and pre-register each one
316
+ ;
317
+ instr.cmd.replace(/\$[A-Za-z_][A-Za-z0-9_]*/g, (tok) => {
318
+ alloc.alloc(tok);
319
+ return tok;
320
+ });
321
+ break;
322
+ }
323
+ }
324
+ /** Register every variable referenced in a terminator with the allocator. */
325
+ function preAllocTerm(term, alloc) {
326
+ switch (term.op) {
327
+ case 'jump_if':
328
+ case 'jump_unless':
329
+ alloc.alloc(term.cond);
330
+ break;
331
+ case 'return':
332
+ if (term.value?.kind === 'var')
333
+ alloc.alloc(term.value.name);
334
+ break;
335
+ }
336
+ }
240
337
  function generateDatapackWithStats(module, options = {}) {
241
338
  const { optimizeCommands = true, mangle = false } = options;
242
339
  const alloc = new var_allocator_1.VarAllocator(mangle);
@@ -316,6 +413,39 @@ function generateDatapackWithStats(module, options = {}) {
316
413
  if (allConsts.size > 0) {
317
414
  loadLines.push(...Array.from(allConsts).sort((a, b) => a - b).map(value => `scoreboard players set ${alloc.constant(value)} ${OBJ} ${value}`));
318
415
  }
416
+ // ─────────────────────────────────────────────────────────────────────────
417
+ // Pre-allocation pass (mangle mode only)
418
+ //
419
+ // When mangle=true, the codegen assigns sequential names ($a, $b, …) the
420
+ // FIRST time alloc.alloc() is called for a given variable. Raw IR commands
421
+ // embed variable names (e.g. "$_0") as plain strings; resolveRaw() can only
422
+ // substitute them if the name was already registered in the allocator.
423
+ //
424
+ // Problem: a freshTemp ($\_0) used in a `raw` instruction and then in the
425
+ // immediately following `assign` gets registered by the `assign` AFTER the
426
+ // `raw` has already been emitted — so resolveRaw sees an unknown name and
427
+ // passes it through verbatim ($\_0), while the assign emits a different
428
+ // mangled slot ($e). The two slots never meet and the value is lost.
429
+ //
430
+ // Fix: walk every instruction (and terminator) of every function in order
431
+ // and call alloc.alloc() for each variable reference. This registers all
432
+ // names — with the same sequential order the main emit pass will encounter
433
+ // them — so that resolveRaw() can always find the correct mangled name.
434
+ // ─────────────────────────────────────────────────────────────────────────
435
+ if (mangle) {
436
+ for (const fn of module.functions) {
437
+ // Register internals used by the calling convention
438
+ for (let i = 0; i < fn.params.length; i++)
439
+ alloc.internal(`p${i}`);
440
+ alloc.internal('ret');
441
+ for (const block of fn.blocks) {
442
+ for (const instr of block.instrs) {
443
+ preAllocInstr(instr, alloc);
444
+ }
445
+ preAllocTerm(block.term, alloc);
446
+ }
447
+ }
448
+ }
319
449
  // Generate each function
320
450
  for (const fn of module.functions) {
321
451
  // Entry block → <fn_name>.mcfunction
@@ -323,12 +453,9 @@ function generateDatapackWithStats(module, options = {}) {
323
453
  for (let i = 0; i < fn.blocks.length; i++) {
324
454
  const block = fn.blocks[i];
325
455
  const lines = [`# block: ${block.label}`];
326
- // Param setup in entry block
327
- if (i === 0) {
328
- for (let j = 0; j < fn.params.length; j++) {
329
- lines.push(`scoreboard players operation ${alloc.alloc(fn.params[j])} ${OBJ} = ${alloc.internal(`p${j}`)} ${OBJ}`);
330
- }
331
- }
456
+ // Param setup is now handled by the lowering IR itself via { kind: 'param' }
457
+ // operands, so we no longer need a separate codegen param-copy loop here.
458
+ // (Removing it prevents the double-assignment that caused mangle-mode collisions.)
332
459
  for (const instr of block.instrs) {
333
460
  lines.push(...emitInstr(instr, ns, alloc));
334
461
  }
@@ -344,11 +471,20 @@ function generateDatapackWithStats(module, options = {}) {
344
471
  files.push({ path: filePath, content: lines.join('\n') });
345
472
  }
346
473
  }
347
- // Call @load functions from __load
474
+ // Call @load functions and @requires-referenced load helpers from __load.
475
+ // We collect them in a set to deduplicate (multiple fns might @requires the same dep).
476
+ const loadCalls = new Set();
348
477
  for (const fn of module.functions) {
349
478
  if (fn.isLoadInit) {
350
- loadLines.push(`function ${ns}:${fn.name}`);
479
+ loadCalls.add(fn.name);
351
480
  }
481
+ // @requires: if this fn is compiled in, its required load-helpers must also run
482
+ for (const dep of fn.requiredLoads ?? []) {
483
+ loadCalls.add(dep);
484
+ }
485
+ }
486
+ for (const name of loadCalls) {
487
+ loadLines.push(`function ${ns}:${name}`);
352
488
  }
353
489
  // Write __load.mcfunction
354
490
  files.push({
@@ -15,6 +15,23 @@ export declare class VarAllocator {
15
15
  alloc(originalName: string): string;
16
16
  /** Allocate a name for a constant value (content-addressed). */
17
17
  constant(value: number): string;
18
+ /**
19
+ * Look up the allocated name for a raw scoreboard fake-player name such as
20
+ * "$_2", "$x", "$p0", or "$ret". Returns the mangled name when mangle=true,
21
+ * or the original name when mangle=false or the name is not yet known.
22
+ *
23
+ * Unlike alloc/internal/constant this does NOT create a new slot — it only
24
+ * resolves names that were already registered. Used by the codegen to
25
+ * rewrite variable references inside `raw` IR instructions.
26
+ */
27
+ resolve(rawName: string): string;
28
+ /**
29
+ * Rewrite all $varname tokens in a raw mcfunction command string so that
30
+ * IR variable names are replaced by their allocated (possibly mangled) names.
31
+ * Tokens that are not registered in the allocator are left untouched (they
32
+ * are literal scoreboard fake-player names like "out" or "#rs").
33
+ */
34
+ resolveRaw(cmd: string): string;
18
35
  /** Allocate a name for a compiler internal (e.g. "ret", "p0"). */
19
36
  internal(suffix: string): string;
20
37
  /** Generate the next sequential name: a, b, ..., z, aa, ab, ..., az, ba, ... */
@@ -34,6 +34,32 @@ class VarAllocator {
34
34
  this.constCache.set(value, name);
35
35
  return name;
36
36
  }
37
+ /**
38
+ * Look up the allocated name for a raw scoreboard fake-player name such as
39
+ * "$_2", "$x", "$p0", or "$ret". Returns the mangled name when mangle=true,
40
+ * or the original name when mangle=false or the name is not yet known.
41
+ *
42
+ * Unlike alloc/internal/constant this does NOT create a new slot — it only
43
+ * resolves names that were already registered. Used by the codegen to
44
+ * rewrite variable references inside `raw` IR instructions.
45
+ */
46
+ resolve(rawName) {
47
+ const clean = rawName.startsWith('$') ? rawName.slice(1) : rawName;
48
+ // Check every cache in priority order: vars, internals, consts
49
+ return (this.varCache.get(clean) ??
50
+ this.internalCache.get(clean) ??
51
+ rawName // not registered → return as-is (literal fake player, not a var)
52
+ );
53
+ }
54
+ /**
55
+ * Rewrite all $varname tokens in a raw mcfunction command string so that
56
+ * IR variable names are replaced by their allocated (possibly mangled) names.
57
+ * Tokens that are not registered in the allocator are left untouched (they
58
+ * are literal scoreboard fake-player names like "out" or "#rs").
59
+ */
60
+ resolveRaw(cmd) {
61
+ return cmd.replace(/\$[A-Za-z_][A-Za-z0-9_]*/g, (tok) => this.resolve(tok));
62
+ }
37
63
  /** Allocate a name for a compiler internal (e.g. "ret", "p0"). */
38
64
  internal(suffix) {
39
65
  const cached = this.internalCache.get(suffix);
package/dist/compile.d.ts CHANGED
@@ -12,6 +12,13 @@ export interface CompileOptions {
12
12
  filePath?: string;
13
13
  optimize?: boolean;
14
14
  dce?: boolean;
15
+ mangle?: boolean;
16
+ /** Additional source files that should be treated as *library* code.
17
+ * Functions in these files are DCE-eligible: they are only compiled into
18
+ * the datapack when actually called from user code. Each string is parsed
19
+ * independently (as if it had `module library;` at the top), so library
20
+ * mode never bleeds into the main `source`. */
21
+ librarySources?: string[];
15
22
  }
16
23
  export interface CompileResult {
17
24
  success: boolean;
@@ -29,6 +36,13 @@ export interface SourceRange {
29
36
  export interface PreprocessedSource {
30
37
  source: string;
31
38
  ranges: SourceRange[];
39
+ /** Imported files that declared `module library;` — parsed separately
40
+ * in library mode so their functions are DCE-eligible. Never concatenated
41
+ * into `source`. */
42
+ libraryImports?: Array<{
43
+ source: string;
44
+ filePath: string;
45
+ }>;
32
46
  }
33
47
  /**
34
48
  * Resolve a combined-source line number back to the original file and line.
package/dist/compile.js CHANGED
@@ -66,6 +66,17 @@ function resolveSourceLine(combinedLine, ranges, fallbackFile) {
66
66
  return { filePath: fallbackFile, line: combinedLine };
67
67
  }
68
68
  const IMPORT_RE = /^\s*import\s+"([^"]+)"\s*;?\s*$/;
69
+ /** Returns true if the source file declares `module library;` at its top
70
+ * (before any non-comment/non-blank lines). */
71
+ function isLibrarySource(source) {
72
+ for (const line of source.split('\n')) {
73
+ const trimmed = line.trim();
74
+ if (!trimmed || trimmed.startsWith('//'))
75
+ continue;
76
+ return /^module\s+library\s*;/.test(trimmed);
77
+ }
78
+ return false;
79
+ }
69
80
  function countLines(source) {
70
81
  return source === '' ? 0 : source.split('\n').length;
71
82
  }
@@ -84,6 +95,8 @@ function preprocessSourceWithMetadata(source, options = {}) {
84
95
  }
85
96
  const lines = source.split('\n');
86
97
  const imports = [];
98
+ /** Library imports: `module library;` files routed here instead of concatenated. */
99
+ const libraryImports = [];
87
100
  const bodyLines = [];
88
101
  let parsingHeader = true;
89
102
  for (let i = 0; i < lines.length; i++) {
@@ -104,7 +117,18 @@ function preprocessSourceWithMetadata(source, options = {}) {
104
117
  catch {
105
118
  throw new diagnostics_1.DiagnosticError('ParseError', `Cannot import '${match[1]}'`, { file: filePath, line: i + 1, col: 1 }, lines);
106
119
  }
107
- imports.push(preprocessSourceWithMetadata(importedSource, { filePath: importPath, seen }));
120
+ if (isLibrarySource(importedSource)) {
121
+ // Library file: parse separately so its functions are DCE-eligible.
122
+ // Also collect any transitive library imports inside it.
123
+ const nested = preprocessSourceWithMetadata(importedSource, { filePath: importPath, seen });
124
+ libraryImports.push({ source: importedSource, filePath: importPath });
125
+ // Propagate transitive library imports (e.g. math.mcrs imports vec.mcrs)
126
+ if (nested.libraryImports)
127
+ libraryImports.push(...nested.libraryImports);
128
+ }
129
+ else {
130
+ imports.push(preprocessSourceWithMetadata(importedSource, { filePath: importPath, seen }));
131
+ }
108
132
  }
109
133
  continue;
110
134
  }
@@ -131,7 +155,11 @@ function preprocessSourceWithMetadata(source, options = {}) {
131
155
  filePath: path.resolve(filePath),
132
156
  });
133
157
  }
134
- return { source: combined, ranges };
158
+ return {
159
+ source: combined,
160
+ ranges,
161
+ libraryImports: libraryImports.length > 0 ? libraryImports : undefined,
162
+ };
135
163
  }
136
164
  function preprocessSource(source, options = {}) {
137
165
  return preprocessSourceWithMetadata(source, options).source;
@@ -149,8 +177,36 @@ function compile(source, options = {}) {
149
177
  sourceLines = preprocessedSource.split('\n');
150
178
  // Lexing
151
179
  const tokens = new lexer_1.Lexer(preprocessedSource, filePath).tokenize();
152
- // Parsing
180
+ // Parsing — user source
153
181
  const parsedAst = new parser_1.Parser(tokens, preprocessedSource, filePath).parse(namespace);
182
+ // Collect all library sources: explicit `librarySources` option +
183
+ // auto-detected imports (files with `module library;` pulled out by the
184
+ // preprocessor rather than concatenated).
185
+ const allLibrarySources = [];
186
+ for (const libSrc of options.librarySources ?? []) {
187
+ allLibrarySources.push({ src: libSrc });
188
+ }
189
+ for (const li of preprocessed.libraryImports ?? []) {
190
+ allLibrarySources.push({ src: li.source, fp: li.filePath });
191
+ }
192
+ // Parse library sources independently (fresh Parser per source) so that
193
+ // `inLibraryMode` never bleeds into user code. All resulting functions get
194
+ // isLibraryFn=true (either via `module library;` in the source, or forced below).
195
+ for (const { src, fp } of allLibrarySources) {
196
+ const libPreprocessed = preprocessSourceWithMetadata(src, fp ? { filePath: fp } : {});
197
+ const libTokens = new lexer_1.Lexer(libPreprocessed.source, fp).tokenize();
198
+ const libAst = new parser_1.Parser(libTokens, libPreprocessed.source, fp).parse(namespace);
199
+ // Force all functions to library mode (even if source lacks `module library;`)
200
+ for (const fn of libAst.declarations)
201
+ fn.isLibraryFn = true;
202
+ // Merge into main AST
203
+ parsedAst.declarations.push(...libAst.declarations);
204
+ parsedAst.structs.push(...libAst.structs);
205
+ parsedAst.implBlocks.push(...libAst.implBlocks);
206
+ parsedAst.enums.push(...libAst.enums);
207
+ parsedAst.consts.push(...libAst.consts);
208
+ parsedAst.globals.push(...libAst.globals);
209
+ }
154
210
  const dceResult = shouldRunDce ? (0, dce_1.eliminateDeadCode)(parsedAst) : { program: parsedAst, warnings: [] };
155
211
  const ast = dceResult.program;
156
212
  // Lowering
@@ -159,8 +215,9 @@ function compile(source, options = {}) {
159
215
  const optimized = shouldOptimize
160
216
  ? { ...ir, functions: ir.functions.map(fn => (0, passes_1.optimize)(fn)) }
161
217
  : ir;
162
- // Code generation
163
- const generated = (0, mcfunction_1.generateDatapackWithStats)(optimized);
218
+ // Code generation — mangle=true by default to prevent cross-function
219
+ // scoreboard variable collisions in the global MC scoreboard namespace.
220
+ const generated = (0, mcfunction_1.generateDatapackWithStats)(optimized, { mangle: options.mangle ?? true });
164
221
  return {
165
222
  success: true,
166
223
  files: [...generated.files, ...generated.advancements],
@@ -0,0 +1,6 @@
1
+ # RedScript runtime init
2
+ scoreboard objectives add rs dummy
3
+ scoreboard players set $const_1 rs 1
4
+ scoreboard players set $const_200 rs 200
5
+ scoreboard players set $const_0 rs 0
6
+ scoreboard players set $const_0 rs 0
@@ -0,0 +1,2 @@
1
+ # RedScript tick dispatcher
2
+ function arena:arena_tick
@@ -0,0 +1,3 @@
1
+ # block: else_1
2
+ say Arena update: no PvP kills yet.
3
+ function arena:announce_leaders/merge_2
@@ -0,0 +1,3 @@
1
+ # block: then_0
2
+ scoreboard players operation $top_kills rs = $kills rs
3
+ function arena:announce_leaders/foreach_0/merge_2
@@ -0,0 +1,7 @@
1
+ # block: entry
2
+ execute store result score $t0 rs run scoreboard players get @s kills
3
+ scoreboard players operation $kills rs = $t0 rs
4
+ scoreboard players set $t1 rs 0
5
+ execute if score $t0 rs > $top_kills rs run scoreboard players set $t1 rs 1
6
+ execute if score $t1 rs matches 1.. run function arena:announce_leaders/foreach_0/then_0
7
+ execute if score $t1 rs matches ..0 run function arena:announce_leaders/foreach_0/merge_2
@@ -0,0 +1,4 @@
1
+ # block: then_0
2
+ tellraw @s {"text":"You are leading the arena right now."}
3
+ title @s title {"text":"Arena Leader"}
4
+ function arena:announce_leaders/foreach_1/merge_2
@@ -0,0 +1,6 @@
1
+ # block: entry
2
+ execute store result score $t0 rs run scoreboard players get @s kills
3
+ scoreboard players set $t1 rs 0
4
+ execute if score $t0 rs = $top_kills rs run scoreboard players set $t1 rs 1
5
+ execute if score $t1 rs matches 1.. run function arena:announce_leaders/foreach_1/then_0
6
+ execute if score $t1 rs matches ..0 run function arena:announce_leaders/foreach_1/merge_2
@@ -0,0 +1,4 @@
1
+ # block: then_0
2
+ say Arena update: current leader check complete.
3
+ execute as @a run function arena:announce_leaders/foreach_1
4
+ function arena:announce_leaders/merge_2
@@ -0,0 +1,6 @@
1
+ # block: entry
2
+ execute as @a run function arena:announce_leaders/foreach_0
3
+ scoreboard players set $t0 rs 0
4
+ execute if score $const_0 rs > $const_0 rs run scoreboard players set $t0 rs 1
5
+ execute if score $t0 rs matches 1.. run function arena:announce_leaders/then_0
6
+ execute if score $t0 rs matches ..0 run function arena:announce_leaders/else_1
@@ -0,0 +1 @@
1
+ # block: merge_2
@@ -0,0 +1,4 @@
1
+ # block: then_0
2
+ function arena:announce_leaders
3
+ scoreboard players operation $t5 rs = $ret rs
4
+ function arena:arena_tick/merge_2
@@ -0,0 +1,11 @@
1
+ # block: entry
2
+ execute store result score $t0 rs run scoreboard players get arena ticks
3
+ scoreboard players operation $t1 rs = $t0 rs
4
+ scoreboard players operation $t1 rs += $const_1 rs
5
+ execute store result score arena ticks run scoreboard players get $t2 rs
6
+ scoreboard players operation $t3 rs = $t1 rs
7
+ scoreboard players operation $t3 rs %= $const_200 rs
8
+ scoreboard players set $t4 rs 0
9
+ execute if score $t3 rs = $const_0 rs run scoreboard players set $t4 rs 1
10
+ execute if score $t4 rs matches 1.. run function arena:arena_tick/then_0
11
+ execute if score $t4 rs matches ..0 run function arena:arena_tick/merge_2
@@ -0,0 +1,5 @@
1
+ # RedScript runtime init
2
+ scoreboard objectives add rs dummy
3
+ scoreboard players set $const_1 rs 1
4
+ scoreboard players set $const_100 rs 100
5
+ scoreboard players set $const_0 rs 0
@@ -0,0 +1,2 @@
1
+ # RedScript tick dispatcher
2
+ function counter:counter_tick
@@ -0,0 +1 @@
1
+ # block: merge_2
@@ -0,0 +1,3 @@
1
+ # block: then_0
2
+ say Counter reached another 100 ticks
3
+ function counter:counter_tick/merge_2
@@ -0,0 +1,11 @@
1
+ # block: entry
2
+ execute store result score $t0 rs run scoreboard players get counter ticks
3
+ scoreboard players operation $t1 rs = $t0 rs
4
+ scoreboard players operation $t1 rs += $const_1 rs
5
+ execute store result score counter ticks run scoreboard players get $t2 rs
6
+ scoreboard players operation $t3 rs = $t1 rs
7
+ scoreboard players operation $t3 rs %= $const_100 rs
8
+ scoreboard players set $t4 rs 0
9
+ execute if score $t3 rs = $const_0 rs run scoreboard players set $t4 rs 1
10
+ execute if score $t4 rs matches 1.. run function counter:counter_tick/then_0
11
+ execute if score $t4 rs matches ..0 run function counter:counter_tick/merge_2
@@ -0,0 +1,3 @@
1
+ # RedScript runtime init
2
+ scoreboard objectives add rs dummy
3
+ scoreboard players set $a rs 0
@@ -0,0 +1,3 @@
1
+ # block: merge_2
2
+ scoreboard players operation $h rs = $b rs
3
+ return run scoreboard players get $b rs
@@ -0,0 +1,5 @@
1
+ # block: then_0
2
+ scoreboard players set $f rs 0
3
+ scoreboard players operation $f rs -= $b rs
4
+ scoreboard players operation $h rs = $f rs
5
+ return run scoreboard players get $f rs
@@ -0,0 +1,7 @@
1
+ # block: entry
2
+ scoreboard players operation $b rs = $c rs
3
+ scoreboard players operation $b rs = $d rs
4
+ scoreboard players set $e rs 0
5
+ execute if score $d rs < $a rs run scoreboard players set $e rs 1
6
+ execute if score $e rs matches 1.. run function gcd2:abs/then_0
7
+ execute if score $e rs matches ..0 run function gcd2:abs/merge_2
@@ -0,0 +1,7 @@
1
+ # block: loop_body_1
2
+ scoreboard players operation $r rs = $b rs
3
+ scoreboard players operation $r rs %= $p rs
4
+ scoreboard players operation $s rs = $r rs
5
+ scoreboard players operation $b rs = $p rs
6
+ scoreboard players operation $p rs = $r rs
7
+ function gcd2:gcd/loop_check_0
@@ -0,0 +1,5 @@
1
+ # block: loop_check_0
2
+ scoreboard players set $q rs 0
3
+ execute if score $p rs > $a rs run scoreboard players set $q rs 1
4
+ execute if score $q rs matches 1.. run function gcd2:gcd/loop_body_1
5
+ execute if score $q rs matches ..0 run function gcd2:gcd/loop_exit_2
@@ -0,0 +1,3 @@
1
+ # block: loop_exit_2
2
+ scoreboard players operation $h rs = $b rs
3
+ return run scoreboard players get $b rs
@@ -0,0 +1,14 @@
1
+ # block: entry
2
+ scoreboard players operation $i rs = $c rs
3
+ scoreboard players operation $j rs = $k rs
4
+ scoreboard players operation $i rs = $d rs
5
+ scoreboard players operation $j rs = $l rs
6
+ scoreboard players operation $m rs = $d rs
7
+ function gcd2:abs
8
+ scoreboard players operation $n rs = $g rs
9
+ scoreboard players operation $b rs = $n rs
10
+ scoreboard players operation $m rs = $l rs
11
+ function gcd2:abs
12
+ scoreboard players operation $o rs = $g rs
13
+ scoreboard players operation $p rs = $o rs
14
+ function gcd2:gcd/loop_check_0
@@ -0,0 +1,3 @@
1
+ # RedScript runtime init
2
+ scoreboard objectives add rs dummy
3
+ scoreboard players set $a rs 0
@@ -0,0 +1,3 @@
1
+ # block: merge_2
2
+ scoreboard players operation $h rs = $b rs
3
+ return run scoreboard players get $b rs
@@ -0,0 +1,5 @@
1
+ # block: then_0
2
+ scoreboard players set $f rs 0
3
+ scoreboard players operation $f rs -= $b rs
4
+ scoreboard players operation $h rs = $f rs
5
+ return run scoreboard players get $f rs