mlogjs 0.4.6 → 0.4.7
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 +744 -739
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +744 -739
- 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,7 +1298,7 @@ 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);
|
|
@@ -1187,7 +1316,7 @@ const SwitchStatement = (c, scope, node) => {
|
|
|
1187
1316
|
inst.push(bodyLine, ...bodyInst);
|
|
1188
1317
|
continue;
|
|
1189
1318
|
}
|
|
1190
|
-
const [test, testInst] = c.
|
|
1319
|
+
const [test, testInst] = c.handleEval(innerScope, scase.test);
|
|
1191
1320
|
// check if it can be evaluated at compile time
|
|
1192
1321
|
const [comp] = ref["=="](innerScope, test);
|
|
1193
1322
|
// if the constant expression resolves to false
|
|
@@ -1230,7 +1359,7 @@ const SwitchStatement = (c, scope, node) => {
|
|
|
1230
1359
|
|
|
1231
1360
|
const WhileStatement = (c, scope, node) => {
|
|
1232
1361
|
const lines = [];
|
|
1233
|
-
const [test, testLines] = c.
|
|
1362
|
+
const [test, testLines] = c.handleEval(scope, node.test);
|
|
1234
1363
|
const childScope = scope.createScope();
|
|
1235
1364
|
const startLoopAddr = new LiteralValue(null);
|
|
1236
1365
|
const endLoopAddr = new LiteralValue(null);
|
|
@@ -1250,7 +1379,7 @@ const WhileStatement = (c, scope, node) => {
|
|
|
1250
1379
|
};
|
|
1251
1380
|
const DoWhileStatement = (c, scope, node) => {
|
|
1252
1381
|
const lines = [];
|
|
1253
|
-
const [test, testLines] = c.
|
|
1382
|
+
const [test, testLines] = c.handleEval(scope, node.test);
|
|
1254
1383
|
const childScope = scope.createScope();
|
|
1255
1384
|
const startLoopAddr = new LiteralValue(null);
|
|
1256
1385
|
const startLoopLine = new AddressResolver(startLoopAddr).bindContinue(childScope);
|
|
@@ -1279,9 +1408,11 @@ const ForStatement = (c, parentScope, node) => {
|
|
|
1279
1408
|
const scope = parentScope.createScope();
|
|
1280
1409
|
const initInst = node.init ? c.handle(scope, node.init)[1] : [];
|
|
1281
1410
|
const [test, testLines] = node.test
|
|
1282
|
-
? c.
|
|
1411
|
+
? c.handleEval(scope, node.test)
|
|
1283
1412
|
: [new LiteralValue(1), []];
|
|
1284
|
-
const updateLines = node.update
|
|
1413
|
+
const updateLines = node.update
|
|
1414
|
+
? c.handle(scope, node.update, undefined, discardedName)[1]
|
|
1415
|
+
: [];
|
|
1285
1416
|
const startLoopAddr = new LiteralValue(null);
|
|
1286
1417
|
// continue statements jump here to run the loop update lines
|
|
1287
1418
|
const beforeEndAddr = new LiteralValue(null);
|
|
@@ -1309,146 +1440,131 @@ const ForStatement = (c, parentScope, node) => {
|
|
|
1309
1440
|
};
|
|
1310
1441
|
|
|
1311
1442
|
class FunctionValue extends VoidValue {
|
|
1312
|
-
constructor({ scope, params, body, c, }) {
|
|
1443
|
+
constructor({ scope, params, body, c, out, }) {
|
|
1313
1444
|
super();
|
|
1314
1445
|
this.mutability = EMutability.constant;
|
|
1315
1446
|
this.macro = true;
|
|
1316
|
-
this.
|
|
1447
|
+
this.paramValues = [];
|
|
1448
|
+
this.paramNames = [];
|
|
1317
1449
|
this.bundled = false;
|
|
1318
1450
|
this.initialized = false;
|
|
1319
1451
|
this.scope = scope;
|
|
1320
1452
|
this.body = body;
|
|
1321
1453
|
this.c = c;
|
|
1322
1454
|
this.params = params;
|
|
1455
|
+
this.out = out;
|
|
1456
|
+
this.name = extractOutName(out) ?? scope.makeTempName();
|
|
1323
1457
|
}
|
|
1324
1458
|
typeof() {
|
|
1325
1459
|
return [new LiteralValue("function"), []];
|
|
1326
1460
|
}
|
|
1327
|
-
initScope(
|
|
1328
|
-
this.childScope = this.scope.createFunction(name);
|
|
1461
|
+
initScope() {
|
|
1462
|
+
this.childScope = this.scope.createFunction(this.name);
|
|
1329
1463
|
this.childScope.function = this;
|
|
1330
1464
|
for (const id of this.params) {
|
|
1331
1465
|
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);
|
|
1466
|
+
const value = new SenseableValue(name, EMutability.mutable);
|
|
1467
|
+
this.childScope.set(id.name, value);
|
|
1468
|
+
this.paramValues.push(value);
|
|
1469
|
+
this.paramNames.push(id.name);
|
|
1342
1470
|
}
|
|
1343
|
-
this.callSize = this.
|
|
1471
|
+
this.callSize = this.paramValues.length + 2;
|
|
1344
1472
|
}
|
|
1345
1473
|
ensureInit() {
|
|
1346
|
-
if (!this.owner)
|
|
1347
|
-
throw new CompilerError(`Functions must be owned by a variable`);
|
|
1348
1474
|
if (this.initialized)
|
|
1349
1475
|
return;
|
|
1350
1476
|
this.initialized = true;
|
|
1351
|
-
|
|
1352
|
-
this.initScope(name);
|
|
1477
|
+
this.initScope();
|
|
1353
1478
|
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
|
-
});
|
|
1479
|
+
this.temp = new SenseableValue(`${internalPrefix}f${this.name}`, EMutability.mutable);
|
|
1480
|
+
this.ret = new StoreValue(`${internalPrefix}r${this.name}`);
|
|
1366
1481
|
this.inst = [
|
|
1367
1482
|
new AddressResolver(this.addr),
|
|
1368
1483
|
...this.c.handle(this.childScope, this.body)[1],
|
|
1369
1484
|
];
|
|
1370
1485
|
if (!endsWithReturn(this.inst)) {
|
|
1371
|
-
this.inst.push(assign(new SetCounterInstruction(this.ret
|
|
1486
|
+
this.inst.push(assign(new SetCounterInstruction(this.ret), {
|
|
1372
1487
|
intent: EInstIntent.return,
|
|
1373
1488
|
}));
|
|
1374
1489
|
}
|
|
1375
1490
|
}
|
|
1376
1491
|
normalReturn(scope, arg) {
|
|
1377
|
-
const
|
|
1378
|
-
|
|
1492
|
+
const inst = [];
|
|
1493
|
+
if (arg) {
|
|
1494
|
+
const [value, valueInst] = arg.eval(scope, this.temp);
|
|
1495
|
+
const argInst = this.temp["="](scope, value)[1];
|
|
1496
|
+
inst.push(...valueInst);
|
|
1497
|
+
inst.push(...argInst);
|
|
1498
|
+
}
|
|
1499
|
+
const returnInst = assign(new SetCounterInstruction(this.ret), {
|
|
1379
1500
|
intent: EInstIntent.return,
|
|
1380
1501
|
});
|
|
1381
|
-
return [null, [...
|
|
1502
|
+
return [null, [...inst, returnInst]];
|
|
1382
1503
|
}
|
|
1383
1504
|
// TODO: use static analysis to determine if
|
|
1384
1505
|
// we can safely skip the temp value assignment
|
|
1385
|
-
normalCall(scope, args) {
|
|
1506
|
+
normalCall(scope, args, out) {
|
|
1386
1507
|
if (!this.bundled)
|
|
1387
1508
|
this.childScope.inst.push(...this.inst);
|
|
1388
1509
|
this.bundled = true;
|
|
1389
1510
|
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])
|
|
1511
|
+
const temp = SenseableValue.from(scope, out, EMutability.mutable);
|
|
1512
|
+
const inst = this.paramValues
|
|
1513
|
+
.map((param, i) => param["="](scope, args[i])[1])
|
|
1395
1514
|
.reduce((s, c) => s.concat(c), [])
|
|
1396
|
-
.concat(...this.ret
|
|
1515
|
+
.concat(...this.ret["="](scope, callAddressLiteral)[1], new JumpInstruction(this.addr, EJumpKind.Always), new AddressResolver(callAddressLiteral),
|
|
1397
1516
|
// ensures that functions can be called multiple times inside expressions
|
|
1398
|
-
...temp["="](scope, this.temp
|
|
1517
|
+
...temp["="](scope, this.temp)[1]);
|
|
1399
1518
|
return [temp, inst];
|
|
1400
1519
|
}
|
|
1401
1520
|
inlineReturn(scope, arg) {
|
|
1402
|
-
const argInst = arg ? this.inlineTemp.value["="](scope, arg)[1] : [];
|
|
1403
1521
|
if (!this.inlineEnd)
|
|
1404
1522
|
throw new CompilerError("Error during inline attempt: missing inline end adress");
|
|
1523
|
+
const inst = [];
|
|
1524
|
+
if (arg) {
|
|
1525
|
+
const [value, valueInst] = arg.eval(scope, this.inlineTemp);
|
|
1526
|
+
const argInst = this.inlineTemp["="](scope, value)[1];
|
|
1527
|
+
inst.push(...valueInst);
|
|
1528
|
+
inst.push(...argInst);
|
|
1529
|
+
}
|
|
1405
1530
|
const jump = assign(new JumpInstruction(this.inlineEnd, EJumpKind.Always), {
|
|
1406
1531
|
intent: EInstIntent.return,
|
|
1532
|
+
intentSource: this,
|
|
1407
1533
|
});
|
|
1408
|
-
return [null, [...
|
|
1534
|
+
return [null, [...inst, jump]];
|
|
1409
1535
|
}
|
|
1410
|
-
inlineCall(scope, args) {
|
|
1536
|
+
inlineCall(scope, args, out) {
|
|
1411
1537
|
// create return value
|
|
1412
|
-
this.inlineTemp =
|
|
1413
|
-
scope: this.childScope,
|
|
1414
|
-
value: assign(new SenseableValue(scope), {
|
|
1415
|
-
mutability: EMutability.mutable,
|
|
1416
|
-
}),
|
|
1417
|
-
});
|
|
1538
|
+
this.inlineTemp = SenseableValue.from(this.childScope, out, EMutability.mutable);
|
|
1418
1539
|
// make a copy of the function scope
|
|
1419
1540
|
const fnScope = this.childScope.copy();
|
|
1420
1541
|
// 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
|
-
}));
|
|
1542
|
+
this.paramNames.forEach((name, i) => {
|
|
1543
|
+
fnScope.hardSet(name, args[i]);
|
|
1431
1544
|
});
|
|
1432
1545
|
this.inlineEnd = new LiteralValue(null);
|
|
1433
1546
|
this.tryingInline = true;
|
|
1434
1547
|
let inst = this.c.handle(fnScope, this.body)[1];
|
|
1435
1548
|
this.tryingInline = false;
|
|
1436
|
-
const returnIndex = inst.findIndex(i => i.alwaysRuns &&
|
|
1549
|
+
const returnIndex = inst.findIndex(i => i.alwaysRuns &&
|
|
1550
|
+
i.intent === EInstIntent.return &&
|
|
1551
|
+
i.intentSource === this);
|
|
1437
1552
|
if (returnIndex !== -1)
|
|
1438
1553
|
inst = inst.slice(0, returnIndex + 1);
|
|
1439
1554
|
inst.push(new AddressResolver(this.inlineEnd));
|
|
1440
1555
|
// return the function value
|
|
1441
|
-
return [this.inlineTemp
|
|
1556
|
+
return [this.inlineTemp, inst];
|
|
1442
1557
|
}
|
|
1443
|
-
call(scope, args) {
|
|
1558
|
+
call(scope, args, out) {
|
|
1444
1559
|
this.ensureInit();
|
|
1445
|
-
if (args.length !== this.
|
|
1560
|
+
if (args.length !== this.paramNames.length)
|
|
1446
1561
|
throw new CompilerError("Cannot call: argument count not matching.");
|
|
1447
|
-
const inlineCall = this.inlineCall(scope, args);
|
|
1562
|
+
const inlineCall = this.inlineCall(scope, args, out);
|
|
1563
|
+
hideRedundantJumps(inlineCall[1]);
|
|
1448
1564
|
const inlineSize = inlineCall[1].filter(i => !i.hidden).length;
|
|
1449
1565
|
if (this.inline || inlineSize <= this.callSize)
|
|
1450
1566
|
return inlineCall;
|
|
1451
|
-
return this.normalCall(scope, args);
|
|
1567
|
+
return this.normalCall(scope, args, out);
|
|
1452
1568
|
}
|
|
1453
1569
|
return(scope, arg) {
|
|
1454
1570
|
this.ensureInit();
|
|
@@ -1463,8 +1579,11 @@ class FunctionValue extends VoidValue {
|
|
|
1463
1579
|
eval(_scope) {
|
|
1464
1580
|
return [this, []];
|
|
1465
1581
|
}
|
|
1466
|
-
|
|
1467
|
-
|
|
1582
|
+
paramOuts() {
|
|
1583
|
+
this.ensureInit();
|
|
1584
|
+
if (this.inline || this.tryingInline)
|
|
1585
|
+
return;
|
|
1586
|
+
return this.paramValues;
|
|
1468
1587
|
}
|
|
1469
1588
|
}
|
|
1470
1589
|
function endsWithReturn(inst) {
|
|
@@ -1485,7 +1604,7 @@ function endsWithReturn(inst) {
|
|
|
1485
1604
|
return false;
|
|
1486
1605
|
}
|
|
1487
1606
|
|
|
1488
|
-
function handleFunctionNode(c, scope, node) {
|
|
1607
|
+
function handleFunctionNode(c, scope, node, out) {
|
|
1489
1608
|
let { params, body } = node;
|
|
1490
1609
|
if (es.isExpression(body)) {
|
|
1491
1610
|
const { loc } = body;
|
|
@@ -1500,46 +1619,44 @@ function handleFunctionNode(c, scope, node) {
|
|
|
1500
1619
|
params: params,
|
|
1501
1620
|
body,
|
|
1502
1621
|
c,
|
|
1622
|
+
out,
|
|
1503
1623
|
}),
|
|
1504
1624
|
[],
|
|
1505
1625
|
];
|
|
1506
1626
|
}
|
|
1507
|
-
const ArrowFunctionExpression = (c, scope, node) => {
|
|
1508
|
-
return handleFunctionNode(c, scope, node);
|
|
1627
|
+
const ArrowFunctionExpression = (c, scope, node, out) => {
|
|
1628
|
+
return handleFunctionNode(c, scope, node, out);
|
|
1509
1629
|
};
|
|
1510
1630
|
const FunctionDeclaration = (c, scope, node) => {
|
|
1511
1631
|
const identifier = node.id.name;
|
|
1512
1632
|
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), []];
|
|
1633
|
+
const functionIns = handleFunctionNode(c, scope, node, name);
|
|
1634
|
+
scope.set(identifier, functionIns[0]);
|
|
1635
|
+
return functionIns;
|
|
1521
1636
|
};
|
|
1522
1637
|
const FunctionExpression = ArrowFunctionExpression;
|
|
1523
1638
|
const ObjectMethod = ArrowFunctionExpression;
|
|
1524
1639
|
|
|
1525
|
-
const CallExpression = (c, scope, node) => {
|
|
1640
|
+
const CallExpression = (c, scope, node, out) => {
|
|
1526
1641
|
const inst = [];
|
|
1527
|
-
const
|
|
1642
|
+
const [callee, calleeInst] = c.handleEval(scope, node.callee);
|
|
1643
|
+
inst.push(...calleeInst);
|
|
1644
|
+
const paramOuts = callee.paramOuts();
|
|
1645
|
+
const args = node.arguments.map((node, index) => {
|
|
1528
1646
|
// TODO: this might be why t0 did not increment
|
|
1529
1647
|
// TODO: figure out if the previous todo is still relevant
|
|
1530
|
-
const
|
|
1648
|
+
const out = paramOuts?.[index];
|
|
1649
|
+
const [v, i] = c.handleEval(scope, node, out);
|
|
1531
1650
|
inst.push(...i);
|
|
1532
1651
|
return v;
|
|
1533
1652
|
});
|
|
1534
|
-
const [
|
|
1535
|
-
inst.push(...calleeInst);
|
|
1536
|
-
const [callValue, callInst] = callee.call(scope, args);
|
|
1653
|
+
const [callValue, callInst] = callee.call(scope, args, out);
|
|
1537
1654
|
inst.push(...callInst);
|
|
1538
1655
|
return [callValue, inst];
|
|
1539
1656
|
};
|
|
1540
1657
|
const NewExpression = CallExpression;
|
|
1541
|
-
const TaggedTemplateExpression = (c, scope, node) => {
|
|
1542
|
-
const [tag, tagInst] = c.
|
|
1658
|
+
const TaggedTemplateExpression = (c, scope, node, out) => {
|
|
1659
|
+
const [tag, tagInst] = c.handleEval(scope, node.tag);
|
|
1543
1660
|
const template = node.quasi;
|
|
1544
1661
|
const stringsObject = ObjectValue.fromArray(template.quasis.map(quasi => new LiteralValue(quasi.value.cooked ?? "")), {
|
|
1545
1662
|
raw: ObjectValue.fromArray(template.quasis.map(quasi => new LiteralValue(quasi.value.raw))),
|
|
@@ -1547,15 +1664,15 @@ const TaggedTemplateExpression = (c, scope, node) => {
|
|
|
1547
1664
|
const expressions = [];
|
|
1548
1665
|
const expressionInsts = [];
|
|
1549
1666
|
template.expressions.forEach(expression => {
|
|
1550
|
-
const [value, inst] = c.
|
|
1667
|
+
const [value, inst] = c.handleEval(scope, expression);
|
|
1551
1668
|
expressions.push(value);
|
|
1552
1669
|
expressionInsts.push(...inst);
|
|
1553
1670
|
});
|
|
1554
|
-
const [result, resultInst] = tag.call(scope, [stringsObject, ...expressions]);
|
|
1671
|
+
const [result, resultInst] = tag.call(scope, [stringsObject, ...expressions], out);
|
|
1555
1672
|
return [result, [...expressionInsts, ...tagInst, ...resultInst]];
|
|
1556
1673
|
};
|
|
1557
1674
|
|
|
1558
|
-
const ObjectExpression = (c, scope, node) => {
|
|
1675
|
+
const ObjectExpression = (c, scope, node, out) => {
|
|
1559
1676
|
const data = {};
|
|
1560
1677
|
const inst = [];
|
|
1561
1678
|
for (const prop of node.properties) {
|
|
@@ -1575,38 +1692,48 @@ const ObjectExpression = (c, scope, node) => {
|
|
|
1575
1692
|
else {
|
|
1576
1693
|
throw new CompilerError(`Unsupported object key type: ${key.type}`, key);
|
|
1577
1694
|
}
|
|
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
|
-
}
|
|
1695
|
+
const [member, memberInst] = c.handleEval(scope, value, extractDestrucuringOut(out, index));
|
|
1587
1696
|
data[index] = member;
|
|
1588
1697
|
inst.push(...memberInst);
|
|
1589
1698
|
}
|
|
1590
1699
|
return [new ObjectValue(data), inst];
|
|
1591
1700
|
};
|
|
1592
|
-
const ArrayExpression = (c, scope, node) => {
|
|
1701
|
+
const ArrayExpression = (c, scope, node, out) => {
|
|
1593
1702
|
const items = [];
|
|
1594
1703
|
const inst = [];
|
|
1595
|
-
node.elements.forEach(element => {
|
|
1704
|
+
node.elements.forEach((element, i) => {
|
|
1596
1705
|
const [value, valueInst] = element
|
|
1597
|
-
? c.handleEval(scope, element)
|
|
1706
|
+
? c.handleEval(scope, element, extractDestrucuringOut(out, i))
|
|
1598
1707
|
: [undefined, []];
|
|
1599
1708
|
items.push(value);
|
|
1600
1709
|
inst.push(...valueInst);
|
|
1601
1710
|
});
|
|
1602
1711
|
return [ObjectValue.fromArray(items), inst];
|
|
1603
1712
|
};
|
|
1604
|
-
const MemberExpression = (c, scope, node) => {
|
|
1605
|
-
const [obj, objInst] = c.handleConsume(scope, node.object);
|
|
1713
|
+
const MemberExpression = (c, scope, node, out) => {
|
|
1606
1714
|
const [prop, propInst] = node.computed
|
|
1607
|
-
? c.
|
|
1715
|
+
? c.handleEval(scope, node.property)
|
|
1608
1716
|
: [new LiteralValue(node.property.name), []];
|
|
1609
|
-
|
|
1717
|
+
if (!out) {
|
|
1718
|
+
const [obj, objInst] = c.handleEval(scope, node.object);
|
|
1719
|
+
const [got, gotInst] = obj.get(scope, prop);
|
|
1720
|
+
return [got, [...objInst, ...propInst, ...gotInst]];
|
|
1721
|
+
}
|
|
1722
|
+
const temp = SenseableValue.out(scope, out);
|
|
1723
|
+
const objOut = new DestructuringValue(new Map([
|
|
1724
|
+
[
|
|
1725
|
+
prop,
|
|
1726
|
+
{
|
|
1727
|
+
value: temp,
|
|
1728
|
+
// a direct assignment should NOT happen
|
|
1729
|
+
handler: () => {
|
|
1730
|
+
throw new CompilerError("Unexpected destructuring assignment");
|
|
1731
|
+
},
|
|
1732
|
+
},
|
|
1733
|
+
],
|
|
1734
|
+
]));
|
|
1735
|
+
const [obj, objInst] = c.handleEval(scope, node.object, objOut);
|
|
1736
|
+
const [got, gotInst] = obj.get(scope, prop, temp);
|
|
1610
1737
|
return [got, [...objInst, ...propInst, ...gotInst]];
|
|
1611
1738
|
};
|
|
1612
1739
|
const ArrayPattern = (c, scope, node) => {
|
|
@@ -1616,11 +1743,21 @@ const ArrayPattern = (c, scope, node) => {
|
|
|
1616
1743
|
const element = node.elements[i];
|
|
1617
1744
|
if (!element)
|
|
1618
1745
|
continue;
|
|
1619
|
-
const valueInst = c.handle(scope, element);
|
|
1620
|
-
if (!
|
|
1746
|
+
const [value, valueInst] = c.handle(scope, element);
|
|
1747
|
+
if (!value)
|
|
1621
1748
|
throw new CompilerError("Destructuring element must resolve to a value", element);
|
|
1622
|
-
inst.push(...valueInst
|
|
1623
|
-
members.set(new LiteralValue(i),
|
|
1749
|
+
inst.push(...valueInst);
|
|
1750
|
+
members.set(new LiteralValue(i), {
|
|
1751
|
+
value,
|
|
1752
|
+
handler(get) {
|
|
1753
|
+
return c.handle(scope, element, () => {
|
|
1754
|
+
const inst = get();
|
|
1755
|
+
// assigns the output to the target value
|
|
1756
|
+
inst[1].push(...value["="](scope, inst[0])[1]);
|
|
1757
|
+
return inst;
|
|
1758
|
+
});
|
|
1759
|
+
},
|
|
1760
|
+
});
|
|
1624
1761
|
}
|
|
1625
1762
|
return [new DestructuringValue(members), inst];
|
|
1626
1763
|
};
|
|
@@ -1635,13 +1772,23 @@ const ObjectPattern = (c, scope, node) => {
|
|
|
1635
1772
|
const { key } = prop;
|
|
1636
1773
|
const keyInst = !prop.computed && key.type === "Identifier"
|
|
1637
1774
|
? [new LiteralValue(key.name), []]
|
|
1638
|
-
: c.
|
|
1775
|
+
: c.handleEval(scope, prop.key);
|
|
1639
1776
|
inst.push(...keyInst[1]);
|
|
1640
|
-
const valueInst = c.handle(scope, prop.value);
|
|
1641
|
-
if (!
|
|
1777
|
+
const [value, valueInst] = c.handle(scope, prop.value);
|
|
1778
|
+
if (!value)
|
|
1642
1779
|
throw new CompilerError("Destructuring member must resolve to a value", prop.value);
|
|
1643
|
-
inst.push(...valueInst
|
|
1644
|
-
members.set(keyInst[0],
|
|
1780
|
+
inst.push(...valueInst);
|
|
1781
|
+
members.set(keyInst[0], {
|
|
1782
|
+
value,
|
|
1783
|
+
handler(get) {
|
|
1784
|
+
return c.handle(scope, prop, () => {
|
|
1785
|
+
const inst = get();
|
|
1786
|
+
// assigns the output to the target value
|
|
1787
|
+
inst[1].push(...value["="](scope, inst[0])[1]);
|
|
1788
|
+
return inst;
|
|
1789
|
+
});
|
|
1790
|
+
},
|
|
1791
|
+
});
|
|
1645
1792
|
}
|
|
1646
1793
|
return [new DestructuringValue(members), inst];
|
|
1647
1794
|
};
|
|
@@ -1686,7 +1833,7 @@ const TSEnumDeclaration = (c, scope, node) => {
|
|
|
1686
1833
|
: [new LiteralValue(counter)];
|
|
1687
1834
|
if (!(value instanceof LiteralValue))
|
|
1688
1835
|
throw new CompilerError("Enum members must contain literal values", member);
|
|
1689
|
-
if (
|
|
1836
|
+
if (value.isNumber()) {
|
|
1690
1837
|
lastType = "number";
|
|
1691
1838
|
counter = value.data + 1;
|
|
1692
1839
|
}
|
|
@@ -1696,13 +1843,9 @@ const TSEnumDeclaration = (c, scope, node) => {
|
|
|
1696
1843
|
const name = member.id.type === "Identifier" ? member.id.name : member.id.value;
|
|
1697
1844
|
data[name] = value;
|
|
1698
1845
|
}
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
constant: true,
|
|
1703
|
-
identifier: node.id.name,
|
|
1704
|
-
name: nodeName(node, !c.compactNames && node.id.name),
|
|
1705
|
-
}));
|
|
1846
|
+
const value = new ObjectValue(data);
|
|
1847
|
+
value.name = nodeName(node, !c.compactNames && node.id.name);
|
|
1848
|
+
scope.set(node.id.name, value);
|
|
1706
1849
|
return [null, []];
|
|
1707
1850
|
};
|
|
1708
1851
|
const TSNonNullExpression = (c, scope, node) => {
|
|
@@ -1766,15 +1909,12 @@ class MacroFunction extends VoidValue {
|
|
|
1766
1909
|
this.mutability = EMutability.constant;
|
|
1767
1910
|
this.fn = fn;
|
|
1768
1911
|
}
|
|
1769
|
-
call(scope, args) {
|
|
1770
|
-
return this.fn.apply(this, [scope, ...args]);
|
|
1912
|
+
call(scope, args, out) {
|
|
1913
|
+
return this.fn.apply(this, [scope, out, ...args]);
|
|
1771
1914
|
}
|
|
1772
1915
|
eval(_scope) {
|
|
1773
1916
|
return [this, []];
|
|
1774
1917
|
}
|
|
1775
|
-
consume(_scope) {
|
|
1776
|
-
return [this, []];
|
|
1777
|
-
}
|
|
1778
1918
|
}
|
|
1779
1919
|
|
|
1780
1920
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
@@ -1798,49 +1938,39 @@ const mathOperations = {
|
|
|
1798
1938
|
sqrt: a => Math.sqrt(a),
|
|
1799
1939
|
rand: null,
|
|
1800
1940
|
};
|
|
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) {
|
|
1941
|
+
function createMacroMathOperations() {
|
|
1812
1942
|
const macroMathOperations = {
|
|
1813
|
-
PI:
|
|
1814
|
-
E:
|
|
1815
|
-
degToRad:
|
|
1816
|
-
radToDeg:
|
|
1943
|
+
PI: new StoreValue("@pi", EMutability.constant),
|
|
1944
|
+
E: new StoreValue("@e", EMutability.constant),
|
|
1945
|
+
degToRad: new StoreValue("@degToRad", EMutability.constant),
|
|
1946
|
+
radToDeg: new StoreValue("@radToDeg", EMutability.constant),
|
|
1817
1947
|
};
|
|
1818
1948
|
for (const key in mathOperations) {
|
|
1819
1949
|
const fn = mathOperations[key];
|
|
1820
|
-
macroMathOperations[key] = new MacroFunction((scope, a, b) => {
|
|
1950
|
+
macroMathOperations[key] = new MacroFunction((scope, out, a, b) => {
|
|
1821
1951
|
if (b) {
|
|
1822
1952
|
if (fn && a instanceof LiteralValue && b instanceof LiteralValue) {
|
|
1823
|
-
if (
|
|
1953
|
+
if (!a.isNumber() || !b.isNumber())
|
|
1824
1954
|
throw new CompilerError("Cannot do math operation with non-numerical literals.");
|
|
1825
1955
|
return [new LiteralValue(fn(a.num, b.num)), []];
|
|
1826
1956
|
}
|
|
1827
|
-
const temp =
|
|
1957
|
+
const temp = StoreValue.from(scope, out);
|
|
1828
1958
|
return [temp, [new OperationInstruction(key, temp, a, b)]];
|
|
1829
1959
|
}
|
|
1830
1960
|
if (fn && a instanceof LiteralValue) {
|
|
1831
|
-
if (
|
|
1961
|
+
if (!a.isNumber())
|
|
1832
1962
|
throw new CompilerError("Cannot do math operation with non-numerical literal.");
|
|
1833
1963
|
return [new LiteralValue(fn(a.num)), []];
|
|
1834
1964
|
}
|
|
1835
|
-
const temp =
|
|
1965
|
+
const temp = StoreValue.from(scope, out);
|
|
1836
1966
|
return [temp, [new OperationInstruction(key, temp, a, b)]];
|
|
1837
1967
|
});
|
|
1838
1968
|
}
|
|
1839
1969
|
return macroMathOperations;
|
|
1840
1970
|
}
|
|
1841
1971
|
class MlogMath extends ObjectValue {
|
|
1842
|
-
constructor(
|
|
1843
|
-
super(createMacroMathOperations(
|
|
1972
|
+
constructor() {
|
|
1973
|
+
super(createMacroMathOperations());
|
|
1844
1974
|
}
|
|
1845
1975
|
}
|
|
1846
1976
|
// op angle result a b
|
|
@@ -1848,7 +1978,7 @@ class MlogMath extends ObjectValue {
|
|
|
1848
1978
|
|
|
1849
1979
|
class Concat extends MacroFunction {
|
|
1850
1980
|
constructor() {
|
|
1851
|
-
super((scope, ...values) => {
|
|
1981
|
+
super((scope, out, ...values) => {
|
|
1852
1982
|
for (const value of values)
|
|
1853
1983
|
if (!(value instanceof LiteralValue))
|
|
1854
1984
|
throw new CompilerError("Concat arguments must all be literal values.");
|
|
@@ -1860,66 +1990,19 @@ class Concat extends MacroFunction {
|
|
|
1860
1990
|
}
|
|
1861
1991
|
}
|
|
1862
1992
|
|
|
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
1993
|
/** Used to create namespaces that contain multiple methods that map into instructions */
|
|
1911
1994
|
function createOverloadNamespace({ overloads, handler, }) {
|
|
1912
1995
|
const result = {};
|
|
1913
1996
|
for (const key in overloads) {
|
|
1914
1997
|
const { named, args } = overloads[key];
|
|
1915
1998
|
if (named) {
|
|
1916
|
-
result[key] = new MacroFunction((scope, options) => {
|
|
1999
|
+
result[key] = new MacroFunction((scope, out, options) => {
|
|
1917
2000
|
assertIsObjectMacro(options, named);
|
|
1918
|
-
return handler(scope, key, ...assertObjectFields(options, args));
|
|
2001
|
+
return handler(scope, key, out, ...assertObjectFields(options, args));
|
|
1919
2002
|
});
|
|
1920
2003
|
continue;
|
|
1921
2004
|
}
|
|
1922
|
-
result[key] = new MacroFunction((scope, ...params) => {
|
|
2005
|
+
result[key] = new MacroFunction((scope, out, ...params) => {
|
|
1923
2006
|
const handlerParams = [];
|
|
1924
2007
|
// validate the paramters
|
|
1925
2008
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -1936,7 +2019,7 @@ function createOverloadNamespace({ overloads, handler, }) {
|
|
|
1936
2019
|
arg.validate?.(param);
|
|
1937
2020
|
handlerParams.push(param);
|
|
1938
2021
|
}
|
|
1939
|
-
return handler(scope, key, ...handlerParams);
|
|
2022
|
+
return handler(scope, key, out, ...handlerParams);
|
|
1940
2023
|
});
|
|
1941
2024
|
}
|
|
1942
2025
|
return result;
|
|
@@ -1981,7 +2064,7 @@ class Draw extends ObjectValue {
|
|
|
1981
2064
|
args: ["x", "y", "image", "size", "rotation"],
|
|
1982
2065
|
},
|
|
1983
2066
|
},
|
|
1984
|
-
handler(scope, overload, ...args) {
|
|
2067
|
+
handler(scope, overload, out, ...args) {
|
|
1985
2068
|
return [null, [new InstructionBase("draw", overload, ...args)]];
|
|
1986
2069
|
},
|
|
1987
2070
|
});
|
|
@@ -1991,7 +2074,7 @@ class Draw extends ObjectValue {
|
|
|
1991
2074
|
|
|
1992
2075
|
class Print extends MacroFunction {
|
|
1993
2076
|
constructor() {
|
|
1994
|
-
super((scope, ...values) => {
|
|
2077
|
+
super((scope, out, ...values) => {
|
|
1995
2078
|
const [first] = values;
|
|
1996
2079
|
const inst = [];
|
|
1997
2080
|
if (!isTemplateObjectArray(first)) {
|
|
@@ -2020,7 +2103,7 @@ class Print extends MacroFunction {
|
|
|
2020
2103
|
const defaultTargetName$1 = "message1";
|
|
2021
2104
|
class PrintFlush extends MacroFunction {
|
|
2022
2105
|
constructor() {
|
|
2023
|
-
super((scope, target) => {
|
|
2106
|
+
super((scope, out, target) => {
|
|
2024
2107
|
if (!target)
|
|
2025
2108
|
return [null, [new InstructionBase("printflush", defaultTargetName$1)]];
|
|
2026
2109
|
if (!(target instanceof SenseableValue))
|
|
@@ -2033,7 +2116,7 @@ class PrintFlush extends MacroFunction {
|
|
|
2033
2116
|
const defaultTargetName = "display1";
|
|
2034
2117
|
class DrawFlush extends MacroFunction {
|
|
2035
2118
|
constructor() {
|
|
2036
|
-
super((scope, target) => {
|
|
2119
|
+
super((scope, out, target) => {
|
|
2037
2120
|
if (!target)
|
|
2038
2121
|
return [null, [new InstructionBase("drawflush", defaultTargetName)]];
|
|
2039
2122
|
if (!(target instanceof SenseableValue))
|
|
@@ -2052,10 +2135,10 @@ class GetBlock extends ObjectValue {
|
|
|
2052
2135
|
block: { args: ["x", "y"] },
|
|
2053
2136
|
building: { args: ["x", "y"] },
|
|
2054
2137
|
},
|
|
2055
|
-
handler(scope, overload, x, y) {
|
|
2138
|
+
handler(scope, overload, out, x, y) {
|
|
2056
2139
|
const output = overload === "building"
|
|
2057
|
-
?
|
|
2058
|
-
:
|
|
2140
|
+
? SenseableValue.from(scope, out, EMutability.constant)
|
|
2141
|
+
: StoreValue.from(scope, out, EMutability.constant);
|
|
2059
2142
|
return [
|
|
2060
2143
|
output,
|
|
2061
2144
|
[new InstructionBase("getblock", overload, output, x, y)],
|
|
@@ -2068,11 +2151,11 @@ class GetBlock extends ObjectValue {
|
|
|
2068
2151
|
|
|
2069
2152
|
class GetLink extends MacroFunction {
|
|
2070
2153
|
constructor() {
|
|
2071
|
-
super((scope, index) => {
|
|
2154
|
+
super((scope, out, index) => {
|
|
2072
2155
|
if (!(index instanceof StoreValue) &&
|
|
2073
|
-
(!(index instanceof LiteralValue) ||
|
|
2156
|
+
(!(index instanceof LiteralValue) || !index.isNumber()))
|
|
2074
2157
|
throw new CompilerError("The getlink index must be a number literal or a store");
|
|
2075
|
-
const outBuild =
|
|
2158
|
+
const outBuild = SenseableValue.from(scope, out, EMutability.constant);
|
|
2076
2159
|
return [outBuild, [new InstructionBase("getlink", outBuild, index)]];
|
|
2077
2160
|
});
|
|
2078
2161
|
}
|
|
@@ -2094,11 +2177,10 @@ class Control extends ObjectValue {
|
|
|
2094
2177
|
config: { args: ["building", "value"] },
|
|
2095
2178
|
color: { args: ["building", "r", "g", "b"] },
|
|
2096
2179
|
},
|
|
2097
|
-
handler(scope, overload, ...args) {
|
|
2180
|
+
handler(scope, overload, out, ...args) {
|
|
2098
2181
|
if (overload !== "color")
|
|
2099
2182
|
return [null, [new InstructionBase("control", overload, ...args)]];
|
|
2100
|
-
const temp =
|
|
2101
|
-
temp.ensureOwned();
|
|
2183
|
+
const temp = StoreValue.from(scope);
|
|
2102
2184
|
// this "patch" is here for compatibility reasons
|
|
2103
2185
|
// I don't want to add another breaking change
|
|
2104
2186
|
// TODO: figure out if this patch should be removed later
|
|
@@ -2134,14 +2216,14 @@ const validRadarSorts = [
|
|
|
2134
2216
|
];
|
|
2135
2217
|
class Radar extends MacroFunction {
|
|
2136
2218
|
constructor() {
|
|
2137
|
-
super((scope, options) => {
|
|
2219
|
+
super((scope, out, options) => {
|
|
2138
2220
|
assertIsObjectMacro(options, "The radar options");
|
|
2139
2221
|
const { building, filters, order, sort } = options.data;
|
|
2140
2222
|
if (!(building instanceof SenseableValue))
|
|
2141
2223
|
throw new CompilerError("The building must a senseable value");
|
|
2142
2224
|
assertIsArrayMacro(filters, "filters");
|
|
2143
2225
|
const { length } = filters.data;
|
|
2144
|
-
if (!(length instanceof LiteralValue) ||
|
|
2226
|
+
if (!(length instanceof LiteralValue) || !length.isNumber())
|
|
2145
2227
|
throw new CompilerError("The length of an array macro must be");
|
|
2146
2228
|
if (length.data !== 3)
|
|
2147
2229
|
throw new CompilerError("The filters array must have 3 items");
|
|
@@ -2153,7 +2235,7 @@ class Radar extends MacroFunction {
|
|
|
2153
2235
|
if (!(order instanceof LiteralValue || order instanceof StoreValue))
|
|
2154
2236
|
throw new CompilerError("The radar order must be a literal or a store");
|
|
2155
2237
|
assertLiteralOneOf(sort, validRadarSorts, "The radar sort");
|
|
2156
|
-
const outUnit =
|
|
2238
|
+
const outUnit = SenseableValue.from(scope, out, EMutability.constant);
|
|
2157
2239
|
return [
|
|
2158
2240
|
outUnit,
|
|
2159
2241
|
[
|
|
@@ -2166,14 +2248,12 @@ class Radar extends MacroFunction {
|
|
|
2166
2248
|
|
|
2167
2249
|
class Sensor extends MacroFunction {
|
|
2168
2250
|
constructor() {
|
|
2169
|
-
super((scope, property, target) => {
|
|
2251
|
+
super((scope, out, property, target) => {
|
|
2170
2252
|
if (!(property instanceof StoreValue))
|
|
2171
2253
|
throw new CompilerError("The sensor property must be a store");
|
|
2172
2254
|
if (!(target instanceof SenseableValue))
|
|
2173
2255
|
throw new CompilerError("The sensor target must be a senseable value");
|
|
2174
|
-
const temp =
|
|
2175
|
-
mutability: EMutability.readonly,
|
|
2176
|
-
});
|
|
2256
|
+
const temp = SenseableValue.from(scope, out, EMutability.readonly);
|
|
2177
2257
|
return [temp, [new InstructionBase("sensor", temp, target, property)]];
|
|
2178
2258
|
});
|
|
2179
2259
|
}
|
|
@@ -2181,9 +2261,9 @@ class Sensor extends MacroFunction {
|
|
|
2181
2261
|
|
|
2182
2262
|
class Wait extends MacroFunction {
|
|
2183
2263
|
constructor() {
|
|
2184
|
-
super((scope, seconds) => {
|
|
2264
|
+
super((scope, out, seconds) => {
|
|
2185
2265
|
if (!(seconds instanceof StoreValue) &&
|
|
2186
|
-
!(seconds instanceof LiteralValue &&
|
|
2266
|
+
!(seconds instanceof LiteralValue && seconds.isNumber()))
|
|
2187
2267
|
throw new CompilerError("The wait seconds must be either a number literal or a store");
|
|
2188
2268
|
return [null, [new InstructionBase("wait", seconds)]];
|
|
2189
2269
|
});
|
|
@@ -2199,8 +2279,8 @@ class Lookup extends ObjectValue {
|
|
|
2199
2279
|
item: { args: ["index"] },
|
|
2200
2280
|
liquid: { args: ["index"] },
|
|
2201
2281
|
},
|
|
2202
|
-
handler(scope, overload, index) {
|
|
2203
|
-
const output =
|
|
2282
|
+
handler(scope, overload, out, index) {
|
|
2283
|
+
const output = StoreValue.from(scope, out);
|
|
2204
2284
|
return [
|
|
2205
2285
|
output,
|
|
2206
2286
|
[new InstructionBase("lookup", overload, output, index)],
|
|
@@ -2213,15 +2293,15 @@ class Lookup extends ObjectValue {
|
|
|
2213
2293
|
|
|
2214
2294
|
class PackColor extends MacroFunction {
|
|
2215
2295
|
constructor() {
|
|
2216
|
-
super((scope, ...args) => {
|
|
2296
|
+
super((scope, out, ...args) => {
|
|
2217
2297
|
if (args.length !== 4) {
|
|
2218
2298
|
throw new CompilerError(`Expected 4 arguments, received ${args.length}`);
|
|
2219
2299
|
}
|
|
2220
2300
|
if (!args.every((value) => value instanceof StoreValue ||
|
|
2221
|
-
(value instanceof LiteralValue &&
|
|
2301
|
+
(value instanceof LiteralValue && value.isNumber()))) {
|
|
2222
2302
|
throw new CompilerError("packColor arguments must be either stores or number literals");
|
|
2223
2303
|
}
|
|
2224
|
-
const output =
|
|
2304
|
+
const output = StoreValue.from(scope, out);
|
|
2225
2305
|
return [output, [new InstructionBase("packcolor", output, ...args)]];
|
|
2226
2306
|
});
|
|
2227
2307
|
}
|
|
@@ -2244,7 +2324,7 @@ class SetBlock extends ObjectValue {
|
|
|
2244
2324
|
args: ["x", "y", "to", "team", { key: "rotation", default: "0" }],
|
|
2245
2325
|
},
|
|
2246
2326
|
},
|
|
2247
|
-
handler(scope, overload, x, y, to, team, rotation) {
|
|
2327
|
+
handler(scope, overload, out, x, y, to, team, rotation) {
|
|
2248
2328
|
return [
|
|
2249
2329
|
null,
|
|
2250
2330
|
[
|
|
@@ -2259,14 +2339,14 @@ class SetBlock extends ObjectValue {
|
|
|
2259
2339
|
|
|
2260
2340
|
class SpawnUnit extends MacroFunction {
|
|
2261
2341
|
constructor() {
|
|
2262
|
-
super((scope, options) => {
|
|
2342
|
+
super((scope, out, options) => {
|
|
2263
2343
|
assertIsObjectMacro(options, "The options");
|
|
2264
2344
|
const { type, x, y, team, rotation } = options.data;
|
|
2265
2345
|
assertIsRuntimeValue(type, "type");
|
|
2266
2346
|
assertIsRuntimeValue(x, "x");
|
|
2267
2347
|
assertIsRuntimeValue(y, "y");
|
|
2268
2348
|
assertIsRuntimeValue(team, "team");
|
|
2269
|
-
const output =
|
|
2349
|
+
const output = SenseableValue.from(scope, out, EMutability.constant);
|
|
2270
2350
|
return [
|
|
2271
2351
|
output,
|
|
2272
2352
|
[
|
|
@@ -2285,7 +2365,7 @@ class Stop extends MacroFunction {
|
|
|
2285
2365
|
|
|
2286
2366
|
class UnitBind extends MacroFunction {
|
|
2287
2367
|
constructor() {
|
|
2288
|
-
super((scope, type) => {
|
|
2368
|
+
super((scope, out, type) => {
|
|
2289
2369
|
if (!(type instanceof StoreValue))
|
|
2290
2370
|
throw new CompilerError("The unitBind type must be a store");
|
|
2291
2371
|
return [null, [new InstructionBase("ubind", type)]];
|
|
@@ -2324,21 +2404,20 @@ class UnitControl extends ObjectValue {
|
|
|
2324
2404
|
args: ["x", "y", "radius"],
|
|
2325
2405
|
},
|
|
2326
2406
|
},
|
|
2327
|
-
handler(scope, overload, ...args) {
|
|
2407
|
+
handler(scope, overload, out, ...args) {
|
|
2328
2408
|
let result = null;
|
|
2329
2409
|
let extraArgs = [];
|
|
2330
2410
|
switch (overload) {
|
|
2331
2411
|
case "getBlock": {
|
|
2332
|
-
const outType =
|
|
2333
|
-
const outBuilding =
|
|
2412
|
+
const outType = StoreValue.from(scope, extractDestrucuringOut(out, 0));
|
|
2413
|
+
const outBuilding = SenseableValue.from(scope, extractDestrucuringOut(out, 1));
|
|
2334
2414
|
result = ObjectValue.fromArray([outType, outBuilding]);
|
|
2335
2415
|
extraArgs = [outType, outBuilding, new LiteralValue(0)];
|
|
2336
2416
|
break;
|
|
2337
2417
|
}
|
|
2338
2418
|
case "within": {
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
extraArgs = [temp, new LiteralValue(0)];
|
|
2419
|
+
result = StoreValue.from(scope, out);
|
|
2420
|
+
extraArgs = [result, new LiteralValue(0)];
|
|
2342
2421
|
break;
|
|
2343
2422
|
}
|
|
2344
2423
|
}
|
|
@@ -2354,12 +2433,12 @@ class UnitControl extends ObjectValue {
|
|
|
2354
2433
|
|
|
2355
2434
|
class UnitRadar extends MacroFunction {
|
|
2356
2435
|
constructor() {
|
|
2357
|
-
super((scope, options) => {
|
|
2436
|
+
super((scope, out, options) => {
|
|
2358
2437
|
assertIsObjectMacro(options, "The radar options");
|
|
2359
2438
|
const { filters, order, sort } = options.data;
|
|
2360
2439
|
assertIsArrayMacro(filters, "filters");
|
|
2361
2440
|
const { length } = filters.data;
|
|
2362
|
-
if (!(length instanceof LiteralValue) ||
|
|
2441
|
+
if (!(length instanceof LiteralValue) || !length.isNumber())
|
|
2363
2442
|
throw new CompilerError("The length of an array macro must be");
|
|
2364
2443
|
if (length.data !== 3)
|
|
2365
2444
|
throw new CompilerError("The filters array must have 3 items");
|
|
@@ -2371,7 +2450,7 @@ class UnitRadar extends MacroFunction {
|
|
|
2371
2450
|
if (!(order instanceof LiteralValue || order instanceof StoreValue))
|
|
2372
2451
|
throw new CompilerError("The radar order must be a literal or a store");
|
|
2373
2452
|
assertLiteralOneOf(sort, validRadarSorts, "The radar sort");
|
|
2374
|
-
const outUnit =
|
|
2453
|
+
const outUnit = SenseableValue.from(scope, out, EMutability.constant);
|
|
2375
2454
|
return [
|
|
2376
2455
|
outUnit,
|
|
2377
2456
|
[
|
|
@@ -2409,11 +2488,11 @@ class UnitLocate extends ObjectValue {
|
|
|
2409
2488
|
args: [],
|
|
2410
2489
|
},
|
|
2411
2490
|
},
|
|
2412
|
-
handler(scope, overload, ...args) {
|
|
2413
|
-
const outFound =
|
|
2414
|
-
const outX =
|
|
2415
|
-
const outY =
|
|
2416
|
-
const outBuilding =
|
|
2491
|
+
handler(scope, overload, out, ...args) {
|
|
2492
|
+
const outFound = StoreValue.from(scope, extractDestrucuringOut(out, 0));
|
|
2493
|
+
const outX = StoreValue.from(scope, extractDestrucuringOut(out, 1));
|
|
2494
|
+
const outY = StoreValue.from(scope, extractDestrucuringOut(out, 2));
|
|
2495
|
+
const outBuilding = SenseableValue.from(scope, overload === "ore" ? discardedName : extractDestrucuringOut(out, 3));
|
|
2417
2496
|
const outArgs = [outX, outY, outFound, outBuilding];
|
|
2418
2497
|
let inputArgs = [];
|
|
2419
2498
|
switch (overload) {
|
|
@@ -2476,7 +2555,7 @@ class ApplyStatus extends ObjectValue {
|
|
|
2476
2555
|
},
|
|
2477
2556
|
clear: { args: ["status", "unit"] },
|
|
2478
2557
|
},
|
|
2479
|
-
handler(scope, overload, effect, unit, duration) {
|
|
2558
|
+
handler(scope, overload, out, effect, unit, duration) {
|
|
2480
2559
|
if (!(effect instanceof LiteralValue))
|
|
2481
2560
|
throw new CompilerError("The status effect must be a string literal");
|
|
2482
2561
|
assertLiteralOneOf(effect, validEffects, "The status effect");
|
|
@@ -2494,7 +2573,7 @@ class ApplyStatus extends ObjectValue {
|
|
|
2494
2573
|
|
|
2495
2574
|
class SpawnWave extends MacroFunction {
|
|
2496
2575
|
constructor() {
|
|
2497
|
-
super((scope, natural, x, y) => {
|
|
2576
|
+
super((scope, out, natural, x, y) => {
|
|
2498
2577
|
if (!(natural instanceof LiteralValue) ||
|
|
2499
2578
|
(natural.data !== 1 && natural.data !== 0))
|
|
2500
2579
|
throw new CompilerError("The 'natural' argument must be a boolean literal");
|
|
@@ -2535,7 +2614,7 @@ class SetRule extends ObjectValue {
|
|
|
2535
2614
|
rtsMinWeight: { args: ["team", "value"] },
|
|
2536
2615
|
rtsMinSquad: { args: ["team", "value"] },
|
|
2537
2616
|
},
|
|
2538
|
-
handler(scope, overload, ...args) {
|
|
2617
|
+
handler(scope, overload, out, ...args) {
|
|
2539
2618
|
const params = ["10", "0", "0", "100", "100"];
|
|
2540
2619
|
switch (overload) {
|
|
2541
2620
|
case "mapArea": {
|
|
@@ -2578,7 +2657,7 @@ class FlushMessage extends ObjectValue {
|
|
|
2578
2657
|
announce: { args: ["duration"] },
|
|
2579
2658
|
toast: { args: ["duration"] },
|
|
2580
2659
|
},
|
|
2581
|
-
handler(scope, overload, duration) {
|
|
2660
|
+
handler(scope, overload, out, duration) {
|
|
2582
2661
|
return [null, [new InstructionBase("message", overload, duration)]];
|
|
2583
2662
|
},
|
|
2584
2663
|
});
|
|
@@ -2594,7 +2673,7 @@ class Cutscene extends ObjectValue {
|
|
|
2594
2673
|
zoom: { args: ["level"] },
|
|
2595
2674
|
stop: { args: [] },
|
|
2596
2675
|
},
|
|
2597
|
-
handler(scope, overload, ...args) {
|
|
2676
|
+
handler(scope, overload, out, ...args) {
|
|
2598
2677
|
const params = Object.assign(["100", "100", "0.06", "0"], args);
|
|
2599
2678
|
return [null, [new InstructionBase("cutscene", overload, ...params)]];
|
|
2600
2679
|
},
|
|
@@ -2605,7 +2684,7 @@ class Cutscene extends ObjectValue {
|
|
|
2605
2684
|
|
|
2606
2685
|
class Explosion extends MacroFunction {
|
|
2607
2686
|
constructor() {
|
|
2608
|
-
super((scope, options) => {
|
|
2687
|
+
super((scope, out, options) => {
|
|
2609
2688
|
assertIsObjectMacro(options, "The options");
|
|
2610
2689
|
const params = assertObjectFields(options, [
|
|
2611
2690
|
"team",
|
|
@@ -2624,7 +2703,7 @@ class Explosion extends MacroFunction {
|
|
|
2624
2703
|
|
|
2625
2704
|
class SetRate extends MacroFunction {
|
|
2626
2705
|
constructor() {
|
|
2627
|
-
super((scope, ipt) => [null, [new InstructionBase("setrate", ipt)]]);
|
|
2706
|
+
super((scope, out, ipt) => [null, [new InstructionBase("setrate", ipt)]]);
|
|
2628
2707
|
}
|
|
2629
2708
|
}
|
|
2630
2709
|
|
|
@@ -2641,8 +2720,8 @@ class Fetch extends ObjectValue {
|
|
|
2641
2720
|
build: { args: ["team", "index", "block"] },
|
|
2642
2721
|
buildCount: { args: ["team", "block"] },
|
|
2643
2722
|
},
|
|
2644
|
-
handler(scope, overload, team, ...rest) {
|
|
2645
|
-
const output =
|
|
2723
|
+
handler(scope, overload, out, team, ...rest) {
|
|
2724
|
+
const output = SenseableValue.from(scope, out, EMutability.constant);
|
|
2646
2725
|
const params = ["0", "@conveyor"];
|
|
2647
2726
|
if (overload === "buildCount") {
|
|
2648
2727
|
params[1] = rest[0];
|
|
@@ -2662,8 +2741,8 @@ class Fetch extends ObjectValue {
|
|
|
2662
2741
|
|
|
2663
2742
|
class GetFlag extends MacroFunction {
|
|
2664
2743
|
constructor() {
|
|
2665
|
-
super((scope, flag) => {
|
|
2666
|
-
const result =
|
|
2744
|
+
super((scope, out, flag) => {
|
|
2745
|
+
const result = StoreValue.from(scope, out);
|
|
2667
2746
|
return [result, [new InstructionBase("getflag", result, flag)]];
|
|
2668
2747
|
});
|
|
2669
2748
|
}
|
|
@@ -2671,7 +2750,7 @@ class GetFlag extends MacroFunction {
|
|
|
2671
2750
|
|
|
2672
2751
|
class SetFlag extends MacroFunction {
|
|
2673
2752
|
constructor() {
|
|
2674
|
-
super((scope, flag, value) => {
|
|
2753
|
+
super((scope, out, flag, value) => {
|
|
2675
2754
|
if (!flag)
|
|
2676
2755
|
throw new CompilerError("Missing flag argument");
|
|
2677
2756
|
if (!value)
|
|
@@ -2690,28 +2769,17 @@ class MemoryEntry extends BaseValue {
|
|
|
2690
2769
|
this.macro = true;
|
|
2691
2770
|
this.store = null;
|
|
2692
2771
|
}
|
|
2693
|
-
eval(scope) {
|
|
2772
|
+
eval(scope, out) {
|
|
2694
2773
|
if (this.store)
|
|
2695
2774
|
return [this.store, []];
|
|
2696
|
-
const temp =
|
|
2775
|
+
const temp = (this.store = StoreValue.from(scope, out));
|
|
2697
2776
|
return [
|
|
2698
2777
|
temp,
|
|
2699
2778
|
[new InstructionBase("read", temp, this.mem.cell, this.prop)],
|
|
2700
2779
|
];
|
|
2701
2780
|
}
|
|
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
2781
|
"="(scope, value) {
|
|
2714
|
-
const [data, dataInst] = value.
|
|
2782
|
+
const [data, dataInst] = value.eval(scope);
|
|
2715
2783
|
return [
|
|
2716
2784
|
data,
|
|
2717
2785
|
[
|
|
@@ -2720,26 +2788,17 @@ class MemoryEntry extends BaseValue {
|
|
|
2720
2788
|
],
|
|
2721
2789
|
];
|
|
2722
2790
|
}
|
|
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
2791
|
}
|
|
2735
2792
|
class MemoryMacro extends ObjectValue {
|
|
2736
2793
|
constructor(cell, size) {
|
|
2737
2794
|
super({
|
|
2738
|
-
$get: new MacroFunction((scope, prop) => {
|
|
2739
|
-
if (prop instanceof LiteralValue &&
|
|
2795
|
+
$get: new MacroFunction((scope, out, prop) => {
|
|
2796
|
+
if (prop instanceof LiteralValue && !prop.isNumber())
|
|
2740
2797
|
throw new CompilerError(`Invalid memory object property: "${prop.data}"`);
|
|
2741
|
-
const
|
|
2742
|
-
|
|
2798
|
+
const entry = new MemoryEntry(scope, this, prop);
|
|
2799
|
+
if (out)
|
|
2800
|
+
return entry.eval(scope, out);
|
|
2801
|
+
return [entry, []];
|
|
2743
2802
|
}),
|
|
2744
2803
|
length: size,
|
|
2745
2804
|
size,
|
|
@@ -2753,10 +2812,10 @@ class MemoryMacro extends ObjectValue {
|
|
|
2753
2812
|
class MemoryBuilder extends ObjectValue {
|
|
2754
2813
|
constructor() {
|
|
2755
2814
|
super({
|
|
2756
|
-
$call: new MacroFunction((scope, cell, size = new LiteralValue(64)) => {
|
|
2815
|
+
$call: new MacroFunction((scope, out, cell, size = new LiteralValue(64)) => {
|
|
2757
2816
|
if (!(cell instanceof SenseableValue))
|
|
2758
2817
|
throw new CompilerError("Memory cell must be a senseable value.");
|
|
2759
|
-
if (!(size instanceof LiteralValue &&
|
|
2818
|
+
if (!(size instanceof LiteralValue && size.isNumber()))
|
|
2760
2819
|
throw new CompilerError("The memory size must be a number literal.");
|
|
2761
2820
|
return [new MemoryMacro(cell, size), []];
|
|
2762
2821
|
}),
|
|
@@ -2768,8 +2827,8 @@ const dynamicVars = ["time", "tick"];
|
|
|
2768
2827
|
class NamespaceMacro extends ObjectValue {
|
|
2769
2828
|
constructor({ changeCasing = false } = {}) {
|
|
2770
2829
|
super({
|
|
2771
|
-
$get: new MacroFunction((scope, prop) => {
|
|
2772
|
-
if (!(prop instanceof LiteralValue) ||
|
|
2830
|
+
$get: new MacroFunction((scope, out, prop) => {
|
|
2831
|
+
if (!(prop instanceof LiteralValue) || !prop.isString())
|
|
2773
2832
|
throw new CompilerError("Cannot use dynamic properties on namespace macros");
|
|
2774
2833
|
const symbolName = this.changeCasing
|
|
2775
2834
|
? camelToDashCase(prop.data)
|
|
@@ -2777,57 +2836,37 @@ class NamespaceMacro extends ObjectValue {
|
|
|
2777
2836
|
const mutability = dynamicVars.includes(symbolName)
|
|
2778
2837
|
? EMutability.readonly
|
|
2779
2838
|
: EMutability.constant;
|
|
2780
|
-
const
|
|
2781
|
-
|
|
2782
|
-
value: assign(new StoreValue(scope), {
|
|
2783
|
-
mutability,
|
|
2784
|
-
}),
|
|
2785
|
-
name: `@${symbolName}`,
|
|
2786
|
-
});
|
|
2787
|
-
return [owner.value, []];
|
|
2839
|
+
const result = new StoreValue(`@${symbolName}`, mutability);
|
|
2840
|
+
return [result, []];
|
|
2788
2841
|
}),
|
|
2789
2842
|
});
|
|
2790
2843
|
this.changeCasing = changeCasing;
|
|
2791
2844
|
}
|
|
2792
2845
|
}
|
|
2793
2846
|
class VarsNamespace extends NamespaceMacro {
|
|
2794
|
-
constructor(
|
|
2847
|
+
constructor() {
|
|
2795
2848
|
super();
|
|
2796
2849
|
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,
|
|
2850
|
+
unit: new SenseableValue("@unit"),
|
|
2851
|
+
this: new SenseableValue("@this"),
|
|
2807
2852
|
});
|
|
2808
2853
|
}
|
|
2809
2854
|
}
|
|
2810
2855
|
|
|
2811
2856
|
class GetGlobal extends MacroFunction {
|
|
2812
2857
|
constructor(mutability) {
|
|
2813
|
-
super((scope, name) => {
|
|
2814
|
-
if (!(name instanceof LiteralValue) ||
|
|
2858
|
+
super((scope, out, name) => {
|
|
2859
|
+
if (!(name instanceof LiteralValue) || !name.isString())
|
|
2815
2860
|
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, []];
|
|
2861
|
+
const result = new SenseableValue(name.data, mutability);
|
|
2862
|
+
return [result, []];
|
|
2824
2863
|
});
|
|
2825
2864
|
}
|
|
2826
2865
|
}
|
|
2827
2866
|
|
|
2828
2867
|
class Asm extends MacroFunction {
|
|
2829
2868
|
constructor() {
|
|
2830
|
-
super((scope, stringsArray, ...values) => {
|
|
2869
|
+
super((scope, out, stringsArray, ...values) => {
|
|
2831
2870
|
if (!isTemplateObjectArray(stringsArray))
|
|
2832
2871
|
throw new CompilerError("Expected to receive a template strings array");
|
|
2833
2872
|
const args = [];
|
|
@@ -2905,7 +2944,7 @@ function validateInstructionArgs(args) {
|
|
|
2905
2944
|
function initScope(scope) {
|
|
2906
2945
|
// namespaces
|
|
2907
2946
|
scope.hardSet("ControlKind", new NamespaceMacro());
|
|
2908
|
-
scope.hardSet("Vars", new VarsNamespace(
|
|
2947
|
+
scope.hardSet("Vars", new VarsNamespace());
|
|
2909
2948
|
scope.hardSet("Teams", new NamespaceMacro());
|
|
2910
2949
|
scope.hardSet("Items", new NamespaceMacro({ changeCasing: true }));
|
|
2911
2950
|
scope.hardSet("Liquids", new NamespaceMacro());
|
|
@@ -2917,7 +2956,7 @@ function initScope(scope) {
|
|
|
2917
2956
|
scope.hardSet("getVar", new GetGlobal(EMutability.mutable));
|
|
2918
2957
|
scope.hardSet("concat", new Concat());
|
|
2919
2958
|
scope.hardSet("asm", new Asm());
|
|
2920
|
-
scope.hardSet("Math", new MlogMath(
|
|
2959
|
+
scope.hardSet("Math", new MlogMath());
|
|
2921
2960
|
scope.hardSet("Memory", new MemoryBuilder());
|
|
2922
2961
|
// commands
|
|
2923
2962
|
scope.hardSet("draw", new Draw());
|
|
@@ -3009,11 +3048,11 @@ class Compiler {
|
|
|
3009
3048
|
}
|
|
3010
3049
|
return result;
|
|
3011
3050
|
}
|
|
3012
|
-
handle(scope, node, handler = this.handlers[node.type]) {
|
|
3051
|
+
handle(scope, node, handler = this.handlers[node.type], out) {
|
|
3013
3052
|
try {
|
|
3014
3053
|
if (!handler)
|
|
3015
3054
|
throw new CompilerError("Missing handler for " + node.type);
|
|
3016
|
-
const result = handler(this, scope, node, null);
|
|
3055
|
+
const result = handler(this, scope, node, out, null);
|
|
3017
3056
|
if (this.sourcemap)
|
|
3018
3057
|
return appendSourceLocations(result, node);
|
|
3019
3058
|
return result;
|
|
@@ -3025,21 +3064,11 @@ class Compiler {
|
|
|
3025
3064
|
throw error;
|
|
3026
3065
|
}
|
|
3027
3066
|
}
|
|
3028
|
-
handleEval(scope, node) {
|
|
3029
|
-
const [res, inst] = this.handle(scope, node);
|
|
3067
|
+
handleEval(scope, node, out) {
|
|
3068
|
+
const [res, inst] = this.handle(scope, node, undefined, out);
|
|
3030
3069
|
if (!res)
|
|
3031
3070
|
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);
|
|
3071
|
+
const [evaluated, evaluatedInst] = res.eval(scope, out);
|
|
3043
3072
|
const result = [evaluated, [...inst, ...evaluatedInst]];
|
|
3044
3073
|
if (this.sourcemap)
|
|
3045
3074
|
return appendSourceLocations(result, node);
|
|
@@ -3051,7 +3080,7 @@ class Compiler {
|
|
|
3051
3080
|
* The usage of this method over a regular loop over an array of nodes
|
|
3052
3081
|
* is only required if the code inside the loop generates
|
|
3053
3082
|
* instructions that are not tracked by the compiler
|
|
3054
|
-
* handler methods ({@link handle}, {@link handleEval}
|
|
3083
|
+
* handler methods ({@link handle}, {@link handleEval}
|
|
3055
3084
|
* and {@link handleMany})
|
|
3056
3085
|
*
|
|
3057
3086
|
*/
|
|
@@ -3064,30 +3093,6 @@ class Compiler {
|
|
|
3064
3093
|
return [null, lines];
|
|
3065
3094
|
}
|
|
3066
3095
|
}
|
|
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
3096
|
function* hoistedFunctionNodes(nodes) {
|
|
3092
3097
|
// sorting is O(n long n) while this is just O(n)
|
|
3093
3098
|
// besides, it's better not to modify the node array
|