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,344 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.optimizeFunctionForStructure = optimizeFunctionForStructure;
|
|
4
|
+
exports.optimizeForStructure = optimizeForStructure;
|
|
5
|
+
exports.optimizeForStructureWithStats = optimizeForStructureWithStats;
|
|
6
|
+
const commands_1 = require("./commands");
|
|
7
|
+
const OBJ = 'rs';
|
|
8
|
+
const INLINE_THRESHOLD = 8;
|
|
9
|
+
const BOP_OP = {
|
|
10
|
+
'+': '+=',
|
|
11
|
+
'-': '-=',
|
|
12
|
+
'*': '*=',
|
|
13
|
+
'/': '/=',
|
|
14
|
+
'%': '%=',
|
|
15
|
+
};
|
|
16
|
+
function varRef(name) {
|
|
17
|
+
return name.startsWith('$') ? name : `$${name}`;
|
|
18
|
+
}
|
|
19
|
+
function operandToScore(op) {
|
|
20
|
+
if (op.kind === 'var')
|
|
21
|
+
return `${varRef(op.name)} ${OBJ}`;
|
|
22
|
+
if (op.kind === 'const')
|
|
23
|
+
return `$const_${op.value} ${OBJ}`;
|
|
24
|
+
throw new Error(`Cannot convert storage operand to score: ${op.path}`);
|
|
25
|
+
}
|
|
26
|
+
function emitInstr(instr, namespace) {
|
|
27
|
+
const commands = [];
|
|
28
|
+
switch (instr.op) {
|
|
29
|
+
case 'assign':
|
|
30
|
+
if (instr.src.kind === 'const') {
|
|
31
|
+
commands.push({ cmd: `scoreboard players set ${varRef(instr.dst)} ${OBJ} ${instr.src.value}` });
|
|
32
|
+
}
|
|
33
|
+
else if (instr.src.kind === 'var') {
|
|
34
|
+
commands.push({
|
|
35
|
+
cmd: `scoreboard players operation ${varRef(instr.dst)} ${OBJ} = ${varRef(instr.src.name)} ${OBJ}`,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
commands.push({
|
|
40
|
+
cmd: `execute store result score ${varRef(instr.dst)} ${OBJ} run data get storage ${instr.src.path}`,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
break;
|
|
44
|
+
case 'binop':
|
|
45
|
+
commands.push(...emitInstr({ op: 'assign', dst: instr.dst, src: instr.lhs }, namespace));
|
|
46
|
+
commands.push({
|
|
47
|
+
cmd: `scoreboard players operation ${varRef(instr.dst)} ${OBJ} ${BOP_OP[instr.bop]} ${operandToScore(instr.rhs)}`,
|
|
48
|
+
});
|
|
49
|
+
break;
|
|
50
|
+
case 'cmp': {
|
|
51
|
+
const dst = varRef(instr.dst);
|
|
52
|
+
const lhs = operandToScore(instr.lhs);
|
|
53
|
+
const rhs = operandToScore(instr.rhs);
|
|
54
|
+
commands.push({ cmd: `scoreboard players set ${dst} ${OBJ} 0` });
|
|
55
|
+
const op = instr.cop === '==' ? 'if score' :
|
|
56
|
+
instr.cop === '!=' ? 'unless score' :
|
|
57
|
+
instr.cop === '<' ? 'if score' :
|
|
58
|
+
instr.cop === '<=' ? 'if score' :
|
|
59
|
+
instr.cop === '>' ? 'if score' :
|
|
60
|
+
'if score';
|
|
61
|
+
const cmp = instr.cop === '==' || instr.cop === '!=' ? '=' :
|
|
62
|
+
instr.cop;
|
|
63
|
+
commands.push({
|
|
64
|
+
cmd: `execute ${op} ${lhs} ${cmp} ${rhs} run scoreboard players set ${dst} ${OBJ} 1`,
|
|
65
|
+
});
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
case 'call':
|
|
69
|
+
for (let i = 0; i < instr.args.length; i++) {
|
|
70
|
+
commands.push(...emitInstr({ op: 'assign', dst: `$p${i}`, src: instr.args[i] }, namespace));
|
|
71
|
+
}
|
|
72
|
+
commands.push({ cmd: `function ${namespace}:${instr.fn}` });
|
|
73
|
+
if (instr.dst) {
|
|
74
|
+
commands.push({
|
|
75
|
+
cmd: `scoreboard players operation ${varRef(instr.dst)} ${OBJ} = $ret ${OBJ}`,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
break;
|
|
79
|
+
case 'raw':
|
|
80
|
+
commands.push({ cmd: instr.cmd });
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
return commands;
|
|
84
|
+
}
|
|
85
|
+
function emitReturn(term) {
|
|
86
|
+
const commands = [];
|
|
87
|
+
if (term.value) {
|
|
88
|
+
commands.push(...emitInstr({ op: 'assign', dst: '$ret', src: term.value }, ''));
|
|
89
|
+
}
|
|
90
|
+
if (term.value?.kind === 'const') {
|
|
91
|
+
commands.push({ cmd: `return ${term.value.value}` });
|
|
92
|
+
}
|
|
93
|
+
else if (term.value?.kind === 'var') {
|
|
94
|
+
commands.push({ cmd: `return run scoreboard players get ${varRef(term.value.name)} ${OBJ}` });
|
|
95
|
+
}
|
|
96
|
+
return commands;
|
|
97
|
+
}
|
|
98
|
+
function markConditional(commands) {
|
|
99
|
+
return commands.map(command => ({
|
|
100
|
+
...command,
|
|
101
|
+
conditional: true,
|
|
102
|
+
}));
|
|
103
|
+
}
|
|
104
|
+
function cloneVisited(visited) {
|
|
105
|
+
return new Set(visited);
|
|
106
|
+
}
|
|
107
|
+
function isRecursiveCommand(command, currentFn, namespace) {
|
|
108
|
+
return command.includes(`function ${namespace}:${currentFn}`);
|
|
109
|
+
}
|
|
110
|
+
function getInlineableBlock(block, currentFn, namespace) {
|
|
111
|
+
if (!block)
|
|
112
|
+
return null;
|
|
113
|
+
if (block.term.op === 'jump_if' || block.term.op === 'jump_unless' || block.term.op === 'tick_yield') {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
const commands = block.instrs.flatMap(instr => emitInstr(instr, namespace));
|
|
117
|
+
if (commands.some(command => isRecursiveCommand(command.cmd, currentFn, namespace))) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
if (block.term.op === 'return') {
|
|
121
|
+
commands.push(...emitReturn(block.term));
|
|
122
|
+
}
|
|
123
|
+
if (commands.length > INLINE_THRESHOLD) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
commands,
|
|
128
|
+
continuation: block.term.op === 'jump' ? block.term.target : undefined,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function flattenBlock(fn, label, namespace, visited) {
|
|
132
|
+
const blockMap = new Map(fn.blocks.map(block => [block.label, block]));
|
|
133
|
+
const block = blockMap.get(label);
|
|
134
|
+
if (!block) {
|
|
135
|
+
return [];
|
|
136
|
+
}
|
|
137
|
+
if (visited.has(label)) {
|
|
138
|
+
return [{ cmd: `function ${namespace}:${fn.name}/${label}`, label }];
|
|
139
|
+
}
|
|
140
|
+
visited.add(label);
|
|
141
|
+
const commands = [];
|
|
142
|
+
if (label === fn.blocks[0]?.label) {
|
|
143
|
+
for (let i = 0; i < fn.params.length; i++) {
|
|
144
|
+
commands.push({
|
|
145
|
+
cmd: `scoreboard players operation ${varRef(fn.params[i])} ${OBJ} = $p${i} ${OBJ}`,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
commands.push(...block.instrs.flatMap(instr => emitInstr(instr, namespace)));
|
|
150
|
+
const term = block.term;
|
|
151
|
+
switch (term.op) {
|
|
152
|
+
case 'jump':
|
|
153
|
+
commands.push(...flattenBlock(fn, term.target, namespace, visited));
|
|
154
|
+
return commands;
|
|
155
|
+
case 'jump_if':
|
|
156
|
+
case 'jump_unless': {
|
|
157
|
+
const trueLabel = term.op === 'jump_if' ? term.then : term.else_;
|
|
158
|
+
const falseLabel = term.op === 'jump_if' ? term.else_ : term.then;
|
|
159
|
+
const trueRange = term.op === 'jump_if' ? '1..' : '..0';
|
|
160
|
+
const falseRange = term.op === 'jump_if' ? '..0' : '1..';
|
|
161
|
+
const trueBlock = getInlineableBlock(blockMap.get(trueLabel), fn.name, namespace);
|
|
162
|
+
const falseBlock = getInlineableBlock(blockMap.get(falseLabel), fn.name, namespace);
|
|
163
|
+
if (trueBlock && falseBlock) {
|
|
164
|
+
if (trueBlock.commands.length > 0) {
|
|
165
|
+
commands.push({ cmd: `execute if score ${varRef(term.cond)} ${OBJ} matches ${trueRange}`, label: trueLabel });
|
|
166
|
+
commands.push(...markConditional(trueBlock.commands));
|
|
167
|
+
}
|
|
168
|
+
if (falseBlock.commands.length > 0) {
|
|
169
|
+
commands.push({ cmd: `execute if score ${varRef(term.cond)} ${OBJ} matches ${falseRange}`, label: falseLabel });
|
|
170
|
+
commands.push(...markConditional(falseBlock.commands));
|
|
171
|
+
}
|
|
172
|
+
const continuation = trueBlock.continuation && trueBlock.continuation === falseBlock.continuation
|
|
173
|
+
? trueBlock.continuation
|
|
174
|
+
: undefined;
|
|
175
|
+
if (continuation) {
|
|
176
|
+
commands.push(...flattenBlock(fn, continuation, namespace, cloneVisited(visited)));
|
|
177
|
+
}
|
|
178
|
+
return commands;
|
|
179
|
+
}
|
|
180
|
+
commands.push({ cmd: `execute if score ${varRef(term.cond)} ${OBJ} matches ${trueRange} run function ${namespace}:${fn.name}/${trueLabel}` });
|
|
181
|
+
commands.push({ cmd: `execute if score ${varRef(term.cond)} ${OBJ} matches ${falseRange} run function ${namespace}:${fn.name}/${falseLabel}` });
|
|
182
|
+
return commands;
|
|
183
|
+
}
|
|
184
|
+
case 'return':
|
|
185
|
+
commands.push(...emitReturn(term));
|
|
186
|
+
return commands;
|
|
187
|
+
case 'tick_yield':
|
|
188
|
+
commands.push({ cmd: `schedule function ${namespace}:${fn.name}/${term.continuation} 1t replace` });
|
|
189
|
+
return commands;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function findVars(command) {
|
|
193
|
+
return Array.from(command.matchAll(/\$[A-Za-z0-9_]+/g), match => match[0]);
|
|
194
|
+
}
|
|
195
|
+
function parsePureWrite(command) {
|
|
196
|
+
let match = command.match(/^scoreboard players set (\$[A-Za-z0-9_]+) rs -?\d+$/);
|
|
197
|
+
if (match) {
|
|
198
|
+
return { dst: match[1], reads: [] };
|
|
199
|
+
}
|
|
200
|
+
match = command.match(/^scoreboard players operation (\$[A-Za-z0-9_]+) rs = (\$[A-Za-z0-9_]+) rs$/);
|
|
201
|
+
if (match) {
|
|
202
|
+
return { dst: match[1], reads: [match[2]] };
|
|
203
|
+
}
|
|
204
|
+
match = command.match(/^execute .* run scoreboard players set (\$[A-Za-z0-9_]+) rs -?\d+$/);
|
|
205
|
+
if (match) {
|
|
206
|
+
return {
|
|
207
|
+
dst: match[1],
|
|
208
|
+
reads: findVars(command).filter(name => name !== match[1]),
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
function deadStoreEliminate(commands) {
|
|
214
|
+
const live = new Set();
|
|
215
|
+
const kept = [];
|
|
216
|
+
for (let i = commands.length - 1; i >= 0; i--) {
|
|
217
|
+
const command = commands[i];
|
|
218
|
+
const pureWrite = parsePureWrite(command.cmd);
|
|
219
|
+
if (pureWrite) {
|
|
220
|
+
pureWrite.reads.forEach(name => live.add(name));
|
|
221
|
+
if (!live.has(pureWrite.dst)) {
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
live.delete(pureWrite.dst);
|
|
225
|
+
kept.push(command);
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
findVars(command.cmd).forEach(name => live.add(name));
|
|
229
|
+
kept.push(command);
|
|
230
|
+
}
|
|
231
|
+
return kept.reverse();
|
|
232
|
+
}
|
|
233
|
+
function isInlineableFunction(fn, currentFn, namespace) {
|
|
234
|
+
if (!fn?.commands || fn.name === currentFn || fn.commands.length > INLINE_THRESHOLD) {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
return !fn.commands.some(command => isRecursiveCommand(command.cmd, currentFn, namespace) ||
|
|
238
|
+
isRecursiveCommand(command.cmd, fn.name, namespace));
|
|
239
|
+
}
|
|
240
|
+
function inlineConditionalCalls(commands, functions, currentFn, namespace) {
|
|
241
|
+
const optimized = [];
|
|
242
|
+
for (const command of commands) {
|
|
243
|
+
const match = command.cmd.match(/^(execute .+) run function ([^:]+):(.+)$/);
|
|
244
|
+
if (!match || match[2] !== namespace) {
|
|
245
|
+
optimized.push(command);
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
const target = functions.get(match[3]);
|
|
249
|
+
if (!isInlineableFunction(target, currentFn, namespace)) {
|
|
250
|
+
optimized.push(command);
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
optimized.push({ cmd: match[1], label: command.label });
|
|
254
|
+
optimized.push(...markConditional(target.commands));
|
|
255
|
+
}
|
|
256
|
+
return optimized;
|
|
257
|
+
}
|
|
258
|
+
function invertExecuteCondition(command) {
|
|
259
|
+
if (command.startsWith('execute if ')) {
|
|
260
|
+
return command.replace(/^execute if /, 'execute unless ');
|
|
261
|
+
}
|
|
262
|
+
if (command.startsWith('execute unless ')) {
|
|
263
|
+
return command.replace(/^execute unless /, 'execute if ');
|
|
264
|
+
}
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
function eliminateBranchVariables(commands, functions, currentFn, namespace) {
|
|
268
|
+
const optimized = [];
|
|
269
|
+
for (let i = 0; i < commands.length; i++) {
|
|
270
|
+
const init = commands[i];
|
|
271
|
+
const set = commands[i + 1];
|
|
272
|
+
const thenCmd = commands[i + 2];
|
|
273
|
+
const elseCmd = commands[i + 3];
|
|
274
|
+
const initMatch = init?.cmd.match(/^scoreboard players set (\$[A-Za-z0-9_]+) rs 0$/);
|
|
275
|
+
const setMatch = set?.cmd.match(/^((?:execute if|execute unless) .+) run scoreboard players set (\$[A-Za-z0-9_]+) rs 1$/);
|
|
276
|
+
const thenMatch = thenCmd?.cmd.match(/^execute if score (\$[A-Za-z0-9_]+) rs matches 1\.\. run function [^:]+:(.+)$/);
|
|
277
|
+
const elseMatch = elseCmd?.cmd.match(/^execute if score (\$[A-Za-z0-9_]+) rs matches ..0 run function [^:]+:(.+)$/) ??
|
|
278
|
+
elseCmd?.cmd.match(/^execute unless score (\$[A-Za-z0-9_]+) rs matches 1\.\. run function [^:]+:(.+)$/);
|
|
279
|
+
if (!initMatch || !setMatch || !thenMatch || !elseMatch) {
|
|
280
|
+
optimized.push(init);
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
const branchVar = initMatch[1];
|
|
284
|
+
if (setMatch[2] !== branchVar || thenMatch[1] !== branchVar || elseMatch[1] !== branchVar) {
|
|
285
|
+
optimized.push(init);
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
const thenFn = functions.get(thenMatch[2]);
|
|
289
|
+
const elseFn = functions.get(elseMatch[2]);
|
|
290
|
+
if (!isInlineableFunction(thenFn, currentFn, namespace) || !isInlineableFunction(elseFn, currentFn, namespace)) {
|
|
291
|
+
optimized.push(init);
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
const thenCondition = setMatch[1];
|
|
295
|
+
const elseCondition = invertExecuteCondition(thenCondition);
|
|
296
|
+
if (!elseCondition) {
|
|
297
|
+
optimized.push(init);
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
optimized.push({ cmd: thenCondition });
|
|
301
|
+
optimized.push(...markConditional(thenFn.commands));
|
|
302
|
+
if (elseFn.commands.length > 0) {
|
|
303
|
+
optimized.push({ cmd: elseCondition });
|
|
304
|
+
optimized.push(...markConditional(elseFn.commands));
|
|
305
|
+
}
|
|
306
|
+
i += 3;
|
|
307
|
+
}
|
|
308
|
+
return optimized;
|
|
309
|
+
}
|
|
310
|
+
function optimizeFunctionForStructure(fn, functions, namespace) {
|
|
311
|
+
if (fn.blocks.length === 0) {
|
|
312
|
+
return [];
|
|
313
|
+
}
|
|
314
|
+
const linear = flattenBlock(fn, fn.blocks[0].label, namespace, new Set());
|
|
315
|
+
const branchEliminated = eliminateBranchVariables(linear, functions, fn.name, namespace);
|
|
316
|
+
const inlined = inlineConditionalCalls(branchEliminated, functions, fn.name, namespace);
|
|
317
|
+
return deadStoreEliminate(inlined);
|
|
318
|
+
}
|
|
319
|
+
function optimizeForStructure(functions, namespace = 'redscript') {
|
|
320
|
+
return optimizeForStructureWithStats(functions, namespace).functions;
|
|
321
|
+
}
|
|
322
|
+
function optimizeForStructureWithStats(functions, namespace = 'redscript') {
|
|
323
|
+
const staged = new Map(functions.map(fn => [fn.name, { ...fn }]));
|
|
324
|
+
for (const fn of staged.values()) {
|
|
325
|
+
fn.commands = flattenBlock(fn, fn.blocks[0]?.label ?? 'entry', namespace, new Set());
|
|
326
|
+
}
|
|
327
|
+
for (const fn of staged.values()) {
|
|
328
|
+
fn.commands = optimizeFunctionForStructure(fn, staged, namespace);
|
|
329
|
+
}
|
|
330
|
+
const optimizedCommands = (0, commands_1.optimizeCommandFunctions)(Array.from(staged.values()).map(fn => ({
|
|
331
|
+
name: fn.name,
|
|
332
|
+
commands: fn.commands ?? [],
|
|
333
|
+
})));
|
|
334
|
+
const stats = (0, commands_1.createEmptyOptimizationStats)();
|
|
335
|
+
(0, commands_1.mergeOptimizationStats)(stats, optimizedCommands.stats);
|
|
336
|
+
return {
|
|
337
|
+
functions: Array.from(staged.values()).map(fn => ({
|
|
338
|
+
...fn,
|
|
339
|
+
commands: optimizedCommands.functions.find(candidate => candidate.name === fn.name)?.commands ?? fn.commands,
|
|
340
|
+
})),
|
|
341
|
+
stats,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
//# sourceMappingURL=structure.js.map
|
package/dist/pack.mcmeta
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RedScript Parser
|
|
3
|
+
*
|
|
4
|
+
* Recursive descent parser that converts tokens into an AST.
|
|
5
|
+
* Uses precedence climbing for expression parsing.
|
|
6
|
+
*/
|
|
7
|
+
import { type Token } from '../lexer';
|
|
8
|
+
import type { Program } from '../ast/types';
|
|
9
|
+
export declare class Parser {
|
|
10
|
+
private tokens;
|
|
11
|
+
private pos;
|
|
12
|
+
private sourceLines;
|
|
13
|
+
private filePath?;
|
|
14
|
+
constructor(tokens: Token[], source?: string, filePath?: string);
|
|
15
|
+
private peek;
|
|
16
|
+
private advance;
|
|
17
|
+
private check;
|
|
18
|
+
private match;
|
|
19
|
+
private expect;
|
|
20
|
+
private error;
|
|
21
|
+
private withLoc;
|
|
22
|
+
private getLocToken;
|
|
23
|
+
parse(defaultNamespace?: string): Program;
|
|
24
|
+
private parseStructDecl;
|
|
25
|
+
private parseEnumDecl;
|
|
26
|
+
private parseConstDecl;
|
|
27
|
+
private parseFnDecl;
|
|
28
|
+
private parseDecorators;
|
|
29
|
+
private parseDecoratorValue;
|
|
30
|
+
private parseParams;
|
|
31
|
+
private parseType;
|
|
32
|
+
private parseFunctionType;
|
|
33
|
+
private parseBlock;
|
|
34
|
+
private parseStmt;
|
|
35
|
+
private parseLetStmt;
|
|
36
|
+
private parseReturnStmt;
|
|
37
|
+
private parseIfStmt;
|
|
38
|
+
private parseWhileStmt;
|
|
39
|
+
private parseForStmt;
|
|
40
|
+
private parseForRangeStmt;
|
|
41
|
+
private parseForeachStmt;
|
|
42
|
+
private parseMatchStmt;
|
|
43
|
+
private parseAsStmt;
|
|
44
|
+
private parseAtStmt;
|
|
45
|
+
private parseExecuteStmt;
|
|
46
|
+
private parseExprStmt;
|
|
47
|
+
private parseExpr;
|
|
48
|
+
private parseAssignment;
|
|
49
|
+
private parseBinaryExpr;
|
|
50
|
+
private parseUnaryExpr;
|
|
51
|
+
private isSubtraction;
|
|
52
|
+
private parsePostfixExpr;
|
|
53
|
+
private parseArgs;
|
|
54
|
+
private parsePrimaryExpr;
|
|
55
|
+
private parseLiteralExpr;
|
|
56
|
+
private parseSingleParamLambda;
|
|
57
|
+
private parseLambdaExpr;
|
|
58
|
+
private finishLambdaExpr;
|
|
59
|
+
private parseStringExpr;
|
|
60
|
+
private parseEmbeddedExpr;
|
|
61
|
+
private parseStructLit;
|
|
62
|
+
private parseArrayLit;
|
|
63
|
+
private isLambdaStart;
|
|
64
|
+
private typeTokenLength;
|
|
65
|
+
private isBlockPosLiteral;
|
|
66
|
+
private coordComponentTokenLength;
|
|
67
|
+
private parseBlockPos;
|
|
68
|
+
private parseCoordComponent;
|
|
69
|
+
private parseSignedCoordOffset;
|
|
70
|
+
private parseSelector;
|
|
71
|
+
private parseSelectorValue;
|
|
72
|
+
private parseSelectorFilters;
|
|
73
|
+
private splitSelectorParams;
|
|
74
|
+
private parseScoresFilter;
|
|
75
|
+
private parseRangeValue;
|
|
76
|
+
}
|