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.
- package/.claude/commands/build-test.md +10 -0
- package/.claude/commands/deploy-demo.md +12 -0
- package/.claude/commands/stage-status.md +13 -0
- package/.claude/settings.json +12 -0
- package/.github/workflows/ci.yml +1 -0
- package/CLAUDE.md +231 -0
- package/README.md +29 -28
- package/README.zh.md +28 -28
- package/demo.gif +0 -0
- package/dist/cli.js +2 -554
- package/dist/compile.js +2 -266
- package/dist/index.js +2 -159
- package/dist/lexer/index.js +9 -1
- package/dist/lowering/index.js +22 -5
- package/dist/src/__tests__/cli.test.d.ts +1 -0
- package/dist/src/__tests__/cli.test.js +104 -0
- package/dist/src/__tests__/codegen.test.d.ts +1 -0
- package/dist/src/__tests__/codegen.test.js +152 -0
- package/dist/src/__tests__/compile-all.test.d.ts +10 -0
- package/dist/src/__tests__/compile-all.test.js +108 -0
- package/dist/src/__tests__/dce.test.d.ts +1 -0
- package/dist/src/__tests__/dce.test.js +102 -0
- package/dist/src/__tests__/diagnostics.test.d.ts +4 -0
- package/dist/src/__tests__/diagnostics.test.js +177 -0
- package/dist/src/__tests__/e2e.test.d.ts +6 -0
- package/dist/src/__tests__/e2e.test.js +1789 -0
- package/dist/src/__tests__/entity-types.test.d.ts +1 -0
- package/dist/src/__tests__/entity-types.test.js +203 -0
- package/dist/src/__tests__/formatter.test.d.ts +1 -0
- package/dist/src/__tests__/formatter.test.js +40 -0
- package/dist/src/__tests__/lexer.test.d.ts +1 -0
- package/dist/src/__tests__/lexer.test.js +343 -0
- package/dist/src/__tests__/lowering.test.d.ts +1 -0
- package/dist/src/__tests__/lowering.test.js +1015 -0
- package/dist/src/__tests__/macro.test.d.ts +8 -0
- package/dist/src/__tests__/macro.test.js +306 -0
- package/dist/src/__tests__/mc-integration.test.d.ts +12 -0
- package/dist/src/__tests__/mc-integration.test.js +817 -0
- package/dist/src/__tests__/mc-syntax.test.d.ts +1 -0
- package/dist/src/__tests__/mc-syntax.test.js +124 -0
- package/dist/src/__tests__/nbt.test.d.ts +1 -0
- package/dist/src/__tests__/nbt.test.js +82 -0
- package/dist/src/__tests__/optimizer-advanced.test.d.ts +1 -0
- package/dist/src/__tests__/optimizer-advanced.test.js +124 -0
- package/dist/src/__tests__/optimizer.test.d.ts +1 -0
- package/dist/src/__tests__/optimizer.test.js +149 -0
- package/dist/src/__tests__/parser.test.d.ts +1 -0
- package/dist/src/__tests__/parser.test.js +807 -0
- package/dist/src/__tests__/repl.test.d.ts +1 -0
- package/dist/src/__tests__/repl.test.js +27 -0
- package/dist/src/__tests__/runtime.test.d.ts +1 -0
- package/dist/src/__tests__/runtime.test.js +289 -0
- package/dist/src/__tests__/stdlib-advanced.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib-advanced.test.js +374 -0
- package/dist/src/__tests__/stdlib-bigint.test.d.ts +7 -0
- package/dist/src/__tests__/stdlib-bigint.test.js +426 -0
- package/dist/src/__tests__/stdlib-math.test.d.ts +7 -0
- package/dist/src/__tests__/stdlib-math.test.js +351 -0
- package/dist/src/__tests__/stdlib-vec.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib-vec.test.js +263 -0
- package/dist/src/__tests__/structure-optimizer.test.d.ts +1 -0
- package/dist/src/__tests__/structure-optimizer.test.js +33 -0
- package/dist/src/__tests__/typechecker.test.d.ts +1 -0
- package/dist/src/__tests__/typechecker.test.js +552 -0
- package/dist/src/__tests__/var-allocator.test.d.ts +1 -0
- package/dist/src/__tests__/var-allocator.test.js +69 -0
- package/dist/src/ast/types.d.ts +515 -0
- package/dist/src/ast/types.js +9 -0
- package/dist/src/builtins/metadata.d.ts +36 -0
- package/dist/src/builtins/metadata.js +1014 -0
- package/dist/src/cli.d.ts +11 -0
- package/dist/src/cli.js +443 -0
- package/dist/src/codegen/cmdblock/index.d.ts +26 -0
- package/dist/src/codegen/cmdblock/index.js +45 -0
- package/dist/src/codegen/mcfunction/index.d.ts +40 -0
- package/dist/src/codegen/mcfunction/index.js +606 -0
- package/dist/src/codegen/structure/index.d.ts +24 -0
- package/dist/src/codegen/structure/index.js +279 -0
- package/dist/src/codegen/var-allocator.d.ts +45 -0
- package/dist/src/codegen/var-allocator.js +104 -0
- package/dist/src/compile.d.ts +37 -0
- package/dist/src/compile.js +165 -0
- package/dist/src/diagnostics/index.d.ts +44 -0
- package/dist/src/diagnostics/index.js +140 -0
- package/dist/src/events/types.d.ts +35 -0
- package/dist/src/events/types.js +59 -0
- package/dist/src/formatter/index.d.ts +1 -0
- package/dist/src/formatter/index.js +26 -0
- package/dist/src/index.d.ts +22 -0
- package/dist/src/index.js +45 -0
- package/dist/src/ir/builder.d.ts +33 -0
- package/dist/src/ir/builder.js +99 -0
- package/dist/src/ir/types.d.ts +132 -0
- package/dist/src/ir/types.js +15 -0
- package/dist/src/lexer/index.d.ts +37 -0
- package/dist/src/lexer/index.js +569 -0
- package/dist/src/lowering/index.d.ts +188 -0
- package/dist/src/lowering/index.js +3405 -0
- package/dist/src/mc-test/client.d.ts +128 -0
- package/dist/src/mc-test/client.js +174 -0
- package/dist/src/mc-test/runner.d.ts +28 -0
- package/dist/src/mc-test/runner.js +151 -0
- package/dist/src/mc-test/setup.d.ts +11 -0
- package/dist/src/mc-test/setup.js +98 -0
- package/dist/src/mc-validator/index.d.ts +17 -0
- package/dist/src/mc-validator/index.js +322 -0
- package/dist/src/nbt/index.d.ts +86 -0
- package/dist/src/nbt/index.js +250 -0
- package/dist/src/optimizer/commands.d.ts +38 -0
- package/dist/src/optimizer/commands.js +451 -0
- package/dist/src/optimizer/dce.d.ts +34 -0
- package/dist/src/optimizer/dce.js +639 -0
- package/dist/src/optimizer/passes.d.ts +34 -0
- package/dist/src/optimizer/passes.js +243 -0
- package/dist/src/optimizer/structure.d.ts +9 -0
- package/dist/src/optimizer/structure.js +356 -0
- package/dist/src/parser/index.d.ts +93 -0
- package/dist/src/parser/index.js +1687 -0
- package/dist/src/repl.d.ts +16 -0
- package/dist/src/repl.js +165 -0
- package/dist/src/runtime/index.d.ts +107 -0
- package/dist/src/runtime/index.js +1409 -0
- package/dist/src/typechecker/index.d.ts +61 -0
- package/dist/src/typechecker/index.js +1034 -0
- package/dist/src/types/entity-hierarchy.d.ts +29 -0
- package/dist/src/types/entity-hierarchy.js +107 -0
- package/dist/src2/__tests__/e2e/basic.test.d.ts +8 -0
- package/dist/src2/__tests__/e2e/basic.test.js +140 -0
- package/dist/src2/__tests__/e2e/macros.test.d.ts +9 -0
- package/dist/src2/__tests__/e2e/macros.test.js +182 -0
- package/dist/src2/__tests__/e2e/migrate.test.d.ts +13 -0
- package/dist/src2/__tests__/e2e/migrate.test.js +2739 -0
- package/dist/src2/__tests__/hir/desugar.test.d.ts +1 -0
- package/dist/src2/__tests__/hir/desugar.test.js +234 -0
- package/dist/src2/__tests__/lir/lower.test.d.ts +1 -0
- package/dist/src2/__tests__/lir/lower.test.js +559 -0
- package/dist/src2/__tests__/lir/types.test.d.ts +1 -0
- package/dist/src2/__tests__/lir/types.test.js +185 -0
- package/dist/src2/__tests__/lir/verify.test.d.ts +1 -0
- package/dist/src2/__tests__/lir/verify.test.js +221 -0
- package/dist/src2/__tests__/mir/arithmetic.test.d.ts +1 -0
- package/dist/src2/__tests__/mir/arithmetic.test.js +130 -0
- package/dist/src2/__tests__/mir/control-flow.test.d.ts +1 -0
- package/dist/src2/__tests__/mir/control-flow.test.js +205 -0
- package/dist/src2/__tests__/mir/verify.test.d.ts +1 -0
- package/dist/src2/__tests__/mir/verify.test.js +223 -0
- package/dist/src2/__tests__/optimizer/block_merge.test.d.ts +1 -0
- package/dist/src2/__tests__/optimizer/block_merge.test.js +78 -0
- package/dist/src2/__tests__/optimizer/branch_simplify.test.d.ts +1 -0
- package/dist/src2/__tests__/optimizer/branch_simplify.test.js +58 -0
- package/dist/src2/__tests__/optimizer/constant_fold.test.d.ts +1 -0
- package/dist/src2/__tests__/optimizer/constant_fold.test.js +131 -0
- package/dist/src2/__tests__/optimizer/copy_prop.test.d.ts +1 -0
- package/dist/src2/__tests__/optimizer/copy_prop.test.js +91 -0
- package/dist/src2/__tests__/optimizer/dce.test.d.ts +1 -0
- package/dist/src2/__tests__/optimizer/dce.test.js +76 -0
- package/dist/src2/__tests__/optimizer/pipeline.test.d.ts +1 -0
- package/dist/src2/__tests__/optimizer/pipeline.test.js +102 -0
- package/dist/src2/emit/compile.d.ts +19 -0
- package/dist/src2/emit/compile.js +80 -0
- package/dist/src2/emit/index.d.ts +17 -0
- package/dist/src2/emit/index.js +172 -0
- package/dist/src2/hir/lower.d.ts +15 -0
- package/dist/src2/hir/lower.js +378 -0
- package/dist/src2/hir/types.d.ts +373 -0
- package/dist/src2/hir/types.js +16 -0
- package/dist/src2/lir/lower.d.ts +15 -0
- package/dist/src2/lir/lower.js +453 -0
- package/dist/src2/lir/types.d.ts +136 -0
- package/dist/src2/lir/types.js +11 -0
- package/dist/src2/lir/verify.d.ts +14 -0
- package/dist/src2/lir/verify.js +113 -0
- package/dist/src2/mir/lower.d.ts +9 -0
- package/dist/src2/mir/lower.js +1030 -0
- package/dist/src2/mir/macro.d.ts +22 -0
- package/dist/src2/mir/macro.js +168 -0
- package/dist/src2/mir/types.d.ts +183 -0
- package/dist/src2/mir/types.js +11 -0
- package/dist/src2/mir/verify.d.ts +16 -0
- package/dist/src2/mir/verify.js +216 -0
- package/dist/src2/optimizer/block_merge.d.ts +12 -0
- package/dist/src2/optimizer/block_merge.js +84 -0
- package/dist/src2/optimizer/branch_simplify.d.ts +9 -0
- package/dist/src2/optimizer/branch_simplify.js +28 -0
- package/dist/src2/optimizer/constant_fold.d.ts +10 -0
- package/dist/src2/optimizer/constant_fold.js +85 -0
- package/dist/src2/optimizer/copy_prop.d.ts +9 -0
- package/dist/src2/optimizer/copy_prop.js +113 -0
- package/dist/src2/optimizer/dce.d.ts +8 -0
- package/dist/src2/optimizer/dce.js +155 -0
- package/dist/src2/optimizer/pipeline.d.ts +10 -0
- package/dist/src2/optimizer/pipeline.js +42 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/docs/compiler-pipeline-redesign.md +2243 -0
- package/docs/optimization-ideas.md +1076 -0
- package/editors/vscode/package-lock.json +3 -3
- package/editors/vscode/package.json +1 -1
- package/examples/readme-demo.mcrs +44 -66
- package/jest.config.js +1 -1
- package/package.json +6 -5
- package/scripts/postbuild.js +15 -0
- package/src/__tests__/cli.test.ts +8 -220
- package/src/__tests__/dce.test.ts +11 -56
- package/src/__tests__/diagnostics.test.ts +59 -38
- package/src/__tests__/mc-integration.test.ts +1 -2
- package/src/ast/types.ts +6 -1
- package/src/cli.ts +29 -156
- package/src/compile.ts +6 -162
- package/src/index.ts +14 -178
- package/src/lexer/index.ts +9 -1
- package/src/mc-test/runner.ts +4 -3
- package/src/parser/index.ts +1 -1
- package/src/repl.ts +1 -1
- package/src/runtime/index.ts +1 -1
- package/src2/__tests__/e2e/basic.test.ts +154 -0
- package/src2/__tests__/e2e/macros.test.ts +199 -0
- package/src2/__tests__/e2e/migrate.test.ts +3008 -0
- package/src2/__tests__/hir/desugar.test.ts +263 -0
- package/src2/__tests__/lir/lower.test.ts +619 -0
- package/src2/__tests__/lir/types.test.ts +207 -0
- package/src2/__tests__/lir/verify.test.ts +249 -0
- package/src2/__tests__/mir/arithmetic.test.ts +156 -0
- package/src2/__tests__/mir/control-flow.test.ts +242 -0
- package/src2/__tests__/mir/verify.test.ts +254 -0
- package/src2/__tests__/optimizer/block_merge.test.ts +84 -0
- package/src2/__tests__/optimizer/branch_simplify.test.ts +64 -0
- package/src2/__tests__/optimizer/constant_fold.test.ts +145 -0
- package/src2/__tests__/optimizer/copy_prop.test.ts +99 -0
- package/src2/__tests__/optimizer/dce.test.ts +83 -0
- package/src2/__tests__/optimizer/pipeline.test.ts +116 -0
- package/src2/emit/compile.ts +99 -0
- package/src2/emit/index.ts +222 -0
- package/src2/hir/lower.ts +428 -0
- package/src2/hir/types.ts +216 -0
- package/src2/lir/lower.ts +556 -0
- package/src2/lir/types.ts +109 -0
- package/src2/lir/verify.ts +129 -0
- package/src2/mir/lower.ts +1160 -0
- package/src2/mir/macro.ts +167 -0
- package/src2/mir/types.ts +106 -0
- package/src2/mir/verify.ts +218 -0
- package/src2/optimizer/block_merge.ts +93 -0
- package/src2/optimizer/branch_simplify.ts +27 -0
- package/src2/optimizer/constant_fold.ts +88 -0
- package/src2/optimizer/copy_prop.ts +106 -0
- package/src2/optimizer/dce.ts +133 -0
- package/src2/optimizer/pipeline.ts +44 -0
- package/tsconfig.json +2 -2
- package/src/__tests__/codegen.test.ts +0 -161
- package/src/__tests__/e2e.test.ts +0 -2039
- package/src/__tests__/entity-types.test.ts +0 -236
- package/src/__tests__/lowering.test.ts +0 -1185
- package/src/__tests__/macro.test.ts +0 -343
- package/src/__tests__/nbt.test.ts +0 -58
- package/src/__tests__/optimizer-advanced.test.ts +0 -144
- package/src/__tests__/optimizer.test.ts +0 -162
- package/src/__tests__/runtime.test.ts +0 -305
- package/src/__tests__/stdlib-advanced.test.ts +0 -379
- package/src/__tests__/stdlib-bigint.test.ts +0 -427
- package/src/__tests__/stdlib-math.test.ts +0 -374
- package/src/__tests__/stdlib-vec.test.ts +0 -259
- package/src/__tests__/structure-optimizer.test.ts +0 -38
- package/src/__tests__/var-allocator.test.ts +0 -75
- package/src/codegen/cmdblock/index.ts +0 -63
- package/src/codegen/mcfunction/index.ts +0 -662
- package/src/codegen/structure/index.ts +0 -346
- package/src/codegen/var-allocator.ts +0 -104
- package/src/ir/builder.ts +0 -116
- package/src/ir/types.ts +0 -134
- package/src/lowering/index.ts +0 -3860
- package/src/optimizer/commands.ts +0 -534
- package/src/optimizer/dce.ts +0 -679
- package/src/optimizer/passes.ts +0 -250
- package/src/optimizer/structure.ts +0 -450
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { constantFold } from '../../optimizer/constant_fold'
|
|
2
|
+
import type { MIRFunction, MIRBlock, MIRInstr, Operand } from '../../mir/types'
|
|
3
|
+
|
|
4
|
+
function mkFn(blocks: MIRBlock[]): MIRFunction {
|
|
5
|
+
return { name: 'test', params: [], blocks, entry: 'entry', isMacro: false }
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function mkBlock(id: string, instrs: MIRInstr[], term: MIRInstr): MIRBlock {
|
|
9
|
+
return { id, instrs, term, preds: [] }
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const c = (v: number): Operand => ({ kind: 'const', value: v })
|
|
13
|
+
const t = (n: string): Operand => ({ kind: 'temp', name: n })
|
|
14
|
+
|
|
15
|
+
describe('constant folding', () => {
|
|
16
|
+
test('folds add(const, const)', () => {
|
|
17
|
+
const fn = mkFn([
|
|
18
|
+
mkBlock('entry', [
|
|
19
|
+
{ kind: 'add', dst: 't0', a: c(3), b: c(4) },
|
|
20
|
+
], { kind: 'return', value: t('t0') }),
|
|
21
|
+
])
|
|
22
|
+
const result = constantFold(fn)
|
|
23
|
+
expect(result.blocks[0].instrs[0]).toEqual({ kind: 'const', dst: 't0', value: 7 })
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test('folds sub(const, const)', () => {
|
|
27
|
+
const fn = mkFn([
|
|
28
|
+
mkBlock('entry', [
|
|
29
|
+
{ kind: 'sub', dst: 't0', a: c(10), b: c(3) },
|
|
30
|
+
], { kind: 'return', value: t('t0') }),
|
|
31
|
+
])
|
|
32
|
+
const result = constantFold(fn)
|
|
33
|
+
expect(result.blocks[0].instrs[0]).toEqual({ kind: 'const', dst: 't0', value: 7 })
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test('folds mul(const, const)', () => {
|
|
37
|
+
const fn = mkFn([
|
|
38
|
+
mkBlock('entry', [
|
|
39
|
+
{ kind: 'mul', dst: 't0', a: c(3), b: c(5) },
|
|
40
|
+
], { kind: 'return', value: t('t0') }),
|
|
41
|
+
])
|
|
42
|
+
const result = constantFold(fn)
|
|
43
|
+
expect(result.blocks[0].instrs[0]).toEqual({ kind: 'const', dst: 't0', value: 15 })
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test('folds div(const, const) with truncation', () => {
|
|
47
|
+
const fn = mkFn([
|
|
48
|
+
mkBlock('entry', [
|
|
49
|
+
{ kind: 'div', dst: 't0', a: c(7), b: c(2) },
|
|
50
|
+
], { kind: 'return', value: t('t0') }),
|
|
51
|
+
])
|
|
52
|
+
const result = constantFold(fn)
|
|
53
|
+
expect(result.blocks[0].instrs[0]).toEqual({ kind: 'const', dst: 't0', value: 3 })
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test('does not fold div by zero', () => {
|
|
57
|
+
const fn = mkFn([
|
|
58
|
+
mkBlock('entry', [
|
|
59
|
+
{ kind: 'div', dst: 't0', a: c(7), b: c(0) },
|
|
60
|
+
], { kind: 'return', value: t('t0') }),
|
|
61
|
+
])
|
|
62
|
+
const result = constantFold(fn)
|
|
63
|
+
expect(result.blocks[0].instrs[0].kind).toBe('div')
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test('folds mod(const, const)', () => {
|
|
67
|
+
const fn = mkFn([
|
|
68
|
+
mkBlock('entry', [
|
|
69
|
+
{ kind: 'mod', dst: 't0', a: c(7), b: c(3) },
|
|
70
|
+
], { kind: 'return', value: t('t0') }),
|
|
71
|
+
])
|
|
72
|
+
const result = constantFold(fn)
|
|
73
|
+
expect(result.blocks[0].instrs[0]).toEqual({ kind: 'const', dst: 't0', value: 1 })
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
test('folds neg(const)', () => {
|
|
77
|
+
const fn = mkFn([
|
|
78
|
+
mkBlock('entry', [
|
|
79
|
+
{ kind: 'neg', dst: 't0', src: c(5) },
|
|
80
|
+
], { kind: 'return', value: t('t0') }),
|
|
81
|
+
])
|
|
82
|
+
const result = constantFold(fn)
|
|
83
|
+
expect(result.blocks[0].instrs[0]).toEqual({ kind: 'const', dst: 't0', value: -5 })
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
test('folds not(0) → 1', () => {
|
|
87
|
+
const fn = mkFn([
|
|
88
|
+
mkBlock('entry', [
|
|
89
|
+
{ kind: 'not', dst: 't0', src: c(0) },
|
|
90
|
+
], { kind: 'return', value: t('t0') }),
|
|
91
|
+
])
|
|
92
|
+
const result = constantFold(fn)
|
|
93
|
+
expect(result.blocks[0].instrs[0]).toEqual({ kind: 'const', dst: 't0', value: 1 })
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
test('folds cmp(lt, 3, 4) → 1', () => {
|
|
97
|
+
const fn = mkFn([
|
|
98
|
+
mkBlock('entry', [
|
|
99
|
+
{ kind: 'cmp', dst: 't0', op: 'lt', a: c(3), b: c(4) },
|
|
100
|
+
], { kind: 'return', value: t('t0') }),
|
|
101
|
+
])
|
|
102
|
+
const result = constantFold(fn)
|
|
103
|
+
expect(result.blocks[0].instrs[0]).toEqual({ kind: 'const', dst: 't0', value: 1 })
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
test('folds cmp(eq, 5, 5) → 1', () => {
|
|
107
|
+
const fn = mkFn([
|
|
108
|
+
mkBlock('entry', [
|
|
109
|
+
{ kind: 'cmp', dst: 't0', op: 'eq', a: c(5), b: c(5) },
|
|
110
|
+
], { kind: 'return', value: t('t0') }),
|
|
111
|
+
])
|
|
112
|
+
const result = constantFold(fn)
|
|
113
|
+
expect(result.blocks[0].instrs[0]).toEqual({ kind: 'const', dst: 't0', value: 1 })
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
test('folds and(1, 0) → 0', () => {
|
|
117
|
+
const fn = mkFn([
|
|
118
|
+
mkBlock('entry', [
|
|
119
|
+
{ kind: 'and', dst: 't0', a: c(1), b: c(0) },
|
|
120
|
+
], { kind: 'return', value: t('t0') }),
|
|
121
|
+
])
|
|
122
|
+
const result = constantFold(fn)
|
|
123
|
+
expect(result.blocks[0].instrs[0]).toEqual({ kind: 'const', dst: 't0', value: 0 })
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
test('folds or(0, 1) → 1', () => {
|
|
127
|
+
const fn = mkFn([
|
|
128
|
+
mkBlock('entry', [
|
|
129
|
+
{ kind: 'or', dst: 't0', a: c(0), b: c(1) },
|
|
130
|
+
], { kind: 'return', value: t('t0') }),
|
|
131
|
+
])
|
|
132
|
+
const result = constantFold(fn)
|
|
133
|
+
expect(result.blocks[0].instrs[0]).toEqual({ kind: 'const', dst: 't0', value: 1 })
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
test('does not fold when operand is temp', () => {
|
|
137
|
+
const fn = mkFn([
|
|
138
|
+
mkBlock('entry', [
|
|
139
|
+
{ kind: 'add', dst: 't0', a: t('a'), b: c(4) },
|
|
140
|
+
], { kind: 'return', value: t('t0') }),
|
|
141
|
+
])
|
|
142
|
+
const result = constantFold(fn)
|
|
143
|
+
expect(result.blocks[0].instrs[0].kind).toBe('add')
|
|
144
|
+
})
|
|
145
|
+
})
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { copyProp } from '../../optimizer/copy_prop'
|
|
2
|
+
import type { MIRFunction, MIRBlock, MIRInstr, Operand } from '../../mir/types'
|
|
3
|
+
|
|
4
|
+
function mkFn(blocks: MIRBlock[]): MIRFunction {
|
|
5
|
+
return { name: 'test', params: [], blocks, entry: 'entry', isMacro: false }
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function mkBlock(id: string, instrs: MIRInstr[], term: MIRInstr): MIRBlock {
|
|
9
|
+
return { id, instrs, term, preds: [] }
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const c = (v: number): Operand => ({ kind: 'const', value: v })
|
|
13
|
+
const t = (n: string): Operand => ({ kind: 'temp', name: n })
|
|
14
|
+
|
|
15
|
+
describe('copy propagation', () => {
|
|
16
|
+
test('propagates copy into subsequent use', () => {
|
|
17
|
+
const fn = mkFn([
|
|
18
|
+
mkBlock('entry', [
|
|
19
|
+
{ kind: 'copy', dst: 'x', src: t('y') },
|
|
20
|
+
{ kind: 'add', dst: 'z', a: t('x'), b: c(1) },
|
|
21
|
+
], { kind: 'return', value: t('z') }),
|
|
22
|
+
])
|
|
23
|
+
const result = copyProp(fn)
|
|
24
|
+
const add = result.blocks[0].instrs[1]
|
|
25
|
+
expect(add.kind).toBe('add')
|
|
26
|
+
expect((add as any).a).toEqual(t('y'))
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test('propagates into terminator', () => {
|
|
30
|
+
const fn = mkFn([
|
|
31
|
+
mkBlock('entry', [
|
|
32
|
+
{ kind: 'copy', dst: 'x', src: t('y') },
|
|
33
|
+
], { kind: 'return', value: t('x') }),
|
|
34
|
+
])
|
|
35
|
+
const result = copyProp(fn)
|
|
36
|
+
expect(result.blocks[0].term).toEqual({ kind: 'return', value: t('y') })
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test('invalidates mapping when source is redefined', () => {
|
|
40
|
+
const fn = mkFn([
|
|
41
|
+
mkBlock('entry', [
|
|
42
|
+
{ kind: 'copy', dst: 'x', src: t('y') },
|
|
43
|
+
{ kind: 'const', dst: 'y', value: 99 },
|
|
44
|
+
{ kind: 'add', dst: 'z', a: t('x'), b: c(1) },
|
|
45
|
+
], { kind: 'return', value: t('z') }),
|
|
46
|
+
])
|
|
47
|
+
const result = copyProp(fn)
|
|
48
|
+
// x's mapping was invalidated because y was redefined
|
|
49
|
+
const add = result.blocks[0].instrs[2]
|
|
50
|
+
expect((add as any).a).toEqual(t('x'))
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
test('propagates const definitions into uses', () => {
|
|
54
|
+
const fn = mkFn([
|
|
55
|
+
mkBlock('entry', [
|
|
56
|
+
{ kind: 'const', dst: 'x', value: 42 },
|
|
57
|
+
{ kind: 'add', dst: 'z', a: t('x'), b: c(1) },
|
|
58
|
+
], { kind: 'return', value: t('z') }),
|
|
59
|
+
])
|
|
60
|
+
const result = copyProp(fn)
|
|
61
|
+
const add = result.blocks[0].instrs[1]
|
|
62
|
+
expect((add as any).a).toEqual(c(42))
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
test('propagates copy-of-const into uses', () => {
|
|
66
|
+
const fn = mkFn([
|
|
67
|
+
mkBlock('entry', [
|
|
68
|
+
{ kind: 'copy', dst: 'x', src: c(42) },
|
|
69
|
+
{ kind: 'add', dst: 'z', a: t('x'), b: c(1) },
|
|
70
|
+
], { kind: 'return', value: t('z') }),
|
|
71
|
+
])
|
|
72
|
+
const result = copyProp(fn)
|
|
73
|
+
const add = result.blocks[0].instrs[1]
|
|
74
|
+
expect((add as any).a).toEqual(c(42))
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test('chains propagation: x=y, z=x → z uses y', () => {
|
|
78
|
+
const fn = mkFn([
|
|
79
|
+
mkBlock('entry', [
|
|
80
|
+
{ kind: 'copy', dst: 'x', src: t('y') },
|
|
81
|
+
{ kind: 'copy', dst: 'z', src: t('x') },
|
|
82
|
+
], { kind: 'return', value: t('z') }),
|
|
83
|
+
])
|
|
84
|
+
const result = copyProp(fn)
|
|
85
|
+
// z = copy x → rewritten to z = copy y
|
|
86
|
+
// then return z → rewritten to return y
|
|
87
|
+
expect(result.blocks[0].term).toEqual({ kind: 'return', value: t('y') })
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
test('propagates into branch condition', () => {
|
|
91
|
+
const fn = mkFn([
|
|
92
|
+
mkBlock('entry', [
|
|
93
|
+
{ kind: 'copy', dst: 'c', src: t('flag') },
|
|
94
|
+
], { kind: 'branch', cond: t('c'), then: 'b1', else: 'b2' }),
|
|
95
|
+
])
|
|
96
|
+
const result = copyProp(fn)
|
|
97
|
+
expect(result.blocks[0].term).toEqual({ kind: 'branch', cond: t('flag'), then: 'b1', else: 'b2' })
|
|
98
|
+
})
|
|
99
|
+
})
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { dce } from '../../optimizer/dce'
|
|
2
|
+
import type { MIRFunction, MIRBlock, MIRInstr, Operand } from '../../mir/types'
|
|
3
|
+
|
|
4
|
+
function mkFn(blocks: MIRBlock[], entry = 'entry'): MIRFunction {
|
|
5
|
+
return { name: 'test', params: [], blocks, entry, isMacro: false }
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function mkBlock(id: string, instrs: MIRInstr[], term: MIRInstr, preds: string[] = []): MIRBlock {
|
|
9
|
+
return { id, instrs, term, preds }
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const c = (v: number): Operand => ({ kind: 'const', value: v })
|
|
13
|
+
const t = (n: string): Operand => ({ kind: 'temp', name: n })
|
|
14
|
+
|
|
15
|
+
describe('dead code elimination', () => {
|
|
16
|
+
test('removes unused temp definition', () => {
|
|
17
|
+
const fn = mkFn([
|
|
18
|
+
mkBlock('entry', [
|
|
19
|
+
{ kind: 'const', dst: 'dead', value: 42 },
|
|
20
|
+
{ kind: 'const', dst: 'live', value: 1 },
|
|
21
|
+
], { kind: 'return', value: t('live') }),
|
|
22
|
+
])
|
|
23
|
+
const result = dce(fn)
|
|
24
|
+
expect(result.blocks[0].instrs).toHaveLength(1)
|
|
25
|
+
expect(result.blocks[0].instrs[0]).toEqual({ kind: 'const', dst: 'live', value: 1 })
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('keeps side-effectful instructions even if dst unused', () => {
|
|
29
|
+
const fn = mkFn([
|
|
30
|
+
mkBlock('entry', [
|
|
31
|
+
{ kind: 'call', dst: 'unused', fn: 'sideEffect', args: [] },
|
|
32
|
+
], { kind: 'return', value: null }),
|
|
33
|
+
])
|
|
34
|
+
const result = dce(fn)
|
|
35
|
+
expect(result.blocks[0].instrs).toHaveLength(1)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test('removes unreachable blocks', () => {
|
|
39
|
+
const fn = mkFn([
|
|
40
|
+
mkBlock('entry', [], { kind: 'return', value: null }),
|
|
41
|
+
mkBlock('dead_block', [
|
|
42
|
+
{ kind: 'const', dst: 't0', value: 99 },
|
|
43
|
+
], { kind: 'return', value: t('t0') }),
|
|
44
|
+
])
|
|
45
|
+
const result = dce(fn)
|
|
46
|
+
expect(result.blocks).toHaveLength(1)
|
|
47
|
+
expect(result.blocks[0].id).toBe('entry')
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
test('keeps reachable blocks', () => {
|
|
51
|
+
const fn = mkFn([
|
|
52
|
+
mkBlock('entry', [
|
|
53
|
+
{ kind: 'const', dst: 't0', value: 1 },
|
|
54
|
+
], { kind: 'branch', cond: t('t0'), then: 'b1', else: 'b2' }),
|
|
55
|
+
mkBlock('b1', [], { kind: 'return', value: null }, ['entry']),
|
|
56
|
+
mkBlock('b2', [], { kind: 'return', value: null }, ['entry']),
|
|
57
|
+
])
|
|
58
|
+
const result = dce(fn)
|
|
59
|
+
expect(result.blocks).toHaveLength(3)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
test('recomputes preds after block removal', () => {
|
|
63
|
+
const fn = mkFn([
|
|
64
|
+
mkBlock('entry', [], { kind: 'jump', target: 'b1' }),
|
|
65
|
+
mkBlock('b1', [], { kind: 'return', value: null }, ['entry']),
|
|
66
|
+
mkBlock('dead', [], { kind: 'jump', target: 'b1' }),
|
|
67
|
+
])
|
|
68
|
+
const result = dce(fn)
|
|
69
|
+
expect(result.blocks).toHaveLength(2)
|
|
70
|
+
const b1 = result.blocks.find(b => b.id === 'b1')!
|
|
71
|
+
expect(b1.preds).toEqual(['entry'])
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
test('keeps nbt_write even though it has no dst', () => {
|
|
75
|
+
const fn = mkFn([
|
|
76
|
+
mkBlock('entry', [
|
|
77
|
+
{ kind: 'nbt_write', ns: 'rs:data', path: 'x', type: 'int', scale: 1, src: c(5) },
|
|
78
|
+
], { kind: 'return', value: null }),
|
|
79
|
+
])
|
|
80
|
+
const result = dce(fn)
|
|
81
|
+
expect(result.blocks[0].instrs).toHaveLength(1)
|
|
82
|
+
})
|
|
83
|
+
})
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { optimizeFunction, optimizeModule } from '../../optimizer/pipeline'
|
|
2
|
+
import type { MIRFunction, MIRBlock, MIRInstr, MIRModule, Operand } from '../../mir/types'
|
|
3
|
+
|
|
4
|
+
function mkFn(blocks: MIRBlock[], entry = 'entry'): MIRFunction {
|
|
5
|
+
return { name: 'test', params: [], blocks, entry, isMacro: false }
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function mkBlock(id: string, instrs: MIRInstr[], term: MIRInstr, preds: string[] = []): MIRBlock {
|
|
9
|
+
return { id, instrs, term, preds }
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const c = (v: number): Operand => ({ kind: 'const', value: v })
|
|
13
|
+
const t = (n: string): Operand => ({ kind: 'temp', name: n })
|
|
14
|
+
|
|
15
|
+
describe('optimization pipeline', () => {
|
|
16
|
+
test('constant fold + branch simplify + DCE removes dead branch', () => {
|
|
17
|
+
// cmp(lt, 1, 2) → 1 → branch(1, then, else) → jump(then) → else is dead
|
|
18
|
+
const fn = mkFn([
|
|
19
|
+
mkBlock('entry', [
|
|
20
|
+
{ kind: 'cmp', dst: 't0', op: 'lt', a: c(1), b: c(2) },
|
|
21
|
+
], { kind: 'branch', cond: t('t0'), then: 'then', else: 'else' }),
|
|
22
|
+
mkBlock('then', [], { kind: 'return', value: c(1) }, ['entry']),
|
|
23
|
+
mkBlock('else', [], { kind: 'return', value: c(0) }, ['entry']),
|
|
24
|
+
])
|
|
25
|
+
|
|
26
|
+
const result = optimizeFunction(fn)
|
|
27
|
+
|
|
28
|
+
// After optimization: entry should return 1 directly, else block removed
|
|
29
|
+
// The cmp folds to const 1, branch simplifies to jump(then),
|
|
30
|
+
// else block becomes unreachable and is removed,
|
|
31
|
+
// then block merges into entry
|
|
32
|
+
expect(result.blocks).toHaveLength(1)
|
|
33
|
+
expect(result.blocks[0].term).toEqual({ kind: 'return', value: c(1) })
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test('copy prop + const fold + DCE eliminates dead copy and folds', () => {
|
|
37
|
+
const fn = mkFn([
|
|
38
|
+
mkBlock('entry', [
|
|
39
|
+
{ kind: 'const', dst: 'a', value: 5 },
|
|
40
|
+
{ kind: 'copy', dst: 'b', src: t('a') },
|
|
41
|
+
{ kind: 'add', dst: 'c', a: t('b'), b: c(1) },
|
|
42
|
+
], { kind: 'return', value: t('c') }),
|
|
43
|
+
])
|
|
44
|
+
|
|
45
|
+
const result = optimizeFunction(fn)
|
|
46
|
+
|
|
47
|
+
// const a=5 propagated into copy and add, add(5,1) folded to 6
|
|
48
|
+
// all dead defs removed, returns const 6
|
|
49
|
+
expect(result.blocks[0].term).toEqual({ kind: 'return', value: c(6) })
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
test('full pipeline: fold + simplify + merge + dce', () => {
|
|
53
|
+
// if (3 > 2) { return 10 + 20; } else { return 0; }
|
|
54
|
+
const fn = mkFn([
|
|
55
|
+
mkBlock('entry', [
|
|
56
|
+
{ kind: 'cmp', dst: 't0', op: 'gt', a: c(3), b: c(2) },
|
|
57
|
+
], { kind: 'branch', cond: t('t0'), then: 'then', else: 'else' }),
|
|
58
|
+
mkBlock('then', [
|
|
59
|
+
{ kind: 'add', dst: 't1', a: c(10), b: c(20) },
|
|
60
|
+
], { kind: 'return', value: t('t1') }, ['entry']),
|
|
61
|
+
mkBlock('else', [], { kind: 'return', value: c(0) }, ['entry']),
|
|
62
|
+
])
|
|
63
|
+
|
|
64
|
+
const result = optimizeFunction(fn)
|
|
65
|
+
|
|
66
|
+
// Everything folds away: single block returning const 30
|
|
67
|
+
expect(result.blocks).toHaveLength(1)
|
|
68
|
+
expect(result.blocks[0].term).toEqual({ kind: 'return', value: c(30) })
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
test('optimizeModule applies to all functions', () => {
|
|
72
|
+
const mod: MIRModule = {
|
|
73
|
+
namespace: 'test',
|
|
74
|
+
objective: '__test',
|
|
75
|
+
functions: [
|
|
76
|
+
mkFn([
|
|
77
|
+
mkBlock('entry', [
|
|
78
|
+
{ kind: 'add', dst: 't0', a: c(1), b: c(2) },
|
|
79
|
+
], { kind: 'return', value: t('t0') }),
|
|
80
|
+
]),
|
|
81
|
+
mkFn([
|
|
82
|
+
mkBlock('entry', [
|
|
83
|
+
{ kind: 'mul', dst: 't0', a: c(3), b: c(4) },
|
|
84
|
+
], { kind: 'return', value: t('t0') }),
|
|
85
|
+
]),
|
|
86
|
+
],
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const result = optimizeModule(mod)
|
|
90
|
+
// Both functions should have their constants folded
|
|
91
|
+
for (const fn of result.functions) {
|
|
92
|
+
const instrs = fn.blocks[0].instrs
|
|
93
|
+
const hasArith = instrs.some(i => i.kind === 'add' || i.kind === 'mul')
|
|
94
|
+
expect(hasArith).toBe(false)
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
test('fixpoint: multiple iterations needed', () => {
|
|
99
|
+
// First iteration: fold add → const, fold cmp → const
|
|
100
|
+
// Second iteration: branch simplify on newly-const cond
|
|
101
|
+
// Third iteration: DCE removes dead block, merge
|
|
102
|
+
const fn = mkFn([
|
|
103
|
+
mkBlock('entry', [
|
|
104
|
+
{ kind: 'add', dst: 't0', a: c(1), b: c(1) },
|
|
105
|
+
{ kind: 'cmp', dst: 't1', op: 'eq', a: t('t0'), b: c(2) },
|
|
106
|
+
], { kind: 'branch', cond: t('t1'), then: 'yes', else: 'no' }),
|
|
107
|
+
mkBlock('yes', [], { kind: 'return', value: c(1) }, ['entry']),
|
|
108
|
+
mkBlock('no', [], { kind: 'return', value: c(0) }, ['entry']),
|
|
109
|
+
])
|
|
110
|
+
|
|
111
|
+
const result = optimizeFunction(fn)
|
|
112
|
+
// 1+1=2, 2==2 → 1, branch(1) → jump(yes), dead block removed, merged
|
|
113
|
+
expect(result.blocks).toHaveLength(1)
|
|
114
|
+
expect(result.blocks[0].term).toEqual({ kind: 'return', value: c(1) })
|
|
115
|
+
})
|
|
116
|
+
})
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Top-level compile function for the v2 pipeline.
|
|
3
|
+
*
|
|
4
|
+
* Pipeline: source → Lexer → Parser → HIR → MIR → optimize → LIR → emit
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Lexer } from '../../src/lexer'
|
|
8
|
+
import { Parser } from '../../src/parser'
|
|
9
|
+
import { preprocessSourceWithMetadata } from '../../src/compile'
|
|
10
|
+
import { lowerToHIR } from '../hir/lower'
|
|
11
|
+
import { lowerToMIR } from '../mir/lower'
|
|
12
|
+
import { optimizeModule } from '../optimizer/pipeline'
|
|
13
|
+
import { lowerToLIR } from '../lir/lower'
|
|
14
|
+
import { emit, type DatapackFile } from './index'
|
|
15
|
+
|
|
16
|
+
export interface CompileOptions {
|
|
17
|
+
namespace?: string
|
|
18
|
+
filePath?: string
|
|
19
|
+
/** v1 compat: inline library sources (treated as `module library;` imports) */
|
|
20
|
+
librarySources?: string[]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface CompileResult {
|
|
24
|
+
files: DatapackFile[]
|
|
25
|
+
warnings: string[]
|
|
26
|
+
/** Always true — v1 compat shim (compile() throws on error) */
|
|
27
|
+
readonly success: true
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function compile(source: string, options: CompileOptions = {}): CompileResult {
|
|
31
|
+
const { namespace = 'redscript', filePath } = options
|
|
32
|
+
const warnings: string[] = []
|
|
33
|
+
|
|
34
|
+
// Preprocess: resolve import directives, merge imported sources
|
|
35
|
+
const preprocessed = preprocessSourceWithMetadata(source, { filePath })
|
|
36
|
+
const processedSource = preprocessed.source
|
|
37
|
+
|
|
38
|
+
// Stage 1: Lex + Parse → AST
|
|
39
|
+
const lexer = new Lexer(processedSource)
|
|
40
|
+
const tokens = lexer.tokenize()
|
|
41
|
+
const parser = new Parser(tokens, processedSource, filePath)
|
|
42
|
+
const ast = parser.parse(namespace)
|
|
43
|
+
|
|
44
|
+
// Merge library imports (files with `module library;`) into AST
|
|
45
|
+
for (const li of preprocessed.libraryImports ?? []) {
|
|
46
|
+
const libPreprocessed = preprocessSourceWithMetadata(li.source, { filePath: li.filePath })
|
|
47
|
+
const libTokens = new Lexer(libPreprocessed.source, li.filePath).tokenize()
|
|
48
|
+
const libAst = new Parser(libTokens, libPreprocessed.source, li.filePath).parse(namespace)
|
|
49
|
+
for (const fn of libAst.declarations) fn.isLibraryFn = true
|
|
50
|
+
ast.declarations.push(...libAst.declarations)
|
|
51
|
+
ast.structs.push(...libAst.structs)
|
|
52
|
+
ast.implBlocks.push(...libAst.implBlocks)
|
|
53
|
+
ast.enums.push(...libAst.enums)
|
|
54
|
+
ast.consts.push(...libAst.consts)
|
|
55
|
+
ast.globals.push(...libAst.globals)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Merge librarySources (v1 compat: inline library strings) before HIR
|
|
59
|
+
if (options.librarySources) {
|
|
60
|
+
for (const libSrc of options.librarySources) {
|
|
61
|
+
const libTokens = new Lexer(libSrc).tokenize()
|
|
62
|
+
const libAst = new Parser(libTokens, libSrc).parse(namespace)
|
|
63
|
+
for (const fn of libAst.declarations) fn.isLibraryFn = true
|
|
64
|
+
ast.declarations.push(...libAst.declarations)
|
|
65
|
+
ast.structs.push(...libAst.structs)
|
|
66
|
+
ast.implBlocks.push(...libAst.implBlocks)
|
|
67
|
+
ast.enums.push(...libAst.enums)
|
|
68
|
+
ast.consts.push(...libAst.consts)
|
|
69
|
+
ast.globals.push(...libAst.globals)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Stage 2: AST → HIR
|
|
74
|
+
const hir = lowerToHIR(ast)
|
|
75
|
+
|
|
76
|
+
// Extract @tick and @load functions from HIR (before decorator info is lost)
|
|
77
|
+
const tickFunctions: string[] = []
|
|
78
|
+
const loadFunctions: string[] = []
|
|
79
|
+
for (const fn of hir.functions) {
|
|
80
|
+
for (const dec of fn.decorators) {
|
|
81
|
+
if (dec.name === 'tick') tickFunctions.push(fn.name)
|
|
82
|
+
if (dec.name === 'load') loadFunctions.push(fn.name)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Stage 3: HIR → MIR
|
|
87
|
+
const mir = lowerToMIR(hir)
|
|
88
|
+
|
|
89
|
+
// Stage 4: MIR optimization
|
|
90
|
+
const mirOpt = optimizeModule(mir)
|
|
91
|
+
|
|
92
|
+
// Stage 5: MIR → LIR
|
|
93
|
+
const lir = lowerToLIR(mirOpt)
|
|
94
|
+
|
|
95
|
+
// Stage 7: LIR → .mcfunction
|
|
96
|
+
const files = emit(lir, { namespace, tickFunctions, loadFunctions })
|
|
97
|
+
|
|
98
|
+
return { files, warnings, success: true as const }
|
|
99
|
+
}
|