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
@@ -235,6 +235,8 @@ class MCRuntime {
235
235
  this.entityIdCounter = 0;
236
236
  // Flag to stop function execution (for return)
237
237
  this.shouldReturn = false;
238
+ // Current MC macro context: key → value (set by 'function ... with storage')
239
+ this.currentMacroContext = null;
238
240
  this.namespace = namespace;
239
241
  // Initialize default objective
240
242
  this.scoreboard.set('rs', new Map());
@@ -321,6 +323,12 @@ class MCRuntime {
321
323
  cmd = cmd.trim();
322
324
  if (!cmd || cmd.startsWith('#'))
323
325
  return true;
326
+ // MC macro command: line starts with '$'.
327
+ // Expand $(key) placeholders from currentMacroContext, then execute.
328
+ if (cmd.startsWith('$')) {
329
+ const expanded = this.expandMacro(cmd.slice(1));
330
+ return this.execCommand(expanded, executor);
331
+ }
324
332
  // Parse command
325
333
  if (cmd.startsWith('scoreboard ')) {
326
334
  return this.execScoreboard(cmd);
@@ -502,7 +510,12 @@ class MCRuntime {
502
510
  const value = storeTarget.type === 'result'
503
511
  ? (this.returnValue ?? (result ? 1 : 0))
504
512
  : (result ? 1 : 0);
505
- this.setScore(storeTarget.player, storeTarget.objective, value);
513
+ if ('storagePath' in storeTarget) {
514
+ this.setStorageField(storeTarget.storagePath, storeTarget.field, value);
515
+ }
516
+ else {
517
+ this.setScore(storeTarget.player, storeTarget.objective, value);
518
+ }
506
519
  }
507
520
  return result;
508
521
  }
@@ -577,15 +590,44 @@ class MCRuntime {
577
590
  // Handle 'unless score ...'
578
591
  if (rest.startsWith('unless score ')) {
579
592
  rest = rest.slice(13);
580
- const scoreParts = rest.match(/^(\S+)\s+(\S+)\s+matches\s+(\S+)(.*)$/);
581
- if (scoreParts) {
582
- const [, player, obj, rangeStr, remaining] = scoreParts;
593
+ // unless score <player> <obj> matches <range>
594
+ const matchesParts = rest.match(/^(\S+)\s+(\S+)\s+matches\s+(\S+)(.*)$/);
595
+ if (matchesParts) {
596
+ const [, player, obj, rangeStr, remaining] = matchesParts;
583
597
  const range = parseRange(rangeStr);
584
598
  const score = this.getScore(player, obj);
585
599
  condition = condition && !matchesRange(score, range);
586
600
  rest = remaining.trim();
587
601
  continue;
588
602
  }
603
+ // unless score <p1> <o1> <op> <p2> <o2>
604
+ const compareMatch = rest.match(/^(\S+)\s+(\S+)\s+([<>=]+)\s+(\S+)\s+(\S+)(.*)$/);
605
+ if (compareMatch) {
606
+ const [, p1, o1, op, p2, o2, remaining] = compareMatch;
607
+ const v1 = this.getScore(p1, o1);
608
+ const v2 = this.getScore(p2, o2);
609
+ let matches = false;
610
+ switch (op) {
611
+ case '=':
612
+ matches = v1 === v2;
613
+ break;
614
+ case '<':
615
+ matches = v1 < v2;
616
+ break;
617
+ case '<=':
618
+ matches = v1 <= v2;
619
+ break;
620
+ case '>':
621
+ matches = v1 > v2;
622
+ break;
623
+ case '>=':
624
+ matches = v1 >= v2;
625
+ break;
626
+ }
627
+ condition = condition && !matches; // unless = negate
628
+ rest = remaining.trim();
629
+ continue;
630
+ }
589
631
  }
590
632
  // Handle 'if entity <selector>'
591
633
  if (rest.startsWith('if entity ')) {
@@ -605,6 +647,28 @@ class MCRuntime {
605
647
  condition = condition && entities.length === 0;
606
648
  continue;
607
649
  }
650
+ // Handle 'store result storage <ns:path> <field>[<idx>] <type> <scale>' (array element)
651
+ if (rest.startsWith('store result storage ')) {
652
+ const sliced = rest.slice(21);
653
+ // Try array-index form first: <ns:path> <field>[<idx>] <type> <scale> <run-cmd>
654
+ // Use [^\[\s]+ for field (no brackets or spaces) so that \[ matches correctly.
655
+ const arrParts = sliced.match(/^(\S+)\s+([^\[\s]+)\[(\d+)\]\s+(\S+)\s+([\d.]+)\s+(.*)$/);
656
+ if (arrParts) {
657
+ const [, storagePath, field, indexStr, , , remaining] = arrParts;
658
+ storeTarget = { storagePath, field: `${field}[${indexStr}]`, type: 'result' };
659
+ rest = remaining.trim();
660
+ continue;
661
+ }
662
+ // Plain form: <ns:path> <field> <type> <scale> <run-cmd>
663
+ const storageParts = sliced.match(/^(\S+)\s+(\S+)\s+(\S+)\s+([\d.]+)\s+(.*)$/);
664
+ if (storageParts) {
665
+ const [, storagePath, field, , , remaining] = storageParts;
666
+ storeTarget = { storagePath, field, type: 'result' };
667
+ rest = remaining.trim();
668
+ continue;
669
+ }
670
+ rest = sliced;
671
+ }
608
672
  // Handle 'store result score <player> <obj>'
609
673
  if (rest.startsWith('store result score ')) {
610
674
  rest = rest.slice(19);
@@ -637,7 +701,12 @@ class MCRuntime {
637
701
  const value = storeTarget.type === 'result'
638
702
  ? (this.returnValue ?? (condition ? 1 : 0))
639
703
  : (condition ? 1 : 0);
640
- this.setScore(storeTarget.player, storeTarget.objective, value);
704
+ if ('storagePath' in storeTarget) {
705
+ this.setStorageField(storeTarget.storagePath, storeTarget.field, value);
706
+ }
707
+ else {
708
+ this.setScore(storeTarget.player, storeTarget.objective, value);
709
+ }
641
710
  }
642
711
  return condition;
643
712
  }
@@ -659,12 +728,35 @@ class MCRuntime {
659
728
  // Function Command
660
729
  // -------------------------------------------------------------------------
661
730
  execFunctionCmd(cmd, executor) {
662
- const fnName = cmd.slice(9).trim(); // remove 'function '
731
+ let fnRef = cmd.slice(9).trim(); // remove 'function '
732
+ // Handle 'function ns:name with storage ns:path' — MC macro calling convention.
733
+ // The called function may have $( ) placeholders that need to be expanded
734
+ // using the provided storage compound. We execute the function after
735
+ // expanding its macro context.
736
+ const withStorageMatch = fnRef.match(/^(\S+)\s+with\s+storage\s+(\S+)$/);
737
+ if (withStorageMatch) {
738
+ const [, actualFnName, storagePath] = withStorageMatch;
739
+ const macroContext = this.getStorageCompound(storagePath) ?? {};
740
+ const outerShouldReturn = this.shouldReturn;
741
+ const outerMacroCtx = this.currentMacroContext;
742
+ this.currentMacroContext = macroContext;
743
+ this.execFunction(actualFnName, executor);
744
+ this.currentMacroContext = outerMacroCtx;
745
+ this.shouldReturn = outerShouldReturn;
746
+ return true;
747
+ }
663
748
  const outerShouldReturn = this.shouldReturn;
664
- this.execFunction(fnName, executor);
749
+ this.execFunction(fnRef, executor);
665
750
  this.shouldReturn = outerShouldReturn;
666
751
  return true;
667
752
  }
753
+ /** Expand MC macro placeholders: $(key) → value from currentMacroContext */
754
+ expandMacro(cmd) {
755
+ return cmd.replace(/\$\(([^)]+)\)/g, (_, key) => {
756
+ const val = this.currentMacroContext?.[key];
757
+ return val !== undefined ? String(val) : `$(${key})`;
758
+ });
759
+ }
668
760
  // -------------------------------------------------------------------------
669
761
  // Data Commands
670
762
  // -------------------------------------------------------------------------
@@ -689,8 +781,29 @@ class MCRuntime {
689
781
  }
690
782
  return true;
691
783
  }
692
- // data get storage <ns:path> <field>
693
- const getMatch = cmd.match(/^data get storage (\S+) (\S+)$/);
784
+ // data modify storage <ns:path> <field>[<index>] set value <val> (array element write)
785
+ const setArrMatch = cmd.match(/^data modify storage (\S+) ([^\[\s]+)\[(\d+)\] set value (.+)$/);
786
+ if (setArrMatch) {
787
+ const [, storagePath, field, indexStr, valueStr] = setArrMatch;
788
+ const arr = this.getStorageField(storagePath, field);
789
+ const idx = parseInt(indexStr, 10);
790
+ if (Array.isArray(arr) && idx >= 0 && idx < arr.length) {
791
+ arr[idx] = this.parseDataValue(valueStr);
792
+ }
793
+ return true;
794
+ }
795
+ // data get storage <ns:path> <field>[<index>] [scale] (array element access)
796
+ const getArrMatch = cmd.match(/^data get storage (\S+) ([^\[\s]+)\[(\d+)\](?:\s+[\d.]+)?$/);
797
+ if (getArrMatch) {
798
+ const [, storagePath, field, indexStr] = getArrMatch;
799
+ const arr = this.getStorageField(storagePath, field);
800
+ const idx = parseInt(indexStr, 10);
801
+ const value = Array.isArray(arr) ? arr[idx] : undefined;
802
+ this.returnValue = typeof value === 'number' ? value : 0;
803
+ return true;
804
+ }
805
+ // data get storage <ns:path> <field> [scale]
806
+ const getMatch = cmd.match(/^data get storage (\S+) (\S+)(?:\s+[\d.]+)?$/);
694
807
  if (getMatch) {
695
808
  const [, storagePath, field] = getMatch;
696
809
  const value = this.getStorageField(storagePath, field);
@@ -736,6 +849,14 @@ class MCRuntime {
736
849
  return str;
737
850
  }
738
851
  }
852
+ /** Return the whole storage compound at storagePath as a flat key→value map.
853
+ * Used by 'function ... with storage' to provide macro context. */
854
+ getStorageCompound(storagePath) {
855
+ const data = this.storage.get(storagePath);
856
+ if (!data || typeof data !== 'object' || Array.isArray(data))
857
+ return null;
858
+ return data;
859
+ }
739
860
  getStorageField(storagePath, field) {
740
861
  const data = this.storage.get(storagePath) ?? {};
741
862
  const segments = this.parseStoragePath(field);
@@ -0,0 +1,7 @@
1
+ {
2
+ "$d": "ticks",
3
+ "$a": "const:0",
4
+ "$b": "const:1",
5
+ "$c": "const:100",
6
+ "$i": "internal:ret"
7
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "$b": "a",
3
+ "$d": "b",
4
+ "$f": "p0",
5
+ "$g": "p1",
6
+ "$h": "x",
7
+ "$i": "y",
8
+ "$k": "tmp",
9
+ "$n": "m",
10
+ "$a": "const:0",
11
+ "$c": "internal:p0",
12
+ "$e": "internal:p1",
13
+ "$m": "internal:ret"
14
+ }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "redscript-vscode",
3
- "version": "1.0.16",
3
+ "version": "1.0.31",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "redscript-vscode",
9
- "version": "1.0.16",
9
+ "version": "1.0.31",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "redscript": "file:../../"
@@ -23,7 +23,7 @@
23
23
  },
24
24
  "../..": {
25
25
  "name": "redscript-mc",
26
- "version": "1.2.25",
26
+ "version": "1.2.26",
27
27
  "license": "MIT",
28
28
  "bin": {
29
29
  "redscript": "dist/cli.js",
@@ -2,7 +2,7 @@
2
2
  "name": "redscript-vscode",
3
3
  "displayName": "RedScript for Minecraft",
4
4
  "description": "Syntax highlighting, error diagnostics, and language support for RedScript — a compiler targeting Minecraft Java Edition",
5
- "version": "1.0.16",
5
+ "version": "1.0.31",
6
6
  "publisher": "bkmashiro",
7
7
  "icon": "icon.png",
8
8
  "license": "MIT",