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.
@@ -1,6 +1,7 @@
1
- import { THandler } from "../types";
1
+ import { IValue, THandler } from "../types";
2
2
  export declare const ObjectExpression: THandler;
3
3
  export declare const ArrayExpression: THandler;
4
4
  export declare const MemberExpression: THandler;
5
5
  export declare const ArrayPattern: THandler;
6
6
  export declare const ObjectPattern: THandler;
7
+ export declare const OptionalMemberExpression: THandler<IValue>;
@@ -6,3 +6,4 @@ export declare const AssignmentExpression: THandler;
6
6
  export declare const UnaryExpression: THandler;
7
7
  export declare const UpdateExpression: THandler;
8
8
  export declare const ConditionalExpression: THandler;
9
+ export declare const SequenceExpression: THandler;
@@ -3,4 +3,5 @@ export declare const ExpressionStatement: THandler<IValue | null>;
3
3
  export declare const BreakStatement: THandler<null>;
4
4
  export declare const ContinueStatement: THandler<null>;
5
5
  export declare const ReturnStatement: THandler<IValue | null>;
6
+ export declare const EmptyStatement: THandler<null>;
6
7
  export declare const LabeledStatement: THandler<null>;
package/dist/index.cjs CHANGED
@@ -283,6 +283,11 @@ function camelToDashCase(name) {
283
283
  // eslint-disable-next-line @typescript-eslint/ban-types
284
284
  function assign(obj, props) {
285
285
  return Object.assign(obj, props);
286
+ }
287
+ /** Lazily computes and caches an expression */
288
+ function lazy(fn) {
289
+ let value;
290
+ return () => (value ??= fn());
286
291
  }
287
292
 
288
293
  function appendSourceLocations(valueInst, node) {
@@ -545,12 +550,16 @@ class BaseValue extends VoidValue {
545
550
  ],
546
551
  ];
547
552
  }
553
+ "!=="(scope, other, out) {
554
+ const [equal, equalInst] = this["==="](scope, other);
555
+ const [result, resultInst] = equal["!"](scope, out);
556
+ return [result, [...equalInst, ...resultInst]];
557
+ }
548
558
  }
549
559
  const operatorMap$1 = {
550
560
  "==": "equal",
551
561
  "===": "strictEqual",
552
562
  "!=": "notEqual",
553
- "!==": "notEqual",
554
563
  "<": "lessThan",
555
564
  ">": "greaterThan",
556
565
  "<=": "lessThanEq",
@@ -760,12 +769,40 @@ class StoreValue extends BaseValue {
760
769
  const [evalValue, evalInst] = value.eval(scope, this);
761
770
  return [this, [...evalInst, new SetInstruction(this, evalValue)]];
762
771
  }
772
+ "=="(scope, value, out) {
773
+ if (compareStores(this, value))
774
+ return [new LiteralValue(1), []];
775
+ return super["=="](scope, value, out);
776
+ }
777
+ "==="(scope, value, out) {
778
+ if (compareStores(this, value))
779
+ return [new LiteralValue(1), []];
780
+ return super["==="](scope, value, out);
781
+ }
782
+ "!="(scope, value, out) {
783
+ if (compareStores(this, value))
784
+ return [new LiteralValue(0), []];
785
+ return super["!="](scope, value, out);
786
+ }
787
+ "!=="(scope, value, out) {
788
+ if (compareStores(this, value))
789
+ return [new LiteralValue(0), []];
790
+ return super["!=="](scope, value, out);
791
+ }
763
792
  eval(_scope) {
764
793
  return [this, []];
765
794
  }
766
795
  toString() {
767
796
  return this.name;
768
797
  }
798
+ }
799
+ function compareStores(left, right) {
800
+ return (left === right ||
801
+ // prevents a store from being equal to a senseable value
802
+ (right instanceof left.constructor &&
803
+ right.mutability === EMutability.constant &&
804
+ left.mutability === EMutability.constant &&
805
+ right.name === left.name));
769
806
  }
770
807
 
771
808
  class ObjectValue extends VoidValue {
@@ -1249,6 +1286,17 @@ const ConditionalExpression = (c, scope, node, out) => {
1249
1286
  new AddressResolver(endExpressionAdress),
1250
1287
  ],
1251
1288
  ];
