mlogjs 0.4.7 → 0.4.9

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
@@ -262,6 +262,11 @@ function camelToDashCase(name) {
262
262
  // eslint-disable-next-line @typescript-eslint/ban-types
263
263
  function assign(obj, props) {
264
264
  return Object.assign(obj, props);
265
+ }
266
+ /** Lazily computes and caches an expression */
267
+ function lazy(fn) {
268
+ let value;
269
+ return () => (value ??= fn());
265
270
  }
266
271
 
267
272
  function appendSourceLocations(valueInst, node) {
@@ -524,12 +529,16 @@ class BaseValue extends VoidValue {
524
529
  ],
525
530
  ];
526
531
  }
532
+ "!=="(scope, other, out) {
533
+ const [equal, equalInst] = this["==="](scope, other);
534
+ const [result, resultInst] = equal["!"](scope, out);
535
+ return [result, [...equalInst, ...resultInst]];
536
+ }
527
537
  }
528
538
  const operatorMap$1 = {
529
539
  "==": "equal",
530
540
  "===": "strictEqual",
531
541
  "!=": "notEqual",
532
- "!==": "notEqual",
533
542
  "<": "lessThan",
534
543
  ">": "greaterThan",
535
544
  "<=": "lessThanEq",
@@ -739,12 +748,40 @@ class StoreValue extends BaseValue {
739
748
  const [evalValue, evalInst] = value.eval(scope, this);
740
749
  return [this, [...evalInst, new SetInstruction(this, evalValue)]];
741
750
  }
751
+ "=="(scope, value, out) {
752
+ if (compareStores(this, value))
753
+ return [new LiteralValue(1), []];
754
+ return super["=="](scope, value, out);
755
+ }
756
+ "==="(scope, value, out) {
757
+ if (compareStores(this, value))
758
+ return [new LiteralValue(1), []];
759
+ return super["==="](scope, value, out);
760
+ }
761
+ "!="(scope, value, out) {
762
+ if (compareStores(this, value))
763
+ return [new LiteralValue(0), []];
764
+ return super["!="](scope, value, out);
765
+ }
766
+ "!=="(scope, value, out) {
767
+ if (compareStores(this, value))
768
+ return [new LiteralValue(0), []];
769
+ return super["!=="](scope, value, out);
770
+ }
742
771
  eval(_scope) {
743
772
  return [this, []];
744
773
  }
745
774
  toString() {
746
775
  return this.name;
747
776
  }
777
+ }
778
+ function compareStores(left, right) {
779
+ return (left === right ||
780
+ // prevents a store from being equal to a senseable value
781
+ (right instanceof left.constructor &&
782
+ right.mutability === EMutability.constant &&
783
+ left.mutability === EMutability.constant &&
784
+ right.name === left.name));
748
785
  }
749
786
 
750
787
  class ObjectValue extends VoidValue {
@@ -1228,6 +1265,17 @@ const ConditionalExpression = (c, scope, node, out) => {
1228
1265
  new AddressResolver(endExpressionAdress),
1229
1266
  ],
1230
1267
  ];
1268
+ };
1269
+ const SequenceExpression = (c, scope, node, out) => {
1270
+ const { expressions } = node;
1271
+ const inst = [];
1272
+ // compute every expression except the last one
1273
+ for (let i = 0; i < expressions.length - 1; i++) {
1274
+ const [, exprInst] = c.handleEval(scope, expressions[i], discardedName);
1275
+ inst.push(...exprInst);
1276
+ }
1277
+ const [value, valueInst] = c.handleEval(scope, expressions[expressions.length - 1], out);
1278
+ return [value, [...inst, ...valueInst]];
1231
1279
  };
1232
1280
 
