redscript-mc 3.0.1 → 3.0.2
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/workflows/ci.yml +1 -0
- package/README.md +119 -313
- package/README.zh.md +118 -314
- package/ROADMAP.md +5 -5
- package/dist/data/impl_test/function/counter/get.mcfunction +5 -0
- package/dist/data/impl_test/function/counter/inc.mcfunction +7 -0
- package/dist/data/impl_test/function/counter/new.mcfunction +4 -0
- package/dist/data/impl_test/function/load.mcfunction +1 -0
- package/dist/data/impl_test/function/test_impl.mcfunction +10 -0
- package/dist/data/minecraft/tags/function/load.json +5 -0
- package/dist/data/playground/function/load.mcfunction +1 -0
- package/dist/data/playground/function/start.mcfunction +4 -0
- package/dist/data/playground/function/start__say_macro_t1.mcfunction +1 -0
- package/dist/data/playground/function/stop.mcfunction +5 -0
- package/dist/data/playground/function/stop__say_macro_t0.mcfunction +1 -0
- package/dist/data/stdlib_queue8_test/function/__queue_append_apply.mcfunction +4 -0
- package/dist/data/stdlib_queue8_test/function/__queue_peek_apply.mcfunction +4 -0
- package/dist/data/stdlib_queue8_test/function/__queue_size_raw_apply.mcfunction +4 -0
- package/dist/data/stdlib_queue8_test/function/load.mcfunction +1 -0
- package/dist/data/stdlib_queue8_test/function/queue_clear.mcfunction +6 -0
- package/dist/data/stdlib_queue8_test/function/queue_empty__merge_1.mcfunction +5 -0
- package/dist/data/stdlib_queue8_test/function/queue_empty__then_0.mcfunction +5 -0
- package/dist/data/stdlib_queue8_test/function/queue_peek__merge_1.mcfunction +13 -0
- package/dist/data/stdlib_queue8_test/function/queue_peek__then_0.mcfunction +5 -0
- package/dist/data/stdlib_queue8_test/function/queue_pop__merge_1.mcfunction +15 -0
- package/dist/data/stdlib_queue8_test/function/queue_pop__then_0.mcfunction +5 -0
- package/dist/data/stdlib_queue8_test/function/queue_push__const_11.mcfunction +6 -0
- package/dist/data/stdlib_queue8_test/function/queue_push__const_22.mcfunction +6 -0
- package/dist/data/stdlib_queue8_test/function/queue_size.mcfunction +13 -0
- package/dist/data/stdlib_queue8_test/function/test_queue_push_and_size.mcfunction +13 -0
- package/dist/data/test/function/load.mcfunction +1 -0
- package/dist/data/test/function/say_at.mcfunction +6 -0
- package/dist/data/test/function/test.mcfunction +4 -0
- package/dist/pack.mcmeta +6 -0
- package/dist/package.json +1 -1
- package/dist/src/__tests__/formatter-extra.test.d.ts +7 -0
- package/dist/src/__tests__/formatter-extra.test.js +123 -0
- package/dist/src/__tests__/global-vars.test.d.ts +13 -0
- package/dist/src/__tests__/global-vars.test.js +156 -0
- package/dist/src/__tests__/lint/new-rules.test.d.ts +9 -0
- package/dist/src/__tests__/lint/new-rules.test.js +402 -0
- package/dist/src/__tests__/lsp-rename.test.d.ts +8 -0
- package/dist/src/__tests__/lsp-rename.test.js +157 -0
- package/dist/src/__tests__/mc-integration/say-fstring.test.d.ts +11 -0
- package/dist/src/__tests__/mc-integration/say-fstring.test.js +220 -0
- package/dist/src/__tests__/mc-integration/stdlib-coverage-2.test.js +1 -1
- package/dist/src/__tests__/mc-integration/stdlib-coverage-3.test.js +1 -1
- package/dist/src/__tests__/mc-integration/stdlib-coverage-4.test.js +1 -1
- package/dist/src/__tests__/mc-integration/stdlib-coverage-5.test.js +1 -1
- package/dist/src/__tests__/mc-integration/stdlib-coverage-6.test.js +1 -1
- package/dist/src/__tests__/mc-integration/stdlib-coverage-7.test.js +1 -1
- package/dist/src/__tests__/mc-integration/stdlib-coverage-8.test.js +1 -1
- package/dist/src/__tests__/mc-syntax.test.js +4 -1
- package/dist/src/__tests__/monomorphize-coverage.test.d.ts +9 -0
- package/dist/src/__tests__/monomorphize-coverage.test.js +204 -0
- package/dist/src/__tests__/optimizer-cse.test.d.ts +7 -0
- package/dist/src/__tests__/optimizer-cse.test.js +226 -0
- package/dist/src/__tests__/parser.test.js +4 -13
- package/dist/src/__tests__/repl-server-extra.test.js +6 -7
- package/dist/src/__tests__/repl-server.test.js +5 -7
- package/dist/src/__tests__/stdlib/queue.test.js +6 -6
- package/dist/src/cli.js +0 -0
- package/dist/src/lexer/index.js +2 -1
- package/dist/src/lint/index.d.ts +12 -5
- package/dist/src/lint/index.js +730 -5
- package/dist/src/lsp/main.js +0 -0
- package/dist/src/mc-test/client.d.ts +21 -0
- package/dist/src/mc-test/client.js +34 -0
- package/dist/src/mir/lower.js +108 -6
- package/dist/src/optimizer/interprocedural.js +37 -2
- package/dist/src/parser/decl-parser.d.ts +19 -0
- package/dist/src/parser/decl-parser.js +323 -0
- package/dist/src/parser/expr-parser.d.ts +46 -0
- package/dist/src/parser/expr-parser.js +759 -0
- package/dist/src/parser/index.d.ts +8 -129
- package/dist/src/parser/index.js +13 -2262
- package/dist/src/parser/stmt-parser.d.ts +28 -0
- package/dist/src/parser/stmt-parser.js +577 -0
- package/dist/src/parser/type-parser.d.ts +20 -0
- package/dist/src/parser/type-parser.js +257 -0
- package/dist/src/parser/utils.d.ts +34 -0
- package/dist/src/parser/utils.js +141 -0
- package/docs/dev/README-mc-integration-tests.md +141 -0
- package/docs/lint-rules.md +162 -0
- package/docs/stdlib/bigint.md +2 -0
- package/editors/vscode/README.md +63 -41
- package/editors/vscode/out/extension.js +1881 -1776
- package/editors/vscode/out/lsp-server.js +4257 -3651
- package/editors/vscode/package-lock.json +3 -3
- package/editors/vscode/package.json +1 -1
- package/examples/loops-demo.mcrs +87 -0
- package/package.json +1 -1
- package/redscript-docs/docs/en/stdlib/advanced.md +629 -0
- package/redscript-docs/docs/en/stdlib/bigint.md +316 -0
- package/redscript-docs/docs/en/stdlib/bits.md +292 -0
- package/redscript-docs/docs/en/stdlib/bossbar.md +177 -0
- package/redscript-docs/docs/en/stdlib/calculus.md +289 -0
- package/redscript-docs/docs/en/stdlib/color.md +353 -0
- package/redscript-docs/docs/en/stdlib/combat.md +88 -0
- package/redscript-docs/docs/en/stdlib/cooldown.md +82 -0
- package/redscript-docs/docs/en/stdlib/dialog.md +155 -0
- package/redscript-docs/docs/en/stdlib/easing.md +558 -0
- package/redscript-docs/docs/en/stdlib/ecs.md +475 -0
- package/redscript-docs/docs/en/stdlib/effects.md +324 -0
- package/redscript-docs/docs/en/stdlib/events.md +3 -0
- package/redscript-docs/docs/en/stdlib/expr.md +45 -0
- package/redscript-docs/docs/en/stdlib/fft.md +141 -0
- package/redscript-docs/docs/en/stdlib/geometry.md +430 -0
- package/redscript-docs/docs/en/stdlib/graph.md +259 -0
- package/redscript-docs/docs/en/stdlib/heap.md +185 -0
- package/redscript-docs/docs/en/stdlib/interactions.md +179 -0
- package/redscript-docs/docs/en/stdlib/inventory.md +97 -0
- package/redscript-docs/docs/en/stdlib/linalg.md +557 -0
- package/redscript-docs/docs/en/stdlib/list.md +559 -0
- package/redscript-docs/docs/en/stdlib/map.md +140 -0
- package/redscript-docs/docs/en/stdlib/math.md +193 -0
- package/redscript-docs/docs/en/stdlib/math_hp.md +149 -0
- package/redscript-docs/docs/en/stdlib/matrix.md +403 -0
- package/redscript-docs/docs/en/stdlib/mobs.md +965 -0
- package/redscript-docs/docs/en/stdlib/noise.md +244 -0
- package/redscript-docs/docs/en/stdlib/ode.md +253 -0
- package/redscript-docs/docs/en/stdlib/parabola.md +342 -0
- package/redscript-docs/docs/en/stdlib/particles.md +311 -0
- package/redscript-docs/docs/en/stdlib/pathfind.md +255 -0
- package/redscript-docs/docs/en/stdlib/physics.md +493 -0
- package/redscript-docs/docs/en/stdlib/player.md +78 -0
- package/redscript-docs/docs/en/stdlib/quaternion.md +673 -0
- package/redscript-docs/docs/en/stdlib/queue.md +134 -0
- package/redscript-docs/docs/en/stdlib/random.md +223 -0
- package/redscript-docs/docs/en/stdlib/result.md +143 -0
- package/redscript-docs/docs/en/stdlib/scheduler.md +183 -0
- package/redscript-docs/docs/en/stdlib/set_int.md +190 -0
- package/redscript-docs/docs/en/stdlib/sets.md +101 -0
- package/redscript-docs/docs/en/stdlib/signal.md +400 -0
- package/redscript-docs/docs/en/stdlib/sort.md +104 -0
- package/redscript-docs/docs/en/stdlib/spawn.md +147 -0
- package/redscript-docs/docs/en/stdlib/state.md +142 -0
- package/redscript-docs/docs/en/stdlib/strings.md +154 -0
- package/redscript-docs/docs/en/stdlib/tags.md +3451 -0
- package/redscript-docs/docs/en/stdlib/teams.md +153 -0
- package/redscript-docs/docs/en/stdlib/timer.md +246 -0
- package/redscript-docs/docs/en/stdlib/vec.md +158 -0
- package/redscript-docs/docs/en/stdlib/world.md +298 -0
- package/redscript-docs/docs/zh/stdlib/advanced.md +615 -0
- package/redscript-docs/docs/zh/stdlib/bigint.md +316 -0
- package/redscript-docs/docs/zh/stdlib/bits.md +292 -0
- package/redscript-docs/docs/zh/stdlib/bossbar.md +170 -0
- package/redscript-docs/docs/zh/stdlib/calculus.md +287 -0
- package/redscript-docs/docs/zh/stdlib/color.md +353 -0
- package/redscript-docs/docs/zh/stdlib/combat.md +88 -0
- package/redscript-docs/docs/zh/stdlib/cooldown.md +84 -0
- package/redscript-docs/docs/zh/stdlib/dialog.md +152 -0
- package/redscript-docs/docs/zh/stdlib/easing.md +558 -0
- package/redscript-docs/docs/zh/stdlib/ecs.md +472 -0
- package/redscript-docs/docs/zh/stdlib/effects.md +324 -0
- package/redscript-docs/docs/zh/stdlib/events.md +3 -0
- package/redscript-docs/docs/zh/stdlib/expr.md +37 -0
- package/redscript-docs/docs/zh/stdlib/fft.md +128 -0
- package/redscript-docs/docs/zh/stdlib/geometry.md +430 -0
- package/redscript-docs/docs/zh/stdlib/graph.md +259 -0
- package/redscript-docs/docs/zh/stdlib/heap.md +185 -0
- package/redscript-docs/docs/zh/stdlib/interactions.md +160 -0
- package/redscript-docs/docs/zh/stdlib/inventory.md +94 -0
- package/redscript-docs/docs/zh/stdlib/linalg.md +543 -0
- package/redscript-docs/docs/zh/stdlib/list.md +561 -0
- package/redscript-docs/docs/zh/stdlib/map.md +132 -0
- package/redscript-docs/docs/zh/stdlib/math.md +193 -0
- package/redscript-docs/docs/zh/stdlib/math_hp.md +143 -0
- package/redscript-docs/docs/zh/stdlib/matrix.md +396 -0
- package/redscript-docs/docs/zh/stdlib/mobs.md +965 -0
- package/redscript-docs/docs/zh/stdlib/noise.md +244 -0
- package/redscript-docs/docs/zh/stdlib/ode.md +243 -0
- package/redscript-docs/docs/zh/stdlib/parabola.md +337 -0
- package/redscript-docs/docs/zh/stdlib/particles.md +307 -0
- package/redscript-docs/docs/zh/stdlib/pathfind.md +255 -0
- package/redscript-docs/docs/zh/stdlib/physics.md +493 -0
- package/redscript-docs/docs/zh/stdlib/player.md +78 -0
- package/redscript-docs/docs/zh/stdlib/quaternion.md +669 -0
- package/redscript-docs/docs/zh/stdlib/queue.md +124 -0
- package/redscript-docs/docs/zh/stdlib/random.md +222 -0
- package/redscript-docs/docs/zh/stdlib/result.md +147 -0
- package/redscript-docs/docs/zh/stdlib/scheduler.md +173 -0
- package/redscript-docs/docs/zh/stdlib/set_int.md +180 -0
- package/redscript-docs/docs/zh/stdlib/sets.md +107 -0
- package/redscript-docs/docs/zh/stdlib/signal.md +373 -0
- package/redscript-docs/docs/zh/stdlib/sort.md +104 -0
- package/redscript-docs/docs/zh/stdlib/spawn.md +142 -0
- package/redscript-docs/docs/zh/stdlib/state.md +134 -0
- package/redscript-docs/docs/zh/stdlib/strings.md +107 -0
- package/redscript-docs/docs/zh/stdlib/tags.md +3451 -0
- package/redscript-docs/docs/zh/stdlib/teams.md +150 -0
- package/redscript-docs/docs/zh/stdlib/timer.md +254 -0
- package/redscript-docs/docs/zh/stdlib/vec.md +158 -0
- package/redscript-docs/docs/zh/stdlib/world.md +289 -0
- package/src/__tests__/formatter-extra.test.ts +139 -0
- package/src/__tests__/global-vars.test.ts +171 -0
- package/src/__tests__/lint/new-rules.test.ts +437 -0
- package/src/__tests__/lsp-rename.test.ts +171 -0
- package/src/__tests__/mc-integration/say-fstring.test.ts +211 -0
- package/src/__tests__/mc-integration/stdlib-coverage-2.test.ts +1 -1
- package/src/__tests__/mc-integration/stdlib-coverage-3.test.ts +1 -1
- package/src/__tests__/mc-integration/stdlib-coverage-4.test.ts +1 -1
- package/src/__tests__/mc-integration/stdlib-coverage-5.test.ts +1 -1
- package/src/__tests__/mc-integration/stdlib-coverage-6.test.ts +1 -1
- package/src/__tests__/mc-integration/stdlib-coverage-7.test.ts +1 -1
- package/src/__tests__/mc-integration/stdlib-coverage-8.test.ts +1 -1
- package/src/__tests__/mc-syntax.test.ts +3 -0
- package/src/__tests__/monomorphize-coverage.test.ts +220 -0
- package/src/__tests__/optimizer-cse.test.ts +250 -0
- package/src/__tests__/parser.test.ts +4 -13
- package/src/__tests__/repl-server-extra.test.ts +6 -6
- package/src/__tests__/repl-server.test.ts +5 -6
- package/src/__tests__/stdlib/queue.test.ts +6 -6
- package/src/lexer/index.ts +2 -1
- package/src/lint/index.ts +713 -5
- package/src/mc-test/client.ts +40 -0
- package/src/mir/lower.ts +111 -2
- package/src/optimizer/interprocedural.ts +40 -2
- package/src/parser/decl-parser.ts +349 -0
- package/src/parser/expr-parser.ts +838 -0
- package/src/parser/index.ts +17 -2558
- package/src/parser/stmt-parser.ts +585 -0
- package/src/parser/type-parser.ts +276 -0
- package/src/parser/utils.ts +173 -0
- package/src/stdlib/queue.mcrs +19 -6
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Tests for src/optimizer/cse.ts — Common Subexpression Elimination
|
|
4
|
+
*
|
|
5
|
+
* Covers: exprKey computation, commutative normalization, CSE replacement,
|
|
6
|
+
* invalidation on side effects, self-modifying instruction handling.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
const cse_1 = require("../optimizer/cse");
|
|
10
|
+
function temp(name) {
|
|
11
|
+
return name;
|
|
12
|
+
}
|
|
13
|
+
function tempOp(name) {
|
|
14
|
+
return { kind: 'temp', name };
|
|
15
|
+
}
|
|
16
|
+
function constOp(value) {
|
|
17
|
+
return { kind: 'const', value };
|
|
18
|
+
}
|
|
19
|
+
function makeBlock(instrs) {
|
|
20
|
+
return { id: 'b0', instrs, term: { kind: 'return' }, preds: [] };
|
|
21
|
+
}
|
|
22
|
+
function makeFn(blocks) {
|
|
23
|
+
return {
|
|
24
|
+
name: 'test',
|
|
25
|
+
params: [],
|
|
26
|
+
returnType: { kind: 'named', name: 'void' },
|
|
27
|
+
blocks,
|
|
28
|
+
temps: [],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
describe('CSE — basic elimination', () => {
|
|
32
|
+
it('replaces duplicate add with copy', () => {
|
|
33
|
+
const block = makeBlock([
|
|
34
|
+
{ kind: 'add', dst: temp('t1'), a: tempOp('t0'), b: constOp(1) },
|
|
35
|
+
{ kind: 'add', dst: temp('t2'), a: tempOp('t0'), b: constOp(1) },
|
|
36
|
+
]);
|
|
37
|
+
const result = (0, cse_1.cse)(makeFn([block]));
|
|
38
|
+
const instrs = result.blocks[0].instrs;
|
|
39
|
+
expect(instrs[0].kind).toBe('add');
|
|
40
|
+
expect(instrs[1].kind).toBe('copy');
|
|
41
|
+
if (instrs[1].kind === 'copy') {
|
|
42
|
+
expect(instrs[1].dst).toBe('t2');
|
|
43
|
+
expect(instrs[1].src).toEqual(tempOp('t1'));
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
it('replaces duplicate sub with copy', () => {
|
|
47
|
+
const block = makeBlock([
|
|
48
|
+
{ kind: 'sub', dst: temp('t1'), a: tempOp('t0'), b: constOp(2) },
|
|
49
|
+
{ kind: 'sub', dst: temp('t2'), a: tempOp('t0'), b: constOp(2) },
|
|
50
|
+
]);
|
|
51
|
+
const result = (0, cse_1.cse)(makeFn([block]));
|
|
52
|
+
expect(result.blocks[0].instrs[1].kind).toBe('copy');
|
|
53
|
+
});
|
|
54
|
+
it('replaces duplicate mul with copy', () => {
|
|
55
|
+
const block = makeBlock([
|
|
56
|
+
{ kind: 'mul', dst: temp('t1'), a: tempOp('x'), b: tempOp('y') },
|
|
57
|
+
{ kind: 'mul', dst: temp('t2'), a: tempOp('x'), b: tempOp('y') },
|
|
58
|
+
]);
|
|
59
|
+
const result = (0, cse_1.cse)(makeFn([block]));
|
|
60
|
+
expect(result.blocks[0].instrs[1].kind).toBe('copy');
|
|
61
|
+
});
|
|
62
|
+
it('replaces duplicate neg with copy', () => {
|
|
63
|
+
const block = makeBlock([
|
|
64
|
+
{ kind: 'neg', dst: temp('t1'), src: tempOp('t0') },
|
|
65
|
+
{ kind: 'neg', dst: temp('t2'), src: tempOp('t0') },
|
|
66
|
+
]);
|
|
67
|
+
const result = (0, cse_1.cse)(makeFn([block]));
|
|
68
|
+
expect(result.blocks[0].instrs[1].kind).toBe('copy');
|
|
69
|
+
});
|
|
70
|
+
it('replaces duplicate not with copy', () => {
|
|
71
|
+
const block = makeBlock([
|
|
72
|
+
{ kind: 'not', dst: temp('t1'), src: tempOp('t0') },
|
|
73
|
+
{ kind: 'not', dst: temp('t2'), src: tempOp('t0') },
|
|
74
|
+
]);
|
|
75
|
+
const result = (0, cse_1.cse)(makeFn([block]));
|
|
76
|
+
expect(result.blocks[0].instrs[1].kind).toBe('copy');
|
|
77
|
+
});
|
|
78
|
+
it('replaces duplicate cmp with copy', () => {
|
|
79
|
+
const block = makeBlock([
|
|
80
|
+
{ kind: 'cmp', dst: temp('t1'), op: 'lt', a: tempOp('x'), b: tempOp('y') },
|
|
81
|
+
{ kind: 'cmp', dst: temp('t2'), op: 'lt', a: tempOp('x'), b: tempOp('y') },
|
|
82
|
+
]);
|
|
83
|
+
const result = (0, cse_1.cse)(makeFn([block]));
|
|
84
|
+
expect(result.blocks[0].instrs[1].kind).toBe('copy');
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
describe('CSE — commutative normalization', () => {
|
|
88
|
+
it('treats a+b and b+a as same expression', () => {
|
|
89
|
+
const block = makeBlock([
|
|
90
|
+
{ kind: 'add', dst: temp('t1'), a: tempOp('x'), b: tempOp('y') },
|
|
91
|
+
{ kind: 'add', dst: temp('t2'), a: tempOp('y'), b: tempOp('x') },
|
|
92
|
+
]);
|
|
93
|
+
const result = (0, cse_1.cse)(makeFn([block]));
|
|
94
|
+
expect(result.blocks[0].instrs[1].kind).toBe('copy');
|
|
95
|
+
});
|
|
96
|
+
it('treats a*b and b*a as same expression', () => {
|
|
97
|
+
const block = makeBlock([
|
|
98
|
+
{ kind: 'mul', dst: temp('t1'), a: tempOp('x'), b: tempOp('y') },
|
|
99
|
+
{ kind: 'mul', dst: temp('t2'), a: tempOp('y'), b: tempOp('x') },
|
|
100
|
+
]);
|
|
101
|
+
const result = (0, cse_1.cse)(makeFn([block]));
|
|
102
|
+
expect(result.blocks[0].instrs[1].kind).toBe('copy');
|
|
103
|
+
});
|
|
104
|
+
it('does NOT treat a-b and b-a as same (sub is non-commutative)', () => {
|
|
105
|
+
const block = makeBlock([
|
|
106
|
+
{ kind: 'sub', dst: temp('t1'), a: tempOp('x'), b: tempOp('y') },
|
|
107
|
+
{ kind: 'sub', dst: temp('t2'), a: tempOp('y'), b: tempOp('x') },
|
|
108
|
+
]);
|
|
109
|
+
const result = (0, cse_1.cse)(makeFn([block]));
|
|
110
|
+
expect(result.blocks[0].instrs[1].kind).toBe('sub');
|
|
111
|
+
});
|
|
112
|
+
it('does NOT treat a/b and b/a as same (div is non-commutative)', () => {
|
|
113
|
+
const block = makeBlock([
|
|
114
|
+
{ kind: 'div', dst: temp('t1'), a: tempOp('x'), b: tempOp('y') },
|
|
115
|
+
{ kind: 'div', dst: temp('t2'), a: tempOp('y'), b: tempOp('x') },
|
|
116
|
+
]);
|
|
117
|
+
const result = (0, cse_1.cse)(makeFn([block]));
|
|
118
|
+
expect(result.blocks[0].instrs[1].kind).toBe('div');
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
describe('CSE — invalidation', () => {
|
|
122
|
+
it('invalidates expression when operand is redefined', () => {
|
|
123
|
+
const block = makeBlock([
|
|
124
|
+
{ kind: 'add', dst: temp('t1'), a: tempOp('x'), b: constOp(1) },
|
|
125
|
+
{ kind: 'copy', dst: temp('x'), src: constOp(99) },
|
|
126
|
+
{ kind: 'add', dst: temp('t2'), a: tempOp('x'), b: constOp(1) },
|
|
127
|
+
]);
|
|
128
|
+
const result = (0, cse_1.cse)(makeFn([block]));
|
|
129
|
+
// t2 should NOT be replaced because x was modified
|
|
130
|
+
expect(result.blocks[0].instrs[2].kind).toBe('add');
|
|
131
|
+
});
|
|
132
|
+
it('invalidates all expressions after a call', () => {
|
|
133
|
+
const block = makeBlock([
|
|
134
|
+
{ kind: 'add', dst: temp('t1'), a: tempOp('x'), b: constOp(1) },
|
|
135
|
+
{ kind: 'call', dst: temp('t_ret'), fn: 'sideEffect', args: [] },
|
|
136
|
+
{ kind: 'add', dst: temp('t2'), a: tempOp('x'), b: constOp(1) },
|
|
137
|
+
]);
|
|
138
|
+
const result = (0, cse_1.cse)(makeFn([block]));
|
|
139
|
+
expect(result.blocks[0].instrs[2].kind).toBe('add');
|
|
140
|
+
});
|
|
141
|
+
it('invalidates all expressions after score_write', () => {
|
|
142
|
+
const block = makeBlock([
|
|
143
|
+
{ kind: 'add', dst: temp('t1'), a: tempOp('x'), b: constOp(1) },
|
|
144
|
+
{ kind: 'score_write', target: '@s', objective: 'test', src: tempOp('v') },
|
|
145
|
+
{ kind: 'add', dst: temp('t2'), a: tempOp('x'), b: constOp(1) },
|
|
146
|
+
]);
|
|
147
|
+
const result = (0, cse_1.cse)(makeFn([block]));
|
|
148
|
+
expect(result.blocks[0].instrs[2].kind).toBe('add');
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
describe('CSE — self-modifying skip', () => {
|
|
152
|
+
it('does not CSE self-modifying instruction (t5 = t5 + 1)', () => {
|
|
153
|
+
const block = makeBlock([
|
|
154
|
+
{ kind: 'add', dst: temp('t5'), a: tempOp('t5'), b: constOp(1) },
|
|
155
|
+
{ kind: 'add', dst: temp('t5'), a: tempOp('t5'), b: constOp(1) },
|
|
156
|
+
]);
|
|
157
|
+
const result = (0, cse_1.cse)(makeFn([block]));
|
|
158
|
+
// Both should remain as 'add' because they're self-modifying
|
|
159
|
+
expect(result.blocks[0].instrs[0].kind).toBe('add');
|
|
160
|
+
expect(result.blocks[0].instrs[1].kind).toBe('add');
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
describe('CSE — different expressions not merged', () => {
|
|
164
|
+
it('does not merge different operations on same operands', () => {
|
|
165
|
+
const block = makeBlock([
|
|
166
|
+
{ kind: 'add', dst: temp('t1'), a: tempOp('x'), b: tempOp('y') },
|
|
167
|
+
{ kind: 'sub', dst: temp('t2'), a: tempOp('x'), b: tempOp('y') },
|
|
168
|
+
]);
|
|
169
|
+
const result = (0, cse_1.cse)(makeFn([block]));
|
|
170
|
+
expect(result.blocks[0].instrs[0].kind).toBe('add');
|
|
171
|
+
expect(result.blocks[0].instrs[1].kind).toBe('sub');
|
|
172
|
+
});
|
|
173
|
+
it('does not merge same operation with different constants', () => {
|
|
174
|
+
const block = makeBlock([
|
|
175
|
+
{ kind: 'add', dst: temp('t1'), a: tempOp('x'), b: constOp(1) },
|
|
176
|
+
{ kind: 'add', dst: temp('t2'), a: tempOp('x'), b: constOp(2) },
|
|
177
|
+
]);
|
|
178
|
+
const result = (0, cse_1.cse)(makeFn([block]));
|
|
179
|
+
expect(result.blocks[0].instrs[0].kind).toBe('add');
|
|
180
|
+
expect(result.blocks[0].instrs[1].kind).toBe('add');
|
|
181
|
+
});
|
|
182
|
+
it('different cmp operators are not merged', () => {
|
|
183
|
+
const block = makeBlock([
|
|
184
|
+
{ kind: 'cmp', dst: temp('t1'), op: 'lt', a: tempOp('x'), b: tempOp('y') },
|
|
185
|
+
{ kind: 'cmp', dst: temp('t2'), op: 'gt', a: tempOp('x'), b: tempOp('y') },
|
|
186
|
+
]);
|
|
187
|
+
const result = (0, cse_1.cse)(makeFn([block]));
|
|
188
|
+
expect(result.blocks[0].instrs[0].kind).toBe('cmp');
|
|
189
|
+
expect(result.blocks[0].instrs[1].kind).toBe('cmp');
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
describe('CSE — multiple blocks', () => {
|
|
193
|
+
it('processes each block independently', () => {
|
|
194
|
+
const block1 = makeBlock([
|
|
195
|
+
{ kind: 'add', dst: temp('t1'), a: tempOp('x'), b: constOp(1) },
|
|
196
|
+
]);
|
|
197
|
+
const block2 = makeBlock([
|
|
198
|
+
// Same expression in different block — should NOT be CSE'd
|
|
199
|
+
{ kind: 'add', dst: temp('t2'), a: tempOp('x'), b: constOp(1) },
|
|
200
|
+
]);
|
|
201
|
+
block2.id = 'b1';
|
|
202
|
+
const result = (0, cse_1.cse)(makeFn([block1, block2]));
|
|
203
|
+
expect(result.blocks[0].instrs[0].kind).toBe('add');
|
|
204
|
+
expect(result.blocks[1].instrs[0].kind).toBe('add');
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
describe('CSE — passthrough of non-pure instructions', () => {
|
|
208
|
+
it('passes through const instruction unchanged', () => {
|
|
209
|
+
const block = makeBlock([
|
|
210
|
+
{ kind: 'const', dst: temp('t1'), value: '42' },
|
|
211
|
+
{ kind: 'const', dst: temp('t2'), value: '42' },
|
|
212
|
+
]);
|
|
213
|
+
const result = (0, cse_1.cse)(makeFn([block]));
|
|
214
|
+
// const instructions don't have exprKey so both should pass through
|
|
215
|
+
expect(result.blocks[0].instrs.length).toBe(2);
|
|
216
|
+
});
|
|
217
|
+
it('preserves jump and branch instructions', () => {
|
|
218
|
+
const block = makeBlock([
|
|
219
|
+
{ kind: 'add', dst: temp('t1'), a: tempOp('x'), b: constOp(1) },
|
|
220
|
+
{ kind: 'branch', cond: tempOp('t1'), thenBlock: 'b1', elseBlock: 'b2' },
|
|
221
|
+
]);
|
|
222
|
+
const result = (0, cse_1.cse)(makeFn([block]));
|
|
223
|
+
expect(result.blocks[0].instrs[1].kind).toBe('branch');
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
//# sourceMappingURL=optimizer-cse.test.js.map
|
|
@@ -424,21 +424,12 @@ impl Point {
|
|
|
424
424
|
const expr = parseExpr('"hello"');
|
|
425
425
|
expect(expr).toEqual({ kind: 'str_lit', value: 'hello' });
|
|
426
426
|
});
|
|
427
|
-
it('parses
|
|
427
|
+
it('parses dollar-brace as plain string literal (no interpolation)', () => {
|
|
428
|
+
// ${...} in plain strings is NOT interpolated — only f"..." strings support {expr}
|
|
428
429
|
const expr = parseExpr('"Hello ${name}, score is ${score + 1}"');
|
|
429
430
|
expect(expr).toEqual({
|
|
430
|
-
kind: '
|
|
431
|
-
|
|
432
|
-
'Hello ',
|
|
433
|
-
{ kind: 'ident', name: 'name' },
|
|
434
|
-
', score is ',
|
|
435
|
-
{
|
|
436
|
-
kind: 'binary',
|
|
437
|
-
op: '+',
|
|
438
|
-
left: { kind: 'ident', name: 'score' },
|
|
439
|
-
right: { kind: 'int_lit', value: 1 },
|
|
440
|
-
},
|
|
441
|
-
],
|
|
431
|
+
kind: 'str_lit',
|
|
432
|
+
value: 'Hello ${name}, score is ${score + 1}',
|
|
442
433
|
});
|
|
443
434
|
});
|
|
444
435
|
it('parses f-string literal', () => {
|
|
@@ -44,9 +44,9 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
44
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
45
|
const http = __importStar(require("http"));
|
|
46
46
|
const repl_server_1 = require("../repl-server");
|
|
47
|
-
// Use
|
|
48
|
-
const PORT = 3001;
|
|
47
|
+
// Use port 0 so the OS assigns a free port — avoids port conflicts
|
|
49
48
|
const server = http.createServer(repl_server_1.requestHandler);
|
|
49
|
+
let PORT = 0;
|
|
50
50
|
function rawRequest(method, urlPath, body, headers = {}) {
|
|
51
51
|
return new Promise((resolve, reject) => {
|
|
52
52
|
const options = {
|
|
@@ -89,12 +89,11 @@ function jsonRequest(method, urlPath, body) {
|
|
|
89
89
|
});
|
|
90
90
|
}
|
|
91
91
|
beforeAll(done => {
|
|
92
|
-
|
|
92
|
+
server.listen(0, () => {
|
|
93
|
+
const addr = server.address();
|
|
94
|
+
PORT = addr.port;
|
|
93
95
|
done();
|
|
94
|
-
}
|
|
95
|
-
else {
|
|
96
|
-
server.listen(PORT, done);
|
|
97
|
-
}
|
|
96
|
+
});
|
|
98
97
|
});
|
|
99
98
|
afterAll(done => {
|
|
100
99
|
server.close(done);
|
|
@@ -40,7 +40,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
40
40
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
41
|
const http = __importStar(require("http"));
|
|
42
42
|
const repl_server_1 = require("../repl-server");
|
|
43
|
-
|
|
43
|
+
let PORT = 0;
|
|
44
44
|
function request(method, path, body) {
|
|
45
45
|
return new Promise((resolve, reject) => {
|
|
46
46
|
const payload = body !== undefined ? JSON.stringify(body) : undefined;
|
|
@@ -74,13 +74,11 @@ function request(method, path, body) {
|
|
|
74
74
|
});
|
|
75
75
|
}
|
|
76
76
|
beforeAll(done => {
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
repl_server_1.server.listen(0, () => {
|
|
78
|
+
const addr = repl_server_1.server.address();
|
|
79
|
+
PORT = addr.port;
|
|
79
80
|
done();
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
repl_server_1.server.listen(PORT, done);
|
|
83
|
-
}
|
|
81
|
+
});
|
|
84
82
|
});
|
|
85
83
|
afterAll(done => {
|
|
86
84
|
repl_server_1.server.close(done);
|
|
@@ -59,18 +59,18 @@ describe('stdlib/queue.mcrs', () => {
|
|
|
59
59
|
test('queue_pop reads head index from rs.q_head scoreboard', () => {
|
|
60
60
|
const r = compileWith(`@keep fn t(): int { return queue_pop(); }`);
|
|
61
61
|
const all = r.files.map(f => f.content).join('\n');
|
|
62
|
-
expect(all).toContain('scoreboard players get rs.q_head
|
|
62
|
+
expect(all).toContain('scoreboard players get rs.q_head rs');
|
|
63
63
|
});
|
|
64
64
|
test('queue_pop advances head pointer after reading', () => {
|
|
65
65
|
const r = compileWith(`@keep fn t(): int { return queue_pop(); }`);
|
|
66
66
|
const all = r.files.map(f => f.content).join('\n');
|
|
67
|
-
expect(all).toContain('scoreboard players add rs.q_head
|
|
67
|
+
expect(all).toContain('scoreboard players add rs.q_head rs 1');
|
|
68
68
|
});
|
|
69
69
|
test('queue_pop uses __queue_peek_apply macro to read element', () => {
|
|
70
70
|
const r = compileWith(`@keep fn t(): int { return queue_pop(); }`);
|
|
71
71
|
const all = r.files.map(f => f.content).join('\n');
|
|
72
72
|
expect(all).toContain('function test:__queue_peek_apply with storage rs:macro_args');
|
|
73
|
-
expect(all).toContain('$execute store result score
|
|
73
|
+
expect(all).toContain('$execute store result score rs.peek_tmp rs run data get storage rs:arrays Queue[$(idx)]');
|
|
74
74
|
});
|
|
75
75
|
test('queue_peek uses __queue_peek_apply macro', () => {
|
|
76
76
|
const r = compileWith(`@keep fn t(): int { return queue_peek(); }`);
|
|
@@ -80,18 +80,18 @@ describe('stdlib/queue.mcrs', () => {
|
|
|
80
80
|
test('queue_size subtracts head from raw list length', () => {
|
|
81
81
|
const r = compileWith(`@keep fn t(): int { return queue_size(); }`);
|
|
82
82
|
const all = r.files.map(f => f.content).join('\n');
|
|
83
|
-
expect(all).toContain('scoreboard players operation $ret __test -= rs.q_head
|
|
83
|
+
expect(all).toContain('scoreboard players operation $ret __test -= rs.q_head rs');
|
|
84
84
|
});
|
|
85
85
|
test('queue_size uses __queue_size_raw_apply to read raw list length', () => {
|
|
86
86
|
const r = compileWith(`@keep fn t(): int { return queue_size(); }`);
|
|
87
87
|
const all = r.files.map(f => f.content).join('\n');
|
|
88
|
-
expect(all).toContain('function test:__queue_size_raw_apply
|
|
88
|
+
expect(all).toContain('function test:__queue_size_raw_apply');
|
|
89
89
|
});
|
|
90
90
|
test('queue_clear resets Queue list and head pointer', () => {
|
|
91
91
|
const r = compileWith(`@keep fn t() { queue_clear(); }`);
|
|
92
92
|
const all = r.files.map(f => f.content).join('\n');
|
|
93
93
|
expect(all).toContain('data modify storage rs:arrays Queue set value []');
|
|
94
|
-
expect(all).toContain('scoreboard players set rs.q_head
|
|
94
|
+
expect(all).toContain('scoreboard players set rs.q_head rs 0');
|
|
95
95
|
});
|
|
96
96
|
});
|
|
97
97
|
//# sourceMappingURL=queue.test.js.map
|
package/dist/src/cli.js
CHANGED
|
File without changes
|
package/dist/src/lexer/index.js
CHANGED
|
@@ -464,8 +464,9 @@ class Lexer {
|
|
|
464
464
|
value += this.advance();
|
|
465
465
|
continue;
|
|
466
466
|
}
|
|
467
|
+
// Check for {...} interpolation
|
|
467
468
|
if (interpolationDepth === 0 && this.peek() === '{') {
|
|
468
|
-
value += this.advance();
|
|
469
|
+
value += this.advance(); // {
|
|
469
470
|
interpolationDepth = 1;
|
|
470
471
|
interpolationString = false;
|
|
471
472
|
continue;
|
package/dist/src/lint/index.d.ts
CHANGED
|
@@ -5,11 +5,16 @@
|
|
|
5
5
|
* Run after HIR lowering, before MIR.
|
|
6
6
|
*
|
|
7
7
|
* Rules:
|
|
8
|
-
* unused-variable
|
|
9
|
-
* unused-import
|
|
10
|
-
* magic-number
|
|
11
|
-
* dead-branch
|
|
12
|
-
* function-too-long
|
|
8
|
+
* unused-variable — let x = 5 but x never read
|
|
9
|
+
* unused-import — import math::sin but sin never called
|
|
10
|
+
* magic-number — literal number > 1 used directly (0 and 1 ignored)
|
|
11
|
+
* dead-branch — if (const == const) always-true/false condition
|
|
12
|
+
* function-too-long — function body exceeds 50 lines
|
|
13
|
+
* no-dead-assignment — variable assigned but never read after the assignment
|
|
14
|
+
* prefer-match-exhaustive — Option match missing Some or None arm
|
|
15
|
+
* no-empty-catch — empty else branch in if_let_some (silent failure)
|
|
16
|
+
* naming-convention — variables must be camelCase; types must be PascalCase
|
|
17
|
+
* no-magic-numbers — any literal number other than 0 or 1 used in an expression
|
|
13
18
|
*/
|
|
14
19
|
import type { HIRModule } from '../hir/types';
|
|
15
20
|
import type { ImportDecl } from '../ast/types';
|
|
@@ -24,6 +29,8 @@ export interface LintOptions {
|
|
|
24
29
|
filePath?: string;
|
|
25
30
|
/** Max function body lines before function-too-long fires (default: 50) */
|
|
26
31
|
maxFunctionLines?: number;
|
|
32
|
+
/** Allowed literal number values for no-magic-numbers (default: [0, 1]) */
|
|
33
|
+
allowedNumbers?: number[];
|
|
27
34
|
}
|
|
28
35
|
/**
|
|
29
36
|
* Lint a RedScript source file. Parses → lowers → analyzes HIR.
|