mlogjs 0.5.2 → 0.6.0

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.
Files changed (79) hide show
  1. package/README.md +2 -2
  2. package/dist/{index.cjs → Compiler-7GMG4MDr.js} +1746 -452
  3. package/dist/Compiler.d.ts +8 -12
  4. package/dist/CompilerError.d.ts +13 -3
  5. package/dist/Scope.d.ts +8 -8
  6. package/dist/bin/files.d.ts +18 -0
  7. package/dist/bin.mjs +170 -0
  8. package/dist/handlers/Import.d.ts +5 -0
  9. package/dist/handlers/Literal.d.ts +2 -2
  10. package/dist/handlers/Typescript.d.ts +2 -2
  11. package/dist/handlers/index.d.ts +1 -0
  12. package/dist/index.mjs +3 -4267
  13. package/dist/instructions/AddressResolver.d.ts +4 -4
  14. package/dist/instructions/ControlInstructions.d.ts +3 -3
  15. package/dist/instructions/InstructionBase.d.ts +1 -0
  16. package/dist/instructions/JumpInstruction.d.ts +5 -5
  17. package/dist/instructions/index.d.ts +0 -1
  18. package/dist/macros/DynamicArray.d.ts +4 -4
  19. package/dist/macros/Function.d.ts +2 -2
  20. package/dist/macros/GetBuildings.d.ts +4 -0
  21. package/dist/macros/GetColor.d.ts +4 -0
  22. package/dist/macros/Marker.d.ts +4 -0
  23. package/dist/macros/Namespace.d.ts +16 -0
  24. package/dist/macros/Queries.d.ts +10 -0
  25. package/dist/macros/StringView.d.ts +4 -0
  26. package/dist/macros/commands/Effect.d.ts +4 -0
  27. package/dist/macros/commands/Format.d.ts +4 -0
  28. package/dist/macros/commands/LocalePrint.d.ts +4 -0
  29. package/dist/macros/commands/PlayMusic.d.ts +4 -0
  30. package/dist/macros/commands/PlaySound.d.ts +4 -0
  31. package/dist/macros/commands/PrintChar.d.ts +4 -0
  32. package/dist/macros/commands/Query.d.ts +4 -0
  33. package/dist/macros/commands/SetProp.d.ts +4 -0
  34. package/dist/macros/commands/SpawnBullet.d.ts +4 -0
  35. package/dist/macros/commands/Sync.d.ts +6 -0
  36. package/dist/macros/commands/UnpackColor.d.ts +4 -0
  37. package/dist/macros/commands/WeatherSense.d.ts +4 -0
  38. package/dist/macros/commands/WeatherSet.d.ts +4 -0
  39. package/dist/macros/commands/index.d.ts +13 -0
  40. package/dist/macros/index.d.ts +7 -1
  41. package/dist/macros/util.d.ts +5 -2
  42. package/dist/modules.d.ts +8 -0
  43. package/dist/operators.d.ts +1 -1
  44. package/dist/types.d.ts +84 -59
  45. package/dist/utils/constants.d.ts +9 -4
  46. package/dist/utils/general.d.ts +8 -9
  47. package/dist/utils/instruction.d.ts +5 -1
  48. package/dist/utils/value.d.ts +14 -5
  49. package/dist/values/AssignmentValue.d.ts +1 -1
  50. package/dist/values/BaseValue.d.ts +4 -2
  51. package/dist/values/DestructuringValue.d.ts +5 -7
  52. package/dist/values/FunctionValue.d.ts +8 -6
  53. package/dist/values/JumpOutValue.d.ts +4 -4
  54. package/dist/values/LazyValue.d.ts +3 -3
  55. package/dist/values/LiteralValue.d.ts +2 -3
  56. package/dist/values/ObjectValue.d.ts +6 -11
  57. package/dist/values/StoreValue.d.ts +10 -5
  58. package/dist/values/VoidValue.d.ts +2 -1
  59. package/dist/values/index.d.ts +0 -1
  60. package/lib/commands.d.ts +776 -730
  61. package/lib/eslib/lib.es2015.collection.d.ts +50 -39
  62. package/lib/eslib/lib.es2015.core.d.ts +59 -0
  63. package/lib/eslib/lib.es2015.d.ts +0 -3
  64. package/lib/eslib/lib.es2015.generator.d.ts +54 -53
  65. package/lib/eslib/lib.es2015.iterable.d.ts +9 -6
  66. package/lib/eslib/lib.es5.d.ts +53 -6
  67. package/lib/globals.d.ts +309 -18
  68. package/lib/helpers.d.ts +86 -52
  69. package/lib/kinds.d.ts +42 -7
  70. package/lib/macros.d.ts +53 -18
  71. package/lib/traits.d.ts +308 -146
  72. package/lib/util.d.ts +144 -48
  73. package/lib/world.d.ts +1197 -0
  74. package/package.json +27 -35
  75. package/dist/bin.cjs +0 -111
  76. package/dist/initScope.d.ts +0 -5
  77. package/dist/instructions/ZeroSpaceInstruction.d.ts +0 -4
  78. package/dist/values/SenseableValue.d.ts +0 -24
  79. /package/dist/{bin.d.ts → bin/index.d.ts} +0 -0
@@ -1,26 +1,5 @@
1
- 'use strict';
2
-
3
- var parser = require('@babel/parser');
4
- var es = require('@babel/types');
5
-
6
- function _interopNamespaceDefault(e) {
7
- var n = Object.create(null);
8
- if (e) {
9
- Object.keys(e).forEach(function (k) {
10
- if (k !== 'default') {
11
- var d = Object.getOwnPropertyDescriptor(e, k);
12
- Object.defineProperty(n, k, d.get ? d : {
13
- enumerable: true,
14
- get: function () { return e[k]; }
15
- });
16
- }
17
- });
18
- }
19
- n.default = e;
20
- return Object.freeze(n);
21
- }
22
-
23
- var es__namespace = /*#__PURE__*/_interopNamespaceDefault(es);
1
+ import { parse } from '@babel/parser';
2
+ import * as es from '@babel/types';
24
3
 
25
4
  class CompilerError extends Error {
26
5
  constructor(message, source) {
@@ -75,29 +54,34 @@ var EInstIntent;
75
54
  EInstIntent[EInstIntent["continue"] = 2] = "continue";
76
55
  EInstIntent[EInstIntent["return"] = 3] = "return";
77
56
  /**
78
- * This is used by the `end` and `stop` instructions.
79
- * Means that the processor will restart or shutdown.
57
+ * This is used by the `end` and `stop` instructions. Means that the processor
58
+ * will restart or shutdown.
80
59
  */
81
60
  EInstIntent[EInstIntent["exit"] = 4] = "exit";
82
61
  })(EInstIntent || (EInstIntent = {}));
83
62
  /** Defines the possible types of mutability of a value */
84
63
  var EMutability;
85
64
  (function (EMutability) {
86
- /**
87
- * The value can be changed by the user's code,
88
- * by the mlog runtime, or us
89
- */
65
+ /** The value can be changed by the user's code, by the mlog runtime, or us */
90
66
  EMutability[EMutability["mutable"] = 0] = "mutable";
91
67
  /**
92
- * The value cannot be directly assigned,
93
- * but it's still not safe from mutations coming from the runtime.
68
+ * The value cannot be directly assigned, but it's still not safe from
69
+ * mutations coming from the runtime.
94
70
  */
95
71
  EMutability[EMutability["readonly"] = 1] = "readonly";
96
- /** Constant values, won't change during execution */
72
+ /** Compile-time constants. */
97
73
  EMutability[EMutability["constant"] = 2] = "constant";
98
74
  /** An immutable value that hasn't been initialized yet */
99
75
  EMutability[EMutability["init"] = 3] = "init";
76
+ /** A value unknown at compile-time that will not change after initialization */
77
+ EMutability[EMutability["immutable"] = 4] = "immutable";
100
78
  })(EMutability || (EMutability = {}));
79
+ var EInlineType;
80
+ (function (EInlineType) {
81
+ EInlineType[EInlineType["auto"] = 0] = "auto";
82
+ EInlineType[EInlineType["always"] = 1] = "always";
83
+ EInlineType[EInlineType["never"] = 2] = "never";
84
+ })(EInlineType || (EInlineType = {}));
101
85
 