1233
1281
  const Identifier = (c, scope, node) => {
@@ -1259,6 +1307,7 @@ const ReturnStatement = (c, scope, node, out) => {
1259
1307
  const [ret, retInst] = scope.function.return(scope, arg, out);
1260
1308
  return [ret, [...argInst, ...retInst]];
1261
1309
  };
1310
+ const EmptyStatement = () => [null, []];
1262
1311
  const LabeledStatement = (c, scope, node) => {
1263
1312
  const inner = scope.createScope();
1264
1313
  inner.label = node.label.name;
@@ -1304,38 +1353,67 @@ const SwitchStatement = (c, scope, node) => {
1304
1353
  const endLine = new AddressResolver(endAdress).bindBreak(innerScope);
1305
1354
  const caseJumps = [];
1306
1355
  let defaultJump;
1356
+ // if there is a case that is guaranteed to run
1357
+ let constantCase = false;
1358
+ let onlyConstantTests = true;
1307
1359
  for (const scase of node.cases) {
1308
- const [, bodyInst] = c.handleMany(innerScope, scase.consequent);
1360
+ const bodyInst = lazy(() => c.handleMany(innerScope, scase.consequent)[1]);
1309
1361
  const bodyAdress = new LiteralValue(null);
1310
1362
  const bodyLine = new AddressResolver(bodyAdress);
1363
+ const canFallInto = !endsWithoutFalltrough(inst, endLine);
1364
+ let includeJump = !constantCase;
1365
+ const includeBody = !constantCase || canFallInto;
1311
1366
  if (!scase.test) {
1312
- [defaultJump] = c.handle(innerScope, scase, () => [
1313
- null,
1314
- [new JumpInstruction(bodyAdress, EJumpKind.Always)],
1315
- ])[1];
1316
- inst.push(bodyLine, ...bodyInst);
1367
+ if (includeJump) {
1368
+ [defaultJump] = c.handle(innerScope, scase, () => [
1369
+ null,
1370
+ [new JumpInstruction(bodyAdress, EJumpKind.Always)],
1371
+ ])[1];
1372
+ }
1373
+ if (includeBody)
1374
+ inst.push(bodyLine, ...bodyInst());
1317
1375
  continue;
1318
1376
  }
1319
1377
  const [test, testInst] = c.handleEval(innerScope, scase.test);
1378
+ if (onlyConstantTests)
1379
+ onlyConstantTests = test.mutability === EMutability.constant;
1380
+ let jump;
1320
1381
  // check if it can be evaluated at compile time
1321
- const [comp] = ref["=="](innerScope, test);
1322
- // if the constant expression resolves to false
1323
- // the whole case gets omitted
1324
- // otherwise it returns the instructions for
1325
- // the body of this case
1382
+ // the scope copy prevents unecessary increases in the temp counter
1383
+ const [comp] = ref["=="](innerScope.copy(), test);
1326
1384
  if (comp instanceof LiteralValue) {
1327
- if (comp.data)
1328
- return [null, bodyInst];
1329
- else
1330
- continue;
1385
+ // whether other cases can falltrough and reach this one
1386
+ const noFallInto = !canFallInto;
1387
+ if (!comp.data) {
1388
+ // this case is not accessible in any way
1389
+ if (noFallInto)
1390
+ continue;
1391
+ includeJump = false;
1392
+ }
1393
+ else {
1394
+ // this case is only accessible via the comparison jump
1395
+ // and all the other cases will never run
1396
+ if (noFallInto &&
1397
+ onlyConstantTests &&
1398
+ endsWithoutFalltrough(bodyInst(), endLine)) {
1399
+ return [null, [...bodyInst(), endLine]];
1400
+ }
1401
+ jump = c.handle(innerScope, scase, () => [
1402
+ null,
1403
+ [new JumpInstruction(bodyAdress, EJumpKind.Always)],
1404
+ ])[1];
1405
+ constantCase = true;
1406
+ }
1331
1407
  }
1332
1408
  // makes sourcemapping for the jump more specific
1333
- const [, jump] = c.handle(innerScope, scase, () => [
1409
+ jump ??= c.handle(innerScope, scase, () => [
1334
1410
  null,
1335
1411
  [new JumpInstruction(bodyAdress, EJumpKind.StrictEqual, ref, test)],
1336
- ]);
1337
- caseJumps.push(...testInst, ...jump);
1338
- inst.push(bodyLine, ...bodyInst);
1412
+ ])[1];
1413
+ if (includeJump)
1414
+ caseJumps.push(...testInst, ...jump);
1415
+ if (includeBody)
1416
+ inst.push(bodyLine, ...bodyInst());
1339
1417
  }
1340
1418
  // ensures that the processor exits
1341
1419
  // the switch if no cases match
@@ -1350,12 +1428,34 @@ const SwitchStatement = (c, scope, node) => {
1350
1428
  [
1351
1429
  ...refInst,
1352
1430
  ...caseJumps,
1353
- ...(caseJumps.length > 0 ? [defaultJump] : []),
1431
+ ...(caseJumps.length > 0 && !constantCase ? [defaultJump] : []),
1354
1432
  ...inst,
1355
1433
  endLine,
1356
1434
  ],
1357
1435
  ];
1358
- };
1436
+ };
1437
+ function endsWithoutFalltrough(inst, endLine) {
1438
+ if (inst.length === 0)
1439
+ return true;
1440
+ for (let i = inst.length - 1; i >= 0; i--) {
1441
+ const instruction = inst[i];
1442
+ // this means that there is an instruction trying to reference
1443
+ // the final function `set @counter` instruction
1444
+ // of course this generates an unecessary
1445
+ // `set @counter` in cases where a control flow
1446
+ // structure contains a fallback return statement
1447
+ // TODO: fix this with static analysis
1448
+ if (instruction instanceof AddressResolver)
1449
+ return false;
1450
+ if (instruction.hidden)
1451
+ continue;
1452
+ return (instruction instanceof SetCounterInstruction ||
1453
+ instruction.intent === EInstIntent.return ||
1454
+ (instruction instanceof BreakInstruction &&
1455
+ endLine.bonds.includes(instruction.address)));
1456
+ }
1457
+ return false;
1458
+ }
1359
1459
 
1360
1460
  const WhileStatement = (c, scope, node) => {
1361
1461
  const lines = [];
@@ -1791,7 +1891,9 @@ const ObjectPattern = (c, scope, node) => {
1791
1891
  });
1792
1892
  }
