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,27 @@
|
|
|
1
|
+
// Automated turret using a spawned armor stand object and a struct value.
|
|
2
|
+
|
|
3
|
+
struct TurretState { health: int }
|
|
4
|
+
|
|
5
|
+
@on_trigger("deploy_turret")
|
|
6
|
+
fn deploy_turret() {
|
|
7
|
+
let turret = spawn_object(0, 64, 0);
|
|
8
|
+
turret.health = 40;
|
|
9
|
+
turret.tag("turret");
|
|
10
|
+
|
|
11
|
+
let state: TurretState = { health: 40 };
|
|
12
|
+
let hp = state.health;
|
|
13
|
+
scoreboard_set("turret", #health, hp);
|
|
14
|
+
|
|
15
|
+
say("Turret deployed.");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@tick(rate=20)
|
|
19
|
+
fn turret_tick() {
|
|
20
|
+
foreach (turret in @e[type=armor_stand, tag=turret]) {
|
|
21
|
+
at @s {
|
|
22
|
+
foreach (z in @e[type=zombie, distance=..8]) {
|
|
23
|
+
kill(z);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// World management helpers for a lobby or event setup.
|
|
2
|
+
// Demonstrates block editing, global rules, and weather/time control.
|
|
3
|
+
|
|
4
|
+
fn reset_lobby_platform() {
|
|
5
|
+
fill((0, 64, 0), (8, 64, 8), "minecraft:smooth_stone");
|
|
6
|
+
fill((1, 65, 1), (7, 68, 7), "minecraft:air");
|
|
7
|
+
setblock((4, 65, 4), "minecraft:gold_block");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
fn configure_world() {
|
|
11
|
+
weather("clear");
|
|
12
|
+
time_set("day");
|
|
13
|
+
gamerule(#doWeatherCycle, "false");
|
|
14
|
+
gamerule(#doDaylightCycle, "false");
|
|
15
|
+
announce("World manager refreshed the lobby.");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@on_trigger("world_reset")
|
|
19
|
+
fn handle_world_reset() {
|
|
20
|
+
reset_lobby_platform();
|
|
21
|
+
configure_world();
|
|
22
|
+
actionbar(@a, "Lobby reset complete");
|
|
23
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export function format(source: string): string {
|
|
2
|
+
const lines = source.split("\n")
|
|
3
|
+
let indent = 0
|
|
4
|
+
const result: string[] = []
|
|
5
|
+
|
|
6
|
+
for (let line of lines) {
|
|
7
|
+
line = line.trim()
|
|
8
|
+
if (!line) { result.push(""); continue }
|
|
9
|
+
|
|
10
|
+
// Decrease indent before }
|
|
11
|
+
if (line.startsWith("}")) indent = Math.max(0, indent - 1)
|
|
12
|
+
|
|
13
|
+
// Add indentation
|
|
14
|
+
result.push(" ".repeat(indent) + line)
|
|
15
|
+
|
|
16
|
+
// Increase indent after {
|
|
17
|
+
if (line.endsWith("{")) indent++
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Ensure single newline at end
|
|
21
|
+
return result.join("\n").trimEnd() + "\n"
|
|
22
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RedScript Compiler
|
|
3
|
+
*
|
|
4
|
+
* Main entry point for programmatic usage.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Lexer } from './lexer'
|
|
8
|
+
import { Parser } from './parser'
|
|
9
|
+
import { TypeChecker } from './typechecker'
|
|
10
|
+
import { Lowering } from './lowering'
|
|
11
|
+
import type { Warning } from './lowering'
|
|
12
|
+
import {
|
|
13
|
+
constantFoldingWithStats,
|
|
14
|
+
copyPropagation,
|
|
15
|
+
deadCodeEliminationWithStats,
|
|
16
|
+
} from './optimizer/passes'
|
|
17
|
+
import {
|
|
18
|
+
countMcfunctionCommands,
|
|
19
|
+
generateDatapackWithStats,
|
|
20
|
+
DatapackFile,
|
|
21
|
+
} from './codegen/mcfunction'
|
|
22
|
+
import { preprocessSource } from './compile'
|
|
23
|
+
import type { IRModule } from './ir/types'
|
|
24
|
+
import type { Program } from './ast/types'
|
|
25
|
+
import type { DiagnosticError } from './diagnostics'
|
|
26
|
+
import { createEmptyOptimizationStats, type OptimizationStats } from './optimizer/commands'
|
|
27
|
+
|
|
28
|
+
export interface CompileOptions {
|
|
29
|
+
namespace?: string
|
|
30
|
+
optimize?: boolean
|
|
31
|
+
typeCheck?: boolean
|
|
32
|
+
filePath?: string
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface CompileResult {
|
|
36
|
+
files: DatapackFile[]
|
|
37
|
+
advancements: DatapackFile[]
|
|
38
|
+
ast: Program
|
|
39
|
+
ir: IRModule
|
|
40
|
+
typeErrors?: DiagnosticError[]
|
|
41
|
+
warnings?: Warning[]
|
|
42
|
+
stats?: OptimizationStats
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Compile RedScript source code to a Minecraft datapack.
|
|
47
|
+
*
|
|
48
|
+
* @param source - The RedScript source code
|
|
49
|
+
* @param options - Compilation options
|
|
50
|
+
* @returns Compiled datapack files
|
|
51
|
+
*/
|
|
52
|
+
export function compile(source: string, options: CompileOptions = {}): CompileResult {
|
|
53
|
+
const namespace = options.namespace ?? 'redscript'
|
|
54
|
+
const shouldOptimize = options.optimize ?? true
|
|
55
|
+
const shouldTypeCheck = options.typeCheck ?? true
|
|
56
|
+
const filePath = options.filePath
|
|
57
|
+
const preprocessedSource = preprocessSource(source, { filePath })
|
|
58
|
+
|
|
59
|
+
// Lexing
|
|
60
|
+
const tokens = new Lexer(preprocessedSource, filePath).tokenize()
|
|
61
|
+
|
|
62
|
+
// Parsing
|
|
63
|
+
const ast = new Parser(tokens, preprocessedSource, filePath).parse(namespace)
|
|
64
|
+
|
|
65
|
+
// Type checking (warn mode - collect errors but don't block)
|
|
66
|
+
let typeErrors: DiagnosticError[] | undefined
|
|
67
|
+
if (shouldTypeCheck) {
|
|
68
|
+
const checker = new TypeChecker(preprocessedSource, filePath)
|
|
69
|
+
typeErrors = checker.check(ast)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Lowering to IR
|
|
73
|
+
const lowering = new Lowering(namespace)
|
|
74
|
+
const ir = lowering.lower(ast)
|
|
75
|
+
|
|
76
|
+
let optimizedIR: IRModule = ir
|
|
77
|
+
let generated = generateDatapackWithStats(ir, { optimizeCommands: shouldOptimize })
|
|
78
|
+
let optimizationStats: OptimizationStats | undefined
|
|
79
|
+
|
|
80
|
+
if (shouldOptimize) {
|
|
81
|
+
const stats = createEmptyOptimizationStats()
|
|
82
|
+
const copyPropagatedFunctions = []
|
|
83
|
+
const deadCodeEliminatedFunctions = []
|
|
84
|
+
|
|
85
|
+
for (const fn of ir.functions) {
|
|
86
|
+
const folded = constantFoldingWithStats(fn)
|
|
87
|
+
stats.constantFolds += folded.stats.constantFolds ?? 0
|
|
88
|
+
|
|
89
|
+
const propagated = copyPropagation(folded.fn)
|
|
90
|
+
copyPropagatedFunctions.push(propagated)
|
|
91
|
+
|
|
92
|
+
const dce = deadCodeEliminationWithStats(propagated)
|
|
93
|
+
deadCodeEliminatedFunctions.push(dce.fn)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const copyPropagatedIR: IRModule = { ...ir, functions: copyPropagatedFunctions }
|
|
97
|
+
optimizedIR = { ...ir, functions: deadCodeEliminatedFunctions }
|
|
98
|
+
|
|
99
|
+
const baselineGenerated = generateDatapackWithStats(ir, { optimizeCommands: false })
|
|
100
|
+
const beforeDceGenerated = generateDatapackWithStats(copyPropagatedIR, { optimizeCommands: false })
|
|
101
|
+
const afterDceGenerated = generateDatapackWithStats(optimizedIR, { optimizeCommands: false })
|
|
102
|
+
generated = generateDatapackWithStats(optimizedIR, { optimizeCommands: true })
|
|
103
|
+
|
|
104
|
+
stats.deadCodeRemoved =
|
|
105
|
+
countMcfunctionCommands(beforeDceGenerated.files) - countMcfunctionCommands(afterDceGenerated.files)
|
|
106
|
+
stats.licmHoists = generated.stats.licmHoists
|
|
107
|
+
stats.licmLoopBodies = generated.stats.licmLoopBodies
|
|
108
|
+
stats.cseRedundantReads = generated.stats.cseRedundantReads
|
|
109
|
+
stats.cseArithmetic = generated.stats.cseArithmetic
|
|
110
|
+
stats.setblockMergedCommands = generated.stats.setblockMergedCommands
|
|
111
|
+
stats.setblockFillCommands = generated.stats.setblockFillCommands
|
|
112
|
+
stats.setblockSavedCommands = generated.stats.setblockSavedCommands
|
|
113
|
+
stats.totalCommandsBefore = countMcfunctionCommands(baselineGenerated.files)
|
|
114
|
+
stats.totalCommandsAfter = countMcfunctionCommands(generated.files)
|
|
115
|
+
optimizationStats = stats
|
|
116
|
+
} else {
|
|
117
|
+
optimizedIR = ir
|
|
118
|
+
generated = generateDatapackWithStats(ir, { optimizeCommands: false })
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
files: [...generated.files, ...generated.advancements],
|
|
123
|
+
advancements: generated.advancements,
|
|
124
|
+
ast,
|
|
125
|
+
ir: optimizedIR,
|
|
126
|
+
typeErrors,
|
|
127
|
+
warnings: lowering.warnings,
|
|
128
|
+
stats: optimizationStats,
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Check RedScript source code for errors without generating output.
|
|
134
|
+
*
|
|
135
|
+
* @param source - The RedScript source code
|
|
136
|
+
* @param namespace - Optional namespace
|
|
137
|
+
* @returns null if no errors, or an error object
|
|
138
|
+
*/
|
|
139
|
+
export function check(source: string, namespace = 'redscript', filePath?: string): Error | null {
|
|
140
|
+
try {
|
|
141
|
+
const preprocessedSource = preprocessSource(source, { filePath })
|
|
142
|
+
const tokens = new Lexer(preprocessedSource, filePath).tokenize()
|
|
143
|
+
new Parser(tokens, preprocessedSource, filePath).parse(namespace)
|
|
144
|
+
return null
|
|
145
|
+
} catch (err) {
|
|
146
|
+
return err as Error
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Re-export types and classes for advanced usage
|
|
151
|
+
export { Lexer } from './lexer'
|
|
152
|
+
export { Parser } from './parser'
|
|
153
|
+
export { TypeChecker } from './typechecker'
|
|
154
|
+
export { Lowering } from './lowering'
|
|
155
|
+
export { optimize } from './optimizer/passes'
|
|
156
|
+
export { generateDatapack } from './codegen/mcfunction'
|
|
157
|
+
export { MCCommandValidator } from './mc-validator'
|
|
158
|
+
export type { DatapackFile } from './codegen/mcfunction'
|
|
159
|
+
export type { IRModule, IRFunction } from './ir/types'
|
|
160
|
+
export type { Program, FnDecl, Expr, Stmt, Span } from './ast/types'
|
|
161
|
+
export type { DiagnosticError } from './diagnostics'
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IRBuilder — helper for constructing IR programmatically.
|
|
3
|
+
* AST → IR lowering uses this.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { IRBlock, IRFunction, IRInstr, IRModule, Operand, Terminator } from './types'
|
|
7
|
+
|
|
8
|
+
export class IRBuilder {
|
|
9
|
+
private tempCount = 0
|
|
10
|
+
private labelCount = 0
|
|
11
|
+
private currentBlock: IRBlock | null = null
|
|
12
|
+
private blocks: IRBlock[] = []
|
|
13
|
+
private locals = new Set<string>()
|
|
14
|
+
|
|
15
|
+
// -------------------------------------------------------------------------
|
|
16
|
+
// Names
|
|
17
|
+
// -------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
freshTemp(): string {
|
|
20
|
+
const name = `$t${this.tempCount++}`
|
|
21
|
+
this.locals.add(name)
|
|
22
|
+
return name
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
freshLabel(hint = 'L'): string {
|
|
26
|
+
return `${hint}_${this.labelCount++}`
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// -------------------------------------------------------------------------
|
|
30
|
+
// Block management
|
|
31
|
+
// -------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
startBlock(label: string): void {
|
|
34
|
+
this.currentBlock = { label, instrs: [], term: { op: 'return' } }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private get block(): IRBlock {
|
|
38
|
+
if (!this.currentBlock) throw new Error('No active block')
|
|
39
|
+
return this.currentBlock
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private sealBlock(term: Terminator): void {
|
|
43
|
+
this.block.term = term
|
|
44
|
+
this.blocks.push(this.block)
|
|
45
|
+
this.currentBlock = null
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// -------------------------------------------------------------------------
|
|
49
|
+
// Emit instructions
|
|
50
|
+
// -------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
emitAssign(dst: string, src: Operand): void {
|
|
53
|
+
this.locals.add(dst)
|
|
54
|
+
this.block.instrs.push({ op: 'assign', dst, src })
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
emitBinop(dst: string, lhs: Operand, bop: IRInstr & { op: 'binop' } extends { bop: infer B } ? B : never, rhs: Operand): void
|
|
58
|
+
emitBinop(dst: string, lhs: Operand, bop: '+' | '-' | '*' | '/' | '%', rhs: Operand): void {
|
|
59
|
+
this.locals.add(dst)
|
|
60
|
+
this.block.instrs.push({ op: 'binop', dst, lhs, bop, rhs })
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
emitCmp(dst: string, lhs: Operand, cop: '==' | '!=' | '<' | '<=' | '>' | '>=', rhs: Operand): void {
|
|
64
|
+
this.locals.add(dst)
|
|
65
|
+
this.block.instrs.push({ op: 'cmp', dst, lhs, cop, rhs })
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
emitCall(fn: string, args: Operand[], dst?: string): void {
|
|
69
|
+
if (dst) this.locals.add(dst)
|
|
70
|
+
this.block.instrs.push({ op: 'call', fn, args, dst })
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
emitRaw(cmd: string): void {
|
|
74
|
+
this.block.instrs.push({ op: 'raw', cmd })
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// -------------------------------------------------------------------------
|
|
78
|
+
// Terminators
|
|
79
|
+
// -------------------------------------------------------------------------
|
|
80
|
+
|
|
81
|
+
emitJump(target: string): void {
|
|
82
|
+
this.sealBlock({ op: 'jump', target })
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
emitJumpIf(cond: string, then: string, else_: string): void {
|
|
86
|
+
this.sealBlock({ op: 'jump_if', cond, then, else_ })
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
emitReturn(value?: Operand): void {
|
|
90
|
+
this.sealBlock({ op: 'return', value })
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
emitTickYield(continuation: string): void {
|
|
94
|
+
this.sealBlock({ op: 'tick_yield', continuation })
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// -------------------------------------------------------------------------
|
|
98
|
+
// Build
|
|
99
|
+
// -------------------------------------------------------------------------
|
|
100
|
+
|
|
101
|
+
build(name: string, params: string[], isTickLoop = false): IRFunction {
|
|
102
|
+
return {
|
|
103
|
+
name,
|
|
104
|
+
params,
|
|
105
|
+
locals: Array.from(this.locals),
|
|
106
|
+
blocks: this.blocks,
|
|
107
|
+
isTickLoop,
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function buildModule(namespace: string, fns: IRFunction[], globals: string[] = []): IRModule {
|
|
113
|
+
return { namespace, functions: fns, globals }
|
|
114
|
+
}
|
package/src/ir/types.ts
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RedScript IR — Three-Address Code (TAC)
|
|
3
|
+
*
|
|
4
|
+
* Compilation pipeline:
|
|
5
|
+
* Source → AST → IR → (optimize) → CodeGen → mcfunction / cmdblock
|
|
6
|
+
*
|
|
7
|
+
* Variable storage in MC Java Edition:
|
|
8
|
+
* - Integer vars → scoreboard fake player ($name on objective "rs_vars")
|
|
9
|
+
* - Complex data → NBT storage (redscript:stack / redscript:heap)
|
|
10
|
+
* - Return value → fake player $ret
|
|
11
|
+
* - Temporaries → $t0, $t1, ...
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Operands
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
export type Operand =
|
|
19
|
+
| { kind: 'var'; name: string } // scoreboard fake player
|
|
20
|
+
| { kind: 'const'; value: number } // integer literal
|
|
21
|
+
| { kind: 'storage'; path: string } // NBT storage path (e.g. "redscript:heap data.x")
|
|
22
|
+
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Binary operators (all map to `scoreboard players operation`)
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
export type BinOp = '+' | '-' | '*' | '/' | '%'
|
|
28
|
+
export type CmpOp = '==' | '!=' | '<' | '<=' | '>' | '>='
|
|
29
|
+
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// IR Instructions
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
export type IRInstr =
|
|
35
|
+
// x = src
|
|
36
|
+
| { op: 'assign'; dst: string; src: Operand }
|
|
37
|
+
|
|
38
|
+
// dst = lhs bop rhs
|
|
39
|
+
| { op: 'binop'; dst: string; lhs: Operand; bop: BinOp; rhs: Operand }
|
|
40
|
+
|
|
41
|
+
// dst = (lhs cop rhs) ? 1 : 0
|
|
42
|
+
| { op: 'cmp'; dst: string; lhs: Operand; cop: CmpOp; rhs: Operand }
|
|
43
|
+
|
|
44
|
+
// goto label
|
|
45
|
+
| { op: 'jump'; target: string }
|
|
46
|
+
|
|
47
|
+
// if cond != 0 goto target
|
|
48
|
+
| { op: 'jump_if'; cond: string; target: string }
|
|
49
|
+
|
|
50
|
+
// if cond == 0 goto target
|
|
51
|
+
| { op: 'jump_unless'; cond: string; target: string }
|
|
52
|
+
|
|
53
|
+
// dst = fn(args)
|
|
54
|
+
| { op: 'call'; fn: string; args: Operand[]; dst?: string }
|
|
55
|
+
|
|
56
|
+
// return value (optional)
|
|
57
|
+
| { op: 'return'; value?: Operand }
|
|
58
|
+
|
|
59
|
+
// label declaration (block entry point)
|
|
60
|
+
| { op: 'label'; id: string }
|
|
61
|
+
|
|
62
|
+
// raw MC command passthrough (escape hatch)
|
|
63
|
+
| { op: 'raw'; cmd: string }
|
|
64
|
+
|
|
65
|
+
// wait one game tick (command block target only)
|
|
66
|
+
// maps to: schedule function <continuation> 1t replace
|
|
67
|
+
| { op: 'tick_yield' }
|
|
68
|
+
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
// Basic Block — straight-line code, ends with a terminator
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
export type Terminator =
|
|
74
|
+
| { op: 'jump'; target: string }
|
|
75
|
+
| { op: 'jump_if'; cond: string; then: string; else_: string }
|
|
76
|
+
| { op: 'jump_unless'; cond: string; then: string; else_: string }
|
|
77
|
+
| { op: 'return'; value?: Operand }
|
|
78
|
+
| { op: 'tick_yield'; continuation: string }
|
|
79
|
+
|
|
80
|
+
export interface IRBlock {
|
|
81
|
+
label: string
|
|
82
|
+
instrs: IRInstr[] // non-terminator instructions
|
|
83
|
+
term: Terminator
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface IRCommand {
|
|
87
|
+
cmd: string
|
|
88
|
+
conditional?: boolean
|
|
89
|
+
label?: string
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
// Function
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
|
|
96
|
+
export interface IRFunction {
|
|
97
|
+
name: string
|
|
98
|
+
params: string[] // parameter names (passed via fake players)
|
|
99
|
+
locals: string[] // all local variable names
|
|
100
|
+
blocks: IRBlock[] // blocks[0] = entry block
|
|
101
|
+
commands?: IRCommand[] // structure target command stream
|
|
102
|
+
isTickLoop?: boolean // true → Repeat command block (runs every tick)
|
|
103
|
+
isTriggerHandler?: boolean // true → handles a trigger event
|
|
104
|
+
triggerName?: string // the trigger objective name
|
|
105
|
+
eventTrigger?: {
|
|
106
|
+
kind: 'advancement' | 'craft' | 'death' | 'login' | 'join_team'
|
|
107
|
+
value?: string
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
// Module — top-level compilation unit
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
|
|
115
|
+
export interface IRModule {
|
|
116
|
+
namespace: string // datapack namespace (e.g. "mypack")
|
|
117
|
+
functions: IRFunction[]
|
|
118
|
+
globals: string[] // global variable names
|
|
119
|
+
}
|