mlogjs 0.2.2 → 0.3.0-beta.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 (188) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +2 -2
  3. package/build/Compiler.js +105 -0
  4. package/build/Compiler.js.map +1 -0
  5. package/build/CompilerError.js +11 -0
  6. package/build/CompilerError.js.map +1 -0
  7. package/build/Scope.js +71 -0
  8. package/build/Scope.js.map +1 -0
  9. package/build/bin.js +75 -0
  10. package/build/bin.js.map +1 -0
  11. package/build/handlers/BlockStatement.js +14 -0
  12. package/build/handlers/BlockStatement.js.map +1 -0
  13. package/build/handlers/CallExpression.js +20 -0
  14. package/build/handlers/CallExpression.js.map +1 -0
  15. package/build/handlers/File.js +9 -0
  16. package/build/handlers/File.js.map +1 -0
  17. package/build/handlers/ForStatement.js +32 -0
  18. package/build/handlers/ForStatement.js.map +1 -0
  19. package/build/handlers/Function.js +47 -0
  20. package/build/handlers/Function.js.map +1 -0
  21. package/build/handlers/Identifier.js +8 -0
  22. package/build/handlers/Identifier.js.map +1 -0
  23. package/build/handlers/IfStatement.js +25 -0
  24. package/build/handlers/IfStatement.js.map +1 -0
  25. package/build/handlers/Literal.js +15 -0
  26. package/build/handlers/Literal.js.map +1 -0
  27. package/build/handlers/Object.js +55 -0
  28. package/build/handlers/Object.js.map +1 -0
  29. package/build/handlers/OperationExpressions.js +38 -0
  30. package/build/handlers/OperationExpressions.js.map +1 -0
  31. package/build/handlers/Program.js +9 -0
  32. package/build/handlers/Program.js.map +1 -0
  33. package/build/handlers/Statements.js +30 -0
  34. package/build/handlers/Statements.js.map +1 -0
  35. package/build/handlers/TemplateExpression.js +18 -0
  36. package/build/handlers/TemplateExpression.js.map +1 -0
  37. package/build/handlers/VariableDeclaration.js +72 -0
  38. package/build/handlers/VariableDeclaration.js.map +1 -0
  39. package/build/handlers/WhileStatement.js +23 -0
  40. package/build/handlers/WhileStatement.js.map +1 -0
  41. package/build/handlers/index.js +32 -0
  42. package/build/handlers/index.js.map +1 -0
  43. package/build/index.js +8 -0
  44. package/build/index.js.map +1 -0
  45. package/build/initScope.js +40 -0
  46. package/build/initScope.js.map +1 -0
  47. package/build/instructions/AddressResolver.js +31 -0
  48. package/build/instructions/AddressResolver.js.map +1 -0
  49. package/build/instructions/ControlInstructions.js +23 -0
  50. package/build/instructions/ControlInstructions.js.map +1 -0
  51. package/build/instructions/EndInstruction.js +11 -0
  52. package/build/instructions/EndInstruction.js.map +1 -0
  53. package/build/instructions/InstructionBase.js +21 -0
  54. package/build/instructions/InstructionBase.js.map +1 -0
  55. package/build/instructions/JumpInstruction.js +22 -0
  56. package/build/instructions/JumpInstruction.js.map +1 -0
  57. package/build/instructions/OperationInstruction.js +11 -0
  58. package/build/instructions/OperationInstruction.js.map +1 -0
  59. package/build/instructions/SetCounterInstruction.js +11 -0
  60. package/build/instructions/SetCounterInstruction.js.map +1 -0
  61. package/build/instructions/SetInstruction.js +11 -0
  62. package/build/instructions/SetInstruction.js.map +1 -0
  63. package/build/instructions/ZeroSpaceInstruction.js +11 -0
  64. package/build/instructions/ZeroSpaceInstruction.js.map +1 -0
  65. package/build/instructions/index.js +26 -0
  66. package/build/instructions/index.js.map +1 -0
  67. package/build/macros/Building.js +89 -0
  68. package/build/macros/Building.js.map +1 -0
  69. package/build/macros/Function.js +20 -0
  70. package/build/macros/Function.js.map +1 -0
  71. package/build/macros/Math.js +62 -0
  72. package/build/macros/Math.js.map +1 -0
  73. package/build/macros/Memory.js +63 -0
  74. package/build/macros/Memory.js.map +1 -0
  75. package/build/macros/Namespace.js +111 -0
  76. package/build/macros/Namespace.js.map +1 -0
  77. package/build/macros/Other.js +21 -0
  78. package/build/macros/Other.js.map +1 -0
  79. package/build/macros/RawValue.js +17 -0
  80. package/build/macros/RawValue.js.map +1 -0
  81. package/build/macros/commands/Control.js +26 -0
  82. package/build/macros/commands/Control.js.map +1 -0
  83. package/build/macros/commands/Draw.js +40 -0
  84. package/build/macros/commands/Draw.js.map +1 -0
  85. package/build/macros/commands/DrawFlush.js +18 -0
  86. package/build/macros/commands/DrawFlush.js.map +1 -0
  87. package/build/macros/commands/GetLink.js +25 -0
  88. package/build/macros/commands/GetLink.js.map +1 -0
  89. package/build/macros/commands/Lookup.js +25 -0
  90. package/build/macros/commands/Lookup.js.map +1 -0
  91. package/build/macros/commands/Print.js +18 -0
  92. package/build/macros/commands/Print.js.map +1 -0
  93. package/build/macros/commands/PrintFlush.js +18 -0
  94. package/build/macros/commands/PrintFlush.js.map +1 -0
  95. package/build/macros/commands/Radar.js +65 -0
  96. package/build/macros/commands/Radar.js.map +1 -0
  97. package/build/macros/commands/Sensor.js +21 -0
  98. package/build/macros/commands/Sensor.js.map +1 -0
  99. package/build/macros/commands/UnitBind.js +18 -0
  100. package/build/macros/commands/UnitBind.js.map +1 -0
  101. package/build/macros/commands/UnitControl.js +73 -0
  102. package/build/macros/commands/UnitControl.js.map +1 -0
  103. package/build/macros/commands/UnitLocate.js +76 -0
  104. package/build/macros/commands/UnitLocate.js.map +1 -0
  105. package/build/macros/commands/UnitRadar.js +48 -0
  106. package/build/macros/commands/UnitRadar.js.map +1 -0
  107. package/build/macros/commands/Wait.js +19 -0
  108. package/build/macros/commands/Wait.js.map +1 -0
  109. package/build/macros/commands/index.js +33 -0
  110. package/build/macros/commands/index.js.map +1 -0
  111. package/build/macros/index.js +40 -0
  112. package/build/macros/index.js.map +1 -0
  113. package/build/operators.js +102 -0
  114. package/build/operators.js.map +1 -0
  115. package/build/types.js +28 -0
  116. package/build/types.js.map +1 -0
  117. package/build/utils.js +18 -0
  118. package/build/utils.js.map +1 -0
  119. package/build/values/BaseValue.js +112 -0
  120. package/build/values/BaseValue.js.map +1 -0
  121. package/build/values/FunctionValue.js +116 -0
  122. package/build/values/FunctionValue.js.map +1 -0
  123. package/build/values/LiteralValue.js +89 -0
  124. package/build/values/LiteralValue.js.map +1 -0
  125. package/build/values/ObjectValue.js +55 -0
  126. package/build/values/ObjectValue.js.map +1 -0
  127. package/build/values/StoreValue.js +32 -0
  128. package/build/values/StoreValue.js.map +1 -0
  129. package/build/values/TempValue.js +78 -0
  130. package/build/values/TempValue.js.map +1 -0
  131. package/build/values/VoidValue.js +32 -0
  132. package/build/values/VoidValue.js.map +1 -0
  133. package/build/values/index.js +23 -0
  134. package/build/values/index.js.map +1 -0
  135. package/dist/bin.cjs +17 -4
  136. package/dist/bin.cjs.map +1 -0
  137. package/dist/index.cjs +940 -570
  138. package/dist/index.cjs.map +1 -0
  139. package/dist/index.mjs +918 -569
  140. package/dist/index.mjs.map +1 -0
  141. package/dist/types/Compiler.d.ts +11 -3
  142. package/dist/types/CompilerError.d.ts +9 -0
  143. package/dist/types/Scope.d.ts +10 -9
  144. package/dist/types/handlers/File.d.ts +2 -0
  145. package/dist/types/handlers/Function.d.ts +1 -0
  146. package/dist/types/handlers/Literal.d.ts +4 -1
  147. package/dist/types/handlers/Object.d.ts +2 -0
  148. package/dist/types/handlers/OperationExpressions.d.ts +1 -0
  149. package/dist/types/handlers/Typescript.d.ts +7 -0
  150. package/dist/types/handlers/VariableDeclaration.d.ts +2 -2
  151. package/dist/types/handlers/WhileStatement.d.ts +1 -0
  152. package/dist/types/handlers/index.d.ts +2 -0
  153. package/dist/types/index.d.ts +2 -1
  154. package/dist/types/initScope.d.ts +3 -0
  155. package/dist/types/instructions/ControlInstructions.d.ts +0 -3
  156. package/dist/types/instructions/InstructionBase.d.ts +2 -2
  157. package/dist/types/instructions/SetInstruction.d.ts +1 -0
  158. package/dist/types/macros/Building.d.ts +1 -2
  159. package/dist/types/macros/Entities.d.ts +11 -0
  160. package/dist/types/macros/Function.d.ts +2 -1
  161. package/dist/types/macros/GetGlobal.d.ts +5 -0
  162. package/dist/types/macros/Namespace.d.ts +0 -5
  163. package/dist/types/macros/commands/End.d.ts +5 -0
  164. package/dist/types/macros/commands/index.d.ts +1 -0
  165. package/dist/types/macros/index.d.ts +0 -1
  166. package/dist/types/operators.d.ts +1 -1
  167. package/dist/types/types.d.ts +121 -12
  168. package/dist/types/utils.d.ts +22 -1
  169. package/dist/types/values/BaseValue.d.ts +3 -1
  170. package/dist/types/values/DestructuringValue.d.ts +17 -0
  171. package/dist/types/values/FunctionValue.d.ts +21 -8
  172. package/dist/types/values/LiteralValue.d.ts +2 -1
  173. package/dist/types/values/ObjectValue.d.ts +5 -1
  174. package/dist/types/values/SenseableValue.d.ts +23 -0
  175. package/dist/types/values/StackValue.d.ts +16 -0
  176. package/dist/types/values/StoreValue.d.ts +7 -3
  177. package/dist/types/values/TempValue.d.ts +9 -2
  178. package/dist/types/values/ValueOwner.d.ts +27 -0
  179. package/dist/types/values/VoidValue.d.ts +8 -5
  180. package/dist/types/values/index.d.ts +2 -1
  181. package/lib/commands.d.ts +242 -36
  182. package/lib/globals.d.ts +172 -159
  183. package/lib/helpers.d.ts +2 -2
  184. package/lib/memory.d.ts +1 -1
  185. package/lib/traits.d.ts +130 -109
  186. package/package.json +23 -18
  187. package/build/bin.cjs +0 -69
  188. package/doc/README.md +0 -9
package/dist/index.cjs CHANGED
@@ -2,7 +2,35 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var acorn = require('acorn');
5
+ var parser = require('@babel/parser');
6
+ var es = require('@babel/types');
7
+
8
+ function _interopNamespace(e) {
9
+ if (e && e.__esModule) return e;
10
+ var n = Object.create(null);
11
+ if (e) {
12
+ Object.keys(e).forEach(function (k) {
13
+ if (k !== 'default') {
14
+ var d = Object.getOwnPropertyDescriptor(e, k);
15
+ Object.defineProperty(n, k, d.get ? d : {
16
+ enumerable: true,
17
+ get: function () { return e[k]; }
18
+ });
19
+ }
20
+ });
21
+ }
22
+ n["default"] = e;
23
+ return Object.freeze(n);
24
+ }
25
+
26
+ var es__namespace = /*#__PURE__*/_interopNamespace(es);
27
+
28
+ class CompilerError extends Error {
29
+ constructor(message, nodeStack = []) {
30
+ super(message);
31
+ this.nodeStack = nodeStack;
32
+ }
33
+ }
6
34
 
