redscript-mc 1.2.30 → 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 (269) 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/demo.gif +0 -0
  8. package/dist/cli.js +2 -554
  9. package/dist/compile.js +2 -266
  10. package/dist/index.js +2 -159
  11. package/dist/lowering/index.js +5 -3
  12. package/dist/src/__tests__/cli.test.d.ts +1 -0
  13. package/dist/src/__tests__/cli.test.js +104 -0
  14. package/dist/src/__tests__/codegen.test.d.ts +1 -0
  15. package/dist/src/__tests__/codegen.test.js +152 -0
  16. package/dist/src/__tests__/compile-all.test.d.ts +10 -0
  17. package/dist/src/__tests__/compile-all.test.js +108 -0
  18. package/dist/src/__tests__/dce.test.d.ts +1 -0
  19. package/dist/src/__tests__/dce.test.js +102 -0
  20. package/dist/src/__tests__/diagnostics.test.d.ts +4 -0
  21. package/dist/src/__tests__/diagnostics.test.js +177 -0
  22. package/dist/src/__tests__/e2e.test.d.ts +6 -0
  23. package/dist/src/__tests__/e2e.test.js +1789 -0
  24. package/dist/src/__tests__/entity-types.test.d.ts +1 -0
  25. package/dist/src/__tests__/entity-types.test.js +203 -0
  26. package/dist/src/__tests__/formatter.test.d.ts +1 -0
  27. package/dist/src/__tests__/formatter.test.js +40 -0
  28. package/dist/src/__tests__/lexer.test.d.ts +1 -0
  29. package/dist/src/__tests__/lexer.test.js +343 -0
  30. package/dist/src/__tests__/lowering.test.d.ts +1 -0
  31. package/dist/src/__tests__/lowering.test.js +1015 -0
  32. package/dist/src/__tests__/macro.test.d.ts +8 -0
  33. package/dist/src/__tests__/macro.test.js +306 -0
  34. package/dist/src/__tests__/mc-integration.test.d.ts +12 -0
  35. package/dist/src/__tests__/mc-integration.test.js +817 -0
  36. package/dist/src/__tests__/mc-syntax.test.d.ts +1 -0
  37. package/dist/src/__tests__/mc-syntax.test.js +124 -0
  38. package/dist/src/__tests__/nbt.test.d.ts +1 -0
  39. package/dist/src/__tests__/nbt.test.js +82 -0
  40. package/dist/src/__tests__/optimizer-advanced.test.d.ts +1 -0
  41. package/dist/src/__tests__/optimizer-advanced.test.js +124 -0
  42. package/dist/src/__tests__/optimizer.test.d.ts +1 -0
  43. package/dist/src/__tests__/optimizer.test.js +149 -0
  44. package/dist/src/__tests__/parser.test.d.ts +1 -0
  45. package/dist/src/__tests__/parser.test.js +807 -0
  46. package/dist/src/__tests__/repl.test.d.ts +1 -0
  47. package/dist/src/__tests__/repl.test.js +27 -0
  48. package/dist/src/__tests__/runtime.test.d.ts +1 -0
  49. package/dist/src/__tests__/runtime.test.js +289 -0
  50. package/dist/src/__tests__/stdlib-advanced.test.d.ts +4 -0
  51. package/dist/src/__tests__/stdlib-advanced.test.js +374 -0
  52. package/dist/src/__tests__/stdlib-bigint.test.d.ts +7 -0
  53. package/dist/src/__tests__/stdlib-bigint.test.js +426 -0
  54. package/dist/src/__tests__/stdlib-math.test.d.ts +7 -0
  55. package/dist/src/__tests__/stdlib-math.test.js +351 -0
  56. package/dist/src/__tests__/stdlib-vec.test.d.ts +4 -0
  57. package/dist/src/__tests__/stdlib-vec.test.js +263 -0
  58. package/dist/src/__tests__/structure-optimizer.test.d.ts +1 -0
  59. package/dist/src/__tests__/structure-optimizer.test.js +33 -0
  60. package/dist/src/__tests__/typechecker.test.d.ts +1 -0
  61. package/dist/src/__tests__/typechecker.test.js +552 -0
  62. package/dist/src/__tests__/var-allocator.test.d.ts +1 -0
  63. package/dist/src/__tests__/var-allocator.test.js +69 -0
  64. package/dist/src/ast/types.d.ts +515 -0
  65. package/dist/src/ast/types.js +9 -0
  66. package/dist/src/builtins/metadata.d.ts +36 -0
  67. package/dist/src/builtins/metadata.js +1014 -0
  68. package/dist/src/cli.d.ts +11 -0
  69. package/dist/src/cli.js +443 -0
  70. package/dist/src/codegen/cmdblock/index.d.ts +26 -0
  71. package/dist/src/codegen/cmdblock/index.js +45 -0
  72. package/dist/src/codegen/mcfunction/index.d.ts +40 -0
  73. package/dist/src/codegen/mcfunction/index.js +606 -0
  74. package/dist/src/codegen/structure/index.d.ts +24 -0
  75. package/dist/src/codegen/structure/index.js +279 -0
  76. package/dist/src/codegen/var-allocator.d.ts +45 -0
  77. package/dist/src/codegen/var-allocator.js +104 -0
  78. package/dist/src/compile.d.ts +37 -0
  79. package/dist/src/compile.js +165 -0
  80. package/dist/src/diagnostics/index.d.ts +44 -0
  81. package/dist/src/diagnostics/index.js +140 -0
  82. package/dist/src/events/types.d.ts +35 -0
  83. package/dist/src/events/types.js +59 -0
  84. package/dist/src/formatter/index.d.ts +1 -0
  85. package/dist/src/formatter/index.js +26 -0
  86. package/dist/src/index.d.ts +22 -0
  87. package/dist/src/index.js +45 -0
  88. package/dist/src/ir/builder.d.ts +33 -0
  89. package/dist/src/ir/builder.js +99 -0
  90. package/dist/src/ir/types.d.ts +132 -0
  91. package/dist/src/ir/types.js +15 -0
  92. package/dist/src/lexer/index.d.ts +37 -0
  93. package/dist/src/lexer/index.js +569 -0
  94. package/dist/src/lowering/index.d.ts +188 -0
  95. package/dist/src/lowering/index.js +3405 -0
  96. package/dist/src/mc-test/client.d.ts +128 -0
  97. package/dist/src/mc-test/client.js +174 -0
  98. package/dist/src/mc-test/runner.d.ts +28 -0
  99. package/dist/src/mc-test/runner.js +151 -0
  100. package/dist/src/mc-test/setup.d.ts +11 -0
  101. package/dist/src/mc-test/setup.js +98 -0
  102. package/dist/src/mc-validator/index.d.ts +17 -0
  103. package/dist/src/mc-validator/index.js +322 -0
  104. package/dist/src/nbt/index.d.ts +86 -0
  105. package/dist/src/nbt/index.js +250 -0
  106. package/dist/src/optimizer/commands.d.ts +38 -0
  107. package/dist/src/optimizer/commands.js +451 -0
  108. package/dist/src/optimizer/dce.d.ts +34 -0
  109. package/dist/src/optimizer/dce.js +639 -0
  110. package/dist/src/optimizer/passes.d.ts +34 -0
  111. package/dist/src/optimizer/passes.js +243 -0
  112. package/dist/src/optimizer/structure.d.ts +9 -0
  113. package/dist/src/optimizer/structure.js +356 -0
  114. package/dist/src/parser/index.d.ts +93 -0
  115. package/dist/src/parser/index.js +1687 -0
  116. package/dist/src/repl.d.ts +16 -0
  117. package/dist/src/repl.js +165 -0
  118. package/dist/src/runtime/index.d.ts +107 -0
  119. package/dist/src/runtime/index.js +1409 -0
  120. package/dist/src/typechecker/index.d.ts +61 -0
  121. package/dist/src/typechecker/index.js +1034 -0
  122. package/dist/src/types/entity-hierarchy.d.ts +29 -0
  123. package/dist/src/types/entity-hierarchy.js +107 -0
  124. package/dist/src2/__tests__/e2e/basic.test.d.ts +8 -0
  125. package/dist/src2/__tests__/e2e/basic.test.js +140 -0
  126. package/dist/src2/__tests__/e2e/macros.test.d.ts +9 -0
  127. package/dist/src2/__tests__/e2e/macros.test.js +182 -0
  128. package/dist/src2/__tests__/e2e/migrate.test.d.ts +13 -0
  129. package/dist/src2/__tests__/e2e/migrate.test.js +2739 -0
  130. package/dist/src2/__tests__/hir/desugar.test.d.ts +1 -0
  131. package/dist/src2/__tests__/hir/desugar.test.js +234 -0
  132. package/dist/src2/__tests__/lir/lower.test.d.ts +1 -0
  133. package/dist/src2/__tests__/lir/lower.test.js +559 -0
  134. package/dist/src2/__tests__/lir/types.test.d.ts +1 -0
  135. package/dist/src2/__tests__/lir/types.test.js +185 -0
  136. package/dist/src2/__tests__/lir/verify.test.d.ts +1 -0
  137. package/dist/src2/__tests__/lir/verify.test.js +221 -0
  138. package/dist/src2/__tests__/mir/arithmetic.test.d.ts +1 -0
  139. package/dist/src2/__tests__/mir/arithmetic.test.js +130 -0
  140. package/dist/src2/__tests__/mir/control-flow.test.d.ts +1 -0
  141. package/dist/src2/__tests__/mir/control-flow.test.js +205 -0
  142. package/dist/src2/__tests__/mir/verify.test.d.ts +1 -0
  143. package/dist/src2/__tests__/mir/verify.test.js +223 -0
  144. package/dist/src2/__tests__/optimizer/block_merge.test.d.ts +1 -0
  145. package/dist/src2/__tests__/optimizer/block_merge.test.js +78 -0
  146. package/dist/src2/__tests__/optimizer/branch_simplify.test.d.ts +1 -0
  147. package/dist/src2/__tests__/optimizer/branch_simplify.test.js +58 -0
  148. package/dist/src2/__tests__/optimizer/constant_fold.test.d.ts +1 -0
  149. package/dist/src2/__tests__/optimizer/constant_fold.test.js +131 -0
  150. package/dist/src2/__tests__/optimizer/copy_prop.test.d.ts +1 -0
  151. package/dist/src2/__tests__/optimizer/copy_prop.test.js +91 -0
  152. package/dist/src2/__tests__/optimizer/dce.test.d.ts +1 -0
  153. package/dist/src2/__tests__/optimizer/dce.test.js +76 -0
  154. package/dist/src2/__tests__/optimizer/pipeline.test.d.ts +1 -0
  155. package/dist/src2/__tests__/optimizer/pipeline.test.js +102 -0
  156. package/dist/src2/emit/compile.d.ts +19 -0
  157. package/dist/src2/emit/compile.js +80 -0
  158. package/dist/src2/emit/index.d.ts +17 -0
  159. package/dist/src2/emit/index.js +172 -0
  160. package/dist/src2/hir/lower.d.ts +15 -0
  161. package/dist/src2/hir/lower.js +378 -0
  162. package/dist/src2/hir/types.d.ts +373 -0
  163. package/dist/src2/hir/types.js +16 -0
  164. package/dist/src2/lir/lower.d.ts +15 -0
  165. package/dist/src2/lir/lower.js +453 -0
  166. package/dist/src2/lir/types.d.ts +136 -0
  167. package/dist/src2/lir/types.js +11 -0
  168. package/dist/src2/lir/verify.d.ts +14 -0
  169. package/dist/src2/lir/verify.js +113 -0
  170. package/dist/src2/mir/lower.d.ts +9 -0
  171. package/dist/src2/mir/lower.js +1030 -0
  172. package/dist/src2/mir/macro.d.ts +22 -0
  173. package/dist/src2/mir/macro.js +168 -0
  174. package/dist/src2/mir/types.d.ts +183 -0
  175. package/dist/src2/mir/types.js +11 -0
  176. package/dist/src2/mir/verify.d.ts +16 -0
  177. package/dist/src2/mir/verify.js +216 -0
  178. package/dist/src2/optimizer/block_merge.d.ts +12 -0
  179. package/dist/src2/optimizer/block_merge.js +84 -0
  180. package/dist/src2/optimizer/branch_simplify.d.ts +9 -0
  181. package/dist/src2/optimizer/branch_simplify.js +28 -0
  182. package/dist/src2/optimizer/constant_fold.d.ts +10 -0
  183. package/dist/src2/optimizer/constant_fold.js +85 -0
  184. package/dist/src2/optimizer/copy_prop.d.ts +9 -0
  185. package/dist/src2/optimizer/copy_prop.js +113 -0
  186. package/dist/src2/optimizer/dce.d.ts +8 -0
  187. package/dist/src2/optimizer/dce.js +155 -0
  188. package/dist/src2/optimizer/pipeline.d.ts +10 -0
  189. package/dist/src2/optimizer/pipeline.js +42 -0
  190. package/dist/tsconfig.tsbuildinfo +1 -0
  191. package/docs/compiler-pipeline-redesign.md +2243 -0
  192. package/docs/optimization-ideas.md +1076 -0
  193. package/editors/vscode/package-lock.json +3 -3
  194. package/editors/vscode/package.json +1 -1
  195. package/jest.config.js +1 -1
  196. package/package.json +6 -5
  197. package/scripts/postbuild.js +15 -0
  198. package/src/__tests__/cli.test.ts +8 -220
  199. package/src/__tests__/dce.test.ts +11 -56
  200. package/src/__tests__/diagnostics.test.ts +59 -38
  201. package/src/__tests__/mc-integration.test.ts +1 -2
  202. package/src/ast/types.ts +6 -1
  203. package/src/cli.ts +29 -156
  204. package/src/compile.ts +6 -162
  205. package/src/index.ts +14 -178
  206. package/src/mc-test/runner.ts +4 -3
  207. package/src/parser/index.ts +1 -1
  208. package/src/repl.ts +1 -1
  209. package/src/runtime/index.ts +1 -1
  210. package/src2/__tests__/e2e/basic.test.ts +154 -0
  211. package/src2/__tests__/e2e/macros.test.ts +199 -0
  212. package/src2/__tests__/e2e/migrate.test.ts +3008 -0
  213. package/src2/__tests__/hir/desugar.test.ts +263 -0
  214. package/src2/__tests__/lir/lower.test.ts +619 -0
  215. package/src2/__tests__/lir/types.test.ts +207 -0
  216. package/src2/__tests__/lir/verify.test.ts +249 -0
  217. package/src2/__tests__/mir/arithmetic.test.ts +156 -0
  218. package/src2/__tests__/mir/control-flow.test.ts +242 -0
  219. package/src2/__tests__/mir/verify.test.ts +254 -0
  220. package/src2/__tests__/optimizer/block_merge.test.ts +84 -0
  221. package/src2/__tests__/optimizer/branch_simplify.test.ts +64 -0
  222. package/src2/__tests__/optimizer/constant_fold.test.ts +145 -0
  223. package/src2/__tests__/optimizer/copy_prop.test.ts +99 -0
  224. package/src2/__tests__/optimizer/dce.test.ts +83 -0
  225. package/src2/__tests__/optimizer/pipeline.test.ts +116 -0
  226. package/src2/emit/compile.ts +99 -0
  227. package/src2/emit/index.ts +222 -0
  228. package/src2/hir/lower.ts +428 -0
  229. package/src2/hir/types.ts +216 -0
  230. package/src2/lir/lower.ts +556 -0
  231. package/src2/lir/types.ts +109 -0
  232. package/src2/lir/verify.ts +129 -0
  233. package/src2/mir/lower.ts +1160 -0
  234. package/src2/mir/macro.ts +167 -0
  235. package/src2/mir/types.ts +106 -0
  236. package/src2/mir/verify.ts +218 -0
  237. package/src2/optimizer/block_merge.ts +93 -0
  238. package/src2/optimizer/branch_simplify.ts +27 -0
  239. package/src2/optimizer/constant_fold.ts +88 -0
  240. package/src2/optimizer/copy_prop.ts +106 -0
  241. package/src2/optimizer/dce.ts +133 -0
  242. package/src2/optimizer/pipeline.ts +44 -0
  243. package/tsconfig.json +2 -2
  244. package/src/__tests__/codegen.test.ts +0 -161
  245. package/src/__tests__/e2e.test.ts +0 -2039
  246. package/src/__tests__/entity-types.test.ts +0 -236
  247. package/src/__tests__/lowering.test.ts +0 -1185
  248. package/src/__tests__/macro.test.ts +0 -343
  249. package/src/__tests__/nbt.test.ts +0 -58
  250. package/src/__tests__/optimizer-advanced.test.ts +0 -144
  251. package/src/__tests__/optimizer.test.ts +0 -162
  252. package/src/__tests__/runtime.test.ts +0 -305
  253. package/src/__tests__/stdlib-advanced.test.ts +0 -379
  254. package/src/__tests__/stdlib-bigint.test.ts +0 -427
  255. package/src/__tests__/stdlib-math.test.ts +0 -374
  256. package/src/__tests__/stdlib-vec.test.ts +0 -259
  257. package/src/__tests__/structure-optimizer.test.ts +0 -38
  258. package/src/__tests__/var-allocator.test.ts +0 -75
  259. package/src/codegen/cmdblock/index.ts +0 -63
  260. package/src/codegen/mcfunction/index.ts +0 -662
  261. package/src/codegen/structure/index.ts +0 -346
  262. package/src/codegen/var-allocator.ts +0 -104
  263. package/src/ir/builder.ts +0 -116
  264. package/src/ir/types.ts +0 -134
  265. package/src/lowering/index.ts +0 -3876
  266. package/src/optimizer/commands.ts +0 -534
  267. package/src/optimizer/dce.ts +0 -679
  268. package/src/optimizer/passes.ts +0 -250
  269. package/src/optimizer/structure.ts +0 -450
