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
@@ -1,679 +0,0 @@
1
- import type { Block, Expr, FnDecl, Program, Stmt } from '../ast/types'
2
-
3
- interface ScopeEntry {
4
- id: string
5
- name: string
6
- }
7
-
8
- function copySpan<T extends object>(target: T, source: object): T {
9
- const descriptor = Object.getOwnPropertyDescriptor(source, 'span')
10
- if (descriptor) {
11
- Object.defineProperty(target, 'span', descriptor)
12
- }
13
- return target
14
- }
15
-
16
- function isConstantBoolean(expr: Expr): boolean | null {
17
- if (expr.kind === 'bool_lit') {
18
- return expr.value
19
- }
20
- return null
21
- }
22
-
23
- function isPureExpr(expr: Expr): boolean {
24
- switch (expr.kind) {
25
- case 'int_lit':
26
- case 'float_lit':
27
- case 'byte_lit':
28
- case 'short_lit':
29
- case 'long_lit':
30
- case 'double_lit':
31
- case 'rel_coord':
32
- case 'local_coord':
33
- case 'bool_lit':
34
- case 'str_lit':
35
- case 'mc_name':
36
- case 'range_lit':
37
- case 'selector':
38
- case 'ident':
39
- case 'blockpos':
40
- return true
41
- case 'str_interp':
42
- return expr.parts.every(part => typeof part === 'string' || isPureExpr(part))
43
- case 'f_string':
44
- return expr.parts.every(part => part.kind === 'text' || isPureExpr(part.expr))
45
- case 'binary':
46
- return isPureExpr(expr.left) && isPureExpr(expr.right)
47
- case 'is_check':
48
- return isPureExpr(expr.expr)
49
- case 'unary':
50
- return isPureExpr(expr.operand)
51
- case 'member':
52
- return isPureExpr(expr.obj)
53
- case 'index':
54
- return isPureExpr(expr.obj) && isPureExpr(expr.index)
55
- case 'array_lit':
56
- return expr.elements.every(isPureExpr)
57
- case 'struct_lit':
58
- return expr.fields.every(field => isPureExpr(field.value))
59
- case 'lambda':
60
- return true
61
- case 'assign':
62
- case 'member_assign':
63
- case 'call':
64
- case 'invoke':
65
- case 'static_call':
66
- return false
67
- }
68
- }
69
-
70
- export interface DCEWarning {
71
- message: string
72
- code: string
73
- line?: number
74
- col?: number
75
- filePath?: string
76
- }
77
-
78
- export class DeadCodeEliminator {
79
- private readonly functionMap = new Map<string, FnDecl>()
80
- private readonly reachableFunctions = new Set<string>()
81
- private readonly usedConstants = new Set<string>()
82
- private readonly localReads = new Set<string>()
83
- private readonly localDeclIds = new WeakMap<Stmt, string>()
84
- private localIdCounter = 0
85
- readonly warnings: DCEWarning[] = []
86
-
87
- eliminate(program: Program): Program {
88
- this.functionMap.clear()
89
- this.reachableFunctions.clear()
90
- this.usedConstants.clear()
91
- this.localReads.clear()
92
- this.localIdCounter = 0
93
- this.warnings.length = 0
94
-
95
- for (const fn of program.declarations) {
96
- this.functionMap.set(fn.name, fn)
97
- }
98
-
99
- const entryPoints = this.findEntryPoints(program)
100
- if (entryPoints.length === 0) {
101
- for (const fn of program.declarations) {
102
- this.markReachable(fn.name)
103
- }
104
- } else {
105
- for (const fnName of entryPoints) {
106
- this.markReachable(fnName)
107
- }
108
- }
109
-
110
- for (const global of program.globals) {
111
- this.collectExprRefs(global.init, [])
112
- }
113
-
114
- for (const implBlock of program.implBlocks) {
115
- for (const method of implBlock.methods) {
116
- this.collectFunctionRefs(method)
117
- }
118
- }
119
-
120
- return {
121
- ...program,
122
- declarations: program.declarations
123
- .filter(fn => this.reachableFunctions.has(fn.name))
124
- .map(fn => this.transformFunction(fn)),
125
- consts: program.consts.filter(constDecl => this.usedConstants.has(constDecl.name)),
126
- implBlocks: program.implBlocks.map(implBlock => ({
127
- ...implBlock,
128
- methods: implBlock.methods.map(method => this.transformFunction(method)),
129
- })),
130
- }
131
- }
132
-
133
- private findEntryPoints(program: Program): string[] {
134
- const entries = new Set<string>()
135
-
136
- for (const fn of program.declarations) {
137
- // Library functions (from `module library;` or `librarySources`) are
138
- // NOT MC entry points — they're only kept if reachable from user code.
139
- // Exception: decorators like @tick / @load / @on / @keep always force inclusion.
140
- if (!fn.isLibraryFn) {
141
- // All top-level non-library functions are entry points (callable via /function)
142
- // Exception: functions starting with _ are considered private/internal
143
- if (!fn.name.startsWith('_')) {
144
- entries.add(fn.name)
145
- }
146
- }
147
-
148
- // Decorated functions are always entry points regardless of library mode or _ prefix
149
- if (fn.decorators.some(decorator => [
150
- 'tick',
151
- 'load',
152
- 'on',
153
- 'on_trigger',
154
- 'on_advancement',
155
- 'on_craft',
156
- 'on_death',
157
- 'on_login',
158
- 'on_join_team',
159
- 'keep',
160
- ].includes(decorator.name))) {
161
- entries.add(fn.name)
162
- }
163
- }
164
-
165
- return [...entries]
166
- }
167
-
168
- private markReachable(fnName: string): void {
169
- if (this.reachableFunctions.has(fnName)) {
170
- return
171
- }
172
-
173
- const fn = this.functionMap.get(fnName)
174
- if (!fn) {
175
- return
176
- }
177
-
178
- this.reachableFunctions.add(fnName)
179
- this.collectFunctionRefs(fn)
180
-
181
- // @requires("dep") — when fn is reachable, its required dependencies are
182
- // also pulled into the reachable set so they survive DCE.
183
- for (const decorator of fn.decorators) {
184
- if (decorator.name === 'require_on_load') {
185
- for (const arg of decorator.rawArgs ?? []) {
186
- if (arg.kind === 'string') {
187
- this.markReachable(arg.value)
188
- }
189
- }
190
- }
191
- }
192
- }
193
-
194
- private collectFunctionRefs(fn: FnDecl): void {
195
- const scope: ScopeEntry[][] = [fn.params.map(param => ({ id: `param:${fn.name}:${param.name}`, name: param.name }))]
196
- for (const param of fn.params) {
197
- if (param.default) {
198
- this.collectExprRefs(param.default, scope)
199
- }
200
- }
201
- this.collectStmtRefs(fn.body, scope)
202
- }
203
-
204
- private collectStmtRefs(block: Block, scope: ScopeEntry[][]): void {
205
- scope.push([])
206
-
207
- for (const stmt of block) {
208
- this.collectStmtRef(stmt, scope)
209
- }
210
-
211
- scope.pop()
212
- }
213
-
214
- private collectStmtRef(stmt: Stmt, scope: ScopeEntry[][]): void {
215
- switch (stmt.kind) {
216
- case 'let': {
217
- this.collectExprRefs(stmt.init, scope)
218
- const id = `local:${stmt.name}:${this.localIdCounter++}:${(stmt.span?.line ?? 0)}:${(stmt.span?.col ?? 0)}`
219
- this.localDeclIds.set(stmt, id)
220
- scope[scope.length - 1].push({ id, name: stmt.name })
221
- break
222
- }
223
- case 'expr':
224
- this.collectExprRefs(stmt.expr, scope)
225
- break
226
- case 'return':
227
- if (stmt.value) {
228
- this.collectExprRefs(stmt.value, scope)
229
- }
230
- break
231
- case 'if': {
232
- this.collectExprRefs(stmt.cond, scope)
233
- const constant = isConstantBoolean(stmt.cond)
234
- if (constant === true) {
235
- this.collectStmtRefs(stmt.then, scope)
236
- } else if (constant === false) {
237
- if (stmt.else_) {
238
- this.collectStmtRefs(stmt.else_, scope)
239
- }
240
- } else {
241
- this.collectStmtRefs(stmt.then, scope)
242
- if (stmt.else_) {
243
- this.collectStmtRefs(stmt.else_, scope)
244
- }
245
- }
246
- break
247
- }
248
- case 'while':
249
- this.collectExprRefs(stmt.cond, scope)
250
- this.collectStmtRefs(stmt.body, scope)
251
- break
252
- case 'for':
253
- scope.push([])
254
- if (stmt.init) {
255
- this.collectStmtRef(stmt.init, scope)
256
- }
257
- this.collectExprRefs(stmt.cond, scope)
258
- this.collectExprRefs(stmt.step, scope)
259
- this.collectStmtRefs(stmt.body, scope)
260
- scope.pop()
261
- break
262
- case 'foreach':
263
- this.collectExprRefs(stmt.iterable, scope)
264
- scope.push([{ id: `foreach:${stmt.binding}:${stmt.span?.line ?? 0}:${stmt.span?.col ?? 0}`, name: stmt.binding }])
265
- this.collectStmtRefs(stmt.body, scope)
266
- scope.pop()
267
- break
268
- case 'for_range':
269
- this.collectExprRefs(stmt.start, scope)
270
- this.collectExprRefs(stmt.end, scope)
271
- scope.push([{ id: `range:${stmt.varName}:${stmt.span?.line ?? 0}:${stmt.span?.col ?? 0}`, name: stmt.varName }])
272
- this.collectStmtRefs(stmt.body, scope)
273
- scope.pop()
274
- break
275
- case 'match':
276
- this.collectExprRefs(stmt.expr, scope)
277
- for (const arm of stmt.arms) {
278
- if (arm.pattern) {
279
- this.collectExprRefs(arm.pattern, scope)
280
- }
281
- this.collectStmtRefs(arm.body, scope)
282
- }
283
- break
284
- case 'as_block':
285
- case 'at_block':
286
- case 'as_at':
287
- case 'execute':
288
- this.collectNestedStmtRefs(stmt, scope)
289
- break
290
- case 'raw':
291
- case 'break':
292
- case 'continue':
293
- break
294
- }
295
- }
296
-
297
- private collectNestedStmtRefs(
298
- stmt: Extract<Stmt, { kind: 'as_block' | 'at_block' | 'as_at' | 'execute' }>,
299
- scope: ScopeEntry[][]
300
- ): void {
301
- if (stmt.kind === 'execute') {
302
- for (const sub of stmt.subcommands) {
303
- if ('varName' in sub && sub.varName) {
304
- const resolved = this.resolveLocal(sub.varName, scope)
305
- if (resolved) {
306
- this.localReads.add(resolved.id)
307
- }
308
- }
309
- }
310
- }
311
- this.collectStmtRefs(stmt.body, scope)
312
- }
313
-
314
- private collectExprRefs(expr: Expr, scope: ScopeEntry[][]): void {
315
- switch (expr.kind) {
316
- case 'ident': {
317
- const resolved = this.resolveLocal(expr.name, scope)
318
- if (resolved) {
319
- this.localReads.add(resolved.id)
320
- } else {
321
- this.usedConstants.add(expr.name)
322
- }
323
- break
324
- }
325
- case 'call':
326
- {
327
- const resolved = this.resolveLocal(expr.fn, scope)
328
- if (resolved) {
329
- this.localReads.add(resolved.id)
330
- } else if (this.functionMap.has(expr.fn)) {
331
- this.markReachable(expr.fn)
332
- }
333
- }
334
- for (const arg of expr.args) {
335
- this.collectExprRefs(arg, scope)
336
- }
337
- break
338
- case 'static_call':
339
- for (const arg of expr.args) {
340
- this.collectExprRefs(arg, scope)
341
- }
342
- break
343
- case 'invoke':
344
- this.collectExprRefs(expr.callee, scope)
345
- for (const arg of expr.args) {
346
- this.collectExprRefs(arg, scope)
347
- }
348
- break
349
- case 'member':
350
- this.collectExprRefs(expr.obj, scope)
351
- break
352
- case 'member_assign':
353
- this.collectExprRefs(expr.obj, scope)
354
- this.collectExprRefs(expr.value, scope)
355
- break
356
- case 'index':
357
- this.collectExprRefs(expr.obj, scope)
358
- this.collectExprRefs(expr.index, scope)
359
- break
360
- case 'array_lit':
361
- expr.elements.forEach(element => this.collectExprRefs(element, scope))
362
- break
363
- case 'struct_lit':
364
- expr.fields.forEach(field => this.collectExprRefs(field.value, scope))
365
- break
366
- case 'binary':
367
- this.collectExprRefs(expr.left, scope)
368
- this.collectExprRefs(expr.right, scope)
369
- break
370
- case 'is_check':
371
- this.collectExprRefs(expr.expr, scope)
372
- break
373
- case 'unary':
374
- this.collectExprRefs(expr.operand, scope)
375
- break
376
- case 'assign': {
377
- this.collectExprRefs(expr.value, scope)
378
- break
379
- }
380
- case 'str_interp':
381
- expr.parts.forEach(part => {
382
- if (typeof part !== 'string') {
383
- this.collectExprRefs(part, scope)
384
- }
385
- })
386
- break
387
- case 'f_string':
388
- expr.parts.forEach(part => {
389
- if (part.kind === 'expr') {
390
- this.collectExprRefs(part.expr, scope)
391
- }
392
- })
393
- break
394
- case 'lambda': {
395
- const lambdaScope: ScopeEntry[][] = [
396
- ...scope.map(entries => [...entries]),
397
- expr.params.map(param => ({ id: `lambda:${param.name}:${expr.span?.line ?? 0}:${expr.span?.col ?? 0}`, name: param.name })),
398
- ]
399
- if (Array.isArray(expr.body)) {
400
- this.collectStmtRefs(expr.body, lambdaScope)
401
- } else {
402
- this.collectExprRefs(expr.body, lambdaScope)
403
- }
404
- break
405
- }
406
- case 'blockpos':
407
- case 'bool_lit':
408
- case 'byte_lit':
409
- case 'double_lit':
410
- case 'float_lit':
411
- case 'int_lit':
412
- case 'long_lit':
413
- case 'mc_name':
414
- case 'range_lit':
415
- case 'selector':
416
- case 'short_lit':
417
- case 'str_lit':
418
- break
419
- }
420
- }
421
-
422
- private resolveLocal(name: string, scope: ScopeEntry[][]): ScopeEntry | null {
423
- for (let i = scope.length - 1; i >= 0; i--) {
424
- for (let j = scope[i].length - 1; j >= 0; j--) {
425
- if (scope[i][j].name === name) {
426
- return scope[i][j]
427
- }
428
- }
429
- }
430
- return null
431
- }
432
-
433
- private transformFunction(fn: FnDecl): FnDecl {
434
- const scope: ScopeEntry[][] = [fn.params.map(param => ({ id: `param:${fn.name}:${param.name}`, name: param.name }))]
435
- const body = this.transformBlock(fn.body, scope)
436
- return body === fn.body ? fn : copySpan({ ...fn, body }, fn)
437
- }
438
-
439
- private transformBlock(block: Block, scope: ScopeEntry[][]): Block {
440
- scope.push([])
441
- const transformed: Stmt[] = []
442
-
443
- for (const stmt of block) {
444
- const next = this.transformStmt(stmt, scope)
445
- transformed.push(...next)
446
- }
447
-
448
- scope.pop()
449
- return transformed
450
- }
451
-
452
- private transformStmt(stmt: Stmt, scope: ScopeEntry[][]): Stmt[] {
453
- switch (stmt.kind) {
454
- case 'let': {
455
- const init = this.transformExpr(stmt.init, scope)
456
- const id = this.localDeclIds.get(stmt) ?? `local:${stmt.name}:${stmt.span?.line ?? 0}:${stmt.span?.col ?? 0}`
457
- scope[scope.length - 1].push({ id, name: stmt.name })
458
- if (this.localReads.has(id)) {
459
- if (init === stmt.init) {
460
- return [stmt]
461
- }
462
- return [copySpan({ ...stmt, init }, stmt)]
463
- }
464
- // Unused variable - emit warning
465
- this.warnings.push({
466
- message: `Unused variable '${stmt.name}'`,
467
- code: 'W_UNUSED_VAR',
468
- line: stmt.span?.line,
469
- col: stmt.span?.col,
470
- })
471
- if (isPureExpr(init)) {
472
- return []
473
- }
474
- return [copySpan({ kind: 'expr', expr: init }, stmt)]
475
- }
476
- case 'expr': {
477
- const expr = this.transformExpr(stmt.expr, scope)
478
- if (expr.kind === 'assign') {
479
- const resolved = this.resolveLocal(expr.target, scope)
480
- if (resolved && !this.localReads.has(resolved.id)) {
481
- if (isPureExpr(expr.value)) {
482
- return []
483
- }
484
- return [copySpan({ kind: 'expr', expr: expr.value }, stmt)]
485
- }
486
- }
487
- if (expr === stmt.expr) {
488
- return [stmt]
489
- }
490
- return [copySpan({ ...stmt, expr }, stmt)]
491
- }
492
- case 'return': {
493
- if (!stmt.value) {
494
- return [stmt]
495
- }
496
- const value = this.transformExpr(stmt.value, scope)
497
- if (value === stmt.value) {
498
- return [stmt]
499
- }
500
- return [copySpan({ ...stmt, value }, stmt)]
501
- }
502
- case 'if': {
503
- const cond = this.transformExpr(stmt.cond, scope)
504
- const constant = isConstantBoolean(cond)
505
- if (constant === true) {
506
- return this.transformBlock(stmt.then, scope)
507
- }
508
- if (constant === false) {
509
- return stmt.else_ ? this.transformBlock(stmt.else_, scope) : []
510
- }
511
- const thenBlock = this.transformBlock(stmt.then, scope)
512
- const elseBlock = stmt.else_ ? this.transformBlock(stmt.else_, scope) : undefined
513
- if (cond === stmt.cond && thenBlock === stmt.then && elseBlock === stmt.else_) {
514
- return [stmt]
515
- }
516
- return [copySpan({ ...stmt, cond, then: thenBlock, else_: elseBlock }, stmt)]
517
- }
518
- case 'while': {
519
- const cond = this.transformExpr(stmt.cond, scope)
520
- if (isConstantBoolean(cond) === false) {
521
- return []
522
- }
523
- const body = this.transformBlock(stmt.body, scope)
524
- return [copySpan({ ...stmt, cond, body }, stmt)]
525
- }
526
- case 'for': {
527
- const forScope: ScopeEntry[][] = [...scope, []]
528
- const init = stmt.init ? this.transformStmt(stmt.init, forScope)[0] : undefined
529
- const cond = this.transformExpr(stmt.cond, forScope)
530
- if (isConstantBoolean(cond) === false) {
531
- return init ? [init] : []
532
- }
533
- const step = this.transformExpr(stmt.step, forScope)
534
- const body = this.transformBlock(stmt.body, forScope)
535
- return [copySpan({ ...stmt, init, cond, step, body }, stmt)]
536
- }
537
- case 'foreach': {
538
- const iterable = this.transformExpr(stmt.iterable, scope)
539
- const foreachScope: ScopeEntry[][] = [...scope, [{ id: `foreach:${stmt.binding}:${stmt.span?.line ?? 0}:${stmt.span?.col ?? 0}`, name: stmt.binding }]]
540
- const body = this.transformBlock(stmt.body, foreachScope)
541
- return [copySpan({ ...stmt, iterable, body }, stmt)]
542
- }
543
- case 'for_range': {
544
- const start = this.transformExpr(stmt.start, scope)
545
- const end = this.transformExpr(stmt.end, scope)
546
- const rangeScope: ScopeEntry[][] = [...scope, [{ id: `range:${stmt.varName}:${stmt.span?.line ?? 0}:${stmt.span?.col ?? 0}`, name: stmt.varName }]]
547
- const body = this.transformBlock(stmt.body, rangeScope)
548
- return [copySpan({ ...stmt, start, end, body }, stmt)]
549
- }
550
- case 'match': {
551
- const expr = this.transformExpr(stmt.expr, scope)
552
- const arms = stmt.arms.map(arm => ({
553
- pattern: arm.pattern ? this.transformExpr(arm.pattern, scope) : null,
554
- body: this.transformBlock(arm.body, scope),
555
- }))
556
- return [copySpan({ ...stmt, expr, arms }, stmt)]
557
- }
558
- case 'as_block':
559
- return [copySpan({ ...stmt, body: this.transformBlock(stmt.body, scope) }, stmt)]
560
- case 'at_block':
561
- return [copySpan({ ...stmt, body: this.transformBlock(stmt.body, scope) }, stmt)]
562
- case 'as_at':
563
- return [copySpan({ ...stmt, body: this.transformBlock(stmt.body, scope) }, stmt)]
564
- case 'execute':
565
- return [copySpan({ ...stmt, body: this.transformBlock(stmt.body, scope) }, stmt)]
566
- case 'raw':
567
- return [stmt]
568
- case 'break':
569
- return [stmt]
570
- case 'continue':
571
- return [stmt]
572
- }
573
- }
574
-
575
- private transformExpr(expr: Expr, scope: ScopeEntry[][]): Expr {
576
- switch (expr.kind) {
577
- case 'call':
578
- return copySpan({ ...expr, args: expr.args.map(arg => this.transformExpr(arg, scope)) }, expr)
579
- case 'static_call':
580
- return copySpan({ ...expr, args: expr.args.map(arg => this.transformExpr(arg, scope)) }, expr)
581
- case 'invoke':
582
- return copySpan({
583
- ...expr,
584
- callee: this.transformExpr(expr.callee, scope),
585
- args: expr.args.map(arg => this.transformExpr(arg, scope)),
586
- }, expr)
587
- case 'binary':
588
- return copySpan({
589
- ...expr,
590
- left: this.transformExpr(expr.left, scope),
591
- right: this.transformExpr(expr.right, scope),
592
- }, expr)
593
- case 'is_check':
594
- return copySpan({ ...expr, expr: this.transformExpr(expr.expr, scope) }, expr)
595
- case 'unary':
596
- return copySpan({ ...expr, operand: this.transformExpr(expr.operand, scope) }, expr)
597
- case 'assign':
598
- return copySpan({ ...expr, value: this.transformExpr(expr.value, scope) }, expr)
599
- case 'member':
600
- return copySpan({ ...expr, obj: this.transformExpr(expr.obj, scope) }, expr)
601
- case 'member_assign':
602
- return copySpan({
603
- ...expr,
604
- obj: this.transformExpr(expr.obj, scope),
605
- value: this.transformExpr(expr.value, scope),
606
- }, expr)
607
- case 'index':
608
- return copySpan({
609
- ...expr,
610
- obj: this.transformExpr(expr.obj, scope),
611
- index: this.transformExpr(expr.index, scope),
612
- }, expr)
613
- case 'array_lit':
614
- return copySpan({ ...expr, elements: expr.elements.map(element => this.transformExpr(element, scope)) }, expr)
615
- case 'struct_lit':
616
- return copySpan({
617
- ...expr,
618
- fields: expr.fields.map(field => ({ ...field, value: this.transformExpr(field.value, scope) })),
619
- }, expr)
620
- case 'str_interp':
621
- return copySpan({
622
- ...expr,
623
- parts: expr.parts.map(part => typeof part === 'string' ? part : this.transformExpr(part, scope)),
624
- }, expr)
625
- case 'f_string':
626
- return copySpan({
627
- ...expr,
628
- parts: expr.parts.map(part => part.kind === 'text' ? part : { kind: 'expr', expr: this.transformExpr(part.expr, scope) }),
629
- }, expr)
630
- case 'lambda': {
631
- const lambdaScope: ScopeEntry[][] = [
632
- ...scope.map(entries => [...entries]),
633
- expr.params.map(param => ({ id: `lambda:${param.name}:${expr.span?.line ?? 0}:${expr.span?.col ?? 0}`, name: param.name })),
634
- ]
635
- const body = Array.isArray(expr.body)
636
- ? this.transformBlock(expr.body, lambdaScope)
637
- : this.transformExpr(expr.body, lambdaScope)
638
- return copySpan({ ...expr, body }, expr)
639
- }
640
- case 'blockpos':
641
- case 'bool_lit':
642
- case 'byte_lit':
643
- case 'double_lit':
644
- case 'float_lit':
645
- case 'ident':
646
- case 'int_lit':
647
- case 'long_lit':
648
- case 'mc_name':
649
- case 'range_lit':
650
- case 'rel_coord':
651
- case 'local_coord':
652
- case 'selector':
653
- case 'short_lit':
654
- case 'str_lit':
655
- return expr
656
- }
657
- }
658
- }
659
-
660
- export function eliminateDeadCode(
661
- program: Program,
662
- sourceRanges?: import('../compile').SourceRange[]
663
- ): { program: Program; warnings: DCEWarning[] } {
664
- const eliminator = new DeadCodeEliminator()
665
- const result = eliminator.eliminate(program)
666
- let warnings = eliminator.warnings
667
-
668
- // Resolve combined-source line numbers back to original file + line
669
- if (sourceRanges && sourceRanges.length > 0) {
670
- const { resolveSourceLine } = require('../compile') as typeof import('../compile')
671
- warnings = warnings.map(w => {
672
- if (w.line == null) return w
673
- const resolved = resolveSourceLine(w.line, sourceRanges)
674
- return { ...w, line: resolved.line, filePath: resolved.filePath ?? w.filePath }
675
- })
676
- }
677
-
678
- return { program: result, warnings }
679
- }