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,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
- }