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.
- package/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +31 -0
- package/.github/ISSUE_TEMPLATE/wrong_output.md +33 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +34 -0
- package/.github/workflows/ci.yml +29 -0
- package/.github/workflows/publish-extension.yml +35 -0
- package/LICENSE +21 -0
- package/README.md +261 -0
- package/README.zh.md +261 -0
- package/dist/__tests__/cli.test.d.ts +1 -0
- package/dist/__tests__/cli.test.js +140 -0
- package/dist/__tests__/codegen.test.d.ts +1 -0
- package/dist/__tests__/codegen.test.js +121 -0
- package/dist/__tests__/diagnostics.test.d.ts +4 -0
- package/dist/__tests__/diagnostics.test.js +149 -0
- package/dist/__tests__/e2e.test.d.ts +6 -0
- package/dist/__tests__/e2e.test.js +1528 -0
- package/dist/__tests__/lexer.test.d.ts +1 -0
- package/dist/__tests__/lexer.test.js +316 -0
- package/dist/__tests__/lowering.test.d.ts +1 -0
- package/dist/__tests__/lowering.test.js +819 -0
- package/dist/__tests__/mc-integration.test.d.ts +12 -0
- package/dist/__tests__/mc-integration.test.js +395 -0
- package/dist/__tests__/mc-syntax.test.d.ts +1 -0
- package/dist/__tests__/mc-syntax.test.js +112 -0
- package/dist/__tests__/nbt.test.d.ts +1 -0
- package/dist/__tests__/nbt.test.js +82 -0
- package/dist/__tests__/optimizer-advanced.test.d.ts +1 -0
- package/dist/__tests__/optimizer-advanced.test.js +124 -0
- package/dist/__tests__/optimizer.test.d.ts +1 -0
- package/dist/__tests__/optimizer.test.js +118 -0
- package/dist/__tests__/parser.test.d.ts +1 -0
- package/dist/__tests__/parser.test.js +717 -0
- package/dist/__tests__/repl.test.d.ts +1 -0
- package/dist/__tests__/repl.test.js +27 -0
- package/dist/__tests__/runtime.test.d.ts +1 -0
- package/dist/__tests__/runtime.test.js +276 -0
- package/dist/__tests__/structure-optimizer.test.d.ts +1 -0
- package/dist/__tests__/structure-optimizer.test.js +33 -0
- package/dist/__tests__/typechecker.test.d.ts +1 -0
- package/dist/__tests__/typechecker.test.js +364 -0
- package/dist/ast/types.d.ts +357 -0
- package/dist/ast/types.js +9 -0
- package/dist/cli.d.ts +11 -0
- package/dist/cli.js +407 -0
- package/dist/codegen/cmdblock/index.d.ts +26 -0
- package/dist/codegen/cmdblock/index.js +45 -0
- package/dist/codegen/mcfunction/index.d.ts +34 -0
- package/dist/codegen/mcfunction/index.js +413 -0
- package/dist/codegen/structure/index.d.ts +18 -0
- package/dist/codegen/structure/index.js +249 -0
- package/dist/compile.d.ts +30 -0
- package/dist/compile.js +152 -0
- package/dist/data/arena/function/__load.mcfunction +6 -0
- package/dist/data/arena/function/__tick.mcfunction +2 -0
- package/dist/data/arena/function/announce_leaders/else_1.mcfunction +3 -0
- package/dist/data/arena/function/announce_leaders/foreach_0/merge_2.mcfunction +1 -0
- package/dist/data/arena/function/announce_leaders/foreach_0/then_0.mcfunction +3 -0
- package/dist/data/arena/function/announce_leaders/foreach_0.mcfunction +7 -0
- package/dist/data/arena/function/announce_leaders/foreach_1/merge_2.mcfunction +1 -0
- package/dist/data/arena/function/announce_leaders/foreach_1/then_0.mcfunction +4 -0
- package/dist/data/arena/function/announce_leaders/foreach_1.mcfunction +6 -0
- package/dist/data/arena/function/announce_leaders/merge_2.mcfunction +1 -0
- package/dist/data/arena/function/announce_leaders/then_0.mcfunction +4 -0
- package/dist/data/arena/function/announce_leaders.mcfunction +6 -0
- package/dist/data/arena/function/arena_tick/merge_2.mcfunction +1 -0
- package/dist/data/arena/function/arena_tick/then_0.mcfunction +4 -0
- package/dist/data/arena/function/arena_tick.mcfunction +11 -0
- package/dist/data/counter/function/__load.mcfunction +5 -0
- package/dist/data/counter/function/__tick.mcfunction +2 -0
- package/dist/data/counter/function/counter_tick/merge_2.mcfunction +1 -0
- package/dist/data/counter/function/counter_tick/then_0.mcfunction +3 -0
- package/dist/data/counter/function/counter_tick.mcfunction +11 -0
- package/dist/data/minecraft/tags/function/load.json +5 -0
- package/dist/data/minecraft/tags/function/tick.json +5 -0
- package/dist/data/quiz/function/__load.mcfunction +16 -0
- package/dist/data/quiz/function/__tick.mcfunction +6 -0
- package/dist/data/quiz/function/__trigger_quiz_a_dispatch.mcfunction +4 -0
- package/dist/data/quiz/function/__trigger_quiz_b_dispatch.mcfunction +4 -0
- package/dist/data/quiz/function/__trigger_quiz_c_dispatch.mcfunction +4 -0
- package/dist/data/quiz/function/__trigger_quiz_start_dispatch.mcfunction +4 -0
- package/dist/data/quiz/function/answer_a.mcfunction +4 -0
- package/dist/data/quiz/function/answer_b.mcfunction +4 -0
- package/dist/data/quiz/function/answer_c.mcfunction +4 -0
- package/dist/data/quiz/function/ask_question/else_1.mcfunction +5 -0
- package/dist/data/quiz/function/ask_question/else_4.mcfunction +5 -0
- package/dist/data/quiz/function/ask_question/else_7.mcfunction +4 -0
- package/dist/data/quiz/function/ask_question/merge_2.mcfunction +1 -0
- package/dist/data/quiz/function/ask_question/merge_5.mcfunction +2 -0
- package/dist/data/quiz/function/ask_question/merge_8.mcfunction +2 -0
- package/dist/data/quiz/function/ask_question/then_0.mcfunction +4 -0
- package/dist/data/quiz/function/ask_question/then_3.mcfunction +4 -0
- package/dist/data/quiz/function/ask_question/then_6.mcfunction +4 -0
- package/dist/data/quiz/function/ask_question.mcfunction +7 -0
- package/dist/data/quiz/function/finish_quiz.mcfunction +6 -0
- package/dist/data/quiz/function/handle_answer/else_1.mcfunction +5 -0
- package/dist/data/quiz/function/handle_answer/else_10.mcfunction +3 -0
- package/dist/data/quiz/function/handle_answer/else_16.mcfunction +3 -0
- package/dist/data/quiz/function/handle_answer/else_4.mcfunction +3 -0
- package/dist/data/quiz/function/handle_answer/else_7.mcfunction +5 -0
- package/dist/data/quiz/function/handle_answer/merge_11.mcfunction +2 -0
- package/dist/data/quiz/function/handle_answer/merge_14.mcfunction +2 -0
- package/dist/data/quiz/function/handle_answer/merge_17.mcfunction +2 -0
- package/dist/data/quiz/function/handle_answer/merge_2.mcfunction +8 -0
- package/dist/data/quiz/function/handle_answer/merge_5.mcfunction +2 -0
- package/dist/data/quiz/function/handle_answer/merge_8.mcfunction +2 -0
- package/dist/data/quiz/function/handle_answer/then_0.mcfunction +5 -0
- package/dist/data/quiz/function/handle_answer/then_12.mcfunction +5 -0
- package/dist/data/quiz/function/handle_answer/then_15.mcfunction +6 -0
- package/dist/data/quiz/function/handle_answer/then_3.mcfunction +6 -0
- package/dist/data/quiz/function/handle_answer/then_6.mcfunction +5 -0
- package/dist/data/quiz/function/handle_answer/then_9.mcfunction +6 -0
- package/dist/data/quiz/function/handle_answer.mcfunction +11 -0
- package/dist/data/quiz/function/start_quiz.mcfunction +5 -0
- package/dist/data/shop/function/__load.mcfunction +7 -0
- package/dist/data/shop/function/__tick.mcfunction +3 -0
- package/dist/data/shop/function/__trigger_shop_buy_dispatch.mcfunction +4 -0
- package/dist/data/shop/function/complete_purchase/else_1.mcfunction +5 -0
- package/dist/data/shop/function/complete_purchase/else_4.mcfunction +5 -0
- package/dist/data/shop/function/complete_purchase/else_7.mcfunction +3 -0
- package/dist/data/shop/function/complete_purchase/merge_2.mcfunction +2 -0
- package/dist/data/shop/function/complete_purchase/merge_5.mcfunction +2 -0
- package/dist/data/shop/function/complete_purchase/merge_8.mcfunction +2 -0
- package/dist/data/shop/function/complete_purchase/then_0.mcfunction +4 -0
- package/dist/data/shop/function/complete_purchase/then_3.mcfunction +4 -0
- package/dist/data/shop/function/complete_purchase/then_6.mcfunction +4 -0
- package/dist/data/shop/function/complete_purchase.mcfunction +7 -0
- package/dist/data/shop/function/handle_shop_trigger.mcfunction +3 -0
- package/dist/data/turret/function/__load.mcfunction +5 -0
- package/dist/data/turret/function/__tick.mcfunction +4 -0
- package/dist/data/turret/function/__trigger_deploy_turret_dispatch.mcfunction +4 -0
- package/dist/data/turret/function/deploy_turret.mcfunction +8 -0
- package/dist/data/turret/function/turret_tick/at_1.mcfunction +2 -0
- package/dist/data/turret/function/turret_tick/foreach_0.mcfunction +2 -0
- package/dist/data/turret/function/turret_tick/foreach_2.mcfunction +2 -0
- package/dist/data/turret/function/turret_tick/tick_body.mcfunction +3 -0
- package/dist/data/turret/function/turret_tick/tick_skip.mcfunction +1 -0
- package/dist/data/turret/function/turret_tick.mcfunction +5 -0
- package/dist/diagnostics/index.d.ts +44 -0
- package/dist/diagnostics/index.js +140 -0
- package/dist/index.d.ts +53 -0
- package/dist/index.js +126 -0
- package/dist/ir/builder.d.ts +32 -0
- package/dist/ir/builder.js +99 -0
- package/dist/ir/types.d.ts +117 -0
- package/dist/ir/types.js +15 -0
- package/dist/lexer/index.d.ts +36 -0
- package/dist/lexer/index.js +458 -0
- package/dist/lowering/index.d.ts +106 -0
- package/dist/lowering/index.js +2041 -0
- package/dist/mc-test/client.d.ts +128 -0
- package/dist/mc-test/client.js +174 -0
- package/dist/mc-test/runner.d.ts +28 -0
- package/dist/mc-test/runner.js +150 -0
- package/dist/mc-test/setup.d.ts +11 -0
- package/dist/mc-test/setup.js +98 -0
- package/dist/mc-validator/index.d.ts +17 -0
- package/dist/mc-validator/index.js +322 -0
- package/dist/nbt/index.d.ts +86 -0
- package/dist/nbt/index.js +250 -0
- package/dist/optimizer/commands.d.ts +36 -0
- package/dist/optimizer/commands.js +349 -0
- package/dist/optimizer/passes.d.ts +34 -0
- package/dist/optimizer/passes.js +227 -0
- package/dist/optimizer/structure.d.ts +8 -0
- package/dist/optimizer/structure.js +344 -0
- package/dist/pack.mcmeta +6 -0
- package/dist/parser/index.d.ts +76 -0
- package/dist/parser/index.js +1193 -0
- package/dist/repl.d.ts +16 -0
- package/dist/repl.js +165 -0
- package/dist/runtime/index.d.ts +101 -0
- package/dist/runtime/index.js +1288 -0
- package/dist/typechecker/index.d.ts +42 -0
- package/dist/typechecker/index.js +629 -0
- package/docs/COMPILATION_STATS.md +142 -0
- package/docs/IMPLEMENTATION_GUIDE.md +512 -0
- package/docs/LANGUAGE_REFERENCE.md +415 -0
- package/docs/MC_MAPPING.md +280 -0
- package/docs/STRUCTURE_TARGET.md +80 -0
- package/docs/mc-reference/commands.md +259 -0
- package/editors/vscode/.vscodeignore +10 -0
- package/editors/vscode/LICENSE +21 -0
- package/editors/vscode/README.md +78 -0
- package/editors/vscode/build.mjs +28 -0
- package/editors/vscode/icon.png +0 -0
- package/editors/vscode/mcfunction-language-configuration.json +28 -0
- package/editors/vscode/out/extension.js +7236 -0
- package/editors/vscode/package-lock.json +566 -0
- package/editors/vscode/package.json +137 -0
- package/editors/vscode/redscript-language-configuration.json +28 -0
- package/editors/vscode/snippets/redscript.json +114 -0
- package/editors/vscode/src/codeactions.ts +89 -0
- package/editors/vscode/src/completion.ts +130 -0
- package/editors/vscode/src/extension.ts +239 -0
- package/editors/vscode/src/hover.ts +1120 -0
- package/editors/vscode/src/symbols.ts +207 -0
- package/editors/vscode/syntaxes/mcfunction.tmLanguage.json +740 -0
- package/editors/vscode/syntaxes/redscript.tmLanguage.json +357 -0
- package/editors/vscode/tsconfig.json +13 -0
- package/jest.config.js +5 -0
- package/package.json +38 -0
- package/src/__tests__/cli.test.ts +130 -0
- package/src/__tests__/codegen.test.ts +128 -0
- package/src/__tests__/diagnostics.test.ts +195 -0
- package/src/__tests__/e2e.test.ts +1721 -0
- package/src/__tests__/fixtures/mc-commands-1.21.4.json +18734 -0
- package/src/__tests__/formatter.test.ts +46 -0
- package/src/__tests__/lexer.test.ts +356 -0
- package/src/__tests__/lowering.test.ts +962 -0
- package/src/__tests__/mc-integration.test.ts +409 -0
- package/src/__tests__/mc-syntax.test.ts +96 -0
- package/src/__tests__/nbt.test.ts +58 -0
- package/src/__tests__/optimizer-advanced.test.ts +144 -0
- package/src/__tests__/optimizer.test.ts +129 -0
- package/src/__tests__/parser.test.ts +800 -0
- package/src/__tests__/repl.test.ts +33 -0
- package/src/__tests__/runtime.test.ts +289 -0
- package/src/__tests__/structure-optimizer.test.ts +38 -0
- package/src/__tests__/typechecker.test.ts +395 -0
- package/src/ast/types.ts +248 -0
- package/src/cli.ts +445 -0
- package/src/codegen/cmdblock/index.ts +63 -0
- package/src/codegen/mcfunction/index.ts +471 -0
- package/src/codegen/structure/index.ts +305 -0
- package/src/compile.ts +188 -0
- package/src/diagnostics/index.ts +186 -0
- package/src/examples/README.md +77 -0
- package/src/examples/SHOWCASE_GAME.md +43 -0
- package/src/examples/arena.rs +44 -0
- package/src/examples/counter.rs +12 -0
- package/src/examples/pvp_arena.rs +131 -0
- package/src/examples/quiz.rs +90 -0
- package/src/examples/rpg.rs +13 -0
- package/src/examples/shop.rs +30 -0
- package/src/examples/showcase_game.rs +552 -0
- package/src/examples/stdlib_demo.rs +181 -0
- package/src/examples/turret.rs +27 -0
- package/src/examples/world_manager.rs +23 -0
- package/src/formatter/index.ts +22 -0
- package/src/index.ts +161 -0
- package/src/ir/builder.ts +114 -0
- package/src/ir/types.ts +119 -0
- package/src/lexer/index.ts +555 -0
- package/src/lowering/index.ts +2406 -0
- package/src/mc-test/client.ts +259 -0
- package/src/mc-test/runner.ts +140 -0
- package/src/mc-test/setup.ts +70 -0
- package/src/mc-validator/index.ts +367 -0
- package/src/nbt/index.ts +321 -0
- package/src/optimizer/commands.ts +416 -0
- package/src/optimizer/passes.ts +233 -0
- package/src/optimizer/structure.ts +441 -0
- package/src/parser/index.ts +1437 -0
- package/src/repl.ts +165 -0
- package/src/runtime/index.ts +1403 -0
- package/src/stdlib/README.md +156 -0
- package/src/stdlib/combat.rs +20 -0
- package/src/stdlib/cooldown.rs +45 -0
- package/src/stdlib/math.rs +49 -0
- package/src/stdlib/mobs.rs +99 -0
- package/src/stdlib/player.rs +29 -0
- package/src/stdlib/strings.rs +7 -0
- package/src/stdlib/timer.rs +51 -0
- package/src/templates/README.md +126 -0
- package/src/templates/combat.rs +96 -0
- package/src/templates/economy.rs +40 -0
- package/src/templates/mini-game-framework.rs +117 -0
- package/src/templates/quest.rs +78 -0
- package/src/test_programs/zombie_game.rs +25 -0
- package/src/typechecker/index.ts +737 -0
- 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
|
+
加油!
|