mlogjs 0.5.0 → 0.5.2

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.
@@ -22,6 +22,8 @@ export declare class Compiler {
22
22
  protected parse(script: string): import("@babel/parser").ParseResult<es.File>;
23
23
  protected mapSources(valueInst: TValueInstructions<IValue | null>): es.SourceLocation[];
24
24
  handle(scope: IScope, node: es.Node, handler?: THandler<IValue | null> | undefined, out?: TEOutput): TValueInstructions<IValue | null>;
25
+ /** Handles the node and asserts that it resolves to a value. */
26
+ handleValue(scope: IScope, node: es.Node, handler?: THandler<IValue | null> | undefined, out?: TEOutput): TValueInstructions;
25
27
  handleEval(scope: IScope, node: es.Node, out?: TEOutput): TValueInstructions;
26
28
  /**
27
29
  * Handles many nodes in order.
package/dist/index.cjs CHANGED
@@ -230,6 +230,9 @@ class VoidValue {
230
230
  return;
231
231
  }
232
232
  postCall(_scope) { }
233
+ toOut() {
234
+ return this;
235
+ }
233
236
  debugString() {
234
237
  return "VoidValue";
235
238
  }
@@ -403,8 +406,8 @@ function extractOutName(out) {
403
406
  return out.name;
404
407
  }
405
408
  function extractDestrucuringOut(out, field) {
406
- if (out === discardedName)
407
- return out;
409
+ if (isDiscardedOut(out))
410
+ return discardedName;
408
411
  if (!(out instanceof DestructuringValue))
409
412
  return;
410
413
  return out.fields[field] ?? discardedName;
@@ -459,6 +462,13 @@ function assertObjectFields(value, fields) {
459
462
  }
460
463
  return result;
461
464
  }
465
+ function isDiscardedOut(out) {
466
+ if (!out)
467
+ return false;
468
+ if (typeof out === "string")
469
+ return out === discardedName;
470
+ return out.name === discardedName;
471
+ }
462
472
 
463
473
  class SetInstruction extends InstructionBase {
464
474
  constructor(store, value) {
@@ -540,7 +550,7 @@ class JumpInstruction extends InstructionBase {
540
550
  if (test === out)
541
551
  return [];
542
552
  return [
543
- new JumpInstruction(out.address, EJumpKind.Equal, test, new LiteralValue(+out.whenTrue)),
553
+ new JumpInstruction(out.address, out.whenTrue ? EJumpKind.NotEqual : EJumpKind.Equal, test, new LiteralValue(0)),
544
554
  ];
545
555
  }
546
556
  }
@@ -642,8 +652,11 @@ class BaseValue extends VoidValue {
642
652
  ];
643
653
  }
644
654
  "u+"(scope, out) {
645
- // TODO: should it return 0 + this ?
646
- return this.eval(scope, out);
655
+ const inst = [];
656
+ const value = pipeInsts(this.eval(scope), inst);
657
+ const zero = new LiteralValue(0);
658
+ const result = pipeInsts(zero["+"](scope, value, out), inst);
659
+ return [result, inst];
647
660
  }
648
661
  "!"(scope, out) {
649
662
  const inst = [];
@@ -665,7 +678,7 @@ class BaseValue extends VoidValue {
665
678
  const endAdress = new LiteralValue(null);
666
679
  const [left, leftInst] = this.eval(scope, result);
667
680
  const [right, rightInst] = other.eval(scope, result);
668
- const [test, testInst] = this["==="](scope, nullLiteral);
681
+ const [test, testInst] = left["==="](scope, nullLiteral);
669
682
  return [
670
683
  result,
671
684
  [
@@ -730,7 +743,7 @@ const assignmentToBinary = {
730
743
  for (const k in assignmentToBinary) {
731
744
  const op = k;
732
745
  BaseValue.prototype[op] = function (scope, value) {
733
- const [opValue, opInst] = this[assignmentToBinary[op]](scope, value, this);
746
+ const [opValue, opInst] = this[assignmentToBinary[op]](scope, value, this.toOut());
734
747
  const [retValue, retInst] = this["="](scope, opValue);
735
748
  return [retValue, [...opInst, ...retInst]];
736
749
  };
@@ -738,9 +751,10 @@ for (const k in assignmentToBinary) {
738
751
  for (const key of updateOperators) {
739
752
  BaseValue.prototype[key] = function (scope, prefix, out) {
740
753
  let [ret, inst] = this.eval(scope);
741
- if (!prefix && out !== discardedName) {
754
+ if (!prefix && !isDiscardedOut(out)) {
742
755
  const temp = StoreValue.from(scope, out);
743
- ret = pipeInsts(temp["="](scope, ret), inst);
756
+ pipeInsts(temp["="](scope, ret), inst);
757
+ ret = temp;
744
758
  }
745
759
  const kind = key === "++" ? "+=" : "-=";
746
760
  pipeInsts(this[kind](scope, new LiteralValue(1)), inst);
@@ -897,7 +911,7 @@ class StoreValue extends BaseValue {
897
911
  const [evalValue, evalInst] = value.eval(scope, this);
898
912
  if (evalValue.macro)
899
913
  throw new CompilerError(`Cannot assign a macro to a store (attempted to assign [${evalValue.debugString()}] to [${this.debugString()}])`);
900
- return [this, [...evalInst, new SetInstruction(this, evalValue)]];
914
+ return [evalValue, [...evalInst, new SetInstruction(this, evalValue)]];
901
915
  }
902
916
  "=="(scope, value, out) {
903
917
  if (compareStores(this, value))
@@ -1160,10 +1174,13 @@ class AssignmentValue extends VoidValue {
1160
1174
  }
1161
1175
  "="(scope, value, out) {
1162
1176
  const inst = [];
1163
- const input = pipeInsts(value["??"](scope, this.right, this.left), inst);
1177
+ const input = pipeInsts(value["??"](scope, this.right, this.left.toOut()), inst);
1164
1178
  const output = pipeInsts(this.left["="](scope, input, out), inst);
1165
1179
  return [output, inst];
1166
1180
  }
1181
+ toOut() {
1182
+ return this.left.toOut();
1183
+ }
1167
1184
  debugString() {
1168
1185
  return "AssignmentValue";
1169
1186
  }
@@ -1495,22 +1512,22 @@ const LogicalExpression = (c, scope, node, out) => {
1495
1512
  if (node.operator !== "??")
1496
1513
  return LRExpression(c, scope, node, out);
1497
1514
  const other = new LazyValue((scope, out) => c.handleEval(scope, node.right, out));
1498
- const [left, leftInst] = c.handleEval(scope, node.left);
1515
+ const [left, leftInst] = c.handleValue(scope, node.left);
1499
1516
  const [result, resultInst] = left["??"](scope, other, out);
1500
1517
  return [result, [...leftInst, ...resultInst]];
1501
1518
  };
1502
1519
  const AssignmentExpression = (c, scope, node) => {
1503
- const [left, leftInst] = c.handle(scope, node.left);
1520
+ const [left, leftInst] = c.handleValue(scope, node.left);
1521
+ const leftOutput = left.toOut();
1504
1522
  const [right, rightInst] = node.operator !== "??="
1505
- ? c.handleEval(scope, node.right, node.operator === "=" && left ? left : undefined)
1523
+ ? c.handleEval(scope, node.right, node.operator === "=" ? leftOutput : undefined)
1506
1524
  : [
1507
1525
  new LazyValue((scope, out) => c.handleEval(scope, node.right, out)),
1508
1526
  [],
1509
1527
  ];
1510
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1511
1528
  const [op, opInst] = left[node.operator](scope, right);
1512
- if (left)
1513
- scope.clearDependentCache(left);
1529
+ scope.clearDependentCache(left);
1530
+ scope.clearDependentCache(leftOutput);
1514
1531
  return [op, [...leftInst, ...rightInst, ...opInst]];
1515
1532
  };
1516
1533
  const UnaryExpression = (c, scope, { argument, operator }, out) => {
@@ -1526,10 +1543,8 @@ const UnaryExpression = (c, scope, { argument, operator }, out) => {
1526
1543
  return [op, [...argInst, ...opInst]];
1527
1544
  };
1528
1545
  const UpdateExpression = (c, scope, { argument, operator, prefix }, out) => {
1529
- const [arg, argInst] = c.handle(scope, argument);
1530
- if (arg)
1531
- scope.clearDependentCache(arg);
1532
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1546
+ const [arg, argInst] = c.handleValue(scope, argument);
1547
+ scope.clearDependentCache(arg);
1533
1548
  const [op, opInst] = arg[operator](scope, prefix, out);
1534
1549
  return [op, [...argInst, ...opInst]];
1535
1550
  };
@@ -1679,7 +1694,9 @@ function asMergeableJumpNode(body) {
1679
1694
  if (body.type === "ContinueStatement" || body.type === "BreakStatement")
1680
1695
  return body;
1681
1696
  if (body.type !== "BlockStatement")
1682
- return undefined;
1697
+ return;
1698
+ if (body.body.length == 0)
1699
+ return;
1683
1700
  return asMergeableJumpNode(body.body[0]);
1684
1701
  }
1685
1702
 
@@ -1961,7 +1978,7 @@ class FunctionValue extends VoidValue {
1961
1978
  for (let i = 0; i < this.params.length; i++) {
1962
1979
  const value = this.unwrapParameter(this.params[i]);
1963
1980
  this.paramValues.push(value);
1964
- this.paramOuts.push(value instanceof AssignmentValue ? value.left : value);
1981
+ this.paramOuts.push(value.toOut());
1965
1982
  if (!(value instanceof AssignmentValue))
1966
1983
  this.minimumArgumentCount = i + 1;
1967
1984
  }
@@ -2133,7 +2150,7 @@ class FunctionValue extends VoidValue {
2133
2150
  const hasDefault = value instanceof AssignmentValue;
2134
2151
  this.destructuringKeyData.set(key, { inst: propInst, value });
2135
2152
  members.set(key, {
2136
- value: hasDefault ? value.left : value,
2153
+ value: value.toOut(),
2137
2154
  handler: (get, propExists, scope) => {
2138
2155
  return this.c.handle(scope, prop, () => {
2139
2156
  const inst = [...propInst];
@@ -2164,7 +2181,7 @@ class FunctionValue extends VoidValue {
2164
2181
  const key = new LiteralValue(i);
2165
2182
  this.destructuringKeyData.set(key, { inst: [], value });
2166
2183
  members.set(key, {
2167
- value: hasDefault ? value.left : value,
2184
+ value: value.toOut(),
2168
2185
  handler: (get, propExists, scope) => {
2169
2186
  return this.c.handle(scope, element, () => {
2170
2187
  if (propExists() || !hasDefault) {
@@ -2379,12 +2396,10 @@ const ArrayPattern = (c, scope, node) => {
2379
2396
  const element = node.elements[i];
2380
2397
  if (!element)
2381
2398
  continue;
2382
- const value = pipeInsts(c.handle(scope, element), inst);
2399
+ const value = pipeInsts(c.handleValue(scope, element), inst);
2383
2400
  const hasDefault = value instanceof AssignmentValue;
2384
- if (!value)
2385
- throw new CompilerError("Destructuring element must resolve to a value", element);
2386
2401
  members.set(new LiteralValue(i), {
2387
- value: hasDefault ? value.left : value,
2402
+ value: value.toOut(),
2388
2403
  handler(get, propExists, scope) {
2389
2404
  return c.handle(scope, element, () => {
2390
2405
  if (propExists() || !hasDefault) {
@@ -2412,12 +2427,10 @@ const ObjectPattern = (c, scope, node) => {
2412
2427
  const [key, keyInst] = propKey.type === "Identifier" && !prop.computed
2413
2428
  ? [new LiteralValue(propKey.name), []]
2414
2429
  : c.handleEval(scope, propKey);
2415
- const value = pipeInsts(c.handle(scope, prop.value), inst);
2430
+ const value = pipeInsts(c.handleValue(scope, prop.value), inst);
2416
2431
  const hasDefault = value instanceof AssignmentValue;
2417
- if (!value)
2418
- throw new CompilerError("Destructuring member must resolve to a value", prop.value);
2419
2432
  members.set(key, {
2420
- value: hasDefault ? value.left : value,
2433
+ value: value.toOut(),
2421
2434
  handler(get, propExists, scope) {
2422
2435
  return c.handle(scope, prop, () => {
2423
2436
  const inst = keyInst;
@@ -2438,9 +2451,7 @@ const ObjectPattern = (c, scope, node) => {
2438
2451
  // the mlog runtime already does this check for us
2439
2452
  const OptionalMemberExpression = (c, scope, node, out) => MemberExpression(c, scope, node, out, true);
2440
2453
  const AssignmentPattern = (c, scope, node) => {
2441
- const [left, inst] = c.handle(scope, node.left);
2442
- if (!left)
2443
- throw new CompilerError("Left side of assignment pattern could not be resolved", node.left);
2454
+ const [left, inst] = c.handleValue(scope, node.left);
2444
2455
  const right = new LazyValue((_, out) => c.handleEval(scope, node.right, out));
2445
2456
  return [new AssignmentValue(left, right), inst];
2446
2457
  };
@@ -2555,8 +2566,9 @@ var handlers = /*#__PURE__*/Object.freeze({
2555
2566
  });
2556
2567
 
2557
2568
  class MacroFunction extends VoidValue {
2558
- constructor(fn) {
2569
+ constructor(fn, paramOuts) {
2559
2570
  super();
2571
+ this.paramOuts = paramOuts;
2560
2572
  this.macro = true;
2561
2573
  this.mutability = EMutability.constant;
2562
2574
  this.fn = fn;
@@ -2567,6 +2579,9 @@ class MacroFunction extends VoidValue {
2567
2579
  eval(_scope) {
2568
2580
  return [this, []];
2569
2581
  }
2582
+ preCall(_scope, _out) {
2583
+ return this.paramOuts;
2584
+ }
2570
2585
  debugString() {
2571
2586
  return "MacroFunction";
2572
2587
  }
@@ -2676,7 +2691,10 @@ class Concat extends MacroFunction {
2676
2691
  const [first] = values;
2677
2692
  if (!isTemplateObjectArray(first)) {
2678
2693
  assertLiterals(values, "Concat arguments must be all literal values");
2679
- return [new LiteralValue(values.map(v => v.data).join("")), []];
2694
+ let text = "";
2695
+ for (const value of values)
2696
+ text += value.data;
2697
+ return [new LiteralValue(text), []];
2680
2698
  }
2681
2699
  let text = "";
2682
2700
  const [, ...interpolated] = values;
@@ -3594,24 +3612,28 @@ class VarsNamespace extends NamespaceMacro {
3594
3612
  const itemSize = 2;
3595
3613
  class DynamicArray extends ObjectValue {
3596
3614
  constructor(scope, name, values, dynamic) {
3597
- const getterName = `${name}.&gtemp`;
3598
- const setterName = `${name}.&stemp`;
3615
+ const getterName = `${name}.&read`;
3616
+ const setterName = `${name}.&write`;
3599
3617
  const returnName = `${name}.&rt`;
3600
3618
  const lengthName = getLengthName(name);
3601
3619
  const lengthStore = new StoreValue(lengthName);
3602
3620
  const sizeValue = new LiteralValue(values.length);
3603
3621
  super({
3604
- // array[index]
3605
3622
  fill: new MacroFunction((scope, out, value) => {
3623
+ if (!value)
3624
+ throw new CompilerError("Missing argument: value");
3625
+ this.initFill();
3606
3626
  const inst = [];
3607
- for (let i = 0; i < values.length; i++) {
3608
- pipeInsts(values[i]["="](scope, value), inst);
3609
- }
3627
+ const returnAddress = new LiteralValue(null);
3628
+ pipeInsts(values[0]["="](scope, value), inst);
3629
+ pipeInsts(this.returnTemp["="](scope, returnAddress), inst);
3630
+ inst.push(new JumpInstruction(this.fillAddr, EJumpKind.Always));
3631
+ inst.push(new AddressResolver(returnAddress));
3610
3632
  if (this.lengthStore) {
3611
3633
  pipeInsts(this.lengthStore["="](scope, new LiteralValue(values.length)), inst);
3612
3634
  }
3613
3635
  return [null, inst];
3614
- }),
3636
+ }, [values[0]]),
3615
3637
  at: new MacroFunction((scope, out, index) => {
3616
3638
  const inst = [];
3617
3639
  const isLiteral = index instanceof LiteralValue;
@@ -3652,7 +3674,7 @@ class DynamicArray extends ObjectValue {
3652
3674
  });
3653
3675
  const [, inst] = entry["="](scope, item);
3654
3676
  return [new LiteralValue(null), inst];
3655
- }),
3677
+ }, [setterName]),
3656
3678
  pop: new MacroFunction((scope, out) => {
3657
3679
  const checked = scope.checkIndexes;
3658
3680
  const inst = [];
@@ -3667,7 +3689,7 @@ class DynamicArray extends ObjectValue {
3667
3689
  failAddress,
3668
3690
  });
3669
3691
  const nullLiteral = new LiteralValue(null);
3670
- const hasOut = out !== discardedName;
3692
+ const hasOut = !isDiscardedOut(out);
3671
3693
  const result = hasOut
3672
3694
  ? pipeInsts(entry.eval(scope, out), inst)
3673
3695
  : nullLiteral;
@@ -3686,7 +3708,7 @@ class DynamicArray extends ObjectValue {
3686
3708
  assertBounds(index, 0, values.length - 1);
3687
3709
  }
3688
3710
  const checked = scope.checkIndexes;
3689
- const hasOut = out !== discardedName;
3711
+ const hasOut = !isDiscardedOut(out);
3690
3712
  const { returnTemp } = this;
3691
3713
  const inst = [];
3692
3714
  let result = new LiteralValue(null);
@@ -3736,6 +3758,7 @@ class DynamicArray extends ObjectValue {
3736
3758
  this.bundledSetter = false;
3737
3759
  this.bundledGetter = false;
3738
3760
  this.bundledRemoveAt = false;
3761
+ this.bundledFill = false;
3739
3762
  this.scope = scope;
3740
3763
  this.name = name;
3741
3764
  this.values = values;
@@ -3747,6 +3770,7 @@ class DynamicArray extends ObjectValue {
3747
3770
  this.getterAddr = new LiteralValue(null);
3748
3771
  this.setterAddr = new LiteralValue(null);
3749
3772
  this.removeAtAddr = new LiteralValue(null);
3773
+ this.fillAddr = new LiteralValue(null);
3750
3774
  }
3751
3775
  get(scope, index, out) {
3752
3776
  if (super.hasProperty(scope, index))
@@ -3806,6 +3830,19 @@ class DynamicArray extends ObjectValue {
3806
3830
  pipeInsts(last["="](this.scope, new LiteralValue(null)), inst);
3807
3831
  inst.push(new SetCounterInstruction(this.returnTemp));
3808
3832
  }
3833
+ initFill() {
3834
+ if (this.bundledFill)
3835
+ return;
3836
+ this.bundledFill = true;
3837
+ const { inst } = this.scope;
3838
+ inst.push(new AddressResolver(this.fillAddr));
3839
+ const firstValue = this.values[0];
3840
+ for (let i = 1; i < this.values.length; i++) {
3841
+ const value = this.values[i];
3842
+ pipeInsts(value["="](this.scope, firstValue), inst);
3843
+ }
3844
+ inst.push(new SetCounterInstruction(this.returnTemp));
3845
+ }
3809
3846
  debugString() {
3810
3847
  const id = this.dynamic ? "DynamicArray" : "MutableArray";
3811
3848
  return `${id}("${this.name}")`;
@@ -3905,6 +3942,9 @@ class DynamicArrayEntry extends BaseValue {
3905
3942
  inst.push(new AddressResolver(failAddress));
3906
3943
  return [value, inst];
3907
3944
  }
3945
+ toOut() {
3946
+ return this.array.setterTemp;
3947
+ }
3908
3948
  debugString() {
3909
3949
  const name = this.array.dynamic ? "DynamicArray" : "MutableArray";
3910
3950
  return `${name}Entry`;
@@ -4191,10 +4231,15 @@ class Compiler {
4191
4231
  throw error;
4192
4232
  }
4193
4233
  }
4234
+ /** Handles the node and asserts that it resolves to a value. */
4235
+ handleValue(scope, node, handler = this.handlers[node.type], out) {
4236
+ const result = this.handle(scope, node, handler, out);
4237
+ if (!result[0])
4238
+ throw new CompilerError(`This node (${node.type}) did not return a value`, node);
4239
+ return result;
4240
+ }
4194
4241
  handleEval(scope, node, out) {
4195
- const [res, inst] = this.handle(scope, node, undefined, out);
4196
- if (!res)
4197
- throw new CompilerError(`This node (${node.type}) does not return a value`, node);
4242
+ const [res, inst] = this.handleValue(scope, node, undefined, out);
4198
4243
  const [evaluated, evaluatedInst] = res.eval(scope, out);
4199
4244
  const result = [evaluated, [...inst, ...evaluatedInst]];
4200
4245
  if (this.sourcemap)
package/dist/index.mjs CHANGED
@@ -209,6 +209,9 @@ class VoidValue {
209
209
  return;
210
210
  }
211
211
  postCall(_scope) { }
212
+ toOut() {
213
+ return this;
214
+ }
212
215
  debugString() {
213
216
  return "VoidValue";
214
217
  }
@@ -382,8 +385,8 @@ function extractOutName(out) {
382
385
  return out.name;
383
386
  }
384
387
  function extractDestrucuringOut(out, field) {
385
- if (out === discardedName)
386
- return out;
388
+ if (isDiscardedOut(out))
389
+ return discardedName;
387
390
  if (!(out instanceof DestructuringValue))
388
391
  return;
389
392
  return out.fields[field] ?? discardedName;
@@ -438,6 +441,13 @@ function assertObjectFields(value, fields) {
438
441
  }
439
442
  return result;
440
443
  }
444
+ function isDiscardedOut(out) {
445
+ if (!out)
446
+ return false;
447
+ if (typeof out === "string")
448
+ return out === discardedName;
449
+ return out.name === discardedName;
450
+ }
441
451
 
442
452
  class SetInstruction extends InstructionBase {
443
453
  constructor(store, value) {
@@ -519,7 +529,7 @@ class JumpInstruction extends InstructionBase {
519
529
  if (test === out)
520
530
  return [];
521
531
  return [
522
- new JumpInstruction(out.address, EJumpKind.Equal, test, new LiteralValue(+out.whenTrue)),
532
+ new JumpInstruction(out.address, out.whenTrue ? EJumpKind.NotEqual : EJumpKind.Equal, test, new LiteralValue(0)),
523
533
  ];
524
534
  }
525
535
  }
@@ -621,8 +631,11 @@ class BaseValue extends VoidValue {
621
631
  ];
622
632
  }
623
633
  "u+"(scope, out) {
624
- // TODO: should it return 0 + this ?
625
- return this.eval(scope, out);
634
+ const inst = [];
635
+ const value = pipeInsts(this.eval(scope), inst);
636
+ const zero = new LiteralValue(0);
637
+ const result = pipeInsts(zero["+"](scope, value, out), inst);
638
+ return [result, inst];
626
639
  }
627
640
  "!"(scope, out) {
628
641
  const inst = [];
@@ -644,7 +657,7 @@ class BaseValue extends VoidValue {
644
657
  const endAdress = new LiteralValue(null);
645
658
  const [left, leftInst] = this.eval(scope, result);
646
659
  const [right, rightInst] = other.eval(scope, result);
647
- const [test, testInst] = this["==="](scope, nullLiteral);
660
+ const [test, testInst] = left["==="](scope, nullLiteral);
648
661
  return [
649
662
  result,
650
663
  [
@@ -709,7 +722,7 @@ const assignmentToBinary = {
709
722
  for (const k in assignmentToBinary) {
710
723
  const op = k;
711
724
  BaseValue.prototype[op] = function (scope, value) {
712
- const [opValue, opInst] = this[assignmentToBinary[op]](scope, value, this);
725
+ const [opValue, opInst] = this[assignmentToBinary[op]](scope, value, this.toOut());
713
726
  const [retValue, retInst] = this["="](scope, opValue);
714
727
  return [retValue, [...opInst, ...retInst]];
715
728
  };
@@ -717,9 +730,10 @@ for (const k in assignmentToBinary) {
717
730
  for (const key of updateOperators) {
718
731
  BaseValue.prototype[key] = function (scope, prefix, out) {
719
732
  let [ret, inst] = this.eval(scope);
720
- if (!prefix && out !== discardedName) {
733
+ if (!prefix && !isDiscardedOut(out)) {
721
734
  const temp = StoreValue.from(scope, out);
722
- ret = pipeInsts(temp["="](scope, ret), inst);
735
+ pipeInsts(temp["="](scope, ret), inst);
736
+ ret = temp;
723
737
  }
724
738
  const kind = key === "++" ? "+=" : "-=";
725
739
  pipeInsts(this[kind](scope, new LiteralValue(1)), inst);
@@ -876,7 +890,7 @@ class StoreValue extends BaseValue {
876
890
  const [evalValue, evalInst] = value.eval(scope, this);
877
891
  if (evalValue.macro)
878
892
  throw new CompilerError(`Cannot assign a macro to a store (attempted to assign [${evalValue.debugString()}] to [${this.debugString()}])`);
879
- return [this, [...evalInst, new SetInstruction(this, evalValue)]];
893
+ return [evalValue, [...evalInst, new SetInstruction(this, evalValue)]];
880
894
  }
881
895
  "=="(scope, value, out) {
882
896
  if (compareStores(this, value))
@@ -1139,10 +1153,13 @@ class AssignmentValue extends VoidValue {
1139
1153
  }
1140
1154
  "="(scope, value, out) {
1141
1155
  const inst = [];
1142
- const input = pipeInsts(value["??"](scope, this.right, this.left), inst);
1156
+ const input = pipeInsts(value["??"](scope, this.right, this.left.toOut()), inst);
1143
1157
  const output = pipeInsts(this.left["="](scope, input, out), inst);
1144
1158
  return [output, inst];
1145
1159
  }
1160
+ toOut() {
1161
+ return this.left.toOut();
1162
+ }
1146
1163
  debugString() {
1147
1164
  return "AssignmentValue";
1148
1165
  }
@@ -1474,22 +1491,22 @@ const LogicalExpression = (c, scope, node, out) => {
1474
1491
  if (node.operator !== "??")
1475
1492
  return LRExpression(c, scope, node, out);
1476
1493
  const other = new LazyValue((scope, out) => c.handleEval(scope, node.right, out));
1477
- const [left, leftInst] = c.handleEval(scope, node.left);
1494
+ const [left, leftInst] = c.handleValue(scope, node.left);
1478
1495
  const [result, resultInst] = left["??"](scope, other, out);
1479
1496
  return [result, [...leftInst, ...resultInst]];
1480
1497
  };
1481
1498
  const AssignmentExpression = (c, scope, node) => {
1482
- const [left, leftInst] = c.handle(scope, node.left);
1499
+ const [left, leftInst] = c.handleValue(scope, node.left);
1500
+ const leftOutput = left.toOut();
1483
1501
  const [right, rightInst] = node.operator !== "??="
1484
- ? c.handleEval(scope, node.right, node.operator === "=" && left ? left : undefined)
1502
+ ? c.handleEval(scope, node.right, node.operator === "=" ? leftOutput : undefined)
1485
1503
  : [
1486
1504
  new LazyValue((scope, out) => c.handleEval(scope, node.right, out)),
1487
1505
  [],
1488
1506
  ];
1489
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1490
1507
  const [op, opInst] = left[node.operator](scope, right);
1491
- if (left)
1492
- scope.clearDependentCache(left);
1508
+ scope.clearDependentCache(left);
1509
+ scope.clearDependentCache(leftOutput);
1493
1510
  return [op, [...leftInst, ...rightInst, ...opInst]];
1494
1511
  };
1495
1512
  const UnaryExpression = (c, scope, { argument, operator }, out) => {
@@ -1505,10 +1522,8 @@ const UnaryExpression = (c, scope, { argument, operator }, out) => {
1505
1522
  return [op, [...argInst, ...opInst]];
1506
1523
  };
1507
1524
  const UpdateExpression = (c, scope, { argument, operator, prefix }, out) => {
1508
- const [arg, argInst] = c.handle(scope, argument);
1509
- if (arg)
1510
- scope.clearDependentCache(arg);
1511
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1525
+ const [arg, argInst] = c.handleValue(scope, argument);
1526
+ scope.clearDependentCache(arg);
1512
1527
  const [op, opInst] = arg[operator](scope, prefix, out);
1513
1528
  return [op, [...argInst, ...opInst]];
1514
1529
  };
@@ -1658,7 +1673,9 @@ function asMergeableJumpNode(body) {
1658
1673
  if (body.type === "ContinueStatement" || body.type === "BreakStatement")
1659
1674
  return body;
1660
1675
  if (body.type !== "BlockStatement")
1661
- return undefined;
1676
+ return;
1677
+ if (body.body.length == 0)
1678
+ return;
1662
1679
  return asMergeableJumpNode(body.body[0]);
1663
1680
  }
1664
1681
 
@@ -1940,7 +1957,7 @@ class FunctionValue extends VoidValue {
1940
1957
  for (let i = 0; i < this.params.length; i++) {
1941
1958
  const value = this.unwrapParameter(this.params[i]);
1942
1959
  this.paramValues.push(value);
1943
- this.paramOuts.push(value instanceof AssignmentValue ? value.left : value);
1960
+ this.paramOuts.push(value.toOut());
1944
1961
  if (!(value instanceof AssignmentValue))
1945
1962
  this.minimumArgumentCount = i + 1;
1946
1963
  }
@@ -2112,7 +2129,7 @@ class FunctionValue extends VoidValue {
2112
2129
  const hasDefault = value instanceof AssignmentValue;
2113
2130
  this.destructuringKeyData.set(key, { inst: propInst, value });
2114
2131
  members.set(key, {
2115
- value: hasDefault ? value.left : value,
2132
+ value: value.toOut(),
2116
2133
  handler: (get, propExists, scope) => {
2117
2134
  return this.c.handle(scope, prop, () => {
2118
2135
  const inst = [...propInst];
@@ -2143,7 +2160,7 @@ class FunctionValue extends VoidValue {
2143
2160
  const key = new LiteralValue(i);
2144
2161
  this.destructuringKeyData.set(key, { inst: [], value });
2145
2162
  members.set(key, {
2146
- value: hasDefault ? value.left : value,
2163
+ value: value.toOut(),
2147
2164
  handler: (get, propExists, scope) => {
2148
2165
  return this.c.handle(scope, element, () => {
2149
2166
  if (propExists() || !hasDefault) {
@@ -2358,12 +2375,10 @@ const ArrayPattern = (c, scope, node) => {
2358
2375
  const element = node.elements[i];
2359
2376
  if (!element)
2360
2377
  continue;
2361
- const value = pipeInsts(c.handle(scope, element), inst);
2378
+ const value = pipeInsts(c.handleValue(scope, element), inst);
2362
2379
  const hasDefault = value instanceof AssignmentValue;
2363
- if (!value)
2364
- throw new CompilerError("Destructuring element must resolve to a value", element);
2365
2380
  members.set(new LiteralValue(i), {
2366
- value: hasDefault ? value.left : value,
2381
+ value: value.toOut(),
2367
2382
  handler(get, propExists, scope) {
2368
2383
  return c.handle(scope, element, () => {
2369
2384
  if (propExists() || !hasDefault) {
@@ -2391,12 +2406,10 @@ const ObjectPattern = (c, scope, node) => {
2391
2406
  const [key, keyInst] = propKey.type === "Identifier" && !prop.computed
2392
2407
  ? [new LiteralValue(propKey.name), []]
2393
2408
  : c.handleEval(scope, propKey);
2394
- const value = pipeInsts(c.handle(scope, prop.value), inst);
2409
+ const value = pipeInsts(c.handleValue(scope, prop.value), inst);
2395
2410
  const hasDefault = value instanceof AssignmentValue;
2396
- if (!value)
2397
- throw new CompilerError("Destructuring member must resolve to a value", prop.value);
2398
2411
  members.set(key, {
2399
- value: hasDefault ? value.left : value,
2412
+ value: value.toOut(),
2400
2413
  handler(get, propExists, scope) {
2401
2414
  return c.handle(scope, prop, () => {
2402
2415
  const inst = keyInst;
@@ -2417,9 +2430,7 @@ const ObjectPattern = (c, scope, node) => {
2417
2430
  // the mlog runtime already does this check for us
2418
2431
  const OptionalMemberExpression = (c, scope, node, out) => MemberExpression(c, scope, node, out, true);
2419
2432
  const AssignmentPattern = (c, scope, node) => {
2420
- const [left, inst] = c.handle(scope, node.left);
2421
- if (!left)
2422
- throw new CompilerError("Left side of assignment pattern could not be resolved", node.left);
2433
+ const [left, inst] = c.handleValue(scope, node.left);
2423
2434
  const right = new LazyValue((_, out) => c.handleEval(scope, node.right, out));
2424
2435
  return [new AssignmentValue(left, right), inst];
2425
2436
  };
@@ -2534,8 +2545,9 @@ var handlers = /*#__PURE__*/Object.freeze({
2534
2545
  });
2535
2546
 
2536
2547
  class MacroFunction extends VoidValue {
2537
- constructor(fn) {
2548
+ constructor(fn, paramOuts) {
2538
2549
  super();
2550
+ this.paramOuts = paramOuts;
2539
2551
  this.macro = true;
2540
2552
  this.mutability = EMutability.constant;
2541
2553
  this.fn = fn;
@@ -2546,6 +2558,9 @@ class MacroFunction extends VoidValue {
2546
2558
  eval(_scope) {
2547
2559
  return [this, []];
2548
2560
  }
2561
+ preCall(_scope, _out) {
2562
+ return this.paramOuts;
2563
+ }
2549
2564
  debugString() {
2550
2565
  return "MacroFunction";
2551
2566
  }
@@ -2655,7 +2670,10 @@ class Concat extends MacroFunction {
2655
2670
  const [first] = values;
2656
2671
  if (!isTemplateObjectArray(first)) {
2657
2672
  assertLiterals(values, "Concat arguments must be all literal values");
2658
- return [new LiteralValue(values.map(v => v.data).join("")), []];
2673
+ let text = "";
2674
+ for (const value of values)
2675
+ text += value.data;
2676
+ return [new LiteralValue(text), []];
2659
2677
  }
2660
2678
  let text = "";
2661
2679
  const [, ...interpolated] = values;
@@ -3573,24 +3591,28 @@ class VarsNamespace extends NamespaceMacro {
3573
3591
  const itemSize = 2;
3574
3592
  class DynamicArray extends ObjectValue {
3575
3593
  constructor(scope, name, values, dynamic) {
3576
- const getterName = `${name}.&gtemp`;
3577
- const setterName = `${name}.&stemp`;
3594
+ const getterName = `${name}.&read`;
3595
+ const setterName = `${name}.&write`;
3578
3596
  const returnName = `${name}.&rt`;
3579
3597
  const lengthName = getLengthName(name);
3580
3598
  const lengthStore = new StoreValue(lengthName);
3581
3599
  const sizeValue = new LiteralValue(values.length);
3582
3600
  super({
3583
- // array[index]
3584
3601
  fill: new MacroFunction((scope, out, value) => {
3602
+ if (!value)
3603
+ throw new CompilerError("Missing argument: value");
3604
+ this.initFill();
3585
3605
  const inst = [];
3586
- for (let i = 0; i < values.length; i++) {
3587
- pipeInsts(values[i]["="](scope, value), inst);
3588
- }
3606
+ const returnAddress = new LiteralValue(null);
3607
+ pipeInsts(values[0]["="](scope, value), inst);
3608
+ pipeInsts(this.returnTemp["="](scope, returnAddress), inst);
3609
+ inst.push(new JumpInstruction(this.fillAddr, EJumpKind.Always));
3610
+ inst.push(new AddressResolver(returnAddress));
3589
3611
  if (this.lengthStore) {
3590
3612
  pipeInsts(this.lengthStore["="](scope, new LiteralValue(values.length)), inst);
3591
3613
  }
3592
3614
  return [null, inst];
3593
- }),
3615
+ }, [values[0]]),
3594
3616
  at: new MacroFunction((scope, out, index) => {
3595
3617
  const inst = [];
3596
3618
  const isLiteral = index instanceof LiteralValue;
@@ -3631,7 +3653,7 @@ class DynamicArray extends ObjectValue {
3631
3653
  });
3632
3654
  const [, inst] = entry["="](scope, item);
3633
3655
  return [new LiteralValue(null), inst];
3634
- }),
3656
+ }, [setterName]),
3635
3657
  pop: new MacroFunction((scope, out) => {
3636
3658
  const checked = scope.checkIndexes;
3637
3659
  const inst = [];
@@ -3646,7 +3668,7 @@ class DynamicArray extends ObjectValue {
3646
3668
  failAddress,
3647
3669
  });
3648
3670
  const nullLiteral = new LiteralValue(null);
3649
- const hasOut = out !== discardedName;
3671
+ const hasOut = !isDiscardedOut(out);
3650
3672
  const result = hasOut
3651
3673
  ? pipeInsts(entry.eval(scope, out), inst)
3652
3674
  : nullLiteral;
@@ -3665,7 +3687,7 @@ class DynamicArray extends ObjectValue {
3665
3687
  assertBounds(index, 0, values.length - 1);
3666
3688
  }
3667
3689
  const checked = scope.checkIndexes;
3668
- const hasOut = out !== discardedName;
3690
+ const hasOut = !isDiscardedOut(out);
3669
3691
  const { returnTemp } = this;
3670
3692
  const inst = [];
3671
3693
  let result = new LiteralValue(null);
@@ -3715,6 +3737,7 @@ class DynamicArray extends ObjectValue {
3715
3737
  this.bundledSetter = false;
3716
3738
  this.bundledGetter = false;
3717
3739
  this.bundledRemoveAt = false;
3740
+ this.bundledFill = false;
3718
3741
  this.scope = scope;
3719
3742
  this.name = name;
3720
3743
  this.values = values;
@@ -3726,6 +3749,7 @@ class DynamicArray extends ObjectValue {
3726
3749
  this.getterAddr = new LiteralValue(null);
3727
3750
  this.setterAddr = new LiteralValue(null);
3728
3751
  this.removeAtAddr = new LiteralValue(null);
3752
+ this.fillAddr = new LiteralValue(null);
3729
3753
  }
3730
3754
  get(scope, index, out) {
3731
3755
  if (super.hasProperty(scope, index))
@@ -3785,6 +3809,19 @@ class DynamicArray extends ObjectValue {
3785
3809
  pipeInsts(last["="](this.scope, new LiteralValue(null)), inst);
3786
3810
  inst.push(new SetCounterInstruction(this.returnTemp));
3787
3811
  }
3812
+ initFill() {
3813
+ if (this.bundledFill)
3814
+ return;
3815
+ this.bundledFill = true;
3816
+ const { inst } = this.scope;
3817
+ inst.push(new AddressResolver(this.fillAddr));
3818
+ const firstValue = this.values[0];
3819
+ for (let i = 1; i < this.values.length; i++) {
3820
+ const value = this.values[i];
3821
+ pipeInsts(value["="](this.scope, firstValue), inst);
3822
+ }
3823
+ inst.push(new SetCounterInstruction(this.returnTemp));
3824
+ }
3788
3825
  debugString() {
3789
3826
  const id = this.dynamic ? "DynamicArray" : "MutableArray";
3790
3827
  return `${id}("${this.name}")`;
@@ -3884,6 +3921,9 @@ class DynamicArrayEntry extends BaseValue {
3884
3921
  inst.push(new AddressResolver(failAddress));
3885
3922
  return [value, inst];
3886
3923
  }
3924
+ toOut() {
3925
+ return this.array.setterTemp;
3926
+ }
3887
3927
  debugString() {
3888
3928
  const name = this.array.dynamic ? "DynamicArray" : "MutableArray";
3889
3929
  return `${name}Entry`;
@@ -4170,10 +4210,15 @@ class Compiler {
4170
4210
  throw error;
4171
4211
  }
4172
4212
  }
4213
+ /** Handles the node and asserts that it resolves to a value. */
4214
+ handleValue(scope, node, handler = this.handlers[node.type], out) {
4215
+ const result = this.handle(scope, node, handler, out);
4216
+ if (!result[0])
4217
+ throw new CompilerError(`This node (${node.type}) did not return a value`, node);
4218
+ return result;
4219
+ }
4173
4220
  handleEval(scope, node, out) {
4174
- const [res, inst] = this.handle(scope, node, undefined, out);
4175
- if (!res)
4176
- throw new CompilerError(`This node (${node.type}) does not return a value`, node);
4221
+ const [res, inst] = this.handleValue(scope, node, undefined, out);
4177
4222
  const [evaluated, evaluatedInst] = res.eval(scope, out);
4178
4223
  const result = [evaluated, [...inst, ...evaluatedInst]];
4179
4224
  if (this.sourcemap)
@@ -12,16 +12,19 @@ export declare class DynamicArray extends ObjectValue {
12
12
  getterAddr: LiteralValue<number | null>;
13
13
  setterAddr: LiteralValue<number | null>;
14
14
  removeAtAddr: LiteralValue<number | null>;
15
+ fillAddr: LiteralValue<number | null>;
15
16
  lengthStore?: StoreValue;
16
17
  bundledSetter: boolean;
17
18
  bundledGetter: boolean;
18
19
  bundledRemoveAt: boolean;
20
+ bundledFill: boolean;
19
21
  constructor(scope: IScope, name: string, values: IValue[], dynamic: boolean);
20
22
  get(scope: IScope, index: IValue, out?: TEOutput): TValueInstructions;
21
23
  hasProperty(scope: IScope, prop: IValue): boolean;
22
24
  initGetter(): void;
23
25
  initSetter(): void;
24
26
  initRemoveAt(): void;
27
+ initFill(): void;
25
28
  debugString(): string;
26
29
  toString(): string;
27
30
  }
@@ -2,12 +2,14 @@ import { EMutability, IScope, IValue, TEOutput, TValueInstructions } from "../ty
2
2
  import { VoidValue } from "../values";
3
3
  type TFunction<T extends IValue | null> = (scope: IScope, out: TEOutput | undefined, ...args: IValue[]) => TValueInstructions<T>;
4
4
  export declare class MacroFunction<RT extends IValue | null = IValue> extends VoidValue {
5
+ paramOuts?: TEOutput[] | undefined;
5
6
  macro: boolean;
6
7
  mutability: EMutability;
7
8
  fn: TFunction<RT>;
8
- constructor(fn: TFunction<RT>);
9
+ constructor(fn: TFunction<RT>, paramOuts?: TEOutput[] | undefined);
9
10
  call(scope: IScope, args: IValue[], out?: TEOutput): TValueInstructions<RT>;
10
11
  eval(_scope: IScope): TValueInstructions;
12
+ preCall(_scope: IScope, _out?: TEOutput | undefined): readonly TEOutput[] | undefined;
11
13
  debugString(): string;
12
14
  toString(): string;
13
15
  }
package/dist/types.d.ts CHANGED
@@ -236,6 +236,11 @@ export interface IValue extends IValueOperators {
236
236
  postCall(scope: IScope): void;
237
237
  /** The string representation of `this` in error messages. */
238
238
  debugString(): string;
239
+ /**
240
+ * Allows some values to choose alternative representations
241
+ * when they are used as operation outputs.
242
+ */
243
+ toOut(): IValue;
239
244
  }
240
245
  /** Helper type that is used in some typescript assertions */
241
246
  export interface INamedValue extends IValue {
@@ -38,3 +38,4 @@ export interface IParameterDescriptor {
38
38
  }
39
39
  /** Asserts that all of the fields described are present on `value`*/
40
40
  export declare function assertObjectFields(value: ObjectValue, fields: (string | IParameterDescriptor)[]): (IValue | string)[];
41
+ export declare function isDiscardedOut(out: TEOutput | undefined): boolean;
@@ -5,6 +5,7 @@ export declare class AssignmentValue extends VoidValue {
5
5
  right: IValue;
6
6
  constructor(left: IValue, right: IValue);
7
7
  "="(scope: IScope, value: IValue, out?: TEOutput): TValueInstructions;
8
+ toOut(): IValue;
8
9
  debugString(): string;
9
10
  toString(): string;
10
11
  }
@@ -9,6 +9,7 @@ export declare class VoidValue implements IValue {
9
9
  hasProperty(_scope: IScope, _prop: IValue): boolean;
10
10
  preCall(_scope: IScope, _out?: TEOutput): readonly TEOutput[] | undefined;
11
11
  postCall(_scope: IScope): void;
12
+ toOut(): IValue;
12
13
  debugString(): string;
13
14
  toString(): string;
14
15
  }
package/lib/commands.d.ts CHANGED
@@ -560,6 +560,8 @@ declare global {
560
560
 
561
561
  /**
562
562
  * Makes the unit bound to this processor move to the enemy spawn
563
+ *
564
+ * @deprecated This command is no longer available in mindustry v7.
563
565
  */
564
566
  function pathfind(): void;
565
567
 
package/lib/helpers.d.ts CHANGED
@@ -20,6 +20,8 @@ declare function getBuilding<T extends BasicBuilding = AnyBuilding>(
20
20
  name: string
21
21
  ): T;
22
22
 
23
+ type ConcatLiteral = string | number | boolean | undefined;
24
+
23
25
  /**
24
26
  * Concatenates a list of constant literal values.
25
27
  *
@@ -34,9 +36,7 @@ declare function getBuilding<T extends BasicBuilding = AnyBuilding>(
34
36
  * ```
35
37
  */
36
38
  declare function concat(
37
- ...args:
38
- | (string | number | undefined)[]
39
- | [TemplateStringsArray, ...(string | number | undefined)[]]
39
+ ...args: ConcatLiteral[] | [TemplateStringsArray, ...ConcatLiteral[]]
40
40
  ): string;
41
41
 
42
42
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mlogjs",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Compiler that compiles Javascript to Mindustry Logic Code.",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",