mlogjs 0.2.1 → 0.3.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/LICENSE +1 -1
- package/README.md +2 -2
- package/build/Compiler.js +104 -99
- package/build/Compiler.js.map +1 -1
- package/build/CompilerError.js +11 -0
- package/build/CompilerError.js.map +1 -0
- package/build/Scope.js +70 -60
- package/build/Scope.js.map +1 -1
- package/build/bin.js +74 -60
- package/build/bin.js.map +1 -1
- package/build/handlers/BlockStatement.js +13 -13
- package/build/handlers/BlockStatement.js.map +1 -1
- package/build/handlers/CallExpression.js +19 -19
- package/build/handlers/CallExpression.js.map +1 -1
- package/build/handlers/File.js +9 -0
- package/build/handlers/File.js.map +1 -0
- package/build/handlers/ForStatement.js +31 -31
- package/build/handlers/ForStatement.js.map +1 -1
- package/build/handlers/Function.js +46 -35
- package/build/handlers/Function.js.map +1 -1
- package/build/handlers/Identifier.js +7 -7
- package/build/handlers/IfStatement.js +24 -24
- package/build/handlers/IfStatement.js.map +1 -1
- package/build/handlers/Literal.js +14 -17
- package/build/handlers/Literal.js.map +1 -1
- package/build/handlers/Object.js +54 -43
- package/build/handlers/Object.js.map +1 -1
- package/build/handlers/OperationExpressions.js +37 -32
- package/build/handlers/OperationExpressions.js.map +1 -1
- package/build/handlers/Program.js +8 -8
- package/build/handlers/Program.js.map +1 -1
- package/build/handlers/Statements.js +29 -29
- package/build/handlers/Statements.js.map +1 -1
- package/build/handlers/TemplateExpression.js +17 -17
- package/build/handlers/TemplateExpression.js.map +1 -1
- package/build/handlers/VariableDeclaration.js +71 -55
- package/build/handlers/VariableDeclaration.js.map +1 -1
- package/build/handlers/WhileStatement.js +22 -22
- package/build/handlers/WhileStatement.js.map +1 -1
- package/build/handlers/index.js +31 -30
- package/build/handlers/index.js.map +1 -1
- package/build/index.js +7 -7
- package/build/index.js.map +1 -1
- package/build/initScope.js +39 -39
- package/build/instructions/AddressResolver.js +30 -30
- package/build/instructions/ControlInstructions.js +22 -22
- package/build/instructions/EndInstruction.js +10 -10
- package/build/instructions/InstructionBase.js +20 -20
- package/build/instructions/InstructionBase.js.map +1 -1
- package/build/instructions/JumpInstruction.js +21 -21
- package/build/instructions/JumpInstruction.js.map +1 -1
- package/build/instructions/OperationInstruction.js +10 -10
- package/build/instructions/OperationInstruction.js.map +1 -1
- package/build/instructions/SetCounterInstruction.js +10 -10
- package/build/instructions/SetInstruction.js +10 -10
- package/build/instructions/ZeroSpaceInstruction.js +10 -10
- package/build/instructions/index.js +25 -25
- package/build/macros/Building.js +88 -80
- package/build/macros/Building.js.map +1 -1
- package/build/macros/Function.js +19 -19
- package/build/macros/Function.js.map +1 -1
- package/build/macros/Math.js +61 -56
- package/build/macros/Math.js.map +1 -1
- package/build/macros/Memory.js +62 -60
- package/build/macros/Memory.js.map +1 -1
- package/build/macros/Namespace.js +110 -97
- package/build/macros/Namespace.js.map +1 -1
- package/build/macros/Other.js +20 -19
- package/build/macros/Other.js.map +1 -1
- package/build/macros/RawValue.js +16 -15
- package/build/macros/RawValue.js.map +1 -1
- package/build/macros/commands/Control.js +25 -24
- package/build/macros/commands/Control.js.map +1 -1
- package/build/macros/commands/Draw.js +39 -38
- package/build/macros/commands/Draw.js.map +1 -1
- package/build/macros/commands/DrawFlush.js +17 -16
- package/build/macros/commands/DrawFlush.js.map +1 -1
- package/build/macros/commands/GetLink.js +24 -20
- package/build/macros/commands/GetLink.js.map +1 -1
- package/build/macros/commands/Lookup.js +24 -23
- package/build/macros/commands/Lookup.js.map +1 -1
- package/build/macros/commands/Print.js +17 -13
- package/build/macros/commands/Print.js.map +1 -1
- package/build/macros/commands/PrintFlush.js +17 -16
- package/build/macros/commands/PrintFlush.js.map +1 -1
- package/build/macros/commands/Radar.js +64 -60
- package/build/macros/commands/Radar.js.map +1 -1
- package/build/macros/commands/Sensor.js +20 -19
- package/build/macros/commands/Sensor.js.map +1 -1
- package/build/macros/commands/UnitBind.js +17 -16
- package/build/macros/commands/UnitBind.js.map +1 -1
- package/build/macros/commands/UnitControl.js +72 -67
- package/build/macros/commands/UnitControl.js.map +1 -1
- package/build/macros/commands/UnitLocate.js +75 -54
- package/build/macros/commands/UnitLocate.js.map +1 -1
- package/build/macros/commands/UnitRadar.js +47 -40
- package/build/macros/commands/UnitRadar.js.map +1 -1
- package/build/macros/commands/Wait.js +18 -17
- package/build/macros/commands/Wait.js.map +1 -1
- package/build/macros/commands/index.js +32 -32
- package/build/macros/index.js +39 -39
- package/build/operators.js +101 -101
- package/build/operators.js.map +1 -1
- package/build/types.js +27 -27
- package/build/types.js.map +1 -1
- package/build/utils.js +17 -12
- package/build/utils.js.map +1 -1
- package/build/values/BaseValue.js +111 -111
- package/build/values/BaseValue.js.map +1 -1
- package/build/values/FunctionValue.js +115 -103
- package/build/values/FunctionValue.js.map +1 -1
- package/build/values/LiteralValue.js +88 -87
- package/build/values/LiteralValue.js.map +1 -1
- package/build/values/ObjectValue.js +54 -49
- package/build/values/ObjectValue.js.map +1 -1
- package/build/values/StoreValue.js +31 -30
- package/build/values/StoreValue.js.map +1 -1
- package/build/values/TempValue.js +77 -66
- package/build/values/TempValue.js.map +1 -1
- package/build/values/VoidValue.js +31 -30
- package/build/values/VoidValue.js.map +1 -1
- package/build/values/index.js +22 -22
- package/dist/bin.cjs +82 -0
- package/dist/bin.cjs.map +1 -0
- package/dist/index.cjs +2220 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.mjs +2195 -0
- package/dist/index.mjs.map +1 -0
- package/dist/types/Compiler.d.ts +26 -0
- package/dist/types/CompilerError.d.ts +9 -0
- package/dist/types/Scope.d.ts +24 -0
- package/dist/types/bin.d.ts +1 -0
- package/dist/types/handlers/BlockStatement.d.ts +2 -0
- package/dist/types/handlers/CallExpression.d.ts +3 -0
- package/dist/types/handlers/File.d.ts +2 -0
- package/dist/types/handlers/ForStatement.d.ts +2 -0
- package/{build → dist/types}/handlers/Function.d.ts +5 -4
- package/{build → dist/types}/handlers/Identifier.d.ts +2 -2
- package/dist/types/handlers/IfStatement.d.ts +2 -0
- package/dist/types/handlers/Literal.d.ts +5 -0
- package/{build → dist/types}/handlers/Object.d.ts +6 -4
- package/{build → dist/types}/handlers/OperationExpressions.d.ts +8 -7
- package/dist/types/handlers/Program.d.ts +2 -0
- package/dist/types/handlers/Statements.d.ts +5 -0
- package/dist/types/handlers/TemplateExpression.d.ts +2 -0
- package/dist/types/handlers/Typescript.d.ts +7 -0
- package/dist/types/handlers/VariableDeclaration.d.ts +3 -0
- package/dist/types/handlers/WhileStatement.d.ts +3 -0
- package/{build → dist/types}/handlers/index.d.ts +16 -14
- package/dist/types/index.d.ts +2 -0
- package/{build → dist/types}/initScope.d.ts +5 -2
- package/{build → dist/types}/instructions/AddressResolver.d.ts +12 -12
- package/{build → dist/types}/instructions/ControlInstructions.d.ts +8 -11
- package/{build → dist/types}/instructions/EndInstruction.d.ts +4 -4
- package/{build → dist/types}/instructions/InstructionBase.d.ts +10 -10
- package/{build → dist/types}/instructions/JumpInstruction.d.ts +15 -15
- package/{build → dist/types}/instructions/OperationInstruction.d.ts +5 -5
- package/{build → dist/types}/instructions/SetCounterInstruction.d.ts +5 -5
- package/{build → dist/types}/instructions/SetInstruction.d.ts +6 -5
- package/{build → dist/types}/instructions/ZeroSpaceInstruction.d.ts +4 -4
- package/{build → dist/types}/instructions/index.d.ts +9 -9
- package/{build → dist/types}/macros/Building.d.ts +10 -11
- package/dist/types/macros/Entities.d.ts +11 -0
- package/dist/types/macros/Function.d.ts +13 -0
- package/dist/types/macros/GetGlobal.d.ts +5 -0
- package/{build → dist/types}/macros/Math.d.ts +5 -5
- package/{build → dist/types}/macros/Memory.d.ts +5 -5
- package/{build → dist/types}/macros/Namespace.d.ts +16 -21
- package/{build → dist/types}/macros/Other.d.ts +5 -5
- package/{build → dist/types}/macros/RawValue.d.ts +5 -5
- package/{build → dist/types}/macros/commands/Control.d.ts +5 -5
- package/dist/types/macros/commands/Draw.d.ts +5 -0
- package/{build → dist/types}/macros/commands/DrawFlush.d.ts +5 -5
- package/{build/macros/commands/Draw.d.ts → dist/types/macros/commands/End.d.ts} +5 -5
- package/{build → dist/types}/macros/commands/GetLink.d.ts +5 -5
- package/{build → dist/types}/macros/commands/Lookup.d.ts +5 -5
- package/{build → dist/types}/macros/commands/Print.d.ts +5 -5
- package/{build → dist/types}/macros/commands/PrintFlush.d.ts +5 -5
- package/{build → dist/types}/macros/commands/Radar.d.ts +7 -7
- package/{build → dist/types}/macros/commands/Sensor.d.ts +5 -5
- package/{build → dist/types}/macros/commands/UnitBind.d.ts +5 -5
- package/dist/types/macros/commands/UnitControl.d.ts +5 -0
- package/{build → dist/types}/macros/commands/UnitLocate.d.ts +5 -5
- package/{build → dist/types}/macros/commands/UnitRadar.d.ts +5 -5
- package/{build → dist/types}/macros/commands/Wait.d.ts +5 -5
- package/{build → dist/types}/macros/commands/index.d.ts +15 -14
- package/{build → dist/types}/macros/index.d.ts +6 -7
- package/{build → dist/types}/operators.d.ts +25 -25
- package/dist/types/types.d.ts +224 -0
- package/dist/types/utils.d.ts +30 -0
- package/dist/types/values/BaseValue.d.ts +7 -0
- package/dist/types/values/DestructuringValue.d.ts +17 -0
- package/dist/types/values/FunctionValue.d.ts +47 -0
- package/{build → dist/types}/values/LiteralValue.d.ts +13 -12
- package/dist/types/values/ObjectValue.d.ts +23 -0
- package/dist/types/values/SenseableValue.d.ts +23 -0
- package/dist/types/values/StackValue.d.ts +16 -0
- package/dist/types/values/StoreValue.d.ts +15 -0
- package/{build → dist/types}/values/TempValue.d.ts +19 -12
- package/dist/types/values/ValueOwner.d.ts +27 -0
- package/dist/types/values/VoidValue.d.ts +16 -0
- package/{build → dist/types}/values/index.d.ts +7 -6
- package/lib/commands.d.ts +242 -36
- package/lib/eslib/lib.es5.d.ts +1 -1
- package/lib/globals.d.ts +172 -159
- package/lib/helpers.d.ts +2 -2
- package/lib/memory.d.ts +1 -1
- package/lib/traits.d.ts +130 -110
- package/package.json +33 -38
- package/build/Compiler.d.ts +0 -18
- package/build/Scope.d.ts +0 -26
- package/build/bin.d.ts +0 -2
- package/build/handlers/BlockStatement.d.ts +0 -2
- package/build/handlers/CallExpression.d.ts +0 -3
- package/build/handlers/ForStatement.d.ts +0 -2
- package/build/handlers/IfStatement.d.ts +0 -2
- package/build/handlers/Literal.d.ts +0 -2
- package/build/handlers/Program.d.ts +0 -2
- package/build/handlers/Statements.d.ts +0 -5
- package/build/handlers/TemplateExpression.d.ts +0 -2
- package/build/handlers/VariableDeclaration.d.ts +0 -3
- package/build/handlers/WhileStatement.d.ts +0 -2
- package/build/index.d.ts +0 -1
- package/build/macros/Function.d.ts +0 -12
- package/build/macros/commands/UnitControl.d.ts +0 -5
- package/build/types.d.ts +0 -49
- package/build/utils.d.ts +0 -3
- package/build/values/BaseValue.d.ts +0 -5
- package/build/values/FunctionValue.d.ts +0 -34
- package/build/values/ObjectValue.d.ts +0 -17
- package/build/values/StoreValue.d.ts +0 -11
- package/build/values/VoidValue.d.ts +0 -11
- package/dist/mlogjs.es.js +0 -6366
- package/dist/mlogjs.umd.js +0 -8
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2195 @@
|
|
|
1
|
+
import { parse } from '@babel/parser';
|
|
2
|
+
import * as es from '@babel/types';
|
|
3
|
+
|
|
4
|
+
class CompilerError extends Error {
|
|
5
|
+
constructor(message, nodeStack = []) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.nodeStack = nodeStack;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Defines the possible types of mutability of a value */
|
|
12
|
+
var EMutability;
|
|
13
|
+
(function (EMutability) {
|
|
14
|
+
/**
|
|
15
|
+
* The value can be changed by the user's code,
|
|
16
|
+
* by the mlog runtime, or us
|
|
17
|
+
*/
|
|
18
|
+
EMutability[EMutability["mutable"] = 0] = "mutable";
|
|
19
|
+
/**
|
|
20
|
+
* The value cannot be directly assigned,
|
|
21
|
+
* but it's still not safe from mutations coming from the runtime.
|
|
22
|
+
*/
|
|
23
|
+
EMutability[EMutability["readonly"] = 1] = "readonly";
|
|
24
|
+
/** Constant values, won't change during execution */
|
|
25
|
+
EMutability[EMutability["constant"] = 2] = "constant";
|
|
26
|
+
})(EMutability || (EMutability = {}));
|
|
27
|
+
|
|
28
|
+
const arithmeticBinaryOperators = [
|
|
29
|
+
"*",
|
|
30
|
+
"**",
|
|
31
|
+
"+",
|
|
32
|
+
"-",
|
|
33
|
+
"/",
|
|
34
|
+
"%",
|
|
35
|
+
];
|
|
36
|
+
const comparisonBinaryOperators = [
|
|
37
|
+
"!=",
|
|
38
|
+
"!==",
|
|
39
|
+
"<",
|
|
40
|
+
"<=",
|
|
41
|
+
"==",
|
|
42
|
+
"===",
|
|
43
|
+
">",
|
|
44
|
+
">=",
|
|
45
|
+
];
|
|
46
|
+
const bitwiseBinaryOperators = [
|
|
47
|
+
"&",
|
|
48
|
+
"<<",
|
|
49
|
+
">>",
|
|
50
|
+
">>>",
|
|
51
|
+
"^",
|
|
52
|
+
"|",
|
|
53
|
+
];
|
|
54
|
+
const binaryOperators = [
|
|
55
|
+
...arithmeticBinaryOperators,
|
|
56
|
+
...comparisonBinaryOperators,
|
|
57
|
+
...bitwiseBinaryOperators,
|
|
58
|
+
"instanceof",
|
|
59
|
+
"in",
|
|
60
|
+
];
|
|
61
|
+
const logicalOperators = ["&&", "??", "||"];
|
|
62
|
+
const arithmeticAssignmentOperators = [
|
|
63
|
+
"%=",
|
|
64
|
+
"&=",
|
|
65
|
+
"*=",
|
|
66
|
+
"**=",
|
|
67
|
+
"+=",
|
|
68
|
+
"-=",
|
|
69
|
+
"/=",
|
|
70
|
+
];
|
|
71
|
+
const logicalAssignmentOperators = ["&&=", "||="];
|
|
72
|
+
const bitwiseAssignmentOperators = [
|
|
73
|
+
"<<=",
|
|
74
|
+
">>=",
|
|
75
|
+
">>>=",
|
|
76
|
+
"^=",
|
|
77
|
+
"|=",
|
|
78
|
+
];
|
|
79
|
+
const assignmentOperators = [
|
|
80
|
+
...arithmeticAssignmentOperators,
|
|
81
|
+
...logicalAssignmentOperators,
|
|
82
|
+
...bitwiseAssignmentOperators,
|
|
83
|
+
"=",
|
|
84
|
+
];
|
|
85
|
+
const leftRightOperators = [
|
|
86
|
+
...binaryOperators,
|
|
87
|
+
...logicalOperators,
|
|
88
|
+
...assignmentOperators,
|
|
89
|
+
];
|
|
90
|
+
const unaryOperators = [
|
|
91
|
+
"!",
|
|
92
|
+
"u+",
|
|
93
|
+
"u-",
|
|
94
|
+
"delete",
|
|
95
|
+
"typeof",
|
|
96
|
+
"void",
|
|
97
|
+
"~",
|
|
98
|
+
];
|
|
99
|
+
const updateOperators = ["++", "--"];
|
|
100
|
+
const singleOperators = [...unaryOperators, ...updateOperators];
|
|
101
|
+
const operators = [...leftRightOperators, ...singleOperators];
|
|
102
|
+
|
|
103
|
+
class VoidValue {
|
|
104
|
+
constructor(scope) {
|
|
105
|
+
this.owner = null;
|
|
106
|
+
this.mutability = EMutability.mutable;
|
|
107
|
+
this.macro = false;
|
|
108
|
+
this.scope = scope;
|
|
109
|
+
}
|
|
110
|
+
eval(_scope) {
|
|
111
|
+
throw new CompilerError(`${this} cannot eval.`);
|
|
112
|
+
}
|
|
113
|
+
consume(_scope) {
|
|
114
|
+
throw new CompilerError(`${this} cannot be consumed.`);
|
|
115
|
+
}
|
|
116
|
+
call(_scope, _args) {
|
|
117
|
+
throw new CompilerError(`${this} cannot call.`);
|
|
118
|
+
}
|
|
119
|
+
get(_scope, _name) {
|
|
120
|
+
throw new CompilerError(`${this} cannot get.`);
|
|
121
|
+
}
|
|
122
|
+
ensureOwned() {
|
|
123
|
+
throw new CompilerError(`${this} cannot be owned`);
|
|
124
|
+
}
|
|
125
|
+
toString() {
|
|
126
|
+
return "void";
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
for (const key of operators) {
|
|
130
|
+
VoidValue.prototype[key] = function () {
|
|
131
|
+
console.log(this);
|
|
132
|
+
throw new CompilerError(`${this} cannot '${key}' operation.`);
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
class InstructionBase {
|
|
137
|
+
constructor(...args) {
|
|
138
|
+
this._hidden = false;
|
|
139
|
+
this.args = args;
|
|
140
|
+
}
|
|
141
|
+
get hidden() {
|
|
142
|
+
return this._hidden;
|
|
143
|
+
}
|
|
144
|
+
set hidden(value) {
|
|
145
|
+
this._hidden = value;
|
|
146
|
+
}
|
|
147
|
+
resolve(_i) { }
|
|
148
|
+
toString() {
|
|
149
|
+
return this.args.filter(arg => arg).join(" ");
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
class OperationInstruction extends InstructionBase {
|
|
154
|
+
constructor(kind, temp, left, right) {
|
|
155
|
+
super("op", kind, temp, left, right);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
class SetInstruction extends InstructionBase {
|
|
160
|
+
constructor(store, value) {
|
|
161
|
+
super("set", store, value);
|
|
162
|
+
}
|
|
163
|
+
get hidden() {
|
|
164
|
+
return this._hidden || !this.args[1].owner;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
class EndInstruction extends InstructionBase {
|
|
169
|
+
constructor() {
|
|
170
|
+
super("end");
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
class AddressResolver extends InstructionBase {
|
|
175
|
+
constructor(...bonds) {
|
|
176
|
+
super();
|
|
177
|
+
this.bonds = bonds;
|
|
178
|
+
}
|
|
179
|
+
get hidden() {
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
set hidden(value) { }
|
|
183
|
+
resolve(i) {
|
|
184
|
+
for (const literal of this.bonds)
|
|
185
|
+
literal.data = i;
|
|
186
|
+
}
|
|
187
|
+
bind(bond) {
|
|
188
|
+
this.bonds.push(bond);
|
|
189
|
+
}
|
|
190
|
+
bindBreak(scope) {
|
|
191
|
+
scope.break = this;
|
|
192
|
+
return this;
|
|
193
|
+
}
|
|
194
|
+
bindContinue(scope) {
|
|
195
|
+
scope.continue = this;
|
|
196
|
+
return this;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
var EJumpKind;
|
|
201
|
+
(function (EJumpKind) {
|
|
202
|
+
EJumpKind["Equal"] = "equal";
|
|
203
|
+
EJumpKind["NotEqual"] = "notEqual";
|
|
204
|
+
EJumpKind["LessThan"] = "lessThan";
|
|
205
|
+
EJumpKind["LessThanEq"] = "lessThanEq";
|
|
206
|
+
EJumpKind["GreaterThan"] = "greaterThan";
|
|
207
|
+
EJumpKind["GreaterThanEq"] = "greaterThanEq";
|
|
208
|
+
EJumpKind["StrictEqual"] = "strictEqual";
|
|
209
|
+
EJumpKind["Always"] = "always";
|
|
210
|
+
})(EJumpKind || (EJumpKind = {}));
|
|
211
|
+
class JumpInstruction extends InstructionBase {
|
|
212
|
+
constructor(address, kind, left = null, right = null) {
|
|
213
|
+
super("jump", address, kind, left, right);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
class SetCounterInstruction extends InstructionBase {
|
|
218
|
+
constructor(value) {
|
|
219
|
+
super("set", "@counter", value);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
class ZeroSpaceInstruction extends InstructionBase {
|
|
224
|
+
toString() {
|
|
225
|
+
return this.args.join("");
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
class BreakInstruction extends JumpInstruction {
|
|
230
|
+
constructor(address) {
|
|
231
|
+
super(address, EJumpKind.Always);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
class ContinueInstruction extends JumpInstruction {
|
|
235
|
+
constructor(address) {
|
|
236
|
+
super(address, EJumpKind.Always);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
class ValueOwner {
|
|
241
|
+
/**
|
|
242
|
+
* Creates a new `ValueOwner`, if `value` is already owned,
|
|
243
|
+
* `this` will be aliased to `value`, else `this` will own `value`
|
|
244
|
+
*/
|
|
245
|
+
constructor({ scope, constant = false, value, identifier, name, }) {
|
|
246
|
+
this.scope = scope;
|
|
247
|
+
this.constant = constant;
|
|
248
|
+
this.value = value;
|
|
249
|
+
this.identifier = identifier;
|
|
250
|
+
this.name = name ?? scope.makeTempName();
|
|
251
|
+
this.persistent = !!name;
|
|
252
|
+
this.owned = new Set();
|
|
253
|
+
if (!value.owner?.persistent)
|
|
254
|
+
this.own(value);
|
|
255
|
+
}
|
|
256
|
+
own(target) {
|
|
257
|
+
target.owner = this;
|
|
258
|
+
this.owned.add(target);
|
|
259
|
+
}
|
|
260
|
+
replace(target) {
|
|
261
|
+
this.value.owner = null;
|
|
262
|
+
this.owned.delete(this.value);
|
|
263
|
+
this.value = target;
|
|
264
|
+
this.own(target);
|
|
265
|
+
}
|
|
266
|
+
moveInto(owner) {
|
|
267
|
+
this.owned.forEach(value => owner.own(value));
|
|
268
|
+
this.owned.clear();
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
class BaseValue extends VoidValue {
|
|
273
|
+
"u-"(scope) {
|
|
274
|
+
const [that, inst] = this.consume(scope);
|
|
275
|
+
const temp = new StoreValue(scope);
|
|
276
|
+
return [
|
|
277
|
+
temp,
|
|
278
|
+
[
|
|
279
|
+
...inst,
|
|
280
|
+
new OperationInstruction("sub", temp, new LiteralValue(scope, 0), that),
|
|
281
|
+
],
|
|
282
|
+
];
|
|
283
|
+
}
|
|
284
|
+
"u+"(scope) {
|
|
285
|
+
// TODO: should it return 0 + this ?
|
|
286
|
+
return this.eval(scope);
|
|
287
|
+
}
|
|
288
|
+
ensureOwned() {
|
|
289
|
+
this.owner ??= new ValueOwner({ scope: this.scope, value: this });
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
const operatorMap$1 = {
|
|
293
|
+
"==": "equal",
|
|
294
|
+
"===": "strictEqual",
|
|
295
|
+
"!=": "notEqual",
|
|
296
|
+
"!==": "notEqual",
|
|
297
|
+
"<": "lessThan",
|
|
298
|
+
">": "greaterThan",
|
|
299
|
+
"<=": "lessThanEq",
|
|
300
|
+
">=": "greaterThanEq",
|
|
301
|
+
"+": "add",
|
|
302
|
+
"-": "sub",
|
|
303
|
+
"*": "mul",
|
|
304
|
+
"/": "div",
|
|
305
|
+
"%": "mod",
|
|
306
|
+
"**": "pow",
|
|
307
|
+
"|": "or",
|
|
308
|
+
"&": "and",
|
|
309
|
+
"^": "xor",
|
|
310
|
+
">>": "shr",
|
|
311
|
+
">>>": "shr",
|
|
312
|
+
"<<": "shl",
|
|
313
|
+
"&&": "land",
|
|
314
|
+
"||": "or",
|
|
315
|
+
};
|
|
316
|
+
for (const key in operatorMap$1) {
|
|
317
|
+
const kind = operatorMap$1[key];
|
|
318
|
+
BaseValue.prototype[key] = function (scope, value) {
|
|
319
|
+
const [left, leftInst] = this.consume(scope);
|
|
320
|
+
const [right, rightInst] = value.consume(scope);
|
|
321
|
+
const temp = new StoreValue(scope);
|
|
322
|
+
return [
|
|
323
|
+
temp,
|
|
324
|
+
[
|
|
325
|
+
...leftInst,
|
|
326
|
+
...rightInst,
|
|
327
|
+
new OperationInstruction(kind, temp, left, right),
|
|
328
|
+
],
|
|
329
|
+
];
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
const unaryOperatorMap$1 = {
|
|
333
|
+
"!": "not",
|
|
334
|
+
"~": "flip",
|
|
335
|
+
};
|
|
336
|
+
for (const key in unaryOperatorMap$1) {
|
|
337
|
+
const name = unaryOperatorMap$1[key];
|
|
338
|
+
BaseValue.prototype[key] = function (scope) {
|
|
339
|
+
const [that, inst] = this.consume(scope);
|
|
340
|
+
const temp = new StoreValue(scope);
|
|
341
|
+
return [temp, [...inst, new OperationInstruction(name, temp, that, null)]];
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
const assignmentToBinary = {
|
|
345
|
+
"+=": "+",
|
|
346
|
+
"-=": "-",
|
|
347
|
+
"*=": "*",
|
|
348
|
+
"/=": "/",
|
|
349
|
+
"%=": "%",
|
|
350
|
+
"**=": "**",
|
|
351
|
+
"|=": "|",
|
|
352
|
+
"&=": "&",
|
|
353
|
+
"^=": "^",
|
|
354
|
+
">>=": ">>",
|
|
355
|
+
">>>=": ">>>",
|
|
356
|
+
"<<=": "<<",
|
|
357
|
+
"&&=": "&&",
|
|
358
|
+
"||=": "||",
|
|
359
|
+
};
|
|
360
|
+
for (const op in assignmentToBinary) {
|
|
361
|
+
BaseValue.prototype[op] = function (scope, value) {
|
|
362
|
+
this.ensureOwned();
|
|
363
|
+
const [opValue, opInst] = this[assignmentToBinary[op]](scope, value);
|
|
364
|
+
const [retValue, retInst] = this["="](scope, opValue);
|
|
365
|
+
return [retValue, [...opInst, ...retInst]];
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
for (const key of updateOperators) {
|
|
369
|
+
BaseValue.prototype[key] = function (scope, prefix) {
|
|
370
|
+
let [ret, inst] = this.consume(scope);
|
|
371
|
+
if (!prefix) {
|
|
372
|
+
const temp = new StoreValue(scope);
|
|
373
|
+
const [tempValue, tempInst] = temp["="](scope, ret);
|
|
374
|
+
ret = tempValue;
|
|
375
|
+
inst.push(...tempInst);
|
|
376
|
+
}
|
|
377
|
+
const kind = key === "++" ? "+=" : "-=";
|
|
378
|
+
return [
|
|
379
|
+
ret,
|
|
380
|
+
[...inst, ...this[kind](scope, new LiteralValue(scope, 1))[1]],
|
|
381
|
+
];
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const literalMethods = {
|
|
386
|
+
length: function (scope) {
|
|
387
|
+
if (typeof this.data !== "string")
|
|
388
|
+
throw new CompilerError("Length method only works on string literal values.");
|
|
389
|
+
return new LiteralValue(scope, this.data.length);
|
|
390
|
+
},
|
|
391
|
+
};
|
|
392
|
+
class LiteralValue extends BaseValue {
|
|
393
|
+
constructor(scope, data) {
|
|
394
|
+
super(scope);
|
|
395
|
+
this.mutability = EMutability.constant;
|
|
396
|
+
this.data = data;
|
|
397
|
+
}
|
|
398
|
+
eval(_scope) {
|
|
399
|
+
return [this, []];
|
|
400
|
+
}
|
|
401
|
+
consume(_scope) {
|
|
402
|
+
return [this, []];
|
|
403
|
+
}
|
|
404
|
+
toString() {
|
|
405
|
+
return JSON.stringify(this.data);
|
|
406
|
+
}
|
|
407
|
+
get(scope, name) {
|
|
408
|
+
if (!(name instanceof LiteralValue && typeof name.data === "string"))
|
|
409
|
+
return super.get(scope, name);
|
|
410
|
+
const method = literalMethods[name.data];
|
|
411
|
+
if (!method)
|
|
412
|
+
throw new CompilerError(`Method ${name.data} does not exist on literal values.`);
|
|
413
|
+
return [method.apply(this, [scope]), []];
|
|
414
|
+
}
|
|
415
|
+
get num() {
|
|
416
|
+
if (this.data === null)
|
|
417
|
+
return 0;
|
|
418
|
+
if (typeof this.data === "string")
|
|
419
|
+
return 1;
|
|
420
|
+
return this.data;
|
|
421
|
+
}
|
|
422
|
+
typeof(scope) {
|
|
423
|
+
return [new LiteralValue(scope, "literal"), []];
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
const operatorMap = {
|
|
427
|
+
"==": (a, b) => +(a == b),
|
|
428
|
+
"===": (a, b) => +(a === b),
|
|
429
|
+
"!=": (a, b) => +(a != b),
|
|
430
|
+
"!==": (a, b) => +(a !== b),
|
|
431
|
+
"<": (a, b) => +(a < b),
|
|
432
|
+
">": (a, b) => +(a > b),
|
|
433
|
+
"<=": (a, b) => +(a <= b),
|
|
434
|
+
">=": (a, b) => +(a >= b),
|
|
435
|
+
"+": (a, b) => a + b,
|
|
436
|
+
"-": (a, b) => a - b,
|
|
437
|
+
"*": (a, b) => a * b,
|
|
438
|
+
"/": (a, b) => a / b,
|
|
439
|
+
"%": (a, b) => a % b,
|
|
440
|
+
"**": (a, b) => a ** b,
|
|
441
|
+
"|": (a, b) => a | b,
|
|
442
|
+
"&": (a, b) => a & b,
|
|
443
|
+
"^": (a, b) => a ^ b,
|
|
444
|
+
">>": (a, b) => a >> b,
|
|
445
|
+
">>>": (a, b) => a >> b,
|
|
446
|
+
"<<": (a, b) => a << b,
|
|
447
|
+
"&&": (a, b) => +(a && b),
|
|
448
|
+
"||": (a, b) => +(a || b),
|
|
449
|
+
};
|
|
450
|
+
for (const key in operatorMap) {
|
|
451
|
+
const fn = operatorMap[key];
|
|
452
|
+
LiteralValue.prototype[key] = function (scope, value) {
|
|
453
|
+
if (!(value instanceof LiteralValue)) {
|
|
454
|
+
return BaseValue.prototype[key].apply(this, [scope, value]);
|
|
455
|
+
}
|
|
456
|
+
return [new LiteralValue(scope, fn(this.num, value.num)), []];
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
const unaryOperatorMap = {
|
|
460
|
+
"!": v => +!v,
|
|
461
|
+
"~": v => ~v,
|
|
462
|
+
"u-": v => -v,
|
|
463
|
+
};
|
|
464
|
+
for (const key in unaryOperatorMap) {
|
|
465
|
+
LiteralValue.prototype[key] = function (scope) {
|
|
466
|
+
const fn = unaryOperatorMap[key];
|
|
467
|
+
return [new LiteralValue(scope, fn(this.num)), []];
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* The prefix for internal variables inside the compiler output
|
|
473
|
+
*/
|
|
474
|
+
const internalPrefix = "&";
|
|
475
|
+
const discardedName = `${internalPrefix}_`;
|
|
476
|
+
/**
|
|
477
|
+
* Returns a string that has the format: [line]:[column].
|
|
478
|
+
*
|
|
479
|
+
* If `name` is provided, and it is a string, the resulting string will
|
|
480
|
+
* have the format: [name]:[line]:[column].
|
|
481
|
+
*/
|
|
482
|
+
function nodeName(node, name) {
|
|
483
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
484
|
+
const { line, column } = node.loc.start;
|
|
485
|
+
if (typeof name === "string")
|
|
486
|
+
return `${name}:${line}:${column}`;
|
|
487
|
+
return `${line}:${column}`;
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Converts a camel case string into a dash case one.
|
|
491
|
+
*
|
|
492
|
+
* As an example `camelCase` becomes `camel-case`
|
|
493
|
+
* @example
|
|
494
|
+
* ```
|
|
495
|
+
* camelToDashCase("camelCase") // returns "camel-case"
|
|
496
|
+
* ```
|
|
497
|
+
*/
|
|
498
|
+
function camelToDashCase(name) {
|
|
499
|
+
return name.replace(/[A-Z]/g, str => `-${str.toLowerCase()}`);
|
|
500
|
+
}
|
|
501
|
+
const itemNames = [
|
|
502
|
+
"copper",
|
|
503
|
+
"lead",
|
|
504
|
+
"metaglass",
|
|
505
|
+
"graphite",
|
|
506
|
+
"sand",
|
|
507
|
+
"coal",
|
|
508
|
+
"titanium",
|
|
509
|
+
"thorium",
|
|
510
|
+
"scrap",
|
|
511
|
+
"silicon",
|
|
512
|
+
"plastanium",
|
|
513
|
+
"phaseFabric",
|
|
514
|
+
"surgeAlloy",
|
|
515
|
+
"sporePod",
|
|
516
|
+
"blastCompound",
|
|
517
|
+
"pyratite",
|
|
518
|
+
];
|
|
519
|
+
/**
|
|
520
|
+
* A more type safe version of `Object.assign`
|
|
521
|
+
*/
|
|
522
|
+
function assign(obj, props) {
|
|
523
|
+
return Object.assign(obj, props);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* `StoreValue` represents values unknown at compile time,
|
|
528
|
+
* mostly used with mutable variables and temporary values
|
|
529
|
+
*/
|
|
530
|
+
class StoreValue extends BaseValue {
|
|
531
|
+
constructor(scope) {
|
|
532
|
+
super(scope);
|
|
533
|
+
this.mutability = EMutability.mutable;
|
|
534
|
+
}
|
|
535
|
+
typeof(scope) {
|
|
536
|
+
return [new LiteralValue(scope, "store"), []];
|
|
537
|
+
}
|
|
538
|
+
"="(scope, value) {
|
|
539
|
+
if (this.mutability !== EMutability.mutable)
|
|
540
|
+
throw new CompilerError(`Cannot assign to immutable store '${this}'.`);
|
|
541
|
+
if (this.owner) {
|
|
542
|
+
if (this.owner === value.owner && value instanceof StoreValue)
|
|
543
|
+
return [this, []];
|
|
544
|
+
if (!value.owner?.persistent) {
|
|
545
|
+
if (!value.owner)
|
|
546
|
+
this.owner.own(value);
|
|
547
|
+
else
|
|
548
|
+
value.owner.moveInto(this.owner);
|
|
549
|
+
if (value instanceof StoreValue)
|
|
550
|
+
return [this, []];
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
const [evalValue, evalInst] = value.consume(scope);
|
|
554
|
+
return [this, [...evalInst, new SetInstruction(this, evalValue)]];
|
|
555
|
+
}
|
|
556
|
+
eval(_scope) {
|
|
557
|
+
return [this, []];
|
|
558
|
+
}
|
|
559
|
+
consume(_scope) {
|
|
560
|
+
this.ensureOwned();
|
|
561
|
+
return [this, []];
|
|
562
|
+
}
|
|
563
|
+
toString() {
|
|
564
|
+
return this.owner?.name ?? discardedName;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
class ObjectValue extends VoidValue {
|
|
569
|
+
constructor(scope, data = {}) {
|
|
570
|
+
super(scope);
|
|
571
|
+
this.mutability = EMutability.constant;
|
|
572
|
+
this.macro = true;
|
|
573
|
+
this.data = data;
|
|
574
|
+
}
|
|
575
|
+
static fromArray(scope, items) {
|
|
576
|
+
const data = {
|
|
577
|
+
length: new LiteralValue(scope, items.length),
|
|
578
|
+
};
|
|
579
|
+
items.forEach((item, i) => {
|
|
580
|
+
if (item)
|
|
581
|
+
data[i] = item;
|
|
582
|
+
});
|
|
583
|
+
return new ObjectValue(scope, data);
|
|
584
|
+
}
|
|
585
|
+
typeof(scope) {
|
|
586
|
+
return [new LiteralValue(scope, "object"), []];
|
|
587
|
+
}
|
|
588
|
+
get(scope, key) {
|
|
589
|
+
// avoids naming collisions with keys like
|
|
590
|
+
// constructor or toString
|
|
591
|
+
if (Object.prototype.hasOwnProperty.call(this.data, key.data)) {
|
|
592
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
593
|
+
const member = this.data[key.data];
|
|
594
|
+
return [member, []];
|
|
595
|
+
}
|
|
596
|
+
const { $get } = this.data;
|
|
597
|
+
if (!$get)
|
|
598
|
+
throw new CompilerError("Cannot get undefined member.");
|
|
599
|
+
return $get.call(scope, [key]);
|
|
600
|
+
}
|
|
601
|
+
eval(scope) {
|
|
602
|
+
const { $eval } = this.data;
|
|
603
|
+
if (!$eval)
|
|
604
|
+
return [this, []];
|
|
605
|
+
return $eval.call(scope, []);
|
|
606
|
+
}
|
|
607
|
+
consume(scope) {
|
|
608
|
+
const { $consume } = this.data;
|
|
609
|
+
if ($consume)
|
|
610
|
+
return $consume.call(scope, []);
|
|
611
|
+
const result = this.eval(scope);
|
|
612
|
+
// required by typescript
|
|
613
|
+
const res = result[0];
|
|
614
|
+
res.ensureOwned();
|
|
615
|
+
return result;
|
|
616
|
+
}
|
|
617
|
+
call(scope, args) {
|
|
618
|
+
const { $call } = this.data;
|
|
619
|
+
if (!$call)
|
|
620
|
+
return super.call(scope, args);
|
|
621
|
+
return $call.call(scope, args);
|
|
622
|
+
}
|
|
623
|
+
ensureOwned() {
|
|
624
|
+
this.owner ??= new ValueOwner({ scope: this.scope, value: this });
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
for (const op of operators) {
|
|
628
|
+
ObjectValue.prototype[op] = function (...args) {
|
|
629
|
+
const $ = this.data[`$${op}`];
|
|
630
|
+
// eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/no-unsafe-return
|
|
631
|
+
if (!$)
|
|
632
|
+
return VoidValue.prototype[op].apply(this, args);
|
|
633
|
+
const [scope, ...fnArgs] = args;
|
|
634
|
+
return $.call(scope, fnArgs);
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* Properties that should return a new `SenseableValue`
|
|
640
|
+
*/
|
|
641
|
+
const senseableProps = ["controller"];
|
|
642
|
+
/**
|
|
643
|
+
* Senseable values are "objects" in the mlog runtime that
|
|
644
|
+
* have special properties that can be accessed
|
|
645
|
+
* via the `sense` instruction.
|
|
646
|
+
*
|
|
647
|
+
* This class extends `StoreValue` to allow dynamic behavior
|
|
648
|
+
* that is not possible with object macros.
|
|
649
|
+
*
|
|
650
|
+
* Note that these values are constant by default, so make sure
|
|
651
|
+
* to set that field to `false` if you want them to be assignable. Example:
|
|
652
|
+
* ```
|
|
653
|
+
* const value = assign(new SenseableValue(scope), {
|
|
654
|
+
* mutability: EMutability.mutable
|
|
655
|
+
* })
|
|
656
|
+
* ```
|
|
657
|
+
*/
|
|
658
|
+
class SenseableValue extends StoreValue {
|
|
659
|
+
constructor(scope) {
|
|
660
|
+
super(scope);
|
|
661
|
+
this.mutability = EMutability.constant;
|
|
662
|
+
}
|
|
663
|
+
get(scope, prop) {
|
|
664
|
+
if (prop instanceof LiteralValue && typeof prop.data === "string") {
|
|
665
|
+
const name = itemNames.includes(prop.data)
|
|
666
|
+
? camelToDashCase(prop.data)
|
|
667
|
+
: prop.data;
|
|
668
|
+
const result = senseableProps.includes(prop.data)
|
|
669
|
+
? new SenseableValue(scope)
|
|
670
|
+
: new StoreValue(scope);
|
|
671
|
+
return [
|
|
672
|
+
result,
|
|
673
|
+
[new InstructionBase("sensor", result, this, `@${name}`)],
|
|
674
|
+
];
|
|
675
|
+
}
|
|
676
|
+
if (prop instanceof StoreValue) {
|
|
677
|
+
const temp = new StoreValue(scope);
|
|
678
|
+
return [temp, [new InstructionBase("sensor", temp, this, prop)]];
|
|
679
|
+
}
|
|
680
|
+
throw new CompilerError(`Cannot sense "${prop}" property`);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* Specific case class, required on the ArrayPattern and ObjectPattern handlers
|
|
686
|
+
* in order to keep the separation of concerns and make the code more modular.
|
|
687
|
+
*
|
|
688
|
+
* When it is assign a value, it will get it's own properties and recursively assign
|
|
689
|
+
* each of them to their counterparts on the right hand value.
|
|
690
|
+
*/
|
|
691
|
+
class DestructuringValue extends VoidValue {
|
|
692
|
+
constructor(scope, members) {
|
|
693
|
+
super(scope);
|
|
694
|
+
this.members = members;
|
|
695
|
+
this.macro = true;
|
|
696
|
+
}
|
|
697
|
+
"="(scope, right) {
|
|
698
|
+
const inst = [];
|
|
699
|
+
for (const [key, value] of this.members) {
|
|
700
|
+
const [item, itemInst] = right.get(scope, key);
|
|
701
|
+
inst.push(...itemInst);
|
|
702
|
+
inst.push(...value["="](scope, item)[1]);
|
|
703
|
+
}
|
|
704
|
+
return [right, inst];
|
|
705
|
+
}
|
|
706
|
+
eval(_scope) {
|
|
707
|
+
return [this, []];
|
|
708
|
+
}
|
|
709
|
+
consume(_scope) {
|
|
710
|
+
return [this, []];
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
class Scope {
|
|
715
|
+
constructor(values, parent = null, stacked = false, ntemp = 0, name = "", inst = []) {
|
|
716
|
+
this.parent = parent;
|
|
717
|
+
this.stacked = stacked;
|
|
718
|
+
this.ntemp = ntemp;
|
|
719
|
+
this.name = name;
|
|
720
|
+
this.inst = inst;
|
|
721
|
+
this.data = values;
|
|
722
|
+
}
|
|
723
|
+
copy() {
|
|
724
|
+
const scope = new Scope({ ...this.data }, this.parent, this.stacked, this.ntemp, this.name, this.inst);
|
|
725
|
+
scope.break = this.break;
|
|
726
|
+
scope.continue = this.continue;
|
|
727
|
+
scope.function = this.function;
|
|
728
|
+
return scope;
|
|
729
|
+
}
|
|
730
|
+
createScope() {
|
|
731
|
+
const scope = this.copy();
|
|
732
|
+
scope.data = {};
|
|
733
|
+
scope.parent = this;
|
|
734
|
+
return scope;
|
|
735
|
+
}
|
|
736
|
+
createFunction(name, stacked = this.stacked) {
|
|
737
|
+
return new Scope({}, this, stacked, 0, name, this.inst);
|
|
738
|
+
}
|
|
739
|
+
has(identifier) {
|
|
740
|
+
if (identifier in this.data)
|
|
741
|
+
return true;
|
|
742
|
+
if (this.parent)
|
|
743
|
+
return this.parent.has(identifier);
|
|
744
|
+
return false;
|
|
745
|
+
}
|
|
746
|
+
get(identifier) {
|
|
747
|
+
const owner = this.data[identifier];
|
|
748
|
+
if (owner)
|
|
749
|
+
return owner.value;
|
|
750
|
+
if (this.parent)
|
|
751
|
+
return this.parent.get(identifier);
|
|
752
|
+
throw new CompilerError(`${identifier} is not declared.`);
|
|
753
|
+
}
|
|
754
|
+
set(...args) {
|
|
755
|
+
const name = args.length === 1 ? args[0].identifier : args[0];
|
|
756
|
+
if (!name)
|
|
757
|
+
throw new CompilerError("Values in a scope must have an identifier");
|
|
758
|
+
if (name in this.data)
|
|
759
|
+
throw new CompilerError(`${name} is already declared.`);
|
|
760
|
+
return this.hardSet(...args);
|
|
761
|
+
}
|
|
762
|
+
hardSet(...args) {
|
|
763
|
+
const owner = args.length === 1
|
|
764
|
+
? args[0]
|
|
765
|
+
: new ValueOwner({
|
|
766
|
+
scope: this,
|
|
767
|
+
identifier: args[0],
|
|
768
|
+
name: args[0],
|
|
769
|
+
value: args[1],
|
|
770
|
+
});
|
|
771
|
+
const { identifier } = owner;
|
|
772
|
+
if (!identifier)
|
|
773
|
+
throw new CompilerError("Values in a scope must have an identifier");
|
|
774
|
+
this.data[identifier] = owner;
|
|
775
|
+
return owner.value;
|
|
776
|
+
}
|
|
777
|
+
make(identifier, name) {
|
|
778
|
+
const owner = new ValueOwner({
|
|
779
|
+
scope: this,
|
|
780
|
+
value: assign(new SenseableValue(this), {
|
|
781
|
+
mutability: EMutability.mutable,
|
|
782
|
+
}),
|
|
783
|
+
identifier,
|
|
784
|
+
name,
|
|
785
|
+
});
|
|
786
|
+
return this.set(owner);
|
|
787
|
+
}
|
|
788
|
+
makeTempName() {
|
|
789
|
+
let result = `${internalPrefix}t${this.ntemp}`;
|
|
790
|
+
if (this.name)
|
|
791
|
+
result += `:${this.name}`;
|
|
792
|
+
this.ntemp++;
|
|
793
|
+
return result;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
const Program = (c, scope, node) => {
|
|
798
|
+
return c.handleMany(scope ?? new Scope({}), node.body);
|
|
799
|
+
};
|
|
800
|
+
|
|
801
|
+
const VariableDeclaration = (c, scope, node) => {
|
|
802
|
+
return c.handleMany(scope, node.declarations, (scope, child) => VariableDeclarator(c, scope, child, node.kind));
|
|
803
|
+
};
|
|
804
|
+
const VariableDeclarator = (c, scope, node, kind = "let") => {
|
|
805
|
+
const [init, inst] = node.init
|
|
806
|
+
? c.handleEval(scope, node.init)
|
|
807
|
+
: [null, []];
|
|
808
|
+
inst.push(...Declare(c, scope, node.id, kind, init)[1]);
|
|
809
|
+
return [null, inst];
|
|
810
|
+
};
|
|
811
|
+
const Declare = (c, scope, node, kind, init) => {
|
|
812
|
+
switch (node.type) {
|
|
813
|
+
case "Identifier":
|
|
814
|
+
return DeclareIdentifier(c, scope, node, kind, init);
|
|
815
|
+
case "ArrayPattern":
|
|
816
|
+
return DeclareArrayPattern(c, scope, node, kind, init);
|
|
817
|
+
case "ObjectPattern":
|
|
818
|
+
return DeclareObjectPattern(c, scope, node, kind, init);
|
|
819
|
+
default:
|
|
820
|
+
throw new CompilerError(`Unsupported declaration type: ${node.type}`, [
|
|
821
|
+
node,
|
|
822
|
+
]);
|
|
823
|
+
}
|
|
824
|
+
};
|
|
825
|
+
const DeclareIdentifier = (c, scope, node, kind, init) => {
|
|
826
|
+
const { name: identifier } = node;
|
|
827
|
+
const name = nodeName(node, !c.compactNames && identifier);
|
|
828
|
+
if (kind === "const" && !init)
|
|
829
|
+
throw new CompilerError("Constants must be initialized.", [node]);
|
|
830
|
+
if (kind === "const" && init?.mutability === EMutability.constant) {
|
|
831
|
+
const owner = new ValueOwner({
|
|
832
|
+
scope,
|
|
833
|
+
identifier,
|
|
834
|
+
name,
|
|
835
|
+
value: init,
|
|
836
|
+
constant: true,
|
|
837
|
+
});
|
|
838
|
+
scope.set(owner);
|
|
839
|
+
return [null, []];
|
|
840
|
+
}
|
|
841
|
+
else {
|
|
842
|
+
const value = scope.make(identifier, name);
|
|
843
|
+
const inst = [];
|
|
844
|
+
if (init) {
|
|
845
|
+
if (init.macro)
|
|
846
|
+
throw new CompilerError("Macro values must be held by constants", [
|
|
847
|
+
node,
|
|
848
|
+
]);
|
|
849
|
+
inst.push(...value["="](scope, init)[1]);
|
|
850
|
+
}
|
|
851
|
+
if (kind === "const")
|
|
852
|
+
value.mutability = EMutability.constant;
|
|
853
|
+
return [null, inst];
|
|
854
|
+
}
|
|
855
|
+
};
|
|
856
|
+
const DeclareArrayPattern = (c, scope, node, kind, init) => {
|
|
857
|
+
const { elements } = node;
|
|
858
|
+
if (!(init instanceof ObjectValue))
|
|
859
|
+
throw new CompilerError("The value being destructured must be an object value");
|
|
860
|
+
const inst = [];
|
|
861
|
+
for (let i = 0; i < elements.length; i++) {
|
|
862
|
+
const element = elements[i];
|
|
863
|
+
if (!element)
|
|
864
|
+
continue;
|
|
865
|
+
const val = init.data[i];
|
|
866
|
+
if (!val)
|
|
867
|
+
throw new CompilerError(`The target object does not have a value at index ${i}`, [element]);
|
|
868
|
+
inst.push(...Declare(c, scope, element, kind, val)[1]);
|
|
869
|
+
}
|
|
870
|
+
return [null, inst];
|
|
871
|
+
};
|
|
872
|
+
const DeclareObjectPattern = (c, scope, node, kind, base) => {
|
|
873
|
+
if (!base)
|
|
874
|
+
throw new CompilerError("Cannot use object destructuring without an initializer", [node]);
|
|
875
|
+
const { properties } = node;
|
|
876
|
+
const [init, inst] = base.consume(scope);
|
|
877
|
+
for (const prop of properties) {
|
|
878
|
+
if (prop.type === "RestElement")
|
|
879
|
+
throw new CompilerError("The rest operator is not supported", [prop]);
|
|
880
|
+
const { key, value } = prop;
|
|
881
|
+
const child = key.type === "Identifier" && !prop.computed
|
|
882
|
+
? [new LiteralValue(scope, key.name), []]
|
|
883
|
+
: c.handleConsume(scope, prop.key);
|
|
884
|
+
inst.push(...child[1]);
|
|
885
|
+
const propInit = init.get(scope, child[0]);
|
|
886
|
+
inst.push(...propInit[1]);
|
|
887
|
+
inst.push(...Declare(c, scope, value, kind, propInit[0])[1]);
|
|
888
|
+
}
|
|
889
|
+
return [null, inst];
|
|
890
|
+
};
|
|
891
|
+
|
|
892
|
+
const Literal = (_c, scope, node) => [new LiteralValue(scope, node.value), []];
|
|
893
|
+
const NumericLiteral = Literal;
|
|
894
|
+
const StringLiteral = Literal;
|
|
895
|
+
const NullLiteral = (_c, scope) => [
|
|
896
|
+
new LiteralValue(scope, null),
|
|
897
|
+
[],
|
|
898
|
+
];
|
|
899
|
+
const BooleanLiteral = (_c, scope, node) => [new LiteralValue(scope, +node.value), []];
|
|
900
|
+
|
|
901
|
+
const LRExpression = (c, scope, node) => {
|
|
902
|
+
const [left, leftInst] = c.handleConsume(scope, node.left);
|
|
903
|
+
const [right, rightInst] = c.handleConsume(scope, node.right);
|
|
904
|
+
const [op, opInst] = left[node.operator](scope, right);
|
|
905
|
+
return [op, [...leftInst, ...rightInst, ...opInst]];
|
|
906
|
+
};
|
|
907
|
+
const BinaryExpression = LRExpression;
|
|
908
|
+
const LogicalExpression = LRExpression;
|
|
909
|
+
const AssignmentExpression = (c, scope, node) => {
|
|
910
|
+
const [left, leftInst] = c.handle(scope, node.left);
|
|
911
|
+
// doesn't need to consume because the operators already do that
|
|
912
|
+
const [right, rightInst] = c.handleEval(scope, node.right);
|
|
913
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
914
|
+
const [op, opInst] = left[node.operator](scope, right);
|
|
915
|
+
return [op, [...leftInst, ...rightInst, ...opInst]];
|
|
916
|
+
};
|
|
917
|
+
const UnaryExpression = (c, scope, { argument, operator }) => {
|
|
918
|
+
const [arg, argInst] = c.handleConsume(scope, argument);
|
|
919
|
+
const operatorId = operator == "+" || operator == "-" ? `u${operator}` : operator;
|
|
920
|
+
if (operatorId === "throw")
|
|
921
|
+
throw new CompilerError("throw operator is not supported");
|
|
922
|
+
const [op, opInst] = arg[operatorId](scope);
|
|
923
|
+
return [op, [...argInst, ...opInst]];
|
|
924
|
+
};
|
|
925
|
+
const UpdateExpression = (c, scope, { argument, operator, prefix }) => {
|
|
926
|
+
const [arg, argInst] = c.handle(scope, argument);
|
|
927
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
928
|
+
const [op, opInst] = arg[operator](scope, prefix);
|
|
929
|
+
return [op, [...argInst, ...opInst]];
|
|
930
|
+
};
|
|
931
|
+
const ConditionalExpression = (c, scope, node) => {
|
|
932
|
+
const [test, testInst] = c.handleConsume(scope, node.test);
|
|
933
|
+
if (test instanceof LiteralValue) {
|
|
934
|
+
if (test.data)
|
|
935
|
+
return c.handleEval(scope, node.consequent);
|
|
936
|
+
return c.handleEval(scope, node.alternate);
|
|
937
|
+
}
|
|
938
|
+
// TODO: this creates those annoying jumps on the
|
|
939
|
+
// temp counter, specially if you nest ternary operators
|
|
940
|
+
const result = new StoreValue(scope);
|
|
941
|
+
result.ensureOwned();
|
|
942
|
+
const consequent = c.handleEval(scope, node.consequent);
|
|
943
|
+
const alternate = c.handleEval(scope, node.alternate);
|
|
944
|
+
const alternateStartAdress = new LiteralValue(scope, null);
|
|
945
|
+
const endExpressionAdress = new LiteralValue(scope, null);
|
|
946
|
+
return [
|
|
947
|
+
result,
|
|
948
|
+
[
|
|
949
|
+
...testInst,
|
|
950
|
+
new JumpInstruction(alternateStartAdress, EJumpKind.Equal, test, new LiteralValue(scope, 0)),
|
|
951
|
+
...consequent[1],
|
|
952
|
+
...result["="](scope, consequent[0])[1],
|
|
953
|
+
new JumpInstruction(endExpressionAdress, EJumpKind.Always),
|
|
954
|
+
new AddressResolver(alternateStartAdress),
|
|
955
|
+
...alternate[1],
|
|
956
|
+
...result["="](scope, alternate[0])[1],
|
|
957
|
+
new AddressResolver(endExpressionAdress),
|
|
958
|
+
],
|
|
959
|
+
];
|
|
960
|
+
};
|
|
961
|
+
|
|
962
|
+
const Identifier = (c, scope, node) => {
|
|
963
|
+
return [scope.get(node.name), []];
|
|
964
|
+
};
|
|
965
|
+
|
|
966
|
+
const ExpressionStatement = (c, scope, node) => {
|
|
967
|
+
return c.handle(scope, node.expression);
|
|
968
|
+
};
|
|
969
|
+
const BreakStatement = (_, scope) => {
|
|
970
|
+
const addr = new LiteralValue(scope, null);
|
|
971
|
+
scope.break.bind(addr);
|
|
972
|
+
return [null, [new BreakInstruction(addr)]];
|
|
973
|
+
};
|
|
974
|
+
const ContinueStatement = (_, scope) => {
|
|
975
|
+
const addr = new LiteralValue(scope, null);
|
|
976
|
+
scope.continue.bind(addr);
|
|
977
|
+
return [null, [new ContinueInstruction(addr)]];
|
|
978
|
+
};
|
|
979
|
+
const ReturnStatement = (c, scope, node) => {
|
|
980
|
+
const [arg, argInst] = node.argument
|
|
981
|
+
? c.handle(scope, node.argument)
|
|
982
|
+
: [null, []];
|
|
983
|
+
const [ret, retInst] = scope.function.return(scope, arg);
|
|
984
|
+
return [ret, [...argInst, ...retInst]];
|
|
985
|
+
};
|
|
986
|
+
|
|
987
|
+
const IfStatement = (c, scope, node) => {
|
|
988
|
+
const inst = [];
|
|
989
|
+
const [test, testInst] = c.handleConsume(scope, node.test);
|
|
990
|
+
if (test instanceof LiteralValue) {
|
|
991
|
+
if (test.data)
|
|
992
|
+
inst.push(...c.handle(scope, node.consequent)[1]);
|
|
993
|
+
else if (node.alternate)
|
|
994
|
+
inst.push(...c.handle(scope, node.alternate)[1]);
|
|
995
|
+
return [null, inst];
|
|
996
|
+
}
|
|
997
|
+
const endIfAddr = new LiteralValue(scope, null);
|
|
998
|
+
inst.push(...testInst, new JumpInstruction(endIfAddr, EJumpKind.Equal, test, new LiteralValue(scope, 0)), ...c.handle(scope, node.consequent)[1], new AddressResolver(endIfAddr));
|
|
999
|
+
const endElseAddr = new LiteralValue(scope, null);
|
|
1000
|
+
if (node.alternate)
|
|
1001
|
+
inst.push(new JumpInstruction(endElseAddr, EJumpKind.Always), new AddressResolver(endIfAddr), ...c.handle(scope, node.alternate)[1], new AddressResolver(endElseAddr));
|
|
1002
|
+
return [null, inst];
|
|
1003
|
+
};
|
|
1004
|
+
|
|
1005
|
+
const WhileStatement = (c, scope, node) => {
|
|
1006
|
+
const lines = [];
|
|
1007
|
+
const [test, testLines] = c.handleConsume(scope, node.test);
|
|
1008
|
+
const startLoopAddr = new LiteralValue(scope, null);
|
|
1009
|
+
const endLoopAddr = new LiteralValue(scope, null);
|
|
1010
|
+
const startLoopLine = new AddressResolver(startLoopAddr).bindContinue(scope);
|
|
1011
|
+
const endLoopLine = new AddressResolver(endLoopAddr).bindBreak(scope);
|
|
1012
|
+
if (test instanceof LiteralValue) {
|
|
1013
|
+
if (test.data) {
|
|
1014
|
+
lines.push(startLoopLine, ...c.handle(scope, node.body)[1], new JumpInstruction(startLoopAddr, EJumpKind.Always), endLoopLine);
|
|
1015
|
+
}
|
|
1016
|
+
return [null, lines];
|
|
1017
|
+
}
|
|
1018
|
+
lines.push(startLoopLine, ...testLines, new JumpInstruction(endLoopAddr, EJumpKind.Equal, test, new LiteralValue(scope, 0)), ...c.handle(scope, node.body)[1], new JumpInstruction(startLoopAddr, EJumpKind.Always), endLoopLine);
|
|
1019
|
+
return [null, lines];
|
|
1020
|
+
};
|
|
1021
|
+
const DoWhileStatement = (c, scope, node) => {
|
|
1022
|
+
const lines = [];
|
|
1023
|
+
const [test, testLines] = c.handleConsume(scope, node.test);
|
|
1024
|
+
const startLoopAddr = new LiteralValue(scope, null);
|
|
1025
|
+
const startLoopLine = new AddressResolver(startLoopAddr).bindContinue(scope);
|
|
1026
|
+
const [, bodyLines] = c.handle(scope, node.body);
|
|
1027
|
+
if (test instanceof LiteralValue) {
|
|
1028
|
+
if (!test.data)
|
|
1029
|
+
return [null, [...lines, ...bodyLines]];
|
|
1030
|
+
return [
|
|
1031
|
+
null,
|
|
1032
|
+
[
|
|
1033
|
+
...lines,
|
|
1034
|
+
startLoopLine,
|
|
1035
|
+
...bodyLines,
|
|
1036
|
+
new JumpInstruction(startLoopAddr, EJumpKind.Always),
|
|
1037
|
+
],
|
|
1038
|
+
];
|
|
1039
|
+
}
|
|
1040
|
+
lines.push(startLoopLine, ...bodyLines, ...testLines, new JumpInstruction(startLoopAddr, EJumpKind.Equal, test, new LiteralValue(scope, 1)));
|
|
1041
|
+
return [null, lines];
|
|
1042
|
+
};
|
|
1043
|
+
|
|
1044
|
+
const ForStatement = (c, scope, node) => {
|
|
1045
|
+
scope = scope.createScope();
|
|
1046
|
+
const initInst = node.init ? c.handle(scope, node.init)[1] : [];
|
|
1047
|
+
const [test, testLines] = node.test
|
|
1048
|
+
? c.handleConsume(scope, node.test)
|
|
1049
|
+
: [new LiteralValue(scope, 1), []];
|
|
1050
|
+
const updateLines = node.update ? c.handle(scope, node.update)[1] : [];
|
|
1051
|
+
const startLoopAddr = new LiteralValue(scope, null);
|
|
1052
|
+
const endLoopAddr = new LiteralValue(scope, null);
|
|
1053
|
+
const startLoopLine = new AddressResolver(startLoopAddr).bindContinue(scope);
|
|
1054
|
+
const endLoopLine = new AddressResolver(endLoopAddr).bindBreak(scope);
|
|
1055
|
+
return [
|
|
1056
|
+
null,
|
|
1057
|
+
[
|
|
1058
|
+
...initInst,
|
|
1059
|
+
startLoopLine,
|
|
1060
|
+
...testLines,
|
|
1061
|
+
new JumpInstruction(endLoopAddr, EJumpKind.Equal, test, new LiteralValue(scope, 0)),
|
|
1062
|
+
...c.handle(scope, node.body)[1],
|
|
1063
|
+
...updateLines,
|
|
1064
|
+
new JumpInstruction(startLoopAddr, EJumpKind.Always),
|
|
1065
|
+
endLoopLine,
|
|
1066
|
+
],
|
|
1067
|
+
];
|
|
1068
|
+
};
|
|
1069
|
+
|
|
1070
|
+
class FunctionValue extends VoidValue {
|
|
1071
|
+
constructor({ scope, params, body, c, node, }) {
|
|
1072
|
+
super(scope);
|
|
1073
|
+
this.mutability = EMutability.constant;
|
|
1074
|
+
this.macro = true;
|
|
1075
|
+
this.paramOwners = [];
|
|
1076
|
+
this.bundled = false;
|
|
1077
|
+
this.initialized = false;
|
|
1078
|
+
this.body = body;
|
|
1079
|
+
this.c = c;
|
|
1080
|
+
this.node = node;
|
|
1081
|
+
this.params = params;
|
|
1082
|
+
}
|
|
1083
|
+
typeof(scope) {
|
|
1084
|
+
return [new LiteralValue(scope, "function"), []];
|
|
1085
|
+
}
|
|
1086
|
+
initScope(name) {
|
|
1087
|
+
this.childScope = this.scope.createFunction(name);
|
|
1088
|
+
this.childScope.function = this;
|
|
1089
|
+
for (const id of this.params) {
|
|
1090
|
+
const name = nodeName(id, !this.c.compactNames && id.name);
|
|
1091
|
+
const owner = new ValueOwner({
|
|
1092
|
+
scope: this.childScope,
|
|
1093
|
+
value: new StoreValue(this.childScope),
|
|
1094
|
+
identifier: id.name,
|
|
1095
|
+
name,
|
|
1096
|
+
});
|
|
1097
|
+
this.childScope.set(owner);
|
|
1098
|
+
this.paramOwners.push(owner);
|
|
1099
|
+
}
|
|
1100
|
+
this.callSize = this.paramOwners.length + 2;
|
|
1101
|
+
}
|
|
1102
|
+
ensureInit() {
|
|
1103
|
+
if (!this.owner)
|
|
1104
|
+
throw new CompilerError(`Functions must be owned by a variable`);
|
|
1105
|
+
if (this.initialized)
|
|
1106
|
+
return;
|
|
1107
|
+
this.initialized = true;
|
|
1108
|
+
const { name } = this.owner;
|
|
1109
|
+
this.initScope(name);
|
|
1110
|
+
this.addr = new LiteralValue(this.childScope, null);
|
|
1111
|
+
this.temp = new ValueOwner({
|
|
1112
|
+
scope: this.childScope,
|
|
1113
|
+
name: `${internalPrefix}f${name}`,
|
|
1114
|
+
value: assign(new SenseableValue(this.childScope), {
|
|
1115
|
+
mutability: EMutability.mutable,
|
|
1116
|
+
}),
|
|
1117
|
+
});
|
|
1118
|
+
this.ret = new ValueOwner({
|
|
1119
|
+
scope: this.childScope,
|
|
1120
|
+
name: `${internalPrefix}r${name}`,
|
|
1121
|
+
value: new StoreValue(this.childScope),
|
|
1122
|
+
});
|
|
1123
|
+
this.inst = [
|
|
1124
|
+
new AddressResolver(this.addr),
|
|
1125
|
+
...this.c.handle(this.childScope, this.body)[1],
|
|
1126
|
+
];
|
|
1127
|
+
if (!endsWithReturn(this.inst)) {
|
|
1128
|
+
this.inst.push(new SetCounterInstruction(this.ret.value));
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
normalReturn(scope, arg) {
|
|
1132
|
+
const argInst = arg ? this.temp.value["="](scope, arg)[1] : [];
|
|
1133
|
+
return [null, [...argInst, new SetCounterInstruction(this.ret.value)]];
|
|
1134
|
+
}
|
|
1135
|
+
normalCall(scope, args) {
|
|
1136
|
+
if (!this.bundled)
|
|
1137
|
+
this.childScope.inst.push(...this.inst);
|
|
1138
|
+
this.bundled = true;
|
|
1139
|
+
const callAddressLiteral = new LiteralValue(scope, null);
|
|
1140
|
+
const inst = this.paramOwners
|
|
1141
|
+
.map(({ value: param }, i) => param["="](scope, args[i])[1])
|
|
1142
|
+
.reduce((s, c) => s.concat(c), [])
|
|
1143
|
+
.concat(...this.ret.value["="](scope, callAddressLiteral)[1], new JumpInstruction(this.addr, EJumpKind.Always), new AddressResolver(callAddressLiteral));
|
|
1144
|
+
return [this.temp.value, inst];
|
|
1145
|
+
}
|
|
1146
|
+
inlineReturn(scope, arg) {
|
|
1147
|
+
const argInst = arg ? this.inlineTemp.value["="](scope, arg)[1] : [];
|
|
1148
|
+
return [null, argInst];
|
|
1149
|
+
}
|
|
1150
|
+
inlineCall(scope, args) {
|
|
1151
|
+
// create return value
|
|
1152
|
+
this.inlineTemp = new ValueOwner({
|
|
1153
|
+
scope: this.childScope,
|
|
1154
|
+
value: assign(new SenseableValue(scope), {
|
|
1155
|
+
mutability: EMutability.mutable,
|
|
1156
|
+
}),
|
|
1157
|
+
});
|
|
1158
|
+
this.inlineEnd = new LiteralValue(scope, null);
|
|
1159
|
+
// make a copy of the function scope
|
|
1160
|
+
const fnScope = this.childScope.copy();
|
|
1161
|
+
// hard set variables within the function scope
|
|
1162
|
+
this.paramOwners.forEach((owner, i) => {
|
|
1163
|
+
const value = args[i];
|
|
1164
|
+
if (!value.owner?.persistent)
|
|
1165
|
+
owner.own(value);
|
|
1166
|
+
fnScope.hardSet(new ValueOwner({
|
|
1167
|
+
scope: fnScope,
|
|
1168
|
+
value,
|
|
1169
|
+
identifier: owner.identifier,
|
|
1170
|
+
name: owner.name,
|
|
1171
|
+
}));
|
|
1172
|
+
});
|
|
1173
|
+
this.tryingInline = true;
|
|
1174
|
+
const inst = this.c.handle(fnScope, this.body)[1];
|
|
1175
|
+
this.tryingInline = false;
|
|
1176
|
+
// removing useless instruction
|
|
1177
|
+
// get the last instructions
|
|
1178
|
+
// const [jump] = inst.slice(-1);
|
|
1179
|
+
// if (jump instanceof JumpInstruction) {
|
|
1180
|
+
// // remove useless jump
|
|
1181
|
+
// if (jump.args[1] === this.inlineEnd) inst.pop();
|
|
1182
|
+
// }
|
|
1183
|
+
inst.push(new AddressResolver(this.inlineEnd));
|
|
1184
|
+
// return the function value
|
|
1185
|
+
return [this.inlineTemp.value, inst];
|
|
1186
|
+
}
|
|
1187
|
+
call(scope, args) {
|
|
1188
|
+
this.ensureInit();
|
|
1189
|
+
if (args.length !== this.paramOwners.length)
|
|
1190
|
+
throw new CompilerError("Cannot call: argument count not matching.");
|
|
1191
|
+
const inlineCall = this.inlineCall(scope, args);
|
|
1192
|
+
const inlineSize = inlineCall[1].filter(i => !i.hidden).length;
|
|
1193
|
+
if (this.inline || inlineSize <= this.callSize)
|
|
1194
|
+
return inlineCall;
|
|
1195
|
+
return this.normalCall(scope, args);
|
|
1196
|
+
}
|
|
1197
|
+
return(scope, arg) {
|
|
1198
|
+
this.ensureInit();
|
|
1199
|
+
if (arg && arg.macro) {
|
|
1200
|
+
this.inline = true;
|
|
1201
|
+
return [null, []];
|
|
1202
|
+
}
|
|
1203
|
+
if (this.inline || this.tryingInline)
|
|
1204
|
+
return this.inlineReturn(scope, arg);
|
|
1205
|
+
return this.normalReturn(scope, arg);
|
|
1206
|
+
}
|
|
1207
|
+
eval(_scope) {
|
|
1208
|
+
return [this, []];
|
|
1209
|
+
}
|
|
1210
|
+
consume(_scope) {
|
|
1211
|
+
return [this, []];
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
function endsWithReturn(inst) {
|
|
1215
|
+
for (let i = inst.length - 1; i >= 0; i--) {
|
|
1216
|
+
const instruction = inst[i];
|
|
1217
|
+
if (instruction.hidden)
|
|
1218
|
+
continue;
|
|
1219
|
+
return instruction instanceof SetCounterInstruction;
|
|
1220
|
+
}
|
|
1221
|
+
return false;
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
function handleFunctionNode(c, scope, node) {
|
|
1225
|
+
let { params, body } = node;
|
|
1226
|
+
if (es.isExpression(body)) {
|
|
1227
|
+
body = es.blockStatement([es.returnStatement(body)]);
|
|
1228
|
+
}
|
|
1229
|
+
return [
|
|
1230
|
+
new FunctionValue({
|
|
1231
|
+
node,
|
|
1232
|
+
scope,
|
|
1233
|
+
params: params,
|
|
1234
|
+
body,
|
|
1235
|
+
c,
|
|
1236
|
+
}),
|
|
1237
|
+
[],
|
|
1238
|
+
];
|
|
1239
|
+
}
|
|
1240
|
+
const ArrowFunctionExpression = (c, scope, node) => {
|
|
1241
|
+
return handleFunctionNode(c, scope, node);
|
|
1242
|
+
};
|
|
1243
|
+
const FunctionDeclaration = (c, scope, node) => {
|
|
1244
|
+
const identifier = node.id.name;
|
|
1245
|
+
const name = nodeName(node, !c.compactNames && identifier);
|
|
1246
|
+
const functionIns = handleFunctionNode(c, scope, node);
|
|
1247
|
+
const owner = new ValueOwner({
|
|
1248
|
+
scope,
|
|
1249
|
+
value: functionIns[0],
|
|
1250
|
+
identifier,
|
|
1251
|
+
name,
|
|
1252
|
+
});
|
|
1253
|
+
return [scope.set(owner), []];
|
|
1254
|
+
};
|
|
1255
|
+
const FunctionExpression = ArrowFunctionExpression;
|
|
1256
|
+
const ObjectMethod = ArrowFunctionExpression;
|
|
1257
|
+
|
|
1258
|
+
const CallExpression = (c, scope, node) => {
|
|
1259
|
+
const inst = [];
|
|
1260
|
+
const args = node.arguments.map(node => {
|
|
1261
|
+
// TODO: this might be why t0 did not increment
|
|
1262
|
+
// TODO: figure out if the previous todo is still relevant
|
|
1263
|
+
const [v, i] = c.handleConsume(scope, node);
|
|
1264
|
+
inst.push(...i);
|
|
1265
|
+
return v;
|
|
1266
|
+
});
|
|
1267
|
+
const [callee, calleeInst] = c.handleConsume(scope, node.callee);
|
|
1268
|
+
inst.push(...calleeInst);
|
|
1269
|
+
const [callValue, callInst] = callee.call(scope, args);
|
|
1270
|
+
inst.push(...callInst);
|
|
1271
|
+
return [callValue, inst];
|
|
1272
|
+
};
|
|
1273
|
+
const NewExpression = CallExpression;
|
|
1274
|
+
|
|
1275
|
+
const ObjectExpression = (c, scope, node) => {
|
|
1276
|
+
const data = {};
|
|
1277
|
+
const inst = [];
|
|
1278
|
+
for (const prop of node.properties) {
|
|
1279
|
+
if (prop.type === "SpreadElement")
|
|
1280
|
+
throw new CompilerError("Cannot handle spread element", [prop]);
|
|
1281
|
+
if (prop.computed)
|
|
1282
|
+
throw new CompilerError("Cannot handle computed property.", [prop]);
|
|
1283
|
+
const { key } = prop;
|
|
1284
|
+
const value = prop.type === "ObjectProperty" ? prop.value : prop;
|
|
1285
|
+
let index;
|
|
1286
|
+
if (key.type === "Identifier") {
|
|
1287
|
+
index = key.name;
|
|
1288
|
+
}
|
|
1289
|
+
else if (key.type === "StringLiteral" || key.type === "NumericLiteral") {
|
|
1290
|
+
index = `${key.value}`;
|
|
1291
|
+
}
|
|
1292
|
+
else {
|
|
1293
|
+
throw new CompilerError(`Unsupported object key type: ${key.type}`, [
|
|
1294
|
+
key,
|
|
1295
|
+
]);
|
|
1296
|
+
}
|
|
1297
|
+
const [member, memberInst] = c.handleEval(scope, value);
|
|
1298
|
+
const owner = new ValueOwner({
|
|
1299
|
+
scope,
|
|
1300
|
+
constant: true,
|
|
1301
|
+
value: member,
|
|
1302
|
+
});
|
|
1303
|
+
data[index] = owner.value;
|
|
1304
|
+
inst.push(...memberInst);
|
|
1305
|
+
}
|
|
1306
|
+
return [new ObjectValue(scope, data), inst];
|
|
1307
|
+
};
|
|
1308
|
+
const ArrayExpression = (c, scope, node) => {
|
|
1309
|
+
const items = [];
|
|
1310
|
+
const inst = [];
|
|
1311
|
+
node.elements.forEach(element => {
|
|
1312
|
+
const [value, valueInst] = element
|
|
1313
|
+
? c.handleEval(scope, element)
|
|
1314
|
+
: [undefined, []];
|
|
1315
|
+
items.push(value);
|
|
1316
|
+
inst.push(...valueInst);
|
|
1317
|
+
});
|
|
1318
|
+
return [ObjectValue.fromArray(scope, items), inst];
|
|
1319
|
+
};
|
|
1320
|
+
const MemberExpression = (c, scope, node) => {
|
|
1321
|
+
const [obj, objInst] = c.handleConsume(scope, node.object);
|
|
1322
|
+
const [prop, propInst] = node.computed
|
|
1323
|
+
? c.handleConsume(scope, node.property)
|
|
1324
|
+
: [new LiteralValue(scope, node.property.name), []];
|
|
1325
|
+
const [got, gotInst] = obj.get(scope, prop);
|
|
1326
|
+
return [got, [...objInst, ...propInst, ...gotInst]];
|
|
1327
|
+
};
|
|
1328
|
+
const ArrayPattern = (c, scope, node) => {
|
|
1329
|
+
const inst = [];
|
|
1330
|
+
const members = new Map();
|
|
1331
|
+
for (let i = 0; i < node.elements.length; i++) {
|
|
1332
|
+
const element = node.elements[i];
|
|
1333
|
+
if (!element)
|
|
1334
|
+
continue;
|
|
1335
|
+
const valueInst = c.handle(scope, element);
|
|
1336
|
+
if (!valueInst[0])
|
|
1337
|
+
throw new CompilerError("Destructuring element must resolve to a value", [
|
|
1338
|
+
element,
|
|
1339
|
+
]);
|
|
1340
|
+
inst.push(...valueInst[1]);
|
|
1341
|
+
members.set(new LiteralValue(scope, i), valueInst[0]);
|
|
1342
|
+
}
|
|
1343
|
+
return [new DestructuringValue(scope, members), inst];
|
|
1344
|
+
};
|
|
1345
|
+
const ObjectPattern = (c, scope, node) => {
|
|
1346
|
+
const inst = [];
|
|
1347
|
+
const members = new Map();
|
|
1348
|
+
for (const prop of node.properties) {
|
|
1349
|
+
if (prop.type === "RestElement")
|
|
1350
|
+
throw new CompilerError("The rest operator is not supported");
|
|
1351
|
+
if (!prop)
|
|
1352
|
+
continue;
|
|
1353
|
+
const { key } = prop;
|
|
1354
|
+
const keyInst = !prop.computed && key.type === "Identifier"
|
|
1355
|
+
? [new LiteralValue(scope, key.name), []]
|
|
1356
|
+
: c.handleConsume(scope, prop.key);
|
|
1357
|
+
inst.push(...keyInst[1]);
|
|
1358
|
+
const valueInst = c.handle(scope, prop.value);
|
|
1359
|
+
if (!valueInst[0])
|
|
1360
|
+
throw new CompilerError("Destructuring member must resolve to a value", [
|
|
1361
|
+
prop.value,
|
|
1362
|
+
]);
|
|
1363
|
+
inst.push(...valueInst[1]);
|
|
1364
|
+
members.set(keyInst[0], valueInst[0]);
|
|
1365
|
+
}
|
|
1366
|
+
return [new DestructuringValue(scope, members), inst];
|
|
1367
|
+
};
|
|
1368
|
+
|
|
1369
|
+
const TemplateLiteral = (c, scope, node) => {
|
|
1370
|
+
const args = [];
|
|
1371
|
+
const inst = [];
|
|
1372
|
+
node.expressions.forEach((expression, i) => {
|
|
1373
|
+
const [v, vi] = c.handleConsume(scope, expression);
|
|
1374
|
+
inst.push(...vi);
|
|
1375
|
+
args.push(node.quasis[i].value.raw, v);
|
|
1376
|
+
});
|
|
1377
|
+
args.push(node.quasis.slice(-1)[0].value.raw);
|
|
1378
|
+
inst.push(new ZeroSpaceInstruction(...args));
|
|
1379
|
+
return [null, inst];
|
|
1380
|
+
};
|
|
1381
|
+
|
|
1382
|
+
const BlockStatement = (c, scope, node) => {
|
|
1383
|
+
const [value, insts] = c.handleMany(scope.createScope(), node.body);
|
|
1384
|
+
// const cleanInst = []
|
|
1385
|
+
// for (const inst of insts) {
|
|
1386
|
+
// if (inst instanceof BreakInstruction || inst instanceof ContinueInstruction || inst instanceof ReturnInstruction || inst instanceof SetCounterInstruction) break
|
|
1387
|
+
// cleanInst.push(inst)
|
|
1388
|
+
// }
|
|
1389
|
+
return [value, insts];
|
|
1390
|
+
};
|
|
1391
|
+
|
|
1392
|
+
const File = (c, scope, node) => {
|
|
1393
|
+
return c.handle(scope ?? new Scope({}), node.program);
|
|
1394
|
+
};
|
|
1395
|
+
|
|
1396
|
+
const TypeCastExpression = (c, scope, node) => {
|
|
1397
|
+
return c.handle(scope, node.expression);
|
|
1398
|
+
};
|
|
1399
|
+
const TSAsExpression = TypeCastExpression;
|
|
1400
|
+
const TSTypeAssertion = TypeCastExpression;
|
|
1401
|
+
const IgnoredHandler = () => [null, []];
|
|
1402
|
+
const TSInterfaceDeclaration = IgnoredHandler;
|
|
1403
|
+
const TSTypeAliasDeclaration = IgnoredHandler;
|
|
1404
|
+
const TSEnumDeclaration = (c, scope, node) => {
|
|
1405
|
+
if (!node.const)
|
|
1406
|
+
throw new CompilerError("All enums must be declared with the const keyword before enum");
|
|
1407
|
+
let counter = 0;
|
|
1408
|
+
let lastType = "number";
|
|
1409
|
+
const data = {};
|
|
1410
|
+
for (const member of node.members) {
|
|
1411
|
+
if (lastType === "string" && !member.initializer)
|
|
1412
|
+
throw new CompilerError("This enum member must be initialized", [member]);
|
|
1413
|
+
const [value] = member.initializer
|
|
1414
|
+
? c.handleEval(scope, member.initializer)
|
|
1415
|
+
: [new LiteralValue(scope, counter)];
|
|
1416
|
+
if (!(value instanceof LiteralValue))
|
|
1417
|
+
throw new CompilerError("Enum members must contain literal values", [
|
|
1418
|
+
member,
|
|
1419
|
+
]);
|
|
1420
|
+
if (typeof value.data === "number") {
|
|
1421
|
+
lastType = "number";
|
|
1422
|
+
counter = value.data + 1;
|
|
1423
|
+
}
|
|
1424
|
+
else {
|
|
1425
|
+
lastType = "string";
|
|
1426
|
+
}
|
|
1427
|
+
const name = member.id.type === "Identifier" ? member.id.name : member.id.value;
|
|
1428
|
+
data[name] = value;
|
|
1429
|
+
}
|
|
1430
|
+
scope.set(new ValueOwner({
|
|
1431
|
+
scope,
|
|
1432
|
+
value: new ObjectValue(scope, data),
|
|
1433
|
+
constant: true,
|
|
1434
|
+
identifier: node.id.name,
|
|
1435
|
+
name: nodeName(node, !c.compactNames && node.id.name),
|
|
1436
|
+
}));
|
|
1437
|
+
return [null, []];
|
|
1438
|
+
};
|
|
1439
|
+
const TSNonNullExpression = (c, scope, node) => {
|
|
1440
|
+
return c.handle(scope, node.expression);
|
|
1441
|
+
};
|
|
1442
|
+
|
|
1443
|
+
var handlers = /*#__PURE__*/Object.freeze({
|
|
1444
|
+
__proto__: null,
|
|
1445
|
+
Program: Program,
|
|
1446
|
+
VariableDeclaration: VariableDeclaration,
|
|
1447
|
+
VariableDeclarator: VariableDeclarator,
|
|
1448
|
+
NumericLiteral: NumericLiteral,
|
|
1449
|
+
StringLiteral: StringLiteral,
|
|
1450
|
+
NullLiteral: NullLiteral,
|
|
1451
|
+
BooleanLiteral: BooleanLiteral,
|
|
1452
|
+
LRExpression: LRExpression,
|
|
1453
|
+
BinaryExpression: BinaryExpression,
|
|
1454
|
+
LogicalExpression: LogicalExpression,
|
|
1455
|
+
AssignmentExpression: AssignmentExpression,
|
|
1456
|
+
UnaryExpression: UnaryExpression,
|
|
1457
|
+
UpdateExpression: UpdateExpression,
|
|
1458
|
+
ConditionalExpression: ConditionalExpression,
|
|
1459
|
+
Identifier: Identifier,
|
|
1460
|
+
ExpressionStatement: ExpressionStatement,
|
|
1461
|
+
BreakStatement: BreakStatement,
|
|
1462
|
+
ContinueStatement: ContinueStatement,
|
|
1463
|
+
ReturnStatement: ReturnStatement,
|
|
1464
|
+
IfStatement: IfStatement,
|
|
1465
|
+
WhileStatement: WhileStatement,
|
|
1466
|
+
DoWhileStatement: DoWhileStatement,
|
|
1467
|
+
ForStatement: ForStatement,
|
|
1468
|
+
ArrowFunctionExpression: ArrowFunctionExpression,
|
|
1469
|
+
FunctionDeclaration: FunctionDeclaration,
|
|
1470
|
+
FunctionExpression: FunctionExpression,
|
|
1471
|
+
ObjectMethod: ObjectMethod,
|
|
1472
|
+
CallExpression: CallExpression,
|
|
1473
|
+
NewExpression: NewExpression,
|
|
1474
|
+
ObjectExpression: ObjectExpression,
|
|
1475
|
+
ArrayExpression: ArrayExpression,
|
|
1476
|
+
MemberExpression: MemberExpression,
|
|
1477
|
+
ArrayPattern: ArrayPattern,
|
|
1478
|
+
ObjectPattern: ObjectPattern,
|
|
1479
|
+
TemplateLiteral: TemplateLiteral,
|
|
1480
|
+
BlockStatement: BlockStatement,
|
|
1481
|
+
File: File,
|
|
1482
|
+
TSAsExpression: TSAsExpression,
|
|
1483
|
+
TSTypeAssertion: TSTypeAssertion,
|
|
1484
|
+
TSInterfaceDeclaration: TSInterfaceDeclaration,
|
|
1485
|
+
TSTypeAliasDeclaration: TSTypeAliasDeclaration,
|
|
1486
|
+
TSEnumDeclaration: TSEnumDeclaration,
|
|
1487
|
+
TSNonNullExpression: TSNonNullExpression
|
|
1488
|
+
});
|
|
1489
|
+
|
|
1490
|
+
class MacroFunction extends VoidValue {
|
|
1491
|
+
constructor(scope, fn) {
|
|
1492
|
+
super(scope);
|
|
1493
|
+
this.macro = true;
|
|
1494
|
+
this.constant = true;
|
|
1495
|
+
this.fn = fn;
|
|
1496
|
+
}
|
|
1497
|
+
call(scope, args) {
|
|
1498
|
+
return this.fn.apply(this, args);
|
|
1499
|
+
}
|
|
1500
|
+
eval(_scope) {
|
|
1501
|
+
return [this, []];
|
|
1502
|
+
}
|
|
1503
|
+
consume(_scope) {
|
|
1504
|
+
return [this, []];
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
1509
|
+
const mathOperations = {
|
|
1510
|
+
max: (a, b) => Math.max(a, b),
|
|
1511
|
+
min: (a, b) => Math.min(a, b),
|
|
1512
|
+
angle: (x, y) => {
|
|
1513
|
+
const angle = (Math.atan2(y, x) * 180) / Math.PI;
|
|
1514
|
+
return angle < 0 ? angle + 360 : angle;
|
|
1515
|
+
},
|
|
1516
|
+
len: (a, b) => Math.sqrt(a ** 2 + b ** 2),
|
|
1517
|
+
noise: null,
|
|
1518
|
+
abs: a => Math.abs(a),
|
|
1519
|
+
log: a => Math.log(a),
|
|
1520
|
+
log10: a => Math.log10(a),
|
|
1521
|
+
sin: a => Math.sin(a),
|
|
1522
|
+
cos: a => Math.cos(a),
|
|
1523
|
+
tan: a => Math.tan(a),
|
|
1524
|
+
floor: a => Math.floor(a),
|
|
1525
|
+
ceil: a => Math.ceil(a),
|
|
1526
|
+
sqrt: a => Math.sqrt(a),
|
|
1527
|
+
rand: null,
|
|
1528
|
+
};
|
|
1529
|
+
function createMacroMathOperations(scope) {
|
|
1530
|
+
const macroMathOperations = {};
|
|
1531
|
+
for (const key in mathOperations) {
|
|
1532
|
+
const fn = mathOperations[key];
|
|
1533
|
+
macroMathOperations[key] = new MacroFunction(scope, (a, b) => {
|
|
1534
|
+
if (b) {
|
|
1535
|
+
if (fn && a instanceof LiteralValue && b instanceof LiteralValue) {
|
|
1536
|
+
if (typeof a.data !== "number" || typeof b.data !== "number")
|
|
1537
|
+
throw new CompilerError("Cannot do math operation with non-numerical literals.");
|
|
1538
|
+
return [new LiteralValue(scope, fn(a.num, b.num)), []];
|
|
1539
|
+
}
|
|
1540
|
+
const temp = new StoreValue(scope);
|
|
1541
|
+
return [temp, [new OperationInstruction(key, temp, a, b)]];
|
|
1542
|
+
}
|
|
1543
|
+
if (fn && a instanceof LiteralValue) {
|
|
1544
|
+
if (typeof a.data !== "number")
|
|
1545
|
+
throw new CompilerError("Cannot do math operation with non-numerical literal.");
|
|
1546
|
+
return [new LiteralValue(scope, fn(a.num)), []];
|
|
1547
|
+
}
|
|
1548
|
+
const temp = new StoreValue(scope);
|
|
1549
|
+
return [temp, [new OperationInstruction(key, temp, a, b)]];
|
|
1550
|
+
});
|
|
1551
|
+
}
|
|
1552
|
+
return macroMathOperations;
|
|
1553
|
+
}
|
|
1554
|
+
class MlogMath extends ObjectValue {
|
|
1555
|
+
constructor(scope) {
|
|
1556
|
+
super(scope, createMacroMathOperations(scope));
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
// op angle result a b
|
|
1560
|
+
// op len result a b
|
|
1561
|
+
|
|
1562
|
+
class Concat extends MacroFunction {
|
|
1563
|
+
constructor(scope) {
|
|
1564
|
+
super(scope, (...values) => {
|
|
1565
|
+
for (const value of values)
|
|
1566
|
+
if (!(value instanceof LiteralValue))
|
|
1567
|
+
throw new CompilerError("Concat arguments must all be literal values.");
|
|
1568
|
+
return [
|
|
1569
|
+
new LiteralValue(scope, values.map(v => v.data).join("")),
|
|
1570
|
+
[],
|
|
1571
|
+
];
|
|
1572
|
+
});
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
class Draw extends MacroFunction {
|
|
1577
|
+
constructor(scope) {
|
|
1578
|
+
super(scope, (kind, ...args) => {
|
|
1579
|
+
if (!(kind instanceof LiteralValue && typeof kind.data === "string"))
|
|
1580
|
+
throw new CompilerError("Draw kind must be a string literal.");
|
|
1581
|
+
if ([
|
|
1582
|
+
"clear",
|
|
1583
|
+
"color",
|
|
1584
|
+
"stroke",
|
|
1585
|
+
"line",
|
|
1586
|
+
"rect",
|
|
1587
|
+
"lineRect",
|
|
1588
|
+
"poly",
|
|
1589
|
+
"linePoly",
|
|
1590
|
+
"triangle",
|
|
1591
|
+
"image",
|
|
1592
|
+
].indexOf(kind.data) === -1)
|
|
1593
|
+
throw new CompilerError("Invalid draw kind");
|
|
1594
|
+
return [
|
|
1595
|
+
null,
|
|
1596
|
+
[
|
|
1597
|
+
new InstructionBase("draw", kind.data, ...args.map(v => {
|
|
1598
|
+
if (v instanceof LiteralValue && typeof v.data === "string")
|
|
1599
|
+
return v.data;
|
|
1600
|
+
return v;
|
|
1601
|
+
})),
|
|
1602
|
+
],
|
|
1603
|
+
];
|
|
1604
|
+
});
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
class Print extends MacroFunction {
|
|
1609
|
+
constructor(scope) {
|
|
1610
|
+
super(scope, (...values) => {
|
|
1611
|
+
const inst = [];
|
|
1612
|
+
for (const value of values) {
|
|
1613
|
+
inst.push(new InstructionBase("print", value));
|
|
1614
|
+
}
|
|
1615
|
+
return [null, inst];
|
|
1616
|
+
});
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
class PrintFlush extends MacroFunction {
|
|
1621
|
+
constructor(scope) {
|
|
1622
|
+
super(scope, (target) => {
|
|
1623
|
+
if (!(target instanceof SenseableValue))
|
|
1624
|
+
throw new CompilerError("The printflush target must be a senseable value");
|
|
1625
|
+
return [null, [new InstructionBase("printflush", target)]];
|
|
1626
|
+
});
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
class DrawFlush extends MacroFunction {
|
|
1631
|
+
constructor(scope) {
|
|
1632
|
+
super(scope, (target) => {
|
|
1633
|
+
if (!(target instanceof SenseableValue))
|
|
1634
|
+
throw new CompilerError("The drawflush target must be a senseable value");
|
|
1635
|
+
return [null, [new InstructionBase("drawflush", target)]];
|
|
1636
|
+
});
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
class GetLink extends MacroFunction {
|
|
1641
|
+
constructor(scope) {
|
|
1642
|
+
super(scope, (index) => {
|
|
1643
|
+
if (!(index instanceof StoreValue) &&
|
|
1644
|
+
(!(index instanceof LiteralValue) || typeof index.data !== "number"))
|
|
1645
|
+
throw new CompilerError("The getlink index must be a number literal or a store");
|
|
1646
|
+
const outBuild = new SenseableValue(scope);
|
|
1647
|
+
return [outBuild, [new InstructionBase("getlink", outBuild, index)]];
|
|
1648
|
+
});
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
const validKinds$1 = ["enabled", "shoot", "shootp", "config", "color"];
|
|
1653
|
+
class Control extends MacroFunction {
|
|
1654
|
+
constructor(scope) {
|
|
1655
|
+
super(scope, (kind, building, ...args) => {
|
|
1656
|
+
if (!(kind instanceof LiteralValue) || typeof kind.data !== "string")
|
|
1657
|
+
throw new CompilerError("The control kind must be a string literal");
|
|
1658
|
+
if (!validKinds$1.includes(kind.data))
|
|
1659
|
+
throw new CompilerError("Invalid control kind");
|
|
1660
|
+
if (!(building instanceof SenseableValue))
|
|
1661
|
+
throw new CompilerError("The building must be a senseable value");
|
|
1662
|
+
return [
|
|
1663
|
+
null,
|
|
1664
|
+
[new InstructionBase("control", kind.data, building, ...args)],
|
|
1665
|
+
];
|
|
1666
|
+
});
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
const validRadarFilters = [
|
|
1671
|
+
"any",
|
|
1672
|
+
"enemy",
|
|
1673
|
+
"ally",
|
|
1674
|
+
"player",
|
|
1675
|
+
"attacker",
|
|
1676
|
+
"flying",
|
|
1677
|
+
"boss",
|
|
1678
|
+
"ground",
|
|
1679
|
+
];
|
|
1680
|
+
const validRadarSorts = [
|
|
1681
|
+
"distance",
|
|
1682
|
+
"health",
|
|
1683
|
+
"shield",
|
|
1684
|
+
"armor",
|
|
1685
|
+
"maxHealth",
|
|
1686
|
+
];
|
|
1687
|
+
class Radar extends MacroFunction {
|
|
1688
|
+
constructor(scope) {
|
|
1689
|
+
super(scope, (building, filter1, filter2, filter3, order, sort) => {
|
|
1690
|
+
if (!(building instanceof SenseableValue))
|
|
1691
|
+
throw new CompilerError("The building must a senseable value");
|
|
1692
|
+
if (!(filter1 instanceof LiteralValue) ||
|
|
1693
|
+
typeof filter1.data !== "string" ||
|
|
1694
|
+
!(filter2 instanceof LiteralValue) ||
|
|
1695
|
+
typeof filter2.data !== "string" ||
|
|
1696
|
+
!(filter3 instanceof LiteralValue) ||
|
|
1697
|
+
typeof filter3.data !== "string")
|
|
1698
|
+
throw new CompilerError("The filters must be string literals");
|
|
1699
|
+
if (!validRadarFilters.includes(filter1.data))
|
|
1700
|
+
throw new CompilerError("Invalid value for filter1");
|
|
1701
|
+
if (!validRadarFilters.includes(filter2.data))
|
|
1702
|
+
throw new CompilerError("Invalid value for filter2");
|
|
1703
|
+
if (!validRadarFilters.includes(filter3.data))
|
|
1704
|
+
throw new CompilerError("Invalid value for filter3");
|
|
1705
|
+
if (!(order instanceof LiteralValue || order instanceof StoreValue))
|
|
1706
|
+
throw new CompilerError("The radar order must be a literal or a store");
|
|
1707
|
+
if (!(sort instanceof LiteralValue) || typeof sort.data !== "string")
|
|
1708
|
+
throw new CompilerError("The radar sort must be a string literal");
|
|
1709
|
+
if (!validRadarSorts.includes(sort.data))
|
|
1710
|
+
throw new CompilerError("Invalid sort value");
|
|
1711
|
+
const outUnit = new SenseableValue(scope);
|
|
1712
|
+
return [
|
|
1713
|
+
outUnit,
|
|
1714
|
+
[
|
|
1715
|
+
new InstructionBase("radar", filter1.data, filter2.data, filter3.data, sort.data, building, order, outUnit),
|
|
1716
|
+
],
|
|
1717
|
+
];
|
|
1718
|
+
});
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
class Sensor extends MacroFunction {
|
|
1723
|
+
constructor(scope) {
|
|
1724
|
+
super(scope, (property, target) => {
|
|
1725
|
+
if (!(property instanceof StoreValue))
|
|
1726
|
+
throw new CompilerError("The sensor property must be a store");
|
|
1727
|
+
if (!(target instanceof SenseableValue))
|
|
1728
|
+
throw new CompilerError("The sensor target must be a senseable value");
|
|
1729
|
+
const temp = new StoreValue(scope);
|
|
1730
|
+
return [temp, [new InstructionBase("sensor", temp, target, property)]];
|
|
1731
|
+
});
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
class Wait extends MacroFunction {
|
|
1736
|
+
constructor(scope) {
|
|
1737
|
+
super(scope, seconds => {
|
|
1738
|
+
if (!(seconds instanceof StoreValue) &&
|
|
1739
|
+
!(seconds instanceof LiteralValue && typeof seconds.data === "number"))
|
|
1740
|
+
throw new CompilerError("The wait seconds must be either a number literal or a store");
|
|
1741
|
+
return [null, [new InstructionBase("wait", seconds)]];
|
|
1742
|
+
});
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
const validKinds = ["block", "unit", "item", "liquid"];
|
|
1747
|
+
class Lookup extends MacroFunction {
|
|
1748
|
+
constructor(scope) {
|
|
1749
|
+
super(scope, (kind, index) => {
|
|
1750
|
+
if (!(kind instanceof LiteralValue && typeof kind.data === "string"))
|
|
1751
|
+
throw new CompilerError("The lookup kind must be a string literal");
|
|
1752
|
+
if (!validKinds.includes(kind.data))
|
|
1753
|
+
throw new CompilerError("Invalid lookup kind");
|
|
1754
|
+
if (!(index instanceof LiteralValue && typeof index.data === "number") &&
|
|
1755
|
+
!(index instanceof StoreValue))
|
|
1756
|
+
throw new CompilerError("The lookup index must be a number literal or a store");
|
|
1757
|
+
const temp = new StoreValue(scope);
|
|
1758
|
+
return [temp, [new InstructionBase("lookup", kind.data, temp, index)]];
|
|
1759
|
+
});
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
class End extends MacroFunction {
|
|
1764
|
+
constructor(scope) {
|
|
1765
|
+
super(scope, () => [null, [new EndInstruction()]]);
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
class UnitBind extends MacroFunction {
|
|
1770
|
+
constructor(scope) {
|
|
1771
|
+
super(scope, type => {
|
|
1772
|
+
if (!(type instanceof StoreValue))
|
|
1773
|
+
throw new CompilerError("The unitBind type must be a store");
|
|
1774
|
+
return [null, [new InstructionBase("ubind", type)]];
|
|
1775
|
+
});
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
const validModes = [
|
|
1780
|
+
"idle",
|
|
1781
|
+
"stop",
|
|
1782
|
+
"move",
|
|
1783
|
+
"approach",
|
|
1784
|
+
"boost",
|
|
1785
|
+
"pathfind",
|
|
1786
|
+
"target",
|
|
1787
|
+
"targetp",
|
|
1788
|
+
"itemDrop",
|
|
1789
|
+
"itemTake",
|
|
1790
|
+
"payDrop",
|
|
1791
|
+
"payTake",
|
|
1792
|
+
"payEnter",
|
|
1793
|
+
"mine",
|
|
1794
|
+
"flag",
|
|
1795
|
+
"build",
|
|
1796
|
+
"getBlock",
|
|
1797
|
+
"within",
|
|
1798
|
+
];
|
|
1799
|
+
class UnitControl extends MacroFunction {
|
|
1800
|
+
constructor(scope) {
|
|
1801
|
+
super(scope, (mode, ...args) => {
|
|
1802
|
+
if (!(mode instanceof LiteralValue) || typeof mode.data !== "string")
|
|
1803
|
+
throw new CompilerError("The unitControl mode must be a string literal");
|
|
1804
|
+
if (!validModes.includes(mode.data))
|
|
1805
|
+
throw new CompilerError("Invalid unitControl mode");
|
|
1806
|
+
if (args.some(value => !(value instanceof StoreValue || value instanceof LiteralValue)))
|
|
1807
|
+
throw new CompilerError("The others arguments of unitControl must be literals or stores");
|
|
1808
|
+
let result = null;
|
|
1809
|
+
let extraArgs = [];
|
|
1810
|
+
switch (mode.data) {
|
|
1811
|
+
case "getBlock": {
|
|
1812
|
+
const outType = new StoreValue(scope);
|
|
1813
|
+
const outBuilding = new SenseableValue(scope);
|
|
1814
|
+
result = ObjectValue.fromArray(scope, [outType, outBuilding]);
|
|
1815
|
+
extraArgs = [outType, outBuilding, new LiteralValue(scope, 0)];
|
|
1816
|
+
break;
|
|
1817
|
+
}
|
|
1818
|
+
case "within": {
|
|
1819
|
+
const temp = new StoreValue(scope);
|
|
1820
|
+
result = temp;
|
|
1821
|
+
extraArgs = [temp, new LiteralValue(scope, 0)];
|
|
1822
|
+
break;
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
return [
|
|
1826
|
+
result,
|
|
1827
|
+
[new InstructionBase("ucontrol", mode.data, ...args, ...extraArgs)],
|
|
1828
|
+
];
|
|
1829
|
+
});
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
class UnitRadar extends MacroFunction {
|
|
1834
|
+
constructor(scope) {
|
|
1835
|
+
super(scope, (filter1, filter2, filter3, order, sort) => {
|
|
1836
|
+
if (!(filter1 instanceof LiteralValue) ||
|
|
1837
|
+
typeof filter1.data !== "string" ||
|
|
1838
|
+
!(filter2 instanceof LiteralValue) ||
|
|
1839
|
+
typeof filter2.data !== "string" ||
|
|
1840
|
+
!(filter3 instanceof LiteralValue) ||
|
|
1841
|
+
typeof filter3.data !== "string")
|
|
1842
|
+
throw new CompilerError("The filters must be string literals");
|
|
1843
|
+
if (!validRadarFilters.includes(filter1.data))
|
|
1844
|
+
throw new CompilerError("Invalid value for filter1");
|
|
1845
|
+
if (!validRadarFilters.includes(filter2.data))
|
|
1846
|
+
throw new CompilerError("Invalid value for filter2");
|
|
1847
|
+
if (!validRadarFilters.includes(filter3.data))
|
|
1848
|
+
throw new CompilerError("Invalid value for filter3");
|
|
1849
|
+
if (!(order instanceof LiteralValue || order instanceof StoreValue))
|
|
1850
|
+
throw new CompilerError("The radar order must be a literal or a store");
|
|
1851
|
+
if (!(sort instanceof LiteralValue) || typeof sort.data !== "string")
|
|
1852
|
+
throw new CompilerError("The radar sort must be a string literal");
|
|
1853
|
+
if (!validRadarSorts.includes(sort.data))
|
|
1854
|
+
throw new CompilerError("Invalid sort value");
|
|
1855
|
+
const outUnit = new SenseableValue(scope);
|
|
1856
|
+
return [
|
|
1857
|
+
outUnit,
|
|
1858
|
+
[
|
|
1859
|
+
new InstructionBase("uradar", filter1.data, filter2.data, filter3.data, sort.data, "0", // I don't know why, but mindustry requires this extra parameter
|
|
1860
|
+
order, outUnit),
|
|
1861
|
+
],
|
|
1862
|
+
];
|
|
1863
|
+
});
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
const validFinds = ["ore", "building", "spawn", "damaged"];
|
|
1868
|
+
const validBuildingGroups = [
|
|
1869
|
+
"core",
|
|
1870
|
+
"storage",
|
|
1871
|
+
"generator",
|
|
1872
|
+
"turret",
|
|
1873
|
+
"factory",
|
|
1874
|
+
"repair",
|
|
1875
|
+
"rally",
|
|
1876
|
+
"battery",
|
|
1877
|
+
"reactor",
|
|
1878
|
+
];
|
|
1879
|
+
class UnitLocate extends MacroFunction {
|
|
1880
|
+
constructor(scope) {
|
|
1881
|
+
super(scope, (find, ...args) => {
|
|
1882
|
+
if (!(find instanceof LiteralValue && typeof find.data === "string"))
|
|
1883
|
+
throw new CompilerError("The unitLocate find must be a string literal");
|
|
1884
|
+
if (!validFinds.includes(find.data))
|
|
1885
|
+
throw new CompilerError("Invalid find value");
|
|
1886
|
+
if (args.some(value => !(value instanceof StoreValue || value instanceof LiteralValue)))
|
|
1887
|
+
throw new CompilerError("The others arguments of unitLocate must be literals or stores");
|
|
1888
|
+
const outFound = new StoreValue(scope);
|
|
1889
|
+
const outX = new StoreValue(scope);
|
|
1890
|
+
const outY = new StoreValue(scope);
|
|
1891
|
+
const outBuilding = new SenseableValue(scope);
|
|
1892
|
+
const outArgs = [outX, outY, outFound, outBuilding];
|
|
1893
|
+
let inputArgs = [];
|
|
1894
|
+
switch (find.data) {
|
|
1895
|
+
case "ore": {
|
|
1896
|
+
const [ore] = args;
|
|
1897
|
+
inputArgs = [find.data, "core", "true", ore];
|
|
1898
|
+
break;
|
|
1899
|
+
}
|
|
1900
|
+
case "building": {
|
|
1901
|
+
const [group, enemy] = args;
|
|
1902
|
+
if (!(group instanceof LiteralValue && typeof group.data === "string")) {
|
|
1903
|
+
throw new CompilerError("The building group must be a string literal");
|
|
1904
|
+
}
|
|
1905
|
+
if (!validBuildingGroups.includes(group.data))
|
|
1906
|
+
throw new CompilerError("Invalid building group");
|
|
1907
|
+
inputArgs = [find.data, group.data, enemy, "@copper"];
|
|
1908
|
+
break;
|
|
1909
|
+
}
|
|
1910
|
+
case "spawn":
|
|
1911
|
+
case "damaged": {
|
|
1912
|
+
inputArgs = [find.data, "core", "true", "@copper"];
|
|
1913
|
+
break;
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
return [
|
|
1917
|
+
ObjectValue.fromArray(scope, [
|
|
1918
|
+
outFound,
|
|
1919
|
+
outX,
|
|
1920
|
+
outY,
|
|
1921
|
+
...(find.data !== "ore" ? [outBuilding] : []),
|
|
1922
|
+
]),
|
|
1923
|
+
[new InstructionBase("ulocate", ...inputArgs, ...outArgs)],
|
|
1924
|
+
];
|
|
1925
|
+
});
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
class MemoryEntry extends ObjectValue {
|
|
1930
|
+
constructor(scope, mem, prop) {
|
|
1931
|
+
super(scope, {
|
|
1932
|
+
$eval: new MacroFunction(scope, () => {
|
|
1933
|
+
if (this.store)
|
|
1934
|
+
return [this.store, []];
|
|
1935
|
+
const temp = new StoreValue(scope);
|
|
1936
|
+
return [temp, [new InstructionBase("read", temp, mem.cell, prop)]];
|
|
1937
|
+
}),
|
|
1938
|
+
$consume: new MacroFunction(scope, () => {
|
|
1939
|
+
if (this.store)
|
|
1940
|
+
return [this.store, []];
|
|
1941
|
+
this.ensureOwned();
|
|
1942
|
+
this.store = new StoreValue(scope);
|
|
1943
|
+
this.owner.own(this.store);
|
|
1944
|
+
return [
|
|
1945
|
+
this.store,
|
|
1946
|
+
[new InstructionBase("read", this.store, mem.cell, prop)],
|
|
1947
|
+
];
|
|
1948
|
+
}),
|
|
1949
|
+
"$=": new MacroFunction(scope, value => {
|
|
1950
|
+
const [data, dataInst] = value.consume(scope);
|
|
1951
|
+
return [
|
|
1952
|
+
data,
|
|
1953
|
+
[...dataInst, new InstructionBase("write", data, mem.cell, prop)],
|
|
1954
|
+
];
|
|
1955
|
+
}),
|
|
1956
|
+
});
|
|
1957
|
+
this.store = null;
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
for (const operator of operators) {
|
|
1961
|
+
if (operator === "=")
|
|
1962
|
+
continue;
|
|
1963
|
+
MemoryEntry.prototype[operator] = function (...args) {
|
|
1964
|
+
// eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/no-unsafe-return
|
|
1965
|
+
return BaseValue.prototype[operator].apply(this, args);
|
|
1966
|
+
};
|
|
1967
|
+
}
|
|
1968
|
+
class MemoryMacro extends ObjectValue {
|
|
1969
|
+
constructor(scope, cell, size) {
|
|
1970
|
+
super(scope, {
|
|
1971
|
+
$get: new MacroFunction(scope, (prop) => {
|
|
1972
|
+
if (prop instanceof LiteralValue && typeof prop.data !== "number")
|
|
1973
|
+
throw new CompilerError(`Invalid memory object property: "${prop.data}"`);
|
|
1974
|
+
const obj = new MemoryEntry(scope, this, prop);
|
|
1975
|
+
return [obj, []];
|
|
1976
|
+
}),
|
|
1977
|
+
length: size,
|
|
1978
|
+
size,
|
|
1979
|
+
});
|
|
1980
|
+
this.cell = cell;
|
|
1981
|
+
this.name = cell.toString();
|
|
1982
|
+
}
|
|
1983
|
+
toString() {
|
|
1984
|
+
return this.name;
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
class MemoryBuilder extends ObjectValue {
|
|
1988
|
+
constructor(scope) {
|
|
1989
|
+
super(scope, {
|
|
1990
|
+
$call: new MacroFunction(scope, (cell, size = new LiteralValue(scope, 64)) => {
|
|
1991
|
+
if (!(cell instanceof SenseableValue))
|
|
1992
|
+
throw new CompilerError("Memory cell must be a senseable value.");
|
|
1993
|
+
if (!(size instanceof LiteralValue && typeof size.data === "number"))
|
|
1994
|
+
throw new CompilerError("The memory size must be a number literal.");
|
|
1995
|
+
return [new MemoryMacro(scope, cell, size), []];
|
|
1996
|
+
}),
|
|
1997
|
+
});
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
|
|
2001
|
+
const dynamicVars = ["time", "tick"];
|
|
2002
|
+
class NamespaceMacro extends ObjectValue {
|
|
2003
|
+
constructor(scope, { changeCasing = false } = {}) {
|
|
2004
|
+
super(scope, {
|
|
2005
|
+
$get: new MacroFunction(scope, prop => {
|
|
2006
|
+
if (!(prop instanceof LiteralValue) || typeof prop.data !== "string")
|
|
2007
|
+
throw new CompilerError("Cannot use dynamic properties on namespace macros");
|
|
2008
|
+
const symbolName = this.changeCasing
|
|
2009
|
+
? camelToDashCase(prop.data)
|
|
2010
|
+
: prop.data;
|
|
2011
|
+
const mutability = dynamicVars.includes(symbolName)
|
|
2012
|
+
? EMutability.readonly
|
|
2013
|
+
: EMutability.constant;
|
|
2014
|
+
const owner = new ValueOwner({
|
|
2015
|
+
scope,
|
|
2016
|
+
value: assign(new StoreValue(scope), {
|
|
2017
|
+
mutability,
|
|
2018
|
+
}),
|
|
2019
|
+
name: `@${symbolName}`,
|
|
2020
|
+
});
|
|
2021
|
+
return [owner.value, []];
|
|
2022
|
+
}),
|
|
2023
|
+
});
|
|
2024
|
+
this.changeCasing = changeCasing;
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
class VarsNamespace extends NamespaceMacro {
|
|
2028
|
+
constructor(scope) {
|
|
2029
|
+
super(scope);
|
|
2030
|
+
Object.assign(this.data, {
|
|
2031
|
+
unit: new ValueOwner({
|
|
2032
|
+
scope,
|
|
2033
|
+
name: "@unit",
|
|
2034
|
+
value: new SenseableValue(scope),
|
|
2035
|
+
}).value,
|
|
2036
|
+
this: new ValueOwner({
|
|
2037
|
+
scope,
|
|
2038
|
+
name: "@this",
|
|
2039
|
+
value: new SenseableValue(scope),
|
|
2040
|
+
}).value,
|
|
2041
|
+
});
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
class UCommandsNamespace extends NamespaceMacro {
|
|
2045
|
+
constructor(scope) {
|
|
2046
|
+
super(scope);
|
|
2047
|
+
this.data.$get = new MacroFunction(scope, prop => {
|
|
2048
|
+
if (!(prop instanceof LiteralValue) || typeof prop.data !== "string")
|
|
2049
|
+
throw new CompilerError("Cannot use dynamic properties on namespace macros");
|
|
2050
|
+
const symbolName = prop.data[0].toUpperCase() + prop.data.slice(1);
|
|
2051
|
+
const owner = new ValueOwner({
|
|
2052
|
+
scope,
|
|
2053
|
+
name: `@command${symbolName}`,
|
|
2054
|
+
value: assign(new StoreValue(scope), {
|
|
2055
|
+
mutability: EMutability.constant,
|
|
2056
|
+
}),
|
|
2057
|
+
});
|
|
2058
|
+
return [owner.value, []];
|
|
2059
|
+
});
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
|
|
2063
|
+
class GetGlobal extends MacroFunction {
|
|
2064
|
+
constructor(scope, mutability) {
|
|
2065
|
+
super(scope, (name) => {
|
|
2066
|
+
if (!(name instanceof LiteralValue) || typeof name.data !== "string")
|
|
2067
|
+
throw new CompilerError("The name parameter must be a string literal.");
|
|
2068
|
+
const owner = new ValueOwner({
|
|
2069
|
+
scope,
|
|
2070
|
+
name: name.data,
|
|
2071
|
+
value: assign(new SenseableValue(scope), {
|
|
2072
|
+
mutability,
|
|
2073
|
+
}),
|
|
2074
|
+
});
|
|
2075
|
+
return [owner.value, []];
|
|
2076
|
+
});
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2080
|
+
/**
|
|
2081
|
+
* Adds all the compiler globals to `scope`
|
|
2082
|
+
*/
|
|
2083
|
+
function initScope(scope) {
|
|
2084
|
+
// namespaces
|
|
2085
|
+
scope.hardSet("ControlKind", new NamespaceMacro(scope));
|
|
2086
|
+
scope.hardSet("Vars", new VarsNamespace(scope));
|
|
2087
|
+
scope.hardSet("Items", new NamespaceMacro(scope, { changeCasing: true }));
|
|
2088
|
+
scope.hardSet("Liquids", new NamespaceMacro(scope));
|
|
2089
|
+
scope.hardSet("Units", new NamespaceMacro(scope));
|
|
2090
|
+
scope.hardSet("LAccess", new NamespaceMacro(scope));
|
|
2091
|
+
scope.hardSet("UnitCommands", new UCommandsNamespace(scope));
|
|
2092
|
+
scope.hardSet("Blocks", new NamespaceMacro(scope, { changeCasing: true }));
|
|
2093
|
+
// helper methods
|
|
2094
|
+
scope.hardSet("getBuilding", new GetGlobal(scope, EMutability.constant));
|
|
2095
|
+
scope.hardSet("getVar", new GetGlobal(scope, EMutability.mutable));
|
|
2096
|
+
scope.hardSet("concat", new Concat(scope));
|
|
2097
|
+
scope.hardSet("Math", new MlogMath(scope));
|
|
2098
|
+
scope.hardSet("Memory", new MemoryBuilder(scope));
|
|
2099
|
+
// commands
|
|
2100
|
+
scope.hardSet("draw", new Draw(scope));
|
|
2101
|
+
scope.hardSet("print", new Print(scope));
|
|
2102
|
+
scope.hardSet("printFlush", new PrintFlush(scope));
|
|
2103
|
+
scope.hardSet("drawFlush", new DrawFlush(scope));
|
|
2104
|
+
scope.hardSet("getLink", new GetLink(scope));
|
|
2105
|
+
scope.hardSet("control", new Control(scope));
|
|
2106
|
+
scope.hardSet("radar", new Radar(scope));
|
|
2107
|
+
scope.hardSet("sensor", new Sensor(scope));
|
|
2108
|
+
scope.hardSet("wait", new Wait(scope));
|
|
2109
|
+
scope.hardSet("lookup", new Lookup(scope));
|
|
2110
|
+
scope.hardSet("endScript", new End(scope));
|
|
2111
|
+
scope.hardSet("unitBind", new UnitBind(scope));
|
|
2112
|
+
scope.hardSet("unitControl", new UnitControl(scope));
|
|
2113
|
+
scope.hardSet("unitRadar", new UnitRadar(scope));
|
|
2114
|
+
scope.hardSet("unitLocate", new UnitLocate(scope));
|
|
2115
|
+
}
|
|
2116
|
+
|
|
2117
|
+
class Compiler {
|
|
2118
|
+
constructor({ stackName, compactNames = false } = {}) {
|
|
2119
|
+
this.handlers = handlers;
|
|
2120
|
+
this.usingStack = !!stackName;
|
|
2121
|
+
this.stackName = stackName;
|
|
2122
|
+
this.compactNames = compactNames;
|
|
2123
|
+
}
|
|
2124
|
+
compile(script) {
|
|
2125
|
+
let output;
|
|
2126
|
+
try {
|
|
2127
|
+
const program = this.parse(script);
|
|
2128
|
+
const scope = new Scope({});
|
|
2129
|
+
initScope(scope);
|
|
2130
|
+
const valueInst = this.handle(scope, program);
|
|
2131
|
+
valueInst[1].push(new EndInstruction(), ...scope.inst);
|
|
2132
|
+
this.resolve(valueInst);
|
|
2133
|
+
output = this.serialize(valueInst) + "\n";
|
|
2134
|
+
}
|
|
2135
|
+
catch (error) {
|
|
2136
|
+
const nodeStack = error instanceof CompilerError ? error.nodeStack : [];
|
|
2137
|
+
return [null, error, nodeStack];
|
|
2138
|
+
}
|
|
2139
|
+
return [output, null, []];
|
|
2140
|
+
}
|
|
2141
|
+
resolve(valueInst) {
|
|
2142
|
+
let i = 0;
|
|
2143
|
+
for (const inst of valueInst[1]) {
|
|
2144
|
+
inst.resolve(i);
|
|
2145
|
+
if (!inst.hidden)
|
|
2146
|
+
i++;
|
|
2147
|
+
}
|
|
2148
|
+
}
|
|
2149
|
+
serialize(resLines) {
|
|
2150
|
+
const [, inst] = resLines;
|
|
2151
|
+
return inst.filter(l => !l.hidden).join("\n");
|
|
2152
|
+
}
|
|
2153
|
+
parse(script) {
|
|
2154
|
+
return parse(script, {
|
|
2155
|
+
ranges: true,
|
|
2156
|
+
plugins: ["typescript"],
|
|
2157
|
+
});
|
|
2158
|
+
}
|
|
2159
|
+
handle(scope, node) {
|
|
2160
|
+
try {
|
|
2161
|
+
const handler = this.handlers[node.type];
|
|
2162
|
+
if (!handler)
|
|
2163
|
+
throw new CompilerError("Missing handler for " + node.type);
|
|
2164
|
+
return handler(this, scope, node, null);
|
|
2165
|
+
}
|
|
2166
|
+
catch (error) {
|
|
2167
|
+
if (error instanceof CompilerError) {
|
|
2168
|
+
error.nodeStack.push(node);
|
|
2169
|
+
}
|
|
2170
|
+
throw error;
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
handleEval(scope, node) {
|
|
2174
|
+
const [res, inst] = this.handle(scope, node);
|
|
2175
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2176
|
+
const [evaluated, evaluatedInst] = res.eval(scope);
|
|
2177
|
+
return [evaluated, [...inst, ...evaluatedInst]];
|
|
2178
|
+
}
|
|
2179
|
+
handleConsume(scope, node) {
|
|
2180
|
+
const [res, inst] = this.handle(scope, node);
|
|
2181
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2182
|
+
const [evaluated, evaluatedInst] = res.consume(scope);
|
|
2183
|
+
return [evaluated, [...inst, ...evaluatedInst]];
|
|
2184
|
+
}
|
|
2185
|
+
handleMany(scope, nodes, handler = this.handle.bind(this)) {
|
|
2186
|
+
const lines = [];
|
|
2187
|
+
for (const node of nodes) {
|
|
2188
|
+
const [, nodeLines] = handler(scope, node);
|
|
2189
|
+
lines.push(...nodeLines);
|
|
2190
|
+
}
|
|
2191
|
+
return [null, lines];
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
|
|
2195
|
+
export { Compiler, CompilerError };
|