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/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], new AddressResolver(endIfAddr));
1341
- const endElseAddr = new LiteralValue(null);
1342
- if (node.alternate) {
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
- return [temp, [new OperationInstruction(key, temp, a, b)]];
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;