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,142 @@
1
+ # RedScript Example Compilation Stats
2
+
3
+ Generated on 2026-03-12 with:
4
+
5
+ ```bash
6
+ for example in src/examples/*.rs; do
7
+ name=$(basename "$example" .rs)
8
+ TS_NODE_TRANSPILE_ONLY=1 npx ts-node src/cli.ts compile "$example" --stats -o /tmp/eval/$name 2>&1
9
+ done
10
+ ```
11
+
12
+ ## Summary
13
+
14
+ | Example | Functions | Commands Before | Commands After | Savings |
15
+ |:--|--:|--:|--:|--:|
16
+ | `arena.rs` | 4 | 208 | 52 | 75% |
17
+ | `counter.rs` | 1 | 76 | 19 | 75% |
18
+ | `pvp_arena.rs` | 15 | 780 | 195 | 75% |
19
+ | `quiz.rs` | 7 | 564 | 141 | 75% |
20
+ | `rpg.rs` | 10 | 548 | 137 | 75% |
21
+ | `shop.rs` | 2 | 160 | 40 | 75% |
22
+ | `showcase_game.rs` | 89 | 3360 | 840 | 75% |
23
+ | `stdlib_demo.rs` | 15 | 1104 | 276 | 75% |
24
+ | `turret.rs` | 5 | 104 | 26 | 75% |
25
+ | `world_manager.rs` | 3 | 80 | 20 | 75% |
26
+
27
+ ## Per-example Optimizer Stats
28
+
29
+ ### `arena.rs`
30
+
31
+ - Functions: `4`
32
+ - Commands: `208 -> 52`
33
+ - Savings: `75%`
34
+ - LICM: `1`
35
+ - CSE eliminations: `0`
36
+ - setblock batching savings: `0`
37
+ - Dead code removed: `2`
38
+ - Constant folds: `0`
39
+
40
+ ### `counter.rs`
41
+
42
+ - Functions: `1`
43
+ - Commands: `76 -> 19`
44
+ - Savings: `75%`
45
+ - LICM: `0`
46
+ - CSE eliminations: `0`
47
+ - setblock batching savings: `0`
48
+ - Dead code removed: `0`
49
+ - Constant folds: `0`
50
+
51
+ ### `pvp_arena.rs`
52
+
53
+ - Functions: `15`
54
+ - Commands: `780 -> 195`
55
+ - Savings: `75%`
56
+ - LICM: `0`
57
+ - CSE eliminations: `0`
58
+ - setblock batching savings: `0`
59
+ - Dead code removed: `4`
60
+ - Constant folds: `0`
61
+
62
+ ### `quiz.rs`
63
+
64
+ - Functions: `7`
65
+ - Commands: `564 -> 141`
66
+ - Savings: `75%`
67
+ - LICM: `0`
68
+ - CSE eliminations: `0`
69
+ - setblock batching savings: `0`
70
+ - Dead code removed: `0`
71
+ - Constant folds: `0`
72
+
73
+ ### `rpg.rs`
74
+
75
+ - Functions: `10`
76
+ - Commands: `548 -> 137`
77
+ - Savings: `75%`
78
+ - LICM: `0`
79
+ - CSE eliminations: `0`
80
+ - setblock batching savings: `0`
81
+ - Dead code removed: `3`
82
+ - Constant folds: `1`
83
+
84
+ ### `shop.rs`
85
+
86
+ - Functions: `2`
87
+ - Commands: `160 -> 40`
88
+ - Savings: `75%`
89
+ - LICM: `0`
90
+ - CSE eliminations: `0`
91
+ - setblock batching savings: `0`
92
+ - Dead code removed: `0`
93
+ - Constant folds: `0`
94
+
95
+ ### `showcase_game.rs`
96
+
97
+ - Functions: `89`
98
+ - Commands: `3360 -> 840`
99
+ - Savings: `75%`
100
+ - LICM: `0`
101
+ - CSE eliminations: `0`
102
+ - setblock batching savings: `0`
103
+ - Dead code removed: `20`
104
+ - Constant folds: `1`
105
+
106
+ ### `stdlib_demo.rs`
107
+
108
+ - Functions: `15`
109
+ - Commands: `1104 -> 276`
110
+ - Savings: `75%`
111
+ - LICM: `0`
112
+ - CSE eliminations: `0`
113
+ - setblock batching savings: `0`
114
+ - Dead code removed: `11`
115
+ - Constant folds: `0`
116
+
117
+ ### `turret.rs`
118
+
119
+ - Functions: `5`
120
+ - Commands: `104 -> 26`
121
+ - Savings: `75%`
122
+ - LICM: `0`
123
+ - CSE eliminations: `0`
124
+ - setblock batching savings: `0`
125
+ - Dead code removed: `0`
126
+ - Constant folds: `0`
127
+
128
+ ### `world_manager.rs`
129
+
130
+ - Functions: `3`
131
+ - Commands: `80 -> 20`
132
+ - Savings: `75%`
133
+ - LICM: `0`
134
+ - CSE eliminations: `0`
135
+ - setblock batching savings: `0`
136
+ - Dead code removed: `0`
137
+ - Constant folds: `0`
138
+
139
+ ## Warnings Observed During Compilation
140
+
141
+ - `showcase_game.rs`: warnings inherited from imported stdlib helpers using quoted selectors in `player.rs`, plus one auto-qualification warning for unnamespaced `zombie`.
142
+ - `turret.rs`: auto-qualification warnings for unnamespaced `armor_stand` and `zombie`.
@@ -0,0 +1,512 @@
1
+ # RedScript Compiler — Implementation Guide for AI Agent
2
+
3
+ > **读这份文档的 agent**:你的任务是为 RedScript 编译器实现 Lexer、Parser 和 AST→IR Lowering 三个模块。
4
+ > 仓库在 `/tmp/redscript`,TypeScript,已有 IR/optimizer/codegen 骨架和 15 个测试。
5
+
6
+ ---
7
+
8
+ ## 目录结构(当前)
9
+
10
+ ```
11
+ src/
12
+ ir/types.ts ← IR 类型(已完成)
13
+ ir/builder.ts ← IRBuilder(已完成)
14
+ optimizer/passes.ts ← 优化 pass(已完成)
15
+ codegen/mcfunction/ ← mcfunction 生成器(已完成)
16
+ lexer/ ← 你来实现
17
+ parser/ ← 你来实现
18
+ ast/ ← 你来实现(AST 类型定义)
19
+ lowering/ ← 你来实现(AST → IR)
20
+ __tests__/ ← 已有 optimizer + codegen 测试
21
+ ```
22
+
23
+ ---
24
+
25
+ ## 任务 1:Lexer (`src/lexer/index.ts`)
26
+
27
+ ### Token 类型完整列表
28
+
29
+ ```ts
30
+ export type TokenKind =
31
+ // 关键字
32
+ | 'fn' | 'let' | 'if' | 'else' | 'while' | 'for' | 'foreach'
33
+ | 'return' | 'as' | 'at' | 'in' | 'struct' | 'trigger'
34
+ // 类型
35
+ | 'int' | 'bool' | 'float' | 'string' | 'void'
36
+ // 布尔字面量
37
+ | 'true' | 'false'
38
+ // 实体选择器
39
+ | 'selector' // @a @e @s @p @r (含 [...] 参数)
40
+ // 装饰器
41
+ | 'decorator' // @tick @on_trigger @tick(rate=N)
42
+ // 字面量
43
+ | 'int_lit' // 42
44
+ | 'float_lit' // 3.14
45
+ | 'string_lit' // "hello"
46
+ | 'range_lit' // ..5 1.. 1..10
47
+ // 运算符
48
+ | '+' | '-' | '*' | '/' | '%'
49
+ | '==' | '!=' | '<' | '<=' | '>' | '>='
50
+ | '&&' | '||' | '!'
51
+ | '=' | '+=' | '-=' | '*=' | '/=' | '%='
52
+ // 分隔符
53
+ | '{' | '}' | '(' | ')' | '[' | ']'
54
+ | ',' | ';' | ':' | '->' | '.'
55
+ // 特殊
56
+ | 'ident' // 变量名、函数名
57
+ | 'raw_cmd' // raw("...") 的内容
58
+ | 'eof'
59
+
60
+ export interface Token {
61
+ kind: TokenKind
62
+ value: string // 原始文本
63
+ line: number
64
+ col: number
65
+ }
66
+ ```
67
+
68
+ ### 棘手的词法规则
69
+
70
+ **1. 实体选择器 vs 装饰器:**
71
+ - `@a` `@e` `@s` `@p` `@r` `@n` → `selector` token(后面可跟 `[...]`)
72
+ - `@tick` `@on_trigger` `@tick(rate=20)` → `decorator` token
73
+ - 规则:`@` 后接 `a/e/s/p/r/n` 且下一字符不是字母 → selector;否则 decorator
74
+
75
+ **2. 范围字面量:**
76
+ - `..5` → `range_lit`, value=`"..5"`
77
+ - `1..` → `range_lit`, value=`"1.."`
78
+ - `1..10` → `range_lit`, value=`"1..10"`
79
+ - 注意 `..` 不是两个点,是范围运算符
80
+
81
+ **3. 实体选择器的 `[...]` 参数:**
82
+ 选择器的过滤参数需要特殊处理(包含嵌套大括号的 NBT):
83
+ ```
84
+ @e[type=zombie, distance=..5, nbt={NoAI:1b}]
85
+ ```
86
+ 词法器可以把 `@e[type=zombie, distance=..5]` 整个作为一个 `selector` token,
87
+ 内部解析交给 Parser。
88
+
89
+ **4. `->` 不要拆成 `-` 和 `>`**
90
+
91
+ **5. 注释:** `//` 行注释,跳过到行尾
92
+
93
+ ---
94
+
95
+ ## 任务 2:AST 类型 (`src/ast/types.ts`)
96
+
97
+ ```ts
98
+ // --- 类型节点 ---
99
+ export type TypeNode =
100
+ | { kind: 'named'; name: 'int' | 'bool' | 'float' | 'string' | 'void' }
101
+ | { kind: 'array'; elem: TypeNode }
102
+
103
+ // --- 范围表达式 ---
104
+ export interface RangeExpr {
105
+ min?: number // undefined = 无下界
106
+ max?: number // undefined = 无上界
107
+ }
108
+
109
+ // --- 实体选择器 ---
110
+ export type SelectorKind = '@a' | '@e' | '@s' | '@p' | '@r' | '@n'
111
+ export interface SelectorFilter {
112
+ type?: string
113
+ distance?: RangeExpr
114
+ tag?: string[]
115
+ notTag?: string[]
116
+ scores?: Record<string, RangeExpr>
117
+ limit?: number
118
+ sort?: 'nearest' | 'furthest' | 'random' | 'arbitrary'
119
+ nbt?: string
120
+ gamemode?: string
121
+ }
122
+ export interface EntitySelector {
123
+ kind: SelectorKind
124
+ filters?: SelectorFilter
125
+ }
126
+
127
+ // --- 表达式 ---
128
+ export type Expr =
129
+ | { kind: 'int_lit'; value: number }
130
+ | { kind: 'float_lit'; value: number }
131
+ | { kind: 'bool_lit'; value: boolean }
132
+ | { kind: 'str_lit'; value: string }
133
+ | { kind: 'range_lit'; range: RangeExpr }
134
+ | { kind: 'ident'; name: string }
135
+ | { kind: 'selector'; sel: EntitySelector }
136
+ | { kind: 'binary'; op: BinOp | CmpOp; left: Expr; right: Expr }
137
+ | { kind: 'unary'; op: '!' | '-'; operand: Expr }
138
+ | { kind: 'assign'; target: string; op: '='|'+='|'-='|'*='|'/='|'%='; value: Expr }
139
+ | { kind: 'call'; fn: string; args: Expr[] }
140
+ | { kind: 'member'; obj: Expr; field: string } // entity.health
141
+
142
+ // --- 语句 ---
143
+ export type Stmt =
144
+ | { kind: 'let'; name: string; type?: TypeNode; init: Expr }
145
+ | { kind: 'expr'; expr: Expr }
146
+ | { kind: 'return'; value?: Expr }
147
+ | { kind: 'if'; cond: Expr; then: Block; else_?: Block }
148
+ | { kind: 'while'; cond: Expr; body: Block }
149
+ | { kind: 'foreach'; binding: string; selector: EntitySelector; body: Block }
150
+ | { kind: 'as_block'; selector: EntitySelector; body: Block }
151
+ | { kind: 'at_block'; selector: EntitySelector; body: Block }
152
+ | { kind: 'as_at'; as_sel: EntitySelector; at_sel: EntitySelector; body: Block }
153
+ | { kind: 'raw'; cmd: string }
154
+
155
+ export type Block = Stmt[]
156
+
157
+ // --- 函数宣告 ---
158
+ export interface Decorator {
159
+ name: 'tick' | 'on_trigger'
160
+ args?: { rate?: number; trigger?: string }
161
+ }
162
+
163
+ export interface Param { name: string; type: TypeNode }
164
+
165
+ export interface FnDecl {
166
+ name: string
167
+ params: Param[]
168
+ returnType: TypeNode
169
+ decorators: Decorator[]
170
+ body: Block
171
+ }
172
+
173
+ // --- 顶层 ---
174
+ export interface Program {
175
+ namespace: string // 从文件名推断 或 首行 `namespace mypack;`
176
+ declarations: FnDecl[]
177
+ }
178
+ ```
179
+
180
+ ---
181
+
182
+ ## 任务 3:Parser (`src/parser/index.ts`)
183
+
184
+ 使用**递归下降**解析器(Recursive Descent Parser)。不需要外部库。
185
+
186
+ ### 结构
187
+
188
+ ```ts
189
+ export class Parser {
190
+ private tokens: Token[]
191
+ private pos: number = 0
192
+
193
+ constructor(tokens: Token[]) { this.tokens = tokens }
194
+
195
+ private peek(): Token { return this.tokens[this.pos] }
196
+ private advance(): Token { return this.tokens[this.pos++] }
197
+ private expect(kind: TokenKind): Token { ... }
198
+ private match(...kinds: TokenKind[]): boolean { ... }
199
+
200
+ parse(): Program { ... }
201
+
202
+ private parseFnDecl(): FnDecl { ... }
203
+ private parseDecorators(): Decorator[] { ... }
204
+ private parseBlock(): Block { ... }
205
+ private parseStmt(): Stmt { ... }
206
+ private parseExpr(): Expr { ... } // Pratt parser 或 precedence climbing
207
+ private parseSelector(): EntitySelector { ... }
208
+ private parseRange(): RangeExpr { ... }
209
+ }
210
+ ```
211
+
212
+ ### 运算符优先级(从低到高)
213
+
214
+ ```
215
+ || (左结合)
216
+ && (左结合)
217
+ == != (左结合)
218
+ < <= > >= (左结合)
219
+ + - (左结合)
220
+ * / % (左结合)
221
+ ! - (前缀) (右结合)
222
+ . [] () (后缀) (左结合)
223
+ ```
224
+
225
+ ### 实体选择器解析细节
226
+
227
+ ```ts
228
+ // parseSelector(): EntitySelector
229
+ // 输入: @e[type=zombie, distance=..5, tag=boss, tag=!excluded, limit=1]
230
+
231
+ // 1. 消费 selector token (已经是整个 @e[...] 字符串)
232
+ // 2. 拆分 kind (@e) 和 filter 参数字符串
233
+ // 3. 解析每个 key=value 对:
234
+ // - type=zombie → filters.type = "zombie"
235
+ // - distance=..5 → filters.distance = { max: 5 }
236
+ // - distance=1..10 → filters.distance = { min: 1, max: 10 }
237
+ // - tag=boss → filters.tag = [..., "boss"]
238
+ // - tag=!excluded → filters.notTag = [..., "excluded"]
239
+ // - limit=1 → filters.limit = 1
240
+ // - sort=nearest → filters.sort = "nearest"
241
+ // - scores={kills=1..} → filters.scores = { kills: { min: 1 } }
242
+ // - nbt={NoAI:1b} → filters.nbt = "{NoAI:1b}"
243
+ ```
244
+
245
+ ---
246
+
247
+ ## 任务 4:AST → IR Lowering (`src/lowering/index.ts`)
248
+
249
+ ### 核心类
250
+
251
+ ```ts
252
+ export class Lowering {
253
+ private module: IRModule
254
+ private builder: IRBuilder
255
+ private varMap: Map<string, string> // source var → IR var name
256
+ private fnQueue: FnDecl[]
257
+
258
+ lower(program: Program): IRModule { ... }
259
+
260
+ private lowerFn(fn: FnDecl): IRFunction { ... }
261
+ private lowerBlock(stmts: Stmt[]): void { ... }
262
+ private lowerStmt(stmt: Stmt): void { ... }
263
+ private lowerExpr(expr: Expr): Operand { ... }
264
+ private lowerForeach(stmt: ForeachStmt): void { ... }
265
+ private lowerBuiltinCall(name: string, args: Expr[]): Operand | null { ... }
266
+ }
267
+ ```
268
+
269
+ ### 关键 Lowering 规则
270
+
271
+ #### 变量声明
272
+ ```
273
+ let x: int = expr;
274
+
275
+ $x = lowerExpr(expr) (assign instr)
276
+ ```
277
+
278
+ #### 二元表达式
279
+ ```
280
+ a + b
281
+
282
+ $t0 = lowerExpr(a)
283
+ $t1 = lowerExpr(b)
284
+ $t2 = binop $t0 + $t1
285
+ return { kind: 'var', name: '$t2' }
286
+ ```
287
+
288
+ #### if/else
289
+ ```
290
+ if (cond) { then } else { else_ }
291
+
292
+ $cond = lowerExpr(cond)
293
+ emit: jump_if $cond → then_label
294
+ emit: jump → else_label
295
+
296
+ [then_label]:
297
+ lowerBlock(then)
298
+ emit: jump → merge_label
299
+
300
+ [else_label]:
301
+ lowerBlock(else_)
302
+ emit: jump → merge_label
303
+
304
+ [merge_label]:
305
+ ```
306
+
307
+ #### while
308
+ ```
309
+ while (cond) { body }
310
+
311
+ [loop_check]:
312
+ $cond = lowerExpr(cond)
313
+ jump_unless $cond → loop_exit
314
+ lowerBlock(body)
315
+ jump → loop_check
316
+
317
+ [loop_exit]:
318
+ ```
319
+
320
+ #### foreach — 最重要的规则
321
+ ```
322
+ foreach (entity in @e[type=zombie]) { body }
323
+
324
+ // 1. 把 body 提取成独立函数(名字: parent_fn/foreach_N)
325
+ // 2. emit raw: "execute as @e[type=zombie] run function ns:parent_fn/foreach_N"
326
+
327
+ 生成的子函数内容:
328
+ lowerBlock(body) (其中 "entity" 变量绑定到 @s)
329
+ ```
330
+
331
+ #### 内建函数(直接 emit raw,不进优化 pipeline)
332
+ ```ts
333
+ const BUILTINS: Record<string, (args: string[]) => string> = {
334
+ say: ([msg]) => `say ${msg}`,
335
+ tell: ([sel, msg]) => `tellraw ${sel} {"text":"${msg}"}`,
336
+ title: ([sel, msg]) => `title ${sel} title {"text":"${msg}"}`,
337
+ give: ([sel, item, count]) => `give ${sel} ${item} ${count ?? 1}`,
338
+ kill: ([sel]) => `kill ${sel ?? '@s'}`,
339
+ effect: ([sel, eff, dur, amp]) => `effect give ${sel} ${eff} ${dur ?? 30} ${amp ?? 0}`,
340
+ summon: ([type, x, y, z, nbt]) => `summon ${type} ${x ?? '~'} ${y ?? '~'} ${z ?? '~'} ${nbt ?? ''}`,
341
+ random: ([min, max]) => null, // special: needs execute store
342
+ }
343
+ // random(min, max) →
344
+ // emit: raw(`execute store result score ${dst} rs run random value ${min} ${max}`)
345
+ // return dst
346
+ ```
347
+
348
+ #### @tick 装饰器
349
+ ```
350
+ @tick fn game_loop() { ... }
351
+ → IRFunction { isTickLoop: true, ... }
352
+ → codegen 自动注册到 minecraft:tick tag
353
+
354
+ @tick(rate=N) fn slow_fn() { ... }
355
+ → IRFunction { isTickLoop: true, tickRate: N }
356
+ → codegen 生成 counter 逻辑:
357
+ scoreboard players add $__tick_slow_fn rs 1
358
+ execute if score $__tick_slow_fn rs matches N.. run function ns:slow_fn
359
+ execute if score $__tick_slow_fn rs matches N.. run scoreboard players set $__tick_slow_fn rs 0
360
+ ```
361
+
362
+ #### Selector 转字符串(在 raw/builtin emit 时)
363
+ ```ts
364
+ function selectorToString(sel: EntitySelector): string {
365
+ const { kind, filters } = sel
366
+ if (!filters) return kind
367
+
368
+ const parts: string[] = []
369
+ if (filters.type) parts.push(`type=${filters.type}`)
370
+ if (filters.distance) parts.push(`distance=${rangeToString(filters.distance)}`)
371
+ if (filters.tag) filters.tag.forEach(t => parts.push(`tag=${t}`))
372
+ if (filters.notTag) filters.notTag.forEach(t => parts.push(`tag=!${t}`))
373
+ if (filters.limit) parts.push(`limit=${filters.limit}`)
374
+ if (filters.sort) parts.push(`sort=${filters.sort}`)
375
+ if (filters.scores) {
376
+ const scoreStr = Object.entries(filters.scores)
377
+ .map(([k, v]) => `${k}=${rangeToString(v)}`).join(',')
378
+ parts.push(`scores={${scoreStr}}`)
379
+ }
380
+ if (filters.nbt) parts.push(`nbt=${filters.nbt}`)
381
+ return parts.length ? `${kind}[${parts.join(', ')}]` : kind
382
+ }
383
+
384
+ function rangeToString(r: RangeExpr): string {
385
+ if (r.min !== undefined && r.max !== undefined) return `${r.min}..${r.max}`
386
+ if (r.min !== undefined) return `${r.min}..`
387
+ if (r.max !== undefined) return `..${r.max}`
388
+ return '..'
389
+ }
390
+ ```
391
+
392
+ ---
393
+
394
+ ## 端到端测试用例
395
+
396
+ 测试文件写在 `src/__tests__/e2e.test.ts`。
397
+
398
+ ### 测试 1:简单函数
399
+ ```rs
400
+ // input: src/test_programs/add.rs
401
+ fn add(a: int, b: int) -> int {
402
+ return a + b;
403
+ }
404
+ ```
405
+ 预期输出 `data/test/function/add.mcfunction`:
406
+ ```
407
+ # block: entry
408
+ scoreboard players operation $a rs = $p0 rs
409
+ scoreboard players operation $b rs = $p1 rs
410
+ scoreboard players operation $result rs = $a rs
411
+ scoreboard players operation $result rs += $b rs
412
+ return run scoreboard players get $result rs
413
+ ```
414
+
415
+ ### 测试 2:if/else
416
+ ```rs
417
+ fn abs(x: int) -> int {
418
+ if (x < 0) {
419
+ return -x;
420
+ } else {
421
+ return x;
422
+ }
423
+ }
424
+ ```
425
+
426
+ ### 测试 3:@tick + say
427
+ ```rs
428
+ @tick(rate=20)
429
+ fn heartbeat() {
430
+ say("still alive");
431
+ }
432
+ ```
433
+ 预期:生成 `minecraft:tick` tag,counter 逻辑,`say still alive`
434
+
435
+ ### 测试 4:foreach
436
+ ```rs
437
+ fn kill_zombies() {
438
+ foreach (z in @e[type=zombie, distance=..10]) {
439
+ kill(z);
440
+ }
441
+ }
442
+ ```
443
+ 预期:entry 函数有 `execute as @e[type=zombie, distance=..10] run function test:kill_zombies/foreach_0`
444
+ 子函数有 `kill @s`
445
+
446
+ ### 测试 5:变量和 while
447
+ ```rs
448
+ fn count_down() {
449
+ let i: int = 10;
450
+ while (i > 0) {
451
+ i = i - 1;
452
+ }
453
+ }
454
+ ```
455
+
456
+ ---
457
+
458
+ ## 实现顺序
459
+
460
+ 1. `src/ast/types.ts` — AST 类型定义(纯类型,不需要测试)
461
+ 2. `src/lexer/index.ts` + `src/__tests__/lexer.test.ts`
462
+ 3. `src/parser/index.ts` + `src/__tests__/parser.test.ts`
463
+ 4. `src/lowering/index.ts` + `src/__tests__/lowering.test.ts`
464
+ 5. `src/__tests__/e2e.test.ts` — 端到端
465
+
466
+ **每完成一个文件就 commit + push(小步提交)。**
467
+
468
+ ---
469
+
470
+ ## 编译器入口(最后)
471
+
472
+ ```ts
473
+ // src/index.ts
474
+ import { Lexer } from './lexer'
475
+ import { Parser } from './parser'
476
+ import { Lowering } from './lowering'
477
+ import { optimize } from './optimizer/passes'
478
+ import { generateDatapack } from './codegen/mcfunction'
479
+
480
+ export function compile(source: string, namespace: string) {
481
+ const tokens = new Lexer(source).tokenize()
482
+ const ast = new Parser(tokens).parse()
483
+ const ir = new Lowering(namespace).lower(ast)
484
+ const optimized = { ...ir, functions: ir.functions.map(optimize) }
485
+ return generateDatapack(optimized)
486
+ }
487
+ ```
488
+
489
+ ---
490
+
491
+ ## 约束和注意事项
492
+
493
+ 1. **MC 整数**:只有整数,`int` 是 32-bit signed。除法是截断除法(`Math.trunc`)。
494
+ 2. **`@s` 是魔法变量**:在 `foreach`/`as` 块内,`entity` 变量绑定到 `@s`(当前执行者)。lowering 时 `kill(entity)` → `kill @s`。
495
+ 3. **函数名冲突**:`foreach` 提取出的子函数要用父函数名作前缀,保证唯一。
496
+ 4. **计分板 objective 名 `rs`**:所有变量用同一个 objective,fake player 名区分变量。
497
+ 5. **`$ret` 是约定的返回值寄存器**。
498
+ 6. **`$p0` `$p1` ... 是参数寄存器**。
499
+ 7. **所有 fake player 名以 `$` 开头**,避免和真实玩家名冲突。
500
+ 8. **`return` 命令(Java 1.20+)**:只用在函数最后。中途 return 需要条件跳转到 exit block。
501
+
502
+ ---
503
+
504
+ ## 参考资料
505
+
506
+ - `docs/mc-reference/commands.md` — 所有命令语法(已整理)
507
+ - `src/ir/types.ts` — IR 类型(你要输出这些)
508
+ - `src/ir/builder.ts` — IRBuilder API(用这个构建 IR)
509
+ - `src/codegen/mcfunction/index.ts` — codegen(了解它期望什么格式的 IR)
510
+ - `src/__tests__/optimizer.test.ts` — 看看测试风格
511
+
512
+ 加油!