redscript-mc 1.2.8 → 1.2.10
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/README.md +35 -33
- package/README.zh.md +35 -33
- package/demo.gif +0 -0
- package/dist/ast/types.d.ts +6 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lexer/index.d.ts +1 -1
- package/dist/lexer/index.js +2 -0
- package/dist/lowering/index.d.ts +3 -0
- package/dist/lowering/index.js +60 -7
- package/dist/optimizer/dce.js +6 -0
- package/dist/parser/index.js +12 -0
- package/package.json +1 -1
- package/src/ast/types.ts +2 -0
- package/src/index.ts +1 -1
- package/src/lexer/index.ts +3 -1
- package/src/lowering/index.ts +64 -7
- package/src/optimizer/dce.ts +6 -0
- package/src/parser/index.ts +14 -0
- package/test-datapacks/README.md +67 -0
- package/test-datapacks/test_control_flow/data/minecraft/tags/function/load.json +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/__load.mcfunction +41 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/run_control_flow_tests.mcfunction +21 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_break/else_7.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_break/else_8.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_break/for_body_1.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_break/for_check_0.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_break/for_continue_2.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_break/for_exit_2.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_break/for_exit_3.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_break/merge_5.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_break/merge_6.mcfunction +2 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_break/merge_8.mcfunction +1 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_break/merge_9.mcfunction +1 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_break/then_3.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_break/then_4.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_break/then_6.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_break/then_7.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_break.mcfunction +4 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_complex_conditions/else_1.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_complex_conditions/else_10.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_complex_conditions/else_4.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_complex_conditions/else_7.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_complex_conditions/merge_11.mcfunction +1 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_complex_conditions/merge_2.mcfunction +9 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_complex_conditions/merge_5.mcfunction +7 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_complex_conditions/merge_8.mcfunction +13 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_complex_conditions/then_0.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_complex_conditions/then_3.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_complex_conditions/then_6.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_complex_conditions/then_9.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_complex_conditions.mcfunction +12 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_continue/else_7.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_continue/else_8.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_continue/for_body_1.mcfunction +7 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_continue/for_check_0.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_continue/for_continue_2.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_continue/for_exit_2.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_continue/for_exit_3.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_continue/merge_5.mcfunction +8 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_continue/merge_6.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_continue/merge_8.mcfunction +1 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_continue/merge_9.mcfunction +1 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_continue/then_3.mcfunction +2 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_continue/then_4.mcfunction +2 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_continue/then_6.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_continue/then_7.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_continue.mcfunction +4 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/else_13.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/else_16.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/else_4.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/else_5.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/for_body_1.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/for_body_10.mcfunction +8 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/for_body_12.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/for_body_7.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/for_body_8.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/for_check_0.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/for_check_11.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/for_check_6.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/for_check_7.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/for_check_9.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/for_continue_13.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/for_continue_2.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/for_continue_9.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/for_exit_10.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/for_exit_11.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/for_exit_14.mcfunction +2 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/for_exit_2.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/for_exit_3.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/for_exit_8.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/merge_14.mcfunction +1 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/merge_17.mcfunction +1 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/merge_5.mcfunction +4 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/merge_6.mcfunction +4 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/then_12.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/then_15.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/then_3.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop/then_4.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_for_loop.mcfunction +4 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_foreach_at/foreach_0.mcfunction +2 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_foreach_at.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_foreach_selector/else_1.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_foreach_selector/foreach_0.mcfunction +4 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_foreach_selector/merge_2.mcfunction +2 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_foreach_selector/then_0.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_foreach_selector.mcfunction +11 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_if_else/else_1.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_if_else/else_13.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_if_else/else_16.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_if_else/else_4.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_if_else/merge_11.mcfunction +2 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_if_else/merge_14.mcfunction +1 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_if_else/merge_17.mcfunction +2 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_if_else/merge_2.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_if_else/merge_5.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_if_else/merge_8.mcfunction +6 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_if_else/then_0.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_if_else/then_12.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_if_else/then_15.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_if_else/then_3.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_if_else/then_6.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_if_else/then_9.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_if_else.mcfunction +6 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_match/match_0.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_match/match_1.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_match/match_2.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_match/match_3.mcfunction +2 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_match/match_4.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_match/match_5.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_match/match_6.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_match/match_7.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_match/match_8.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_match/match_9.mcfunction +2 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_match.mcfunction +15 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_while_loop/else_13.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_while_loop/else_4.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_while_loop/loop_body_1.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_while_loop/loop_body_7.mcfunction +8 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_while_loop/loop_check_0.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_while_loop/loop_check_6.mcfunction +4 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_while_loop/loop_exit_2.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_while_loop/loop_exit_8.mcfunction +5 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_while_loop/merge_11.mcfunction +2 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_while_loop/merge_14.mcfunction +1 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_while_loop/merge_5.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_while_loop/then_12.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_while_loop/then_3.mcfunction +3 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_while_loop/then_9.mcfunction +2 -0
- package/test-datapacks/test_control_flow/data/test_control_flow/function/test_while_loop.mcfunction +3 -0
- package/test-datapacks/test_control_flow/pack.mcmeta +6 -0
- package/test-datapacks/test_control_flow.mcrs +242 -0
- package/test-datapacks/test_decorators/data/minecraft/tags/function/load.json +5 -0
- package/test-datapacks/test_decorators/data/minecraft/tags/function/tick.json +5 -0
- package/test-datapacks/test_decorators/data/test_decorators/function/__load.mcfunction +20 -0
- package/test-datapacks/test_decorators/data/test_decorators/function/__tick.mcfunction +3 -0
- package/test-datapacks/test_decorators/data/test_decorators/function/every_second/merge_2.mcfunction +9 -0
- package/test-datapacks/test_decorators/data/test_decorators/function/every_second/merge_5.mcfunction +1 -0
- package/test-datapacks/test_decorators/data/test_decorators/function/every_second/then_0.mcfunction +1 -0
- package/test-datapacks/test_decorators/data/test_decorators/function/every_second/then_3.mcfunction +4 -0
- package/test-datapacks/test_decorators/data/test_decorators/function/every_second/tick_body.mcfunction +6 -0
- package/test-datapacks/test_decorators/data/test_decorators/function/every_second/tick_skip.mcfunction +1 -0
- package/test-datapacks/test_decorators/data/test_decorators/function/every_second.mcfunction +5 -0
- package/test-datapacks/test_decorators/data/test_decorators/function/on_load.mcfunction +2 -0
- package/test-datapacks/test_decorators/data/test_decorators/function/on_test_trigger.mcfunction +2 -0
- package/test-datapacks/test_decorators/data/test_decorators/function/on_tick/merge_2.mcfunction +10 -0
- package/test-datapacks/test_decorators/data/test_decorators/function/on_tick/merge_5.mcfunction +5 -0
- package/test-datapacks/test_decorators/data/test_decorators/function/on_tick/merge_8.mcfunction +1 -0
- package/test-datapacks/test_decorators/data/test_decorators/function/on_tick/then_0.mcfunction +1 -0
- package/test-datapacks/test_decorators/data/test_decorators/function/on_tick/then_3.mcfunction +3 -0
- package/test-datapacks/test_decorators/data/test_decorators/function/on_tick/then_6.mcfunction +4 -0
- package/test-datapacks/test_decorators/data/test_decorators/function/on_tick.mcfunction +5 -0
- package/test-datapacks/test_decorators/data/test_decorators/function/run_decorator_tests.mcfunction +7 -0
- package/test-datapacks/test_decorators/data/test_decorators/function/setup_trigger_test.mcfunction +4 -0
- package/test-datapacks/test_decorators/data/test_decorators/function/start_slow_tick_test.mcfunction +4 -0
- package/test-datapacks/test_decorators/data/test_decorators/function/start_tick_test.mcfunction +4 -0
- package/test-datapacks/test_decorators/data/test_decorators/function/stop_tick_test.mcfunction +3 -0
- package/test-datapacks/test_decorators/pack.mcmeta +6 -0
- package/test-datapacks/test_decorators.mcrs +81 -0
- package/test-datapacks/test_events/data/minecraft/tags/function/load.json +5 -0
- package/test-datapacks/test_events/data/test_events/advancements/on_death_on_player_death.json +10 -0
- package/test-datapacks/test_events/data/test_events/function/__load.mcfunction +3 -0
- package/test-datapacks/test_events/data/test_events/function/on_player_death.mcfunction +3 -0
- package/test-datapacks/test_events/data/test_events/function/run_event_tests.mcfunction +5 -0
- package/test-datapacks/test_events/data/test_events/function/setup_events.mcfunction +3 -0
- package/test-datapacks/test_events/data/test_events/function/spawn_test_entity.mcfunction +3 -0
- package/test-datapacks/test_events/data/test_events/function/test_advancement_event.mcfunction +4 -0
- package/test-datapacks/test_events/pack.mcmeta +6 -0
- package/test-datapacks/test_events.mcrs +37 -0
- package/test-datapacks/test_fstrings/data/minecraft/tags/function/load.json +5 -0
- package/test-datapacks/test_fstrings/data/test_fstrings/function/__load.mcfunction +10 -0
- package/test-datapacks/test_fstrings/data/test_fstrings/function/run_fstring_tests.mcfunction +15 -0
- package/test-datapacks/test_fstrings/data/test_fstrings/function/test_actionbar_fstring.mcfunction +4 -0
- package/test-datapacks/test_fstrings/data/test_fstrings/function/test_announce.mcfunction +3 -0
- package/test-datapacks/test_fstrings/data/test_fstrings/function/test_multi_var_fstring.mcfunction +9 -0
- package/test-datapacks/test_fstrings/data/test_fstrings/function/test_say_fstring.mcfunction +4 -0
- package/test-datapacks/test_fstrings/data/test_fstrings/function/test_tell.mcfunction +3 -0
- package/test-datapacks/test_fstrings/data/test_fstrings/function/test_title_fstring.mcfunction +4 -0
- package/test-datapacks/test_fstrings/pack.mcmeta +6 -0
- package/test-datapacks/test_fstrings.mcrs +58 -0
- package/test-datapacks/test_timers/data/minecraft/tags/function/load.json +5 -0
- package/test-datapacks/test_timers/data/test_timers/function/__interval_0.mcfunction +3 -0
- package/test-datapacks/test_timers/data/test_timers/function/__interval_body_0.mcfunction +5 -0
- package/test-datapacks/test_timers/data/test_timers/function/__load.mcfunction +5 -0
- package/test-datapacks/test_timers/data/test_timers/function/__timeout_0.mcfunction +2 -0
- package/test-datapacks/test_timers/data/test_timers/function/run_timer_tests.mcfunction +5 -0
- package/test-datapacks/test_timers/data/test_timers/function/test_set_interval.mcfunction +5 -0
- package/test-datapacks/test_timers/data/test_timers/function/test_set_timeout.mcfunction +3 -0
- package/test-datapacks/test_timers/pack.mcmeta +6 -0
- package/test-datapacks/test_timers.mcrs +35 -0
package/README.md
CHANGED
|
@@ -20,55 +20,56 @@ Write clean game logic. RedScript handles the scoreboard spaghetti.
|
|
|
20
20
|
|
|
21
21
|
### 🚀 [Try it online — no install needed!](https://redscript-ide.pages.dev)
|
|
22
22
|
|
|
23
|
+
<img src="./demo.gif" alt="RedScript Demo" width="400" />
|
|
24
|
+
|
|
25
|
+
*↑ Particles spawning at player position every tick — 100% vanilla, no mods! Just 30 lines of RedScript with full control flow: `if`, `foreach`, `@tick`, f-strings, and more.*
|
|
26
|
+
|
|
23
27
|
</div>
|
|
24
28
|
|
|
25
29
|
---
|
|
26
30
|
|
|
27
31
|
### What is RedScript?
|
|
28
32
|
|
|
29
|
-
|
|
33
|
+
RedScript is a typed scripting language that compiles to vanilla Minecraft datapacks. Write clean code with variables, functions, loops, and events — RedScript handles the scoreboard commands and `.mcfunction` files for you.
|
|
30
34
|
|
|
31
|
-
|
|
35
|
+
**The demo above?** Just 30 lines:
|
|
32
36
|
|
|
33
37
|
```rs
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
let counter: int = 0;
|
|
39
|
+
let running: bool = false;
|
|
40
|
+
|
|
41
|
+
@tick fn demo_tick() {
|
|
42
|
+
if (!running) { return; }
|
|
43
|
+
counter = counter + 1;
|
|
44
|
+
|
|
45
|
+
foreach (p in @a) at @s {
|
|
46
|
+
particle("minecraft:end_rod", ~0, ~1, ~0, 0.5, 0.5, 0.5, 0.1, 5);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (counter % 20 == 0) {
|
|
50
|
+
say(f"Running for {counter} ticks");
|
|
46
51
|
}
|
|
47
|
-
|
|
48
|
-
scoreboard_set(#game, #timer, time - 1);
|
|
49
|
-
actionbar(@a, "⏱ ${time}s remaining");
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
fn start_game() {
|
|
53
|
-
scoreboard_set(#game, #timer, GAME_TIME);
|
|
54
|
-
scoreboard_set(#game, #running, 1);
|
|
55
|
-
title(@a, "Fight!", "Game started");
|
|
56
|
-
tp(@a, (0, 64, 0));
|
|
57
52
|
}
|
|
58
53
|
|
|
59
|
-
fn
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
54
|
+
@keep fn start() {
|
|
55
|
+
running = true;
|
|
56
|
+
counter = 0;
|
|
57
|
+
say(f"Demo started!");
|
|
63
58
|
}
|
|
64
59
|
|
|
65
|
-
@
|
|
66
|
-
|
|
67
|
-
|
|
60
|
+
@keep fn stop() {
|
|
61
|
+
running = false;
|
|
62
|
+
say(f"Demo stopped at {counter} ticks.");
|
|
68
63
|
}
|
|
69
64
|
```
|
|
70
65
|
|
|
71
|
-
|
|
66
|
+
**What you get:**
|
|
67
|
+
- ✅ `let` / `const` variables (no more `scoreboard players set`)
|
|
68
|
+
- ✅ `if` / `else` / `for` / `foreach` control flow
|
|
69
|
+
- ✅ `@tick` / `@load` / `@on(Event)` decorators
|
|
70
|
+
- ✅ `foreach (p in @a) at @s` — iterate entities with execute context
|
|
71
|
+
- ✅ f-strings like `f"Score: {points}"` for dynamic output
|
|
72
|
+
- ✅ One file → ready-to-use datapack
|
|
72
73
|
|
|
73
74
|
---
|
|
74
75
|
|
|
@@ -199,7 +200,8 @@ if (hp <= 0) {
|
|
|
199
200
|
}
|
|
200
201
|
|
|
201
202
|
for (let i: int = 0; i < 10; i = i + 1) {
|
|
202
|
-
|
|
203
|
+
say(f"Spawning wave {i}");
|
|
204
|
+
summon("minecraft:zombie", ~0, ~0, ~0);
|
|
203
205
|
}
|
|
204
206
|
|
|
205
207
|
foreach (player in @a) {
|
package/README.zh.md
CHANGED
|
@@ -20,55 +20,56 @@
|
|
|
20
20
|
|
|
21
21
|
### 🚀 [在线试用 — 无需安装!](https://redscript-ide.pages.dev)
|
|
22
22
|
|
|
23
|
+
<img src="./demo.gif" alt="RedScript Demo" width="400" />
|
|
24
|
+
|
|
25
|
+
*↑ 每 tick 在玩家位置生成粒子 — 纯原版,无需 MOD!仅 30 行 RedScript,包含完整控制流:`if`、`foreach`、`@tick`、f-strings 等。*
|
|
26
|
+
|
|
23
27
|
</div>
|
|
24
28
|
|
|
25
29
|
---
|
|
26
30
|
|
|
27
31
|
### RedScript 是什么?
|
|
28
32
|
|
|
29
|
-
|
|
33
|
+
RedScript 是一门编译到原版 Minecraft 数据包的脚本语言。用变量、函数、循环、事件写代码,RedScript 帮你生成记分板命令和 `.mcfunction` 文件。
|
|
30
34
|
|
|
31
|
-
|
|
35
|
+
**上面的演示?** 只有 30 行:
|
|
32
36
|
|
|
33
37
|
```rs
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
let counter: int = 0;
|
|
39
|
+
let running: bool = false;
|
|
40
|
+
|
|
41
|
+
@tick fn demo_tick() {
|
|
42
|
+
if (!running) { return; }
|
|
43
|
+
counter = counter + 1;
|
|
44
|
+
|
|
45
|
+
foreach (p in @a) at @s {
|
|
46
|
+
particle("minecraft:end_rod", ~0, ~1, ~0, 0.5, 0.5, 0.5, 0.1, 5);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (counter % 20 == 0) {
|
|
50
|
+
say(f"已运行 {counter} ticks");
|
|
46
51
|
}
|
|
47
|
-
|
|
48
|
-
scoreboard_set(#game, #timer, time - 1);
|
|
49
|
-
actionbar(@a, "⏱ 剩余 ${time} 秒");
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
fn start_game() {
|
|
53
|
-
scoreboard_set(#game, #timer, GAME_TIME);
|
|
54
|
-
scoreboard_set(#game, #running, 1);
|
|
55
|
-
title(@a, "开始战斗!", "游戏已开始");
|
|
56
|
-
tp(@a, (0, 64, 0));
|
|
57
52
|
}
|
|
58
53
|
|
|
59
|
-
fn
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
54
|
+
@keep fn start() {
|
|
55
|
+
running = true;
|
|
56
|
+
counter = 0;
|
|
57
|
+
say(f"Demo 已启动!");
|
|
63
58
|
}
|
|
64
59
|
|
|
65
|
-
@
|
|
66
|
-
|
|
67
|
-
|
|
60
|
+
@keep fn stop() {
|
|
61
|
+
running = false;
|
|
62
|
+
say(f"Demo 已停止,共运行 {counter} ticks。");
|
|
68
63
|
}
|
|
69
64
|
```
|
|
70
65
|
|
|
71
|
-
|
|
66
|
+
**你得到:**
|
|
67
|
+
- ✅ `let` / `const` 变量(告别 `scoreboard players set`)
|
|
68
|
+
- ✅ `if` / `else` / `for` / `foreach` 完整控制流
|
|
69
|
+
- ✅ `@tick` / `@load` / `@on(Event)` 装饰器
|
|
70
|
+
- ✅ `foreach (p in @a) at @s` — 遍历实体并设置执行上下文
|
|
71
|
+
- ✅ f-strings 如 `f"分数: {points}"` 动态输出
|
|
72
|
+
- ✅ 一个文件 → 可直接使用的 datapack
|
|
72
73
|
|
|
73
74
|
---
|
|
74
75
|
|
|
@@ -199,7 +200,8 @@ if (hp <= 0) {
|
|
|
199
200
|
}
|
|
200
201
|
|
|
201
202
|
for (let i: int = 0; i < 10; i = i + 1) {
|
|
202
|
-
|
|
203
|
+
say(f"生成第 {i} 波");
|
|
204
|
+
summon("minecraft:zombie", ~0, ~0, ~0);
|
|
203
205
|
}
|
|
204
206
|
|
|
205
207
|
foreach (player in @a) {
|
package/demo.gif
ADDED
|
Binary file
|
package/dist/ast/types.d.ts
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Main entry point for programmatic usage.
|
|
5
5
|
*/
|
|
6
|
-
export declare const version = "1.2.
|
|
6
|
+
export declare const version = "1.2.10";
|
|
7
7
|
import type { Warning } from './lowering';
|
|
8
8
|
import { DatapackFile } from './codegen/mcfunction';
|
|
9
9
|
import type { IRModule } from './ir/types';
|
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ exports.MCCommandValidator = exports.generateDatapack = exports.optimize = expor
|
|
|
9
9
|
exports.compile = compile;
|
|
10
10
|
exports.check = check;
|
|
11
11
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
12
|
-
exports.version = '1.2.
|
|
12
|
+
exports.version = '1.2.10';
|
|
13
13
|
const lexer_1 = require("./lexer");
|
|
14
14
|
const parser_1 = require("./parser");
|
|
15
15
|
const typechecker_1 = require("./typechecker");
|
package/dist/lexer/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Handles special cases like entity selectors vs decorators,
|
|
6
6
|
* range literals, and raw commands.
|
|
7
7
|
*/
|
|
8
|
-
export type TokenKind = 'fn' | 'let' | 'const' | 'if' | 'else' | 'while' | 'for' | 'foreach' | 'match' | 'return' | 'as' | 'at' | 'in' | 'is' | 'struct' | 'impl' | 'enum' | 'trigger' | 'namespace' | 'execute' | 'run' | 'unless' | 'int' | 'bool' | 'float' | 'string' | 'void' | 'BlockPos' | 'true' | 'false' | 'selector' | 'decorator' | 'int_lit' | 'float_lit' | 'byte_lit' | 'short_lit' | 'long_lit' | 'double_lit' | 'string_lit' | 'f_string' | 'range_lit' | 'rel_coord' | 'local_coord' | '+' | '-' | '*' | '/' | '%' | '~' | '^' | '==' | '!=' | '<' | '<=' | '>' | '>=' | '&&' | '||' | '!' | '=' | '+=' | '-=' | '*=' | '/=' | '%=' | '{' | '}' | '(' | ')' | '[' | ']' | ',' | ';' | ':' | '::' | '->' | '=>' | '.' | 'ident' | 'mc_name' | 'raw_cmd' | 'eof';
|
|
8
|
+
export type TokenKind = 'fn' | 'let' | 'const' | 'if' | 'else' | 'while' | 'for' | 'foreach' | 'match' | 'return' | 'break' | 'continue' | 'as' | 'at' | 'in' | 'is' | 'struct' | 'impl' | 'enum' | 'trigger' | 'namespace' | 'execute' | 'run' | 'unless' | 'int' | 'bool' | 'float' | 'string' | 'void' | 'BlockPos' | 'true' | 'false' | 'selector' | 'decorator' | 'int_lit' | 'float_lit' | 'byte_lit' | 'short_lit' | 'long_lit' | 'double_lit' | 'string_lit' | 'f_string' | 'range_lit' | 'rel_coord' | 'local_coord' | '+' | '-' | '*' | '/' | '%' | '~' | '^' | '==' | '!=' | '<' | '<=' | '>' | '>=' | '&&' | '||' | '!' | '=' | '+=' | '-=' | '*=' | '/=' | '%=' | '{' | '}' | '(' | ')' | '[' | ']' | ',' | ';' | ':' | '::' | '->' | '=>' | '.' | 'ident' | 'mc_name' | 'raw_cmd' | 'eof';
|
|
9
9
|
export interface Token {
|
|
10
10
|
kind: TokenKind;
|
|
11
11
|
value: string;
|
package/dist/lexer/index.js
CHANGED
package/dist/lowering/index.d.ts
CHANGED
|
@@ -45,6 +45,7 @@ export declare class Lowering {
|
|
|
45
45
|
private varTypes;
|
|
46
46
|
private floatVars;
|
|
47
47
|
private worldObjCounter;
|
|
48
|
+
private loopStack;
|
|
48
49
|
constructor(namespace: string, sourceRanges?: SourceRange[]);
|
|
49
50
|
lower(program: Program): IRModule;
|
|
50
51
|
private lowerFn;
|
|
@@ -54,6 +55,8 @@ export declare class Lowering {
|
|
|
54
55
|
private lowerStmt;
|
|
55
56
|
private lowerLetStmt;
|
|
56
57
|
private lowerReturnStmt;
|
|
58
|
+
private lowerBreakStmt;
|
|
59
|
+
private lowerContinueStmt;
|
|
57
60
|
private lowerIfStmt;
|
|
58
61
|
private lowerIsCheckIfStmt;
|
|
59
62
|
private lowerWhileStmt;
|
package/dist/lowering/index.js
CHANGED
|
@@ -209,6 +209,8 @@ class Lowering {
|
|
|
209
209
|
this.floatVars = new Set();
|
|
210
210
|
// World object counter for unique tags
|
|
211
211
|
this.worldObjCounter = 0;
|
|
212
|
+
// Loop context stack for break/continue
|
|
213
|
+
this.loopStack = [];
|
|
212
214
|
this.namespace = namespace;
|
|
213
215
|
this.sourceRanges = sourceRanges;
|
|
214
216
|
LoweringBuilder.resetTempCounter();
|
|
@@ -462,6 +464,12 @@ class Lowering {
|
|
|
462
464
|
case 'return':
|
|
463
465
|
this.lowerReturnStmt(stmt);
|
|
464
466
|
break;
|
|
467
|
+
case 'break':
|
|
468
|
+
this.lowerBreakStmt();
|
|
469
|
+
break;
|
|
470
|
+
case 'continue':
|
|
471
|
+
this.lowerContinueStmt();
|
|
472
|
+
break;
|
|
465
473
|
case 'if':
|
|
466
474
|
this.lowerIfStmt(stmt);
|
|
467
475
|
break;
|
|
@@ -599,6 +607,20 @@ class Lowering {
|
|
|
599
607
|
this.builder.emitReturn();
|
|
600
608
|
}
|
|
601
609
|
}
|
|
610
|
+
lowerBreakStmt() {
|
|
611
|
+
if (this.loopStack.length === 0) {
|
|
612
|
+
throw new diagnostics_1.DiagnosticError('LoweringError', 'break statement outside of loop', { line: 1, col: 1 });
|
|
613
|
+
}
|
|
614
|
+
const loop = this.loopStack[this.loopStack.length - 1];
|
|
615
|
+
this.builder.emitJump(loop.breakLabel);
|
|
616
|
+
}
|
|
617
|
+
lowerContinueStmt() {
|
|
618
|
+
if (this.loopStack.length === 0) {
|
|
619
|
+
throw new diagnostics_1.DiagnosticError('LoweringError', 'continue statement outside of loop', { line: 1, col: 1 });
|
|
620
|
+
}
|
|
621
|
+
const loop = this.loopStack[this.loopStack.length - 1];
|
|
622
|
+
this.builder.emitJump(loop.continueLabel);
|
|
623
|
+
}
|
|
602
624
|
lowerIfStmt(stmt) {
|
|
603
625
|
if (stmt.cond.kind === 'is_check') {
|
|
604
626
|
this.lowerIsCheckIfStmt(stmt);
|
|
@@ -671,12 +693,16 @@ class Lowering {
|
|
|
671
693
|
const condVar = this.lowerExpr(stmt.cond);
|
|
672
694
|
const condName = this.operandToVar(condVar);
|
|
673
695
|
this.builder.emitJumpIf(condName, bodyLabel, exitLabel);
|
|
696
|
+
// Push loop context for break/continue (while has no step, so continue goes to check)
|
|
697
|
+
this.loopStack.push({ breakLabel: exitLabel, continueLabel: checkLabel });
|
|
674
698
|
// Body block
|
|
675
699
|
this.builder.startBlock(bodyLabel);
|
|
676
700
|
this.lowerBlock(stmt.body);
|
|
677
701
|
if (!this.builder.isBlockSealed()) {
|
|
678
702
|
this.builder.emitJump(checkLabel);
|
|
679
703
|
}
|
|
704
|
+
// Pop loop context
|
|
705
|
+
this.loopStack.pop();
|
|
680
706
|
// Exit block
|
|
681
707
|
this.builder.startBlock(exitLabel);
|
|
682
708
|
}
|
|
@@ -688,6 +714,7 @@ class Lowering {
|
|
|
688
714
|
}
|
|
689
715
|
const checkLabel = this.builder.freshLabel('for_check');
|
|
690
716
|
const bodyLabel = this.builder.freshLabel('for_body');
|
|
717
|
+
const continueLabel = this.builder.freshLabel('for_continue');
|
|
691
718
|
const exitLabel = this.builder.freshLabel('for_exit');
|
|
692
719
|
this.builder.emitJump(checkLabel);
|
|
693
720
|
// Check block
|
|
@@ -695,14 +722,20 @@ class Lowering {
|
|
|
695
722
|
const condVar = this.lowerExpr(stmt.cond);
|
|
696
723
|
const condName = this.operandToVar(condVar);
|
|
697
724
|
this.builder.emitJumpIf(condName, bodyLabel, exitLabel);
|
|
725
|
+
// Push loop context for break/continue
|
|
726
|
+
this.loopStack.push({ breakLabel: exitLabel, continueLabel });
|
|
698
727
|
// Body block
|
|
699
728
|
this.builder.startBlock(bodyLabel);
|
|
700
729
|
this.lowerBlock(stmt.body);
|
|
701
|
-
// Step expression
|
|
702
|
-
this.lowerExpr(stmt.step);
|
|
703
730
|
if (!this.builder.isBlockSealed()) {
|
|
704
|
-
this.builder.emitJump(
|
|
731
|
+
this.builder.emitJump(continueLabel);
|
|
705
732
|
}
|
|
733
|
+
// Continue block (step + loop back)
|
|
734
|
+
this.builder.startBlock(continueLabel);
|
|
735
|
+
this.lowerExpr(stmt.step);
|
|
736
|
+
this.builder.emitJump(checkLabel);
|
|
737
|
+
// Pop loop context
|
|
738
|
+
this.loopStack.pop();
|
|
706
739
|
// Exit block
|
|
707
740
|
this.builder.startBlock(exitLabel);
|
|
708
741
|
}
|
|
@@ -794,12 +827,32 @@ class Lowering {
|
|
|
794
827
|
defaultArm = arm;
|
|
795
828
|
continue;
|
|
796
829
|
}
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
830
|
+
// Handle range patterns specially
|
|
831
|
+
let matchCondition;
|
|
832
|
+
if (arm.pattern.kind === 'range_lit') {
|
|
833
|
+
const range = arm.pattern.range;
|
|
834
|
+
if (range.min !== undefined && range.max !== undefined) {
|
|
835
|
+
matchCondition = `${range.min}..${range.max}`;
|
|
836
|
+
}
|
|
837
|
+
else if (range.min !== undefined) {
|
|
838
|
+
matchCondition = `${range.min}..`;
|
|
839
|
+
}
|
|
840
|
+
else if (range.max !== undefined) {
|
|
841
|
+
matchCondition = `..${range.max}`;
|
|
842
|
+
}
|
|
843
|
+
else {
|
|
844
|
+
matchCondition = '0..'; // Match any
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
else {
|
|
848
|
+
const patternValue = this.lowerExpr(arm.pattern);
|
|
849
|
+
if (patternValue.kind !== 'const') {
|
|
850
|
+
throw new Error('Match patterns must lower to compile-time constants');
|
|
851
|
+
}
|
|
852
|
+
matchCondition = String(patternValue.value);
|
|
800
853
|
}
|
|
801
854
|
const subFnName = `${this.currentFn}/match_${this.foreachCounter++}`;
|
|
802
|
-
this.builder.emitRaw(`execute if score ${matchedVar} rs matches ..0 if score ${subject} rs matches ${
|
|
855
|
+
this.builder.emitRaw(`execute if score ${matchedVar} rs matches ..0 if score ${subject} rs matches ${matchCondition} run function ${this.namespace}:${subFnName}`);
|
|
803
856
|
this.emitMatchArmSubFunction(subFnName, matchedVar, arm.body, true);
|
|
804
857
|
}
|
|
805
858
|
if (defaultArm) {
|
package/dist/optimizer/dce.js
CHANGED
|
@@ -241,6 +241,8 @@ class DeadCodeEliminator {
|
|
|
241
241
|
this.collectNestedStmtRefs(stmt, scope);
|
|
242
242
|
break;
|
|
243
243
|
case 'raw':
|
|
244
|
+
case 'break':
|
|
245
|
+
case 'continue':
|
|
244
246
|
break;
|
|
245
247
|
}
|
|
246
248
|
}
|
|
@@ -508,6 +510,10 @@ class DeadCodeEliminator {
|
|
|
508
510
|
return [copySpan({ ...stmt, body: this.transformBlock(stmt.body, scope) }, stmt)];
|
|
509
511
|
case 'raw':
|
|
510
512
|
return [stmt];
|
|
513
|
+
case 'break':
|
|
514
|
+
return [stmt];
|
|
515
|
+
case 'continue':
|
|
516
|
+
return [stmt];
|
|
511
517
|
}
|
|
512
518
|
}
|
|
513
519
|
transformExpr(expr, scope) {
|
package/dist/parser/index.js
CHANGED
|
@@ -397,6 +397,18 @@ class Parser {
|
|
|
397
397
|
if (this.check('return')) {
|
|
398
398
|
return this.parseReturnStmt();
|
|
399
399
|
}
|
|
400
|
+
// Break statement
|
|
401
|
+
if (this.check('break')) {
|
|
402
|
+
const token = this.advance();
|
|
403
|
+
this.match(';');
|
|
404
|
+
return this.withLoc({ kind: 'break' }, token);
|
|
405
|
+
}
|
|
406
|
+
// Continue statement
|
|
407
|
+
if (this.check('continue')) {
|
|
408
|
+
const token = this.advance();
|
|
409
|
+
this.match(';');
|
|
410
|
+
return this.withLoc({ kind: 'continue' }, token);
|
|
411
|
+
}
|
|
400
412
|
// If statement
|
|
401
413
|
if (this.check('if')) {
|
|
402
414
|
return this.parseIfStmt();
|
package/package.json
CHANGED
package/src/ast/types.ts
CHANGED
|
@@ -189,6 +189,8 @@ export type Stmt =
|
|
|
189
189
|
| { kind: 'let'; name: string; type?: TypeNode; init: Expr; span?: Span }
|
|
190
190
|
| { kind: 'expr'; expr: Expr; span?: Span }
|
|
191
191
|
| { kind: 'return'; value?: Expr; span?: Span }
|
|
192
|
+
| { kind: 'break'; span?: Span }
|
|
193
|
+
| { kind: 'continue'; span?: Span }
|
|
192
194
|
| { kind: 'if'; cond: Expr; then: Block; else_?: Block; span?: Span }
|
|
193
195
|
| { kind: 'while'; cond: Expr; body: Block; span?: Span }
|
|
194
196
|
| { kind: 'for'; init?: Stmt; cond: Expr; step: Expr; body: Block; span?: Span }
|
package/src/index.ts
CHANGED
package/src/lexer/index.ts
CHANGED
|
@@ -15,7 +15,7 @@ import { DiagnosticError } from '../diagnostics'
|
|
|
15
15
|
export type TokenKind =
|
|
16
16
|
// Keywords
|
|
17
17
|
| 'fn' | 'let' | 'const' | 'if' | 'else' | 'while' | 'for' | 'foreach' | 'match'
|
|
18
|
-
| 'return' | 'as' | 'at' | 'in' | 'is' | 'struct' | 'impl' | 'enum' | 'trigger' | 'namespace'
|
|
18
|
+
| 'return' | 'break' | 'continue' | 'as' | 'at' | 'in' | 'is' | 'struct' | 'impl' | 'enum' | 'trigger' | 'namespace'
|
|
19
19
|
| 'execute' | 'run' | 'unless'
|
|
20
20
|
// Types
|
|
21
21
|
| 'int' | 'bool' | 'float' | 'string' | 'void'
|
|
@@ -75,6 +75,8 @@ const KEYWORDS: Record<string, TokenKind> = {
|
|
|
75
75
|
foreach: 'foreach',
|
|
76
76
|
match: 'match',
|
|
77
77
|
return: 'return',
|
|
78
|
+
break: 'break',
|
|
79
|
+
continue: 'continue',
|
|
78
80
|
as: 'as',
|
|
79
81
|
at: 'at',
|
|
80
82
|
in: 'in',
|
package/src/lowering/index.ts
CHANGED
|
@@ -224,6 +224,9 @@ export class Lowering {
|
|
|
224
224
|
// World object counter for unique tags
|
|
225
225
|
private worldObjCounter: number = 0
|
|
226
226
|
|
|
227
|
+
// Loop context stack for break/continue
|
|
228
|
+
private loopStack: Array<{ breakLabel: string; continueLabel: string; stepFn?: () => void }> = []
|
|
229
|
+
|
|
227
230
|
constructor(namespace: string, sourceRanges: SourceRange[] = []) {
|
|
228
231
|
this.namespace = namespace
|
|
229
232
|
this.sourceRanges = sourceRanges
|
|
@@ -530,6 +533,12 @@ export class Lowering {
|
|
|
530
533
|
case 'return':
|
|
531
534
|
this.lowerReturnStmt(stmt)
|
|
532
535
|
break
|
|
536
|
+
case 'break':
|
|
537
|
+
this.lowerBreakStmt()
|
|
538
|
+
break
|
|
539
|
+
case 'continue':
|
|
540
|
+
this.lowerContinueStmt()
|
|
541
|
+
break
|
|
533
542
|
case 'if':
|
|
534
543
|
this.lowerIfStmt(stmt)
|
|
535
544
|
break
|
|
@@ -682,6 +691,22 @@ export class Lowering {
|
|
|
682
691
|
}
|
|
683
692
|
}
|
|
684
693
|
|
|
694
|
+
private lowerBreakStmt(): void {
|
|
695
|
+
if (this.loopStack.length === 0) {
|
|
696
|
+
throw new DiagnosticError('LoweringError', 'break statement outside of loop', { line: 1, col: 1 })
|
|
697
|
+
}
|
|
698
|
+
const loop = this.loopStack[this.loopStack.length - 1]
|
|
699
|
+
this.builder.emitJump(loop.breakLabel)
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
private lowerContinueStmt(): void {
|
|
703
|
+
if (this.loopStack.length === 0) {
|
|
704
|
+
throw new DiagnosticError('LoweringError', 'continue statement outside of loop', { line: 1, col: 1 })
|
|
705
|
+
}
|
|
706
|
+
const loop = this.loopStack[this.loopStack.length - 1]
|
|
707
|
+
this.builder.emitJump(loop.continueLabel)
|
|
708
|
+
}
|
|
709
|
+
|
|
685
710
|
private lowerIfStmt(stmt: Extract<Stmt, { kind: 'if' }>): void {
|
|
686
711
|
if (stmt.cond.kind === 'is_check') {
|
|
687
712
|
this.lowerIsCheckIfStmt(stmt)
|
|
@@ -790,6 +815,9 @@ export class Lowering {
|
|
|
790
815
|
const condName = this.operandToVar(condVar)
|
|
791
816
|
this.builder.emitJumpIf(condName, bodyLabel, exitLabel)
|
|
792
817
|
|
|
818
|
+
// Push loop context for break/continue (while has no step, so continue goes to check)
|
|
819
|
+
this.loopStack.push({ breakLabel: exitLabel, continueLabel: checkLabel })
|
|
820
|
+
|
|
793
821
|
// Body block
|
|
794
822
|
this.builder.startBlock(bodyLabel)
|
|
795
823
|
this.lowerBlock(stmt.body)
|
|
@@ -797,6 +825,9 @@ export class Lowering {
|
|
|
797
825
|
this.builder.emitJump(checkLabel)
|
|
798
826
|
}
|
|
799
827
|
|
|
828
|
+
// Pop loop context
|
|
829
|
+
this.loopStack.pop()
|
|
830
|
+
|
|
800
831
|
// Exit block
|
|
801
832
|
this.builder.startBlock(exitLabel)
|
|
802
833
|
}
|
|
@@ -811,6 +842,7 @@ export class Lowering {
|
|
|
811
842
|
|
|
812
843
|
const checkLabel = this.builder.freshLabel('for_check')
|
|
813
844
|
const bodyLabel = this.builder.freshLabel('for_body')
|
|
845
|
+
const continueLabel = this.builder.freshLabel('for_continue')
|
|
814
846
|
const exitLabel = this.builder.freshLabel('for_exit')
|
|
815
847
|
|
|
816
848
|
this.builder.emitJump(checkLabel)
|
|
@@ -821,15 +853,24 @@ export class Lowering {
|
|
|
821
853
|
const condName = this.operandToVar(condVar)
|
|
822
854
|
this.builder.emitJumpIf(condName, bodyLabel, exitLabel)
|
|
823
855
|
|
|
856
|
+
// Push loop context for break/continue
|
|
857
|
+
this.loopStack.push({ breakLabel: exitLabel, continueLabel })
|
|
858
|
+
|
|
824
859
|
// Body block
|
|
825
860
|
this.builder.startBlock(bodyLabel)
|
|
826
861
|
this.lowerBlock(stmt.body)
|
|
827
|
-
// Step expression
|
|
828
|
-
this.lowerExpr(stmt.step)
|
|
829
862
|
if (!this.builder.isBlockSealed()) {
|
|
830
|
-
this.builder.emitJump(
|
|
863
|
+
this.builder.emitJump(continueLabel)
|
|
831
864
|
}
|
|
832
865
|
|
|
866
|
+
// Continue block (step + loop back)
|
|
867
|
+
this.builder.startBlock(continueLabel)
|
|
868
|
+
this.lowerExpr(stmt.step)
|
|
869
|
+
this.builder.emitJump(checkLabel)
|
|
870
|
+
|
|
871
|
+
// Pop loop context
|
|
872
|
+
this.loopStack.pop()
|
|
873
|
+
|
|
833
874
|
// Exit block
|
|
834
875
|
this.builder.startBlock(exitLabel)
|
|
835
876
|
}
|
|
@@ -945,13 +986,29 @@ export class Lowering {
|
|
|
945
986
|
continue
|
|
946
987
|
}
|
|
947
988
|
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
989
|
+
// Handle range patterns specially
|
|
990
|
+
let matchCondition: string
|
|
991
|
+
if (arm.pattern.kind === 'range_lit') {
|
|
992
|
+
const range = arm.pattern.range
|
|
993
|
+
if (range.min !== undefined && range.max !== undefined) {
|
|
994
|
+
matchCondition = `${range.min}..${range.max}`
|
|
995
|
+
} else if (range.min !== undefined) {
|
|
996
|
+
matchCondition = `${range.min}..`
|
|
997
|
+
} else if (range.max !== undefined) {
|
|
998
|
+
matchCondition = `..${range.max}`
|
|
999
|
+
} else {
|
|
1000
|
+
matchCondition = '0..' // Match any
|
|
1001
|
+
}
|
|
1002
|
+
} else {
|
|
1003
|
+
const patternValue = this.lowerExpr(arm.pattern)
|
|
1004
|
+
if (patternValue.kind !== 'const') {
|
|
1005
|
+
throw new Error('Match patterns must lower to compile-time constants')
|
|
1006
|
+
}
|
|
1007
|
+
matchCondition = String(patternValue.value)
|
|
951
1008
|
}
|
|
952
1009
|
|
|
953
1010
|
const subFnName = `${this.currentFn}/match_${this.foreachCounter++}`
|
|
954
|
-
this.builder.emitRaw(`execute if score ${matchedVar} rs matches ..0 if score ${subject} rs matches ${
|
|
1011
|
+
this.builder.emitRaw(`execute if score ${matchedVar} rs matches ..0 if score ${subject} rs matches ${matchCondition} run function ${this.namespace}:${subFnName}`)
|
|
955
1012
|
this.emitMatchArmSubFunction(subFnName, matchedVar, arm.body, true)
|
|
956
1013
|
}
|
|
957
1014
|
|
package/src/optimizer/dce.ts
CHANGED
|
@@ -267,6 +267,8 @@ export class DeadCodeEliminator {
|
|
|
267
267
|
this.collectNestedStmtRefs(stmt, scope)
|
|
268
268
|
break
|
|
269
269
|
case 'raw':
|
|
270
|
+
case 'break':
|
|
271
|
+
case 'continue':
|
|
270
272
|
break
|
|
271
273
|
}
|
|
272
274
|
}
|
|
@@ -542,6 +544,10 @@ export class DeadCodeEliminator {
|
|
|
542
544
|
return [copySpan({ ...stmt, body: this.transformBlock(stmt.body, scope) }, stmt)]
|
|
543
545
|
case 'raw':
|
|
544
546
|
return [stmt]
|
|
547
|
+
case 'break':
|
|
548
|
+
return [stmt]
|
|
549
|
+
case 'continue':
|
|
550
|
+
return [stmt]
|
|
545
551
|
}
|
|
546
552
|
}
|
|
547
553
|
|
package/src/parser/index.ts
CHANGED
|
@@ -470,6 +470,20 @@ export class Parser {
|
|
|
470
470
|
return this.parseReturnStmt()
|
|
471
471
|
}
|
|
472
472
|
|
|
473
|
+
// Break statement
|
|
474
|
+
if (this.check('break')) {
|
|
475
|
+
const token = this.advance()
|
|
476
|
+
this.match(';')
|
|
477
|
+
return this.withLoc({ kind: 'break' }, token)
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// Continue statement
|
|
481
|
+
if (this.check('continue')) {
|
|
482
|
+
const token = this.advance()
|
|
483
|
+
this.match(';')
|
|
484
|
+
return this.withLoc({ kind: 'continue' }, token)
|
|
485
|
+
}
|
|
486
|
+
|
|
473
487
|
// If statement
|
|
474
488
|
if (this.check('if')) {
|
|
475
489
|
return this.parseIfStmt()
|