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/Compiler.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CompilerError } from "./CompilerError";
|
|
2
2
|
import { es, IScope, IValue, TEOutput, THandler, TValueInstructions } from "./types";
|
|
3
|
-
|
|
3
|
+
type THandlerMap = {
|
|
4
4
|
[k in es.Node["type"]]?: THandler<IValue | null>;
|
|
5
5
|
};
|
|
6
6
|
export interface CompilerOptions {
|
package/dist/CompilerError.d.ts
CHANGED
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]
|
|
1362
|
-
|
|
1363
|
-
|
|
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
|
-
|
|
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;
|