1289
+ };
1290
+ const SequenceExpression = (c, scope, node, out) => {
1291
+ const { expressions } = node;
1292
+ const inst = [];
1293
+ // compute every expression except the last one
1294
+ for (let i = 0; i < expressions.length - 1; i++) {
1295
+ const [, exprInst] = c.handleEval(scope, expressions[i], discardedName);
1296
+ inst.push(...exprInst);
1297
+ }
1298
+ const [value, valueInst] = c.handleEval(scope, expressions[expressions.length - 1], out);
1299
+ return [value, [...inst, ...valueInst]];
1252
1300
  };
1253
1301
 
1254
1302
  const Identifier = (c, scope, node) => {
@@ -1280,6 +1328,7 @@ const ReturnStatement = (c, scope, node, out) => {
1280
1328
  const [ret, retInst] = scope.function.return(scope, arg, out);
1281
1329
  return [ret, [...argInst, ...retInst]];
1282
1330
  };
1331
+ const EmptyStatement = () => [null, []];
1283
1332
  const LabeledStatement = (c, scope, node) => {
1284
1333
  const inner = scope.createScope();
1285
1334
  inner.label = node.label.name;
@@ -1325,38 +1374,67 @@ const SwitchStatement = (c, scope, node) => {
1325
1374
  const endLine = new AddressResolver(endAdress).bindBreak(innerScope);
1326
1375
  const caseJumps = [];
1327
1376
  let defaultJump;
1377
+ // if there is a case that is guaranteed to run
1378
+ let constantCase = false;
1379
+ let onlyConstantTests = true;
1328
1380
  for (const scase of node.cases) {
1329
- const [, bodyInst] = c.handleMany(innerScope, scase.consequent);
1381
+ const bodyInst = lazy(() => c.handleMany(innerScope, scase.consequent)[1]);
1330
1382
  const bodyAdress = new LiteralValue(null);
1331
1383
  const bodyLine = new AddressResolver(bodyAdress);
1384
+ const canFallInto = !endsWithoutFalltrough(inst, endLine);
1385
+ let includeJump = !constantCase;
1386
+ const includeBody = !constantCase || canFallInto;
1332
1387
  if (!scase.test) {
1333
- [defaultJump] = c.handle(innerScope, scase, () => [
1334
- null,
1335
- [new JumpInstruction(bodyAdress, EJumpKind.Always)],
1336
- ])[1];
1337
- inst.push(bodyLine, ...bodyInst);
1388
+ if (includeJump) {
1389
+ [defaultJump] = c.handle(innerScope, scase, () => [
1390
+ null,
1391
+ [new JumpInstruction(bodyAdress, EJumpKind.Always)],
1392
+ ])[1];
1393
+ }
1394
+ if (includeBody)
1395
+ inst.push(bodyLine, ...bodyInst());
1338
1396
  continue;
1339
1397
  }
1340
1398
  const [test, testInst] = c.handleEval(innerScope, scase.test);
1399
+ if (onlyConstantTests)
1400
+ onlyConstantTests = test.mutability === EMutability.constant;
1401
+ let jump;
1341
1402
  // check if it can be evaluated at compile time
1342
- const [comp] = ref["=="](innerScope, test);
1343
- // if the constant expression resolves to false
1344
- // the whole case gets omitted
1345
- // otherwise it returns the instructions for
1346
- // the body of this case
1403
+ // the scope copy prevents unecessary increases in the temp counter
1404
+ const [comp] = ref["=="](innerScope.copy(), test);
1347
1405
  if (comp instanceof LiteralValue) {
1348
- if (comp.data)
1349
- return [null, bodyInst];
1350
- else
1351
- continue;
1406
+ // whether other cases can falltrough and reach this one
1407
+ const noFallInto = !canFallInto;
1408
+ if (!comp.data) {
1409
+ // this case is not accessible in any way
1410
+ if (noFallInto)
1411
+ continue;
1412
+ includeJump = false;
1413
+ }
1414
+ else {
1415
+ // this case is only accessible via the comparison jump
1416
+ // and all the other cases will never run
1417
+ if (noFallInto &&
1418
+ onlyConstantTests &&
1419
+ endsWithoutFalltrough(bodyInst(), endLine)) {
1420
+ return [null, [...bodyInst(), endLine]];
1421
+ }
1422
+ jump = c.handle(innerScope, scase, () => [
1423
+ null,
1424
+ [new JumpInstruction(bodyAdress, EJumpKind.Always)],
1425
+ ])[1];
1426
+ constantCase = true;
1427
+ }
1352
1428
  }
1353
1429
  // makes sourcemapping for the jump more specific
1354
- const [, jump] = c.handle(innerScope, scase, () => [
1430
+ jump ??= c.handle(innerScope, scase, () => [
1355
1431
  null,
1356
1432
  [new JumpInstruction(bodyAdress, EJumpKind.StrictEqual, ref, test)],
1357
- ]);
1358
- caseJumps.push(...testInst, ...jump);
1359
- inst.push(bodyLine, ...bodyInst);
1433
+ ])[1];
1434
+ if (includeJump)
1435
+ caseJumps.push(...testInst, ...jump);
1436
+ if (includeBody)
1437
+ inst.push(bodyLine, ...bodyInst());
1360
1438
  }
1361
1439
  // ensures that the processor exits
1362
1440
  // the switch if no cases match
@@ -1371,12 +1449,34 @@ const SwitchStatement = (c, scope, node) => {
1371
1449
  [
1372
1450
  ...refInst,
1373
1451
  ...caseJumps,
1374
- ...(caseJumps.length > 0 ? [defaultJump] : []),
1452
+ ...(caseJumps.length > 0 && !constantCase ? [defaultJump] : []),
1375
1453
  ...inst,
1376
1454
  endLine,
1377
1455
  ],
1378
1456
  ];
1379
- };
1457
+ };
1458
+ function endsWithoutFalltrough(inst, endLine) {
1459
+ if (inst.length === 0)
1460
+ return true;
1461
+ for (let i = inst.length - 1; i >= 0; i--) {
1462
+ const instruction = inst[i];
1463
+ // this means that there is an instruction trying to reference
1464
+ // the final function `set @counter` instruction
1465
+ // of course this generates an unecessary
1466
+ // `set @counter` in cases where a control flow
1467
+ // structure contains a fallback return statement
1468
+ // TODO: fix this with static analysis
1469
+ if (instruction instanceof AddressResolver)
1470
+ return false;
1471
+ if (instruction.hidden)
1472
+ continue;
1473
+ return (instruction instanceof SetCounterInstruction ||
1474
+ instruction.intent === EInstIntent.return ||
1475
+ (instruction instanceof BreakInstruction &&
1476
+ endLine.bonds.includes(instruction.address)));
1477
+ }
1478
+ return false;
1479
+ }
1380
1480
 
1381
1481
  const WhileStatement = (c, scope, node) => {
1382
1482
  const lines = [];
@@ -1812,7 +1912,9 @@ const ObjectPattern = (c, scope, node) => {
1812
1912
  });
1813
1913
  }
