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,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for src/optimizer/cse.ts — Common Subexpression Elimination
|
|
3
|
+
*
|
|
4
|
+
* Covers: exprKey computation, commutative normalization, CSE replacement,
|
|
5
|
+
* invalidation on side effects, self-modifying instruction handling.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { cse } from '../optimizer/cse'
|
|
9
|
+
import type { MIRFunction, MIRBlock, MIRInstr, Operand, Temp, CmpOp } from '../mir/types'
|
|
10
|
+
|
|
11
|
+
function temp(name: string): Temp {
|
|
12
|
+
return name
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function tempOp(name: string): Operand {
|
|
16
|
+
return { kind: 'temp', name }
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function constOp(value: number): Operand {
|
|
20
|
+
return { kind: 'const', value }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function makeBlock(instrs: MIRInstr[]): MIRBlock {
|
|
24
|
+
return { id: 'b0', instrs, term: { kind: 'return' } as MIRInstr, preds: [] }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function makeFn(blocks: MIRBlock[]): MIRFunction {
|
|
28
|
+
return {
|
|
29
|
+
name: 'test',
|
|
30
|
+
params: [],
|
|
31
|
+
returnType: { kind: 'named', name: 'void' },
|
|
32
|
+
blocks,
|
|
33
|
+
temps: [],
|
|
34
|
+
} as unknown as MIRFunction
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
describe('CSE — basic elimination', () => {
|
|
38
|
+
it('replaces duplicate add with copy', () => {
|
|
39
|
+
const block = makeBlock([
|
|
40
|
+
{ kind: 'add', dst: temp('t1'), a: tempOp('t0'), b: constOp(1) },
|
|
41
|
+
{ kind: 'add', dst: temp('t2'), a: tempOp('t0'), b: constOp(1) },
|
|
42
|
+
])
|
|
43
|
+
const result = cse(makeFn([block]))
|
|
44
|
+
const instrs = result.blocks[0].instrs
|
|
45
|
+
expect(instrs[0].kind).toBe('add')
|
|
46
|
+
expect(instrs[1].kind).toBe('copy')
|
|
47
|
+
if (instrs[1].kind === 'copy') {
|
|
48
|
+
expect(instrs[1].dst).toBe('t2')
|
|
49
|
+
expect(instrs[1].src).toEqual(tempOp('t1'))
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('replaces duplicate sub with copy', () => {
|
|
54
|
+
const block = makeBlock([
|
|
55
|
+
{ kind: 'sub', dst: temp('t1'), a: tempOp('t0'), b: constOp(2) },
|
|
56
|
+
{ kind: 'sub', dst: temp('t2'), a: tempOp('t0'), b: constOp(2) },
|
|
57
|
+
])
|
|
58
|
+
const result = cse(makeFn([block]))
|
|
59
|
+
expect(result.blocks[0].instrs[1].kind).toBe('copy')
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('replaces duplicate mul with copy', () => {
|
|
63
|
+
const block = makeBlock([
|
|
64
|
+
{ kind: 'mul', dst: temp('t1'), a: tempOp('x'), b: tempOp('y') },
|
|
65
|
+
{ kind: 'mul', dst: temp('t2'), a: tempOp('x'), b: tempOp('y') },
|
|
66
|
+
])
|
|
67
|
+
const result = cse(makeFn([block]))
|
|
68
|
+
expect(result.blocks[0].instrs[1].kind).toBe('copy')
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('replaces duplicate neg with copy', () => {
|
|
72
|
+
const block = makeBlock([
|
|
73
|
+
{ kind: 'neg', dst: temp('t1'), src: tempOp('t0') },
|
|
74
|
+
{ kind: 'neg', dst: temp('t2'), src: tempOp('t0') },
|
|
75
|
+
])
|
|
76
|
+
const result = cse(makeFn([block]))
|
|
77
|
+
expect(result.blocks[0].instrs[1].kind).toBe('copy')
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('replaces duplicate not with copy', () => {
|
|
81
|
+
const block = makeBlock([
|
|
82
|
+
{ kind: 'not', dst: temp('t1'), src: tempOp('t0') },
|
|
83
|
+
{ kind: 'not', dst: temp('t2'), src: tempOp('t0') },
|
|
84
|
+
])
|
|
85
|
+
const result = cse(makeFn([block]))
|
|
86
|
+
expect(result.blocks[0].instrs[1].kind).toBe('copy')
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('replaces duplicate cmp with copy', () => {
|
|
90
|
+
const block = makeBlock([
|
|
91
|
+
{ kind: 'cmp', dst: temp('t1'), op: 'lt' as CmpOp, a: tempOp('x'), b: tempOp('y') },
|
|
92
|
+
{ kind: 'cmp', dst: temp('t2'), op: 'lt' as CmpOp, a: tempOp('x'), b: tempOp('y') },
|
|
93
|
+
])
|
|
94
|
+
const result = cse(makeFn([block]))
|
|
95
|
+
expect(result.blocks[0].instrs[1].kind).toBe('copy')
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
describe('CSE — commutative normalization', () => {
|
|
100
|
+
it('treats a+b and b+a as same expression', () => {
|
|
101
|
+
const block = makeBlock([
|
|
102
|
+
{ kind: 'add', dst: temp('t1'), a: tempOp('x'), b: tempOp('y') },
|
|
103
|
+
{ kind: 'add', dst: temp('t2'), a: tempOp('y'), b: tempOp('x') },
|
|
104
|
+
])
|
|
105
|
+
const result = cse(makeFn([block]))
|
|
106
|
+
expect(result.blocks[0].instrs[1].kind).toBe('copy')
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('treats a*b and b*a as same expression', () => {
|
|
110
|
+
const block = makeBlock([
|
|
111
|
+
{ kind: 'mul', dst: temp('t1'), a: tempOp('x'), b: tempOp('y') },
|
|
112
|
+
{ kind: 'mul', dst: temp('t2'), a: tempOp('y'), b: tempOp('x') },
|
|
113
|
+
])
|
|
114
|
+
const result = cse(makeFn([block]))
|
|
115
|
+
expect(result.blocks[0].instrs[1].kind).toBe('copy')
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('does NOT treat a-b and b-a as same (sub is non-commutative)', () => {
|
|
119
|
+
const block = makeBlock([
|
|
120
|
+
{ kind: 'sub', dst: temp('t1'), a: tempOp('x'), b: tempOp('y') },
|
|
121
|
+
{ kind: 'sub', dst: temp('t2'), a: tempOp('y'), b: tempOp('x') },
|
|
122
|
+
])
|
|
123
|
+
const result = cse(makeFn([block]))
|
|
124
|
+
expect(result.blocks[0].instrs[1].kind).toBe('sub')
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it('does NOT treat a/b and b/a as same (div is non-commutative)', () => {
|
|
128
|
+
const block = makeBlock([
|
|
129
|
+
{ kind: 'div', dst: temp('t1'), a: tempOp('x'), b: tempOp('y') },
|
|
130
|
+
{ kind: 'div', dst: temp('t2'), a: tempOp('y'), b: tempOp('x') },
|
|
131
|
+
])
|
|
132
|
+
const result = cse(makeFn([block]))
|
|
133
|
+
expect(result.blocks[0].instrs[1].kind).toBe('div')
|
|
134
|
+
})
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
describe('CSE — invalidation', () => {
|
|
138
|
+
it('invalidates expression when operand is redefined', () => {
|
|
139
|
+
const block = makeBlock([
|
|
140
|
+
{ kind: 'add', dst: temp('t1'), a: tempOp('x'), b: constOp(1) },
|
|
141
|
+
{ kind: 'copy', dst: temp('x'), src: constOp(99) },
|
|
142
|
+
{ kind: 'add', dst: temp('t2'), a: tempOp('x'), b: constOp(1) },
|
|
143
|
+
])
|
|
144
|
+
const result = cse(makeFn([block]))
|
|
145
|
+
// t2 should NOT be replaced because x was modified
|
|
146
|
+
expect(result.blocks[0].instrs[2].kind).toBe('add')
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
it('invalidates all expressions after a call', () => {
|
|
150
|
+
const block = makeBlock([
|
|
151
|
+
{ kind: 'add', dst: temp('t1'), a: tempOp('x'), b: constOp(1) },
|
|
152
|
+
{ kind: 'call', dst: temp('t_ret'), fn: 'sideEffect', args: [] } as unknown as MIRInstr,
|
|
153
|
+
{ kind: 'add', dst: temp('t2'), a: tempOp('x'), b: constOp(1) },
|
|
154
|
+
])
|
|
155
|
+
const result = cse(makeFn([block]))
|
|
156
|
+
expect(result.blocks[0].instrs[2].kind).toBe('add')
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it('invalidates all expressions after score_write', () => {
|
|
160
|
+
const block = makeBlock([
|
|
161
|
+
{ kind: 'add', dst: temp('t1'), a: tempOp('x'), b: constOp(1) },
|
|
162
|
+
{ kind: 'score_write', target: '@s', objective: 'test', src: tempOp('v') } as unknown as MIRInstr,
|
|
163
|
+
{ kind: 'add', dst: temp('t2'), a: tempOp('x'), b: constOp(1) },
|
|
164
|
+
])
|
|
165
|
+
const result = cse(makeFn([block]))
|
|
166
|
+
expect(result.blocks[0].instrs[2].kind).toBe('add')
|
|
167
|
+
})
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
describe('CSE — self-modifying skip', () => {
|
|
171
|
+
it('does not CSE self-modifying instruction (t5 = t5 + 1)', () => {
|
|
172
|
+
const block = makeBlock([
|
|
173
|
+
{ kind: 'add', dst: temp('t5'), a: tempOp('t5'), b: constOp(1) },
|
|
174
|
+
{ kind: 'add', dst: temp('t5'), a: tempOp('t5'), b: constOp(1) },
|
|
175
|
+
])
|
|
176
|
+
const result = cse(makeFn([block]))
|
|
177
|
+
// Both should remain as 'add' because they're self-modifying
|
|
178
|
+
expect(result.blocks[0].instrs[0].kind).toBe('add')
|
|
179
|
+
expect(result.blocks[0].instrs[1].kind).toBe('add')
|
|
180
|
+
})
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
describe('CSE — different expressions not merged', () => {
|
|
184
|
+
it('does not merge different operations on same operands', () => {
|
|
185
|
+
const block = makeBlock([
|
|
186
|
+
{ kind: 'add', dst: temp('t1'), a: tempOp('x'), b: tempOp('y') },
|
|
187
|
+
{ kind: 'sub', dst: temp('t2'), a: tempOp('x'), b: tempOp('y') },
|
|
188
|
+
])
|
|
189
|
+
const result = cse(makeFn([block]))
|
|
190
|
+
expect(result.blocks[0].instrs[0].kind).toBe('add')
|
|
191
|
+
expect(result.blocks[0].instrs[1].kind).toBe('sub')
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
it('does not merge same operation with different constants', () => {
|
|
195
|
+
const block = makeBlock([
|
|
196
|
+
{ kind: 'add', dst: temp('t1'), a: tempOp('x'), b: constOp(1) },
|
|
197
|
+
{ kind: 'add', dst: temp('t2'), a: tempOp('x'), b: constOp(2) },
|
|
198
|
+
])
|
|
199
|
+
const result = cse(makeFn([block]))
|
|
200
|
+
expect(result.blocks[0].instrs[0].kind).toBe('add')
|
|
201
|
+
expect(result.blocks[0].instrs[1].kind).toBe('add')
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
it('different cmp operators are not merged', () => {
|
|
205
|
+
const block = makeBlock([
|
|
206
|
+
{ kind: 'cmp', dst: temp('t1'), op: 'lt' as CmpOp, a: tempOp('x'), b: tempOp('y') },
|
|
207
|
+
{ kind: 'cmp', dst: temp('t2'), op: 'gt' as CmpOp, a: tempOp('x'), b: tempOp('y') },
|
|
208
|
+
])
|
|
209
|
+
const result = cse(makeFn([block]))
|
|
210
|
+
expect(result.blocks[0].instrs[0].kind).toBe('cmp')
|
|
211
|
+
expect(result.blocks[0].instrs[1].kind).toBe('cmp')
|
|
212
|
+
})
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
describe('CSE — multiple blocks', () => {
|
|
216
|
+
it('processes each block independently', () => {
|
|
217
|
+
const block1 = makeBlock([
|
|
218
|
+
{ kind: 'add', dst: temp('t1'), a: tempOp('x'), b: constOp(1) },
|
|
219
|
+
])
|
|
220
|
+
const block2 = makeBlock([
|
|
221
|
+
// Same expression in different block — should NOT be CSE'd
|
|
222
|
+
{ kind: 'add', dst: temp('t2'), a: tempOp('x'), b: constOp(1) },
|
|
223
|
+
])
|
|
224
|
+
block2.id = 'b1'
|
|
225
|
+
const result = cse(makeFn([block1, block2]))
|
|
226
|
+
expect(result.blocks[0].instrs[0].kind).toBe('add')
|
|
227
|
+
expect(result.blocks[1].instrs[0].kind).toBe('add')
|
|
228
|
+
})
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
describe('CSE — passthrough of non-pure instructions', () => {
|
|
232
|
+
it('passes through const instruction unchanged', () => {
|
|
233
|
+
const block = makeBlock([
|
|
234
|
+
{ kind: 'const', dst: temp('t1'), value: '42' } as unknown as MIRInstr,
|
|
235
|
+
{ kind: 'const', dst: temp('t2'), value: '42' } as unknown as MIRInstr,
|
|
236
|
+
])
|
|
237
|
+
const result = cse(makeFn([block]))
|
|
238
|
+
// const instructions don't have exprKey so both should pass through
|
|
239
|
+
expect(result.blocks[0].instrs.length).toBe(2)
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
it('preserves jump and branch instructions', () => {
|
|
243
|
+
const block = makeBlock([
|
|
244
|
+
{ kind: 'add', dst: temp('t1'), a: tempOp('x'), b: constOp(1) },
|
|
245
|
+
{ kind: 'branch', cond: tempOp('t1'), thenBlock: 'b1', elseBlock: 'b2' } as unknown as MIRInstr,
|
|
246
|
+
])
|
|
247
|
+
const result = cse(makeFn([block]))
|
|
248
|
+
expect(result.blocks[0].instrs[1].kind).toBe('branch')
|
|
249
|
+
})
|
|
250
|
+
})
|
|
@@ -477,21 +477,12 @@ impl Point {
|
|
|
477
477
|
expect(expr).toEqual({ kind: 'str_lit', value: 'hello' })
|
|
478
478
|
})
|
|
479
479
|
|
|
480
|
-
it('parses
|
|
480
|
+
it('parses dollar-brace as plain string literal (no interpolation)', () => {
|
|
481
|
+
// ${...} in plain strings is NOT interpolated — only f"..." strings support {expr}
|
|
481
482
|
const expr = parseExpr('"Hello ${name}, score is ${score + 1}"')
|
|
482
483
|
expect(expr).toEqual({
|
|
483
|
-
kind: '
|
|
484
|
-
|
|
485
|
-
'Hello ',
|
|
486
|
-
{ kind: 'ident', name: 'name' },
|
|
487
|
-
', score is ',
|
|
488
|
-
{
|
|
489
|
-
kind: 'binary',
|
|
490
|
-
op: '+',
|
|
491
|
-
left: { kind: 'ident', name: 'score' },
|
|
492
|
-
right: { kind: 'int_lit', value: 1 },
|
|
493
|
-
},
|
|
494
|
-
],
|
|
484
|
+
kind: 'str_lit',
|
|
485
|
+
value: 'Hello ${name}, score is ${score + 1}',
|
|
495
486
|
})
|
|
496
487
|
})
|
|
497
488
|
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
import * as http from 'http'
|
|
12
12
|
import { requestHandler } from '../repl-server'
|
|
13
13
|
|
|
14
|
-
// Use
|
|
15
|
-
const PORT = 3001
|
|
14
|
+
// Use port 0 so the OS assigns a free port — avoids port conflicts
|
|
16
15
|
const server = http.createServer(requestHandler)
|
|
16
|
+
let PORT = 0
|
|
17
17
|
|
|
18
18
|
function rawRequest(
|
|
19
19
|
method: string,
|
|
@@ -65,11 +65,11 @@ function jsonRequest(
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
beforeAll(done => {
|
|
68
|
-
|
|
68
|
+
server.listen(0, () => {
|
|
69
|
+
const addr = server.address() as import('net').AddressInfo
|
|
70
|
+
PORT = addr.port
|
|
69
71
|
done()
|
|
70
|
-
}
|
|
71
|
-
server.listen(PORT, done)
|
|
72
|
-
}
|
|
72
|
+
})
|
|
73
73
|
})
|
|
74
74
|
|
|
75
75
|
afterAll(done => {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import * as http from 'http'
|
|
8
8
|
import { server } from '../repl-server'
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
let PORT = 0
|
|
11
11
|
|
|
12
12
|
function request(
|
|
13
13
|
method: string,
|
|
@@ -46,12 +46,11 @@ function request(
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
beforeAll(done => {
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
server.listen(0, () => {
|
|
50
|
+
const addr = server.address() as import('net').AddressInfo
|
|
51
|
+
PORT = addr.port
|
|
51
52
|
done()
|
|
52
|
-
}
|
|
53
|
-
server.listen(PORT, done)
|
|
54
|
-
}
|
|
53
|
+
})
|
|
55
54
|
})
|
|
56
55
|
|
|
57
56
|
afterAll(done => {
|
|
@@ -30,20 +30,20 @@ describe('stdlib/queue.mcrs', () => {
|
|
|
30
30
|
test('queue_pop reads head index from rs.q_head scoreboard', () => {
|
|
31
31
|
const r = compileWith(`@keep fn t(): int { return queue_pop(); }`)
|
|
32
32
|
const all = r.files.map(f => f.content).join('\n')
|
|
33
|
-
expect(all).toContain('scoreboard players get rs.q_head
|
|
33
|
+
expect(all).toContain('scoreboard players get rs.q_head rs')
|
|
34
34
|
})
|
|
35
35
|
|
|
36
36
|
test('queue_pop advances head pointer after reading', () => {
|
|
37
37
|
const r = compileWith(`@keep fn t(): int { return queue_pop(); }`)
|
|
38
38
|
const all = r.files.map(f => f.content).join('\n')
|
|
39
|
-
expect(all).toContain('scoreboard players add rs.q_head
|
|
39
|
+
expect(all).toContain('scoreboard players add rs.q_head rs 1')
|
|
40
40
|
})
|
|
41
41
|
|
|
42
42
|
test('queue_pop uses __queue_peek_apply macro to read element', () => {
|
|
43
43
|
const r = compileWith(`@keep fn t(): int { return queue_pop(); }`)
|
|
44
44
|
const all = r.files.map(f => f.content).join('\n')
|
|
45
45
|
expect(all).toContain('function test:__queue_peek_apply with storage rs:macro_args')
|
|
46
|
-
expect(all).toContain('$execute store result score
|
|
46
|
+
expect(all).toContain('$execute store result score rs.peek_tmp rs run data get storage rs:arrays Queue[$(idx)]')
|
|
47
47
|
})
|
|
48
48
|
|
|
49
49
|
test('queue_peek uses __queue_peek_apply macro', () => {
|
|
@@ -55,19 +55,19 @@ describe('stdlib/queue.mcrs', () => {
|
|
|
55
55
|
test('queue_size subtracts head from raw list length', () => {
|
|
56
56
|
const r = compileWith(`@keep fn t(): int { return queue_size(); }`)
|
|
57
57
|
const all = r.files.map(f => f.content).join('\n')
|
|
58
|
-
expect(all).toContain('scoreboard players operation $ret __test -= rs.q_head
|
|
58
|
+
expect(all).toContain('scoreboard players operation $ret __test -= rs.q_head rs')
|
|
59
59
|
})
|
|
60
60
|
|
|
61
61
|
test('queue_size uses __queue_size_raw_apply to read raw list length', () => {
|
|
62
62
|
const r = compileWith(`@keep fn t(): int { return queue_size(); }`)
|
|
63
63
|
const all = r.files.map(f => f.content).join('\n')
|
|
64
|
-
expect(all).toContain('function test:__queue_size_raw_apply
|
|
64
|
+
expect(all).toContain('function test:__queue_size_raw_apply')
|
|
65
65
|
})
|
|
66
66
|
|
|
67
67
|
test('queue_clear resets Queue list and head pointer', () => {
|
|
68
68
|
const r = compileWith(`@keep fn t() { queue_clear(); }`)
|
|
69
69
|
const all = r.files.map(f => f.content).join('\n')
|
|
70
70
|
expect(all).toContain('data modify storage rs:arrays Queue set value []')
|
|
71
|
-
expect(all).toContain('scoreboard players set rs.q_head
|
|
71
|
+
expect(all).toContain('scoreboard players set rs.q_head rs 0')
|
|
72
72
|
})
|
|
73
73
|
})
|
package/src/lexer/index.ts
CHANGED
|
@@ -568,8 +568,9 @@ export class Lexer {
|
|
|
568
568
|
continue
|
|
569
569
|
}
|
|
570
570
|
|
|
571
|
+
// Check for {...} interpolation
|
|
571
572
|
if (interpolationDepth === 0 && this.peek() === '{') {
|
|
572
|
-
value += this.advance()
|
|
573
|
+
value += this.advance() // {
|
|
573
574
|
interpolationDepth = 1
|
|
574
575
|
interpolationString = false
|
|
575
576
|
continue
|