mlogjs 0.4.8 → 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/handlers/Object.d.ts +2 -1
- package/dist/handlers/OperationExpressions.d.ts +1 -0
- package/dist/handlers/Statements.d.ts +1 -0
- package/dist/index.cjs +177 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +177 -24
- 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/general.d.ts +2 -0
- package/dist/utils/value.d.ts +1 -1
- package/dist/values/BaseValue.d.ts +1 -0
- package/dist/values/DestructuringValue.d.ts +1 -1
- package/dist/values/FunctionValue.d.ts +1 -1
- package/dist/values/StoreValue.d.ts +4 -0
- 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(" ");
|
|
@@ -262,6 +274,11 @@ function camelToDashCase(name) {
|
|
|
262
274
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
263
275
|
function assign(obj, props) {
|
|
264
276
|
return Object.assign(obj, props);
|
|
277
|
+
}
|
|
278
|
+
/** Lazily computes and caches an expression */
|
|
279
|
+
function lazy(fn) {
|
|
280
|
+
let value;
|
|
281
|
+
return () => (value ??= fn());
|
|
265
282
|
}
|
|
266
283
|
|
|
267
284
|
function appendSourceLocations(valueInst, node) {
|
|
@@ -401,14 +418,14 @@ class EndInstruction extends InstructionBase {
|
|
|
401
418
|
}
|
|
402
419
|
|
|
403
420
|
class AddressResolver extends InstructionBase {
|
|
404
|
-
constructor(...bonds) {
|
|
405
|
-
super();
|
|
406
|
-
this.bonds = bonds;
|
|
407
|
-
}
|
|
408
421
|
get hidden() {
|
|
409
422
|
return true;
|
|
410
423
|
}
|
|
411
424
|
set hidden(value) { }
|
|
425
|
+
constructor(...bonds) {
|
|
426
|
+
super();
|
|
427
|
+
this.bonds = bonds;
|
|
428
|
+
}
|
|
412
429
|
resolve(i) {
|
|
413
430
|
for (const literal of this.bonds)
|
|
414
431
|
literal.data = i;
|
|
@@ -524,12 +541,16 @@ class BaseValue extends VoidValue {
|
|
|
524
541
|
],
|
|
525
542
|
];
|
|
526
543
|
}
|
|
544
|
+
"!=="(scope, other, out) {
|
|
545
|
+
const [equal, equalInst] = this["==="](scope, other);
|
|
546
|
+
const [result, resultInst] = equal["!"](scope, out);
|
|
547
|
+
return [result, [...equalInst, ...resultInst]];
|
|
548
|
+
}
|
|
527
549
|
}
|
|
528
550
|
const operatorMap$1 = {
|
|
529
551
|
"==": "equal",
|
|
530
552
|
"===": "strictEqual",
|
|
531
553
|
"!=": "notEqual",
|
|
532
|
-
"!==": "notEqual",
|
|
533
554
|
"<": "lessThan",
|
|
534
555
|
">": "greaterThan",
|
|
535
556
|
"<=": "lessThanEq",
|
|
@@ -616,6 +637,7 @@ class LiteralValue extends BaseValue {
|
|
|
616
637
|
super();
|
|
617
638
|
this.mutability = EMutability.constant;
|
|
618
639
|
this.data = data;
|
|
640
|
+
this.name = this.toString();
|
|
619
641
|
}
|
|
620
642
|
eval(_scope) {
|
|
621
643
|
return [this, []];
|
|
@@ -739,12 +761,40 @@ class StoreValue extends BaseValue {
|
|
|
739
761
|
const [evalValue, evalInst] = value.eval(scope, this);
|
|
740
762
|
return [this, [...evalInst, new SetInstruction(this, evalValue)]];
|
|
741
763
|
}
|
|
764
|
+
"=="(scope, value, out) {
|
|
765
|
+
if (compareStores(this, value))
|
|
766
|
+
return [new LiteralValue(1), []];
|
|
767
|
+
return super["=="](scope, value, out);
|
|
768
|
+
}
|
|
769
|
+
"==="(scope, value, out) {
|
|
770
|
+
if (compareStores(this, value))
|
|
771
|
+
return [new LiteralValue(1), []];
|
|
772
|
+
return super["==="](scope, value, out);
|
|
773
|
+
}
|
|
774
|
+
"!="(scope, value, out) {
|
|
775
|
+
if (compareStores(this, value))
|
|
776
|
+
return [new LiteralValue(0), []];
|
|
777
|
+
return super["!="](scope, value, out);
|
|
778
|
+
}
|
|
779
|
+
"!=="(scope, value, out) {
|
|
780
|
+
if (compareStores(this, value))
|
|
781
|
+
return [new LiteralValue(0), []];
|
|
782
|
+
return super["!=="](scope, value, out);
|
|
783
|
+
}
|
|
742
784
|
eval(_scope) {
|
|
743
785
|
return [this, []];
|
|
744
786
|
}
|
|
745
787
|
toString() {
|
|
746
788
|
return this.name;
|
|
747
789
|
}
|
|
790
|
+
}
|
|
791
|
+
function compareStores(left, right) {
|
|
792
|
+
return (left === right ||
|
|
793
|
+
// prevents a store from being equal to a senseable value
|
|
794
|
+
(right instanceof left.constructor &&
|
|
795
|
+
right.mutability === EMutability.constant &&
|
|
796
|
+
left.mutability === EMutability.constant &&
|
|
797
|
+
right.name === left.name));
|
|
748
798
|
}
|
|
749
799
|
|
|
750
800
|
class ObjectValue extends VoidValue {
|
|
@@ -926,16 +976,18 @@ class LazyValue extends VoidValue {
|
|
|
926
976
|
}
|
|
927
977
|
|
|
928
978
|
class Scope {
|
|
929
|
-
constructor(values, parent = null, stacked = false, ntemp = 0, name = "", inst = []) {
|
|
979
|
+
constructor(values, parent = null, stacked = false, ntemp = 0, name = "", inst = [], operationCache = {}, cacheDependencies = {}) {
|
|
930
980
|
this.parent = parent;
|
|
931
981
|
this.stacked = stacked;
|
|
932
982
|
this.ntemp = ntemp;
|
|
933
983
|
this.name = name;
|
|
934
984
|
this.inst = inst;
|
|
985
|
+
this.operationCache = operationCache;
|
|
986
|
+
this.cacheDependencies = cacheDependencies;
|
|
935
987
|
this.data = values;
|
|
936
988
|
}
|
|
937
989
|
copy() {
|
|
938
|
-
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 });
|
|
939
991
|
scope.break = this.break;
|
|
940
992
|
scope.continue = this.continue;
|
|
941
993
|
scope.function = this.function;
|
|
@@ -944,6 +996,8 @@ class Scope {
|
|
|
944
996
|
createScope() {
|
|
945
997
|
const scope = this.copy();
|
|
946
998
|
scope.data = {};
|
|
999
|
+
scope.operationCache = {};
|
|
1000
|
+
scope.cacheDependencies = {};
|
|
947
1001
|
scope.parent = this;
|
|
948
1002
|
return scope;
|
|
949
1003
|
}
|
|
@@ -989,6 +1043,46 @@ class Scope {
|
|
|
989
1043
|
this.ntemp++;
|
|
990
1044
|
return result;
|
|
991
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);
|
|
992
1086
|
}
|
|
993
1087
|
|
|
994
1088
|
const Program = (c, scope, node) => {
|
|
@@ -1164,7 +1258,21 @@ const BooleanLiteral = (_c, scope, node) => [new LiteralValue(+node.value), []];
|
|
|
1164
1258
|
const LRExpression = (c, scope, node, out) => {
|
|
1165
1259
|
const [left, leftInst] = c.handleEval(scope, node.left);
|
|
1166
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
|
+
}
|
|
1167
1272
|
const [op, opInst] = left[node.operator](scope, right, out);
|
|
1273
|
+
if (!(op instanceof LiteralValue)) {
|
|
1274
|
+
scope.addCachedOperation(node.operator, op, left, right);
|
|
1275
|
+
}
|
|
1168
1276
|
return [op, [...leftInst, ...rightInst, ...opInst]];
|
|
1169
1277
|
};
|
|
1170
1278
|
const BinaryExpression = LRExpression;
|
|
@@ -1186,18 +1294,26 @@ const AssignmentExpression = (c, scope, node) => {
|
|
|
1186
1294
|
];
|
|
1187
1295
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1188
1296
|
const [op, opInst] = left[node.operator](scope, right);
|
|
1297
|
+
if (left)
|
|
1298
|
+
scope.clearDependentCache(left);
|
|
1189
1299
|
return [op, [...leftInst, ...rightInst, ...opInst]];
|
|
1190
1300
|
};
|
|
1191
1301
|
const UnaryExpression = (c, scope, { argument, operator }, out) => {
|
|
1192
1302
|
const [arg, argInst] = c.handleEval(scope, argument);
|
|
1303
|
+
const cachedResult = scope.getCachedOperation(operator, arg);
|
|
1304
|
+
if (cachedResult)
|
|
1305
|
+
return [cachedResult, argInst];
|
|
1193
1306
|
const operatorId = operator == "+" || operator == "-" ? `u${operator}` : operator;
|
|
1194
1307
|
if (operatorId === "throw")
|
|
1195
1308
|
throw new CompilerError("throw operator is not supported");
|
|
1196
1309
|
const [op, opInst] = arg[operatorId](scope, out);
|
|
1310
|
+
scope.addCachedOperation(operator, op, arg);
|
|
1197
1311
|
return [op, [...argInst, ...opInst]];
|
|
1198
1312
|
};
|
|
1199
1313
|
const UpdateExpression = (c, scope, { argument, operator, prefix }, out) => {
|
|
1200
1314
|
const [arg, argInst] = c.handle(scope, argument);
|
|
1315
|
+
if (arg)
|
|
1316
|
+
scope.clearDependentCache(arg);
|
|
1201
1317
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1202
1318
|
const [op, opInst] = arg[operator](scope, prefix, out);
|
|
1203
1319
|
return [op, [...argInst, ...opInst]];
|
|
@@ -1228,6 +1344,17 @@ const ConditionalExpression = (c, scope, node, out) => {
|
|
|
1228
1344
|
new AddressResolver(endExpressionAdress),
|
|
1229
1345
|
],
|
|
1230
1346
|
];
|
|
1347
|
+
};
|
|
1348
|
+
const SequenceExpression = (c, scope, node, out) => {
|
|
1349
|
+
const { expressions } = node;
|
|
1350
|
+
const inst = [];
|
|
1351
|
+
// compute every expression except the last one
|
|
1352
|
+
for (let i = 0; i < expressions.length - 1; i++) {
|
|
1353
|
+
const [, exprInst] = c.handleEval(scope, expressions[i], discardedName);
|
|
1354
|
+
inst.push(...exprInst);
|
|
1355
|
+
}
|
|
1356
|
+
const [value, valueInst] = c.handleEval(scope, expressions[expressions.length - 1], out);
|
|
1357
|
+
return [value, [...inst, ...valueInst]];
|
|
1231
1358
|
};
|
|
1232
1359
|
|
|
1233
1360
|
const Identifier = (c, scope, node) => {
|
|
@@ -1259,6 +1386,7 @@ const ReturnStatement = (c, scope, node, out) => {
|
|
|
1259
1386
|
const [ret, retInst] = scope.function.return(scope, arg, out);
|
|
1260
1387
|
return [ret, [...argInst, ...retInst]];
|
|
1261
1388
|
};
|
|
1389
|
+
const EmptyStatement = () => [null, []];
|
|
1262
1390
|
const LabeledStatement = (c, scope, node) => {
|
|
1263
1391
|
const inner = scope.createScope();
|
|
1264
1392
|
inner.label = node.label.name;
|
|
@@ -1288,9 +1416,12 @@ const IfStatement = (c, scope, node) => {
|
|
|
1288
1416
|
return [null, inst];
|
|
1289
1417
|
}
|
|
1290
1418
|
const endIfAddr = new LiteralValue(null);
|
|
1291
|
-
inst.push(...testInst, new JumpInstruction(endIfAddr, EJumpKind.Equal, test, new LiteralValue(0)), ...withAlwaysRuns(c.handle(scope, node.consequent), false)[1]
|
|
1292
|
-
|
|
1293
|
-
|
|
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);
|
|
1294
1425
|
inst.push(new JumpInstruction(endElseAddr, EJumpKind.Always), new AddressResolver(endIfAddr), ...withAlwaysRuns(c.handle(scope, node.alternate), false)[1], new AddressResolver(endElseAddr));
|
|
1295
1426
|
}
|
|
1296
1427
|
return [null, inst];
|
|
@@ -1308,7 +1439,7 @@ const SwitchStatement = (c, scope, node) => {
|
|
|
1308
1439
|
let constantCase = false;
|
|
1309
1440
|
let onlyConstantTests = true;
|
|
1310
1441
|
for (const scase of node.cases) {
|
|
1311
|
-
const
|
|
1442
|
+
const bodyInst = lazy(() => c.handleMany(innerScope, scase.consequent)[1]);
|
|
1312
1443
|
const bodyAdress = new LiteralValue(null);
|
|
1313
1444
|
const bodyLine = new AddressResolver(bodyAdress);
|
|
1314
1445
|
const canFallInto = !endsWithoutFalltrough(inst, endLine);
|
|
@@ -1322,7 +1453,7 @@ const SwitchStatement = (c, scope, node) => {
|
|
|
1322
1453
|
])[1];
|
|
1323
1454
|
}
|
|
1324
1455
|
if (includeBody)
|
|
1325
|
-
inst.push(bodyLine, ...bodyInst);
|
|
1456
|
+
inst.push(bodyLine, ...bodyInst());
|
|
1326
1457
|
continue;
|
|
1327
1458
|
}
|
|
1328
1459
|
const [test, testInst] = c.handleEval(innerScope, scase.test);
|
|
@@ -1346,8 +1477,8 @@ const SwitchStatement = (c, scope, node) => {
|
|
|
1346
1477
|
// and all the other cases will never run
|
|
1347
1478
|
if (noFallInto &&
|
|
1348
1479
|
onlyConstantTests &&
|
|
1349
|
-
endsWithoutFalltrough(bodyInst, endLine)) {
|
|
1350
|
-
return [null, [...bodyInst, endLine]];
|
|
1480
|
+
endsWithoutFalltrough(bodyInst(), endLine)) {
|
|
1481
|
+
return [null, [...bodyInst(), endLine]];
|
|
1351
1482
|
}
|
|
1352
1483
|
jump = c.handle(innerScope, scase, () => [
|
|
1353
1484
|
null,
|
|
@@ -1364,7 +1495,7 @@ const SwitchStatement = (c, scope, node) => {
|
|
|
1364
1495
|
if (includeJump)
|
|
1365
1496
|
caseJumps.push(...testInst, ...jump);
|
|
1366
1497
|
if (includeBody)
|
|
1367
|
-
inst.push(bodyLine, ...bodyInst);
|
|
1498
|
+
inst.push(bodyLine, ...bodyInst());
|
|
1368
1499
|
}
|
|
1369
1500
|
// ensures that the processor exits
|
|
1370
1501
|
// the switch if no cases match
|
|
@@ -1842,7 +1973,9 @@ const ObjectPattern = (c, scope, node) => {
|
|
|
1842
1973
|
});
|
|
1843
1974
|
}
|
|
1844
1975
|
return [new DestructuringValue(members), inst];
|
|
1845
|
-
};
|
|
1976
|
+
};
|
|
1977
|
+
// the mlog runtime already does this check for us
|
|
1978
|
+
const OptionalMemberExpression = MemberExpression;
|
|
1846
1979
|
|
|
1847
1980
|
const TemplateLiteral = () => {
|
|
1848
1981
|
throw new CompilerError("Template strings are currently unavailable. To inline mlog code use the asm function.");
|
|
@@ -1919,11 +2052,13 @@ var handlers = /*#__PURE__*/Object.freeze({
|
|
|
1919
2052
|
UnaryExpression: UnaryExpression,
|
|
1920
2053
|
UpdateExpression: UpdateExpression,
|
|
1921
2054
|
ConditionalExpression: ConditionalExpression,
|
|
2055
|
+
SequenceExpression: SequenceExpression,
|
|
1922
2056
|
Identifier: Identifier,
|
|
1923
2057
|
ExpressionStatement: ExpressionStatement,
|
|
1924
2058
|
BreakStatement: BreakStatement,
|
|
1925
2059
|
ContinueStatement: ContinueStatement,
|
|
1926
2060
|
ReturnStatement: ReturnStatement,
|
|
2061
|
+
EmptyStatement: EmptyStatement,
|
|
1927
2062
|
LabeledStatement: LabeledStatement,
|
|
1928
2063
|
IfStatement: IfStatement,
|
|
1929
2064
|
SwitchStatement: SwitchStatement,
|
|
@@ -1942,6 +2077,7 @@ var handlers = /*#__PURE__*/Object.freeze({
|
|
|
1942
2077
|
MemberExpression: MemberExpression,
|
|
1943
2078
|
ArrayPattern: ArrayPattern,
|
|
1944
2079
|
ObjectPattern: ObjectPattern,
|
|
2080
|
+
OptionalMemberExpression: OptionalMemberExpression,
|
|
1945
2081
|
TemplateLiteral: TemplateLiteral,
|
|
1946
2082
|
BlockStatement: BlockStatement,
|
|
1947
2083
|
File: File,
|
|
@@ -1989,6 +2125,7 @@ const mathOperations = {
|
|
|
1989
2125
|
sqrt: a => Math.sqrt(a),
|
|
1990
2126
|
rand: null,
|
|
1991
2127
|
};
|
|
2128
|
+
const orderIndependentOperations = ["max", "min", "len"];
|
|
1992
2129
|
function createMacroMathOperations() {
|
|
1993
2130
|
const macroMathOperations = {
|
|
1994
2131
|
PI: new StoreValue("@pi", EMutability.constant),
|
|
@@ -2005,7 +2142,17 @@ function createMacroMathOperations() {
|
|
|
2005
2142
|
throw new CompilerError("Cannot do math operation with non-numerical literals.");
|
|
2006
2143
|
return [new LiteralValue(fn(a.num, b.num)), []];
|
|
2007
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
|
+
}
|
|
2008
2154
|
const temp = StoreValue.from(scope, out);
|
|
2155
|
+
scope.addCachedOperation(key, temp, a, b);
|
|
2009
2156
|
return [temp, [new OperationInstruction(key, temp, a, b)]];
|
|
2010
2157
|
}
|
|
2011
2158
|
if (fn && a instanceof LiteralValue) {
|
|
@@ -2013,8 +2160,14 @@ function createMacroMathOperations() {
|
|
|
2013
2160
|
throw new CompilerError("Cannot do math operation with non-numerical literal.");
|
|
2014
2161
|
return [new LiteralValue(fn(a.num)), []];
|
|
2015
2162
|
}
|
|
2163
|
+
const cachedResult = scope.getCachedOperation(key, a);
|
|
2164
|
+
if (cachedResult)
|
|
2165
|
+
return [cachedResult, []];
|
|
2016
2166
|
const temp = StoreValue.from(scope, out);
|
|
2017
|
-
|
|
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)]];
|
|
2018
2171
|
});
|
|
2019
2172
|
}
|
|
2020
2173
|
return macroMathOperations;
|