@@ -0,0 +1,222 @@
1
+ /**
2
+ * Stage 7 — LIR → .mcfunction Emission
3
+ *
4
+ * Converts a LIRModule into DatapackFile[] representing a Minecraft datapack.
5
+ * Each LIRFunction becomes a .mcfunction file under data/<ns>/function/.
6
+ */
7
+
8
+ import type { LIRModule, LIRFunction, LIRInstr, Slot, CmpOp, ExecuteSubcmd } from '../lir/types'
9
+
10
+ export interface DatapackFile {
11
+ path: string
12
+ content: string
13
+ }
14
+
15
+ export interface EmitOptions {
16
+ namespace: string
17
+ tickFunctions?: string[]
18
+ loadFunctions?: string[]
19
+ }
20
+
21
+ // ---------------------------------------------------------------------------
22
+ // Public API
23
+ // ---------------------------------------------------------------------------
24
+
25
+ export function emit(module: LIRModule, options: EmitOptions): DatapackFile[] {
26
+ const { namespace } = options
27
+ const tickFns = options.tickFunctions ?? []
28
+ const loadFns = options.loadFunctions ?? []
29
+ const objective = module.objective
30
+ const files: DatapackFile[] = []
31
+
32
+ // pack.mcmeta
33
+ files.push({
34
+ path: 'pack.mcmeta',
35
+ content: JSON.stringify({
36
+ pack: { pack_format: 26, description: `RedScript datapack: ${namespace}` },
37
+ }, null, 2) + '\n',
38
+ })
39
+
40
+ // load.mcfunction — creates the scoreboard objective
41
+ const loadCmds = [`scoreboard objectives add ${objective} dummy`]
42
+ files.push({
43
+ path: `data/${namespace}/function/load.mcfunction`,
44
+ content: loadCmds.join('\n') + '\n',
45
+ })
46
+
47
+ // Each LIR function → .mcfunction file
48
+ for (const fn of module.functions) {
49
+ const lines = emitFunction(fn, namespace, objective)
50
+ const fnPath = fnNameToPath(fn.name, namespace)
51
+ files.push({ path: fnPath, content: lines.join('\n') + '\n' })
52
+ }
53
+
54
+ // Tag files for tick/load
55
+ if (loadFns.length > 0 || true) {
56
+ // Always include load.json — it must reference the load.mcfunction
57
+ const loadValues = [`${namespace}:load`, ...loadFns.map(fn => `${namespace}:${fn}`)]
58
+ files.push({
59
+ path: 'data/minecraft/tags/function/load.json',
60
+ content: JSON.stringify({ values: loadValues }, null, 2) + '\n',
61
+ })
62
+ }
63
+
64
+ if (tickFns.length > 0) {
65
+ const tickValues = tickFns.map(fn => `${namespace}:${fn}`)
66
+ files.push({
67
+ path: 'data/minecraft/tags/function/tick.json',
68
+ content: JSON.stringify({ values: tickValues }, null, 2) + '\n',
69
+ })
70
+ }
71
+
72
+ return files
73
+ }
74
+
75
+ // ---------------------------------------------------------------------------
76
+ // Function emission
77
+ // ---------------------------------------------------------------------------
78
+
79
+ function emitFunction(fn: LIRFunction, namespace: string, objective: string): string[] {
80
+ const lines: string[] = []
81
+ for (const instr of fn.instructions) {
82
+ lines.push(emitInstr(instr, namespace, objective))
83
+ }
84
+ return lines
85
+ }
86
+
87
+ function fnNameToPath(name: string, namespace: string): string {
88
+ // LIR function names may contain :: for methods — convert to /
89
+ const mcName = name.replace(/::/g, '/').toLowerCase()
90
+ return `data/${namespace}/function/${mcName}.mcfunction`
91
+ }
92
+
93
+ // ---------------------------------------------------------------------------
94
+ // Instruction emission
95
+ // ---------------------------------------------------------------------------
96
+
97
+ function emitInstr(instr: LIRInstr, ns: string, obj: string): string {
98
+ switch (instr.kind) {
99
+ case 'score_set':
100
+ return `scoreboard players set ${slot(instr.dst)} ${instr.value}`
101
+
102
+ case 'score_copy':
103
+ return `scoreboard players operation ${slot(instr.dst)} = ${slot(instr.src)}`
104
+
105
+ case 'score_add':
106
+ return `scoreboard players operation ${slot(instr.dst)} += ${slot(instr.src)}`
107
+
108
+ case 'score_sub':
109
+ return `scoreboard players operation ${slot(instr.dst)} -= ${slot(instr.src)}`
110
+
111
+ case 'score_mul':
112
+ return `scoreboard players operation ${slot(instr.dst)} *= ${slot(instr.src)}`
113
+
114
+ case 'score_div':
115
+ return `scoreboard players operation ${slot(instr.dst)} /= ${slot(instr.src)}`
116
+
117
+ case 'score_mod':
118
+ return `scoreboard players operation ${slot(instr.dst)} %= ${slot(instr.src)}`
119
+
120
+ case 'score_min':
121
+ return `scoreboard players operation ${slot(instr.dst)} < ${slot(instr.src)}`
122
+
123
+ case 'score_max':
124
+ return `scoreboard players operation ${slot(instr.dst)} > ${slot(instr.src)}`
125
+
126
+ case 'score_swap':
127
+ return `scoreboard players operation ${slot(instr.a)} >< ${slot(instr.b)}`
128
+
129
+ case 'store_cmd_to_score':
130
+ return `execute store result score ${slot(instr.dst)} run ${emitInstr(instr.cmd, ns, obj)}`
131
+
132
+ case 'store_score_to_nbt':
133
+ return `execute store result storage ${instr.ns} ${instr.path} ${instr.type} ${instr.scale} run scoreboard players get ${slot(instr.src)}`
134
+
135
+ case 'store_nbt_to_score':
136
+ return `execute store result score ${slot(instr.dst)} run data get storage ${instr.ns} ${instr.path} ${instr.scale}`
137
+
138
+ case 'nbt_set_literal':
139
+ return `data modify storage ${instr.ns} ${instr.path} set value ${instr.value}`
140
+
141
+ case 'nbt_copy':
142
+ return `data modify storage ${instr.dstNs} ${instr.dstPath} set from storage ${instr.srcNs} ${instr.srcPath}`
143
+
144
+ case 'call':
145
+ return `function ${instr.fn}`
146
+
147
+ case 'call_macro':
148
+ return `function ${instr.fn} with storage ${instr.storage}`
149
+
150
+ case 'call_if_matches':
151
+ return `execute if score ${slot(instr.slot)} matches ${instr.range} run function ${instr.fn}`
152
+
153
+ case 'call_unless_matches':
154
+ return `execute unless score ${slot(instr.slot)} matches ${instr.range} run function ${instr.fn}`
155
+
156
+ case 'call_if_score':
157
+ return `execute if score ${slot(instr.a)} ${cmpToMC(instr.op)} ${slot(instr.b)} run function ${instr.fn}`
158
+
159
+ case 'call_unless_score':
160
+ return `execute unless score ${slot(instr.a)} ${cmpToMC(instr.op)} ${slot(instr.b)} run function ${instr.fn}`
161
+
162
+ case 'call_context': {
163
+ const subcmds = instr.subcommands.map(emitSubcmd).join(' ')
164
+ return `execute ${subcmds} run function ${instr.fn}`
165
+ }
166
+
167
+ case 'return_value':
168
+ return `scoreboard players operation $ret ${instr.slot.obj} = ${slot(instr.slot)}`
169
+
170
+ case 'macro_line':
171
+ return `$${instr.template}`
172
+
173
+ case 'raw':
174
+ return instr.cmd
175
+ }
176
+ }
177
+
178
+ // ---------------------------------------------------------------------------
179
+ // Helpers
180
+ // ---------------------------------------------------------------------------
181
+
182
+ function slot(s: Slot): string {
183
+ return `${s.player} ${s.obj}`
184
+ }
185
+
186
+ function cmpToMC(op: CmpOp): string {
187
+ switch (op) {
188
+ case 'eq': return '='
189
+ case 'ne': return '=' // ne uses "unless" form, but when used in if score context
190
+ case 'lt': return '<'
191
+ case 'le': return '<='
192
+ case 'gt': return '>'
193
+ case 'ge': return '>='
194
+ }
195
+ }
196
+
197
+ function emitSubcmd(sub: ExecuteSubcmd): string {
198
+ switch (sub.kind) {
199
+ case 'as':
200
+ return `as ${sub.selector}`
201
+ case 'at':
202
+ return `at ${sub.selector}`
203
+ case 'at_self':
204
+ return 'at @s'
205
+ case 'positioned':
206
+ return `positioned ${sub.x} ${sub.y} ${sub.z}`
207
+ case 'rotated':
208
+ return `rotated ${sub.yaw} ${sub.pitch}`
209
+ case 'in':
210
+ return `in ${sub.dimension}`
211
+ case 'anchored':
212
+ return `anchored ${sub.anchor}`
213
+ case 'if_score':
214
+ return `if score ${sub.a} ${cmpToMC(sub.op)} ${sub.b}`
215
+ case 'unless_score':
216
+ return `unless score ${sub.a} ${cmpToMC(sub.op)} ${sub.b}`
217
+ case 'if_matches':
218
+ return `if score ${sub.score} matches ${sub.range}`
219
+ case 'unless_matches':
220
+ return `unless score ${sub.score} matches ${sub.range}`
221
+ }
222
+ }
@@ -0,0 +1,428 @@
1
+ /**
2
+ * AST → HIR Lowering — Stage 2 of the RedScript compiler pipeline.
3
+ *
4
+ * Desugaring transforms:
5
+ * - for(init;cond;step) → block { init; while(cond) { body; step } }
6
+ * - for_range(v,start,end) → block { let v = start; while(v < end) { body; v = v + 1 } }
7
+ * - a += b / -= / *= / /= / %= → a = a OP b
8
+ * - a && b → if(a) { b } else { false }
9
+ * - a || b → if(a) { true } else { b }
10
+ * - as_block / at_block / as_at → unified execute with subcommands
11
+ * - All other nodes pass through with field-wise recursion
12
+ */
13
+
14
+ import type {
15
+ Program, Expr, Stmt, Block, FnDecl, Param,
16
+ AssignOp, ExecuteSubcommand,
17
+ } from '../../src/ast/types'
18
+ import type {
19
+ HIRModule, HIRFunction, HIRParam, HIRExpr, HIRStmt, HIRBlock,
20
+ HIRExecuteSubcommand, HIRStruct, HIRImplBlock, HIREnum, HIRConst, HIRGlobal,
21
+ } from './types'
22
+
23
+ // ---------------------------------------------------------------------------
24
+ // Public API
25
+ // ---------------------------------------------------------------------------
26
+
27
+ export function lowerToHIR(program: Program): HIRModule {
28
+ return {
29
+ namespace: program.namespace,
30
+ globals: program.globals.map(lowerGlobal),
31
+ functions: program.declarations.map(lowerFunction),
32
+ structs: program.structs.map((s): HIRStruct => ({
33
+ name: s.name,
34
+ fields: s.fields.map(f => ({ name: f.name, type: f.type })),
35
+ span: s.span,
36
+ })),
37
+ implBlocks: program.implBlocks.map((ib): HIRImplBlock => ({
38
+ typeName: ib.typeName,
39
+ methods: ib.methods.map(lowerFunction),
40
+ span: ib.span,
41
+ })),
42
+ enums: program.enums.map((e): HIREnum => ({
43
+ name: e.name,
44
+ variants: e.variants.map(v => ({ name: v.name, value: v.value })),
45
+ span: e.span,
46
+ })),
47
+ consts: program.consts.map((c): HIRConst => ({
48
+ name: c.name,
49
+ type: c.type,
50
+ value: lowerExpr(c.value),
51
+ span: c.span,
52
+ })),
53
+ isLibrary: program.isLibrary,
54
+ }
55
+ }
56
+
57
+ // ---------------------------------------------------------------------------
58
+ // Functions & globals
59
+ // ---------------------------------------------------------------------------
60
+
61
+ function lowerGlobal(g: Program['globals'][0]): HIRGlobal {
62
+ return {
63
+ name: g.name,
64
+ type: g.type,
65
+ init: lowerExpr(g.init),
66
+ mutable: g.mutable,
67
+ span: g.span,
68
+ }
69
+ }
70
+
71
+ function lowerFunction(fn: FnDecl): HIRFunction {
72
+ return {
73
+ name: fn.name,
74
+ params: fn.params.map(lowerParam),
75
+ returnType: fn.returnType,
76
+ decorators: fn.decorators,
77
+ body: lowerBlock(fn.body),
78
+ isLibraryFn: fn.isLibraryFn,
79
+ span: fn.span,
80
+ }
81
+ }
82
+
83
+ function lowerParam(p: Param): HIRParam {
84
+ return {
85
+ name: p.name,
86
+ type: p.type,
87
+ default: p.default ? lowerExpr(p.default) : undefined,
88
+ }
89
+ }
90
+
91
+ // ---------------------------------------------------------------------------
92
+ // Blocks & statements
93
+ // ---------------------------------------------------------------------------
94
+
95
+ function lowerBlock(block: Block): HIRBlock {
96
+ const result: HIRBlock = []
97
+ for (const stmt of block) {
98
+ const lowered = lowerStmt(stmt)
99
+ // lowerStmt may return an array (e.g. for → [init, while])
100
+ if (Array.isArray(lowered)) {
101
+ result.push(...lowered)
102
+ } else {
103
+ result.push(lowered)
104
+ }
105
+ }
106
+ return result
107
+ }
108
+
109
+ function lowerStmt(stmt: Stmt): HIRStmt | HIRStmt[] {
110
+ switch (stmt.kind) {
111
+ case 'let':
112
+ return { kind: 'let', name: stmt.name, type: stmt.type, init: lowerExpr(stmt.init), span: stmt.span }
113
+
114
+ case 'expr':
115
+ return { kind: 'expr', expr: lowerExpr(stmt.expr), span: stmt.span }
116
+
117
+ case 'return':
118
+ return { kind: 'return', value: stmt.value ? lowerExpr(stmt.value) : undefined, span: stmt.span }
119
+
120
+ case 'break':
121
+ return { kind: 'break', span: stmt.span }
122
+
123
+ case 'continue':
124
+ return { kind: 'continue', span: stmt.span }
125
+
126
+ case 'if':
127
+ return {
128
+ kind: 'if',
129
+ cond: lowerExpr(stmt.cond),
130
+ then: lowerBlock(stmt.then),
131
+ else_: stmt.else_ ? lowerBlock(stmt.else_) : undefined,
132
+ span: stmt.span,
133
+ }
134
+
135
+ case 'while':
136
+ return { kind: 'while', cond: lowerExpr(stmt.cond), body: lowerBlock(stmt.body), span: stmt.span }
137
+
138
+ // --- Desugaring: for → while ---
139
+ case 'for': {
140
+ const stmts: HIRStmt[] = []
141
+ // Init
142
+ if (stmt.init) {
143
+ const init = lowerStmt(stmt.init)
144
+ if (Array.isArray(init)) stmts.push(...init)
145
+ else stmts.push(init)
146
+ }
147
+ // while(cond) { body } step { step_expr }
148
+ const body = lowerBlock(stmt.body)
149
+ const step: HIRStmt[] = [{ kind: 'expr', expr: lowerExpr(stmt.step), span: stmt.span }]
150
+ stmts.push({ kind: 'while', cond: lowerExpr(stmt.cond), body, step, span: stmt.span })
151
+ return stmts
152
+ }
153
+
154
+ // --- Desugaring: for_range → let + while(cond) { body } step { v++ } ---
155
+ case 'for_range': {
156
+ const varName = stmt.varName
157
+ const initStmt: HIRStmt = {
158
+ kind: 'let',
159
+ name: varName,
160
+ type: { kind: 'named', name: 'int' },
161
+ init: lowerExpr(stmt.start),
162
+ span: stmt.span,
163
+ }
164
+ const body = lowerBlock(stmt.body)
165
+ // step: v = v + 1 (in separate step block so continue still increments)
166
+ const step: HIRStmt[] = [{
167
+ kind: 'expr',
168
+ expr: {
169
+ kind: 'assign',
170
+ target: varName,
171
+ value: {
172
+ kind: 'binary',
173
+ op: '+',
174
+ left: { kind: 'ident', name: varName },
175
+ right: { kind: 'int_lit', value: 1 },
176
+ },
177
+ },
178
+ }]
179
+ const whileStmt: HIRStmt = {
180
+ kind: 'while',
181
+ cond: {
182
+ kind: 'binary',
183
+ op: '<',
184
+ left: { kind: 'ident', name: varName },
185
+ right: lowerExpr(stmt.end),
186
+ },
187
+ body,
188
+ step,
189
+ span: stmt.span,
190
+ }
191
+ return [initStmt, whileStmt]
192
+ }
193
+
194
+ case 'foreach':
195
+ return {
196
+ kind: 'foreach',
197
+ binding: stmt.binding,
198
+ iterable: lowerExpr(stmt.iterable),
199
+ body: lowerBlock(stmt.body),
200
+ executeContext: stmt.executeContext,
201
+ span: stmt.span,
202
+ }
203
+
204
+ case 'match':
205
+ return {
206
+ kind: 'match',
207
+ expr: lowerExpr(stmt.expr),
208
+ arms: stmt.arms.map(arm => ({
209
+ pattern: arm.pattern ? lowerExpr(arm.pattern) : null,
210
+ body: lowerBlock(arm.body),
211
+ })),
212
+ span: stmt.span,
213
+ }
214
+
215
+ // --- Desugaring: as_block → execute [as] ---
216
+ case 'as_block':
217
+ return {
218
+ kind: 'execute',
219
+ subcommands: [{ kind: 'as', selector: stmt.selector }],
220
+ body: lowerBlock(stmt.body),
221
+ span: stmt.span,
222
+ }
223
+
224
+ // --- Desugaring: at_block → execute [at] ---
225
+ case 'at_block':
226
+ return {
227
+ kind: 'execute',
228
+ subcommands: [{ kind: 'at', selector: stmt.selector }],
229
+ body: lowerBlock(stmt.body),
230
+ span: stmt.span,
231
+ }
232
+
233
+ // --- Desugaring: as_at → execute [as, at] ---
234
+ case 'as_at':
235
+ return {
236
+ kind: 'execute',
237
+ subcommands: [
238
+ { kind: 'as', selector: stmt.as_sel },
239
+ { kind: 'at', selector: stmt.at_sel },
240
+ ],
241
+ body: lowerBlock(stmt.body),
242
+ span: stmt.span,
243
+ }
244
+
245
+ case 'execute':
246
+ return {
247
+ kind: 'execute',
248
+ subcommands: stmt.subcommands.map(lowerExecuteSubcommand),
249
+ body: lowerBlock(stmt.body),
250
+ span: stmt.span,
251
+ }
252
+
253
+ case 'raw':
254
+ return { kind: 'raw', cmd: stmt.cmd, span: stmt.span }
255
+
256
+ default: {
257
+ const _exhaustive: never = stmt
258
+ throw new Error(`Unknown statement kind: ${(_exhaustive as any).kind}`)
259
+ }
260
+ }
261
+ }
262
+
263
+ // ---------------------------------------------------------------------------
264
+ // Execute subcommands (pass-through — same shape)
265
+ // ---------------------------------------------------------------------------
266
+
267
+ function lowerExecuteSubcommand(sub: ExecuteSubcommand): HIRExecuteSubcommand {
268
+ // All subcommand types share the same shape between AST and HIR
269
+ return sub as HIRExecuteSubcommand
270
+ }
271
+
272
+ // ---------------------------------------------------------------------------
273
+ // Expressions
274
+ // ---------------------------------------------------------------------------
275
+
276
+ /** Map compound assignment operator to its base binary op */
277
+ const COMPOUND_TO_BINOP: Record<string, string> = {
278
+ '+=': '+',
279
+ '-=': '-',
280
+ '*=': '*',
281
+ '/=': '/',
282
+ '%=': '%',
283
+ }
284
+
285
+ function lowerExpr(expr: Expr): HIRExpr {
286
+ switch (expr.kind) {
287
+ // --- Pass-through literals ---
288
+ case 'int_lit':
289
+ case 'float_lit':
290
+ case 'byte_lit':
291
+ case 'short_lit':
292
+ case 'long_lit':
293
+ case 'double_lit':
294
+ case 'bool_lit':
295
+ case 'str_lit':
296
+ case 'range_lit':
297
+ case 'rel_coord':
298
+ case 'local_coord':
299
+ case 'mc_name':
300
+ case 'blockpos':
301
+ return expr as HIRExpr
302
+
303
+ case 'ident':
304
+ return { kind: 'ident', name: expr.name, span: expr.span }
305
+
306
+ case 'selector':
307
+ return { kind: 'selector', raw: expr.raw, isSingle: expr.isSingle, sel: expr.sel, span: expr.span }
308
+
309
+ case 'array_lit':
310
+ return { kind: 'array_lit', elements: expr.elements.map(lowerExpr), span: expr.span }
311
+
312
+ case 'struct_lit':
313
+ return {
314
+ kind: 'struct_lit',
315
+ fields: expr.fields.map(f => ({ name: f.name, value: lowerExpr(f.value) })),
316
+ span: expr.span,
317
+ }
318
+
319
+ case 'str_interp':
320
+ return {
321
+ kind: 'str_interp',
322
+ parts: expr.parts.map(p => typeof p === 'string' ? p : lowerExpr(p)),
323
+ span: expr.span,
324
+ }
325
+
326
+ case 'f_string':
327
+ return { kind: 'f_string', parts: expr.parts, span: expr.span }
328
+
329
+ // Binary ops — && and || preserved as-is (short-circuit → control flow in MIR)
330
+ case 'binary':
331
+ return {
332
+ kind: 'binary',
333
+ op: expr.op,
334
+ left: lowerExpr(expr.left),
335
+ right: lowerExpr(expr.right),
336
+ span: expr.span,
337
+ }
338
+
339
+ case 'unary':
340
+ return { kind: 'unary', op: expr.op, operand: lowerExpr(expr.operand), span: expr.span }
341
+
342
+ case 'is_check':
343
+ return { kind: 'is_check', expr: lowerExpr(expr.expr), entityType: expr.entityType, span: expr.span }
344
+
345
+ // --- Desugaring: compound assignment → plain assign ---
346
+ case 'assign':
347
+ if (expr.op !== '=') {
348
+ const binOp = COMPOUND_TO_BINOP[expr.op]
349
+ return {
350
+ kind: 'assign',
351
+ target: expr.target,
352
+ value: {
353
+ kind: 'binary',
354
+ op: binOp as any,
355
+ left: { kind: 'ident', name: expr.target },
356
+ right: lowerExpr(expr.value),
357
+ span: expr.span,
358
+ },
359
+ span: expr.span,
360
+ }
361
+ }
362
+ return { kind: 'assign', target: expr.target, value: lowerExpr(expr.value), span: expr.span }
363
+
364
+ // --- Desugaring: compound member_assign → plain member_assign ---
365
+ case 'member_assign':
366
+ if (expr.op !== '=') {
367
+ const binOp = COMPOUND_TO_BINOP[expr.op]
368
+ const obj = lowerExpr(expr.obj)
369
+ return {
370
+ kind: 'member_assign',
371
+ obj,
372
+ field: expr.field,
373
+ value: {
374
+ kind: 'binary',
375
+ op: binOp as any,
376
+ left: { kind: 'member', obj, field: expr.field },
377
+ right: lowerExpr(expr.value),
378
+ span: expr.span,
379
+ },
380
+ span: expr.span,
381
+ }
382
+ }
383
+ return {
384
+ kind: 'member_assign',
385
+ obj: lowerExpr(expr.obj),
386
+ field: expr.field,
387
+ value: lowerExpr(expr.value),
388
+ span: expr.span,
389
+ }
390
+
391
+ case 'member':
392
+ return { kind: 'member', obj: lowerExpr(expr.obj), field: expr.field, span: expr.span }
393
+
394
+ case 'index':
395
+ return { kind: 'index', obj: lowerExpr(expr.obj), index: lowerExpr(expr.index), span: expr.span }
396
+
397
+ case 'call':
398
+ return { kind: 'call', fn: expr.fn, args: expr.args.map(lowerExpr), span: expr.span }
399
+
400
+ case 'invoke':
401
+ return { kind: 'invoke', callee: lowerExpr(expr.callee), args: expr.args.map(lowerExpr), span: expr.span }
402
+
403
+ case 'static_call':
404
+ return {
405
+ kind: 'static_call',
406
+ type: expr.type,
407
+ method: expr.method,
408
+ args: expr.args.map(lowerExpr),
409
+ span: expr.span,
410
+ }
411
+
412
+ case 'lambda': {
413
+ const body = Array.isArray(expr.body) ? lowerBlock(expr.body) : lowerExpr(expr.body)
414
+ return {
415
+ kind: 'lambda',
416
+ params: expr.params,
417
+ returnType: expr.returnType,
418
+ body,
419
+ span: expr.span,
420
+ }
421
+ }
422
+
423
+ default: {
424
+ const _exhaustive: never = expr
425
+ throw new Error(`Unknown expression kind: ${(_exhaustive as any).kind}`)
426
+ }
427
+ }
428
+ }