@minduscript/compiler 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/.npmignore +2 -0
- package/dist/asm/gen-asm.d.ts +2 -0
- package/dist/asm/gen-asm.js +432 -0
- package/dist/counter.d.ts +5 -0
- package/dist/counter.js +13 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +17 -0
- package/dist/ir/build-ir.d.ts +3 -0
- package/dist/ir/build-ir.js +1057 -0
- package/dist/ir/gather-macros.d.ts +8 -0
- package/dist/ir/gather-macros.js +204 -0
- package/dist/ir/ir.d.ts +478 -0
- package/dist/ir/ir.js +120 -0
- package/dist/ir/project.d.ts +24 -0
- package/dist/ir/project.js +339 -0
- package/dist/ir/symbol-table.d.ts +19 -0
- package/dist/ir/symbol-table.js +385 -0
- package/dist/macro/check-macro.d.ts +14 -0
- package/dist/macro/check-macro.js +136 -0
- package/dist/macro/expand-macros.d.ts +6 -0
- package/dist/macro/expand-macros.js +285 -0
- package/dist/optimizer/default-passes.d.ts +2 -0
- package/dist/optimizer/default-passes.js +20 -0
- package/dist/optimizer/merge-labels.d.ts +2 -0
- package/dist/optimizer/merge-labels.js +45 -0
- package/dist/optimizer/optimizer.d.ts +4 -0
- package/dist/optimizer/optimizer.js +26 -0
- package/dist/optimizer/passes/constant-folding.d.ts +2 -0
- package/dist/optimizer/passes/constant-folding.js +160 -0
- package/dist/optimizer/passes/control-flow.d.ts +5 -0
- package/dist/optimizer/passes/control-flow.js +138 -0
- package/dist/optimizer/passes/copy-propagation.d.ts +2 -0
- package/dist/optimizer/passes/copy-propagation.js +127 -0
- package/dist/optimizer/passes/dead-code.d.ts +2 -0
- package/dist/optimizer/passes/dead-code.js +200 -0
- package/dist/optimizer/passes/merge-labels-pass.d.ts +2 -0
- package/dist/optimizer/passes/merge-labels-pass.js +14 -0
- package/dist/optimizer/types.d.ts +10 -0
- package/dist/optimizer/types.js +3 -0
- package/dist/test.d.ts +1 -0
- package/dist/test.js +14 -0
- package/package.json +22 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.propagateAssignValues = void 0;
|
|
4
|
+
const ir_1 = require("../../ir/ir");
|
|
5
|
+
const cloneValue = (value) => {
|
|
6
|
+
if (value.isLiteral) {
|
|
7
|
+
return {
|
|
8
|
+
isLiteral: true,
|
|
9
|
+
value: value.value,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
isLiteral: false,
|
|
14
|
+
name: value.name,
|
|
15
|
+
isMindustry: value.isMindustry,
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
const resolveValue = (value, env) => {
|
|
19
|
+
if (value.isLiteral || value.isMindustry) {
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
let current = value;
|
|
23
|
+
const visited = new Set();
|
|
24
|
+
while (!current.isLiteral && !current.isMindustry) {
|
|
25
|
+
if (visited.has(current.name)) {
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
visited.add(current.name);
|
|
29
|
+
const replacement = env.get(current.name);
|
|
30
|
+
if (!replacement) {
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
current = replacement;
|
|
34
|
+
}
|
|
35
|
+
return cloneValue(current);
|
|
36
|
+
};
|
|
37
|
+
const invalidateDependentMappings = (env, changedName) => {
|
|
38
|
+
for (const [name, value] of [...env.entries()]) {
|
|
39
|
+
if (!value.isLiteral && !value.isMindustry && value.name === changedName) {
|
|
40
|
+
env.delete(name);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
const DEF_KEYS = new Set(['target', 'output', 'result', 'outType', 'building', 'floor', 'returnTarget']);
|
|
45
|
+
const mapValuesDeep = (input, env, changedRef) => {
|
|
46
|
+
if (!input || typeof input !== 'object') {
|
|
47
|
+
return input;
|
|
48
|
+
}
|
|
49
|
+
if (Array.isArray(input)) {
|
|
50
|
+
let arrayChanged = false;
|
|
51
|
+
const next = input.map((item) => {
|
|
52
|
+
const mapped = mapValuesDeep(item, env, changedRef);
|
|
53
|
+
if (mapped !== item) {
|
|
54
|
+
arrayChanged = true;
|
|
55
|
+
}
|
|
56
|
+
return mapped;
|
|
57
|
+
});
|
|
58
|
+
if (arrayChanged) {
|
|
59
|
+
changedRef.changed = true;
|
|
60
|
+
return next;
|
|
61
|
+
}
|
|
62
|
+
return input;
|
|
63
|
+
}
|
|
64
|
+
const maybeValue = input;
|
|
65
|
+
if (typeof maybeValue.isLiteral === 'boolean' && typeof maybeValue.name === 'string') {
|
|
66
|
+
const resolved = resolveValue(input, env);
|
|
67
|
+
const original = input;
|
|
68
|
+
if (resolved.isLiteral !== original.isLiteral ||
|
|
69
|
+
(resolved.isLiteral && original.isLiteral && resolved.value !== original.value) ||
|
|
70
|
+
(!resolved.isLiteral &&
|
|
71
|
+
!original.isLiteral &&
|
|
72
|
+
(resolved.name !== original.name || resolved.isMindustry !== original.isMindustry))) {
|
|
73
|
+
changedRef.changed = true;
|
|
74
|
+
return resolved;
|
|
75
|
+
}
|
|
76
|
+
return input;
|
|
77
|
+
}
|
|
78
|
+
const source = input;
|
|
79
|
+
let objectChanged = false;
|
|
80
|
+
const output = {};
|
|
81
|
+
for (const key in source) {
|
|
82
|
+
const mapped = mapValuesDeep(source[key], env, changedRef);
|
|
83
|
+
output[key] = mapped;
|
|
84
|
+
if (mapped !== source[key]) {
|
|
85
|
+
objectChanged = true;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return objectChanged ? output : input;
|
|
89
|
+
};
|
|
90
|
+
const removeDefsFromEnv = (node, env) => {
|
|
91
|
+
const temp = node;
|
|
92
|
+
for (const key in temp) {
|
|
93
|
+
if (!DEF_KEYS.has(key)) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const value = temp[key];
|
|
97
|
+
if (typeof value === 'string') {
|
|
98
|
+
env.delete(value);
|
|
99
|
+
invalidateDependentMappings(env, value);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
const propagateAssignValues = (program) => {
|
|
104
|
+
const env = new Map();
|
|
105
|
+
const changedRef = { changed: false };
|
|
106
|
+
const body = program.body.map((node) => {
|
|
107
|
+
const mappedNode = mapValuesDeep(node, env, changedRef);
|
|
108
|
+
removeDefsFromEnv(mappedNode, env);
|
|
109
|
+
if (mappedNode.type === ir_1.IRNodeType.ASSIGN) {
|
|
110
|
+
const resolved = mappedNode.value;
|
|
111
|
+
if (!resolved.isLiteral && !resolved.isMindustry && resolved.name === mappedNode.target) {
|
|
112
|
+
return mappedNode;
|
|
113
|
+
}
|
|
114
|
+
env.set(mappedNode.target, cloneValue(resolved));
|
|
115
|
+
}
|
|
116
|
+
return mappedNode;
|
|
117
|
+
});
|
|
118
|
+
return {
|
|
119
|
+
program: {
|
|
120
|
+
body,
|
|
121
|
+
boundVariables: program.boundVariables,
|
|
122
|
+
},
|
|
123
|
+
changed: changedRef.changed,
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
exports.propagateAssignValues = propagateAssignValues;
|
|
127
|
+
//# sourceMappingURL=copy-propagation.js.map
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.removeDeadCode = void 0;
|
|
4
|
+
const ir_1 = require("../../ir/ir");
|
|
5
|
+
const DCE_REMOVABLE_TYPES = new Set([
|
|
6
|
+
ir_1.IRNodeType.ASSIGN,
|
|
7
|
+
ir_1.IRNodeType.FLIP,
|
|
8
|
+
ir_1.IRNodeType.AND,
|
|
9
|
+
ir_1.IRNodeType.EQ,
|
|
10
|
+
ir_1.IRNodeType.NE,
|
|
11
|
+
ir_1.IRNodeType.LESS,
|
|
12
|
+
ir_1.IRNodeType.LE,
|
|
13
|
+
ir_1.IRNodeType.GREATER,
|
|
14
|
+
ir_1.IRNodeType.GE,
|
|
15
|
+
ir_1.IRNodeType.STRICT_EQ,
|
|
16
|
+
ir_1.IRNodeType.BITOR,
|
|
17
|
+
ir_1.IRNodeType.XOR,
|
|
18
|
+
ir_1.IRNodeType.BITAND,
|
|
19
|
+
ir_1.IRNodeType.SHL,
|
|
20
|
+
ir_1.IRNodeType.SHR,
|
|
21
|
+
ir_1.IRNodeType.ADD,
|
|
22
|
+
ir_1.IRNodeType.SUB,
|
|
23
|
+
ir_1.IRNodeType.MUL,
|
|
24
|
+
ir_1.IRNodeType.DIV,
|
|
25
|
+
ir_1.IRNodeType.IDIV,
|
|
26
|
+
ir_1.IRNodeType.MOD,
|
|
27
|
+
ir_1.IRNodeType.POW,
|
|
28
|
+
ir_1.IRNodeType.MAX,
|
|
29
|
+
ir_1.IRNodeType.MIN,
|
|
30
|
+
ir_1.IRNodeType.ANGLE,
|
|
31
|
+
ir_1.IRNodeType.ANGLE_DIFF,
|
|
32
|
+
ir_1.IRNodeType.LEN,
|
|
33
|
+
ir_1.IRNodeType.PACK_COLOR,
|
|
34
|
+
ir_1.IRNodeType.ABS,
|
|
35
|
+
ir_1.IRNodeType.LOG,
|
|
36
|
+
ir_1.IRNodeType.LOG10,
|
|
37
|
+
ir_1.IRNodeType.FLOOR,
|
|
38
|
+
ir_1.IRNodeType.CEIL,
|
|
39
|
+
ir_1.IRNodeType.SQRT,
|
|
40
|
+
ir_1.IRNodeType.SIN,
|
|
41
|
+
ir_1.IRNodeType.COS,
|
|
42
|
+
ir_1.IRNodeType.TAN,
|
|
43
|
+
ir_1.IRNodeType.ASIN,
|
|
44
|
+
ir_1.IRNodeType.ACOS,
|
|
45
|
+
ir_1.IRNodeType.ATAN,
|
|
46
|
+
]);
|
|
47
|
+
const addValueUse = (value, used) => {
|
|
48
|
+
if (!value || typeof value !== 'object') {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const temp = value;
|
|
52
|
+
if (typeof temp.isLiteral === 'boolean' && temp.isLiteral === false && typeof temp.name === 'string') {
|
|
53
|
+
used.add(temp.name);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
const collectNodeUses = (node) => {
|
|
57
|
+
const used = new Set();
|
|
58
|
+
const visited = new Set();
|
|
59
|
+
const walk = (value) => {
|
|
60
|
+
if (!value || typeof value !== 'object') {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (visited.has(value)) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
visited.add(value);
|
|
67
|
+
addValueUse(value, used);
|
|
68
|
+
if (Array.isArray(value)) {
|
|
69
|
+
for (const element of value) {
|
|
70
|
+
walk(element);
|
|
71
|
+
}
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const temp = value;
|
|
75
|
+
for (const key in temp) {
|
|
76
|
+
walk(temp[key]);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
walk(node);
|
|
80
|
+
return used;
|
|
81
|
+
};
|
|
82
|
+
const getDefVariable = (node) => {
|
|
83
|
+
if (!DCE_REMOVABLE_TYPES.has(node.type)) {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
const temp = node;
|
|
87
|
+
if (node.type === ir_1.IRNodeType.PACK_COLOR && typeof temp.result === 'string') {
|
|
88
|
+
return temp.result;
|
|
89
|
+
}
|
|
90
|
+
if (typeof temp.target !== 'string') {
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
return temp.target;
|
|
94
|
+
};
|
|
95
|
+
const buildLabelIndexMap = (body) => {
|
|
96
|
+
const labelIndex = new Map();
|
|
97
|
+
for (let i = 0; i < body.length; i++) {
|
|
98
|
+
const node = body[i];
|
|
99
|
+
if (node.type === ir_1.IRNodeType.LABEL) {
|
|
100
|
+
labelIndex.set(node.name, i);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return labelIndex;
|
|
104
|
+
};
|
|
105
|
+
const buildSuccessors = (body, labelIndex) => {
|
|
106
|
+
const successors = Array.from({ length: body.length }, () => []);
|
|
107
|
+
for (let i = 0; i < body.length; i++) {
|
|
108
|
+
const node = body[i];
|
|
109
|
+
const nextIndex = i + 1 < body.length ? i + 1 : undefined;
|
|
110
|
+
if (node.type === ir_1.IRNodeType.JUMP) {
|
|
111
|
+
const target = labelIndex.get(node.label);
|
|
112
|
+
if (target !== undefined) {
|
|
113
|
+
successors[i].push(target);
|
|
114
|
+
}
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (node.type === ir_1.IRNodeType.CONDITIONAL_JUMP) {
|
|
118
|
+
const target = labelIndex.get(node.label);
|
|
119
|
+
if (target !== undefined) {
|
|
120
|
+
successors[i].push(target);
|
|
121
|
+
}
|
|
122
|
+
if (nextIndex !== undefined) {
|
|
123
|
+
successors[i].push(nextIndex);
|
|
124
|
+
}
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (nextIndex !== undefined) {
|
|
128
|
+
successors[i].push(nextIndex);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return successors;
|
|
132
|
+
};
|
|
133
|
+
const removeDeadCode = (program) => {
|
|
134
|
+
const body = program.body;
|
|
135
|
+
if (body.length === 0) {
|
|
136
|
+
return {
|
|
137
|
+
program,
|
|
138
|
+
changed: false,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
const labelIndex = buildLabelIndexMap(body);
|
|
142
|
+
const successors = buildSuccessors(body, labelIndex);
|
|
143
|
+
const useSets = body.map((node) => collectNodeUses(node));
|
|
144
|
+
const defVars = body.map((node) => getDefVariable(node));
|
|
145
|
+
const inSets = Array.from({ length: body.length }, () => new Set());
|
|
146
|
+
const outSets = Array.from({ length: body.length }, () => new Set());
|
|
147
|
+
let changed = true;
|
|
148
|
+
while (changed) {
|
|
149
|
+
changed = false;
|
|
150
|
+
for (let i = body.length - 1; i >= 0; i--) {
|
|
151
|
+
const nextOut = new Set();
|
|
152
|
+
for (const succ of successors[i]) {
|
|
153
|
+
for (const variable of inSets[succ]) {
|
|
154
|
+
nextOut.add(variable);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const nextIn = new Set(useSets[i]);
|
|
158
|
+
const defVar = defVars[i];
|
|
159
|
+
for (const variable of nextOut) {
|
|
160
|
+
if (variable !== defVar) {
|
|
161
|
+
nextIn.add(variable);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const outBefore = outSets[i];
|
|
165
|
+
const inBefore = inSets[i];
|
|
166
|
+
if (outBefore.size !== nextOut.size || [...outBefore].some((v) => !nextOut.has(v))) {
|
|
167
|
+
outSets[i] = nextOut;
|
|
168
|
+
changed = true;
|
|
169
|
+
}
|
|
170
|
+
if (inBefore.size !== nextIn.size || [...inBefore].some((v) => !nextIn.has(v))) {
|
|
171
|
+
inSets[i] = nextIn;
|
|
172
|
+
changed = true;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
let removed = false;
|
|
177
|
+
const optimizedBody = body.filter((_, index) => {
|
|
178
|
+
const defVar = defVars[index];
|
|
179
|
+
if (!defVar) {
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
if (defVar.startsWith('@') || program.boundVariables.has(defVar)) {
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
if (outSets[index].has(defVar)) {
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
removed = true;
|
|
189
|
+
return false;
|
|
190
|
+
});
|
|
191
|
+
return {
|
|
192
|
+
program: {
|
|
193
|
+
body: optimizedBody,
|
|
194
|
+
boundVariables: program.boundVariables,
|
|
195
|
+
},
|
|
196
|
+
changed: removed,
|
|
197
|
+
};
|
|
198
|
+
};
|
|
199
|
+
exports.removeDeadCode = removeDeadCode;
|
|
200
|
+
//# sourceMappingURL=dead-code.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mergeLabelsPass = void 0;
|
|
4
|
+
const merge_labels_1 = require("../merge-labels");
|
|
5
|
+
const mergeLabelsPass = (program) => {
|
|
6
|
+
const merged = (0, merge_labels_1.mergeLabels)(program);
|
|
7
|
+
const changed = merged.body.length !== program.body.length;
|
|
8
|
+
return {
|
|
9
|
+
program: merged,
|
|
10
|
+
changed,
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
exports.mergeLabelsPass = mergeLabelsPass;
|
|
14
|
+
//# sourceMappingURL=merge-labels-pass.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SimplifiedIRProgram } from '../macro/expand-macros';
|
|
2
|
+
export type OptimizerPassResult = {
|
|
3
|
+
program: SimplifiedIRProgram;
|
|
4
|
+
changed: boolean;
|
|
5
|
+
};
|
|
6
|
+
export type OptimizerPass = (program: SimplifiedIRProgram) => OptimizerPassResult;
|
|
7
|
+
export type OptimizeOptions = {
|
|
8
|
+
maxIterations?: number;
|
|
9
|
+
passes?: OptimizerPass[];
|
|
10
|
+
};
|
package/dist/test.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/test.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const build_ir_1 = require("./ir/build-ir");
|
|
4
|
+
const expand_macros_1 = require("./macro/expand-macros");
|
|
5
|
+
const optimizer_1 = require("./optimizer/optimizer");
|
|
6
|
+
const project_1 = require("./ir/project");
|
|
7
|
+
const gen_asm_1 = require("./asm/gen-asm");
|
|
8
|
+
const project = (0, project_1.parseProject)('test.minduscript');
|
|
9
|
+
const ir = (0, build_ir_1.buildIR)(project);
|
|
10
|
+
const expanded = (0, expand_macros_1.expandMacros)(ir);
|
|
11
|
+
const optimized = (0, optimizer_1.optimizeIR)(expanded);
|
|
12
|
+
const asm = (0, gen_asm_1.genASM)(optimized.body);
|
|
13
|
+
console.log(asm);
|
|
14
|
+
//# sourceMappingURL=test.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@minduscript/compiler",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"types": "dist/index.d.ts",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist",
|
|
8
|
+
".npmignore"
|
|
9
|
+
],
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@minduscript/parser": "1.0.0",
|
|
12
|
+
"@minduscript/lexer": "1.0.0"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@types/node": "^25.5.0",
|
|
16
|
+
"rimraf": "^6.1.3",
|
|
17
|
+
"typescript": "^5.9.3"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "rimraf dist && tsc"
|
|
21
|
+
}
|
|
22
|
+
}
|