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.mjs
CHANGED
|
@@ -69,6 +69,8 @@ var EMutability;
|
|
|
69
69
|
EMutability[EMutability["readonly"] = 1] = "readonly";
|
|
70
70
|
/** Constant values, won't change during execution */
|
|
71
71
|
EMutability[EMutability["constant"] = 2] = "constant";
|
|
72
|
+
/** An immutable value that hasn't been initialized yet */
|
|
73
|
+
EMutability[EMutability["init"] = 3] = "init";
|
|
72
74
|
})(EMutability || (EMutability = {}));
|
|
73
75
|
|
|
74
76
|
const arithmeticBinaryOperators = [
|
|
@@ -148,24 +150,20 @@ const operators = [...leftRightOperators, ...singleOperators];
|
|
|
148
150
|
|
|
149
151
|
class VoidValue {
|
|
150
152
|
constructor() {
|
|
151
|
-
this.owner = null;
|
|
152
153
|
this.mutability = EMutability.mutable;
|
|
153
154
|
this.macro = false;
|
|
154
155
|
}
|
|
155
|
-
eval(_scope) {
|
|
156
|
+
eval(_scope, _out) {
|
|
156
157
|
throw new CompilerError(`${this} cannot eval.`);
|
|
157
158
|
}
|
|
158
|
-
|
|
159
|
-
throw new CompilerError(`${this} cannot be consumed.`);
|
|
160
|
-
}
|
|
161
|
-
call(_scope, _args) {
|
|
159
|
+
call(_scope, _args, _out) {
|
|
162
160
|
throw new CompilerError(`${this} cannot call.`);
|
|
163
161
|
}
|
|
164
162
|
get(_scope, _name) {
|
|
165
163
|
throw new CompilerError(`${this} cannot get.`);
|
|
166
164
|
}
|
|
167
|
-
|
|
168
|
-
|
|
165
|
+
paramOuts() {
|
|
166
|
+
return;
|
|
169
167
|
}
|
|
170
168
|
toString() {
|
|
171
169
|
return "void";
|
|
@@ -203,6 +201,176 @@ class OperationInstruction extends InstructionBase {
|
|
|
203
201
|
}
|
|
204
202
|
}
|
|
205
203
|
|
|
204
|
+
/**
|
|
205
|
+
* The prefix for internal variables inside the compiler output
|
|
206
|
+
*/
|
|
207
|
+
const internalPrefix = "&";
|
|
208
|
+
const discardedName = `${internalPrefix}_`;
|
|
209
|
+
const itemNames = [
|
|
210
|
+
"copper",
|
|
211
|
+
"lead",
|
|
212
|
+
"metaglass",
|
|
213
|
+
"graphite",
|
|
214
|
+
"sand",
|
|
215
|
+
"coal",
|
|
216
|
+
"titanium",
|
|
217
|
+
"thorium",
|
|
218
|
+
"scrap",
|
|
219
|
+
"silicon",
|
|
220
|
+
"plastanium",
|
|
221
|
+
"phaseFabric",
|
|
222
|
+
"surgeAlloy",
|
|
223
|
+
"sporePod",
|
|
224
|
+
"blastCompound",
|
|
225
|
+
"pyratite",
|
|
226
|
+
"beryllium",
|
|
227
|
+
"tungsten",
|
|
228
|
+
"oxide",
|
|
229
|
+
"carbide",
|
|
230
|
+
"fissileMatter",
|
|
231
|
+
"dormantCyst",
|
|
232
|
+
];
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Returns a string that has the format: [line]:[column].
|
|
236
|
+
*
|
|
237
|
+
* If `name` is provided, and it is a string, the resulting string will
|
|
238
|
+
* have the format: [name]:[line]:[column].
|
|
239
|
+
*/
|
|
240
|
+
function nodeName(node, name) {
|
|
241
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
242
|
+
const { line, column } = node.loc.start;
|
|
243
|
+
if (typeof name === "string")
|
|
244
|
+
return `${name}:${line}:${column}`;
|
|
245
|
+
return `${line}:${column}`;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Converts a camel case string into a dash case one.
|
|
249
|
+
*
|
|
250
|
+
* As an example `camelCase` becomes `camel-case`
|
|
251
|
+
* @example
|
|
252
|
+
* ```
|
|
253
|
+
* camelToDashCase("camelCase") // returns "camel-case"
|
|
254
|
+
* ```
|
|
255
|
+
*/
|
|
256
|
+
function camelToDashCase(name) {
|
|
257
|
+
return name.replace(/[A-Z]/g, str => `-${str.toLowerCase()}`);
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* A more type safe version of `Object.assign`
|
|
261
|
+
*/
|
|
262
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
263
|
+
function assign(obj, props) {
|
|
264
|
+
return Object.assign(obj, props);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function appendSourceLocations(valueInst, node) {
|
|
268
|
+
for (const inst of valueInst[1]) {
|
|
269
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
270
|
+
inst.source ??= node.loc;
|
|
271
|
+
}
|
|
272
|
+
return valueInst;
|
|
273
|
+
}
|
|
274
|
+
function withAlwaysRuns(valueInst, value) {
|
|
275
|
+
valueInst[1].forEach(inst => (inst.alwaysRuns = value));
|
|
276
|
+
return valueInst;
|
|
277
|
+
}
|
|
278
|
+
/** Removes jump instructions that point right to the next line */
|
|
279
|
+
function hideRedundantJumps(inst) {
|
|
280
|
+
for (let i = 0; i < inst.length; i++) {
|
|
281
|
+
const instruction = inst[i];
|
|
282
|
+
if (!(instruction instanceof JumpInstruction))
|
|
283
|
+
continue;
|
|
284
|
+
const { address } = instruction;
|
|
285
|
+
let searchIndex = i + 1;
|
|
286
|
+
let current = inst[searchIndex];
|
|
287
|
+
while (current?.hidden) {
|
|
288
|
+
if (current instanceof AddressResolver &&
|
|
289
|
+
current.bonds.includes(address)) {
|
|
290
|
+
// only gets assigned if `current` is an adress resolver
|
|
291
|
+
// that resolves the jump adress
|
|
292
|
+
// and that is also right next to the jump,
|
|
293
|
+
// without any actual instructions in between
|
|
294
|
+
instruction.hidden = true;
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
searchIndex++;
|
|
298
|
+
current = inst[searchIndex];
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function isTemplateObjectArray(value) {
|
|
304
|
+
return (value instanceof ObjectValue &&
|
|
305
|
+
value.data.length instanceof LiteralValue &&
|
|
306
|
+
value.data.length.isNumber() &&
|
|
307
|
+
value.data.raw instanceof ObjectValue &&
|
|
308
|
+
value.data.raw.data.length instanceof LiteralValue &&
|
|
309
|
+
value.data.raw.data.length.isNumber());
|
|
310
|
+
}
|
|
311
|
+
function extractOutName(out) {
|
|
312
|
+
if (!out || typeof out === "string")
|
|
313
|
+
return out;
|
|
314
|
+
return out.name;
|
|
315
|
+
}
|
|
316
|
+
function extractDestrucuringOut(out, field) {
|
|
317
|
+
if (out === discardedName)
|
|
318
|
+
return out;
|
|
319
|
+
if (!(out instanceof DestructuringValue))
|
|
320
|
+
return;
|
|
321
|
+
return out.fields[field] ?? discardedName;
|
|
322
|
+
}
|
|
323
|
+
/** Asserts that `value` is a `LiteralValue` that contains a string */
|
|
324
|
+
function assertStringLiteral(value, name) {
|
|
325
|
+
if (!(value instanceof LiteralValue) || !value.isString())
|
|
326
|
+
throw new CompilerError(`${name} must be a string literal`);
|
|
327
|
+
}
|
|
328
|
+
/** Asserts that `value` is a string literal that is part of a given set of `members`*/
|
|
329
|
+
function assertLiteralOneOf(value, members, name) {
|
|
330
|
+
assertStringLiteral(value, name);
|
|
331
|
+
if (!members.includes(value.data))
|
|
332
|
+
throw new CompilerError(`${name} must be one of: ${members
|
|
333
|
+
.slice(0, -1)
|
|
334
|
+
.map(member => `"${member}"`)
|
|
335
|
+
.join(", ")} or "${members[members.length - 1]}"`);
|
|
336
|
+
}
|
|
337
|
+
/** Asserts that `value` is either a store or a literal */
|
|
338
|
+
function assertIsRuntimeValue(value, name) {
|
|
339
|
+
if (!(value instanceof LiteralValue) && !(value instanceof StoreValue))
|
|
340
|
+
throw new CompilerError(`${name} must be a valid runtime value`);
|
|
341
|
+
}
|
|
342
|
+
/** Asserts that `value` is an `ObjectValue` */
|
|
343
|
+
function assertIsObjectMacro(value, name) {
|
|
344
|
+
if (!(value instanceof ObjectValue))
|
|
345
|
+
throw new CompilerError(`${name} must be an object macro`);
|
|
346
|
+
}
|
|
347
|
+
/** Asserts that `value` is an `ObjectValue` that has a length property */
|
|
348
|
+
function assertIsArrayMacro(value, name) {
|
|
349
|
+
if (value instanceof ObjectValue) {
|
|
350
|
+
const { length } = value.data;
|
|
351
|
+
if (length instanceof LiteralValue && length.isNumber())
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
throw new CompilerError(`${name} must be an array macro`);
|
|
355
|
+
}
|
|
356
|
+
/** Asserts that all of the fields described are present on `value`*/
|
|
357
|
+
function assertObjectFields(value, fields) {
|
|
358
|
+
const result = [];
|
|
359
|
+
for (const field of fields) {
|
|
360
|
+
const param = typeof field === "object" ? field : { key: field };
|
|
361
|
+
const item = value.data[param.key];
|
|
362
|
+
if (item) {
|
|
363
|
+
param.validate?.(item);
|
|
364
|
+
result.push(item);
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
if (param.default == undefined)
|
|
368
|
+
throw new CompilerError(`The field "${param.name ?? param.key}" must be present`);
|
|
369
|
+
result.push(param.default);
|
|
370
|
+
}
|
|
371
|
+
return result;
|
|
372
|
+
}
|
|
373
|
+
|
|
206
374
|
class SetInstruction extends InstructionBase {
|
|
207
375
|
constructor(store, value) {
|
|
208
376
|
super("set", store, value);
|
|
@@ -217,12 +385,12 @@ class SetInstruction extends InstructionBase {
|
|
|
217
385
|
const { store, value } = this;
|
|
218
386
|
return (this._hidden ||
|
|
219
387
|
// assignment to discarded value
|
|
220
|
-
|
|
388
|
+
store.name === discardedName ||
|
|
221
389
|
// self assignment of a store
|
|
222
|
-
(!!store.
|
|
390
|
+
(!!store.name &&
|
|
223
391
|
store instanceof StoreValue &&
|
|
224
392
|
value instanceof StoreValue &&
|
|
225
|
-
store.
|
|
393
|
+
store.name === value.name));
|
|
226
394
|
}
|
|
227
395
|
}
|
|
228
396
|
|
|
@@ -302,9 +470,9 @@ class ContinueInstruction extends JumpInstruction {
|
|
|
302
470
|
}
|
|
303
471
|
|
|
304
472
|
class BaseValue extends VoidValue {
|
|
305
|
-
"u-"(scope) {
|
|
306
|
-
const [that, inst] = this.
|
|
307
|
-
const temp =
|
|
473
|
+
"u-"(scope, out) {
|
|
474
|
+
const [that, inst] = this.eval(scope);
|
|
475
|
+
const temp = StoreValue.from(scope, out);
|
|
308
476
|
return [
|
|
309
477
|
temp,
|
|
310
478
|
[
|
|
@@ -313,40 +481,37 @@ class BaseValue extends VoidValue {
|
|
|
313
481
|
],
|
|
314
482
|
];
|
|
315
483
|
}
|
|
316
|
-
"u+"(scope) {
|
|
484
|
+
"u+"(scope, out) {
|
|
317
485
|
// TODO: should it return 0 + this ?
|
|
318
|
-
return this.eval(scope);
|
|
486
|
+
return this.eval(scope, out);
|
|
319
487
|
}
|
|
320
|
-
"!"(scope) {
|
|
321
|
-
const [that, inst] = this.
|
|
322
|
-
const temp =
|
|
488
|
+
"!"(scope, out) {
|
|
489
|
+
const [that, inst] = this.eval(scope);
|
|
490
|
+
const temp = StoreValue.from(scope, out);
|
|
323
491
|
const falseLiteral = new LiteralValue(0);
|
|
324
492
|
return [
|
|
325
493
|
temp,
|
|
326
494
|
[...inst, new OperationInstruction("equal", temp, that, falseLiteral)],
|
|
327
495
|
];
|
|
328
496
|
}
|
|
329
|
-
"~"(scope) {
|
|
330
|
-
const [that, inst] = this.
|
|
331
|
-
const temp =
|
|
497
|
+
"~"(scope, out) {
|
|
498
|
+
const [that, inst] = this.eval(scope);
|
|
499
|
+
const temp = StoreValue.from(scope, out);
|
|
332
500
|
return [temp, [...inst, new OperationInstruction("not", temp, that, null)]];
|
|
333
501
|
}
|
|
334
502
|
// requires special handling
|
|
335
503
|
// the handler should give an object value to allow the lazy evaluation
|
|
336
|
-
"??"(scope, other) {
|
|
337
|
-
|
|
504
|
+
"??"(scope, other, out) {
|
|
505
|
+
if (this instanceof LiteralValue) {
|
|
506
|
+
if (this.data === null)
|
|
507
|
+
return other.eval(scope, out);
|
|
508
|
+
return [this, []];
|
|
509
|
+
}
|
|
510
|
+
const result = SenseableValue.out(scope, out);
|
|
511
|
+
const [left, leftInst] = this.eval(scope, result);
|
|
512
|
+
const [right, rightInst] = other.eval(scope, result);
|
|
338
513
|
const nullLiteral = new LiteralValue(null);
|
|
339
514
|
const endAdress = new LiteralValue(null);
|
|
340
|
-
const [nullTest] = left["=="](scope, nullLiteral);
|
|
341
|
-
if (nullTest instanceof LiteralValue) {
|
|
342
|
-
if (nullTest.data)
|
|
343
|
-
return other.eval(scope);
|
|
344
|
-
else
|
|
345
|
-
return [left, leftInst];
|
|
346
|
-
}
|
|
347
|
-
const [right, rightInst] = other.eval(scope);
|
|
348
|
-
const result = new StoreValue(scope);
|
|
349
|
-
result.ensureOwned();
|
|
350
515
|
return [
|
|
351
516
|
result,
|
|
352
517
|
[
|
|
@@ -386,10 +551,10 @@ const operatorMap$1 = {
|
|
|
386
551
|
};
|
|
387
552
|
for (const key in operatorMap$1) {
|
|
388
553
|
const kind = operatorMap$1[key];
|
|
389
|
-
BaseValue.prototype[key] = function (scope, value) {
|
|
390
|
-
const [left, leftInst] = this.
|
|
391
|
-
const [right, rightInst] = value.
|
|
392
|
-
const temp =
|
|
554
|
+
BaseValue.prototype[key] = function (scope, value, out) {
|
|
555
|
+
const [left, leftInst] = this.eval(scope);
|
|
556
|
+
const [right, rightInst] = value.eval(scope);
|
|
557
|
+
const temp = StoreValue.from(scope, out);
|
|
393
558
|
return [
|
|
394
559
|
temp,
|
|
395
560
|
[
|
|
@@ -417,19 +582,19 @@ const assignmentToBinary = {
|
|
|
417
582
|
"||=": "||",
|
|
418
583
|
"??=": "??",
|
|
419
584
|
};
|
|
420
|
-
for (const
|
|
585
|
+
for (const k in assignmentToBinary) {
|
|
586
|
+
const op = k;
|
|
421
587
|
BaseValue.prototype[op] = function (scope, value) {
|
|
422
|
-
this
|
|
423
|
-
const [opValue, opInst] = this[assignmentToBinary[op]](scope, value);
|
|
588
|
+
const [opValue, opInst] = this[assignmentToBinary[op]](scope, value, this);
|
|
424
589
|
const [retValue, retInst] = this["="](scope, opValue);
|
|
425
590
|
return [retValue, [...opInst, ...retInst]];
|
|
426
591
|
};
|
|
427
592
|
}
|
|
428
593
|
for (const key of updateOperators) {
|
|
429
|
-
BaseValue.prototype[key] = function (scope, prefix) {
|
|
430
|
-
let [ret, inst] = this.
|
|
594
|
+
BaseValue.prototype[key] = function (scope, prefix, out) {
|
|
595
|
+
let [ret, inst] = this.eval(scope);
|
|
431
596
|
if (!prefix) {
|
|
432
|
-
const temp =
|
|
597
|
+
const temp = StoreValue.out(scope, out);
|
|
433
598
|
const [tempValue, tempInst] = temp["="](scope, ret);
|
|
434
599
|
ret = tempValue;
|
|
435
600
|
inst.push(...tempInst);
|
|
@@ -441,7 +606,7 @@ for (const key of updateOperators) {
|
|
|
441
606
|
|
|
442
607
|
const literalMethods = {
|
|
443
608
|
length: function () {
|
|
444
|
-
if (
|
|
609
|
+
if (!this.isString())
|
|
445
610
|
throw new CompilerError("Length method only works on string literal values.");
|
|
446
611
|
return new LiteralValue(this.data.length);
|
|
447
612
|
},
|
|
@@ -455,14 +620,11 @@ class LiteralValue extends BaseValue {
|
|
|
455
620
|
eval(_scope) {
|
|
456
621
|
return [this, []];
|
|
457
622
|
}
|
|
458
|
-
consume(_scope) {
|
|
459
|
-
return [this, []];
|
|
460
|
-
}
|
|
461
623
|
toString() {
|
|
462
624
|
return JSON.stringify(this.data);
|
|
463
625
|
}
|
|
464
626
|
get(scope, name) {
|
|
465
|
-
if (!(name instanceof LiteralValue &&
|
|
627
|
+
if (!(name instanceof LiteralValue && name.isString()))
|
|
466
628
|
return super.get(scope, name);
|
|
467
629
|
const method = literalMethods[name.data];
|
|
468
630
|
if (!method)
|
|
@@ -479,7 +641,12 @@ class LiteralValue extends BaseValue {
|
|
|
479
641
|
typeof() {
|
|
480
642
|
return [new LiteralValue("literal"), []];
|
|
481
643
|
}
|
|
482
|
-
|
|
644
|
+
isString() {
|
|
645
|
+
return typeof this.data === "string";
|
|
646
|
+
}
|
|
647
|
+
isNumber() {
|
|
648
|
+
return typeof this.data === "number";
|
|
649
|
+
}
|
|
483
650
|
}
|
|
484
651
|
const operatorMap = {
|
|
485
652
|
"==": (a, b) => +(a == b),
|
|
@@ -505,11 +672,22 @@ const operatorMap = {
|
|
|
505
672
|
"&&": (a, b) => +(a && b),
|
|
506
673
|
"||": (a, b) => +(a || b),
|
|
507
674
|
};
|
|
508
|
-
for (const
|
|
675
|
+
for (const k in operatorMap) {
|
|
676
|
+
const key = k;
|
|
509
677
|
const fn = operatorMap[key];
|
|
510
|
-
LiteralValue.prototype[key] = function (scope, value) {
|
|
678
|
+
LiteralValue.prototype[key] = function (scope, value, out) {
|
|
679
|
+
if (key === "&&") {
|
|
680
|
+
if (this.data)
|
|
681
|
+
return [value, []];
|
|
682
|
+
return [new LiteralValue(0), []];
|
|
683
|
+
}
|
|
684
|
+
if (key === "||") {
|
|
685
|
+
if (!this.data)
|
|
686
|
+
return [value, []];
|
|
687
|
+
return [new LiteralValue(1), []];
|
|
688
|
+
}
|
|
511
689
|
if (!(value instanceof LiteralValue)) {
|
|
512
|
-
return BaseValue.prototype[key].apply(this, [scope, value]);
|
|
690
|
+
return BaseValue.prototype[key].apply(this, [scope, value, out]);
|
|
513
691
|
}
|
|
514
692
|
return [new LiteralValue(fn(this.data, value.data)), []];
|
|
515
693
|
};
|
|
@@ -527,162 +705,45 @@ for (const key in unaryOperatorMap) {
|
|
|
527
705
|
};
|
|
528
706
|
}
|
|
529
707
|
|
|
530
|
-
/**
|
|
531
|
-
* The prefix for internal variables inside the compiler output
|
|
532
|
-
*/
|
|
533
|
-
const internalPrefix = "&";
|
|
534
|
-
const discardedName = `${internalPrefix}_`;
|
|
535
|
-
/**
|
|
536
|
-
* Returns a string that has the format: [line]:[column].
|
|
537
|
-
*
|
|
538
|
-
* If `name` is provided, and it is a string, the resulting string will
|
|
539
|
-
* have the format: [name]:[line]:[column].
|
|
540
|
-
*/
|
|
541
|
-
function nodeName(node, name) {
|
|
542
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
543
|
-
const { line, column } = node.loc.start;
|
|
544
|
-
if (typeof name === "string")
|
|
545
|
-
return `${name}:${line}:${column}`;
|
|
546
|
-
return `${line}:${column}`;
|
|
547
|
-
}
|
|
548
|
-
/**
|
|
549
|
-
* Converts a camel case string into a dash case one.
|
|
550
|
-
*
|
|
551
|
-
* As an example `camelCase` becomes `camel-case`
|
|
552
|
-
* @example
|
|
553
|
-
* ```
|
|
554
|
-
* camelToDashCase("camelCase") // returns "camel-case"
|
|
555
|
-
* ```
|
|
556
|
-
*/
|
|
557
|
-
function camelToDashCase(name) {
|
|
558
|
-
return name.replace(/[A-Z]/g, str => `-${str.toLowerCase()}`);
|
|
559
|
-
}
|
|
560
|
-
const itemNames = [
|
|
561
|
-
"copper",
|
|
562
|
-
"lead",
|
|
563
|
-
"metaglass",
|
|
564
|
-
"graphite",
|
|
565
|
-
"sand",
|
|
566
|
-
"coal",
|
|
567
|
-
"titanium",
|
|
568
|
-
"thorium",
|
|
569
|
-
"scrap",
|
|
570
|
-
"silicon",
|
|
571
|
-
"plastanium",
|
|
572
|
-
"phaseFabric",
|
|
573
|
-
"surgeAlloy",
|
|
574
|
-
"sporePod",
|
|
575
|
-
"blastCompound",
|
|
576
|
-
"pyratite",
|
|
577
|
-
"beryllium",
|
|
578
|
-
"tungsten",
|
|
579
|
-
"oxide",
|
|
580
|
-
"carbide",
|
|
581
|
-
"fissileMatter",
|
|
582
|
-
"dormantCyst",
|
|
583
|
-
];
|
|
584
|
-
/**
|
|
585
|
-
* A more type safe version of `Object.assign`
|
|
586
|
-
*/
|
|
587
|
-
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
588
|
-
function assign(obj, props) {
|
|
589
|
-
return Object.assign(obj, props);
|
|
590
|
-
}
|
|
591
|
-
function appendSourceLocations(valueInst, node) {
|
|
592
|
-
for (const inst of valueInst[1]) {
|
|
593
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
594
|
-
inst.source ??= node.loc;
|
|
595
|
-
}
|
|
596
|
-
return valueInst;
|
|
597
|
-
}
|
|
598
|
-
function withAlwaysRuns(valueInst, value) {
|
|
599
|
-
valueInst[1].forEach(inst => (inst.alwaysRuns = value));
|
|
600
|
-
return valueInst;
|
|
601
|
-
}
|
|
602
|
-
function isTemplateObjectArray(value) {
|
|
603
|
-
return (value instanceof ObjectValue &&
|
|
604
|
-
value.data.length instanceof LiteralValue &&
|
|
605
|
-
typeof value.data.length.data === "number" &&
|
|
606
|
-
value.data.raw instanceof ObjectValue &&
|
|
607
|
-
value.data.raw.data.length instanceof LiteralValue &&
|
|
608
|
-
typeof value.data.raw.data.length.data === "number");
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
class ValueOwner {
|
|
612
|
-
/**
|
|
613
|
-
* Creates a new `ValueOwner`, if `value` is already owned,
|
|
614
|
-
* `this` will be aliased to `value`, else `this` will own `value`
|
|
615
|
-
*/
|
|
616
|
-
constructor({ scope, constant = false, value, identifier, name, }) {
|
|
617
|
-
this.scope = scope;
|
|
618
|
-
this.constant = constant;
|
|
619
|
-
this.value = value;
|
|
620
|
-
this.identifier = identifier;
|
|
621
|
-
this.name = name ?? scope.makeTempName();
|
|
622
|
-
this.persistent = !!name;
|
|
623
|
-
this.owned = new Set();
|
|
624
|
-
if (!value.owner?.persistent)
|
|
625
|
-
this.own(value);
|
|
626
|
-
}
|
|
627
|
-
own(target) {
|
|
628
|
-
target.owner = this;
|
|
629
|
-
this.owned.add(target);
|
|
630
|
-
}
|
|
631
|
-
replace(target) {
|
|
632
|
-
this.value.owner = null;
|
|
633
|
-
this.owned.delete(this.value);
|
|
634
|
-
this.value = target;
|
|
635
|
-
this.own(target);
|
|
636
|
-
}
|
|
637
|
-
moveInto(owner) {
|
|
638
|
-
this.owned.forEach(value => owner.own(value));
|
|
639
|
-
this.owned.clear();
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
|
|
643
708
|
/**
|
|
644
709
|
* `StoreValue` represents values unknown at compile time,
|
|
645
710
|
* mostly used with mutable variables and temporary values
|
|
646
711
|
*/
|
|
647
712
|
class StoreValue extends BaseValue {
|
|
648
|
-
constructor(
|
|
713
|
+
constructor(name, mutability = EMutability.mutable) {
|
|
649
714
|
super();
|
|
650
|
-
this.
|
|
651
|
-
this.mutability =
|
|
715
|
+
this.name = name;
|
|
716
|
+
this.mutability = mutability;
|
|
717
|
+
}
|
|
718
|
+
static from(scope, out, mutability = EMutability.mutable) {
|
|
719
|
+
if (out instanceof StoreValue)
|
|
720
|
+
return out;
|
|
721
|
+
const name = typeof out === "string" ? out : scope.makeTempName();
|
|
722
|
+
return new StoreValue(name, mutability);
|
|
723
|
+
}
|
|
724
|
+
static out(scope, out, mutability = EMutability.mutable) {
|
|
725
|
+
if (!out || typeof out === "string") {
|
|
726
|
+
return new StoreValue(out ?? scope.makeTempName(), mutability);
|
|
727
|
+
}
|
|
728
|
+
return out;
|
|
652
729
|
}
|
|
653
730
|
typeof() {
|
|
654
731
|
return [new LiteralValue("store"), []];
|
|
655
732
|
}
|
|
656
733
|
"="(scope, value) {
|
|
657
|
-
if (this.mutability !== EMutability.mutable
|
|
734
|
+
if (this.mutability !== EMutability.mutable &&
|
|
735
|
+
this.mutability !== EMutability.init)
|
|
658
736
|
throw new CompilerError(`Cannot assign to immutable store '${this}'.`);
|
|
659
|
-
if (this.
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
if (!value.owner?.persistent) {
|
|
663
|
-
if (!value.owner)
|
|
664
|
-
this.owner.own(value);
|
|
665
|
-
else
|
|
666
|
-
value.owner.moveInto(this.owner);
|
|
667
|
-
if (value instanceof StoreValue)
|
|
668
|
-
return [this, []];
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
const [evalValue, evalInst] = value.consume(scope);
|
|
737
|
+
if (this.name === value.name && value instanceof StoreValue)
|
|
738
|
+
return [this, []];
|
|
739
|
+
const [evalValue, evalInst] = value.eval(scope, this);
|
|
672
740
|
return [this, [...evalInst, new SetInstruction(this, evalValue)]];
|
|
673
741
|
}
|
|
674
742
|
eval(_scope) {
|
|
675
743
|
return [this, []];
|
|
676
744
|
}
|
|
677
|
-
consume(_scope) {
|
|
678
|
-
this.ensureOwned();
|
|
679
|
-
return [this, []];
|
|
680
|
-
}
|
|
681
|
-
ensureOwned() {
|
|
682
|
-
this.owner ??= new ValueOwner({ scope: this.scope, value: this });
|
|
683
|
-
}
|
|
684
745
|
toString() {
|
|
685
|
-
return this.
|
|
746
|
+
return this.name;
|
|
686
747
|
}
|
|
687
748
|
}
|
|
688
749
|
|
|
@@ -704,10 +765,13 @@ class ObjectValue extends VoidValue {
|
|
|
704
765
|
});
|
|
705
766
|
return new ObjectValue(data);
|
|
706
767
|
}
|
|
707
|
-
typeof() {
|
|
768
|
+
typeof(scope, out) {
|
|
769
|
+
const { $typeof } = this.data;
|
|
770
|
+
if ($typeof)
|
|
771
|
+
return $typeof.call(scope, [], out);
|
|
708
772
|
return [new LiteralValue("object"), []];
|
|
709
773
|
}
|
|
710
|
-
get(scope, key) {
|
|
774
|
+
get(scope, key, out) {
|
|
711
775
|
// avoids naming collisions with keys like
|
|
712
776
|
// constructor or toString
|
|
713
777
|
if (Object.prototype.hasOwnProperty.call(this.data, key.data)) {
|
|
@@ -718,40 +782,45 @@ class ObjectValue extends VoidValue {
|
|
|
718
782
|
const { $get } = this.data;
|
|
719
783
|
if (!$get)
|
|
720
784
|
throw new CompilerError("Cannot get undefined member.");
|
|
721
|
-
return $get.call(scope, [key]);
|
|
785
|
+
return $get.call(scope, [key], out);
|
|
722
786
|
}
|
|
723
|
-
eval(scope) {
|
|
787
|
+
eval(scope, out) {
|
|
724
788
|
const { $eval } = this.data;
|
|
725
789
|
if (!$eval)
|
|
726
790
|
return [this, []];
|
|
727
|
-
return $eval.call(scope, []);
|
|
728
|
-
}
|
|
729
|
-
consume(scope) {
|
|
730
|
-
const { $consume } = this.data;
|
|
731
|
-
if ($consume)
|
|
732
|
-
return $consume.call(scope, []);
|
|
733
|
-
const result = this.eval(scope);
|
|
734
|
-
// required by typescript
|
|
735
|
-
const res = result[0];
|
|
736
|
-
res.ensureOwned();
|
|
737
|
-
return result;
|
|
791
|
+
return $eval.call(scope, [], out);
|
|
738
792
|
}
|
|
739
|
-
call(scope, args) {
|
|
793
|
+
call(scope, args, out) {
|
|
740
794
|
const { $call } = this.data;
|
|
741
795
|
if (!$call)
|
|
742
|
-
return super.call(scope, args);
|
|
743
|
-
return $call.call(scope, args);
|
|
796
|
+
return super.call(scope, args, out);
|
|
797
|
+
return $call.call(scope, args, out);
|
|
744
798
|
}
|
|
745
|
-
ensureOwned() { }
|
|
746
799
|
}
|
|
747
|
-
for (const op of
|
|
748
|
-
ObjectValue.prototype[op] = function (
|
|
800
|
+
for (const op of leftRightOperators) {
|
|
801
|
+
ObjectValue.prototype[op] = function (scope, value, out) {
|
|
802
|
+
const $ = this.data[`$${op}`];
|
|
803
|
+
if (!$)
|
|
804
|
+
return VoidValue.prototype[op].apply(this, [scope, value, out]);
|
|
805
|
+
return $.call(scope, [value], out);
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
for (const op of unaryOperators) {
|
|
809
|
+
if (op === "typeof")
|
|
810
|
+
continue;
|
|
811
|
+
ObjectValue.prototype[op] = function (scope, out) {
|
|
812
|
+
const $ = this.data[`$${op}`];
|
|
813
|
+
if (!$)
|
|
814
|
+
return VoidValue.prototype[op].apply(this, [scope, out]);
|
|
815
|
+
return $.call(scope, [], out);
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
for (const op of updateOperators) {
|
|
819
|
+
ObjectValue.prototype[op] = function (scope, prefix, out) {
|
|
749
820
|
const $ = this.data[`$${op}`];
|
|
750
|
-
// eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/no-unsafe-return
|
|
751
821
|
if (!$)
|
|
752
|
-
return VoidValue.prototype[op].apply(this,
|
|
753
|
-
|
|
754
|
-
return $.call(scope, fnArgs);
|
|
822
|
+
return VoidValue.prototype[op].apply(this, [scope, prefix, out]);
|
|
823
|
+
return $.call(scope, [new LiteralValue(+prefix)], out);
|
|
755
824
|
};
|
|
756
825
|
}
|
|
757
826
|
|
|
@@ -776,27 +845,37 @@ const senseableProps = ["controller"];
|
|
|
776
845
|
* ```
|
|
777
846
|
*/
|
|
778
847
|
class SenseableValue extends StoreValue {
|
|
779
|
-
constructor(
|
|
780
|
-
super(
|
|
781
|
-
|
|
848
|
+
constructor(name, mutability = EMutability.constant) {
|
|
849
|
+
super(name, mutability);
|
|
850
|
+
}
|
|
851
|
+
static from(scope, out, mutability = EMutability.constant) {
|
|
852
|
+
if (out instanceof SenseableValue)
|
|
853
|
+
return out;
|
|
854
|
+
const name = typeof out === "string" ? out : scope.makeTempName();
|
|
855
|
+
return new SenseableValue(name, mutability);
|
|
856
|
+
}
|
|
857
|
+
static out(scope, out, mutability = EMutability.mutable) {
|
|
858
|
+
if (!out || typeof out === "string") {
|
|
859
|
+
return new SenseableValue(out ?? scope.makeTempName(), mutability);
|
|
860
|
+
}
|
|
861
|
+
return out;
|
|
782
862
|
}
|
|
783
|
-
get(scope, prop) {
|
|
784
|
-
|
|
863
|
+
get(scope, prop, out) {
|
|
864
|
+
const mutability = EMutability.readonly;
|
|
865
|
+
if (prop instanceof LiteralValue && prop.isString()) {
|
|
785
866
|
const name = itemNames.includes(prop.data)
|
|
786
867
|
? camelToDashCase(prop.data)
|
|
787
868
|
: prop.data;
|
|
788
869
|
const result = senseableProps.includes(prop.data)
|
|
789
|
-
?
|
|
790
|
-
:
|
|
870
|
+
? SenseableValue.from(scope, out, mutability)
|
|
871
|
+
: StoreValue.from(scope, out, mutability);
|
|
791
872
|
return [
|
|
792
873
|
result,
|
|
793
874
|
[new InstructionBase("sensor", result, this, `@${name}`)],
|
|
794
875
|
];
|
|
795
876
|
}
|
|
796
877
|
if (prop instanceof StoreValue) {
|
|
797
|
-
const temp =
|
|
798
|
-
mutability: EMutability.readonly,
|
|
799
|
-
});
|
|
878
|
+
const temp = SenseableValue.from(scope, out, mutability);
|
|
800
879
|
return [temp, [new InstructionBase("sensor", temp, this, prop)]];
|
|
801
880
|
}
|
|
802
881
|
throw new CompilerError(`Cannot sense "${prop}" property`);
|
|
@@ -815,21 +894,34 @@ class DestructuringValue extends VoidValue {
|
|
|
815
894
|
super();
|
|
816
895
|
this.members = members;
|
|
817
896
|
this.macro = true;
|
|
897
|
+
this.fields = {};
|
|
898
|
+
for (const [key, { value }] of this.members) {
|
|
899
|
+
if (key instanceof LiteralValue && (key.isString() || key.isNumber())) {
|
|
900
|
+
this.fields[key.data] = value;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
818
903
|
}
|
|
819
904
|
"="(scope, right) {
|
|
820
905
|
const inst = [];
|
|
821
|
-
for (const [key, value] of this.members) {
|
|
822
|
-
const [
|
|
823
|
-
inst.push(...
|
|
824
|
-
inst.push(...value["="](scope, item)[1]);
|
|
906
|
+
for (const [key, { value, handler }] of this.members) {
|
|
907
|
+
const [, handlerInst] = handler(() => right.get(scope, key, value));
|
|
908
|
+
inst.push(...handlerInst);
|
|
825
909
|
}
|
|
826
910
|
return [right, inst];
|
|
827
911
|
}
|
|
828
912
|
eval(_scope) {
|
|
829
913
|
return [this, []];
|
|
830
914
|
}
|
|
831
|
-
|
|
832
|
-
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
/**
|
|
918
|
+
* This class is used to handle some cases where the value
|
|
919
|
+
* can't be directly evaluated by the node handler
|
|
920
|
+
*/
|
|
921
|
+
class LazyValue extends VoidValue {
|
|
922
|
+
constructor(evalutate) {
|
|
923
|
+
super();
|
|
924
|
+
this.eval = evalutate;
|
|
833
925
|
}
|
|
834
926
|
}
|
|
835
927
|
|
|
@@ -866,46 +958,29 @@ class Scope {
|
|
|
866
958
|
return false;
|
|
867
959
|
}
|
|
868
960
|
get(identifier) {
|
|
869
|
-
const
|
|
870
|
-
if (
|
|
871
|
-
return
|
|
961
|
+
const value = this.data[identifier];
|
|
962
|
+
if (value)
|
|
963
|
+
return value;
|
|
872
964
|
if (this.parent)
|
|
873
965
|
return this.parent.get(identifier);
|
|
874
966
|
throw new CompilerError(`${identifier} is not declared.`);
|
|
875
967
|
}
|
|
876
|
-
set(
|
|
877
|
-
const name = args.length === 1 ? args[0].identifier : args[0];
|
|
878
|
-
if (!name)
|
|
879
|
-
throw new CompilerError("Values in a scope must have an identifier");
|
|
968
|
+
set(name, value) {
|
|
880
969
|
if (name in this.data)
|
|
881
970
|
throw new CompilerError(`${name} is already declared.`);
|
|
882
|
-
return this.hardSet(
|
|
883
|
-
}
|
|
884
|
-
hardSet(
|
|
885
|
-
|
|
886
|
-
? args[0]
|
|
887
|
-
: new ValueOwner({
|
|
888
|
-
scope: this,
|
|
889
|
-
identifier: args[0],
|
|
890
|
-
name: args[0],
|
|
891
|
-
value: args[1],
|
|
892
|
-
});
|
|
893
|
-
const { identifier } = owner;
|
|
894
|
-
if (!identifier)
|
|
971
|
+
return this.hardSet(name, value);
|
|
972
|
+
}
|
|
973
|
+
hardSet(name, value) {
|
|
974
|
+
if (!name)
|
|
895
975
|
throw new CompilerError("Values in a scope must have an identifier");
|
|
896
|
-
this.data[
|
|
897
|
-
return
|
|
976
|
+
this.data[name] = value;
|
|
977
|
+
return value;
|
|
898
978
|
}
|
|
899
979
|
make(identifier, name) {
|
|
900
|
-
const
|
|
901
|
-
|
|
902
|
-
value: assign(new SenseableValue(this), {
|
|
903
|
-
mutability: EMutability.mutable,
|
|
904
|
-
}),
|
|
905
|
-
identifier,
|
|
906
|
-
name,
|
|
980
|
+
const value = assign(new SenseableValue(name), {
|
|
981
|
+
mutability: EMutability.mutable,
|
|
907
982
|
});
|
|
908
|
-
return this.set(
|
|
983
|
+
return this.set(identifier, value);
|
|
909
984
|
}
|
|
910
985
|
makeTempName() {
|
|
911
986
|
let result = `${internalPrefix}t${this.ntemp}`;
|
|
@@ -921,163 +996,222 @@ const Program = (c, scope, node) => {
|
|
|
921
996
|
};
|
|
922
997
|
|
|
923
998
|
const VariableDeclaration = (c, scope, node) => {
|
|
924
|
-
return c.handleMany(scope, node.declarations, child => VariableDeclarator(c, scope, child, node.kind));
|
|
999
|
+
return c.handleMany(scope, node.declarations, child => VariableDeclarator(c, scope, child, undefined, node.kind));
|
|
925
1000
|
};
|
|
926
|
-
const VariableDeclarator = (c, scope, node, kind = "let") => {
|
|
927
|
-
const
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
1001
|
+
const VariableDeclarator = (c, scope, node, out, kind = "let") => {
|
|
1002
|
+
const { init } = node;
|
|
1003
|
+
const [value, inst] = Declare(c, scope, node.id, kind);
|
|
1004
|
+
if (init) {
|
|
1005
|
+
const [result, resultInst] = c.handleEval(scope, init, value.out);
|
|
1006
|
+
inst.push(...value.handler(result, resultInst)[1]);
|
|
1007
|
+
}
|
|
1008
|
+
else {
|
|
1009
|
+
inst.push(...value.handler(null, [])[1]);
|
|
1010
|
+
}
|
|
931
1011
|
return [null, inst];
|
|
932
1012
|
};
|
|
933
|
-
const Declare = (c, scope, node, kind
|
|
1013
|
+
const Declare = (c, scope, node, kind) => {
|
|
934
1014
|
return c.handle(scope, node, () => {
|
|
935
1015
|
switch (node.type) {
|
|
936
1016
|
case "Identifier":
|
|
937
|
-
return DeclareIdentifier(c, scope, node, kind
|
|
1017
|
+
return DeclareIdentifier(c, scope, node, kind);
|
|
938
1018
|
case "ArrayPattern":
|
|
939
|
-
return DeclareArrayPattern(c, scope, node, kind
|
|
1019
|
+
return DeclareArrayPattern(c, scope, node, kind);
|
|
940
1020
|
case "ObjectPattern":
|
|
941
|
-
return DeclareObjectPattern(c, scope, node, kind
|
|
1021
|
+
return DeclareObjectPattern(c, scope, node, kind);
|
|
942
1022
|
default:
|
|
943
1023
|
throw new CompilerError(`Unsupported declaration type: ${node.type}`, node);
|
|
944
1024
|
}
|
|
945
1025
|
});
|
|
946
1026
|
};
|
|
947
|
-
const DeclareIdentifier = (c, scope, node, kind
|
|
1027
|
+
const DeclareIdentifier = (c, scope, node, kind) => {
|
|
948
1028
|
const { name: identifier } = node;
|
|
949
1029
|
const name = nodeName(node, !c.compactNames && identifier);
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
1030
|
+
const out = SenseableValue.from(scope, name, kind === "const" ? EMutability.init : EMutability.mutable);
|
|
1031
|
+
const declarationValue = new DeclarationValue({
|
|
1032
|
+
c,
|
|
1033
|
+
scope,
|
|
1034
|
+
node,
|
|
1035
|
+
out,
|
|
1036
|
+
handler(init, inst) {
|
|
1037
|
+
if (kind === "const" && !init)
|
|
1038
|
+
throw new CompilerError("Constants must be initialized.", node);
|
|
1039
|
+
if (kind === "const" && init?.mutability === EMutability.constant) {
|
|
1040
|
+
scope.set(identifier, init);
|
|
1041
|
+
return [null, inst];
|
|
1042
|
+
}
|
|
1043
|
+
else {
|
|
1044
|
+
const value = scope.make(identifier, name);
|
|
1045
|
+
if (init) {
|
|
1046
|
+
if (init.macro)
|
|
1047
|
+
throw new CompilerError("Macro values must be held by constants", node);
|
|
1048
|
+
inst.push(...value["="](scope, init)[1]);
|
|
1049
|
+
}
|
|
1050
|
+
if (kind === "const")
|
|
1051
|
+
value.mutability = EMutability.constant;
|
|
1052
|
+
return [null, inst];
|
|
1053
|
+
}
|
|
1054
|
+
},
|
|
1055
|
+
});
|
|
1056
|
+
return [declarationValue, []];
|
|
975
1057
|
};
|
|
976
|
-
const DeclareArrayPattern = (c, scope, node, kind
|
|
1058
|
+
const DeclareArrayPattern = (c, scope, node, kind) => {
|
|
1059
|
+
const members = new Map();
|
|
977
1060
|
const { elements } = node;
|
|
978
|
-
if (!(init instanceof ObjectValue))
|
|
979
|
-
throw new CompilerError("The value being destructured must be an object value");
|
|
980
1061
|
const inst = [];
|
|
981
1062
|
for (let i = 0; i < elements.length; i++) {
|
|
982
1063
|
const element = elements[i];
|
|
983
1064
|
if (!element)
|
|
984
1065
|
continue;
|
|
985
|
-
const
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
1066
|
+
const [value, valueInst] = Declare(c, scope, element, kind);
|
|
1067
|
+
inst.push(...valueInst);
|
|
1068
|
+
members.set(new LiteralValue(i), {
|
|
1069
|
+
value: value.out,
|
|
1070
|
+
handler(get) {
|
|
1071
|
+
return c.handle(scope, element, () => {
|
|
1072
|
+
let [init, initInst] = [null, []];
|
|
1073
|
+
try {
|
|
1074
|
+
[init, initInst] = get();
|
|
1075
|
+
}
|
|
1076
|
+
catch (e) {
|
|
1077
|
+
// catches the "cannot get undefined member" error
|
|
1078
|
+
// TODO: refactor and add error codes
|
|
1079
|
+
// so we don't have to do string checks
|
|
1080
|
+
if (!(e instanceof CompilerError) ||
|
|
1081
|
+
!e.message.includes("undefined member"))
|
|
1082
|
+
throw e;
|
|
1083
|
+
}
|
|
1084
|
+
if (!init)
|
|
1085
|
+
throw new CompilerError(`The target object does not have a value at index ${i}`, element);
|
|
1086
|
+
return value.handler(init, initInst);
|
|
1087
|
+
});
|
|
1088
|
+
},
|
|
1089
|
+
});
|
|
989
1090
|
}
|
|
990
|
-
|
|
1091
|
+
const out = new DestructuringValue(members);
|
|
1092
|
+
const value = new DeclarationValue({
|
|
1093
|
+
c,
|
|
1094
|
+
scope,
|
|
1095
|
+
node,
|
|
1096
|
+
out,
|
|
1097
|
+
handler(init, initInst) {
|
|
1098
|
+
if (!(init instanceof ObjectValue))
|
|
1099
|
+
throw new CompilerError("The value being destructured must be an object value");
|
|
1100
|
+
initInst.push(...out["="](scope, init)[1]);
|
|
1101
|
+
return [null, initInst];
|
|
1102
|
+
},
|
|
1103
|
+
});
|
|
1104
|
+
return [value, inst];
|
|
991
1105
|
};
|
|
992
|
-
const DeclareObjectPattern = (c, scope, node, kind
|
|
993
|
-
|
|
994
|
-
|
|
1106
|
+
const DeclareObjectPattern = (c, scope, node, kind) => {
|
|
1107
|
+
const inst = [];
|
|
1108
|
+
const members = new Map();
|
|
995
1109
|
const { properties } = node;
|
|
996
|
-
const [init, inst] = base.consume(scope);
|
|
997
1110
|
const propertiesInst = c.handleMany(scope, properties, prop => {
|
|
998
1111
|
if (prop.type === "RestElement")
|
|
999
1112
|
throw new CompilerError("The rest operator is not supported", prop);
|
|
1000
|
-
const { key, value } = prop;
|
|
1113
|
+
const { key, value: propValue } = prop;
|
|
1001
1114
|
const keyInst = key.type === "Identifier" && !prop.computed
|
|
1002
1115
|
? [new LiteralValue(key.name), []]
|
|
1003
|
-
: c.
|
|
1004
|
-
const
|
|
1005
|
-
|
|
1006
|
-
|
|
1116
|
+
: c.handleEval(scope, prop.key);
|
|
1117
|
+
const [value, valueInst] = Declare(c, scope, propValue, kind);
|
|
1118
|
+
members.set(keyInst[0], {
|
|
1119
|
+
value: value.out,
|
|
1120
|
+
handler(get) {
|
|
1121
|
+
return c.handle(scope, propValue, () => {
|
|
1122
|
+
const [init, initInst] = get();
|
|
1123
|
+
return value.handler(init, initInst);
|
|
1124
|
+
});
|
|
1125
|
+
},
|
|
1126
|
+
});
|
|
1127
|
+
return [null, [...keyInst[1], ...valueInst]];
|
|
1007
1128
|
});
|
|
1008
|
-
|
|
1009
|
-
|
|
1129
|
+
const out = new DestructuringValue(members);
|
|
1130
|
+
const declarationValue = new DeclarationValue({
|
|
1131
|
+
c,
|
|
1132
|
+
scope,
|
|
1133
|
+
node,
|
|
1134
|
+
out,
|
|
1135
|
+
handler(init, initInst) {
|
|
1136
|
+
if (!init)
|
|
1137
|
+
throw new CompilerError("Cannot use object destructuring without an initializer", node);
|
|
1138
|
+
initInst.push(...out["="](scope, init)[1]);
|
|
1139
|
+
return [null, initInst];
|
|
1140
|
+
},
|
|
1141
|
+
});
|
|
1142
|
+
return [declarationValue, [...inst, ...propertiesInst[1]]];
|
|
1143
|
+
};
|
|
1144
|
+
class DeclarationValue extends VoidValue {
|
|
1145
|
+
constructor({ c, scope, node, out, handler, }) {
|
|
1146
|
+
super();
|
|
1147
|
+
this.out = out;
|
|
1148
|
+
this.c = c;
|
|
1149
|
+
this.scope = scope;
|
|
1150
|
+
this.node = node;
|
|
1151
|
+
this.handle = handler;
|
|
1152
|
+
}
|
|
1153
|
+
handler(init, inst) {
|
|
1154
|
+
return this.c.handle(this.scope, this.node, () => this.handle(init, inst));
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1010
1157
|
|
|
1011
1158
|
const Literal = (_c, scope, node) => [new LiteralValue(node.value), []];
|
|
1012
1159
|
const NumericLiteral = Literal;
|
|
1013
1160
|
const StringLiteral = Literal;
|
|
1014
|
-
const NullLiteral = () => [
|
|
1015
|
-
new LiteralValue(null),
|
|
1016
|
-
[],
|
|
1017
|
-
];
|
|
1161
|
+
const NullLiteral = () => [new LiteralValue(null), []];
|
|
1018
1162
|
const BooleanLiteral = (_c, scope, node) => [new LiteralValue(+node.value), []];
|
|
1019
1163
|
|
|
1020
|
-
const LRExpression = (c, scope, node) => {
|
|
1021
|
-
const [left, leftInst] = c.
|
|
1022
|
-
const [right, rightInst] = c.
|
|
1023
|
-
const [op, opInst] = left[node.operator](scope, right);
|
|
1164
|
+
const LRExpression = (c, scope, node, out) => {
|
|
1165
|
+
const [left, leftInst] = c.handleEval(scope, node.left);
|
|
1166
|
+
const [right, rightInst] = c.handleEval(scope, node.right);
|
|
1167
|
+
const [op, opInst] = left[node.operator](scope, right, out);
|
|
1024
1168
|
return [op, [...leftInst, ...rightInst, ...opInst]];
|
|
1025
1169
|
};
|
|
1026
1170
|
const BinaryExpression = LRExpression;
|
|
1027
|
-
const LogicalExpression = (c, scope, node) => {
|
|
1171
|
+
const LogicalExpression = (c, scope, node, out) => {
|
|
1028
1172
|
if (node.operator !== "??")
|
|
1029
|
-
return LRExpression(c, scope, node);
|
|
1030
|
-
const other =
|
|
1031
|
-
eval: scope => c.handleEval(scope, node.right),
|
|
1032
|
-
consume: scope => c.handleConsume(scope, node.right),
|
|
1033
|
-
});
|
|
1173
|
+
return LRExpression(c, scope, node, undefined);
|
|
1174
|
+
const other = new LazyValue((scope, out) => c.handleEval(scope, node.right, out));
|
|
1034
1175
|
const [left, leftInst] = c.handleEval(scope, node.left);
|
|
1035
|
-
const [result, resultInst] = left["??"](scope, other);
|
|
1176
|
+
const [result, resultInst] = left["??"](scope, other, out);
|
|
1036
1177
|
return [result, [...leftInst, ...resultInst]];
|
|
1037
1178
|
};
|
|
1038
1179
|
const AssignmentExpression = (c, scope, node) => {
|
|
1039
1180
|
const [left, leftInst] = c.handle(scope, node.left);
|
|
1040
|
-
// doesn't need to consume because the operators already do that
|
|
1041
1181
|
const [right, rightInst] = node.operator !== "??="
|
|
1042
|
-
? c.handleEval(scope, node.right)
|
|
1182
|
+
? c.handleEval(scope, node.right, node.operator === "=" && left ? left : undefined)
|
|
1043
1183
|
: [
|
|
1044
|
-
|
|
1045
|
-
eval: scope => c.handleEval(scope, node.right),
|
|
1046
|
-
consume: scope => c.handleConsume(scope, node.right),
|
|
1047
|
-
}),
|
|
1184
|
+
new LazyValue((scope, out) => c.handleEval(scope, node.right, out)),
|
|
1048
1185
|
[],
|
|
1049
1186
|
];
|
|
1050
1187
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1051
1188
|
const [op, opInst] = left[node.operator](scope, right);
|
|
1052
1189
|
return [op, [...leftInst, ...rightInst, ...opInst]];
|
|
1053
1190
|
};
|
|
1054
|
-
const UnaryExpression = (c, scope, { argument, operator }) => {
|
|
1055
|
-
const [arg, argInst] = c.
|
|
1191
|
+
const UnaryExpression = (c, scope, { argument, operator }, out) => {
|
|
1192
|
+
const [arg, argInst] = c.handleEval(scope, argument);
|
|
1056
1193
|
const operatorId = operator == "+" || operator == "-" ? `u${operator}` : operator;
|
|
1057
1194
|
if (operatorId === "throw")
|
|
1058
1195
|
throw new CompilerError("throw operator is not supported");
|
|
1059
|
-
const [op, opInst] = arg[operatorId](scope);
|
|
1196
|
+
const [op, opInst] = arg[operatorId](scope, out);
|
|
1060
1197
|
return [op, [...argInst, ...opInst]];
|
|
1061
1198
|
};
|
|
1062
|
-
const UpdateExpression = (c, scope, { argument, operator, prefix }) => {
|
|
1199
|
+
const UpdateExpression = (c, scope, { argument, operator, prefix }, out) => {
|
|
1063
1200
|
const [arg, argInst] = c.handle(scope, argument);
|
|
1064
1201
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1065
|
-
const [op, opInst] = arg[operator](scope, prefix);
|
|
1202
|
+
const [op, opInst] = arg[operator](scope, prefix, out);
|
|
1066
1203
|
return [op, [...argInst, ...opInst]];
|
|
1067
1204
|
};
|
|
1068
|
-
const ConditionalExpression = (c, scope, node) => {
|
|
1069
|
-
const [test, testInst] = c.
|
|
1205
|
+
const ConditionalExpression = (c, scope, node, out) => {
|
|
1206
|
+
const [test, testInst] = c.handleEval(scope, node.test);
|
|
1070
1207
|
if (test instanceof LiteralValue) {
|
|
1071
1208
|
if (test.data)
|
|
1072
|
-
return c.handleEval(scope, node.consequent);
|
|
1073
|
-
return c.handleEval(scope, node.alternate);
|
|
1074
|
-
}
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
const
|
|
1078
|
-
result.ensureOwned();
|
|
1079
|
-
const consequent = c.handleEval(scope, node.consequent);
|
|
1080
|
-
const alternate = c.handleEval(scope, node.alternate);
|
|
1209
|
+
return c.handleEval(scope, node.consequent, out);
|
|
1210
|
+
return c.handleEval(scope, node.alternate, out);
|
|
1211
|
+
}
|
|
1212
|
+
const result = SenseableValue.out(scope, out);
|
|
1213
|
+
const consequent = c.handleEval(scope, node.consequent, result);
|
|
1214
|
+
const alternate = c.handleEval(scope, node.alternate, result);
|
|
1081
1215
|
const alternateStartAdress = new LiteralValue(null);
|
|
1082
1216
|
const endExpressionAdress = new LiteralValue(null);
|
|
1083
1217
|
return [
|
|
@@ -1094,20 +1228,14 @@ const ConditionalExpression = (c, scope, node) => {
|
|
|
1094
1228
|
new AddressResolver(endExpressionAdress),
|
|
1095
1229
|
],
|
|
1096
1230
|
];
|
|
1097
|
-
};
|
|
1098
|
-
function lazyValue(options) {
|
|
1099
|
-
const value = new VoidValue();
|
|
1100
|
-
value.eval = options.eval;
|
|
1101
|
-
value.consume = options.consume;
|
|
1102
|
-
return value;
|
|
1103
|
-
}
|
|
1231
|
+
};
|
|
1104
1232
|
|
|
1105
1233
|
const Identifier = (c, scope, node) => {
|
|
1106
1234
|
return [scope.get(node.name), []];
|
|
1107
1235
|
};
|
|
1108
1236
|
|
|
1109
1237
|
const ExpressionStatement = (c, scope, node) => {
|
|
1110
|
-
return c.handle(scope, node.expression);
|
|
1238
|
+
return c.handle(scope, node.expression, undefined, discardedName);
|
|
1111
1239
|
};
|
|
1112
1240
|
const BreakStatement = (_, scope, node) => {
|
|
1113
1241
|
const label = node.label?.name;
|
|
@@ -1123,11 +1251,12 @@ const ContinueStatement = (_, scope, node) => {
|
|
|
1123
1251
|
target.continue.bind(addr);
|
|
1124
1252
|
return [null, [new ContinueInstruction(addr)]];
|
|
1125
1253
|
};
|
|
1126
|
-
const ReturnStatement = (c, scope, node) => {
|
|
1127
|
-
const
|
|
1128
|
-
|
|
1254
|
+
const ReturnStatement = (c, scope, node, out) => {
|
|
1255
|
+
const { argument } = node;
|
|
1256
|
+
const [arg, argInst] = argument
|
|
1257
|
+
? [new LazyValue((scope, out) => c.handleEval(scope, argument, out)), []]
|
|
1129
1258
|
: [null, []];
|
|
1130
|
-
const [ret, retInst] = scope.function.return(scope, arg);
|
|
1259
|
+
const [ret, retInst] = scope.function.return(scope, arg, out);
|
|
1131
1260
|
return [ret, [...argInst, ...retInst]];
|
|
1132
1261
|
};
|
|
1133
1262
|
const LabeledStatement = (c, scope, node) => {
|
|
@@ -1150,7 +1279,7 @@ function findScopeLabel(scope, label) {
|
|
|
1150
1279
|
|
|
1151
1280
|
const IfStatement = (c, scope, node) => {
|
|
1152
1281
|
const inst = [];
|
|
1153
|
-
const [test, testInst] = c.
|
|
1282
|
+
const [test, testInst] = c.handleEval(scope, node.test);
|
|
1154
1283
|
if (test instanceof LiteralValue) {
|
|
1155
1284
|
if (test.data)
|
|
1156
1285
|
inst.push(...c.handle(scope, node.consequent)[1]);
|
|
@@ -1169,44 +1298,73 @@ const IfStatement = (c, scope, node) => {
|
|
|
1169
1298
|
|
|
1170
1299
|
const SwitchStatement = (c, scope, node) => {
|
|
1171
1300
|
const innerScope = scope.createScope();
|
|
1172
|
-
const [ref, refInst] = c.
|
|
1301
|
+
const [ref, refInst] = c.handleEval(scope, node.discriminant);
|
|
1173
1302
|
const inst = [];
|
|
1174
1303
|
const endAdress = new LiteralValue(null);
|
|
1175
1304
|
const endLine = new AddressResolver(endAdress).bindBreak(innerScope);
|
|
1176
1305
|
const caseJumps = [];
|
|
1177
1306
|
let defaultJump;
|
|
1307
|
+
// if there is a case that is guaranteed to run
|
|
1308
|
+
let constantCase = false;
|
|
1309
|
+
let onlyConstantTests = true;
|
|
1178
1310
|
for (const scase of node.cases) {
|
|
1179
1311
|
const [, bodyInst] = c.handleMany(innerScope, scase.consequent);
|
|
1180
1312
|
const bodyAdress = new LiteralValue(null);
|
|
1181
1313
|
const bodyLine = new AddressResolver(bodyAdress);
|
|
1314
|
+
const canFallInto = !endsWithoutFalltrough(inst, endLine);
|
|
1315
|
+
let includeJump = !constantCase;
|
|
1316
|
+
const includeBody = !constantCase || canFallInto;
|
|
1182
1317
|
if (!scase.test) {
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1318
|
+
if (includeJump) {
|
|
1319
|
+
[defaultJump] = c.handle(innerScope, scase, () => [
|
|
1320
|
+
null,
|
|
1321
|
+
[new JumpInstruction(bodyAdress, EJumpKind.Always)],
|
|
1322
|
+
])[1];
|
|
1323
|
+
}
|
|
1324
|
+
if (includeBody)
|
|
1325
|
+
inst.push(bodyLine, ...bodyInst);
|
|
1188
1326
|
continue;
|
|
1189
1327
|
}
|
|
1190
|
-
const [test, testInst] = c.
|
|
1328
|
+
const [test, testInst] = c.handleEval(innerScope, scase.test);
|
|
1329
|
+
if (onlyConstantTests)
|
|
1330
|
+
onlyConstantTests = test.mutability === EMutability.constant;
|
|
1331
|
+
let jump;
|
|
1191
1332
|
// check if it can be evaluated at compile time
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
// the whole case gets omitted
|
|
1195
|
-
// otherwise it returns the instructions for
|
|
1196
|
-
// the body of this case
|
|
1333
|
+
// the scope copy prevents unecessary increases in the temp counter
|
|
1334
|
+
const [comp] = ref["=="](innerScope.copy(), test);
|
|
1197
1335
|
if (comp instanceof LiteralValue) {
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1336
|
+
// whether other cases can falltrough and reach this one
|
|
1337
|
+
const noFallInto = !canFallInto;
|
|
1338
|
+
if (!comp.data) {
|
|
1339
|
+
// this case is not accessible in any way
|
|
1340
|
+
if (noFallInto)
|
|
1341
|
+
continue;
|
|
1342
|
+
includeJump = false;
|
|
1343
|
+
}
|
|
1344
|
+
else {
|
|
1345
|
+
// this case is only accessible via the comparison jump
|
|
1346
|
+
// and all the other cases will never run
|
|
1347
|
+
if (noFallInto &&
|
|
1348
|
+
onlyConstantTests &&
|
|
1349
|
+
endsWithoutFalltrough(bodyInst, endLine)) {
|
|
1350
|
+
return [null, [...bodyInst, endLine]];
|
|
1351
|
+
}
|
|
1352
|
+
jump = c.handle(innerScope, scase, () => [
|
|
1353
|
+
null,
|
|
1354
|
+
[new JumpInstruction(bodyAdress, EJumpKind.Always)],
|
|
1355
|
+
])[1];
|
|
1356
|
+
constantCase = true;
|
|
1357
|
+
}
|
|
1202
1358
|
}
|
|
1203
1359
|
// makes sourcemapping for the jump more specific
|
|
1204
|
-
|
|
1360
|
+
jump ??= c.handle(innerScope, scase, () => [
|
|
1205
1361
|
null,
|
|
1206
1362
|
[new JumpInstruction(bodyAdress, EJumpKind.StrictEqual, ref, test)],
|
|
1207
|
-
]);
|
|
1208
|
-
|
|
1209
|
-
|
|
1363
|
+
])[1];
|
|
1364
|
+
if (includeJump)
|
|
1365
|
+
caseJumps.push(...testInst, ...jump);
|
|
1366
|
+
if (includeBody)
|
|
1367
|
+
inst.push(bodyLine, ...bodyInst);
|
|
1210
1368
|
}
|
|
1211
1369
|
// ensures that the processor exits
|
|
1212
1370
|
// the switch if no cases match
|
|
@@ -1221,16 +1379,38 @@ const SwitchStatement = (c, scope, node) => {
|
|
|
1221
1379
|
[
|
|
1222
1380
|
...refInst,
|
|
1223
1381
|
...caseJumps,
|
|
1224
|
-
...(caseJumps.length > 0 ? [defaultJump] : []),
|
|
1382
|
+
...(caseJumps.length > 0 && !constantCase ? [defaultJump] : []),
|
|
1225
1383
|
...inst,
|
|
1226
1384
|
endLine,
|
|
1227
1385
|
],
|
|
1228
1386
|
];
|
|
1229
|
-
};
|
|
1387
|
+
};
|
|
1388
|
+
function endsWithoutFalltrough(inst, endLine) {
|
|
1389
|
+
if (inst.length === 0)
|
|
1390
|
+
return true;
|
|
1391
|
+
for (let i = inst.length - 1; i >= 0; i--) {
|
|
1392
|
+
const instruction = inst[i];
|
|
1393
|
+
// this means that there is an instruction trying to reference
|
|
1394
|
+
// the final function `set @counter` instruction
|
|
1395
|
+
// of course this generates an unecessary
|
|
1396
|
+
// `set @counter` in cases where a control flow
|
|
1397
|
+
// structure contains a fallback return statement
|
|
1398
|
+
// TODO: fix this with static analysis
|
|
1399
|
+
if (instruction instanceof AddressResolver)
|
|
1400
|
+
return false;
|
|
1401
|
+
if (instruction.hidden)
|
|
1402
|
+
continue;
|
|
1403
|
+
return (instruction instanceof SetCounterInstruction ||
|
|
1404
|
+
instruction.intent === EInstIntent.return ||
|
|
1405
|
+
(instruction instanceof BreakInstruction &&
|
|
1406
|
+
endLine.bonds.includes(instruction.address)));
|
|
1407
|
+
}
|
|
1408
|
+
return false;
|
|
1409
|
+
}
|
|
1230
1410
|
|
|
1231
1411
|
const WhileStatement = (c, scope, node) => {
|
|
1232
1412
|
const lines = [];
|
|
1233
|
-
const [test, testLines] = c.
|
|
1413
|
+
const [test, testLines] = c.handleEval(scope, node.test);
|
|
1234
1414
|
const childScope = scope.createScope();
|
|
1235
1415
|
const startLoopAddr = new LiteralValue(null);
|
|
1236
1416
|
const endLoopAddr = new LiteralValue(null);
|
|
@@ -1250,7 +1430,7 @@ const WhileStatement = (c, scope, node) => {
|
|
|
1250
1430
|
};
|
|
1251
1431
|
const DoWhileStatement = (c, scope, node) => {
|
|
1252
1432
|
const lines = [];
|
|
1253
|
-
const [test, testLines] = c.
|
|
1433
|
+
const [test, testLines] = c.handleEval(scope, node.test);
|
|
1254
1434
|
const childScope = scope.createScope();
|
|
1255
1435
|
const startLoopAddr = new LiteralValue(null);
|
|
1256
1436
|
const startLoopLine = new AddressResolver(startLoopAddr).bindContinue(childScope);
|
|
@@ -1279,9 +1459,11 @@ const ForStatement = (c, parentScope, node) => {
|
|
|
1279
1459
|
const scope = parentScope.createScope();
|
|
1280
1460
|
const initInst = node.init ? c.handle(scope, node.init)[1] : [];
|
|
1281
1461
|
const [test, testLines] = node.test
|
|
1282
|
-
? c.
|
|
1462
|
+
? c.handleEval(scope, node.test)
|
|
1283
1463
|
: [new LiteralValue(1), []];
|
|
1284
|
-
const updateLines = node.update
|
|
1464
|
+
const updateLines = node.update
|
|
1465
|
+
? c.handle(scope, node.update, undefined, discardedName)[1]
|
|
1466
|
+
: [];
|
|
1285
1467
|
const startLoopAddr = new LiteralValue(null);
|
|
1286
1468
|
// continue statements jump here to run the loop update lines
|
|
1287
1469
|
const beforeEndAddr = new LiteralValue(null);
|
|
@@ -1309,146 +1491,131 @@ const ForStatement = (c, parentScope, node) => {
|
|
|
1309
1491
|
};
|
|
1310
1492
|
|
|
1311
1493
|
class FunctionValue extends VoidValue {
|
|
1312
|
-
constructor({ scope, params, body, c, }) {
|
|
1494
|
+
constructor({ scope, params, body, c, out, }) {
|
|
1313
1495
|
super();
|
|
1314
1496
|
this.mutability = EMutability.constant;
|
|
1315
1497
|
this.macro = true;
|
|
1316
|
-
this.
|
|
1498
|
+
this.paramValues = [];
|
|
1499
|
+
this.paramNames = [];
|
|
1317
1500
|
this.bundled = false;
|
|
1318
1501
|
this.initialized = false;
|
|
1319
1502
|
this.scope = scope;
|
|
1320
1503
|
this.body = body;
|
|
1321
1504
|
this.c = c;
|
|
1322
1505
|
this.params = params;
|
|
1506
|
+
this.out = out;
|
|
1507
|
+
this.name = extractOutName(out) ?? scope.makeTempName();
|
|
1323
1508
|
}
|
|
1324
1509
|
typeof() {
|
|
1325
1510
|
return [new LiteralValue("function"), []];
|
|
1326
1511
|
}
|
|
1327
|
-
initScope(
|
|
1328
|
-
this.childScope = this.scope.createFunction(name);
|
|
1512
|
+
initScope() {
|
|
1513
|
+
this.childScope = this.scope.createFunction(this.name);
|
|
1329
1514
|
this.childScope.function = this;
|
|
1330
1515
|
for (const id of this.params) {
|
|
1331
1516
|
const name = nodeName(id, !this.c.compactNames && id.name);
|
|
1332
|
-
const
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
}),
|
|
1337
|
-
identifier: id.name,
|
|
1338
|
-
name,
|
|
1339
|
-
});
|
|
1340
|
-
this.childScope.set(owner);
|
|
1341
|
-
this.paramOwners.push(owner);
|
|
1517
|
+
const value = new SenseableValue(name, EMutability.mutable);
|
|
1518
|
+
this.childScope.set(id.name, value);
|
|
1519
|
+
this.paramValues.push(value);
|
|
1520
|
+
this.paramNames.push(id.name);
|
|
1342
1521
|
}
|
|
1343
|
-
this.callSize = this.
|
|
1522
|
+
this.callSize = this.paramValues.length + 2;
|
|
1344
1523
|
}
|
|
1345
1524
|
ensureInit() {
|
|
1346
|
-
if (!this.owner)
|
|
1347
|
-
throw new CompilerError(`Functions must be owned by a variable`);
|
|
1348
1525
|
if (this.initialized)
|
|
1349
1526
|
return;
|
|
1350
1527
|
this.initialized = true;
|
|
1351
|
-
|
|
1352
|
-
this.initScope(name);
|
|
1528
|
+
this.initScope();
|
|
1353
1529
|
this.addr = new LiteralValue(null);
|
|
1354
|
-
this.temp = new
|
|
1355
|
-
|
|
1356
|
-
name: `${internalPrefix}f${name}`,
|
|
1357
|
-
value: assign(new SenseableValue(this.childScope), {
|
|
1358
|
-
mutability: EMutability.mutable,
|
|
1359
|
-
}),
|
|
1360
|
-
});
|
|
1361
|
-
this.ret = new ValueOwner({
|
|
1362
|
-
scope: this.childScope,
|
|
1363
|
-
name: `${internalPrefix}r${name}`,
|
|
1364
|
-
value: new StoreValue(this.childScope),
|
|
1365
|
-
});
|
|
1530
|
+
this.temp = new SenseableValue(`${internalPrefix}f${this.name}`, EMutability.mutable);
|
|
1531
|
+
this.ret = new StoreValue(`${internalPrefix}r${this.name}`);
|
|
1366
1532
|
this.inst = [
|
|
1367
1533
|
new AddressResolver(this.addr),
|
|
1368
1534
|
...this.c.handle(this.childScope, this.body)[1],
|
|
1369
1535
|
];
|
|
1370
1536
|
if (!endsWithReturn(this.inst)) {
|
|
1371
|
-
this.inst.push(assign(new SetCounterInstruction(this.ret
|
|
1537
|
+
this.inst.push(assign(new SetCounterInstruction(this.ret), {
|
|
1372
1538
|
intent: EInstIntent.return,
|
|
1373
1539
|
}));
|
|
1374
1540
|
}
|
|
1375
1541
|
}
|
|
1376
1542
|
normalReturn(scope, arg) {
|
|
1377
|
-
const
|
|
1378
|
-
|
|
1543
|
+
const inst = [];
|
|
1544
|
+
if (arg) {
|
|
1545
|
+
const [value, valueInst] = arg.eval(scope, this.temp);
|
|
1546
|
+
const argInst = this.temp["="](scope, value)[1];
|
|
1547
|
+
inst.push(...valueInst);
|
|
1548
|
+
inst.push(...argInst);
|
|
1549
|
+
}
|
|
1550
|
+
const returnInst = assign(new SetCounterInstruction(this.ret), {
|
|
1379
1551
|
intent: EInstIntent.return,
|
|
1380
1552
|
});
|
|
1381
|
-
return [null, [...
|
|
1553
|
+
return [null, [...inst, returnInst]];
|
|
1382
1554
|
}
|
|
1383
1555
|
// TODO: use static analysis to determine if
|
|
1384
1556
|
// we can safely skip the temp value assignment
|
|
1385
|
-
normalCall(scope, args) {
|
|
1557
|
+
normalCall(scope, args, out) {
|
|
1386
1558
|
if (!this.bundled)
|
|
1387
1559
|
this.childScope.inst.push(...this.inst);
|
|
1388
1560
|
this.bundled = true;
|
|
1389
1561
|
const callAddressLiteral = new LiteralValue(null);
|
|
1390
|
-
const temp =
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
const inst = this.paramOwners
|
|
1394
|
-
.map(({ value: param }, i) => param["="](scope, args[i])[1])
|
|
1562
|
+
const temp = SenseableValue.from(scope, out, EMutability.mutable);
|
|
1563
|
+
const inst = this.paramValues
|
|
1564
|
+
.map((param, i) => param["="](scope, args[i])[1])
|
|
1395
1565
|
.reduce((s, c) => s.concat(c), [])
|
|
1396
|
-
.concat(...this.ret
|
|
1566
|
+
.concat(...this.ret["="](scope, callAddressLiteral)[1], new JumpInstruction(this.addr, EJumpKind.Always), new AddressResolver(callAddressLiteral),
|
|
1397
1567
|
// ensures that functions can be called multiple times inside expressions
|
|
1398
|
-
...temp["="](scope, this.temp
|
|
1568
|
+
...temp["="](scope, this.temp)[1]);
|
|
1399
1569
|
return [temp, inst];
|
|
1400
1570
|
}
|
|
1401
1571
|
inlineReturn(scope, arg) {
|
|
1402
|
-
const argInst = arg ? this.inlineTemp.value["="](scope, arg)[1] : [];
|
|
1403
1572
|
if (!this.inlineEnd)
|
|
1404
1573
|
throw new CompilerError("Error during inline attempt: missing inline end adress");
|
|
1574
|
+
const inst = [];
|
|
1575
|
+
if (arg) {
|
|
1576
|
+
const [value, valueInst] = arg.eval(scope, this.inlineTemp);
|
|
1577
|
+
const argInst = this.inlineTemp["="](scope, value)[1];
|
|
1578
|
+
inst.push(...valueInst);
|
|
1579
|
+
inst.push(...argInst);
|
|
1580
|
+
}
|
|
1405
1581
|
const jump = assign(new JumpInstruction(this.inlineEnd, EJumpKind.Always), {
|
|
1406
1582
|
intent: EInstIntent.return,
|
|
1583
|
+
intentSource: this,
|
|
1407
1584
|
});
|
|
1408
|
-
return [null, [...
|
|
1585
|
+
return [null, [...inst, jump]];
|
|
1409
1586
|
}
|
|
1410
|
-
inlineCall(scope, args) {
|
|
1587
|
+
inlineCall(scope, args, out) {
|
|
1411
1588
|
// create return value
|
|
1412
|
-
this.inlineTemp =
|
|
1413
|
-
scope: this.childScope,
|
|
1414
|
-
value: assign(new SenseableValue(scope), {
|
|
1415
|
-
mutability: EMutability.mutable,
|
|
1416
|
-
}),
|
|
1417
|
-
});
|
|
1589
|
+
this.inlineTemp = SenseableValue.from(this.childScope, out, EMutability.mutable);
|
|
1418
1590
|
// make a copy of the function scope
|
|
1419
1591
|
const fnScope = this.childScope.copy();
|
|
1420
1592
|
// hard set variables within the function scope
|
|
1421
|
-
this.
|
|
1422
|
-
|
|
1423
|
-
if (!value.owner?.persistent)
|
|
1424
|
-
owner.own(value);
|
|
1425
|
-
fnScope.hardSet(new ValueOwner({
|
|
1426
|
-
scope: fnScope,
|
|
1427
|
-
value,
|
|
1428
|
-
identifier: owner.identifier,
|
|
1429
|
-
name: owner.name,
|
|
1430
|
-
}));
|
|
1593
|
+
this.paramNames.forEach((name, i) => {
|
|
1594
|
+
fnScope.hardSet(name, args[i]);
|
|
1431
1595
|
});
|
|
1432
1596
|
this.inlineEnd = new LiteralValue(null);
|
|
1433
1597
|
this.tryingInline = true;
|
|
1434
1598
|
let inst = this.c.handle(fnScope, this.body)[1];
|
|
1435
1599
|
this.tryingInline = false;
|
|
1436
|
-
const returnIndex = inst.findIndex(i => i.alwaysRuns &&
|
|
1600
|
+
const returnIndex = inst.findIndex(i => i.alwaysRuns &&
|
|
1601
|
+
i.intent === EInstIntent.return &&
|
|
1602
|
+
i.intentSource === this);
|
|
1437
1603
|
if (returnIndex !== -1)
|
|
1438
1604
|
inst = inst.slice(0, returnIndex + 1);
|
|
1439
1605
|
inst.push(new AddressResolver(this.inlineEnd));
|
|
1440
1606
|
// return the function value
|
|
1441
|
-
return [this.inlineTemp
|
|
1607
|
+
return [this.inlineTemp, inst];
|
|
1442
1608
|
}
|
|
1443
|
-
call(scope, args) {
|
|
1609
|
+
call(scope, args, out) {
|
|
1444
1610
|
this.ensureInit();
|
|
1445
|
-
if (args.length !== this.
|
|
1611
|
+
if (args.length !== this.paramNames.length)
|
|
1446
1612
|
throw new CompilerError("Cannot call: argument count not matching.");
|
|
1447
|
-
const inlineCall = this.inlineCall(scope, args);
|
|
1613
|
+
const inlineCall = this.inlineCall(scope, args, out);
|
|
1614
|
+
hideRedundantJumps(inlineCall[1]);
|
|
1448
1615
|
const inlineSize = inlineCall[1].filter(i => !i.hidden).length;
|
|
1449
1616
|
if (this.inline || inlineSize <= this.callSize)
|
|
1450
1617
|
return inlineCall;
|
|
1451
|
-
return this.normalCall(scope, args);
|
|
1618
|
+
return this.normalCall(scope, args, out);
|
|
1452
1619
|
}
|
|
1453
1620
|
return(scope, arg) {
|
|
1454
1621
|
this.ensureInit();
|
|
@@ -1463,8 +1630,11 @@ class FunctionValue extends VoidValue {
|
|
|
1463
1630
|
eval(_scope) {
|
|
1464
1631
|
return [this, []];
|
|
1465
1632
|
}
|
|
1466
|
-
|
|
1467
|
-
|
|
1633
|
+
paramOuts() {
|
|
1634
|
+
this.ensureInit();
|
|
1635
|
+
if (this.inline || this.tryingInline)
|
|
1636
|
+
return;
|
|
1637
|
+
return this.paramValues;
|
|
1468
1638
|
}
|
|
1469
1639
|
}
|
|
1470
1640
|
function endsWithReturn(inst) {
|
|
@@ -1485,7 +1655,7 @@ function endsWithReturn(inst) {
|
|
|
1485
1655
|
return false;
|
|
1486
1656
|
}
|
|
1487
1657
|
|
|
1488
|
-
function handleFunctionNode(c, scope, node) {
|
|
1658
|
+
function handleFunctionNode(c, scope, node, out) {
|
|
1489
1659
|
let { params, body } = node;
|
|
1490
1660
|
if (es.isExpression(body)) {
|
|
1491
1661
|
const { loc } = body;
|
|
@@ -1500,46 +1670,44 @@ function handleFunctionNode(c, scope, node) {
|
|
|
1500
1670
|
params: params,
|
|
1501
1671
|
body,
|
|
1502
1672
|
c,
|
|
1673
|
+
out,
|
|
1503
1674
|
}),
|
|
1504
1675
|
[],
|
|
1505
1676
|
];
|
|
1506
1677
|
}
|
|
1507
|
-
const ArrowFunctionExpression = (c, scope, node) => {
|
|
1508
|
-
return handleFunctionNode(c, scope, node);
|
|
1678
|
+
const ArrowFunctionExpression = (c, scope, node, out) => {
|
|
1679
|
+
return handleFunctionNode(c, scope, node, out);
|
|
1509
1680
|
};
|
|
1510
1681
|
const FunctionDeclaration = (c, scope, node) => {
|
|
1511
1682
|
const identifier = node.id.name;
|
|
1512
1683
|
const name = nodeName(node, !c.compactNames && identifier);
|
|
1513
|
-
const functionIns = handleFunctionNode(c, scope, node);
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
value: functionIns[0],
|
|
1517
|
-
identifier,
|
|
1518
|
-
name,
|
|
1519
|
-
});
|
|
1520
|
-
return [scope.set(owner), []];
|
|
1684
|
+
const functionIns = handleFunctionNode(c, scope, node, name);
|
|
1685
|
+
scope.set(identifier, functionIns[0]);
|
|
1686
|
+
return functionIns;
|
|
1521
1687
|
};
|
|
1522
1688
|
const FunctionExpression = ArrowFunctionExpression;
|
|
1523
1689
|
const ObjectMethod = ArrowFunctionExpression;
|
|
1524
1690
|
|
|
1525
|
-
const CallExpression = (c, scope, node) => {
|
|
1691
|
+
const CallExpression = (c, scope, node, out) => {
|
|
1526
1692
|
const inst = [];
|
|
1527
|
-
const
|
|
1693
|
+
const [callee, calleeInst] = c.handleEval(scope, node.callee);
|
|
1694
|
+
inst.push(...calleeInst);
|
|
1695
|
+
const paramOuts = callee.paramOuts();
|
|
1696
|
+
const args = node.arguments.map((node, index) => {
|
|
1528
1697
|
// TODO: this might be why t0 did not increment
|
|
1529
1698
|
// TODO: figure out if the previous todo is still relevant
|
|
1530
|
-
const
|
|
1699
|
+
const out = paramOuts?.[index];
|
|
1700
|
+
const [v, i] = c.handleEval(scope, node, out);
|
|
1531
1701
|
inst.push(...i);
|
|
1532
1702
|
return v;
|
|
1533
1703
|
});
|
|
1534
|
-
const [
|
|
1535
|
-
inst.push(...calleeInst);
|
|
1536
|
-
const [callValue, callInst] = callee.call(scope, args);
|
|
1704
|
+
const [callValue, callInst] = callee.call(scope, args, out);
|
|
1537
1705
|
inst.push(...callInst);
|
|
1538
1706
|
return [callValue, inst];
|
|
1539
1707
|
};
|
|
1540
1708
|
const NewExpression = CallExpression;
|
|
1541
|
-
const TaggedTemplateExpression = (c, scope, node) => {
|
|
1542
|
-
const [tag, tagInst] = c.
|
|
1709
|
+
const TaggedTemplateExpression = (c, scope, node, out) => {
|
|
1710
|
+
const [tag, tagInst] = c.handleEval(scope, node.tag);
|
|
1543
1711
|
const template = node.quasi;
|
|
1544
1712
|
const stringsObject = ObjectValue.fromArray(template.quasis.map(quasi => new LiteralValue(quasi.value.cooked ?? "")), {
|
|
1545
1713
|
raw: ObjectValue.fromArray(template.quasis.map(quasi => new LiteralValue(quasi.value.raw))),
|
|
@@ -1547,15 +1715,15 @@ const TaggedTemplateExpression = (c, scope, node) => {
|
|
|
1547
1715
|
const expressions = [];
|
|
1548
1716
|
const expressionInsts = [];
|
|
1549
1717
|
template.expressions.forEach(expression => {
|
|
1550
|
-
const [value, inst] = c.
|
|
1718
|
+
const [value, inst] = c.handleEval(scope, expression);
|
|
1551
1719
|
expressions.push(value);
|
|
1552
1720
|
expressionInsts.push(...inst);
|
|
1553
1721
|
});
|
|
1554
|
-
const [result, resultInst] = tag.call(scope, [stringsObject, ...expressions]);
|
|
1722
|
+
const [result, resultInst] = tag.call(scope, [stringsObject, ...expressions], out);
|
|
1555
1723
|
return [result, [...expressionInsts, ...tagInst, ...resultInst]];
|
|
1556
1724
|
};
|
|
1557
1725
|
|
|
1558
|
-
const ObjectExpression = (c, scope, node) => {
|
|
1726
|
+
const ObjectExpression = (c, scope, node, out) => {
|
|
1559
1727
|
const data = {};
|
|
1560
1728
|
const inst = [];
|
|
1561
1729
|
for (const prop of node.properties) {
|
|
@@ -1575,38 +1743,48 @@ const ObjectExpression = (c, scope, node) => {
|
|
|
1575
1743
|
else {
|
|
1576
1744
|
throw new CompilerError(`Unsupported object key type: ${key.type}`, key);
|
|
1577
1745
|
}
|
|
1578
|
-
const [member, memberInst] = c.handleEval(scope, value);
|
|
1579
|
-
// TODO: use an approach that can be defined by each type
|
|
1580
|
-
if (!member.owner && !(member instanceof LiteralValue)) {
|
|
1581
|
-
new ValueOwner({
|
|
1582
|
-
scope,
|
|
1583
|
-
constant: true,
|
|
1584
|
-
value: member,
|
|
1585
|
-
});
|
|
1586
|
-
}
|
|
1746
|
+
const [member, memberInst] = c.handleEval(scope, value, extractDestrucuringOut(out, index));
|
|
1587
1747
|
data[index] = member;
|
|
1588
1748
|
inst.push(...memberInst);
|
|
1589
1749
|
}
|
|
1590
1750
|
return [new ObjectValue(data), inst];
|
|
1591
1751
|
};
|
|
1592
|
-
const ArrayExpression = (c, scope, node) => {
|
|
1752
|
+
const ArrayExpression = (c, scope, node, out) => {
|
|
1593
1753
|
const items = [];
|
|
1594
1754
|
const inst = [];
|
|
1595
|
-
node.elements.forEach(element => {
|
|
1755
|
+
node.elements.forEach((element, i) => {
|
|
1596
1756
|
const [value, valueInst] = element
|
|
1597
|
-
? c.handleEval(scope, element)
|
|
1757
|
+
? c.handleEval(scope, element, extractDestrucuringOut(out, i))
|
|
1598
1758
|
: [undefined, []];
|
|
1599
1759
|
items.push(value);
|
|
1600
1760
|
inst.push(...valueInst);
|
|
1601
1761
|
});
|
|
1602
1762
|
return [ObjectValue.fromArray(items), inst];
|
|
1603
1763
|
};
|
|
1604
|
-
const MemberExpression = (c, scope, node) => {
|
|
1605
|
-
const [obj, objInst] = c.handleConsume(scope, node.object);
|
|
1764
|
+
const MemberExpression = (c, scope, node, out) => {
|
|
1606
1765
|
const [prop, propInst] = node.computed
|
|
1607
|
-
? c.
|
|
1766
|
+
? c.handleEval(scope, node.property)
|
|
1608
1767
|
: [new LiteralValue(node.property.name), []];
|
|
1609
|
-
|
|
1768
|
+
if (!out) {
|
|
1769
|
+
const [obj, objInst] = c.handleEval(scope, node.object);
|
|
1770
|
+
const [got, gotInst] = obj.get(scope, prop);
|
|
1771
|
+
return [got, [...objInst, ...propInst, ...gotInst]];
|
|
1772
|
+
}
|
|
1773
|
+
const temp = SenseableValue.out(scope, out);
|
|
1774
|
+
const objOut = new DestructuringValue(new Map([
|
|
1775
|
+
[
|
|
1776
|
+
prop,
|
|
1777
|
+
{
|
|
1778
|
+
value: temp,
|
|
1779
|
+
// a direct assignment should NOT happen
|
|
1780
|
+
handler: () => {
|
|
1781
|
+
throw new CompilerError("Unexpected destructuring assignment");
|
|
1782
|
+
},
|
|
1783
|
+
},
|
|
1784
|
+
],
|
|
1785
|
+
]));
|
|
1786
|
+
const [obj, objInst] = c.handleEval(scope, node.object, objOut);
|
|
1787
|
+
const [got, gotInst] = obj.get(scope, prop, temp);
|
|
1610
1788
|
return [got, [...objInst, ...propInst, ...gotInst]];
|
|
1611
1789
|
};
|
|
1612
1790
|
const ArrayPattern = (c, scope, node) => {
|
|
@@ -1616,11 +1794,21 @@ const ArrayPattern = (c, scope, node) => {
|
|
|
1616
1794
|
const element = node.elements[i];
|
|
1617
1795
|
if (!element)
|
|
1618
1796
|
continue;
|
|
1619
|
-
const valueInst = c.handle(scope, element);
|
|
1620
|
-
if (!
|
|
1797
|
+
const [value, valueInst] = c.handle(scope, element);
|
|
1798
|
+
if (!value)
|
|
1621
1799
|
throw new CompilerError("Destructuring element must resolve to a value", element);
|
|
1622
|
-
inst.push(...valueInst
|
|
1623
|
-
members.set(new LiteralValue(i),
|
|
1800
|
+
inst.push(...valueInst);
|
|
1801
|
+
members.set(new LiteralValue(i), {
|
|
1802
|
+
value,
|
|
1803
|
+
handler(get) {
|
|
1804
|
+
return c.handle(scope, element, () => {
|
|
1805
|
+
const inst = get();
|
|
1806
|
+
// assigns the output to the target value
|
|
1807
|
+
inst[1].push(...value["="](scope, inst[0])[1]);
|
|
1808
|
+
return inst;
|
|
1809
|
+
});
|
|
1810
|
+
},
|
|
1811
|
+
});
|
|
1624
1812
|
}
|
|
1625
1813
|
return [new DestructuringValue(members), inst];
|
|
1626
1814
|
};
|
|
@@ -1635,13 +1823,23 @@ const ObjectPattern = (c, scope, node) => {
|
|
|
1635
1823
|
const { key } = prop;
|
|
1636
1824
|
const keyInst = !prop.computed && key.type === "Identifier"
|
|
1637
1825
|
? [new LiteralValue(key.name), []]
|
|
1638
|
-
: c.
|
|
1826
|
+
: c.handleEval(scope, prop.key);
|
|
1639
1827
|
inst.push(...keyInst[1]);
|
|
1640
|
-
const valueInst = c.handle(scope, prop.value);
|
|
1641
|
-
if (!
|
|
1828
|
+
const [value, valueInst] = c.handle(scope, prop.value);
|
|
1829
|
+
if (!value)
|
|
1642
1830
|
throw new CompilerError("Destructuring member must resolve to a value", prop.value);
|
|
1643
|
-
inst.push(...valueInst
|
|
1644
|
-
members.set(keyInst[0],
|
|
1831
|
+
inst.push(...valueInst);
|
|
1832
|
+
members.set(keyInst[0], {
|
|
1833
|
+
value,
|
|
1834
|
+
handler(get) {
|
|
1835
|
+
return c.handle(scope, prop, () => {
|
|
1836
|
+
const inst = get();
|
|
1837
|
+
// assigns the output to the target value
|
|
1838
|
+
inst[1].push(...value["="](scope, inst[0])[1]);
|
|
1839
|
+
return inst;
|
|
1840
|
+
});
|
|
1841
|
+
},
|
|
1842
|
+
});
|
|
1645
1843
|
}
|
|
1646
1844
|
return [new DestructuringValue(members), inst];
|
|
1647
1845
|
};
|
|
@@ -1686,7 +1884,7 @@ const TSEnumDeclaration = (c, scope, node) => {
|
|
|
1686
1884
|
: [new LiteralValue(counter)];
|
|
1687
1885
|
if (!(value instanceof LiteralValue))
|
|
1688
1886
|
throw new CompilerError("Enum members must contain literal values", member);
|
|
1689
|
-
if (
|
|
1887
|
+
if (value.isNumber()) {
|
|
1690
1888
|
lastType = "number";
|
|
1691
1889
|
counter = value.data + 1;
|
|
1692
1890
|
}
|
|
@@ -1696,13 +1894,9 @@ const TSEnumDeclaration = (c, scope, node) => {
|
|
|
1696
1894
|
const name = member.id.type === "Identifier" ? member.id.name : member.id.value;
|
|
1697
1895
|
data[name] = value;
|
|
1698
1896
|
}
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
constant: true,
|
|
1703
|
-
identifier: node.id.name,
|
|
1704
|
-
name: nodeName(node, !c.compactNames && node.id.name),
|
|
1705
|
-
}));
|
|
1897
|
+
const value = new ObjectValue(data);
|
|
1898
|
+
value.name = nodeName(node, !c.compactNames && node.id.name);
|
|
1899
|
+
scope.set(node.id.name, value);
|
|
1706
1900
|
return [null, []];
|
|
1707
1901
|
};
|
|
1708
1902
|
const TSNonNullExpression = (c, scope, node) => {
|
|
@@ -1766,15 +1960,12 @@ class MacroFunction extends VoidValue {
|
|
|
1766
1960
|
this.mutability = EMutability.constant;
|
|
1767
1961
|
this.fn = fn;
|
|
1768
1962
|
}
|
|
1769
|
-
call(scope, args) {
|
|
1770
|
-
return this.fn.apply(this, [scope, ...args]);
|
|
1963
|
+
call(scope, args, out) {
|
|
1964
|
+
return this.fn.apply(this, [scope, out, ...args]);
|
|
1771
1965
|
}
|
|
1772
1966
|
eval(_scope) {
|
|
1773
1967
|
return [this, []];
|
|
1774
1968
|
}
|
|
1775
|
-
consume(_scope) {
|
|
1776
|
-
return [this, []];
|
|
1777
|
-
}
|
|
1778
1969
|
}
|
|
1779
1970
|
|
|
1780
1971
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
@@ -1798,49 +1989,39 @@ const mathOperations = {
|
|
|
1798
1989
|
sqrt: a => Math.sqrt(a),
|
|
1799
1990
|
rand: null,
|
|
1800
1991
|
};
|
|
1801
|
-
function
|
|
1802
|
-
return new ValueOwner({
|
|
1803
|
-
scope,
|
|
1804
|
-
constant: true,
|
|
1805
|
-
name,
|
|
1806
|
-
value: assign(new StoreValue(scope), {
|
|
1807
|
-
mutability: EMutability.constant,
|
|
1808
|
-
}),
|
|
1809
|
-
}).value;
|
|
1810
|
-
}
|
|
1811
|
-
function createMacroMathOperations(scope) {
|
|
1992
|
+
function createMacroMathOperations() {
|
|
1812
1993
|
const macroMathOperations = {
|
|
1813
|
-
PI:
|
|
1814
|
-
E:
|
|
1815
|
-
degToRad:
|
|
1816
|
-
radToDeg:
|
|
1994
|
+
PI: new StoreValue("@pi", EMutability.constant),
|
|
1995
|
+
E: new StoreValue("@e", EMutability.constant),
|
|
1996
|
+
degToRad: new StoreValue("@degToRad", EMutability.constant),
|
|
1997
|
+
radToDeg: new StoreValue("@radToDeg", EMutability.constant),
|
|
1817
1998
|
};
|
|
1818
1999
|
for (const key in mathOperations) {
|
|
1819
2000
|
const fn = mathOperations[key];
|
|
1820
|
-
macroMathOperations[key] = new MacroFunction((scope, a, b) => {
|
|
2001
|
+
macroMathOperations[key] = new MacroFunction((scope, out, a, b) => {
|
|
1821
2002
|
if (b) {
|
|
1822
2003
|
if (fn && a instanceof LiteralValue && b instanceof LiteralValue) {
|
|
1823
|
-
if (
|
|
2004
|
+
if (!a.isNumber() || !b.isNumber())
|
|
1824
2005
|
throw new CompilerError("Cannot do math operation with non-numerical literals.");
|
|
1825
2006
|
return [new LiteralValue(fn(a.num, b.num)), []];
|
|
1826
2007
|
}
|
|
1827
|
-
const temp =
|
|
2008
|
+
const temp = StoreValue.from(scope, out);
|
|
1828
2009
|
return [temp, [new OperationInstruction(key, temp, a, b)]];
|
|
1829
2010
|
}
|
|
1830
2011
|
if (fn && a instanceof LiteralValue) {
|
|
1831
|
-
if (
|
|
2012
|
+
if (!a.isNumber())
|
|
1832
2013
|
throw new CompilerError("Cannot do math operation with non-numerical literal.");
|
|
1833
2014
|
return [new LiteralValue(fn(a.num)), []];
|
|
1834
2015
|
}
|
|
1835
|
-
const temp =
|
|
2016
|
+
const temp = StoreValue.from(scope, out);
|
|
1836
2017
|
return [temp, [new OperationInstruction(key, temp, a, b)]];
|
|
1837
2018
|
});
|
|
1838
2019
|
}
|
|
1839
2020
|
return macroMathOperations;
|
|
1840
2021
|
}
|
|
1841
2022
|
class MlogMath extends ObjectValue {
|
|
1842
|
-
constructor(
|
|
1843
|
-
super(createMacroMathOperations(
|
|
2023
|
+
constructor() {
|
|
2024
|
+
super(createMacroMathOperations());
|
|
1844
2025
|
}
|
|
1845
2026
|
}
|
|
1846
2027
|
// op angle result a b
|
|
@@ -1848,7 +2029,7 @@ class MlogMath extends ObjectValue {
|
|
|
1848
2029
|
|
|
1849
2030
|
class Concat extends MacroFunction {
|
|
1850
2031
|
constructor() {
|
|
1851
|
-
super((scope, ...values) => {
|
|
2032
|
+
super((scope, out, ...values) => {
|
|
1852
2033
|
for (const value of values)
|
|
1853
2034
|
if (!(value instanceof LiteralValue))
|
|
1854
2035
|
throw new CompilerError("Concat arguments must all be literal values.");
|
|
@@ -1860,66 +2041,19 @@ class Concat extends MacroFunction {
|
|
|
1860
2041
|
}
|
|
1861
2042
|
}
|
|
1862
2043
|
|
|
1863
|
-
/** Asserts that `value` is a `LiteralValue` that contains a string */
|
|
1864
|
-
function assertStringLiteral(value, name) {
|
|
1865
|
-
if (!(value instanceof LiteralValue) || typeof value.data !== "string")
|
|
1866
|
-
throw new CompilerError(`${name} must be a string literal`);
|
|
1867
|
-
}
|
|
1868
|
-
/** Asserts that `value` is a string literal that is part of a given set of `members`*/
|
|
1869
|
-
function assertLiteralOneOf(value, members, name) {
|
|
1870
|
-
assertStringLiteral(value, name);
|
|
1871
|
-
if (!members.includes(value.data))
|
|
1872
|
-
throw new CompilerError(`${name} must be one of: ${members
|
|
1873
|
-
.slice(0, -1)
|
|
1874
|
-
.map(member => `"${member}"`)
|
|
1875
|
-
.join(", ")} or "${members[members.length - 1]}"`);
|
|
1876
|
-
}
|
|
1877
|
-
/** Asserts that `value` is either a store or a literal */
|
|
1878
|
-
function assertIsRuntimeValue(value, name) {
|
|
1879
|
-
if (!(value instanceof LiteralValue) && !(value instanceof StoreValue))
|
|
1880
|
-
throw new CompilerError(`${name} must be a valid runtime value`);
|
|
1881
|
-
}
|
|
1882
|
-
/** Asserts that `value` is an `ObjectValue` */
|
|
1883
|
-
function assertIsObjectMacro(value, name) {
|
|
1884
|
-
if (!(value instanceof ObjectValue))
|
|
1885
|
-
throw new CompilerError(`${name} must be an object macro`);
|
|
1886
|
-
}
|
|
1887
|
-
/** Asserts that `value` is an `ObjectValue` that has a length property */
|
|
1888
|
-
function assertIsArrayMacro(value, name) {
|
|
1889
|
-
if (!(value instanceof ObjectValue) || !value.data.length)
|
|
1890
|
-
throw new CompilerError(`${name} must be an array macro`);
|
|
1891
|
-
}
|
|
1892
|
-
/** Asserts that all of the fields described are present on `value`*/
|
|
1893
|
-
function assertObjectFields(value, fields) {
|
|
1894
|
-
const result = [];
|
|
1895
|
-
for (const field of fields) {
|
|
1896
|
-
const param = typeof field === "object" ? field : { key: field };
|
|
1897
|
-
const item = value.data[param.key];
|
|
1898
|
-
if (item) {
|
|
1899
|
-
param.validate?.(item);
|
|
1900
|
-
result.push(item);
|
|
1901
|
-
continue;
|
|
1902
|
-
}
|
|
1903
|
-
if (param.default == undefined)
|
|
1904
|
-
throw new CompilerError(`The field "${param.name ?? param.key}" must be present`);
|
|
1905
|
-
result.push(param.default);
|
|
1906
|
-
}
|
|
1907
|
-
return result;
|
|
1908
|
-
}
|
|
1909
|
-
|
|
1910
2044
|
/** Used to create namespaces that contain multiple methods that map into instructions */
|
|
1911
2045
|
function createOverloadNamespace({ overloads, handler, }) {
|
|
1912
2046
|
const result = {};
|
|
1913
2047
|
for (const key in overloads) {
|
|
1914
2048
|
const { named, args } = overloads[key];
|
|
1915
2049
|
if (named) {
|
|
1916
|
-
result[key] = new MacroFunction((scope, options) => {
|
|
2050
|
+
result[key] = new MacroFunction((scope, out, options) => {
|
|
1917
2051
|
assertIsObjectMacro(options, named);
|
|
1918
|
-
return handler(scope, key, ...assertObjectFields(options, args));
|
|
2052
|
+
return handler(scope, key, out, ...assertObjectFields(options, args));
|
|
1919
2053
|
});
|
|
1920
2054
|
continue;
|
|
1921
2055
|
}
|
|
1922
|
-
result[key] = new MacroFunction((scope, ...params) => {
|
|
2056
|
+
result[key] = new MacroFunction((scope, out, ...params) => {
|
|
1923
2057
|
const handlerParams = [];
|
|
1924
2058
|
// validate the paramters
|
|
1925
2059
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -1936,7 +2070,7 @@ function createOverloadNamespace({ overloads, handler, }) {
|
|
|
1936
2070
|
arg.validate?.(param);
|
|
1937
2071
|
handlerParams.push(param);
|
|
1938
2072
|
}
|
|
1939
|
-
return handler(scope, key, ...handlerParams);
|
|
2073
|
+
return handler(scope, key, out, ...handlerParams);
|
|
1940
2074
|
});
|
|
1941
2075
|
}
|
|
1942
2076
|
return result;
|
|
@@ -1981,7 +2115,7 @@ class Draw extends ObjectValue {
|
|
|
1981
2115
|
args: ["x", "y", "image", "size", "rotation"],
|
|
1982
2116
|
},
|
|
1983
2117
|
},
|
|
1984
|
-
handler(scope, overload, ...args) {
|
|
2118
|
+
handler(scope, overload, out, ...args) {
|
|
1985
2119
|
return [null, [new InstructionBase("draw", overload, ...args)]];
|
|
1986
2120
|
},
|
|
1987
2121
|
});
|
|
@@ -1991,7 +2125,7 @@ class Draw extends ObjectValue {
|
|
|
1991
2125
|
|
|
1992
2126
|
class Print extends MacroFunction {
|
|
1993
2127
|
constructor() {
|
|
1994
|
-
super((scope, ...values) => {
|
|
2128
|
+
super((scope, out, ...values) => {
|
|
1995
2129
|
const [first] = values;
|
|
1996
2130
|
const inst = [];
|
|
1997
2131
|
if (!isTemplateObjectArray(first)) {
|
|
@@ -2020,7 +2154,7 @@ class Print extends MacroFunction {
|
|
|
2020
2154
|
const defaultTargetName$1 = "message1";
|
|
2021
2155
|
class PrintFlush extends MacroFunction {
|
|
2022
2156
|
constructor() {
|
|
2023
|
-
super((scope, target) => {
|
|
2157
|
+
super((scope, out, target) => {
|
|
2024
2158
|
if (!target)
|
|
2025
2159
|
return [null, [new InstructionBase("printflush", defaultTargetName$1)]];
|
|
2026
2160
|
if (!(target instanceof SenseableValue))
|
|
@@ -2033,7 +2167,7 @@ class PrintFlush extends MacroFunction {
|
|
|
2033
2167
|
const defaultTargetName = "display1";
|
|
2034
2168
|
class DrawFlush extends MacroFunction {
|
|
2035
2169
|
constructor() {
|
|
2036
|
-
super((scope, target) => {
|
|
2170
|
+
super((scope, out, target) => {
|
|
2037
2171
|
if (!target)
|
|
2038
2172
|
return [null, [new InstructionBase("drawflush", defaultTargetName)]];
|
|
2039
2173
|
if (!(target instanceof SenseableValue))
|
|
@@ -2052,10 +2186,10 @@ class GetBlock extends ObjectValue {
|
|
|
2052
2186
|
block: { args: ["x", "y"] },
|
|
2053
2187
|
building: { args: ["x", "y"] },
|
|
2054
2188
|
},
|
|
2055
|
-
handler(scope, overload, x, y) {
|
|
2189
|
+
handler(scope, overload, out, x, y) {
|
|
2056
2190
|
const output = overload === "building"
|
|
2057
|
-
?
|
|
2058
|
-
:
|
|
2191
|
+
? SenseableValue.from(scope, out, EMutability.constant)
|
|
2192
|
+
: StoreValue.from(scope, out, EMutability.constant);
|
|
2059
2193
|
return [
|
|
2060
2194
|
output,
|
|
2061
2195
|
[new InstructionBase("getblock", overload, output, x, y)],
|
|
@@ -2068,11 +2202,11 @@ class GetBlock extends ObjectValue {
|
|
|
2068
2202
|
|
|
2069
2203
|
class GetLink extends MacroFunction {
|
|
2070
2204
|
constructor() {
|
|
2071
|
-
super((scope, index) => {
|
|
2205
|
+
super((scope, out, index) => {
|
|
2072
2206
|
if (!(index instanceof StoreValue) &&
|
|
2073
|
-
(!(index instanceof LiteralValue) ||
|
|
2207
|
+
(!(index instanceof LiteralValue) || !index.isNumber()))
|
|
2074
2208
|
throw new CompilerError("The getlink index must be a number literal or a store");
|
|
2075
|
-
const outBuild =
|
|
2209
|
+
const outBuild = SenseableValue.from(scope, out, EMutability.constant);
|
|
2076
2210
|
return [outBuild, [new InstructionBase("getlink", outBuild, index)]];
|
|
2077
2211
|
});
|
|
2078
2212
|
}
|
|
@@ -2094,11 +2228,10 @@ class Control extends ObjectValue {
|
|
|
2094
2228
|
config: { args: ["building", "value"] },
|
|
2095
2229
|
color: { args: ["building", "r", "g", "b"] },
|
|
2096
2230
|
},
|
|
2097
|
-
handler(scope, overload, ...args) {
|
|
2231
|
+
handler(scope, overload, out, ...args) {
|
|
2098
2232
|
if (overload !== "color")
|
|
2099
2233
|
return [null, [new InstructionBase("control", overload, ...args)]];
|
|
2100
|
-
const temp =
|
|
2101
|
-
temp.ensureOwned();
|
|
2234
|
+
const temp = StoreValue.from(scope);
|
|
2102
2235
|
// this "patch" is here for compatibility reasons
|
|
2103
2236
|
// I don't want to add another breaking change
|
|
2104
2237
|
// TODO: figure out if this patch should be removed later
|
|
@@ -2134,14 +2267,14 @@ const validRadarSorts = [
|
|
|
2134
2267
|
];
|
|
2135
2268
|
class Radar extends MacroFunction {
|
|
2136
2269
|
constructor() {
|
|
2137
|
-
super((scope, options) => {
|
|
2270
|
+
super((scope, out, options) => {
|
|
2138
2271
|
assertIsObjectMacro(options, "The radar options");
|
|
2139
2272
|
const { building, filters, order, sort } = options.data;
|
|
2140
2273
|
if (!(building instanceof SenseableValue))
|
|
2141
2274
|
throw new CompilerError("The building must a senseable value");
|
|
2142
2275
|
assertIsArrayMacro(filters, "filters");
|
|
2143
2276
|
const { length } = filters.data;
|
|
2144
|
-
if (!(length instanceof LiteralValue) ||
|
|
2277
|
+
if (!(length instanceof LiteralValue) || !length.isNumber())
|
|
2145
2278
|
throw new CompilerError("The length of an array macro must be");
|
|
2146
2279
|
if (length.data !== 3)
|
|
2147
2280
|
throw new CompilerError("The filters array must have 3 items");
|
|
@@ -2153,7 +2286,7 @@ class Radar extends MacroFunction {
|
|
|
2153
2286
|
if (!(order instanceof LiteralValue || order instanceof StoreValue))
|
|
2154
2287
|
throw new CompilerError("The radar order must be a literal or a store");
|
|
2155
2288
|
assertLiteralOneOf(sort, validRadarSorts, "The radar sort");
|
|
2156
|
-
const outUnit =
|
|
2289
|
+
const outUnit = SenseableValue.from(scope, out, EMutability.constant);
|
|
2157
2290
|
return [
|
|
2158
2291
|
outUnit,
|
|
2159
2292
|
[
|
|
@@ -2166,14 +2299,12 @@ class Radar extends MacroFunction {
|
|
|
2166
2299
|
|
|
2167
2300
|
class Sensor extends MacroFunction {
|
|
2168
2301
|
constructor() {
|
|
2169
|
-
super((scope, property, target) => {
|
|
2302
|
+
super((scope, out, property, target) => {
|
|
2170
2303
|
if (!(property instanceof StoreValue))
|
|
2171
2304
|
throw new CompilerError("The sensor property must be a store");
|
|
2172
2305
|
if (!(target instanceof SenseableValue))
|
|
2173
2306
|
throw new CompilerError("The sensor target must be a senseable value");
|
|
2174
|
-
const temp =
|
|
2175
|
-
mutability: EMutability.readonly,
|
|
2176
|
-
});
|
|
2307
|
+
const temp = SenseableValue.from(scope, out, EMutability.readonly);
|
|
2177
2308
|
return [temp, [new InstructionBase("sensor", temp, target, property)]];
|
|
2178
2309
|
});
|
|
2179
2310
|
}
|
|
@@ -2181,9 +2312,9 @@ class Sensor extends MacroFunction {
|
|
|
2181
2312
|
|
|
2182
2313
|
class Wait extends MacroFunction {
|
|
2183
2314
|
constructor() {
|
|
2184
|
-
super((scope, seconds) => {
|
|
2315
|
+
super((scope, out, seconds) => {
|
|
2185
2316
|
if (!(seconds instanceof StoreValue) &&
|
|
2186
|
-
!(seconds instanceof LiteralValue &&
|
|
2317
|
+
!(seconds instanceof LiteralValue && seconds.isNumber()))
|
|
2187
2318
|
throw new CompilerError("The wait seconds must be either a number literal or a store");
|
|
2188
2319
|
return [null, [new InstructionBase("wait", seconds)]];
|
|
2189
2320
|
});
|
|
@@ -2199,8 +2330,8 @@ class Lookup extends ObjectValue {
|
|
|
2199
2330
|
item: { args: ["index"] },
|
|
2200
2331
|
liquid: { args: ["index"] },
|
|
2201
2332
|
},
|
|
2202
|
-
handler(scope, overload, index) {
|
|
2203
|
-
const output =
|
|
2333
|
+
handler(scope, overload, out, index) {
|
|
2334
|
+
const output = StoreValue.from(scope, out);
|
|
2204
2335
|
return [
|
|
2205
2336
|
output,
|
|
2206
2337
|
[new InstructionBase("lookup", overload, output, index)],
|
|
@@ -2213,15 +2344,15 @@ class Lookup extends ObjectValue {
|
|
|
2213
2344
|
|
|
2214
2345
|
class PackColor extends MacroFunction {
|
|
2215
2346
|
constructor() {
|
|
2216
|
-
super((scope, ...args) => {
|
|
2347
|
+
super((scope, out, ...args) => {
|
|
2217
2348
|
if (args.length !== 4) {
|
|
2218
2349
|
throw new CompilerError(`Expected 4 arguments, received ${args.length}`);
|
|
2219
2350
|
}
|
|
2220
2351
|
if (!args.every((value) => value instanceof StoreValue ||
|
|
2221
|
-
(value instanceof LiteralValue &&
|
|
2352
|
+
(value instanceof LiteralValue && value.isNumber()))) {
|
|
2222
2353
|
throw new CompilerError("packColor arguments must be either stores or number literals");
|
|
2223
2354
|
}
|
|
2224
|
-
const output =
|
|
2355
|
+
const output = StoreValue.from(scope, out);
|
|
2225
2356
|
return [output, [new InstructionBase("packcolor", output, ...args)]];
|
|
2226
2357
|
});
|
|
2227
2358
|
}
|
|
@@ -2244,7 +2375,7 @@ class SetBlock extends ObjectValue {
|
|
|
2244
2375
|
args: ["x", "y", "to", "team", { key: "rotation", default: "0" }],
|
|
2245
2376
|
},
|
|
2246
2377
|
},
|
|
2247
|
-
handler(scope, overload, x, y, to, team, rotation) {
|
|
2378
|
+
handler(scope, overload, out, x, y, to, team, rotation) {
|
|
2248
2379
|
return [
|
|
2249
2380
|
null,
|
|
2250
2381
|
[
|
|
@@ -2259,14 +2390,14 @@ class SetBlock extends ObjectValue {
|
|
|
2259
2390
|
|
|
2260
2391
|
class SpawnUnit extends MacroFunction {
|
|
2261
2392
|
constructor() {
|
|
2262
|
-
super((scope, options) => {
|
|
2393
|
+
super((scope, out, options) => {
|
|
2263
2394
|
assertIsObjectMacro(options, "The options");
|
|
2264
2395
|
const { type, x, y, team, rotation } = options.data;
|
|
2265
2396
|
assertIsRuntimeValue(type, "type");
|
|
2266
2397
|
assertIsRuntimeValue(x, "x");
|
|
2267
2398
|
assertIsRuntimeValue(y, "y");
|
|
2268
2399
|
assertIsRuntimeValue(team, "team");
|
|
2269
|
-
const output =
|
|
2400
|
+
const output = SenseableValue.from(scope, out, EMutability.constant);
|
|
2270
2401
|
return [
|
|
2271
2402
|
output,
|
|
2272
2403
|
[
|
|
@@ -2285,7 +2416,7 @@ class Stop extends MacroFunction {
|
|
|
2285
2416
|
|
|
2286
2417
|
class UnitBind extends MacroFunction {
|
|
2287
2418
|
constructor() {
|
|
2288
|
-
super((scope, type) => {
|
|
2419
|
+
super((scope, out, type) => {
|
|
2289
2420
|
if (!(type instanceof StoreValue))
|
|
2290
2421
|
throw new CompilerError("The unitBind type must be a store");
|
|
2291
2422
|
return [null, [new InstructionBase("ubind", type)]];
|
|
@@ -2324,21 +2455,20 @@ class UnitControl extends ObjectValue {
|
|
|
2324
2455
|
args: ["x", "y", "radius"],
|
|
2325
2456
|
},
|
|
2326
2457
|
},
|
|
2327
|
-
handler(scope, overload, ...args) {
|
|
2458
|
+
handler(scope, overload, out, ...args) {
|
|
2328
2459
|
let result = null;
|
|
2329
2460
|
let extraArgs = [];
|
|
2330
2461
|
switch (overload) {
|
|
2331
2462
|
case "getBlock": {
|
|
2332
|
-
const outType =
|
|
2333
|
-
const outBuilding =
|
|
2463
|
+
const outType = StoreValue.from(scope, extractDestrucuringOut(out, 0));
|
|
2464
|
+
const outBuilding = SenseableValue.from(scope, extractDestrucuringOut(out, 1));
|
|
2334
2465
|
result = ObjectValue.fromArray([outType, outBuilding]);
|
|
2335
2466
|
extraArgs = [outType, outBuilding, new LiteralValue(0)];
|
|
2336
2467
|
break;
|
|
2337
2468
|
}
|
|
2338
2469
|
case "within": {
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
extraArgs = [temp, new LiteralValue(0)];
|
|
2470
|
+
result = StoreValue.from(scope, out);
|
|
2471
|
+
extraArgs = [result, new LiteralValue(0)];
|
|
2342
2472
|
break;
|
|
2343
2473
|
}
|
|
2344
2474
|
}
|
|
@@ -2354,12 +2484,12 @@ class UnitControl extends ObjectValue {
|
|
|
2354
2484
|
|
|
2355
2485
|
class UnitRadar extends MacroFunction {
|
|
2356
2486
|
constructor() {
|
|
2357
|
-
super((scope, options) => {
|
|
2487
|
+
super((scope, out, options) => {
|
|
2358
2488
|
assertIsObjectMacro(options, "The radar options");
|
|
2359
2489
|
const { filters, order, sort } = options.data;
|
|
2360
2490
|
assertIsArrayMacro(filters, "filters");
|
|
2361
2491
|
const { length } = filters.data;
|
|
2362
|
-
if (!(length instanceof LiteralValue) ||
|
|
2492
|
+
if (!(length instanceof LiteralValue) || !length.isNumber())
|
|
2363
2493
|
throw new CompilerError("The length of an array macro must be");
|
|
2364
2494
|
if (length.data !== 3)
|
|
2365
2495
|
throw new CompilerError("The filters array must have 3 items");
|
|
@@ -2371,7 +2501,7 @@ class UnitRadar extends MacroFunction {
|
|
|
2371
2501
|
if (!(order instanceof LiteralValue || order instanceof StoreValue))
|
|
2372
2502
|
throw new CompilerError("The radar order must be a literal or a store");
|
|
2373
2503
|
assertLiteralOneOf(sort, validRadarSorts, "The radar sort");
|
|
2374
|
-
const outUnit =
|
|
2504
|
+
const outUnit = SenseableValue.from(scope, out, EMutability.constant);
|
|
2375
2505
|
return [
|
|
2376
2506
|
outUnit,
|
|
2377
2507
|
[
|
|
@@ -2409,11 +2539,11 @@ class UnitLocate extends ObjectValue {
|
|
|
2409
2539
|
args: [],
|
|
2410
2540
|
},
|
|
2411
2541
|
},
|
|
2412
|
-
handler(scope, overload, ...args) {
|
|
2413
|
-
const outFound =
|
|
2414
|
-
const outX =
|
|
2415
|
-
const outY =
|
|
2416
|
-
const outBuilding =
|
|
2542
|
+
handler(scope, overload, out, ...args) {
|
|
2543
|
+
const outFound = StoreValue.from(scope, extractDestrucuringOut(out, 0));
|
|
2544
|
+
const outX = StoreValue.from(scope, extractDestrucuringOut(out, 1));
|
|
2545
|
+
const outY = StoreValue.from(scope, extractDestrucuringOut(out, 2));
|
|
2546
|
+
const outBuilding = SenseableValue.from(scope, overload === "ore" ? discardedName : extractDestrucuringOut(out, 3));
|
|
2417
2547
|
const outArgs = [outX, outY, outFound, outBuilding];
|
|
2418
2548
|
let inputArgs = [];
|
|
2419
2549
|
switch (overload) {
|
|
@@ -2476,7 +2606,7 @@ class ApplyStatus extends ObjectValue {
|
|
|
2476
2606
|
},
|
|
2477
2607
|
clear: { args: ["status", "unit"] },
|
|
2478
2608
|
},
|
|
2479
|
-
handler(scope, overload, effect, unit, duration) {
|
|
2609
|
+
handler(scope, overload, out, effect, unit, duration) {
|
|
2480
2610
|
if (!(effect instanceof LiteralValue))
|
|
2481
2611
|
throw new CompilerError("The status effect must be a string literal");
|
|
2482
2612
|
assertLiteralOneOf(effect, validEffects, "The status effect");
|
|
@@ -2494,7 +2624,7 @@ class ApplyStatus extends ObjectValue {
|
|
|
2494
2624
|
|
|
2495
2625
|
class SpawnWave extends MacroFunction {
|
|
2496
2626
|
constructor() {
|
|
2497
|
-
super((scope, natural, x, y) => {
|
|
2627
|
+
super((scope, out, natural, x, y) => {
|
|
2498
2628
|
if (!(natural instanceof LiteralValue) ||
|
|
2499
2629
|
(natural.data !== 1 && natural.data !== 0))
|
|
2500
2630
|
throw new CompilerError("The 'natural' argument must be a boolean literal");
|
|
@@ -2535,7 +2665,7 @@ class SetRule extends ObjectValue {
|
|
|
2535
2665
|
rtsMinWeight: { args: ["team", "value"] },
|
|
2536
2666
|
rtsMinSquad: { args: ["team", "value"] },
|
|
2537
2667
|
},
|
|
2538
|
-
handler(scope, overload, ...args) {
|
|
2668
|
+
handler(scope, overload, out, ...args) {
|
|
2539
2669
|
const params = ["10", "0", "0", "100", "100"];
|
|
2540
2670
|
switch (overload) {
|
|
2541
2671
|
case "mapArea": {
|
|
@@ -2578,7 +2708,7 @@ class FlushMessage extends ObjectValue {
|
|
|
2578
2708
|
announce: { args: ["duration"] },
|
|
2579
2709
|
toast: { args: ["duration"] },
|
|
2580
2710
|
},
|
|
2581
|
-
handler(scope, overload, duration) {
|
|
2711
|
+
handler(scope, overload, out, duration) {
|
|
2582
2712
|
return [null, [new InstructionBase("message", overload, duration)]];
|
|
2583
2713
|
},
|
|
2584
2714
|
});
|
|
@@ -2594,7 +2724,7 @@ class Cutscene extends ObjectValue {
|
|
|
2594
2724
|
zoom: { args: ["level"] },
|
|
2595
2725
|
stop: { args: [] },
|
|
2596
2726
|
},
|
|
2597
|
-
handler(scope, overload, ...args) {
|
|
2727
|
+
handler(scope, overload, out, ...args) {
|
|
2598
2728
|
const params = Object.assign(["100", "100", "0.06", "0"], args);
|
|
2599
2729
|
return [null, [new InstructionBase("cutscene", overload, ...params)]];
|
|
2600
2730
|
},
|
|
@@ -2605,7 +2735,7 @@ class Cutscene extends ObjectValue {
|
|
|
2605
2735
|
|
|
2606
2736
|
class Explosion extends MacroFunction {
|
|
2607
2737
|
constructor() {
|
|
2608
|
-
super((scope, options) => {
|
|
2738
|
+
super((scope, out, options) => {
|
|
2609
2739
|
assertIsObjectMacro(options, "The options");
|
|
2610
2740
|
const params = assertObjectFields(options, [
|
|
2611
2741
|
"team",
|
|
@@ -2624,7 +2754,7 @@ class Explosion extends MacroFunction {
|
|
|
2624
2754
|
|
|
2625
2755
|
class SetRate extends MacroFunction {
|
|
2626
2756
|
constructor() {
|
|
2627
|
-
super((scope, ipt) => [null, [new InstructionBase("setrate", ipt)]]);
|
|
2757
|
+
super((scope, out, ipt) => [null, [new InstructionBase("setrate", ipt)]]);
|
|
2628
2758
|
}
|
|
2629
2759
|
}
|
|
2630
2760
|
|
|
@@ -2641,8 +2771,8 @@ class Fetch extends ObjectValue {
|
|
|
2641
2771
|
build: { args: ["team", "index", "block"] },
|
|
2642
2772
|
buildCount: { args: ["team", "block"] },
|
|
2643
2773
|
},
|
|
2644
|
-
handler(scope, overload, team, ...rest) {
|
|
2645
|
-
const output =
|
|
2774
|
+
handler(scope, overload, out, team, ...rest) {
|
|
2775
|
+
const output = SenseableValue.from(scope, out, EMutability.constant);
|
|
2646
2776
|
const params = ["0", "@conveyor"];
|
|
2647
2777
|
if (overload === "buildCount") {
|
|
2648
2778
|
params[1] = rest[0];
|
|
@@ -2662,8 +2792,8 @@ class Fetch extends ObjectValue {
|
|
|
2662
2792
|
|
|
2663
2793
|
class GetFlag extends MacroFunction {
|
|
2664
2794
|
constructor() {
|
|
2665
|
-
super((scope, flag) => {
|
|
2666
|
-
const result =
|
|
2795
|
+
super((scope, out, flag) => {
|
|
2796
|
+
const result = StoreValue.from(scope, out);
|
|
2667
2797
|
return [result, [new InstructionBase("getflag", result, flag)]];
|
|
2668
2798
|
});
|
|
2669
2799
|
}
|
|
@@ -2671,7 +2801,7 @@ class GetFlag extends MacroFunction {
|
|
|
2671
2801
|
|
|
2672
2802
|
class SetFlag extends MacroFunction {
|
|
2673
2803
|
constructor() {
|
|
2674
|
-
super((scope, flag, value) => {
|
|
2804
|
+
super((scope, out, flag, value) => {
|
|
2675
2805
|
if (!flag)
|
|
2676
2806
|
throw new CompilerError("Missing flag argument");
|
|
2677
2807
|
if (!value)
|
|
@@ -2690,28 +2820,17 @@ class MemoryEntry extends BaseValue {
|
|
|
2690
2820
|
this.macro = true;
|
|
2691
2821
|
this.store = null;
|
|
2692
2822
|
}
|
|
2693
|
-
eval(scope) {
|
|
2823
|
+
eval(scope, out) {
|
|
2694
2824
|
if (this.store)
|
|
2695
2825
|
return [this.store, []];
|
|
2696
|
-
const temp =
|
|
2826
|
+
const temp = (this.store = StoreValue.from(scope, out));
|
|
2697
2827
|
return [
|
|
2698
2828
|
temp,
|
|
2699
2829
|
[new InstructionBase("read", temp, this.mem.cell, this.prop)],
|
|
2700
2830
|
];
|
|
2701
2831
|
}
|
|
2702
|
-
consume(scope) {
|
|
2703
|
-
if (this.store)
|
|
2704
|
-
return [this.store, []];
|
|
2705
|
-
this.ensureOwned();
|
|
2706
|
-
this.store = new StoreValue(scope);
|
|
2707
|
-
this.owner.own(this.store);
|
|
2708
|
-
return [
|
|
2709
|
-
this.store,
|
|
2710
|
-
[new InstructionBase("read", this.store, this.mem.cell, this.prop)],
|
|
2711
|
-
];
|
|
2712
|
-
}
|
|
2713
2832
|
"="(scope, value) {
|
|
2714
|
-
const [data, dataInst] = value.
|
|
2833
|
+
const [data, dataInst] = value.eval(scope);
|
|
2715
2834
|
return [
|
|
2716
2835
|
data,
|
|
2717
2836
|
[
|
|
@@ -2720,26 +2839,17 @@ class MemoryEntry extends BaseValue {
|
|
|
2720
2839
|
],
|
|
2721
2840
|
];
|
|
2722
2841
|
}
|
|
2723
|
-
ensureOwned() {
|
|
2724
|
-
this.owner ??= new ValueOwner({ scope: this.scope, value: this });
|
|
2725
|
-
}
|
|
2726
|
-
}
|
|
2727
|
-
for (const operator of operators) {
|
|
2728
|
-
if (operator === "=")
|
|
2729
|
-
continue;
|
|
2730
|
-
MemoryEntry.prototype[operator] = function (...args) {
|
|
2731
|
-
// eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/no-unsafe-return
|
|
2732
|
-
return BaseValue.prototype[operator].apply(this, args);
|
|
2733
|
-
};
|
|
2734
2842
|
}
|
|
2735
2843
|
class MemoryMacro extends ObjectValue {
|
|
2736
2844
|
constructor(cell, size) {
|
|
2737
2845
|
super({
|
|
2738
|
-
$get: new MacroFunction((scope, prop) => {
|
|
2739
|
-
if (prop instanceof LiteralValue &&
|
|
2846
|
+
$get: new MacroFunction((scope, out, prop) => {
|
|
2847
|
+
if (prop instanceof LiteralValue && !prop.isNumber())
|
|
2740
2848
|
throw new CompilerError(`Invalid memory object property: "${prop.data}"`);
|
|
2741
|
-
const
|
|
2742
|
-
|
|
2849
|
+
const entry = new MemoryEntry(scope, this, prop);
|
|
2850
|
+
if (out)
|
|
2851
|
+
return entry.eval(scope, out);
|
|
2852
|
+
return [entry, []];
|
|
2743
2853
|
}),
|
|
2744
2854
|
length: size,
|
|
2745
2855
|
size,
|
|
@@ -2753,10 +2863,10 @@ class MemoryMacro extends ObjectValue {
|
|
|
2753
2863
|
class MemoryBuilder extends ObjectValue {
|
|
2754
2864
|
constructor() {
|
|
2755
2865
|
super({
|
|
2756
|
-
$call: new MacroFunction((scope, cell, size = new LiteralValue(64)) => {
|
|
2866
|
+
$call: new MacroFunction((scope, out, cell, size = new LiteralValue(64)) => {
|
|
2757
2867
|
if (!(cell instanceof SenseableValue))
|
|
2758
2868
|
throw new CompilerError("Memory cell must be a senseable value.");
|
|
2759
|
-
if (!(size instanceof LiteralValue &&
|
|
2869
|
+
if (!(size instanceof LiteralValue && size.isNumber()))
|
|
2760
2870
|
throw new CompilerError("The memory size must be a number literal.");
|
|
2761
2871
|
return [new MemoryMacro(cell, size), []];
|
|
2762
2872
|
}),
|
|
@@ -2768,8 +2878,8 @@ const dynamicVars = ["time", "tick"];
|
|
|
2768
2878
|
class NamespaceMacro extends ObjectValue {
|
|
2769
2879
|
constructor({ changeCasing = false } = {}) {
|
|
2770
2880
|
super({
|
|
2771
|
-
$get: new MacroFunction((scope, prop) => {
|
|
2772
|
-
if (!(prop instanceof LiteralValue) ||
|
|
2881
|
+
$get: new MacroFunction((scope, out, prop) => {
|
|
2882
|
+
if (!(prop instanceof LiteralValue) || !prop.isString())
|
|
2773
2883
|
throw new CompilerError("Cannot use dynamic properties on namespace macros");
|
|
2774
2884
|
const symbolName = this.changeCasing
|
|
2775
2885
|
? camelToDashCase(prop.data)
|
|
@@ -2777,57 +2887,37 @@ class NamespaceMacro extends ObjectValue {
|
|
|
2777
2887
|
const mutability = dynamicVars.includes(symbolName)
|
|
2778
2888
|
? EMutability.readonly
|
|
2779
2889
|
: EMutability.constant;
|
|
2780
|
-
const
|
|
2781
|
-
|
|
2782
|
-
value: assign(new StoreValue(scope), {
|
|
2783
|
-
mutability,
|
|
2784
|
-
}),
|
|
2785
|
-
name: `@${symbolName}`,
|
|
2786
|
-
});
|
|
2787
|
-
return [owner.value, []];
|
|
2890
|
+
const result = new StoreValue(`@${symbolName}`, mutability);
|
|
2891
|
+
return [result, []];
|
|
2788
2892
|
}),
|
|
2789
2893
|
});
|
|
2790
2894
|
this.changeCasing = changeCasing;
|
|
2791
2895
|
}
|
|
2792
2896
|
}
|
|
2793
2897
|
class VarsNamespace extends NamespaceMacro {
|
|
2794
|
-
constructor(
|
|
2898
|
+
constructor() {
|
|
2795
2899
|
super();
|
|
2796
2900
|
Object.assign(this.data, {
|
|
2797
|
-
unit: new
|
|
2798
|
-
|
|
2799
|
-
name: "@unit",
|
|
2800
|
-
value: new SenseableValue(scope),
|
|
2801
|
-
}).value,
|
|
2802
|
-
this: new ValueOwner({
|
|
2803
|
-
scope,
|
|
2804
|
-
name: "@this",
|
|
2805
|
-
value: new SenseableValue(scope),
|
|
2806
|
-
}).value,
|
|
2901
|
+
unit: new SenseableValue("@unit"),
|
|
2902
|
+
this: new SenseableValue("@this"),
|
|
2807
2903
|
});
|
|
2808
2904
|
}
|
|
2809
2905
|
}
|
|
2810
2906
|
|
|
2811
2907
|
class GetGlobal extends MacroFunction {
|
|
2812
2908
|
constructor(mutability) {
|
|
2813
|
-
super((scope, name) => {
|
|
2814
|
-
if (!(name instanceof LiteralValue) ||
|
|
2909
|
+
super((scope, out, name) => {
|
|
2910
|
+
if (!(name instanceof LiteralValue) || !name.isString())
|
|
2815
2911
|
throw new CompilerError("The name parameter must be a string literal.");
|
|
2816
|
-
const
|
|
2817
|
-
|
|
2818
|
-
name: name.data,
|
|
2819
|
-
value: assign(new SenseableValue(scope), {
|
|
2820
|
-
mutability,
|
|
2821
|
-
}),
|
|
2822
|
-
});
|
|
2823
|
-
return [owner.value, []];
|
|
2912
|
+
const result = new SenseableValue(name.data, mutability);
|
|
2913
|
+
return [result, []];
|
|
2824
2914
|
});
|
|
2825
2915
|
}
|
|
2826
2916
|
}
|
|
2827
2917
|
|
|
2828
2918
|
class Asm extends MacroFunction {
|
|
2829
2919
|
constructor() {
|
|
2830
|
-
super((scope, stringsArray, ...values) => {
|
|
2920
|
+
super((scope, out, stringsArray, ...values) => {
|
|
2831
2921
|
if (!isTemplateObjectArray(stringsArray))
|
|
2832
2922
|
throw new CompilerError("Expected to receive a template strings array");
|
|
2833
2923
|
const args = [];
|
|
@@ -2905,7 +2995,7 @@ function validateInstructionArgs(args) {
|
|
|
2905
2995
|
function initScope(scope) {
|
|
2906
2996
|
// namespaces
|
|
2907
2997
|
scope.hardSet("ControlKind", new NamespaceMacro());
|
|
2908
|
-
scope.hardSet("Vars", new VarsNamespace(
|
|
2998
|
+
scope.hardSet("Vars", new VarsNamespace());
|
|
2909
2999
|
scope.hardSet("Teams", new NamespaceMacro());
|
|
2910
3000
|
scope.hardSet("Items", new NamespaceMacro({ changeCasing: true }));
|
|
2911
3001
|
scope.hardSet("Liquids", new NamespaceMacro());
|
|
@@ -2917,7 +3007,7 @@ function initScope(scope) {
|
|
|
2917
3007
|
scope.hardSet("getVar", new GetGlobal(EMutability.mutable));
|
|
2918
3008
|
scope.hardSet("concat", new Concat());
|
|
2919
3009
|
scope.hardSet("asm", new Asm());
|
|
2920
|
-
scope.hardSet("Math", new MlogMath(
|
|
3010
|
+
scope.hardSet("Math", new MlogMath());
|
|
2921
3011
|
scope.hardSet("Memory", new MemoryBuilder());
|
|
2922
3012
|
// commands
|
|
2923
3013
|
scope.hardSet("draw", new Draw());
|
|
@@ -3009,11 +3099,11 @@ class Compiler {
|
|
|
3009
3099
|
}
|
|
3010
3100
|
return result;
|
|
3011
3101
|
}
|
|
3012
|
-
handle(scope, node, handler = this.handlers[node.type]) {
|
|
3102
|
+
handle(scope, node, handler = this.handlers[node.type], out) {
|
|
3013
3103
|
try {
|
|
3014
3104
|
if (!handler)
|
|
3015
3105
|
throw new CompilerError("Missing handler for " + node.type);
|
|
3016
|
-
const result = handler(this, scope, node, null);
|
|
3106
|
+
const result = handler(this, scope, node, out, null);
|
|
3017
3107
|
if (this.sourcemap)
|
|
3018
3108
|
return appendSourceLocations(result, node);
|
|
3019
3109
|
return result;
|
|
@@ -3025,21 +3115,11 @@ class Compiler {
|
|
|
3025
3115
|
throw error;
|
|
3026
3116
|
}
|
|
3027
3117
|
}
|
|
3028
|
-
handleEval(scope, node) {
|
|
3029
|
-
const [res, inst] = this.handle(scope, node);
|
|
3118
|
+
handleEval(scope, node, out) {
|
|
3119
|
+
const [res, inst] = this.handle(scope, node, undefined, out);
|
|
3030
3120
|
if (!res)
|
|
3031
3121
|
throw new CompilerError("Cannot eval null", node);
|
|
3032
|
-
const [evaluated, evaluatedInst] = res.eval(scope);
|
|
3033
|
-
const result = [evaluated, [...inst, ...evaluatedInst]];
|
|
3034
|
-
if (this.sourcemap)
|
|
3035
|
-
return appendSourceLocations(result, node);
|
|
3036
|
-
return result;
|
|
3037
|
-
}
|
|
3038
|
-
handleConsume(scope, node) {
|
|
3039
|
-
const [res, inst] = this.handle(scope, node);
|
|
3040
|
-
if (!res)
|
|
3041
|
-
throw new CompilerError("Cannot consume null", node);
|
|
3042
|
-
const [evaluated, evaluatedInst] = res.consume(scope);
|
|
3122
|
+
const [evaluated, evaluatedInst] = res.eval(scope, out);
|
|
3043
3123
|
const result = [evaluated, [...inst, ...evaluatedInst]];
|
|
3044
3124
|
if (this.sourcemap)
|
|
3045
3125
|
return appendSourceLocations(result, node);
|
|
@@ -3051,7 +3131,7 @@ class Compiler {
|
|
|
3051
3131
|
* The usage of this method over a regular loop over an array of nodes
|
|
3052
3132
|
* is only required if the code inside the loop generates
|
|
3053
3133
|
* instructions that are not tracked by the compiler
|
|
3054
|
-
* handler methods ({@link handle}, {@link handleEval}
|
|
3134
|
+
* handler methods ({@link handle}, {@link handleEval}
|
|
3055
3135
|
* and {@link handleMany})
|
|
3056
3136
|
*
|
|
3057
3137
|
*/
|
|
@@ -3064,30 +3144,6 @@ class Compiler {
|
|
|
3064
3144
|
return [null, lines];
|
|
3065
3145
|
}
|
|
3066
3146
|
}
|
|
3067
|
-
/** Removes jump instructions that point right to the next line */
|
|
3068
|
-
function hideRedundantJumps(inst) {
|
|
3069
|
-
for (let i = 0; i < inst.length; i++) {
|
|
3070
|
-
const instruction = inst[i];
|
|
3071
|
-
if (!(instruction instanceof JumpInstruction))
|
|
3072
|
-
continue;
|
|
3073
|
-
const { address } = instruction;
|
|
3074
|
-
let searchIndex = i + 1;
|
|
3075
|
-
let current = inst[searchIndex];
|
|
3076
|
-
while (current.hidden) {
|
|
3077
|
-
if (current instanceof AddressResolver &&
|
|
3078
|
-
current.bonds.includes(address)) {
|
|
3079
|
-
// only gets assigned if `current` is an adress resolver
|
|
3080
|
-
// that resolves the jump adress
|
|
3081
|
-
// and that is also right next to the jump,
|
|
3082
|
-
// without any actual instructions in between
|
|
3083
|
-
instruction.hidden = true;
|
|
3084
|
-
break;
|
|
3085
|
-
}
|
|
3086
|
-
searchIndex++;
|
|
3087
|
-
current = inst[searchIndex];
|
|
3088
|
-
}
|
|
3089
|
-
}
|
|
3090
|
-
}
|
|
3091
3147
|
function* hoistedFunctionNodes(nodes) {
|
|
3092
3148
|
// sorting is O(n long n) while this is just O(n)
|
|
3093
3149
|
// besides, it's better not to modify the node array
|