mlogjs 0.4.6 → 0.4.8
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.
- package/dist/Compiler.d.ts +4 -5
- package/dist/Scope.d.ts +6 -6
- package/dist/assertions.d.ts +7 -7
- package/dist/discardedName.d.ts +5 -0
- package/dist/index.cjs +815 -759
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +815 -759
- package/dist/index.mjs.map +1 -1
- package/dist/instructions/AddressResolver.d.ts +3 -3
- package/dist/instructions/ControlInstructions.d.ts +2 -2
- package/dist/instructions/InstructionBase.d.ts +1 -0
- package/dist/instructions/JumpInstruction.d.ts +2 -2
- package/dist/macros/DynamicArray.d.ts +9 -9
- package/dist/macros/Function.d.ts +3 -4
- package/dist/macros/Math.d.ts +1 -2
- package/dist/macros/Namespace.d.ts +1 -2
- package/dist/macros/util.d.ts +3 -3
- package/dist/types.d.ts +84 -104
- package/dist/utils/constants.d.ts +6 -0
- package/dist/utils/general.d.ts +24 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/instruction.d.ts +5 -0
- package/dist/utils/value.d.ts +36 -0
- package/dist/utils.d.ts +5 -7
- package/dist/values/BaseValue.d.ts +6 -6
- package/dist/values/DestructuringValue.d.ts +16 -3
- package/dist/values/FunctionValue.d.ts +9 -5
- package/dist/values/LazyValue.d.ts +1 -1
- package/dist/values/LiteralValue.d.ts +6 -6
- package/dist/values/ObjectValue.d.ts +5 -8
- package/dist/values/SenseableValue.d.ts +5 -4
- package/dist/values/StoreValue.d.ts +5 -5
- package/dist/values/VoidValue.d.ts +5 -6
- package/dist/values/index.d.ts +1 -0
- package/package.json +1 -1
- package/dist/values/ValueOwner.d.ts +0 -27
package/dist/index.cjs
CHANGED
|
@@ -90,6 +90,8 @@ var EMutability;
|
|
|
90
90
|
EMutability[EMutability["readonly"] = 1] = "readonly";
|
|
91
91
|
/** Constant values, won't change during execution */
|
|
92
92
|
EMutability[EMutability["constant"] = 2] = "constant";
|
|
93
|
+
/** An immutable value that hasn't been initialized yet */
|
|
94
|
+
EMutability[EMutability["init"] = 3] = "init";
|
|
93
95
|
})(EMutability || (EMutability = {}));
|
|
94
96
|
|
|
95
97
|
const arithmeticBinaryOperators = [
|
|
@@ -169,24 +171,20 @@ const operators = [...leftRightOperators, ...singleOperators];
|
|
|
169
171
|
|
|
170
172
|
class VoidValue {
|
|
171
173
|
constructor() {
|
|
172
|
-
this.owner = null;
|
|
173
174
|
this.mutability = EMutability.mutable;
|
|
174
175
|
this.macro = false;
|
|
175
176
|
}
|
|
176
|
-
eval(_scope) {
|
|
177
|
+
eval(_scope, _out) {
|
|
177
178
|
throw new CompilerError(`${this} cannot eval.`);
|
|
178
179
|
}
|
|
179
|
-
|
|
180
|
-
throw new CompilerError(`${this} cannot be consumed.`);
|
|
181
|
-
}
|
|
182
|
-
call(_scope, _args) {
|
|
180
|
+
call(_scope, _args, _out) {
|
|
183
181
|
throw new CompilerError(`${this} cannot call.`);
|
|
184
182
|
}
|
|
185
183
|
get(_scope, _name) {
|
|
186
184
|
throw new CompilerError(`${this} cannot get.`);
|
|
187
185
|
}
|
|
188
|
-
|
|
189
|
-
|
|
186
|
+
paramOuts() {
|
|
187
|
+
return;
|
|
190
188
|
}
|
|
191
189
|
toString() {
|
|
192
190
|
return "void";
|
|
@@ -224,6 +222,176 @@ class OperationInstruction extends InstructionBase {
|
|
|
224
222
|
}
|
|
225
223
|
}
|
|
226
224
|
|
|
225
|
+
/**
|
|
226
|
+
* The prefix for internal variables inside the compiler output
|
|
227
|
+
*/
|
|
228
|
+
const internalPrefix = "&";
|
|
229
|
+
const discardedName = `${internalPrefix}_`;
|
|
230
|
+
const itemNames = [
|
|
231
|
+
"copper",
|
|
232
|
+
"lead",
|
|
233
|
+
"metaglass",
|
|
234
|
+
"graphite",
|
|
235
|
+
"sand",
|
|
236
|
+
"coal",
|
|
237
|
+
"titanium",
|
|
238
|
+
"thorium",
|
|
239
|
+
"scrap",
|
|
240
|
+
"silicon",
|
|
241
|
+
"plastanium",
|
|
242
|
+
"phaseFabric",
|
|
243
|
+
"surgeAlloy",
|
|
244
|
+
"sporePod",
|
|
245
|
+
"blastCompound",
|
|
246
|
+
"pyratite",
|
|
247
|
+
"beryllium",
|
|
248
|
+
"tungsten",
|
|
249
|
+
"oxide",
|
|
250
|
+
"carbide",
|
|
251
|
+
"fissileMatter",
|
|
252
|
+
"dormantCyst",
|
|
253
|
+
];
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Returns a string that has the format: [line]:[column].
|
|
257
|
+
*
|
|
258
|
+
* If `name` is provided, and it is a string, the resulting string will
|
|
259
|
+
* have the format: [name]:[line]:[column].
|
|
260
|
+
*/
|
|
261
|
+
function nodeName(node, name) {
|
|
262
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
263
|
+
const { line, column } = node.loc.start;
|
|
264
|
+
if (typeof name === "string")
|
|
265
|
+
return `${name}:${line}:${column}`;
|
|
266
|
+
return `${line}:${column}`;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Converts a camel case string into a dash case one.
|
|
270
|
+
*
|
|
271
|
+
* As an example `camelCase` becomes `camel-case`
|
|
272
|
+
* @example
|
|
273
|
+
* ```
|
|
274
|
+
* camelToDashCase("camelCase") // returns "camel-case"
|
|
275
|
+
* ```
|
|
276
|
+
*/
|
|
277
|
+
function camelToDashCase(name) {
|
|
278
|
+
return name.replace(/[A-Z]/g, str => `-${str.toLowerCase()}`);
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* A more type safe version of `Object.assign`
|
|
282
|
+
*/
|
|
283
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
284
|
+
function assign(obj, props) {
|
|
285
|
+
return Object.assign(obj, props);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function appendSourceLocations(valueInst, node) {
|
|
289
|
+
for (const inst of valueInst[1]) {
|
|
290
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
291
|
+
inst.source ??= node.loc;
|
|
292
|
+
}
|
|
293
|
+
return valueInst;
|
|
294
|
+
}
|
|
295
|
+
function withAlwaysRuns(valueInst, value) {
|
|
296
|
+
valueInst[1].forEach(inst => (inst.alwaysRuns = value));
|
|
297
|
+
return valueInst;
|
|
298
|
+
}
|
|
299
|
+
/** Removes jump instructions that point right to the next line */
|
|
300
|
+
function hideRedundantJumps(inst) {
|
|
301
|
+
for (let i = 0; i < inst.length; i++) {
|
|
302
|
+
const instruction = inst[i];
|
|
303
|
+
if (!(instruction instanceof JumpInstruction))
|
|
304
|
+
continue;
|
|
305
|
+
const { address } = instruction;
|
|
306
|
+
let searchIndex = i + 1;
|
|
307
|
+
let current = inst[searchIndex];
|
|
308
|
+
while (current?.hidden) {
|
|
309
|
+
if (current instanceof AddressResolver &&
|
|
310
|
+
current.bonds.includes(address)) {
|
|
311
|
+
// only gets assigned if `current` is an adress resolver
|
|
312
|
+
// that resolves the jump adress
|
|
313
|
+
// and that is also right next to the jump,
|
|
314
|
+
// without any actual instructions in between
|
|
315
|
+
instruction.hidden = true;
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
searchIndex++;
|
|
319
|
+
current = inst[searchIndex];
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function isTemplateObjectArray(value) {
|
|
325
|
+
return (value instanceof ObjectValue &&
|
|
326
|
+
value.data.length instanceof LiteralValue &&
|
|
327
|
+
value.data.length.isNumber() &&
|
|
328
|
+
value.data.raw instanceof ObjectValue &&
|
|
329
|
+
value.data.raw.data.length instanceof LiteralValue &&
|
|
330
|
+
value.data.raw.data.length.isNumber());
|
|
331
|
+
}
|
|
332
|
+
function extractOutName(out) {
|
|
333
|
+
if (!out || typeof out === "string")
|
|
334
|
+
return out;
|
|
335
|
+
return out.name;
|
|
336
|
+
}
|
|
337
|
+
function extractDestrucuringOut(out, field) {
|
|
338
|
+
if (out === discardedName)
|
|
339
|
+
return out;
|
|
340
|
+
if (!(out instanceof DestructuringValue))
|
|
341
|
+
return;
|
|
342
|
+
return out.fields[field] ?? discardedName;
|
|
343
|
+
}
|
|
344
|
+
/** Asserts that `value` is a `LiteralValue` that contains a string */
|
|
345
|
+
function assertStringLiteral(value, name) {
|
|
346
|
+
if (!(value instanceof LiteralValue) || !value.isString())
|
|
347
|
+
throw new CompilerError(`${name} must be a string literal`);
|
|
348
|
+
}
|
|
349
|
+
/** Asserts that `value` is a string literal that is part of a given set of `members`*/
|
|
350
|
+
function assertLiteralOneOf(value, members, name) {
|
|
351
|
+
assertStringLiteral(value, name);
|
|
352
|
+
if (!members.includes(value.data))
|
|
353
|
+
throw new CompilerError(`${name} must be one of: ${members
|
|
354
|
+
.slice(0, -1)
|
|
355
|
+
.map(member => `"${member}"`)
|
|
356
|
+
.join(", ")} or "${members[members.length - 1]}"`);
|
|
357
|
+
}
|
|
358
|
+
/** Asserts that `value` is either a store or a literal */
|
|
359
|
+
function assertIsRuntimeValue(value, name) {
|
|
360
|
+
if (!(value instanceof LiteralValue) && !(value instanceof StoreValue))
|
|
361
|
+
throw new CompilerError(`${name} must be a valid runtime value`);
|
|
362
|
+
}
|
|
363
|
+
/** Asserts that `value` is an `ObjectValue` */
|
|
364
|
+
function assertIsObjectMacro(value, name) {
|
|
365
|
+
if (!(value instanceof ObjectValue))
|
|
366
|
+
throw new CompilerError(`${name} must be an object macro`);
|
|
367
|
+
}
|
|
368
|
+
/** Asserts that `value` is an `ObjectValue` that has a length property */
|
|
369
|
+
function assertIsArrayMacro(value, name) {
|
|
370
|
+
if (value instanceof ObjectValue) {
|
|
371
|
+
const { length } = value.data;
|
|
372
|
+
if (length instanceof LiteralValue && length.isNumber())
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
throw new CompilerError(`${name} must be an array macro`);
|
|
376
|
+
}
|
|
377
|
+
/** Asserts that all of the fields described are present on `value`*/
|
|
378
|
+
function assertObjectFields(value, fields) {
|
|
379
|
+
const result = [];
|
|
380
|
+
for (const field of fields) {
|
|
381
|
+
const param = typeof field === "object" ? field : { key: field };
|
|
382
|
+
const item = value.data[param.key];
|
|
383
|
+
if (item) {
|
|
384
|
+
param.validate?.(item);
|
|
385
|
+
result.push(item);
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
if (param.default == undefined)
|
|
389
|
+
throw new CompilerError(`The field "${param.name ?? param.key}" must be present`);
|
|
390
|
+
result.push(param.default);
|
|
391
|
+
}
|
|
392
|
+
return result;
|
|
393
|
+
}
|
|
394
|
+
|
|
227
395
|
class SetInstruction extends InstructionBase {
|
|
228
396
|
constructor(store, value) {
|
|
229
397
|
super("set", store, value);
|
|
@@ -238,12 +406,12 @@ class SetInstruction extends InstructionBase {
|
|
|
238
406
|
const { store, value } = this;
|
|
239
407
|
return (this._hidden ||
|
|
240
408
|
// assignment to discarded value
|
|
241
|
-
|
|
409
|
+
store.name === discardedName ||
|
|
242
410
|
// self assignment of a store
|
|
243
|
-
(!!store.
|
|
411
|
+
(!!store.name &&
|
|
244
412
|
store instanceof StoreValue &&
|
|
245
413
|
value instanceof StoreValue &&
|
|
246
|
-
store.
|
|
414
|
+
store.name === value.name));
|
|
247
415
|
}
|
|
248
416
|
}
|
|
249
417
|
|
|
@@ -323,9 +491,9 @@ class ContinueInstruction extends JumpInstruction {
|
|
|
323
491
|
}
|
|
324
492
|
|
|
325
493
|
class BaseValue extends VoidValue {
|
|
326
|
-
"u-"(scope) {
|
|
327
|
-
const [that, inst] = this.
|
|
328
|
-
const temp =
|
|
494
|
+
"u-"(scope, out) {
|
|
495
|
+
const [that, inst] = this.eval(scope);
|
|
496
|
+
const temp = StoreValue.from(scope, out);
|
|
329
497
|
return [
|
|
330
498
|
temp,
|
|
331
499
|
[
|
|
@@ -334,40 +502,37 @@ class BaseValue extends VoidValue {
|
|
|
334
502
|
],
|
|
335
503
|
];
|
|
336
504
|
}
|
|
337
|
-
"u+"(scope) {
|
|
505
|
+
"u+"(scope, out) {
|
|
338
506
|
// TODO: should it return 0 + this ?
|
|
339
|
-
return this.eval(scope);
|
|
507
|
+
return this.eval(scope, out);
|
|
340
508
|
}
|
|
341
|
-
"!"(scope) {
|
|
342
|
-
const [that, inst] = this.
|
|
343
|
-
const temp =
|
|
509
|
+
"!"(scope, out) {
|
|
510
|
+
const [that, inst] = this.eval(scope);
|
|
511
|
+
const temp = StoreValue.from(scope, out);
|
|
344
512
|
const falseLiteral = new LiteralValue(0);
|
|
345
513
|
return [
|
|
346
514
|
temp,
|
|
347
515
|
[...inst, new OperationInstruction("equal", temp, that, falseLiteral)],
|
|
348
516
|
];
|
|
349
517
|
}
|
|
350
|
-
"~"(scope) {
|
|
351
|
-
const [that, inst] = this.
|
|
352
|
-
const temp =
|
|
518
|
+
"~"(scope, out) {
|
|
519
|
+
const [that, inst] = this.eval(scope);
|
|
520
|
+
const temp = StoreValue.from(scope, out);
|
|
353
521
|
return [temp, [...inst, new OperationInstruction("not", temp, that, null)]];
|
|
354
522
|
}
|
|
355
523
|
// requires special handling
|
|
356
524
|
// the handler should give an object value to allow the lazy evaluation
|
|
357
|
-
"??"(scope, other) {
|
|
358
|
-
|
|
525
|
+
"??"(scope, other, out) {
|
|
526
|
+
if (this instanceof LiteralValue) {
|
|
527
|
+
if (this.data === null)
|
|
528
|
+
return other.eval(scope, out);
|
|
529
|
+
return [this, []];
|
|
530
|
+
}
|
|
531
|
+
const result = SenseableValue.out(scope, out);
|
|
532
|
+
const [left, leftInst] = this.eval(scope, result);
|
|
533
|
+
const [right, rightInst] = other.eval(scope, result);
|
|
359
534
|
const nullLiteral = new LiteralValue(null);
|
|
360
535
|
const endAdress = new LiteralValue(null);
|
|
361
|
-
const [nullTest] = left["=="](scope, nullLiteral);
|
|
362
|
-
if (nullTest instanceof LiteralValue) {
|
|
363
|
-
if (nullTest.data)
|
|
364
|
-
return other.eval(scope);
|
|
365
|
-
else
|
|
366
|
-
return [left, leftInst];
|
|
367
|
-
}
|
|
368
|
-
const [right, rightInst] = other.eval(scope);
|
|
369
|
-
const result = new StoreValue(scope);
|
|
370
|
-
result.ensureOwned();
|
|
371
536
|
return [
|
|
372
537
|
result,
|
|
373
538
|
[
|
|
@@ -407,10 +572,10 @@ const operatorMap$1 = {
|
|
|
407
572
|
};
|
|
408
573
|
for (const key in operatorMap$1) {
|
|
409
574
|
const kind = operatorMap$1[key];
|
|
410
|
-
BaseValue.prototype[key] = function (scope, value) {
|
|
411
|
-
const [left, leftInst] = this.
|
|
412
|
-
const [right, rightInst] = value.
|
|
413
|
-
const temp =
|
|
575
|
+
BaseValue.prototype[key] = function (scope, value, out) {
|
|
576
|
+
const [left, leftInst] = this.eval(scope);
|
|
577
|
+
const [right, rightInst] = value.eval(scope);
|
|
578
|
+
const temp = StoreValue.from(scope, out);
|
|
414
579
|
return [
|
|
415
580
|
temp,
|
|
416
581
|
[
|
|
@@ -438,19 +603,19 @@ const assignmentToBinary = {
|
|
|
438
603
|
"||=": "||",
|
|
439
604
|
"??=": "??",
|
|
440
605
|
};
|
|
441
|
-
for (const
|
|
606
|
+
for (const k in assignmentToBinary) {
|
|
607
|
+
const op = k;
|
|
442
608
|
BaseValue.prototype[op] = function (scope, value) {
|
|
443
|
-
this
|
|
444
|
-
const [opValue, opInst] = this[assignmentToBinary[op]](scope, value);
|
|
609
|
+
const [opValue, opInst] = this[assignmentToBinary[op]](scope, value, this);
|
|
445
610
|
const [retValue, retInst] = this["="](scope, opValue);
|
|
446
611
|
return [retValue, [...opInst, ...retInst]];
|
|
447
612
|
};
|
|
448
613
|
}
|
|
449
614
|
for (const key of updateOperators) {
|
|
450
|
-
BaseValue.prototype[key] = function (scope, prefix) {
|
|
451
|
-
let [ret, inst] = this.
|
|
615
|
+
BaseValue.prototype[key] = function (scope, prefix, out) {
|
|
616
|
+
let [ret, inst] = this.eval(scope);
|
|
452
617
|
if (!prefix) {
|
|
453
|
-
const temp =
|
|
618
|
+
const temp = StoreValue.out(scope, out);
|
|
454
619
|
const [tempValue, tempInst] = temp["="](scope, ret);
|
|
455
620
|
ret = tempValue;
|
|
456
621
|
inst.push(...tempInst);
|
|
@@ -462,7 +627,7 @@ for (const key of updateOperators) {
|
|
|
462
627
|
|
|
463
628
|
const literalMethods = {
|
|
464
629
|
length: function () {
|
|
465
|
-
if (
|
|
630
|
+
if (!this.isString())
|
|
466
631
|
throw new CompilerError("Length method only works on string literal values.");
|
|
467
632
|
return new LiteralValue(this.data.length);
|
|
468
633
|
},
|
|
@@ -476,14 +641,11 @@ class LiteralValue extends BaseValue {
|
|
|
476
641
|
eval(_scope) {
|
|
477
642
|
return [this, []];
|
|
478
643
|
}
|
|
479
|
-
consume(_scope) {
|
|
480
|
-
return [this, []];
|
|
481
|
-
}
|
|
482
644
|
toString() {
|
|
483
645
|
return JSON.stringify(this.data);
|
|
484
646
|
}
|
|
485
647
|
get(scope, name) {
|
|
486
|
-
if (!(name instanceof LiteralValue &&
|
|
648
|
+
if (!(name instanceof LiteralValue && name.isString()))
|
|
487
649
|
return super.get(scope, name);
|
|
488
650
|
const method = literalMethods[name.data];
|
|
489
651
|
if (!method)
|
|
@@ -500,7 +662,12 @@ class LiteralValue extends BaseValue {
|
|
|
500
662
|
typeof() {
|
|
501
663
|
return [new LiteralValue("literal"), []];
|
|
502
664
|
}
|
|
503
|
-
|
|
665
|
+
isString() {
|
|
666
|
+
return typeof this.data === "string";
|
|
667
|
+
}
|
|
668
|
+
isNumber() {
|
|
669
|
+
return typeof this.data === "number";
|
|
670
|
+
}
|
|
504
671
|
}
|
|
505
672
|
const operatorMap = {
|
|
506
673
|
"==": (a, b) => +(a == b),
|
|
@@ -526,11 +693,22 @@ const operatorMap = {
|
|
|
526
693
|
"&&": (a, b) => +(a && b),
|
|
527
694
|
"||": (a, b) => +(a || b),
|
|
528
695
|
};
|
|
529
|
-
for (const
|
|
696
|
+
for (const k in operatorMap) {
|
|
697
|
+
const key = k;
|
|
530
698
|
const fn = operatorMap[key];
|
|
531
|
-
LiteralValue.prototype[key] = function (scope, value) {
|
|
699
|
+
LiteralValue.prototype[key] = function (scope, value, out) {
|
|
700
|
+
if (key === "&&") {
|
|
701
|
+
if (this.data)
|
|
702
|
+
return [value, []];
|
|
703
|
+
return [new LiteralValue(0), []];
|
|
704
|
+
}
|
|
705
|
+
if (key === "||") {
|
|
706
|
+
if (!this.data)
|
|
707
|
+
return [value, []];
|
|
708
|
+
return [new LiteralValue(1), []];
|
|
709
|
+
}
|
|
532
710
|
if (!(value instanceof LiteralValue)) {
|
|
533
|
-
return BaseValue.prototype[key].apply(this, [scope, value]);
|
|
711
|
+
return BaseValue.prototype[key].apply(this, [scope, value, out]);
|
|
534
712
|
}
|
|
535
713
|
return [new LiteralValue(fn(this.data, value.data)), []];
|
|
536
714
|
};
|
|
@@ -548,162 +726,45 @@ for (const key in unaryOperatorMap) {
|
|
|
548
726
|
};
|
|
549
727
|
}
|
|
550
728
|
|
|
551
|
-
/**
|
|
552
|
-
* The prefix for internal variables inside the compiler output
|
|
553
|
-
*/
|
|
554
|
-
const internalPrefix = "&";
|
|
555
|
-
const discardedName = `${internalPrefix}_`;
|
|
556
|
-
/**
|
|
557
|
-
* Returns a string that has the format: [line]:[column].
|
|
558
|
-
*
|
|
559
|
-
* If `name` is provided, and it is a string, the resulting string will
|
|
560
|
-
* have the format: [name]:[line]:[column].
|
|
561
|
-
*/
|
|
562
|
-
function nodeName(node, name) {
|
|
563
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
564
|
-
const { line, column } = node.loc.start;
|
|
565
|
-
if (typeof name === "string")
|
|
566
|
-
return `${name}:${line}:${column}`;
|
|
567
|
-
return `${line}:${column}`;
|
|
568
|
-
}
|
|
569
|
-
/**
|
|
570
|
-
* Converts a camel case string into a dash case one.
|
|
571
|
-
*
|
|
572
|
-
* As an example `camelCase` becomes `camel-case`
|
|
573
|
-
* @example
|
|
574
|
-
* ```
|
|
575
|
-
* camelToDashCase("camelCase") // returns "camel-case"
|
|
576
|
-
* ```
|
|
577
|
-
*/
|
|
578
|
-
function camelToDashCase(name) {
|
|
579
|
-
return name.replace(/[A-Z]/g, str => `-${str.toLowerCase()}`);
|
|
580
|
-
}
|
|
581
|
-
const itemNames = [
|
|
582
|
-
"copper",
|
|
583
|
-
"lead",
|
|
584
|
-
"metaglass",
|
|
585
|
-
"graphite",
|
|
586
|
-
"sand",
|
|
587
|
-
"coal",
|
|
588
|
-
"titanium",
|
|
589
|
-
"thorium",
|
|
590
|
-
"scrap",
|
|
591
|
-
"silicon",
|
|
592
|
-
"plastanium",
|
|
593
|
-
"phaseFabric",
|
|
594
|
-
"surgeAlloy",
|
|
595
|
-
"sporePod",
|
|
596
|
-
"blastCompound",
|
|
597
|
-
"pyratite",
|
|
598
|
-
"beryllium",
|
|
599
|
-
"tungsten",
|
|
600
|
-
"oxide",
|
|
601
|
-
"carbide",
|
|
602
|
-
"fissileMatter",
|
|
603
|
-
"dormantCyst",
|
|
604
|
-
];
|
|
605
|
-
/**
|
|
606
|
-
* A more type safe version of `Object.assign`
|
|
607
|
-
*/
|
|
608
|
-
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
609
|
-
function assign(obj, props) {
|
|
610
|
-
return Object.assign(obj, props);
|
|
611
|
-
}
|
|
612
|
-
function appendSourceLocations(valueInst, node) {
|
|
613
|
-
for (const inst of valueInst[1]) {
|
|
614
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
615
|
-
inst.source ??= node.loc;
|
|
616
|
-
}
|
|
617
|
-
return valueInst;
|
|
618
|
-
}
|
|
619
|
-
function withAlwaysRuns(valueInst, value) {
|
|
620
|
-
valueInst[1].forEach(inst => (inst.alwaysRuns = value));
|
|
621
|
-
return valueInst;
|
|
622
|
-
}
|
|
623
|
-
function isTemplateObjectArray(value) {
|
|
624
|
-
return (value instanceof ObjectValue &&
|
|
625
|
-
value.data.length instanceof LiteralValue &&
|
|
626
|
-
typeof value.data.length.data === "number" &&
|
|
627
|
-
value.data.raw instanceof ObjectValue &&
|
|
628
|
-
value.data.raw.data.length instanceof LiteralValue &&
|
|
629
|
-
typeof value.data.raw.data.length.data === "number");
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
class ValueOwner {
|
|
633
|
-
/**
|
|
634
|
-
* Creates a new `ValueOwner`, if `value` is already owned,
|
|
635
|
-
* `this` will be aliased to `value`, else `this` will own `value`
|
|
636
|
-
*/
|
|
637
|
-
constructor({ scope, constant = false, value, identifier, name, }) {
|
|
638
|
-
this.scope = scope;
|
|
639
|
-
this.constant = constant;
|
|
640
|
-
this.value = value;
|
|
641
|
-
this.identifier = identifier;
|
|
642
|
-
this.name = name ?? scope.makeTempName();
|
|
643
|
-
this.persistent = !!name;
|
|
644
|
-
this.owned = new Set();
|
|
645
|
-
if (!value.owner?.persistent)
|
|
646
|
-
this.own(value);
|
|
647
|
-
}
|
|
648
|
-
own(target) {
|
|
649
|
-
target.owner = this;
|
|
650
|
-
this.owned.add(target);
|
|
651
|
-
}
|
|
652
|
-
replace(target) {
|
|
653
|
-
this.value.owner = null;
|
|
654
|
-
this.owned.delete(this.value);
|
|
655
|
-
this.value = target;
|
|
656
|
-
this.own(target);
|
|
657
|
-
}
|
|
658
|
-
moveInto(owner) {
|
|
659
|
-
this.owned.forEach(value => owner.own(value));
|
|
660
|
-
this.owned.clear();
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
|
|
664
729
|
/**
|
|
665
730
|
* `StoreValue` represents values unknown at compile time,
|
|
666
731
|
* mostly used with mutable variables and temporary values
|
|
667
732
|
*/
|
|
668
733
|
class StoreValue extends BaseValue {
|
|
669
|
-
constructor(
|
|
734
|
+
constructor(name, mutability = EMutability.mutable) {
|
|
670
735
|
super();
|
|
671
|
-
this.
|
|
672
|
-
this.mutability =
|
|
736
|
+
this.name = name;
|
|
737
|
+
this.mutability = mutability;
|
|
738
|
+
}
|
|
739
|
+
static from(scope, out, mutability = EMutability.mutable) {
|
|
740
|
+
if (out instanceof StoreValue)
|
|
741
|
+
return out;
|
|
742
|
+
const name = typeof out === "string" ? out : scope.makeTempName();
|
|
743
|
+
return new StoreValue(name, mutability);
|
|
744
|
+
}
|
|
745
|
+
static out(scope, out, mutability = EMutability.mutable) {
|
|
746
|
+
if (!out || typeof out === "string") {
|
|
747
|
+
return new StoreValue(out ?? scope.makeTempName(), mutability);
|
|
748
|
+
}
|
|
749
|
+
return out;
|
|
673
750
|
}
|
|
674
751
|
typeof() {
|
|
675
752
|
return [new LiteralValue("store"), []];
|
|
676
753
|
}
|
|
677
754
|
"="(scope, value) {
|
|
678
|
-
if (this.mutability !== EMutability.mutable
|
|
755
|
+
if (this.mutability !== EMutability.mutable &&
|
|
756
|
+
this.mutability !== EMutability.init)
|
|
679
757
|
throw new CompilerError(`Cannot assign to immutable store '${this}'.`);
|
|
680
|
-
if (this.
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
if (!value.owner?.persistent) {
|
|
684
|
-
if (!value.owner)
|
|
685
|
-
this.owner.own(value);
|
|
686
|
-
else
|
|
687
|
-
value.owner.moveInto(this.owner);
|
|
688
|
-
if (value instanceof StoreValue)
|
|
689
|
-
return [this, []];
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
const [evalValue, evalInst] = value.consume(scope);
|
|
758
|
+
if (this.name === value.name && value instanceof StoreValue)
|
|
759
|
+
return [this, []];
|
|
760
|
+
const [evalValue, evalInst] = value.eval(scope, this);
|
|
693
761
|
return [this, [...evalInst, new SetInstruction(this, evalValue)]];
|
|
694
762
|
}
|
|
695
763
|
eval(_scope) {
|
|
696
764
|
return [this, []];
|
|
697
765
|
}
|
|
698
|
-
consume(_scope) {
|
|
699
|
-
this.ensureOwned();
|
|
700
|
-
return [this, []];
|
|
701
|
-
}
|
|
702
|
-
ensureOwned() {
|
|
703
|
-
this.owner ??= new ValueOwner({ scope: this.scope, value: this });
|
|
704
|
-
}
|
|
705
766
|
toString() {
|
|
706
|
-
return this.
|
|
767
|
+
return this.name;
|
|
707
768
|
}
|
|
708
769
|
}
|
|
709
770
|
|
|
@@ -725,10 +786,13 @@ class ObjectValue extends VoidValue {
|
|
|
725
786
|
});
|
|
726
787
|
return new ObjectValue(data);
|
|
727
788
|
}
|
|
728
|
-
typeof() {
|
|
789
|
+
typeof(scope, out) {
|
|
790
|
+
const { $typeof } = this.data;
|
|
791
|
+
if ($typeof)
|
|
792
|
+
return $typeof.call(scope, [], out);
|
|
729
793
|
return [new LiteralValue("object"), []];
|
|
730
794
|
}
|
|
731
|
-
get(scope, key) {
|
|
795
|
+
get(scope, key, out) {
|
|
732
796
|
// avoids naming collisions with keys like
|
|
733
797
|
// constructor or toString
|
|
734
798
|
if (Object.prototype.hasOwnProperty.call(this.data, key.data)) {
|
|
@@ -739,40 +803,45 @@ class ObjectValue extends VoidValue {
|
|
|
739
803
|
const { $get } = this.data;
|
|
740
804
|
if (!$get)
|
|
741
805
|
throw new CompilerError("Cannot get undefined member.");
|
|
742
|
-
return $get.call(scope, [key]);
|
|
806
|
+
return $get.call(scope, [key], out);
|
|
743
807
|
}
|
|
744
|
-
eval(scope) {
|
|
808
|
+
eval(scope, out) {
|
|
745
809
|
const { $eval } = this.data;
|
|
746
810
|
if (!$eval)
|
|
747
811
|
return [this, []];
|
|
748
|
-
return $eval.call(scope, []);
|
|
749
|
-
}
|
|
750
|
-
consume(scope) {
|
|
751
|
-
const { $consume } = this.data;
|
|
752
|
-
if ($consume)
|
|
753
|
-
return $consume.call(scope, []);
|
|
754
|
-
const result = this.eval(scope);
|
|
755
|
-
// required by typescript
|
|
756
|
-
const res = result[0];
|
|
757
|
-
res.ensureOwned();
|
|
758
|
-
return result;
|
|
812
|
+
return $eval.call(scope, [], out);
|
|
759
813
|
}
|
|
760
|
-
call(scope, args) {
|
|
814
|
+
call(scope, args, out) {
|
|
761
815
|
const { $call } = this.data;
|
|
762
816
|
if (!$call)
|
|
763
|
-
return super.call(scope, args);
|
|
764
|
-
return $call.call(scope, args);
|
|
817
|
+
return super.call(scope, args, out);
|
|
818
|
+
return $call.call(scope, args, out);
|
|
765
819
|
}
|
|
766
|
-
ensureOwned() { }
|
|
767
820
|
}
|
|
768
|
-
for (const op of
|
|
769
|
-
ObjectValue.prototype[op] = function (
|
|
821
|
+
for (const op of leftRightOperators) {
|
|
822
|
+
ObjectValue.prototype[op] = function (scope, value, out) {
|
|
823
|
+
const $ = this.data[`$${op}`];
|
|
824
|
+
if (!$)
|
|
825
|
+
return VoidValue.prototype[op].apply(this, [scope, value, out]);
|
|
826
|
+
return $.call(scope, [value], out);
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
for (const op of unaryOperators) {
|
|
830
|
+
if (op === "typeof")
|
|
831
|
+
continue;
|
|
832
|
+
ObjectValue.prototype[op] = function (scope, out) {
|
|
833
|
+
const $ = this.data[`$${op}`];
|
|
834
|
+
if (!$)
|
|
835
|
+
return VoidValue.prototype[op].apply(this, [scope, out]);
|
|
836
|
+
return $.call(scope, [], out);
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
for (const op of updateOperators) {
|
|
840
|
+
ObjectValue.prototype[op] = function (scope, prefix, out) {
|
|
770
841
|
const $ = this.data[`$${op}`];
|
|
771
|
-
// eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/no-unsafe-return
|
|
772
842
|
if (!$)
|
|
773
|
-
return VoidValue.prototype[op].apply(this,
|
|
774
|
-
|
|
775
|
-
return $.call(scope, fnArgs);
|
|
843
|
+
return VoidValue.prototype[op].apply(this, [scope, prefix, out]);
|
|
844
|
+
return $.call(scope, [new LiteralValue(+prefix)], out);
|
|
776
845
|
};
|
|
777
846
|
}
|
|
778
847
|
|
|
@@ -797,27 +866,37 @@ const senseableProps = ["controller"];
|
|
|
797
866
|
* ```
|
|
798
867
|
*/
|
|
799
868
|
class SenseableValue extends StoreValue {
|
|
800
|
-
constructor(
|
|
801
|
-
super(
|
|
802
|
-
|
|
869
|
+
constructor(name, mutability = EMutability.constant) {
|
|
870
|
+
super(name, mutability);
|
|
871
|
+
}
|
|
872
|
+
static from(scope, out, mutability = EMutability.constant) {
|
|
873
|
+
if (out instanceof SenseableValue)
|
|
874
|
+
return out;
|
|
875
|
+
const name = typeof out === "string" ? out : scope.makeTempName();
|
|
876
|
+
return new SenseableValue(name, mutability);
|
|
877
|
+
}
|
|
878
|
+
static out(scope, out, mutability = EMutability.mutable) {
|
|
879
|
+
if (!out || typeof out === "string") {
|
|
880
|
+
return new SenseableValue(out ?? scope.makeTempName(), mutability);
|
|
881
|
+
}
|
|
882
|
+
return out;
|
|
803
883
|
}
|
|
804
|
-
get(scope, prop) {
|
|
805
|
-
|
|
884
|
+
get(scope, prop, out) {
|
|
885
|
+
const mutability = EMutability.readonly;
|
|
886
|
+
if (prop instanceof LiteralValue && prop.isString()) {
|
|
806
887
|
const name = itemNames.includes(prop.data)
|
|
807
888
|
? camelToDashCase(prop.data)
|
|
808
889
|
: prop.data;
|
|
809
890
|
const result = senseableProps.includes(prop.data)
|
|
810
|
-
?
|
|
811
|
-
:
|
|
891
|
+
? SenseableValue.from(scope, out, mutability)
|
|
892
|
+
: StoreValue.from(scope, out, mutability);
|
|
812
893
|
return [
|
|
813
894
|
result,
|
|
814
895
|
[new InstructionBase("sensor", result, this, `@${name}`)],
|
|
815
896
|
];
|
|
816
897
|
}
|
|
817
898
|
if (prop instanceof StoreValue) {
|
|
818
|
-
const temp =
|
|
819
|
-
mutability: EMutability.readonly,
|
|
820
|
-
});
|
|
899
|
+
const temp = SenseableValue.from(scope, out, mutability);
|
|
821
900
|
return [temp, [new InstructionBase("sensor", temp, this, prop)]];
|
|
822
901
|
}
|
|
823
902
|
throw new CompilerError(`Cannot sense "${prop}" property`);
|
|
@@ -836,21 +915,34 @@ class DestructuringValue extends VoidValue {
|
|
|
836
915
|
super();
|
|
837
916
|
this.members = members;
|
|
838
917
|
this.macro = true;
|
|
918
|
+
this.fields = {};
|
|
919
|
+
for (const [key, { value }] of this.members) {
|
|
920
|
+
if (key instanceof LiteralValue && (key.isString() || key.isNumber())) {
|
|
921
|
+
this.fields[key.data] = value;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
839
924
|
}
|
|
840
925
|
"="(scope, right) {
|
|
841
926
|
const inst = [];
|
|
842
|
-
for (const [key, value] of this.members) {
|
|
843
|
-
const [
|
|
844
|
-
inst.push(...
|
|
845
|
-
inst.push(...value["="](scope, item)[1]);
|
|
927
|
+
for (const [key, { value, handler }] of this.members) {
|
|
928
|
+
const [, handlerInst] = handler(() => right.get(scope, key, value));
|
|
929
|
+
inst.push(...handlerInst);
|
|
846
930
|
}
|
|
847
931
|
return [right, inst];
|
|
848
932
|
}
|
|
849
933
|
eval(_scope) {
|
|
850
934
|
return [this, []];
|
|
851
935
|
}
|
|
852
|
-
|
|
853
|
-
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
/**
|
|
939
|
+
* This class is used to handle some cases where the value
|
|
940
|
+
* can't be directly evaluated by the node handler
|
|
941
|
+
*/
|
|
942
|
+
class LazyValue extends VoidValue {
|
|
943
|
+
constructor(evalutate) {
|
|
944
|
+
super();
|
|
945
|
+
this.eval = evalutate;
|
|
854
946
|
}
|
|
855
947
|
}
|
|
856
948
|
|
|
@@ -887,46 +979,29 @@ class Scope {
|
|
|
887
979
|
return false;
|
|
888
980
|
}
|
|
889
981
|
get(identifier) {
|
|
890
|
-
const
|
|
891
|
-
if (
|
|
892
|
-
return
|
|
982
|
+
const value = this.data[identifier];
|
|
983
|
+
if (value)
|
|
984
|
+
return value;
|
|
893
985
|
if (this.parent)
|
|
894
986
|
return this.parent.get(identifier);
|
|
895
987
|
throw new CompilerError(`${identifier} is not declared.`);
|
|
896
988
|
}
|
|
897
|
-
set(
|
|
898
|
-
const name = args.length === 1 ? args[0].identifier : args[0];
|
|
899
|
-
if (!name)
|
|
900
|
-
throw new CompilerError("Values in a scope must have an identifier");
|
|
989
|
+
set(name, value) {
|
|
901
990
|
if (name in this.data)
|
|
902
991
|
throw new CompilerError(`${name} is already declared.`);
|
|
903
|
-
return this.hardSet(
|
|
904
|
-
}
|
|
905
|
-
hardSet(
|
|
906
|
-
|
|
907
|
-
? args[0]
|
|
908
|
-
: new ValueOwner({
|
|
909
|
-
scope: this,
|
|
910
|
-
identifier: args[0],
|
|
911
|
-
name: args[0],
|
|
912
|
-
value: args[1],
|
|
913
|
-
});
|
|
914
|
-
const { identifier } = owner;
|
|
915
|
-
if (!identifier)
|
|
992
|
+
return this.hardSet(name, value);
|
|
993
|
+
}
|
|
994
|
+
hardSet(name, value) {
|
|
995
|
+
if (!name)
|
|
916
996
|
throw new CompilerError("Values in a scope must have an identifier");
|
|
917
|
-
this.data[
|
|
918
|
-
return
|
|
997
|
+
this.data[name] = value;
|
|
998
|
+
return value;
|
|
919
999
|
}
|
|
920
1000
|
make(identifier, name) {
|
|
921
|
-
const
|
|
922
|
-
|
|
923
|
-
value: assign(new SenseableValue(this), {
|
|
924
|
-
mutability: EMutability.mutable,
|
|
925
|
-
}),
|
|
926
|
-
identifier,
|
|
927
|
-
name,
|
|
1001
|
+
const value = assign(new SenseableValue(name), {
|
|
1002
|
+
mutability: EMutability.mutable,
|
|
928
1003
|
});
|
|
929
|
-
return this.set(
|
|
1004
|
+
return this.set(identifier, value);
|
|
930
1005
|
}
|
|
931
1006
|
makeTempName() {
|
|
932
1007
|
let result = `${internalPrefix}t${this.ntemp}`;
|
|
@@ -942,163 +1017,222 @@ const Program = (c, scope, node) => {
|
|
|
942
1017
|
};
|
|
943
1018
|
|
|
944
1019
|
const VariableDeclaration = (c, scope, node) => {
|
|
945
|
-
return c.handleMany(scope, node.declarations, child => VariableDeclarator(c, scope, child, node.kind));
|
|
1020
|
+
return c.handleMany(scope, node.declarations, child => VariableDeclarator(c, scope, child, undefined, node.kind));
|
|
946
1021
|
};
|
|
947
|
-
const VariableDeclarator = (c, scope, node, kind = "let") => {
|
|
948
|
-
const
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
1022
|
+
const VariableDeclarator = (c, scope, node, out, kind = "let") => {
|
|
1023
|
+
const { init } = node;
|
|
1024
|
+
const [value, inst] = Declare(c, scope, node.id, kind);
|
|
1025
|
+
if (init) {
|
|
1026
|
+
const [result, resultInst] = c.handleEval(scope, init, value.out);
|
|
1027
|
+
inst.push(...value.handler(result, resultInst)[1]);
|
|
1028
|
+
}
|
|
1029
|
+
else {
|
|
1030
|
+
inst.push(...value.handler(null, [])[1]);
|
|
1031
|
+
}
|
|
952
1032
|
return [null, inst];
|
|
953
1033
|
};
|
|
954
|
-
const Declare = (c, scope, node, kind
|
|
1034
|
+
const Declare = (c, scope, node, kind) => {
|
|
955
1035
|
return c.handle(scope, node, () => {
|
|
956
1036
|
switch (node.type) {
|
|
957
1037
|
case "Identifier":
|
|
958
|
-
return DeclareIdentifier(c, scope, node, kind
|
|
1038
|
+
return DeclareIdentifier(c, scope, node, kind);
|
|
959
1039
|
case "ArrayPattern":
|
|
960
|
-
return DeclareArrayPattern(c, scope, node, kind
|
|
1040
|
+
return DeclareArrayPattern(c, scope, node, kind);
|
|
961
1041
|
case "ObjectPattern":
|
|
962
|
-
return DeclareObjectPattern(c, scope, node, kind
|
|
1042
|
+
return DeclareObjectPattern(c, scope, node, kind);
|
|
963
1043
|
default:
|
|
964
1044
|
throw new CompilerError(`Unsupported declaration type: ${node.type}`, node);
|
|
965
1045
|
}
|
|
966
1046
|
});
|
|
967
1047
|
};
|
|
968
|
-
const DeclareIdentifier = (c, scope, node, kind
|
|
1048
|
+
const DeclareIdentifier = (c, scope, node, kind) => {
|
|
969
1049
|
const { name: identifier } = node;
|
|
970
1050
|
const name = nodeName(node, !c.compactNames && identifier);
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
1051
|
+
const out = SenseableValue.from(scope, name, kind === "const" ? EMutability.init : EMutability.mutable);
|
|
1052
|
+
const declarationValue = new DeclarationValue({
|
|
1053
|
+
c,
|
|
1054
|
+
scope,
|
|
1055
|
+
node,
|
|
1056
|
+
out,
|
|
1057
|
+
handler(init, inst) {
|
|
1058
|
+
if (kind === "const" && !init)
|
|
1059
|
+
throw new CompilerError("Constants must be initialized.", node);
|
|
1060
|
+
if (kind === "const" && init?.mutability === EMutability.constant) {
|
|
1061
|
+
scope.set(identifier, init);
|
|
1062
|
+
return [null, inst];
|
|
1063
|
+
}
|
|
1064
|
+
else {
|
|
1065
|
+
const value = scope.make(identifier, name);
|
|
1066
|
+
if (init) {
|
|
1067
|
+
if (init.macro)
|
|
1068
|
+
throw new CompilerError("Macro values must be held by constants", node);
|
|
1069
|
+
inst.push(...value["="](scope, init)[1]);
|
|
1070
|
+
}
|
|
1071
|
+
if (kind === "const")
|
|
1072
|
+
value.mutability = EMutability.constant;
|
|
1073
|
+
return [null, inst];
|
|
1074
|
+
}
|
|
1075
|
+
},
|
|
1076
|
+
});
|
|
1077
|
+
return [declarationValue, []];
|
|
996
1078
|
};
|
|
997
|
-
const DeclareArrayPattern = (c, scope, node, kind
|
|
1079
|
+
const DeclareArrayPattern = (c, scope, node, kind) => {
|
|
1080
|
+
const members = new Map();
|
|
998
1081
|
const { elements } = node;
|
|
999
|
-
if (!(init instanceof ObjectValue))
|
|
1000
|
-
throw new CompilerError("The value being destructured must be an object value");
|
|
1001
1082
|
const inst = [];
|
|
1002
1083
|
for (let i = 0; i < elements.length; i++) {
|
|
1003
1084
|
const element = elements[i];
|
|
1004
1085
|
if (!element)
|
|
1005
1086
|
continue;
|
|
1006
|
-
const
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1087
|
+
const [value, valueInst] = Declare(c, scope, element, kind);
|
|
1088
|
+
inst.push(...valueInst);
|
|
1089
|
+
members.set(new LiteralValue(i), {
|
|
1090
|
+
value: value.out,
|
|
1091
|
+
handler(get) {
|
|
1092
|
+
return c.handle(scope, element, () => {
|
|
1093
|
+
let [init, initInst] = [null, []];
|
|
1094
|
+
try {
|
|
1095
|
+
[init, initInst] = get();
|
|
1096
|
+
}
|
|
1097
|
+
catch (e) {
|
|
1098
|
+
// catches the "cannot get undefined member" error
|
|
1099
|
+
// TODO: refactor and add error codes
|
|
1100
|
+
// so we don't have to do string checks
|
|
1101
|
+
if (!(e instanceof CompilerError) ||
|
|
1102
|
+
!e.message.includes("undefined member"))
|
|
1103
|
+
throw e;
|
|
1104
|
+
}
|
|
1105
|
+
if (!init)
|
|
1106
|
+
throw new CompilerError(`The target object does not have a value at index ${i}`, element);
|
|
1107
|
+
return value.handler(init, initInst);
|
|
1108
|
+
});
|
|
1109
|
+
},
|
|
1110
|
+
});
|
|
1010
1111
|
}
|
|
1011
|
-
|
|
1112
|
+
const out = new DestructuringValue(members);
|
|
1113
|
+
const value = new DeclarationValue({
|
|
1114
|
+
c,
|
|
1115
|
+
scope,
|
|
1116
|
+
node,
|
|
1117
|
+
out,
|
|
1118
|
+
handler(init, initInst) {
|
|
1119
|
+
if (!(init instanceof ObjectValue))
|
|
1120
|
+
throw new CompilerError("The value being destructured must be an object value");
|
|
1121
|
+
initInst.push(...out["="](scope, init)[1]);
|
|
1122
|
+
return [null, initInst];
|
|
1123
|
+
},
|
|
1124
|
+
});
|
|
1125
|
+
return [value, inst];
|
|
1012
1126
|
};
|
|
1013
|
-
const DeclareObjectPattern = (c, scope, node, kind
|
|
1014
|
-
|
|
1015
|
-
|
|
1127
|
+
const DeclareObjectPattern = (c, scope, node, kind) => {
|
|
1128
|
+
const inst = [];
|
|
1129
|
+
const members = new Map();
|
|
1016
1130
|
const { properties } = node;
|
|
1017
|
-
const [init, inst] = base.consume(scope);
|
|
1018
1131
|
const propertiesInst = c.handleMany(scope, properties, prop => {
|
|
1019
1132
|
if (prop.type === "RestElement")
|
|
1020
1133
|
throw new CompilerError("The rest operator is not supported", prop);
|
|
1021
|
-
const { key, value } = prop;
|
|
1134
|
+
const { key, value: propValue } = prop;
|
|
1022
1135
|
const keyInst = key.type === "Identifier" && !prop.computed
|
|
1023
1136
|
? [new LiteralValue(key.name), []]
|
|
1024
|
-
: c.
|
|
1025
|
-
const
|
|
1026
|
-
|
|
1027
|
-
|
|
1137
|
+
: c.handleEval(scope, prop.key);
|
|
1138
|
+
const [value, valueInst] = Declare(c, scope, propValue, kind);
|
|
1139
|
+
members.set(keyInst[0], {
|
|
1140
|
+
value: value.out,
|
|
1141
|
+
handler(get) {
|
|
1142
|
+
return c.handle(scope, propValue, () => {
|
|
1143
|
+
const [init, initInst] = get();
|
|
1144
|
+
return value.handler(init, initInst);
|
|
1145
|
+
});
|
|
1146
|
+
},
|
|
1147
|
+
});
|
|
1148
|
+
return [null, [...keyInst[1], ...valueInst]];
|
|
1028
1149
|
});
|
|
1029
|
-
|
|
1030
|
-
|
|
1150
|
+
const out = new DestructuringValue(members);
|
|
1151
|
+
const declarationValue = new DeclarationValue({
|
|
1152
|
+
c,
|
|
1153
|
+
scope,
|
|
1154
|
+
node,
|
|
1155
|
+
out,
|
|
1156
|
+
handler(init, initInst) {
|
|
1157
|
+
if (!init)
|
|
1158
|
+
throw new CompilerError("Cannot use object destructuring without an initializer", node);
|
|
1159
|
+
initInst.push(...out["="](scope, init)[1]);
|
|
1160
|
+
return [null, initInst];
|
|
1161
|
+
},
|
|
1162
|
+
});
|
|
1163
|
+
return [declarationValue, [...inst, ...propertiesInst[1]]];
|
|
1164
|
+
};
|
|
1165
|
+
class DeclarationValue extends VoidValue {
|
|
1166
|
+
constructor({ c, scope, node, out, handler, }) {
|
|
1167
|
+
super();
|
|
1168
|
+
this.out = out;
|
|
1169
|
+
this.c = c;
|
|
1170
|
+
this.scope = scope;
|
|
1171
|
+
this.node = node;
|
|
1172
|
+
this.handle = handler;
|
|
1173
|
+
}
|
|
1174
|
+
handler(init, inst) {
|
|
1175
|
+
return this.c.handle(this.scope, this.node, () => this.handle(init, inst));
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1031
1178
|
|
|
1032
1179
|
const Literal = (_c, scope, node) => [new LiteralValue(node.value), []];
|
|
1033
1180
|
const NumericLiteral = Literal;
|
|
1034
1181
|
const StringLiteral = Literal;
|
|
1035
|
-
const NullLiteral = () => [
|
|
1036
|
-
new LiteralValue(null),
|
|
1037
|
-
[],
|
|
1038
|
-
];
|
|
1182
|
+
const NullLiteral = () => [new LiteralValue(null), []];
|
|
1039
1183
|
const BooleanLiteral = (_c, scope, node) => [new LiteralValue(+node.value), []];
|
|
1040
1184
|
|
|
1041
|
-
const LRExpression = (c, scope, node) => {
|
|
1042
|
-
const [left, leftInst] = c.
|
|
1043
|
-
const [right, rightInst] = c.
|
|
1044
|
-
const [op, opInst] = left[node.operator](scope, right);
|
|
1185
|
+
const LRExpression = (c, scope, node, out) => {
|
|
1186
|
+
const [left, leftInst] = c.handleEval(scope, node.left);
|
|
1187
|
+
const [right, rightInst] = c.handleEval(scope, node.right);
|
|
1188
|
+
const [op, opInst] = left[node.operator](scope, right, out);
|
|
1045
1189
|
return [op, [...leftInst, ...rightInst, ...opInst]];
|
|
1046
1190
|
};
|
|
1047
1191
|
const BinaryExpression = LRExpression;
|
|
1048
|
-
const LogicalExpression = (c, scope, node) => {
|
|
1192
|
+
const LogicalExpression = (c, scope, node, out) => {
|
|
1049
1193
|
if (node.operator !== "??")
|
|
1050
|
-
return LRExpression(c, scope, node);
|
|
1051
|
-
const other =
|
|
1052
|
-
eval: scope => c.handleEval(scope, node.right),
|
|
1053
|
-
consume: scope => c.handleConsume(scope, node.right),
|
|
1054
|
-
});
|
|
1194
|
+
return LRExpression(c, scope, node, undefined);
|
|
1195
|
+
const other = new LazyValue((scope, out) => c.handleEval(scope, node.right, out));
|
|
1055
1196
|
const [left, leftInst] = c.handleEval(scope, node.left);
|
|
1056
|
-
const [result, resultInst] = left["??"](scope, other);
|
|
1197
|
+
const [result, resultInst] = left["??"](scope, other, out);
|
|
1057
1198
|
return [result, [...leftInst, ...resultInst]];
|
|
1058
1199
|
};
|
|
1059
1200
|
const AssignmentExpression = (c, scope, node) => {
|
|
1060
1201
|
const [left, leftInst] = c.handle(scope, node.left);
|
|
1061
|
-
// doesn't need to consume because the operators already do that
|
|
1062
1202
|
const [right, rightInst] = node.operator !== "??="
|
|
1063
|
-
? c.handleEval(scope, node.right)
|
|
1203
|
+
? c.handleEval(scope, node.right, node.operator === "=" && left ? left : undefined)
|
|
1064
1204
|
: [
|
|
1065
|
-
|
|
1066
|
-
eval: scope => c.handleEval(scope, node.right),
|
|
1067
|
-
consume: scope => c.handleConsume(scope, node.right),
|
|
1068
|
-
}),
|
|
1205
|
+
new LazyValue((scope, out) => c.handleEval(scope, node.right, out)),
|
|
1069
1206
|
[],
|
|
1070
1207
|
];
|
|
1071
1208
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1072
1209
|
const [op, opInst] = left[node.operator](scope, right);
|
|
1073
1210
|
return [op, [...leftInst, ...rightInst, ...opInst]];
|
|
1074
1211
|
};
|
|
1075
|
-
const UnaryExpression = (c, scope, { argument, operator }) => {
|
|
1076
|
-
const [arg, argInst] = c.
|
|
1212
|
+
const UnaryExpression = (c, scope, { argument, operator }, out) => {
|
|
1213
|
+
const [arg, argInst] = c.handleEval(scope, argument);
|
|
1077
1214
|
const operatorId = operator == "+" || operator == "-" ? `u${operator}` : operator;
|
|
1078
1215
|
if (operatorId === "throw")
|
|
1079
1216
|
throw new CompilerError("throw operator is not supported");
|
|
1080
|
-
const [op, opInst] = arg[operatorId](scope);
|
|
1217
|
+
const [op, opInst] = arg[operatorId](scope, out);
|
|
1081
1218
|
return [op, [...argInst, ...opInst]];
|
|
1082
1219
|
};
|
|
1083
|
-
const UpdateExpression = (c, scope, { argument, operator, prefix }) => {
|
|
1220
|
+
const UpdateExpression = (c, scope, { argument, operator, prefix }, out) => {
|
|
1084
1221
|
const [arg, argInst] = c.handle(scope, argument);
|
|
1085
1222
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1086
|
-
const [op, opInst] = arg[operator](scope, prefix);
|
|
1223
|
+
const [op, opInst] = arg[operator](scope, prefix, out);
|
|
1087
1224
|
return [op, [...argInst, ...opInst]];
|
|
1088
1225
|
};
|
|
1089
|
-
const ConditionalExpression = (c, scope, node) => {
|
|
1090
|
-
const [test, testInst] = c.
|
|
1226
|
+
const ConditionalExpression = (c, scope, node, out) => {
|
|
1227
|
+
const [test, testInst] = c.handleEval(scope, node.test);
|
|
1091
1228
|
if (test instanceof LiteralValue) {
|
|
1092
1229
|
if (test.data)
|
|
1093
|
-
return c.handleEval(scope, node.consequent);
|
|
1094
|
-
return c.handleEval(scope, node.alternate);
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
const
|
|
1099
|
-
result.ensureOwned();
|
|
1100
|
-
const consequent = c.handleEval(scope, node.consequent);
|
|
1101
|
-
const alternate = c.handleEval(scope, node.alternate);
|
|
1230
|
+
return c.handleEval(scope, node.consequent, out);
|
|
1231
|
+
return c.handleEval(scope, node.alternate, out);
|
|
1232
|
+
}
|
|
1233
|
+
const result = SenseableValue.out(scope, out);
|
|
1234
|
+
const consequent = c.handleEval(scope, node.consequent, result);
|
|
1235
|
+
const alternate = c.handleEval(scope, node.alternate, result);
|
|
1102
1236
|
const alternateStartAdress = new LiteralValue(null);
|
|
1103
1237
|
const endExpressionAdress = new LiteralValue(null);
|
|
1104
1238
|
return [
|
|
@@ -1115,20 +1249,14 @@ const ConditionalExpression = (c, scope, node) => {
|
|
|
1115
1249
|
new AddressResolver(endExpressionAdress),
|
|
1116
1250
|
],
|
|
1117
1251
|
];
|
|
1118
|
-
};
|
|
1119
|
-
function lazyValue(options) {
|
|
1120
|
-
const value = new VoidValue();
|
|
1121
|
-
value.eval = options.eval;
|
|
1122
|
-
value.consume = options.consume;
|
|
1123
|
-
return value;
|
|
1124
|
-
}
|
|
1252
|
+
};
|
|
1125
1253
|
|
|
1126
1254
|
const Identifier = (c, scope, node) => {
|
|
1127
1255
|
return [scope.get(node.name), []];
|
|
1128
1256
|
};
|
|
1129
1257
|
|
|
1130
1258
|
const ExpressionStatement = (c, scope, node) => {
|
|
1131
|
-
return c.handle(scope, node.expression);
|
|
1259
|
+
return c.handle(scope, node.expression, undefined, discardedName);
|
|
1132
1260
|
};
|
|
1133
1261
|
const BreakStatement = (_, scope, node) => {
|
|
1134
1262
|
const label = node.label?.name;
|
|
@@ -1144,11 +1272,12 @@ const ContinueStatement = (_, scope, node) => {
|
|
|
1144
1272
|
target.continue.bind(addr);
|
|
1145
1273
|
return [null, [new ContinueInstruction(addr)]];
|
|
1146
1274
|
};
|
|
1147
|
-
const ReturnStatement = (c, scope, node) => {
|
|
1148
|
-
const
|
|
1149
|
-
|
|
1275
|
+
const ReturnStatement = (c, scope, node, out) => {
|
|
1276
|
+
const { argument } = node;
|
|
1277
|
+
const [arg, argInst] = argument
|
|
1278
|
+
? [new LazyValue((scope, out) => c.handleEval(scope, argument, out)), []]
|
|
1150
1279
|
: [null, []];
|
|
1151
|
-
const [ret, retInst] = scope.function.return(scope, arg);
|
|
1280
|
+
const [ret, retInst] = scope.function.return(scope, arg, out);
|
|
1152
1281
|
return [ret, [...argInst, ...retInst]];
|
|
1153
1282
|
};
|
|
1154
1283
|
const LabeledStatement = (c, scope, node) => {
|
|
@@ -1171,7 +1300,7 @@ function findScopeLabel(scope, label) {
|
|
|
1171
1300
|
|
|
1172
1301
|
const IfStatement = (c, scope, node) => {
|
|
1173
1302
|
const inst = [];
|
|
1174
|
-
const [test, testInst] = c.
|
|
1303
|
+
const [test, testInst] = c.handleEval(scope, node.test);
|
|
1175
1304
|
if (test instanceof LiteralValue) {
|
|
1176
1305
|
if (test.data)
|
|
1177
1306
|
inst.push(...c.handle(scope, node.consequent)[1]);
|
|
@@ -1190,44 +1319,73 @@ const IfStatement = (c, scope, node) => {
|
|
|
1190
1319
|
|
|
1191
1320
|
const SwitchStatement = (c, scope, node) => {
|
|
1192
1321
|
const innerScope = scope.createScope();
|
|
1193
|
-
const [ref, refInst] = c.
|
|
1322
|
+
const [ref, refInst] = c.handleEval(scope, node.discriminant);
|
|
1194
1323
|
const inst = [];
|
|
1195
1324
|
const endAdress = new LiteralValue(null);
|
|
1196
1325
|
const endLine = new AddressResolver(endAdress).bindBreak(innerScope);
|
|
1197
1326
|
const caseJumps = [];
|
|
1198
1327
|
let defaultJump;
|
|
1328
|
+
// if there is a case that is guaranteed to run
|
|
1329
|
+
let constantCase = false;
|
|
1330
|
+
let onlyConstantTests = true;
|
|
1199
1331
|
for (const scase of node.cases) {
|
|
1200
1332
|
const [, bodyInst] = c.handleMany(innerScope, scase.consequent);
|
|
1201
1333
|
const bodyAdress = new LiteralValue(null);
|
|
1202
1334
|
const bodyLine = new AddressResolver(bodyAdress);
|
|
1335
|
+
const canFallInto = !endsWithoutFalltrough(inst, endLine);
|
|
1336
|
+
let includeJump = !constantCase;
|
|
1337
|
+
const includeBody = !constantCase || canFallInto;
|
|
1203
1338
|
if (!scase.test) {
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1339
|
+
if (includeJump) {
|
|
1340
|
+
[defaultJump] = c.handle(innerScope, scase, () => [
|
|
1341
|
+
null,
|
|
1342
|
+
[new JumpInstruction(bodyAdress, EJumpKind.Always)],
|
|
1343
|
+
])[1];
|
|
1344
|
+
}
|
|
1345
|
+
if (includeBody)
|
|
1346
|
+
inst.push(bodyLine, ...bodyInst);
|
|
1209
1347
|
continue;
|
|
1210
1348
|
}
|
|
1211
|
-
const [test, testInst] = c.
|
|
1349
|
+
const [test, testInst] = c.handleEval(innerScope, scase.test);
|
|
1350
|
+
if (onlyConstantTests)
|
|
1351
|
+
onlyConstantTests = test.mutability === EMutability.constant;
|
|
1352
|
+
let jump;
|
|
1212
1353
|
// check if it can be evaluated at compile time
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
// the whole case gets omitted
|
|
1216
|
-
// otherwise it returns the instructions for
|
|
1217
|
-
// the body of this case
|
|
1354
|
+
// the scope copy prevents unecessary increases in the temp counter
|
|
1355
|
+
const [comp] = ref["=="](innerScope.copy(), test);
|
|
1218
1356
|
if (comp instanceof LiteralValue) {
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1357
|
+
// whether other cases can falltrough and reach this one
|
|
1358
|
+
const noFallInto = !canFallInto;
|
|
1359
|
+
if (!comp.data) {
|
|
1360
|
+
// this case is not accessible in any way
|
|
1361
|
+
if (noFallInto)
|
|
1362
|
+
continue;
|
|
1363
|
+
includeJump = false;
|
|
1364
|
+
}
|
|
1365
|
+
else {
|
|
1366
|
+
// this case is only accessible via the comparison jump
|
|
1367
|
+
// and all the other cases will never run
|
|
1368
|
+
if (noFallInto &&
|
|
1369
|
+
onlyConstantTests &&
|
|
1370
|
+
endsWithoutFalltrough(bodyInst, endLine)) {
|
|
1371
|
+
return [null, [...bodyInst, endLine]];
|
|
1372
|
+
}
|
|
1373
|
+
jump = c.handle(innerScope, scase, () => [
|
|
1374
|
+
null,
|
|
1375
|
+
[new JumpInstruction(bodyAdress, EJumpKind.Always)],
|
|
1376
|
+
])[1];
|
|
1377
|
+
constantCase = true;
|
|
1378
|
+
}
|
|
1223
1379
|
}
|
|
1224
1380
|
// makes sourcemapping for the jump more specific
|
|
1225
|
-
|
|
1381
|
+
jump ??= c.handle(innerScope, scase, () => [
|
|
1226
1382
|
null,
|
|
1227
1383
|
[new JumpInstruction(bodyAdress, EJumpKind.StrictEqual, ref, test)],
|
|
1228
|
-
]);
|
|
1229
|
-
|
|
1230
|
-
|
|
1384
|
+
])[1];
|
|
1385
|
+
if (includeJump)
|
|
1386
|
+
caseJumps.push(...testInst, ...jump);
|
|
1387
|
+
if (includeBody)
|
|
1388
|
+
inst.push(bodyLine, ...bodyInst);
|
|
1231
1389
|
}
|
|
1232
1390
|
// ensures that the processor exits
|
|
1233
1391
|
// the switch if no cases match
|
|
@@ -1242,16 +1400,38 @@ const SwitchStatement = (c, scope, node) => {
|
|
|
1242
1400
|
[
|
|
1243
1401
|
...refInst,
|
|
1244
1402
|
...caseJumps,
|
|
1245
|
-
...(caseJumps.length > 0 ? [defaultJump] : []),
|
|
1403
|
+
...(caseJumps.length > 0 && !constantCase ? [defaultJump] : []),
|
|
1246
1404
|
...inst,
|
|
1247
1405
|
endLine,
|
|
1248
1406
|
],
|
|
1249
1407
|
];
|
|
1250
|
-
};
|
|
1408
|
+
};
|
|
1409
|
+
function endsWithoutFalltrough(inst, endLine) {
|
|
1410
|
+
if (inst.length === 0)
|
|
1411
|
+
return true;
|
|
1412
|
+
for (let i = inst.length - 1; i >= 0; i--) {
|
|
1413
|
+
const instruction = inst[i];
|
|
1414
|
+
// this means that there is an instruction trying to reference
|
|
1415
|
+
// the final function `set @counter` instruction
|
|
1416
|
+
// of course this generates an unecessary
|
|
1417
|
+
// `set @counter` in cases where a control flow
|
|
1418
|
+
// structure contains a fallback return statement
|
|
1419
|
+
// TODO: fix this with static analysis
|
|
1420
|
+
if (instruction instanceof AddressResolver)
|
|
1421
|
+
return false;
|
|
1422
|
+
if (instruction.hidden)
|
|
1423
|
+
continue;
|
|
1424
|
+
return (instruction instanceof SetCounterInstruction ||
|
|
1425
|
+
instruction.intent === EInstIntent.return ||
|
|
1426
|
+
(instruction instanceof BreakInstruction &&
|
|
1427
|
+
endLine.bonds.includes(instruction.address)));
|
|
1428
|
+
}
|
|
1429
|
+
return false;
|
|
1430
|
+
}
|
|
1251
1431
|
|
|
1252
1432
|
const WhileStatement = (c, scope, node) => {
|
|
1253
1433
|
const lines = [];
|
|
1254
|
-
const [test, testLines] = c.
|
|
1434
|
+
const [test, testLines] = c.handleEval(scope, node.test);
|
|
1255
1435
|
const childScope = scope.createScope();
|
|
1256
1436
|
const startLoopAddr = new LiteralValue(null);
|
|
1257
1437
|
const endLoopAddr = new LiteralValue(null);
|
|
@@ -1271,7 +1451,7 @@ const WhileStatement = (c, scope, node) => {
|
|
|
1271
1451
|
};
|
|
1272
1452
|
const DoWhileStatement = (c, scope, node) => {
|
|
1273
1453
|
const lines = [];
|
|
1274
|
-
const [test, testLines] = c.
|
|
1454
|
+
const [test, testLines] = c.handleEval(scope, node.test);
|
|
1275
1455
|
const childScope = scope.createScope();
|
|
1276
1456
|
const startLoopAddr = new LiteralValue(null);
|
|
1277
1457
|
const startLoopLine = new AddressResolver(startLoopAddr).bindContinue(childScope);
|
|
@@ -1300,9 +1480,11 @@ const ForStatement = (c, parentScope, node) => {
|
|
|
1300
1480
|
const scope = parentScope.createScope();
|
|
1301
1481
|
const initInst = node.init ? c.handle(scope, node.init)[1] : [];
|
|
1302
1482
|
const [test, testLines] = node.test
|
|
1303
|
-
? c.
|
|
1483
|
+
? c.handleEval(scope, node.test)
|
|
1304
1484
|
: [new LiteralValue(1), []];
|
|
1305
|
-
const updateLines = node.update
|
|
1485
|
+
const updateLines = node.update
|
|
1486
|
+
? c.handle(scope, node.update, undefined, discardedName)[1]
|
|
1487
|
+
: [];
|
|
1306
1488
|
const startLoopAddr = new LiteralValue(null);
|
|
1307
1489
|
// continue statements jump here to run the loop update lines
|
|
1308
1490
|
const beforeEndAddr = new LiteralValue(null);
|
|
@@ -1330,146 +1512,131 @@ const ForStatement = (c, parentScope, node) => {
|
|
|
1330
1512
|
};
|
|
1331
1513
|
|
|
1332
1514
|
class FunctionValue extends VoidValue {
|
|
1333
|
-
constructor({ scope, params, body, c, }) {
|
|
1515
|
+
constructor({ scope, params, body, c, out, }) {
|
|
1334
1516
|
super();
|
|
1335
1517
|
this.mutability = EMutability.constant;
|
|
1336
1518
|
this.macro = true;
|
|
1337
|
-
this.
|
|
1519
|
+
this.paramValues = [];
|
|
1520
|
+
this.paramNames = [];
|
|
1338
1521
|
this.bundled = false;
|
|
1339
1522
|
this.initialized = false;
|
|
1340
1523
|
this.scope = scope;
|
|
1341
1524
|
this.body = body;
|
|
1342
1525
|
this.c = c;
|
|
1343
1526
|
this.params = params;
|
|
1527
|
+
this.out = out;
|
|
1528
|
+
this.name = extractOutName(out) ?? scope.makeTempName();
|
|
1344
1529
|
}
|
|
1345
1530
|
typeof() {
|
|
1346
1531
|
return [new LiteralValue("function"), []];
|
|
1347
1532
|
}
|
|
1348
|
-
initScope(
|
|
1349
|
-
this.childScope = this.scope.createFunction(name);
|
|
1533
|
+
initScope() {
|
|
1534
|
+
this.childScope = this.scope.createFunction(this.name);
|
|
1350
1535
|
this.childScope.function = this;
|
|
1351
1536
|
for (const id of this.params) {
|
|
1352
1537
|
const name = nodeName(id, !this.c.compactNames && id.name);
|
|
1353
|
-
const
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
}),
|
|
1358
|
-
identifier: id.name,
|
|
1359
|
-
name,
|
|
1360
|
-
});
|
|
1361
|
-
this.childScope.set(owner);
|
|
1362
|
-
this.paramOwners.push(owner);
|
|
1538
|
+
const value = new SenseableValue(name, EMutability.mutable);
|
|
1539
|
+
this.childScope.set(id.name, value);
|
|
1540
|
+
this.paramValues.push(value);
|
|
1541
|
+
this.paramNames.push(id.name);
|
|
1363
1542
|
}
|
|
1364
|
-
this.callSize = this.
|
|
1543
|
+
this.callSize = this.paramValues.length + 2;
|
|
1365
1544
|
}
|
|
1366
1545
|
ensureInit() {
|
|
1367
|
-
if (!this.owner)
|
|
1368
|
-
throw new CompilerError(`Functions must be owned by a variable`);
|
|
1369
1546
|
if (this.initialized)
|
|
1370
1547
|
return;
|
|
1371
1548
|
this.initialized = true;
|
|
1372
|
-
|
|
1373
|
-
this.initScope(name);
|
|
1549
|
+
this.initScope();
|
|
1374
1550
|
this.addr = new LiteralValue(null);
|
|
1375
|
-
this.temp = new
|
|
1376
|
-
|
|
1377
|
-
name: `${internalPrefix}f${name}`,
|
|
1378
|
-
value: assign(new SenseableValue(this.childScope), {
|
|
1379
|
-
mutability: EMutability.mutable,
|
|
1380
|
-
}),
|
|
1381
|
-
});
|
|
1382
|
-
this.ret = new ValueOwner({
|
|
1383
|
-
scope: this.childScope,
|
|
1384
|
-
name: `${internalPrefix}r${name}`,
|
|
1385
|
-
value: new StoreValue(this.childScope),
|
|
1386
|
-
});
|
|
1551
|
+
this.temp = new SenseableValue(`${internalPrefix}f${this.name}`, EMutability.mutable);
|
|
1552
|
+
this.ret = new StoreValue(`${internalPrefix}r${this.name}`);
|
|
1387
1553
|
this.inst = [
|
|
1388
1554
|
new AddressResolver(this.addr),
|
|
1389
1555
|
...this.c.handle(this.childScope, this.body)[1],
|
|
1390
1556
|
];
|
|
1391
1557
|
if (!endsWithReturn(this.inst)) {
|
|
1392
|
-
this.inst.push(assign(new SetCounterInstruction(this.ret
|
|
1558
|
+
this.inst.push(assign(new SetCounterInstruction(this.ret), {
|
|
1393
1559
|
intent: EInstIntent.return,
|
|
1394
1560
|
}));
|
|
1395
1561
|
}
|
|
1396
1562
|
}
|
|
1397
1563
|
normalReturn(scope, arg) {
|
|
1398
|
-
const
|
|
1399
|
-
|
|
1564
|
+
const inst = [];
|
|
1565
|
+
if (arg) {
|
|
1566
|
+
const [value, valueInst] = arg.eval(scope, this.temp);
|
|
1567
|
+
const argInst = this.temp["="](scope, value)[1];
|
|
1568
|
+
inst.push(...valueInst);
|
|
1569
|
+
inst.push(...argInst);
|
|
1570
|
+
}
|
|
1571
|
+
const returnInst = assign(new SetCounterInstruction(this.ret), {
|
|
1400
1572
|
intent: EInstIntent.return,
|
|
1401
1573
|
});
|
|
1402
|
-
return [null, [...
|
|
1574
|
+
return [null, [...inst, returnInst]];
|
|
1403
1575
|
}
|
|
1404
1576
|
// TODO: use static analysis to determine if
|
|
1405
1577
|
// we can safely skip the temp value assignment
|
|
1406
|
-
normalCall(scope, args) {
|
|
1578
|
+
normalCall(scope, args, out) {
|
|
1407
1579
|
if (!this.bundled)
|
|
1408
1580
|
this.childScope.inst.push(...this.inst);
|
|
1409
1581
|
this.bundled = true;
|
|
1410
1582
|
const callAddressLiteral = new LiteralValue(null);
|
|
1411
|
-
const temp =
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
const inst = this.paramOwners
|
|
1415
|
-
.map(({ value: param }, i) => param["="](scope, args[i])[1])
|
|
1583
|
+
const temp = SenseableValue.from(scope, out, EMutability.mutable);
|
|
1584
|
+
const inst = this.paramValues
|
|
1585
|
+
.map((param, i) => param["="](scope, args[i])[1])
|
|
1416
1586
|
.reduce((s, c) => s.concat(c), [])
|
|
1417
|
-
.concat(...this.ret
|
|
1587
|
+
.concat(...this.ret["="](scope, callAddressLiteral)[1], new JumpInstruction(this.addr, EJumpKind.Always), new AddressResolver(callAddressLiteral),
|
|
1418
1588
|
// ensures that functions can be called multiple times inside expressions
|
|
1419
|
-
...temp["="](scope, this.temp
|
|
1589
|
+
...temp["="](scope, this.temp)[1]);
|
|
1420
1590
|
return [temp, inst];
|
|
1421
1591
|
}
|
|
1422
1592
|
inlineReturn(scope, arg) {
|
|
1423
|
-
const argInst = arg ? this.inlineTemp.value["="](scope, arg)[1] : [];
|
|
1424
1593
|
if (!this.inlineEnd)
|
|
1425
1594
|
throw new CompilerError("Error during inline attempt: missing inline end adress");
|
|
1595
|
+
const inst = [];
|
|
1596
|
+
if (arg) {
|
|
1597
|
+
const [value, valueInst] = arg.eval(scope, this.inlineTemp);
|
|
1598
|
+
const argInst = this.inlineTemp["="](scope, value)[1];
|
|
1599
|
+
inst.push(...valueInst);
|
|
1600
|
+
inst.push(...argInst);
|
|
1601
|
+
}
|
|
1426
1602
|
const jump = assign(new JumpInstruction(this.inlineEnd, EJumpKind.Always), {
|
|
1427
1603
|
intent: EInstIntent.return,
|
|
1604
|
+
intentSource: this,
|
|
1428
1605
|
});
|
|
1429
|
-
return [null, [...
|
|
1606
|
+
return [null, [...inst, jump]];
|
|
1430
1607
|
}
|
|
1431
|
-
inlineCall(scope, args) {
|
|
1608
|
+
inlineCall(scope, args, out) {
|
|
1432
1609
|
// create return value
|
|
1433
|
-
this.inlineTemp =
|
|
1434
|
-
scope: this.childScope,
|
|
1435
|
-
value: assign(new SenseableValue(scope), {
|
|
1436
|
-
mutability: EMutability.mutable,
|
|
1437
|
-
}),
|
|
1438
|
-
});
|
|
1610
|
+
this.inlineTemp = SenseableValue.from(this.childScope, out, EMutability.mutable);
|
|
1439
1611
|
// make a copy of the function scope
|
|
1440
1612
|
const fnScope = this.childScope.copy();
|
|
1441
1613
|
// hard set variables within the function scope
|
|
1442
|
-
this.
|
|
1443
|
-
|
|
1444
|
-
if (!value.owner?.persistent)
|
|
1445
|
-
owner.own(value);
|
|
1446
|
-
fnScope.hardSet(new ValueOwner({
|
|
1447
|
-
scope: fnScope,
|
|
1448
|
-
value,
|
|
1449
|
-
identifier: owner.identifier,
|
|
1450
|
-
name: owner.name,
|
|
1451
|
-
}));
|
|
1614
|
+
this.paramNames.forEach((name, i) => {
|
|
1615
|
+
fnScope.hardSet(name, args[i]);
|
|
1452
1616
|
});
|
|
1453
1617
|
this.inlineEnd = new LiteralValue(null);
|
|
1454
1618
|
this.tryingInline = true;
|
|
1455
1619
|
let inst = this.c.handle(fnScope, this.body)[1];
|
|
1456
1620
|
this.tryingInline = false;
|
|
1457
|
-
const returnIndex = inst.findIndex(i => i.alwaysRuns &&
|
|
1621
|
+
const returnIndex = inst.findIndex(i => i.alwaysRuns &&
|
|
1622
|
+
i.intent === EInstIntent.return &&
|
|
1623
|
+
i.intentSource === this);
|
|
1458
1624
|
if (returnIndex !== -1)
|
|
1459
1625
|
inst = inst.slice(0, returnIndex + 1);
|
|
1460
1626
|
inst.push(new AddressResolver(this.inlineEnd));
|
|
1461
1627
|
// return the function value
|
|
1462
|
-
return [this.inlineTemp
|
|
1628
|
+
return [this.inlineTemp, inst];
|
|
1463
1629
|
}
|
|
1464
|
-
call(scope, args) {
|
|
1630
|
+
call(scope, args, out) {
|
|
1465
1631
|
this.ensureInit();
|
|
1466
|
-
if (args.length !== this.
|
|
1632
|
+
if (args.length !== this.paramNames.length)
|
|
1467
1633
|
throw new CompilerError("Cannot call: argument count not matching.");
|
|
1468
|
-
const inlineCall = this.inlineCall(scope, args);
|
|
1634
|
+
const inlineCall = this.inlineCall(scope, args, out);
|
|
1635
|
+
hideRedundantJumps(inlineCall[1]);
|
|
1469
1636
|
const inlineSize = inlineCall[1].filter(i => !i.hidden).length;
|
|
1470
1637
|
if (this.inline || inlineSize <= this.callSize)
|
|
1471
1638
|
return inlineCall;
|
|
1472
|
-
return this.normalCall(scope, args);
|
|
1639
|
+
return this.normalCall(scope, args, out);
|
|
1473
1640
|
}
|
|
1474
1641
|
return(scope, arg) {
|
|
1475
1642
|
this.ensureInit();
|
|
@@ -1484,8 +1651,11 @@ class FunctionValue extends VoidValue {
|
|
|
1484
1651
|
eval(_scope) {
|
|
1485
1652
|
return [this, []];
|
|
1486
1653
|
}
|
|
1487
|
-
|
|
1488
|
-
|
|
1654
|
+
paramOuts() {
|
|
1655
|
+
this.ensureInit();
|
|
1656
|
+
if (this.inline || this.tryingInline)
|
|
1657
|
+
return;
|
|
1658
|
+
return this.paramValues;
|
|
1489
1659
|
}
|
|
1490
1660
|
}
|
|
1491
1661
|
function endsWithReturn(inst) {
|
|
@@ -1506,7 +1676,7 @@ function endsWithReturn(inst) {
|
|
|
1506
1676
|
return false;
|
|
1507
1677
|
}
|
|
1508
1678
|
|
|
1509
|
-
function handleFunctionNode(c, scope, node) {
|
|
1679
|
+
function handleFunctionNode(c, scope, node, out) {
|
|
1510
1680
|
let { params, body } = node;
|
|
1511
1681
|
if (es__namespace.isExpression(body)) {
|
|
1512
1682
|
const { loc } = body;
|
|
@@ -1521,46 +1691,44 @@ function handleFunctionNode(c, scope, node) {
|
|
|
1521
1691
|
params: params,
|
|
1522
1692
|
body,
|
|
1523
1693
|
c,
|
|
1694
|
+
out,
|
|
1524
1695
|
}),
|
|
1525
1696
|
[],
|
|
1526
1697
|
];
|
|
1527
1698
|
}
|
|
1528
|
-
const ArrowFunctionExpression = (c, scope, node) => {
|
|
1529
|
-
return handleFunctionNode(c, scope, node);
|
|
1699
|
+
const ArrowFunctionExpression = (c, scope, node, out) => {
|
|
1700
|
+
return handleFunctionNode(c, scope, node, out);
|
|
1530
1701
|
};
|
|
1531
1702
|
const FunctionDeclaration = (c, scope, node) => {
|
|
1532
1703
|
const identifier = node.id.name;
|
|
1533
1704
|
const name = nodeName(node, !c.compactNames && identifier);
|
|
1534
|
-
const functionIns = handleFunctionNode(c, scope, node);
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
value: functionIns[0],
|
|
1538
|
-
identifier,
|
|
1539
|
-
name,
|
|
1540
|
-
});
|
|
1541
|
-
return [scope.set(owner), []];
|
|
1705
|
+
const functionIns = handleFunctionNode(c, scope, node, name);
|
|
1706
|
+
scope.set(identifier, functionIns[0]);
|
|
1707
|
+
return functionIns;
|
|
1542
1708
|
};
|
|
1543
1709
|
const FunctionExpression = ArrowFunctionExpression;
|
|
1544
1710
|
const ObjectMethod = ArrowFunctionExpression;
|
|
1545
1711
|
|
|
1546
|
-
const CallExpression = (c, scope, node) => {
|
|
1712
|
+
const CallExpression = (c, scope, node, out) => {
|
|
1547
1713
|
const inst = [];
|
|
1548
|
-
const
|
|
1714
|
+
const [callee, calleeInst] = c.handleEval(scope, node.callee);
|
|
1715
|
+
inst.push(...calleeInst);
|
|
1716
|
+
const paramOuts = callee.paramOuts();
|
|
1717
|
+
const args = node.arguments.map((node, index) => {
|
|
1549
1718
|
// TODO: this might be why t0 did not increment
|
|
1550
1719
|
// TODO: figure out if the previous todo is still relevant
|
|
1551
|
-
const
|
|
1720
|
+
const out = paramOuts?.[index];
|
|
1721
|
+
const [v, i] = c.handleEval(scope, node, out);
|
|
1552
1722
|
inst.push(...i);
|
|
1553
1723
|
return v;
|
|
1554
1724
|
});
|
|
1555
|
-
const [
|
|
1556
|
-
inst.push(...calleeInst);
|
|
1557
|
-
const [callValue, callInst] = callee.call(scope, args);
|
|
1725
|
+
const [callValue, callInst] = callee.call(scope, args, out);
|
|
1558
1726
|
inst.push(...callInst);
|
|
1559
1727
|
return [callValue, inst];
|
|
1560
1728
|
};
|
|
1561
1729
|
const NewExpression = CallExpression;
|
|
1562
|
-
const TaggedTemplateExpression = (c, scope, node) => {
|
|
1563
|
-
const [tag, tagInst] = c.
|
|
1730
|
+
const TaggedTemplateExpression = (c, scope, node, out) => {
|
|
1731
|
+
const [tag, tagInst] = c.handleEval(scope, node.tag);
|
|
1564
1732
|
const template = node.quasi;
|
|
1565
1733
|
const stringsObject = ObjectValue.fromArray(template.quasis.map(quasi => new LiteralValue(quasi.value.cooked ?? "")), {
|
|
1566
1734
|
raw: ObjectValue.fromArray(template.quasis.map(quasi => new LiteralValue(quasi.value.raw))),
|
|
@@ -1568,15 +1736,15 @@ const TaggedTemplateExpression = (c, scope, node) => {
|
|
|
1568
1736
|
const expressions = [];
|
|
1569
1737
|
const expressionInsts = [];
|
|
1570
1738
|
template.expressions.forEach(expression => {
|
|
1571
|
-
const [value, inst] = c.
|
|
1739
|
+
const [value, inst] = c.handleEval(scope, expression);
|
|
1572
1740
|
expressions.push(value);
|
|
1573
1741
|
expressionInsts.push(...inst);
|
|
1574
1742
|
});
|
|
1575
|
-
const [result, resultInst] = tag.call(scope, [stringsObject, ...expressions]);
|
|
1743
|
+
const [result, resultInst] = tag.call(scope, [stringsObject, ...expressions], out);
|
|
1576
1744
|
return [result, [...expressionInsts, ...tagInst, ...resultInst]];
|
|
1577
1745
|
};
|
|
1578
1746
|
|
|
1579
|
-
const ObjectExpression = (c, scope, node) => {
|
|
1747
|
+
const ObjectExpression = (c, scope, node, out) => {
|
|
1580
1748
|
const data = {};
|
|
1581
1749
|
const inst = [];
|
|
1582
1750
|
for (const prop of node.properties) {
|
|
@@ -1596,38 +1764,48 @@ const ObjectExpression = (c, scope, node) => {
|
|
|
1596
1764
|
else {
|
|
1597
1765
|
throw new CompilerError(`Unsupported object key type: ${key.type}`, key);
|
|
1598
1766
|
}
|
|
1599
|
-
const [member, memberInst] = c.handleEval(scope, value);
|
|
1600
|
-
// TODO: use an approach that can be defined by each type
|
|
1601
|
-
if (!member.owner && !(member instanceof LiteralValue)) {
|
|
1602
|
-
new ValueOwner({
|
|
1603
|
-
scope,
|
|
1604
|
-
constant: true,
|
|
1605
|
-
value: member,
|
|
1606
|
-
});
|
|
1607
|
-
}
|
|
1767
|
+
const [member, memberInst] = c.handleEval(scope, value, extractDestrucuringOut(out, index));
|
|
1608
1768
|
data[index] = member;
|
|
1609
1769
|
inst.push(...memberInst);
|
|
1610
1770
|
}
|
|
1611
1771
|
return [new ObjectValue(data), inst];
|
|
1612
1772
|
};
|
|
1613
|
-
const ArrayExpression = (c, scope, node) => {
|
|
1773
|
+
const ArrayExpression = (c, scope, node, out) => {
|
|
1614
1774
|
const items = [];
|
|
1615
1775
|
const inst = [];
|
|
1616
|
-
node.elements.forEach(element => {
|
|
1776
|
+
node.elements.forEach((element, i) => {
|
|
1617
1777
|
const [value, valueInst] = element
|
|
1618
|
-
? c.handleEval(scope, element)
|
|
1778
|
+
? c.handleEval(scope, element, extractDestrucuringOut(out, i))
|
|
1619
1779
|
: [undefined, []];
|
|
1620
1780
|
items.push(value);
|
|
1621
1781
|
inst.push(...valueInst);
|
|
1622
1782
|
});
|
|
1623
1783
|
return [ObjectValue.fromArray(items), inst];
|
|
1624
1784
|
};
|
|
1625
|
-
const MemberExpression = (c, scope, node) => {
|
|
1626
|
-
const [obj, objInst] = c.handleConsume(scope, node.object);
|
|
1785
|
+
const MemberExpression = (c, scope, node, out) => {
|
|
1627
1786
|
const [prop, propInst] = node.computed
|
|
1628
|
-
? c.
|
|
1787
|
+
? c.handleEval(scope, node.property)
|
|
1629
1788
|
: [new LiteralValue(node.property.name), []];
|
|
1630
|
-
|
|
1789
|
+
if (!out) {
|
|
1790
|
+
const [obj, objInst] = c.handleEval(scope, node.object);
|
|
1791
|
+
const [got, gotInst] = obj.get(scope, prop);
|
|
1792
|
+
return [got, [...objInst, ...propInst, ...gotInst]];
|
|
1793
|
+
}
|
|
1794
|
+
const temp = SenseableValue.out(scope, out);
|
|
1795
|
+
const objOut = new DestructuringValue(new Map([
|
|
1796
|
+
[
|
|
1797
|
+
prop,
|
|
1798
|
+
{
|
|
1799
|
+
value: temp,
|
|
1800
|
+
// a direct assignment should NOT happen
|
|
1801
|
+
handler: () => {
|
|
1802
|
+
throw new CompilerError("Unexpected destructuring assignment");
|
|
1803
|
+
},
|
|
1804
|
+
},
|
|
1805
|
+
],
|
|
1806
|
+
]));
|
|
1807
|
+
const [obj, objInst] = c.handleEval(scope, node.object, objOut);
|
|
1808
|
+
const [got, gotInst] = obj.get(scope, prop, temp);
|
|
1631
1809
|
return [got, [...objInst, ...propInst, ...gotInst]];
|
|
1632
1810
|
};
|
|
1633
1811
|
const ArrayPattern = (c, scope, node) => {
|
|
@@ -1637,11 +1815,21 @@ const ArrayPattern = (c, scope, node) => {
|
|
|
1637
1815
|
const element = node.elements[i];
|
|
1638
1816
|
if (!element)
|
|
1639
1817
|
continue;
|
|
1640
|
-
const valueInst = c.handle(scope, element);
|
|
1641
|
-
if (!
|
|
1818
|
+
const [value, valueInst] = c.handle(scope, element);
|
|
1819
|
+
if (!value)
|
|
1642
1820
|
throw new CompilerError("Destructuring element must resolve to a value", element);
|
|
1643
|
-
inst.push(...valueInst
|
|
1644
|
-
members.set(new LiteralValue(i),
|
|
1821
|
+
inst.push(...valueInst);
|
|
1822
|
+
members.set(new LiteralValue(i), {
|
|
1823
|
+
value,
|
|
1824
|
+
handler(get) {
|
|
1825
|
+
return c.handle(scope, element, () => {
|
|
1826
|
+
const inst = get();
|
|
1827
|
+
// assigns the output to the target value
|
|
1828
|
+
inst[1].push(...value["="](scope, inst[0])[1]);
|
|
1829
|
+
return inst;
|
|
1830
|
+
});
|
|
1831
|
+
},
|
|
1832
|
+
});
|
|
1645
1833
|
}
|
|
1646
1834
|
return [new DestructuringValue(members), inst];
|
|
1647
1835
|
};
|
|
@@ -1656,13 +1844,23 @@ const ObjectPattern = (c, scope, node) => {
|
|
|
1656
1844
|
const { key } = prop;
|
|
1657
1845
|
const keyInst = !prop.computed && key.type === "Identifier"
|
|
1658
1846
|
? [new LiteralValue(key.name), []]
|
|
1659
|
-
: c.
|
|
1847
|
+
: c.handleEval(scope, prop.key);
|
|
1660
1848
|
inst.push(...keyInst[1]);
|
|
1661
|
-
const valueInst = c.handle(scope, prop.value);
|
|
1662
|
-
if (!
|
|
1849
|
+
const [value, valueInst] = c.handle(scope, prop.value);
|
|
1850
|
+
if (!value)
|
|
1663
1851
|
throw new CompilerError("Destructuring member must resolve to a value", prop.value);
|
|
1664
|
-
inst.push(...valueInst
|
|
1665
|
-
members.set(keyInst[0],
|
|
1852
|
+
inst.push(...valueInst);
|
|
1853
|
+
members.set(keyInst[0], {
|
|
1854
|
+
value,
|
|
1855
|
+
handler(get) {
|
|
1856
|
+
return c.handle(scope, prop, () => {
|
|
1857
|
+
const inst = get();
|
|
1858
|
+
// assigns the output to the target value
|
|
1859
|
+
inst[1].push(...value["="](scope, inst[0])[1]);
|
|
1860
|
+
return inst;
|
|
1861
|
+
});
|
|
1862
|
+
},
|
|
1863
|
+
});
|
|
1666
1864
|
}
|
|
1667
1865
|
return [new DestructuringValue(members), inst];
|
|
1668
1866
|
};
|
|
@@ -1707,7 +1905,7 @@ const TSEnumDeclaration = (c, scope, node) => {
|
|
|
1707
1905
|
: [new LiteralValue(counter)];
|
|
1708
1906
|
if (!(value instanceof LiteralValue))
|
|
1709
1907
|
throw new CompilerError("Enum members must contain literal values", member);
|
|
1710
|
-
if (
|
|
1908
|
+
if (value.isNumber()) {
|
|
1711
1909
|
lastType = "number";
|
|
1712
1910
|
counter = value.data + 1;
|
|
1713
1911
|
}
|
|
@@ -1717,13 +1915,9 @@ const TSEnumDeclaration = (c, scope, node) => {
|
|
|
1717
1915
|
const name = member.id.type === "Identifier" ? member.id.name : member.id.value;
|
|
1718
1916
|
data[name] = value;
|
|
1719
1917
|
}
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
constant: true,
|
|
1724
|
-
identifier: node.id.name,
|
|
1725
|
-
name: nodeName(node, !c.compactNames && node.id.name),
|
|
1726
|
-
}));
|
|
1918
|
+
const value = new ObjectValue(data);
|
|
1919
|
+
value.name = nodeName(node, !c.compactNames && node.id.name);
|
|
1920
|
+
scope.set(node.id.name, value);
|
|
1727
1921
|
return [null, []];
|
|
1728
1922
|
};
|
|
1729
1923
|
const TSNonNullExpression = (c, scope, node) => {
|
|
@@ -1787,15 +1981,12 @@ class MacroFunction extends VoidValue {
|
|
|
1787
1981
|
this.mutability = EMutability.constant;
|
|
1788
1982
|
this.fn = fn;
|
|
1789
1983
|
}
|
|
1790
|
-
call(scope, args) {
|
|
1791
|
-
return this.fn.apply(this, [scope, ...args]);
|
|
1984
|
+
call(scope, args, out) {
|
|
1985
|
+
return this.fn.apply(this, [scope, out, ...args]);
|
|
1792
1986
|
}
|
|
1793
1987
|
eval(_scope) {
|
|
1794
1988
|
return [this, []];
|
|
1795
1989
|
}
|
|
1796
|
-
consume(_scope) {
|
|
1797
|
-
return [this, []];
|
|
1798
|
-
}
|
|
1799
1990
|
}
|
|
1800
1991
|
|
|
1801
1992
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
@@ -1819,49 +2010,39 @@ const mathOperations = {
|
|
|
1819
2010
|
sqrt: a => Math.sqrt(a),
|
|
1820
2011
|
rand: null,
|
|
1821
2012
|
};
|
|
1822
|
-
function
|
|
1823
|
-
return new ValueOwner({
|
|
1824
|
-
scope,
|
|
1825
|
-
constant: true,
|
|
1826
|
-
name,
|
|
1827
|
-
value: assign(new StoreValue(scope), {
|
|
1828
|
-
mutability: EMutability.constant,
|
|
1829
|
-
}),
|
|
1830
|
-
}).value;
|
|
1831
|
-
}
|
|
1832
|
-
function createMacroMathOperations(scope) {
|
|
2013
|
+
function createMacroMathOperations() {
|
|
1833
2014
|
const macroMathOperations = {
|
|
1834
|
-
PI:
|
|
1835
|
-
E:
|
|
1836
|
-
degToRad:
|
|
1837
|
-
radToDeg:
|
|
2015
|
+
PI: new StoreValue("@pi", EMutability.constant),
|
|
2016
|
+
E: new StoreValue("@e", EMutability.constant),
|
|
2017
|
+
degToRad: new StoreValue("@degToRad", EMutability.constant),
|
|
2018
|
+
radToDeg: new StoreValue("@radToDeg", EMutability.constant),
|
|
1838
2019
|
};
|
|
1839
2020
|
for (const key in mathOperations) {
|
|
1840
2021
|
const fn = mathOperations[key];
|
|
1841
|
-
macroMathOperations[key] = new MacroFunction((scope, a, b) => {
|
|
2022
|
+
macroMathOperations[key] = new MacroFunction((scope, out, a, b) => {
|
|
1842
2023
|
if (b) {
|
|
1843
2024
|
if (fn && a instanceof LiteralValue && b instanceof LiteralValue) {
|
|
1844
|
-
if (
|
|
2025
|
+
if (!a.isNumber() || !b.isNumber())
|
|
1845
2026
|
throw new CompilerError("Cannot do math operation with non-numerical literals.");
|
|
1846
2027
|
return [new LiteralValue(fn(a.num, b.num)), []];
|
|
1847
2028
|
}
|
|
1848
|
-
const temp =
|
|
2029
|
+
const temp = StoreValue.from(scope, out);
|
|
1849
2030
|
return [temp, [new OperationInstruction(key, temp, a, b)]];
|
|
1850
2031
|
}
|
|
1851
2032
|
if (fn && a instanceof LiteralValue) {
|
|
1852
|
-
if (
|
|
2033
|
+
if (!a.isNumber())
|
|
1853
2034
|
throw new CompilerError("Cannot do math operation with non-numerical literal.");
|
|
1854
2035
|
return [new LiteralValue(fn(a.num)), []];
|
|
1855
2036
|
}
|
|
1856
|
-
const temp =
|
|
2037
|
+
const temp = StoreValue.from(scope, out);
|
|
1857
2038
|
return [temp, [new OperationInstruction(key, temp, a, b)]];
|
|
1858
2039
|
});
|
|
1859
2040
|
}
|
|
1860
2041
|
return macroMathOperations;
|
|
1861
2042
|
}
|
|
1862
2043
|
class MlogMath extends ObjectValue {
|
|
1863
|
-
constructor(
|
|
1864
|
-
super(createMacroMathOperations(
|
|
2044
|
+
constructor() {
|
|
2045
|
+
super(createMacroMathOperations());
|
|
1865
2046
|
}
|
|
1866
2047
|
}
|
|
1867
2048
|
// op angle result a b
|
|
@@ -1869,7 +2050,7 @@ class MlogMath extends ObjectValue {
|
|
|
1869
2050
|
|
|
1870
2051
|
class Concat extends MacroFunction {
|
|
1871
2052
|
constructor() {
|
|
1872
|
-
super((scope, ...values) => {
|
|
2053
|
+
super((scope, out, ...values) => {
|
|
1873
2054
|
for (const value of values)
|
|
1874
2055
|
if (!(value instanceof LiteralValue))
|
|
1875
2056
|
throw new CompilerError("Concat arguments must all be literal values.");
|
|
@@ -1881,66 +2062,19 @@ class Concat extends MacroFunction {
|
|
|
1881
2062
|
}
|
|
1882
2063
|
}
|
|
1883
2064
|
|
|
1884
|
-
/** Asserts that `value` is a `LiteralValue` that contains a string */
|
|
1885
|
-
function assertStringLiteral(value, name) {
|
|
1886
|
-
if (!(value instanceof LiteralValue) || typeof value.data !== "string")
|
|
1887
|
-
throw new CompilerError(`${name} must be a string literal`);
|
|
1888
|
-
}
|
|
1889
|
-
/** Asserts that `value` is a string literal that is part of a given set of `members`*/
|
|
1890
|
-
function assertLiteralOneOf(value, members, name) {
|
|
1891
|
-
assertStringLiteral(value, name);
|
|
1892
|
-
if (!members.includes(value.data))
|
|
1893
|
-
throw new CompilerError(`${name} must be one of: ${members
|
|
1894
|
-
.slice(0, -1)
|
|
1895
|
-
.map(member => `"${member}"`)
|
|
1896
|
-
.join(", ")} or "${members[members.length - 1]}"`);
|
|
1897
|
-
}
|
|
1898
|
-
/** Asserts that `value` is either a store or a literal */
|
|
1899
|
-
function assertIsRuntimeValue(value, name) {
|
|
1900
|
-
if (!(value instanceof LiteralValue) && !(value instanceof StoreValue))
|
|
1901
|
-
throw new CompilerError(`${name} must be a valid runtime value`);
|
|
1902
|
-
}
|
|
1903
|
-
/** Asserts that `value` is an `ObjectValue` */
|
|
1904
|
-
function assertIsObjectMacro(value, name) {
|
|
1905
|
-
if (!(value instanceof ObjectValue))
|
|
1906
|
-
throw new CompilerError(`${name} must be an object macro`);
|
|
1907
|
-
}
|
|
1908
|
-
/** Asserts that `value` is an `ObjectValue` that has a length property */
|
|
1909
|
-
function assertIsArrayMacro(value, name) {
|
|
1910
|
-
if (!(value instanceof ObjectValue) || !value.data.length)
|
|
1911
|
-
throw new CompilerError(`${name} must be an array macro`);
|
|
1912
|
-
}
|
|
1913
|
-
/** Asserts that all of the fields described are present on `value`*/
|
|
1914
|
-
function assertObjectFields(value, fields) {
|
|
1915
|
-
const result = [];
|
|
1916
|
-
for (const field of fields) {
|
|
1917
|
-
const param = typeof field === "object" ? field : { key: field };
|
|
1918
|
-
const item = value.data[param.key];
|
|
1919
|
-
if (item) {
|
|
1920
|
-
param.validate?.(item);
|
|
1921
|
-
result.push(item);
|
|
1922
|
-
continue;
|
|
1923
|
-
}
|
|
1924
|
-
if (param.default == undefined)
|
|
1925
|
-
throw new CompilerError(`The field "${param.name ?? param.key}" must be present`);
|
|
1926
|
-
result.push(param.default);
|
|
1927
|
-
}
|
|
1928
|
-
return result;
|
|
1929
|
-
}
|
|
1930
|
-
|
|
1931
2065
|
/** Used to create namespaces that contain multiple methods that map into instructions */
|
|
1932
2066
|
function createOverloadNamespace({ overloads, handler, }) {
|
|
1933
2067
|
const result = {};
|
|
1934
2068
|
for (const key in overloads) {
|
|
1935
2069
|
const { named, args } = overloads[key];
|
|
1936
2070
|
if (named) {
|
|
1937
|
-
result[key] = new MacroFunction((scope, options) => {
|
|
2071
|
+
result[key] = new MacroFunction((scope, out, options) => {
|
|
1938
2072
|
assertIsObjectMacro(options, named);
|
|
1939
|
-
return handler(scope, key, ...assertObjectFields(options, args));
|
|
2073
|
+
return handler(scope, key, out, ...assertObjectFields(options, args));
|
|
1940
2074
|
});
|
|
1941
2075
|
continue;
|
|
1942
2076
|
}
|
|
1943
|
-
result[key] = new MacroFunction((scope, ...params) => {
|
|
2077
|
+
result[key] = new MacroFunction((scope, out, ...params) => {
|
|
1944
2078
|
const handlerParams = [];
|
|
1945
2079
|
// validate the paramters
|
|
1946
2080
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -1957,7 +2091,7 @@ function createOverloadNamespace({ overloads, handler, }) {
|
|
|
1957
2091
|
arg.validate?.(param);
|
|
1958
2092
|
handlerParams.push(param);
|
|
1959
2093
|
}
|
|
1960
|
-
return handler(scope, key, ...handlerParams);
|
|
2094
|
+
return handler(scope, key, out, ...handlerParams);
|
|
1961
2095
|
});
|
|
1962
2096
|
}
|
|
1963
2097
|
return result;
|
|
@@ -2002,7 +2136,7 @@ class Draw extends ObjectValue {
|
|
|
2002
2136
|
args: ["x", "y", "image", "size", "rotation"],
|
|
2003
2137
|
},
|
|
2004
2138
|
},
|
|
2005
|
-
handler(scope, overload, ...args) {
|
|
2139
|
+
handler(scope, overload, out, ...args) {
|
|
2006
2140
|
return [null, [new InstructionBase("draw", overload, ...args)]];
|
|
2007
2141
|
},
|
|
2008
2142
|
});
|
|
@@ -2012,7 +2146,7 @@ class Draw extends ObjectValue {
|
|
|
2012
2146
|
|
|
2013
2147
|
class Print extends MacroFunction {
|
|
2014
2148
|
constructor() {
|
|
2015
|
-
super((scope, ...values) => {
|
|
2149
|
+
super((scope, out, ...values) => {
|
|
2016
2150
|
const [first] = values;
|
|
2017
2151
|
const inst = [];
|
|
2018
2152
|
if (!isTemplateObjectArray(first)) {
|
|
@@ -2041,7 +2175,7 @@ class Print extends MacroFunction {
|
|
|
2041
2175
|
const defaultTargetName$1 = "message1";
|
|
2042
2176
|
class PrintFlush extends MacroFunction {
|
|
2043
2177
|
constructor() {
|
|
2044
|
-
super((scope, target) => {
|
|
2178
|
+
super((scope, out, target) => {
|
|
2045
2179
|
if (!target)
|
|
2046
2180
|
return [null, [new InstructionBase("printflush", defaultTargetName$1)]];
|
|
2047
2181
|
if (!(target instanceof SenseableValue))
|
|
@@ -2054,7 +2188,7 @@ class PrintFlush extends MacroFunction {
|
|
|
2054
2188
|
const defaultTargetName = "display1";
|
|
2055
2189
|
class DrawFlush extends MacroFunction {
|
|
2056
2190
|
constructor() {
|
|
2057
|
-
super((scope, target) => {
|
|
2191
|
+
super((scope, out, target) => {
|
|
2058
2192
|
if (!target)
|
|
2059
2193
|
return [null, [new InstructionBase("drawflush", defaultTargetName)]];
|
|
2060
2194
|
if (!(target instanceof SenseableValue))
|
|
@@ -2073,10 +2207,10 @@ class GetBlock extends ObjectValue {
|
|
|
2073
2207
|
block: { args: ["x", "y"] },
|
|
2074
2208
|
building: { args: ["x", "y"] },
|
|
2075
2209
|
},
|
|
2076
|
-
handler(scope, overload, x, y) {
|
|
2210
|
+
handler(scope, overload, out, x, y) {
|
|
2077
2211
|
const output = overload === "building"
|
|
2078
|
-
?
|
|
2079
|
-
:
|
|
2212
|
+
? SenseableValue.from(scope, out, EMutability.constant)
|
|
2213
|
+
: StoreValue.from(scope, out, EMutability.constant);
|
|
2080
2214
|
return [
|
|
2081
2215
|
output,
|
|
2082
2216
|
[new InstructionBase("getblock", overload, output, x, y)],
|
|
@@ -2089,11 +2223,11 @@ class GetBlock extends ObjectValue {
|
|
|
2089
2223
|
|
|
2090
2224
|
class GetLink extends MacroFunction {
|
|
2091
2225
|
constructor() {
|
|
2092
|
-
super((scope, index) => {
|
|
2226
|
+
super((scope, out, index) => {
|
|
2093
2227
|
if (!(index instanceof StoreValue) &&
|
|
2094
|
-
(!(index instanceof LiteralValue) ||
|
|
2228
|
+
(!(index instanceof LiteralValue) || !index.isNumber()))
|
|
2095
2229
|
throw new CompilerError("The getlink index must be a number literal or a store");
|
|
2096
|
-
const outBuild =
|
|
2230
|
+
const outBuild = SenseableValue.from(scope, out, EMutability.constant);
|
|
2097
2231
|
return [outBuild, [new InstructionBase("getlink", outBuild, index)]];
|
|
2098
2232
|
});
|
|
2099
2233
|
}
|
|
@@ -2115,11 +2249,10 @@ class Control extends ObjectValue {
|
|
|
2115
2249
|
config: { args: ["building", "value"] },
|
|
2116
2250
|
color: { args: ["building", "r", "g", "b"] },
|
|
2117
2251
|
},
|
|
2118
|
-
handler(scope, overload, ...args) {
|
|
2252
|
+
handler(scope, overload, out, ...args) {
|
|
2119
2253
|
if (overload !== "color")
|
|
2120
2254
|
return [null, [new InstructionBase("control", overload, ...args)]];
|
|
2121
|
-
const temp =
|
|
2122
|
-
temp.ensureOwned();
|
|
2255
|
+
const temp = StoreValue.from(scope);
|
|
2123
2256
|
// this "patch" is here for compatibility reasons
|
|
2124
2257
|
// I don't want to add another breaking change
|
|
2125
2258
|
// TODO: figure out if this patch should be removed later
|
|
@@ -2155,14 +2288,14 @@ const validRadarSorts = [
|
|
|
2155
2288
|
];
|
|
2156
2289
|
class Radar extends MacroFunction {
|
|
2157
2290
|
constructor() {
|
|
2158
|
-
super((scope, options) => {
|
|
2291
|
+
super((scope, out, options) => {
|
|
2159
2292
|
assertIsObjectMacro(options, "The radar options");
|
|
2160
2293
|
const { building, filters, order, sort } = options.data;
|
|
2161
2294
|
if (!(building instanceof SenseableValue))
|
|
2162
2295
|
throw new CompilerError("The building must a senseable value");
|
|
2163
2296
|
assertIsArrayMacro(filters, "filters");
|
|
2164
2297
|
const { length } = filters.data;
|
|
2165
|
-
if (!(length instanceof LiteralValue) ||
|
|
2298
|
+
if (!(length instanceof LiteralValue) || !length.isNumber())
|
|
2166
2299
|
throw new CompilerError("The length of an array macro must be");
|
|
2167
2300
|
if (length.data !== 3)
|
|
2168
2301
|
throw new CompilerError("The filters array must have 3 items");
|
|
@@ -2174,7 +2307,7 @@ class Radar extends MacroFunction {
|
|
|
2174
2307
|
if (!(order instanceof LiteralValue || order instanceof StoreValue))
|
|
2175
2308
|
throw new CompilerError("The radar order must be a literal or a store");
|
|
2176
2309
|
assertLiteralOneOf(sort, validRadarSorts, "The radar sort");
|
|
2177
|
-
const outUnit =
|
|
2310
|
+
const outUnit = SenseableValue.from(scope, out, EMutability.constant);
|
|
2178
2311
|
return [
|
|
2179
2312
|
outUnit,
|
|
2180
2313
|
[
|
|
@@ -2187,14 +2320,12 @@ class Radar extends MacroFunction {
|
|
|
2187
2320
|
|
|
2188
2321
|
class Sensor extends MacroFunction {
|
|
2189
2322
|
constructor() {
|
|
2190
|
-
super((scope, property, target) => {
|
|
2323
|
+
super((scope, out, property, target) => {
|
|
2191
2324
|
if (!(property instanceof StoreValue))
|
|
2192
2325
|
throw new CompilerError("The sensor property must be a store");
|
|
2193
2326
|
if (!(target instanceof SenseableValue))
|
|
2194
2327
|
throw new CompilerError("The sensor target must be a senseable value");
|
|
2195
|
-
const temp =
|
|
2196
|
-
mutability: EMutability.readonly,
|
|
2197
|
-
});
|
|
2328
|
+
const temp = SenseableValue.from(scope, out, EMutability.readonly);
|
|
2198
2329
|
return [temp, [new InstructionBase("sensor", temp, target, property)]];
|
|
2199
2330
|
});
|
|
2200
2331
|
}
|
|
@@ -2202,9 +2333,9 @@ class Sensor extends MacroFunction {
|
|
|
2202
2333
|
|
|
2203
2334
|
class Wait extends MacroFunction {
|
|
2204
2335
|
constructor() {
|
|
2205
|
-
super((scope, seconds) => {
|
|
2336
|
+
super((scope, out, seconds) => {
|
|
2206
2337
|
if (!(seconds instanceof StoreValue) &&
|
|
2207
|
-
!(seconds instanceof LiteralValue &&
|
|
2338
|
+
!(seconds instanceof LiteralValue && seconds.isNumber()))
|
|
2208
2339
|
throw new CompilerError("The wait seconds must be either a number literal or a store");
|
|
2209
2340
|
return [null, [new InstructionBase("wait", seconds)]];
|
|
2210
2341
|
});
|
|
@@ -2220,8 +2351,8 @@ class Lookup extends ObjectValue {
|
|
|
2220
2351
|
item: { args: ["index"] },
|
|
2221
2352
|
liquid: { args: ["index"] },
|
|
2222
2353
|
},
|
|
2223
|
-
handler(scope, overload, index) {
|
|
2224
|
-
const output =
|
|
2354
|
+
handler(scope, overload, out, index) {
|
|
2355
|
+
const output = StoreValue.from(scope, out);
|
|
2225
2356
|
return [
|
|
2226
2357
|
output,
|
|
2227
2358
|
[new InstructionBase("lookup", overload, output, index)],
|
|
@@ -2234,15 +2365,15 @@ class Lookup extends ObjectValue {
|
|
|
2234
2365
|
|
|
2235
2366
|
class PackColor extends MacroFunction {
|
|
2236
2367
|
constructor() {
|
|
2237
|
-
super((scope, ...args) => {
|
|
2368
|
+
super((scope, out, ...args) => {
|
|
2238
2369
|
if (args.length !== 4) {
|
|
2239
2370
|
throw new CompilerError(`Expected 4 arguments, received ${args.length}`);
|
|
2240
2371
|
}
|
|
2241
2372
|
if (!args.every((value) => value instanceof StoreValue ||
|
|
2242
|
-
(value instanceof LiteralValue &&
|
|
2373
|
+
(value instanceof LiteralValue && value.isNumber()))) {
|
|
2243
2374
|
throw new CompilerError("packColor arguments must be either stores or number literals");
|
|
2244
2375
|
}
|
|
2245
|
-
const output =
|
|
2376
|
+
const output = StoreValue.from(scope, out);
|
|
2246
2377
|
return [output, [new InstructionBase("packcolor", output, ...args)]];
|
|
2247
2378
|
});
|
|
2248
2379
|
}
|
|
@@ -2265,7 +2396,7 @@ class SetBlock extends ObjectValue {
|
|
|
2265
2396
|
args: ["x", "y", "to", "team", { key: "rotation", default: "0" }],
|
|
2266
2397
|
},
|
|
2267
2398
|
},
|
|
2268
|
-
handler(scope, overload, x, y, to, team, rotation) {
|
|
2399
|
+
handler(scope, overload, out, x, y, to, team, rotation) {
|
|
2269
2400
|
return [
|
|
2270
2401
|
null,
|
|
2271
2402
|
[
|
|
@@ -2280,14 +2411,14 @@ class SetBlock extends ObjectValue {
|
|
|
2280
2411
|
|
|
2281
2412
|
class SpawnUnit extends MacroFunction {
|
|
2282
2413
|
constructor() {
|
|
2283
|
-
super((scope, options) => {
|
|
2414
|
+
super((scope, out, options) => {
|
|
2284
2415
|
assertIsObjectMacro(options, "The options");
|
|
2285
2416
|
const { type, x, y, team, rotation } = options.data;
|
|
2286
2417
|
assertIsRuntimeValue(type, "type");
|
|
2287
2418
|
assertIsRuntimeValue(x, "x");
|
|
2288
2419
|
assertIsRuntimeValue(y, "y");
|
|
2289
2420
|
assertIsRuntimeValue(team, "team");
|
|
2290
|
-
const output =
|
|
2421
|
+
const output = SenseableValue.from(scope, out, EMutability.constant);
|
|
2291
2422
|
return [
|
|
2292
2423
|
output,
|
|
2293
2424
|
[
|
|
@@ -2306,7 +2437,7 @@ class Stop extends MacroFunction {
|
|
|
2306
2437
|
|
|
2307
2438
|
class UnitBind extends MacroFunction {
|
|
2308
2439
|
constructor() {
|
|
2309
|
-
super((scope, type) => {
|
|
2440
|
+
super((scope, out, type) => {
|
|
2310
2441
|
if (!(type instanceof StoreValue))
|
|
2311
2442
|
throw new CompilerError("The unitBind type must be a store");
|
|
2312
2443
|
return [null, [new InstructionBase("ubind", type)]];
|
|
@@ -2345,21 +2476,20 @@ class UnitControl extends ObjectValue {
|
|
|
2345
2476
|
args: ["x", "y", "radius"],
|
|
2346
2477
|
},
|
|
2347
2478
|
},
|
|
2348
|
-
handler(scope, overload, ...args) {
|
|
2479
|
+
handler(scope, overload, out, ...args) {
|
|
2349
2480
|
let result = null;
|
|
2350
2481
|
let extraArgs = [];
|
|
2351
2482
|
switch (overload) {
|
|
2352
2483
|
case "getBlock": {
|
|
2353
|
-
const outType =
|
|
2354
|
-
const outBuilding =
|
|
2484
|
+
const outType = StoreValue.from(scope, extractDestrucuringOut(out, 0));
|
|
2485
|
+
const outBuilding = SenseableValue.from(scope, extractDestrucuringOut(out, 1));
|
|
2355
2486
|
result = ObjectValue.fromArray([outType, outBuilding]);
|
|
2356
2487
|
extraArgs = [outType, outBuilding, new LiteralValue(0)];
|
|
2357
2488
|
break;
|
|
2358
2489
|
}
|
|
2359
2490
|
case "within": {
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
extraArgs = [temp, new LiteralValue(0)];
|
|
2491
|
+
result = StoreValue.from(scope, out);
|
|
2492
|
+
extraArgs = [result, new LiteralValue(0)];
|
|
2363
2493
|
break;
|
|
2364
2494
|
}
|
|
2365
2495
|
}
|
|
@@ -2375,12 +2505,12 @@ class UnitControl extends ObjectValue {
|
|
|
2375
2505
|
|
|
2376
2506
|
class UnitRadar extends MacroFunction {
|
|
2377
2507
|
constructor() {
|
|
2378
|
-
super((scope, options) => {
|
|
2508
|
+
super((scope, out, options) => {
|
|
2379
2509
|
assertIsObjectMacro(options, "The radar options");
|
|
2380
2510
|
const { filters, order, sort } = options.data;
|
|
2381
2511
|
assertIsArrayMacro(filters, "filters");
|
|
2382
2512
|
const { length } = filters.data;
|
|
2383
|
-
if (!(length instanceof LiteralValue) ||
|
|
2513
|
+
if (!(length instanceof LiteralValue) || !length.isNumber())
|
|
2384
2514
|
throw new CompilerError("The length of an array macro must be");
|
|
2385
2515
|
if (length.data !== 3)
|
|
2386
2516
|
throw new CompilerError("The filters array must have 3 items");
|
|
@@ -2392,7 +2522,7 @@ class UnitRadar extends MacroFunction {
|
|
|
2392
2522
|
if (!(order instanceof LiteralValue || order instanceof StoreValue))
|
|
2393
2523
|
throw new CompilerError("The radar order must be a literal or a store");
|
|
2394
2524
|
assertLiteralOneOf(sort, validRadarSorts, "The radar sort");
|
|
2395
|
-
const outUnit =
|
|
2525
|
+
const outUnit = SenseableValue.from(scope, out, EMutability.constant);
|
|
2396
2526
|
return [
|
|
2397
2527
|
outUnit,
|
|
2398
2528
|
[
|
|
@@ -2430,11 +2560,11 @@ class UnitLocate extends ObjectValue {
|
|
|
2430
2560
|
args: [],
|
|
2431
2561
|
},
|
|
2432
2562
|
},
|
|
2433
|
-
handler(scope, overload, ...args) {
|
|
2434
|
-
const outFound =
|
|
2435
|
-
const outX =
|
|
2436
|
-
const outY =
|
|
2437
|
-
const outBuilding =
|
|
2563
|
+
handler(scope, overload, out, ...args) {
|
|
2564
|
+
const outFound = StoreValue.from(scope, extractDestrucuringOut(out, 0));
|
|
2565
|
+
const outX = StoreValue.from(scope, extractDestrucuringOut(out, 1));
|
|
2566
|
+
const outY = StoreValue.from(scope, extractDestrucuringOut(out, 2));
|
|
2567
|
+
const outBuilding = SenseableValue.from(scope, overload === "ore" ? discardedName : extractDestrucuringOut(out, 3));
|
|
2438
2568
|
const outArgs = [outX, outY, outFound, outBuilding];
|
|
2439
2569
|
let inputArgs = [];
|
|
2440
2570
|
switch (overload) {
|
|
@@ -2497,7 +2627,7 @@ class ApplyStatus extends ObjectValue {
|
|
|
2497
2627
|
},
|
|
2498
2628
|
clear: { args: ["status", "unit"] },
|
|
2499
2629
|
},
|
|
2500
|
-
handler(scope, overload, effect, unit, duration) {
|
|
2630
|
+
handler(scope, overload, out, effect, unit, duration) {
|
|
2501
2631
|
if (!(effect instanceof LiteralValue))
|
|
2502
2632
|
throw new CompilerError("The status effect must be a string literal");
|
|
2503
2633
|
assertLiteralOneOf(effect, validEffects, "The status effect");
|
|
@@ -2515,7 +2645,7 @@ class ApplyStatus extends ObjectValue {
|
|
|
2515
2645
|
|
|
2516
2646
|
class SpawnWave extends MacroFunction {
|
|
2517
2647
|
constructor() {
|
|
2518
|
-
super((scope, natural, x, y) => {
|
|
2648
|
+
super((scope, out, natural, x, y) => {
|
|
2519
2649
|
if (!(natural instanceof LiteralValue) ||
|
|
2520
2650
|
(natural.data !== 1 && natural.data !== 0))
|
|
2521
2651
|
throw new CompilerError("The 'natural' argument must be a boolean literal");
|
|
@@ -2556,7 +2686,7 @@ class SetRule extends ObjectValue {
|
|
|
2556
2686
|
rtsMinWeight: { args: ["team", "value"] },
|
|
2557
2687
|
rtsMinSquad: { args: ["team", "value"] },
|
|
2558
2688
|
},
|
|
2559
|
-
handler(scope, overload, ...args) {
|
|
2689
|
+
handler(scope, overload, out, ...args) {
|
|
2560
2690
|
const params = ["10", "0", "0", "100", "100"];
|
|
2561
2691
|
switch (overload) {
|
|
2562
2692
|
case "mapArea": {
|
|
@@ -2599,7 +2729,7 @@ class FlushMessage extends ObjectValue {
|
|
|
2599
2729
|
announce: { args: ["duration"] },
|
|
2600
2730
|
toast: { args: ["duration"] },
|
|
2601
2731
|
},
|
|
2602
|
-
handler(scope, overload, duration) {
|
|
2732
|
+
handler(scope, overload, out, duration) {
|
|
2603
2733
|
return [null, [new InstructionBase("message", overload, duration)]];
|
|
2604
2734
|
},
|
|
2605
2735
|
});
|
|
@@ -2615,7 +2745,7 @@ class Cutscene extends ObjectValue {
|
|
|
2615
2745
|
zoom: { args: ["level"] },
|
|
2616
2746
|
stop: { args: [] },
|
|
2617
2747
|
},
|
|
2618
|
-
handler(scope, overload, ...args) {
|
|
2748
|
+
handler(scope, overload, out, ...args) {
|
|
2619
2749
|
const params = Object.assign(["100", "100", "0.06", "0"], args);
|
|
2620
2750
|
return [null, [new InstructionBase("cutscene", overload, ...params)]];
|
|
2621
2751
|
},
|
|
@@ -2626,7 +2756,7 @@ class Cutscene extends ObjectValue {
|
|
|
2626
2756
|
|
|
2627
2757
|
class Explosion extends MacroFunction {
|
|
2628
2758
|
constructor() {
|
|
2629
|
-
super((scope, options) => {
|
|
2759
|
+
super((scope, out, options) => {
|
|
2630
2760
|
assertIsObjectMacro(options, "The options");
|
|
2631
2761
|
const params = assertObjectFields(options, [
|
|
2632
2762
|
"team",
|
|
@@ -2645,7 +2775,7 @@ class Explosion extends MacroFunction {
|
|
|
2645
2775
|
|
|
2646
2776
|
class SetRate extends MacroFunction {
|
|
2647
2777
|
constructor() {
|
|
2648
|
-
super((scope, ipt) => [null, [new InstructionBase("setrate", ipt)]]);
|
|
2778
|
+
super((scope, out, ipt) => [null, [new InstructionBase("setrate", ipt)]]);
|
|
2649
2779
|
}
|
|
2650
2780
|
}
|
|
2651
2781
|
|
|
@@ -2662,8 +2792,8 @@ class Fetch extends ObjectValue {
|
|
|
2662
2792
|
build: { args: ["team", "index", "block"] },
|
|
2663
2793
|
buildCount: { args: ["team", "block"] },
|
|
2664
2794
|
},
|
|
2665
|
-
handler(scope, overload, team, ...rest) {
|
|
2666
|
-
const output =
|
|
2795
|
+
handler(scope, overload, out, team, ...rest) {
|
|
2796
|
+
const output = SenseableValue.from(scope, out, EMutability.constant);
|
|
2667
2797
|
const params = ["0", "@conveyor"];
|
|
2668
2798
|
if (overload === "buildCount") {
|
|
2669
2799
|
params[1] = rest[0];
|
|
@@ -2683,8 +2813,8 @@ class Fetch extends ObjectValue {
|
|
|
2683
2813
|
|
|
2684
2814
|
class GetFlag extends MacroFunction {
|
|
2685
2815
|
constructor() {
|
|
2686
|
-
super((scope, flag) => {
|
|
2687
|
-
const result =
|
|
2816
|
+
super((scope, out, flag) => {
|
|
2817
|
+
const result = StoreValue.from(scope, out);
|
|
2688
2818
|
return [result, [new InstructionBase("getflag", result, flag)]];
|
|
2689
2819
|
});
|
|
2690
2820
|
}
|
|
@@ -2692,7 +2822,7 @@ class GetFlag extends MacroFunction {
|
|
|
2692
2822
|
|
|
2693
2823
|
class SetFlag extends MacroFunction {
|
|
2694
2824
|
constructor() {
|
|
2695
|
-
super((scope, flag, value) => {
|
|
2825
|
+
super((scope, out, flag, value) => {
|
|
2696
2826
|
if (!flag)
|
|
2697
2827
|
throw new CompilerError("Missing flag argument");
|
|
2698
2828
|
if (!value)
|
|
@@ -2711,28 +2841,17 @@ class MemoryEntry extends BaseValue {
|
|
|
2711
2841
|
this.macro = true;
|
|
2712
2842
|
this.store = null;
|
|
2713
2843
|
}
|
|
2714
|
-
eval(scope) {
|
|
2844
|
+
eval(scope, out) {
|
|
2715
2845
|
if (this.store)
|
|
2716
2846
|
return [this.store, []];
|
|
2717
|
-
const temp =
|
|
2847
|
+
const temp = (this.store = StoreValue.from(scope, out));
|
|
2718
2848
|
return [
|
|
2719
2849
|
temp,
|
|
2720
2850
|
[new InstructionBase("read", temp, this.mem.cell, this.prop)],
|
|
2721
2851
|
];
|
|
2722
2852
|
}
|
|
2723
|
-
consume(scope) {
|
|
2724
|
-
if (this.store)
|
|
2725
|
-
return [this.store, []];
|
|
2726
|
-
this.ensureOwned();
|
|
2727
|
-
this.store = new StoreValue(scope);
|
|
2728
|
-
this.owner.own(this.store);
|
|
2729
|
-
return [
|
|
2730
|
-
this.store,
|
|
2731
|
-
[new InstructionBase("read", this.store, this.mem.cell, this.prop)],
|
|
2732
|
-
];
|
|
2733
|
-
}
|
|
2734
2853
|
"="(scope, value) {
|
|
2735
|
-
const [data, dataInst] = value.
|
|
2854
|
+
const [data, dataInst] = value.eval(scope);
|
|
2736
2855
|
return [
|
|
2737
2856
|
data,
|
|
2738
2857
|
[
|
|
@@ -2741,26 +2860,17 @@ class MemoryEntry extends BaseValue {
|
|
|
2741
2860
|
],
|
|
2742
2861
|
];
|
|
2743
2862
|
}
|
|
2744
|
-
ensureOwned() {
|
|
2745
|
-
this.owner ??= new ValueOwner({ scope: this.scope, value: this });
|
|
2746
|
-
}
|
|
2747
|
-
}
|
|
2748
|
-
for (const operator of operators) {
|
|
2749
|
-
if (operator === "=")
|
|
2750
|
-
continue;
|
|
2751
|
-
MemoryEntry.prototype[operator] = function (...args) {
|
|
2752
|
-
// eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/no-unsafe-return
|
|
2753
|
-
return BaseValue.prototype[operator].apply(this, args);
|
|
2754
|
-
};
|
|
2755
2863
|
}
|
|
2756
2864
|
class MemoryMacro extends ObjectValue {
|
|
2757
2865
|
constructor(cell, size) {
|
|
2758
2866
|
super({
|
|
2759
|
-
$get: new MacroFunction((scope, prop) => {
|
|
2760
|
-
if (prop instanceof LiteralValue &&
|
|
2867
|
+
$get: new MacroFunction((scope, out, prop) => {
|
|
2868
|
+
if (prop instanceof LiteralValue && !prop.isNumber())
|
|
2761
2869
|
throw new CompilerError(`Invalid memory object property: "${prop.data}"`);
|
|
2762
|
-
const
|
|
2763
|
-
|
|
2870
|
+
const entry = new MemoryEntry(scope, this, prop);
|
|
2871
|
+
if (out)
|
|
2872
|
+
return entry.eval(scope, out);
|
|
2873
|
+
return [entry, []];
|
|
2764
2874
|
}),
|
|
2765
2875
|
length: size,
|
|
2766
2876
|
size,
|
|
@@ -2774,10 +2884,10 @@ class MemoryMacro extends ObjectValue {
|
|
|
2774
2884
|
class MemoryBuilder extends ObjectValue {
|
|
2775
2885
|
constructor() {
|
|
2776
2886
|
super({
|
|
2777
|
-
$call: new MacroFunction((scope, cell, size = new LiteralValue(64)) => {
|
|
2887
|
+
$call: new MacroFunction((scope, out, cell, size = new LiteralValue(64)) => {
|
|
2778
2888
|
if (!(cell instanceof SenseableValue))
|
|
2779
2889
|
throw new CompilerError("Memory cell must be a senseable value.");
|
|
2780
|
-
if (!(size instanceof LiteralValue &&
|
|
2890
|
+
if (!(size instanceof LiteralValue && size.isNumber()))
|
|
2781
2891
|
throw new CompilerError("The memory size must be a number literal.");
|
|
2782
2892
|
return [new MemoryMacro(cell, size), []];
|
|
2783
2893
|
}),
|
|
@@ -2789,8 +2899,8 @@ const dynamicVars = ["time", "tick"];
|
|
|
2789
2899
|
class NamespaceMacro extends ObjectValue {
|
|
2790
2900
|
constructor({ changeCasing = false } = {}) {
|
|
2791
2901
|
super({
|
|
2792
|
-
$get: new MacroFunction((scope, prop) => {
|
|
2793
|
-
if (!(prop instanceof LiteralValue) ||
|
|
2902
|
+
$get: new MacroFunction((scope, out, prop) => {
|
|
2903
|
+
if (!(prop instanceof LiteralValue) || !prop.isString())
|
|
2794
2904
|
throw new CompilerError("Cannot use dynamic properties on namespace macros");
|
|
2795
2905
|
const symbolName = this.changeCasing
|
|
2796
2906
|
? camelToDashCase(prop.data)
|
|
@@ -2798,57 +2908,37 @@ class NamespaceMacro extends ObjectValue {
|
|
|
2798
2908
|
const mutability = dynamicVars.includes(symbolName)
|
|
2799
2909
|
? EMutability.readonly
|
|
2800
2910
|
: EMutability.constant;
|
|
2801
|
-
const
|
|
2802
|
-
|
|
2803
|
-
value: assign(new StoreValue(scope), {
|
|
2804
|
-
mutability,
|
|
2805
|
-
}),
|
|
2806
|
-
name: `@${symbolName}`,
|
|
2807
|
-
});
|
|
2808
|
-
return [owner.value, []];
|
|
2911
|
+
const result = new StoreValue(`@${symbolName}`, mutability);
|
|
2912
|
+
return [result, []];
|
|
2809
2913
|
}),
|
|
2810
2914
|
});
|
|
2811
2915
|
this.changeCasing = changeCasing;
|
|
2812
2916
|
}
|
|
2813
2917
|
}
|
|
2814
2918
|
class VarsNamespace extends NamespaceMacro {
|
|
2815
|
-
constructor(
|
|
2919
|
+
constructor() {
|
|
2816
2920
|
super();
|
|
2817
2921
|
Object.assign(this.data, {
|
|
2818
|
-
unit: new
|
|
2819
|
-
|
|
2820
|
-
name: "@unit",
|
|
2821
|
-
value: new SenseableValue(scope),
|
|
2822
|
-
}).value,
|
|
2823
|
-
this: new ValueOwner({
|
|
2824
|
-
scope,
|
|
2825
|
-
name: "@this",
|
|
2826
|
-
value: new SenseableValue(scope),
|
|
2827
|
-
}).value,
|
|
2922
|
+
unit: new SenseableValue("@unit"),
|
|
2923
|
+
this: new SenseableValue("@this"),
|
|
2828
2924
|
});
|
|
2829
2925
|
}
|
|
2830
2926
|
}
|
|
2831
2927
|
|
|
2832
2928
|
class GetGlobal extends MacroFunction {
|
|
2833
2929
|
constructor(mutability) {
|
|
2834
|
-
super((scope, name) => {
|
|
2835
|
-
if (!(name instanceof LiteralValue) ||
|
|
2930
|
+
super((scope, out, name) => {
|
|
2931
|
+
if (!(name instanceof LiteralValue) || !name.isString())
|
|
2836
2932
|
throw new CompilerError("The name parameter must be a string literal.");
|
|
2837
|
-
const
|
|
2838
|
-
|
|
2839
|
-
name: name.data,
|
|
2840
|
-
value: assign(new SenseableValue(scope), {
|
|
2841
|
-
mutability,
|
|
2842
|
-
}),
|
|
2843
|
-
});
|
|
2844
|
-
return [owner.value, []];
|
|
2933
|
+
const result = new SenseableValue(name.data, mutability);
|
|
2934
|
+
return [result, []];
|
|
2845
2935
|
});
|
|
2846
2936
|
}
|
|
2847
2937
|
}
|
|
2848
2938
|
|
|
2849
2939
|
class Asm extends MacroFunction {
|
|
2850
2940
|
constructor() {
|
|
2851
|
-
super((scope, stringsArray, ...values) => {
|
|
2941
|
+
super((scope, out, stringsArray, ...values) => {
|
|
2852
2942
|
if (!isTemplateObjectArray(stringsArray))
|
|
2853
2943
|
throw new CompilerError("Expected to receive a template strings array");
|
|
2854
2944
|
const args = [];
|
|
@@ -2926,7 +3016,7 @@ function validateInstructionArgs(args) {
|
|
|
2926
3016
|
function initScope(scope) {
|
|
2927
3017
|
// namespaces
|
|
2928
3018
|
scope.hardSet("ControlKind", new NamespaceMacro());
|
|
2929
|
-
scope.hardSet("Vars", new VarsNamespace(
|
|
3019
|
+
scope.hardSet("Vars", new VarsNamespace());
|
|
2930
3020
|
scope.hardSet("Teams", new NamespaceMacro());
|
|
2931
3021
|
scope.hardSet("Items", new NamespaceMacro({ changeCasing: true }));
|
|
2932
3022
|
scope.hardSet("Liquids", new NamespaceMacro());
|
|
@@ -2938,7 +3028,7 @@ function initScope(scope) {
|
|
|
2938
3028
|
scope.hardSet("getVar", new GetGlobal(EMutability.mutable));
|
|
2939
3029
|
scope.hardSet("concat", new Concat());
|
|
2940
3030
|
scope.hardSet("asm", new Asm());
|
|
2941
|
-
scope.hardSet("Math", new MlogMath(
|
|
3031
|
+
scope.hardSet("Math", new MlogMath());
|
|
2942
3032
|
scope.hardSet("Memory", new MemoryBuilder());
|
|
2943
3033
|
// commands
|
|
2944
3034
|
scope.hardSet("draw", new Draw());
|
|
@@ -3030,11 +3120,11 @@ class Compiler {
|
|
|
3030
3120
|
}
|
|
3031
3121
|
return result;
|
|
3032
3122
|
}
|
|
3033
|
-
handle(scope, node, handler = this.handlers[node.type]) {
|
|
3123
|
+
handle(scope, node, handler = this.handlers[node.type], out) {
|
|
3034
3124
|
try {
|
|
3035
3125
|
if (!handler)
|
|
3036
3126
|
throw new CompilerError("Missing handler for " + node.type);
|
|
3037
|
-
const result = handler(this, scope, node, null);
|
|
3127
|
+
const result = handler(this, scope, node, out, null);
|
|
3038
3128
|
if (this.sourcemap)
|
|
3039
3129
|
return appendSourceLocations(result, node);
|
|
3040
3130
|
return result;
|
|
@@ -3046,21 +3136,11 @@ class Compiler {
|
|
|
3046
3136
|
throw error;
|
|
3047
3137
|
}
|
|
3048
3138
|
}
|
|
3049
|
-
handleEval(scope, node) {
|
|
3050
|
-
const [res, inst] = this.handle(scope, node);
|
|
3139
|
+
handleEval(scope, node, out) {
|
|
3140
|
+
const [res, inst] = this.handle(scope, node, undefined, out);
|
|
3051
3141
|
if (!res)
|
|
3052
3142
|
throw new CompilerError("Cannot eval null", node);
|
|
3053
|
-
const [evaluated, evaluatedInst] = res.eval(scope);
|
|
3054
|
-
const result = [evaluated, [...inst, ...evaluatedInst]];
|
|
3055
|
-
if (this.sourcemap)
|
|
3056
|
-
return appendSourceLocations(result, node);
|
|
3057
|
-
return result;
|
|
3058
|
-
}
|
|
3059
|
-
handleConsume(scope, node) {
|
|
3060
|
-
const [res, inst] = this.handle(scope, node);
|
|
3061
|
-
if (!res)
|
|
3062
|
-
throw new CompilerError("Cannot consume null", node);
|
|
3063
|
-
const [evaluated, evaluatedInst] = res.consume(scope);
|
|
3143
|
+
const [evaluated, evaluatedInst] = res.eval(scope, out);
|
|
3064
3144
|
const result = [evaluated, [...inst, ...evaluatedInst]];
|
|
3065
3145
|
if (this.sourcemap)
|
|
3066
3146
|
return appendSourceLocations(result, node);
|
|
@@ -3072,7 +3152,7 @@ class Compiler {
|
|
|
3072
3152
|
* The usage of this method over a regular loop over an array of nodes
|
|
3073
3153
|
* is only required if the code inside the loop generates
|
|
3074
3154
|
* instructions that are not tracked by the compiler
|
|
3075
|
-
* handler methods ({@link handle}, {@link handleEval}
|
|
3155
|
+
* handler methods ({@link handle}, {@link handleEval}
|
|
3076
3156
|
* and {@link handleMany})
|
|
3077
3157
|
*
|
|
3078
3158
|
*/
|
|
@@ -3085,30 +3165,6 @@ class Compiler {
|
|
|
3085
3165
|
return [null, lines];
|
|
3086
3166
|
}
|
|
3087
3167
|
}
|
|
3088
|
-
/** Removes jump instructions that point right to the next line */
|
|
3089
|
-
function hideRedundantJumps(inst) {
|
|
3090
|
-
for (let i = 0; i < inst.length; i++) {
|
|
3091
|
-
const instruction = inst[i];
|
|
3092
|
-
if (!(instruction instanceof JumpInstruction))
|
|
3093
|
-
continue;
|
|
3094
|
-
const { address } = instruction;
|
|
3095
|
-
let searchIndex = i + 1;
|
|
3096
|
-
let current = inst[searchIndex];
|
|
3097
|
-
while (current.hidden) {
|
|
3098
|
-
if (current instanceof AddressResolver &&
|
|
3099
|
-
current.bonds.includes(address)) {
|
|
3100
|
-
// only gets assigned if `current` is an adress resolver
|
|
3101
|
-
// that resolves the jump adress
|
|
3102
|
-
// and that is also right next to the jump,
|
|
3103
|
-
// without any actual instructions in between
|
|
3104
|
-
instruction.hidden = true;
|
|
3105
|
-
break;
|
|
3106
|
-
}
|
|
3107
|
-
searchIndex++;
|
|
3108
|
-
current = inst[searchIndex];
|
|
3109
|
-
}
|
|
3110
|
-
}
|
|
3111
|
-
}
|
|
3112
3168
|
function* hoistedFunctionNodes(nodes) {
|
|
3113
3169
|
// sorting is O(n long n) while this is just O(n)
|
|
3114
3170
|
// besides, it's better not to modify the node array
|