7
35
  const arithmeticBinaryOperators = [
8
36
  "*",
@@ -77,46 +105,29 @@ const unaryOperators = [
77
105
  ];
78
106
  const updateOperators = ["++", "--"];
79
107
  const singleOperators = [...unaryOperators, ...updateOperators];
80
- const operators = [...leftRightOperators, ...singleOperators];
81
- const operatorMap$2 = {
82
- "==": "equal",
83
- "===": "strictEqual",
84
- "!=": "notEqual",
85
- "!==": "notEqual",
86
- "<": "lessThan",
87
- ">": "greaterThan",
88
- "<=": "lessThanEq",
89
- ">=": "greaterThanEq",
90
- "+": "add",
91
- "-": "sub",
92
- "*": "mul",
93
- "/": "div",
94
- "%": "mod",
95
- "**": "pow",
96
- "|": "or",
97
- "&": "and",
98
- "^": "xor",
99
- ">>": "shr",
100
- ">>>": "shr",
101
- "<<": "shl",
102
- "&&": "land",
103
- "||": "or",
104
- };
108
+ const operators = [...leftRightOperators, ...singleOperators];
105
109
 
106
110
  class VoidValue {
107
111
  constructor(scope) {
112
+ this.owner = null;
108
113
  this.constant = false;
109
114
  this.macro = false;
110
115
  this.scope = scope;
111
116
  }
112
- eval(scope) {
113
- throw new Error(`${this} cannot eval.`);
117
+ eval(_scope) {
118
+ throw new CompilerError(`${this} cannot eval.`);
114
119
  }
115
- call(scope, args) {
116
- throw new Error(`${this} cannot call.`);
120
+ consume(_scope) {
121
+ throw new CompilerError(`${this} cannot be consumed.`);
117
122
  }
118
- get(scope, name) {
119
- throw new Error(`${this} cannot get.`);
123
+ call(_scope, _args) {
124
+ throw new CompilerError(`${this} cannot call.`);
125
+ }
126
+ get(_scope, _name) {
127
+ throw new CompilerError(`${this} cannot get.`);
128
+ }
129
+ ensureOwned() {
130
+ throw new CompilerError(`${this} cannot be owned`);
120
131
  }
121
132
  toString() {
122
133
  return "void";
@@ -125,7 +136,7 @@ class VoidValue {
125
136
  for (const key of operators) {
126
137
  VoidValue.prototype[key] = function () {
127
138
  console.log(this);
128
- throw new Error(`${this} cannot '${key}' operation.`);
139
+ throw new CompilerError(`${this} cannot '${key}' operation.`);
129
140
  };
130
141
  }
131
142
 
@@ -140,7 +151,7 @@ class InstructionBase {
140
151
  set hidden(value) {
141
152
  this._hidden = value;
142
153
  }
143
- resolve(i) { }
154
+ resolve(_i) { }
144
155
  toString() {
145
156
  return this.args.filter(arg => arg).join(" ");
146
157
  }
@@ -156,6 +167,9 @@ class SetInstruction extends InstructionBase {
156
167
  constructor(store, value) {
157
168
  super("set", store, value);
158
169
  }
170
+ get hidden() {
171
+ return this._hidden || !this.args[1].owner;
172
+ }
159
173
  }
160
174
 
161
175
  class EndInstruction extends InstructionBase {
@@ -228,17 +242,44 @@ class ContinueInstruction extends JumpInstruction {
228
242
  constructor(address) {
229
243
  super(address, EJumpKind.Always);
230
244
  }
231
- }
232
- class ReturnInstruction extends JumpInstruction {
233
- constructor(address) {
234
- super(address, EJumpKind.Always);
245
+ }
246
+
247
+ class ValueOwner {
248
+ /**
249
+ * Creates a new `ValueOwner`, if `value` is already owned,
250
+ * `this` will be aliased to `value`, else `this` will own `value`
251
+ */
252
+ constructor({ scope, constant = false, value, identifier, name, }) {
253
+ this.scope = scope;
254
+ this.constant = constant;
255
+ this.value = value;
256
+ this.identifier = identifier;
257
+ this.name = name ?? scope.makeTempName();
258
+ this.persistent = !!name;
259
+ this.owned = new Set();
260
+ if (!value.owner?.persistent)
261
+ this.own(value);
262
+ }
263
+ own(target) {
264
+ target.owner = this;
265
+ this.owned.add(target);
266
+ }
267
+ replace(target) {
268
+ this.value.owner = null;
269
+ this.owned.delete(this.value);
270
+ this.value = target;
271
+ this.own(target);
272
+ }
273
+ moveInto(owner) {
274
+ this.owned.forEach(value => owner.own(value));
275
+ this.owned.clear();
235
276
  }
236
277
  }
237
278
 
238
279
  class BaseValue extends VoidValue {
239
280
  "u-"(scope) {
240
- const [that, inst] = this.eval(scope);
241
- const temp = new TempValue(scope);
281
+ const [that, inst] = this.consume(scope);
282
+ const temp = new StoreValue(scope);
242
283
  return [
243
284
  temp,
244
285
  [
@@ -247,6 +288,13 @@ class BaseValue extends VoidValue {
247
288
  ],
248
289
  ];
249
290
  }
291
+ "u+"(scope) {
292
+ // TODO: should it return 0 + this ?
293
+ return this.eval(scope);
294
+ }
295
+ ensureOwned() {
296
+ this.owner ??= new ValueOwner({ scope: this.scope, value: this });
297
+ }
250
298
  }
251
299
  const operatorMap$1 = {
252
300
  "==": "equal",
@@ -275,9 +323,9 @@ const operatorMap$1 = {
275
323
  for (const key in operatorMap$1) {
276
324
  const kind = operatorMap$1[key];
277
325
  BaseValue.prototype[key] = function (scope, value) {
278
- const [left, leftInst] = this.eval(scope);
279
- const [right, rightInst] = value.eval(scope);
280
- const temp = new TempValue(scope);
326
+ const [left, leftInst] = this.consume(scope);
327
+ const [right, rightInst] = value.consume(scope);
328
+ const temp = new StoreValue(scope);
281
329
  return [
282
330
  temp,
283
331
  [
@@ -295,8 +343,8 @@ const unaryOperatorMap$1 = {
295
343
  for (const key in unaryOperatorMap$1) {
296
344
  const name = unaryOperatorMap$1[key];
297
345
  BaseValue.prototype[key] = function (scope) {
298
- const [that, inst] = this.eval(scope);
299
- const temp = new TempValue(scope);
346
+ const [that, inst] = this.consume(scope);
347
+ const temp = new StoreValue(scope);
300
348
  return [temp, [...inst, new OperationInstruction(name, temp, that, null)]];
301
349
  };
302
350
  }
@@ -318,6 +366,7 @@ const assignmentToBinary = {
318
366
  };
319
367
  for (const op in assignmentToBinary) {
320
368
  BaseValue.prototype[op] = function (scope, value) {
369
+ this.ensureOwned();
321
370
  const [opValue, opInst] = this[assignmentToBinary[op]](scope, value);
322
371
  const [retValue, retInst] = this["="](scope, opValue);
323
372
  return [retValue, [...opInst, ...retInst]];
@@ -325,9 +374,9 @@ for (const op in assignmentToBinary) {
325
374
  }
326
375
  for (const key of updateOperators) {
327
376
  BaseValue.prototype[key] = function (scope, prefix) {
328
- let [ret, inst] = this.eval(scope);
377
+ let [ret, inst] = this.consume(scope);
329
378
  if (!prefix) {
330
- const temp = new TempValue(scope);
379
+ const temp = new StoreValue(scope);
331
380
  const [tempValue, tempInst] = temp["="](scope, ret);
332
381
  ret = tempValue;
333
382
  inst.push(...tempInst);
@@ -343,7 +392,7 @@ for (const key of updateOperators) {
343
392
  const literalMethods = {
344
393
  length: function (scope) {
345
394
  if (typeof this.data !== "string")
346
- throw new Error("Length method only works on string literal values.");
395
+ throw new CompilerError("Length method only works on string literal values.");
347
396
  return new LiteralValue(scope, this.data.length);
348
397
  },
349
398
  };
@@ -353,7 +402,10 @@ class LiteralValue extends BaseValue {
353
402
  this.constant = true;
354
403
  this.data = data;
355
404
  }
356
- eval(scope) {
405
+ eval(_scope) {
406
+ return [this, []];
407
+ }
408
+ consume(_scope) {
357
409
  return [this, []];
358
410
  }
359
411
  toString() {
@@ -364,7 +416,7 @@ class LiteralValue extends BaseValue {
364
416
  return super.get(scope, name);
365
417
  const method = literalMethods[name.data];
366
418
  if (!method)
367
- throw new Error(`Method ${name.data} does not exist on literal values.`);
419
+ throw new CompilerError(`Method ${name.data} does not exist on literal values.`);
368
420
  return [method.apply(this, [scope]), []];
369
421
  }
370
422
  get num() {
@@ -423,87 +475,94 @@ for (const key in unaryOperatorMap) {
423
475
  };
424
476
  }
425
477
 
478
+ /**
479
+ * The prefix for internal variables inside the compiler output
480
+ */
481
+ const internalPrefix = "&";
482
+ const discardedName = `${internalPrefix}_`;
483
+ /**
484
+ * Returns a string that has the format: [line]:[column].
485
+ *
486
+ * If `name` is provided, and it is a string, the resulting string will
487
+ * have the format: [name]:[line]:[column].
488
+ */
489
+ function nodeName(node, name) {
490
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
491
+ const { line, column } = node.loc.start;
492
+ if (typeof name === "string")
493
+ return `${name}:${line}:${column}`;
494
+ return `${line}:${column}`;
495
+ }
496
+ /**
497
+ * Converts a camel case string into a dash case one.
498
+ *
499
+ * As an example `camelCase` becomes `camel-case`
500
+ * @example
501
+ * ```
502
+ * camelToDashCase("camelCase") // returns "camel-case"
503
+ * ```
504
+ */
505
+ function camelToDashCase(name) {
506
+ return name.replace(/[A-Z]/g, str => `-${str.toLowerCase()}`);
507
+ }
508
+ const itemNames = [
509
+ "copper",
510
+ "lead",
511
+ "metaglass",
512
+ "graphite",
513
+ "sand",
514
+ "coal",
515
+ "titanium",
516
+ "thorium",
517
+ "scrap",
518
+ "silicon",
519
+ "plastanium",
520
+ "phaseFabric",
521
+ "surgeAlloy",
522
+ "sporePod",
523
+ "blastCompound",
524
+ "pyratite",
525
+ ];
526
+
527
+ /**
528
+ * `StoreValue` represents values unknown at compile time,
529
+ * mostly used with mutable variables and temporary values
530
+ */
426
531
  class StoreValue extends BaseValue {
427
- constructor(scope, name) {
532
+ constructor(scope) {
428
533
  super(scope);
429
534
  this.constant = false;
430
- this.name = name;
431
535
  }
432
536
  typeof(scope) {
433
537
  return [new LiteralValue(scope, "store"), []];
434
538
  }
435
539
  "="(scope, value) {
436
540
  if (this.constant)
437
- throw new Error(`Cannot assign to unmutable store '${this.name}'.`);
438
- if (value instanceof TempValue && value.canProxy)
439
- return value.proxy(this);
440
- const [evalValue, evalInst] = value.eval(scope);
541
+ throw new CompilerError(`Cannot assign to immutable store '${this}'.`);
542
+ if (this.owner) {
543
+ if (this.owner === value.owner && value instanceof StoreValue)
544
+ return [this, []];
545
+ if (!value.owner?.persistent) {
546
+ if (!value.owner)
547
+ this.owner.own(value);
548
+ else
549
+ value.owner.moveInto(this.owner);
550
+ if (value instanceof StoreValue)
551
+ return [this, []];
552
+ }
553
+ }
554
+ const [evalValue, evalInst] = value.consume(scope);
441
555
  return [this, [...evalInst, new SetInstruction(this, evalValue)]];
442
556
  }
443
- eval(scope) {
557
+ eval(_scope) {
444
558
  return [this, []];
445
559
  }
446
- toString() {
447
- return this.name;
448
- }
449
- }
450
-
451
- class TempValue extends StoreValue {
452
- constructor(scope, name) {
453
- super(scope, name ?? "t" + scope.ntemp + (scope.name ? ":" + scope.name : ""));
454
- this.canProxy = true;
455
- if (!name)
456
- scope.ntemp++;
457
- }
458
- eval(scope) {
459
- return super.eval(scope);
460
- }
461
- "="(scope, value) {
462
- if (this.proxied) {
463
- this.noProxy();
464
- return [this, [new SetInstruction(this, value)]];
465
- }
466
- if (value instanceof LiteralValue && this.canProxy) {
467
- return this.proxy(value);
468
- }
469
- return super["="](scope, value);
560
+ consume(_scope) {
561
+ this.ensureOwned();
562
+ return [this, []];
470
563
  }
471
- proxy(value) {
472
- if (!this.canProxy) {
473
- console.log(this.proxied);
474
- throw new Error("Cannot proxy (canProxy = false).");
475
- }
476
- if (this.proxied)
477
- throw new Error("Cannot proxy multiple times.");
478
- this.proxied = value;
479
- this.canProxy = false;
480
- for (const key of [
481
- ...operators,
482
- "eval",
483
- "get",
484
- "call",
485
- "toString",
486
- "proxy",
487
- ]) {
488
- if (key !== "=" && key in value)
489
- this[key] = (...args) => value[key].apply(value, args);
490
- }
491
- this.setInst = new SetInstruction(this, value);
492
- this.setInst.hidden = true;
493
- return [this, [this.setInst]];
494
- }
495
- noProxy() {
496
- this.setInst.hidden = false;
497
- for (const key of [
498
- ...operators,
499
- "eval",
500
- "get",
501
- "call",
502
- "toString",
503
- "proxy",
504
- ]) {
505
- this[key] = TempValue.prototype[key];
506
- }
564
+ toString() {
565
+ return this.owner?.name ?? discardedName;
507
566
  }
508
567
  }
509
568
 
@@ -514,19 +573,30 @@ class ObjectValue extends VoidValue {
514
573
  this.macro = true;
515
574
  this.data = data;
516
575
  }
576
+ static fromArray(scope, items) {
577
+ const data = {
578
+ length: new LiteralValue(scope, items.length),
579
+ };
580
+ items.forEach((item, i) => {
581
+ if (item)
582
+ data[i] = item;
583
+ });
584
+ return new ObjectValue(scope, data);
585
+ }
517
586
  typeof(scope) {
518
587
  return [new LiteralValue(scope, "object"), []];
519
588
  }
520
589
  get(scope, key) {
521
590
  // avoids naming collisions with keys like
522
591
  // constructor or toString
523
- if (this.data.hasOwnProperty(key.data)) {
592
+ if (Object.prototype.hasOwnProperty.call(this.data, key.data)) {
593
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
524
594
  const member = this.data[key.data];
525
595
  return [member, []];
526
596
  }
527
597
  const { $get } = this.data;
528
598
  if (!$get)
529
- throw new Error("Cannot get undefined member.");
599
+ throw new CompilerError("Cannot get undefined member.");
530
600
  return $get.call(scope, [key]);
531
601
  }
532
602
  eval(scope) {
@@ -535,23 +605,115 @@ class ObjectValue extends VoidValue {
535
605
  return [this, []];
536
606
  return $eval.call(scope, []);
537
607
  }
608
+ consume(scope) {
609
+ const { $consume } = this.data;
610
+ if ($consume)
611
+ return $consume.call(scope, []);
612
+ const result = this.eval(scope);
613
+ // required by typescript
614
+ const res = result[0];
615
+ res.ensureOwned();
616
+ return result;
617
+ }
538
618
  call(scope, args) {
539
619
  const { $call } = this.data;
540
620
  if (!$call)
541
621
  return super.call(scope, args);
542
622
  return $call.call(scope, args);
543
623
  }
624
+ ensureOwned() {
625
+ this.owner ??= new ValueOwner({ scope: this.scope, value: this });
626
+ }
544
627
  }
545
628
  for (const op of operators) {
546
629
  ObjectValue.prototype[op] = function (...args) {
547
630
  const $ = this.data[`$${op}`];
631
+ // eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/no-unsafe-return
548
632
  if (!$)
549
633
  return VoidValue.prototype[op].apply(this, args);
550
- let [scope, ...fnArgs] = args;
634
+ const [scope, ...fnArgs] = args;
551
635
  return $.call(scope, fnArgs);
552
636
  };
553
637
  }
554
638
 
639
+ /**
640
+ * Properties that should return a new `SenseableValue`
641
+ */
642
+ const senseableProps = ["controller"];
643
+ /**
644
+ * Senseable values are "objects" in the mlog runtime that
645
+ * have special properties that can be accessed
646
+ * via the `sense` instruction.
647
+ *
648
+ * This class extends `StoreValue` to allow dynamic behavior
649
+ * that is not possible with object macros.
650
+ *
651
+ * Note that these values are constant by default, so make sure
652
+ * to set that field to `false` if you want them to be assignable. Example:
653
+ * ```
654
+ * const value = new SenseableValue(scope)
655
+ * value.constant = false
656
+ * ```
657
+ */
658
+ class SenseableValue extends StoreValue {
659
+ constructor(scope) {
660
+ super(scope);
661
+ this.constant = true;
662
+ }
663
+ get(scope, prop) {
664
+ if (prop instanceof LiteralValue && typeof prop.data === "string") {
665
+ const name = itemNames.includes(prop.data)
666
+ ? camelToDashCase(prop.data)
667
+ : prop.data;
668
+ const result = senseableProps.includes(prop.data)
669
+ ? new SenseableValue(scope)
670
+ : new StoreValue(scope);
671
+ return [
672
+ result,
673
+ [new InstructionBase("sensor", result, this, `@${name}`)],
674
+ ];
675
+ }
676
+ if (prop instanceof StoreValue) {
677
+ const temp = new StoreValue(scope);
678
+ return [temp, [new InstructionBase("sensor", temp, this, prop)]];
679
+ }
680
+ throw new CompilerError(`Cannot sense "${prop}" property`);
681
+ }
682
+ typeof(scope) {
683
+ return [new LiteralValue(scope, "senseable"), []];
684
+ }
685
+ }
686
+
687
+ /**
688
+ * Specific case class, required on the ArrayPattern and ObjectPattern handlers
689
+ * in order to keep the separation of concerns and make the code more modular.
690
+ *
691
+ * When it is assign a value, it will get it's own properties and recursively assign
692
+ * each of them to their counterparts on the right hand value.
693
+ */
694
+ class DestructuringValue extends VoidValue {
695
+ constructor(scope, members) {
696
+ super(scope);
697
+ this.members = members;
698
+ this.macro = true;
699
+ }
700
+ "="(scope, right) {
701
+ const inst = [];
702
+ for (const [key, value] of this.members) {
703
+ const [item, itemInst] = right.get(scope, key);
704
+ inst.push(...itemInst);
705
+ inst.push(...value["="](scope, item)[1]);
706
+ }
707
+ return [right, inst];
708
+ }
709
+ eval(_scope) {
710
+ return [this, []];
711
+ }
712
+ consume(_scope) {
713
+ return [this, []];
714
+ }
715
+ }
716
+
555
717
  class Scope {
556
718
  constructor(values, parent = null, stacked = false, ntemp = 0, name = "", inst = []) {
557
719
  this.parent = parent;
@@ -574,35 +736,64 @@ class Scope {
574
736
  scope.parent = this;
575
737
  return scope;
576
738
  }
577
- createFunction(name, stacked) {
578
- return new Scope({}, this, stacked ?? this.stacked, 0, name, this.inst);
739
+ createFunction(name, stacked = this.stacked) {
740
+ return new Scope({}, this, stacked, 0, name, this.inst);
579
741
  }
580
- has(name) {
581
- if (name in this.data)
742
+ has(identifier) {
743
+ if (identifier in this.data)
582
744
  return true;
583
745
  if (this.parent)
584
- return this.parent.has(name);
746
+ return this.parent.has(identifier);
585
747
  return false;
586
748
  }
587
- get(name) {
588
- const value = this.data[name];
589
- if (value)
590
- return value;
749
+ get(identifier) {
750
+ const owner = this.data[identifier];
751
+ if (owner)
752
+ return owner.value;
591
753
  if (this.parent)
592
- return this.parent.get(name);
593
- throw Error(`${name} is not declared.`);
754
+ return this.parent.get(identifier);
755
+ throw new CompilerError(`${identifier} is not declared.`);
594
756
  }
595
- set(name, value) {
757
+ set(...args) {
758
+ const name = args.length === 1 ? args[0].identifier : args[0];
759
+ if (!name)
760
+ throw new CompilerError("Values in a scope must have an identifier");
596
761
  if (name in this.data)
597
- throw Error(`${name} is already declared.`);
598
- return this.hardSet(name, value);
599
- }
600
- hardSet(name, value) {
601
- this.data[name] = value;
602
- return value;
762
+ throw new CompilerError(`${name} is already declared.`);
763
+ return this.hardSet(...args);
764
+ }
765
+ hardSet(...args) {
766
+ const owner = args.length === 1
767
+ ? args[0]
768
+ : new ValueOwner({
769
+ scope: this,
770
+ identifier: args[0],
771
+ name: args[0],
772
+ value: args[1],
773
+ });
774
+ const { identifier } = owner;
775
+ if (!identifier)
776
+ throw new CompilerError("Values in a scope must have an identifier");
777
+ this.data[identifier] = owner;
778
+ return owner.value;
779
+ }
780
+ make(identifier, name) {
781
+ const value = new SenseableValue(this);
782
+ value.constant = false;
783
+ const owner = new ValueOwner({
784
+ scope: this,
785
+ value,
786
+ identifier,
787
+ name,
788
+ });
789
+ return this.set(owner);
603
790
  }
604
- make(name, storeName) {
605
- return this.set(name, this.stacked ? null : new StoreValue(this, storeName));
791
+ makeTempName() {
792
+ let result = `${internalPrefix}t${this.ntemp}`;
793
+ if (this.name)
794
+ result += `:${this.name}`;
795
+ this.ntemp++;
796
+ return result;
606
797
  }
607
798
  }
608
799
 
@@ -610,86 +801,109 @@ const Program = (c, scope, node) => {
610
801
  return c.handleMany(scope ?? new Scope({}), node.body);
611
802
  };
612
803
 
613
- function nodeName(node) {
614
- const { line, column } = node.loc.start;
615
- return line + ":" + column;
616
- }
617
- function camelToDashCase(name) {
618
- return name.replace(/[A-Z]/g, str => `-${str.toLowerCase()}`);
619
- }
620
-
621
804
  const VariableDeclaration = (c, scope, node) => {
622
805
  return c.handleMany(scope, node.declarations, (scope, child) => VariableDeclarator(c, scope, child, node.kind));
623
806
  };
624
807
  const VariableDeclarator = (c, scope, node, kind = "let") => {
625
- let valinst = node.init
808
+ const [init, inst] = node.init
626
809
  ? c.handleEval(scope, node.init)
627
810
  : [null, []];
628
- switch (node.id.type) {
629
- case "Identifier": {
630
- const { name } = node.id;
631
- const [init] = valinst;
632
- if (kind === "const" && !init)
633
- throw Error("Cannot create constant with void value.");
634
- if (kind === "const" && init?.constant) {
635
- scope.set(name, init);
636
- return valinst;
637
- }
638
- else {
639
- const value = scope.make(name, nodeName(node));
640
- if (init) {
641
- if (init.macro)
642
- throw new Error("Macro value must be constant.");
643
- valinst[1].push(...value["="](scope, init)[1]);
644
- }
645
- if (kind === "const")
646
- value.constant = true;
647
- return valinst;
648
- }
649
- }
650
- case "ArrayPattern": {
651
- const { elements } = node.id;
652
- const [init] = valinst;
653
- if (!init)
654
- throw new Error("Cannot use array destructuring with constant with void value.");
655
- if (!init.macro)
656
- throw new Error("Cannot use array destructuring on non macro values");
657
- if (!(init instanceof ObjectValue)) {
658
- throw new Error("Array destructuring target must be an object value");
659
- }
660
- for (let i = 0; i < elements.length; i++) {
661
- const element = elements[i];
662
- if (!element)
663
- continue;
664
- if (element.type !== "Identifier") {
665
- throw new Error("Array destructuring expression can only have empty items or identifiers");
666
- }
667
- const val = init.data[i];
668
- scope.set(element.name, val);
669
- }
670
- return valinst;
671
- }
811
+ inst.push(...Declare(c, scope, node.id, kind, init)[1]);
812
+ return [null, inst];
813
+ };
814
+ const Declare = (c, scope, node, kind, init) => {
815
+ switch (node.type) {
816
+ case "Identifier":
817
+ return DeclareIdentifier(c, scope, node, kind, init);
818
+ case "ArrayPattern":
819
+ return DeclareArrayPattern(c, scope, node, kind, init);
820
+ case "ObjectPattern":
821
+ return DeclareObjectPattern(c, scope, node, kind, init);
672
822
  default:
673
- throw new Error("");
823
+ throw new CompilerError(`Unsupported declaration type: ${node.type}`, [
824
+ node,
825
+ ]);
674
826
  }
675
- };
676
-
677
- const Literal = (c, scope, node) => {
678
- const { value } = node;
679
- if (value === null ||
680
- typeof value === "string" ||
681
- typeof value === "number") {
682
- return [new LiteralValue(scope, value), []];
827
+ };
828
+ const DeclareIdentifier = (c, scope, node, kind, init) => {
829
+ const { name: identifier } = node;
830
+ const name = nodeName(node, !c.compactNames && identifier);
831
+ if (kind === "const" && !init)
832
+ throw new CompilerError("Constants must be initialized.", [node]);
833
+ if (kind === "const" && init?.constant) {
834
+ const owner = new ValueOwner({
835
+ scope,
836
+ identifier,
837
+ name,
838
+ value: init,
839
+ constant: true,
840
+ });
841
+ scope.set(owner);
842
+ return [null, []];
843
+ }
844
+ else {
845
+ const value = scope.make(identifier, name);
846
+ const inst = [];
847
+ if (init) {
848
+ if (init.macro)
849
+ throw new CompilerError("Macro values must be held by constants", [
850
+ node,
851
+ ]);
852
+ inst.push(...value["="](scope, init)[1]);
853
+ }
854
+ if (kind === "const")
855
+ value.constant = true;
856
+ return [null, inst];
683
857
  }
684
- if (typeof value === "boolean") {
685
- return [new LiteralValue(scope, +value), []];
858
+ };
859
+ const DeclareArrayPattern = (c, scope, node, kind, init) => {
860
+ const { elements } = node;
861
+ if (!(init instanceof ObjectValue))
862
+ throw new CompilerError("The value being destructured must be an object value");
863
+ const inst = [];
864
+ for (let i = 0; i < elements.length; i++) {
865
+ const element = elements[i];
866
+ if (!element)
867
+ continue;
868
+ const val = init.data[i];
869
+ if (!val)
870
+ throw new CompilerError(`The target object does not have a value at index ${i}`, [element]);
871
+ inst.push(...Declare(c, scope, element, kind, val)[1]);
686
872
  }
687
- throw Error("Cannot create literal " + typeof value);
873
+ return [null, inst];
874
+ };
875
+ const DeclareObjectPattern = (c, scope, node, kind, base) => {
876
+ if (!base)
877
+ throw new CompilerError("Cannot use object destructuring without an initializer", [node]);
878
+ const { properties } = node;
879
+ const [init, inst] = base.consume(scope);
880
+ for (const prop of properties) {
881
+ if (prop.type === "RestElement")
882
+ throw new CompilerError("The rest operator is not supported", [prop]);
883
+ const { key, value } = prop;
884
+ const child = key.type === "Identifier" && !prop.computed
885
+ ? [new LiteralValue(scope, key.name), []]
886
+ : c.handleConsume(scope, prop.key);
887
+ inst.push(...child[1]);
888
+ const propInit = init.get(scope, child[0]);
889
+ inst.push(...propInit[1]);
890
+ inst.push(...Declare(c, scope, value, kind, propInit[0])[1]);
891
+ }
892
+ return [null, inst];
688
893
  };
689
894
 
895
+ const Literal = (_c, scope, node) => [new LiteralValue(scope, node.value), []];
896
+ const NumericLiteral = Literal;
897
+ const StringLiteral = Literal;
898
+ const NullLiteral = (_c, scope) => [
899
+ new LiteralValue(scope, null),
900
+ [],
901
+ ];
902
+ const BooleanLiteral = (_c, scope, node) => [new LiteralValue(scope, +node.value), []];
903
+
690
904
  const LRExpression = (c, scope, node) => {
691
- const [left, leftInst] = c.handleEval(scope, node.left);
692
- const [right, rightInst] = c.handleEval(scope, node.right);
905
+ const [left, leftInst] = c.handleConsume(scope, node.left);
906
+ const [right, rightInst] = c.handleConsume(scope, node.right);
693
907
  const [op, opInst] = left[node.operator](scope, right);
694
908
  return [op, [...leftInst, ...rightInst, ...opInst]];
695
909
  };
@@ -697,20 +911,55 @@ const BinaryExpression = LRExpression;
697
911
  const LogicalExpression = LRExpression;
698
912
  const AssignmentExpression = (c, scope, node) => {
699
913
  const [left, leftInst] = c.handle(scope, node.left);
914
+ // doesn't need to consume because the operators already do that
700
915
  const [right, rightInst] = c.handleEval(scope, node.right);
916
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
701
917
  const [op, opInst] = left[node.operator](scope, right);
702
918
  return [op, [...leftInst, ...rightInst, ...opInst]];
703
919
  };
704
920
  const UnaryExpression = (c, scope, { argument, operator }) => {
705
- const [arg, argInst] = c.handleEval(scope, argument);
921
+ const [arg, argInst] = c.handleConsume(scope, argument);
706
922
  const operatorId = operator == "+" || operator == "-" ? `u${operator}` : operator;
923
+ if (operatorId === "throw")
924
+ throw new CompilerError("throw operator is not supported");
707
925
  const [op, opInst] = arg[operatorId](scope);
708
926
  return [op, [...argInst, ...opInst]];
709
927
  };
710
928
  const UpdateExpression = (c, scope, { argument, operator, prefix }) => {
711
929
  const [arg, argInst] = c.handle(scope, argument);
930
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
712
931
  const [op, opInst] = arg[operator](scope, prefix);
713
932
  return [op, [...argInst, ...opInst]];
933
+ };
934
+ const ConditionalExpression = (c, scope, node) => {
935
+ const [test, testInst] = c.handleConsume(scope, node.test);
936
+ if (test instanceof LiteralValue) {
937
+ if (test.data)
938
+ return c.handleEval(scope, node.consequent);
939
+ return c.handleEval(scope, node.alternate);
940
+ }
941
+ // TODO: this creates those annoying jumps on the
942
+ // temp counter, specially if you nest ternary operators
943
+ const result = new StoreValue(scope);
944
+ result.ensureOwned();
945
+ const consequent = c.handleEval(scope, node.consequent);
946
+ const alternate = c.handleEval(scope, node.alternate);
947
+ const alternateStartAdress = new LiteralValue(scope, null);
948
+ const endExpressionAdress = new LiteralValue(scope, null);
949
+ return [
950
+ result,
951
+ [
952
+ ...testInst,
953
+ new JumpInstruction(alternateStartAdress, EJumpKind.Equal, test, new LiteralValue(scope, 0)),
954
+ ...consequent[1],
955
+ ...result["="](scope, consequent[0])[1],
956
+ new JumpInstruction(endExpressionAdress, EJumpKind.Always),
957
+ new AddressResolver(alternateStartAdress),
958
+ ...alternate[1],
959
+ ...result["="](scope, alternate[0])[1],
960
+ new AddressResolver(endExpressionAdress),
961
+ ],
962
+ ];
714
963
  };
715
964
 
716
965
  const Identifier = (c, scope, node) => {
@@ -740,7 +989,7 @@ const ReturnStatement = (c, scope, node) => {
740
989
 
741
990
  const IfStatement = (c, scope, node) => {
742
991
  const inst = [];
743
- const [test, testInst] = c.handleEval(scope, node.test);
992
+ const [test, testInst] = c.handleConsume(scope, node.test);
744
993
  if (test instanceof LiteralValue) {
745
994
  if (test.data)
746
995
  inst.push(...c.handle(scope, node.consequent)[1]);
@@ -758,7 +1007,7 @@ const IfStatement = (c, scope, node) => {
758
1007
 
759
1008
  const WhileStatement = (c, scope, node) => {
760
1009
  const lines = [];
761
- const [test, testLines] = c.handleEval(scope, node.test);
1010
+ const [test, testLines] = c.handleConsume(scope, node.test);
762
1011
  const startLoopAddr = new LiteralValue(scope, null);
763
1012
  const endLoopAddr = new LiteralValue(scope, null);
764
1013
  const startLoopLine = new AddressResolver(startLoopAddr).bindContinue(scope);
@@ -771,13 +1020,35 @@ const WhileStatement = (c, scope, node) => {
771
1020
  }
772
1021
  lines.push(startLoopLine, ...testLines, new JumpInstruction(endLoopAddr, EJumpKind.Equal, test, new LiteralValue(scope, 0)), ...c.handle(scope, node.body)[1], new JumpInstruction(startLoopAddr, EJumpKind.Always), endLoopLine);
773
1022
  return [null, lines];
1023
+ };
1024
+ const DoWhileStatement = (c, scope, node) => {
1025
+ const lines = [];
1026
+ const [test, testLines] = c.handleConsume(scope, node.test);
1027
+ const startLoopAddr = new LiteralValue(scope, null);
1028
+ const startLoopLine = new AddressResolver(startLoopAddr).bindContinue(scope);
1029
+ const [, bodyLines] = c.handle(scope, node.body);
1030
+ if (test instanceof LiteralValue) {
1031
+ if (!test.data)
1032
+ return [null, [...lines, ...bodyLines]];
1033
+ return [
1034
+ null,
1035
+ [
1036
+ ...lines,
1037
+ startLoopLine,
1038
+ ...bodyLines,
1039
+ new JumpInstruction(startLoopAddr, EJumpKind.Always),
1040
+ ],
1041
+ ];
1042
+ }
1043
+ lines.push(startLoopLine, ...bodyLines, ...testLines, new JumpInstruction(startLoopAddr, EJumpKind.Equal, test, new LiteralValue(scope, 1)));
1044
+ return [null, lines];
774
1045
  };
775
1046
 
776
1047
  const ForStatement = (c, scope, node) => {
777
1048
  scope = scope.createScope();
778
1049
  const initInst = node.init ? c.handle(scope, node.init)[1] : [];
779
1050
  const [test, testLines] = node.test
780
- ? c.handleEval(scope, node.test)
1051
+ ? c.handleConsume(scope, node.test)
781
1052
  : [new LiteralValue(scope, 1), []];
782
1053
  const updateLines = node.update ? c.handle(scope, node.update)[1] : [];
783
1054
  const startLoopAddr = new LiteralValue(scope, null);
@@ -800,65 +1071,108 @@ const ForStatement = (c, scope, node) => {
800
1071
  };
801
1072
 
802
1073
  class FunctionValue extends VoidValue {
803
- constructor(scope, name, paramNames, paramStores, body, c) {
1074
+ constructor({ scope, params, body, c, node, }) {
804
1075
  super(scope);
805
1076
  this.constant = true;
806
1077
  this.macro = true;
1078
+ this.paramOwners = [];
807
1079
  this.bundled = false;
808
- this.name = name;
809
- this.paramNames = paramNames;
810
- this.paramStores = paramStores;
1080
+ this.initialized = false;
811
1081
  this.body = body;
812
1082
  this.c = c;
813
- this.callSize = paramStores.length + 2;
814
- scope.function = this;
815
- this.createValues();
816
- this.createInst();
817
- }
818
- createValues() {
819
- this.addr = new LiteralValue(this.scope, null);
820
- this.temp = new StoreValue(this.scope, "f" + this.name);
821
- this.ret = new StoreValue(this.scope, "r" + this.name);
1083
+ this.node = node;
1084
+ this.params = params;
822
1085
  }
823
1086
  typeof(scope) {
824
1087
  return [new LiteralValue(scope, "function"), []];
825
1088
  }
826
- createInst() {
1089
+ initScope(name) {
1090
+ this.childScope = this.scope.createFunction(name);
1091
+ this.childScope.function = this;
1092
+ for (const id of this.params) {
1093
+ const name = nodeName(id, !this.c.compactNames && id.name);
1094
+ const owner = new ValueOwner({
1095
+ scope: this.childScope,
1096
+ value: new StoreValue(this.childScope),
1097
+ identifier: id.name,
1098
+ name,
1099
+ });
1100
+ this.childScope.set(owner);
1101
+ this.paramOwners.push(owner);
1102
+ }
1103
+ this.callSize = this.paramOwners.length + 2;
1104
+ }
1105
+ ensureInit() {
1106
+ if (!this.owner)
1107
+ throw new CompilerError(`Functions must be owned by a variable`);
1108
+ if (this.initialized)
1109
+ return;
1110
+ this.initialized = true;
1111
+ const name = nodeName(this.node, !this.c.compactNames && this.owner.name);
1112
+ this.initScope(name);
1113
+ this.addr = new LiteralValue(this.childScope, null);
1114
+ this.temp = new ValueOwner({
1115
+ scope: this.childScope,
1116
+ name: `${internalPrefix}f${name}`,
1117
+ value: new SenseableValue(this.childScope),
1118
+ });
1119
+ this.temp.value.constant = false;
1120
+ this.ret = new ValueOwner({
1121
+ scope: this.childScope,
1122
+ name: `${internalPrefix}r${name}`,
1123
+ value: new StoreValue(this.childScope),
1124
+ });
827
1125
  this.inst = [
828
1126
  new AddressResolver(this.addr),
829
- ...this.c.handle(this.scope, this.body)[1],
1127
+ ...this.c.handle(this.childScope, this.body)[1],
830
1128
  ];
831
- this.inst.push(new SetCounterInstruction(this.ret));
1129
+ if (!endsWithReturn(this.inst)) {
1130
+ this.inst.push(new SetCounterInstruction(this.ret.value));
1131
+ }
832
1132
  }
833
1133
  normalReturn(scope, arg) {
834
- const argInst = arg ? this.temp["="](scope, arg)[1] : [];
835
- return [null, [...argInst, new SetCounterInstruction(this.ret)]];
1134
+ const argInst = arg ? this.temp.value["="](scope, arg)[1] : [];
1135
+ return [null, [...argInst, new SetCounterInstruction(this.ret.value)]];
836
1136
  }
837
1137
  normalCall(scope, args) {
838
1138
  if (!this.bundled)
839
- this.scope.inst.push(...this.inst);
1139
+ this.childScope.inst.push(...this.inst);
840
1140
  this.bundled = true;
841
1141
  const callAddressLiteral = new LiteralValue(scope, null);
842
- const inst = this.paramStores
843
- .map((param, i) => param["="](scope, args[i])[1])
1142
+ const inst = this.paramOwners
1143
+ .map(({ value: param }, i) => param["="](scope, args[i])[1])
844
1144
  .reduce((s, c) => s.concat(c), [])
845
- .concat(...this.ret["="](scope, callAddressLiteral)[1], new JumpInstruction(this.addr, EJumpKind.Always), new AddressResolver(callAddressLiteral));
846
- return [this.temp, inst];
1145
+ .concat(...this.ret.value["="](scope, callAddressLiteral)[1], new JumpInstruction(this.addr, EJumpKind.Always), new AddressResolver(callAddressLiteral));
1146
+ return [this.temp.value, inst];
847
1147
  }
848
1148
  inlineReturn(scope, arg) {
849
- const argInst = arg ? this.inlineTemp["="](scope, arg)[1] : [];
850
- return [null, [...argInst, new ReturnInstruction(this.inlineEnd)]];
1149
+ const argInst = arg ? this.inlineTemp.value["="](scope, arg)[1] : [];
1150
+ return [null, argInst];
851
1151
  }
852
1152
  inlineCall(scope, args) {
853
1153
  // create return value
854
- this.inlineTemp = new TempValue(scope);
1154
+ this.inlineTemp = new ValueOwner({
1155
+ scope: this.childScope,
1156
+ value: new SenseableValue(scope),
1157
+ });
1158
+ this.inlineTemp.value.constant = false;
855
1159
  this.inlineEnd = new LiteralValue(scope, null);
856
1160
  // make a copy of the function scope
857
- const fnScope = this.scope.copy();
1161
+ const fnScope = this.childScope.copy();
858
1162
  // hard set variables within the function scope
859
- this.paramNames.forEach((name, i) => fnScope.hardSet(name, args[i]));
1163
+ this.paramOwners.forEach((owner, i) => {
1164
+ const value = args[i];
1165
+ if (!value.owner?.persistent)
1166
+ owner.own(value);
1167
+ fnScope.hardSet(new ValueOwner({
1168
+ scope: fnScope,
1169
+ value,
1170
+ identifier: owner.identifier,
1171
+ name: owner.name,
1172
+ }));
1173
+ });
860
1174
  this.tryingInline = true;
861
- let inst = this.c.handle(fnScope, this.body)[1];
1175
+ const inst = this.c.handle(fnScope, this.body)[1];
862
1176
  this.tryingInline = false;
863
1177
  // removing useless instruction
864
1178
  // get the last instructions
@@ -869,11 +1183,12 @@ class FunctionValue extends VoidValue {
869
1183
  // }
870
1184
  inst.push(new AddressResolver(this.inlineEnd));
871
1185
  // return the function value
872
- return [this.inlineTemp, inst];
1186
+ return [this.inlineTemp.value, inst];
873
1187
  }
874
1188
  call(scope, args) {
875
- if (args.length !== this.paramStores.length)
876
- throw new Error("Cannot call: argument count not matching.");
1189
+ this.ensureInit();
1190
+ if (args.length !== this.paramOwners.length)
1191
+ throw new CompilerError("Cannot call: argument count not matching.");
877
1192
  const inlineCall = this.inlineCall(scope, args);
878
1193
  const inlineSize = inlineCall[1].filter(i => !i.hidden).length;
879
1194
  if (this.inline || inlineSize <= this.callSize)
@@ -881,6 +1196,7 @@ class FunctionValue extends VoidValue {
881
1196
  return this.normalCall(scope, args);
882
1197
  }
883
1198
  return(scope, arg) {
1199
+ this.ensureInit();
884
1200
  if (arg && arg.macro) {
885
1201
  this.inline = true;
886
1202
  return [null, []];
@@ -889,49 +1205,67 @@ class FunctionValue extends VoidValue {
889
1205
  return this.inlineReturn(scope, arg);
890
1206
  return this.normalReturn(scope, arg);
891
1207
  }
892
- eval(scope) {
1208
+ eval(_scope) {
1209
+ return [this, []];
1210
+ }
1211
+ consume(_scope) {
893
1212
  return [this, []];
894
1213
  }
1214
+ }
1215
+ function endsWithReturn(inst) {
1216
+ for (let i = inst.length - 1; i >= 0; i--) {
1217
+ const instruction = inst[i];
1218
+ if (instruction.hidden)
1219
+ continue;
1220
+ return instruction instanceof SetCounterInstruction;
1221
+ }
1222
+ return false;
895
1223
  }
896
1224
 
897
- const ArrowFunctionExpression = (c, scope, node) => {
898
- const name = nodeName(node);
899
- scope = scope.createFunction(name);
1225
+ function handleFunctionNode(c, scope, node) {
900
1226
  let { params, body } = node;
901
- if (node.expression) {
902
- body = {
903
- type: "BlockStatement",
904
- body: [{ type: "ReturnStatement", argument: body }],
905
- };
906
- }
907
- const paramNames = [];
908
- const paramStores = [];
909
- for (const id of params) {
910
- paramNames.push(id.name);
911
- paramStores.push(scope.make(id.name, nodeName(id)));
1227
+ if (es__namespace.isExpression(body)) {
1228
+ body = es__namespace.blockStatement([es__namespace.returnStatement(body)]);
912
1229
  }
913
1230
  return [
914
- new FunctionValue(scope, name, paramNames, paramStores, body, c),
1231
+ new FunctionValue({
1232
+ node,
1233
+ scope,
1234
+ params: params,
1235
+ body,
1236
+ c,
1237
+ }),
915
1238
  [],
916
1239
  ];
1240
+ }
1241
+ const ArrowFunctionExpression = (c, scope, node) => {
1242
+ return handleFunctionNode(c, scope, node);
917
1243
  };
918
1244
  const FunctionDeclaration = (c, scope, node) => {
919
- return [
920
- scope.set(node.id.name, ArrowFunctionExpression(c, scope, node, null)[0]),
921
- [],
922
- ];
1245
+ const identifier = node.id.name;
1246
+ const name = nodeName(node, !c.compactNames && identifier);
1247
+ const functionIns = handleFunctionNode(c, scope, node);
1248
+ const owner = new ValueOwner({
1249
+ scope,
1250
+ value: functionIns[0],
1251
+ identifier,
1252
+ name,
1253
+ });
1254
+ return [scope.set(owner), []];
923
1255
  };
924
- const FunctionExpression = ArrowFunctionExpression;
1256
+ const FunctionExpression = ArrowFunctionExpression;
1257
+ const ObjectMethod = ArrowFunctionExpression;
925
1258
 
926
1259
  const CallExpression = (c, scope, node) => {
927
1260
  const inst = [];
928
1261
  const args = node.arguments.map(node => {
929
1262
  // TODO: this might be why t0 did not increment
930
- const [v, i] = c.handleEval(scope, node);
1263
+ // TODO: figure out if the previous todo is still relevant
1264
+ const [v, i] = c.handleConsume(scope, node);
931
1265
  inst.push(...i);
932
1266
  return v;
933
1267
  });
934
- const [callee, calleeInst] = c.handleEval(scope, node.callee);
1268
+ const [callee, calleeInst] = c.handleConsume(scope, node.callee);
935
1269
  inst.push(...calleeInst);
936
1270
  const [callValue, callInst] = callee.call(scope, args);
937
1271
  inst.push(...callInst);
@@ -943,49 +1277,101 @@ const ObjectExpression = (c, scope, node) => {
943
1277
  const data = {};
944
1278
  const inst = [];
945
1279
  for (const prop of node.properties) {
1280
+ if (prop.type === "SpreadElement")
1281
+ throw new CompilerError("Cannot handle spread element", [prop]);
946
1282
  if (prop.computed)
947
- throw new Error("Cannot handle computed property.");
948
- const { key, value } = prop;
1283
+ throw new CompilerError("Cannot handle computed property.", [prop]);
1284
+ const { key } = prop;
1285
+ const value = prop.type === "ObjectProperty" ? prop.value : prop;
949
1286
  let index;
950
- if (key.type === "Identifier")
1287
+ if (key.type === "Identifier") {
951
1288
  index = key.name;
952
- else if (key.type === "Literal")
953
- index = "" + key.value;
1289
+ }
1290
+ else if (key.type === "StringLiteral" || key.type === "NumericLiteral") {
1291
+ index = `${key.value}`;
1292
+ }
954
1293
  else {
955
- throw new Error(`Unsupported object key type: ${key.type}`);
1294
+ throw new CompilerError(`Unsupported object key type: ${key.type}`, [
1295
+ key,
1296
+ ]);
956
1297
  }
957
1298
  const [member, memberInst] = c.handleEval(scope, value);
958
- data[index] = member;
1299
+ const owner = new ValueOwner({
1300
+ scope,
1301
+ constant: true,
1302
+ value: member,
1303
+ });
1304
+ data[index] = owner.value;
959
1305
  inst.push(...memberInst);
960
1306
  }
961
1307
  return [new ObjectValue(scope, data), inst];
962
1308
  };
963
1309
  const ArrayExpression = (c, scope, node) => {
964
- const data = {};
1310
+ const items = [];
965
1311
  const inst = [];
966
- node.elements.forEach((element, i) => {
967
- if (!element)
968
- return;
969
- const [value, valueInst] = c.handleEval(scope, element);
970
- data[i] = value;
1312
+ node.elements.forEach(element => {
1313
+ const [value, valueInst] = element
1314
+ ? c.handleEval(scope, element)
1315
+ : [undefined, []];
1316
+ items.push(value);
971
1317
  inst.push(...valueInst);
972
1318
  });
973
- return [new ObjectValue(scope, data), inst];
1319
+ return [ObjectValue.fromArray(scope, items), inst];
974
1320
  };
975
1321
  const MemberExpression = (c, scope, node) => {
976
- const [obj, objInst] = c.handleEval(scope, node.object);
1322
+ const [obj, objInst] = c.handleConsume(scope, node.object);
977
1323
  const [prop, propInst] = node.computed
978
- ? c.handleEval(scope, node.property)
1324
+ ? c.handleConsume(scope, node.property)
979
1325
  : [new LiteralValue(scope, node.property.name), []];
980
1326
  const [got, gotInst] = obj.get(scope, prop);
981
1327
  return [got, [...objInst, ...propInst, ...gotInst]];
1328
+ };
1329
+ const ArrayPattern = (c, scope, node) => {
1330
+ const inst = [];
1331
+ const members = new Map();
1332
+ for (let i = 0; i < node.elements.length; i++) {
1333
+ const element = node.elements[i];
1334
+ if (!element)
1335
+ continue;
1336
+ const valueInst = c.handle(scope, element);
1337
+ if (!valueInst[0])
1338
+ throw new CompilerError("Destructuring element must resolve to a value", [
1339
+ element,
1340
+ ]);
1341
+ inst.push(...valueInst[1]);
1342
+ members.set(new LiteralValue(scope, i), valueInst[0]);
1343
+ }
1344
+ return [new DestructuringValue(scope, members), inst];
1345
+ };
1346
+ const ObjectPattern = (c, scope, node) => {
1347
+ const inst = [];
1348
+ const members = new Map();
1349
+ for (const prop of node.properties) {
1350
+ if (prop.type === "RestElement")
1351
+ throw new CompilerError("The rest operator is not supported");
1352
+ if (!prop)
1353
+ continue;
1354
+ const { key } = prop;
1355
+ const keyInst = !prop.computed && key.type === "Identifier"
1356
+ ? [new LiteralValue(scope, key.name), []]
1357
+ : c.handleConsume(scope, prop.key);
1358
+ inst.push(...keyInst[1]);
1359
+ const valueInst = c.handle(scope, prop.value);
1360
+ if (!valueInst[0])
1361
+ throw new CompilerError("Destructuring member must resolve to a value", [
1362
+ prop.value,
1363
+ ]);
1364
+ inst.push(...valueInst[1]);
1365
+ members.set(keyInst[0], valueInst[0]);
1366
+ }
1367
+ return [new DestructuringValue(scope, members), inst];
982
1368
  };
983
1369
 
984
1370
  const TemplateLiteral = (c, scope, node) => {
985
1371
  const args = [];
986
1372
  const inst = [];
987
1373
  node.expressions.forEach((expression, i) => {
988
- const [v, vi] = c.handleEval(scope, expression);
1374
+ const [v, vi] = c.handleConsume(scope, expression);
989
1375
  inst.push(...vi);
990
1376
  args.push(node.quasis[i].value.raw, v);
991
1377
  });
@@ -1004,18 +1390,73 @@ const BlockStatement = (c, scope, node) => {
1004
1390
  return [value, insts];
1005
1391
  };
1006
1392
 
1393
+ const File = (c, scope, node) => {
1394
+ return c.handle(scope ?? new Scope({}), node.program);
1395
+ };
1396
+
1397
+ const TypeCastExpression = (c, scope, node) => {
1398
+ return c.handle(scope, node.expression);
1399
+ };
1400
+ const TSAsExpression = TypeCastExpression;
1401
+ const TSTypeAssertion = TypeCastExpression;
1402
+ const IgnoredHandler = () => [null, []];
1403
+ const TSInterfaceDeclaration = IgnoredHandler;
1404
+ const TSTypeAliasDeclaration = IgnoredHandler;
1405
+ const TSEnumDeclaration = (c, scope, node) => {
1406
+ if (!node.const)
1407
+ throw new CompilerError("All enums must be declared with the const keyword before enum");
1408
+ let counter = 0;
1409
+ let lastType = "number";
1410
+ const data = {};
1411
+ for (const member of node.members) {
1412
+ if (lastType === "string" && !member.initializer)
1413
+ throw new CompilerError("This enum member must be initialized", [member]);
1414
+ const [value] = member.initializer
1415
+ ? c.handleEval(scope, member.initializer)
1416
+ : [new LiteralValue(scope, counter)];
1417
+ if (!(value instanceof LiteralValue))
1418
+ throw new CompilerError("Enum members must contain literal values", [
1419
+ member,
1420
+ ]);
1421
+ if (typeof value.data === "number") {
1422
+ lastType = "number";
1423
+ counter = value.data + 1;
1424
+ }
1425
+ else {
1426
+ lastType = "string";
1427
+ }
1428
+ const name = member.id.type === "Identifier" ? member.id.name : member.id.value;
1429
+ data[name] = value;
1430
+ }
1431
+ scope.set(new ValueOwner({
1432
+ scope,
1433
+ value: new ObjectValue(scope, data),
1434
+ constant: true,
1435
+ identifier: node.id.name,
1436
+ name: nodeName(node, !c.compactNames && node.id.name),
1437
+ }));
1438
+ return [null, []];
1439
+ };
1440
+ const TSNonNullExpression = (c, scope, node) => {
1441
+ return c.handle(scope, node.expression);
1442
+ };
1443
+
1007
1444
  var handlers = /*#__PURE__*/Object.freeze({
1008
1445
  __proto__: null,
1009
1446
  Program: Program,
1010
1447
  VariableDeclaration: VariableDeclaration,
1011
1448
  VariableDeclarator: VariableDeclarator,
1012
- Literal: Literal,
1449
+ NumericLiteral: NumericLiteral,
1450
+ StringLiteral: StringLiteral,
1451
+ NullLiteral: NullLiteral,
1452
+ BooleanLiteral: BooleanLiteral,
1013
1453
  LRExpression: LRExpression,
1014
1454
  BinaryExpression: BinaryExpression,
1015
1455
  LogicalExpression: LogicalExpression,
1016
1456
  AssignmentExpression: AssignmentExpression,
1017
1457
  UnaryExpression: UnaryExpression,
1018
1458
  UpdateExpression: UpdateExpression,
1459
+ ConditionalExpression: ConditionalExpression,
1019
1460
  Identifier: Identifier,
1020
1461
  ExpressionStatement: ExpressionStatement,
1021
1462
  BreakStatement: BreakStatement,
@@ -1023,17 +1464,28 @@ var handlers = /*#__PURE__*/Object.freeze({
1023
1464
  ReturnStatement: ReturnStatement,
1024
1465
  IfStatement: IfStatement,
1025
1466
  WhileStatement: WhileStatement,
1467
+ DoWhileStatement: DoWhileStatement,
1026
1468
  ForStatement: ForStatement,
1027
1469
  ArrowFunctionExpression: ArrowFunctionExpression,
1028
1470
  FunctionDeclaration: FunctionDeclaration,
1029
1471
  FunctionExpression: FunctionExpression,
1472
+ ObjectMethod: ObjectMethod,
1030
1473
  CallExpression: CallExpression,
1031
1474
  NewExpression: NewExpression,
1032
1475
  ObjectExpression: ObjectExpression,
1033
1476
  ArrayExpression: ArrayExpression,
1034
1477
  MemberExpression: MemberExpression,
1478
+ ArrayPattern: ArrayPattern,
1479
+ ObjectPattern: ObjectPattern,
1035
1480
  TemplateLiteral: TemplateLiteral,
1036
- BlockStatement: BlockStatement
1481
+ BlockStatement: BlockStatement,
1482
+ File: File,
1483
+ TSAsExpression: TSAsExpression,
1484
+ TSTypeAssertion: TSTypeAssertion,
1485
+ TSInterfaceDeclaration: TSInterfaceDeclaration,
1486
+ TSTypeAliasDeclaration: TSTypeAliasDeclaration,
1487
+ TSEnumDeclaration: TSEnumDeclaration,
1488
+ TSNonNullExpression: TSNonNullExpression
1037
1489
  });
1038
1490
 
1039
1491
  class MacroFunction extends VoidValue {
@@ -1046,11 +1498,15 @@ class MacroFunction extends VoidValue {
1046
1498
  call(scope, args) {
1047
1499
  return this.fn.apply(this, args);
1048
1500
  }
1049
- eval(scope) {
1501
+ eval(_scope) {
1502
+ return [this, []];
1503
+ }
1504
+ consume(_scope) {
1050
1505
  return [this, []];
1051
1506
  }
1052
1507
  }
1053
1508
 
1509
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
1054
1510
  const mathOperations = {
1055
1511
  max: (a, b) => Math.max(a, b),
1056
1512
  min: (a, b) => Math.min(a, b),
@@ -1079,18 +1535,18 @@ function createMacroMathOperations(scope) {
1079
1535
  if (b) {
1080
1536
  if (fn && a instanceof LiteralValue && b instanceof LiteralValue) {
1081
1537
  if (typeof a.data !== "number" || typeof b.data !== "number")
1082
- throw new Error("Cannot do math operation with non-numerical literals.");
1538
+ throw new CompilerError("Cannot do math operation with non-numerical literals.");
1083
1539
  return [new LiteralValue(scope, fn(a.num, b.num)), []];
1084
1540
  }
1085
- const temp = new TempValue(scope);
1541
+ const temp = new StoreValue(scope);
1086
1542
  return [temp, [new OperationInstruction(key, temp, a, b)]];
1087
1543
  }
1088
1544
  if (fn && a instanceof LiteralValue) {
1089
1545
  if (typeof a.data !== "number")
1090
- throw new Error("Cannot do math operation with non-numerical literal.");
1546
+ throw new CompilerError("Cannot do math operation with non-numerical literal.");
1091
1547
  return [new LiteralValue(scope, fn(a.num)), []];
1092
1548
  }
1093
- const temp = new TempValue(scope);
1549
+ const temp = new StoreValue(scope);
1094
1550
  return [temp, [new OperationInstruction(key, temp, a, b)]];
1095
1551
  });
1096
1552
  }
@@ -1109,7 +1565,7 @@ class Concat extends MacroFunction {
1109
1565
  super(scope, (...values) => {
1110
1566
  for (const value of values)
1111
1567
  if (!(value instanceof LiteralValue))
1112
- throw new Error("Concat arguments must all be literal values.");
1568
+ throw new CompilerError("Concat arguments must all be literal values.");
1113
1569
  return [
1114
1570
  new LiteralValue(scope, values.map(v => v.data).join("")),
1115
1571
  [],
@@ -1122,7 +1578,7 @@ class Draw extends MacroFunction {
1122
1578
  constructor(scope) {
1123
1579
  super(scope, (kind, ...args) => {
1124
1580
  if (!(kind instanceof LiteralValue && typeof kind.data === "string"))
1125
- throw new Error("Draw kind must be literal.");
1581
+ throw new CompilerError("Draw kind must be a string literal.");
1126
1582
  if ([
1127
1583
  "clear",
1128
1584
  "color",
@@ -1135,7 +1591,7 @@ class Draw extends MacroFunction {
1135
1591
  "triangle",
1136
1592
  "image",
1137
1593
  ].indexOf(kind.data) === -1)
1138
- throw new Error("Draw kind must be valid");
1594
+ throw new CompilerError("Invalid draw kind");
1139
1595
  return [
1140
1596
  null,
1141
1597
  [
@@ -1165,8 +1621,8 @@ class Print extends MacroFunction {
1165
1621
  class PrintFlush extends MacroFunction {
1166
1622
  constructor(scope) {
1167
1623
  super(scope, (target) => {
1168
- if (!(target instanceof ObjectValue))
1169
- throw new Error("The printflush target must be a building");
1624
+ if (!(target instanceof SenseableValue))
1625
+ throw new CompilerError("The printflush target must be a senseable value");
1170
1626
  return [null, [new InstructionBase("printflush", target)]];
1171
1627
  });
1172
1628
  }
@@ -1175,93 +1631,21 @@ class PrintFlush extends MacroFunction {
1175
1631
  class DrawFlush extends MacroFunction {
1176
1632
  constructor(scope) {
1177
1633
  super(scope, (target) => {
1178
- if (!(target instanceof ObjectValue))
1179
- throw new Error("The drawflush target must be a building");
1634
+ if (!(target instanceof SenseableValue))
1635
+ throw new CompilerError("The drawflush target must be a senseable value");
1180
1636
  return [null, [new InstructionBase("drawflush", target)]];
1181
1637
  });
1182
1638
  }
1183
1639
  }
1184
1640
 
1185
- const itemNames = [
1186
- "copper",
1187
- "lead",
1188
- "metaglass",
1189
- "graphite",
1190
- "sand",
1191
- "coal",
1192
- "titanium",
1193
- "thorium",
1194
- "scrap",
1195
- "silicon",
1196
- "plastanium",
1197
- "phaseFabric",
1198
- "surgeAlloy",
1199
- "sporePod",
1200
- "blastCompound",
1201
- "pyratite",
1202
- ];
1203
- class Building extends ObjectValue {
1204
- constructor(scope, name) {
1205
- super(scope, {
1206
- $get: new MacroFunction(scope, (prop) => {
1207
- if (prop instanceof LiteralValue && typeof prop.data === "string") {
1208
- const name = itemNames.includes(prop.data)
1209
- ? camelToDashCase(prop.data)
1210
- : prop.data;
1211
- const temp = new TempValue(scope);
1212
- return [
1213
- temp,
1214
- [new InstructionBase("sensor", temp, this, `@${name}`)],
1215
- ];
1216
- }
1217
- if (prop instanceof StoreValue) {
1218
- const temp = new TempValue(scope);
1219
- return [temp, [new InstructionBase("sensor", temp, this, prop)]];
1220
- }
1221
- throw new Error("Building property acessors must be string literals or stores");
1222
- }),
1223
- });
1224
- this.name = name;
1225
- }
1226
- toString() {
1227
- return this.name;
1228
- }
1229
- }
1230
- class BuildingBuilder extends ObjectValue {
1231
- constructor(scope) {
1232
- super(scope, {
1233
- $call: new MacroFunction(scope, (nameLit) => {
1234
- if (!(nameLit instanceof LiteralValue))
1235
- throw new Error("Block name must be a literal.");
1236
- if (typeof nameLit.data !== "string")
1237
- throw new Error("Block name must be a string.");
1238
- return [new Building(scope, nameLit.data), []];
1239
- }),
1240
- });
1241
- }
1242
- }
1243
- for (const key in operatorMap$2) {
1244
- const kind = operatorMap$2[key];
1245
- Building.prototype[key] = function (scope, value) {
1246
- const left = new StoreValue(scope, this.name);
1247
- const [right, rightInst] = value.eval(scope);
1248
- const temp = new TempValue(scope);
1249
- return [
1250
- temp,
1251
- [...rightInst, new OperationInstruction(kind, temp, left, right)],
1252
- ];
1253
- };
1254
- }
1255
-
1256
1641
  class GetLink extends MacroFunction {
1257
1642
  constructor(scope) {
1258
1643
  super(scope, (index) => {
1259
1644
  if (!(index instanceof StoreValue) &&
1260
1645
  (!(index instanceof LiteralValue) || typeof index.data !== "number"))
1261
- throw new Error("The getlink index must be a number literal or a store");
1262
- const temp = new TempValue(scope);
1263
- const outBuild = new Building(scope, temp.name);
1264
- return [outBuild, [new InstructionBase("getlink", temp, index)]];
1646
+ throw new CompilerError("The getlink index must be a number literal or a store");
1647
+ const outBuild = new SenseableValue(scope);
1648
+ return [outBuild, [new InstructionBase("getlink", outBuild, index)]];
1265
1649
  });
1266
1650
  }
1267
1651
  }
@@ -1271,11 +1655,11 @@ class Control extends MacroFunction {
1271
1655
  constructor(scope) {
1272
1656
  super(scope, (kind, building, ...args) => {
1273
1657
  if (!(kind instanceof LiteralValue) || typeof kind.data !== "string")
1274
- throw new Error("The control kind must be a string literal");
1658
+ throw new CompilerError("The control kind must be a string literal");
1275
1659
  if (!validKinds$1.includes(kind.data))
1276
- throw new Error("Invalid control kind");
1277
- if (!(building instanceof ObjectValue))
1278
- throw new Error("The building must be an object value");
1660
+ throw new CompilerError("Invalid control kind");
1661
+ if (!(building instanceof SenseableValue))
1662
+ throw new CompilerError("The building must be a senseable value");
1279
1663
  return [
1280
1664
  null,
1281
1665
  [new InstructionBase("control", kind.data, building, ...args)],
@@ -1284,91 +1668,6 @@ class Control extends MacroFunction {
1284
1668
  }
1285
1669
  }
1286
1670
 
1287
- class NamespaceMacro extends ObjectValue {
1288
- constructor(scope, { changeCasing = false } = {}) {
1289
- super(scope, {
1290
- $get: new MacroFunction(scope, prop => {
1291
- if (!(prop instanceof LiteralValue) || typeof prop.data !== "string")
1292
- throw new Error("Cannot use dynamic properties on object macros");
1293
- const symbolName = this.changeCasing
1294
- ? camelToDashCase(prop.data)
1295
- : prop.data;
1296
- return [new StoreValue(scope, `@${symbolName}`), []];
1297
- }),
1298
- });
1299
- this.changeCasing = changeCasing;
1300
- }
1301
- }
1302
- class VarsNamespace extends NamespaceMacro {
1303
- constructor(scope) {
1304
- super(scope);
1305
- const $get = this.data.$get;
1306
- this.data.$get = new MacroFunction(scope, prop => {
1307
- if (prop instanceof LiteralValue) {
1308
- if (prop.data === "unit")
1309
- return [new Unit(scope, "@unit"), []];
1310
- if (prop.data === "this")
1311
- return [new Building(scope, "@this"), []];
1312
- }
1313
- return $get.call(scope, [prop]);
1314
- });
1315
- }
1316
- }
1317
- class UCommandsNamespace extends NamespaceMacro {
1318
- constructor(scope) {
1319
- super(scope);
1320
- const $get = this.data.$get;
1321
- this.data.$get = new MacroFunction(scope, prop => {
1322
- if (!(prop instanceof LiteralValue) || typeof prop.data !== "string")
1323
- throw new Error("Cannot use dynamic properties on object macros");
1324
- const symbolName = prop.data[0].toUpperCase() + prop.data.slice(1);
1325
- return [new StoreValue(scope, `@command${symbolName}`), []];
1326
- });
1327
- }
1328
- }
1329
- // TODO: repeated logic between UnitMacro and Building
1330
- class Unit extends ObjectValue {
1331
- constructor(scope, name) {
1332
- super(scope, {
1333
- $get: new MacroFunction(scope, prop => {
1334
- if (prop instanceof LiteralValue && typeof prop.data === "string") {
1335
- const name = itemNames.includes(prop.data)
1336
- ? camelToDashCase(prop.data)
1337
- : prop.data;
1338
- const temp = new TempValue(scope);
1339
- // special case, should return another unit or building
1340
- const result = prop.data === "controller" ? new Unit(scope, temp.name) : temp;
1341
- return [
1342
- result,
1343
- [new InstructionBase("sensor", result, this, `@${name}`)],
1344
- ];
1345
- }
1346
- if (prop instanceof StoreValue) {
1347
- const temp = new TempValue(scope);
1348
- return [temp, [new InstructionBase("sensor", temp, this, prop)]];
1349
- }
1350
- throw new Error("Building property acessors must be string literals or stores");
1351
- }),
1352
- });
1353
- this.name = name;
1354
- }
1355
- toString() {
1356
- return this.name;
1357
- }
1358
- }
1359
- for (const key in operatorMap$2) {
1360
- const kind = operatorMap$2[key];
1361
- Unit.prototype[key] = function (scope, value) {
1362
- const left = new StoreValue(scope, this.name);
1363
- const [right, rightInst] = value.eval(scope);
1364
- const temp = new TempValue(scope);
1365
- return [
1366
- temp,
1367
- [...rightInst, new OperationInstruction(kind, temp, left, right)],
1368
- ];
1369
- };
1370
- }
1371
-
1372
1671
  const validRadarFilters = [
1373
1672
  "any",
1374
1673
  "enemy",
@@ -1389,33 +1688,32 @@ const validRadarSorts = [
1389
1688
  class Radar extends MacroFunction {
1390
1689
  constructor(scope) {
1391
1690
  super(scope, (building, filter1, filter2, filter3, order, sort) => {
1392
- if (!(building instanceof ObjectValue))
1393
- throw new Error("The building must a store");
1691
+ if (!(building instanceof SenseableValue))
1692
+ throw new CompilerError("The building must a senseable value");
1394
1693
  if (!(filter1 instanceof LiteralValue) ||
1395
1694
  typeof filter1.data !== "string" ||
1396
1695
  !(filter2 instanceof LiteralValue) ||
1397
1696
  typeof filter2.data !== "string" ||
1398
1697
  !(filter3 instanceof LiteralValue) ||
1399
1698
  typeof filter3.data !== "string")
1400
- throw new Error("The filters must be string literals");
1699
+ throw new CompilerError("The filters must be string literals");
1401
1700
  if (!validRadarFilters.includes(filter1.data))
1402
- throw new Error("Invalid value for filter1");
1701
+ throw new CompilerError("Invalid value for filter1");
1403
1702
  if (!validRadarFilters.includes(filter2.data))
1404
- throw new Error("Invalid value for filter2");
1703
+ throw new CompilerError("Invalid value for filter2");
1405
1704
  if (!validRadarFilters.includes(filter3.data))
1406
- throw new Error("Invalid value for filter3");
1705
+ throw new CompilerError("Invalid value for filter3");
1407
1706
  if (!(order instanceof LiteralValue || order instanceof StoreValue))
1408
- throw new Error("The radar order must be a literal or a store");
1707
+ throw new CompilerError("The radar order must be a literal or a store");
1409
1708
  if (!(sort instanceof LiteralValue) || typeof sort.data !== "string")
1410
- throw new Error("The radar sort must be a string literal");
1709
+ throw new CompilerError("The radar sort must be a string literal");
1411
1710
  if (!validRadarSorts.includes(sort.data))
1412
- throw new Error("Invalid sort value");
1413
- const temp = new TempValue(scope);
1414
- const resultUnit = new Unit(scope, temp.name);
1711
+ throw new CompilerError("Invalid sort value");
1712
+ const outUnit = new SenseableValue(scope);
1415
1713
  return [
1416
- resultUnit,
1714
+ outUnit,
1417
1715
  [
1418
- new InstructionBase("radar", filter1.data, filter2.data, filter3.data, sort.data, building, order, temp),
1716
+ new InstructionBase("radar", filter1.data, filter2.data, filter3.data, sort.data, building, order, outUnit),
1419
1717
  ],
1420
1718
  ];
1421
1719
  });
@@ -1426,11 +1724,11 @@ class Sensor extends MacroFunction {
1426
1724
  constructor(scope) {
1427
1725
  super(scope, (property, target) => {
1428
1726
  if (!(property instanceof StoreValue))
1429
- throw new Error("The sensor property must be a store");
1430
- if (!(target instanceof ObjectValue))
1431
- throw new Error("The sensor target must be a store");
1432
- const temp = new TempValue(scope);
1433
- return [temp, [new InstructionBase("sensor", temp, property, target)]];
1727
+ throw new CompilerError("The sensor property must be a store");
1728
+ if (!(target instanceof SenseableValue))
1729
+ throw new CompilerError("The sensor target must be a senseable value");
1730
+ const temp = new StoreValue(scope);
1731
+ return [temp, [new InstructionBase("sensor", temp, target, property)]];
1434
1732
  });
1435
1733
  }
1436
1734
  }
@@ -1440,7 +1738,7 @@ class Wait extends MacroFunction {
1440
1738
  super(scope, seconds => {
1441
1739
  if (!(seconds instanceof StoreValue) &&
1442
1740
  !(seconds instanceof LiteralValue && typeof seconds.data === "number"))
1443
- throw new Error("The wait seconds must be either a number literal or a store");
1741
+ throw new CompilerError("The wait seconds must be either a number literal or a store");
1444
1742
  return [null, [new InstructionBase("wait", seconds)]];
1445
1743
  });
1446
1744
  }
@@ -1451,23 +1749,29 @@ class Lookup extends MacroFunction {
1451
1749
  constructor(scope) {
1452
1750
  super(scope, (kind, index) => {
1453
1751
  if (!(kind instanceof LiteralValue && typeof kind.data === "string"))
1454
- throw new Error("The lookup kind must be a string literal");
1752
+ throw new CompilerError("The lookup kind must be a string literal");
1455
1753
  if (!validKinds.includes(kind.data))
1456
- throw new Error("Invalid lookup kind");
1754
+ throw new CompilerError("Invalid lookup kind");
1457
1755
  if (!(index instanceof LiteralValue && typeof index.data === "number") &&
1458
1756
  !(index instanceof StoreValue))
1459
- throw new Error("The lookup index must be a number literal or a store");
1460
- const temp = new TempValue(scope);
1757
+ throw new CompilerError("The lookup index must be a number literal or a store");
1758
+ const temp = new StoreValue(scope);
1461
1759
  return [temp, [new InstructionBase("lookup", kind.data, temp, index)]];
1462
1760
  });
1463
1761
  }
1464
1762
  }
1465
1763
 
1764
+ class End extends MacroFunction {
1765
+ constructor(scope) {
1766
+ super(scope, () => [null, [new EndInstruction()]]);
1767
+ }
1768
+ }
1769
+
1466
1770
  class UnitBind extends MacroFunction {
1467
1771
  constructor(scope) {
1468
1772
  super(scope, type => {
1469
1773
  if (!(type instanceof StoreValue))
1470
- throw new Error("The unitBind type must be a store");
1774
+ throw new CompilerError("The unitBind type must be a store");
1471
1775
  return [null, [new InstructionBase("ubind", type)]];
1472
1776
  });
1473
1777
  }
@@ -1497,29 +1801,23 @@ class UnitControl extends MacroFunction {
1497
1801
  constructor(scope) {
1498
1802
  super(scope, (mode, ...args) => {
1499
1803
  if (!(mode instanceof LiteralValue) || typeof mode.data !== "string")
1500
- throw new Error("The unitControl mode must be a string literal");
1804
+ throw new CompilerError("The unitControl mode must be a string literal");
1501
1805
  if (!validModes.includes(mode.data))
1502
- throw new Error("Invalid unitControl mode");
1503
- if (args.some(value => !(value instanceof StoreValue ||
1504
- value instanceof LiteralValue ||
1505
- value instanceof ObjectValue)))
1506
- throw new Error("The others arguments of unitControl must be literals or stores");
1806
+ throw new CompilerError("Invalid unitControl mode");
1807
+ if (args.some(value => !(value instanceof StoreValue || value instanceof LiteralValue)))
1808
+ throw new CompilerError("The others arguments of unitControl must be literals or stores");
1507
1809
  let result = null;
1508
1810
  let extraArgs = [];
1509
1811
  switch (mode.data) {
1510
1812
  case "getBlock": {
1511
- const outType = new TempValue(scope);
1512
- const outBuilding = new TempValue(scope);
1513
- result = new ObjectValue(scope, {
1514
- 0: outType,
1515
- 1: new Building(scope, outBuilding.name),
1516
- length: new LiteralValue(scope, 2),
1517
- });
1813
+ const outType = new StoreValue(scope);
1814
+ const outBuilding = new SenseableValue(scope);
1815
+ result = ObjectValue.fromArray(scope, [outType, outBuilding]);
1518
1816
  extraArgs = [outType, outBuilding, new LiteralValue(scope, 0)];
1519
1817
  break;
1520
1818
  }
1521
1819
  case "within": {
1522
- const temp = new TempValue(scope);
1820
+ const temp = new StoreValue(scope);
1523
1821
  result = temp;
1524
1822
  extraArgs = [temp, new LiteralValue(scope, 0)];
1525
1823
  break;
@@ -1542,24 +1840,25 @@ class UnitRadar extends MacroFunction {
1542
1840
  typeof filter2.data !== "string" ||
1543
1841
  !(filter3 instanceof LiteralValue) ||
1544
1842
  typeof filter3.data !== "string")
1545
- throw new Error("The filters must be string literals");
1843
+ throw new CompilerError("The filters must be string literals");
1546
1844
  if (!validRadarFilters.includes(filter1.data))
1547
- throw new Error("Invalid value for filter1");
1845
+ throw new CompilerError("Invalid value for filter1");
1548
1846
  if (!validRadarFilters.includes(filter2.data))
1549
- throw new Error("Invalid value for filter2");
1847
+ throw new CompilerError("Invalid value for filter2");
1550
1848
  if (!validRadarFilters.includes(filter3.data))
1551
- throw new Error("Invalid value for filter3");
1849
+ throw new CompilerError("Invalid value for filter3");
1552
1850
  if (!(order instanceof LiteralValue || order instanceof StoreValue))
1553
- throw new Error("The radar order must be a literal or a store");
1851
+ throw new CompilerError("The radar order must be a literal or a store");
1554
1852
  if (!(sort instanceof LiteralValue) || typeof sort.data !== "string")
1555
- throw new Error("The radar sort must be a string literal");
1853
+ throw new CompilerError("The radar sort must be a string literal");
1556
1854
  if (!validRadarSorts.includes(sort.data))
1557
- throw new Error("Invalid sort value");
1558
- const temp = new TempValue(scope);
1855
+ throw new CompilerError("Invalid sort value");
1856
+ const outUnit = new SenseableValue(scope);
1559
1857
  return [
1560
- temp,
1858
+ outUnit,
1561
1859
  [
1562
- new InstructionBase("uradar", filter1.data, filter2.data, filter3.data, order, sort.data, temp),
1860
+ new InstructionBase("uradar", filter1.data, filter2.data, filter3.data, sort.data, "0", // I don't know why, but mindustry requires this extra parameter
1861
+ order, outUnit),
1563
1862
  ],
1564
1863
  ];
1565
1864
  });
@@ -1582,15 +1881,15 @@ class UnitLocate extends MacroFunction {
1582
1881
  constructor(scope) {
1583
1882
  super(scope, (find, ...args) => {
1584
1883
  if (!(find instanceof LiteralValue && typeof find.data === "string"))
1585
- throw new Error("The unitLocate find must be a string literal");
1884
+ throw new CompilerError("The unitLocate find must be a string literal");
1586
1885
  if (!validFinds.includes(find.data))
1587
- throw new Error("Invalid find value");
1886
+ throw new CompilerError("Invalid find value");
1588
1887
  if (args.some(value => !(value instanceof StoreValue || value instanceof LiteralValue)))
1589
- throw new Error("The others arguments of unitLocate must be literals or stores");
1590
- const outFound = new TempValue(scope);
1591
- const outX = new TempValue(scope);
1592
- const outY = new TempValue(scope);
1593
- const outBuilding = new TempValue(scope);
1888
+ throw new CompilerError("The others arguments of unitLocate must be literals or stores");
1889
+ const outFound = new StoreValue(scope);
1890
+ const outX = new StoreValue(scope);
1891
+ const outY = new StoreValue(scope);
1892
+ const outBuilding = new SenseableValue(scope);
1594
1893
  const outArgs = [outX, outY, outFound, outBuilding];
1595
1894
  let inputArgs = [];
1596
1895
  switch (find.data) {
@@ -1602,10 +1901,10 @@ class UnitLocate extends MacroFunction {
1602
1901
  case "building": {
1603
1902
  const [group, enemy] = args;
1604
1903
  if (!(group instanceof LiteralValue && typeof group.data === "string")) {
1605
- throw new Error("The building group must be a string literal");
1904
+ throw new CompilerError("The building group must be a string literal");
1606
1905
  }
1607
1906
  if (!validBuildingGroups.includes(group.data))
1608
- throw new Error("Invalid building group");
1907
+ throw new CompilerError("Invalid building group");
1609
1908
  inputArgs = [find.data, group.data, enemy, "@copper"];
1610
1909
  break;
1611
1910
  }
@@ -1616,54 +1915,54 @@ class UnitLocate extends MacroFunction {
1616
1915
  }
1617
1916
  }
1618
1917
  return [
1619
- new ObjectValue(scope, {
1620
- 0: outFound,
1621
- 1: outX,
1622
- 2: outY,
1623
- 3: new Building(scope, outBuilding.name),
1624
- length: new LiteralValue(scope, find.data === "ore" ? 3 : 4),
1625
- }),
1918
+ ObjectValue.fromArray(scope, [
1919
+ outFound,
1920
+ outX,
1921
+ outY,
1922
+ ...(find.data !== "ore" ? [outBuilding] : []),
1923
+ ]),
1626
1924
  [new InstructionBase("ulocate", ...inputArgs, ...outArgs)],
1627
1925
  ];
1628
1926
  });
1629
1927
  }
1630
1928
  }
1631
1929
 
1632
- var index = /*#__PURE__*/Object.freeze({
1633
- __proto__: null,
1634
- Radar: Radar,
1635
- Draw: Draw,
1636
- Print: Print,
1637
- PrintFlush: PrintFlush,
1638
- DrawFlush: DrawFlush,
1639
- GetLink: GetLink,
1640
- Control: Control,
1641
- Sensor: Sensor,
1642
- Wait: Wait,
1643
- Lookup: Lookup,
1644
- UnitBind: UnitBind,
1645
- UnitControl: UnitControl,
1646
- UnitRadar: UnitRadar,
1647
- UnitLocate: UnitLocate
1648
- });
1649
-
1650
1930
  class MemoryEntry extends ObjectValue {
1651
1931
  constructor(scope, mem, prop) {
1652
1932
  super(scope, {
1653
1933
  $eval: new MacroFunction(scope, () => {
1654
- const temp = new TempValue(scope);
1934
+ if (this.store)
1935
+ return [this.store, []];
1936
+ const temp = new StoreValue(scope);
1655
1937
  return [temp, [new InstructionBase("read", temp, mem.cell, prop)]];
1656
1938
  }),
1939
+ $consume: new MacroFunction(scope, () => {
1940
+ if (this.store)
1941
+ return [this.store, []];
1942
+ this.ensureOwned();
1943
+ this.store = new StoreValue(scope);
1944
+ this.owner.own(this.store);
1945
+ return [
1946
+ this.store,
1947
+ [new InstructionBase("read", this.store, mem.cell, prop)],
1948
+ ];
1949
+ }),
1657
1950
  "$=": new MacroFunction(scope, value => {
1658
- return [value, [new InstructionBase("write", value, mem.cell, prop)]];
1951
+ const [data, dataInst] = value.consume(scope);
1952
+ return [
1953
+ data,
1954
+ [...dataInst, new InstructionBase("write", data, mem.cell, prop)],
1955
+ ];
1659
1956
  }),
1660
1957
  });
1958
+ this.store = null;
1661
1959
  }
1662
1960
  }
1663
1961
  for (const operator of operators) {
1664
1962
  if (operator === "=")
1665
1963
  continue;
1666
1964
  MemoryEntry.prototype[operator] = function (...args) {
1965
+ // eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/no-unsafe-return
1667
1966
  return BaseValue.prototype[operator].apply(this, args);
1668
1967
  };
1669
1968
  }
@@ -1672,7 +1971,7 @@ class MemoryMacro extends ObjectValue {
1672
1971
  super(scope, {
1673
1972
  $get: new MacroFunction(scope, (prop) => {
1674
1973
  if (prop instanceof LiteralValue && typeof prop.data !== "number")
1675
- throw new Error(`Invalid memory object property: "${prop.data}"`);
1974
+ throw new CompilerError(`Invalid memory object property: "${prop.data}"`);
1676
1975
  const obj = new MemoryEntry(scope, this, prop);
1677
1976
  return [obj, []];
1678
1977
  }),
@@ -1690,26 +1989,88 @@ class MemoryBuilder extends ObjectValue {
1690
1989
  constructor(scope) {
1691
1990
  super(scope, {
1692
1991
  $call: new MacroFunction(scope, (cell, size = new LiteralValue(scope, 64)) => {
1693
- if (!(cell instanceof ObjectValue))
1694
- throw new Error("Memory cell must be a building object.");
1992
+ if (!(cell instanceof SenseableValue))
1993
+ throw new CompilerError("Memory cell must be a senseable value.");
1695
1994
  if (!(size instanceof LiteralValue && typeof size.data === "number"))
1696
- throw new Error("The memory size must be a number literal.");
1995
+ throw new CompilerError("The memory size must be a number literal.");
1697
1996
  return [new MemoryMacro(scope, cell, size), []];
1698
1997
  }),
1699
1998
  });
1700
1999
  }
1701
2000
  }
1702
2001
 
1703
- class RawValueMacro extends MacroFunction {
2002
+ class NamespaceMacro extends ObjectValue {
2003
+ constructor(scope, { changeCasing = false } = {}) {
2004
+ super(scope, {
2005
+ $get: new MacroFunction(scope, prop => {
2006
+ if (!(prop instanceof LiteralValue) || typeof prop.data !== "string")
2007
+ throw new CompilerError("Cannot use dynamic properties on namespace macros");
2008
+ const symbolName = this.changeCasing
2009
+ ? camelToDashCase(prop.data)
2010
+ : prop.data;
2011
+ const owner = new ValueOwner({
2012
+ scope,
2013
+ value: new StoreValue(scope),
2014
+ name: `@${symbolName}`,
2015
+ });
2016
+ return [owner.value, []];
2017
+ }),
2018
+ });
2019
+ this.changeCasing = changeCasing;
2020
+ }
2021
+ }
2022
+ class VarsNamespace extends NamespaceMacro {
2023
+ constructor(scope) {
2024
+ super(scope);
2025
+ Object.assign(this.data, {
2026
+ unit: new ValueOwner({
2027
+ scope,
2028
+ name: "@unit",
2029
+ value: new SenseableValue(scope),
2030
+ }).value,
2031
+ this: new ValueOwner({
2032
+ scope,
2033
+ name: "@this",
2034
+ value: new SenseableValue(scope),
2035
+ }).value,
2036
+ });
2037
+ }
2038
+ }
2039
+ class UCommandsNamespace extends NamespaceMacro {
2040
+ constructor(scope) {
2041
+ super(scope);
2042
+ this.data.$get = new MacroFunction(scope, prop => {
2043
+ if (!(prop instanceof LiteralValue) || typeof prop.data !== "string")
2044
+ throw new CompilerError("Cannot use dynamic properties on namespace macros");
2045
+ const symbolName = prop.data[0].toUpperCase() + prop.data.slice(1);
2046
+ const owner = new ValueOwner({
2047
+ scope,
2048
+ name: `@command${symbolName}`,
2049
+ value: new StoreValue(scope),
2050
+ });
2051
+ return [owner.value, []];
2052
+ });
2053
+ }
2054
+ }
2055
+
2056
+ class GetGlobal extends MacroFunction {
1704
2057
  constructor(scope) {
1705
2058
  super(scope, (name) => {
1706
2059
  if (!(name instanceof LiteralValue) || typeof name.data !== "string")
1707
- throw new Error("The name of the building must be a string literal.");
1708
- return [new StoreValue(scope, name.data), []];
2060
+ throw new CompilerError("The name parameter must be a string literal.");
2061
+ const owner = new ValueOwner({
2062
+ scope,
2063
+ name: name.data,
2064
+ value: new SenseableValue(scope),
2065
+ });
2066
+ return [owner.value, []];
1709
2067
  });
1710
2068
  }
1711
2069
  }
1712
2070
 
2071
+ /**
2072
+ * Adds all the compiler globals to `scope`
2073
+ */
1713
2074
  function initScope(scope) {
1714
2075
  // namespaces
1715
2076
  scope.hardSet("ControlKind", new NamespaceMacro(scope));
@@ -1721,8 +2082,8 @@ function initScope(scope) {
1721
2082
  scope.hardSet("UnitCommands", new UCommandsNamespace(scope));
1722
2083
  scope.hardSet("Blocks", new NamespaceMacro(scope, { changeCasing: true }));
1723
2084
  // helper methods
1724
- scope.hardSet("getBuilding", new BuildingBuilder(scope));
1725
- scope.hardSet("getVar", new RawValueMacro(scope));
2085
+ scope.hardSet("getBuilding", new GetGlobal(scope));
2086
+ scope.hardSet("getVar", new GetGlobal(scope));
1726
2087
  scope.hardSet("concat", new Concat(scope));
1727
2088
  scope.hardSet("Math", new MlogMath(scope));
1728
2089
  scope.hardSet("Memory", new MemoryBuilder(scope));
@@ -1737,6 +2098,7 @@ function initScope(scope) {
1737
2098
  scope.hardSet("sensor", new Sensor(scope));
1738
2099
  scope.hardSet("wait", new Wait(scope));
1739
2100
  scope.hardSet("lookup", new Lookup(scope));
2101
+ scope.hardSet("endScript", new End(scope));
1740
2102
  scope.hardSet("unitBind", new UnitBind(scope));
1741
2103
  scope.hardSet("unitControl", new UnitControl(scope));
1742
2104
  scope.hardSet("unitRadar", new UnitRadar(scope));
@@ -1744,10 +2106,11 @@ function initScope(scope) {
1744
2106
  }
1745
2107
 
1746
2108
  class Compiler {
1747
- constructor(stackName) {
2109
+ constructor({ stackName, compactNames = false } = {}) {
1748
2110
  this.handlers = handlers;
1749
2111
  this.usingStack = !!stackName;
1750
2112
  this.stackName = stackName;
2113
+ this.compactNames = compactNames;
1751
2114
  }
1752
2115
  compile(script) {
1753
2116
  let output;
@@ -1761,7 +2124,8 @@ class Compiler {
1761
2124
  output = this.serialize(valueInst) + "\n";
1762
2125
  }
1763
2126
  catch (error) {
1764
- return [null, error, error?.nodeStack ?? []];
2127
+ const nodeStack = error instanceof CompilerError ? error.nodeStack : [];
2128
+ return [null, error, nodeStack];
1765
2129
  }
1766
2130
  return [output, null, []];
1767
2131
  }
@@ -1774,44 +2138,50 @@ class Compiler {
1774
2138
  }
1775
2139
  }
1776
2140
  serialize(resLines) {
1777
- const [_, inst] = resLines;
2141
+ const [, inst] = resLines;
1778
2142
  return inst.filter(l => !l.hidden).join("\n");
1779
2143
  }
1780
2144
  parse(script) {
1781
- return acorn.parse(script, {
1782
- locations: true,
1783
- ecmaVersion: "latest",
2145
+ return parser.parse(script, {
2146
+ ranges: true,
2147
+ plugins: ["typescript"],
1784
2148
  });
1785
2149
  }
1786
2150
  handle(scope, node) {
1787
2151
  try {
1788
2152
  const handler = this.handlers[node.type];
1789
2153
  if (!handler)
1790
- throw Error("Missing handler for " + node.type);
2154
+ throw new CompilerError("Missing handler for " + node.type);
1791
2155
  return handler(this, scope, node, null);
1792
2156
  }
1793
2157
  catch (error) {
1794
- error.nodeStack ??= [];
1795
- error.nodeStack.push(node);
2158
+ if (error instanceof CompilerError) {
2159
+ error.nodeStack.push(node);
2160
+ }
1796
2161
  throw error;
1797
2162
  }
1798
2163
  }
1799
2164
  handleEval(scope, node) {
1800
2165
  const [res, inst] = this.handle(scope, node);
1801
- const [evaluated, evaluatedLines] = res.eval(scope);
1802
- return [evaluated, [...inst, ...evaluatedLines]];
2166
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
2167
+ const [evaluated, evaluatedInst] = res.eval(scope);
2168
+ return [evaluated, [...inst, ...evaluatedInst]];
2169
+ }
2170
+ handleConsume(scope, node) {
2171
+ const [res, inst] = this.handle(scope, node);
2172
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
2173
+ const [evaluated, evaluatedInst] = res.consume(scope);
2174
+ return [evaluated, [...inst, ...evaluatedInst]];
1803
2175
  }
1804
2176
  handleMany(scope, nodes, handler = this.handle.bind(this)) {
1805
2177
  const lines = [];
1806
2178
  for (const node of nodes) {
1807
- const [_, nodeLines] = handler(scope, node);
2179
+ const [, nodeLines] = handler(scope, node);
1808
2180
  lines.push(...nodeLines);
1809
2181
  }
1810
2182
  return [null, lines];
1811
2183
  }
1812
2184
  }
1813
2185
 
1814
- const compiler = new Compiler();
1815
- const compile = (script) => compiler.compile(script);
1816
-
1817
- exports.compile = compile;
2186
+ exports.Compiler = Compiler;
2187
+ exports.CompilerError = CompilerError;