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
package/src/index.ts CHANGED
@@ -1,181 +1,30 @@
1
1
  /**
2
2
  * RedScript Compiler
3
- *
3
+ *
4
4
  * Main entry point for programmatic usage.
5
5
  */
6
6
 
7
- // eslint-disable-next-line @typescript-eslint/no-var-requires
8
- export const version = '1.2.11'
7
+ export const version = '2.0.0'
9
8
 
10
9
  import { Lexer } from './lexer'
11
10
  import { Parser } from './parser'
12
- import { TypeChecker } from './typechecker'
13
- import { Lowering, setScoreboardObjective } from './lowering'
14
- import type { Warning } from './lowering'
15
- import {
16
- constantFoldingWithStats,
17
- copyPropagation,
18
- deadCodeEliminationWithStats,
19
- } from './optimizer/passes'
20
- import { eliminateDeadCode } from './optimizer/dce'
21
- import {
22
- countMcfunctionCommands,
23
- generateDatapackWithStats,
24
- DatapackFile,
25
- } from './codegen/mcfunction'
26
- import { preprocessSource, preprocessSourceWithMetadata } from './compile'
27
- import type { IRModule } from './ir/types'
28
- import type { Program } from './ast/types'
29
- import type { DiagnosticError } from './diagnostics'
30
- import { createEmptyOptimizationStats, type OptimizationStats } from './optimizer/commands'
11
+ import { preprocessSource } from './compile'
31
12
 
