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.
@@ -1,6 +1,6 @@
1
1
  import { CompilerError } from "./CompilerError";
2
2
  import { es, IScope, IValue, TEOutput, THandler, TValueInstructions } from "./types";
3
- declare type THandlerMap = {
3
+ type THandlerMap = {
4
4
  [k in es.Node["type"]]?: THandler<IValue | null>;
5
5
  };
6
6
  export interface CompilerOptions {
@@ -12,7 +12,7 @@ export interface ParsingError extends Error {
12
12
  column: number;
13
13
  };
14
14
  }
15
- declare type CompilerErrorSource = es.Node | es.SourceLocation | {
15
+ type CompilerErrorSource = es.Node | es.SourceLocation | {
16
16
  line: number;
17
17
  column: number;
18
18
  };
package/dist/Scope.d.ts CHANGED
@@ -7,12 +7,14 @@ export declare class Scope implements IScope {
7
7
  ntemp: number;
8
8
  name: string;
9
9
  inst: IInstruction[];
10
+ operationCache: Record<string, IValue>;
11
+ cacheDependencies: Record<string, string[]>;
10
12
  data: Record<string, IValue | null>;
11
13
  break: AddressResolver;
12
14
  continue: AddressResolver;
13
15
  function: IFunctionValue;
14
16
  label?: string;
15
- constructor(values: Record<string, IValue | null>, parent?: IScope | null, stacked?: boolean, ntemp?: number, name?: string, inst?: IInstruction[]);
17
+ constructor(values: Record<string, IValue | null>, parent?: IScope | null, stacked?: boolean, ntemp?: number, name?: string, inst?: IInstruction[], operationCache?: Record<string, IValue>, cacheDependencies?: Record<string, string[]>);
16
18
  copy(): IScope;
17
19
  createScope(): IScope;
18
20
  createFunction(name: string, stacked?: boolean): IScope;
@@ -22,4 +24,7 @@ export declare class Scope implements IScope {
22
24
  hardSet<T extends IValue>(name: string, value: T): T;
23
25
  make(identifier: string, name: string): SenseableValue;
24
26
  makeTempName(): string;
27
+ addCachedOperation(op: string, result: IValue, left: IValue, right?: IValue | undefined): void;
28
+ getCachedOperation(op: string, left: IValue, right?: IValue | undefined): IValue | undefined;
29
+ clearDependentCache(value: IValue): void;
25
30
  }
package/dist/index.cjs CHANGED
@@ -167,7 +167,19 @@ const unaryOperators = [
167
167
  ];
168
168
  const updateOperators = ["++", "--"];
169
169
  const singleOperators = [...unaryOperators, ...updateOperators];
170
- const operators = [...leftRightOperators, ...singleOperators];
170
+ const operators = [...leftRightOperators, ...singleOperators];
171
+ const orderIndependentOperators = [
172
+ "!=",
173
+ "!==",
174
+ "&&",
175
+ "&",
176
+ "*",
177
+ "+",
178
+ "==",
179
+ "===",
180
+ "|",
181
+ "||",
182
+ ];
171
183
 
172
184
  class VoidValue {
173
185
  constructor() {
@@ -198,18 +210,18 @@ for (const key of operators) {
198
210
  }
199
211
 
200
212
  class InstructionBase {
201
- constructor(...args) {
202
- this.intent = EInstIntent.none;
203
- this.alwaysRuns = true;
204
- this._hidden = false;
205
- this.args = args;
206
- }
207
213
  get hidden() {
208
214
  return this._hidden;
209
215
  }
210
216
  set hidden(value) {
211
217
  this._hidden = value;
212
218
  }
219
+ constructor(...args) {
220
+ this.intent = EInstIntent.none;
221
+ this.alwaysRuns = true;
222
+ this._hidden = false;
223
+ this.args = args;
224
+ }
213
225
  resolve(_i) { }
214
226
  toString() {
215
227
  return this.args.filter(arg => arg).join(" ");
@@ -427,14 +439,14 @@ class EndInstruction extends InstructionBase {
427
439
  }
428
440
 
429
441
  class AddressResolver extends InstructionBase {
430
- constructor(...bonds) {
431
- super();
432
- this.bonds = bonds;
433
- }
434
442
  get hidden() {
435
443
  return true;
436
444
  }
437
445
  set hidden(value) { }
446
+ constructor(...bonds) {
447
+ super();
448
+ this.bonds = bonds;
449
+ }
438
450
  resolve(i) {
439
451
  for (const literal of this.bonds)
440
452
  literal.data = i;
@@ -646,6 +658,7 @@ class LiteralValue extends BaseValue {
646
658
  super();
647
659
  this.mutability = EMutability.constant;
648
660
  this.data = data;
661
+ this.name = this.toString();
649
662
  }
650
663
  eval(_scope) {
651
664
  return [this, []];
@@ -984,16 +997,18 @@ class LazyValue extends VoidValue {
984
997
  }
985
998
 
986
999
  class Scope {
987
- constructor(values, parent = null, stacked = false, ntemp = 0, name = "", inst = []) {
1000
+ constructor(values, parent = null, stacked = false, ntemp = 0, name = "", inst = [], operationCache = {}, cacheDependencies = {}) {
988
1001
  this.parent = parent;
989
1002
  this.stacked = stacked;
990
1003
  this.ntemp = ntemp;
991
1004
  this.name = name;
992
1005
  this.inst = inst;
1006
+ this.operationCache = operationCache;
1007
+ this.cacheDependencies = cacheDependencies;
993
1008
  this.data = values;
994
1009
  }
995
1010
  copy() {
996
- const scope = new Scope({ ...this.data }, this.parent, this.stacked, this.ntemp, this.name, this.inst);
1011
+ const scope = new Scope({ ...this.data }, this.parent, this.stacked, this.ntemp, this.name, this.inst, { ...this.operationCache }, { ...this.cacheDependencies });
997
1012
  scope.break = this.break;
998
1013
  scope.continue = this.continue;
999
1014
  scope.function = this.function;
@@ -1002,6 +1017,8 @@ class Scope {
1002
1017
  createScope() {
1003
1018
  const scope = this.copy();
1004
1019
  scope.data = {};
1020
+ scope.operationCache = {};
1021
+ scope.cacheDependencies = {};
1005
1022
  scope.parent = this;
1006
1023
  return scope;
1007
1024
  }
@@ -1047,6 +1064,46 @@ class Scope {
1047
1064
  this.ntemp++;
1048
1065
  return result;
1049
1066
  }
1067
+ addCachedOperation(op, result, left, right) {
1068
+ if (!left.name || !result.name || (right && !right.name))
1069
+ return;
1070
+ const id = formatCacheId(op, left, right);
1071
+ this.operationCache[id] = result;
1072
+ this.clearDependentCache(result);
1073
+ addCacheDependency(this, left.name, id);
1074
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1075
+ if (right)
1076
+ addCacheDependency(this, right.name, id);
1077
+ addCacheDependency(this, result.name, id);
1078
+ }
1079
+ getCachedOperation(op, left, right) {
1080
+ const id = formatCacheId(op, left, right);
1081
+ const result = this.operationCache[id];
1082
+ if (result)
1083
+ return result;
1084
+ if (this.parent)
1085
+ return this.parent.getCachedOperation(op, left, right);
1086
+ }
1087
+ clearDependentCache(value) {
1088
+ if (!value.name)
1089
+ return;
1090
+ const dependents = this.cacheDependencies[value.name];
1091
+ if (dependents) {
1092
+ for (const id of dependents) {
1093
+ delete this.operationCache[id];
1094
+ }
1095
+ }
1096
+ if (this.parent)
1097
+ this.parent.clearDependentCache(value);
1098
+ }
1099
+ }
1100
+ function formatCacheId(op, left, right) {
1101
+ if (right)
1102
+ return `{${op}}{${left.name}}{${right.name}}`;
1103
+ return `{${op}}{${left.name}}`;
1104
+ }
1105
+ function addCacheDependency(scope, name, id) {
1106
+ (scope.cacheDependencies[name] ??= []).push(id);
1050
1107
  }
1051
1108
 
1052
1109
  const Program = (c, scope, node) => {
@@ -1222,7 +1279,21 @@ const BooleanLiteral = (_c, scope, node) => [new LiteralValue(+node.value), []];
1222
1279
  const LRExpression = (c, scope, node, out) => {
1223
1280
  const [left, leftInst] = c.handleEval(scope, node.left);
1224
1281
  const [right, rightInst] = c.handleEval(scope, node.right);
1282
+ if (!(left instanceof LiteralValue) || !(right instanceof LiteralValue)) {
1283
+ const cachedResult = scope.getCachedOperation(node.operator, left, right);
1284
+ if (cachedResult)
1285
+ return [cachedResult, [...leftInst, ...rightInst]];
1286
+ // because a * b is the same as b * a
1287
+ if (orderIndependentOperators.includes(node.operator)) {
1288
+ const cachedResult = scope.getCachedOperation(node.operator, right, left);
1289
+ if (cachedResult)
1290
+ return [cachedResult, [...leftInst, ...rightInst]];
1291
+ }
1292
+ }
1225
1293
  const [op, opInst] = left[node.operator](scope, right, out);
1294
+ if (!(op instanceof LiteralValue)) {
1295
+ scope.addCachedOperation(node.operator, op, left, right);
1296
+ }
1226
1297
  return [op, [...leftInst, ...rightInst, ...opInst]];
1227
1298
  };
1228
1299
  const BinaryExpression = LRExpression;
@@ -1244,18 +1315,26 @@ const AssignmentExpression = (c, scope, node) => {
1244
1315
  ];
1245
1316
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1246
1317
  const [op, opInst] = left[node.operator](scope, right);
1318
+ if (left)
1319
+ scope.clearDependentCache(left);
1247
1320
  return [op, [...leftInst, ...rightInst, ...opInst]];
1248
1321
  };
1249
1322
  const UnaryExpression = (c, scope, { argument, operator }, out) => {
1250
1323
  const [arg, argInst] = c.handleEval(scope, argument);
1324
+ const cachedResult = scope.getCachedOperation(operator, arg);
1325
+ if (cachedResult)
1326
+ return [cachedResult, argInst];
1251
1327
  const operatorId = operator == "+" || operator == "-" ? `u${operator}` : operator;
1252
1328
  if (operatorId === "throw")
1253
1329
  throw new CompilerError("throw operator is not supported");
1254
1330
  const [op, opInst] = arg[operatorId](scope, out);
1331
+ scope.addCachedOperation(operator, op, arg);
1255
1332
  return [op, [...argInst, ...opInst]];
1256
1333
  };
1257
1334
  const UpdateExpression = (c, scope, { argument, operator, prefix }, out) => {
1258
1335
  const [arg, argInst] = c.handle(scope, argument);
1336
+ if (arg)
1337
+ scope.clearDependentCache(arg);
1259
1338
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1260
1339
  const [op, opInst] = arg[operator](scope, prefix, out);
1261
1340
  return [op, [...argInst, ...opInst]];
@@ -1358,9 +1437,12 @@ const IfStatement = (c, scope, node) => {
1358
1437
  return [null, inst];
1359
1438
  }
1360
1439
  const endIfAddr = new LiteralValue(null);
1361
- inst.push(...testInst, new JumpInstruction(endIfAddr, EJumpKind.Equal, test, new LiteralValue(0)), ...withAlwaysRuns(c.handle(scope, node.consequent), false)[1], new AddressResolver(endIfAddr));
1362
- const endElseAddr = new LiteralValue(null);
1363
- if (node.alternate) {
1440
+ inst.push(...testInst, new JumpInstruction(endIfAddr, EJumpKind.Equal, test, new LiteralValue(0)), ...withAlwaysRuns(c.handle(scope, node.consequent), false)[1]);
1441
+ if (!node.alternate) {
1442
+ inst.push(new AddressResolver(endIfAddr));
1443
+ }
1444
+ else {
1445
+ const endElseAddr = new LiteralValue(null);
1364
1446
  inst.push(new JumpInstruction(endElseAddr, EJumpKind.Always), new AddressResolver(endIfAddr), ...withAlwaysRuns(c.handle(scope, node.alternate), false)[1], new AddressResolver(endElseAddr));
1365
1447
  }
1366
1448
  return [null, inst];
@@ -2064,6 +2146,7 @@ const mathOperations = {
2064
2146
  sqrt: a => Math.sqrt(a),
2065
2147
  rand: null,
2066
2148
  };
2149
+ const orderIndependentOperations = ["max", "min", "len"];
2067
2150
  function createMacroMathOperations() {
2068
2151
  const macroMathOperations = {
2069
2152
  PI: new StoreValue("@pi", EMutability.constant),
@@ -2080,7 +2163,17 @@ function createMacroMathOperations() {
2080
2163
  throw new CompilerError("Cannot do math operation with non-numerical literals.");
2081
2164
  return [new LiteralValue(fn(a.num, b.num)), []];
2082
2165
  }
2166
+ const cachedResult = scope.getCachedOperation(key, a, b);
2167
+ if (cachedResult)
2168
+ return [cachedResult, []];
2169
+ // max, min and len do not change if the arguments change in order
2170
+ if (orderIndependentOperations.includes(key)) {
2171
+ const cachedResult = scope.getCachedOperation(key, b, a);
2172
+ if (cachedResult)
2173
+ return [cachedResult, []];
2174
+ }
2083
2175
  const temp = StoreValue.from(scope, out);
2176
+ scope.addCachedOperation(key, temp, a, b);
2084
2177
  return [temp, [new OperationInstruction(key, temp, a, b)]];
2085
2178
  }
2086
2179
  if (fn && a instanceof LiteralValue) {
@@ -2088,8 +2181,14 @@ function createMacroMathOperations() {
2088
2181
  throw new CompilerError("Cannot do math operation with non-numerical literal.");
2089
2182
  return [new LiteralValue(fn(a.num)), []];
2090
2183
  }
2184
+ const cachedResult = scope.getCachedOperation(key, a);
2185
+ if (cachedResult)
2186
+ return [cachedResult, []];
2091
2187
  const temp = StoreValue.from(scope, out);
2092
- return [temp, [new OperationInstruction(key, temp, a, b)]];
2188
+ // ensures that operations like `rand` are not cached
2189
+ if (mathOperations[key])
2190
+ scope.addCachedOperation(key, temp, a);
2191
+ return [temp, [new OperationInstruction(key, temp, a, null)]];
2093
2192
  });
2094
2193
  }
2095
2194
  return macroMathOperations;