mlogjs 0.4.9 → 0.4.10
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/dist/Compiler.d.ts +1 -1
- package/dist/CompilerError.d.ts +1 -1
- package/dist/Scope.d.ts +6 -1
- package/dist/index.cjs +116 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +116 -17
- package/dist/index.mjs.map +1 -1
- package/dist/macros/Function.d.ts +1 -1
- package/dist/macros/Unchecked.d.ts +7 -0
- package/dist/operators.d.ts +9 -8
- package/dist/types.d.ts +22 -5
- package/dist/utils/value.d.ts +1 -1
- package/dist/values/DestructuringValue.d.ts +1 -1
- package/dist/values/FunctionValue.d.ts +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -146,7 +146,19 @@ const unaryOperators = [
|
|
|
146
146
|
];
|
|
147
147
|
const updateOperators = ["++", "--"];
|
|
148
148
|
const singleOperators = [...unaryOperators, ...updateOperators];
|
|
149
|
-
const operators = [...leftRightOperators, ...singleOperators];
|
|
149
|
+
const operators = [...leftRightOperators, ...singleOperators];
|
|
150
|
+
const orderIndependentOperators = [
|
|
151
|
+
"!=",
|
|
152
|
+
"!==",
|
|
153
|
+
"&&",
|
|
154
|
+
"&",
|
|
155
|
+
"*",
|
|
156
|
+
"+",
|
|
157
|
+
"==",
|
|
158
|
+
"===",
|
|
159
|
+
"|",
|
|
160
|
+
"||",
|
|
161
|
+
];
|
|
150
162
|
|
|
151
163
|
class VoidValue {
|
|
152
164
|
constructor() {
|
|
@@ -177,18 +189,18 @@ for (const key of operators) {
|
|
|
177
189
|
}
|
|
178
190
|
|
|
179
191
|
class InstructionBase {
|
|
180
|
-
constructor(...args) {
|
|
181
|
-
this.intent = EInstIntent.none;
|
|
182
|
-
this.alwaysRuns = true;
|
|
183
|
-
this._hidden = false;
|
|
184
|
-
this.args = args;
|
|
185
|
-
}
|
|
186
192
|
get hidden() {
|
|
187
193
|
return this._hidden;
|
|
188
194
|
}
|
|
189
195
|
set hidden(value) {
|
|
190
196
|
this._hidden = value;
|
|
191
197
|
}
|
|
198
|
+
constructor(...args) {
|
|
199
|
+
this.intent = EInstIntent.none;
|
|
200
|
+
this.alwaysRuns = true;
|
|
201
|
+
this._hidden = false;
|
|
202
|
+
this.args = args;
|
|
203
|
+
}
|
|
192
204
|
resolve(_i) { }
|
|
193
205
|
toString() {
|
|
194
206
|
return this.args.filter(arg => arg).join(" ");
|
|
@@ -406,14 +418,14 @@ class EndInstruction extends InstructionBase {
|
|
|
406
418
|
}
|
|
407
419
|
|
|
408
420
|
class AddressResolver extends InstructionBase {
|
|
409
|
-
constructor(...bonds) {
|
|
410
|
-
super();
|
|
411
|
-
this.bonds = bonds;
|
|
412
|
-
}
|
|
413
421
|
get hidden() {
|
|
414
422
|
return true;
|
|
415
423
|
}
|
|
416
424
|
set hidden(value) { }
|
|
425
|
+
constructor(...bonds) {
|
|
426
|
+
super();
|
|
427
|
+
this.bonds = bonds;
|
|
428
|
+
}
|
|
417
429
|
resolve(i) {
|
|
418
430
|
for (const literal of this.bonds)
|
|
419
431
|
literal.data = i;
|
|
@@ -625,6 +637,7 @@ class LiteralValue extends BaseValue {
|
|
|
625
637
|
super();
|
|
626
638
|
this.mutability = EMutability.constant;
|
|
627
639
|
this.data = data;
|
|
640
|
+
this.name = this.toString();
|
|
628
641
|
}
|
|
629
642
|
eval(_scope) {
|
|
630
643
|
return [this, []];
|
|
@@ -963,16 +976,18 @@ class LazyValue extends VoidValue {
|
|
|
963
976
|
}
|
|
964
977
|
|
|
965
978
|
class Scope {
|
|
966
|
-
constructor(values, parent = null, stacked = false, ntemp = 0, name = "", inst = []) {
|
|
979
|
+
constructor(values, parent = null, stacked = false, ntemp = 0, name = "", inst = [], operationCache = {}, cacheDependencies = {}) {
|
|
967
980
|
this.parent = parent;
|
|
968
981
|
this.stacked = stacked;
|
|
969
982
|
this.ntemp = ntemp;
|
|
970
983
|
this.name = name;
|
|
971
984
|
this.inst = inst;
|
|
985
|
+
this.operationCache = operationCache;
|
|
986
|
+
this.cacheDependencies = cacheDependencies;
|
|
972
987
|
this.data = values;
|
|
973
988
|
}
|
|
974
989
|
copy() {
|
|
975
|
-
const scope = new Scope({ ...this.data }, this.parent, this.stacked, this.ntemp, this.name, this.inst);
|
|
990
|
+
const scope = new Scope({ ...this.data }, this.parent, this.stacked, this.ntemp, this.name, this.inst, { ...this.operationCache }, { ...this.cacheDependencies });
|
|
976
991
|
scope.break = this.break;
|
|
977
992
|
scope.continue = this.continue;
|
|
978
993
|
scope.function = this.function;
|
|
@@ -981,6 +996,8 @@ class Scope {
|
|
|
981
996
|
createScope() {
|
|
982
997
|
const scope = this.copy();
|
|
983
998
|
scope.data = {};
|
|
999
|
+
scope.operationCache = {};
|
|
1000
|
+
scope.cacheDependencies = {};
|
|
984
1001
|
scope.parent = this;
|
|
985
1002
|
return scope;
|
|
986
1003
|
}
|
|
@@ -1026,6 +1043,46 @@ class Scope {
|
|
|
1026
1043
|
this.ntemp++;
|
|
1027
1044
|
return result;
|
|
1028
1045
|
}
|
|
1046
|
+
addCachedOperation(op, result, left, right) {
|
|
1047
|
+
if (!left.name || !result.name || (right && !right.name))
|
|
1048
|
+
return;
|
|
1049
|
+
const id = formatCacheId(op, left, right);
|
|
1050
|
+
this.operationCache[id] = result;
|
|
1051
|
+
this.clearDependentCache(result);
|
|
1052
|
+
addCacheDependency(this, left.name, id);
|
|
1053
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1054
|
+
if (right)
|
|
1055
|
+
addCacheDependency(this, right.name, id);
|
|
1056
|
+
addCacheDependency(this, result.name, id);
|
|
1057
|
+
}
|
|
1058
|
+
getCachedOperation(op, left, right) {
|
|
1059
|
+
const id = formatCacheId(op, left, right);
|
|
1060
|
+
const result = this.operationCache[id];
|
|
1061
|
+
if (result)
|
|
1062
|
+
return result;
|
|
1063
|
+
if (this.parent)
|
|
1064
|
+
return this.parent.getCachedOperation(op, left, right);
|
|
1065
|
+
}
|
|
1066
|
+
clearDependentCache(value) {
|
|
1067
|
+
if (!value.name)
|
|
1068
|
+
return;
|
|
1069
|
+
const dependents = this.cacheDependencies[value.name];
|
|
1070
|
+
if (dependents) {
|
|
1071
|
+
for (const id of dependents) {
|
|
1072
|
+
delete this.operationCache[id];
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
if (this.parent)
|
|
1076
|
+
this.parent.clearDependentCache(value);
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
function formatCacheId(op, left, right) {
|
|
1080
|
+
if (right)
|
|
1081
|
+
return `{${op}}{${left.name}}{${right.name}}`;
|
|
1082
|
+
return `{${op}}{${left.name}}`;
|
|
1083
|
+
}
|
|
1084
|
+
function addCacheDependency(scope, name, id) {
|
|
1085
|
+
(scope.cacheDependencies[name] ??= []).push(id);
|
|
1029
1086
|
}
|
|
1030
1087
|
|
|
1031
1088
|
const Program = (c, scope, node) => {
|
|
@@ -1201,7 +1258,21 @@ const BooleanLiteral = (_c, scope, node) => [new LiteralValue(+node.value), []];
|
|
|
1201
1258
|
const LRExpression = (c, scope, node, out) => {
|
|
1202
1259
|
const [left, leftInst] = c.handleEval(scope, node.left);
|
|
1203
1260
|
const [right, rightInst] = c.handleEval(scope, node.right);
|
|
1261
|
+
if (!(left instanceof LiteralValue) || !(right instanceof LiteralValue)) {
|
|
1262
|
+
const cachedResult = scope.getCachedOperation(node.operator, left, right);
|
|
1263
|
+
if (cachedResult)
|
|
1264
|
+
return [cachedResult, [...leftInst, ...rightInst]];
|
|
1265
|
+
// because a * b is the same as b * a
|
|
1266
|
+
if (orderIndependentOperators.includes(node.operator)) {
|
|
1267
|
+
const cachedResult = scope.getCachedOperation(node.operator, right, left);
|
|
1268
|
+
if (cachedResult)
|
|
1269
|
+
return [cachedResult, [...leftInst, ...rightInst]];
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1204
1272
|
const [op, opInst] = left[node.operator](scope, right, out);
|
|
1273
|
+
if (!(op instanceof LiteralValue)) {
|
|
1274
|
+
scope.addCachedOperation(node.operator, op, left, right);
|
|
1275
|
+
}
|
|
1205
1276
|
return [op, [...leftInst, ...rightInst, ...opInst]];
|
|
1206
1277
|
};
|
|
1207
1278
|
const BinaryExpression = LRExpression;
|
|
@@ -1223,18 +1294,26 @@ const AssignmentExpression = (c, scope, node) => {
|
|
|
1223
1294
|
];
|
|
1224
1295
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1225
1296
|
const [op, opInst] = left[node.operator](scope, right);
|
|
1297
|
+
if (left)
|
|
1298
|
+
scope.clearDependentCache(left);
|
|
1226
1299
|
return [op, [...leftInst, ...rightInst, ...opInst]];
|
|
1227
1300
|
};
|
|
1228
1301
|
const UnaryExpression = (c, scope, { argument, operator }, out) => {
|
|
1229
1302
|
const [arg, argInst] = c.handleEval(scope, argument);
|
|
1303
|
+
const cachedResult = scope.getCachedOperation(operator, arg);
|
|
1304
|
+
if (cachedResult)
|
|
1305
|
+
return [cachedResult, argInst];
|
|
1230
1306
|
const operatorId = operator == "+" || operator == "-" ? `u${operator}` : operator;
|
|
1231
1307
|
if (operatorId === "throw")
|
|
1232
1308
|
throw new CompilerError("throw operator is not supported");
|
|
1233
1309
|
const [op, opInst] = arg[operatorId](scope, out);
|
|
1310
|
+
scope.addCachedOperation(operator, op, arg);
|
|
1234
1311
|
return [op, [...argInst, ...opInst]];
|
|
1235
1312
|
};
|
|
1236
1313
|
const UpdateExpression = (c, scope, { argument, operator, prefix }, out) => {
|
|
1237
1314
|
const [arg, argInst] = c.handle(scope, argument);
|
|
1315
|
+
if (arg)
|
|
1316
|
+
scope.clearDependentCache(arg);
|
|
1238
1317
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1239
1318
|
const [op, opInst] = arg[operator](scope, prefix, out);
|
|
1240
1319
|
return [op, [...argInst, ...opInst]];
|
|
@@ -1337,9 +1416,12 @@ const IfStatement = (c, scope, node) => {
|
|
|
1337
1416
|
return [null, inst];
|
|
1338
1417
|
}
|
|
1339
1418
|
const endIfAddr = new LiteralValue(null);
|
|
1340
|
-
inst.push(...testInst, new JumpInstruction(endIfAddr, EJumpKind.Equal, test, new LiteralValue(0)), ...withAlwaysRuns(c.handle(scope, node.consequent), false)[1]
|
|
1341
|
-
|
|
1342
|
-
|
|
1419
|
+
inst.push(...testInst, new JumpInstruction(endIfAddr, EJumpKind.Equal, test, new LiteralValue(0)), ...withAlwaysRuns(c.handle(scope, node.consequent), false)[1]);
|
|
1420
|
+
if (!node.alternate) {
|
|
1421
|
+
inst.push(new AddressResolver(endIfAddr));
|
|
1422
|
+
}
|
|
1423
|
+
else {
|
|
1424
|
+
const endElseAddr = new LiteralValue(null);
|
|
1343
1425
|
inst.push(new JumpInstruction(endElseAddr, EJumpKind.Always), new AddressResolver(endIfAddr), ...withAlwaysRuns(c.handle(scope, node.alternate), false)[1], new AddressResolver(endElseAddr));
|
|
1344
1426
|
}
|
|
1345
1427
|
return [null, inst];
|
|
@@ -2043,6 +2125,7 @@ const mathOperations = {
|
|
|
2043
2125
|
sqrt: a => Math.sqrt(a),
|
|
2044
2126
|
rand: null,
|
|
2045
2127
|
};
|
|
2128
|
+
const orderIndependentOperations = ["max", "min", "len"];
|
|
2046
2129
|
function createMacroMathOperations() {
|
|
2047
2130
|
const macroMathOperations = {
|
|
2048
2131
|
PI: new StoreValue("@pi", EMutability.constant),
|
|
@@ -2059,7 +2142,17 @@ function createMacroMathOperations() {
|
|
|
2059
2142
|
throw new CompilerError("Cannot do math operation with non-numerical literals.");
|
|
2060
2143
|
return [new LiteralValue(fn(a.num, b.num)), []];
|
|
2061
2144
|
}
|
|
2145
|
+
const cachedResult = scope.getCachedOperation(key, a, b);
|
|
2146
|
+
if (cachedResult)
|
|
2147
|
+
return [cachedResult, []];
|
|
2148
|
+
// max, min and len do not change if the arguments change in order
|
|
2149
|
+
if (orderIndependentOperations.includes(key)) {
|
|
2150
|
+
const cachedResult = scope.getCachedOperation(key, b, a);
|
|
2151
|
+
if (cachedResult)
|
|
2152
|
+
return [cachedResult, []];
|
|
2153
|
+
}
|
|
2062
2154
|
const temp = StoreValue.from(scope, out);
|
|
2155
|
+
scope.addCachedOperation(key, temp, a, b);
|
|
2063
2156
|
return [temp, [new OperationInstruction(key, temp, a, b)]];
|
|
2064
2157
|
}
|
|
2065
2158
|
if (fn && a instanceof LiteralValue) {
|
|
@@ -2067,8 +2160,14 @@ function createMacroMathOperations() {
|
|
|
2067
2160
|
throw new CompilerError("Cannot do math operation with non-numerical literal.");
|
|
2068
2161
|
return [new LiteralValue(fn(a.num)), []];
|
|
2069
2162
|
}
|
|
2163
|
+
const cachedResult = scope.getCachedOperation(key, a);
|
|
2164
|
+
if (cachedResult)
|
|
2165
|
+
return [cachedResult, []];
|
|
2070
2166
|
const temp = StoreValue.from(scope, out);
|
|
2071
|
-
|
|
2167
|
+
// ensures that operations like `rand` are not cached
|
|
2168
|
+
if (mathOperations[key])
|
|
2169
|
+
scope.addCachedOperation(key, temp, a);
|
|
2170
|
+
return [temp, [new OperationInstruction(key, temp, a, null)]];
|
|
2072
2171
|
});
|
|
2073
2172
|
}
|
|
2074
2173
|
return macroMathOperations;
|