102
86
  const arithmeticBinaryOperators = [
103
87
  "*",
@@ -142,7 +126,7 @@ const arithmeticAssignmentOperators = [
142
126
  "-=",
143
127
  "/=",
144
128
  ];
145
- const logicalAssignmentOperators = ["&&=", "||=", "??="];
129
+ const logicalAssignmentOperators$1 = ["&&=", "||=", "??="];
146
130
  const bitwiseAssignmentOperators = [
147
131
  "<<=",
148
132
  ">>=",
@@ -152,7 +136,7 @@ const bitwiseAssignmentOperators = [
152
136
  ];
153
137
  const assignmentOperators = [
154
138
  ...arithmeticAssignmentOperators,
155
- ...logicalAssignmentOperators,
139
+ ...logicalAssignmentOperators$1,
156
140
  ...bitwiseAssignmentOperators,
157
141
  "=",
158
142
  ];
@@ -191,7 +175,7 @@ const operatorMap$2 = {
191
175
  "&": "and",
192
176
  "^": "xor",
193
177
  ">>": "shr",
194
- ">>>": "shr",
178
+ ">>>": "ushr",
195
179
  "<<": "shl",
196
180
  "&&": "land",
197
181
  "||": "or",
@@ -209,10 +193,12 @@ const orderIndependentOperators = [
209
193
  "||",
210
194
  ];
211
195
 
196
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
212
197
  class VoidValue {
213
198
  constructor() {
214
199
  this.mutability = EMutability.mutable;
215
200
  this.macro = false;
201
+ this.volatile = false;
216
202
  }
217
203
  eval(_scope, _out) {
218
204
  throw new CompilerError(`[${this.debugString()}] cannot be evaluated into a value.`);
@@ -236,7 +222,7 @@ class VoidValue {
236
222
  debugString() {
237
223
  return "VoidValue";
238
224
  }
239
- toString() {
225
+ toMlogString() {
240
226
  return '"[macro VoidValue]"';
241
227
  }
242
228
  }
@@ -247,34 +233,7 @@ for (const key of operators) {
247
233
  };
248
234
  }
249
235
 
250
- class InstructionBase {
251
- get hidden() {
252
- return this._hidden;
253
- }
254
- set hidden(value) {
255
- this._hidden = value;
256
- }
257
- constructor(...args) {
258
- this.intent = EInstIntent.none;
259
- this.alwaysRuns = true;
260
- this._hidden = false;
261
- this.args = args;
262
- }
263
- resolve(_i) { }
264
- toString() {
265
- return this.args.filter(arg => arg).join(" ");
266
- }
267
- }
268
-
269
- class OperationInstruction extends InstructionBase {
270
- constructor(kind, temp, left, right) {
271
- super("op", kind, temp, left, right);
272
- }
273
- }
274
-
275
- /**
276
- * The prefix for internal variables inside the compiler output
277
- */
236
+ /** The prefix for internal variables inside the compiler output */
278
237
  const internalPrefix = "&";
279
238
  const discardedName = `${internalPrefix}_`;
280
239
  const itemNames = [
@@ -303,15 +262,21 @@ const itemNames = [
303
262
  ];
304
263
  /** The name of a special processor variable, controls the instruction pointer */
305
264
  const counterName = "@counter";
265
+ const worldModuleName = "mlogjs:world";
266
+ const mathConstants = {
267
+ E: Math.E,
268
+ PI: Math.PI,
269
+ radToDeg: 180 / Math.PI,
270
+ degToRad: Math.PI / 180,
271
+ };
306
272
 
307
273
  /**
308
274
  * Returns a string that has the format: [line]:[column].
309
275
  *
310
- * If `name` is provided, and it is a string, the resulting string will
311
- * have the format: [name]:[line]:[column].
276
+ * If `name` is provided, and it is a string, the resulting string will have the
277
+ * format: [name]:[line]:[column].
312
278
  */
313
279
  function nodeName(node, name) {
314
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
315
280
  const { line, column } = node.loc.start;
316
281
  if (typeof name === "string")
317
282
  return `${name}:${line}:${column}`;
@@ -321,18 +286,16 @@ function nodeName(node, name) {
321
286
  * Converts a camel case string into a dash case one.
322
287
  *
323
288
  * As an example `camelCase` becomes `camel-case`
289
+ *
324
290
  * @example
325
- * ```
326
- * camelToDashCase("camelCase") // returns "camel-case"
327
- * ```
291
+ * ```
292
+ * camelToDashCase("camelCase") // returns "camel-case"
293
+ * ```;
328
294
  */
329
295
  function camelToDashCase(name) {
330
296
  return name.replace(/[A-Z]/g, str => `-${str.toLowerCase()}`);
331
297
  }
332
- /**
333
- * A more type safe version of `Object.assign`
334
- */
335
- // eslint-disable-next-line @typescript-eslint/ban-types
298
+ /** A more type safe version of `Object.assign` */
336
299
  function assign(obj, props) {
337
300
  return Object.assign(obj, props);
338
301
  }
@@ -344,7 +307,6 @@ function lazy(fn) {
344
307
 
345
308
  function appendSourceLocations(valueInst, node) {
346
309
  for (const inst of valueInst[1]) {
347
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
348
310
  inst.source ??= node.loc;
349
311
  }
350
312
  return valueInst;
@@ -377,7 +339,10 @@ function hideRedundantJumps(inst) {
377
339
  }
378
340
  }
379
341
  }
380
- /** A helper that appends the instructions from `value` into `inst` and returns it's `IValue` instance */
342
+ /**
343
+ * A helper that appends the instructions from `value` into `inst` and returns
344
+ * it's `IValue` instance
345
+ */
381
346
  function pipeInsts(value, inst) {
382
347
  inst.push(...value[1]);
383
348
  return value[0];
@@ -391,6 +356,11 @@ function usesAddressResolver(resolver, instructions) {
391
356
  }
392
357
  return false;
393
358
  }
359
+ function formatInstructionArgs(args) {
360
+ return args
361
+ .filter((value) => !!value)
362
+ .map(value => (typeof value === "string" ? value : value.toMlogString()));
363
+ }
394
364
 
395
365
  function isTemplateObjectArray(value) {
396
366
  return (value instanceof ObjectValue &&
@@ -417,7 +387,10 @@ function assertStringLiteral(value, name) {
417
387
  if (!(value instanceof LiteralValue) || !value.isString())
418
388
  throw new CompilerError(`${name} must be a string literal`);
419
389
  }
420
- /** Asserts that `value` is a string literal that is part of a given set of `members`*/
390
+ /**
391
+ * Asserts that `value` is a string literal that is part of a given set of
392
+ * `members`
393
+ */
421
394
  function assertLiteralOneOf(value, members, name) {
422
395
  assertStringLiteral(value, name);
423
396
  if (!members.includes(value.data))
@@ -445,7 +418,7 @@ function assertIsArrayMacro(value, name) {
445
418
  }
446
419
  throw new CompilerError(`${name} must be an array macro`);
447
420
  }
448
- /** Asserts that all of the fields described are present on `value`*/
421
+ /** Asserts that all of the fields described are present on `value` */
449
422
  function assertObjectFields(value, fields) {
450
423
  const result = [];
451
424
  for (const field of fields) {
@@ -470,6 +443,32 @@ function isDiscardedOut(out) {
470
443
  return out.name === discardedName;
471
444
  }
472
445
 
446
+ class InstructionBase {
447
+ get hidden() {
448
+ return this._hidden;
449
+ }
450
+ set hidden(value) {
451
+ this._hidden = value;
452
+ }
453
+ constructor(...args) {
454
+ this.intent = EInstIntent.none;
455
+ this.alwaysRuns = true;
456
+ this._hidden = false;
457
+ this.ignoredByParser = false;
458
+ this.args = args;
459
+ }
460
+ resolve(_i) { }
461
+ toString() {
462
+ return formatInstructionArgs(this.args).join(" ");
463
+ }
464
+ }
465
+
466
+ class OperationInstruction extends InstructionBase {
467
+ constructor(kind, temp, left, right) {
468
+ super("op", kind, temp, left, right);
469
+ }
470
+ }
471
+
473
472
  class SetInstruction extends InstructionBase {
474
473
  constructor(store, value) {
475
474
  super("set", store, value);
@@ -543,8 +542,8 @@ class JumpInstruction extends InstructionBase {
543
542
  this.address = address;
544
543
  }
545
544
  /**
546
- * Works with the `JumpOutValue` class to determine
547
- * wether a jump should be added if the expression wasn't jump compressed.
545
+ * Works with the `JumpOutValue` class to determine whether a jump should be
546
+ * added if the expression wasn't jump compressed.
548
547
  */
549
548
  static or(test, out) {
550
549
  if (test === out)
@@ -561,12 +560,6 @@ class SetCounterInstruction extends InstructionBase {
561
560
  }
562
561
  }
563
562
 
564
- class ZeroSpaceInstruction extends InstructionBase {
565
- toString() {
566
- return this.args.join("");
567
- }
568
- }
569
-
570
563
  class BreakInstruction extends JumpInstruction {
571
564
  constructor(address) {
572
565
  super(address, EJumpKind.Always);
@@ -624,7 +617,6 @@ class JumpOutValue extends VoidValue {
624
617
  kind = invertedOperatorMap[operator];
625
618
  }
626
619
  const jump = new JumpInstruction(this.address, kind, left, right);
627
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
628
620
  jump.source = this.node.loc;
629
621
  return jump;
630
622
  }
@@ -634,7 +626,7 @@ class JumpOutValue extends VoidValue {
634
626
  debugString() {
635
627
  return "JumpOutValue";
636
628
  }
637
- toString() {
629
+ toMlogString() {
638
630
  return '"[macro JumpOutValue]"';
639
631
  }
640
632
  }
@@ -672,10 +664,10 @@ class BaseValue extends VoidValue {
672
664
  }
673
665
  // requires special handling
674
666
  // the handler should give an object value to allow the lazy evaluation
675
- "??"(scope, other, out) {
676
- const result = SenseableValue.from(scope, out, EMutability.mutable);
667
+ "??"(scope, other, out, endAddress) {
668
+ const result = StoreValue.from(scope, out);
677
669
  const nullLiteral = new LiteralValue(null);
678
- const endAdress = new LiteralValue(null);
670
+ const targetAddress = endAddress ?? new LiteralValue(null);
679
671
  const [left, leftInst] = this.eval(scope, result);
680
672
  const [right, rightInst] = other.eval(scope, result);
681
673
  const [test, testInst] = left["==="](scope, nullLiteral);
@@ -685,10 +677,10 @@ class BaseValue extends VoidValue {
685
677
  ...leftInst,
686
678
  ...result["="](scope, left)[1],
687
679
  ...testInst,
688
- new JumpInstruction(endAdress, EJumpKind.Equal, test, new LiteralValue(0)),
680
+ new JumpInstruction(targetAddress, EJumpKind.Equal, test, new LiteralValue(0)),
689
681
  ...rightInst,
690
682
  ...result["="](scope, right)[1],
691
- new AddressResolver(endAdress),
683
+ ...(endAddress ? [] : [new AddressResolver(targetAddress)]),
692
684
  ],
693
685
  ];
694
686
  }
@@ -702,8 +694,45 @@ class BaseValue extends VoidValue {
702
694
  const [result, resultInst] = equal["!"](scope, out);
703
695
  return [result, [...equalInst, ...resultInst]];
704
696
  }
697
+ "&&"(scope, value, out, endAddress) {
698
+ const temp = StoreValue.from(scope, out);
699
+ const [left, leftInst] = this.eval(scope, temp);
700
+ const [right, rightInst] = value.eval(scope, temp);
701
+ const targetAddress = endAddress ?? new LiteralValue(null);
702
+ return [
703
+ temp,
704
+ [
705
+ ...leftInst,
706
+ ...temp["="](scope, left)[1],
707
+ new JumpInstruction(targetAddress, EJumpKind.Equal, left, new LiteralValue(0)),
708
+ ...rightInst,
709
+ ...temp["="](scope, right)[1],
710
+ ...(endAddress ? [] : [new AddressResolver(targetAddress)]),
711
+ ],
712
+ ];
713
+ }
714
+ "||"(scope, value, out, endAddress) {
715
+ const temp = StoreValue.from(scope, out);
716
+ const [left, leftInst] = this.eval(scope, temp);
717
+ const [right, rightInst] = value.eval(scope, temp);
718
+ const targetAddress = endAddress ?? new LiteralValue(null);
719
+ return [
720
+ temp,
721
+ [
722
+ ...leftInst,
723
+ ...temp["="](scope, left)[1],
724
+ new JumpInstruction(targetAddress, EJumpKind.NotEqual, left, new LiteralValue(0)),
725
+ ...rightInst,
726
+ ...temp["="](scope, right)[1],
727
+ ...(endAddress ? [] : [new AddressResolver(targetAddress)]),
728
+ ],
729
+ ];
730
+ }
705
731
  }
706
- for (const key in operatorMap$2) {
732
+ for (const k in operatorMap$2) {
733
+ const key = k;
734
+ if (key === "&&" || key == "||")
735
+ continue;
707
736
  const kind = operatorMap$2[key];
708
737
  BaseValue.prototype[key] = function (scope, value, out) {
709
738
  const [left, leftInst] = this.eval(scope);
@@ -774,19 +803,38 @@ class LiteralValue extends BaseValue {
774
803
  super();
775
804
  this.mutability = EMutability.constant;
776
805
  this.data = data;
777
- this.name = this.toString();
806
+ this.name = JSON.stringify(this.data);
778
807
  }
779
808
  eval(_scope) {
780
809
  return [this, []];
781
810
  }
782
- toString() {
783
- return JSON.stringify(this.data);
811
+ toMlogString() {
812
+ const { data } = this;
813
+ // math constants are literal values
814
+ // so that the compiler can optimize them
815
+ // in operations
816
+ // this helps to print them in their global variable form
817
+ // instead of their literal value
818
+ switch (data) {
819
+ case mathConstants.E:
820
+ return "@e";
821
+ case mathConstants.PI:
822
+ return "@pi";
823
+ case mathConstants.degToRad:
824
+ return "@degToRad";
825
+ case mathConstants.radToDeg:
826
+ return "@radToDeg";
827
+ }
828
+ if (typeof data !== "string")
829
+ return JSON.stringify(data);
830
+ return formatMlogString(data);
784
831
  }
785
832
  get(scope, name) {
786
833
  if (!(name instanceof LiteralValue && name.isString()))
787
834
  return super.get(scope, name);
788
835
  const method = literalMethods[name.data];
789
- if (!method)
836
+ if (!method ||
837
+ !Object.prototype.hasOwnProperty.call(literalMethods, name.data))
790
838
  throw new CompilerError(`The member [${name.debugString()}] does not exist on literal values.`);
791
839
  return [method.apply(this, [scope]), []];
792
840
  }
@@ -807,9 +855,6 @@ class LiteralValue extends BaseValue {
807
855
  return other.eval(scope, out);
808
856
  return [this, []];
809
857
  }
810
- typeof() {
811
- return [new LiteralValue("literal"), []];
812
- }
813
858
  isString() {
814
859
  return typeof this.data === "string";
815
860
  }
@@ -817,7 +862,7 @@ class LiteralValue extends BaseValue {
817
862
  return typeof this.data === "number";
818
863
  }
819
864
  debugString() {
820
- return this.toString();
865
+ return this.toMlogString();
821
866
  }
822
867
  }
823
868
  const operatorMap = {
@@ -835,12 +880,13 @@ const operatorMap = {
835
880
  "/": (a, b) => a / b,
836
881
  "%": (a, b) => a % b,
837
882
  "**": (a, b) => a ** b,
838
- "|": (a, b) => a | b,
839
- "&": (a, b) => a & b,
840
- "^": (a, b) => a ^ b,
841
- ">>": (a, b) => a >> b,
842
- ">>>": (a, b) => a >> b,
843
- "<<": (a, b) => a << b,
883
+ "|": bitwiseOp((a, b) => a | b),
884
+ "&": bitwiseOp((a, b) => a & b),
885
+ "^": bitwiseOp((a, b) => a ^ b),
886
+ // bit shifting only takes the lower 6 bits of the right operand
887
+ ">>": bitwiseOp((a, b) => a >> (b & 63n)),
888
+ "<<": bitwiseOp((a, b) => a << (b & 63n)),
889
+ ">>>": bitwiseOp((a, b) => BigInt.asUintN(64, a) >> (b & 63n)),
844
890
  "&&": (a, b) => +(a && b),
845
891
  "||": (a, b) => +(a || b),
846
892
  };
@@ -861,47 +907,99 @@ for (const k in operatorMap) {
861
907
  if (!(value instanceof LiteralValue)) {
862
908
  return BaseValue.prototype[key].apply(this, [scope, value, out]);
863
909
  }
864
- return [new LiteralValue(fn(this.data, value.data)), []];
910
+ // patch constant string concatenation
911
+ // TODO: remove this in favor of the `concat` function
912
+ if (key === "+" && this.isString()) {
913
+ return [new LiteralValue(this.data + value.data), []];
914
+ }
915
+ return [new LiteralValue(fn(this.num, value.num)), []];
865
916
  };
866
917
  }
867
918
  const unaryOperatorMap = {
868
919
  "!": v => +!v,
869
- "~": v => ~v,
920
+ "~": bitwiseOp(v => ~v),
870
921
  "u-": v => -v,
871
922
  "u+": v => +v,
872
923
  };
873
924
  for (const key in unaryOperatorMap) {
874
925
  LiteralValue.prototype[key] = function () {
875
926
  const fn = unaryOperatorMap[key];
876
- return [new LiteralValue(fn(this.data)), []];
927
+ return [new LiteralValue(fn(this.num)), []];
928
+ };
929
+ }
930
+ /**
931
+ * Performs bitwise operations on 64-bit integers to ensure that the operations
932
+ * evaluated at compile time produce the same results as the mlog runtime.
933
+ *
934
+ * This is necessary because javascript converts its 64-bit floats into 32-bit
935
+ * integers to perform bitwise operations, however mlog casts 64-bit floats into
936
+ * 64-bit integers to achieve the same goal. This means that using javascript
937
+ * numbers to evaluate these operations at compile time can cause disparity
938
+ * between the compiler and the runtime for values bigger than `2^31-1`.
939
+ */
940
+ function bitwiseOp(fn) {
941
+ return (...args) => {
942
+ const bigResult = fn(...args.map(BigInt));
943
+ // limit the result to 64 bits of precision (signed long)
944
+ // and convert it back into a number
945
+ return Number(BigInt.asIntN(64, bigResult));
877
946
  };
878
947
  }
948
+ function formatMlogString(str) {
949
+ let result = '"';
950
+ for (let i = 0; i < str.length; i++) {
951
+ const char = str[i];
952
+ const code = str.charCodeAt(i);
953
+ if (char === '"') {
954
+ result += '\\"';
955
+ }
956
+ else if (char === "\\") {
957
+ result += "\\\\";
958
+ }
959
+ else if (char === "\n") {
960
+ result += "\\n";
961
+ }
962
+ else if (code < 0x20 || code >= 0x7f) {
963
+ result += `\\u${code.toString(16).padStart(4, "0")}`;
964
+ }
965
+ else {
966
+ result += char;
967
+ }
968
+ }
969
+ return result + '"';
970
+ }
879
971
 
880
972
  /**
881
- * `StoreValue` represents values unknown at compile time,
882
- * mostly used with mutable variables and temporary values
973
+ * `StoreValue` represents values unknown at compile time, mostly used with
974
+ * mutable variables and temporary values.
975
+ *
976
+ * Stores are mutable by default.
883
977
  */
884
978
  class StoreValue extends BaseValue {
885
- constructor(name, mutability = EMutability.mutable) {
979
+ constructor(name, mutability = EMutability.mutable, { temporary = false, volatile = false, } = {}) {
886
980
  super();
887
981
  this.name = name;
888
982
  this.mutability = mutability;
983
+ this.temporary = temporary;
984
+ this.volatile = volatile;
889
985
  }
890
986
  static from(scope, out, mutability = EMutability.mutable) {
891
987
  if (out instanceof StoreValue)
892
988
  return out;
893
- const name = typeof out === "string" ? out : scope.makeTempName();
894
- return new StoreValue(name, mutability);
989
+ const hasName = typeof out === "string";
990
+ const name = hasName ? out : scope.makeTempName();
991
+ return new StoreValue(name, mutability, {
992
+ temporary: !hasName,
993
+ });
895
994
  }
896
995
  static out(scope, out, mutability = EMutability.mutable) {
897
996
  if (!out || typeof out === "string") {
898
- return new StoreValue(out ?? scope.makeTempName(), mutability);
997
+ return new StoreValue(out ?? scope.makeTempName(), mutability, {
998
+ temporary: !out,
999
+ });
899
1000
  }
900
1001
  return out;
901
1002
  }
902
- typeof() {
903
- return [new LiteralValue("store"), []];
904
- }
905
1003
  "="(scope, value) {
906
1004
  if (this.mutability !== EMutability.mutable &&
907
1005
  this.mutability !== EMutability.init)
@@ -936,16 +1034,67 @@ class StoreValue extends BaseValue {
936
1034
  eval(_scope) {
937
1035
  return [this, []];
938
1036
  }
1037
+ get(scope, prop, out) {
1038
+ const mutability = EMutability.readonly;
1039
+ const thisCoordName = getThisCoordName(this, prop);
1040
+ if (thisCoordName)
1041
+ return [
1042
+ new StoreValue(`@this${thisCoordName}`, EMutability.constant),
1043
+ [],
1044
+ ];
1045
+ if (prop instanceof LiteralValue && prop.isString()) {
1046
+ const result = StoreValue.from(scope, out, mutability);
1047
+ // handle string length property
1048
+ const propName = prop.data === "length" ? "size" : prop.data;
1049
+ return [
1050
+ result,
1051
+ [
1052
+ new InstructionBase("sensor", result, this, formatSenseablePropName(propName)),
1053
+ ],
1054
+ ];
1055
+ }
1056
+ if (prop instanceof StoreValue) {
1057
+ const temp = StoreValue.from(scope, out, mutability);
1058
+ return [temp, [new InstructionBase("sensor", temp, this, prop)]];
1059
+ }
1060
+ throw new CompilerError(`The property [${prop.debugString()}] cannot be sensed`);
1061
+ }
1062
+ hasProperty(scope, prop) {
1063
+ return ((prop instanceof LiteralValue && prop.isString()) ||
1064
+ prop instanceof StoreValue);
1065
+ }
939
1066
  debugString() {
940
1067
  return `StoreValue("${this.name}")`;
941
1068
  }
942
- toString() {
1069
+ toMlogString() {
943
1070
  return this.name;
944
1071
  }
945
1072
  }
1073
+ function formatSenseablePropName(name) {
1074
+ if (itemNames.includes(name))
1075
+ return "@" + camelToDashCase(name);
1076
+ return "@" + name;
1077
+ }
946
1078
  function compareStores(left, right) {
947
1079
  return right instanceof StoreValue && right.name === left.name;
948
1080
  }
1081
+ /**
1082
+ * If `prop` is sensing on of the coordinates of `@this`, returns the coordinate
1083
+ * name.
1084
+ */
1085
+ function getThisCoordName(value, prop) {
1086
+ if (value.name !== "@this")
1087
+ return;
1088
+ let name;
1089
+ if (prop instanceof LiteralValue && prop.isString()) {
1090
+ name = prop.data;
1091
+ }
1092
+ else if (prop instanceof StoreValue && prop.name.startsWith("@")) {
1093
+ name = prop.name.slice(1);
1094
+ }
1095
+ if (name === "x" || name === "y")
1096
+ return name;
1097
+ }
949
1098
 
950
1099
  class ObjectValue extends VoidValue {
951
1100
  constructor(data = {}) {
@@ -965,25 +1114,18 @@ class ObjectValue extends VoidValue {
965
1114
  });
966
1115
  return new ObjectValue(data);
967
1116
  }
968
- typeof(scope, out) {
969
- const { $typeof } = this.data;
970
- if ($typeof)
971
- return $typeof.call(scope, [], out);
972
- return [new LiteralValue("object"), []];
973
- }
974
1117
  get(scope, key, out) {
975
1118
  if (key instanceof LiteralValue && (key.isNumber() || key.isString())) {
976
1119
  // avoids naming collisions with keys like
977
1120
  // constructor or toString
978
1121
  if (Object.prototype.hasOwnProperty.call(this.data, key.data)) {
979
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
980
- return [this.data[key.data], []];
1122
+ const member = this.data[key.data];
1123
+ if (out)
1124
+ return member.eval(scope, out);
1125
+ return [member, []];
981
1126
  }
982
1127
  }
983
- const { $get } = this.data;
984
- if (!$get)
985
- throw new CompilerError(`The member [${key.debugString()}] is not present in [${this.debugString()}]`);
986
- return $get.call(scope, [key], out);
1128
+ throw new CompilerError(`The member [${key.debugString()}] is not present in [${this.debugString()}]`);
987
1129
  }
988
1130
  hasProperty(scope, prop) {
989
1131
  if (prop instanceof LiteralValue && (prop.isNumber() || prop.isString())) {
@@ -992,22 +1134,10 @@ class ObjectValue extends VoidValue {
992
1134
  }
993
1135
  return false;
994
1136
  }
995
- eval(scope, out) {
996
- const { $eval } = this.data;
997
- if (!$eval)
998
- return [this, []];
999
- return $eval.call(scope, [], out);
1000
- }
1001
- call(scope, args, out) {
1002
- const { $call } = this.data;
1003
- if (!$call)
1004
- return super.call(scope, args, out);
1005
- return $call.call(scope, args, out);
1137
+ eval() {
1138
+ return [this, []];
1006
1139
  }
1007
- "??"(scope, other, out) {
1008
- const $ = this.data["$??"];
1009
- if ($)
1010
- return $.call(scope, [other], out);
1140
+ "??"() {
1011
1141
  return [this, []];
1012
1142
  }
1013
1143
  debugString() {
@@ -1015,108 +1145,17 @@ class ObjectValue extends VoidValue {
1015
1145
  return `ObjectValue("${this.name}")`;
1016
1146
  return "ObjectValue";
1017
1147
  }
1018
- toString() {
1148
+ toMlogString() {
1019
1149
  return '"[macro ObjectValue]"';
1020
1150
  }
1021
1151
  }
1022
- for (const op of leftRightOperators) {
1023
- if (op === "??")
1024
- continue;
1025
- ObjectValue.prototype[op] = function (scope, value, out) {
1026
- const $ = this.data[`$${op}`];
1027
- if (!$)
1028
- return VoidValue.prototype[op].apply(this, [scope, value, out]);
1029
- return $.call(scope, [value], out);
1030
- };
1031
- }
1032
- for (const op of unaryOperators) {
1033
- if (op === "typeof")
1034
- continue;
1035
- ObjectValue.prototype[op] = function (scope, out) {
1036
- const $ = this.data[`$${op}`];
1037
- if (!$)
1038
- return VoidValue.prototype[op].apply(this, [scope, out]);
1039
- return $.call(scope, [], out);
1040
- };
1041
- }
1042
- for (const op of updateOperators) {
1043
- ObjectValue.prototype[op] = function (scope, prefix, out) {
1044
- const $ = this.data[`$${op}`];
1045
- if (!$)
1046
- return VoidValue.prototype[op].apply(this, [scope, prefix, out]);
1047
- return $.call(scope, [new LiteralValue(+prefix)], out);
1048
- };
1049
- }
1050
-
1051
- /**
1052
- * Properties that should return a new `SenseableValue`
1053
- */
1054
- const senseableProps = ["controller"];
1055
- /**
1056
- * Senseable values are "objects" in the mlog runtime that
1057
- * have special properties that can be accessed
1058
- * via the `sense` instruction.
1059
- *
1060
- * This class extends `StoreValue` to allow dynamic behavior
1061
- * that is not possible with object macros.
1062
- *
1063
- * Note that these values are constant by default, so make sure
1064
- * to set that field to `EMutability.mutable` if you want them to be assignable. Example:
1065
- * ```
1066
- * const value = new SenseableValue(name, EMutability.mutable);
1067
- * ```
1068
- */
1069
- class SenseableValue extends StoreValue {
1070
- constructor(name, mutability = EMutability.constant) {
1071
- super(name, mutability);
1072
- }
1073
- static from(scope, out, mutability = EMutability.constant) {
1074
- if (out instanceof SenseableValue)
1075
- return out;
1076
- const name = typeof out === "string" ? out : scope.makeTempName();
1077
- return new SenseableValue(name, mutability);
1078
- }
1079
- static out(scope, out, mutability = EMutability.mutable) {
1080
- if (!out || typeof out === "string") {
1081
- return new SenseableValue(out ?? scope.makeTempName(), mutability);
1082
- }
1083
- return out;
1084
- }
1085
- get(scope, prop, out) {
1086
- const mutability = EMutability.readonly;
1087
- if (prop instanceof LiteralValue && prop.isString()) {
1088
- const name = itemNames.includes(prop.data)
1089
- ? camelToDashCase(prop.data)
1090
- : prop.data;
1091
- const result = senseableProps.includes(prop.data)
1092
- ? SenseableValue.from(scope, out, mutability)
1093
- : StoreValue.from(scope, out, mutability);
1094
- return [
1095
- result,
1096
- [new InstructionBase("sensor", result, this, `@${name}`)],
1097
- ];
1098
- }
1099
- if (prop instanceof StoreValue) {
1100
- const temp = SenseableValue.from(scope, out, mutability);
1101
- return [temp, [new InstructionBase("sensor", temp, this, prop)]];
1102
- }
1103
- throw new CompilerError(`The property [${prop.debugString()}] cannot be sensed`);
1104
- }
1105
- debugString() {
1106
- return `SenseableValue("${this.name}")`;
1107
- }
1108
- hasProperty(scope, prop) {
1109
- return ((prop instanceof LiteralValue && prop.isString()) ||
1110
- prop instanceof StoreValue);
1111
- }
1112
- }
1113
1152
 
1114
1153
  /**
1115
1154
  * Specific case class, required on the ArrayPattern and ObjectPattern handlers
1116
1155
  * in order to keep the separation of concerns and make the code more modular.
1117
1156
  *
1118
- * When it is assign a value, it will get it's own properties and recursively assign
1119
- * each of them to their counterparts on the right hand value.
1157
+ * When it is assign a value, it will get it's own properties and recursively
1158
+ * assign each of them to their counterparts on the right hand value.
1120
1159
  */
1121
1160
  class DestructuringValue extends VoidValue {
1122
1161
  constructor(members) {
@@ -1143,14 +1182,14 @@ class DestructuringValue extends VoidValue {
1143
1182
  debugString() {
1144
1183
  return "DestructuringValue";
1145
1184
  }
1146
- toString() {
1185
+ toMlogString() {
1147
1186
  return '"[macro DestructuringValue]"';
1148
1187
  }
1149
1188
  }
1150
1189
 
1151
1190
  /**
1152
- * This class is used to handle some cases where the value
1153
- * can't be directly evaluated by the node handler
1191
+ * This class is used to handle some cases where the value can't be directly
1192
+ * evaluated by the node handler
1154
1193
  */
1155
1194
  class LazyValue extends VoidValue {
1156
1195
  constructor(evalutate) {
@@ -1160,7 +1199,7 @@ class LazyValue extends VoidValue {
1160
1199
  debugString() {
1161
1200
  return "LazyValue";
1162
1201
  }
1163
- toString() {
1202
+ toMlogString() {
1164
1203
  return '"[macro LazyValue]"';
1165
1204
  }
1166
1205
  }
@@ -1184,26 +1223,31 @@ class AssignmentValue extends VoidValue {
1184
1223
  debugString() {
1185
1224
  return "AssignmentValue";
1186
1225
  }
1187
- toString() {
1226
+ toMlogString() {
1188
1227
  return '"[macro AssignmentValue]"';
1189
1228
  }
1190
1229
  }
1191
1230
 
1192
1231
  class Scope {
1193
- constructor(values, parent = null, stacked = false, ntemp = 0, name = "", inst = [], operationCache = {}, cacheDependencies = {}) {
1232
+ constructor({ data = {}, parent = null, ntemp = 0, name = "", inst = [], operationCache = {}, cacheDependencies = {}, builtInModules = {}, } = {}) {
1233
+ // Only `unchecked` is supposed to change this
1234
+ this.checkIndexes = true;
1235
+ this.data = data;
1194
1236
  this.parent = parent;
1195
- this.stacked = stacked;
1196
1237
  this.ntemp = ntemp;
1197
1238
  this.name = name;
1198
1239
  this.inst = inst;
1199
1240
  this.operationCache = operationCache;
1200
1241
  this.cacheDependencies = cacheDependencies;
1201
- // Only `unchecked` is supposed to change this
1202
- this.checkIndexes = true;
1203
- this.data = values;
1242
+ this.builtInModules = builtInModules;
1204
1243
  }
1205
1244
  copy() {
1206
- const scope = new Scope({ ...this.data }, this.parent, this.stacked, this.ntemp, this.name, this.inst, { ...this.operationCache }, { ...this.cacheDependencies });
1245
+ const scope = new Scope({
1246
+ ...this,
1247
+ data: { ...this.data },
1248
+ operationCache: { ...this.operationCache },
1249
+ cacheDependencies: { ...this.cacheDependencies },
1250
+ });
1207
1251
  scope.break = this.break;
1208
1252
  scope.continue = this.continue;
1209
1253
  scope.function = this.function;
@@ -1217,8 +1261,14 @@ class Scope {
1217
1261
  scope.parent = this;
1218
1262
  return scope;
1219
1263
  }
1220
- createFunction(name, stacked = this.stacked) {
1221
- return new Scope({}, this, stacked, 0, name, this.inst);
1264
+ createFunction(name) {
1265
+ return new Scope({
1266
+ data: {},
1267
+ parent: this,
1268
+ ntemp: 0,
1269
+ name,
1270
+ inst: this.inst,
1271
+ });
1222
1272
  }
1223
1273
  has(identifier) {
1224
1274
  if (identifier in this.data)
@@ -1233,7 +1283,15 @@ class Scope {
1233
1283
  return value;
1234
1284
  if (this.parent)
1235
1285
  return this.parent.get(identifier);
1236
- throw new CompilerError(`${identifier} is not declared.`);
1286
+ let message = `${identifier} is not declared.`;
1287
+ const name = new LiteralValue(identifier);
1288
+ for (const moduleName in this.builtInModules) {
1289
+ const module = this.builtInModules[moduleName];
1290
+ if (module.hasProperty(this, name)) {
1291
+ message += ` Did you mean to use "${identifier}" exported from "${moduleName}"?`;
1292
+ }
1293
+ }
1294
+ throw new CompilerError(message);
1237
1295
  }
1238
1296
  set(name, value) {
1239
1297
  if (name in this.data)
@@ -1247,9 +1305,7 @@ class Scope {
1247
1305
  return value;
1248
1306
  }
1249
1307
  make(identifier, name) {
1250
- const value = assign(new SenseableValue(name), {
1251
- mutability: EMutability.mutable,
1252
- });
1308
+ const value = new StoreValue(name);
1253
1309
  return this.set(identifier, value);
1254
1310
  }
1255
1311
  makeTempName() {
@@ -1262,16 +1318,19 @@ class Scope {
1262
1318
  addCachedOperation(op, result, left, right) {
1263
1319
  if (!left.name || !result.name || (right && !right.name))
1264
1320
  return;
1321
+ if (result.volatile || left.volatile || right?.volatile)
1322
+ return;
1265
1323
  const id = formatCacheId(op, left, right);
1266
1324
  this.operationCache[id] = result;
1267
1325
  this.clearDependentCache(result);
1268
1326
  addCacheDependency(this, left.name, id);
1269
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1270
1327
  if (right)
1271
1328
  addCacheDependency(this, right.name, id);
1272
1329
  addCacheDependency(this, result.name, id);
1273
1330
  }
1274
1331
  getCachedOperation(op, left, right) {
1332
+ if (left.volatile || right?.volatile)
1333
+ return;
1275
1334
  const id = formatCacheId(op, left, right);
1276
1335
  const result = this.operationCache[id];
1277
1336
  if (result)
@@ -1310,6 +1369,9 @@ const VariableDeclaration = (c, scope, node) => {
1310
1369
  };
1311
1370
  const VariableDeclarator = (c, scope, node, out, kind = "let") => {
1312
1371
  const { init } = node;
1372
+ if (es.isVoidPattern(node.id)) {
1373
+ throw new CompilerError(`Cannot declare a variable with a void pattern`, node.id);
1374
+ }
1313
1375
  const [value, inst] = Declare(c, scope, node.id, kind);
1314
1376
  if (init) {
1315
1377
  const [result, resultInst] = c.handleEval(scope, init, value.out);
@@ -1339,7 +1401,7 @@ const Declare = (c, scope, node, kind) => {
1339
1401
  const DeclareIdentifier = (c, scope, node, kind) => {
1340
1402
  const { name: identifier } = node;
1341
1403
  const name = nodeName(node, !c.compactNames && identifier);
1342
- const out = new SenseableValue(name, kind === "const" ? EMutability.init : EMutability.mutable);
1404
+ const out = new StoreValue(name, kind === "const" ? EMutability.init : EMutability.mutable);
1343
1405
  const declarationValue = new DeclarationValue({
1344
1406
  c,
1345
1407
  scope,
@@ -1348,7 +1410,9 @@ const DeclareIdentifier = (c, scope, node, kind) => {
1348
1410
  handler(init, inst) {
1349
1411
  if (kind === "const" && !init)
1350
1412
  throw new CompilerError("Constants must be initialized.", node);
1351
- if (kind === "const" && init?.mutability === EMutability.constant) {
1413
+ if (kind === "const" &&
1414
+ (init?.mutability === EMutability.constant ||
1415
+ init?.mutability === EMutability.immutable)) {
1352
1416
  scope.set(identifier, init);
1353
1417
  return [null, inst];
1354
1418
  }
@@ -1360,7 +1424,7 @@ const DeclareIdentifier = (c, scope, node, kind) => {
1360
1424
  pipeInsts(value["="](scope, init), inst);
1361
1425
  }
1362
1426
  if (kind === "const")
1363
- value.mutability = EMutability.constant;
1427
+ value.mutability = EMutability.immutable;
1364
1428
  return [null, inst];
1365
1429
  }
1366
1430
  },
@@ -1474,7 +1538,7 @@ class DeclarationValue extends VoidValue {
1474
1538
  debugString() {
1475
1539
  return "DeclarationValue";
1476
1540
  }
1477
- toString() {
1541
+ toMlogString() {
1478
1542
  return '"[macro DeclarationValue]"';
1479
1543
  }
1480
1544
  }
@@ -1508,23 +1572,92 @@ const LRExpression = (c, scope, node, out) => {
1508
1572
  return [op, [...leftInst, ...rightInst, ...opInst]];
1509
1573
  };
1510
1574
  const BinaryExpression = LRExpression;
1511
- const LogicalExpression = (c, scope, node, out) => {
1512
- if (node.operator !== "??")
1513
- return LRExpression(c, scope, node, out);
1514
- const other = new LazyValue((scope, out) => c.handleEval(scope, node.right, out));
1515
- const [left, leftInst] = c.handleValue(scope, node.left);
1516
- const [result, resultInst] = left["??"](scope, other, out);
1517
- return [result, [...leftInst, ...resultInst]];
1575
+ const LogicalExpression = (c, scope, node, out, arg) => {
1576
+ // TODO: these expressions can be optimized
1577
+ // to skip some jump checks inside if statements.
1578
+ // Remove this comment if this optimization is
1579
+ // really not necessary
1580
+ // patch to prevent the jump out value
1581
+ // from being double-used later in the handler
1582
+ if (out instanceof JumpOutValue) {
1583
+ out = StoreValue.from(scope);
1584
+ }
1585
+ let leftNodeArg;
1586
+ let rightNodeArg;
1587
+ let operatorAddress;
1588
+ let afterRightAddress;
1589
+ let beforeRightAddress;
1590
+ switch (node.operator) {
1591
+ case "||": {
1592
+ const { onFalsyAddress = new LiteralValue(null), onTruthyAddress = new LiteralValue(null), } = arg ?? {};
1593
+ leftNodeArg = {
1594
+ onTruthyAddress,
1595
+ onFalsyAddress,
1596
+ };
1597
+ rightNodeArg = { onTruthyAddress };
1598
+ operatorAddress = onTruthyAddress;
1599
+ if (!arg?.onFalsyAddress)
1600
+ beforeRightAddress = onFalsyAddress;
1601
+ if (!arg?.onTruthyAddress)
1602
+ afterRightAddress = onTruthyAddress;
1603
+ break;
1604
+ }
1605
+ case "&&": {
1606
+ const { onFalsyAddress = new LiteralValue(null), onTruthyAddress = new LiteralValue(null), } = arg ?? {};
1607
+ leftNodeArg = { onTruthyAddress, onFalsyAddress };
1608
+ rightNodeArg = { onFalsyAddress };
1609
+ operatorAddress = onFalsyAddress;
1610
+ if (!arg?.onTruthyAddress)
1611
+ beforeRightAddress = onTruthyAddress;
1612
+ if (!arg?.onFalsyAddress)
1613
+ afterRightAddress = onFalsyAddress;
1614
+ break;
1615
+ }
1616
+ case "??": {
1617
+ const { onNonNullishAddress = new LiteralValue(null) } = arg ?? {};
1618
+ leftNodeArg = {
1619
+ onNonNullishAddress,
1620
+ onTruthyAddress: onNonNullishAddress,
1621
+ };
1622
+ rightNodeArg = {
1623
+ onNonNullishAddress,
1624
+ onTruthyAddress: onNonNullishAddress,
1625
+ };
1626
+ operatorAddress = onNonNullishAddress;
1627
+ if (!arg?.onNonNullishAddress)
1628
+ afterRightAddress = onNonNullishAddress;
1629
+ break;
1630
+ }
1631
+ }
1632
+ const other = new LazyValue((scope, out) => {
1633
+ const [value, inst] = c.handleEval(scope, node.right, out, node.right.type === "LogicalExpression" ? rightNodeArg : undefined);
1634
+ if (!beforeRightAddress)
1635
+ return [value, inst];
1636
+ return [value, [new AddressResolver(beforeRightAddress), ...inst]];
1637
+ });
1638
+ const [left, leftInst] = c.handleEval(scope, node.left, out, node.left.type === "LogicalExpression" ? leftNodeArg : undefined);
1639
+ // patch for situations where `left` is a temporary value and `out` is `undefined`
1640
+ // since `out` is undefined there is potential for up to three temp values
1641
+ // to be created. So to prevent that we will try to reuse `left` as an out value
1642
+ // whenever possible
1643
+ const resultOut = reuseTemporaryValue(left, out);
1644
+ const [result, resultInst] = left[node.operator](scope, other, resultOut, operatorAddress);
1645
+ return [
1646
+ result,
1647
+ [
1648
+ ...leftInst,
1649
+ ...resultInst,
1650
+ ...(afterRightAddress ? [new AddressResolver(afterRightAddress)] : []),
1651
+ ],
1652
+ ];
1518
1653
  };
1654
+ const logicalAssignmentOperators = ["??=", "||=", "&&="];
1519
1655
  const AssignmentExpression = (c, scope, node) => {
1520
1656
  const [left, leftInst] = c.handleValue(scope, node.left);
1521
1657
  const leftOutput = left.toOut();
1522
- const [right, rightInst] = node.operator !== "??="
1658
+ const [right, rightInst] = !logicalAssignmentOperators.includes(node.operator)
1523
1659
  ? c.handleEval(scope, node.right, node.operator === "=" ? leftOutput : undefined)
1524
- : [
1525
- new LazyValue((scope, out) => c.handleEval(scope, node.right, out)),
1526
- [],
1527
- ];
1660
+ : [new LazyValue((scope, out) => c.handleEval(scope, node.right, out)), []];
1528
1661
  const [op, opInst] = left[node.operator](scope, right);
1529
1662
  scope.clearDependentCache(left);
1530
1663
  scope.clearDependentCache(leftOutput);
@@ -1548,7 +1681,60 @@ const UpdateExpression = (c, scope, { argument, operator, prefix }, out) => {
1548
1681
  const [op, opInst] = arg[operator](scope, prefix, out);
1549
1682
  return [op, [...argInst, ...opInst]];
1550
1683
  };
1684
+ const selectOperatorMap = {
1685
+ "==": "equal",
1686
+ "!=": "notEqual",
1687
+ "<": "lessThan",
1688
+ "<=": "lessThanEq",
1689
+ ">": "greaterThan",
1690
+ ">=": "greaterThanEq",
1691
+ "===": "strictEqual",
1692
+ };
1551
1693
  const ConditionalExpression = (c, scope, node, out) => {
1694
+ if (node.test.type === "BooleanLiteral" ||
1695
+ node.test.type === "NumericLiteral" ||
1696
+ node.test.type === "StringLiteral") {
1697
+ if (node.test.value)
1698
+ return c.handleEval(scope, node.consequent, out);
1699
+ return c.handleEval(scope, node.alternate, out);
1700
+ }
1701
+ const result = StoreValue.from(scope, out);
1702
+ const consequent = c.handleEval(scope, node.consequent, result);
1703
+ const alternate = c.handleEval(scope, node.alternate, result);
1704
+ if (consequent[1].length === 0 && alternate[1].length === 0) {
1705
+ if (node.test.type === "BinaryExpression" &&
1706
+ (node.test.operator in selectOperatorMap || node.test.operator === "!==")) {
1707
+ const [left, leftInst] = c.handleEval(scope, node.test.left);
1708
+ const [right, rightInst] = c.handleEval(scope, node.test.right);
1709
+ const isStrictNotEqual = node.test.operator === "!==";
1710
+ const op = isStrictNotEqual
1711
+ ? "strictEqual"
1712
+ : selectOperatorMap[node.test.operator];
1713
+ const trueVal = isStrictNotEqual ? alternate[0] : consequent[0];
1714
+ const falseVal = isStrictNotEqual ? consequent[0] : alternate[0];
1715
+ return [
1716
+ result,
1717
+ [
1718
+ ...leftInst,
1719
+ ...rightInst,
1720
+ new InstructionBase("select", result, op, left, right, trueVal, falseVal),
1721
+ ],
1722
+ ];
1723
+ }
1724
+ const [test, testInst] = c.handleEval(scope, node.test);
1725
+ if (test instanceof LiteralValue) {
1726
+ if (test.data)
1727
+ return [result, [...result["="](scope, consequent[0])[1]]];
1728
+ return [result, [...result["="](scope, alternate[0])[1]]];
1729
+ }
1730
+ return [
1731
+ result,
1732
+ [
1733
+ ...testInst,
1734
+ new InstructionBase("select", result, "notEqual", test, "0", consequent[0], alternate[0]),
1735
+ ],
1736
+ ];
1737
+ }
1552
1738
  const alternateStartAdress = new LiteralValue(null);
1553
1739
  const endExpressionAdress = new LiteralValue(null);
1554
1740
  const testOut = new JumpOutValue(node, alternateStartAdress, false);
@@ -1558,9 +1744,6 @@ const ConditionalExpression = (c, scope, node, out) => {
1558
1744
  return c.handleEval(scope, node.consequent, out);
1559
1745
  return c.handleEval(scope, node.alternate, out);
1560
1746
  }
1561
- const result = SenseableValue.from(scope, out, EMutability.mutable);
1562
- const consequent = c.handleEval(scope, node.consequent, result);
1563
- const alternate = c.handleEval(scope, node.alternate, result);
1564
1747
  return [
1565
1748
  result,
1566
1749
  [
@@ -1586,6 +1769,13 @@ const SequenceExpression = (c, scope, node, out) => {
1586
1769
  const value = pipeInsts(c.handleEval(scope, expressions[expressions.length - 1], out), inst);
1587
1770
  return [value, inst];
1588
1771
  };
1772
+ function reuseTemporaryValue(value, out) {
1773
+ if (value instanceof StoreValue && value.temporary)
1774
+ return new StoreValue(value.name, EMutability.mutable, {
1775
+ temporary: true,
1776
+ });
1777
+ return out;
1778
+ }
1589
1779
 
1590
1780
  const Identifier = (c, scope, node) => {
1591
1781
  return [scope.get(node.name), []];
@@ -1669,8 +1859,9 @@ const IfStatement = (c, scope, node) => {
1669
1859
  return [null, inst];
1670
1860
  };
1671
1861
  /**
1672
- * Returns `true` if the instruction array ends with an instruction
1673
- * that already changes the control flow of the script (like `jump x always`, `end` , `stop`, etc)
1862
+ * Returns `true` if the instruction array ends with an instruction that already
1863
+ * changes the control flow of the script (like `jump x always`, `end` , `stop`,
1864
+ * etc)
1674
1865
  */
1675
1866
  function endsWithControlFlow(inst) {
1676
1867
  for (let i = inst.length - 1; i >= 0; i--) {
@@ -1715,7 +1906,7 @@ const SwitchStatement = (c, scope, node) => {
1715
1906
  const bodyInst = lazy(() => c.handleMany(createCaseScope(innerScope), scase.consequent)[1]);
1716
1907
  const bodyAdress = new LiteralValue(null);
1717
1908
  const bodyLine = new AddressResolver(bodyAdress);
1718
- const canFallInto = !endsWithoutFalltrough(inst, endLine);
1909
+ const canFallInto = !endsWithoutFalltrough(inst);
1719
1910
  let includeJump = !constantCase;
1720
1911
  const includeBody = !constantCase || canFallInto;
1721
1912
  if (!scase.test) {
@@ -1751,7 +1942,7 @@ const SwitchStatement = (c, scope, node) => {
1751
1942
  // and all the other cases will never run
1752
1943
  if (noFallInto &&
1753
1944
  onlyConstantTests &&
1754
- (isLastCase || endsWithoutFalltrough(bodyInst(), endLine))) {
1945
+ (isLastCase || endsWithoutFalltrough(bodyInst()))) {
1755
1946
  return [null, [...bodyInst(), endLine]];
1756
1947
  }
1757
1948
  jump = c.handle(innerScope, scase, () => [
@@ -1789,7 +1980,7 @@ const SwitchStatement = (c, scope, node) => {
1789
1980
  ],
1790
1981
  ];
1791
1982
  };
1792
- function endsWithoutFalltrough(inst, endLine) {
1983
+ function endsWithoutFalltrough(inst) {
1793
1984
  if (inst.length === 0)
1794
1985
  return true;
1795
1986
  for (let i = inst.length - 1; i >= 0; i--) {
@@ -1804,10 +1995,7 @@ function endsWithoutFalltrough(inst, endLine) {
1804
1995
  return false;
1805
1996
  if (instruction.hidden)
1806
1997
  continue;
1807
- return (instruction instanceof SetCounterInstruction ||
1808
- instruction.intent === EInstIntent.return ||
1809
- (instruction instanceof BreakInstruction &&
1810
- endLine.bonds.includes(instruction.address)));
1998
+ return instruction.intent !== EInstIntent.none;
1811
1999
  }
1812
2000
  return false;
1813
2001
  }
@@ -1934,6 +2122,7 @@ const ForStatement = (c, parentScope, node) => {
1934
2122
  return [null, [...initInst]];
1935
2123
  }
1936
2124
  const isInfiniteLoop = test instanceof LiteralValue;
2125
+ const [, bodyLines] = withAlwaysRuns(c.handle(scope, node.body), false);
1937
2126
  return [
1938
2127
  null,
1939
2128
  [
@@ -1941,17 +2130,19 @@ const ForStatement = (c, parentScope, node) => {
1941
2130
  startLoopLine,
1942
2131
  ...testLines,
1943
2132
  ...(isInfiniteLoop ? [] : JumpInstruction.or(test, testOut)),
1944
- ...withAlwaysRuns(c.handle(scope, node.body), false)[1],
2133
+ ...bodyLines,
1945
2134
  beforeEndLine,
1946
2135
  ...updateLines,
1947
2136
  new JumpInstruction(startLoopAddr, EJumpKind.Always),
1948
- ...(isInfiniteLoop ? [] : [endLoopLine]),
2137
+ ...(isInfiniteLoop && !usesAddressResolver(endLoopLine, bodyLines)
2138
+ ? []
2139
+ : [endLoopLine]),
1949
2140
  ],
1950
2141
  ];
1951
2142
  };
1952
2143
 
1953
2144
  class FunctionValue extends VoidValue {
1954
- constructor({ scope, params, body, c, out, }) {
2145
+ constructor({ scope, params, body, c, out, inlineType, }) {
1955
2146
  super();
1956
2147
  this.mutability = EMutability.constant;
1957
2148
  this.macro = true;
@@ -1968,9 +2159,7 @@ class FunctionValue extends VoidValue {
1968
2159
  this.c = c;
1969
2160
  this.params = params;
1970
2161
  this.name = extractOutName(out) ?? scope.makeTempName();
1971
- }
1972
- typeof() {
1973
- return [new LiteralValue("function"), []];
2162
+ this.inlineType = inlineType;
1974
2163
  }
1975
2164
  initScope() {
1976
2165
  this.childScope = this.scope.createFunction(this.name);
@@ -1991,8 +2180,10 @@ class FunctionValue extends VoidValue {
1991
2180
  this.initialized = true;
1992
2181
  this.initScope();
1993
2182
  this.addr = new LiteralValue(null);
1994
- this.temp = new SenseableValue(`${internalPrefix}f${this.name}`, EMutability.mutable);
2183
+ this.temp = new StoreValue(`${internalPrefix}f${this.name}`);
1995
2184
  this.ret = new StoreValue(`${internalPrefix}r${this.name}`);
2185
+ if (this.inlineType === EInlineType.always)
2186
+ return;
1996
2187
  this.inst = [
1997
2188
  new AddressResolver(this.addr),
1998
2189
  ...this.c.handle(this.childScope, this.body)[1],
@@ -2026,9 +2217,7 @@ class FunctionValue extends VoidValue {
2026
2217
  const callAddressLiteral = new LiteralValue(null);
2027
2218
  // the value will be stored somewhere
2028
2219
  // no need to save it in a temporary variable
2029
- const temp = out
2030
- ? this.temp
2031
- : SenseableValue.from(scope, undefined, EMutability.mutable);
2220
+ const temp = out ? this.temp : StoreValue.from(scope);
2032
2221
  const inst = this.paramValues
2033
2222
  .map((param, i) => param["="](scope, args[i] ?? new LiteralValue(null))[1])
2034
2223
  .reduce((s, c) => s.concat(c), [])
@@ -2053,7 +2242,7 @@ class FunctionValue extends VoidValue {
2053
2242
  }
2054
2243
  inlineCall(scope, args, out) {
2055
2244
  // create return value
2056
- this.inlineTemp = SenseableValue.from(this.childScope, out, EMutability.mutable);
2245
+ this.inlineTemp = StoreValue.from(this.childScope, out);
2057
2246
  // make a copy of the function scope
2058
2247
  const fnScope = this.childScope.copy();
2059
2248
  this.tryingInline = true;
@@ -2084,20 +2273,24 @@ class FunctionValue extends VoidValue {
2084
2273
  throw new CompilerError(`Cannot call: expected ${min} arguments but got: ${args.length}`);
2085
2274
  }
2086
2275
  validateParameters(args, this.paramValues);
2087
- const inlineCall = this.inlineCall(scope, args, out);
2088
- hideRedundantJumps(inlineCall[1]);
2089
- const inlineSize = inlineCall[1].filter(i => !i.hidden).length;
2090
- if (this.inline || inlineSize <= this.callSize)
2091
- return inlineCall;
2276
+ if (this.inlineType !== EInlineType.never) {
2277
+ const inlineCall = this.inlineCall(scope, args, out);
2278
+ hideRedundantJumps(inlineCall[1]);
2279
+ if (this.inlineType === EInlineType.always)
2280
+ return inlineCall;
2281
+ const inlineSize = inlineCall[1].filter(i => !i.hidden).length;
2282
+ if (inlineSize <= this.callSize)
2283
+ return inlineCall;
2284
+ }
2092
2285
  return this.normalCall(scope, args, out);
2093
2286
  }
2094
2287
  return(scope, arg) {
2095
2288
  this.ensureInit();
2096
2289
  if (arg && arg.macro) {
2097
- this.inline = true;
2290
+ this.inlineType = EInlineType.always;
2098
2291
  return [null, []];
2099
2292
  }
2100
- if (this.inline || this.tryingInline)
2293
+ if (this.tryingInline)
2101
2294
  return this.inlineReturn(scope, arg);
2102
2295
  return this.normalReturn(scope, arg);
2103
2296
  }
@@ -2106,21 +2299,21 @@ class FunctionValue extends VoidValue {
2106
2299
  }
2107
2300
  preCall() {
2108
2301
  this.ensureInit();
2109
- if (this.inline || this.tryingInline)
2302
+ if (this.inlineType === EInlineType.always || this.tryingInline)
2110
2303
  return;
2111
2304
  return this.paramOuts;
2112
2305
  }
2113
2306
  debugString() {
2114
2307
  return `FunctionValue("${this.name}")`;
2115
2308
  }
2116
- toString() {
2309
+ toMlogString() {
2117
2310
  return '"[macro FunctionValue]"';
2118
2311
  }
2119
2312
  unwrapParameter(param) {
2120
2313
  switch (param.type) {
2121
2314
  case "Identifier": {
2122
2315
  const name = nodeName(param, !this.c.compactNames && param.name);
2123
- const value = new SenseableValue(name, EMutability.mutable);
2316
+ const value = new StoreValue(name);
2124
2317
  this.childScope.set(param.name, value);
2125
2318
  this.paramNames.set(value, param.name);
2126
2319
  return value;
@@ -2129,7 +2322,7 @@ class FunctionValue extends VoidValue {
2129
2322
  const left = this.unwrapParameter(param.left);
2130
2323
  const [rightValue, rightInst] = this.c.handleEval(this.childScope, param.right, left);
2131
2324
  const right = new LazyValue(scope => {
2132
- if (this.inline || this.tryingInline) {
2325
+ if (this.tryingInline) {
2133
2326
  return this.c.handleEval(scope, param.right, left);
2134
2327
  }
2135
2328
  return [rightValue, [...rightInst]];
@@ -2202,7 +2395,7 @@ class FunctionValue extends VoidValue {
2202
2395
  }
2203
2396
  }
2204
2397
  hardSetParameter(scope, param, value = new LiteralValue(null)) {
2205
- if (param instanceof SenseableValue) {
2398
+ if (param instanceof StoreValue) {
2206
2399
  const name = this.paramNames.get(param);
2207
2400
  scope.hardSet(name, value);
2208
2401
  return [];
@@ -2260,20 +2453,22 @@ function validateParameters(args, params) {
2260
2453
 
2261
2454
  function handleFunctionNode(c, scope, node, out) {
2262
2455
  let { params, body } = node;
2263
- if (es__namespace.isExpression(body)) {
2456
+ if (es.isExpression(body)) {
2264
2457
  const { loc } = body;
2265
- const statement = es__namespace.returnStatement(body);
2458
+ const statement = es.returnStatement(body);
2266
2459
  statement.loc = loc;
2267
- body = es__namespace.blockStatement([statement]);
2460
+ body = es.blockStatement([statement]);
2268
2461
  body.loc = loc;
2269
2462
  }
2463
+ const inlineType = getInlineType(body.directives);
2270
2464
  return [
2271
2465
  new FunctionValue({
2272
2466
  scope,
2273
- params: params,
2467
+ params,
2274
2468
  body,
2275
2469
  c,
2276
2470
  out,
2471
+ inlineType,
2277
2472
  }),
2278
2473
  [],
2279
2474
  ];
@@ -2290,6 +2485,20 @@ const FunctionDeclaration = (c, scope, node) => {
2290
2485
  };
2291
2486
  const FunctionExpression = ArrowFunctionExpression;
2292
2487
  const ObjectMethod = ArrowFunctionExpression;
2488
+ function getInlineType(directives) {
2489
+ for (const directive of directives) {
2490
+ const value = directive.value.value;
2491
+ switch (value) {
2492
+ case "inline":
2493
+ return EInlineType.always;
2494
+ case "inline never":
2495
+ return EInlineType.never;
2496
+ default:
2497
+ throw new CompilerError(`Invalid function directive: "${value}"`);
2498
+ }
2499
+ }
2500
+ return EInlineType.auto;
2501
+ }
2293
2502
 
2294
2503
  const CallExpression = (c, scope, node, out) => {
2295
2504
  const inst = [];
@@ -2342,7 +2551,20 @@ const ObjectExpression = (c, scope, node, out) => {
2342
2551
  }
2343
2552
  const memberOut = extractDestrucuringOut(out, index);
2344
2553
  const member = pipeInsts(c.handleEval(scope, value, memberOut), inst);
2345
- data[index] = member;
2554
+ if (prop.type !== "ObjectMethod" || prop.kind === "method") {
2555
+ data[index] = member;
2556
+ }
2557
+ else {
2558
+ if (!(data[index] instanceof ObjectGetSetEntry))
2559
+ data[index] = new ObjectGetSetEntry();
2560
+ const entry = data[index];
2561
+ if (prop.kind === "get") {
2562
+ entry.getter = member;
2563
+ }
2564
+ else {
2565
+ entry.setter = member;
2566
+ }
2567
+ }
2346
2568
  }
2347
2569
  return [new ObjectValue(data), inst];
2348
2570
  };
@@ -2351,7 +2573,7 @@ const ArrayExpression = (c, scope, node, out) => {
2351
2573
  const inst = [];
2352
2574
  node.elements.forEach((element, i) => {
2353
2575
  if (!element) {
2354
- items.push(undefined);
2576
+ items.push(new LiteralValue(null));
2355
2577
  return;
2356
2578
  }
2357
2579
  const value = pipeInsts(c.handleEval(scope, element, extractDestrucuringOut(out, i)), inst);
@@ -2370,7 +2592,7 @@ const MemberExpression = (c, scope, node, out, optional = false) => {
2370
2592
  const [got, gotInst] = obj.get(scope, prop);
2371
2593
  return [got, [...objInst, ...propInst, ...gotInst]];
2372
2594
  }
2373
- const temp = SenseableValue.out(scope, out);
2595
+ const temp = StoreValue.out(scope, out);
2374
2596
  const objOut = new DestructuringValue(new Map([
2375
2597
  [
2376
2598
  prop,
@@ -2455,6 +2677,24 @@ const AssignmentPattern = (c, scope, node) => {
2455
2677
  const right = new LazyValue((_, out) => c.handleEval(scope, node.right, out));
2456
2678
  return [new AssignmentValue(left, right), inst];
2457
2679
  };
2680
+ class ObjectGetSetEntry extends BaseValue {
2681
+ constructor() {
2682
+ super();
2683
+ this.macro = true;
2684
+ }
2685
+ eval(scope, out) {
2686
+ if (!this.getter)
2687
+ throw new CompilerError("This property does not have a getter");
2688
+ const [value, inst] = this.getter.call(scope, [], out);
2689
+ return [value ?? new LiteralValue(null), inst];
2690
+ }
2691
+ "="(scope, value, out) {
2692
+ if (!this.setter)
2693
+ throw new CompilerError("This property does not have a setter");
2694
+ const [, inst] = this.setter.call(scope, [value], out);
2695
+ return [value, inst];
2696
+ }
2697
+ }
2458
2698
 
2459
2699
  const TemplateLiteral = () => {
2460
2700
  throw new CompilerError("Template strings can only be used in tagged template literals. To inline mlog code use the asm function.");
@@ -2482,7 +2722,7 @@ const TSEnumDeclaration = (c, scope, node) => {
2482
2722
  let counter = 0;
2483
2723
  let lastType = "number";
2484
2724
  const data = {};
2485
- for (const member of node.members) {
2725
+ for (const member of node.body.members) {
2486
2726
  if (lastType === "string" && !member.initializer)
2487
2727
  throw new CompilerError("This enum member must be initialized", member);
2488
2728
  const [value] = member.initializer
@@ -2510,6 +2750,41 @@ const TSNonNullExpression = (c, scope, node, out) => {
2510
2750
  };
2511
2751
  const TSSatisfiesExpression = (c, scope, node, out) => c.handle(scope, node.expression, undefined, out);
2512
2752
 
2753
+ const noExternalModuleErrorMessage = "Cannot import values from modules that are not built into the compiler";
2754
+ const ImportDeclaration = (c, scope, node) => {
2755
+ if (node.importKind === "type")
2756
+ return [null, []];
2757
+ if (!(node.source.value in scope.builtInModules))
2758
+ throw new CompilerError(noExternalModuleErrorMessage);
2759
+ for (const specifier of node.specifiers) {
2760
+ c.handle(scope, specifier, undefined, undefined, node.source.value);
2761
+ }
2762
+ return [null, []];
2763
+ };
2764
+ const ImportDefaultSpecifier = (c, scope, node, out, source) => {
2765
+ if (!(source in scope.builtInModules))
2766
+ return [null, []];
2767
+ throw new CompilerError(`"${source}" does not have a default export`);
2768
+ };
2769
+ const ImportNamespaceSpecifier = (c, scope, node, out, source) => {
2770
+ scope.set(node.local.name, scope.builtInModules[source]);
2771
+ return [null, []];
2772
+ };
2773
+ const ImportSpecifier = (c, scope, node, out, source) => {
2774
+ if (node.importKind === "type")
2775
+ return [null, []];
2776
+ const module = scope.builtInModules[source];
2777
+ const { imported, local } = node;
2778
+ const importedName = imported.type === "Identifier" ? imported.name : imported.value;
2779
+ const key = new LiteralValue(importedName);
2780
+ if (!module.hasProperty(scope, key)) {
2781
+ throw new CompilerError(`The requested module '${source}' does not provide an export named '${importedName}'`);
2782
+ }
2783
+ const [value] = module.get(scope, key);
2784
+ scope.set(local.name, value);
2785
+ return [null, []];
2786
+ };
2787
+
2513
2788
  var handlers = /*#__PURE__*/Object.freeze({
2514
2789
  __proto__: null,
2515
2790
  ArrayExpression: ArrayExpression,
@@ -2533,6 +2808,10 @@ var handlers = /*#__PURE__*/Object.freeze({
2533
2808
  FunctionExpression: FunctionExpression,
2534
2809
  Identifier: Identifier,
2535
2810
  IfStatement: IfStatement,
2811
+ ImportDeclaration: ImportDeclaration,
2812
+ ImportDefaultSpecifier: ImportDefaultSpecifier,
2813
+ ImportNamespaceSpecifier: ImportNamespaceSpecifier,
2814
+ ImportSpecifier: ImportSpecifier,
2536
2815
  LRExpression: LRExpression,
2537
2816
  LabeledStatement: LabeledStatement,
2538
2817
  LogicalExpression: LogicalExpression,
@@ -2585,14 +2864,14 @@ class MacroFunction extends VoidValue {
2585
2864
  debugString() {
2586
2865
  return "MacroFunction";
2587
2866
  }
2588
- toString() {
2867
+ toMlogString() {
2589
2868
  return '"[macro MacroFunction]"';
2590
2869
  }
2591
2870
  }
2592
2871
 
2593
- /* eslint-disable @typescript-eslint/no-non-null-assertion */
2594
2872
  const toRadians = (degrees) => (degrees * Math.PI) / 180;
2595
2873
  const toDegrees = (radians) => (radians * 180) / Math.PI;
2874
+ const mod = (f, n) => ((f % n) + n) % n;
2596
2875
  const mathOperations = {
2597
2876
  max: (a, b) => Math.max(a, b),
2598
2877
  min: (a, b) => Math.min(a, b),
@@ -2600,16 +2879,30 @@ const mathOperations = {
2600
2879
  const angle = toDegrees(Math.atan2(y, x));
2601
2880
  return angle < 0 ? angle + 360 : angle;
2602
2881
  },
2882
+ angleDiff: (x, y) => {
2883
+ const a = mod(x, 360);
2884
+ const b = mod(y, 360);
2885
+ return Math.min(a - b < 0 ? a - b + 360 : a - b, b - a < 0 ? b - a + 360 : b - a);
2886
+ },
2603
2887
  len: (a, b) => Math.sqrt(a ** 2 + b ** 2),
2604
2888
  noise: null,
2605
2889
  abs: a => Math.abs(a),
2890
+ sign: a => {
2891
+ if (a > 0)
2892
+ return 1;
2893
+ if (a < 0)
2894
+ return -1;
2895
+ return 0;
2896
+ },
2606
2897
  log: a => Math.log(a),
2898
+ logn: (a, b) => Math.log(a) / Math.log(b),
2607
2899
  log10: a => Math.log10(a),
2608
2900
  sin: a => Math.sin(toRadians(a)),
2609
2901
  cos: a => Math.cos(toRadians(a)),
2610
2902
  tan: a => Math.tan(toRadians(a)),
2611
2903
  floor: a => Math.floor(a),
2612
2904
  ceil: a => Math.ceil(a),
2905
+ round: a => Math.round(a),
2613
2906
  sqrt: a => Math.sqrt(a),
2614
2907
  asin: a => toDegrees(Math.asin(a)),
2615
2908
  acos: a => toDegrees(Math.acos(a)),
@@ -2617,23 +2910,139 @@ const mathOperations = {
2617
2910
  // yes, this doesn't work with negative numbers
2618
2911
  // but the game also implements it this way.
2619
2912
  idiv: (a, b) => Math.floor(a / b),
2913
+ emod: (a, b) => mod(a, b),
2620
2914
  pow: (a, b) => Math.pow(a, b),
2621
2915
  rand: null,
2622
2916
  };
2623
2917
  const orderIndependentOperations = ["max", "min", "len"];
2624
2918
  function createMacroMathOperations() {
2625
2919
  const macroMathOperations = {
2626
- PI: new StoreValue("@pi", EMutability.constant),
2627
- E: new StoreValue("@e", EMutability.constant),
2628
- degToRad: new StoreValue("@degToRad", EMutability.constant),
2629
- radToDeg: new StoreValue("@radToDeg", EMutability.constant),
2920
+ PI: new LiteralValue(mathConstants.PI),
2921
+ E: new LiteralValue(mathConstants.E),
2922
+ degToRad: new LiteralValue(mathConstants.degToRad),
2923
+ radToDeg: new LiteralValue(mathConstants.radToDeg),
2924
+ trunc: new MacroFunction((scope, out, x) => {
2925
+ assertArgumentCount(+!!x, 1);
2926
+ // subtracts the decimal part of a number from itself
2927
+ // works with both positive and negative numbers,
2928
+ // returning the integer part of the number
2929
+ // return a - (a % 1);
2930
+ const inst = [];
2931
+ const one = new LiteralValue(1);
2932
+ const rest = pipeInsts(x["%"](scope, one), inst);
2933
+ const result = pipeInsts(x["-"](scope, rest, out), inst);
2934
+ return [result, inst];
2935
+ }),
2936
+ exp: new MacroFunction((scope, out, x) => {
2937
+ assertArgumentCount(+!!x, 1);
2938
+ // return Math.E ** x;
2939
+ const inst = [];
2940
+ const { E } = macroMathOperations;
2941
+ const result = pipeInsts(E["**"](scope, x, out), inst);
2942
+ return [result, inst];
2943
+ }),
2944
+ expm1: new MacroFunction((scope, out, x) => {
2945
+ assertArgumentCount(+!!x, 1);
2946
+ // return Math.exp(x) - 1;
2947
+ const inst = [];
2948
+ const { exp } = macroMathOperations;
2949
+ const ex = pipeInsts(exp.call(scope, [x]), inst);
2950
+ const one = new LiteralValue(1);
2951
+ const subtracted = pipeInsts(ex["-"](scope, one, out), inst);
2952
+ return [subtracted, inst];
2953
+ }),
2954
+ cosh: new MacroFunction((scope, out, degrees) => {
2955
+ assertArgumentCount(+!!degrees, 1);
2956
+ // const x = degrees * Math.degToRad;
2957
+ // return (Math.exp(x) + Math.exp(-x)) / 2;
2958
+ const { exp, degToRad } = macroMathOperations;
2959
+ const inst = [];
2960
+ const x = pipeInsts(degrees["*"](scope, degToRad), inst);
2961
+ const expx = pipeInsts(exp.call(scope, [x]), inst);
2962
+ const negativeX = pipeInsts(x["u-"](scope), inst);
2963
+ const expnegx = pipeInsts(exp.call(scope, [negativeX]), inst);
2964
+ const sum = pipeInsts(expx["+"](scope, expnegx), inst);
2965
+ const result = pipeInsts(sum["/"](scope, new LiteralValue(2), out), inst);
2966
+ return [result, inst];
2967
+ }),
2968
+ acosh: new MacroFunction((scope, out, x) => {
2969
+ assertArgumentCount(+!!x, 1);
2970
+ // return Math.log(x + Math.sqrt(x ** 2 - 1)) * Math.radToDeg;
2971
+ const { log, sqrt, radToDeg } = macroMathOperations;
2972
+ const inst = [];
2973
+ const x2 = pipeInsts(x["**"](scope, new LiteralValue(2)), inst);
2974
+ const one = new LiteralValue(1);
2975
+ const x2minus1 = pipeInsts(x2["-"](scope, one), inst);
2976
+ const sqrtx2minus1 = pipeInsts(sqrt.call(scope, [x2minus1]), inst);
2977
+ const sum = pipeInsts(x["+"](scope, sqrtx2minus1), inst);
2978
+ const radians = pipeInsts(log.call(scope, [sum], out), inst);
2979
+ const degrees = pipeInsts(radians["*"](scope, radToDeg), inst);
2980
+ return [degrees, inst];
2981
+ }),
2982
+ sinh: new MacroFunction((scope, out, degrees) => {
2983
+ assertArgumentCount(+!!degrees, 1);
2984
+ // const x = degrees * Math.degToRad;
2985
+ // return (Math.exp(x) - Math.exp(-x)) / 2;
2986
+ const { exp, degToRad } = macroMathOperations;
2987
+ const inst = [];
2988
+ const x = pipeInsts(degrees["*"](scope, degToRad), inst);
2989
+ const expx = pipeInsts(exp.call(scope, [x]), inst);
2990
+ const negativeX = pipeInsts(x["u-"](scope), inst);
2991
+ const expnegx = pipeInsts(exp.call(scope, [negativeX]), inst);
2992
+ const sub = pipeInsts(expx["-"](scope, expnegx), inst);
2993
+ const result = pipeInsts(sub["/"](scope, new LiteralValue(2)), inst);
2994
+ return [result, inst];
2995
+ }),
2996
+ asinh: new MacroFunction((scope, out, x) => {
2997
+ assertArgumentCount(+!!x, 1);
2998
+ // return Math.log(x + Math.sqrt(x ** 2 + 1)) * Math.radToDeg;
2999
+ const { log, sqrt, radToDeg } = macroMathOperations;
3000
+ const inst = [];
3001
+ const x2 = pipeInsts(x["**"](scope, new LiteralValue(2)), inst);
3002
+ const x2plus1 = pipeInsts(x2["+"](scope, new LiteralValue(1)), inst);
3003
+ const sqrtx2plus1 = pipeInsts(sqrt.call(scope, [x2plus1]), inst);
3004
+ const sum = pipeInsts(x["+"](scope, sqrtx2plus1), inst);
3005
+ const radians = pipeInsts(log.call(scope, [sum], out), inst);
3006
+ const degrees = pipeInsts(radians["*"](scope, radToDeg), inst);
3007
+ return [degrees, inst];
3008
+ }),
3009
+ tanh: new MacroFunction((scope, out, degrees) => {
3010
+ assertArgumentCount(+!!degrees, 1);
3011
+ // const x = degrees * Math.degToRad;
3012
+ // return (Math.exp(x) - Math.exp(-x)) / (Math.exp(x) + Math.exp(-x));
3013
+ const { exp, degToRad } = macroMathOperations;
3014
+ const inst = [];
3015
+ const x = pipeInsts(degrees["*"](scope, degToRad), inst);
3016
+ const expx = pipeInsts(exp.call(scope, [x]), inst);
3017
+ const negativeX = pipeInsts(x["u-"](scope), inst);
3018
+ const expnegx = pipeInsts(exp.call(scope, [negativeX]), inst);
3019
+ const sub = pipeInsts(expx["-"](scope, expnegx), inst);
3020
+ const sum = pipeInsts(expx["+"](scope, expnegx), inst);
3021
+ const result = pipeInsts(sub["/"](scope, sum, out), inst);
3022
+ return [result, inst];
3023
+ }),
3024
+ atanh: new MacroFunction((scope, out, x) => {
3025
+ assertArgumentCount(+!!x, 1);
3026
+ // return (Math.log((1 + x) / (1 - x)) / 2) * Math.radToDeg;
3027
+ const { log, radToDeg } = macroMathOperations;
3028
+ const inst = [];
3029
+ const one = new LiteralValue(1);
3030
+ const two = new LiteralValue(2);
3031
+ const sum = pipeInsts(one["+"](scope, x), inst);
3032
+ const sub = pipeInsts(one["-"](scope, x), inst);
3033
+ const div = pipeInsts(sum["/"](scope, sub), inst);
3034
+ const logOfDiv = pipeInsts(log.call(scope, [div]), inst);
3035
+ const radians = pipeInsts(logOfDiv["/"](scope, two, out), inst);
3036
+ const degrees = pipeInsts(radians["*"](scope, radToDeg), inst);
3037
+ return [degrees, inst];
3038
+ }),
2630
3039
  };
2631
3040
  for (const key in mathOperations) {
2632
3041
  const fn = mathOperations[key];
2633
3042
  macroMathOperations[key] = new MacroFunction((scope, out, a, b) => {
2634
3043
  const argumentCount = +!!a + +!!b;
2635
- if (fn && argumentCount != fn.length) {
2636
- throw new CompilerError(`Expected ${fn.length} arguments, but got ${argumentCount}`);
3044
+ if (fn) {
3045
+ assertArgumentCount(argumentCount, fn.length);
2637
3046
  }
2638
3047
  const cacheKey = getCacheKey(key);
2639
3048
  if (b) {
@@ -2677,6 +3086,11 @@ function getCacheKey(key) {
2677
3086
  return "**";
2678
3087
  return key;
2679
3088
  }
3089
+ function assertArgumentCount(current, expected) {
3090
+ if (current != expected) {
3091
+ throw new CompilerError(`Expected ${expected} arguments, but got ${current}`);
3092
+ }
3093
+ }
2680
3094
  class MlogMath extends ObjectValue {
2681
3095
  constructor() {
2682
3096
  super(createMacroMathOperations());
@@ -2716,7 +3130,10 @@ function assertLiterals(values, message) {
2716
3130
  }
2717
3131
  }
2718
3132
 
2719
- /** Used to create namespaces that contain multiple methods that map into instructions */
3133
+ /**
3134
+ * Used to create namespaces that contain multiple methods that map into
3135
+ * instructions
3136
+ */
2720
3137
  function createOverloadNamespace({ overloads, handler, }) {
2721
3138
  const result = {};
2722
3139
  for (const key in overloads) {
@@ -2792,6 +3209,20 @@ class Draw extends ObjectValue {
2792
3209
  named: "options",
2793
3210
  args: ["x", "y", "image", "size", "rotation"],
2794
3211
  },
3212
+ print: {
3213
+ named: "options",
3214
+ args: ["x", "y", "align"],
3215
+ },
3216
+ translate: {
3217
+ args: ["x", "y"],
3218
+ },
3219
+ scale: {
3220
+ args: ["x", "y"],
3221
+ },
3222
+ rotate: {
3223
+ args: ["degrees"],
3224
+ },
3225
+ reset: { args: [] },
2795
3226
  },
2796
3227
  handler(scope, overload, out, ...args) {
2797
3228
  return [null, [new InstructionBase("draw", overload, ...args)]];
@@ -2803,31 +3234,72 @@ class Draw extends ObjectValue {
2803
3234
 
2804
3235
  class Print extends MacroFunction {
2805
3236
  constructor() {
2806
- super((scope, out, ...values) => {
2807
- const [first] = values;
3237
+ super((scope, out, ...args) => {
2808
3238
  const inst = [];
2809
- if (!isTemplateObjectArray(first)) {
2810
- for (const value of values) {
3239
+ const values = getPrintValues(scope, args).filter(value => !(value instanceof LiteralValue) ||
3240
+ !value.isString() ||
3241
+ value.data.length > 0);
3242
+ if (!needsFormatString(values)) {
3243
+ for (let i = 0; i < values.length; i++) {
3244
+ const value = values[i];
2811
3245
  inst.push(new InstructionBase("print", value));
2812
3246
  }
2813
- return [null, inst];
2814
3247
  }
2815
- // `first` is likely a template strings array
2816
- // maybe this should be checked in another way?
2817
- const { length } = first.data;
2818
- for (let i = 1; i < values.length; i++) {
2819
- const [string] = first.get(scope, new LiteralValue(i - 1));
2820
- if (string.data)
2821
- inst.push(new InstructionBase("print", string));
2822
- inst.push(new InstructionBase("print", values[i]));
3248
+ else {
3249
+ let formatString = "";
3250
+ for (let i = 0; i < values.length; i++) {
3251
+ const value = values[i];
3252
+ if (value instanceof LiteralValue) {
3253
+ formatString += value.data;
3254
+ }
3255
+ else {
3256
+ // mlog replaces the first occurrence of {0}
3257
+ // so we don't need to increment the placeholder
3258
+ // number
3259
+ formatString += "{0}";
3260
+ }
3261
+ }
3262
+ inst.push(new InstructionBase("print", new LiteralValue(formatString)));
3263
+ for (let i = 0; i < values.length; i++) {
3264
+ const value = values[i];
3265
+ if (value instanceof LiteralValue)
3266
+ continue;
3267
+ inst.push(new InstructionBase("format", value));
3268
+ }
2823
3269
  }
2824
- const [tail] = first.get(scope, new LiteralValue(length.data - 1));
2825
- if (tail.data)
2826
- inst.push(new InstructionBase("print", tail));
2827
3270
  return [null, inst];
2828
3271
  });
2829
3272
  }
2830
3273
  }
3274
+ function getPrintValues(scope, args) {
3275
+ const first = args[0];
3276
+ if (!isTemplateObjectArray(first)) {
3277
+ return args;
3278
+ }
3279
+ const values = [];
3280
+ const rest = args.slice(1);
3281
+ // the number of strings in the template object array
3282
+ // is always one more than the number of values
3283
+ // this loop intercalates the values and the strings
3284
+ // into the values array
3285
+ for (let i = 0; i <= rest.length; i++) {
3286
+ const [string] = first.get(scope, new LiteralValue(i));
3287
+ values.push(string);
3288
+ if (i < rest.length)
3289
+ values.push(rest[i]);
3290
+ }
3291
+ return values;
3292
+ }
3293
+ function needsFormatString(values) {
3294
+ for (let i = 0; i < values.length; i++) {
3295
+ const value = values[i];
3296
+ if (!(value instanceof LiteralValue))
3297
+ continue;
3298
+ if (!value.isString() || value.data.length > 0)
3299
+ return true;
3300
+ }
3301
+ return false;
3302
+ }
2831
3303
 
2832
3304
  const defaultTargetName$1 = "message1";
2833
3305
  class PrintFlush extends MacroFunction {
@@ -2835,8 +3307,8 @@ class PrintFlush extends MacroFunction {
2835
3307
  super((scope, out, target) => {
2836
3308
  if (!target)
2837
3309
  return [null, [new InstructionBase("printflush", defaultTargetName$1)]];
2838
- if (!(target instanceof SenseableValue))
2839
- throw new CompilerError("The printflush target must be a senseable value");
3310
+ if (!(target instanceof StoreValue))
3311
+ throw new CompilerError("The printflush target must be a store value");
2840
3312
  return [null, [new InstructionBase("printflush", target)]];
2841
3313
  });
2842
3314
  }
@@ -2848,8 +3320,8 @@ class DrawFlush extends MacroFunction {
2848
3320
  super((scope, out, target) => {
2849
3321
  if (!target)
2850
3322
  return [null, [new InstructionBase("drawflush", defaultTargetName)]];
2851
- if (!(target instanceof SenseableValue))
2852
- throw new CompilerError("The drawflush target must be a senseable value");
3323
+ if (!(target instanceof StoreValue))
3324
+ throw new CompilerError("The drawflush target must be a store value");
2853
3325
  return [null, [new InstructionBase("drawflush", target)]];
2854
3326
  });
2855
3327
  }
@@ -2865,9 +3337,7 @@ class GetBlock extends ObjectValue {
2865
3337
  building: { args: ["x", "y"] },
2866
3338
  },
2867
3339
  handler(scope, overload, out, x, y) {
2868
- const output = overload === "building"
2869
- ? SenseableValue.from(scope, out, EMutability.constant)
2870
- : StoreValue.from(scope, out, EMutability.constant);
3340
+ const output = StoreValue.from(scope, out);
2871
3341
  return [
2872
3342
  output,
2873
3343
  [new InstructionBase("getblock", overload, output, x, y)],
@@ -2884,7 +3354,7 @@ class GetLink extends MacroFunction {
2884
3354
  if (!(index instanceof StoreValue) &&
2885
3355
  (!(index instanceof LiteralValue) || !index.isNumber()))
2886
3356
  throw new CompilerError("The getlink index must be a number literal or a store");
2887
- const outBuild = SenseableValue.from(scope, out, EMutability.constant);
3357
+ const outBuild = StoreValue.from(scope, out);
2888
3358
  return [outBuild, [new InstructionBase("getlink", outBuild, index)]];
2889
3359
  });
2890
3360
  }
@@ -2935,8 +3405,8 @@ class Radar extends MacroFunction {
2935
3405
  super((scope, out, options) => {
2936
3406
  assertIsObjectMacro(options, "The radar options");
2937
3407
  const { building, filters, order, sort } = options.data;
2938
- if (!(building instanceof SenseableValue))
2939
- throw new CompilerError("The building must a senseable value");
3408
+ if (!(building instanceof StoreValue))
3409
+ throw new CompilerError("The building must a store value");
2940
3410
  assertIsArrayMacro(filters, "filters");
2941
3411
  const { length } = filters.data;
2942
3412
  if (!(length instanceof LiteralValue) || !length.isNumber())
@@ -2951,7 +3421,7 @@ class Radar extends MacroFunction {
2951
3421
  if (!(order instanceof LiteralValue || order instanceof StoreValue))
2952
3422
  throw new CompilerError("The radar order must be a literal or a store");
2953
3423
  assertLiteralOneOf(sort, validRadarSorts, "The radar sort");
2954
- const outUnit = SenseableValue.from(scope, out, EMutability.constant);
3424
+ const outUnit = StoreValue.from(scope, out);
2955
3425
  return [
2956
3426
  outUnit,
2957
3427
  [
@@ -2967,9 +3437,9 @@ class Sensor extends MacroFunction {
2967
3437
  super((scope, out, property, target) => {
2968
3438
  if (!(property instanceof StoreValue))
2969
3439
  throw new CompilerError("The sensor property must be a store");
2970
- if (!(target instanceof SenseableValue))
2971
- throw new CompilerError("The sensor target must be a senseable value");
2972
- const temp = SenseableValue.from(scope, out, EMutability.readonly);
3440
+ if (!(target instanceof StoreValue))
3441
+ throw new CompilerError("The sensor target must be a store value");
3442
+ const temp = StoreValue.from(scope, out);
2973
3443
  return [temp, [new InstructionBase("sensor", temp, target, property)]];
2974
3444
  });
2975
3445
  }
@@ -2994,6 +3464,7 @@ class Lookup extends ObjectValue {
2994
3464
  unit: { args: ["index"] },
2995
3465
  item: { args: ["index"] },
2996
3466
  liquid: { args: ["index"] },
3467
+ team: { args: ["index"] },
2997
3468
  },
2998
3469
  handler(scope, overload, out, index) {
2999
3470
  const output = StoreValue.from(scope, out);
@@ -3057,16 +3528,16 @@ class SpawnUnit extends MacroFunction {
3057
3528
  constructor() {
3058
3529
  super((scope, out, options) => {
3059
3530
  assertIsObjectMacro(options, "The options");
3060
- const { type, x, y, team, rotation } = options.data;
3531
+ const { type, x, y, team, rotation, effect } = options.data;
3061
3532
  assertIsRuntimeValue(type, "type");
3062
3533
  assertIsRuntimeValue(x, "x");
3063
3534
  assertIsRuntimeValue(y, "y");
3064
3535
  assertIsRuntimeValue(team, "team");
3065
- const output = SenseableValue.from(scope, out, EMutability.constant);
3536
+ const output = StoreValue.from(scope, out);
3066
3537
  return [
3067
3538
  output,
3068
3539
  [
3069
- new InstructionBase("spawn", type, x, y, rotation ?? "0", team, output),
3540
+ new InstructionBase("spawn", type, x, y, rotation ?? "0", team, output, effect ?? "true"),
3070
3541
  ],
3071
3542
  ];
3072
3543
  });
@@ -3104,8 +3575,9 @@ class UnitControl extends ObjectValue {
3104
3575
  stop: { args: [] },
3105
3576
  move: { args: ["x", "y"] },
3106
3577
  approach: { named: "options", args: ["x", "y", "radius"] },
3578
+ pathfind: { args: ["x", "y"] },
3579
+ autoPathfind: { args: [] },
3107
3580
  boost: { args: ["enable"] },
3108
- pathfind: { args: [] },
3109
3581
  target: { named: "options", args: ["x", "y", "shoot"] },
3110
3582
  targetp: { named: "options", args: ["unit", "shoot"] },
3111
3583
  itemDrop: { args: ["target", "amount"] },
@@ -3119,6 +3591,9 @@ class UnitControl extends ObjectValue {
3119
3591
  named: "options",
3120
3592
  args: ["x", "y", "block", "rotation", { key: "config", default: "" }],
3121
3593
  },
3594
+ deconstruct: {
3595
+ args: ["x", "y"],
3596
+ },
3122
3597
  getBlock: {
3123
3598
  args: ["x", "y"],
3124
3599
  },
@@ -3136,7 +3611,7 @@ class UnitControl extends ObjectValue {
3136
3611
  switch (overload) {
3137
3612
  case "getBlock": {
3138
3613
  const outType = StoreValue.from(scope, extractDestrucuringOut(out, 0));
3139
- const outBuilding = SenseableValue.from(scope, extractDestrucuringOut(out, 1));
3614
+ const outBuilding = StoreValue.from(scope, extractDestrucuringOut(out, 1));
3140
3615
  const outFloor = StoreValue.from(scope, extractDestrucuringOut(out, 2));
3141
3616
  result = ObjectValue.fromArray([outType, outBuilding, outFloor]);
3142
3617
  extraArgs = [outType, outBuilding, outFloor];
@@ -3177,7 +3652,7 @@ class UnitRadar extends MacroFunction {
3177
3652
  if (!(order instanceof LiteralValue || order instanceof StoreValue))
3178
3653
  throw new CompilerError("The radar order must be a literal or a store");
3179
3654
  assertLiteralOneOf(sort, validRadarSorts, "The radar sort");
3180
- const outUnit = SenseableValue.from(scope, out, EMutability.constant);
3655
+ const outUnit = StoreValue.from(scope, out);
3181
3656
  return [
3182
3657
  outUnit,
3183
3658
  [
@@ -3219,7 +3694,7 @@ class UnitLocate extends ObjectValue {
3219
3694
  const outFound = StoreValue.from(scope, extractDestrucuringOut(out, 0));
3220
3695
  const outX = StoreValue.from(scope, extractDestrucuringOut(out, 1));
3221
3696
  const outY = StoreValue.from(scope, extractDestrucuringOut(out, 2));
3222
- const outBuilding = SenseableValue.from(scope, overload === "ore" ? discardedName : extractDestrucuringOut(out, 3));
3697
+ const outBuilding = StoreValue.from(scope, overload === "ore" ? discardedName : extractDestrucuringOut(out, 3));
3223
3698
  const outArgs = [outX, outY, outFound, outBuilding];
3224
3699
  let inputArgs = [];
3225
3700
  switch (overload) {
@@ -3258,20 +3733,29 @@ class UnitLocate extends ObjectValue {
3258
3733
  }
3259
3734
 
3260
3735
  const validEffects = [
3736
+ "none",
3261
3737
  "burning",
3262
3738
  "freezing",
3263
3739
  "unmoving",
3740
+ "slow",
3741
+ "fast",
3264
3742
  "wet",
3743
+ "muddy",
3265
3744
  "melting",
3266
3745
  "sapped",
3267
- "electrified",
3268
- "spore-slowed",
3269
3746
  "tarred",
3270
3747
  "overdrive",
3271
3748
  "overclock",
3272
- "boss",
3749
+ "shielded",
3273
3750
  "shocked",
3274
3751
  "blasted",
3752
+ "corroded",
3753
+ "boss",
3754
+ "spore-slowed",
3755
+ "disarmed",
3756
+ "electrified",
3757
+ "invincible",
3758
+ "dynamic",
3275
3759
  ];
3276
3760
  class ApplyStatus extends ObjectValue {
3277
3761
  constructor() {
@@ -3283,15 +3767,25 @@ class ApplyStatus extends ObjectValue {
3283
3767
  clear: { args: ["status", "unit"] },
3284
3768
  },
3285
3769
  handler(scope, overload, out, effect, unit, duration) {
3286
- if (!(effect instanceof LiteralValue))
3287
- throw new CompilerError("The status effect must be a string literal");
3288
- assertLiteralOneOf(effect, validEffects, "The status effect");
3289
- return [
3290
- null,
3291
- [
3292
- new InstructionBase("status", String(overload !== "apply"), effect.data, unit, duration),
3293
- ],
3770
+ let effectValue = effect;
3771
+ if (effect instanceof LiteralValue && effect.isString()) {
3772
+ if (effect.data.startsWith("@status-")) {
3773
+ effectValue = effect.data;
3774
+ }
3775
+ else {
3776
+ assertLiteralOneOf(effect, validEffects, "The status effect");
3777
+ effectValue = `@status-${effect.data}`;
3778
+ }
3779
+ }
3780
+ const instArgs = [
3781
+ "status",
3782
+ String(overload !== "apply"),
3783
+ effectValue,
3784
+ unit,
3294
3785
  ];
3786
+ if (duration)
3787
+ instArgs.push(duration);
3788
+ return [null, [new InstructionBase(...instArgs)]];
3295
3789
  },
3296
3790
  });
3297
3791
  super(data);
@@ -3301,14 +3795,9 @@ class ApplyStatus extends ObjectValue {
3301
3795
  class SpawnWave extends MacroFunction {
3302
3796
  constructor() {
3303
3797
  super((scope, out, natural, x, y) => {
3304
- if (!(natural instanceof LiteralValue) ||
3305
- (natural.data !== 1 && natural.data !== 0))
3306
- throw new CompilerError("The 'natural' argument must be a boolean literal");
3307
3798
  return [
3308
3799
  null,
3309
- [
3310
- new InstructionBase("spawnwave", x ?? "10", y ?? "10", String(!!natural.data)),
3311
- ],
3800
+ [new InstructionBase("spawnwave", x ?? "null", y ?? "null", natural)],
3312
3801
  ];
3313
3802
  });
3314
3803
  }
@@ -3330,16 +3819,25 @@ class SetRule extends ObjectValue {
3330
3819
  unitCap: { args: ["cap"] },
3331
3820
  mapArea: { named: "options", args: ["x", "y", "width", "height"] },
3332
3821
  lighting: { args: ["enabled"] },
3822
+ canGameOver: { args: ["enabled"] },
3333
3823
  ambientLight: { args: ["rgbaData"] },
3334
3824
  solarMultiplier: { args: ["multiplier"] },
3825
+ dragMultiplier: { args: ["multiplier"] },
3826
+ ban: { args: ["content"] },
3827
+ unban: { args: ["content"] },
3335
3828
  buildSpeed: { args: ["team", "multiplier"] },
3829
+ unitHealth: { args: ["team", "multiplier"] },
3336
3830
  unitBuildSpeed: { args: ["team", "multiplier"] },
3831
+ unitMineSpeed: { args: ["team", "multiplier"] },
3337
3832
  unitCost: { args: ["team", "multiplier"] },
3338
3833
  unitDamage: { args: ["team", "multiplier"] },
3339
3834
  blockHealth: { args: ["team", "multiplier"] },
3340
3835
  blockDamage: { args: ["team", "multiplier"] },
3341
3836
  rtsMinWeight: { args: ["team", "value"] },
3342
3837
  rtsMinSquad: { args: ["team", "value"] },
3838
+ unitLight: { args: ["enabled"] },
3839
+ pauseDisabled: { args: ["enabled"] },
3840
+ musicVolume: { args: ["volume"] },
3343
3841
  },
3344
3842
  handler(scope, overload, out, ...args) {
3345
3843
  const params = ["10", "0", "0", "100", "100"];
@@ -3353,7 +3851,9 @@ class SetRule extends ObjectValue {
3353
3851
  break;
3354
3852
  }
3355
3853
  case "buildSpeed":
3854
+ case "unitHealth":
3356
3855
  case "unitBuildSpeed":
3856
+ case "unitMineSpeed":
3357
3857
  case "unitCost":
3358
3858
  case "unitDamage":
3359
3859
  case "blockHealth":
@@ -3383,9 +3883,34 @@ class FlushMessage extends ObjectValue {
3383
3883
  mission: { args: [] },
3384
3884
  announce: { args: ["duration"] },
3385
3885
  toast: { args: ["duration"] },
3886
+ notifySync: { args: [] },
3887
+ missionSync: { args: [] },
3888
+ announceSync: { args: ["duration"] },
3889
+ toastSync: { args: ["duration"] },
3386
3890
  },
3387
3891
  handler(scope, overload, out, duration) {
3388
- return [null, [new InstructionBase("message", overload, duration)]];
3892
+ let result = null;
3893
+ let arg = "0";
3894
+ switch (overload) {
3895
+ case "announce":
3896
+ case "toast":
3897
+ case "mission":
3898
+ case "notify":
3899
+ result = StoreValue.from(scope, out);
3900
+ arg = result;
3901
+ break;
3902
+ case "announceSync":
3903
+ case "missionSync":
3904
+ case "toastSync":
3905
+ case "notifySync":
3906
+ result = null;
3907
+ arg = "@wait";
3908
+ break;
3909
+ }
3910
+ return [
3911
+ result,
3912
+ [new InstructionBase("message", overload, duration, arg)],
3913
+ ];
3389
3914
  },
3390
3915
  });
3391
3916
  super(data);
@@ -3399,8 +3924,19 @@ class Cutscene extends ObjectValue {
3399
3924
  pan: { named: "options", args: ["x", "y", "speed"] },
3400
3925
  zoom: { args: ["level"] },
3401
3926
  stop: { args: [] },
3927
+ active: { args: [] },
3928
+ shake: { args: ["amount", "duration"] },
3929
+ getHud: { args: [] },
3930
+ setHud: { args: ["shown"] },
3402
3931
  },
3403
3932
  handler(scope, overload, out, ...args) {
3933
+ if (overload === "active" || overload === "getHud") {
3934
+ const result = StoreValue.from(scope, out);
3935
+ return [
3936
+ result,
3937
+ [new InstructionBase("cutscene", overload, result, "0", "0", "0")],
3938
+ ];
3939
+ }
3404
3940
  const params = Object.assign(["100", "100", "0.06", "0"], args);
3405
3941
  return [null, [new InstructionBase("cutscene", overload, ...params)]];
3406
3942
  },
@@ -3438,8 +3974,10 @@ class Fetch extends ObjectValue {
3438
3974
  constructor() {
3439
3975
  const data = createOverloadNamespace({
3440
3976
  overloads: {
3441
- unit: { args: ["team", "index"] },
3442
- unitCount: { args: ["team"] },
3977
+ unit: {
3978
+ args: ["team", "index", { key: "type", default: "null" }],
3979
+ },
3980
+ unitCount: { args: ["team", { key: "type", default: "null" }] },
3443
3981
  player: { args: ["team", "index"] },
3444
3982
  playerCount: { args: ["team"] },
3445
3983
  core: { args: ["team", "index"] },
@@ -3448,9 +3986,9 @@ class Fetch extends ObjectValue {
3448
3986
  buildCount: { args: ["team", "block"] },
3449
3987
  },
3450
3988
  handler(scope, overload, out, team, ...rest) {
3451
- const output = SenseableValue.from(scope, out, EMutability.constant);
3989
+ const output = StoreValue.from(scope, out);
3452
3990
  const params = ["0", "@conveyor"];
3453
- if (overload === "buildCount") {
3991
+ if (overload === "buildCount" || overload === "unitCount") {
3454
3992
  params[1] = rest[0];
3455
3993
  }
3456
3994
  else {
@@ -3487,6 +4025,373 @@ class SetFlag extends MacroFunction {
3487
4025
  }
3488
4026
  }
3489
4027
 
4028
+ class SetProp extends MacroFunction {
4029
+ constructor() {
4030
+ super((scope, out, target) => {
4031
+ if (!target)
4032
+ throw new CompilerError("Missing argument: target");
4033
+ return [new Settable(target), []];
4034
+ });
4035
+ }
4036
+ }
4037
+ class SettableEntry extends VoidValue {
4038
+ constructor(target, prop) {
4039
+ super();
4040
+ this.target = target;
4041
+ this.prop = prop;
4042
+ this.macro = true;
4043
+ }
4044
+ eval(scope, out) {
4045
+ return this.target.get(scope, this.prop, out);
4046
+ }
4047
+ "="(scope, value) {
4048
+ let prop = this.prop;
4049
+ if (prop instanceof LiteralValue && prop.isString())
4050
+ prop = formatSenseablePropName(prop.data);
4051
+ return [value, [new InstructionBase("setprop", prop, this.target, value)]];
4052
+ }
4053
+ debugString() {
4054
+ return "SettableEntry";
4055
+ }
4056
+ toMlogString() {
4057
+ return "[macro SettableEntry]";
4058
+ }
4059
+ }
4060
+ class Settable extends ObjectValue {
4061
+ constructor(target) {
4062
+ super({});
4063
+ this.target = target;
4064
+ }
4065
+ get(scope, key, out) {
4066
+ const entry = new SettableEntry(this.target, key);
4067
+ if (out)
4068
+ return entry.eval(scope, out);
4069
+ return [entry, []];
4070
+ }
4071
+ }
4072
+
4073
+ class SyncLock extends ObjectValue {
4074
+ constructor(value) {
4075
+ super({
4076
+ value,
4077
+ sendToClients: new MacroFunction(() => {
4078
+ return [null, [new InstructionBase("sync", this.value)]];
4079
+ }),
4080
+ });
4081
+ this.value = value;
4082
+ }
4083
+ }
4084
+ class SyncLockConstructor extends MacroFunction {
4085
+ constructor() {
4086
+ super((scope, out, init) => {
4087
+ const inst = [];
4088
+ const value = StoreValue.from(scope, out, EMutability.mutable);
4089
+ if (init) {
4090
+ if (!(init instanceof StoreValue || init instanceof LiteralValue)) {
4091
+ throw new CompilerError("Expected a store value or a literal value");
4092
+ }
4093
+ pipeInsts(value["="](scope, init), inst);
4094
+ }
4095
+ return [new SyncLock(value), inst];
4096
+ });
4097
+ }
4098
+ preCall(scope, out) {
4099
+ if (out)
4100
+ return [out];
4101
+ }
4102
+ }
4103
+
4104
+ class Effect extends ObjectValue {
4105
+ constructor() {
4106
+ const data = createOverloadNamespace({
4107
+ overloads: {
4108
+ warn: { args: ["x", "y"] },
4109
+ cross: { args: ["x", "y"] },
4110
+ blockFall: { args: ["x", "y", "data"] },
4111
+ placeBlock: { args: ["x", "y", "size"] },
4112
+ placeBlockSpark: { args: ["x", "y", "size"] },
4113
+ breakBlock: { args: ["x", "y", "size"] },
4114
+ spawn: { args: ["x", "y"] },
4115
+ trail: { args: ["x", "y", "color", "size"], named: "options" },
4116
+ breakPop: { args: ["x", "y", "color", "size"], named: "options" },
4117
+ breakProp: { args: ["x", "y", "color", "size"], named: "options" },
4118
+ smokeCloud: { args: ["x", "y", "color"] },
4119
+ vapor: { args: ["x", "y", "color"] },
4120
+ hit: { args: ["x", "y", "color"] },
4121
+ hitSquare: { args: ["x", "y", "color"] },
4122
+ shootSmall: { args: ["x", "y", "color", "rotation"], named: "options" },
4123
+ shootBig: { args: ["x", "y", "color", "rotation"], named: "options" },
4124
+ smokeSmall: { args: ["x", "y", "rotation"] },
4125
+ smokeBig: { args: ["x", "y", "rotation"] },
4126
+ smokeColor: { args: ["x", "y", "color", "rotation"], named: "options" },
4127
+ smokeSquare: {
4128
+ args: ["x", "y", "color", "rotation"],
4129
+ named: "options",
4130
+ },
4131
+ smokeSquareBig: {
4132
+ args: ["x", "y", "color", "rotation"],
4133
+ named: "options",
4134
+ },
4135
+ spark: { args: ["x", "y", "color"] },
4136
+ sparkBig: { args: ["x", "y", "color"] },
4137
+ sparkShoot: { args: ["x", "y", "color", "rotation"], named: "options" },
4138
+ sparkShootBig: {
4139
+ args: ["x", "y", "color", "rotation"],
4140
+ named: "options",
4141
+ },
4142
+ drill: { args: ["x", "y", "color"] },
4143
+ drillBig: { args: ["x", "y", "color"] },
4144
+ lightBlock: { args: ["x", "y", "color", "size"], named: "options" },
4145
+ explosion: { args: ["x", "y", "size"] },
4146
+ smokePuff: { args: ["x", "y", "color"] },
4147
+ sparkExplosion: { args: ["x", "y", "color"] },
4148
+ crossExplosion: { args: ["x", "y", "color", "size"], named: "options" },
4149
+ wave: { args: ["x", "y", "color", "size"], named: "options" },
4150
+ bubble: { args: ["x", "y"] },
4151
+ },
4152
+ handler(scope, overload, out, ...args) {
4153
+ const params = ["0", "0", "2", "%ffaaff", ""];
4154
+ params[0] = args[0];
4155
+ params[1] = args[1];
4156
+ switch (overload) {
4157
+ case "blockFall":
4158
+ params[4] = args[2];
4159
+ break;
4160
+ case "placeBlock":
4161
+ case "smokeSmall":
4162
+ case "smokeBig":
4163
+ case "explosion":
4164
+ params[2] = args[2];
4165
+ break;
4166
+ case "trail":
4167
+ case "breakPop":
4168
+ case "breakProp":
4169
+ case "shootSmall":
4170
+ case "shootBig":
4171
+ case "smokeColor":
4172
+ case "smokeSquare":
4173
+ case "smokeSquareBig":
4174
+ case "sparkShoot":
4175
+ case "sparkShootBig":
4176
+ case "lightBlock":
4177
+ case "crossExplosion":
4178
+ case "wave":
4179
+ params[3] = args[2];
4180
+ params[2] = args[3];
4181
+ break;
4182
+ case "smokeCloud":
4183
+ case "vapor":
4184
+ case "hit":
4185
+ case "hitSquare":
4186
+ case "spark":
4187
+ case "sparkBig":
4188
+ case "drill":
4189
+ case "drillBig":
4190
+ case "smokePuff":
4191
+ case "sparkExplosion":
4192
+ params[3] = args[2];
4193
+ break;
4194
+ }
4195
+ return [
4196
+ null,
4197
+ [
4198
+ new InstructionBase("effect", overload === "breakPop" ? "breakProp" : overload, ...params),
4199
+ ],
4200
+ ];
4201
+ },
4202
+ });
4203
+ super(data);
4204
+ }
4205
+ }
4206
+
4207
+ class LocalePrint extends MacroFunction {
4208
+ constructor() {
4209
+ super((scope, out, name) => {
4210
+ return [null, [new InstructionBase("localeprint", name)]];
4211
+ });
4212
+ }
4213
+ }
4214
+
4215
+ class WeatherSense extends MacroFunction {
4216
+ constructor() {
4217
+ super((scope, out, weather) => {
4218
+ const state = StoreValue.from(scope, out);
4219
+ return [state, [new InstructionBase("weathersense", state, weather)]];
4220
+ });
4221
+ }
4222
+ }
4223
+
4224
+ class WeatherSet extends MacroFunction {
4225
+ constructor() {
4226
+ super((scope, out, weather, state) => {
4227
+ return [null, [new InstructionBase("weatherset", weather, state)]];
4228
+ });
4229
+ }
4230
+ }
4231
+
4232
+ class PlaySound extends ObjectValue {
4233
+ constructor() {
4234
+ const data = createOverloadNamespace({
4235
+ overloads: {
4236
+ positional: {
4237
+ named: "options",
4238
+ args: ["sound", "volume", "pitch", "x", "y", "limit"],
4239
+ },
4240
+ global: {
4241
+ named: "options",
4242
+ args: ["sound", "volume", "pitch", "pan", "limit"],
4243
+ },
4244
+ },
4245
+ //playsound false @sfx-pew 12 0.1 globalyespan @thisx @thisy true
4246
+ // wait 1
4247
+ handler(scope, overload, out, ...args) {
4248
+ let sound = "0";
4249
+ let volume = "0";
4250
+ let pitch = "0";
4251
+ let pan = "0";
4252
+ let x = "0";
4253
+ let y = "0";
4254
+ let limit = "0";
4255
+ // let volume =
4256
+ switch (overload) {
4257
+ case "positional": {
4258
+ // playsound true id volume pitch _pan x y limit
4259
+ [sound, volume, pitch, x, y, limit] = args;
4260
+ break;
4261
+ }
4262
+ case "global": {
4263
+ // playsound false id volume pitch pan _x _y limit
4264
+ [sound, volume, pitch, pan, limit] = args;
4265
+ break;
4266
+ }
4267
+ }
4268
+ return [
4269
+ null,
4270
+ [
4271
+ new InstructionBase("playsound", String(overload === "positional"), sound, volume, pitch, pan, x, y, limit),
4272
+ ],
4273
+ ];
4274
+ },
4275
+ });
4276
+ super(data);
4277
+ }
4278
+ }
4279
+
4280
+ class PrintChar extends MacroFunction {
4281
+ constructor() {
4282
+ super((scope, out, character) => {
4283
+ return [null, [new InstructionBase("printchar", character)]];
4284
+ });
4285
+ }
4286
+ }
4287
+
4288
+ class UnpackColor extends MacroFunction {
4289
+ constructor() {
4290
+ super((scope, out, color) => {
4291
+ if (!color) {
4292
+ throw new CompilerError(`Missing argument: color`);
4293
+ }
4294
+ const outR = StoreValue.from(scope, extractDestrucuringOut(out, "r"));
4295
+ const outG = StoreValue.from(scope, extractDestrucuringOut(out, "g"));
4296
+ const outB = StoreValue.from(scope, extractDestrucuringOut(out, "b"));
4297
+ const outA = StoreValue.from(scope, extractDestrucuringOut(out, "a"));
4298
+ return [
4299
+ new ObjectValue({
4300
+ r: outR,
4301
+ g: outG,
4302
+ b: outB,
4303
+ a: outA,
4304
+ }),
4305
+ [new InstructionBase("unpackcolor", outR, outG, outB, outA, color)],
4306
+ ];
4307
+ });
4308
+ }
4309
+ }
4310
+
4311
+ class Format extends MacroFunction {
4312
+ constructor() {
4313
+ super((scope, out, value) => {
4314
+ return [null, [new InstructionBase("format", value)]];
4315
+ });
4316
+ }
4317
+ }
4318
+
4319
+ class Query extends ObjectValue {
4320
+ constructor() {
4321
+ const data = createOverloadNamespace({
4322
+ overloads: {
4323
+ circle: {
4324
+ named: "options",
4325
+ args: ["type", { key: "team", default: "null" }, "x", "y", "radius"],
4326
+ },
4327
+ rect: {
4328
+ named: "options",
4329
+ args: [
4330
+ "type",
4331
+ { key: "team", default: "null" },
4332
+ "x",
4333
+ "y",
4334
+ "width",
4335
+ "height",
4336
+ ],
4337
+ },
4338
+ },
4339
+ handler(scope, overload, out, ...args) {
4340
+ if (overload === "circle") {
4341
+ const [type, team, x, y, radius] = args;
4342
+ return [
4343
+ null,
4344
+ [
4345
+ new InstructionBase("query", "circle", type, team, x, y, radius, "0"),
4346
+ ],
4347
+ ];
4348
+ }
4349
+ const [type, team, x, y, width, height] = args;
4350
+ return [
4351
+ null,
4352
+ [
4353
+ new InstructionBase("query", "rect", type, team, x, y, width, height),
4354
+ ],
4355
+ ];
4356
+ },
4357
+ });
4358
+ super(data);
4359
+ }
4360
+ }
4361
+
4362
+ class SpawnBullet extends MacroFunction {
4363
+ constructor() {
4364
+ super((scope, out, options) => {
4365
+ assertIsObjectMacro(options, "The options");
4366
+ const { from, index, x, y, rotation, team, owner, damage, velocityScl, lifeScl, aimX, aimY, } = options.data;
4367
+ assertIsRuntimeValue(from, "from");
4368
+ assertIsRuntimeValue(x, "x");
4369
+ assertIsRuntimeValue(y, "y");
4370
+ assertIsRuntimeValue(rotation, "rotation");
4371
+ assertIsRuntimeValue(team, "team");
4372
+ const output = StoreValue.from(scope, out);
4373
+ return [
4374
+ output,
4375
+ [
4376
+ new InstructionBase("bullet", output, from, index ?? "0", x, y, rotation, team, owner ?? "null", damage ?? "-1", velocityScl ?? "1", lifeScl ?? "1", aimX ?? "-1", aimY ?? "-1"),
4377
+ ],
4378
+ ];
4379
+ });
4380
+ }
4381
+ }
4382
+
4383
+ class PlayMusic extends MacroFunction {
4384
+ constructor() {
4385
+ super((scope, out, name, interrupt) => {
4386
+ assertIsRuntimeValue(name, "name");
4387
+ return [
4388
+ null,
4389
+ [new InstructionBase("playmusic", name, interrupt ?? "true")],
4390
+ ];
4391
+ });
4392
+ }
4393
+ }
4394
+
3490
4395
  class MemoryEntry extends BaseValue {
3491
4396
  constructor(scope, mem, prop) {
3492
4397
  super();
@@ -3518,7 +4423,7 @@ class MemoryEntry extends BaseValue {
3518
4423
  debugString() {
3519
4424
  return "MemoryEntry";
3520
4425
  }
3521
- toString() {
4426
+ toMlogString() {
3522
4427
  return '"[macro MemoryEntry]"';
3523
4428
  }
3524
4429
  }
@@ -3547,33 +4452,108 @@ class MemoryMacro extends ObjectValue {
3547
4452
  return super.hasProperty(scope, prop);
3548
4453
  }
3549
4454
  debugString() {
3550
- return `Memory("${this.cell.toString()}")`;
4455
+ return `Memory("${this.cell.toMlogString()}")`;
3551
4456
  }
3552
- toString() {
4457
+ toMlogString() {
3553
4458
  return '"[macro Memory]"';
3554
4459
  }
3555
4460
  }
3556
4461
  class MemoryBuilder extends MacroFunction {
3557
4462
  constructor() {
3558
4463
  super((scope, out, cell, size = new LiteralValue(64)) => {
3559
- if (!(cell instanceof SenseableValue))
3560
- throw new CompilerError("Memory cell must be a senseable value.");
4464
+ if (!(cell instanceof StoreValue))
4465
+ throw new CompilerError("Memory cell must be a store value.");
3561
4466
  if (!(size instanceof LiteralValue && size.isNumber()) &&
3562
4467
  !(size instanceof StoreValue))
3563
4468
  throw new CompilerError("The memory size must be a number literal or a store.");
3564
- if (size.mutability === EMutability.constant) {
4469
+ if (size.mutability === EMutability.constant ||
4470
+ size.mutability === EMutability.immutable) {
3565
4471
  return [new MemoryMacro(cell, size), []];
3566
4472
  }
3567
4473
  const name = extractOutName(out) ?? scope.makeTempName();
3568
4474
  const store = new StoreValue(`${name}.&len`);
3569
4475
  const [, inst] = store["="](scope, size);
3570
- store.mutability = EMutability.constant;
4476
+ store.mutability = EMutability.immutable;
3571
4477
  return [new MemoryMacro(cell, store), inst];
3572
4478
  });
3573
4479
  }
3574
4480
  }
3575
4481
 
3576
- const dynamicVars = ["time", "tick"];
4482
+ class QueriesMacro extends ObjectValue {
4483
+ constructor() {
4484
+ super({
4485
+ at: new MacroFunction((scope, out, index) => {
4486
+ if (!index) {
4487
+ throw new CompilerError("Expected index argument for queries.at()");
4488
+ }
4489
+ const outValue = StoreValue.from(scope, out);
4490
+ return [
4491
+ outValue,
4492
+ [new InstructionBase("read", outValue, "@queries", index)],
4493
+ ];
4494
+ }),
4495
+ });
4496
+ this.queriesStore = new StoreValue("@queries", EMutability.readonly);
4497
+ }
4498
+ get(scope, key, out) {
4499
+ if (super.hasProperty(scope, key))
4500
+ return super.get(scope, key, out);
4501
+ if (key instanceof LiteralValue && key.isString()) {
4502
+ if (key.data === "length") {
4503
+ const outValue = StoreValue.from(scope, out);
4504
+ return [
4505
+ outValue,
4506
+ [new InstructionBase("sensor", outValue, "@queries", "@size")],
4507
+ ];
4508
+ }
4509
+ if (key.data === "value") {
4510
+ return [this.queriesStore, []];
4511
+ }
4512
+ }
4513
+ if ((key instanceof LiteralValue && key.isNumber()) ||
4514
+ !(key instanceof LiteralValue)) {
4515
+ const outValue = StoreValue.from(scope, out);
4516
+ return [
4517
+ outValue,
4518
+ [new InstructionBase("read", outValue, "@queries", key)],
4519
+ ];
4520
+ }
4521
+ throw new CompilerError(`The member [${key.debugString()}] is not present in QueriesView`);
4522
+ }
4523
+ hasProperty(scope, prop) {
4524
+ if ((prop instanceof LiteralValue &&
4525
+ (prop.isNumber() || prop.data === "length" || prop.data === "value")) ||
4526
+ !(prop instanceof LiteralValue)) {
4527
+ return true;
4528
+ }
4529
+ return super.hasProperty(scope, prop);
4530
+ }
4531
+ debugString() {
4532
+ return "QueriesView";
4533
+ }
4534
+ toMlogString() {
4535
+ return '"[macro QueriesView]"';
4536
+ }
4537
+ }
4538
+
4539
+ const dynamicVars = [
4540
+ "unit",
4541
+ "tick",
4542
+ "time",
4543
+ "second",
4544
+ "minute",
4545
+ "waveNumber",
4546
+ "waveTime",
4547
+ "server",
4548
+ "client",
4549
+ "clientLocale",
4550
+ "clientUnit",
4551
+ "clientName",
4552
+ "clientTeam",
4553
+ "clientMobile",
4554
+ "clientMusicPlaying",
4555
+ "clientCurrentMusic",
4556
+ ];
3577
4557
  class NamespaceMacro extends ObjectValue {
3578
4558
  constructor({ changeCasing = false } = {}) {
3579
4559
  super();
@@ -3585,11 +4565,15 @@ class NamespaceMacro extends ObjectValue {
3585
4565
  if (!(key instanceof LiteralValue) || !key.isString())
3586
4566
  throw new CompilerError("Cannot use dynamic properties on namespace macros");
3587
4567
  const symbolName = this.changeCasing ? camelToDashCase(key.data) : key.data;
3588
- const mutability = dynamicVars.includes(symbolName)
3589
- ? EMutability.readonly
3590
- : EMutability.constant;
3591
- const result = new StoreValue(`@${symbolName}`, mutability);
3592
- return [result, []];
4568
+ if (dynamicVars.includes(symbolName)) {
4569
+ return [
4570
+ new StoreValue(`@${symbolName}`, EMutability.readonly, {
4571
+ volatile: true,
4572
+ }),
4573
+ [],
4574
+ ];
4575
+ }
4576
+ return [new StoreValue(`@${symbolName}`, EMutability.constant), []];
3593
4577
  }
3594
4578
  hasProperty(scope, prop) {
3595
4579
  return prop instanceof LiteralValue && prop.isString();
@@ -3598,16 +4582,59 @@ class NamespaceMacro extends ObjectValue {
3598
4582
  class VarsNamespace extends NamespaceMacro {
3599
4583
  constructor() {
3600
4584
  super();
3601
- Object.assign(this.data, {
3602
- unit: new SenseableValue("@unit"),
3603
- this: new SenseableValue("@this"),
3604
- });
4585
+ }
4586
+ }
4587
+ class ColorsNamespace extends NamespaceMacro {
4588
+ constructor() {
4589
+ super();
4590
+ }
4591
+ get(scope, key, out) {
4592
+ if (!(key instanceof LiteralValue) || !key.isString())
4593
+ return super.get(scope, key, out);
4594
+ const plainName = key.data;
4595
+ const name = `@color${plainName[0].toUpperCase()}${plainName.slice(1)}`;
4596
+ const result = new StoreValue(name, EMutability.constant);
4597
+ return [result, []];
4598
+ }
4599
+ }
4600
+ class SoundsNamespace extends NamespaceMacro {
4601
+ constructor() {
4602
+ super();
4603
+ }
4604
+ get(scope, key, out) {
4605
+ if (!(key instanceof LiteralValue) || !key.isString())
4606
+ return super.get(scope, key, out);
4607
+ const result = new StoreValue(`@sfx-${key.data}`, EMutability.constant);
4608
+ return [result, []];
4609
+ }
4610
+ }
4611
+ class StatusEffectsNamespace extends NamespaceMacro {
4612
+ constructor() {
4613
+ super({ changeCasing: true });
4614
+ }
4615
+ get(scope, key, out) {
4616
+ if (!(key instanceof LiteralValue) || !key.isString())
4617
+ return super.get(scope, key, out);
4618
+ const name = `@status-${camelToDashCase(key.data)}`;
4619
+ const result = new StoreValue(name, EMutability.constant);
4620
+ return [result, []];
4621
+ }
4622
+ }
4623
+ class PVarsNamespace extends NamespaceMacro {
4624
+ constructor() {
4625
+ super();
4626
+ }
4627
+ get(scope, key, out) {
4628
+ if (key instanceof LiteralValue && key.data === "queries") {
4629
+ return [new QueriesMacro(), []];
4630
+ }
4631
+ return super.get(scope, key, out);
3605
4632
  }
3606
4633
  }
3607
4634
 
3608
4635
  /**
3609
- * The size of a dynamic array iteration item.
3610
- * Includes the set counter instruction
4636
+ * The size of a dynamic array iteration item. Includes the set counter
4637
+ * instruction
3611
4638
  */
3612
4639
  const itemSize = 2;
3613
4640
  class DynamicArray extends ObjectValue {
@@ -3762,8 +4789,8 @@ class DynamicArray extends ObjectValue {
3762
4789
  this.scope = scope;
3763
4790
  this.name = name;
3764
4791
  this.values = values;
3765
- this.getterTemp = new SenseableValue(getterName, EMutability.mutable);
3766
- this.setterTemp = new SenseableValue(setterName, EMutability.mutable);
4792
+ this.getterTemp = new StoreValue(getterName);
4793
+ this.setterTemp = new StoreValue(setterName);
3767
4794
  this.returnTemp = new StoreValue(returnName);
3768
4795
  if (dynamic)
3769
4796
  this.lengthStore = lengthStore;
@@ -3847,7 +4874,7 @@ class DynamicArray extends ObjectValue {
3847
4874
  const id = this.dynamic ? "DynamicArray" : "MutableArray";
3848
4875
  return `${id}("${this.name}")`;
3849
4876
  }
3850
- toString() {
4877
+ toMlogString() {
3851
4878
  const id = this.dynamic ? "DynamicArray" : "MutableArray";
3852
4879
  return `"[macro ${id}]"`;
3853
4880
  }
@@ -3878,7 +4905,7 @@ class DynamicArrayEntry extends BaseValue {
3878
4905
  ? out
3879
4906
  : out
3880
4907
  ? getterTemp
3881
- : SenseableValue.from(scope, undefined, EMutability.mutable);
4908
+ : StoreValue.from(scope);
3882
4909
  // used in checked mode, jumps to this address
3883
4910
  // if the index is out of bounds
3884
4911
  const failAddr = this.failAddress ?? new LiteralValue(null);
@@ -3949,7 +4976,7 @@ class DynamicArrayEntry extends BaseValue {
3949
4976
  const name = this.array.dynamic ? "DynamicArray" : "MutableArray";
3950
4977
  return `${name}Entry`;
3951
4978
  }
3952
- toString() {
4979
+ toMlogString() {
3953
4980
  const name = this.array.dynamic ? "DynamicArray" : "MutableArray";
3954
4981
  return `"[macro ${name}Entry]"`;
3955
4982
  }
@@ -3972,7 +4999,7 @@ class DynamicArrayConstructor extends MacroFunction {
3972
4999
  throw new CompilerError("The dynamic array initializer must be an array macro or a number literal");
3973
5000
  }
3974
5001
  for (let i = 0; i < length; i++) {
3975
- values.push(new SenseableValue(`${name}->${i}`, EMutability.mutable));
5002
+ values.push(new StoreValue(`${name}->${i}`));
3976
5003
  }
3977
5004
  if (init instanceof ObjectValue) {
3978
5005
  const length = init.data.length.data;
@@ -4013,17 +5040,234 @@ class Unchecked extends MacroFunction {
4013
5040
  }
4014
5041
  }
4015
5042
 
5043
+ class GetBuildings extends MacroFunction {
5044
+ constructor() {
5045
+ super(() => [new BuildingsMacro(), []]);
5046
+ }
5047
+ }
5048
+ class BuildingsMacro extends ObjectValue {
5049
+ hasProperty(scope, prop) {
5050
+ if (isKeyBuildingName(prop)) {
5051
+ return true;
5052
+ }
5053
+ return super.hasProperty(scope, prop);
5054
+ }
5055
+ get(scope, key, out) {
5056
+ if (super.hasProperty(scope, key))
5057
+ return super.get(scope, key, out);
5058
+ if (!isKeyBuildingName(key)) {
5059
+ throw new CompilerError(`[${key.debugString()}] is not a valid building name`);
5060
+ }
5061
+ return [new StoreValue(key.data, EMutability.constant), []];
5062
+ }
5063
+ }
5064
+ function isKeyBuildingName(key) {
5065
+ if (!(key instanceof LiteralValue) || !key.isString())
5066
+ return false;
5067
+ const regex = /^\w+$/;
5068
+ return regex.test(key.data);
5069
+ }
5070
+
4016
5071
  class GetGlobal extends MacroFunction {
4017
5072
  constructor(mutability) {
4018
5073
  super((scope, out, name) => {
4019
5074
  if (!(name instanceof LiteralValue) || !name.isString())
4020
5075
  throw new CompilerError("The name parameter must be a string literal.");
4021
- const result = new SenseableValue(name.data, mutability);
5076
+ const result = new StoreValue(name.data, mutability);
4022
5077
  return [result, []];
4023
5078
  });
4024
5079
  }
4025
5080
  }
4026
5081
 
5082
+ class GetColor extends MacroFunction {
5083
+ constructor() {
5084
+ super((scope, out, color) => {
5085
+ if (!(color instanceof LiteralValue) || !color.isString())
5086
+ throw new CompilerError("The color parameter must be a string literal.");
5087
+ return [new StoreValue(`%${color.data}`, EMutability.constant), []];
5088
+ });
5089
+ }
5090
+ }
5091
+
5092
+ class MarkerConstructor extends ObjectValue {
5093
+ constructor() {
5094
+ const data = createOverloadNamespace({
5095
+ overloads: {
5096
+ of: { args: ["id"] },
5097
+ shapeText: {
5098
+ named: "options",
5099
+ args: ["id", "x", "y", "replace"],
5100
+ },
5101
+ point: {
5102
+ named: "options",
5103
+ args: ["id", "x", "y", "replace"],
5104
+ },
5105
+ shape: {
5106
+ named: "options",
5107
+ args: ["id", "x", "y", "replace"],
5108
+ },
5109
+ line: {
5110
+ named: "options",
5111
+ args: ["id", "x", "y", "replace"],
5112
+ },
5113
+ text: {
5114
+ named: "options",
5115
+ args: ["id", "x", "y", "replace"],
5116
+ },
5117
+ texture: {
5118
+ named: "options",
5119
+ args: ["id", "x", "y", "replace"],
5120
+ },
5121
+ quad: {
5122
+ named: "options",
5123
+ args: ["id", "x", "y", "replace"],
5124
+ },
5125
+ },
5126
+ handler(scope, overload, out, ...args) {
5127
+ // makemarker id line x y replace
5128
+ const inst = [];
5129
+ let id = args[0];
5130
+ const rest = args.slice(1);
5131
+ if (!(id instanceof LiteralValue)) {
5132
+ // prevent mutations to the source
5133
+ // from affecting the object's internal value
5134
+ const store = StoreValue.from(scope);
5135
+ pipeInsts(store["="](scope, id), inst);
5136
+ id = store;
5137
+ }
5138
+ const marker = new MarkerMacro(id);
5139
+ if (overload !== "of") {
5140
+ inst.push(new InstructionBase("makemarker", overload, id, ...rest));
5141
+ }
5142
+ return [marker, inst];
5143
+ },
5144
+ });
5145
+ super(data);
5146
+ }
5147
+ }
5148
+ class MarkerMacro extends ObjectValue {
5149
+ constructor(id) {
5150
+ super({
5151
+ remove: new MacroFunction(() => [null, [setmarker(id, "remove")]]),
5152
+ world: new MarkerMacroSetter(id, "world"),
5153
+ minimap: new MarkerMacroSetter(id, "minimap"),
5154
+ light: new MarkerMacroSetter(id, "light"),
5155
+ autoscale: new MarkerMacroSetter(id, "autoscale"),
5156
+ pos: new MarkerMacroSetter(id, "pos", ["x", "y"]),
5157
+ endPos: new MarkerMacroSetter(id, "endPos", ["x", "y"]),
5158
+ drawLayer: new MarkerMacroSetter(id, "drawLayer"),
5159
+ color: new MarkerMacroSetter(id, "color"),
5160
+ radius: new MarkerMacroSetter(id, "radius"),
5161
+ stroke: new MarkerMacroSetter(id, "stroke"),
5162
+ rotation: new MarkerMacroSetter(id, "rotation"),
5163
+ shape: new MarkerMacroSetter(id, "shape", ["sides", "fill", "outline"]),
5164
+ arc: new MarkerMacroSetter(id, "arc", ["start", "end"]),
5165
+ flushText: new MacroFunction((scope, out, options) => {
5166
+ assertIsObjectMacro(options, "options");
5167
+ const [fetch] = assertObjectFields(options, ["fetch"]);
5168
+ return [null, [setmarker(id, "flushText", fetch)]];
5169
+ }),
5170
+ fontSize: new MarkerMacroSetter(id, "fontSize"),
5171
+ textAlign: new MarkerMacroSetter(id, "textAlign"),
5172
+ lineAlign: new MarkerMacroSetter(id, "lineAlign"),
5173
+ textHeight: new MarkerMacroSetter(id, "textHeight"),
5174
+ outline: new MarkerMacroSetter(id, "outline"),
5175
+ labelFlags: new MarkerMacroSetter(id, "labelFlags", [
5176
+ "background",
5177
+ "outline",
5178
+ ]),
5179
+ texture: new MarkerMacroSetter(id, "texture", [], args => ["0", ...args]),
5180
+ flushTexture: new MacroFunction(() => [
5181
+ null,
5182
+ [setmarker(id, "texture", "1")],
5183
+ ]),
5184
+ textureSize: new MarkerMacroSetter(id, "textureSize", [
5185
+ "width",
5186
+ "height",
5187
+ ]),
5188
+ posi: new MarkerMacroSetter(id, "posi", ["index", "x", "y"]),
5189
+ uvi: new MarkerMacroSetter(id, "uvi", ["index", "x", "y"]),
5190
+ colori: new MarkerMacroSetter(id, "colori", ["index", "color"]),
5191
+ });
5192
+ }
5193
+ }
5194
+ class MarkerMacroSetter extends VoidValue {
5195
+ constructor(id, prop, keys = [], modifyArgs) {
5196
+ super();
5197
+ this.id = id;
5198
+ this.prop = prop;
5199
+ this.keys = keys;
5200
+ this.modifyArgs = modifyArgs;
5201
+ }
5202
+ "="(scope, value) {
5203
+ const inst = [];
5204
+ let args = [];
5205
+ if (this.keys.length === 0) {
5206
+ args.push(value);
5207
+ }
5208
+ else {
5209
+ for (const key of this.keys) {
5210
+ const member = pipeInsts(value.get(scope, new LiteralValue(key)), inst);
5211
+ args.push(member);
5212
+ }
5213
+ }
5214
+ if (this.modifyArgs) {
5215
+ args = this.modifyArgs(args);
5216
+ }
5217
+ inst.push(setmarker(this.id, this.prop, ...args));
5218
+ return [value, inst];
5219
+ }
5220
+ }
5221
+ function setmarker(id, prop, ...args) {
5222
+ return new InstructionBase("setmarker", prop, id, ...args);
5223
+ }
5224
+
5225
+ class StringView extends ObjectValue {
5226
+ constructor(string) {
5227
+ super();
5228
+ this.string = string;
5229
+ }
5230
+ get(scope, key, out) {
5231
+ if (super.hasProperty(scope, key))
5232
+ return super.get(scope, key, out);
5233
+ if (key instanceof LiteralValue) {
5234
+ if (key.data === "length") {
5235
+ return this.string.get(scope, key, out);
5236
+ }
5237
+ if (!key.isNumber())
5238
+ throw new CompilerError(`The member [${key.debugString()}] is not present in [${this.debugString()}]`);
5239
+ if (this.string instanceof LiteralValue && this.string.isString()) {
5240
+ const char = this.string.data.charCodeAt(key.data);
5241
+ return [new LiteralValue(char), []];
5242
+ }
5243
+ }
5244
+ const outValue = StoreValue.from(scope, out);
5245
+ return [
5246
+ outValue,
5247
+ [new InstructionBase("read", outValue, this.string, key)],
5248
+ ];
5249
+ }
5250
+ hasProperty(scope, prop) {
5251
+ if ((prop instanceof LiteralValue && prop.isNumber()) ||
5252
+ prop instanceof StoreValue)
5253
+ return true;
5254
+ return super.hasProperty(scope, prop);
5255
+ }
5256
+ debugString() {
5257
+ return `StringView(${this.string.debugString()})`;
5258
+ }
5259
+ toMlogString() {
5260
+ return '"[macro StringView]"';
5261
+ }
5262
+ }
5263
+ class StringViewBuilder extends MacroFunction {
5264
+ constructor() {
5265
+ super((scope, out, string) => {
5266
+ return [new StringView(string), []];
5267
+ });
5268
+ }
5269
+ }
5270
+
4027
5271
  class Asm extends MacroFunction {
4028
5272
  constructor() {
4029
5273
  super((scope, out, stringsArray, ...values) => {
@@ -4055,7 +5299,7 @@ function formatInstructions(args) {
4055
5299
  const segments = arg.split("\n");
4056
5300
  const params = [...buffer, segments[0]];
4057
5301
  if (validateInstructionArgs(params)) {
4058
- instructions.push(new ZeroSpaceInstruction(...params));
5302
+ instructions.push(new AsmInstruction(...params));
4059
5303
  buffer = [];
4060
5304
  }
4061
5305
  // iterates over all segments except the first and last
@@ -4067,18 +5311,21 @@ function formatInstructions(args) {
4067
5311
  const trimmed = segments[i].trim();
4068
5312
  if (trimmed.length == 0)
4069
5313
  continue;
4070
- instructions.push(new ZeroSpaceInstruction(trimmed));
5314
+ instructions.push(new AsmInstruction(trimmed));
4071
5315
  }
4072
5316
  if (segments.length > 1) {
4073
5317
  buffer.push(segments[segments.length - 1]);
4074
5318
  }
4075
5319
  }
4076
5320
  if (buffer.length > 0 && validateInstructionArgs(buffer)) {
4077
- instructions.push(new ZeroSpaceInstruction(...buffer));
5321
+ instructions.push(new AsmInstruction(...buffer));
4078
5322
  }
4079
5323
  return instructions;
4080
5324
  }
4081
- /** Determines if an asm line should be generated and trims it at the start and end */
5325
+ /**
5326
+ * Determines if an asm line should be generated and trims it at the start and
5327
+ * end
5328
+ */
4082
5329
  function validateInstructionArgs(args) {
4083
5330
  if (args.length === 0)
4084
5331
  return false;
@@ -4097,11 +5344,29 @@ function validateInstructionArgs(args) {
4097
5344
  args[args.length - 1] = last.trimEnd();
4098
5345
  return true;
4099
5346
  }
5347
+ class AsmInstruction extends InstructionBase {
5348
+ constructor(...args) {
5349
+ super(...args);
5350
+ const [first] = args;
5351
+ if (typeof first === "string") {
5352
+ this.ignoredByParser = /^\s*(#|\S+:)/.test(first);
5353
+ }
5354
+ }
5355
+ toString() {
5356
+ return formatInstructionArgs(this.args).join("");
5357
+ }
5358
+ }
4100
5359
 
4101
5360
  /**
4102
- * Adds all the compiler globals to `scope`
5361
+ * Creates the global scope of the user's script, contains all built-ins that
5362
+ * are not privileged
4103
5363
  */
4104
- function initScope(scope) {
5364
+ function createGlobalScope() {
5365
+ const scope = new Scope({
5366
+ builtInModules: {
5367
+ [worldModuleName]: createWordModule(),
5368
+ },
5369
+ });
4105
5370
  scope.hardSet("undefined", new LiteralValue(null));
4106
5371
  // namespaces
4107
5372
  scope.hardSet("ControlKind", new NamespaceMacro());
@@ -4112,19 +5377,30 @@ function initScope(scope) {
4112
5377
  scope.hardSet("Units", new NamespaceMacro({ changeCasing: true }));
4113
5378
  scope.hardSet("LAccess", new NamespaceMacro());
4114
5379
  scope.hardSet("Blocks", new NamespaceMacro({ changeCasing: true }));
5380
+ scope.hardSet("Colors", new ColorsNamespace());
5381
+ scope.hardSet("StatusEffects", new StatusEffectsNamespace());
4115
5382
  // helper methods
4116
5383
  scope.hardSet("getBuilding", new GetGlobal(EMutability.constant));
5384
+ scope.hardSet("getBuildings", new GetBuildings());
4117
5385
  scope.hardSet("getVar", new GetGlobal(EMutability.mutable));
5386
+ scope.hardSet("getColor", new GetColor());
4118
5387
  scope.hardSet("concat", new Concat());
4119
5388
  scope.hardSet("asm", new Asm());
4120
5389
  scope.hardSet("Math", new MlogMath());
4121
5390
  scope.hardSet("Memory", new MemoryBuilder());
5391
+ scope.hardSet("MemoryView", new MemoryBuilder());
5392
+ scope.hardSet("StringView", new StringViewBuilder());
4122
5393
  scope.hardSet("MutableArray", new DynamicArrayConstructor(false));
4123
5394
  scope.hardSet("DynamicArray", new DynamicArrayConstructor(true));
4124
5395
  scope.hardSet("unchecked", new Unchecked());
5396
+ scope.hardSet("Align", new NamespaceMacro());
5397
+ scope.hardSet("Weathers", new NamespaceMacro({ changeCasing: true }));
5398
+ scope.hardSet("Sounds", new SoundsNamespace());
4125
5399
  // commands
4126
5400
  scope.hardSet("draw", new Draw());
4127
5401
  scope.hardSet("print", new Print());
5402
+ scope.hardSet("format", new Format());
5403
+ scope.hardSet("printChar", new PrintChar());
4128
5404
  scope.hardSet("printFlush", new PrintFlush());
4129
5405
  scope.hardSet("drawFlush", new DrawFlush());
4130
5406
  scope.hardSet("getLink", new GetLink());
@@ -4134,33 +5410,50 @@ function initScope(scope) {
4134
5410
  scope.hardSet("wait", new Wait());
4135
5411
  scope.hardSet("lookup", new Lookup());
4136
5412
  scope.hardSet("packColor", new PackColor());
5413
+ scope.hardSet("unpackColor", new UnpackColor());
4137
5414
  scope.hardSet("endScript", new End());
4138
5415
  scope.hardSet("stopScript", new Stop());
4139
5416
  scope.hardSet("unitBind", new UnitBind());
4140
5417
  scope.hardSet("unitControl", new UnitControl());
4141
5418
  scope.hardSet("unitRadar", new UnitRadar());
4142
5419
  scope.hardSet("unitLocate", new UnitLocate());
4143
- // world processor commands
4144
- scope.hardSet("getBlock", new GetBlock());
4145
- scope.hardSet("setBlock", new SetBlock());
4146
- scope.hardSet("spawnUnit", new SpawnUnit());
4147
- scope.hardSet("applyStatus", new ApplyStatus());
4148
- scope.hardSet("spawnWave", new SpawnWave());
4149
- scope.hardSet("setRule", new SetRule());
4150
- scope.hardSet("flushMessage", new FlushMessage());
4151
- scope.hardSet("cutscene", new Cutscene());
4152
- scope.hardSet("explosion", new Explosion());
4153
5420
  scope.hardSet("setRate", new SetRate());
4154
- scope.hardSet("fetch", new Fetch());
4155
- scope.hardSet("getFlag", new GetFlag());
4156
- scope.hardSet("setFlag", new SetFlag());
5421
+ return scope;
5422
+ }
5423
+ function createWordModule() {
5424
+ const module = new ObjectValue({
5425
+ PVars: new PVarsNamespace(),
5426
+ getBlock: new GetBlock(),
5427
+ setBlock: new SetBlock(),
5428
+ spawnUnit: new SpawnUnit(),
5429
+ spawnBullet: new SpawnBullet(),
5430
+ applyStatus: new ApplyStatus(),
5431
+ spawnWave: new SpawnWave(),
5432
+ setRule: new SetRule(),
5433
+ flushMessage: new FlushMessage(),
5434
+ cutscene: new Cutscene(),
5435
+ explosion: new Explosion(),
5436
+ setRate: new SetRate(),
5437
+ fetch: new Fetch(),
5438
+ getFlag: new GetFlag(),
5439
+ setFlag: new SetFlag(),
5440
+ setProp: new SetProp(),
5441
+ SyncLock: new SyncLockConstructor(),
5442
+ effect: new Effect(),
5443
+ localePrint: new LocalePrint(),
5444
+ Marker: new MarkerConstructor(),
5445
+ senseWeather: new WeatherSense(),
5446
+ setWeather: new WeatherSet(),
5447
+ playSound: new PlaySound(),
5448
+ playMusic: new PlayMusic(),
5449
+ query: new Query(),
5450
+ });
5451
+ return module;
4157
5452
  }
4158
5453
 
4159
5454
  class Compiler {
4160
- constructor({ stackName, compactNames = false, sourcemap = false, } = {}) {
5455
+ constructor({ compactNames = false, sourcemap = false, } = {}) {
4161
5456
  this.handlers = handlers;
4162
- this.usingStack = !!stackName;
4163
- this.stackName = stackName;
4164
5457
  this.compactNames = compactNames;
4165
5458
  this.sourcemap = sourcemap;
4166
5459
  }
@@ -4169,8 +5462,10 @@ class Compiler {
4169
5462
  let sourcemaps;
4170
5463
  try {
4171
5464
  const program = this.parse(script);
4172
- const scope = new Scope({});
4173
- initScope(scope);
5465
+ const globalScope = createGlobalScope();
5466
+ // this allows the user to shadow global variables inside
5467
+ // the script, since it is treated as a module
5468
+ const scope = globalScope.createScope();
4174
5469
  const valueInst = this.handle(scope, program);
4175
5470
  if (needsEndInstruction(valueInst[1], scope)) {
4176
5471
  valueInst[1].push(new EndInstruction());
@@ -4193,18 +5488,20 @@ class Compiler {
4193
5488
  let i = 0;
4194
5489
  for (const inst of valueInst[1]) {
4195
5490
  inst.resolve(i);
4196
- if (!inst.hidden)
5491
+ if (!inst.hidden && !inst.ignoredByParser)
4197
5492
  i++;
4198
5493
  }
4199
5494
  }
4200
5495
  serialize(resLines) {
4201
5496
  const [, inst] = resLines;
5497
+ // eslint-disable-next-line @typescript-eslint/no-base-to-string
4202
5498
  return inst.filter(l => !l.hidden).join("\n");
4203
5499
  }
4204
5500
  parse(script) {
4205
- return parser.parse(script, {
5501
+ return parse(script, {
4206
5502
  ranges: true,
4207
5503
  plugins: ["typescript"],
5504
+ sourceType: "module",
4208
5505
  });
4209
5506
  }
4210
5507
  mapSources(valueInst) {
@@ -4215,11 +5512,11 @@ class Compiler {
4215
5512
  }
4216
5513
  return result;
4217
5514
  }
4218
- handle(scope, node, handler = this.handlers[node.type], out) {
5515
+ handle(scope, node, handler = this.handlers[node.type], out, arg) {
4219
5516
  try {
4220
5517
  if (!handler)
4221
5518
  throw new CompilerError("Missing handler for " + node.type);
4222
- const result = handler(this, scope, node, out, null);
5519
+ const result = handler(this, scope, node, out, arg);
4223
5520
  if (this.sourcemap)
4224
5521
  return appendSourceLocations(result, node);
4225
5522
  return result;
@@ -4232,14 +5529,14 @@ class Compiler {
4232
5529
  }
4233
5530
  }
4234
5531
  /** 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);
5532
+ handleValue(scope, node, handler = this.handlers[node.type], out, arg) {
5533
+ const result = this.handle(scope, node, handler, out, arg);
4237
5534
  if (!result[0])
4238
5535
  throw new CompilerError(`This node (${node.type}) did not return a value`, node);
4239
5536
  return result;
4240
5537
  }
4241
- handleEval(scope, node, out) {
4242
- const [res, inst] = this.handleValue(scope, node, undefined, out);
5538
+ handleEval(scope, node, out, arg) {
5539
+ const [res, inst] = this.handleValue(scope, node, undefined, out, arg);
4243
5540
  const [evaluated, evaluatedInst] = res.eval(scope, out);
4244
5541
  const result = [evaluated, [...inst, ...evaluatedInst]];
4245
5542
  if (this.sourcemap)
@@ -4249,12 +5546,10 @@ class Compiler {
4249
5546
  /**
4250
5547
  * Handles many nodes in order.
4251
5548
  *
4252
- * The usage of this method over a regular loop over an array of nodes
4253
- * is only required if the code inside the loop generates
4254
- * instructions that are not tracked by the compiler
4255
- * handler methods ({@link handle}, {@link handleEval}
5549
+ * The usage of this method over a regular loop over an array of nodes is only
5550
+ * required if the code inside the loop generates instructions that are not
5551
+ * tracked by the compiler handler methods ({@link handle}, {@link handleEval}
4256
5552
  * and {@link handleMany})
4257
- *
4258
5553
  */
4259
5554
  handleMany(scope, nodes, handler) {
4260
5555
  const lines = [];
@@ -4285,5 +5580,4 @@ function needsEndInstruction(inst, scope) {
4285
5580
  return scope.inst.length > 0;
4286
5581
  }
4287
5582
 
4288
- exports.Compiler = Compiler;
4289
- exports.CompilerError = CompilerError;
5583
+ export { Compiler as C, CompilerError as a };