redscript-mc 1.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 (272) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +31 -0
  3. package/.github/ISSUE_TEMPLATE/wrong_output.md +33 -0
  4. package/.github/PULL_REQUEST_TEMPLATE.md +34 -0
  5. package/.github/workflows/ci.yml +29 -0
  6. package/.github/workflows/publish-extension.yml +35 -0
  7. package/LICENSE +21 -0
  8. package/README.md +261 -0
  9. package/README.zh.md +261 -0
  10. package/dist/__tests__/cli.test.d.ts +1 -0
  11. package/dist/__tests__/cli.test.js +140 -0
  12. package/dist/__tests__/codegen.test.d.ts +1 -0
  13. package/dist/__tests__/codegen.test.js +121 -0
  14. package/dist/__tests__/diagnostics.test.d.ts +4 -0
  15. package/dist/__tests__/diagnostics.test.js +149 -0
  16. package/dist/__tests__/e2e.test.d.ts +6 -0
  17. package/dist/__tests__/e2e.test.js +1528 -0
  18. package/dist/__tests__/lexer.test.d.ts +1 -0
  19. package/dist/__tests__/lexer.test.js +316 -0
  20. package/dist/__tests__/lowering.test.d.ts +1 -0
  21. package/dist/__tests__/lowering.test.js +819 -0
  22. package/dist/__tests__/mc-integration.test.d.ts +12 -0
  23. package/dist/__tests__/mc-integration.test.js +395 -0
  24. package/dist/__tests__/mc-syntax.test.d.ts +1 -0
  25. package/dist/__tests__/mc-syntax.test.js +112 -0
  26. package/dist/__tests__/nbt.test.d.ts +1 -0
  27. package/dist/__tests__/nbt.test.js +82 -0
  28. package/dist/__tests__/optimizer-advanced.test.d.ts +1 -0
  29. package/dist/__tests__/optimizer-advanced.test.js +124 -0
  30. package/dist/__tests__/optimizer.test.d.ts +1 -0
  31. package/dist/__tests__/optimizer.test.js +118 -0
  32. package/dist/__tests__/parser.test.d.ts +1 -0
  33. package/dist/__tests__/parser.test.js +717 -0
  34. package/dist/__tests__/repl.test.d.ts +1 -0
  35. package/dist/__tests__/repl.test.js +27 -0
  36. package/dist/__tests__/runtime.test.d.ts +1 -0
  37. package/dist/__tests__/runtime.test.js +276 -0
  38. package/dist/__tests__/structure-optimizer.test.d.ts +1 -0
  39. package/dist/__tests__/structure-optimizer.test.js +33 -0
  40. package/dist/__tests__/typechecker.test.d.ts +1 -0
  41. package/dist/__tests__/typechecker.test.js +364 -0
  42. package/dist/ast/types.d.ts +357 -0
  43. package/dist/ast/types.js +9 -0
  44. package/dist/cli.d.ts +11 -0
  45. package/dist/cli.js +407 -0
  46. package/dist/codegen/cmdblock/index.d.ts +26 -0
  47. package/dist/codegen/cmdblock/index.js +45 -0
  48. package/dist/codegen/mcfunction/index.d.ts +34 -0
  49. package/dist/codegen/mcfunction/index.js +413 -0
  50. package/dist/codegen/structure/index.d.ts +18 -0
  51. package/dist/codegen/structure/index.js +249 -0
  52. package/dist/compile.d.ts +30 -0
  53. package/dist/compile.js +152 -0
  54. package/dist/data/arena/function/__load.mcfunction +6 -0
  55. package/dist/data/arena/function/__tick.mcfunction +2 -0
  56. package/dist/data/arena/function/announce_leaders/else_1.mcfunction +3 -0
  57. package/dist/data/arena/function/announce_leaders/foreach_0/merge_2.mcfunction +1 -0
  58. package/dist/data/arena/function/announce_leaders/foreach_0/then_0.mcfunction +3 -0
  59. package/dist/data/arena/function/announce_leaders/foreach_0.mcfunction +7 -0
  60. package/dist/data/arena/function/announce_leaders/foreach_1/merge_2.mcfunction +1 -0
  61. package/dist/data/arena/function/announce_leaders/foreach_1/then_0.mcfunction +4 -0
  62. package/dist/data/arena/function/announce_leaders/foreach_1.mcfunction +6 -0
  63. package/dist/data/arena/function/announce_leaders/merge_2.mcfunction +1 -0
  64. package/dist/data/arena/function/announce_leaders/then_0.mcfunction +4 -0
  65. package/dist/data/arena/function/announce_leaders.mcfunction +6 -0
  66. package/dist/data/arena/function/arena_tick/merge_2.mcfunction +1 -0
  67. package/dist/data/arena/function/arena_tick/then_0.mcfunction +4 -0
  68. package/dist/data/arena/function/arena_tick.mcfunction +11 -0
  69. package/dist/data/counter/function/__load.mcfunction +5 -0
  70. package/dist/data/counter/function/__tick.mcfunction +2 -0
  71. package/dist/data/counter/function/counter_tick/merge_2.mcfunction +1 -0
  72. package/dist/data/counter/function/counter_tick/then_0.mcfunction +3 -0
  73. package/dist/data/counter/function/counter_tick.mcfunction +11 -0
  74. package/dist/data/minecraft/tags/function/load.json +5 -0
  75. package/dist/data/minecraft/tags/function/tick.json +5 -0
  76. package/dist/data/quiz/function/__load.mcfunction +16 -0
  77. package/dist/data/quiz/function/__tick.mcfunction +6 -0
  78. package/dist/data/quiz/function/__trigger_quiz_a_dispatch.mcfunction +4 -0
  79. package/dist/data/quiz/function/__trigger_quiz_b_dispatch.mcfunction +4 -0
  80. package/dist/data/quiz/function/__trigger_quiz_c_dispatch.mcfunction +4 -0
  81. package/dist/data/quiz/function/__trigger_quiz_start_dispatch.mcfunction +4 -0
  82. package/dist/data/quiz/function/answer_a.mcfunction +4 -0
  83. package/dist/data/quiz/function/answer_b.mcfunction +4 -0
  84. package/dist/data/quiz/function/answer_c.mcfunction +4 -0
  85. package/dist/data/quiz/function/ask_question/else_1.mcfunction +5 -0
  86. package/dist/data/quiz/function/ask_question/else_4.mcfunction +5 -0
  87. package/dist/data/quiz/function/ask_question/else_7.mcfunction +4 -0
  88. package/dist/data/quiz/function/ask_question/merge_2.mcfunction +1 -0
  89. package/dist/data/quiz/function/ask_question/merge_5.mcfunction +2 -0
  90. package/dist/data/quiz/function/ask_question/merge_8.mcfunction +2 -0
  91. package/dist/data/quiz/function/ask_question/then_0.mcfunction +4 -0
  92. package/dist/data/quiz/function/ask_question/then_3.mcfunction +4 -0
  93. package/dist/data/quiz/function/ask_question/then_6.mcfunction +4 -0
  94. package/dist/data/quiz/function/ask_question.mcfunction +7 -0
  95. package/dist/data/quiz/function/finish_quiz.mcfunction +6 -0
  96. package/dist/data/quiz/function/handle_answer/else_1.mcfunction +5 -0
  97. package/dist/data/quiz/function/handle_answer/else_10.mcfunction +3 -0
  98. package/dist/data/quiz/function/handle_answer/else_16.mcfunction +3 -0
  99. package/dist/data/quiz/function/handle_answer/else_4.mcfunction +3 -0
  100. package/dist/data/quiz/function/handle_answer/else_7.mcfunction +5 -0
  101. package/dist/data/quiz/function/handle_answer/merge_11.mcfunction +2 -0
  102. package/dist/data/quiz/function/handle_answer/merge_14.mcfunction +2 -0
  103. package/dist/data/quiz/function/handle_answer/merge_17.mcfunction +2 -0
  104. package/dist/data/quiz/function/handle_answer/merge_2.mcfunction +8 -0
  105. package/dist/data/quiz/function/handle_answer/merge_5.mcfunction +2 -0
  106. package/dist/data/quiz/function/handle_answer/merge_8.mcfunction +2 -0
  107. package/dist/data/quiz/function/handle_answer/then_0.mcfunction +5 -0
  108. package/dist/data/quiz/function/handle_answer/then_12.mcfunction +5 -0
  109. package/dist/data/quiz/function/handle_answer/then_15.mcfunction +6 -0
  110. package/dist/data/quiz/function/handle_answer/then_3.mcfunction +6 -0
  111. package/dist/data/quiz/function/handle_answer/then_6.mcfunction +5 -0
  112. package/dist/data/quiz/function/handle_answer/then_9.mcfunction +6 -0
  113. package/dist/data/quiz/function/handle_answer.mcfunction +11 -0
  114. package/dist/data/quiz/function/start_quiz.mcfunction +5 -0
  115. package/dist/data/shop/function/__load.mcfunction +7 -0
  116. package/dist/data/shop/function/__tick.mcfunction +3 -0
  117. package/dist/data/shop/function/__trigger_shop_buy_dispatch.mcfunction +4 -0
  118. package/dist/data/shop/function/complete_purchase/else_1.mcfunction +5 -0
  119. package/dist/data/shop/function/complete_purchase/else_4.mcfunction +5 -0
  120. package/dist/data/shop/function/complete_purchase/else_7.mcfunction +3 -0
  121. package/dist/data/shop/function/complete_purchase/merge_2.mcfunction +2 -0
  122. package/dist/data/shop/function/complete_purchase/merge_5.mcfunction +2 -0
  123. package/dist/data/shop/function/complete_purchase/merge_8.mcfunction +2 -0
  124. package/dist/data/shop/function/complete_purchase/then_0.mcfunction +4 -0
  125. package/dist/data/shop/function/complete_purchase/then_3.mcfunction +4 -0
  126. package/dist/data/shop/function/complete_purchase/then_6.mcfunction +4 -0
  127. package/dist/data/shop/function/complete_purchase.mcfunction +7 -0
  128. package/dist/data/shop/function/handle_shop_trigger.mcfunction +3 -0
  129. package/dist/data/turret/function/__load.mcfunction +5 -0
  130. package/dist/data/turret/function/__tick.mcfunction +4 -0
  131. package/dist/data/turret/function/__trigger_deploy_turret_dispatch.mcfunction +4 -0
  132. package/dist/data/turret/function/deploy_turret.mcfunction +8 -0
  133. package/dist/data/turret/function/turret_tick/at_1.mcfunction +2 -0
  134. package/dist/data/turret/function/turret_tick/foreach_0.mcfunction +2 -0
  135. package/dist/data/turret/function/turret_tick/foreach_2.mcfunction +2 -0
  136. package/dist/data/turret/function/turret_tick/tick_body.mcfunction +3 -0
  137. package/dist/data/turret/function/turret_tick/tick_skip.mcfunction +1 -0
  138. package/dist/data/turret/function/turret_tick.mcfunction +5 -0
  139. package/dist/diagnostics/index.d.ts +44 -0
  140. package/dist/diagnostics/index.js +140 -0
  141. package/dist/index.d.ts +53 -0
  142. package/dist/index.js +126 -0
  143. package/dist/ir/builder.d.ts +32 -0
  144. package/dist/ir/builder.js +99 -0
  145. package/dist/ir/types.d.ts +117 -0
  146. package/dist/ir/types.js +15 -0
  147. package/dist/lexer/index.d.ts +36 -0
  148. package/dist/lexer/index.js +458 -0
  149. package/dist/lowering/index.d.ts +106 -0
  150. package/dist/lowering/index.js +2041 -0
  151. package/dist/mc-test/client.d.ts +128 -0
  152. package/dist/mc-test/client.js +174 -0
  153. package/dist/mc-test/runner.d.ts +28 -0
  154. package/dist/mc-test/runner.js +150 -0
  155. package/dist/mc-test/setup.d.ts +11 -0
  156. package/dist/mc-test/setup.js +98 -0
  157. package/dist/mc-validator/index.d.ts +17 -0
  158. package/dist/mc-validator/index.js +322 -0
  159. package/dist/nbt/index.d.ts +86 -0
  160. package/dist/nbt/index.js +250 -0
  161. package/dist/optimizer/commands.d.ts +36 -0
  162. package/dist/optimizer/commands.js +349 -0
  163. package/dist/optimizer/passes.d.ts +34 -0
  164. package/dist/optimizer/passes.js +227 -0
  165. package/dist/optimizer/structure.d.ts +8 -0
  166. package/dist/optimizer/structure.js +344 -0
  167. package/dist/pack.mcmeta +6 -0
  168. package/dist/parser/index.d.ts +76 -0
  169. package/dist/parser/index.js +1193 -0
  170. package/dist/repl.d.ts +16 -0
  171. package/dist/repl.js +165 -0
  172. package/dist/runtime/index.d.ts +101 -0
  173. package/dist/runtime/index.js +1288 -0
  174. package/dist/typechecker/index.d.ts +42 -0
  175. package/dist/typechecker/index.js +629 -0
  176. package/docs/COMPILATION_STATS.md +142 -0
  177. package/docs/IMPLEMENTATION_GUIDE.md +512 -0
  178. package/docs/LANGUAGE_REFERENCE.md +415 -0
  179. package/docs/MC_MAPPING.md +280 -0
  180. package/docs/STRUCTURE_TARGET.md +80 -0
  181. package/docs/mc-reference/commands.md +259 -0
  182. package/editors/vscode/.vscodeignore +10 -0
  183. package/editors/vscode/LICENSE +21 -0
  184. package/editors/vscode/README.md +78 -0
  185. package/editors/vscode/build.mjs +28 -0
  186. package/editors/vscode/icon.png +0 -0
  187. package/editors/vscode/mcfunction-language-configuration.json +28 -0
  188. package/editors/vscode/out/extension.js +7236 -0
  189. package/editors/vscode/package-lock.json +566 -0
  190. package/editors/vscode/package.json +137 -0
  191. package/editors/vscode/redscript-language-configuration.json +28 -0
  192. package/editors/vscode/snippets/redscript.json +114 -0
  193. package/editors/vscode/src/codeactions.ts +89 -0
  194. package/editors/vscode/src/completion.ts +130 -0
  195. package/editors/vscode/src/extension.ts +239 -0
  196. package/editors/vscode/src/hover.ts +1120 -0
  197. package/editors/vscode/src/symbols.ts +207 -0
  198. package/editors/vscode/syntaxes/mcfunction.tmLanguage.json +740 -0
  199. package/editors/vscode/syntaxes/redscript.tmLanguage.json +357 -0
  200. package/editors/vscode/tsconfig.json +13 -0
  201. package/jest.config.js +5 -0
  202. package/package.json +38 -0
  203. package/src/__tests__/cli.test.ts +130 -0
  204. package/src/__tests__/codegen.test.ts +128 -0
  205. package/src/__tests__/diagnostics.test.ts +195 -0
  206. package/src/__tests__/e2e.test.ts +1721 -0
  207. package/src/__tests__/fixtures/mc-commands-1.21.4.json +18734 -0
  208. package/src/__tests__/formatter.test.ts +46 -0
  209. package/src/__tests__/lexer.test.ts +356 -0
  210. package/src/__tests__/lowering.test.ts +962 -0
  211. package/src/__tests__/mc-integration.test.ts +409 -0
  212. package/src/__tests__/mc-syntax.test.ts +96 -0
  213. package/src/__tests__/nbt.test.ts +58 -0
  214. package/src/__tests__/optimizer-advanced.test.ts +144 -0
  215. package/src/__tests__/optimizer.test.ts +129 -0
  216. package/src/__tests__/parser.test.ts +800 -0
  217. package/src/__tests__/repl.test.ts +33 -0
  218. package/src/__tests__/runtime.test.ts +289 -0
  219. package/src/__tests__/structure-optimizer.test.ts +38 -0
  220. package/src/__tests__/typechecker.test.ts +395 -0
  221. package/src/ast/types.ts +248 -0
  222. package/src/cli.ts +445 -0
  223. package/src/codegen/cmdblock/index.ts +63 -0
  224. package/src/codegen/mcfunction/index.ts +471 -0
  225. package/src/codegen/structure/index.ts +305 -0
  226. package/src/compile.ts +188 -0
  227. package/src/diagnostics/index.ts +186 -0
  228. package/src/examples/README.md +77 -0
  229. package/src/examples/SHOWCASE_GAME.md +43 -0
  230. package/src/examples/arena.rs +44 -0
  231. package/src/examples/counter.rs +12 -0
  232. package/src/examples/pvp_arena.rs +131 -0
  233. package/src/examples/quiz.rs +90 -0
  234. package/src/examples/rpg.rs +13 -0
  235. package/src/examples/shop.rs +30 -0
  236. package/src/examples/showcase_game.rs +552 -0
  237. package/src/examples/stdlib_demo.rs +181 -0
  238. package/src/examples/turret.rs +27 -0
  239. package/src/examples/world_manager.rs +23 -0
  240. package/src/formatter/index.ts +22 -0
  241. package/src/index.ts +161 -0
  242. package/src/ir/builder.ts +114 -0
  243. package/src/ir/types.ts +119 -0
  244. package/src/lexer/index.ts +555 -0
  245. package/src/lowering/index.ts +2406 -0
  246. package/src/mc-test/client.ts +259 -0
  247. package/src/mc-test/runner.ts +140 -0
  248. package/src/mc-test/setup.ts +70 -0
  249. package/src/mc-validator/index.ts +367 -0
  250. package/src/nbt/index.ts +321 -0
  251. package/src/optimizer/commands.ts +416 -0
  252. package/src/optimizer/passes.ts +233 -0
  253. package/src/optimizer/structure.ts +441 -0
  254. package/src/parser/index.ts +1437 -0
  255. package/src/repl.ts +165 -0
  256. package/src/runtime/index.ts +1403 -0
  257. package/src/stdlib/README.md +156 -0
  258. package/src/stdlib/combat.rs +20 -0
  259. package/src/stdlib/cooldown.rs +45 -0
  260. package/src/stdlib/math.rs +49 -0
  261. package/src/stdlib/mobs.rs +99 -0
  262. package/src/stdlib/player.rs +29 -0
  263. package/src/stdlib/strings.rs +7 -0
  264. package/src/stdlib/timer.rs +51 -0
  265. package/src/templates/README.md +126 -0
  266. package/src/templates/combat.rs +96 -0
  267. package/src/templates/economy.rs +40 -0
  268. package/src/templates/mini-game-framework.rs +117 -0
  269. package/src/templates/quest.rs +78 -0
  270. package/src/test_programs/zombie_game.rs +25 -0
  271. package/src/typechecker/index.ts +737 -0
  272. package/tsconfig.json +16 -0
