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
@@ -0,0 +1,242 @@
1
+ import { Lexer } from '../../../src/lexer'
2
+ import { Parser } from '../../../src/parser'
3
+ import { lowerToHIR } from '../../hir/lower'
4
+ import { lowerToMIR } from '../../mir/lower'
5
+ import { verifyMIR } from '../../mir/verify'
6
+ import type { MIRModule, MIRBlock, MIRInstr, MIRFunction } from '../../mir/types'
7
+
8
+ function compile(source: string): MIRModule {
9
+ const tokens = new Lexer(source).tokenize()
10
+ const ast = new Parser(tokens).parse('test')
11
+ const hir = lowerToHIR(ast)
12
+ return lowerToMIR(hir)
13
+ }
14
+
15
+ function getFn(mod: MIRModule, name?: string): MIRFunction {
16
+ if (name) return mod.functions.find(f => f.name === name)!
17
+ return mod.functions[0]
18
+ }
19
+
20
+ function getBlock(fn: MIRFunction, id: string): MIRBlock | undefined {
21
+ return fn.blocks.find(b => b.id === id)
22
+ }
23
+
24
+ describe('MIR lowering — if/else → branch CFG', () => {
25
+ test('if without else creates branch to then + merge', () => {
26
+ const mod = compile('fn f(x: int): int { if (x > 0) { return x; } return 0; }')
27
+ expect(verifyMIR(mod)).toEqual([])
28
+
29
+ const fn = getFn(mod)
30
+ // Entry block should end with a branch
31
+ const entry = getBlock(fn, fn.entry)!
32
+ expect(entry.term.kind).toBe('branch')
33
+
34
+ const branch = entry.term as Extract<MIRInstr, { kind: 'branch' }>
35
+ // Both targets should exist
36
+ expect(fn.blocks.some(b => b.id === branch.then)).toBe(true)
37
+ expect(fn.blocks.some(b => b.id === branch.else)).toBe(true)
38
+ })
39
+
40
+ test('if/else creates branch to then + else + merge', () => {
41
+ const mod = compile(`
42
+ fn f(x: int): int {
43
+ if (x > 0) { return 1; }
44
+ else { return -1; }
45
+ }
46
+ `)
47
+ expect(verifyMIR(mod)).toEqual([])
48
+
49
+ const fn = getFn(mod)
50
+ const entry = getBlock(fn, fn.entry)!
51
+ expect(entry.term.kind).toBe('branch')
52
+
53
+ const branch = entry.term as Extract<MIRInstr, { kind: 'branch' }>
54
+ const thenBlock = getBlock(fn, branch.then)!
55
+ const elseBlock = getBlock(fn, branch.else)!
56
+
57
+ // Both branches should have return terminators
58
+ expect(thenBlock.term.kind).toBe('return')
59
+ expect(elseBlock.term.kind).toBe('return')
60
+ })
61
+
62
+ test('nested if/else produces correct block structure', () => {
63
+ const mod = compile(`
64
+ fn f(x: int): int {
65
+ if (x > 0) {
66
+ if (x > 10) { return 2; }
67
+ return 1;
68
+ }
69
+ return 0;
70
+ }
71
+ `)
72
+ expect(verifyMIR(mod)).toEqual([])
73
+
74
+ const fn = getFn(mod)
75
+ // Should have multiple blocks due to nesting
76
+ expect(fn.blocks.length).toBeGreaterThanOrEqual(4)
77
+ })
78
+ })
79
+
80
+ describe('MIR lowering — while → loop CFG', () => {
81
+ test('while loop creates header + body + exit blocks', () => {
82
+ const mod = compile(`
83
+ fn f(x: int): int {
84
+ let sum: int = 0;
85
+ while (x > 0) {
86
+ sum = sum + x;
87
+ x = x - 1;
88
+ }
89
+ return sum;
90
+ }
91
+ `)
92
+ expect(verifyMIR(mod)).toEqual([])
93
+
94
+ const fn = getFn(mod)
95
+ // Find loop header block (has branch terminator based on condition)
96
+ const headerBlock = fn.blocks.find(b =>
97
+ b.id.startsWith('loop_header') && b.term.kind === 'branch'
98
+ )
99
+ expect(headerBlock).toBeDefined()
100
+
101
+ // Find loop body block
102
+ const bodyBlock = fn.blocks.find(b => b.id.startsWith('loop_body'))
103
+ expect(bodyBlock).toBeDefined()
104
+
105
+ // Find loop exit block
106
+ const exitBlock = fn.blocks.find(b => b.id.startsWith('loop_exit'))
107
+ expect(exitBlock).toBeDefined()
108
+
109
+ // Header should branch to body (then) and exit (else)
110
+ const branch = headerBlock!.term as Extract<MIRInstr, { kind: 'branch' }>
111
+ expect(branch.then).toBe(bodyBlock!.id)
112
+ expect(branch.else).toBe(exitBlock!.id)
113
+ })
114
+
115
+ test('break jumps to loop exit', () => {
116
+ const mod = compile(`
117
+ fn f(x: int): void {
118
+ while (x > 0) {
119
+ if (x == 5) { break; }
120
+ x = x - 1;
121
+ }
122
+ }
123
+ `)
124
+ expect(verifyMIR(mod)).toEqual([])
125
+
126
+ const fn = getFn(mod)
127
+ const exitBlock = fn.blocks.find(b => b.id.startsWith('loop_exit'))
128
+ expect(exitBlock).toBeDefined()
129
+
130
+ // Some block should jump directly to the exit (the break)
131
+ const breakBlock = fn.blocks.find(b =>
132
+ b.term.kind === 'jump' && (b.term as any).target === exitBlock!.id
133
+ && !b.id.startsWith('loop_header') && !b.id.startsWith('loop_exit')
134
+ && !b.id.startsWith('entry')
135
+ )
136
+ expect(breakBlock).toBeDefined()
137
+ })
138
+
139
+ test('continue jumps to loop header', () => {
140
+ const mod = compile(`
141
+ fn f(x: int): void {
142
+ while (x > 0) {
143
+ x = x - 1;
144
+ if (x == 3) { continue; }
145
+ }
146
+ }
147
+ `)
148
+ expect(verifyMIR(mod)).toEqual([])
149
+
150
+ const fn = getFn(mod)
151
+ const headerBlock = fn.blocks.find(b => b.id.startsWith('loop_header'))
152
+ expect(headerBlock).toBeDefined()
153
+
154
+ // Some block should jump to header (the continue)
155
+ const continueBlock = fn.blocks.find(b =>
156
+ b.term.kind === 'jump' && (b.term as any).target === headerBlock!.id
157
+ && !b.id.startsWith('loop_body') && !b.id.startsWith('entry')
158
+ )
159
+ expect(continueBlock).toBeDefined()
160
+ })
161
+ })
162
+
163
+ describe('MIR lowering — short-circuit operators', () => {
164
+ test('&& produces branch (short-circuit)', () => {
165
+ const mod = compile('fn f(a: bool, b: bool): bool { return a && b; }')
166
+ expect(verifyMIR(mod)).toEqual([])
167
+
168
+ const fn = getFn(mod)
169
+ // Should have and_right, and_false, and_merge blocks
170
+ expect(fn.blocks.some(b => b.id.startsWith('and_right'))).toBe(true)
171
+ expect(fn.blocks.some(b => b.id.startsWith('and_false'))).toBe(true)
172
+ expect(fn.blocks.some(b => b.id.startsWith('and_merge'))).toBe(true)
173
+ })
174
+
175
+ test('|| produces branch (short-circuit)', () => {
176
+ const mod = compile('fn f(a: bool, b: bool): bool { return a || b; }')
177
+ expect(verifyMIR(mod)).toEqual([])
178
+
179
+ const fn = getFn(mod)
180
+ expect(fn.blocks.some(b => b.id.startsWith('or_true'))).toBe(true)
181
+ expect(fn.blocks.some(b => b.id.startsWith('or_right'))).toBe(true)
182
+ expect(fn.blocks.some(b => b.id.startsWith('or_merge'))).toBe(true)
183
+ })
184
+ })
185
+
186
+ describe('MIR lowering — return', () => {
187
+ test('void return', () => {
188
+ const mod = compile('fn f(): void { return; }')
189
+ expect(verifyMIR(mod)).toEqual([])
190
+
191
+ const fn = getFn(mod)
192
+ const entry = getBlock(fn, fn.entry)!
193
+ expect(entry.term.kind).toBe('return')
194
+ expect((entry.term as any).value).toBeNull()
195
+ })
196
+
197
+ test('value return', () => {
198
+ const mod = compile('fn f(): int { return 42; }')
199
+ expect(verifyMIR(mod)).toEqual([])
200
+
201
+ const fn = getFn(mod)
202
+ const entry = getBlock(fn, fn.entry)!
203
+ expect(entry.term.kind).toBe('return')
204
+ expect((entry.term as any).value).toEqual({ kind: 'const', value: 42 })
205
+ })
206
+
207
+ test('early return creates dead block for subsequent code', () => {
208
+ const mod = compile(`
209
+ fn f(x: int): int {
210
+ return x;
211
+ let y: int = 0;
212
+ }
213
+ `)
214
+ expect(verifyMIR(mod)).toEqual([])
215
+
216
+ const fn = getFn(mod)
217
+ const entry = getBlock(fn, fn.entry)!
218
+ expect(entry.term.kind).toBe('return')
219
+ })
220
+ })
221
+
222
+ describe('MIR lowering — all blocks reachable and well-formed', () => {
223
+ test('complex function verifies clean', () => {
224
+ const mod = compile(`
225
+ fn f(n: int): int {
226
+ let sum: int = 0;
227
+ let i: int = 0;
228
+ while (i < n) {
229
+ if (i % 2 == 0) {
230
+ sum = sum + i;
231
+ } else {
232
+ sum = sum - i;
233
+ }
234
+ i = i + 1;
235
+ }
236
+ return sum;
237
+ }
238
+ `)
239
+ const errors = verifyMIR(mod)
240
+ expect(errors).toEqual([])
241
+ })
242
+ })
@@ -0,0 +1,254 @@
1
+ import { verifyMIR } from '../../mir/verify'
2
+ import type { MIRModule, MIRBlock, MIRFunction } from '../../mir/types'
3
+
4
+ function makeModule(functions: MIRFunction[]): MIRModule {
5
+ return { functions, namespace: 'test', objective: '__test' }
6
+ }
7
+
8
+ function makeBlock(id: string, instrs: any[], term: any, preds: string[] = []): MIRBlock {
9
+ return { id, instrs, term, preds }
10
+ }
11
+
12
+ describe('MIR verifier — terminator checks', () => {
13
+ test('rejects block without proper terminator', () => {
14
+ const fn: MIRFunction = {
15
+ name: 'bad',
16
+ params: [],
17
+ blocks: [
18
+ makeBlock('entry', [], { kind: 'const', dst: 't0', value: 42 }),
19
+ ],
20
+ entry: 'entry',
21
+ isMacro: false,
22
+ }
23
+
24
+ const errors = verifyMIR(makeModule([fn]))
25
+ expect(errors.length).toBeGreaterThan(0)
26
+ expect(errors[0].message).toContain('does not end with a terminator')
27
+ })
28
+
29
+ test('rejects terminator in non-terminal position', () => {
30
+ const fn: MIRFunction = {
31
+ name: 'bad',
32
+ params: [],
33
+ blocks: [
34
+ makeBlock(
35
+ 'entry',
36
+ [{ kind: 'jump', target: 'entry' }], // terminator in instrs
37
+ { kind: 'return', value: null },
38
+ ),
39
+ ],
40
+ entry: 'entry',
41
+ isMacro: false,
42
+ }
43
+
44
+ const errors = verifyMIR(makeModule([fn]))
45
+ expect(errors.length).toBeGreaterThan(0)
46
+ expect(errors[0].message).toContain('terminator')
47
+ expect(errors[0].message).toContain('non-terminal position')
48
+ })
49
+
50
+ test('accepts valid terminator (return)', () => {
51
+ const fn: MIRFunction = {
52
+ name: 'good',
53
+ params: [],
54
+ blocks: [
55
+ makeBlock('entry', [], { kind: 'return', value: null }),
56
+ ],
57
+ entry: 'entry',
58
+ isMacro: false,
59
+ }
60
+
61
+ expect(verifyMIR(makeModule([fn]))).toEqual([])
62
+ })
63
+
64
+ test('accepts valid terminator (jump)', () => {
65
+ const fn: MIRFunction = {
66
+ name: 'good',
67
+ params: [],
68
+ blocks: [
69
+ makeBlock('entry', [], { kind: 'jump', target: 'b1' }),
70
+ makeBlock('b1', [], { kind: 'return', value: null }),
71
+ ],
72
+ entry: 'entry',
73
+ isMacro: false,
74
+ }
75
+
76
+ expect(verifyMIR(makeModule([fn]))).toEqual([])
77
+ })
78
+ })
79
+
80
+ describe('MIR verifier — target existence', () => {
81
+ test('rejects jump to non-existent block', () => {
82
+ const fn: MIRFunction = {
83
+ name: 'bad',
84
+ params: [],
85
+ blocks: [
86
+ makeBlock('entry', [], { kind: 'jump', target: 'nonexistent' }),
87
+ ],
88
+ entry: 'entry',
89
+ isMacro: false,
90
+ }
91
+
92
+ const errors = verifyMIR(makeModule([fn]))
93
+ expect(errors.length).toBeGreaterThan(0)
94
+ expect(errors[0].message).toContain('non-existent target')
95
+ })
96
+
97
+ test('rejects branch to non-existent block', () => {
98
+ const fn: MIRFunction = {
99
+ name: 'bad',
100
+ params: [],
101
+ blocks: [
102
+ makeBlock('entry', [
103
+ { kind: 'const', dst: 't0', value: 1 },
104
+ ], { kind: 'branch', cond: { kind: 'temp', name: 't0' }, then: 'yes', else: 'no' }),
105
+ ],
106
+ entry: 'entry',
107
+ isMacro: false,
108
+ }
109
+
110
+ const errors = verifyMIR(makeModule([fn]))
111
+ expect(errors.length).toBe(2) // both targets missing
112
+ expect(errors[0].message).toContain('non-existent target')
113
+ })
114
+ })
115
+
116
+ describe('MIR verifier — reachability', () => {
117
+ test('rejects unreachable block', () => {
118
+ const fn: MIRFunction = {
119
+ name: 'bad',
120
+ params: [],
121
+ blocks: [
122
+ makeBlock('entry', [], { kind: 'return', value: null }),
123
+ makeBlock('orphan', [], { kind: 'return', value: null }),
124
+ ],
125
+ entry: 'entry',
126
+ isMacro: false,
127
+ }
128
+
129
+ const errors = verifyMIR(makeModule([fn]))
130
+ expect(errors.length).toBe(1)
131
+ expect(errors[0].message).toContain('unreachable')
132
+ expect(errors[0].block).toBe('orphan')
133
+ })
134
+
135
+ test('accepts all blocks reachable via jumps', () => {
136
+ const fn: MIRFunction = {
137
+ name: 'good',
138
+ params: [],
139
+ blocks: [
140
+ makeBlock('entry', [], { kind: 'jump', target: 'b1' }),
141
+ makeBlock('b1', [], { kind: 'jump', target: 'b2' }),
142
+ makeBlock('b2', [], { kind: 'return', value: null }),
143
+ ],
144
+ entry: 'entry',
145
+ isMacro: false,
146
+ }
147
+
148
+ expect(verifyMIR(makeModule([fn]))).toEqual([])
149
+ })
150
+
151
+ test('accepts blocks reachable via branch', () => {
152
+ const fn: MIRFunction = {
153
+ name: 'good',
154
+ params: [],
155
+ blocks: [
156
+ makeBlock('entry', [
157
+ { kind: 'const', dst: 't0', value: 1 },
158
+ ], { kind: 'branch', cond: { kind: 'temp', name: 't0' }, then: 'yes', else: 'no' }),
159
+ makeBlock('yes', [], { kind: 'return', value: null }),
160
+ makeBlock('no', [], { kind: 'return', value: null }),
161
+ ],
162
+ entry: 'entry',
163
+ isMacro: false,
164
+ }
165
+
166
+ expect(verifyMIR(makeModule([fn]))).toEqual([])
167
+ })
168
+ })
169
+
170
+ describe('MIR verifier — use-before-def', () => {
171
+ test('rejects use of undefined temp', () => {
172
+ const fn: MIRFunction = {
173
+ name: 'bad',
174
+ params: [],
175
+ blocks: [
176
+ makeBlock('entry', [
177
+ { kind: 'copy', dst: 't0', src: { kind: 'temp', name: 'undefined_temp' } },
178
+ ], { kind: 'return', value: null }),
179
+ ],
180
+ entry: 'entry',
181
+ isMacro: false,
182
+ }
183
+
184
+ const errors = verifyMIR(makeModule([fn]))
185
+ expect(errors.length).toBeGreaterThan(0)
186
+ expect(errors[0].message).toContain('undefined_temp')
187
+ expect(errors[0].message).toContain('never defined')
188
+ })
189
+
190
+ test('accepts temps defined as params', () => {
191
+ const fn: MIRFunction = {
192
+ name: 'good',
193
+ params: [{ name: 't0', isMacroParam: false }],
194
+ blocks: [
195
+ makeBlock('entry', [], { kind: 'return', value: { kind: 'temp', name: 't0' } }),
196
+ ],
197
+ entry: 'entry',
198
+ isMacro: false,
199
+ }
200
+
201
+ expect(verifyMIR(makeModule([fn]))).toEqual([])
202
+ })
203
+
204
+ test('accepts temps defined in instructions', () => {
205
+ const fn: MIRFunction = {
206
+ name: 'good',
207
+ params: [],
208
+ blocks: [
209
+ makeBlock('entry', [
210
+ { kind: 'const', dst: 't0', value: 42 },
211
+ { kind: 'copy', dst: 't1', src: { kind: 'temp', name: 't0' } },
212
+ ], { kind: 'return', value: { kind: 'temp', name: 't1' } }),
213
+ ],
214
+ entry: 'entry',
215
+ isMacro: false,
216
+ }
217
+
218
+ expect(verifyMIR(makeModule([fn]))).toEqual([])
219
+ })
220
+
221
+ test('const operands do not require definition', () => {
222
+ const fn: MIRFunction = {
223
+ name: 'good',
224
+ params: [],
225
+ blocks: [
226
+ makeBlock('entry', [
227
+ { kind: 'const', dst: 't0', value: 5 },
228
+ { kind: 'add', dst: 't1', a: { kind: 'temp', name: 't0' }, b: { kind: 'const', value: 3 } },
229
+ ], { kind: 'return', value: { kind: 'temp', name: 't1' } }),
230
+ ],
231
+ entry: 'entry',
232
+ isMacro: false,
233
+ }
234
+
235
+ expect(verifyMIR(makeModule([fn]))).toEqual([])
236
+ })
237
+ })
238
+
239
+ describe('MIR verifier — entry block', () => {
240
+ test('rejects missing entry block', () => {
241
+ const fn: MIRFunction = {
242
+ name: 'bad',
243
+ params: [],
244
+ blocks: [
245
+ makeBlock('not_entry', [], { kind: 'return', value: null }),
246
+ ],
247
+ entry: 'entry',
248
+ isMacro: false,
249
+ }
250
+
251
+ const errors = verifyMIR(makeModule([fn]))
252
+ expect(errors.some(e => e.message.includes('entry block'))).toBe(true)
253
+ })
254
+ })
@@ -0,0 +1,84 @@
1
+ import { blockMerge } from '../../optimizer/block_merge'
2
+ import type { MIRFunction, MIRBlock, MIRInstr, Operand } from '../../mir/types'
3
+
4
+ function mkFn(blocks: MIRBlock[], entry = 'entry'): MIRFunction {
5
+ return { name: 'test', params: [], blocks, entry, isMacro: false }
6
+ }
7
+
8
+ function mkBlock(id: string, instrs: MIRInstr[], term: MIRInstr, preds: string[] = []): MIRBlock {
9
+ return { id, instrs, term, preds }
10
+ }
11
+
12
+ const c = (v: number): Operand => ({ kind: 'const', value: v })
13
+ const t = (n: string): Operand => ({ kind: 'temp', name: n })
14
+
15
+ describe('block merging', () => {
16
+ test('merges single-pred successor into predecessor', () => {
17
+ const fn = mkFn([
18
+ mkBlock('entry', [
19
+ { kind: 'const', dst: 't0', value: 1 },
20
+ ], { kind: 'jump', target: 'b1' }),
21
+ mkBlock('b1', [
22
+ { kind: 'const', dst: 't1', value: 2 },
23
+ ], { kind: 'return', value: t('t1') }, ['entry']),
24
+ ])
25
+ const result = blockMerge(fn)
26
+ expect(result.blocks).toHaveLength(1)
27
+ expect(result.blocks[0].id).toBe('entry')
28
+ expect(result.blocks[0].instrs).toHaveLength(2)
29
+ expect(result.blocks[0].term).toEqual({ kind: 'return', value: t('t1') })
30
+ })
31
+
32
+ test('does not merge when successor has multiple preds', () => {
33
+ const fn = mkFn([
34
+ mkBlock('entry', [], { kind: 'branch', cond: t('c'), then: 'b1', else: 'b2' }),
35
+ mkBlock('b1', [], { kind: 'jump', target: 'merge' }, ['entry']),
36
+ mkBlock('b2', [], { kind: 'jump', target: 'merge' }, ['entry']),
37
+ mkBlock('merge', [], { kind: 'return', value: null }, ['b1', 'b2']),
38
+ ])
39
+ const result = blockMerge(fn)
40
+ // merge has 2 preds → no merging of merge block
41
+ expect(result.blocks.length).toBeGreaterThanOrEqual(3)
42
+ expect(result.blocks.some(b => b.id === 'merge')).toBe(true)
43
+ })
44
+
45
+ test('does not merge entry block into predecessor', () => {
46
+ // Entry block should never be merged away
47
+ const fn = mkFn([
48
+ mkBlock('entry', [], { kind: 'jump', target: 'b1' }),
49
+ mkBlock('b1', [], { kind: 'return', value: null }, ['entry']),
50
+ ])
51
+ const result = blockMerge(fn)
52
+ // b1 has single pred → merged into entry
53
+ expect(result.blocks).toHaveLength(1)
54
+ expect(result.blocks[0].id).toBe('entry')
55
+ })
56
+
57
+ test('chains merges: A→B→C all single-pred', () => {
58
+ const fn = mkFn([
59
+ mkBlock('entry', [
60
+ { kind: 'const', dst: 't0', value: 1 },
61
+ ], { kind: 'jump', target: 'b1' }),
62
+ mkBlock('b1', [
63
+ { kind: 'const', dst: 't1', value: 2 },
64
+ ], { kind: 'jump', target: 'b2' }, ['entry']),
65
+ mkBlock('b2', [
66
+ { kind: 'const', dst: 't2', value: 3 },
67
+ ], { kind: 'return', value: t('t2') }, ['b1']),
68
+ ])
69
+ const result = blockMerge(fn)
70
+ expect(result.blocks).toHaveLength(1)
71
+ expect(result.blocks[0].instrs).toHaveLength(3)
72
+ })
73
+
74
+ test('recomputes preds after merge', () => {
75
+ const fn = mkFn([
76
+ mkBlock('entry', [], { kind: 'jump', target: 'b1' }),
77
+ mkBlock('b1', [], { kind: 'jump', target: 'b2' }, ['entry']),
78
+ mkBlock('b2', [], { kind: 'return', value: null }, ['b1']),
79
+ ])
80
+ const result = blockMerge(fn)
81
+ expect(result.blocks).toHaveLength(1)
82
+ expect(result.blocks[0].preds).toEqual([])
83
+ })
84
+ })
@@ -0,0 +1,64 @@
1
+ import { branchSimplify } from '../../optimizer/branch_simplify'
2
+ import type { MIRFunction, MIRBlock, MIRInstr, Operand } from '../../mir/types'
3
+
4
+ function mkFn(blocks: MIRBlock[]): MIRFunction {
5
+ return { name: 'test', params: [], blocks, entry: 'entry', isMacro: false }
6
+ }
7
+
8
+ function mkBlock(id: string, instrs: MIRInstr[], term: MIRInstr, preds: string[] = []): MIRBlock {
9
+ return { id, instrs, term, preds }
10
+ }
11
+
12
+ const c = (v: number): Operand => ({ kind: 'const', value: v })
13
+ const t = (n: string): Operand => ({ kind: 'temp', name: n })
14
+
15
+ describe('branch simplification', () => {
16
+ test('branch(1, then, else) → jump(then)', () => {
17
+ const fn = mkFn([
18
+ mkBlock('entry', [], { kind: 'branch', cond: c(1), then: 'b1', else: 'b2' }),
19
+ mkBlock('b1', [], { kind: 'return', value: null }, ['entry']),
20
+ mkBlock('b2', [], { kind: 'return', value: null }, ['entry']),
21
+ ])
22
+ const result = branchSimplify(fn)
23
+ expect(result.blocks[0].term).toEqual({ kind: 'jump', target: 'b1' })
24
+ })
25
+
26
+ test('branch(0, then, else) → jump(else)', () => {
27
+ const fn = mkFn([
28
+ mkBlock('entry', [], { kind: 'branch', cond: c(0), then: 'b1', else: 'b2' }),
29
+ mkBlock('b1', [], { kind: 'return', value: null }, ['entry']),
30
+ mkBlock('b2', [], { kind: 'return', value: null }, ['entry']),
31
+ ])
32
+ const result = branchSimplify(fn)
33
+ expect(result.blocks[0].term).toEqual({ kind: 'jump', target: 'b2' })
34
+ })
35
+
36
+ test('nonzero const (42) → jump(then)', () => {
37
+ const fn = mkFn([
38
+ mkBlock('entry', [], { kind: 'branch', cond: c(42), then: 'b1', else: 'b2' }),
39
+ mkBlock('b1', [], { kind: 'return', value: null }),
40
+ mkBlock('b2', [], { kind: 'return', value: null }),
41
+ ])
42
+ const result = branchSimplify(fn)
43
+ expect(result.blocks[0].term).toEqual({ kind: 'jump', target: 'b1' })
44
+ })
45
+
46
+ test('does not simplify branch with temp cond', () => {
47
+ const fn = mkFn([
48
+ mkBlock('entry', [], { kind: 'branch', cond: t('flag'), then: 'b1', else: 'b2' }),
49
+ mkBlock('b1', [], { kind: 'return', value: null }),
50
+ mkBlock('b2', [], { kind: 'return', value: null }),
51
+ ])
52
+ const result = branchSimplify(fn)
53
+ expect(result.blocks[0].term.kind).toBe('branch')
54
+ })
55
+
56
+ test('does not touch jump terminators', () => {
57
+ const fn = mkFn([
58
+ mkBlock('entry', [], { kind: 'jump', target: 'b1' }),
59
+ mkBlock('b1', [], { kind: 'return', value: null }),
60
+ ])
61
+ const result = branchSimplify(fn)
62
+ expect(result.blocks[0].term).toEqual({ kind: 'jump', target: 'b1' })
63
+ })
64
+ })