mlogjs 0.4.5 → 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 +748 -736
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +748 -736
- 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,139 +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
|
|
1505
|
+
// we can safely skip the temp value assignment
|
|
1506
|
+
normalCall(scope, args, out) {
|
|
1384
1507
|
if (!this.bundled)
|
|
1385
1508
|
this.childScope.inst.push(...this.inst);
|
|
1386
1509
|
this.bundled = true;
|
|
1387
1510
|
const callAddressLiteral = new LiteralValue(null);
|
|
1388
|
-
const
|
|
1389
|
-
|
|
1511
|
+
const temp = SenseableValue.from(scope, out, EMutability.mutable);
|
|
1512
|
+
const inst = this.paramValues
|
|
1513
|
+
.map((param, i) => param["="](scope, args[i])[1])
|
|
1390
1514
|
.reduce((s, c) => s.concat(c), [])
|
|
1391
|
-
.concat(...this.ret
|
|
1392
|
-
|
|
1515
|
+
.concat(...this.ret["="](scope, callAddressLiteral)[1], new JumpInstruction(this.addr, EJumpKind.Always), new AddressResolver(callAddressLiteral),
|
|
1516
|
+
// ensures that functions can be called multiple times inside expressions
|
|
1517
|
+
...temp["="](scope, this.temp)[1]);
|
|
1518
|
+
return [temp, inst];
|
|
1393
1519
|
}
|
|
1394
1520
|
inlineReturn(scope, arg) {
|
|
1395
|
-
const argInst = arg ? this.inlineTemp.value["="](scope, arg)[1] : [];
|
|
1396
1521
|
if (!this.inlineEnd)
|
|
1397
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
|
+
}
|
|
1398
1530
|
const jump = assign(new JumpInstruction(this.inlineEnd, EJumpKind.Always), {
|
|
1399
1531
|
intent: EInstIntent.return,
|
|
1532
|
+
intentSource: this,
|
|
1400
1533
|
});
|
|
1401
|
-
return [null, [...
|
|
1534
|
+
return [null, [...inst, jump]];
|
|
1402
1535
|
}
|
|
1403
|
-
inlineCall(scope, args) {
|
|
1536
|
+
inlineCall(scope, args, out) {
|
|
1404
1537
|
// create return value
|
|
1405
|
-
this.inlineTemp =
|
|
1406
|
-
scope: this.childScope,
|
|
1407
|
-
value: assign(new SenseableValue(scope), {
|
|
1408
|
-
mutability: EMutability.mutable,
|
|
1409
|
-
}),
|
|
1410
|
-
});
|
|
1538
|
+
this.inlineTemp = SenseableValue.from(this.childScope, out, EMutability.mutable);
|
|
1411
1539
|
// make a copy of the function scope
|
|
1412
1540
|
const fnScope = this.childScope.copy();
|
|
1413
1541
|
// hard set variables within the function scope
|
|
1414
|
-
this.
|
|
1415
|
-
|
|
1416
|
-
if (!value.owner?.persistent)
|
|
1417
|
-
owner.own(value);
|
|
1418
|
-
fnScope.hardSet(new ValueOwner({
|
|
1419
|
-
scope: fnScope,
|
|
1420
|
-
value,
|
|
1421
|
-
identifier: owner.identifier,
|
|
1422
|
-
name: owner.name,
|
|
1423
|
-
}));
|
|
1542
|
+
this.paramNames.forEach((name, i) => {
|
|
1543
|
+
fnScope.hardSet(name, args[i]);
|
|
1424
1544
|
});
|
|
1425
1545
|
this.inlineEnd = new LiteralValue(null);
|
|
1426
1546
|
this.tryingInline = true;
|
|
1427
1547
|
let inst = this.c.handle(fnScope, this.body)[1];
|
|
1428
1548
|
this.tryingInline = false;
|
|
1429
|
-
const returnIndex = inst.findIndex(i => i.alwaysRuns &&
|
|
1549
|
+
const returnIndex = inst.findIndex(i => i.alwaysRuns &&
|
|
1550
|
+
i.intent === EInstIntent.return &&
|
|
1551
|
+
i.intentSource === this);
|
|
1430
1552
|
if (returnIndex !== -1)
|
|
1431
1553
|
inst = inst.slice(0, returnIndex + 1);
|
|
1432
1554
|
inst.push(new AddressResolver(this.inlineEnd));
|
|
1433
1555
|
// return the function value
|
|
1434
|
-
return [this.inlineTemp
|
|
1556
|
+
return [this.inlineTemp, inst];
|
|
1435
1557
|
}
|
|
1436
|
-
call(scope, args) {
|
|
1558
|
+
call(scope, args, out) {
|
|
1437
1559
|
this.ensureInit();
|
|
1438
|
-
if (args.length !== this.
|
|
1560
|
+
if (args.length !== this.paramNames.length)
|
|
1439
1561
|
throw new CompilerError("Cannot call: argument count not matching.");
|
|
1440
|
-
const inlineCall = this.inlineCall(scope, args);
|
|
1562
|
+
const inlineCall = this.inlineCall(scope, args, out);
|
|
1563
|
+
hideRedundantJumps(inlineCall[1]);
|
|
1441
1564
|
const inlineSize = inlineCall[1].filter(i => !i.hidden).length;
|
|
1442
1565
|
if (this.inline || inlineSize <= this.callSize)
|
|
1443
1566
|
return inlineCall;
|
|
1444
|
-
return this.normalCall(scope, args);
|
|
1567
|
+
return this.normalCall(scope, args, out);
|
|
1445
1568
|
}
|
|
1446
1569
|
return(scope, arg) {
|
|
1447
1570
|
this.ensureInit();
|
|
@@ -1456,8 +1579,11 @@ class FunctionValue extends VoidValue {
|
|
|
1456
1579
|
eval(_scope) {
|
|
1457
1580
|
return [this, []];
|
|
1458
1581
|
}
|
|
1459
|
-
|
|
1460
|
-
|
|
1582
|
+
paramOuts() {
|
|
1583
|
+
this.ensureInit();
|
|
1584
|
+
if (this.inline || this.tryingInline)
|
|
1585
|
+
return;
|
|
1586
|
+
return this.paramValues;
|
|
1461
1587
|
}
|
|
1462
1588
|
}
|
|
1463
1589
|
function endsWithReturn(inst) {
|
|
@@ -1478,7 +1604,7 @@ function endsWithReturn(inst) {
|
|
|
1478
1604
|
return false;
|
|
1479
1605
|
}
|
|
1480
1606
|
|
|
1481
|
-
function handleFunctionNode(c, scope, node) {
|
|
1607
|
+
function handleFunctionNode(c, scope, node, out) {
|
|
1482
1608
|
let { params, body } = node;
|
|
1483
1609
|
if (es.isExpression(body)) {
|
|
1484
1610
|
const { loc } = body;
|
|
@@ -1493,46 +1619,44 @@ function handleFunctionNode(c, scope, node) {
|
|
|
1493
1619
|
params: params,
|
|
1494
1620
|
body,
|
|
1495
1621
|
c,
|
|
1622
|
+
out,
|
|
1496
1623
|
}),
|
|
1497
1624
|
[],
|
|
1498
1625
|
];
|
|
1499
1626
|
}
|
|
1500
|
-
const ArrowFunctionExpression = (c, scope, node) => {
|
|
1501
|
-
return handleFunctionNode(c, scope, node);
|
|
1627
|
+
const ArrowFunctionExpression = (c, scope, node, out) => {
|
|
1628
|
+
return handleFunctionNode(c, scope, node, out);
|
|
1502
1629
|
};
|
|
1503
1630
|
const FunctionDeclaration = (c, scope, node) => {
|
|
1504
1631
|
const identifier = node.id.name;
|
|
1505
1632
|
const name = nodeName(node, !c.compactNames && identifier);
|
|
1506
|
-
const functionIns = handleFunctionNode(c, scope, node);
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
value: functionIns[0],
|
|
1510
|
-
identifier,
|
|
1511
|
-
name,
|
|
1512
|
-
});
|
|
1513
|
-
return [scope.set(owner), []];
|
|
1633
|
+
const functionIns = handleFunctionNode(c, scope, node, name);
|
|
1634
|
+
scope.set(identifier, functionIns[0]);
|
|
1635
|
+
return functionIns;
|
|
1514
1636
|
};
|
|
1515
1637
|
const FunctionExpression = ArrowFunctionExpression;
|
|
1516
1638
|
const ObjectMethod = ArrowFunctionExpression;
|
|
1517
1639
|
|
|
1518
|
-
const CallExpression = (c, scope, node) => {
|
|
1640
|
+
const CallExpression = (c, scope, node, out) => {
|
|
1519
1641
|
const inst = [];
|
|
1520
|
-
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) => {
|
|
1521
1646
|
// TODO: this might be why t0 did not increment
|
|
1522
1647
|
// TODO: figure out if the previous todo is still relevant
|
|
1523
|
-
const
|
|
1648
|
+
const out = paramOuts?.[index];
|
|
1649
|
+
const [v, i] = c.handleEval(scope, node, out);
|
|
1524
1650
|
inst.push(...i);
|
|
1525
1651
|
return v;
|
|
1526
1652
|
});
|
|
1527
|
-
const [
|
|
1528
|
-
inst.push(...calleeInst);
|
|
1529
|
-
const [callValue, callInst] = callee.call(scope, args);
|
|
1653
|
+
const [callValue, callInst] = callee.call(scope, args, out);
|
|
1530
1654
|
inst.push(...callInst);
|
|
1531
1655
|
return [callValue, inst];
|
|
1532
1656
|
};
|
|
1533
1657
|
const NewExpression = CallExpression;
|
|
1534
|
-
const TaggedTemplateExpression = (c, scope, node) => {
|
|
1535
|
-
const [tag, tagInst] = c.
|
|
1658
|
+
const TaggedTemplateExpression = (c, scope, node, out) => {
|
|
1659
|
+
const [tag, tagInst] = c.handleEval(scope, node.tag);
|
|
1536
1660
|
const template = node.quasi;
|
|
1537
1661
|
const stringsObject = ObjectValue.fromArray(template.quasis.map(quasi => new LiteralValue(quasi.value.cooked ?? "")), {
|
|
1538
1662
|
raw: ObjectValue.fromArray(template.quasis.map(quasi => new LiteralValue(quasi.value.raw))),
|
|
@@ -1540,15 +1664,15 @@ const TaggedTemplateExpression = (c, scope, node) => {
|
|
|
1540
1664
|
const expressions = [];
|
|
1541
1665
|
const expressionInsts = [];
|
|
1542
1666
|
template.expressions.forEach(expression => {
|
|
1543
|
-
const [value, inst] = c.
|
|
1667
|
+
const [value, inst] = c.handleEval(scope, expression);
|
|
1544
1668
|
expressions.push(value);
|
|
1545
1669
|
expressionInsts.push(...inst);
|
|
1546
1670
|
});
|
|
1547
|
-
const [result, resultInst] = tag.call(scope, [stringsObject, ...expressions]);
|
|
1671
|
+
const [result, resultInst] = tag.call(scope, [stringsObject, ...expressions], out);
|
|
1548
1672
|
return [result, [...expressionInsts, ...tagInst, ...resultInst]];
|
|
1549
1673
|
};
|
|
1550
1674
|
|
|
1551
|
-
const ObjectExpression = (c, scope, node) => {
|
|
1675
|
+
const ObjectExpression = (c, scope, node, out) => {
|
|
1552
1676
|
const data = {};
|
|
1553
1677
|
const inst = [];
|
|
1554
1678
|
for (const prop of node.properties) {
|
|
@@ -1568,38 +1692,48 @@ const ObjectExpression = (c, scope, node) => {
|
|
|
1568
1692
|
else {
|
|
1569
1693
|
throw new CompilerError(`Unsupported object key type: ${key.type}`, key);
|
|
1570
1694
|
}
|
|
1571
|
-
const [member, memberInst] = c.handleEval(scope, value);
|
|
1572
|
-
// TODO: use an approach that can be defined by each type
|
|
1573
|
-
if (!member.owner && !(member instanceof LiteralValue)) {
|
|
1574
|
-
new ValueOwner({
|
|
1575
|
-
scope,
|
|
1576
|
-
constant: true,
|
|
1577
|
-
value: member,
|
|
1578
|
-
});
|
|
1579
|
-
}
|
|
1695
|
+
const [member, memberInst] = c.handleEval(scope, value, extractDestrucuringOut(out, index));
|
|
1580
1696
|
data[index] = member;
|
|
1581
1697
|
inst.push(...memberInst);
|
|
1582
1698
|
}
|
|
1583
1699
|
return [new ObjectValue(data), inst];
|
|
1584
1700
|
};
|
|
1585
|
-
const ArrayExpression = (c, scope, node) => {
|
|
1701
|
+
const ArrayExpression = (c, scope, node, out) => {
|
|
1586
1702
|
const items = [];
|
|
1587
1703
|
const inst = [];
|
|
1588
|
-
node.elements.forEach(element => {
|
|
1704
|
+
node.elements.forEach((element, i) => {
|
|
1589
1705
|
const [value, valueInst] = element
|
|
1590
|
-
? c.handleEval(scope, element)
|
|
1706
|
+
? c.handleEval(scope, element, extractDestrucuringOut(out, i))
|
|
1591
1707
|
: [undefined, []];
|
|
1592
1708
|
items.push(value);
|
|
1593
1709
|
inst.push(...valueInst);
|
|
1594
1710
|
});
|
|
1595
1711
|
return [ObjectValue.fromArray(items), inst];
|
|
1596
1712
|
};
|
|
1597
|
-
const MemberExpression = (c, scope, node) => {
|
|
1598
|
-
const [obj, objInst] = c.handleConsume(scope, node.object);
|
|
1713
|
+
const MemberExpression = (c, scope, node, out) => {
|
|
1599
1714
|
const [prop, propInst] = node.computed
|
|
1600
|
-
? c.
|
|
1715
|
+
? c.handleEval(scope, node.property)
|
|
1601
1716
|
: [new LiteralValue(node.property.name), []];
|
|
1602
|
-
|
|
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);
|
|
1603
1737
|
return [got, [...objInst, ...propInst, ...gotInst]];
|
|
1604
1738
|
};
|
|
1605
1739
|
const ArrayPattern = (c, scope, node) => {
|
|
@@ -1609,11 +1743,21 @@ const ArrayPattern = (c, scope, node) => {
|
|
|
1609
1743
|
const element = node.elements[i];
|
|
1610
1744
|
if (!element)
|
|
1611
1745
|
continue;
|
|
1612
|
-
const valueInst = c.handle(scope, element);
|
|
1613
|
-
if (!
|
|
1746
|
+
const [value, valueInst] = c.handle(scope, element);
|
|
1747
|
+
if (!value)
|
|
1614
1748
|
throw new CompilerError("Destructuring element must resolve to a value", element);
|
|
1615
|
-
inst.push(...valueInst
|
|
1616
|
-
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
|
+
});
|
|
1617
1761
|
}
|
|
1618
1762
|
return [new DestructuringValue(members), inst];
|
|
1619
1763
|
};
|
|
@@ -1628,13 +1772,23 @@ const ObjectPattern = (c, scope, node) => {
|
|
|
1628
1772
|
const { key } = prop;
|
|
1629
1773
|
const keyInst = !prop.computed && key.type === "Identifier"
|
|
1630
1774
|
? [new LiteralValue(key.name), []]
|
|
1631
|
-
: c.
|
|
1775
|
+
: c.handleEval(scope, prop.key);
|
|
1632
1776
|
inst.push(...keyInst[1]);
|
|
1633
|
-
const valueInst = c.handle(scope, prop.value);
|
|
1634
|
-
if (!
|
|
1777
|
+
const [value, valueInst] = c.handle(scope, prop.value);
|
|
1778
|
+
if (!value)
|
|
1635
1779
|
throw new CompilerError("Destructuring member must resolve to a value", prop.value);
|
|
1636
|
-
inst.push(...valueInst
|
|
1637
|
-
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
|
+
});
|
|
1638
1792
|
}
|
|
1639
1793
|
return [new DestructuringValue(members), inst];
|
|
1640
1794
|
};
|
|
@@ -1679,7 +1833,7 @@ const TSEnumDeclaration = (c, scope, node) => {
|
|
|
1679
1833
|
: [new LiteralValue(counter)];
|
|
1680
1834
|
if (!(value instanceof LiteralValue))
|
|
1681
1835
|
throw new CompilerError("Enum members must contain literal values", member);
|
|
1682
|
-
if (
|
|
1836
|
+
if (value.isNumber()) {
|
|
1683
1837
|
lastType = "number";
|
|
1684
1838
|
counter = value.data + 1;
|
|
1685
1839
|
}
|
|
@@ -1689,13 +1843,9 @@ const TSEnumDeclaration = (c, scope, node) => {
|
|
|
1689
1843
|
const name = member.id.type === "Identifier" ? member.id.name : member.id.value;
|
|
1690
1844
|
data[name] = value;
|
|
1691
1845
|
}
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
constant: true,
|
|
1696
|
-
identifier: node.id.name,
|
|
1697
|
-
name: nodeName(node, !c.compactNames && node.id.name),
|
|
1698
|
-
}));
|
|
1846
|
+
const value = new ObjectValue(data);
|
|
1847
|
+
value.name = nodeName(node, !c.compactNames && node.id.name);
|
|
1848
|
+
scope.set(node.id.name, value);
|
|
1699
1849
|
return [null, []];
|
|
1700
1850
|
};
|
|
1701
1851
|
const TSNonNullExpression = (c, scope, node) => {
|
|
@@ -1759,15 +1909,12 @@ class MacroFunction extends VoidValue {
|
|
|
1759
1909
|
this.mutability = EMutability.constant;
|
|
1760
1910
|
this.fn = fn;
|
|
1761
1911
|
}
|
|
1762
|
-
call(scope, args) {
|
|
1763
|
-
return this.fn.apply(this, [scope, ...args]);
|
|
1912
|
+
call(scope, args, out) {
|
|
1913
|
+
return this.fn.apply(this, [scope, out, ...args]);
|
|
1764
1914
|
}
|
|
1765
1915
|
eval(_scope) {
|
|
1766
1916
|
return [this, []];
|
|
1767
1917
|
}
|
|
1768
|
-
consume(_scope) {
|
|
1769
|
-
return [this, []];
|
|
1770
|
-
}
|
|
1771
1918
|
}
|
|
1772
1919
|
|
|
1773
1920
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
@@ -1791,49 +1938,39 @@ const mathOperations = {
|
|
|
1791
1938
|
sqrt: a => Math.sqrt(a),
|
|
1792
1939
|
rand: null,
|
|
1793
1940
|
};
|
|
1794
|
-
function
|
|
1795
|
-
return new ValueOwner({
|
|
1796
|
-
scope,
|
|
1797
|
-
constant: true,
|
|
1798
|
-
name,
|
|
1799
|
-
value: assign(new StoreValue(scope), {
|
|
1800
|
-
mutability: EMutability.constant,
|
|
1801
|
-
}),
|
|
1802
|
-
}).value;
|
|
1803
|
-
}
|
|
1804
|
-
function createMacroMathOperations(scope) {
|
|
1941
|
+
function createMacroMathOperations() {
|
|
1805
1942
|
const macroMathOperations = {
|
|
1806
|
-
PI:
|
|
1807
|
-
E:
|
|
1808
|
-
degToRad:
|
|
1809
|
-
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),
|
|
1810
1947
|
};
|
|
1811
1948
|
for (const key in mathOperations) {
|
|
1812
1949
|
const fn = mathOperations[key];
|
|
1813
|
-
macroMathOperations[key] = new MacroFunction((scope, a, b) => {
|
|
1950
|
+
macroMathOperations[key] = new MacroFunction((scope, out, a, b) => {
|
|
1814
1951
|
if (b) {
|
|
1815
1952
|
if (fn && a instanceof LiteralValue && b instanceof LiteralValue) {
|
|
1816
|
-
if (
|
|
1953
|
+
if (!a.isNumber() || !b.isNumber())
|
|
1817
1954
|
throw new CompilerError("Cannot do math operation with non-numerical literals.");
|
|
1818
1955
|
return [new LiteralValue(fn(a.num, b.num)), []];
|
|
1819
1956
|
}
|
|
1820
|
-
const temp =
|
|
1957
|
+
const temp = StoreValue.from(scope, out);
|
|
1821
1958
|
return [temp, [new OperationInstruction(key, temp, a, b)]];
|
|
1822
1959
|
}
|
|
1823
1960
|
if (fn && a instanceof LiteralValue) {
|
|
1824
|
-
if (
|
|
1961
|
+
if (!a.isNumber())
|
|
1825
1962
|
throw new CompilerError("Cannot do math operation with non-numerical literal.");
|
|
1826
1963
|
return [new LiteralValue(fn(a.num)), []];
|
|
1827
1964
|
}
|
|
1828
|
-
const temp =
|
|
1965
|
+
const temp = StoreValue.from(scope, out);
|
|
1829
1966
|
return [temp, [new OperationInstruction(key, temp, a, b)]];
|
|
1830
1967
|
});
|
|
1831
1968
|
}
|
|
1832
1969
|
return macroMathOperations;
|
|
1833
1970
|
}
|
|
1834
1971
|
class MlogMath extends ObjectValue {
|
|
1835
|
-
constructor(
|
|
1836
|
-
super(createMacroMathOperations(
|
|
1972
|
+
constructor() {
|
|
1973
|
+
super(createMacroMathOperations());
|
|
1837
1974
|
}
|
|
1838
1975
|
}
|
|
1839
1976
|
// op angle result a b
|
|
@@ -1841,7 +1978,7 @@ class MlogMath extends ObjectValue {
|
|
|
1841
1978
|
|
|
1842
1979
|
class Concat extends MacroFunction {
|
|
1843
1980
|
constructor() {
|
|
1844
|
-
super((scope, ...values) => {
|
|
1981
|
+
super((scope, out, ...values) => {
|
|
1845
1982
|
for (const value of values)
|
|
1846
1983
|
if (!(value instanceof LiteralValue))
|
|
1847
1984
|
throw new CompilerError("Concat arguments must all be literal values.");
|
|
@@ -1853,66 +1990,19 @@ class Concat extends MacroFunction {
|
|
|
1853
1990
|
}
|
|
1854
1991
|
}
|
|
1855
1992
|
|
|
1856
|
-
/** Asserts that `value` is a `LiteralValue` that contains a string */
|
|
1857
|
-
function assertStringLiteral(value, name) {
|
|
1858
|
-
if (!(value instanceof LiteralValue) || typeof value.data !== "string")
|
|
1859
|
-
throw new CompilerError(`${name} must be a string literal`);
|
|
1860
|
-
}
|
|
1861
|
-
/** Asserts that `value` is a string literal that is part of a given set of `members`*/
|
|
1862
|
-
function assertLiteralOneOf(value, members, name) {
|
|
1863
|
-
assertStringLiteral(value, name);
|
|
1864
|
-
if (!members.includes(value.data))
|
|
1865
|
-
throw new CompilerError(`${name} must be one of: ${members
|
|
1866
|
-
.slice(0, -1)
|
|
1867
|
-
.map(member => `"${member}"`)
|
|
1868
|
-
.join(", ")} or "${members[members.length - 1]}"`);
|
|
1869
|
-
}
|
|
1870
|
-
/** Asserts that `value` is either a store or a literal */
|
|
1871
|
-
function assertIsRuntimeValue(value, name) {
|
|
1872
|
-
if (!(value instanceof LiteralValue) && !(value instanceof StoreValue))
|
|
1873
|
-
throw new CompilerError(`${name} must be a valid runtime value`);
|
|
1874
|
-
}
|
|
1875
|
-
/** Asserts that `value` is an `ObjectValue` */
|
|
1876
|
-
function assertIsObjectMacro(value, name) {
|
|
1877
|
-
if (!(value instanceof ObjectValue))
|
|
1878
|
-
throw new CompilerError(`${name} must be an object macro`);
|
|
1879
|
-
}
|
|
1880
|
-
/** Asserts that `value` is an `ObjectValue` that has a length property */
|
|
1881
|
-
function assertIsArrayMacro(value, name) {
|
|
1882
|
-
if (!(value instanceof ObjectValue) || !value.data.length)
|
|
1883
|
-
throw new CompilerError(`${name} must be an array macro`);
|
|
1884
|
-
}
|
|
1885
|
-
/** Asserts that all of the fields described are present on `value`*/
|
|
1886
|
-
function assertObjectFields(value, fields) {
|
|
1887
|
-
const result = [];
|
|
1888
|
-
for (const field of fields) {
|
|
1889
|
-
const param = typeof field === "object" ? field : { key: field };
|
|
1890
|
-
const item = value.data[param.key];
|
|
1891
|
-
if (item) {
|
|
1892
|
-
param.validate?.(item);
|
|
1893
|
-
result.push(item);
|
|
1894
|
-
continue;
|
|
1895
|
-
}
|
|
1896
|
-
if (param.default == undefined)
|
|
1897
|
-
throw new CompilerError(`The field "${param.name ?? param.key}" must be present`);
|
|
1898
|
-
result.push(param.default);
|
|
1899
|
-
}
|
|
1900
|
-
return result;
|
|
1901
|
-
}
|
|
1902
|
-
|
|
1903
1993
|
/** Used to create namespaces that contain multiple methods that map into instructions */
|
|
1904
1994
|
function createOverloadNamespace({ overloads, handler, }) {
|
|
1905
1995
|
const result = {};
|
|
1906
1996
|
for (const key in overloads) {
|
|
1907
1997
|
const { named, args } = overloads[key];
|
|
1908
1998
|
if (named) {
|
|
1909
|
-
result[key] = new MacroFunction((scope, options) => {
|
|
1999
|
+
result[key] = new MacroFunction((scope, out, options) => {
|
|
1910
2000
|
assertIsObjectMacro(options, named);
|
|
1911
|
-
return handler(scope, key, ...assertObjectFields(options, args));
|
|
2001
|
+
return handler(scope, key, out, ...assertObjectFields(options, args));
|
|
1912
2002
|
});
|
|
1913
2003
|
continue;
|
|
1914
2004
|
}
|
|
1915
|
-
result[key] = new MacroFunction((scope, ...params) => {
|
|
2005
|
+
result[key] = new MacroFunction((scope, out, ...params) => {
|
|
1916
2006
|
const handlerParams = [];
|
|
1917
2007
|
// validate the paramters
|
|
1918
2008
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -1929,7 +2019,7 @@ function createOverloadNamespace({ overloads, handler, }) {
|
|
|
1929
2019
|
arg.validate?.(param);
|
|
1930
2020
|
handlerParams.push(param);
|
|
1931
2021
|
}
|
|
1932
|
-
return handler(scope, key, ...handlerParams);
|
|
2022
|
+
return handler(scope, key, out, ...handlerParams);
|
|
1933
2023
|
});
|
|
1934
2024
|
}
|
|
1935
2025
|
return result;
|
|
@@ -1974,7 +2064,7 @@ class Draw extends ObjectValue {
|
|
|
1974
2064
|
args: ["x", "y", "image", "size", "rotation"],
|
|
1975
2065
|
},
|
|
1976
2066
|
},
|
|
1977
|
-
handler(scope, overload, ...args) {
|
|
2067
|
+
handler(scope, overload, out, ...args) {
|
|
1978
2068
|
return [null, [new InstructionBase("draw", overload, ...args)]];
|
|
1979
2069
|
},
|
|
1980
2070
|
});
|
|
@@ -1984,7 +2074,7 @@ class Draw extends ObjectValue {
|
|
|
1984
2074
|
|
|
1985
2075
|
class Print extends MacroFunction {
|
|
1986
2076
|
constructor() {
|
|
1987
|
-
super((scope, ...values) => {
|
|
2077
|
+
super((scope, out, ...values) => {
|
|
1988
2078
|
const [first] = values;
|
|
1989
2079
|
const inst = [];
|
|
1990
2080
|
if (!isTemplateObjectArray(first)) {
|
|
@@ -2013,7 +2103,7 @@ class Print extends MacroFunction {
|
|
|
2013
2103
|
const defaultTargetName$1 = "message1";
|
|
2014
2104
|
class PrintFlush extends MacroFunction {
|
|
2015
2105
|
constructor() {
|
|
2016
|
-
super((scope, target) => {
|
|
2106
|
+
super((scope, out, target) => {
|
|
2017
2107
|
if (!target)
|
|
2018
2108
|
return [null, [new InstructionBase("printflush", defaultTargetName$1)]];
|
|
2019
2109
|
if (!(target instanceof SenseableValue))
|
|
@@ -2026,7 +2116,7 @@ class PrintFlush extends MacroFunction {
|
|
|
2026
2116
|
const defaultTargetName = "display1";
|
|
2027
2117
|
class DrawFlush extends MacroFunction {
|
|
2028
2118
|
constructor() {
|
|
2029
|
-
super((scope, target) => {
|
|
2119
|
+
super((scope, out, target) => {
|
|
2030
2120
|
if (!target)
|
|
2031
2121
|
return [null, [new InstructionBase("drawflush", defaultTargetName)]];
|
|
2032
2122
|
if (!(target instanceof SenseableValue))
|
|
@@ -2045,10 +2135,10 @@ class GetBlock extends ObjectValue {
|
|
|
2045
2135
|
block: { args: ["x", "y"] },
|
|
2046
2136
|
building: { args: ["x", "y"] },
|
|
2047
2137
|
},
|
|
2048
|
-
handler(scope, overload, x, y) {
|
|
2138
|
+
handler(scope, overload, out, x, y) {
|
|
2049
2139
|
const output = overload === "building"
|
|
2050
|
-
?
|
|
2051
|
-
:
|
|
2140
|
+
? SenseableValue.from(scope, out, EMutability.constant)
|
|
2141
|
+
: StoreValue.from(scope, out, EMutability.constant);
|
|
2052
2142
|
return [
|
|
2053
2143
|
output,
|
|
2054
2144
|
[new InstructionBase("getblock", overload, output, x, y)],
|
|
@@ -2061,11 +2151,11 @@ class GetBlock extends ObjectValue {
|
|
|
2061
2151
|
|
|
2062
2152
|
class GetLink extends MacroFunction {
|
|
2063
2153
|
constructor() {
|
|
2064
|
-
super((scope, index) => {
|
|
2154
|
+
super((scope, out, index) => {
|
|
2065
2155
|
if (!(index instanceof StoreValue) &&
|
|
2066
|
-
(!(index instanceof LiteralValue) ||
|
|
2156
|
+
(!(index instanceof LiteralValue) || !index.isNumber()))
|
|
2067
2157
|
throw new CompilerError("The getlink index must be a number literal or a store");
|
|
2068
|
-
const outBuild =
|
|
2158
|
+
const outBuild = SenseableValue.from(scope, out, EMutability.constant);
|
|
2069
2159
|
return [outBuild, [new InstructionBase("getlink", outBuild, index)]];
|
|
2070
2160
|
});
|
|
2071
2161
|
}
|
|
@@ -2087,11 +2177,10 @@ class Control extends ObjectValue {
|
|
|
2087
2177
|
config: { args: ["building", "value"] },
|
|
2088
2178
|
color: { args: ["building", "r", "g", "b"] },
|
|
2089
2179
|
},
|
|
2090
|
-
handler(scope, overload, ...args) {
|
|
2180
|
+
handler(scope, overload, out, ...args) {
|
|
2091
2181
|
if (overload !== "color")
|
|
2092
2182
|
return [null, [new InstructionBase("control", overload, ...args)]];
|
|
2093
|
-
const temp =
|
|
2094
|
-
temp.ensureOwned();
|
|
2183
|
+
const temp = StoreValue.from(scope);
|
|
2095
2184
|
// this "patch" is here for compatibility reasons
|
|
2096
2185
|
// I don't want to add another breaking change
|
|
2097
2186
|
// TODO: figure out if this patch should be removed later
|
|
@@ -2127,14 +2216,14 @@ const validRadarSorts = [
|
|
|
2127
2216
|
];
|
|
2128
2217
|
class Radar extends MacroFunction {
|
|
2129
2218
|
constructor() {
|
|
2130
|
-
super((scope, options) => {
|
|
2219
|
+
super((scope, out, options) => {
|
|
2131
2220
|
assertIsObjectMacro(options, "The radar options");
|
|
2132
2221
|
const { building, filters, order, sort } = options.data;
|
|
2133
2222
|
if (!(building instanceof SenseableValue))
|
|
2134
2223
|
throw new CompilerError("The building must a senseable value");
|
|
2135
2224
|
assertIsArrayMacro(filters, "filters");
|
|
2136
2225
|
const { length } = filters.data;
|
|
2137
|
-
if (!(length instanceof LiteralValue) ||
|
|
2226
|
+
if (!(length instanceof LiteralValue) || !length.isNumber())
|
|
2138
2227
|
throw new CompilerError("The length of an array macro must be");
|
|
2139
2228
|
if (length.data !== 3)
|
|
2140
2229
|
throw new CompilerError("The filters array must have 3 items");
|
|
@@ -2146,7 +2235,7 @@ class Radar extends MacroFunction {
|
|
|
2146
2235
|
if (!(order instanceof LiteralValue || order instanceof StoreValue))
|
|
2147
2236
|
throw new CompilerError("The radar order must be a literal or a store");
|
|
2148
2237
|
assertLiteralOneOf(sort, validRadarSorts, "The radar sort");
|
|
2149
|
-
const outUnit =
|
|
2238
|
+
const outUnit = SenseableValue.from(scope, out, EMutability.constant);
|
|
2150
2239
|
return [
|
|
2151
2240
|
outUnit,
|
|
2152
2241
|
[
|
|
@@ -2159,14 +2248,12 @@ class Radar extends MacroFunction {
|
|
|
2159
2248
|
|
|
2160
2249
|
class Sensor extends MacroFunction {
|
|
2161
2250
|
constructor() {
|
|
2162
|
-
super((scope, property, target) => {
|
|
2251
|
+
super((scope, out, property, target) => {
|
|
2163
2252
|
if (!(property instanceof StoreValue))
|
|
2164
2253
|
throw new CompilerError("The sensor property must be a store");
|
|
2165
2254
|
if (!(target instanceof SenseableValue))
|
|
2166
2255
|
throw new CompilerError("The sensor target must be a senseable value");
|
|
2167
|
-
const temp =
|
|
2168
|
-
mutability: EMutability.readonly,
|
|
2169
|
-
});
|
|
2256
|
+
const temp = SenseableValue.from(scope, out, EMutability.readonly);
|
|
2170
2257
|
return [temp, [new InstructionBase("sensor", temp, target, property)]];
|
|
2171
2258
|
});
|
|
2172
2259
|
}
|
|
@@ -2174,9 +2261,9 @@ class Sensor extends MacroFunction {
|
|
|
2174
2261
|
|
|
2175
2262
|
class Wait extends MacroFunction {
|
|
2176
2263
|
constructor() {
|
|
2177
|
-
super((scope, seconds) => {
|
|
2264
|
+
super((scope, out, seconds) => {
|
|
2178
2265
|
if (!(seconds instanceof StoreValue) &&
|
|
2179
|
-
!(seconds instanceof LiteralValue &&
|
|
2266
|
+
!(seconds instanceof LiteralValue && seconds.isNumber()))
|
|
2180
2267
|
throw new CompilerError("The wait seconds must be either a number literal or a store");
|
|
2181
2268
|
return [null, [new InstructionBase("wait", seconds)]];
|
|
2182
2269
|
});
|
|
@@ -2192,8 +2279,8 @@ class Lookup extends ObjectValue {
|
|
|
2192
2279
|
item: { args: ["index"] },
|
|
2193
2280
|
liquid: { args: ["index"] },
|
|
2194
2281
|
},
|
|
2195
|
-
handler(scope, overload, index) {
|
|
2196
|
-
const output =
|
|
2282
|
+
handler(scope, overload, out, index) {
|
|
2283
|
+
const output = StoreValue.from(scope, out);
|
|
2197
2284
|
return [
|
|
2198
2285
|
output,
|
|
2199
2286
|
[new InstructionBase("lookup", overload, output, index)],
|
|
@@ -2206,15 +2293,15 @@ class Lookup extends ObjectValue {
|
|
|
2206
2293
|
|
|
2207
2294
|
class PackColor extends MacroFunction {
|
|
2208
2295
|
constructor() {
|
|
2209
|
-
super((scope, ...args) => {
|
|
2296
|
+
super((scope, out, ...args) => {
|
|
2210
2297
|
if (args.length !== 4) {
|
|
2211
2298
|
throw new CompilerError(`Expected 4 arguments, received ${args.length}`);
|
|
2212
2299
|
}
|
|
2213
2300
|
if (!args.every((value) => value instanceof StoreValue ||
|
|
2214
|
-
(value instanceof LiteralValue &&
|
|
2301
|
+
(value instanceof LiteralValue && value.isNumber()))) {
|
|
2215
2302
|
throw new CompilerError("packColor arguments must be either stores or number literals");
|
|
2216
2303
|
}
|
|
2217
|
-
const output =
|
|
2304
|
+
const output = StoreValue.from(scope, out);
|
|
2218
2305
|
return [output, [new InstructionBase("packcolor", output, ...args)]];
|
|
2219
2306
|
});
|
|
2220
2307
|
}
|
|
@@ -2237,7 +2324,7 @@ class SetBlock extends ObjectValue {
|
|
|
2237
2324
|
args: ["x", "y", "to", "team", { key: "rotation", default: "0" }],
|
|
2238
2325
|
},
|
|
2239
2326
|
},
|
|
2240
|
-
handler(scope, overload, x, y, to, team, rotation) {
|
|
2327
|
+
handler(scope, overload, out, x, y, to, team, rotation) {
|
|
2241
2328
|
return [
|
|
2242
2329
|
null,
|
|
2243
2330
|
[
|
|
@@ -2252,14 +2339,14 @@ class SetBlock extends ObjectValue {
|
|
|
2252
2339
|
|
|
2253
2340
|
class SpawnUnit extends MacroFunction {
|
|
2254
2341
|
constructor() {
|
|
2255
|
-
super((scope, options) => {
|
|
2342
|
+
super((scope, out, options) => {
|
|
2256
2343
|
assertIsObjectMacro(options, "The options");
|
|
2257
2344
|
const { type, x, y, team, rotation } = options.data;
|
|
2258
2345
|
assertIsRuntimeValue(type, "type");
|
|
2259
2346
|
assertIsRuntimeValue(x, "x");
|
|
2260
2347
|
assertIsRuntimeValue(y, "y");
|
|
2261
2348
|
assertIsRuntimeValue(team, "team");
|
|
2262
|
-
const output =
|
|
2349
|
+
const output = SenseableValue.from(scope, out, EMutability.constant);
|
|
2263
2350
|
return [
|
|
2264
2351
|
output,
|
|
2265
2352
|
[
|
|
@@ -2278,7 +2365,7 @@ class Stop extends MacroFunction {
|
|
|
2278
2365
|
|
|
2279
2366
|
class UnitBind extends MacroFunction {
|
|
2280
2367
|
constructor() {
|
|
2281
|
-
super((scope, type) => {
|
|
2368
|
+
super((scope, out, type) => {
|
|
2282
2369
|
if (!(type instanceof StoreValue))
|
|
2283
2370
|
throw new CompilerError("The unitBind type must be a store");
|
|
2284
2371
|
return [null, [new InstructionBase("ubind", type)]];
|
|
@@ -2317,21 +2404,20 @@ class UnitControl extends ObjectValue {
|
|
|
2317
2404
|
args: ["x", "y", "radius"],
|
|
2318
2405
|
},
|
|
2319
2406
|
},
|
|
2320
|
-
handler(scope, overload, ...args) {
|
|
2407
|
+
handler(scope, overload, out, ...args) {
|
|
2321
2408
|
let result = null;
|
|
2322
2409
|
let extraArgs = [];
|
|
2323
2410
|
switch (overload) {
|
|
2324
2411
|
case "getBlock": {
|
|
2325
|
-
const outType =
|
|
2326
|
-
const outBuilding =
|
|
2412
|
+
const outType = StoreValue.from(scope, extractDestrucuringOut(out, 0));
|
|
2413
|
+
const outBuilding = SenseableValue.from(scope, extractDestrucuringOut(out, 1));
|
|
2327
2414
|
result = ObjectValue.fromArray([outType, outBuilding]);
|
|
2328
2415
|
extraArgs = [outType, outBuilding, new LiteralValue(0)];
|
|
2329
2416
|
break;
|
|
2330
2417
|
}
|
|
2331
2418
|
case "within": {
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
extraArgs = [temp, new LiteralValue(0)];
|
|
2419
|
+
result = StoreValue.from(scope, out);
|
|
2420
|
+
extraArgs = [result, new LiteralValue(0)];
|
|
2335
2421
|
break;
|
|
2336
2422
|
}
|
|
2337
2423
|
}
|
|
@@ -2347,12 +2433,12 @@ class UnitControl extends ObjectValue {
|
|
|
2347
2433
|
|
|
2348
2434
|
class UnitRadar extends MacroFunction {
|
|
2349
2435
|
constructor() {
|
|
2350
|
-
super((scope, options) => {
|
|
2436
|
+
super((scope, out, options) => {
|
|
2351
2437
|
assertIsObjectMacro(options, "The radar options");
|
|
2352
2438
|
const { filters, order, sort } = options.data;
|
|
2353
2439
|
assertIsArrayMacro(filters, "filters");
|
|
2354
2440
|
const { length } = filters.data;
|
|
2355
|
-
if (!(length instanceof LiteralValue) ||
|
|
2441
|
+
if (!(length instanceof LiteralValue) || !length.isNumber())
|
|
2356
2442
|
throw new CompilerError("The length of an array macro must be");
|
|
2357
2443
|
if (length.data !== 3)
|
|
2358
2444
|
throw new CompilerError("The filters array must have 3 items");
|
|
@@ -2364,7 +2450,7 @@ class UnitRadar extends MacroFunction {
|
|
|
2364
2450
|
if (!(order instanceof LiteralValue || order instanceof StoreValue))
|
|
2365
2451
|
throw new CompilerError("The radar order must be a literal or a store");
|
|
2366
2452
|
assertLiteralOneOf(sort, validRadarSorts, "The radar sort");
|
|
2367
|
-
const outUnit =
|
|
2453
|
+
const outUnit = SenseableValue.from(scope, out, EMutability.constant);
|
|
2368
2454
|
return [
|
|
2369
2455
|
outUnit,
|
|
2370
2456
|
[
|
|
@@ -2402,11 +2488,11 @@ class UnitLocate extends ObjectValue {
|
|
|
2402
2488
|
args: [],
|
|
2403
2489
|
},
|
|
2404
2490
|
},
|
|
2405
|
-
handler(scope, overload, ...args) {
|
|
2406
|
-
const outFound =
|
|
2407
|
-
const outX =
|
|
2408
|
-
const outY =
|
|
2409
|
-
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));
|
|
2410
2496
|
const outArgs = [outX, outY, outFound, outBuilding];
|
|
2411
2497
|
let inputArgs = [];
|
|
2412
2498
|
switch (overload) {
|
|
@@ -2469,7 +2555,7 @@ class ApplyStatus extends ObjectValue {
|
|
|
2469
2555
|
},
|
|
2470
2556
|
clear: { args: ["status", "unit"] },
|
|
2471
2557
|
},
|
|
2472
|
-
handler(scope, overload, effect, unit, duration) {
|
|
2558
|
+
handler(scope, overload, out, effect, unit, duration) {
|
|
2473
2559
|
if (!(effect instanceof LiteralValue))
|
|
2474
2560
|
throw new CompilerError("The status effect must be a string literal");
|
|
2475
2561
|
assertLiteralOneOf(effect, validEffects, "The status effect");
|
|
@@ -2487,7 +2573,7 @@ class ApplyStatus extends ObjectValue {
|
|
|
2487
2573
|
|
|
2488
2574
|
class SpawnWave extends MacroFunction {
|
|
2489
2575
|
constructor() {
|
|
2490
|
-
super((scope, natural, x, y) => {
|
|
2576
|
+
super((scope, out, natural, x, y) => {
|
|
2491
2577
|
if (!(natural instanceof LiteralValue) ||
|
|
2492
2578
|
(natural.data !== 1 && natural.data !== 0))
|
|
2493
2579
|
throw new CompilerError("The 'natural' argument must be a boolean literal");
|
|
@@ -2528,7 +2614,7 @@ class SetRule extends ObjectValue {
|
|
|
2528
2614
|
rtsMinWeight: { args: ["team", "value"] },
|
|
2529
2615
|
rtsMinSquad: { args: ["team", "value"] },
|
|
2530
2616
|
},
|
|
2531
|
-
handler(scope, overload, ...args) {
|
|
2617
|
+
handler(scope, overload, out, ...args) {
|
|
2532
2618
|
const params = ["10", "0", "0", "100", "100"];
|
|
2533
2619
|
switch (overload) {
|
|
2534
2620
|
case "mapArea": {
|
|
@@ -2571,7 +2657,7 @@ class FlushMessage extends ObjectValue {
|
|
|
2571
2657
|
announce: { args: ["duration"] },
|
|
2572
2658
|
toast: { args: ["duration"] },
|
|
2573
2659
|
},
|
|
2574
|
-
handler(scope, overload, duration) {
|
|
2660
|
+
handler(scope, overload, out, duration) {
|
|
2575
2661
|
return [null, [new InstructionBase("message", overload, duration)]];
|
|
2576
2662
|
},
|
|
2577
2663
|
});
|
|
@@ -2587,7 +2673,7 @@ class Cutscene extends ObjectValue {
|
|
|
2587
2673
|
zoom: { args: ["level"] },
|
|
2588
2674
|
stop: { args: [] },
|
|
2589
2675
|
},
|
|
2590
|
-
handler(scope, overload, ...args) {
|
|
2676
|
+
handler(scope, overload, out, ...args) {
|
|
2591
2677
|
const params = Object.assign(["100", "100", "0.06", "0"], args);
|
|
2592
2678
|
return [null, [new InstructionBase("cutscene", overload, ...params)]];
|
|
2593
2679
|
},
|
|
@@ -2598,7 +2684,7 @@ class Cutscene extends ObjectValue {
|
|
|
2598
2684
|
|
|
2599
2685
|
class Explosion extends MacroFunction {
|
|
2600
2686
|
constructor() {
|
|
2601
|
-
super((scope, options) => {
|
|
2687
|
+
super((scope, out, options) => {
|
|
2602
2688
|
assertIsObjectMacro(options, "The options");
|
|
2603
2689
|
const params = assertObjectFields(options, [
|
|
2604
2690
|
"team",
|
|
@@ -2617,7 +2703,7 @@ class Explosion extends MacroFunction {
|
|
|
2617
2703
|
|
|
2618
2704
|
class SetRate extends MacroFunction {
|
|
2619
2705
|
constructor() {
|
|
2620
|
-
super((scope, ipt) => [null, [new InstructionBase("setrate", ipt)]]);
|
|
2706
|
+
super((scope, out, ipt) => [null, [new InstructionBase("setrate", ipt)]]);
|
|
2621
2707
|
}
|
|
2622
2708
|
}
|
|
2623
2709
|
|
|
@@ -2634,8 +2720,8 @@ class Fetch extends ObjectValue {
|
|
|
2634
2720
|
build: { args: ["team", "index", "block"] },
|
|
2635
2721
|
buildCount: { args: ["team", "block"] },
|
|
2636
2722
|
},
|
|
2637
|
-
handler(scope, overload, team, ...rest) {
|
|
2638
|
-
const output =
|
|
2723
|
+
handler(scope, overload, out, team, ...rest) {
|
|
2724
|
+
const output = SenseableValue.from(scope, out, EMutability.constant);
|
|
2639
2725
|
const params = ["0", "@conveyor"];
|
|
2640
2726
|
if (overload === "buildCount") {
|
|
2641
2727
|
params[1] = rest[0];
|
|
@@ -2655,8 +2741,8 @@ class Fetch extends ObjectValue {
|
|
|
2655
2741
|
|
|
2656
2742
|
class GetFlag extends MacroFunction {
|
|
2657
2743
|
constructor() {
|
|
2658
|
-
super((scope, flag) => {
|
|
2659
|
-
const result =
|
|
2744
|
+
super((scope, out, flag) => {
|
|
2745
|
+
const result = StoreValue.from(scope, out);
|
|
2660
2746
|
return [result, [new InstructionBase("getflag", result, flag)]];
|
|
2661
2747
|
});
|
|
2662
2748
|
}
|
|
@@ -2664,7 +2750,7 @@ class GetFlag extends MacroFunction {
|
|
|
2664
2750
|
|
|
2665
2751
|
class SetFlag extends MacroFunction {
|
|
2666
2752
|
constructor() {
|
|
2667
|
-
super((scope, flag, value) => {
|
|
2753
|
+
super((scope, out, flag, value) => {
|
|
2668
2754
|
if (!flag)
|
|
2669
2755
|
throw new CompilerError("Missing flag argument");
|
|
2670
2756
|
if (!value)
|
|
@@ -2683,28 +2769,17 @@ class MemoryEntry extends BaseValue {
|
|
|
2683
2769
|
this.macro = true;
|
|
2684
2770
|
this.store = null;
|
|
2685
2771
|
}
|
|
2686
|
-
eval(scope) {
|
|
2772
|
+
eval(scope, out) {
|
|
2687
2773
|
if (this.store)
|
|
2688
2774
|
return [this.store, []];
|
|
2689
|
-
const temp =
|
|
2775
|
+
const temp = (this.store = StoreValue.from(scope, out));
|
|
2690
2776
|
return [
|
|
2691
2777
|
temp,
|
|
2692
2778
|
[new InstructionBase("read", temp, this.mem.cell, this.prop)],
|
|
2693
2779
|
];
|
|
2694
2780
|
}
|
|
2695
|
-
consume(scope) {
|
|
2696
|
-
if (this.store)
|
|
2697
|
-
return [this.store, []];
|
|
2698
|
-
this.ensureOwned();
|
|
2699
|
-
this.store = new StoreValue(scope);
|
|
2700
|
-
this.owner.own(this.store);
|
|
2701
|
-
return [
|
|
2702
|
-
this.store,
|
|
2703
|
-
[new InstructionBase("read", this.store, this.mem.cell, this.prop)],
|
|
2704
|
-
];
|
|
2705
|
-
}
|
|
2706
2781
|
"="(scope, value) {
|
|
2707
|
-
const [data, dataInst] = value.
|
|
2782
|
+
const [data, dataInst] = value.eval(scope);
|
|
2708
2783
|
return [
|
|
2709
2784
|
data,
|
|
2710
2785
|
[
|
|
@@ -2713,26 +2788,17 @@ class MemoryEntry extends BaseValue {
|
|
|
2713
2788
|
],
|
|
2714
2789
|
];
|
|
2715
2790
|
}
|
|
2716
|
-
ensureOwned() {
|
|
2717
|
-
this.owner ??= new ValueOwner({ scope: this.scope, value: this });
|
|
2718
|
-
}
|
|
2719
|
-
}
|
|
2720
|
-
for (const operator of operators) {
|
|
2721
|
-
if (operator === "=")
|
|
2722
|
-
continue;
|
|
2723
|
-
MemoryEntry.prototype[operator] = function (...args) {
|
|
2724
|
-
// eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/no-unsafe-return
|
|
2725
|
-
return BaseValue.prototype[operator].apply(this, args);
|
|
2726
|
-
};
|
|
2727
2791
|
}
|
|
2728
2792
|
class MemoryMacro extends ObjectValue {
|
|
2729
2793
|
constructor(cell, size) {
|
|
2730
2794
|
super({
|
|
2731
|
-
$get: new MacroFunction((scope, prop) => {
|
|
2732
|
-
if (prop instanceof LiteralValue &&
|
|
2795
|
+
$get: new MacroFunction((scope, out, prop) => {
|
|
2796
|
+
if (prop instanceof LiteralValue && !prop.isNumber())
|
|
2733
2797
|
throw new CompilerError(`Invalid memory object property: "${prop.data}"`);
|
|
2734
|
-
const
|
|
2735
|
-
|
|
2798
|
+
const entry = new MemoryEntry(scope, this, prop);
|
|
2799
|
+
if (out)
|
|
2800
|
+
return entry.eval(scope, out);
|
|
2801
|
+
return [entry, []];
|
|
2736
2802
|
}),
|
|
2737
2803
|
length: size,
|
|
2738
2804
|
size,
|
|
@@ -2746,10 +2812,10 @@ class MemoryMacro extends ObjectValue {
|
|
|
2746
2812
|
class MemoryBuilder extends ObjectValue {
|
|
2747
2813
|
constructor() {
|
|
2748
2814
|
super({
|
|
2749
|
-
$call: new MacroFunction((scope, cell, size = new LiteralValue(64)) => {
|
|
2815
|
+
$call: new MacroFunction((scope, out, cell, size = new LiteralValue(64)) => {
|
|
2750
2816
|
if (!(cell instanceof SenseableValue))
|
|
2751
2817
|
throw new CompilerError("Memory cell must be a senseable value.");
|
|
2752
|
-
if (!(size instanceof LiteralValue &&
|
|
2818
|
+
if (!(size instanceof LiteralValue && size.isNumber()))
|
|
2753
2819
|
throw new CompilerError("The memory size must be a number literal.");
|
|
2754
2820
|
return [new MemoryMacro(cell, size), []];
|
|
2755
2821
|
}),
|
|
@@ -2761,8 +2827,8 @@ const dynamicVars = ["time", "tick"];
|
|
|
2761
2827
|
class NamespaceMacro extends ObjectValue {
|
|
2762
2828
|
constructor({ changeCasing = false } = {}) {
|
|
2763
2829
|
super({
|
|
2764
|
-
$get: new MacroFunction((scope, prop) => {
|
|
2765
|
-
if (!(prop instanceof LiteralValue) ||
|
|
2830
|
+
$get: new MacroFunction((scope, out, prop) => {
|
|
2831
|
+
if (!(prop instanceof LiteralValue) || !prop.isString())
|
|
2766
2832
|
throw new CompilerError("Cannot use dynamic properties on namespace macros");
|
|
2767
2833
|
const symbolName = this.changeCasing
|
|
2768
2834
|
? camelToDashCase(prop.data)
|
|
@@ -2770,57 +2836,37 @@ class NamespaceMacro extends ObjectValue {
|
|
|
2770
2836
|
const mutability = dynamicVars.includes(symbolName)
|
|
2771
2837
|
? EMutability.readonly
|
|
2772
2838
|
: EMutability.constant;
|
|
2773
|
-
const
|
|
2774
|
-
|
|
2775
|
-
value: assign(new StoreValue(scope), {
|
|
2776
|
-
mutability,
|
|
2777
|
-
}),
|
|
2778
|
-
name: `@${symbolName}`,
|
|
2779
|
-
});
|
|
2780
|
-
return [owner.value, []];
|
|
2839
|
+
const result = new StoreValue(`@${symbolName}`, mutability);
|
|
2840
|
+
return [result, []];
|
|
2781
2841
|
}),
|
|
2782
2842
|
});
|
|
2783
2843
|
this.changeCasing = changeCasing;
|
|
2784
2844
|
}
|
|
2785
2845
|
}
|
|
2786
2846
|
class VarsNamespace extends NamespaceMacro {
|
|
2787
|
-
constructor(
|
|
2847
|
+
constructor() {
|
|
2788
2848
|
super();
|
|
2789
2849
|
Object.assign(this.data, {
|
|
2790
|
-
unit: new
|
|
2791
|
-
|
|
2792
|
-
name: "@unit",
|
|
2793
|
-
value: new SenseableValue(scope),
|
|
2794
|
-
}).value,
|
|
2795
|
-
this: new ValueOwner({
|
|
2796
|
-
scope,
|
|
2797
|
-
name: "@this",
|
|
2798
|
-
value: new SenseableValue(scope),
|
|
2799
|
-
}).value,
|
|
2850
|
+
unit: new SenseableValue("@unit"),
|
|
2851
|
+
this: new SenseableValue("@this"),
|
|
2800
2852
|
});
|
|
2801
2853
|
}
|
|
2802
2854
|
}
|
|
2803
2855
|
|
|
2804
2856
|
class GetGlobal extends MacroFunction {
|
|
2805
2857
|
constructor(mutability) {
|
|
2806
|
-
super((scope, name) => {
|
|
2807
|
-
if (!(name instanceof LiteralValue) ||
|
|
2858
|
+
super((scope, out, name) => {
|
|
2859
|
+
if (!(name instanceof LiteralValue) || !name.isString())
|
|
2808
2860
|
throw new CompilerError("The name parameter must be a string literal.");
|
|
2809
|
-
const
|
|
2810
|
-
|
|
2811
|
-
name: name.data,
|
|
2812
|
-
value: assign(new SenseableValue(scope), {
|
|
2813
|
-
mutability,
|
|
2814
|
-
}),
|
|
2815
|
-
});
|
|
2816
|
-
return [owner.value, []];
|
|
2861
|
+
const result = new SenseableValue(name.data, mutability);
|
|
2862
|
+
return [result, []];
|
|
2817
2863
|
});
|
|
2818
2864
|
}
|
|
2819
2865
|
}
|
|
2820
2866
|
|
|
2821
2867
|
class Asm extends MacroFunction {
|
|
2822
2868
|
constructor() {
|
|
2823
|
-
super((scope, stringsArray, ...values) => {
|
|
2869
|
+
super((scope, out, stringsArray, ...values) => {
|
|
2824
2870
|
if (!isTemplateObjectArray(stringsArray))
|
|
2825
2871
|
throw new CompilerError("Expected to receive a template strings array");
|
|
2826
2872
|
const args = [];
|
|
@@ -2898,7 +2944,7 @@ function validateInstructionArgs(args) {
|
|
|
2898
2944
|
function initScope(scope) {
|
|
2899
2945
|
// namespaces
|
|
2900
2946
|
scope.hardSet("ControlKind", new NamespaceMacro());
|
|
2901
|
-
scope.hardSet("Vars", new VarsNamespace(
|
|
2947
|
+
scope.hardSet("Vars", new VarsNamespace());
|
|
2902
2948
|
scope.hardSet("Teams", new NamespaceMacro());
|
|
2903
2949
|
scope.hardSet("Items", new NamespaceMacro({ changeCasing: true }));
|
|
2904
2950
|
scope.hardSet("Liquids", new NamespaceMacro());
|
|
@@ -2910,7 +2956,7 @@ function initScope(scope) {
|
|
|
2910
2956
|
scope.hardSet("getVar", new GetGlobal(EMutability.mutable));
|
|
2911
2957
|
scope.hardSet("concat", new Concat());
|
|
2912
2958
|
scope.hardSet("asm", new Asm());
|
|
2913
|
-
scope.hardSet("Math", new MlogMath(
|
|
2959
|
+
scope.hardSet("Math", new MlogMath());
|
|
2914
2960
|
scope.hardSet("Memory", new MemoryBuilder());
|
|
2915
2961
|
// commands
|
|
2916
2962
|
scope.hardSet("draw", new Draw());
|
|
@@ -3002,11 +3048,11 @@ class Compiler {
|
|
|
3002
3048
|
}
|
|
3003
3049
|
return result;
|
|
3004
3050
|
}
|
|
3005
|
-
handle(scope, node, handler = this.handlers[node.type]) {
|
|
3051
|
+
handle(scope, node, handler = this.handlers[node.type], out) {
|
|
3006
3052
|
try {
|
|
3007
3053
|
if (!handler)
|
|
3008
3054
|
throw new CompilerError("Missing handler for " + node.type);
|
|
3009
|
-
const result = handler(this, scope, node, null);
|
|
3055
|
+
const result = handler(this, scope, node, out, null);
|
|
3010
3056
|
if (this.sourcemap)
|
|
3011
3057
|
return appendSourceLocations(result, node);
|
|
3012
3058
|
return result;
|
|
@@ -3018,21 +3064,11 @@ class Compiler {
|
|
|
3018
3064
|
throw error;
|
|
3019
3065
|
}
|
|
3020
3066
|
}
|
|
3021
|
-
handleEval(scope, node) {
|
|
3022
|
-
const [res, inst] = this.handle(scope, node);
|
|
3067
|
+
handleEval(scope, node, out) {
|
|
3068
|
+
const [res, inst] = this.handle(scope, node, undefined, out);
|
|
3023
3069
|
if (!res)
|
|
3024
3070
|
throw new CompilerError("Cannot eval null", node);
|
|
3025
|
-
const [evaluated, evaluatedInst] = res.eval(scope);
|
|
3026
|
-
const result = [evaluated, [...inst, ...evaluatedInst]];
|
|
3027
|
-
if (this.sourcemap)
|
|
3028
|
-
return appendSourceLocations(result, node);
|
|
3029
|
-
return result;
|
|
3030
|
-
}
|
|
3031
|
-
handleConsume(scope, node) {
|
|
3032
|
-
const [res, inst] = this.handle(scope, node);
|
|
3033
|
-
if (!res)
|
|
3034
|
-
throw new CompilerError("Cannot consume null", node);
|
|
3035
|
-
const [evaluated, evaluatedInst] = res.consume(scope);
|
|
3071
|
+
const [evaluated, evaluatedInst] = res.eval(scope, out);
|
|
3036
3072
|
const result = [evaluated, [...inst, ...evaluatedInst]];
|
|
3037
3073
|
if (this.sourcemap)
|
|
3038
3074
|
return appendSourceLocations(result, node);
|
|
@@ -3044,7 +3080,7 @@ class Compiler {
|
|
|
3044
3080
|
* The usage of this method over a regular loop over an array of nodes
|
|
3045
3081
|
* is only required if the code inside the loop generates
|
|
3046
3082
|
* instructions that are not tracked by the compiler
|
|
3047
|
-
* handler methods ({@link handle}, {@link handleEval}
|
|
3083
|
+
* handler methods ({@link handle}, {@link handleEval}
|
|
3048
3084
|
* and {@link handleMany})
|
|
3049
3085
|
*
|
|
3050
3086
|
*/
|
|
@@ -3057,30 +3093,6 @@ class Compiler {
|
|
|
3057
3093
|
return [null, lines];
|
|
3058
3094
|
}
|
|
3059
3095
|
}
|
|
3060
|
-
/** Removes jump instructions that point right to the next line */
|
|
3061
|
-
function hideRedundantJumps(inst) {
|
|
3062
|
-
for (let i = 0; i < inst.length; i++) {
|
|
3063
|
-
const instruction = inst[i];
|
|
3064
|
-
if (!(instruction instanceof JumpInstruction))
|
|
3065
|
-
continue;
|
|
3066
|
-
const { address } = instruction;
|
|
3067
|
-
let searchIndex = i + 1;
|
|
3068
|
-
let current = inst[searchIndex];
|
|
3069
|
-
while (current.hidden) {
|
|
3070
|
-
if (current instanceof AddressResolver &&
|
|
3071
|
-
current.bonds.includes(address)) {
|
|
3072
|
-
// only gets assigned if `current` is an adress resolver
|
|
3073
|
-
// that resolves the jump adress
|
|
3074
|
-
// and that is also right next to the jump,
|
|
3075
|
-
// without any actual instructions in between
|
|
3076
|
-
instruction.hidden = true;
|
|
3077
|
-
break;
|
|
3078
|
-
}
|
|
3079
|
-
searchIndex++;
|
|
3080
|
-
current = inst[searchIndex];
|
|
3081
|
-
}
|
|
3082
|
-
}
|
|
3083
|
-
}
|
|
3084
3096
|
function* hoistedFunctionNodes(nodes) {
|
|
3085
3097
|
// sorting is O(n long n) while this is just O(n)
|
|
3086
3098
|
// besides, it's better not to modify the node array
|