@@ -0,0 +1,27 @@
1
+ // Automated turret using a spawned armor stand object and a struct value.
2
+
3
+ struct TurretState { health: int }
4
+
5
+ @on_trigger("deploy_turret")
6
+ fn deploy_turret() {
7
+ let turret = spawn_object(0, 64, 0);
8
+ turret.health = 40;
9
+ turret.tag("turret");
10
+
11
+ let state: TurretState = { health: 40 };
12
+ let hp = state.health;
13
+ scoreboard_set("turret", #health, hp);
14
+
15
+ say("Turret deployed.");
16
+ }
17
+
18
+ @tick(rate=20)
19
+ fn turret_tick() {
20
+ foreach (turret in @e[type=armor_stand, tag=turret]) {
21
+ at @s {
22
+ foreach (z in @e[type=zombie, distance=..8]) {
23
+ kill(z);
24
+ }
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,23 @@
1
+ // World management helpers for a lobby or event setup.
2
+ // Demonstrates block editing, global rules, and weather/time control.
3
+
4
+ fn reset_lobby_platform() {
5
+ fill((0, 64, 0), (8, 64, 8), "minecraft:smooth_stone");
6
+ fill((1, 65, 1), (7, 68, 7), "minecraft:air");
7
+ setblock((4, 65, 4), "minecraft:gold_block");
8
+ }
9
+
10
+ fn configure_world() {
11
+ weather("clear");
12
+ time_set("day");
13
+ gamerule(#doWeatherCycle, "false");
14
+ gamerule(#doDaylightCycle, "false");
15
+ announce("World manager refreshed the lobby.");
16
+ }
17
+
18
+ @on_trigger("world_reset")
19
+ fn handle_world_reset() {
20
+ reset_lobby_platform();
21
+ configure_world();
22
+ actionbar(@a, "Lobby reset complete");
23
+ }
@@ -0,0 +1,22 @@
1
+ export function format(source: string): string {
2
+ const lines = source.split("\n")
3
+ let indent = 0
4
+ const result: string[] = []
5
+
6
+ for (let line of lines) {
7
+ line = line.trim()
8
+ if (!line) { result.push(""); continue }
9
+
10
+ // Decrease indent before }
11
+ if (line.startsWith("}")) indent = Math.max(0, indent - 1)
12
+
13
+ // Add indentation
14
+ result.push(" ".repeat(indent) + line)
15
+
16
+ // Increase indent after {
17
+ if (line.endsWith("{")) indent++
18
+ }
19
+
20
+ // Ensure single newline at end
21
+ return result.join("\n").trimEnd() + "\n"
22
+ }
package/src/index.ts ADDED
@@ -0,0 +1,161 @@
1
+ /**
2
+ * RedScript Compiler
3
+ *
4
+ * Main entry point for programmatic usage.
5
+ */
6
+
7
+ import { Lexer } from './lexer'
8
+ import { Parser } from './parser'
9
+ import { TypeChecker } from './typechecker'
10
+ import { Lowering } from './lowering'
11
+ import type { Warning } from './lowering'
12
+ import {
13
+ constantFoldingWithStats,
14
+ copyPropagation,
15
+ deadCodeEliminationWithStats,
16
+ } from './optimizer/passes'
17
+ import {
18
+ countMcfunctionCommands,
19
+ generateDatapackWithStats,
20
+ DatapackFile,
21
+ } from './codegen/mcfunction'
22
+ import { preprocessSource } from './compile'
23
+ import type { IRModule } from './ir/types'
24
+ import type { Program } from './ast/types'
25
+ import type { DiagnosticError } from './diagnostics'
26
+ import { createEmptyOptimizationStats, type OptimizationStats } from './optimizer/commands'
27
+
28
+ export interface CompileOptions {
29
+ namespace?: string
30
+ optimize?: boolean
31
+ typeCheck?: boolean
32
+ filePath?: string
33
+ }
34
+
35
+ export interface CompileResult {
36
+ files: DatapackFile[]
37
+ advancements: DatapackFile[]
38
+ ast: Program
39
+ ir: IRModule
40
+ typeErrors?: DiagnosticError[]
41
+ warnings?: Warning[]
42
+ stats?: OptimizationStats
43
+ }
44
+
45
+ /**
46
+ * Compile RedScript source code to a Minecraft datapack.
47
+ *
48
+ * @param source - The RedScript source code
49
+ * @param options - Compilation options
50
+ * @returns Compiled datapack files
51
+ */
52
+ export function compile(source: string, options: CompileOptions = {}): CompileResult {
53
+ const namespace = options.namespace ?? 'redscript'
54
+ const shouldOptimize = options.optimize ?? true
55
+ const shouldTypeCheck = options.typeCheck ?? true
56
+ const filePath = options.filePath
57
+ const preprocessedSource = preprocessSource(source, { filePath })
58
+
59
+ // Lexing
60
+ const tokens = new Lexer(preprocessedSource, filePath).tokenize()
61
+
62
+ // Parsing
63
+ const ast = new Parser(tokens, preprocessedSource, filePath).parse(namespace)
64
+
65
+ // Type checking (warn mode - collect errors but don't block)
66
+ let typeErrors: DiagnosticError[] | undefined
67
+ if (shouldTypeCheck) {
68
+ const checker = new TypeChecker(preprocessedSource, filePath)
69
+ typeErrors = checker.check(ast)
70
+ }
71
+
72
+ // Lowering to IR
73
+ const lowering = new Lowering(namespace)
74
+ const ir = lowering.lower(ast)
75
+
76
+ let optimizedIR: IRModule = ir
77
+ let generated = generateDatapackWithStats(ir, { optimizeCommands: shouldOptimize })
78
+ let optimizationStats: OptimizationStats | undefined
79
+
80
+ if (shouldOptimize) {
81
+ const stats = createEmptyOptimizationStats()
82
+ const copyPropagatedFunctions = []
83
+ const deadCodeEliminatedFunctions = []
84
+
85
+ for (const fn of ir.functions) {
86
+ const folded = constantFoldingWithStats(fn)
87
+ stats.constantFolds += folded.stats.constantFolds ?? 0
88
+
89
+ const propagated = copyPropagation(folded.fn)
90
+ copyPropagatedFunctions.push(propagated)
91
+
92
+ const dce = deadCodeEliminationWithStats(propagated)
93
+ deadCodeEliminatedFunctions.push(dce.fn)
94
+ }
95
+
96
+ const copyPropagatedIR: IRModule = { ...ir, functions: copyPropagatedFunctions }
97
+ optimizedIR = { ...ir, functions: deadCodeEliminatedFunctions }
98
+
99
+ const baselineGenerated = generateDatapackWithStats(ir, { optimizeCommands: false })
100
+ const beforeDceGenerated = generateDatapackWithStats(copyPropagatedIR, { optimizeCommands: false })
101
+ const afterDceGenerated = generateDatapackWithStats(optimizedIR, { optimizeCommands: false })
102
+ generated = generateDatapackWithStats(optimizedIR, { optimizeCommands: true })
103
+
104
+ stats.deadCodeRemoved =
105
+ countMcfunctionCommands(beforeDceGenerated.files) - countMcfunctionCommands(afterDceGenerated.files)
106
+ stats.licmHoists = generated.stats.licmHoists
107
+ stats.licmLoopBodies = generated.stats.licmLoopBodies
108
+ stats.cseRedundantReads = generated.stats.cseRedundantReads
109
+ stats.cseArithmetic = generated.stats.cseArithmetic
110
+ stats.setblockMergedCommands = generated.stats.setblockMergedCommands
111
+ stats.setblockFillCommands = generated.stats.setblockFillCommands
112
+ stats.setblockSavedCommands = generated.stats.setblockSavedCommands
113
+ stats.totalCommandsBefore = countMcfunctionCommands(baselineGenerated.files)
114
+ stats.totalCommandsAfter = countMcfunctionCommands(generated.files)
115
+ optimizationStats = stats
116
+ } else {
117
+ optimizedIR = ir
118
+ generated = generateDatapackWithStats(ir, { optimizeCommands: false })
119
+ }
120
+
121
+ return {
122
+ files: [...generated.files, ...generated.advancements],
123
+ advancements: generated.advancements,
124
+ ast,
125
+ ir: optimizedIR,
126
+ typeErrors,
127
+ warnings: lowering.warnings,
128
+ stats: optimizationStats,
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Check RedScript source code for errors without generating output.
134
+ *
135
+ * @param source - The RedScript source code
136
+ * @param namespace - Optional namespace
137
+ * @returns null if no errors, or an error object
138
+ */
139
+ export function check(source: string, namespace = 'redscript', filePath?: string): Error | null {
140
+ try {
141
+ const preprocessedSource = preprocessSource(source, { filePath })
142
+ const tokens = new Lexer(preprocessedSource, filePath).tokenize()
143
+ new Parser(tokens, preprocessedSource, filePath).parse(namespace)
144
+ return null
145
+ } catch (err) {
146
+ return err as Error
147
+ }
148
+ }
149
+
150
+ // Re-export types and classes for advanced usage
151
+ export { Lexer } from './lexer'
152
+ export { Parser } from './parser'
153
+ export { TypeChecker } from './typechecker'
154
+ export { Lowering } from './lowering'
155
+ export { optimize } from './optimizer/passes'
156
+ export { generateDatapack } from './codegen/mcfunction'
157
+ export { MCCommandValidator } from './mc-validator'
158
+ export type { DatapackFile } from './codegen/mcfunction'
159
+ export type { IRModule, IRFunction } from './ir/types'
160
+ export type { Program, FnDecl, Expr, Stmt, Span } from './ast/types'
161
+ export type { DiagnosticError } from './diagnostics'
@@ -0,0 +1,114 @@
1
+ /**
2
+ * IRBuilder — helper for constructing IR programmatically.
3
+ * AST → IR lowering uses this.
4
+ */
5
+
6
+ import type { IRBlock, IRFunction, IRInstr, IRModule, Operand, Terminator } from './types'
7
+
8
+ export class IRBuilder {
9
+ private tempCount = 0
10
+ private labelCount = 0
11
+ private currentBlock: IRBlock | null = null
12
+ private blocks: IRBlock[] = []
13
+ private locals = new Set<string>()
14
+
15
+ // -------------------------------------------------------------------------
16
+ // Names
17
+ // -------------------------------------------------------------------------
18
+
19
+ freshTemp(): string {
20
+ const name = `$t${this.tempCount++}`
21
+ this.locals.add(name)
22
+ return name
23
+ }
24
+
25
+ freshLabel(hint = 'L'): string {
26
+ return `${hint}_${this.labelCount++}`
27
+ }
28
+
29
+ // -------------------------------------------------------------------------
30
+ // Block management
31
+ // -------------------------------------------------------------------------
32
+
33
+ startBlock(label: string): void {
34
+ this.currentBlock = { label, instrs: [], term: { op: 'return' } }
35
+ }
36
+
37
+ private get block(): IRBlock {
38
+ if (!this.currentBlock) throw new Error('No active block')
39
+ return this.currentBlock
40
+ }
41
+
42
+ private sealBlock(term: Terminator): void {
43
+ this.block.term = term
44
+ this.blocks.push(this.block)
45
+ this.currentBlock = null
46
+ }
47
+
48
+ // -------------------------------------------------------------------------
49
+ // Emit instructions
50
+ // -------------------------------------------------------------------------
51
+
52
+ emitAssign(dst: string, src: Operand): void {
53
+ this.locals.add(dst)
54
+ this.block.instrs.push({ op: 'assign', dst, src })
55
+ }
56
+
57
+ emitBinop(dst: string, lhs: Operand, bop: IRInstr & { op: 'binop' } extends { bop: infer B } ? B : never, rhs: Operand): void
58
+ emitBinop(dst: string, lhs: Operand, bop: '+' | '-' | '*' | '/' | '%', rhs: Operand): void {
59
+ this.locals.add(dst)
60
+ this.block.instrs.push({ op: 'binop', dst, lhs, bop, rhs })
61
+ }
62
+
63
+ emitCmp(dst: string, lhs: Operand, cop: '==' | '!=' | '<' | '<=' | '>' | '>=', rhs: Operand): void {
64
+ this.locals.add(dst)
65
+ this.block.instrs.push({ op: 'cmp', dst, lhs, cop, rhs })
66
+ }
67
+
68
+ emitCall(fn: string, args: Operand[], dst?: string): void {
69
+ if (dst) this.locals.add(dst)
70
+ this.block.instrs.push({ op: 'call', fn, args, dst })
71
+ }
72
+
73
+ emitRaw(cmd: string): void {
74
+ this.block.instrs.push({ op: 'raw', cmd })
75
+ }
76
+
77
+ // -------------------------------------------------------------------------
78
+ // Terminators
79
+ // -------------------------------------------------------------------------
80
+
81
+ emitJump(target: string): void {
82
+ this.sealBlock({ op: 'jump', target })
83
+ }
84
+
85
+ emitJumpIf(cond: string, then: string, else_: string): void {
86
+ this.sealBlock({ op: 'jump_if', cond, then, else_ })
87
+ }
88
+
89
+ emitReturn(value?: Operand): void {
90
+ this.sealBlock({ op: 'return', value })
91
+ }
92
+
93
+ emitTickYield(continuation: string): void {
94
+ this.sealBlock({ op: 'tick_yield', continuation })
95
+ }
96
+
97
+ // -------------------------------------------------------------------------
98
+ // Build
99
+ // -------------------------------------------------------------------------
100
+
101
+ build(name: string, params: string[], isTickLoop = false): IRFunction {
102
+ return {
103
+ name,
104
+ params,
105
+ locals: Array.from(this.locals),
106
+ blocks: this.blocks,
107
+ isTickLoop,
108
+ }
109
+ }
110
+ }
111
+
112
+ export function buildModule(namespace: string, fns: IRFunction[], globals: string[] = []): IRModule {
113
+ return { namespace, functions: fns, globals }
114
+ }
@@ -0,0 +1,119 @@
1
+ /**
2
+ * RedScript IR — Three-Address Code (TAC)
3
+ *
4
+ * Compilation pipeline:
5
+ * Source → AST → IR → (optimize) → CodeGen → mcfunction / cmdblock
6
+ *
7
+ * Variable storage in MC Java Edition:
8
+ * - Integer vars → scoreboard fake player ($name on objective "rs_vars")
9
+ * - Complex data → NBT storage (redscript:stack / redscript:heap)
10
+ * - Return value → fake player $ret
11
+ * - Temporaries → $t0, $t1, ...
12
+ */
13
+
14
+ // ---------------------------------------------------------------------------
15
+ // Operands
16
+ // ---------------------------------------------------------------------------
17
+
18
+ export type Operand =
19
+ | { kind: 'var'; name: string } // scoreboard fake player
20
+ | { kind: 'const'; value: number } // integer literal
21
+ | { kind: 'storage'; path: string } // NBT storage path (e.g. "redscript:heap data.x")
22
+
23
+ // ---------------------------------------------------------------------------
24
+ // Binary operators (all map to `scoreboard players operation`)
25
+ // ---------------------------------------------------------------------------
26
+
27
+ export type BinOp = '+' | '-' | '*' | '/' | '%'
28
+ export type CmpOp = '==' | '!=' | '<' | '<=' | '>' | '>='
29
+
30
+ // ---------------------------------------------------------------------------
31
+ // IR Instructions
32
+ // ---------------------------------------------------------------------------
33
+
34
+ export type IRInstr =
35
+ // x = src
36
+ | { op: 'assign'; dst: string; src: Operand }
37
+
38
+ // dst = lhs bop rhs
39
+ | { op: 'binop'; dst: string; lhs: Operand; bop: BinOp; rhs: Operand }
40
+
41
+ // dst = (lhs cop rhs) ? 1 : 0
42
+ | { op: 'cmp'; dst: string; lhs: Operand; cop: CmpOp; rhs: Operand }
43
+
44
+ // goto label
45
+ | { op: 'jump'; target: string }
46
+
47
+ // if cond != 0 goto target
48
+ | { op: 'jump_if'; cond: string; target: string }
49
+
50
+ // if cond == 0 goto target
51
+ | { op: 'jump_unless'; cond: string; target: string }
52
+
53
+ // dst = fn(args)
54
+ | { op: 'call'; fn: string; args: Operand[]; dst?: string }
55
+
56
+ // return value (optional)
57
+ | { op: 'return'; value?: Operand }
58
+
59
+ // label declaration (block entry point)
60
+ | { op: 'label'; id: string }
61
+
62
+ // raw MC command passthrough (escape hatch)
63
+ | { op: 'raw'; cmd: string }
64
+
65
+ // wait one game tick (command block target only)
66
+ // maps to: schedule function <continuation> 1t replace
67
+ | { op: 'tick_yield' }
68
+
69
+ // ---------------------------------------------------------------------------
70
+ // Basic Block — straight-line code, ends with a terminator
71
+ // ---------------------------------------------------------------------------
72
+
73
+ export type Terminator =
74
+ | { op: 'jump'; target: string }
75
+ | { op: 'jump_if'; cond: string; then: string; else_: string }
76
+ | { op: 'jump_unless'; cond: string; then: string; else_: string }
77
+ | { op: 'return'; value?: Operand }
78
+ | { op: 'tick_yield'; continuation: string }
79
+
80
+ export interface IRBlock {
81
+ label: string
82
+ instrs: IRInstr[] // non-terminator instructions
83
+ term: Terminator
84
+ }
85
+
86
+ export interface IRCommand {
87
+ cmd: string
88
+ conditional?: boolean
89
+ label?: string
90
+ }
91
+
92
+ // ---------------------------------------------------------------------------
93
+ // Function
94
+ // ---------------------------------------------------------------------------
95
+
96
+ export interface IRFunction {
97
+ name: string
98
+ params: string[] // parameter names (passed via fake players)
99
+ locals: string[] // all local variable names
100
+ blocks: IRBlock[] // blocks[0] = entry block
101
+ commands?: IRCommand[] // structure target command stream
102
+ isTickLoop?: boolean // true → Repeat command block (runs every tick)
103
+ isTriggerHandler?: boolean // true → handles a trigger event
104
+ triggerName?: string // the trigger objective name
105
+ eventTrigger?: {
106
+ kind: 'advancement' | 'craft' | 'death' | 'login' | 'join_team'
107
+ value?: string
108
+ }
109
+ }
110
+
111
+ // ---------------------------------------------------------------------------
112
+ // Module — top-level compilation unit
113
+ // ---------------------------------------------------------------------------
114
+
115
+ export interface IRModule {
116
+ namespace: string // datapack namespace (e.g. "mypack")
117
+ functions: IRFunction[]
118
+ globals: string[] // global variable names
119
+ }