1814
1914
  return [new DestructuringValue(members), inst];
1815
- };
1915
+ };
1916
+ // the mlog runtime already does this check for us
1917
+ const OptionalMemberExpression = MemberExpression;
1816
1918
 
1817
1919
  const TemplateLiteral = () => {
1818
1920
  throw new CompilerError("Template strings are currently unavailable. To inline mlog code use the asm function.");
@@ -1889,11 +1991,13 @@ var handlers = /*#__PURE__*/Object.freeze({
1889
1991
  UnaryExpression: UnaryExpression,
1890
1992
  UpdateExpression: UpdateExpression,
1891
1993
  ConditionalExpression: ConditionalExpression,
1994
+ SequenceExpression: SequenceExpression,
1892
1995
  Identifier: Identifier,
1893
1996
  ExpressionStatement: ExpressionStatement,
1894
1997
  BreakStatement: BreakStatement,
1895
1998
  ContinueStatement: ContinueStatement,
1896
1999
  ReturnStatement: ReturnStatement,
2000
+ EmptyStatement: EmptyStatement,
1897
2001
  LabeledStatement: LabeledStatement,
1898
2002
  IfStatement: IfStatement,
1899
2003
  SwitchStatement: SwitchStatement,
@@ -1912,6 +2016,7 @@ var handlers = /*#__PURE__*/Object.freeze({
1912
2016
  MemberExpression: MemberExpression,
1913
2017
  ArrayPattern: ArrayPattern,
1914
2018
  ObjectPattern: ObjectPattern,
2019
+ OptionalMemberExpression: OptionalMemberExpression,
1915
2020
  TemplateLiteral: TemplateLiteral,
1916
2021
  BlockStatement: BlockStatement,
1917
2022
  File: File,