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