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
|
@@ -1,346 +0,0 @@
|
|
|
1
|
-
import { Lexer } from '../../lexer'
|
|
2
|
-
import { Parser } from '../../parser'
|
|
3
|
-
import { Lowering } from '../../lowering'
|
|
4
|
-
import { nbt, TagType, writeNbt, type CompoundTag, type NbtTag } from '../../nbt'
|
|
5
|
-
import { createEmptyOptimizationStats, mergeOptimizationStats, type OptimizationStats } from '../../optimizer/commands'
|
|
6
|
-
import { optimizeWithStats } from '../../optimizer/passes'
|
|
7
|
-
import { optimizeForStructure, optimizeForStructureWithStats } from '../../optimizer/structure'
|
|
8
|
-
import { eliminateDeadCode } from '../../optimizer/dce'
|
|
9
|
-
import { preprocessSource } from '../../compile'
|
|
10
|
-
import type { IRCommand, IRFunction, IRModule } from '../../ir/types'
|
|
11
|
-
import type { DatapackFile } from '../mcfunction'
|
|
12
|
-
import { EVENT_TYPES, isEventTypeName, type EventTypeName } from '../../events/types'
|
|
13
|
-
import { VarAllocator } from '../var-allocator'
|
|
14
|
-
|
|
15
|
-
const DATA_VERSION = 3953
|
|
16
|
-
const MAX_WIDTH = 16
|
|
17
|
-
const OBJ = 'rs'
|
|
18
|
-
|
|
19
|
-
const PALETTE_IMPULSE = 0
|
|
20
|
-
const PALETTE_CHAIN_UNCONDITIONAL = 1
|
|
21
|
-
const PALETTE_CHAIN_CONDITIONAL = 2
|
|
22
|
-
const PALETTE_REPEAT = 3
|
|
23
|
-
|
|
24
|
-
const palette = [
|
|
25
|
-
{ Name: 'minecraft:command_block', Properties: { conditional: 'false', facing: 'east' } },
|
|
26
|
-
{ Name: 'minecraft:chain_command_block', Properties: { conditional: 'false', facing: 'east' } },
|
|
27
|
-
{ Name: 'minecraft:chain_command_block', Properties: { conditional: 'true', facing: 'east' } },
|
|
28
|
-
{ Name: 'minecraft:repeating_command_block', Properties: { conditional: 'false', facing: 'east' } },
|
|
29
|
-
{ Name: 'minecraft:air', Properties: {} },
|
|
30
|
-
]
|
|
31
|
-
|
|
32
|
-
interface CommandEntry {
|
|
33
|
-
functionName: string
|
|
34
|
-
lineNumber: number
|
|
35
|
-
command: string
|
|
36
|
-
state: number
|
|
37
|
-
conditional: boolean
|
|
38
|
-
isRepeat: boolean
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface StructureBlockInfo {
|
|
42
|
-
command: string
|
|
43
|
-
conditional: boolean
|
|
44
|
-
state: number
|
|
45
|
-
functionName: string
|
|
46
|
-
lineNumber: number
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export interface StructureCompileResult {
|
|
50
|
-
buffer: Buffer
|
|
51
|
-
blockCount: number
|
|
52
|
-
blocks: StructureBlockInfo[]
|
|
53
|
-
stats?: OptimizationStats
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export interface StructureCompileOptions {
|
|
57
|
-
dce?: boolean
|
|
58
|
-
mangle?: boolean
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function escapeJsonString(value: string): string {
|
|
62
|
-
return JSON.stringify(value).slice(1, -1)
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function collectConsts(fn: IRFunction): Set<number> {
|
|
66
|
-
const consts = new Set<number>()
|
|
67
|
-
for (const block of fn.blocks) {
|
|
68
|
-
for (const instr of block.instrs) {
|
|
69
|
-
if (instr.op === 'assign' && instr.src.kind === 'const') consts.add(instr.src.value)
|
|
70
|
-
if (instr.op === 'binop') {
|
|
71
|
-
if (instr.lhs.kind === 'const') consts.add(instr.lhs.value)
|
|
72
|
-
if (instr.rhs.kind === 'const') consts.add(instr.rhs.value)
|
|
73
|
-
}
|
|
74
|
-
if (instr.op === 'cmp') {
|
|
75
|
-
if (instr.lhs.kind === 'const') consts.add(instr.lhs.value)
|
|
76
|
-
if (instr.rhs.kind === 'const') consts.add(instr.rhs.value)
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
if (block.term.op === 'return' && block.term.value?.kind === 'const') {
|
|
80
|
-
consts.add(block.term.value.value)
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return consts
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function collectCommandEntriesFromModule(module: IRModule, mangle = false): CommandEntry[] {
|
|
87
|
-
const alloc = new VarAllocator(mangle)
|
|
88
|
-
const entries: CommandEntry[] = []
|
|
89
|
-
const triggerHandlers = module.functions.filter(fn => fn.isTriggerHandler && fn.triggerName)
|
|
90
|
-
const triggerNames = new Set(triggerHandlers.map(fn => fn.triggerName!))
|
|
91
|
-
const eventHandlers = module.functions.filter((fn): fn is IRFunction & { eventHandler: { eventType: EventTypeName; tag: string } } =>
|
|
92
|
-
!!fn.eventHandler && isEventTypeName(fn.eventHandler.eventType)
|
|
93
|
-
)
|
|
94
|
-
const eventTypes = new Set<EventTypeName>(eventHandlers.map(fn => fn.eventHandler.eventType))
|
|
95
|
-
const loadCommands = [
|
|
96
|
-
`scoreboard objectives add ${OBJ} dummy`,
|
|
97
|
-
...module.globals.map(g => `scoreboard players set ${alloc.alloc(g.name)} ${OBJ} ${g.init}`),
|
|
98
|
-
...Array.from(triggerNames).flatMap(triggerName => [
|
|
99
|
-
`scoreboard objectives add ${triggerName} trigger`,
|
|
100
|
-
`scoreboard players enable @a ${triggerName}`,
|
|
101
|
-
]),
|
|
102
|
-
...Array.from(
|
|
103
|
-
new Set(module.functions.flatMap(fn => Array.from(collectConsts(fn))))
|
|
104
|
-
).map(value => `scoreboard players set ${alloc.constant(value)} ${OBJ} ${value}`),
|
|
105
|
-
]
|
|
106
|
-
|
|
107
|
-
for (const eventType of eventTypes) {
|
|
108
|
-
if (eventType === 'PlayerDeath') {
|
|
109
|
-
loadCommands.push('scoreboard objectives add rs.deaths deathCount')
|
|
110
|
-
} else if (eventType === 'EntityKill') {
|
|
111
|
-
loadCommands.push('scoreboard objectives add rs.kills totalKillCount')
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Call @load functions from __load
|
|
116
|
-
for (const fn of module.functions) {
|
|
117
|
-
if (fn.isLoadInit) {
|
|
118
|
-
loadCommands.push(`function ${module.namespace}:${fn.name}`)
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const sections: Array<{ name: string; commands: IRCommand[]; repeat?: boolean }> = []
|
|
123
|
-
|
|
124
|
-
if (loadCommands.length > 0) {
|
|
125
|
-
sections.push({
|
|
126
|
-
name: '__load',
|
|
127
|
-
commands: loadCommands.map(cmd => ({ cmd })),
|
|
128
|
-
})
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
for (const triggerName of triggerNames) {
|
|
132
|
-
const handlers = triggerHandlers.filter(fn => fn.triggerName === triggerName)
|
|
133
|
-
sections.push({
|
|
134
|
-
name: `__trigger_${triggerName}_dispatch`,
|
|
135
|
-
commands: [
|
|
136
|
-
...handlers.map(handler => ({ cmd: `function ${module.namespace}:${handler.name}` })),
|
|
137
|
-
{ cmd: `scoreboard players set @s ${triggerName} 0` },
|
|
138
|
-
{ cmd: `scoreboard players enable @s ${triggerName}` },
|
|
139
|
-
],
|
|
140
|
-
})
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
for (const fn of module.functions) {
|
|
144
|
-
if (!fn.commands || fn.commands.length === 0) continue
|
|
145
|
-
sections.push({
|
|
146
|
-
name: fn.name,
|
|
147
|
-
commands: fn.commands,
|
|
148
|
-
})
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const tickCommands: IRCommand[] = []
|
|
152
|
-
for (const fn of module.functions.filter(candidate => candidate.isTickLoop)) {
|
|
153
|
-
tickCommands.push({ cmd: `function ${module.namespace}:${fn.name}` })
|
|
154
|
-
}
|
|
155
|
-
if (triggerNames.size > 0) {
|
|
156
|
-
for (const triggerName of triggerNames) {
|
|
157
|
-
tickCommands.push({
|
|
158
|
-
cmd: `execute as @a[scores={${triggerName}=1..}] run function ${module.namespace}:__trigger_${triggerName}_dispatch`,
|
|
159
|
-
})
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
if (eventHandlers.length > 0) {
|
|
163
|
-
for (const eventType of eventTypes) {
|
|
164
|
-
const tag = EVENT_TYPES[eventType].tag
|
|
165
|
-
const handlers = eventHandlers.filter(fn => fn.eventHandler?.eventType === eventType)
|
|
166
|
-
for (const handler of handlers) {
|
|
167
|
-
tickCommands.push({
|
|
168
|
-
cmd: `execute as @a[tag=${tag}] run function ${module.namespace}:${handler.name}`,
|
|
169
|
-
})
|
|
170
|
-
}
|
|
171
|
-
tickCommands.push({
|
|
172
|
-
cmd: `tag @a[tag=${tag}] remove ${tag}`,
|
|
173
|
-
})
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
if (tickCommands.length > 0) {
|
|
177
|
-
sections.push({
|
|
178
|
-
name: '__tick',
|
|
179
|
-
commands: tickCommands,
|
|
180
|
-
repeat: true,
|
|
181
|
-
})
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
for (const section of sections) {
|
|
185
|
-
for (let i = 0; i < section.commands.length; i++) {
|
|
186
|
-
const command = section.commands[i]
|
|
187
|
-
const state =
|
|
188
|
-
i === 0
|
|
189
|
-
? (section.repeat ? PALETTE_REPEAT : PALETTE_IMPULSE)
|
|
190
|
-
: (command.conditional ? PALETTE_CHAIN_CONDITIONAL : PALETTE_CHAIN_UNCONDITIONAL)
|
|
191
|
-
|
|
192
|
-
entries.push({
|
|
193
|
-
functionName: section.name,
|
|
194
|
-
lineNumber: i + 1,
|
|
195
|
-
command: command.cmd,
|
|
196
|
-
conditional: Boolean(command.conditional),
|
|
197
|
-
state,
|
|
198
|
-
isRepeat: Boolean(section.repeat && i === 0),
|
|
199
|
-
})
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
return entries
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
function toFunctionName(file: DatapackFile): string | null {
|
|
207
|
-
const match = file.path.match(/^data\/[^/]+\/function\/(.+)\.mcfunction$/)
|
|
208
|
-
return match?.[1] ?? null
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
function collectCommandEntriesFromFiles(files: DatapackFile[]): CommandEntry[] {
|
|
212
|
-
const entries: CommandEntry[] = []
|
|
213
|
-
|
|
214
|
-
for (const file of files) {
|
|
215
|
-
const functionName = toFunctionName(file)
|
|
216
|
-
if (!functionName) continue
|
|
217
|
-
|
|
218
|
-
const lines = file.content.split('\n')
|
|
219
|
-
let isFirstCommand = true
|
|
220
|
-
const isTickFunction = functionName === '__tick'
|
|
221
|
-
|
|
222
|
-
for (let i = 0; i < lines.length; i++) {
|
|
223
|
-
const command = lines[i].trim()
|
|
224
|
-
if (command === '' || command.startsWith('#')) continue
|
|
225
|
-
|
|
226
|
-
const state = isFirstCommand
|
|
227
|
-
? (isTickFunction ? PALETTE_REPEAT : PALETTE_IMPULSE)
|
|
228
|
-
: PALETTE_CHAIN_UNCONDITIONAL
|
|
229
|
-
|
|
230
|
-
entries.push({
|
|
231
|
-
functionName,
|
|
232
|
-
lineNumber: i + 1,
|
|
233
|
-
command,
|
|
234
|
-
conditional: false,
|
|
235
|
-
state,
|
|
236
|
-
isRepeat: isTickFunction && isFirstCommand,
|
|
237
|
-
})
|
|
238
|
-
|
|
239
|
-
isFirstCommand = false
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
return entries
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
function createPaletteTag(): CompoundTag[] {
|
|
247
|
-
return palette.map(entry =>
|
|
248
|
-
nbt.compound({
|
|
249
|
-
Name: nbt.string(entry.Name),
|
|
250
|
-
Properties: nbt.compound(
|
|
251
|
-
Object.fromEntries(
|
|
252
|
-
Object.entries(entry.Properties).map(([key, value]) => [key, nbt.string(value)])
|
|
253
|
-
)
|
|
254
|
-
),
|
|
255
|
-
})
|
|
256
|
-
)
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
function createBlockEntityTag(entry: CommandEntry): NbtTag {
|
|
260
|
-
return nbt.compound({
|
|
261
|
-
id: nbt.string('minecraft:command_block'),
|
|
262
|
-
Command: nbt.string(entry.command),
|
|
263
|
-
auto: nbt.byte(entry.isRepeat ? 1 : 0),
|
|
264
|
-
powered: nbt.byte(0),
|
|
265
|
-
conditionMet: nbt.byte(0),
|
|
266
|
-
UpdateLastExecution: nbt.byte(1),
|
|
267
|
-
LastExecution: nbt.long(0n),
|
|
268
|
-
TrackOutput: nbt.byte(1),
|
|
269
|
-
SuccessCount: nbt.int(0),
|
|
270
|
-
LastOutput: nbt.string(''),
|
|
271
|
-
CustomName: nbt.string(`{"text":"${escapeJsonString(`${entry.functionName}:${entry.lineNumber}`)}"}`),
|
|
272
|
-
})
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
function createBlockTag(entry: CommandEntry, index: number): CompoundTag {
|
|
276
|
-
const x = index % MAX_WIDTH
|
|
277
|
-
const z = Math.floor(index / MAX_WIDTH) % MAX_WIDTH
|
|
278
|
-
const y = Math.floor(index / (MAX_WIDTH * MAX_WIDTH))
|
|
279
|
-
|
|
280
|
-
return nbt.compound({
|
|
281
|
-
pos: nbt.list(TagType.Int, [nbt.int(x), nbt.int(y), nbt.int(z)]),
|
|
282
|
-
state: nbt.int(entry.state),
|
|
283
|
-
nbt: createBlockEntityTag(entry),
|
|
284
|
-
})
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
export function generateStructure(input: IRModule | DatapackFile[], options?: { mangle?: boolean }): StructureCompileResult {
|
|
288
|
-
const entries = Array.isArray(input)
|
|
289
|
-
? collectCommandEntriesFromFiles(input)
|
|
290
|
-
: collectCommandEntriesFromModule(input, options?.mangle)
|
|
291
|
-
|
|
292
|
-
const blockTags = entries.map(createBlockTag)
|
|
293
|
-
const sizeX = Math.max(1, Math.min(MAX_WIDTH, entries.length || 1))
|
|
294
|
-
const sizeZ = Math.max(1, Math.min(MAX_WIDTH, Math.ceil(entries.length / MAX_WIDTH) || 1))
|
|
295
|
-
const sizeY = Math.max(1, Math.ceil(entries.length / (MAX_WIDTH * MAX_WIDTH)) || 1)
|
|
296
|
-
|
|
297
|
-
const root = nbt.compound({
|
|
298
|
-
DataVersion: nbt.int(DATA_VERSION),
|
|
299
|
-
size: nbt.list(TagType.Int, [nbt.int(sizeX), nbt.int(sizeY), nbt.int(sizeZ)]),
|
|
300
|
-
palette: nbt.list(TagType.Compound, createPaletteTag()),
|
|
301
|
-
blocks: nbt.list(TagType.Compound, blockTags),
|
|
302
|
-
entities: nbt.list(TagType.Compound, []),
|
|
303
|
-
})
|
|
304
|
-
|
|
305
|
-
return {
|
|
306
|
-
buffer: writeNbt(root, ''),
|
|
307
|
-
blockCount: entries.length,
|
|
308
|
-
blocks: entries.map(entry => ({
|
|
309
|
-
command: entry.command,
|
|
310
|
-
conditional: entry.conditional,
|
|
311
|
-
state: entry.state,
|
|
312
|
-
functionName: entry.functionName,
|
|
313
|
-
lineNumber: entry.lineNumber,
|
|
314
|
-
})),
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
export function compileToStructure(
|
|
319
|
-
source: string,
|
|
320
|
-
namespace: string,
|
|
321
|
-
filePath?: string,
|
|
322
|
-
options: StructureCompileOptions = {}
|
|
323
|
-
): StructureCompileResult {
|
|
324
|
-
const preprocessedSource = preprocessSource(source, { filePath })
|
|
325
|
-
const tokens = new Lexer(preprocessedSource, filePath).tokenize()
|
|
326
|
-
const parsedAst = new Parser(tokens, preprocessedSource, filePath).parse(namespace)
|
|
327
|
-
const dceResult = options.dce ?? true ? eliminateDeadCode(parsedAst) : { program: parsedAst, warnings: [] }
|
|
328
|
-
const ast = dceResult.program
|
|
329
|
-
const ir = new Lowering(namespace).lower(ast)
|
|
330
|
-
const stats = createEmptyOptimizationStats()
|
|
331
|
-
const optimizedIRFunctions = ir.functions.map(fn => {
|
|
332
|
-
const optimized = optimizeWithStats(fn)
|
|
333
|
-
mergeOptimizationStats(stats, optimized.stats)
|
|
334
|
-
return optimized.fn
|
|
335
|
-
})
|
|
336
|
-
const structureOptimized = optimizeForStructureWithStats(optimizedIRFunctions, namespace)
|
|
337
|
-
mergeOptimizationStats(stats, structureOptimized.stats)
|
|
338
|
-
const optimizedModule: IRModule = {
|
|
339
|
-
...ir,
|
|
340
|
-
functions: structureOptimized.functions,
|
|
341
|
-
}
|
|
342
|
-
return {
|
|
343
|
-
...generateStructure(optimizedModule, { mangle: options.mangle }),
|
|
344
|
-
stats,
|
|
345
|
-
}
|
|
346
|
-
}
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* VarAllocator — assigns scoreboard fake-player names to variables.
|
|
3
|
-
*
|
|
4
|
-
* mangle=true: sequential short names ($a, $b, ..., $z, $aa, $ab, ...)
|
|
5
|
-
* mangle=false: legacy names ($<name> for vars, $const_<v> for consts, $p0/$ret for internals)
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export class VarAllocator {
|
|
9
|
-
private readonly mangle: boolean
|
|
10
|
-
private seq = 0
|
|
11
|
-
private readonly varCache = new Map<string, string>()
|
|
12
|
-
private readonly constCache = new Map<number, string>()
|
|
13
|
-
private readonly internalCache = new Map<string, string>()
|
|
14
|
-
|
|
15
|
-
constructor(mangle = true) {
|
|
16
|
-
this.mangle = mangle
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/** Allocate a name for a user variable. Strips leading '$' if present. */
|
|
20
|
-
alloc(originalName: string): string {
|
|
21
|
-
const clean = originalName.startsWith('$') ? originalName.slice(1) : originalName
|
|
22
|
-
const cached = this.varCache.get(clean)
|
|
23
|
-
if (cached) return cached
|
|
24
|
-
const name = this.mangle ? `$${this.nextSeqName()}` : `$${clean}`
|
|
25
|
-
this.varCache.set(clean, name)
|
|
26
|
-
return name
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** Allocate a name for a constant value (content-addressed). */
|
|
30
|
-
constant(value: number): string {
|
|
31
|
-
const cached = this.constCache.get(value)
|
|
32
|
-
if (cached) return cached
|
|
33
|
-
const name = this.mangle ? `$${this.nextSeqName()}` : `$const_${value}`
|
|
34
|
-
this.constCache.set(value, name)
|
|
35
|
-
return name
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Look up the allocated name for a raw scoreboard fake-player name such as
|
|
40
|
-
* "$_2", "$x", "$p0", or "$ret". Returns the mangled name when mangle=true,
|
|
41
|
-
* or the original name when mangle=false or the name is not yet known.
|
|
42
|
-
*
|
|
43
|
-
* Unlike alloc/internal/constant this does NOT create a new slot — it only
|
|
44
|
-
* resolves names that were already registered. Used by the codegen to
|
|
45
|
-
* rewrite variable references inside `raw` IR instructions.
|
|
46
|
-
*/
|
|
47
|
-
resolve(rawName: string): string {
|
|
48
|
-
const clean = rawName.startsWith('$') ? rawName.slice(1) : rawName
|
|
49
|
-
// Check every cache in priority order: vars, internals, consts
|
|
50
|
-
return (
|
|
51
|
-
this.varCache.get(clean) ??
|
|
52
|
-
this.internalCache.get(clean) ??
|
|
53
|
-
rawName // not registered → return as-is (literal fake player, not a var)
|
|
54
|
-
)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Rewrite all $varname tokens in a raw mcfunction command string so that
|
|
59
|
-
* IR variable names are replaced by their allocated (possibly mangled) names.
|
|
60
|
-
* Tokens that are not registered in the allocator are left untouched (they
|
|
61
|
-
* are literal scoreboard fake-player names like "out" or "#rs").
|
|
62
|
-
*/
|
|
63
|
-
resolveRaw(cmd: string): string {
|
|
64
|
-
return cmd.replace(/\$[A-Za-z_][A-Za-z0-9_]*/g, (tok) => this.resolve(tok))
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/** Allocate a name for a compiler internal (e.g. "ret", "p0"). */
|
|
68
|
-
internal(suffix: string): string {
|
|
69
|
-
const cached = this.internalCache.get(suffix)
|
|
70
|
-
if (cached) return cached
|
|
71
|
-
const name = this.mangle ? `$${this.nextSeqName()}` : `$${suffix}`
|
|
72
|
-
this.internalCache.set(suffix, name)
|
|
73
|
-
return name
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/** Generate the next sequential name: a, b, ..., z, aa, ab, ..., az, ba, ... */
|
|
77
|
-
private nextSeqName(): string {
|
|
78
|
-
const n = this.seq++
|
|
79
|
-
let result = ''
|
|
80
|
-
let remaining = n
|
|
81
|
-
do {
|
|
82
|
-
result = String.fromCharCode(97 + (remaining % 26)) + result
|
|
83
|
-
remaining = Math.floor(remaining / 26) - 1
|
|
84
|
-
} while (remaining >= 0)
|
|
85
|
-
return result
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Returns a sourcemap object mapping allocated name → original name.
|
|
90
|
-
* Useful for debugging: write to <output>.map.json alongside the datapack.
|
|
91
|
-
* Only meaningful when mangle=true.
|
|
92
|
-
*/
|
|
93
|
-
toSourceMap(): Record<string, string> {
|
|
94
|
-
const map: Record<string, string> = {}
|
|
95
|
-
for (const [orig, alloc] of this.varCache) {
|
|
96
|
-
// Skip compiler-generated temporaries (start with _ followed by digits)
|
|
97
|
-
if (/^_\d+$/.test(orig)) continue
|
|
98
|
-
map[alloc] = orig
|
|
99
|
-
}
|
|
100
|
-
for (const [val, alloc] of this.constCache) map[alloc] = `const:${val}`
|
|
101
|
-
for (const [suf, alloc] of this.internalCache) map[alloc] = `internal:${suf}`
|
|
102
|
-
return map
|
|
103
|
-
}
|
|
104
|
-
}
|
package/src/ir/builder.ts
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* IRBuilder — helper for constructing IR programmatically.
|
|
3
|
-
* AST → IR lowering uses this.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { IRBlock, IRFunction, IRInstr, IRModule, Operand, Terminator } from './types'
|
|
7
|
-
|
|
8
|
-
export class IRBuilder {
|
|
9
|
-
private tempCount = 0
|
|
10
|
-
private labelCount = 0
|
|
11
|
-
private currentBlock: IRBlock | null = null
|
|
12
|
-
private blocks: IRBlock[] = []
|
|
13
|
-
private locals = new Set<string>()
|
|
14
|
-
|
|
15
|
-
// -------------------------------------------------------------------------
|
|
16
|
-
// Names
|
|
17
|
-
// -------------------------------------------------------------------------
|
|
18
|
-
|
|
19
|
-
freshTemp(): string {
|
|
20
|
-
const name = `$t${this.tempCount++}`
|
|
21
|
-
this.locals.add(name)
|
|
22
|
-
return name
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
freshLabel(hint = 'L'): string {
|
|
26
|
-
return `${hint}_${this.labelCount++}`
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// -------------------------------------------------------------------------
|
|
30
|
-
// Block management
|
|
31
|
-
// -------------------------------------------------------------------------
|
|
32
|
-
|
|
33
|
-
startBlock(label: string): void {
|
|
34
|
-
this.currentBlock = { label, instrs: [], term: { op: 'return' } }
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
private get block(): IRBlock {
|
|
38
|
-
if (!this.currentBlock) throw new Error('No active block')
|
|
39
|
-
return this.currentBlock
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
private sealBlock(term: Terminator): void {
|
|
43
|
-
this.block.term = term
|
|
44
|
-
this.blocks.push(this.block)
|
|
45
|
-
this.currentBlock = null
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// -------------------------------------------------------------------------
|
|
49
|
-
// Emit instructions
|
|
50
|
-
// -------------------------------------------------------------------------
|
|
51
|
-
|
|
52
|
-
emitAssign(dst: string, src: Operand): void {
|
|
53
|
-
this.locals.add(dst)
|
|
54
|
-
this.block.instrs.push({ op: 'assign', dst, src })
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
emitBinop(dst: string, lhs: Operand, bop: IRInstr & { op: 'binop' } extends { bop: infer B } ? B : never, rhs: Operand): void
|
|
58
|
-
emitBinop(dst: string, lhs: Operand, bop: '+' | '-' | '*' | '/' | '%', rhs: Operand): void {
|
|
59
|
-
this.locals.add(dst)
|
|
60
|
-
this.block.instrs.push({ op: 'binop', dst, lhs, bop, rhs })
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
emitCmp(dst: string, lhs: Operand, cop: '==' | '!=' | '<' | '<=' | '>' | '>=', rhs: Operand): void {
|
|
64
|
-
this.locals.add(dst)
|
|
65
|
-
this.block.instrs.push({ op: 'cmp', dst, lhs, cop, rhs })
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
emitCall(fn: string, args: Operand[], dst?: string): void {
|
|
69
|
-
if (dst) this.locals.add(dst)
|
|
70
|
-
this.block.instrs.push({ op: 'call', fn, args, dst })
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
emitRaw(cmd: string): void {
|
|
74
|
-
this.block.instrs.push({ op: 'raw', cmd })
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// -------------------------------------------------------------------------
|
|
78
|
-
// Terminators
|
|
79
|
-
// -------------------------------------------------------------------------
|
|
80
|
-
|
|
81
|
-
emitJump(target: string): void {
|
|
82
|
-
this.sealBlock({ op: 'jump', target })
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
emitJumpIf(cond: string, then: string, else_: string): void {
|
|
86
|
-
this.sealBlock({ op: 'jump_if', cond, then, else_ })
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
emitReturn(value?: Operand): void {
|
|
90
|
-
this.sealBlock({ op: 'return', value })
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
emitTickYield(continuation: string): void {
|
|
94
|
-
this.sealBlock({ op: 'tick_yield', continuation })
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// -------------------------------------------------------------------------
|
|
98
|
-
// Build
|
|
99
|
-
// -------------------------------------------------------------------------
|
|
100
|
-
|
|
101
|
-
build(name: string, params: string[], isTickLoop = false): IRFunction {
|
|
102
|
-
return {
|
|
103
|
-
name,
|
|
104
|
-
params,
|
|
105
|
-
locals: Array.from(this.locals),
|
|
106
|
-
blocks: this.blocks,
|
|
107
|
-
isTickLoop,
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
import type { GlobalVar } from './types'
|
|
113
|
-
|
|
114
|
-
export function buildModule(namespace: string, fns: IRFunction[], globals: GlobalVar[] = []): IRModule {
|
|
115
|
-
return { namespace, functions: fns, globals }
|
|
116
|
-
}
|
package/src/ir/types.ts
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* RedScript IR — Three-Address Code (TAC)
|
|
3
|
-
*
|
|
4
|
-
* Compilation pipeline:
|
|
5
|
-
* Source → AST → IR → (optimize) → CodeGen → mcfunction / cmdblock
|
|
6
|
-
*
|
|
7
|
-
* Variable storage in MC Java Edition:
|
|
8
|
-
* - Integer vars → scoreboard fake player ($name on objective "rs_vars")
|
|
9
|
-
* - Complex data → NBT storage (redscript:stack / redscript:heap)
|
|
10
|
-
* - Return value → fake player $ret
|
|
11
|
-
* - Temporaries → $_0, $_1, ...
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
// ---------------------------------------------------------------------------
|
|
15
|
-
// Operands
|
|
16
|
-
// ---------------------------------------------------------------------------
|
|
17
|
-
|
|
18
|
-
export type Operand =
|
|
19
|
-
| { kind: 'var'; name: string } // scoreboard fake player
|
|
20
|
-
| { kind: 'const'; value: number } // integer literal
|
|
21
|
-
| { kind: 'storage'; path: string } // NBT storage path (e.g. "redscript:heap data.x")
|
|
22
|
-
| { kind: 'param'; index: number } // function parameter slot (alloc.internal('p{i}')), avoids mangle collision
|
|
23
|
-
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
|
-
// Binary operators (all map to `scoreboard players operation`)
|
|
26
|
-
// ---------------------------------------------------------------------------
|
|
27
|
-
|
|
28
|
-
export type BinOp = '+' | '-' | '*' | '/' | '%'
|
|
29
|
-
export type CmpOp = '==' | '!=' | '<' | '<=' | '>' | '>='
|
|
30
|
-
|
|
31
|
-
// ---------------------------------------------------------------------------
|
|
32
|
-
// IR Instructions
|
|
33
|
-
// ---------------------------------------------------------------------------
|
|
34
|
-
|
|
35
|
-
export type IRInstr =
|
|
36
|
-
// x = src
|
|
37
|
-
| { op: 'assign'; dst: string; src: Operand }
|
|
38
|
-
|
|
39
|
-
// dst = lhs bop rhs
|
|
40
|
-
| { op: 'binop'; dst: string; lhs: Operand; bop: BinOp; rhs: Operand }
|
|
41
|
-
|
|
42
|
-
// dst = (lhs cop rhs) ? 1 : 0
|
|
43
|
-
| { op: 'cmp'; dst: string; lhs: Operand; cop: CmpOp; rhs: Operand }
|
|
44
|
-
|
|
45
|
-
// goto label
|
|
46
|
-
| { op: 'jump'; target: string }
|
|
47
|
-
|
|
48
|
-
// if cond != 0 goto target
|
|
49
|
-
| { op: 'jump_if'; cond: string; target: string }
|
|
50
|
-
|
|
51
|
-
// if cond == 0 goto target
|
|
52
|
-
| { op: 'jump_unless'; cond: string; target: string }
|
|
53
|
-
|
|
54
|
-
// dst = fn(args)
|
|
55
|
-
| { op: 'call'; fn: string; args: Operand[]; dst?: string }
|
|
56
|
-
|
|
57
|
-
// return value (optional)
|
|
58
|
-
| { op: 'return'; value?: Operand }
|
|
59
|
-
|
|
60
|
-
// label declaration (block entry point)
|
|
61
|
-
| { op: 'label'; id: string }
|
|
62
|
-
|
|
63
|
-
// raw MC command passthrough (escape hatch)
|
|
64
|
-
| { op: 'raw'; cmd: string }
|
|
65
|
-
|
|
66
|
-
// wait one game tick (command block target only)
|
|
67
|
-
// maps to: schedule function <continuation> 1t replace
|
|
68
|
-
| { op: 'tick_yield' }
|
|
69
|
-
|
|
70
|
-
// ---------------------------------------------------------------------------
|
|
71
|
-
// Basic Block — straight-line code, ends with a terminator
|
|
72
|
-
// ---------------------------------------------------------------------------
|
|
73
|
-
|
|
74
|
-
export type Terminator =
|
|
75
|
-
| { op: 'jump'; target: string }
|
|
76
|
-
| { op: 'jump_if'; cond: string; then: string; else_: string }
|
|
77
|
-
| { op: 'jump_unless'; cond: string; then: string; else_: string }
|
|
78
|
-
| { op: 'return'; value?: Operand }
|
|
79
|
-
| { op: 'tick_yield'; continuation: string }
|
|
80
|
-
|
|
81
|
-
export interface IRBlock {
|
|
82
|
-
label: string
|
|
83
|
-
instrs: IRInstr[] // non-terminator instructions
|
|
84
|
-
term: Terminator
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export interface IRCommand {
|
|
88
|
-
cmd: string
|
|
89
|
-
conditional?: boolean
|
|
90
|
-
label?: string
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// ---------------------------------------------------------------------------
|
|
94
|
-
// Function
|
|
95
|
-
// ---------------------------------------------------------------------------
|
|
96
|
-
|
|
97
|
-
export interface IRFunction {
|
|
98
|
-
name: string
|
|
99
|
-
params: string[] // parameter names (passed via fake players)
|
|
100
|
-
locals: string[] // all local variable names
|
|
101
|
-
blocks: IRBlock[] // blocks[0] = entry block
|
|
102
|
-
commands?: IRCommand[] // structure target command stream
|
|
103
|
-
isTickLoop?: boolean // true → Repeat command block (runs every tick)
|
|
104
|
-
isLoadInit?: boolean // true → called from __load.mcfunction
|
|
105
|
-
requiredLoads?: string[] // @requires("fn") — these fns are also called from __load when this fn is compiled in
|
|
106
|
-
isTriggerHandler?: boolean // true → handles a trigger event
|
|
107
|
-
triggerName?: string // the trigger objective name
|
|
108
|
-
eventTrigger?: {
|
|
109
|
-
kind: 'advancement' | 'craft' | 'death' | 'login' | 'join_team'
|
|
110
|
-
value?: string
|
|
111
|
-
}
|
|
112
|
-
eventHandler?: {
|
|
113
|
-
eventType: string
|
|
114
|
-
tag: string
|
|
115
|
-
}
|
|
116
|
-
// MC 1.20.2+ macro function support
|
|
117
|
-
isMacroFunction?: boolean // true → function uses MC macro syntax ($-prefixed commands)
|
|
118
|
-
macroParamNames?: string[] // parameter names that are passed via NBT macro (not just scoreboard)
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// ---------------------------------------------------------------------------
|
|
122
|
-
// Module — top-level compilation unit
|
|
123
|
-
// ---------------------------------------------------------------------------
|
|
124
|
-
|
|
125
|
-
export interface GlobalVar {
|
|
126
|
-
name: string
|
|
127
|
-
init: number
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export interface IRModule {
|
|
131
|
-
namespace: string // datapack namespace (e.g. "mypack")
|
|
132
|
-
functions: IRFunction[]
|
|
133
|
-
globals: GlobalVar[] // global variable declarations with init values
|
|
134
|
-
}
|