32
- export interface CompileOptions {
33
- namespace?: string
34
- optimize?: boolean
35
- typeCheck?: boolean
36
- filePath?: string
37
- dce?: boolean
38
- mangle?: boolean
39
- /** Scoreboard objective used for all variable slots.
40
- * Defaults to '__<namespace>' (e.g. '__mathshow') to avoid collisions when
41
- * multiple RedScript datapacks are loaded simultaneously, without occupying
42
- * the user's own namespace. Override only if you need a specific name. */
43
- scoreboardObjective?: string
44
- }
45
-
46
- export interface CompileResult {
47
- files: DatapackFile[]
48
- advancements: DatapackFile[]
49
- ast: Program
50
- ir: IRModule
51
- typeErrors?: DiagnosticError[]
52
- warnings?: Warning[]
53
- stats?: OptimizationStats
54
- sourceMap?: Record<string, string>
55
- }
56
-
57
- /**
58
- * Compile RedScript source code to a Minecraft datapack.
59
- *
60
- * @param source - The RedScript source code
61
- * @param options - Compilation options
62
- * @returns Compiled datapack files
63
- */
64
- export function compile(source: string, options: CompileOptions = {}): CompileResult {
65
- const namespace = options.namespace ?? 'redscript'
66
- const shouldOptimize = options.optimize ?? true
67
- const shouldTypeCheck = options.typeCheck ?? true
68
- const shouldRunDce = options.dce ?? shouldOptimize
69
- const mangle = options.mangle ?? false
70
- const filePath = options.filePath
71
- const preprocessed = preprocessSourceWithMetadata(source, { filePath })
72
- const preprocessedSource = preprocessed.source
73
-
74
- // Lexing
75
- const tokens = new Lexer(preprocessedSource, filePath).tokenize()
76
-
77
- // Parsing — user source
78
- const parsedAst = new Parser(tokens, preprocessedSource, filePath).parse(namespace)
79
-
80
- // Library imports: files that declared `module library;` are parsed independently
81
- // (fresh Parser per file) so their functions are DCE-eligible but never bleed into user code.
82
- const allLibrarySources: Array<{ src: string; fp?: string }> = []
83
- for (const li of preprocessed.libraryImports ?? []) {
84
- allLibrarySources.push({ src: li.source, fp: li.filePath })
85
- }
86
- for (const { src, fp } of allLibrarySources) {
87
- const libPreprocessed = preprocessSourceWithMetadata(src, fp ? { filePath: fp } : {})
88
- const libTokens = new Lexer(libPreprocessed.source, fp).tokenize()
89
- const libAst = new Parser(libTokens, libPreprocessed.source, fp).parse(namespace)
90
- for (const fn of libAst.declarations) fn.isLibraryFn = true
91
- parsedAst.declarations.push(...libAst.declarations)
92
- parsedAst.structs.push(...libAst.structs)
93
- parsedAst.implBlocks.push(...libAst.implBlocks)
94
- parsedAst.enums.push(...libAst.enums)
95
- parsedAst.consts.push(...libAst.consts)
96
- parsedAst.globals.push(...libAst.globals)
97
- }
98
-
99
- const dceResult = shouldRunDce ? eliminateDeadCode(parsedAst, preprocessed.ranges) : { program: parsedAst, warnings: [] }
100
- const ast = dceResult.program
101
-
102
- // Type checking (warn mode - collect errors but don't block)
103
- let typeErrors: DiagnosticError[] | undefined
104
- if (shouldTypeCheck) {
105
- const checker = new TypeChecker(preprocessedSource, filePath)
106
- typeErrors = checker.check(ast)
107
- }
108
-
109
- // Configure scoreboard objective for this compilation.
110
- // Default: use the datapack namespace so each datapack gets its own objective
111
- // automatically, preventing variable collisions when multiple datapacks coexist.
112
- const scoreboardObj = options.scoreboardObjective ?? `__${namespace}`
113
- setScoreboardObjective(scoreboardObj)
114
-
115
- // Lowering to IR
116
- const lowering = new Lowering(namespace, preprocessed.ranges)
117
- const ir = lowering.lower(ast)
118
-
119
- let optimizedIR: IRModule = ir
120
- let generated = generateDatapackWithStats(ir, { optimizeCommands: shouldOptimize, mangle, scoreboardObjective: scoreboardObj })
121
- let optimizationStats: OptimizationStats | undefined
122
-
123
- if (shouldOptimize) {
124
- const stats = createEmptyOptimizationStats()
125
- const copyPropagatedFunctions = []
126
- const deadCodeEliminatedFunctions = []
127
-
128
- for (const fn of ir.functions) {
129
- const folded = constantFoldingWithStats(fn)
130
- stats.constantFolds += folded.stats.constantFolds ?? 0
13
+ // Re-export v2 compile API
14
+ export { compile, CompileOptions, CompileResult } from '../src2/emit/compile'
15
+ export type { DatapackFile } from '../src2/emit/index'
131
16
 
132
- const propagated = copyPropagation(folded.fn)
133
- copyPropagatedFunctions.push(propagated)
134
-
135
- const dce = deadCodeEliminationWithStats(propagated)
136
- deadCodeEliminatedFunctions.push(dce.fn)
137
- }
138
-
139
- const copyPropagatedIR: IRModule = { ...ir, functions: copyPropagatedFunctions }
140
- optimizedIR = { ...ir, functions: deadCodeEliminatedFunctions }
141
-
142
- const baselineGenerated = generateDatapackWithStats(ir, { optimizeCommands: false, mangle, scoreboardObjective: scoreboardObj })
143
- const beforeDceGenerated = generateDatapackWithStats(copyPropagatedIR, { optimizeCommands: false, mangle, scoreboardObjective: scoreboardObj })
144
- const afterDceGenerated = generateDatapackWithStats(optimizedIR, { optimizeCommands: false, mangle, scoreboardObjective: scoreboardObj })
145
- generated = generateDatapackWithStats(optimizedIR, { optimizeCommands: true, mangle, scoreboardObjective: scoreboardObj })
146
-
147
- stats.deadCodeRemoved =
148
- countMcfunctionCommands(beforeDceGenerated.files) - countMcfunctionCommands(afterDceGenerated.files)
149
- stats.licmHoists = generated.stats.licmHoists
150
- stats.licmLoopBodies = generated.stats.licmLoopBodies
151
- stats.cseRedundantReads = generated.stats.cseRedundantReads
152
- stats.cseArithmetic = generated.stats.cseArithmetic
153
- stats.setblockMergedCommands = generated.stats.setblockMergedCommands
154
- stats.setblockFillCommands = generated.stats.setblockFillCommands
155
- stats.setblockSavedCommands = generated.stats.setblockSavedCommands
156
- stats.totalCommandsBefore = countMcfunctionCommands(baselineGenerated.files)
157
- stats.totalCommandsAfter = countMcfunctionCommands(generated.files)
158
- optimizationStats = stats
159
- } else {
160
- optimizedIR = ir
161
- generated = generateDatapackWithStats(ir, { optimizeCommands: false, mangle, scoreboardObjective: scoreboardObj })
162
- }
163
-
164
- return {
165
- files: [...generated.files, ...generated.advancements],
166
- advancements: generated.advancements,
167
- ast,
168
- ir: optimizedIR,
169
- typeErrors,
170
- warnings: [...dceResult.warnings, ...lowering.warnings],
171
- stats: optimizationStats,
172
- sourceMap: generated.sourceMap,
173
- }
174
- }
17
+ // Re-export utilities
18
+ export { Lexer } from './lexer'
19
+ export { Parser } from './parser'
20
+ export { preprocessSource, preprocessSourceWithMetadata } from './compile'
21
+ export { MCCommandValidator } from './mc-validator'
22
+ export type { Program, FnDecl, Expr, Stmt, Span } from './ast/types'
23
+ export type { DiagnosticError } from './diagnostics'
175
24
 
