redscript-mc 1.2.29 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (274) hide show
  1. package/.claude/commands/build-test.md +10 -0
  2. package/.claude/commands/deploy-demo.md +12 -0
  3. package/.claude/commands/stage-status.md +13 -0
  4. package/.claude/settings.json +12 -0
  5. package/.github/workflows/ci.yml +1 -0
  6. package/CLAUDE.md +231 -0
  7. package/README.md +29 -28
  8. package/README.zh.md +28 -28
  9. package/demo.gif +0 -0
  10. package/dist/cli.js +2 -554
  11. package/dist/compile.js +2 -266
  12. package/dist/index.js +2 -159
  13. package/dist/lexer/index.js +9 -1
  14. package/dist/lowering/index.js +22 -5
  15. package/dist/src/__tests__/cli.test.d.ts +1 -0
  16. package/dist/src/__tests__/cli.test.js +104 -0
  17. package/dist/src/__tests__/codegen.test.d.ts +1 -0
  18. package/dist/src/__tests__/codegen.test.js +152 -0
  19. package/dist/src/__tests__/compile-all.test.d.ts +10 -0
  20. package/dist/src/__tests__/compile-all.test.js +108 -0
  21. package/dist/src/__tests__/dce.test.d.ts +1 -0
  22. package/dist/src/__tests__/dce.test.js +102 -0
  23. package/dist/src/__tests__/diagnostics.test.d.ts +4 -0
  24. package/dist/src/__tests__/diagnostics.test.js +177 -0
  25. package/dist/src/__tests__/e2e.test.d.ts +6 -0
  26. package/dist/src/__tests__/e2e.test.js +1789 -0
  27. package/dist/src/__tests__/entity-types.test.d.ts +1 -0
  28. package/dist/src/__tests__/entity-types.test.js +203 -0
  29. package/dist/src/__tests__/formatter.test.d.ts +1 -0
  30. package/dist/src/__tests__/formatter.test.js +40 -0
  31. package/dist/src/__tests__/lexer.test.d.ts +1 -0
  32. package/dist/src/__tests__/lexer.test.js +343 -0
  33. package/dist/src/__tests__/lowering.test.d.ts +1 -0
  34. package/dist/src/__tests__/lowering.test.js +1015 -0
  35. package/dist/src/__tests__/macro.test.d.ts +8 -0
  36. package/dist/src/__tests__/macro.test.js +306 -0
  37. package/dist/src/__tests__/mc-integration.test.d.ts +12 -0
  38. package/dist/src/__tests__/mc-integration.test.js +817 -0
  39. package/dist/src/__tests__/mc-syntax.test.d.ts +1 -0
  40. package/dist/src/__tests__/mc-syntax.test.js +124 -0
  41. package/dist/src/__tests__/nbt.test.d.ts +1 -0
  42. package/dist/src/__tests__/nbt.test.js +82 -0
  43. package/dist/src/__tests__/optimizer-advanced.test.d.ts +1 -0
  44. package/dist/src/__tests__/optimizer-advanced.test.js +124 -0
  45. package/dist/src/__tests__/optimizer.test.d.ts +1 -0
  46. package/dist/src/__tests__/optimizer.test.js +149 -0
  47. package/dist/src/__tests__/parser.test.d.ts +1 -0
  48. package/dist/src/__tests__/parser.test.js +807 -0
  49. package/dist/src/__tests__/repl.test.d.ts +1 -0
  50. package/dist/src/__tests__/repl.test.js +27 -0
  51. package/dist/src/__tests__/runtime.test.d.ts +1 -0
  52. package/dist/src/__tests__/runtime.test.js +289 -0
  53. package/dist/src/__tests__/stdlib-advanced.test.d.ts +4 -0
  54. package/dist/src/__tests__/stdlib-advanced.test.js +374 -0
  55. package/dist/src/__tests__/stdlib-bigint.test.d.ts +7 -0
  56. package/dist/src/__tests__/stdlib-bigint.test.js +426 -0
  57. package/dist/src/__tests__/stdlib-math.test.d.ts +7 -0
  58. package/dist/src/__tests__/stdlib-math.test.js +351 -0
  59. package/dist/src/__tests__/stdlib-vec.test.d.ts +4 -0
  60. package/dist/src/__tests__/stdlib-vec.test.js +263 -0
  61. package/dist/src/__tests__/structure-optimizer.test.d.ts +1 -0
  62. package/dist/src/__tests__/structure-optimizer.test.js +33 -0
  63. package/dist/src/__tests__/typechecker.test.d.ts +1 -0
  64. package/dist/src/__tests__/typechecker.test.js +552 -0
  65. package/dist/src/__tests__/var-allocator.test.d.ts +1 -0
  66. package/dist/src/__tests__/var-allocator.test.js +69 -0
  67. package/dist/src/ast/types.d.ts +515 -0
  68. package/dist/src/ast/types.js +9 -0
  69. package/dist/src/builtins/metadata.d.ts +36 -0
  70. package/dist/src/builtins/metadata.js +1014 -0
  71. package/dist/src/cli.d.ts +11 -0
  72. package/dist/src/cli.js +443 -0
  73. package/dist/src/codegen/cmdblock/index.d.ts +26 -0
  74. package/dist/src/codegen/cmdblock/index.js +45 -0
  75. package/dist/src/codegen/mcfunction/index.d.ts +40 -0
  76. package/dist/src/codegen/mcfunction/index.js +606 -0
  77. package/dist/src/codegen/structure/index.d.ts +24 -0
  78. package/dist/src/codegen/structure/index.js +279 -0
  79. package/dist/src/codegen/var-allocator.d.ts +45 -0
  80. package/dist/src/codegen/var-allocator.js +104 -0
  81. package/dist/src/compile.d.ts +37 -0
  82. package/dist/src/compile.js +165 -0
  83. package/dist/src/diagnostics/index.d.ts +44 -0
  84. package/dist/src/diagnostics/index.js +140 -0
  85. package/dist/src/events/types.d.ts +35 -0
  86. package/dist/src/events/types.js +59 -0
  87. package/dist/src/formatter/index.d.ts +1 -0
  88. package/dist/src/formatter/index.js +26 -0
  89. package/dist/src/index.d.ts +22 -0
  90. package/dist/src/index.js +45 -0
  91. package/dist/src/ir/builder.d.ts +33 -0
  92. package/dist/src/ir/builder.js +99 -0
  93. package/dist/src/ir/types.d.ts +132 -0
  94. package/dist/src/ir/types.js +15 -0
  95. package/dist/src/lexer/index.d.ts +37 -0
  96. package/dist/src/lexer/index.js +569 -0
  97. package/dist/src/lowering/index.d.ts +188 -0
  98. package/dist/src/lowering/index.js +3405 -0
  99. package/dist/src/mc-test/client.d.ts +128 -0
  100. package/dist/src/mc-test/client.js +174 -0
  101. package/dist/src/mc-test/runner.d.ts +28 -0
  102. package/dist/src/mc-test/runner.js +151 -0
  103. package/dist/src/mc-test/setup.d.ts +11 -0
  104. package/dist/src/mc-test/setup.js +98 -0
  105. package/dist/src/mc-validator/index.d.ts +17 -0
  106. package/dist/src/mc-validator/index.js +322 -0
  107. package/dist/src/nbt/index.d.ts +86 -0
  108. package/dist/src/nbt/index.js +250 -0
  109. package/dist/src/optimizer/commands.d.ts +38 -0
  110. package/dist/src/optimizer/commands.js +451 -0
  111. package/dist/src/optimizer/dce.d.ts +34 -0
  112. package/dist/src/optimizer/dce.js +639 -0
  113. package/dist/src/optimizer/passes.d.ts +34 -0
  114. package/dist/src/optimizer/passes.js +243 -0
  115. package/dist/src/optimizer/structure.d.ts +9 -0
  116. package/dist/src/optimizer/structure.js +356 -0
  117. package/dist/src/parser/index.d.ts +93 -0
  118. package/dist/src/parser/index.js +1687 -0
  119. package/dist/src/repl.d.ts +16 -0
  120. package/dist/src/repl.js +165 -0
  121. package/dist/src/runtime/index.d.ts +107 -0
  122. package/dist/src/runtime/index.js +1409 -0
  123. package/dist/src/typechecker/index.d.ts +61 -0
  124. package/dist/src/typechecker/index.js +1034 -0
  125. package/dist/src/types/entity-hierarchy.d.ts +29 -0
  126. package/dist/src/types/entity-hierarchy.js +107 -0
  127. package/dist/src2/__tests__/e2e/basic.test.d.ts +8 -0
  128. package/dist/src2/__tests__/e2e/basic.test.js +140 -0
  129. package/dist/src2/__tests__/e2e/macros.test.d.ts +9 -0
  130. package/dist/src2/__tests__/e2e/macros.test.js +182 -0
  131. package/dist/src2/__tests__/e2e/migrate.test.d.ts +13 -0
  132. package/dist/src2/__tests__/e2e/migrate.test.js +2739 -0
  133. package/dist/src2/__tests__/hir/desugar.test.d.ts +1 -0
  134. package/dist/src2/__tests__/hir/desugar.test.js +234 -0
  135. package/dist/src2/__tests__/lir/lower.test.d.ts +1 -0
  136. package/dist/src2/__tests__/lir/lower.test.js +559 -0
  137. package/dist/src2/__tests__/lir/types.test.d.ts +1 -0
  138. package/dist/src2/__tests__/lir/types.test.js +185 -0
  139. package/dist/src2/__tests__/lir/verify.test.d.ts +1 -0
  140. package/dist/src2/__tests__/lir/verify.test.js +221 -0
  141. package/dist/src2/__tests__/mir/arithmetic.test.d.ts +1 -0
  142. package/dist/src2/__tests__/mir/arithmetic.test.js +130 -0
  143. package/dist/src2/__tests__/mir/control-flow.test.d.ts +1 -0
  144. package/dist/src2/__tests__/mir/control-flow.test.js +205 -0
  145. package/dist/src2/__tests__/mir/verify.test.d.ts +1 -0
  146. package/dist/src2/__tests__/mir/verify.test.js +223 -0
  147. package/dist/src2/__tests__/optimizer/block_merge.test.d.ts +1 -0
  148. package/dist/src2/__tests__/optimizer/block_merge.test.js +78 -0
  149. package/dist/src2/__tests__/optimizer/branch_simplify.test.d.ts +1 -0
  150. package/dist/src2/__tests__/optimizer/branch_simplify.test.js +58 -0
  151. package/dist/src2/__tests__/optimizer/constant_fold.test.d.ts +1 -0
  152. package/dist/src2/__tests__/optimizer/constant_fold.test.js +131 -0
  153. package/dist/src2/__tests__/optimizer/copy_prop.test.d.ts +1 -0
  154. package/dist/src2/__tests__/optimizer/copy_prop.test.js +91 -0
  155. package/dist/src2/__tests__/optimizer/dce.test.d.ts +1 -0
  156. package/dist/src2/__tests__/optimizer/dce.test.js +76 -0
  157. package/dist/src2/__tests__/optimizer/pipeline.test.d.ts +1 -0
  158. package/dist/src2/__tests__/optimizer/pipeline.test.js +102 -0
  159. package/dist/src2/emit/compile.d.ts +19 -0
  160. package/dist/src2/emit/compile.js +80 -0
  161. package/dist/src2/emit/index.d.ts +17 -0
  162. package/dist/src2/emit/index.js +172 -0
  163. package/dist/src2/hir/lower.d.ts +15 -0
  164. package/dist/src2/hir/lower.js +378 -0
  165. package/dist/src2/hir/types.d.ts +373 -0
  166. package/dist/src2/hir/types.js +16 -0
  167. package/dist/src2/lir/lower.d.ts +15 -0
  168. package/dist/src2/lir/lower.js +453 -0
  169. package/dist/src2/lir/types.d.ts +136 -0
  170. package/dist/src2/lir/types.js +11 -0
  171. package/dist/src2/lir/verify.d.ts +14 -0
  172. package/dist/src2/lir/verify.js +113 -0
  173. package/dist/src2/mir/lower.d.ts +9 -0
  174. package/dist/src2/mir/lower.js +1030 -0
  175. package/dist/src2/mir/macro.d.ts +22 -0
  176. package/dist/src2/mir/macro.js +168 -0
  177. package/dist/src2/mir/types.d.ts +183 -0
  178. package/dist/src2/mir/types.js +11 -0
  179. package/dist/src2/mir/verify.d.ts +16 -0
  180. package/dist/src2/mir/verify.js +216 -0
  181. package/dist/src2/optimizer/block_merge.d.ts +12 -0
  182. package/dist/src2/optimizer/block_merge.js +84 -0
  183. package/dist/src2/optimizer/branch_simplify.d.ts +9 -0
  184. package/dist/src2/optimizer/branch_simplify.js +28 -0
  185. package/dist/src2/optimizer/constant_fold.d.ts +10 -0
  186. package/dist/src2/optimizer/constant_fold.js +85 -0
  187. package/dist/src2/optimizer/copy_prop.d.ts +9 -0
  188. package/dist/src2/optimizer/copy_prop.js +113 -0
  189. package/dist/src2/optimizer/dce.d.ts +8 -0
  190. package/dist/src2/optimizer/dce.js +155 -0
  191. package/dist/src2/optimizer/pipeline.d.ts +10 -0
  192. package/dist/src2/optimizer/pipeline.js +42 -0
  193. package/dist/tsconfig.tsbuildinfo +1 -0
  194. package/docs/compiler-pipeline-redesign.md +2243 -0
  195. package/docs/optimization-ideas.md +1076 -0
  196. package/editors/vscode/package-lock.json +3 -3
  197. package/editors/vscode/package.json +1 -1
  198. package/examples/readme-demo.mcrs +44 -66
  199. package/jest.config.js +1 -1
  200. package/package.json +6 -5
  201. package/scripts/postbuild.js +15 -0
  202. package/src/__tests__/cli.test.ts +8 -220
  203. package/src/__tests__/dce.test.ts +11 -56
  204. package/src/__tests__/diagnostics.test.ts +59 -38
  205. package/src/__tests__/mc-integration.test.ts +1 -2
  206. package/src/ast/types.ts +6 -1
  207. package/src/cli.ts +29 -156
  208. package/src/compile.ts +6 -162
  209. package/src/index.ts +14 -178
  210. package/src/lexer/index.ts +9 -1
  211. package/src/mc-test/runner.ts +4 -3
  212. package/src/parser/index.ts +1 -1
  213. package/src/repl.ts +1 -1
  214. package/src/runtime/index.ts +1 -1
  215. package/src2/__tests__/e2e/basic.test.ts +154 -0
  216. package/src2/__tests__/e2e/macros.test.ts +199 -0
  217. package/src2/__tests__/e2e/migrate.test.ts +3008 -0
  218. package/src2/__tests__/hir/desugar.test.ts +263 -0
  219. package/src2/__tests__/lir/lower.test.ts +619 -0
  220. package/src2/__tests__/lir/types.test.ts +207 -0
  221. package/src2/__tests__/lir/verify.test.ts +249 -0
  222. package/src2/__tests__/mir/arithmetic.test.ts +156 -0
  223. package/src2/__tests__/mir/control-flow.test.ts +242 -0
  224. package/src2/__tests__/mir/verify.test.ts +254 -0
  225. package/src2/__tests__/optimizer/block_merge.test.ts +84 -0
  226. package/src2/__tests__/optimizer/branch_simplify.test.ts +64 -0
  227. package/src2/__tests__/optimizer/constant_fold.test.ts +145 -0
  228. package/src2/__tests__/optimizer/copy_prop.test.ts +99 -0
  229. package/src2/__tests__/optimizer/dce.test.ts +83 -0
  230. package/src2/__tests__/optimizer/pipeline.test.ts +116 -0
  231. package/src2/emit/compile.ts +99 -0
  232. package/src2/emit/index.ts +222 -0
  233. package/src2/hir/lower.ts +428 -0
  234. package/src2/hir/types.ts +216 -0
  235. package/src2/lir/lower.ts +556 -0
  236. package/src2/lir/types.ts +109 -0
  237. package/src2/lir/verify.ts +129 -0
  238. package/src2/mir/lower.ts +1160 -0
  239. package/src2/mir/macro.ts +167 -0
  240. package/src2/mir/types.ts +106 -0
  241. package/src2/mir/verify.ts +218 -0
  242. package/src2/optimizer/block_merge.ts +93 -0
  243. package/src2/optimizer/branch_simplify.ts +27 -0
  244. package/src2/optimizer/constant_fold.ts +88 -0
  245. package/src2/optimizer/copy_prop.ts +106 -0
  246. package/src2/optimizer/dce.ts +133 -0
  247. package/src2/optimizer/pipeline.ts +44 -0
  248. package/tsconfig.json +2 -2
  249. package/src/__tests__/codegen.test.ts +0 -161
  250. package/src/__tests__/e2e.test.ts +0 -2039
  251. package/src/__tests__/entity-types.test.ts +0 -236
  252. package/src/__tests__/lowering.test.ts +0 -1185
  253. package/src/__tests__/macro.test.ts +0 -343
  254. package/src/__tests__/nbt.test.ts +0 -58
  255. package/src/__tests__/optimizer-advanced.test.ts +0 -144
  256. package/src/__tests__/optimizer.test.ts +0 -162
  257. package/src/__tests__/runtime.test.ts +0 -305
  258. package/src/__tests__/stdlib-advanced.test.ts +0 -379
  259. package/src/__tests__/stdlib-bigint.test.ts +0 -427
  260. package/src/__tests__/stdlib-math.test.ts +0 -374
  261. package/src/__tests__/stdlib-vec.test.ts +0 -259
  262. package/src/__tests__/structure-optimizer.test.ts +0 -38
  263. package/src/__tests__/var-allocator.test.ts +0 -75
  264. package/src/codegen/cmdblock/index.ts +0 -63
  265. package/src/codegen/mcfunction/index.ts +0 -662
  266. package/src/codegen/structure/index.ts +0 -346
  267. package/src/codegen/var-allocator.ts +0 -104
  268. package/src/ir/builder.ts +0 -116
  269. package/src/ir/types.ts +0 -134
  270. package/src/lowering/index.ts +0 -3860
  271. package/src/optimizer/commands.ts +0 -534
  272. package/src/optimizer/dce.ts +0 -679
  273. package/src/optimizer/passes.ts +0 -250
  274. package/src/optimizer/structure.ts +0 -450