1793
1893
  return [new DestructuringValue(members), inst];
1794
- };
1894
+ };
1895
+ // the mlog runtime already does this check for us
1896
+ const OptionalMemberExpression = MemberExpression;
1795
1897
 
1796
1898
  const TemplateLiteral = () => {
1797
1899
  throw new CompilerError("Template strings are currently unavailable. To inline mlog code use the asm function.");
@@ -1868,11 +1970,13 @@ var handlers = /*#__PURE__*/Object.freeze({
1868
1970
  UnaryExpression: UnaryExpression,
1869
1971
  UpdateExpression: UpdateExpression,
1870
1972
  ConditionalExpression: ConditionalExpression,
1973
+ SequenceExpression: SequenceExpression,
1871
1974
  Identifier: Identifier,
1872
1975
  ExpressionStatement: ExpressionStatement,
1873
1976
  BreakStatement: BreakStatement,
1874
1977
  ContinueStatement: ContinueStatement,
1875
1978
  ReturnStatement: ReturnStatement,
1979
+ EmptyStatement: EmptyStatement,
1876
1980
  LabeledStatement: LabeledStatement,
1877
1981
  IfStatement: IfStatement,
1878
1982
  SwitchStatement: SwitchStatement,
@@ -1891,6 +1995,7 @@ var handlers = /*#__PURE__*/Object.freeze({
1891
1995
  MemberExpression: MemberExpression,
1892
1996
  ArrayPattern: ArrayPattern,
1893
1997
  ObjectPattern: ObjectPattern,
1998
+ OptionalMemberExpression: OptionalMemberExpression,
1894
1999
  TemplateLiteral: TemplateLiteral,
1895
2000
  BlockStatement: BlockStatement,
1896
2001
  File: File,