176
25
  /**
177
26
  * Check RedScript source code for errors without generating output.
178
- *
27
+ *
179
28
  * @param source - The RedScript source code
180
29
  * @param namespace - Optional namespace
181
30
  * @returns null if no errors, or an error object
@@ -190,16 +39,3 @@ export function check(source: string, namespace = 'redscript', filePath?: string
190
39
  return err as Error
191
40
  }
192
41
  }
193
-
194
- // Re-export types and classes for advanced usage
195
- export { Lexer } from './lexer'
196
- export { Parser } from './parser'
197
- export { TypeChecker } from './typechecker'
198
- export { Lowering } from './lowering'
199
- export { optimize } from './optimizer/passes'
200
- export { generateDatapack } from './codegen/mcfunction'
201
- export { MCCommandValidator } from './mc-validator'
202
- export type { DatapackFile } from './codegen/mcfunction'
203
- export type { IRModule, IRFunction } from './ir/types'
204
- export type { Program, FnDecl, Expr, Stmt, Span } from './ast/types'
205
- export type { DiagnosticError } from './diagnostics'
@@ -54,9 +54,10 @@ export async function runMCTests(
54
54
  // Compile and install datapack
55
55
  const outDir = path.join(MC_SERVER_DIR, 'world', 'datapacks', 'redscript-test')
56
56
  console.log(`Compiling ${sourceFile}...`)
57
- const result = compile(fs.readFileSync(sourceFile, 'utf-8'))
58
- if (!result.success || !result.files) {
59
- throw result.error ?? new Error('Compilation failed')
57
+ const ns = path.basename(sourceFile, '.mcrs')
58
+ const result = compile(fs.readFileSync(sourceFile, 'utf-8'), { namespace: ns, filePath: sourceFile })
59
+ if (!result.files) {
60
+ throw new Error('Compilation failed')
60
61
  }
61
62
  // Write files
62
63
  fs.mkdirSync(outDir, { recursive: true })
@@ -12,7 +12,7 @@ import type {
12
12
  StructDecl, StructField, ExecuteSubcommand, EnumDecl, EnumVariant, BlockPosExpr, ImplBlock,
13
13
  CoordComponent, LambdaParam, EntityTypeName
14
14
  } from '../ast/types'
15
- import type { BinOp, CmpOp } from '../ir/types'
15
+ import type { BinOp, CmpOp } from '../ast/types'
16
16
  import { DiagnosticError } from '../diagnostics'
17
17
 
18
18
  // ---------------------------------------------------------------------------
package/src/repl.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as readline from 'readline'
2
2
 
3
3
  import { compile } from './index'
4
- import type { DatapackFile } from './codegen/mcfunction'
4
+ import type { DatapackFile } from '../src2/emit/index'
5
5
 
6
6
  const REPL_FN = '__repl'
7
7
 
@@ -1496,7 +1496,7 @@ export class MCRuntime {
1496
1496
 
1497
1497
  compileAndLoad(source: string): void {
1498
1498
  const result = rsCompile(source, { namespace: this.namespace })
1499
- if (!result.success || !result.files) {
1499
+ if (!result.files) {
1500
1500
  throw new Error('Compilation failed')
1501
1501
  }
1502
1502
 
@@ -0,0 +1,154 @@
1
+ /**
2
+ * End-to-end tests for the v2 compiler pipeline.
3
+ *
4
+ * These tests compile RedScript source through the full pipeline
5
+ * (Lexer → Parser → HIR → MIR → optimize → LIR → emit) and verify
6
+ * the generated .mcfunction output contains expected MC commands.
7
+ */
8
+
9
+ import { compile } from '../../emit/compile'
10
+
11
+ function getFile(files: { path: string; content: string }[], pathSubstr: string): string | undefined {
12
+ const f = files.find(f => f.path.includes(pathSubstr))
13
+ return f?.content
14
+ }
15
+
16
+ describe('e2e: basic compilation', () => {
17
+ test('simple arithmetic function produces scoreboard commands', () => {
18
+ const source = `
19
+ fn add(a: int, b: int): int {
20
+ return a + b;
21
+ }
22
+ `
23
+ const result = compile(source, { namespace: 'test' })
24
+ expect(result.files.length).toBeGreaterThan(0)
25
+
26
+ const addFn = getFile(result.files, 'add.mcfunction')
27
+ expect(addFn).toBeDefined()
28
+ expect(addFn).toContain('scoreboard players operation')
29
+ expect(addFn).toContain('__test')
30
+ })
31
+
32
+ test('pack.mcmeta is generated with pack_format 26', () => {
33
+ const source = `fn noop(): void {}`
34
+ const result = compile(source, { namespace: 'demo' })
35
+ const meta = getFile(result.files, 'pack.mcmeta')
36
+ expect(meta).toBeDefined()
37
+ const parsed = JSON.parse(meta!)
38
+ expect(parsed.pack.pack_format).toBe(26)
39
+ })
40
+
41
+ test('load.mcfunction creates scoreboard objective', () => {
42
+ const source = `fn noop(): void {}`
43
+ const result = compile(source, { namespace: 'mypack' })
44
+ const load = getFile(result.files, 'load.mcfunction')
45
+ expect(load).toBeDefined()
46
+ expect(load).toContain('scoreboard objectives add __mypack dummy')
47
+ })
48
+
49
+ test('@tick function appears in tick.json', () => {
50
+ const source = `
51
+ @tick fn game_tick(): void {
52
+ let x: int = 1;
53
+ }
54
+ `
55
+ const result = compile(source, { namespace: 'ticktest' })
56
+ const tickJson = getFile(result.files, 'tick.json')
57
+ expect(tickJson).toBeDefined()
58
+ const parsed = JSON.parse(tickJson!)
59
+ expect(parsed.values).toContain('ticktest:game_tick')
60
+ })
61
+
62
+ test('@load function appears in load.json', () => {
63
+ const source = `
64
+ @load fn setup(): void {
65
+ let x: int = 42;
66
+ }
67
+ `
68
+ const result = compile(source, { namespace: 'loadtest' })
69
+ const loadJson = getFile(result.files, 'load.json')
70
+ expect(loadJson).toBeDefined()
71
+ const parsed = JSON.parse(loadJson!)
72
+ expect(parsed.values).toContain('loadtest:setup')
73
+ // load.json should also reference the objective-init load function
74
+ expect(parsed.values).toContain('loadtest:load')
75
+ })
76
+
77
+ test('if/else produces conditional call pattern', () => {
78
+ const source = `
79
+ fn check(x: int): int {
80
+ if (x > 0) {
81
+ return 1;
82
+ } else {
83
+ return 0;
84
+ }
85
+ }
86
+ `
87
+ const result = compile(source, { namespace: 'cond' })
88
+
89
+ // The main function should contain call_if_matches / call_unless_matches
90
+ const checkFn = getFile(result.files, 'check.mcfunction')
91
+ expect(checkFn).toBeDefined()
92
+ expect(checkFn).toContain('execute if score')
93
+ expect(checkFn).toContain('matches')
94
+ expect(checkFn).toContain('run function')
95
+ })
96
+
97
+ test('while loop produces loop structure with recursive call', () => {
98
+ const source = `
99
+ fn count(): void {
100
+ let i: int = 0;
101
+ while (i < 10) {
102
+ i = i + 1;
103
+ }
104
+ }
105
+ `
106
+ const result = compile(source, { namespace: 'loop' })
107
+
108
+ // There should be a loop body function that calls itself (or a header)
109
+ const fnFiles = result.files.filter(f => f.path.endsWith('.mcfunction'))
110
+ expect(fnFiles.length).toBeGreaterThan(1) // main + at least one extracted block
111
+
112
+ // At least one file should have a conditional call pattern for the loop
113
+ const allContent = fnFiles.map(f => f.content).join('\n')
114
+ expect(allContent).toContain('execute if score')
115
+ expect(allContent).toContain('run function')
116
+ })
117
+
118
+ test('function names are lowercased in output paths', () => {
119
+ const source = `fn MyFunc(): void {}`
120
+ const result = compile(source, { namespace: 'ns' })
121
+ const fn = result.files.find(f => f.path.includes('myfunc.mcfunction'))
122
+ expect(fn).toBeDefined()
123
+ })
124
+
125
+ test('constant assignment produces score_set', () => {
126
+ const source = `
127
+ fn init(): int {
128
+ let x: int = 42;
129
+ return x;
130
+ }
131
+ `
132
+ const result = compile(source, { namespace: 'cst' })
133
+ const fn = getFile(result.files, 'init.mcfunction')
134
+ expect(fn).toBeDefined()
135
+ expect(fn).toContain('scoreboard players set')
136
+ expect(fn).toContain('42')
137
+ })
138
+
139
+ test('load.json always includes namespace:load', () => {
140
+ const source = `fn noop(): void {}`
141
+ const result = compile(source, { namespace: 'abc' })
142
+ const loadJson = getFile(result.files, 'load.json')
143
+ expect(loadJson).toBeDefined()
144
+ const parsed = JSON.parse(loadJson!)
145
+ expect(parsed.values).toContain('abc:load')
146
+ })
147
+
148
+ test('no tick.json when no @tick functions', () => {
149
+ const source = `fn noop(): void {}`
150
+ const result = compile(source, { namespace: 'notick' })
151
+ const tickJson = getFile(result.files, 'tick.json')
152
+ expect(tickJson).toBeUndefined()
153
+ })
154
+ })
@@ -0,0 +1,199 @@
1
+ /**
2
+ * End-to-end tests for MC 1.20.2+ macro function support in the v2 pipeline.
3
+ *
4
+ * When a function uses runtime parameters in positions that require literal
5
+ * values in MC commands (coordinates, entity types, etc.), the compiler should
6
+ * automatically compile it as a macro function using $-prefixed syntax and
7
+ * call it via `function ns:fn with storage rs:macro_args`.
8
+ */
9
+
10
+ import { compile } from '../../emit/compile'
11
+
12
+ function getFile(files: { path: string; content: string }[], pathSubstr: string): string | undefined {
13
+ const f = files.find(f => f.path.includes(pathSubstr))
14
+ return f?.content
15
+ }
16
+
17
+ // ---------------------------------------------------------------------------
18
+ // Macro function detection
19
+ // ---------------------------------------------------------------------------
20
+
21
+ describe('e2e: macro function detection', () => {
22
+ test('function with int params in summon coords emits $-prefixed command', () => {
23
+ const source = `
24
+ fn spawn_zombie(x: int, y: int, z: int) {
25
+ summon("minecraft:zombie", x, y, z);
26
+ }
27
+ `
28
+ const result = compile(source, { namespace: 'test' })
29
+ const fn = getFile(result.files, 'spawn_zombie.mcfunction')
30
+ expect(fn).toBeDefined()
31
+ // The function body should have a $summon macro line
32
+ expect(fn).toContain('$summon minecraft:zombie $(x) $(y) $(z)')
33
+ })
34
+
35
+ test('function with all constant args does NOT produce macro line', () => {
36
+ const source = `
37
+ fn spawn_fixed() {
38
+ summon("minecraft:zombie", 100, 64, 200);
39
+ }
40
+ `
41
+ const result = compile(source, { namespace: 'test' })
42
+ const fn = getFile(result.files, 'spawn_fixed.mcfunction')
43
+ expect(fn).toBeDefined()
44
+ expect(fn).toContain('summon minecraft:zombie 100 64 200')
45
+ // No $ prefix
46
+ expect(fn).not.toMatch(/^\$summon/m)
47
+ })
48
+
49
+ test('function with int params in particle coords emits $-prefixed command', () => {
50
+ const source = `
51
+ fn show_particle(x: int, y: int, z: int) {
52
+ particle("minecraft:flame", x, y, z);
53
+ }
54
+ `
55
+ const result = compile(source, { namespace: 'test' })
56
+ const fn = getFile(result.files, 'show_particle.mcfunction')
57
+ expect(fn).toBeDefined()
58
+ expect(fn).toContain('$particle minecraft:flame $(x) $(y) $(z)')
59
+ })
60
+
61
+ test('function with int params in setblock coords emits $-prefixed command', () => {
62
+ const source = `
63
+ fn place_block(x: int, y: int, z: int) {
64
+ setblock(x, y, z, "minecraft:stone");
65
+ }
66
+ `
67
+ const result = compile(source, { namespace: 'test' })
68
+ const fn = getFile(result.files, 'place_block.mcfunction')
69
+ expect(fn).toBeDefined()
70
+ expect(fn).toContain('$setblock $(x) $(y) $(z) minecraft:stone')
71
+ })
72
+
73
+ test('mixed literal and variable args: only variable args get $()', () => {
74
+ const source = `
75
+ fn teleport_y(y: int) {
76
+ summon("minecraft:zombie", 100, y, 200);
77
+ }
78
+ `
79
+ const result = compile(source, { namespace: 'test' })
80
+ const fn = getFile(result.files, 'teleport_y.mcfunction')
81
+ expect(fn).toBeDefined()
82
+ expect(fn).toContain('$summon minecraft:zombie 100 $(y) 200')
83
+ })
84
+ })
85
+
86
+ // ---------------------------------------------------------------------------
87
+ // Macro call site generation
88
+ // ---------------------------------------------------------------------------
89
+
90
+ describe('e2e: macro call site generation', () => {
91
+ test('call site emits store_score_to_nbt + function with storage', () => {
92
+ const source = `
93
+ fn spawn_zombie(x: int, y: int, z: int) {
94
+ summon("minecraft:zombie", x, y, z);
95
+ }
96
+
97
+ fn caller(px: int, pz: int) {
98
+ spawn_zombie(px, 64, pz);
99
+ }
100
+ `
101
+ const result = compile(source, { namespace: 'test' })
102
+ const callerFn = getFile(result.files, 'caller.mcfunction')
103
+ expect(callerFn).toBeDefined()
104
+
105
+ // Should have 'function test:spawn_zombie with storage rs:macro_args'
106
+ expect(callerFn).toContain('with storage rs:macro_args')
107
+ expect(callerFn).toContain('spawn_zombie')
108
+ })
109
+
110
+ test('call site stores args to rs:macro_args NBT', () => {
111
+ const source = `
112
+ fn spawn_zombie(x: int, y: int, z: int) {
113
+ summon("minecraft:zombie", x, y, z);
114
+ }
115
+
116
+ fn caller(my_x: int) {
117
+ spawn_zombie(my_x, 64, 0);
118
+ }
119
+ `
120
+ const result = compile(source, { namespace: 'test' })
121
+ const callerFn = getFile(result.files, 'caller.mcfunction')
122
+ expect(callerFn).toBeDefined()
123
+
124
+ // Should have NBT storage setup for macro args
125
+ expect(callerFn).toContain('rs:macro_args')
126
+ expect(callerFn).toContain('with storage')
127
+ })
128
+ })
129
+
130
+ // ---------------------------------------------------------------------------
131
+ // Float macro params (local coords)
132
+ // ---------------------------------------------------------------------------
133
+
134
+ describe('e2e: float macro params with local coords', () => {
135
+ test('float params in ^coord positions produce macro function', () => {
136
+ const source = `
137
+ fn draw_pt(px: float, py: float) {
138
+ particle("minecraft:end_rod", ^px, ^py, ^5, 0.02, 0.02, 0.02, 0.0, 10);
139
+ }
140
+ `
141
+ const result = compile(source, { namespace: 'test' })
142
+ const fn = getFile(result.files, 'draw_pt.mcfunction')
143
+ expect(fn).toBeDefined()
144
+ // Should have $particle with ^$(px) and ^$(py)
145
+ expect(fn).toContain('$particle minecraft:end_rod ^$(px) ^$(py) ^5')
146
+ })
147
+
148
+ test('float macro call site uses double 0.01 scale for NBT storage', () => {
149
+ const source = `
150
+ fn draw_pt(px: float, py: float) {
151
+ particle("minecraft:end_rod", ^px, ^py, ^5, 0.02, 0.02, 0.02, 0.0, 10);
152
+ }
153
+
154
+ fn caller() {
155
+ draw_pt(100, 200);
156
+ }
157
+ `
158
+ const result = compile(source, { namespace: 'test' })
159
+ const callerFn = getFile(result.files, 'caller.mcfunction')
160
+ expect(callerFn).toBeDefined()
161
+
162
+ // Should store to NBT with double type and 0.01 scale
163
+ expect(callerFn).toContain('rs:macro_args')
164
+ expect(callerFn).toContain('double 0.01')
165
+ expect(callerFn).toContain('with storage rs:macro_args')
166
+ })
167
+ })
168
+
169
+ // ---------------------------------------------------------------------------
170
+ // Non-macro functions still work
171
+ // ---------------------------------------------------------------------------
172
+
173
+ describe('e2e: non-macro functions', () => {
174
+ test('say builtin emits normal (non-macro) command', () => {
175
+ const source = `
176
+ fn greet() {
177
+ say("hello world");
178
+ }
179
+ `
180
+ const result = compile(source, { namespace: 'test' })
181
+ const fn = getFile(result.files, 'greet.mcfunction')
182
+ expect(fn).toBeDefined()
183
+ expect(fn).toContain('say hello world')
184
+ // No $ prefix
185
+ expect(fn).not.toMatch(/^\$/m)
186
+ })
187
+
188
+ test('kill builtin emits normal command', () => {
189
+ const source = `
190
+ fn cleanup() {
191
+ kill(@e[tag=temp]);
192
+ }
193
+ `
194
+ const result = compile(source, { namespace: 'test' })
195
+ const fn = getFile(result.files, 'cleanup.mcfunction')
196
+ expect(fn).toBeDefined()
197
+ expect(fn).toContain('kill @e[tag=temp]')
198
+ })
199
+ })