@@ -0,0 +1,453 @@
1
+ "use strict";
2
+ /**
3
+ * MIR → LIR Lowering — Stage 5 of the RedScript compiler pipeline.
4
+ *
5
+ * Converts 3-address MIR (CFG with basic blocks) to 2-address LIR
6
+ * (flat instruction lists with MC scoreboard semantics).
7
+ *
8
+ * Key transformations:
9
+ * - Each MIR Temp → a Slot (player = $tempname, obj = module.objective)
10
+ * - 3-address arithmetic → score_copy dst←a, then score_op dst←b
11
+ * - CFG control flow → call_if_matches / call_unless_matches to extracted functions
12
+ * - MIR calls → parameter slot setup + call instruction
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.lowerToLIR = lowerToLIR;
16
+ // ---------------------------------------------------------------------------
17
+ // Public API
18
+ // ---------------------------------------------------------------------------
19
+ function lowerToLIR(mir) {
20
+ const ctx = new LoweringContext(mir.namespace, mir.objective);
21
+ for (const fn of mir.functions) {
22
+ lowerFunction(fn, ctx);
23
+ }
24
+ return {
25
+ functions: ctx.functions,
26
+ namespace: mir.namespace,
27
+ objective: mir.objective,
28
+ };
29
+ }
30
+ // ---------------------------------------------------------------------------
31
+ // Lowering context
32
+ // ---------------------------------------------------------------------------
33
+ class LoweringContext {
34
+ constructor(namespace, objective) {
35
+ this.functions = [];
36
+ /** Track which blocks have multiple predecessors (need their own function) */
37
+ this.multiPredBlocks = new Set();
38
+ /** Map block id → generated LIR function name for multi-pred blocks */
39
+ this.blockFnNames = new Map();
40
+ /** Current MIR function being lowered */
41
+ this.currentMIRFn = null;
42
+ /** Block map for quick lookup */
43
+ this.blockMap = new Map();
44
+ this.namespace = namespace;
45
+ this.objective = objective;
46
+ }
47
+ slot(temp) {
48
+ // Return field temps are global (shared between caller/callee)
49
+ if (temp.startsWith('__rf_')) {
50
+ return { player: `$ret_${temp.slice(5)}`, obj: this.objective };
51
+ }
52
+ // Prefix temp names with function name to avoid caller/callee collision
53
+ const fn = this.currentMIRFn;
54
+ const prefix = fn ? fn.name : '';
55
+ return { player: `$${prefix}_${temp}`, obj: this.objective };
56
+ }
57
+ qualifiedName(fnName) {
58
+ // Convert :: to / and lowercase for MC function paths
59
+ const mcName = fnName.replace(/::/g, '/').toLowerCase();
60
+ return `${this.namespace}:${mcName}`;
61
+ }
62
+ addFunction(fn) {
63
+ this.functions.push(fn);
64
+ }
65
+ analyzeBlocks(fn) {
66
+ this.currentMIRFn = fn;
67
+ this.multiPredBlocks.clear();
68
+ this.blockFnNames.clear();
69
+ this.blockMap.clear();
70
+ for (const block of fn.blocks) {
71
+ this.blockMap.set(block.id, block);
72
+ }
73
+ // Count predecessors for each block
74
+ const predCount = new Map();
75
+ for (const block of fn.blocks) {
76
+ const targets = getTermTargets(block.term);
77
+ for (const target of targets) {
78
+ predCount.set(target, (predCount.get(target) || 0) + 1);
79
+ }
80
+ }
81
+ // Blocks with >1 predecessors or that are branch targets need their own function
82
+ for (const [blockId, count] of predCount) {
83
+ if (count > 1 && blockId !== fn.entry) {
84
+ this.multiPredBlocks.add(blockId);
85
+ this.blockFnNames.set(blockId, `${fn.name}__${blockId}`);
86
+ }
87
+ }
88
+ }
89
+ isMultiPred(blockId) {
90
+ return this.multiPredBlocks.has(blockId);
91
+ }
92
+ getBlockFnName(blockId) {
93
+ const name = this.blockFnNames.get(blockId);
94
+ if (name)
95
+ return name;
96
+ // Generate one on demand
97
+ const generated = `${this.currentMIRFn.name}__${blockId}`;
98
+ this.blockFnNames.set(blockId, generated);
99
+ return generated;
100
+ }
101
+ getBlock(id) {
102
+ return this.blockMap.get(id);
103
+ }
104
+ }
105
+ // ---------------------------------------------------------------------------
106
+ // Function lowering
107
+ // ---------------------------------------------------------------------------
108
+ function lowerFunction(fn, ctx) {
109
+ ctx.analyzeBlocks(fn);
110
+ // Lower the entry block as the main function body
111
+ const instrs = [];
112
+ const visited = new Set();
113
+ // Copy parameter slots ($p0, $p1, ...) into the callee's temp slots
114
+ for (let i = 0; i < fn.params.length; i++) {
115
+ const paramSlot = { player: `$p${i}`, obj: ctx.objective };
116
+ const tempSlot = ctx.slot(fn.params[i].name);
117
+ instrs.push({ kind: 'score_copy', dst: tempSlot, src: paramSlot });
118
+ }
119
+ lowerBlock(fn.entry, fn, ctx, instrs, visited);
120
+ ctx.addFunction({
121
+ name: fn.name,
122
+ instructions: instrs,
123
+ isMacro: fn.isMacro,
124
+ macroParams: fn.params.filter(p => p.isMacroParam).map(p => p.name),
125
+ });
126
+ // Emit separate functions for multi-pred blocks
127
+ for (const blockId of ctx['multiPredBlocks']) {
128
+ if (!visited.has(blockId)) {
129
+ const blockInstrs = [];
130
+ const blockVisited = new Set();
131
+ lowerBlock(blockId, fn, ctx, blockInstrs, blockVisited);
132
+ ctx.addFunction({
133
+ name: ctx.getBlockFnName(blockId),
134
+ instructions: blockInstrs,
135
+ isMacro: false,
136
+ macroParams: [],
137
+ });
138
+ }
139
+ }
140
+ }
141
+ function lowerBlock(blockId, fn, ctx, instrs, visited) {
142
+ if (visited.has(blockId))
143
+ return;
144
+ visited.add(blockId);
145
+ const block = ctx.getBlock(blockId);
146
+ if (!block)
147
+ return;
148
+ // Lower all non-terminator instructions
149
+ for (const instr of block.instrs) {
150
+ lowerInstr(instr, fn, ctx, instrs);
151
+ }
152
+ // Lower the terminator
153
+ lowerTerminator(block.term, fn, ctx, instrs, visited);
154
+ }
155
+ // ---------------------------------------------------------------------------
156
+ // Instruction lowering
157
+ // ---------------------------------------------------------------------------
158
+ function lowerInstr(instr, fn, ctx, instrs) {
159
+ switch (instr.kind) {
160
+ case 'const': {
161
+ instrs.push({ kind: 'score_set', dst: ctx.slot(instr.dst), value: instr.value });
162
+ break;
163
+ }
164
+ case 'copy': {
165
+ lowerOperandToSlot(instr.dst, instr.src, ctx, instrs);
166
+ break;
167
+ }
168
+ case 'add':
169
+ case 'sub':
170
+ case 'mul':
171
+ case 'div':
172
+ case 'mod': {
173
+ // 3-address → 2-address: copy a to dst, then op dst with b
174
+ lowerOperandToSlot(instr.dst, instr.a, ctx, instrs);
175
+ const scoreOp = {
176
+ add: 'score_add',
177
+ sub: 'score_sub',
178
+ mul: 'score_mul',
179
+ div: 'score_div',
180
+ mod: 'score_mod',
181
+ };
182
+ lowerBinOp(instr.dst, instr.b, scoreOp[instr.kind], ctx, instrs);
183
+ break;
184
+ }
185
+ case 'neg': {
186
+ // 0 - src: set tmp to 0, then subtract src
187
+ const dst = ctx.slot(instr.dst);
188
+ instrs.push({ kind: 'score_set', dst, value: 0 });
189
+ const srcSlot = operandToSlot(instr.src, ctx, instrs);
190
+ instrs.push({ kind: 'score_sub', dst, src: srcSlot });
191
+ break;
192
+ }
193
+ case 'cmp': {
194
+ // Strategy: set dst=0, then conditionally set to 1
195
+ // MC pattern: execute if score $a <op> $b run scoreboard players set $dst 1
196
+ const dst = ctx.slot(instr.dst);
197
+ const aSlot = operandToSlot(instr.a, ctx, instrs);
198
+ const bSlot = operandToSlot(instr.b, ctx, instrs);
199
+ instrs.push({ kind: 'score_set', dst, value: 0 });
200
+ const cmpOps = {
201
+ eq: '=', ne: '=', lt: '<', le: '<=', gt: '>', ge: '>=',
202
+ };
203
+ const op = cmpOps[instr.op];
204
+ const guard = instr.op === 'ne' ? 'unless' : 'if';
205
+ const dstStr = `${dst.player} ${dst.obj}`;
206
+ const aStr = `${aSlot.player} ${aSlot.obj}`;
207
+ const bStr = `${bSlot.player} ${bSlot.obj}`;
208
+ instrs.push({
209
+ kind: 'raw',
210
+ cmd: `execute ${guard} score ${aStr} ${op} ${bStr} run scoreboard players set ${dstStr} 1`,
211
+ });
212
+ break;
213
+ }
214
+ case 'and': {
215
+ // Bitwise/logical AND: both are 0/1, so multiply works
216
+ // But more accurately: dst = (a != 0) && (b != 0)
217
+ // Simple approach: copy a, then score_mul with b (since both are 0/1)
218
+ lowerOperandToSlot(instr.dst, instr.a, ctx, instrs);
219
+ lowerBinOp(instr.dst, instr.b, 'score_mul', ctx, instrs);
220
+ break;
221
+ }
222
+ case 'or': {
223
+ // OR for 0/1 values: add then clamp to 1
224
+ // dst = a + b; if dst > 1, dst = 1
225
+ const dst = ctx.slot(instr.dst);
226
+ lowerOperandToSlot(instr.dst, instr.a, ctx, instrs);
227
+ lowerBinOp(instr.dst, instr.b, 'score_add', ctx, instrs);
228
+ // Clamp: use score_min with a const slot set to 1
229
+ const oneSlot = constSlot(1, ctx, instrs);
230
+ instrs.push({ kind: 'score_min', dst, src: oneSlot });
231
+ break;
232
+ }
233
+ case 'not': {
234
+ // NOT for 0/1: dst = 1 - src
235
+ const dst = ctx.slot(instr.dst);
236
+ instrs.push({ kind: 'score_set', dst, value: 1 });
237
+ const srcSlot = operandToSlot(instr.src, ctx, instrs);
238
+ instrs.push({ kind: 'score_sub', dst, src: srcSlot });
239
+ break;
240
+ }
241
+ case 'nbt_read': {
242
+ const dst = ctx.slot(instr.dst);
243
+ instrs.push({
244
+ kind: 'store_nbt_to_score',
245
+ dst,
246
+ ns: instr.ns,
247
+ path: instr.path,
248
+ scale: instr.scale,
249
+ });
250
+ break;
251
+ }
252
+ case 'nbt_write': {
253
+ const srcSlot = operandToSlot(instr.src, ctx, instrs);
254
+ instrs.push({
255
+ kind: 'store_score_to_nbt',
256
+ ns: instr.ns,
257
+ path: instr.path,
258
+ type: instr.type,
259
+ scale: instr.scale,
260
+ src: srcSlot,
261
+ });
262
+ break;
263
+ }
264
+ case 'call': {
265
+ // Set parameter slots $p0, $p1, ...
266
+ for (let i = 0; i < instr.args.length; i++) {
267
+ const paramSlot = { player: `$p${i}`, obj: ctx.objective };
268
+ lowerOperandToSlotDirect(paramSlot, instr.args[i], ctx, instrs);
269
+ }
270
+ // Handle raw commands embedded in call
271
+ if (instr.fn.startsWith('__raw:')) {
272
+ const cmd = instr.fn.slice(6);
273
+ if (cmd.startsWith('\x01')) {
274
+ // Macro sentinel → emit as macro_line ($ prefix added by emit)
275
+ instrs.push({ kind: 'macro_line', template: cmd.slice(1) });
276
+ }
277
+ else {
278
+ instrs.push({ kind: 'raw', cmd });
279
+ }
280
+ }
281
+ else {
282
+ instrs.push({ kind: 'call', fn: ctx.qualifiedName(instr.fn) });
283
+ }
284
+ // Copy return value to dst if needed
285
+ if (instr.dst) {
286
+ const retSlot = { player: '$ret', obj: ctx.objective };
287
+ instrs.push({ kind: 'score_copy', dst: ctx.slot(instr.dst), src: retSlot });
288
+ }
289
+ break;
290
+ }
291
+ case 'call_macro': {
292
+ const macroStorage = `rs:macro_args`;
293
+ // Store each arg to NBT
294
+ for (const arg of instr.args) {
295
+ const srcSlot = operandToSlot(arg.value, ctx, instrs);
296
+ instrs.push({
297
+ kind: 'store_score_to_nbt',
298
+ ns: 'rs:macro_args',
299
+ path: arg.name,
300
+ type: arg.type,
301
+ scale: arg.scale,
302
+ src: srcSlot,
303
+ });
304
+ }
305
+ instrs.push({ kind: 'call_macro', fn: ctx.qualifiedName(instr.fn), storage: macroStorage });
306
+ // Copy return value to dst if needed
307
+ if (instr.dst) {
308
+ const retSlot = { player: '$ret', obj: ctx.objective };
309
+ instrs.push({ kind: 'score_copy', dst: ctx.slot(instr.dst), src: retSlot });
310
+ }
311
+ break;
312
+ }
313
+ case 'call_context': {
314
+ instrs.push({
315
+ kind: 'call_context',
316
+ fn: ctx.qualifiedName(instr.fn),
317
+ subcommands: instr.subcommands,
318
+ });
319
+ break;
320
+ }
321
+ default:
322
+ break;
323
+ }
324
+ }
325
+ // ---------------------------------------------------------------------------
326
+ // Terminator lowering
327
+ // ---------------------------------------------------------------------------
328
+ function lowerTerminator(term, fn, ctx, instrs, visited) {
329
+ switch (term.kind) {
330
+ case 'return': {
331
+ if (term.value) {
332
+ const retSlot = { player: '$ret', obj: ctx.objective };
333
+ const srcSlot = operandToSlot(term.value, ctx, instrs);
334
+ instrs.push({ kind: 'return_value', slot: srcSlot });
335
+ }
336
+ break;
337
+ }
338
+ case 'jump': {
339
+ if (ctx.isMultiPred(term.target)) {
340
+ // Target has multiple predecessors — call the extracted function
341
+ instrs.push({ kind: 'call', fn: ctx.qualifiedName(ctx.getBlockFnName(term.target)) });
342
+ }
343
+ else {
344
+ // Inline the target block's instructions
345
+ lowerBlock(term.target, fn, ctx, instrs, visited);
346
+ }
347
+ break;
348
+ }
349
+ case 'branch': {
350
+ const condSlot = operandToSlot(term.cond, ctx, instrs);
351
+ // Then branch: use `return run function` to atomically call and exit.
352
+ // This prevents fallthrough to the else-branch even when recursive calls
353
+ // (e.g. continue → loop header → loop body) clobber the condition slot.
354
+ const thenFnName = emitBranchTarget(term.then, fn, ctx, visited);
355
+ instrs.push({
356
+ kind: 'raw',
357
+ cmd: `execute if score ${condSlot.player} ${condSlot.obj} matches 1 run return run function ${ctx.qualifiedName(thenFnName)}`,
358
+ });
359
+ // Else branch: if we reach here, cond was not 1 (the then-path returned).
360
+ const elseFnName = emitBranchTarget(term.else, fn, ctx, visited);
361
+ instrs.push({ kind: 'call', fn: ctx.qualifiedName(elseFnName) });
362
+ break;
363
+ }
364
+ }
365
+ }
366
+ /**
367
+ * Emit a branch target as a separate LIR function and return its name.
368
+ * If the target is already a multi-pred block with a function, reuse it.
369
+ */
370
+ function emitBranchTarget(blockId, fn, ctx, parentVisited) {
371
+ // If already has a function (multi-pred), return its name
372
+ if (ctx.isMultiPred(blockId)) {
373
+ // Make sure the block gets emitted
374
+ if (!parentVisited.has(blockId)) {
375
+ const blockInstrs = [];
376
+ const blockVisited = new Set();
377
+ lowerBlock(blockId, fn, ctx, blockInstrs, blockVisited);
378
+ ctx.addFunction({
379
+ name: ctx.getBlockFnName(blockId),
380
+ instructions: blockInstrs,
381
+ isMacro: false,
382
+ macroParams: [],
383
+ });
384
+ parentVisited.add(blockId);
385
+ }
386
+ return ctx.getBlockFnName(blockId);
387
+ }
388
+ // Create a new function for this branch target
389
+ const branchFnName = ctx.getBlockFnName(blockId);
390
+ const blockInstrs = [];
391
+ const blockVisited = new Set();
392
+ lowerBlock(blockId, fn, ctx, blockInstrs, blockVisited);
393
+ ctx.addFunction({
394
+ name: branchFnName,
395
+ instructions: blockInstrs,
396
+ isMacro: false,
397
+ macroParams: [],
398
+ });
399
+ // Mark visited so parent doesn't re-inline
400
+ parentVisited.add(blockId);
401
+ return branchFnName;
402
+ }
403
+ // ---------------------------------------------------------------------------
404
+ // Helpers
405
+ // ---------------------------------------------------------------------------
406
+ /** Lower an operand into a named temp slot (copy const or score_copy) */
407
+ function lowerOperandToSlot(dstTemp, src, ctx, instrs) {
408
+ const dst = ctx.slot(dstTemp);
409
+ if (src.kind === 'const') {
410
+ instrs.push({ kind: 'score_set', dst, value: src.value });
411
+ }
412
+ else {
413
+ instrs.push({ kind: 'score_copy', dst, src: ctx.slot(src.name) });
414
+ }
415
+ }
416
+ /** Lower an operand into a specific slot (not by temp name) */
417
+ function lowerOperandToSlotDirect(dst, src, ctx, instrs) {
418
+ if (src.kind === 'const') {
419
+ instrs.push({ kind: 'score_set', dst, value: src.value });
420
+ }
421
+ else {
422
+ instrs.push({ kind: 'score_copy', dst, src: ctx.slot(src.name) });
423
+ }
424
+ }
425
+ /** Get a slot for an operand, emitting a score_set for constants into a temp */
426
+ function operandToSlot(op, ctx, instrs) {
427
+ if (op.kind === 'temp') {
428
+ return ctx.slot(op.name);
429
+ }
430
+ // Constant → need a temporary slot
431
+ return constSlot(op.value, ctx, instrs);
432
+ }
433
+ /** Create a constant slot with a given value */
434
+ function constSlot(value, ctx, instrs) {
435
+ const slot = { player: `$__const_${value}`, obj: ctx.objective };
436
+ instrs.push({ kind: 'score_set', dst: slot, value });
437
+ return slot;
438
+ }
439
+ /** Apply a binary score operation: dst op= src */
440
+ function lowerBinOp(dstTemp, b, scoreKind, ctx, instrs) {
441
+ const dst = ctx.slot(dstTemp);
442
+ const srcSlot = operandToSlot(b, ctx, instrs);
443
+ instrs.push({ kind: scoreKind, dst, src: srcSlot });
444
+ }
445
+ function getTermTargets(term) {
446
+ switch (term.kind) {
447
+ case 'jump': return [term.target];
448
+ case 'branch': return [term.then, term.else];
449
+ case 'return': return [];
450
+ default: return [];
451
+ }
452
+ }
453
+ //# sourceMappingURL=lower.js.map
@@ -0,0 +1,136 @@
1
+ /**
2
+ * LIR (Low-level IR) Types — Stage 5 of the RedScript compiler pipeline.
3
+ *
4
+ * LIR is 2-address, MC-specific, typed nodes — no raw strings.
5
+ * Each LIR instruction maps 1:1 (or near) to one MC command.
6
+ *
7
+ * Spec: docs/compiler-pipeline-redesign.md § "LIR Instruction Set"
8
+ */
9
+ import type { CmpOp, NBTType, ExecuteSubcmd } from '../mir/types';
10
+ export interface Slot {
11
+ player: string;
12
+ obj: string;
13
+ }
14
+ export type { CmpOp, NBTType, ExecuteSubcmd };
15
+ export type LIRInstr = {
16
+ kind: 'score_set';
17
+ dst: Slot;
18
+ value: number;
19
+ } | {
20
+ kind: 'score_copy';
21
+ dst: Slot;
22
+ src: Slot;
23
+ } | {
24
+ kind: 'score_add';
25
+ dst: Slot;
26
+ src: Slot;
27
+ } | {
28
+ kind: 'score_sub';
29
+ dst: Slot;
30
+ src: Slot;
31
+ } | {
32
+ kind: 'score_mul';
33
+ dst: Slot;
34
+ src: Slot;
35
+ } | {
36
+ kind: 'score_div';
37
+ dst: Slot;
38
+ src: Slot;
39
+ } | {
40
+ kind: 'score_mod';
41
+ dst: Slot;
42
+ src: Slot;
43
+ } | {
44
+ kind: 'score_min';
45
+ dst: Slot;
46
+ src: Slot;
47
+ } | {
48
+ kind: 'score_max';
49
+ dst: Slot;
50
+ src: Slot;
51
+ } | {
52
+ kind: 'score_swap';
53
+ a: Slot;
54
+ b: Slot;
55
+ } | {
56
+ kind: 'store_cmd_to_score';
57
+ dst: Slot;
58
+ cmd: LIRInstr;
59
+ } | {
60
+ kind: 'store_score_to_nbt';
61
+ ns: string;
62
+ path: string;
63
+ type: NBTType;
64
+ scale: number;
65
+ src: Slot;
66
+ } | {
67
+ kind: 'store_nbt_to_score';
68
+ dst: Slot;
69
+ ns: string;
70
+ path: string;
71
+ scale: number;
72
+ } | {
73
+ kind: 'nbt_set_literal';
74
+ ns: string;
75
+ path: string;
76
+ value: string;
77
+ } | {
78
+ kind: 'nbt_copy';
79
+ srcNs: string;
80
+ srcPath: string;
81
+ dstNs: string;
82
+ dstPath: string;
83
+ } | {
84
+ kind: 'call';
85
+ fn: string;
86
+ } | {
87
+ kind: 'call_macro';
88
+ fn: string;
89
+ storage: string;
90
+ } | {
91
+ kind: 'call_if_matches';
92
+ fn: string;
93
+ slot: Slot;
94
+ range: string;
95
+ } | {
96
+ kind: 'call_unless_matches';
97
+ fn: string;
98
+ slot: Slot;
99
+ range: string;
100
+ } | {
101
+ kind: 'call_if_score';
102
+ fn: string;
103
+ a: Slot;
104
+ op: CmpOp;
105
+ b: Slot;
106
+ } | {
107
+ kind: 'call_unless_score';
108
+ fn: string;
109
+ a: Slot;
110
+ op: CmpOp;
111
+ b: Slot;
112
+ } | {
113
+ kind: 'call_context';
114
+ fn: string;
115
+ subcommands: ExecuteSubcmd[];
116
+ } | {
117
+ kind: 'return_value';
118
+ slot: Slot;
119
+ } | {
120
+ kind: 'macro_line';
121
+ template: string;
122
+ } | {
123
+ kind: 'raw';
124
+ cmd: string;
125
+ };
126
+ export interface LIRFunction {
127
+ name: string;
128
+ instructions: LIRInstr[];
129
+ isMacro: boolean;
130
+ macroParams: string[];
131
+ }
132
+ export interface LIRModule {
133
+ functions: LIRFunction[];
134
+ namespace: string;
135
+ objective: string;
136
+ }
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ /**
3
+ * LIR (Low-level IR) Types — Stage 5 of the RedScript compiler pipeline.
4
+ *
5
+ * LIR is 2-address, MC-specific, typed nodes — no raw strings.
6
+ * Each LIR instruction maps 1:1 (or near) to one MC command.
7
+ *
8
+ * Spec: docs/compiler-pipeline-redesign.md § "LIR Instruction Set"
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,14 @@
1
+ /**
2
+ * LIR Verifier — validates structural invariants of LIR modules.
3
+ *
4
+ * Checks:
5
+ * 1. All Slots use the module's objective
6
+ * 2. No undefined function references (call/call_if_* targets exist)
7
+ * 3. macro_line only appears in isMacro functions
8
+ */
9
+ import type { LIRModule } from './types';
10
+ export interface LIRVerifyError {
11
+ fn: string;
12
+ message: string;
13
+ }
14
+ export declare function verifyLIR(module: LIRModule): LIRVerifyError[];