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.
Files changed (274) 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/README.md +29 -28
  8. package/README.zh.md +28 -28
  9. package/demo.gif +0 -0
  10. package/dist/cli.js +2 -554
  11. package/dist/compile.js +2 -266
  12. package/dist/index.js +2 -159
  13. package/dist/lexer/index.js +9 -1
  14. package/dist/lowering/index.js +22 -5
  15. package/dist/src/__tests__/cli.test.d.ts +1 -0
  16. package/dist/src/__tests__/cli.test.js +104 -0
  17. package/dist/src/__tests__/codegen.test.d.ts +1 -0
  18. package/dist/src/__tests__/codegen.test.js +152 -0
  19. package/dist/src/__tests__/compile-all.test.d.ts +10 -0
  20. package/dist/src/__tests__/compile-all.test.js +108 -0
  21. package/dist/src/__tests__/dce.test.d.ts +1 -0
  22. package/dist/src/__tests__/dce.test.js +102 -0
  23. package/dist/src/__tests__/diagnostics.test.d.ts +4 -0
  24. package/dist/src/__tests__/diagnostics.test.js +177 -0
  25. package/dist/src/__tests__/e2e.test.d.ts +6 -0
  26. package/dist/src/__tests__/e2e.test.js +1789 -0
  27. package/dist/src/__tests__/entity-types.test.d.ts +1 -0
  28. package/dist/src/__tests__/entity-types.test.js +203 -0
  29. package/dist/src/__tests__/formatter.test.d.ts +1 -0
  30. package/dist/src/__tests__/formatter.test.js +40 -0
  31. package/dist/src/__tests__/lexer.test.d.ts +1 -0
  32. package/dist/src/__tests__/lexer.test.js +343 -0
  33. package/dist/src/__tests__/lowering.test.d.ts +1 -0
  34. package/dist/src/__tests__/lowering.test.js +1015 -0
  35. package/dist/src/__tests__/macro.test.d.ts +8 -0
  36. package/dist/src/__tests__/macro.test.js +306 -0
  37. package/dist/src/__tests__/mc-integration.test.d.ts +12 -0
  38. package/dist/src/__tests__/mc-integration.test.js +817 -0
  39. package/dist/src/__tests__/mc-syntax.test.d.ts +1 -0
  40. package/dist/src/__tests__/mc-syntax.test.js +124 -0
  41. package/dist/src/__tests__/nbt.test.d.ts +1 -0
  42. package/dist/src/__tests__/nbt.test.js +82 -0
  43. package/dist/src/__tests__/optimizer-advanced.test.d.ts +1 -0
  44. package/dist/src/__tests__/optimizer-advanced.test.js +124 -0
  45. package/dist/src/__tests__/optimizer.test.d.ts +1 -0
  46. package/dist/src/__tests__/optimizer.test.js +149 -0
  47. package/dist/src/__tests__/parser.test.d.ts +1 -0
  48. package/dist/src/__tests__/parser.test.js +807 -0
  49. package/dist/src/__tests__/repl.test.d.ts +1 -0
  50. package/dist/src/__tests__/repl.test.js +27 -0
  51. package/dist/src/__tests__/runtime.test.d.ts +1 -0
  52. package/dist/src/__tests__/runtime.test.js +289 -0
  53. package/dist/src/__tests__/stdlib-advanced.test.d.ts +4 -0
  54. package/dist/src/__tests__/stdlib-advanced.test.js +374 -0
  55. package/dist/src/__tests__/stdlib-bigint.test.d.ts +7 -0
  56. package/dist/src/__tests__/stdlib-bigint.test.js +426 -0
  57. package/dist/src/__tests__/stdlib-math.test.d.ts +7 -0
  58. package/dist/src/__tests__/stdlib-math.test.js +351 -0
  59. package/dist/src/__tests__/stdlib-vec.test.d.ts +4 -0
  60. package/dist/src/__tests__/stdlib-vec.test.js +263 -0
  61. package/dist/src/__tests__/structure-optimizer.test.d.ts +1 -0
  62. package/dist/src/__tests__/structure-optimizer.test.js +33 -0
  63. package/dist/src/__tests__/typechecker.test.d.ts +1 -0
  64. package/dist/src/__tests__/typechecker.test.js +552 -0
  65. package/dist/src/__tests__/var-allocator.test.d.ts +1 -0
  66. package/dist/src/__tests__/var-allocator.test.js +69 -0
  67. package/dist/src/ast/types.d.ts +515 -0
  68. package/dist/src/ast/types.js +9 -0
  69. package/dist/src/builtins/metadata.d.ts +36 -0
  70. package/dist/src/builtins/metadata.js +1014 -0
  71. package/dist/src/cli.d.ts +11 -0
  72. package/dist/src/cli.js +443 -0
  73. package/dist/src/codegen/cmdblock/index.d.ts +26 -0
  74. package/dist/src/codegen/cmdblock/index.js +45 -0
  75. package/dist/src/codegen/mcfunction/index.d.ts +40 -0
  76. package/dist/src/codegen/mcfunction/index.js +606 -0
  77. package/dist/src/codegen/structure/index.d.ts +24 -0
  78. package/dist/src/codegen/structure/index.js +279 -0
  79. package/dist/src/codegen/var-allocator.d.ts +45 -0
  80. package/dist/src/codegen/var-allocator.js +104 -0
  81. package/dist/src/compile.d.ts +37 -0
  82. package/dist/src/compile.js +165 -0
  83. package/dist/src/diagnostics/index.d.ts +44 -0
  84. package/dist/src/diagnostics/index.js +140 -0
  85. package/dist/src/events/types.d.ts +35 -0
  86. package/dist/src/events/types.js +59 -0
  87. package/dist/src/formatter/index.d.ts +1 -0
  88. package/dist/src/formatter/index.js +26 -0
  89. package/dist/src/index.d.ts +22 -0
  90. package/dist/src/index.js +45 -0
  91. package/dist/src/ir/builder.d.ts +33 -0
  92. package/dist/src/ir/builder.js +99 -0
  93. package/dist/src/ir/types.d.ts +132 -0
  94. package/dist/src/ir/types.js +15 -0
  95. package/dist/src/lexer/index.d.ts +37 -0
  96. package/dist/src/lexer/index.js +569 -0
  97. package/dist/src/lowering/index.d.ts +188 -0
  98. package/dist/src/lowering/index.js +3405 -0
  99. package/dist/src/mc-test/client.d.ts +128 -0
  100. package/dist/src/mc-test/client.js +174 -0
  101. package/dist/src/mc-test/runner.d.ts +28 -0
  102. package/dist/src/mc-test/runner.js +151 -0
  103. package/dist/src/mc-test/setup.d.ts +11 -0
  104. package/dist/src/mc-test/setup.js +98 -0
  105. package/dist/src/mc-validator/index.d.ts +17 -0
  106. package/dist/src/mc-validator/index.js +322 -0
  107. package/dist/src/nbt/index.d.ts +86 -0
  108. package/dist/src/nbt/index.js +250 -0
  109. package/dist/src/optimizer/commands.d.ts +38 -0
  110. package/dist/src/optimizer/commands.js +451 -0
  111. package/dist/src/optimizer/dce.d.ts +34 -0
  112. package/dist/src/optimizer/dce.js +639 -0
  113. package/dist/src/optimizer/passes.d.ts +34 -0
  114. package/dist/src/optimizer/passes.js +243 -0
  115. package/dist/src/optimizer/structure.d.ts +9 -0
  116. package/dist/src/optimizer/structure.js +356 -0
  117. package/dist/src/parser/index.d.ts +93 -0
  118. package/dist/src/parser/index.js +1687 -0
  119. package/dist/src/repl.d.ts +16 -0
  120. package/dist/src/repl.js +165 -0
  121. package/dist/src/runtime/index.d.ts +107 -0
  122. package/dist/src/runtime/index.js +1409 -0
  123. package/dist/src/typechecker/index.d.ts +61 -0
  124. package/dist/src/typechecker/index.js +1034 -0
  125. package/dist/src/types/entity-hierarchy.d.ts +29 -0
  126. package/dist/src/types/entity-hierarchy.js +107 -0
  127. package/dist/src2/__tests__/e2e/basic.test.d.ts +8 -0
  128. package/dist/src2/__tests__/e2e/basic.test.js +140 -0
  129. package/dist/src2/__tests__/e2e/macros.test.d.ts +9 -0
  130. package/dist/src2/__tests__/e2e/macros.test.js +182 -0
  131. package/dist/src2/__tests__/e2e/migrate.test.d.ts +13 -0
  132. package/dist/src2/__tests__/e2e/migrate.test.js +2739 -0
  133. package/dist/src2/__tests__/hir/desugar.test.d.ts +1 -0
  134. package/dist/src2/__tests__/hir/desugar.test.js +234 -0
  135. package/dist/src2/__tests__/lir/lower.test.d.ts +1 -0
  136. package/dist/src2/__tests__/lir/lower.test.js +559 -0
  137. package/dist/src2/__tests__/lir/types.test.d.ts +1 -0
  138. package/dist/src2/__tests__/lir/types.test.js +185 -0
  139. package/dist/src2/__tests__/lir/verify.test.d.ts +1 -0
  140. package/dist/src2/__tests__/lir/verify.test.js +221 -0
  141. package/dist/src2/__tests__/mir/arithmetic.test.d.ts +1 -0
  142. package/dist/src2/__tests__/mir/arithmetic.test.js +130 -0
  143. package/dist/src2/__tests__/mir/control-flow.test.d.ts +1 -0
  144. package/dist/src2/__tests__/mir/control-flow.test.js +205 -0
  145. package/dist/src2/__tests__/mir/verify.test.d.ts +1 -0
  146. package/dist/src2/__tests__/mir/verify.test.js +223 -0
  147. package/dist/src2/__tests__/optimizer/block_merge.test.d.ts +1 -0
  148. package/dist/src2/__tests__/optimizer/block_merge.test.js +78 -0
  149. package/dist/src2/__tests__/optimizer/branch_simplify.test.d.ts +1 -0
  150. package/dist/src2/__tests__/optimizer/branch_simplify.test.js +58 -0
  151. package/dist/src2/__tests__/optimizer/constant_fold.test.d.ts +1 -0
  152. package/dist/src2/__tests__/optimizer/constant_fold.test.js +131 -0
  153. package/dist/src2/__tests__/optimizer/copy_prop.test.d.ts +1 -0
  154. package/dist/src2/__tests__/optimizer/copy_prop.test.js +91 -0
  155. package/dist/src2/__tests__/optimizer/dce.test.d.ts +1 -0
  156. package/dist/src2/__tests__/optimizer/dce.test.js +76 -0
  157. package/dist/src2/__tests__/optimizer/pipeline.test.d.ts +1 -0
  158. package/dist/src2/__tests__/optimizer/pipeline.test.js +102 -0
  159. package/dist/src2/emit/compile.d.ts +19 -0
  160. package/dist/src2/emit/compile.js +80 -0
  161. package/dist/src2/emit/index.d.ts +17 -0
  162. package/dist/src2/emit/index.js +172 -0
  163. package/dist/src2/hir/lower.d.ts +15 -0
  164. package/dist/src2/hir/lower.js +378 -0
  165. package/dist/src2/hir/types.d.ts +373 -0
  166. package/dist/src2/hir/types.js +16 -0
  167. package/dist/src2/lir/lower.d.ts +15 -0
  168. package/dist/src2/lir/lower.js +453 -0
  169. package/dist/src2/lir/types.d.ts +136 -0
  170. package/dist/src2/lir/types.js +11 -0
  171. package/dist/src2/lir/verify.d.ts +14 -0
  172. package/dist/src2/lir/verify.js +113 -0
  173. package/dist/src2/mir/lower.d.ts +9 -0
  174. package/dist/src2/mir/lower.js +1030 -0
  175. package/dist/src2/mir/macro.d.ts +22 -0
  176. package/dist/src2/mir/macro.js +168 -0
  177. package/dist/src2/mir/types.d.ts +183 -0
  178. package/dist/src2/mir/types.js +11 -0
  179. package/dist/src2/mir/verify.d.ts +16 -0
  180. package/dist/src2/mir/verify.js +216 -0
  181. package/dist/src2/optimizer/block_merge.d.ts +12 -0
  182. package/dist/src2/optimizer/block_merge.js +84 -0
  183. package/dist/src2/optimizer/branch_simplify.d.ts +9 -0
  184. package/dist/src2/optimizer/branch_simplify.js +28 -0
  185. package/dist/src2/optimizer/constant_fold.d.ts +10 -0
  186. package/dist/src2/optimizer/constant_fold.js +85 -0
  187. package/dist/src2/optimizer/copy_prop.d.ts +9 -0
  188. package/dist/src2/optimizer/copy_prop.js +113 -0
  189. package/dist/src2/optimizer/dce.d.ts +8 -0
  190. package/dist/src2/optimizer/dce.js +155 -0
  191. package/dist/src2/optimizer/pipeline.d.ts +10 -0
  192. package/dist/src2/optimizer/pipeline.js +42 -0
  193. package/dist/tsconfig.tsbuildinfo +1 -0
  194. package/docs/compiler-pipeline-redesign.md +2243 -0
  195. package/docs/optimization-ideas.md +1076 -0
  196. package/editors/vscode/package-lock.json +3 -3
  197. package/editors/vscode/package.json +1 -1
  198. package/examples/readme-demo.mcrs +44 -66
  199. package/jest.config.js +1 -1
  200. package/package.json +6 -5
  201. package/scripts/postbuild.js +15 -0
  202. package/src/__tests__/cli.test.ts +8 -220
  203. package/src/__tests__/dce.test.ts +11 -56
  204. package/src/__tests__/diagnostics.test.ts +59 -38
  205. package/src/__tests__/mc-integration.test.ts +1 -2
  206. package/src/ast/types.ts +6 -1
  207. package/src/cli.ts +29 -156
  208. package/src/compile.ts +6 -162
  209. package/src/index.ts +14 -178
  210. package/src/lexer/index.ts +9 -1
  211. package/src/mc-test/runner.ts +4 -3
  212. package/src/parser/index.ts +1 -1
  213. package/src/repl.ts +1 -1
  214. package/src/runtime/index.ts +1 -1
  215. package/src2/__tests__/e2e/basic.test.ts +154 -0
  216. package/src2/__tests__/e2e/macros.test.ts +199 -0
  217. package/src2/__tests__/e2e/migrate.test.ts +3008 -0
  218. package/src2/__tests__/hir/desugar.test.ts +263 -0
  219. package/src2/__tests__/lir/lower.test.ts +619 -0
  220. package/src2/__tests__/lir/types.test.ts +207 -0
  221. package/src2/__tests__/lir/verify.test.ts +249 -0
  222. package/src2/__tests__/mir/arithmetic.test.ts +156 -0
  223. package/src2/__tests__/mir/control-flow.test.ts +242 -0
  224. package/src2/__tests__/mir/verify.test.ts +254 -0
  225. package/src2/__tests__/optimizer/block_merge.test.ts +84 -0
  226. package/src2/__tests__/optimizer/branch_simplify.test.ts +64 -0
  227. package/src2/__tests__/optimizer/constant_fold.test.ts +145 -0
  228. package/src2/__tests__/optimizer/copy_prop.test.ts +99 -0
  229. package/src2/__tests__/optimizer/dce.test.ts +83 -0
  230. package/src2/__tests__/optimizer/pipeline.test.ts +116 -0
  231. package/src2/emit/compile.ts +99 -0
  232. package/src2/emit/index.ts +222 -0
  233. package/src2/hir/lower.ts +428 -0
  234. package/src2/hir/types.ts +216 -0
  235. package/src2/lir/lower.ts +556 -0
  236. package/src2/lir/types.ts +109 -0
  237. package/src2/lir/verify.ts +129 -0
  238. package/src2/mir/lower.ts +1160 -0
  239. package/src2/mir/macro.ts +167 -0
  240. package/src2/mir/types.ts +106 -0
  241. package/src2/mir/verify.ts +218 -0
  242. package/src2/optimizer/block_merge.ts +93 -0
  243. package/src2/optimizer/branch_simplify.ts +27 -0
  244. package/src2/optimizer/constant_fold.ts +88 -0
  245. package/src2/optimizer/copy_prop.ts +106 -0
  246. package/src2/optimizer/dce.ts +133 -0
  247. package/src2/optimizer/pipeline.ts +44 -0
  248. package/tsconfig.json +2 -2
  249. package/src/__tests__/codegen.test.ts +0 -161
  250. package/src/__tests__/e2e.test.ts +0 -2039
  251. package/src/__tests__/entity-types.test.ts +0 -236
  252. package/src/__tests__/lowering.test.ts +0 -1185
  253. package/src/__tests__/macro.test.ts +0 -343
  254. package/src/__tests__/nbt.test.ts +0 -58
  255. package/src/__tests__/optimizer-advanced.test.ts +0 -144
  256. package/src/__tests__/optimizer.test.ts +0 -162
  257. package/src/__tests__/runtime.test.ts +0 -305
  258. package/src/__tests__/stdlib-advanced.test.ts +0 -379
  259. package/src/__tests__/stdlib-bigint.test.ts +0 -427
  260. package/src/__tests__/stdlib-math.test.ts +0 -374
  261. package/src/__tests__/stdlib-vec.test.ts +0 -259
  262. package/src/__tests__/structure-optimizer.test.ts +0 -38
  263. package/src/__tests__/var-allocator.test.ts +0 -75
  264. package/src/codegen/cmdblock/index.ts +0 -63
  265. package/src/codegen/mcfunction/index.ts +0 -662
  266. package/src/codegen/structure/index.ts +0 -346
  267. package/src/codegen/var-allocator.ts +0 -104
  268. package/src/ir/builder.ts +0 -116
  269. package/src/ir/types.ts +0 -134
  270. package/src/lowering/index.ts +0 -3860
  271. package/src/optimizer/commands.ts +0 -534
  272. package/src/optimizer/dce.ts +0 -679
  273. package/src/optimizer/passes.ts +0 -250
  274. package/src/optimizer/structure.ts +0 -450
