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