@@ -1,162 +0,0 @@
1
- import { constantFolding, copyPropagation, deadCodeElimination, optimize } from '../optimizer/passes'
2
- import type { IRFunction } from '../ir/types'
3
-
4
- function makeFn(instrs: any[], term: any = { op: 'return' }): IRFunction {
5
- return {
6
- name: 'test',
7
- params: [],
8
- locals: [],
9
- blocks: [{ label: 'entry', instrs, term }],
10
- }
11
- }
12
-
13
- describe('constantFolding', () => {
14
- it('folds 2 + 3 → 5', () => {
15
- const fn = makeFn([
16
- { op: 'binop', dst: '$x', lhs: { kind: 'const', value: 2 }, bop: '+', rhs: { kind: 'const', value: 3 } },
17
- ])
18
- const opt = constantFolding(fn)
19
- expect(opt.blocks[0].instrs[0]).toEqual({
20
- op: 'assign', dst: '$x', src: { kind: 'const', value: 5 },
21
- })
22
- })
23
-
24
- it('folds 10 / 3 → 3 (truncated int division)', () => {
25
- const fn = makeFn([
26
- { op: 'binop', dst: '$x', lhs: { kind: 'const', value: 10 }, bop: '/', rhs: { kind: 'const', value: 3 } },
27
- ])
28
- const opt = constantFolding(fn)
29
- expect((opt.blocks[0].instrs[0] as any).src.value).toBe(3)
30
- })
31
-
32
- it('folds cmp 5 == 5 → 1', () => {
33
- const fn = makeFn([
34
- { op: 'cmp', dst: '$r', lhs: { kind: 'const', value: 5 }, cop: '==', rhs: { kind: 'const', value: 5 } },
35
- ])
36
- const opt = constantFolding(fn)
37
- expect((opt.blocks[0].instrs[0] as any).src.value).toBe(1)
38
- })
39
-
40
- it('folds cmp 5 > 10 → 0', () => {
41
- const fn = makeFn([
42
- { op: 'cmp', dst: '$r', lhs: { kind: 'const', value: 5 }, cop: '>', rhs: { kind: 'const', value: 10 } },
43
- ])
44
- const opt = constantFolding(fn)
45
- expect((opt.blocks[0].instrs[0] as any).src.value).toBe(0)
46
- })
47
-
48
- it('does not fold division by zero', () => {
49
- const fn = makeFn([
50
- { op: 'binop', dst: '$x', lhs: { kind: 'const', value: 5 }, bop: '/', rhs: { kind: 'const', value: 0 } },
51
- ])
52
- const opt = constantFolding(fn)
53
- expect(opt.blocks[0].instrs[0].op).toBe('binop')
54
- })
55
- })
56
-
57
- describe('copyPropagation', () => {
58
- it('propagates simple copy', () => {
59
- const fn = makeFn([
60
- { op: 'assign', dst: '$t0', src: { kind: 'var', name: '$x' } },
61
- { op: 'binop', dst: '$y', lhs: { kind: 'var', name: '$t0' }, bop: '+', rhs: { kind: 'const', value: 1 } },
62
- ])
63
- const opt = copyPropagation(fn)
64
- const binop = opt.blocks[0].instrs[1] as any
65
- expect(binop.lhs).toEqual({ kind: 'var', name: '$x' })
66
- })
67
-
68
- it('propagates constant copies', () => {
69
- const fn = makeFn([
70
- { op: 'assign', dst: '$t0', src: { kind: 'const', value: 42 } },
71
- { op: 'assign', dst: '$y', src: { kind: 'var', name: '$t0' } },
72
- ])
73
- const opt = copyPropagation(fn)
74
- const second = opt.blocks[0].instrs[1] as any
75
- expect(second.src).toEqual({ kind: 'const', value: 42 })
76
- })
77
- })
78
-
79
- describe('deadCodeElimination', () => {
80
- it('removes unused assignment', () => {
81
- const fn = makeFn([
82
- { op: 'assign', dst: '$t0', src: { kind: 'const', value: 99 } }, // unused temp
83
- { op: 'assign', dst: '$t1', src: { kind: 'const', value: 1 } }, // used temp
84
- ], { op: 'return', value: { kind: 'var', name: '$t1' } })
85
- const opt = deadCodeElimination(fn)
86
- expect(opt.blocks[0].instrs).toHaveLength(1)
87
- expect((opt.blocks[0].instrs[0] as any).dst).toBe('$t1')
88
- })
89
-
90
- it('keeps call even if return value unused (side effects)', () => {
91
- const fn = makeFn([
92
- { op: 'call', fn: 'foo', args: [], dst: '$unused' },
93
- ])
94
- const opt = deadCodeElimination(fn)
95
- expect(opt.blocks[0].instrs).toHaveLength(1)
96
- })
97
-
98
- it('keeps assignments referenced by raw commands', () => {
99
- const fn = makeFn([
100
- { op: 'assign', dst: '$used_by_raw', src: { kind: 'const', value: 7 } },
101
- { op: 'raw', cmd: 'execute store result score player obj run scoreboard players get $used_by_raw rs' },
102
- ])
103
- const opt = deadCodeElimination(fn)
104
- expect(opt.blocks[0].instrs).toHaveLength(2)
105
- expect((opt.blocks[0].instrs[0] as any).dst).toBe('$used_by_raw')
106
- })
107
- })
108
-
109
- describe('copyPropagation – stale alias invalidation', () => {
110
- it('does not propagate $tmp = $y after $y is overwritten (swap pattern)', () => {
111
- // Simulates: let tmp = y; y = x % y; x = tmp
112
- // The copy $tmp = $y must be invalidated when $y is reassigned.
113
- // Before fix: x = tmp was propagated to x = y (new y, wrong value).
114
- const fn = makeFn([
115
- { op: 'assign', dst: '$tmp', src: { kind: 'var', name: '$y' } }, // tmp = y
116
- { op: 'binop', dst: '$r', lhs: { kind: 'var', name: '$x' }, bop: '%', rhs: { kind: 'var', name: '$y' } }, // r = x%y
117
- { op: 'assign', dst: '$y', src: { kind: 'var', name: '$r' } }, // y = r ← stale: tmp still points to OLD y
118
- { op: 'assign', dst: '$x', src: { kind: 'var', name: '$tmp' } }, // x = tmp (should NOT be x = y)
119
- ])
120
- const opt = copyPropagation(fn)
121
- const instrs = opt.blocks[0].instrs
122
- const xAssign = instrs.find((i: any) => i.dst === '$x') as any
123
- // x = tmp must NOT be optimised to x = $y (stale) or x = $r (new y).
124
- // It should stay as x = $tmp (the original copy).
125
- expect(xAssign.src).toEqual({ kind: 'var', name: '$tmp' })
126
- })
127
-
128
- it('still propagates simple non-conflicting copies', () => {
129
- // a = 5; b = a; c = b → after propagation b and c should both be const 5
130
- const fn = makeFn([
131
- { op: 'assign', dst: '$a', src: { kind: 'const', value: 5 } },
132
- { op: 'assign', dst: '$b', src: { kind: 'var', name: '$a' } },
133
- { op: 'assign', dst: '$c', src: { kind: 'var', name: '$b' } },
134
- ])
135
- const opt = copyPropagation(fn)
136
- const instrs = opt.blocks[0].instrs
137
- const cAssign = instrs.find((i: any) => i.dst === '$c') as any
138
- expect(cAssign.src).toEqual({ kind: 'const', value: 5 })
139
- })
140
- })
141
-
142
- describe('optimize pipeline', () => {
143
- it('combines all passes', () => {
144
- // t0 = 2 + 3 (→ constant fold → t0 = 5)
145
- // x = t0 (→ copy prop → x = 5)
146
- // unused = 0 (→ DCE → removed)
147
- // return x
148
- const fn = makeFn([
149
- { op: 'binop', dst: '$t0', lhs: { kind: 'const', value: 2 }, bop: '+', rhs: { kind: 'const', value: 3 } },
150
- { op: 'assign', dst: '$x', src: { kind: 'var', name: '$t0' } },
151
- { op: 'assign', dst: '$t1', src: { kind: 'const', value: 0 } }, // unused temp, should be removed
152
- ], { op: 'return', value: { kind: 'var', name: '$x' } })
153
-
154
- const opt = optimize(fn)
155
- const instrs = opt.blocks[0].instrs
156
- // $t1 (unused temp) should be gone
157
- expect(instrs.some((i: any) => i.dst === '$t1')).toBe(false)
158
- // $x should be const 5 (after folding + propagation)
159
- const xInstr = instrs.find((i: any) => i.dst === '$x') as any
160
- expect(xInstr?.src).toEqual({ kind: 'const', value: 5 })
161
- })
162
- })
@@ -1,305 +0,0 @@
1
- import * as fs from 'fs'
2
- import * as path from 'path'
3
-
4
- import { compile } from '../compile'
5
- import { MCRuntime, Entity } from '../runtime'
6
-
7
- function loadCompiledProgram(source: string, namespace = 'runtime'): MCRuntime {
8
- const result = compile(source, { namespace })
9
- expect(result.success).toBe(true)
10
- expect(result.files).toBeDefined()
11
-
12
- const runtime = new MCRuntime(namespace)
13
- for (const file of result.files ?? []) {
14
- if (!file.path.endsWith('.mcfunction')) continue
15
-
16
- const match = file.path.match(/data\/([^/]+)\/function\/(.+)\.mcfunction$/)
17
- if (!match) continue
18
-
19
- const [, ns, fnPath] = match
20
- runtime.loadFunction(`${ns}:${fnPath}`, file.content.split('\n'))
21
- }
22
-
23
- return runtime
24
- }
25
-
26
- function loadExample(name: string): string {
27
- return fs.readFileSync(path.join(__dirname, '..', 'examples', name), 'utf-8')
28
- }
29
-
30
- describe('MCRuntime behavioral integration', () => {
31
- it('runs the counter example and increments the scoreboard across ticks', () => {
32
- const runtime = loadCompiledProgram(loadExample('counter.mcrs'))
33
-
34
- runtime.load()
35
- runtime.ticks(5)
36
-
37
- expect(runtime.getScore('counter', 'ticks')).toBe(5)
38
- expect(runtime.tickCount).toBe(5)
39
- })
40
-
41
- it('executes compiled math control flow and stores the expected result', () => {
42
- const runtime = loadCompiledProgram(`
43
- fn compute() {
44
- let x: int = 3;
45
- let result: int = 0;
46
-
47
- if (x > 2) {
48
- result = 11;
49
- } else {
50
- result = 29;
51
- }
52
-
53
- scoreboard_set("math", "result", result);
54
- }
55
- `)
56
-
57
- runtime.load()
58
- runtime.execFunction('compute')
59
-
60
- expect(runtime.getScore('math', 'runtime.result')).toBe(11)
61
- })
62
-
63
- it('captures say, announce, actionbar, and title output in the chat log', () => {
64
- const runtime = loadCompiledProgram(`
65
- fn chat() {
66
- say("hello");
67
- announce("broadcast");
68
- actionbar(@a, "warning");
69
- title(@a, "Boss Wave");
70
- }
71
- `)
72
-
73
- runtime.load()
74
- runtime.execFunction('chat')
75
-
76
- expect(runtime.getChatLog()).toEqual([
77
- '[Server] hello',
78
- 'broadcast',
79
- '[ACTIONBAR] warning',
80
- '[TITLE] Boss Wave',
81
- ])
82
- })
83
-
84
- it('renders interpolated strings through tellraw score components', () => {
85
- const runtime = loadCompiledProgram(`
86
- fn chat() {
87
- let score: int = 7;
88
- say("You have \${score} points");
89
- }
90
- `)
91
-
92
- runtime.load()
93
- runtime.execFunction('chat')
94
-
95
- expect(runtime.getChatLog()).toEqual([
96
- 'You have 7 points',
97
- ])
98
- })
99
-
100
- it('renders f-strings through tellraw score components', () => {
101
- const runtime = loadCompiledProgram(`
102
- fn chat() {
103
- let score: int = 7;
104
- say(f"You have {score} points");
105
- }
106
- `)
107
-
108
- runtime.load()
109
- runtime.execFunction('chat')
110
-
111
- expect(runtime.getChatLog()).toEqual([
112
- 'You have 7 points',
113
- ])
114
- })
115
-
116
- it('kills only entities matched by a foreach selector', () => {
117
- const runtime = loadCompiledProgram(`
118
- fn purge_zombies() {
119
- foreach (z in @e[type=zombie]) {
120
- kill(z);
121
- }
122
- }
123
- `)
124
-
125
- const zombieA = runtime.spawnEntity(['hostile'], 'minecraft:zombie')
126
- const zombieB = runtime.spawnEntity(['hostile'], 'zombie')
127
- const skeleton = runtime.spawnEntity(['hostile'], 'minecraft:skeleton')
128
-
129
- runtime.load()
130
- runtime.execFunction('purge_zombies')
131
-
132
- expect(runtime.entities.map(entity => entity.id)).toEqual([skeleton.id])
133
- expect(runtime.entities.find(entity => entity.id === zombieA.id)).toBeUndefined()
134
- expect(runtime.entities.find(entity => entity.id === zombieB.id)).toBeUndefined()
135
- })
136
-
137
- it('executes array push, pop, and len through storage operations', () => {
138
- const runtime = loadCompiledProgram(`
139
- fn arrays() {
140
- let arr: int[] = [];
141
- arr.push(4);
142
- arr.push(9);
143
- let popped: int = arr.pop();
144
- let len: int = arr.len;
145
-
146
- scoreboard_set("arrays", "len", len);
147
- scoreboard_set("arrays", "last", popped);
148
- }
149
- `)
150
-
151
- runtime.load()
152
- runtime.execFunction('arrays')
153
-
154
- expect(runtime.getScore('arrays', 'runtime.len')).toBe(1)
155
- expect(runtime.getScore('arrays', 'runtime.last')).toBe(9)
156
- expect(runtime.getStorage('rs:heap.arr')).toEqual([4])
157
- })
158
-
159
- it('tracks world state, weather, and time from compiled world commands', () => {
160
- const runtime = loadCompiledProgram(`
161
- fn reset_world() {
162
- let floor_start: BlockPos = (0, 64, 0);
163
- let floor_end: BlockPos = (1, 64, 1);
164
- let centerpiece: BlockPos = (1, 64, 1);
165
- fill(floor_start, floor_end, "minecraft:stone");
166
- setblock(centerpiece, "minecraft:gold_block");
167
- weather("rain");
168
- time_set("noon");
169
- }
170
- `)
171
-
172
- runtime.load()
173
- runtime.execFunction('reset_world')
174
-
175
- expect(runtime.world.get('0,64,0')).toBe('minecraft:stone')
176
- expect(runtime.world.get('0,64,1')).toBe('minecraft:stone')
177
- expect(runtime.world.get('1,64,0')).toBe('minecraft:stone')
178
- expect(runtime.world.get('1,64,1')).toBe('minecraft:gold_block')
179
- expect(runtime.weather).toBe('rain')
180
- expect(runtime.worldTime).toBe(6000)
181
- })
182
-
183
- it('respects @tick(rate=5) scheduling when ticking the runtime', () => {
184
- const runtime = loadCompiledProgram(`
185
- @tick(rate=5)
186
- fn pulse() {
187
- let count: int = scoreboard_get("pulse", "count");
188
- count = count + 1;
189
- scoreboard_set("pulse", "count", count);
190
- }
191
- `)
192
-
193
- runtime.load()
194
- runtime.ticks(10)
195
-
196
- expect(runtime.getScore('pulse', 'runtime.count')).toBe(2)
197
- })
198
-
199
- it('executes only the matching match arm', () => {
200
- const runtime = loadCompiledProgram(`
201
- fn choose() {
202
- let choice: int = 2;
203
- match (choice) {
204
- 1 => { say("one"); }
205
- 2 => { say("two"); }
206
- _ => { say("other"); }
207
- }
208
- }
209
- `)
210
-
211
- runtime.load()
212
- runtime.execFunction('choose')
213
-
214
- expect(runtime.getChatLog()).toEqual([
215
- '[Server] two',
216
- ])
217
- })
218
-
219
- it('updates position, effects, and xp for executor-targeted builtins', () => {
220
- const runtime = loadCompiledProgram(`
221
- fn buff_player() {
222
- tp(@s, (5, 70, -2));
223
- effect(@s, "speed", 15, 2);
224
- xp_add(@s, 5);
225
- xp_set(@s, 12, "levels");
226
- }
227
- `)
228
-
229
- const player: Entity = runtime.spawnEntity(['player'], 'minecraft:player')
230
-
231
- runtime.load()
232
- runtime.execFunction('buff_player', player)
233
-
234
- expect(player.position).toEqual({ x: 5, y: 70, z: -2 })
235
- expect(runtime.effects.get(player.id)).toEqual([
236
- { effect: 'speed', duration: 15, amplifier: 2 },
237
- ])
238
- expect(runtime.xp.get(player.id)).toBe(12)
239
- })
240
-
241
- it('executes lambda variables through generated sub-functions', () => {
242
- const runtime = loadCompiledProgram(`
243
- fn test() {
244
- let double: (int) -> int = (x: int) => x * 2;
245
- let result: int = double(5);
246
- scoreboard_set("lambda", "direct", result);
247
- }
248
- `)
249
-
250
- runtime.load()
251
- runtime.execFunction('test')
252
-
253
- expect(runtime.getScore('lambda', 'runtime.direct')).toBe(10)
254
- })
255
-
256
- it('executes lambdas passed as callback arguments', () => {
257
- const runtime = loadCompiledProgram(`
258
- fn apply(val: int, cb: (int) -> int) -> int {
259
- return cb(val);
260
- }
261
-
262
- fn test() {
263
- let result: int = apply(5, (x: int) => x * 3);
264
- scoreboard_set("lambda", "callback", result);
265
- }
266
- `)
267
-
268
- runtime.load()
269
- runtime.execFunction('test')
270
-
271
- expect(runtime.getScore('lambda', 'runtime.callback')).toBe(15)
272
- })
273
-
274
- it('executes block-body lambdas', () => {
275
- const runtime = loadCompiledProgram(`
276
- fn test() {
277
- let process: (int) -> int = (x: int) => {
278
- let doubled: int = x * 2;
279
- return doubled + 1;
280
- };
281
- let result: int = process(5);
282
- scoreboard_set("lambda", "block", result);
283
- }
284
- `)
285
-
286
- runtime.load()
287
- runtime.execFunction('test')
288
-
289
- expect(runtime.getScore('lambda', 'runtime.block')).toBe(11)
290
- })
291
-
292
- it('executes immediately-invoked expression-body lambdas', () => {
293
- const runtime = loadCompiledProgram(`
294
- fn test() {
295
- let result: int = ((x: int) => x * 2)(5);
296
- scoreboard_set("lambda", "iife", result);
297
- }
298
- `)
299
-
300
- runtime.load()
301
- runtime.execFunction('test')
302
-
303
- expect(runtime.getScore('lambda', 'runtime.iife')).toBe(10)
304
- })
305
- })