mlogjs 0.5.2 → 0.6.0
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/README.md +2 -2
- package/dist/{index.cjs → Compiler-7GMG4MDr.js} +1746 -452
- package/dist/Compiler.d.ts +8 -12
- package/dist/CompilerError.d.ts +13 -3
- package/dist/Scope.d.ts +8 -8
- package/dist/bin/files.d.ts +18 -0
- package/dist/bin.mjs +170 -0
- package/dist/handlers/Import.d.ts +5 -0
- package/dist/handlers/Literal.d.ts +2 -2
- package/dist/handlers/Typescript.d.ts +2 -2
- package/dist/handlers/index.d.ts +1 -0
- package/dist/index.mjs +3 -4267
- package/dist/instructions/AddressResolver.d.ts +4 -4
- package/dist/instructions/ControlInstructions.d.ts +3 -3
- package/dist/instructions/InstructionBase.d.ts +1 -0
- package/dist/instructions/JumpInstruction.d.ts +5 -5
- package/dist/instructions/index.d.ts +0 -1
- package/dist/macros/DynamicArray.d.ts +4 -4
- package/dist/macros/Function.d.ts +2 -2
- package/dist/macros/GetBuildings.d.ts +4 -0
- package/dist/macros/GetColor.d.ts +4 -0
- package/dist/macros/Marker.d.ts +4 -0
- package/dist/macros/Namespace.d.ts +16 -0
- package/dist/macros/Queries.d.ts +10 -0
- package/dist/macros/StringView.d.ts +4 -0
- package/dist/macros/commands/Effect.d.ts +4 -0
- package/dist/macros/commands/Format.d.ts +4 -0
- package/dist/macros/commands/LocalePrint.d.ts +4 -0
- package/dist/macros/commands/PlayMusic.d.ts +4 -0
- package/dist/macros/commands/PlaySound.d.ts +4 -0
- package/dist/macros/commands/PrintChar.d.ts +4 -0
- package/dist/macros/commands/Query.d.ts +4 -0
- package/dist/macros/commands/SetProp.d.ts +4 -0
- package/dist/macros/commands/SpawnBullet.d.ts +4 -0
- package/dist/macros/commands/Sync.d.ts +6 -0
- package/dist/macros/commands/UnpackColor.d.ts +4 -0
- package/dist/macros/commands/WeatherSense.d.ts +4 -0
- package/dist/macros/commands/WeatherSet.d.ts +4 -0
- package/dist/macros/commands/index.d.ts +13 -0
- package/dist/macros/index.d.ts +7 -1
- package/dist/macros/util.d.ts +5 -2
- package/dist/modules.d.ts +8 -0
- package/dist/operators.d.ts +1 -1
- package/dist/types.d.ts +84 -59
- package/dist/utils/constants.d.ts +9 -4
- package/dist/utils/general.d.ts +8 -9
- package/dist/utils/instruction.d.ts +5 -1
- package/dist/utils/value.d.ts +14 -5
- package/dist/values/AssignmentValue.d.ts +1 -1
- package/dist/values/BaseValue.d.ts +4 -2
- package/dist/values/DestructuringValue.d.ts +5 -7
- package/dist/values/FunctionValue.d.ts +8 -6
- package/dist/values/JumpOutValue.d.ts +4 -4
- package/dist/values/LazyValue.d.ts +3 -3
- package/dist/values/LiteralValue.d.ts +2 -3
- package/dist/values/ObjectValue.d.ts +6 -11
- package/dist/values/StoreValue.d.ts +10 -5
- package/dist/values/VoidValue.d.ts +2 -1
- package/dist/values/index.d.ts +0 -1
- package/lib/commands.d.ts +776 -730
- package/lib/eslib/lib.es2015.collection.d.ts +50 -39
- package/lib/eslib/lib.es2015.core.d.ts +59 -0
- package/lib/eslib/lib.es2015.d.ts +0 -3
- package/lib/eslib/lib.es2015.generator.d.ts +54 -53
- package/lib/eslib/lib.es2015.iterable.d.ts +9 -6
- package/lib/eslib/lib.es5.d.ts +53 -6
- package/lib/globals.d.ts +309 -18
- package/lib/helpers.d.ts +86 -52
- package/lib/kinds.d.ts +42 -7
- package/lib/macros.d.ts +53 -18
- package/lib/traits.d.ts +308 -146
- package/lib/util.d.ts +144 -48
- package/lib/world.d.ts +1197 -0
- package/package.json +27 -35
- package/dist/bin.cjs +0 -111
- package/dist/initScope.d.ts +0 -5
- package/dist/instructions/ZeroSpaceInstruction.d.ts +0 -4
- package/dist/values/SenseableValue.d.ts +0 -24
- /package/dist/{bin.d.ts → bin/index.d.ts} +0 -0
|
@@ -1,26 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
var parser = require('@babel/parser');
|
|
4
|
-
var es = require('@babel/types');
|
|
5
|
-
|
|
6
|
-
function _interopNamespaceDefault(e) {
|
|
7
|
-
var n = Object.create(null);
|
|
8
|
-
if (e) {
|
|
9
|
-
Object.keys(e).forEach(function (k) {
|
|
10
|
-
if (k !== 'default') {
|
|
11
|
-
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
12
|
-
Object.defineProperty(n, k, d.get ? d : {
|
|
13
|
-
enumerable: true,
|
|
14
|
-
get: function () { return e[k]; }
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
n.default = e;
|
|
20
|
-
return Object.freeze(n);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
var es__namespace = /*#__PURE__*/_interopNamespaceDefault(es);
|
|
1
|
+
import { parse } from '@babel/parser';
|
|
2
|
+
import * as es from '@babel/types';
|
|
24
3
|
|
|
25
4
|
class CompilerError extends Error {
|
|
26
5
|
constructor(message, source) {
|
|
@@ -75,29 +54,34 @@ var EInstIntent;
|
|
|
75
54
|
EInstIntent[EInstIntent["continue"] = 2] = "continue";
|
|
76
55
|
EInstIntent[EInstIntent["return"] = 3] = "return";
|
|
77
56
|
/**
|
|
78
|
-
* This is used by the `end` and `stop` instructions.
|
|
79
|
-
*
|
|
57
|
+
* This is used by the `end` and `stop` instructions. Means that the processor
|
|
58
|
+
* will restart or shutdown.
|
|
80
59
|
*/
|
|
81
60
|
EInstIntent[EInstIntent["exit"] = 4] = "exit";
|
|
82
61
|
})(EInstIntent || (EInstIntent = {}));
|
|
83
62
|
/** Defines the possible types of mutability of a value */
|
|
84
63
|
var EMutability;
|
|
85
64
|
(function (EMutability) {
|
|
86
|
-
/**
|
|
87
|
-
* The value can be changed by the user's code,
|
|
88
|
-
* by the mlog runtime, or us
|
|
89
|
-
*/
|
|
65
|
+
/** The value can be changed by the user's code, by the mlog runtime, or us */
|
|
90
66
|
EMutability[EMutability["mutable"] = 0] = "mutable";
|
|
91
67
|
/**
|
|
92
|
-
* The value cannot be directly assigned,
|
|
93
|
-
*
|
|
68
|
+
* The value cannot be directly assigned, but it's still not safe from
|
|
69
|
+
* mutations coming from the runtime.
|
|
94
70
|
*/
|
|
95
71
|
EMutability[EMutability["readonly"] = 1] = "readonly";
|
|
96
|
-
/**
|
|
72
|
+
/** Compile-time constants. */
|
|
97
73
|
EMutability[EMutability["constant"] = 2] = "constant";
|
|
98
74
|
/** An immutable value that hasn't been initialized yet */
|
|
99
75
|
EMutability[EMutability["init"] = 3] = "init";
|
|
76
|
+
/** A value unknown at compile-time that will not change after initialization */
|
|
77
|
+
EMutability[EMutability["immutable"] = 4] = "immutable";
|
|
100
78
|
})(EMutability || (EMutability = {}));
|
|
79
|
+
var EInlineType;
|
|
80
|
+
(function (EInlineType) {
|
|
81
|
+
EInlineType[EInlineType["auto"] = 0] = "auto";
|
|
82
|
+
EInlineType[EInlineType["always"] = 1] = "always";
|
|
83
|
+
EInlineType[EInlineType["never"] = 2] = "never";
|
|
84
|
+
})(EInlineType || (EInlineType = {}));
|
|
101
85
|
|
|
102
86
|
const arithmeticBinaryOperators = [
|
|
103
87
|
"*",
|
|
@@ -142,7 +126,7 @@ const arithmeticAssignmentOperators = [
|
|
|
142
126
|
"-=",
|
|
143
127
|
"/=",
|
|
144
128
|
];
|
|
145
|
-
const logicalAssignmentOperators = ["&&=", "||=", "??="];
|
|
129
|
+
const logicalAssignmentOperators$1 = ["&&=", "||=", "??="];
|
|
146
130
|
const bitwiseAssignmentOperators = [
|
|
147
131
|
"<<=",
|
|
148
132
|
">>=",
|
|
@@ -152,7 +136,7 @@ const bitwiseAssignmentOperators = [
|
|
|
152
136
|
];
|
|
153
137
|
const assignmentOperators = [
|
|
154
138
|
...arithmeticAssignmentOperators,
|
|
155
|
-
...logicalAssignmentOperators,
|
|
139
|
+
...logicalAssignmentOperators$1,
|
|
156
140
|
...bitwiseAssignmentOperators,
|
|
157
141
|
"=",
|
|
158
142
|
];
|
|
@@ -191,7 +175,7 @@ const operatorMap$2 = {
|
|
|
191
175
|
"&": "and",
|
|
192
176
|
"^": "xor",
|
|
193
177
|
">>": "shr",
|
|
194
|
-
">>>": "
|
|
178
|
+
">>>": "ushr",
|
|
195
179
|
"<<": "shl",
|
|
196
180
|
"&&": "land",
|
|
197
181
|
"||": "or",
|
|
@@ -209,10 +193,12 @@ const orderIndependentOperators = [
|
|
|
209
193
|
"||",
|
|
210
194
|
];
|
|
211
195
|
|
|
196
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
|
212
197
|
class VoidValue {
|
|
213
198
|
constructor() {
|
|
214
199
|
this.mutability = EMutability.mutable;
|
|
215
200
|
this.macro = false;
|
|
201
|
+
this.volatile = false;
|
|
216
202
|
}
|
|
217
203
|
eval(_scope, _out) {
|
|
218
204
|
throw new CompilerError(`[${this.debugString()}] cannot be evaluated into a value.`);
|
|
@@ -236,7 +222,7 @@ class VoidValue {
|
|
|
236
222
|
debugString() {
|
|
237
223
|
return "VoidValue";
|
|
238
224
|
}
|
|
239
|
-
|
|
225
|
+
toMlogString() {
|
|
240
226
|
return '"[macro VoidValue]"';
|
|
241
227
|
}
|
|
242
228
|
}
|
|
@@ -247,34 +233,7 @@ for (const key of operators) {
|
|
|
247
233
|
};
|
|
248
234
|
}
|
|
249
235
|
|
|
250
|
-
|
|
251
|
-
get hidden() {
|
|
252
|
-
return this._hidden;
|
|
253
|
-
}
|
|
254
|
-
set hidden(value) {
|
|
255
|
-
this._hidden = value;
|
|
256
|
-
}
|
|
257
|
-
constructor(...args) {
|
|
258
|
-
this.intent = EInstIntent.none;
|
|
259
|
-
this.alwaysRuns = true;
|
|
260
|
-
this._hidden = false;
|
|
261
|
-
this.args = args;
|
|
262
|
-
}
|
|
263
|
-
resolve(_i) { }
|
|
264
|
-
toString() {
|
|
265
|
-
return this.args.filter(arg => arg).join(" ");
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
class OperationInstruction extends InstructionBase {
|
|
270
|
-
constructor(kind, temp, left, right) {
|
|
271
|
-
super("op", kind, temp, left, right);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* The prefix for internal variables inside the compiler output
|
|
277
|
-
*/
|
|
236
|
+
/** The prefix for internal variables inside the compiler output */
|
|
278
237
|
const internalPrefix = "&";
|
|
279
238
|
const discardedName = `${internalPrefix}_`;
|
|
280
239
|
const itemNames = [
|
|
@@ -303,15 +262,21 @@ const itemNames = [
|
|
|
303
262
|
];
|
|
304
263
|
/** The name of a special processor variable, controls the instruction pointer */
|
|
305
264
|
const counterName = "@counter";
|
|
265
|
+
const worldModuleName = "mlogjs:world";
|
|
266
|
+
const mathConstants = {
|
|
267
|
+
E: Math.E,
|
|
268
|
+
PI: Math.PI,
|
|
269
|
+
radToDeg: 180 / Math.PI,
|
|
270
|
+
degToRad: Math.PI / 180,
|
|
271
|
+
};
|
|
306
272
|
|
|
307
273
|
/**
|
|
308
274
|
* Returns a string that has the format: [line]:[column].
|
|
309
275
|
*
|
|
310
|
-
* If `name` is provided, and it is a string, the resulting string will
|
|
311
|
-
*
|
|
276
|
+
* If `name` is provided, and it is a string, the resulting string will have the
|
|
277
|
+
* format: [name]:[line]:[column].
|
|
312
278
|
*/
|
|
313
279
|
function nodeName(node, name) {
|
|
314
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
315
280
|
const { line, column } = node.loc.start;
|
|
316
281
|
if (typeof name === "string")
|
|
317
282
|
return `${name}:${line}:${column}`;
|
|
@@ -321,18 +286,16 @@ function nodeName(node, name) {
|
|
|
321
286
|
* Converts a camel case string into a dash case one.
|
|
322
287
|
*
|
|
323
288
|
* As an example `camelCase` becomes `camel-case`
|
|
289
|
+
*
|
|
324
290
|
* @example
|
|
325
|
-
*
|
|
326
|
-
*
|
|
327
|
-
*
|
|
291
|
+
* ```
|
|
292
|
+
* camelToDashCase("camelCase") // returns "camel-case"
|
|
293
|
+
* ```;
|
|
328
294
|
*/
|
|
329
295
|
function camelToDashCase(name) {
|
|
330
296
|
return name.replace(/[A-Z]/g, str => `-${str.toLowerCase()}`);
|
|
331
297
|
}
|
|
332
|
-
/**
|
|
333
|
-
* A more type safe version of `Object.assign`
|
|
334
|
-
*/
|
|
335
|
-
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
298
|
+
/** A more type safe version of `Object.assign` */
|
|
336
299
|
function assign(obj, props) {
|
|
337
300
|
return Object.assign(obj, props);
|
|
338
301
|
}
|
|
@@ -344,7 +307,6 @@ function lazy(fn) {
|
|
|
344
307
|
|
|
345
308
|
function appendSourceLocations(valueInst, node) {
|
|
346
309
|
for (const inst of valueInst[1]) {
|
|
347
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
348
310
|
inst.source ??= node.loc;
|
|
349
311
|
}
|
|
350
312
|
return valueInst;
|
|
@@ -377,7 +339,10 @@ function hideRedundantJumps(inst) {
|
|
|
377
339
|
}
|
|
378
340
|
}
|
|
379
341
|
}
|
|
380
|
-
/**
|
|
342
|
+
/**
|
|
343
|
+
* A helper that appends the instructions from `value` into `inst` and returns
|
|
344
|
+
* it's `IValue` instance
|
|
345
|
+
*/
|
|
381
346
|
function pipeInsts(value, inst) {
|
|
382
347
|
inst.push(...value[1]);
|
|
383
348
|
return value[0];
|
|
@@ -391,6 +356,11 @@ function usesAddressResolver(resolver, instructions) {
|
|
|
391
356
|
}
|
|
392
357
|
return false;
|
|
393
358
|
}
|
|
359
|
+
function formatInstructionArgs(args) {
|
|
360
|
+
return args
|
|
361
|
+
.filter((value) => !!value)
|
|
362
|
+
.map(value => (typeof value === "string" ? value : value.toMlogString()));
|
|
363
|
+
}
|
|
394
364
|
|
|
395
365
|
function isTemplateObjectArray(value) {
|
|
396
366
|
return (value instanceof ObjectValue &&
|
|
@@ -417,7 +387,10 @@ function assertStringLiteral(value, name) {
|
|
|
417
387
|
if (!(value instanceof LiteralValue) || !value.isString())
|
|
418
388
|
throw new CompilerError(`${name} must be a string literal`);
|
|
419
389
|
}
|
|
420
|
-
/**
|
|
390
|
+
/**
|
|
391
|
+
* Asserts that `value` is a string literal that is part of a given set of
|
|
392
|
+
* `members`
|
|
393
|
+
*/
|
|
421
394
|
function assertLiteralOneOf(value, members, name) {
|
|
422
395
|
assertStringLiteral(value, name);
|
|
423
396
|
if (!members.includes(value.data))
|
|
@@ -445,7 +418,7 @@ function assertIsArrayMacro(value, name) {
|
|
|
445
418
|
}
|
|
446
419
|
throw new CompilerError(`${name} must be an array macro`);
|
|
447
420
|
}
|
|
448
|
-
/** Asserts that all of the fields described are present on `value
|
|
421
|
+
/** Asserts that all of the fields described are present on `value` */
|
|
449
422
|
function assertObjectFields(value, fields) {
|
|
450
423
|
const result = [];
|
|
451
424
|
for (const field of fields) {
|
|
@@ -470,6 +443,32 @@ function isDiscardedOut(out) {
|
|
|
470
443
|
return out.name === discardedName;
|
|
471
444
|
}
|
|
472
445
|
|
|
446
|
+
class InstructionBase {
|
|
447
|
+
get hidden() {
|
|
448
|
+
return this._hidden;
|
|
449
|
+
}
|
|
450
|
+
set hidden(value) {
|
|
451
|
+
this._hidden = value;
|
|
452
|
+
}
|
|
453
|
+
constructor(...args) {
|
|
454
|
+
this.intent = EInstIntent.none;
|
|
455
|
+
this.alwaysRuns = true;
|
|
456
|
+
this._hidden = false;
|
|
457
|
+
this.ignoredByParser = false;
|
|
458
|
+
this.args = args;
|
|
459
|
+
}
|
|
460
|
+
resolve(_i) { }
|
|
461
|
+
toString() {
|
|
462
|
+
return formatInstructionArgs(this.args).join(" ");
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
class OperationInstruction extends InstructionBase {
|
|
467
|
+
constructor(kind, temp, left, right) {
|
|
468
|
+
super("op", kind, temp, left, right);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
473
472
|
class SetInstruction extends InstructionBase {
|
|
474
473
|
constructor(store, value) {
|
|
475
474
|
super("set", store, value);
|
|
@@ -543,8 +542,8 @@ class JumpInstruction extends InstructionBase {
|
|
|
543
542
|
this.address = address;
|
|
544
543
|
}
|
|
545
544
|
/**
|
|
546
|
-
* Works with the `JumpOutValue` class to determine
|
|
547
|
-
*
|
|
545
|
+
* Works with the `JumpOutValue` class to determine whether a jump should be
|
|
546
|
+
* added if the expression wasn't jump compressed.
|
|
548
547
|
*/
|
|
549
548
|
static or(test, out) {
|
|
550
549
|
if (test === out)
|
|
@@ -561,12 +560,6 @@ class SetCounterInstruction extends InstructionBase {
|
|
|
561
560
|
}
|
|
562
561
|
}
|
|
563
562
|
|
|
564
|
-
class ZeroSpaceInstruction extends InstructionBase {
|
|
565
|
-
toString() {
|
|
566
|
-
return this.args.join("");
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
|
|
570
563
|
class BreakInstruction extends JumpInstruction {
|
|
571
564
|
constructor(address) {
|
|
572
565
|
super(address, EJumpKind.Always);
|
|
@@ -624,7 +617,6 @@ class JumpOutValue extends VoidValue {
|
|
|
624
617
|
kind = invertedOperatorMap[operator];
|
|
625
618
|
}
|
|
626
619
|
const jump = new JumpInstruction(this.address, kind, left, right);
|
|
627
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
628
620
|
jump.source = this.node.loc;
|
|
629
621
|
return jump;
|
|
630
622
|
}
|
|
@@ -634,7 +626,7 @@ class JumpOutValue extends VoidValue {
|
|
|
634
626
|
debugString() {
|
|
635
627
|
return "JumpOutValue";
|
|
636
628
|
}
|
|
637
|
-
|
|
629
|
+
toMlogString() {
|
|
638
630
|
return '"[macro JumpOutValue]"';
|
|
639
631
|
}
|
|
640
632
|
}
|
|
@@ -672,10 +664,10 @@ class BaseValue extends VoidValue {
|
|
|
672
664
|
}
|
|
673
665
|
// requires special handling
|
|
674
666
|
// the handler should give an object value to allow the lazy evaluation
|
|
675
|
-
"??"(scope, other, out) {
|
|
676
|
-
const result =
|
|
667
|
+
"??"(scope, other, out, endAddress) {
|
|
668
|
+
const result = StoreValue.from(scope, out);
|
|
677
669
|
const nullLiteral = new LiteralValue(null);
|
|
678
|
-
const
|
|
670
|
+
const targetAddress = endAddress ?? new LiteralValue(null);
|
|
679
671
|
const [left, leftInst] = this.eval(scope, result);
|
|
680
672
|
const [right, rightInst] = other.eval(scope, result);
|
|
681
673
|
const [test, testInst] = left["==="](scope, nullLiteral);
|
|
@@ -685,10 +677,10 @@ class BaseValue extends VoidValue {
|
|
|
685
677
|
...leftInst,
|
|
686
678
|
...result["="](scope, left)[1],
|
|
687
679
|
...testInst,
|
|
688
|
-
new JumpInstruction(
|
|
680
|
+
new JumpInstruction(targetAddress, EJumpKind.Equal, test, new LiteralValue(0)),
|
|
689
681
|
...rightInst,
|
|
690
682
|
...result["="](scope, right)[1],
|
|
691
|
-
new AddressResolver(
|
|
683
|
+
...(endAddress ? [] : [new AddressResolver(targetAddress)]),
|
|
692
684
|
],
|
|
693
685
|
];
|
|
694
686
|
}
|
|
@@ -702,8 +694,45 @@ class BaseValue extends VoidValue {
|
|
|
702
694
|
const [result, resultInst] = equal["!"](scope, out);
|
|
703
695
|
return [result, [...equalInst, ...resultInst]];
|
|
704
696
|
}
|
|
697
|
+
"&&"(scope, value, out, endAddress) {
|
|
698
|
+
const temp = StoreValue.from(scope, out);
|
|
699
|
+
const [left, leftInst] = this.eval(scope, temp);
|
|
700
|
+
const [right, rightInst] = value.eval(scope, temp);
|
|
701
|
+
const targetAddress = endAddress ?? new LiteralValue(null);
|
|
702
|
+
return [
|
|
703
|
+
temp,
|
|
704
|
+
[
|
|
705
|
+
...leftInst,
|
|
706
|
+
...temp["="](scope, left)[1],
|
|
707
|
+
new JumpInstruction(targetAddress, EJumpKind.Equal, left, new LiteralValue(0)),
|
|
708
|
+
...rightInst,
|
|
709
|
+
...temp["="](scope, right)[1],
|
|
710
|
+
...(endAddress ? [] : [new AddressResolver(targetAddress)]),
|
|
711
|
+
],
|
|
712
|
+
];
|
|
713
|
+
}
|
|
714
|
+
"||"(scope, value, out, endAddress) {
|
|
715
|
+
const temp = StoreValue.from(scope, out);
|
|
716
|
+
const [left, leftInst] = this.eval(scope, temp);
|
|
717
|
+
const [right, rightInst] = value.eval(scope, temp);
|
|
718
|
+
const targetAddress = endAddress ?? new LiteralValue(null);
|
|
719
|
+
return [
|
|
720
|
+
temp,
|
|
721
|
+
[
|
|
722
|
+
...leftInst,
|
|
723
|
+
...temp["="](scope, left)[1],
|
|
724
|
+
new JumpInstruction(targetAddress, EJumpKind.NotEqual, left, new LiteralValue(0)),
|
|
725
|
+
...rightInst,
|
|
726
|
+
...temp["="](scope, right)[1],
|
|
727
|
+
...(endAddress ? [] : [new AddressResolver(targetAddress)]),
|
|
728
|
+
],
|
|
729
|
+
];
|
|
730
|
+
}
|
|
705
731
|
}
|
|
706
|
-
for (const
|
|
732
|
+
for (const k in operatorMap$2) {
|
|
733
|
+
const key = k;
|
|
734
|
+
if (key === "&&" || key == "||")
|
|
735
|
+
continue;
|
|
707
736
|
const kind = operatorMap$2[key];
|
|
708
737
|
BaseValue.prototype[key] = function (scope, value, out) {
|
|
709
738
|
const [left, leftInst] = this.eval(scope);
|
|
@@ -774,19 +803,38 @@ class LiteralValue extends BaseValue {
|
|
|
774
803
|
super();
|
|
775
804
|
this.mutability = EMutability.constant;
|
|
776
805
|
this.data = data;
|
|
777
|
-
this.name = this.
|
|
806
|
+
this.name = JSON.stringify(this.data);
|
|
778
807
|
}
|
|
779
808
|
eval(_scope) {
|
|
780
809
|
return [this, []];
|
|
781
810
|
}
|
|
782
|
-
|
|
783
|
-
|
|
811
|
+
toMlogString() {
|
|
812
|
+
const { data } = this;
|
|
813
|
+
// math constants are literal values
|
|
814
|
+
// so that the compiler can optimize them
|
|
815
|
+
// in operations
|
|
816
|
+
// this helps to print them in their global variable form
|
|
817
|
+
// instead of their literal value
|
|
818
|
+
switch (data) {
|
|
819
|
+
case mathConstants.E:
|
|
820
|
+
return "@e";
|
|
821
|
+
case mathConstants.PI:
|
|
822
|
+
return "@pi";
|
|
823
|
+
case mathConstants.degToRad:
|
|
824
|
+
return "@degToRad";
|
|
825
|
+
case mathConstants.radToDeg:
|
|
826
|
+
return "@radToDeg";
|
|
827
|
+
}
|
|
828
|
+
if (typeof data !== "string")
|
|
829
|
+
return JSON.stringify(data);
|
|
830
|
+
return formatMlogString(data);
|
|
784
831
|
}
|
|
785
832
|
get(scope, name) {
|
|
786
833
|
if (!(name instanceof LiteralValue && name.isString()))
|
|
787
834
|
return super.get(scope, name);
|
|
788
835
|
const method = literalMethods[name.data];
|
|
789
|
-
if (!method
|
|
836
|
+
if (!method ||
|
|
837
|
+
!Object.prototype.hasOwnProperty.call(literalMethods, name.data))
|
|
790
838
|
throw new CompilerError(`The member [${name.debugString()}] does not exist on literal values.`);
|
|
791
839
|
return [method.apply(this, [scope]), []];
|
|
792
840
|
}
|
|
@@ -807,9 +855,6 @@ class LiteralValue extends BaseValue {
|
|
|
807
855
|
return other.eval(scope, out);
|
|
808
856
|
return [this, []];
|
|
809
857
|
}
|
|
810
|
-
typeof() {
|
|
811
|
-
return [new LiteralValue("literal"), []];
|
|
812
|
-
}
|
|
813
858
|
isString() {
|
|
814
859
|
return typeof this.data === "string";
|
|
815
860
|
}
|
|
@@ -817,7 +862,7 @@ class LiteralValue extends BaseValue {
|
|
|
817
862
|
return typeof this.data === "number";
|
|
818
863
|
}
|
|
819
864
|
debugString() {
|
|
820
|
-
return this.
|
|
865
|
+
return this.toMlogString();
|
|
821
866
|
}
|
|
822
867
|
}
|
|
823
868
|
const operatorMap = {
|
|
@@ -835,12 +880,13 @@ const operatorMap = {
|
|
|
835
880
|
"/": (a, b) => a / b,
|
|
836
881
|
"%": (a, b) => a % b,
|
|
837
882
|
"**": (a, b) => a ** b,
|
|
838
|
-
"|": (a, b) => a | b,
|
|
839
|
-
"&": (a, b) => a & b,
|
|
840
|
-
"^": (a, b) => a ^ b,
|
|
841
|
-
|
|
842
|
-
"
|
|
843
|
-
"<<": (a, b) => a << b,
|
|
883
|
+
"|": bitwiseOp((a, b) => a | b),
|
|
884
|
+
"&": bitwiseOp((a, b) => a & b),
|
|
885
|
+
"^": bitwiseOp((a, b) => a ^ b),
|
|
886
|
+
// bit shifting only takes the lower 6 bits of the right operand
|
|
887
|
+
">>": bitwiseOp((a, b) => a >> (b & 63n)),
|
|
888
|
+
"<<": bitwiseOp((a, b) => a << (b & 63n)),
|
|
889
|
+
">>>": bitwiseOp((a, b) => BigInt.asUintN(64, a) >> (b & 63n)),
|
|
844
890
|
"&&": (a, b) => +(a && b),
|
|
845
891
|
"||": (a, b) => +(a || b),
|
|
846
892
|
};
|
|
@@ -861,47 +907,99 @@ for (const k in operatorMap) {
|
|
|
861
907
|
if (!(value instanceof LiteralValue)) {
|
|
862
908
|
return BaseValue.prototype[key].apply(this, [scope, value, out]);
|
|
863
909
|
}
|
|
864
|
-
|
|
910
|
+
// patch constant string concatenation
|
|
911
|
+
// TODO: remove this in favor of the `concat` function
|
|
912
|
+
if (key === "+" && this.isString()) {
|
|
913
|
+
return [new LiteralValue(this.data + value.data), []];
|
|
914
|
+
}
|
|
915
|
+
return [new LiteralValue(fn(this.num, value.num)), []];
|
|
865
916
|
};
|
|
866
917
|
}
|
|
867
918
|
const unaryOperatorMap = {
|
|
868
919
|
"!": v => +!v,
|
|
869
|
-
"~": v => ~v,
|
|
920
|
+
"~": bitwiseOp(v => ~v),
|
|
870
921
|
"u-": v => -v,
|
|
871
922
|
"u+": v => +v,
|
|
872
923
|
};
|
|
873
924
|
for (const key in unaryOperatorMap) {
|
|
874
925
|
LiteralValue.prototype[key] = function () {
|
|
875
926
|
const fn = unaryOperatorMap[key];
|
|
876
|
-
return [new LiteralValue(fn(this.
|
|
927
|
+
return [new LiteralValue(fn(this.num)), []];
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
/**
|
|
931
|
+
* Performs bitwise operations on 64-bit integers to ensure that the operations
|
|
932
|
+
* evaluated at compile time produce the same results as the mlog runtime.
|
|
933
|
+
*
|
|
934
|
+
* This is necessary because javascript converts its 64-bit floats into 32-bit
|
|
935
|
+
* integers to perform bitwise operations, however mlog casts 64-bit floats into
|
|
936
|
+
* 64-bit integers to achieve the same goal. This means that using javascript
|
|
937
|
+
* numbers to evaluate these operations at compile time can cause disparity
|
|
938
|
+
* between the compiler and the runtime for values bigger than `2^31-1`.
|
|
939
|
+
*/
|
|
940
|
+
function bitwiseOp(fn) {
|
|
941
|
+
return (...args) => {
|
|
942
|
+
const bigResult = fn(...args.map(BigInt));
|
|
943
|
+
// limit the result to 64 bits of precision (signed long)
|
|
944
|
+
// and convert it back into a number
|
|
945
|
+
return Number(BigInt.asIntN(64, bigResult));
|
|
877
946
|
};
|
|
878
947
|
}
|
|
948
|
+
function formatMlogString(str) {
|
|
949
|
+
let result = '"';
|
|
950
|
+
for (let i = 0; i < str.length; i++) {
|
|
951
|
+
const char = str[i];
|
|
952
|
+
const code = str.charCodeAt(i);
|
|
953
|
+
if (char === '"') {
|
|
954
|
+
result += '\\"';
|
|
955
|
+
}
|
|
956
|
+
else if (char === "\\") {
|
|
957
|
+
result += "\\\\";
|
|
958
|
+
}
|
|
959
|
+
else if (char === "\n") {
|
|
960
|
+
result += "\\n";
|
|
961
|
+
}
|
|
962
|
+
else if (code < 0x20 || code >= 0x7f) {
|
|
963
|
+
result += `\\u${code.toString(16).padStart(4, "0")}`;
|
|
964
|
+
}
|
|
965
|
+
else {
|
|
966
|
+
result += char;
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
return result + '"';
|
|
970
|
+
}
|
|
879
971
|
|
|
880
972
|
/**
|
|
881
|
-
* `StoreValue` represents values unknown at compile time,
|
|
882
|
-
*
|
|
973
|
+
* `StoreValue` represents values unknown at compile time, mostly used with
|
|
974
|
+
* mutable variables and temporary values.
|
|
975
|
+
*
|
|
976
|
+
* Stores are mutable by default.
|
|
883
977
|
*/
|
|
884
978
|
class StoreValue extends BaseValue {
|
|
885
|
-
constructor(name, mutability = EMutability.mutable) {
|
|
979
|
+
constructor(name, mutability = EMutability.mutable, { temporary = false, volatile = false, } = {}) {
|
|
886
980
|
super();
|
|
887
981
|
this.name = name;
|
|
888
982
|
this.mutability = mutability;
|
|
983
|
+
this.temporary = temporary;
|
|
984
|
+
this.volatile = volatile;
|
|
889
985
|
}
|
|
890
986
|
static from(scope, out, mutability = EMutability.mutable) {
|
|
891
987
|
if (out instanceof StoreValue)
|
|
892
988
|
return out;
|
|
893
|
-
const
|
|
894
|
-
|
|
989
|
+
const hasName = typeof out === "string";
|
|
990
|
+
const name = hasName ? out : scope.makeTempName();
|
|
991
|
+
return new StoreValue(name, mutability, {
|
|
992
|
+
temporary: !hasName,
|
|
993
|
+
});
|
|
895
994
|
}
|
|
896
995
|
static out(scope, out, mutability = EMutability.mutable) {
|
|
897
996
|
if (!out || typeof out === "string") {
|
|
898
|
-
return new StoreValue(out ?? scope.makeTempName(), mutability
|
|
997
|
+
return new StoreValue(out ?? scope.makeTempName(), mutability, {
|
|
998
|
+
temporary: !out,
|
|
999
|
+
});
|
|
899
1000
|
}
|
|
900
1001
|
return out;
|
|
901
1002
|
}
|
|
902
|
-
typeof() {
|
|
903
|
-
return [new LiteralValue("store"), []];
|
|
904
|
-
}
|
|
905
1003
|
"="(scope, value) {
|
|
906
1004
|
if (this.mutability !== EMutability.mutable &&
|
|
907
1005
|
this.mutability !== EMutability.init)
|
|
@@ -936,16 +1034,67 @@ class StoreValue extends BaseValue {
|
|
|
936
1034
|
eval(_scope) {
|
|
937
1035
|
return [this, []];
|
|
938
1036
|
}
|
|
1037
|
+
get(scope, prop, out) {
|
|
1038
|
+
const mutability = EMutability.readonly;
|
|
1039
|
+
const thisCoordName = getThisCoordName(this, prop);
|
|
1040
|
+
if (thisCoordName)
|
|
1041
|
+
return [
|
|
1042
|
+
new StoreValue(`@this${thisCoordName}`, EMutability.constant),
|
|
1043
|
+
[],
|
|
1044
|
+
];
|
|
1045
|
+
if (prop instanceof LiteralValue && prop.isString()) {
|
|
1046
|
+
const result = StoreValue.from(scope, out, mutability);
|
|
1047
|
+
// handle string length property
|
|
1048
|
+
const propName = prop.data === "length" ? "size" : prop.data;
|
|
1049
|
+
return [
|
|
1050
|
+
result,
|
|
1051
|
+
[
|
|
1052
|
+
new InstructionBase("sensor", result, this, formatSenseablePropName(propName)),
|
|
1053
|
+
],
|
|
1054
|
+
];
|
|
1055
|
+
}
|
|
1056
|
+
if (prop instanceof StoreValue) {
|
|
1057
|
+
const temp = StoreValue.from(scope, out, mutability);
|
|
1058
|
+
return [temp, [new InstructionBase("sensor", temp, this, prop)]];
|
|
1059
|
+
}
|
|
1060
|
+
throw new CompilerError(`The property [${prop.debugString()}] cannot be sensed`);
|
|
1061
|
+
}
|
|
1062
|
+
hasProperty(scope, prop) {
|
|
1063
|
+
return ((prop instanceof LiteralValue && prop.isString()) ||
|
|
1064
|
+
prop instanceof StoreValue);
|
|
1065
|
+
}
|
|
939
1066
|
debugString() {
|
|
940
1067
|
return `StoreValue("${this.name}")`;
|
|
941
1068
|
}
|
|
942
|
-
|
|
1069
|
+
toMlogString() {
|
|
943
1070
|
return this.name;
|
|
944
1071
|
}
|
|
945
1072
|
}
|
|
1073
|
+
function formatSenseablePropName(name) {
|
|
1074
|
+
if (itemNames.includes(name))
|
|
1075
|
+
return "@" + camelToDashCase(name);
|
|
1076
|
+
return "@" + name;
|
|
1077
|
+
}
|
|
946
1078
|
function compareStores(left, right) {
|
|
947
1079
|
return right instanceof StoreValue && right.name === left.name;
|
|
948
1080
|
}
|
|
1081
|
+
/**
|
|
1082
|
+
* If `prop` is sensing on of the coordinates of `@this`, returns the coordinate
|
|
1083
|
+
* name.
|
|
1084
|
+
*/
|
|
1085
|
+
function getThisCoordName(value, prop) {
|
|
1086
|
+
if (value.name !== "@this")
|
|
1087
|
+
return;
|
|
1088
|
+
let name;
|
|
1089
|
+
if (prop instanceof LiteralValue && prop.isString()) {
|
|
1090
|
+
name = prop.data;
|
|
1091
|
+
}
|
|
1092
|
+
else if (prop instanceof StoreValue && prop.name.startsWith("@")) {
|
|
1093
|
+
name = prop.name.slice(1);
|
|
1094
|
+
}
|
|
1095
|
+
if (name === "x" || name === "y")
|
|
1096
|
+
return name;
|
|
1097
|
+
}
|
|
949
1098
|
|
|
950
1099
|
class ObjectValue extends VoidValue {
|
|
951
1100
|
constructor(data = {}) {
|
|
@@ -965,25 +1114,18 @@ class ObjectValue extends VoidValue {
|
|
|
965
1114
|
});
|
|
966
1115
|
return new ObjectValue(data);
|
|
967
1116
|
}
|
|
968
|
-
typeof(scope, out) {
|
|
969
|
-
const { $typeof } = this.data;
|
|
970
|
-
if ($typeof)
|
|
971
|
-
return $typeof.call(scope, [], out);
|
|
972
|
-
return [new LiteralValue("object"), []];
|
|
973
|
-
}
|
|
974
1117
|
get(scope, key, out) {
|
|
975
1118
|
if (key instanceof LiteralValue && (key.isNumber() || key.isString())) {
|
|
976
1119
|
// avoids naming collisions with keys like
|
|
977
1120
|
// constructor or toString
|
|
978
1121
|
if (Object.prototype.hasOwnProperty.call(this.data, key.data)) {
|
|
979
|
-
|
|
980
|
-
|
|
1122
|
+
const member = this.data[key.data];
|
|
1123
|
+
if (out)
|
|
1124
|
+
return member.eval(scope, out);
|
|
1125
|
+
return [member, []];
|
|
981
1126
|
}
|
|
982
1127
|
}
|
|
983
|
-
|
|
984
|
-
if (!$get)
|
|
985
|
-
throw new CompilerError(`The member [${key.debugString()}] is not present in [${this.debugString()}]`);
|
|
986
|
-
return $get.call(scope, [key], out);
|
|
1128
|
+
throw new CompilerError(`The member [${key.debugString()}] is not present in [${this.debugString()}]`);
|
|
987
1129
|
}
|
|
988
1130
|
hasProperty(scope, prop) {
|
|
989
1131
|
if (prop instanceof LiteralValue && (prop.isNumber() || prop.isString())) {
|
|
@@ -992,22 +1134,10 @@ class ObjectValue extends VoidValue {
|
|
|
992
1134
|
}
|
|
993
1135
|
return false;
|
|
994
1136
|
}
|
|
995
|
-
eval(
|
|
996
|
-
|
|
997
|
-
if (!$eval)
|
|
998
|
-
return [this, []];
|
|
999
|
-
return $eval.call(scope, [], out);
|
|
1000
|
-
}
|
|
1001
|
-
call(scope, args, out) {
|
|
1002
|
-
const { $call } = this.data;
|
|
1003
|
-
if (!$call)
|
|
1004
|
-
return super.call(scope, args, out);
|
|
1005
|
-
return $call.call(scope, args, out);
|
|
1137
|
+
eval() {
|
|
1138
|
+
return [this, []];
|
|
1006
1139
|
}
|
|
1007
|
-
"??"(
|
|
1008
|
-
const $ = this.data["$??"];
|
|
1009
|
-
if ($)
|
|
1010
|
-
return $.call(scope, [other], out);
|
|
1140
|
+
"??"() {
|
|
1011
1141
|
return [this, []];
|
|
1012
1142
|
}
|
|
1013
1143
|
debugString() {
|
|
@@ -1015,108 +1145,17 @@ class ObjectValue extends VoidValue {
|
|
|
1015
1145
|
return `ObjectValue("${this.name}")`;
|
|
1016
1146
|
return "ObjectValue";
|
|
1017
1147
|
}
|
|
1018
|
-
|
|
1148
|
+
toMlogString() {
|
|
1019
1149
|
return '"[macro ObjectValue]"';
|
|
1020
1150
|
}
|
|
1021
1151
|
}
|
|
1022
|
-
for (const op of leftRightOperators) {
|
|
1023
|
-
if (op === "??")
|
|
1024
|
-
continue;
|
|
1025
|
-
ObjectValue.prototype[op] = function (scope, value, out) {
|
|
1026
|
-
const $ = this.data[`$${op}`];
|
|
1027
|
-
if (!$)
|
|
1028
|
-
return VoidValue.prototype[op].apply(this, [scope, value, out]);
|
|
1029
|
-
return $.call(scope, [value], out);
|
|
1030
|
-
};
|
|
1031
|
-
}
|
|
1032
|
-
for (const op of unaryOperators) {
|
|
1033
|
-
if (op === "typeof")
|
|
1034
|
-
continue;
|
|
1035
|
-
ObjectValue.prototype[op] = function (scope, out) {
|
|
1036
|
-
const $ = this.data[`$${op}`];
|
|
1037
|
-
if (!$)
|
|
1038
|
-
return VoidValue.prototype[op].apply(this, [scope, out]);
|
|
1039
|
-
return $.call(scope, [], out);
|
|
1040
|
-
};
|
|
1041
|
-
}
|
|
1042
|
-
for (const op of updateOperators) {
|
|
1043
|
-
ObjectValue.prototype[op] = function (scope, prefix, out) {
|
|
1044
|
-
const $ = this.data[`$${op}`];
|
|
1045
|
-
if (!$)
|
|
1046
|
-
return VoidValue.prototype[op].apply(this, [scope, prefix, out]);
|
|
1047
|
-
return $.call(scope, [new LiteralValue(+prefix)], out);
|
|
1048
|
-
};
|
|
1049
|
-
}
|
|
1050
|
-
|
|
1051
|
-
/**
|
|
1052
|
-
* Properties that should return a new `SenseableValue`
|
|
1053
|
-
*/
|
|
1054
|
-
const senseableProps = ["controller"];
|
|
1055
|
-
/**
|
|
1056
|
-
* Senseable values are "objects" in the mlog runtime that
|
|
1057
|
-
* have special properties that can be accessed
|
|
1058
|
-
* via the `sense` instruction.
|
|
1059
|
-
*
|
|
1060
|
-
* This class extends `StoreValue` to allow dynamic behavior
|
|
1061
|
-
* that is not possible with object macros.
|
|
1062
|
-
*
|
|
1063
|
-
* Note that these values are constant by default, so make sure
|
|
1064
|
-
* to set that field to `EMutability.mutable` if you want them to be assignable. Example:
|
|
1065
|
-
* ```
|
|
1066
|
-
* const value = new SenseableValue(name, EMutability.mutable);
|
|
1067
|
-
* ```
|
|
1068
|
-
*/
|
|
1069
|
-
class SenseableValue extends StoreValue {
|
|
1070
|
-
constructor(name, mutability = EMutability.constant) {
|
|
1071
|
-
super(name, mutability);
|
|
1072
|
-
}
|
|
1073
|
-
static from(scope, out, mutability = EMutability.constant) {
|
|
1074
|
-
if (out instanceof SenseableValue)
|
|
1075
|
-
return out;
|
|
1076
|
-
const name = typeof out === "string" ? out : scope.makeTempName();
|
|
1077
|
-
return new SenseableValue(name, mutability);
|
|
1078
|
-
}
|
|
1079
|
-
static out(scope, out, mutability = EMutability.mutable) {
|
|
1080
|
-
if (!out || typeof out === "string") {
|
|
1081
|
-
return new SenseableValue(out ?? scope.makeTempName(), mutability);
|
|
1082
|
-
}
|
|
1083
|
-
return out;
|
|
1084
|
-
}
|
|
1085
|
-
get(scope, prop, out) {
|
|
1086
|
-
const mutability = EMutability.readonly;
|
|
1087
|
-
if (prop instanceof LiteralValue && prop.isString()) {
|
|
1088
|
-
const name = itemNames.includes(prop.data)
|
|
1089
|
-
? camelToDashCase(prop.data)
|
|
1090
|
-
: prop.data;
|
|
1091
|
-
const result = senseableProps.includes(prop.data)
|
|
1092
|
-
? SenseableValue.from(scope, out, mutability)
|
|
1093
|
-
: StoreValue.from(scope, out, mutability);
|
|
1094
|
-
return [
|
|
1095
|
-
result,
|
|
1096
|
-
[new InstructionBase("sensor", result, this, `@${name}`)],
|
|
1097
|
-
];
|
|
1098
|
-
}
|
|
1099
|
-
if (prop instanceof StoreValue) {
|
|
1100
|
-
const temp = SenseableValue.from(scope, out, mutability);
|
|
1101
|
-
return [temp, [new InstructionBase("sensor", temp, this, prop)]];
|
|
1102
|
-
}
|
|
1103
|
-
throw new CompilerError(`The property [${prop.debugString()}] cannot be sensed`);
|
|
1104
|
-
}
|
|
1105
|
-
debugString() {
|
|
1106
|
-
return `SenseableValue("${this.name}")`;
|
|
1107
|
-
}
|
|
1108
|
-
hasProperty(scope, prop) {
|
|
1109
|
-
return ((prop instanceof LiteralValue && prop.isString()) ||
|
|
1110
|
-
prop instanceof StoreValue);
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
1152
|
|
|
1114
1153
|
/**
|
|
1115
1154
|
* Specific case class, required on the ArrayPattern and ObjectPattern handlers
|
|
1116
1155
|
* in order to keep the separation of concerns and make the code more modular.
|
|
1117
1156
|
*
|
|
1118
|
-
* When it is assign a value, it will get it's own properties and recursively
|
|
1119
|
-
* each of them to their counterparts on the right hand value.
|
|
1157
|
+
* When it is assign a value, it will get it's own properties and recursively
|
|
1158
|
+
* assign each of them to their counterparts on the right hand value.
|
|
1120
1159
|
*/
|
|
1121
1160
|
class DestructuringValue extends VoidValue {
|
|
1122
1161
|
constructor(members) {
|
|
@@ -1143,14 +1182,14 @@ class DestructuringValue extends VoidValue {
|
|
|
1143
1182
|
debugString() {
|
|
1144
1183
|
return "DestructuringValue";
|
|
1145
1184
|
}
|
|
1146
|
-
|
|
1185
|
+
toMlogString() {
|
|
1147
1186
|
return '"[macro DestructuringValue]"';
|
|
1148
1187
|
}
|
|
1149
1188
|
}
|
|
1150
1189
|
|
|
1151
1190
|
/**
|
|
1152
|
-
* This class is used to handle some cases where the value
|
|
1153
|
-
*
|
|
1191
|
+
* This class is used to handle some cases where the value can't be directly
|
|
1192
|
+
* evaluated by the node handler
|
|
1154
1193
|
*/
|
|
1155
1194
|
class LazyValue extends VoidValue {
|
|
1156
1195
|
constructor(evalutate) {
|
|
@@ -1160,7 +1199,7 @@ class LazyValue extends VoidValue {
|
|
|
1160
1199
|
debugString() {
|
|
1161
1200
|
return "LazyValue";
|
|
1162
1201
|
}
|
|
1163
|
-
|
|
1202
|
+
toMlogString() {
|
|
1164
1203
|
return '"[macro LazyValue]"';
|
|
1165
1204
|
}
|
|
1166
1205
|
}
|
|
@@ -1184,26 +1223,31 @@ class AssignmentValue extends VoidValue {
|
|
|
1184
1223
|
debugString() {
|
|
1185
1224
|
return "AssignmentValue";
|
|
1186
1225
|
}
|
|
1187
|
-
|
|
1226
|
+
toMlogString() {
|
|
1188
1227
|
return '"[macro AssignmentValue]"';
|
|
1189
1228
|
}
|
|
1190
1229
|
}
|
|
1191
1230
|
|
|
1192
1231
|
class Scope {
|
|
1193
|
-
constructor(
|
|
1232
|
+
constructor({ data = {}, parent = null, ntemp = 0, name = "", inst = [], operationCache = {}, cacheDependencies = {}, builtInModules = {}, } = {}) {
|
|
1233
|
+
// Only `unchecked` is supposed to change this
|
|
1234
|
+
this.checkIndexes = true;
|
|
1235
|
+
this.data = data;
|
|
1194
1236
|
this.parent = parent;
|
|
1195
|
-
this.stacked = stacked;
|
|
1196
1237
|
this.ntemp = ntemp;
|
|
1197
1238
|
this.name = name;
|
|
1198
1239
|
this.inst = inst;
|
|
1199
1240
|
this.operationCache = operationCache;
|
|
1200
1241
|
this.cacheDependencies = cacheDependencies;
|
|
1201
|
-
|
|
1202
|
-
this.checkIndexes = true;
|
|
1203
|
-
this.data = values;
|
|
1242
|
+
this.builtInModules = builtInModules;
|
|
1204
1243
|
}
|
|
1205
1244
|
copy() {
|
|
1206
|
-
const scope = new Scope({
|
|
1245
|
+
const scope = new Scope({
|
|
1246
|
+
...this,
|
|
1247
|
+
data: { ...this.data },
|
|
1248
|
+
operationCache: { ...this.operationCache },
|
|
1249
|
+
cacheDependencies: { ...this.cacheDependencies },
|
|
1250
|
+
});
|
|
1207
1251
|
scope.break = this.break;
|
|
1208
1252
|
scope.continue = this.continue;
|
|
1209
1253
|
scope.function = this.function;
|
|
@@ -1217,8 +1261,14 @@ class Scope {
|
|
|
1217
1261
|
scope.parent = this;
|
|
1218
1262
|
return scope;
|
|
1219
1263
|
}
|
|
1220
|
-
createFunction(name
|
|
1221
|
-
return new Scope({
|
|
1264
|
+
createFunction(name) {
|
|
1265
|
+
return new Scope({
|
|
1266
|
+
data: {},
|
|
1267
|
+
parent: this,
|
|
1268
|
+
ntemp: 0,
|
|
1269
|
+
name,
|
|
1270
|
+
inst: this.inst,
|
|
1271
|
+
});
|
|
1222
1272
|
}
|
|
1223
1273
|
has(identifier) {
|
|
1224
1274
|
if (identifier in this.data)
|
|
@@ -1233,7 +1283,15 @@ class Scope {
|
|
|
1233
1283
|
return value;
|
|
1234
1284
|
if (this.parent)
|
|
1235
1285
|
return this.parent.get(identifier);
|
|
1236
|
-
|
|
1286
|
+
let message = `${identifier} is not declared.`;
|
|
1287
|
+
const name = new LiteralValue(identifier);
|
|
1288
|
+
for (const moduleName in this.builtInModules) {
|
|
1289
|
+
const module = this.builtInModules[moduleName];
|
|
1290
|
+
if (module.hasProperty(this, name)) {
|
|
1291
|
+
message += ` Did you mean to use "${identifier}" exported from "${moduleName}"?`;
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
throw new CompilerError(message);
|
|
1237
1295
|
}
|
|
1238
1296
|
set(name, value) {
|
|
1239
1297
|
if (name in this.data)
|
|
@@ -1247,9 +1305,7 @@ class Scope {
|
|
|
1247
1305
|
return value;
|
|
1248
1306
|
}
|
|
1249
1307
|
make(identifier, name) {
|
|
1250
|
-
const value =
|
|
1251
|
-
mutability: EMutability.mutable,
|
|
1252
|
-
});
|
|
1308
|
+
const value = new StoreValue(name);
|
|
1253
1309
|
return this.set(identifier, value);
|
|
1254
1310
|
}
|
|
1255
1311
|
makeTempName() {
|
|
@@ -1262,16 +1318,19 @@ class Scope {
|
|
|
1262
1318
|
addCachedOperation(op, result, left, right) {
|
|
1263
1319
|
if (!left.name || !result.name || (right && !right.name))
|
|
1264
1320
|
return;
|
|
1321
|
+
if (result.volatile || left.volatile || right?.volatile)
|
|
1322
|
+
return;
|
|
1265
1323
|
const id = formatCacheId(op, left, right);
|
|
1266
1324
|
this.operationCache[id] = result;
|
|
1267
1325
|
this.clearDependentCache(result);
|
|
1268
1326
|
addCacheDependency(this, left.name, id);
|
|
1269
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1270
1327
|
if (right)
|
|
1271
1328
|
addCacheDependency(this, right.name, id);
|
|
1272
1329
|
addCacheDependency(this, result.name, id);
|
|
1273
1330
|
}
|
|
1274
1331
|
getCachedOperation(op, left, right) {
|
|
1332
|
+
if (left.volatile || right?.volatile)
|
|
1333
|
+
return;
|
|
1275
1334
|
const id = formatCacheId(op, left, right);
|
|
1276
1335
|
const result = this.operationCache[id];
|
|
1277
1336
|
if (result)
|
|
@@ -1310,6 +1369,9 @@ const VariableDeclaration = (c, scope, node) => {
|
|
|
1310
1369
|
};
|
|
1311
1370
|
const VariableDeclarator = (c, scope, node, out, kind = "let") => {
|
|
1312
1371
|
const { init } = node;
|
|
1372
|
+
if (es.isVoidPattern(node.id)) {
|
|
1373
|
+
throw new CompilerError(`Cannot declare a variable with a void pattern`, node.id);
|
|
1374
|
+
}
|
|
1313
1375
|
const [value, inst] = Declare(c, scope, node.id, kind);
|
|
1314
1376
|
if (init) {
|
|
1315
1377
|
const [result, resultInst] = c.handleEval(scope, init, value.out);
|
|
@@ -1339,7 +1401,7 @@ const Declare = (c, scope, node, kind) => {
|
|
|
1339
1401
|
const DeclareIdentifier = (c, scope, node, kind) => {
|
|
1340
1402
|
const { name: identifier } = node;
|
|
1341
1403
|
const name = nodeName(node, !c.compactNames && identifier);
|
|
1342
|
-
const out = new
|
|
1404
|
+
const out = new StoreValue(name, kind === "const" ? EMutability.init : EMutability.mutable);
|
|
1343
1405
|
const declarationValue = new DeclarationValue({
|
|
1344
1406
|
c,
|
|
1345
1407
|
scope,
|
|
@@ -1348,7 +1410,9 @@ const DeclareIdentifier = (c, scope, node, kind) => {
|
|
|
1348
1410
|
handler(init, inst) {
|
|
1349
1411
|
if (kind === "const" && !init)
|
|
1350
1412
|
throw new CompilerError("Constants must be initialized.", node);
|
|
1351
|
-
if (kind === "const" &&
|
|
1413
|
+
if (kind === "const" &&
|
|
1414
|
+
(init?.mutability === EMutability.constant ||
|
|
1415
|
+
init?.mutability === EMutability.immutable)) {
|
|
1352
1416
|
scope.set(identifier, init);
|
|
1353
1417
|
return [null, inst];
|
|
1354
1418
|
}
|
|
@@ -1360,7 +1424,7 @@ const DeclareIdentifier = (c, scope, node, kind) => {
|
|
|
1360
1424
|
pipeInsts(value["="](scope, init), inst);
|
|
1361
1425
|
}
|
|
1362
1426
|
if (kind === "const")
|
|
1363
|
-
value.mutability = EMutability.
|
|
1427
|
+
value.mutability = EMutability.immutable;
|
|
1364
1428
|
return [null, inst];
|
|
1365
1429
|
}
|
|
1366
1430
|
},
|
|
@@ -1474,7 +1538,7 @@ class DeclarationValue extends VoidValue {
|
|
|
1474
1538
|
debugString() {
|
|
1475
1539
|
return "DeclarationValue";
|
|
1476
1540
|
}
|
|
1477
|
-
|
|
1541
|
+
toMlogString() {
|
|
1478
1542
|
return '"[macro DeclarationValue]"';
|
|
1479
1543
|
}
|
|
1480
1544
|
}
|
|
@@ -1508,23 +1572,92 @@ const LRExpression = (c, scope, node, out) => {
|
|
|
1508
1572
|
return [op, [...leftInst, ...rightInst, ...opInst]];
|
|
1509
1573
|
};
|
|
1510
1574
|
const BinaryExpression = LRExpression;
|
|
1511
|
-
const LogicalExpression = (c, scope, node, out) => {
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1575
|
+
const LogicalExpression = (c, scope, node, out, arg) => {
|
|
1576
|
+
// TODO: these expressions can be optimized
|
|
1577
|
+
// to skip some jump checks inside if statements.
|
|
1578
|
+
// Remove this comment if this optimization is
|
|
1579
|
+
// really not necessary
|
|
1580
|
+
// patch to prevent the jump out value
|
|
1581
|
+
// from being double-used later in the handler
|
|
1582
|
+
if (out instanceof JumpOutValue) {
|
|
1583
|
+
out = StoreValue.from(scope);
|
|
1584
|
+
}
|
|
1585
|
+
let leftNodeArg;
|
|
1586
|
+
let rightNodeArg;
|
|
1587
|
+
let operatorAddress;
|
|
1588
|
+
let afterRightAddress;
|
|
1589
|
+
let beforeRightAddress;
|
|
1590
|
+
switch (node.operator) {
|
|
1591
|
+
case "||": {
|
|
1592
|
+
const { onFalsyAddress = new LiteralValue(null), onTruthyAddress = new LiteralValue(null), } = arg ?? {};
|
|
1593
|
+
leftNodeArg = {
|
|
1594
|
+
onTruthyAddress,
|
|
1595
|
+
onFalsyAddress,
|
|
1596
|
+
};
|
|
1597
|
+
rightNodeArg = { onTruthyAddress };
|
|
1598
|
+
operatorAddress = onTruthyAddress;
|
|
1599
|
+
if (!arg?.onFalsyAddress)
|
|
1600
|
+
beforeRightAddress = onFalsyAddress;
|
|
1601
|
+
if (!arg?.onTruthyAddress)
|
|
1602
|
+
afterRightAddress = onTruthyAddress;
|
|
1603
|
+
break;
|
|
1604
|
+
}
|
|
1605
|
+
case "&&": {
|
|
1606
|
+
const { onFalsyAddress = new LiteralValue(null), onTruthyAddress = new LiteralValue(null), } = arg ?? {};
|
|
1607
|
+
leftNodeArg = { onTruthyAddress, onFalsyAddress };
|
|
1608
|
+
rightNodeArg = { onFalsyAddress };
|
|
1609
|
+
operatorAddress = onFalsyAddress;
|
|
1610
|
+
if (!arg?.onTruthyAddress)
|
|
1611
|
+
beforeRightAddress = onTruthyAddress;
|
|
1612
|
+
if (!arg?.onFalsyAddress)
|
|
1613
|
+
afterRightAddress = onFalsyAddress;
|
|
1614
|
+
break;
|
|
1615
|
+
}
|
|
1616
|
+
case "??": {
|
|
1617
|
+
const { onNonNullishAddress = new LiteralValue(null) } = arg ?? {};
|
|
1618
|
+
leftNodeArg = {
|
|
1619
|
+
onNonNullishAddress,
|
|
1620
|
+
onTruthyAddress: onNonNullishAddress,
|
|
1621
|
+
};
|
|
1622
|
+
rightNodeArg = {
|
|
1623
|
+
onNonNullishAddress,
|
|
1624
|
+
onTruthyAddress: onNonNullishAddress,
|
|
1625
|
+
};
|
|
1626
|
+
operatorAddress = onNonNullishAddress;
|
|
1627
|
+
if (!arg?.onNonNullishAddress)
|
|
1628
|
+
afterRightAddress = onNonNullishAddress;
|
|
1629
|
+
break;
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
const other = new LazyValue((scope, out) => {
|
|
1633
|
+
const [value, inst] = c.handleEval(scope, node.right, out, node.right.type === "LogicalExpression" ? rightNodeArg : undefined);
|
|
1634
|
+
if (!beforeRightAddress)
|
|
1635
|
+
return [value, inst];
|
|
1636
|
+
return [value, [new AddressResolver(beforeRightAddress), ...inst]];
|
|
1637
|
+
});
|
|
1638
|
+
const [left, leftInst] = c.handleEval(scope, node.left, out, node.left.type === "LogicalExpression" ? leftNodeArg : undefined);
|
|
1639
|
+
// patch for situations where `left` is a temporary value and `out` is `undefined`
|
|
1640
|
+
// since `out` is undefined there is potential for up to three temp values
|
|
1641
|
+
// to be created. So to prevent that we will try to reuse `left` as an out value
|
|
1642
|
+
// whenever possible
|
|
1643
|
+
const resultOut = reuseTemporaryValue(left, out);
|
|
1644
|
+
const [result, resultInst] = left[node.operator](scope, other, resultOut, operatorAddress);
|
|
1645
|
+
return [
|
|
1646
|
+
result,
|
|
1647
|
+
[
|
|
1648
|
+
...leftInst,
|
|
1649
|
+
...resultInst,
|
|
1650
|
+
...(afterRightAddress ? [new AddressResolver(afterRightAddress)] : []),
|
|
1651
|
+
],
|
|
1652
|
+
];
|
|
1518
1653
|
};
|
|
1654
|
+
const logicalAssignmentOperators = ["??=", "||=", "&&="];
|
|
1519
1655
|
const AssignmentExpression = (c, scope, node) => {
|
|
1520
1656
|
const [left, leftInst] = c.handleValue(scope, node.left);
|
|
1521
1657
|
const leftOutput = left.toOut();
|
|
1522
|
-
const [right, rightInst] = node.operator
|
|
1658
|
+
const [right, rightInst] = !logicalAssignmentOperators.includes(node.operator)
|
|
1523
1659
|
? c.handleEval(scope, node.right, node.operator === "=" ? leftOutput : undefined)
|
|
1524
|
-
: [
|
|
1525
|
-
new LazyValue((scope, out) => c.handleEval(scope, node.right, out)),
|
|
1526
|
-
[],
|
|
1527
|
-
];
|
|
1660
|
+
: [new LazyValue((scope, out) => c.handleEval(scope, node.right, out)), []];
|
|
1528
1661
|
const [op, opInst] = left[node.operator](scope, right);
|
|
1529
1662
|
scope.clearDependentCache(left);
|
|
1530
1663
|
scope.clearDependentCache(leftOutput);
|
|
@@ -1548,7 +1681,60 @@ const UpdateExpression = (c, scope, { argument, operator, prefix }, out) => {
|
|
|
1548
1681
|
const [op, opInst] = arg[operator](scope, prefix, out);
|
|
1549
1682
|
return [op, [...argInst, ...opInst]];
|
|
1550
1683
|
};
|
|
1684
|
+
const selectOperatorMap = {
|
|
1685
|
+
"==": "equal",
|
|
1686
|
+
"!=": "notEqual",
|
|
1687
|
+
"<": "lessThan",
|
|
1688
|
+
"<=": "lessThanEq",
|
|
1689
|
+
">": "greaterThan",
|
|
1690
|
+
">=": "greaterThanEq",
|
|
1691
|
+
"===": "strictEqual",
|
|
1692
|
+
};
|
|
1551
1693
|
const ConditionalExpression = (c, scope, node, out) => {
|
|
1694
|
+
if (node.test.type === "BooleanLiteral" ||
|
|
1695
|
+
node.test.type === "NumericLiteral" ||
|
|
1696
|
+
node.test.type === "StringLiteral") {
|
|
1697
|
+
if (node.test.value)
|
|
1698
|
+
return c.handleEval(scope, node.consequent, out);
|
|
1699
|
+
return c.handleEval(scope, node.alternate, out);
|
|
1700
|
+
}
|
|
1701
|
+
const result = StoreValue.from(scope, out);
|
|
1702
|
+
const consequent = c.handleEval(scope, node.consequent, result);
|
|
1703
|
+
const alternate = c.handleEval(scope, node.alternate, result);
|
|
1704
|
+
if (consequent[1].length === 0 && alternate[1].length === 0) {
|
|
1705
|
+
if (node.test.type === "BinaryExpression" &&
|
|
1706
|
+
(node.test.operator in selectOperatorMap || node.test.operator === "!==")) {
|
|
1707
|
+
const [left, leftInst] = c.handleEval(scope, node.test.left);
|
|
1708
|
+
const [right, rightInst] = c.handleEval(scope, node.test.right);
|
|
1709
|
+
const isStrictNotEqual = node.test.operator === "!==";
|
|
1710
|
+
const op = isStrictNotEqual
|
|
1711
|
+
? "strictEqual"
|
|
1712
|
+
: selectOperatorMap[node.test.operator];
|
|
1713
|
+
const trueVal = isStrictNotEqual ? alternate[0] : consequent[0];
|
|
1714
|
+
const falseVal = isStrictNotEqual ? consequent[0] : alternate[0];
|
|
1715
|
+
return [
|
|
1716
|
+
result,
|
|
1717
|
+
[
|
|
1718
|
+
...leftInst,
|
|
1719
|
+
...rightInst,
|
|
1720
|
+
new InstructionBase("select", result, op, left, right, trueVal, falseVal),
|
|
1721
|
+
],
|
|
1722
|
+
];
|
|
1723
|
+
}
|
|
1724
|
+
const [test, testInst] = c.handleEval(scope, node.test);
|
|
1725
|
+
if (test instanceof LiteralValue) {
|
|
1726
|
+
if (test.data)
|
|
1727
|
+
return [result, [...result["="](scope, consequent[0])[1]]];
|
|
1728
|
+
return [result, [...result["="](scope, alternate[0])[1]]];
|
|
1729
|
+
}
|
|
1730
|
+
return [
|
|
1731
|
+
result,
|
|
1732
|
+
[
|
|
1733
|
+
...testInst,
|
|
1734
|
+
new InstructionBase("select", result, "notEqual", test, "0", consequent[0], alternate[0]),
|
|
1735
|
+
],
|
|
1736
|
+
];
|
|
1737
|
+
}
|
|
1552
1738
|
const alternateStartAdress = new LiteralValue(null);
|
|
1553
1739
|
const endExpressionAdress = new LiteralValue(null);
|
|
1554
1740
|
const testOut = new JumpOutValue(node, alternateStartAdress, false);
|
|
@@ -1558,9 +1744,6 @@ const ConditionalExpression = (c, scope, node, out) => {
|
|
|
1558
1744
|
return c.handleEval(scope, node.consequent, out);
|
|
1559
1745
|
return c.handleEval(scope, node.alternate, out);
|
|
1560
1746
|
}
|
|
1561
|
-
const result = SenseableValue.from(scope, out, EMutability.mutable);
|
|
1562
|
-
const consequent = c.handleEval(scope, node.consequent, result);
|
|
1563
|
-
const alternate = c.handleEval(scope, node.alternate, result);
|
|
1564
1747
|
return [
|
|
1565
1748
|
result,
|
|
1566
1749
|
[
|
|
@@ -1586,6 +1769,13 @@ const SequenceExpression = (c, scope, node, out) => {
|
|
|
1586
1769
|
const value = pipeInsts(c.handleEval(scope, expressions[expressions.length - 1], out), inst);
|
|
1587
1770
|
return [value, inst];
|
|
1588
1771
|
};
|
|
1772
|
+
function reuseTemporaryValue(value, out) {
|
|
1773
|
+
if (value instanceof StoreValue && value.temporary)
|
|
1774
|
+
return new StoreValue(value.name, EMutability.mutable, {
|
|
1775
|
+
temporary: true,
|
|
1776
|
+
});
|
|
1777
|
+
return out;
|
|
1778
|
+
}
|
|
1589
1779
|
|
|
1590
1780
|
const Identifier = (c, scope, node) => {
|
|
1591
1781
|
return [scope.get(node.name), []];
|
|
@@ -1669,8 +1859,9 @@ const IfStatement = (c, scope, node) => {
|
|
|
1669
1859
|
return [null, inst];
|
|
1670
1860
|
};
|
|
1671
1861
|
/**
|
|
1672
|
-
* Returns `true` if the instruction array ends with an instruction
|
|
1673
|
-
*
|
|
1862
|
+
* Returns `true` if the instruction array ends with an instruction that already
|
|
1863
|
+
* changes the control flow of the script (like `jump x always`, `end` , `stop`,
|
|
1864
|
+
* etc)
|
|
1674
1865
|
*/
|
|
1675
1866
|
function endsWithControlFlow(inst) {
|
|
1676
1867
|
for (let i = inst.length - 1; i >= 0; i--) {
|
|
@@ -1715,7 +1906,7 @@ const SwitchStatement = (c, scope, node) => {
|
|
|
1715
1906
|
const bodyInst = lazy(() => c.handleMany(createCaseScope(innerScope), scase.consequent)[1]);
|
|
1716
1907
|
const bodyAdress = new LiteralValue(null);
|
|
1717
1908
|
const bodyLine = new AddressResolver(bodyAdress);
|
|
1718
|
-
const canFallInto = !endsWithoutFalltrough(inst
|
|
1909
|
+
const canFallInto = !endsWithoutFalltrough(inst);
|
|
1719
1910
|
let includeJump = !constantCase;
|
|
1720
1911
|
const includeBody = !constantCase || canFallInto;
|
|
1721
1912
|
if (!scase.test) {
|
|
@@ -1751,7 +1942,7 @@ const SwitchStatement = (c, scope, node) => {
|
|
|
1751
1942
|
// and all the other cases will never run
|
|
1752
1943
|
if (noFallInto &&
|
|
1753
1944
|
onlyConstantTests &&
|
|
1754
|
-
(isLastCase || endsWithoutFalltrough(bodyInst()
|
|
1945
|
+
(isLastCase || endsWithoutFalltrough(bodyInst()))) {
|
|
1755
1946
|
return [null, [...bodyInst(), endLine]];
|
|
1756
1947
|
}
|
|
1757
1948
|
jump = c.handle(innerScope, scase, () => [
|
|
@@ -1789,7 +1980,7 @@ const SwitchStatement = (c, scope, node) => {
|
|
|
1789
1980
|
],
|
|
1790
1981
|
];
|
|
1791
1982
|
};
|
|
1792
|
-
function endsWithoutFalltrough(inst
|
|
1983
|
+
function endsWithoutFalltrough(inst) {
|
|
1793
1984
|
if (inst.length === 0)
|
|
1794
1985
|
return true;
|
|
1795
1986
|
for (let i = inst.length - 1; i >= 0; i--) {
|
|
@@ -1804,10 +1995,7 @@ function endsWithoutFalltrough(inst, endLine) {
|
|
|
1804
1995
|
return false;
|
|
1805
1996
|
if (instruction.hidden)
|
|
1806
1997
|
continue;
|
|
1807
|
-
return
|
|
1808
|
-
instruction.intent === EInstIntent.return ||
|
|
1809
|
-
(instruction instanceof BreakInstruction &&
|
|
1810
|
-
endLine.bonds.includes(instruction.address)));
|
|
1998
|
+
return instruction.intent !== EInstIntent.none;
|
|
1811
1999
|
}
|
|
1812
2000
|
return false;
|
|
1813
2001
|
}
|
|
@@ -1934,6 +2122,7 @@ const ForStatement = (c, parentScope, node) => {
|
|
|
1934
2122
|
return [null, [...initInst]];
|
|
1935
2123
|
}
|
|
1936
2124
|
const isInfiniteLoop = test instanceof LiteralValue;
|
|
2125
|
+
const [, bodyLines] = withAlwaysRuns(c.handle(scope, node.body), false);
|
|
1937
2126
|
return [
|
|
1938
2127
|
null,
|
|
1939
2128
|
[
|
|
@@ -1941,17 +2130,19 @@ const ForStatement = (c, parentScope, node) => {
|
|
|
1941
2130
|
startLoopLine,
|
|
1942
2131
|
...testLines,
|
|
1943
2132
|
...(isInfiniteLoop ? [] : JumpInstruction.or(test, testOut)),
|
|
1944
|
-
...
|
|
2133
|
+
...bodyLines,
|
|
1945
2134
|
beforeEndLine,
|
|
1946
2135
|
...updateLines,
|
|
1947
2136
|
new JumpInstruction(startLoopAddr, EJumpKind.Always),
|
|
1948
|
-
...(isInfiniteLoop
|
|
2137
|
+
...(isInfiniteLoop && !usesAddressResolver(endLoopLine, bodyLines)
|
|
2138
|
+
? []
|
|
2139
|
+
: [endLoopLine]),
|
|
1949
2140
|
],
|
|
1950
2141
|
];
|
|
1951
2142
|
};
|
|
1952
2143
|
|
|
1953
2144
|
class FunctionValue extends VoidValue {
|
|
1954
|
-
constructor({ scope, params, body, c, out, }) {
|
|
2145
|
+
constructor({ scope, params, body, c, out, inlineType, }) {
|
|
1955
2146
|
super();
|
|
1956
2147
|
this.mutability = EMutability.constant;
|
|
1957
2148
|
this.macro = true;
|
|
@@ -1968,9 +2159,7 @@ class FunctionValue extends VoidValue {
|
|
|
1968
2159
|
this.c = c;
|
|
1969
2160
|
this.params = params;
|
|
1970
2161
|
this.name = extractOutName(out) ?? scope.makeTempName();
|
|
1971
|
-
|
|
1972
|
-
typeof() {
|
|
1973
|
-
return [new LiteralValue("function"), []];
|
|
2162
|
+
this.inlineType = inlineType;
|
|
1974
2163
|
}
|
|
1975
2164
|
initScope() {
|
|
1976
2165
|
this.childScope = this.scope.createFunction(this.name);
|
|
@@ -1991,8 +2180,10 @@ class FunctionValue extends VoidValue {
|
|
|
1991
2180
|
this.initialized = true;
|
|
1992
2181
|
this.initScope();
|
|
1993
2182
|
this.addr = new LiteralValue(null);
|
|
1994
|
-
this.temp = new
|
|
2183
|
+
this.temp = new StoreValue(`${internalPrefix}f${this.name}`);
|
|
1995
2184
|
this.ret = new StoreValue(`${internalPrefix}r${this.name}`);
|
|
2185
|
+
if (this.inlineType === EInlineType.always)
|
|
2186
|
+
return;
|
|
1996
2187
|
this.inst = [
|
|
1997
2188
|
new AddressResolver(this.addr),
|
|
1998
2189
|
...this.c.handle(this.childScope, this.body)[1],
|
|
@@ -2026,9 +2217,7 @@ class FunctionValue extends VoidValue {
|
|
|
2026
2217
|
const callAddressLiteral = new LiteralValue(null);
|
|
2027
2218
|
// the value will be stored somewhere
|
|
2028
2219
|
// no need to save it in a temporary variable
|
|
2029
|
-
const temp = out
|
|
2030
|
-
? this.temp
|
|
2031
|
-
: SenseableValue.from(scope, undefined, EMutability.mutable);
|
|
2220
|
+
const temp = out ? this.temp : StoreValue.from(scope);
|
|
2032
2221
|
const inst = this.paramValues
|
|
2033
2222
|
.map((param, i) => param["="](scope, args[i] ?? new LiteralValue(null))[1])
|
|
2034
2223
|
.reduce((s, c) => s.concat(c), [])
|
|
@@ -2053,7 +2242,7 @@ class FunctionValue extends VoidValue {
|
|
|
2053
2242
|
}
|
|
2054
2243
|
inlineCall(scope, args, out) {
|
|
2055
2244
|
// create return value
|
|
2056
|
-
this.inlineTemp =
|
|
2245
|
+
this.inlineTemp = StoreValue.from(this.childScope, out);
|
|
2057
2246
|
// make a copy of the function scope
|
|
2058
2247
|
const fnScope = this.childScope.copy();
|
|
2059
2248
|
this.tryingInline = true;
|
|
@@ -2084,20 +2273,24 @@ class FunctionValue extends VoidValue {
|
|
|
2084
2273
|
throw new CompilerError(`Cannot call: expected ${min} arguments but got: ${args.length}`);
|
|
2085
2274
|
}
|
|
2086
2275
|
validateParameters(args, this.paramValues);
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2276
|
+
if (this.inlineType !== EInlineType.never) {
|
|
2277
|
+
const inlineCall = this.inlineCall(scope, args, out);
|
|
2278
|
+
hideRedundantJumps(inlineCall[1]);
|
|
2279
|
+
if (this.inlineType === EInlineType.always)
|
|
2280
|
+
return inlineCall;
|
|
2281
|
+
const inlineSize = inlineCall[1].filter(i => !i.hidden).length;
|
|
2282
|
+
if (inlineSize <= this.callSize)
|
|
2283
|
+
return inlineCall;
|
|
2284
|
+
}
|
|
2092
2285
|
return this.normalCall(scope, args, out);
|
|
2093
2286
|
}
|
|
2094
2287
|
return(scope, arg) {
|
|
2095
2288
|
this.ensureInit();
|
|
2096
2289
|
if (arg && arg.macro) {
|
|
2097
|
-
this.
|
|
2290
|
+
this.inlineType = EInlineType.always;
|
|
2098
2291
|
return [null, []];
|
|
2099
2292
|
}
|
|
2100
|
-
if (this.
|
|
2293
|
+
if (this.tryingInline)
|
|
2101
2294
|
return this.inlineReturn(scope, arg);
|
|
2102
2295
|
return this.normalReturn(scope, arg);
|
|
2103
2296
|
}
|
|
@@ -2106,21 +2299,21 @@ class FunctionValue extends VoidValue {
|
|
|
2106
2299
|
}
|
|
2107
2300
|
preCall() {
|
|
2108
2301
|
this.ensureInit();
|
|
2109
|
-
if (this.
|
|
2302
|
+
if (this.inlineType === EInlineType.always || this.tryingInline)
|
|
2110
2303
|
return;
|
|
2111
2304
|
return this.paramOuts;
|
|
2112
2305
|
}
|
|
2113
2306
|
debugString() {
|
|
2114
2307
|
return `FunctionValue("${this.name}")`;
|
|
2115
2308
|
}
|
|
2116
|
-
|
|
2309
|
+
toMlogString() {
|
|
2117
2310
|
return '"[macro FunctionValue]"';
|
|
2118
2311
|
}
|
|
2119
2312
|
unwrapParameter(param) {
|
|
2120
2313
|
switch (param.type) {
|
|
2121
2314
|
case "Identifier": {
|
|
2122
2315
|
const name = nodeName(param, !this.c.compactNames && param.name);
|
|
2123
|
-
const value = new
|
|
2316
|
+
const value = new StoreValue(name);
|
|
2124
2317
|
this.childScope.set(param.name, value);
|
|
2125
2318
|
this.paramNames.set(value, param.name);
|
|
2126
2319
|
return value;
|
|
@@ -2129,7 +2322,7 @@ class FunctionValue extends VoidValue {
|
|
|
2129
2322
|
const left = this.unwrapParameter(param.left);
|
|
2130
2323
|
const [rightValue, rightInst] = this.c.handleEval(this.childScope, param.right, left);
|
|
2131
2324
|
const right = new LazyValue(scope => {
|
|
2132
|
-
if (this.
|
|
2325
|
+
if (this.tryingInline) {
|
|
2133
2326
|
return this.c.handleEval(scope, param.right, left);
|
|
2134
2327
|
}
|
|
2135
2328
|
return [rightValue, [...rightInst]];
|
|
@@ -2202,7 +2395,7 @@ class FunctionValue extends VoidValue {
|
|
|
2202
2395
|
}
|
|
2203
2396
|
}
|
|
2204
2397
|
hardSetParameter(scope, param, value = new LiteralValue(null)) {
|
|
2205
|
-
if (param instanceof
|
|
2398
|
+
if (param instanceof StoreValue) {
|
|
2206
2399
|
const name = this.paramNames.get(param);
|
|
2207
2400
|
scope.hardSet(name, value);
|
|
2208
2401
|
return [];
|
|
@@ -2260,20 +2453,22 @@ function validateParameters(args, params) {
|
|
|
2260
2453
|
|
|
2261
2454
|
function handleFunctionNode(c, scope, node, out) {
|
|
2262
2455
|
let { params, body } = node;
|
|
2263
|
-
if (
|
|
2456
|
+
if (es.isExpression(body)) {
|
|
2264
2457
|
const { loc } = body;
|
|
2265
|
-
const statement =
|
|
2458
|
+
const statement = es.returnStatement(body);
|
|
2266
2459
|
statement.loc = loc;
|
|
2267
|
-
body =
|
|
2460
|
+
body = es.blockStatement([statement]);
|
|
2268
2461
|
body.loc = loc;
|
|
2269
2462
|
}
|
|
2463
|
+
const inlineType = getInlineType(body.directives);
|
|
2270
2464
|
return [
|
|
2271
2465
|
new FunctionValue({
|
|
2272
2466
|
scope,
|
|
2273
|
-
params
|
|
2467
|
+
params,
|
|
2274
2468
|
body,
|
|
2275
2469
|
c,
|
|
2276
2470
|
out,
|
|
2471
|
+
inlineType,
|
|
2277
2472
|
}),
|
|
2278
2473
|
[],
|
|
2279
2474
|
];
|
|
@@ -2290,6 +2485,20 @@ const FunctionDeclaration = (c, scope, node) => {
|
|
|
2290
2485
|
};
|
|
2291
2486
|
const FunctionExpression = ArrowFunctionExpression;
|
|
2292
2487
|
const ObjectMethod = ArrowFunctionExpression;
|
|
2488
|
+
function getInlineType(directives) {
|
|
2489
|
+
for (const directive of directives) {
|
|
2490
|
+
const value = directive.value.value;
|
|
2491
|
+
switch (value) {
|
|
2492
|
+
case "inline":
|
|
2493
|
+
return EInlineType.always;
|
|
2494
|
+
case "inline never":
|
|
2495
|
+
return EInlineType.never;
|
|
2496
|
+
default:
|
|
2497
|
+
throw new CompilerError(`Invalid function directive: "${value}"`);
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
return EInlineType.auto;
|
|
2501
|
+
}
|
|
2293
2502
|
|
|
2294
2503
|
const CallExpression = (c, scope, node, out) => {
|
|
2295
2504
|
const inst = [];
|
|
@@ -2342,7 +2551,20 @@ const ObjectExpression = (c, scope, node, out) => {
|
|
|
2342
2551
|
}
|
|
2343
2552
|
const memberOut = extractDestrucuringOut(out, index);
|
|
2344
2553
|
const member = pipeInsts(c.handleEval(scope, value, memberOut), inst);
|
|
2345
|
-
|
|
2554
|
+
if (prop.type !== "ObjectMethod" || prop.kind === "method") {
|
|
2555
|
+
data[index] = member;
|
|
2556
|
+
}
|
|
2557
|
+
else {
|
|
2558
|
+
if (!(data[index] instanceof ObjectGetSetEntry))
|
|
2559
|
+
data[index] = new ObjectGetSetEntry();
|
|
2560
|
+
const entry = data[index];
|
|
2561
|
+
if (prop.kind === "get") {
|
|
2562
|
+
entry.getter = member;
|
|
2563
|
+
}
|
|
2564
|
+
else {
|
|
2565
|
+
entry.setter = member;
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2346
2568
|
}
|
|
2347
2569
|
return [new ObjectValue(data), inst];
|
|
2348
2570
|
};
|
|
@@ -2351,7 +2573,7 @@ const ArrayExpression = (c, scope, node, out) => {
|
|
|
2351
2573
|
const inst = [];
|
|
2352
2574
|
node.elements.forEach((element, i) => {
|
|
2353
2575
|
if (!element) {
|
|
2354
|
-
items.push(
|
|
2576
|
+
items.push(new LiteralValue(null));
|
|
2355
2577
|
return;
|
|
2356
2578
|
}
|
|
2357
2579
|
const value = pipeInsts(c.handleEval(scope, element, extractDestrucuringOut(out, i)), inst);
|
|
@@ -2370,7 +2592,7 @@ const MemberExpression = (c, scope, node, out, optional = false) => {
|
|
|
2370
2592
|
const [got, gotInst] = obj.get(scope, prop);
|
|
2371
2593
|
return [got, [...objInst, ...propInst, ...gotInst]];
|
|
2372
2594
|
}
|
|
2373
|
-
const temp =
|
|
2595
|
+
const temp = StoreValue.out(scope, out);
|
|
2374
2596
|
const objOut = new DestructuringValue(new Map([
|
|
2375
2597
|
[
|
|
2376
2598
|
prop,
|
|
@@ -2455,6 +2677,24 @@ const AssignmentPattern = (c, scope, node) => {
|
|
|
2455
2677
|
const right = new LazyValue((_, out) => c.handleEval(scope, node.right, out));
|
|
2456
2678
|
return [new AssignmentValue(left, right), inst];
|
|
2457
2679
|
};
|
|
2680
|
+
class ObjectGetSetEntry extends BaseValue {
|
|
2681
|
+
constructor() {
|
|
2682
|
+
super();
|
|
2683
|
+
this.macro = true;
|
|
2684
|
+
}
|
|
2685
|
+
eval(scope, out) {
|
|
2686
|
+
if (!this.getter)
|
|
2687
|
+
throw new CompilerError("This property does not have a getter");
|
|
2688
|
+
const [value, inst] = this.getter.call(scope, [], out);
|
|
2689
|
+
return [value ?? new LiteralValue(null), inst];
|
|
2690
|
+
}
|
|
2691
|
+
"="(scope, value, out) {
|
|
2692
|
+
if (!this.setter)
|
|
2693
|
+
throw new CompilerError("This property does not have a setter");
|
|
2694
|
+
const [, inst] = this.setter.call(scope, [value], out);
|
|
2695
|
+
return [value, inst];
|
|
2696
|
+
}
|
|
2697
|
+
}
|
|
2458
2698
|
|
|
2459
2699
|
const TemplateLiteral = () => {
|
|
2460
2700
|
throw new CompilerError("Template strings can only be used in tagged template literals. To inline mlog code use the asm function.");
|
|
@@ -2482,7 +2722,7 @@ const TSEnumDeclaration = (c, scope, node) => {
|
|
|
2482
2722
|
let counter = 0;
|
|
2483
2723
|
let lastType = "number";
|
|
2484
2724
|
const data = {};
|
|
2485
|
-
for (const member of node.members) {
|
|
2725
|
+
for (const member of node.body.members) {
|
|
2486
2726
|
if (lastType === "string" && !member.initializer)
|
|
2487
2727
|
throw new CompilerError("This enum member must be initialized", member);
|
|
2488
2728
|
const [value] = member.initializer
|
|
@@ -2510,6 +2750,41 @@ const TSNonNullExpression = (c, scope, node, out) => {
|
|
|
2510
2750
|
};
|
|
2511
2751
|
const TSSatisfiesExpression = (c, scope, node, out) => c.handle(scope, node.expression, undefined, out);
|
|
2512
2752
|
|
|
2753
|
+
const noExternalModuleErrorMessage = "Cannot import values from modules that are not built into the compiler";
|
|
2754
|
+
const ImportDeclaration = (c, scope, node) => {
|
|
2755
|
+
if (node.importKind === "type")
|
|
2756
|
+
return [null, []];
|
|
2757
|
+
if (!(node.source.value in scope.builtInModules))
|
|
2758
|
+
throw new CompilerError(noExternalModuleErrorMessage);
|
|
2759
|
+
for (const specifier of node.specifiers) {
|
|
2760
|
+
c.handle(scope, specifier, undefined, undefined, node.source.value);
|
|
2761
|
+
}
|
|
2762
|
+
return [null, []];
|
|
2763
|
+
};
|
|
2764
|
+
const ImportDefaultSpecifier = (c, scope, node, out, source) => {
|
|
2765
|
+
if (!(source in scope.builtInModules))
|
|
2766
|
+
return [null, []];
|
|
2767
|
+
throw new CompilerError(`"${source}" does not have a default export`);
|
|
2768
|
+
};
|
|
2769
|
+
const ImportNamespaceSpecifier = (c, scope, node, out, source) => {
|
|
2770
|
+
scope.set(node.local.name, scope.builtInModules[source]);
|
|
2771
|
+
return [null, []];
|
|
2772
|
+
};
|
|
2773
|
+
const ImportSpecifier = (c, scope, node, out, source) => {
|
|
2774
|
+
if (node.importKind === "type")
|
|
2775
|
+
return [null, []];
|
|
2776
|
+
const module = scope.builtInModules[source];
|
|
2777
|
+
const { imported, local } = node;
|
|
2778
|
+
const importedName = imported.type === "Identifier" ? imported.name : imported.value;
|
|
2779
|
+
const key = new LiteralValue(importedName);
|
|
2780
|
+
if (!module.hasProperty(scope, key)) {
|
|
2781
|
+
throw new CompilerError(`The requested module '${source}' does not provide an export named '${importedName}'`);
|
|
2782
|
+
}
|
|
2783
|
+
const [value] = module.get(scope, key);
|
|
2784
|
+
scope.set(local.name, value);
|
|
2785
|
+
return [null, []];
|
|
2786
|
+
};
|
|
2787
|
+
|
|
2513
2788
|
var handlers = /*#__PURE__*/Object.freeze({
|
|
2514
2789
|
__proto__: null,
|
|
2515
2790
|
ArrayExpression: ArrayExpression,
|
|
@@ -2533,6 +2808,10 @@ var handlers = /*#__PURE__*/Object.freeze({
|
|
|
2533
2808
|
FunctionExpression: FunctionExpression,
|
|
2534
2809
|
Identifier: Identifier,
|
|
2535
2810
|
IfStatement: IfStatement,
|
|
2811
|
+
ImportDeclaration: ImportDeclaration,
|
|
2812
|
+
ImportDefaultSpecifier: ImportDefaultSpecifier,
|
|
2813
|
+
ImportNamespaceSpecifier: ImportNamespaceSpecifier,
|
|
2814
|
+
ImportSpecifier: ImportSpecifier,
|
|
2536
2815
|
LRExpression: LRExpression,
|
|
2537
2816
|
LabeledStatement: LabeledStatement,
|
|
2538
2817
|
LogicalExpression: LogicalExpression,
|
|
@@ -2585,14 +2864,14 @@ class MacroFunction extends VoidValue {
|
|
|
2585
2864
|
debugString() {
|
|
2586
2865
|
return "MacroFunction";
|
|
2587
2866
|
}
|
|
2588
|
-
|
|
2867
|
+
toMlogString() {
|
|
2589
2868
|
return '"[macro MacroFunction]"';
|
|
2590
2869
|
}
|
|
2591
2870
|
}
|
|
2592
2871
|
|
|
2593
|
-
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
2594
2872
|
const toRadians = (degrees) => (degrees * Math.PI) / 180;
|
|
2595
2873
|
const toDegrees = (radians) => (radians * 180) / Math.PI;
|
|
2874
|
+
const mod = (f, n) => ((f % n) + n) % n;
|
|
2596
2875
|
const mathOperations = {
|
|
2597
2876
|
max: (a, b) => Math.max(a, b),
|
|
2598
2877
|
min: (a, b) => Math.min(a, b),
|
|
@@ -2600,16 +2879,30 @@ const mathOperations = {
|
|
|
2600
2879
|
const angle = toDegrees(Math.atan2(y, x));
|
|
2601
2880
|
return angle < 0 ? angle + 360 : angle;
|
|
2602
2881
|
},
|
|
2882
|
+
angleDiff: (x, y) => {
|
|
2883
|
+
const a = mod(x, 360);
|
|
2884
|
+
const b = mod(y, 360);
|
|
2885
|
+
return Math.min(a - b < 0 ? a - b + 360 : a - b, b - a < 0 ? b - a + 360 : b - a);
|
|
2886
|
+
},
|
|
2603
2887
|
len: (a, b) => Math.sqrt(a ** 2 + b ** 2),
|
|
2604
2888
|
noise: null,
|
|
2605
2889
|
abs: a => Math.abs(a),
|
|
2890
|
+
sign: a => {
|
|
2891
|
+
if (a > 0)
|
|
2892
|
+
return 1;
|
|
2893
|
+
if (a < 0)
|
|
2894
|
+
return -1;
|
|
2895
|
+
return 0;
|
|
2896
|
+
},
|
|
2606
2897
|
log: a => Math.log(a),
|
|
2898
|
+
logn: (a, b) => Math.log(a) / Math.log(b),
|
|
2607
2899
|
log10: a => Math.log10(a),
|
|
2608
2900
|
sin: a => Math.sin(toRadians(a)),
|
|
2609
2901
|
cos: a => Math.cos(toRadians(a)),
|
|
2610
2902
|
tan: a => Math.tan(toRadians(a)),
|
|
2611
2903
|
floor: a => Math.floor(a),
|
|
2612
2904
|
ceil: a => Math.ceil(a),
|
|
2905
|
+
round: a => Math.round(a),
|
|
2613
2906
|
sqrt: a => Math.sqrt(a),
|
|
2614
2907
|
asin: a => toDegrees(Math.asin(a)),
|
|
2615
2908
|
acos: a => toDegrees(Math.acos(a)),
|
|
@@ -2617,23 +2910,139 @@ const mathOperations = {
|
|
|
2617
2910
|
// yes, this doesn't work with negative numbers
|
|
2618
2911
|
// but the game also implements it this way.
|
|
2619
2912
|
idiv: (a, b) => Math.floor(a / b),
|
|
2913
|
+
emod: (a, b) => mod(a, b),
|
|
2620
2914
|
pow: (a, b) => Math.pow(a, b),
|
|
2621
2915
|
rand: null,
|
|
2622
2916
|
};
|
|
2623
2917
|
const orderIndependentOperations = ["max", "min", "len"];
|
|
2624
2918
|
function createMacroMathOperations() {
|
|
2625
2919
|
const macroMathOperations = {
|
|
2626
|
-
PI: new
|
|
2627
|
-
E: new
|
|
2628
|
-
degToRad: new
|
|
2629
|
-
radToDeg: new
|
|
2920
|
+
PI: new LiteralValue(mathConstants.PI),
|
|
2921
|
+
E: new LiteralValue(mathConstants.E),
|
|
2922
|
+
degToRad: new LiteralValue(mathConstants.degToRad),
|
|
2923
|
+
radToDeg: new LiteralValue(mathConstants.radToDeg),
|
|
2924
|
+
trunc: new MacroFunction((scope, out, x) => {
|
|
2925
|
+
assertArgumentCount(+!!x, 1);
|
|
2926
|
+
// subtracts the decimal part of a number from itself
|
|
2927
|
+
// works with both positive and negative numbers,
|
|
2928
|
+
// returning the integer part of the number
|
|
2929
|
+
// return a - (a % 1);
|
|
2930
|
+
const inst = [];
|
|
2931
|
+
const one = new LiteralValue(1);
|
|
2932
|
+
const rest = pipeInsts(x["%"](scope, one), inst);
|
|
2933
|
+
const result = pipeInsts(x["-"](scope, rest, out), inst);
|
|
2934
|
+
return [result, inst];
|
|
2935
|
+
}),
|
|
2936
|
+
exp: new MacroFunction((scope, out, x) => {
|
|
2937
|
+
assertArgumentCount(+!!x, 1);
|
|
2938
|
+
// return Math.E ** x;
|
|
2939
|
+
const inst = [];
|
|
2940
|
+
const { E } = macroMathOperations;
|
|
2941
|
+
const result = pipeInsts(E["**"](scope, x, out), inst);
|
|
2942
|
+
return [result, inst];
|
|
2943
|
+
}),
|
|
2944
|
+
expm1: new MacroFunction((scope, out, x) => {
|
|
2945
|
+
assertArgumentCount(+!!x, 1);
|
|
2946
|
+
// return Math.exp(x) - 1;
|
|
2947
|
+
const inst = [];
|
|
2948
|
+
const { exp } = macroMathOperations;
|
|
2949
|
+
const ex = pipeInsts(exp.call(scope, [x]), inst);
|
|
2950
|
+
const one = new LiteralValue(1);
|
|
2951
|
+
const subtracted = pipeInsts(ex["-"](scope, one, out), inst);
|
|
2952
|
+
return [subtracted, inst];
|
|
2953
|
+
}),
|
|
2954
|
+
cosh: new MacroFunction((scope, out, degrees) => {
|
|
2955
|
+
assertArgumentCount(+!!degrees, 1);
|
|
2956
|
+
// const x = degrees * Math.degToRad;
|
|
2957
|
+
// return (Math.exp(x) + Math.exp(-x)) / 2;
|
|
2958
|
+
const { exp, degToRad } = macroMathOperations;
|
|
2959
|
+
const inst = [];
|
|
2960
|
+
const x = pipeInsts(degrees["*"](scope, degToRad), inst);
|
|
2961
|
+
const expx = pipeInsts(exp.call(scope, [x]), inst);
|
|
2962
|
+
const negativeX = pipeInsts(x["u-"](scope), inst);
|
|
2963
|
+
const expnegx = pipeInsts(exp.call(scope, [negativeX]), inst);
|
|
2964
|
+
const sum = pipeInsts(expx["+"](scope, expnegx), inst);
|
|
2965
|
+
const result = pipeInsts(sum["/"](scope, new LiteralValue(2), out), inst);
|
|
2966
|
+
return [result, inst];
|
|
2967
|
+
}),
|
|
2968
|
+
acosh: new MacroFunction((scope, out, x) => {
|
|
2969
|
+
assertArgumentCount(+!!x, 1);
|
|
2970
|
+
// return Math.log(x + Math.sqrt(x ** 2 - 1)) * Math.radToDeg;
|
|
2971
|
+
const { log, sqrt, radToDeg } = macroMathOperations;
|
|
2972
|
+
const inst = [];
|
|
2973
|
+
const x2 = pipeInsts(x["**"](scope, new LiteralValue(2)), inst);
|
|
2974
|
+
const one = new LiteralValue(1);
|
|
2975
|
+
const x2minus1 = pipeInsts(x2["-"](scope, one), inst);
|
|
2976
|
+
const sqrtx2minus1 = pipeInsts(sqrt.call(scope, [x2minus1]), inst);
|
|
2977
|
+
const sum = pipeInsts(x["+"](scope, sqrtx2minus1), inst);
|
|
2978
|
+
const radians = pipeInsts(log.call(scope, [sum], out), inst);
|
|
2979
|
+
const degrees = pipeInsts(radians["*"](scope, radToDeg), inst);
|
|
2980
|
+
return [degrees, inst];
|
|
2981
|
+
}),
|
|
2982
|
+
sinh: new MacroFunction((scope, out, degrees) => {
|
|
2983
|
+
assertArgumentCount(+!!degrees, 1);
|
|
2984
|
+
// const x = degrees * Math.degToRad;
|
|
2985
|
+
// return (Math.exp(x) - Math.exp(-x)) / 2;
|
|
2986
|
+
const { exp, degToRad } = macroMathOperations;
|
|
2987
|
+
const inst = [];
|
|
2988
|
+
const x = pipeInsts(degrees["*"](scope, degToRad), inst);
|
|
2989
|
+
const expx = pipeInsts(exp.call(scope, [x]), inst);
|
|
2990
|
+
const negativeX = pipeInsts(x["u-"](scope), inst);
|
|
2991
|
+
const expnegx = pipeInsts(exp.call(scope, [negativeX]), inst);
|
|
2992
|
+
const sub = pipeInsts(expx["-"](scope, expnegx), inst);
|
|
2993
|
+
const result = pipeInsts(sub["/"](scope, new LiteralValue(2)), inst);
|
|
2994
|
+
return [result, inst];
|
|
2995
|
+
}),
|
|
2996
|
+
asinh: new MacroFunction((scope, out, x) => {
|
|
2997
|
+
assertArgumentCount(+!!x, 1);
|
|
2998
|
+
// return Math.log(x + Math.sqrt(x ** 2 + 1)) * Math.radToDeg;
|
|
2999
|
+
const { log, sqrt, radToDeg } = macroMathOperations;
|
|
3000
|
+
const inst = [];
|
|
3001
|
+
const x2 = pipeInsts(x["**"](scope, new LiteralValue(2)), inst);
|
|
3002
|
+
const x2plus1 = pipeInsts(x2["+"](scope, new LiteralValue(1)), inst);
|
|
3003
|
+
const sqrtx2plus1 = pipeInsts(sqrt.call(scope, [x2plus1]), inst);
|
|
3004
|
+
const sum = pipeInsts(x["+"](scope, sqrtx2plus1), inst);
|
|
3005
|
+
const radians = pipeInsts(log.call(scope, [sum], out), inst);
|
|
3006
|
+
const degrees = pipeInsts(radians["*"](scope, radToDeg), inst);
|
|
3007
|
+
return [degrees, inst];
|
|
3008
|
+
}),
|
|
3009
|
+
tanh: new MacroFunction((scope, out, degrees) => {
|
|
3010
|
+
assertArgumentCount(+!!degrees, 1);
|
|
3011
|
+
// const x = degrees * Math.degToRad;
|
|
3012
|
+
// return (Math.exp(x) - Math.exp(-x)) / (Math.exp(x) + Math.exp(-x));
|
|
3013
|
+
const { exp, degToRad } = macroMathOperations;
|
|
3014
|
+
const inst = [];
|
|
3015
|
+
const x = pipeInsts(degrees["*"](scope, degToRad), inst);
|
|
3016
|
+
const expx = pipeInsts(exp.call(scope, [x]), inst);
|
|
3017
|
+
const negativeX = pipeInsts(x["u-"](scope), inst);
|
|
3018
|
+
const expnegx = pipeInsts(exp.call(scope, [negativeX]), inst);
|
|
3019
|
+
const sub = pipeInsts(expx["-"](scope, expnegx), inst);
|
|
3020
|
+
const sum = pipeInsts(expx["+"](scope, expnegx), inst);
|
|
3021
|
+
const result = pipeInsts(sub["/"](scope, sum, out), inst);
|
|
3022
|
+
return [result, inst];
|
|
3023
|
+
}),
|
|
3024
|
+
atanh: new MacroFunction((scope, out, x) => {
|
|
3025
|
+
assertArgumentCount(+!!x, 1);
|
|
3026
|
+
// return (Math.log((1 + x) / (1 - x)) / 2) * Math.radToDeg;
|
|
3027
|
+
const { log, radToDeg } = macroMathOperations;
|
|
3028
|
+
const inst = [];
|
|
3029
|
+
const one = new LiteralValue(1);
|
|
3030
|
+
const two = new LiteralValue(2);
|
|
3031
|
+
const sum = pipeInsts(one["+"](scope, x), inst);
|
|
3032
|
+
const sub = pipeInsts(one["-"](scope, x), inst);
|
|
3033
|
+
const div = pipeInsts(sum["/"](scope, sub), inst);
|
|
3034
|
+
const logOfDiv = pipeInsts(log.call(scope, [div]), inst);
|
|
3035
|
+
const radians = pipeInsts(logOfDiv["/"](scope, two, out), inst);
|
|
3036
|
+
const degrees = pipeInsts(radians["*"](scope, radToDeg), inst);
|
|
3037
|
+
return [degrees, inst];
|
|
3038
|
+
}),
|
|
2630
3039
|
};
|
|
2631
3040
|
for (const key in mathOperations) {
|
|
2632
3041
|
const fn = mathOperations[key];
|
|
2633
3042
|
macroMathOperations[key] = new MacroFunction((scope, out, a, b) => {
|
|
2634
3043
|
const argumentCount = +!!a + +!!b;
|
|
2635
|
-
if (fn
|
|
2636
|
-
|
|
3044
|
+
if (fn) {
|
|
3045
|
+
assertArgumentCount(argumentCount, fn.length);
|
|
2637
3046
|
}
|
|
2638
3047
|
const cacheKey = getCacheKey(key);
|
|
2639
3048
|
if (b) {
|
|
@@ -2677,6 +3086,11 @@ function getCacheKey(key) {
|
|
|
2677
3086
|
return "**";
|
|
2678
3087
|
return key;
|
|
2679
3088
|
}
|
|
3089
|
+
function assertArgumentCount(current, expected) {
|
|
3090
|
+
if (current != expected) {
|
|
3091
|
+
throw new CompilerError(`Expected ${expected} arguments, but got ${current}`);
|
|
3092
|
+
}
|
|
3093
|
+
}
|
|
2680
3094
|
class MlogMath extends ObjectValue {
|
|
2681
3095
|
constructor() {
|
|
2682
3096
|
super(createMacroMathOperations());
|
|
@@ -2716,7 +3130,10 @@ function assertLiterals(values, message) {
|
|
|
2716
3130
|
}
|
|
2717
3131
|
}
|
|
2718
3132
|
|
|
2719
|
-
/**
|
|
3133
|
+
/**
|
|
3134
|
+
* Used to create namespaces that contain multiple methods that map into
|
|
3135
|
+
* instructions
|
|
3136
|
+
*/
|
|
2720
3137
|
function createOverloadNamespace({ overloads, handler, }) {
|
|
2721
3138
|
const result = {};
|
|
2722
3139
|
for (const key in overloads) {
|
|
@@ -2792,6 +3209,20 @@ class Draw extends ObjectValue {
|
|
|
2792
3209
|
named: "options",
|
|
2793
3210
|
args: ["x", "y", "image", "size", "rotation"],
|
|
2794
3211
|
},
|
|
3212
|
+
print: {
|
|
3213
|
+
named: "options",
|
|
3214
|
+
args: ["x", "y", "align"],
|
|
3215
|
+
},
|
|
3216
|
+
translate: {
|
|
3217
|
+
args: ["x", "y"],
|
|
3218
|
+
},
|
|
3219
|
+
scale: {
|
|
3220
|
+
args: ["x", "y"],
|
|
3221
|
+
},
|
|
3222
|
+
rotate: {
|
|
3223
|
+
args: ["degrees"],
|
|
3224
|
+
},
|
|
3225
|
+
reset: { args: [] },
|
|
2795
3226
|
},
|
|
2796
3227
|
handler(scope, overload, out, ...args) {
|
|
2797
3228
|
return [null, [new InstructionBase("draw", overload, ...args)]];
|
|
@@ -2803,31 +3234,72 @@ class Draw extends ObjectValue {
|
|
|
2803
3234
|
|
|
2804
3235
|
class Print extends MacroFunction {
|
|
2805
3236
|
constructor() {
|
|
2806
|
-
super((scope, out, ...
|
|
2807
|
-
const [first] = values;
|
|
3237
|
+
super((scope, out, ...args) => {
|
|
2808
3238
|
const inst = [];
|
|
2809
|
-
|
|
2810
|
-
|
|
3239
|
+
const values = getPrintValues(scope, args).filter(value => !(value instanceof LiteralValue) ||
|
|
3240
|
+
!value.isString() ||
|
|
3241
|
+
value.data.length > 0);
|
|
3242
|
+
if (!needsFormatString(values)) {
|
|
3243
|
+
for (let i = 0; i < values.length; i++) {
|
|
3244
|
+
const value = values[i];
|
|
2811
3245
|
inst.push(new InstructionBase("print", value));
|
|
2812
3246
|
}
|
|
2813
|
-
return [null, inst];
|
|
2814
3247
|
}
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
3248
|
+
else {
|
|
3249
|
+
let formatString = "";
|
|
3250
|
+
for (let i = 0; i < values.length; i++) {
|
|
3251
|
+
const value = values[i];
|
|
3252
|
+
if (value instanceof LiteralValue) {
|
|
3253
|
+
formatString += value.data;
|
|
3254
|
+
}
|
|
3255
|
+
else {
|
|
3256
|
+
// mlog replaces the first occurrence of {0}
|
|
3257
|
+
// so we don't need to increment the placeholder
|
|
3258
|
+
// number
|
|
3259
|
+
formatString += "{0}";
|
|
3260
|
+
}
|
|
3261
|
+
}
|
|
3262
|
+
inst.push(new InstructionBase("print", new LiteralValue(formatString)));
|
|
3263
|
+
for (let i = 0; i < values.length; i++) {
|
|
3264
|
+
const value = values[i];
|
|
3265
|
+
if (value instanceof LiteralValue)
|
|
3266
|
+
continue;
|
|
3267
|
+
inst.push(new InstructionBase("format", value));
|
|
3268
|
+
}
|
|
2823
3269
|
}
|
|
2824
|
-
const [tail] = first.get(scope, new LiteralValue(length.data - 1));
|
|
2825
|
-
if (tail.data)
|
|
2826
|
-
inst.push(new InstructionBase("print", tail));
|
|
2827
3270
|
return [null, inst];
|
|
2828
3271
|
});
|
|
2829
3272
|
}
|
|
2830
3273
|
}
|
|
3274
|
+
function getPrintValues(scope, args) {
|
|
3275
|
+
const first = args[0];
|
|
3276
|
+
if (!isTemplateObjectArray(first)) {
|
|
3277
|
+
return args;
|
|
3278
|
+
}
|
|
3279
|
+
const values = [];
|
|
3280
|
+
const rest = args.slice(1);
|
|
3281
|
+
// the number of strings in the template object array
|
|
3282
|
+
// is always one more than the number of values
|
|
3283
|
+
// this loop intercalates the values and the strings
|
|
3284
|
+
// into the values array
|
|
3285
|
+
for (let i = 0; i <= rest.length; i++) {
|
|
3286
|
+
const [string] = first.get(scope, new LiteralValue(i));
|
|
3287
|
+
values.push(string);
|
|
3288
|
+
if (i < rest.length)
|
|
3289
|
+
values.push(rest[i]);
|
|
3290
|
+
}
|
|
3291
|
+
return values;
|
|
3292
|
+
}
|
|
3293
|
+
function needsFormatString(values) {
|
|
3294
|
+
for (let i = 0; i < values.length; i++) {
|
|
3295
|
+
const value = values[i];
|
|
3296
|
+
if (!(value instanceof LiteralValue))
|
|
3297
|
+
continue;
|
|
3298
|
+
if (!value.isString() || value.data.length > 0)
|
|
3299
|
+
return true;
|
|
3300
|
+
}
|
|
3301
|
+
return false;
|
|
3302
|
+
}
|
|
2831
3303
|
|
|
2832
3304
|
const defaultTargetName$1 = "message1";
|
|
2833
3305
|
class PrintFlush extends MacroFunction {
|
|
@@ -2835,8 +3307,8 @@ class PrintFlush extends MacroFunction {
|
|
|
2835
3307
|
super((scope, out, target) => {
|
|
2836
3308
|
if (!target)
|
|
2837
3309
|
return [null, [new InstructionBase("printflush", defaultTargetName$1)]];
|
|
2838
|
-
if (!(target instanceof
|
|
2839
|
-
throw new CompilerError("The printflush target must be a
|
|
3310
|
+
if (!(target instanceof StoreValue))
|
|
3311
|
+
throw new CompilerError("The printflush target must be a store value");
|
|
2840
3312
|
return [null, [new InstructionBase("printflush", target)]];
|
|
2841
3313
|
});
|
|
2842
3314
|
}
|
|
@@ -2848,8 +3320,8 @@ class DrawFlush extends MacroFunction {
|
|
|
2848
3320
|
super((scope, out, target) => {
|
|
2849
3321
|
if (!target)
|
|
2850
3322
|
return [null, [new InstructionBase("drawflush", defaultTargetName)]];
|
|
2851
|
-
if (!(target instanceof
|
|
2852
|
-
throw new CompilerError("The drawflush target must be a
|
|
3323
|
+
if (!(target instanceof StoreValue))
|
|
3324
|
+
throw new CompilerError("The drawflush target must be a store value");
|
|
2853
3325
|
return [null, [new InstructionBase("drawflush", target)]];
|
|
2854
3326
|
});
|
|
2855
3327
|
}
|
|
@@ -2865,9 +3337,7 @@ class GetBlock extends ObjectValue {
|
|
|
2865
3337
|
building: { args: ["x", "y"] },
|
|
2866
3338
|
},
|
|
2867
3339
|
handler(scope, overload, out, x, y) {
|
|
2868
|
-
const output =
|
|
2869
|
-
? SenseableValue.from(scope, out, EMutability.constant)
|
|
2870
|
-
: StoreValue.from(scope, out, EMutability.constant);
|
|
3340
|
+
const output = StoreValue.from(scope, out);
|
|
2871
3341
|
return [
|
|
2872
3342
|
output,
|
|
2873
3343
|
[new InstructionBase("getblock", overload, output, x, y)],
|
|
@@ -2884,7 +3354,7 @@ class GetLink extends MacroFunction {
|
|
|
2884
3354
|
if (!(index instanceof StoreValue) &&
|
|
2885
3355
|
(!(index instanceof LiteralValue) || !index.isNumber()))
|
|
2886
3356
|
throw new CompilerError("The getlink index must be a number literal or a store");
|
|
2887
|
-
const outBuild =
|
|
3357
|
+
const outBuild = StoreValue.from(scope, out);
|
|
2888
3358
|
return [outBuild, [new InstructionBase("getlink", outBuild, index)]];
|
|
2889
3359
|
});
|
|
2890
3360
|
}
|
|
@@ -2935,8 +3405,8 @@ class Radar extends MacroFunction {
|
|
|
2935
3405
|
super((scope, out, options) => {
|
|
2936
3406
|
assertIsObjectMacro(options, "The radar options");
|
|
2937
3407
|
const { building, filters, order, sort } = options.data;
|
|
2938
|
-
if (!(building instanceof
|
|
2939
|
-
throw new CompilerError("The building must a
|
|
3408
|
+
if (!(building instanceof StoreValue))
|
|
3409
|
+
throw new CompilerError("The building must a store value");
|
|
2940
3410
|
assertIsArrayMacro(filters, "filters");
|
|
2941
3411
|
const { length } = filters.data;
|
|
2942
3412
|
if (!(length instanceof LiteralValue) || !length.isNumber())
|
|
@@ -2951,7 +3421,7 @@ class Radar extends MacroFunction {
|
|
|
2951
3421
|
if (!(order instanceof LiteralValue || order instanceof StoreValue))
|
|
2952
3422
|
throw new CompilerError("The radar order must be a literal or a store");
|
|
2953
3423
|
assertLiteralOneOf(sort, validRadarSorts, "The radar sort");
|
|
2954
|
-
const outUnit =
|
|
3424
|
+
const outUnit = StoreValue.from(scope, out);
|
|
2955
3425
|
return [
|
|
2956
3426
|
outUnit,
|
|
2957
3427
|
[
|
|
@@ -2967,9 +3437,9 @@ class Sensor extends MacroFunction {
|
|
|
2967
3437
|
super((scope, out, property, target) => {
|
|
2968
3438
|
if (!(property instanceof StoreValue))
|
|
2969
3439
|
throw new CompilerError("The sensor property must be a store");
|
|
2970
|
-
if (!(target instanceof
|
|
2971
|
-
throw new CompilerError("The sensor target must be a
|
|
2972
|
-
const temp =
|
|
3440
|
+
if (!(target instanceof StoreValue))
|
|
3441
|
+
throw new CompilerError("The sensor target must be a store value");
|
|
3442
|
+
const temp = StoreValue.from(scope, out);
|
|
2973
3443
|
return [temp, [new InstructionBase("sensor", temp, target, property)]];
|
|
2974
3444
|
});
|
|
2975
3445
|
}
|
|
@@ -2994,6 +3464,7 @@ class Lookup extends ObjectValue {
|
|
|
2994
3464
|
unit: { args: ["index"] },
|
|
2995
3465
|
item: { args: ["index"] },
|
|
2996
3466
|
liquid: { args: ["index"] },
|
|
3467
|
+
team: { args: ["index"] },
|
|
2997
3468
|
},
|
|
2998
3469
|
handler(scope, overload, out, index) {
|
|
2999
3470
|
const output = StoreValue.from(scope, out);
|
|
@@ -3057,16 +3528,16 @@ class SpawnUnit extends MacroFunction {
|
|
|
3057
3528
|
constructor() {
|
|
3058
3529
|
super((scope, out, options) => {
|
|
3059
3530
|
assertIsObjectMacro(options, "The options");
|
|
3060
|
-
const { type, x, y, team, rotation } = options.data;
|
|
3531
|
+
const { type, x, y, team, rotation, effect } = options.data;
|
|
3061
3532
|
assertIsRuntimeValue(type, "type");
|
|
3062
3533
|
assertIsRuntimeValue(x, "x");
|
|
3063
3534
|
assertIsRuntimeValue(y, "y");
|
|
3064
3535
|
assertIsRuntimeValue(team, "team");
|
|
3065
|
-
const output =
|
|
3536
|
+
const output = StoreValue.from(scope, out);
|
|
3066
3537
|
return [
|
|
3067
3538
|
output,
|
|
3068
3539
|
[
|
|
3069
|
-
new InstructionBase("spawn", type, x, y, rotation ?? "0", team, output),
|
|
3540
|
+
new InstructionBase("spawn", type, x, y, rotation ?? "0", team, output, effect ?? "true"),
|
|
3070
3541
|
],
|
|
3071
3542
|
];
|
|
3072
3543
|
});
|
|
@@ -3104,8 +3575,9 @@ class UnitControl extends ObjectValue {
|
|
|
3104
3575
|
stop: { args: [] },
|
|
3105
3576
|
move: { args: ["x", "y"] },
|
|
3106
3577
|
approach: { named: "options", args: ["x", "y", "radius"] },
|
|
3578
|
+
pathfind: { args: ["x", "y"] },
|
|
3579
|
+
autoPathfind: { args: [] },
|
|
3107
3580
|
boost: { args: ["enable"] },
|
|
3108
|
-
pathfind: { args: [] },
|
|
3109
3581
|
target: { named: "options", args: ["x", "y", "shoot"] },
|
|
3110
3582
|
targetp: { named: "options", args: ["unit", "shoot"] },
|
|
3111
3583
|
itemDrop: { args: ["target", "amount"] },
|
|
@@ -3119,6 +3591,9 @@ class UnitControl extends ObjectValue {
|
|
|
3119
3591
|
named: "options",
|
|
3120
3592
|
args: ["x", "y", "block", "rotation", { key: "config", default: "" }],
|
|
3121
3593
|
},
|
|
3594
|
+
deconstruct: {
|
|
3595
|
+
args: ["x", "y"],
|
|
3596
|
+
},
|
|
3122
3597
|
getBlock: {
|
|
3123
3598
|
args: ["x", "y"],
|
|
3124
3599
|
},
|
|
@@ -3136,7 +3611,7 @@ class UnitControl extends ObjectValue {
|
|
|
3136
3611
|
switch (overload) {
|
|
3137
3612
|
case "getBlock": {
|
|
3138
3613
|
const outType = StoreValue.from(scope, extractDestrucuringOut(out, 0));
|
|
3139
|
-
const outBuilding =
|
|
3614
|
+
const outBuilding = StoreValue.from(scope, extractDestrucuringOut(out, 1));
|
|
3140
3615
|
const outFloor = StoreValue.from(scope, extractDestrucuringOut(out, 2));
|
|
3141
3616
|
result = ObjectValue.fromArray([outType, outBuilding, outFloor]);
|
|
3142
3617
|
extraArgs = [outType, outBuilding, outFloor];
|
|
@@ -3177,7 +3652,7 @@ class UnitRadar extends MacroFunction {
|
|
|
3177
3652
|
if (!(order instanceof LiteralValue || order instanceof StoreValue))
|
|
3178
3653
|
throw new CompilerError("The radar order must be a literal or a store");
|
|
3179
3654
|
assertLiteralOneOf(sort, validRadarSorts, "The radar sort");
|
|
3180
|
-
const outUnit =
|
|
3655
|
+
const outUnit = StoreValue.from(scope, out);
|
|
3181
3656
|
return [
|
|
3182
3657
|
outUnit,
|
|
3183
3658
|
[
|
|
@@ -3219,7 +3694,7 @@ class UnitLocate extends ObjectValue {
|
|
|
3219
3694
|
const outFound = StoreValue.from(scope, extractDestrucuringOut(out, 0));
|
|
3220
3695
|
const outX = StoreValue.from(scope, extractDestrucuringOut(out, 1));
|
|
3221
3696
|
const outY = StoreValue.from(scope, extractDestrucuringOut(out, 2));
|
|
3222
|
-
const outBuilding =
|
|
3697
|
+
const outBuilding = StoreValue.from(scope, overload === "ore" ? discardedName : extractDestrucuringOut(out, 3));
|
|
3223
3698
|
const outArgs = [outX, outY, outFound, outBuilding];
|
|
3224
3699
|
let inputArgs = [];
|
|
3225
3700
|
switch (overload) {
|
|
@@ -3258,20 +3733,29 @@ class UnitLocate extends ObjectValue {
|
|
|
3258
3733
|
}
|
|
3259
3734
|
|
|
3260
3735
|
const validEffects = [
|
|
3736
|
+
"none",
|
|
3261
3737
|
"burning",
|
|
3262
3738
|
"freezing",
|
|
3263
3739
|
"unmoving",
|
|
3740
|
+
"slow",
|
|
3741
|
+
"fast",
|
|
3264
3742
|
"wet",
|
|
3743
|
+
"muddy",
|
|
3265
3744
|
"melting",
|
|
3266
3745
|
"sapped",
|
|
3267
|
-
"electrified",
|
|
3268
|
-
"spore-slowed",
|
|
3269
3746
|
"tarred",
|
|
3270
3747
|
"overdrive",
|
|
3271
3748
|
"overclock",
|
|
3272
|
-
"
|
|
3749
|
+
"shielded",
|
|
3273
3750
|
"shocked",
|
|
3274
3751
|
"blasted",
|
|
3752
|
+
"corroded",
|
|
3753
|
+
"boss",
|
|
3754
|
+
"spore-slowed",
|
|
3755
|
+
"disarmed",
|
|
3756
|
+
"electrified",
|
|
3757
|
+
"invincible",
|
|
3758
|
+
"dynamic",
|
|
3275
3759
|
];
|
|
3276
3760
|
class ApplyStatus extends ObjectValue {
|
|
3277
3761
|
constructor() {
|
|
@@ -3283,15 +3767,25 @@ class ApplyStatus extends ObjectValue {
|
|
|
3283
3767
|
clear: { args: ["status", "unit"] },
|
|
3284
3768
|
},
|
|
3285
3769
|
handler(scope, overload, out, effect, unit, duration) {
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3770
|
+
let effectValue = effect;
|
|
3771
|
+
if (effect instanceof LiteralValue && effect.isString()) {
|
|
3772
|
+
if (effect.data.startsWith("@status-")) {
|
|
3773
|
+
effectValue = effect.data;
|
|
3774
|
+
}
|
|
3775
|
+
else {
|
|
3776
|
+
assertLiteralOneOf(effect, validEffects, "The status effect");
|
|
3777
|
+
effectValue = `@status-${effect.data}`;
|
|
3778
|
+
}
|
|
3779
|
+
}
|
|
3780
|
+
const instArgs = [
|
|
3781
|
+
"status",
|
|
3782
|
+
String(overload !== "apply"),
|
|
3783
|
+
effectValue,
|
|
3784
|
+
unit,
|
|
3294
3785
|
];
|
|
3786
|
+
if (duration)
|
|
3787
|
+
instArgs.push(duration);
|
|
3788
|
+
return [null, [new InstructionBase(...instArgs)]];
|
|
3295
3789
|
},
|
|
3296
3790
|
});
|
|
3297
3791
|
super(data);
|
|
@@ -3301,14 +3795,9 @@ class ApplyStatus extends ObjectValue {
|
|
|
3301
3795
|
class SpawnWave extends MacroFunction {
|
|
3302
3796
|
constructor() {
|
|
3303
3797
|
super((scope, out, natural, x, y) => {
|
|
3304
|
-
if (!(natural instanceof LiteralValue) ||
|
|
3305
|
-
(natural.data !== 1 && natural.data !== 0))
|
|
3306
|
-
throw new CompilerError("The 'natural' argument must be a boolean literal");
|
|
3307
3798
|
return [
|
|
3308
3799
|
null,
|
|
3309
|
-
[
|
|
3310
|
-
new InstructionBase("spawnwave", x ?? "10", y ?? "10", String(!!natural.data)),
|
|
3311
|
-
],
|
|
3800
|
+
[new InstructionBase("spawnwave", x ?? "null", y ?? "null", natural)],
|
|
3312
3801
|
];
|
|
3313
3802
|
});
|
|
3314
3803
|
}
|
|
@@ -3330,16 +3819,25 @@ class SetRule extends ObjectValue {
|
|
|
3330
3819
|
unitCap: { args: ["cap"] },
|
|
3331
3820
|
mapArea: { named: "options", args: ["x", "y", "width", "height"] },
|
|
3332
3821
|
lighting: { args: ["enabled"] },
|
|
3822
|
+
canGameOver: { args: ["enabled"] },
|
|
3333
3823
|
ambientLight: { args: ["rgbaData"] },
|
|
3334
3824
|
solarMultiplier: { args: ["multiplier"] },
|
|
3825
|
+
dragMultiplier: { args: ["multiplier"] },
|
|
3826
|
+
ban: { args: ["content"] },
|
|
3827
|
+
unban: { args: ["content"] },
|
|
3335
3828
|
buildSpeed: { args: ["team", "multiplier"] },
|
|
3829
|
+
unitHealth: { args: ["team", "multiplier"] },
|
|
3336
3830
|
unitBuildSpeed: { args: ["team", "multiplier"] },
|
|
3831
|
+
unitMineSpeed: { args: ["team", "multiplier"] },
|
|
3337
3832
|
unitCost: { args: ["team", "multiplier"] },
|
|
3338
3833
|
unitDamage: { args: ["team", "multiplier"] },
|
|
3339
3834
|
blockHealth: { args: ["team", "multiplier"] },
|
|
3340
3835
|
blockDamage: { args: ["team", "multiplier"] },
|
|
3341
3836
|
rtsMinWeight: { args: ["team", "value"] },
|
|
3342
3837
|
rtsMinSquad: { args: ["team", "value"] },
|
|
3838
|
+
unitLight: { args: ["enabled"] },
|
|
3839
|
+
pauseDisabled: { args: ["enabled"] },
|
|
3840
|
+
musicVolume: { args: ["volume"] },
|
|
3343
3841
|
},
|
|
3344
3842
|
handler(scope, overload, out, ...args) {
|
|
3345
3843
|
const params = ["10", "0", "0", "100", "100"];
|
|
@@ -3353,7 +3851,9 @@ class SetRule extends ObjectValue {
|
|
|
3353
3851
|
break;
|
|
3354
3852
|
}
|
|
3355
3853
|
case "buildSpeed":
|
|
3854
|
+
case "unitHealth":
|
|
3356
3855
|
case "unitBuildSpeed":
|
|
3856
|
+
case "unitMineSpeed":
|
|
3357
3857
|
case "unitCost":
|
|
3358
3858
|
case "unitDamage":
|
|
3359
3859
|
case "blockHealth":
|
|
@@ -3383,9 +3883,34 @@ class FlushMessage extends ObjectValue {
|
|
|
3383
3883
|
mission: { args: [] },
|
|
3384
3884
|
announce: { args: ["duration"] },
|
|
3385
3885
|
toast: { args: ["duration"] },
|
|
3886
|
+
notifySync: { args: [] },
|
|
3887
|
+
missionSync: { args: [] },
|
|
3888
|
+
announceSync: { args: ["duration"] },
|
|
3889
|
+
toastSync: { args: ["duration"] },
|
|
3386
3890
|
},
|
|
3387
3891
|
handler(scope, overload, out, duration) {
|
|
3388
|
-
|
|
3892
|
+
let result = null;
|
|
3893
|
+
let arg = "0";
|
|
3894
|
+
switch (overload) {
|
|
3895
|
+
case "announce":
|
|
3896
|
+
case "toast":
|
|
3897
|
+
case "mission":
|
|
3898
|
+
case "notify":
|
|
3899
|
+
result = StoreValue.from(scope, out);
|
|
3900
|
+
arg = result;
|
|
3901
|
+
break;
|
|
3902
|
+
case "announceSync":
|
|
3903
|
+
case "missionSync":
|
|
3904
|
+
case "toastSync":
|
|
3905
|
+
case "notifySync":
|
|
3906
|
+
result = null;
|
|
3907
|
+
arg = "@wait";
|
|
3908
|
+
break;
|
|
3909
|
+
}
|
|
3910
|
+
return [
|
|
3911
|
+
result,
|
|
3912
|
+
[new InstructionBase("message", overload, duration, arg)],
|
|
3913
|
+
];
|
|
3389
3914
|
},
|
|
3390
3915
|
});
|
|
3391
3916
|
super(data);
|
|
@@ -3399,8 +3924,19 @@ class Cutscene extends ObjectValue {
|
|
|
3399
3924
|
pan: { named: "options", args: ["x", "y", "speed"] },
|
|
3400
3925
|
zoom: { args: ["level"] },
|
|
3401
3926
|
stop: { args: [] },
|
|
3927
|
+
active: { args: [] },
|
|
3928
|
+
shake: { args: ["amount", "duration"] },
|
|
3929
|
+
getHud: { args: [] },
|
|
3930
|
+
setHud: { args: ["shown"] },
|
|
3402
3931
|
},
|
|
3403
3932
|
handler(scope, overload, out, ...args) {
|
|
3933
|
+
if (overload === "active" || overload === "getHud") {
|
|
3934
|
+
const result = StoreValue.from(scope, out);
|
|
3935
|
+
return [
|
|
3936
|
+
result,
|
|
3937
|
+
[new InstructionBase("cutscene", overload, result, "0", "0", "0")],
|
|
3938
|
+
];
|
|
3939
|
+
}
|
|
3404
3940
|
const params = Object.assign(["100", "100", "0.06", "0"], args);
|
|
3405
3941
|
return [null, [new InstructionBase("cutscene", overload, ...params)]];
|
|
3406
3942
|
},
|
|
@@ -3438,8 +3974,10 @@ class Fetch extends ObjectValue {
|
|
|
3438
3974
|
constructor() {
|
|
3439
3975
|
const data = createOverloadNamespace({
|
|
3440
3976
|
overloads: {
|
|
3441
|
-
unit: {
|
|
3442
|
-
|
|
3977
|
+
unit: {
|
|
3978
|
+
args: ["team", "index", { key: "type", default: "null" }],
|
|
3979
|
+
},
|
|
3980
|
+
unitCount: { args: ["team", { key: "type", default: "null" }] },
|
|
3443
3981
|
player: { args: ["team", "index"] },
|
|
3444
3982
|
playerCount: { args: ["team"] },
|
|
3445
3983
|
core: { args: ["team", "index"] },
|
|
@@ -3448,9 +3986,9 @@ class Fetch extends ObjectValue {
|
|
|
3448
3986
|
buildCount: { args: ["team", "block"] },
|
|
3449
3987
|
},
|
|
3450
3988
|
handler(scope, overload, out, team, ...rest) {
|
|
3451
|
-
const output =
|
|
3989
|
+
const output = StoreValue.from(scope, out);
|
|
3452
3990
|
const params = ["0", "@conveyor"];
|
|
3453
|
-
if (overload === "buildCount") {
|
|
3991
|
+
if (overload === "buildCount" || overload === "unitCount") {
|
|
3454
3992
|
params[1] = rest[0];
|
|
3455
3993
|
}
|
|
3456
3994
|
else {
|
|
@@ -3487,6 +4025,373 @@ class SetFlag extends MacroFunction {
|
|
|
3487
4025
|
}
|
|
3488
4026
|
}
|
|
3489
4027
|
|
|
4028
|
+
class SetProp extends MacroFunction {
|
|
4029
|
+
constructor() {
|
|
4030
|
+
super((scope, out, target) => {
|
|
4031
|
+
if (!target)
|
|
4032
|
+
throw new CompilerError("Missing argument: target");
|
|
4033
|
+
return [new Settable(target), []];
|
|
4034
|
+
});
|
|
4035
|
+
}
|
|
4036
|
+
}
|
|
4037
|
+
class SettableEntry extends VoidValue {
|
|
4038
|
+
constructor(target, prop) {
|
|
4039
|
+
super();
|
|
4040
|
+
this.target = target;
|
|
4041
|
+
this.prop = prop;
|
|
4042
|
+
this.macro = true;
|
|
4043
|
+
}
|
|
4044
|
+
eval(scope, out) {
|
|
4045
|
+
return this.target.get(scope, this.prop, out);
|
|
4046
|
+
}
|
|
4047
|
+
"="(scope, value) {
|
|
4048
|
+
let prop = this.prop;
|
|
4049
|
+
if (prop instanceof LiteralValue && prop.isString())
|
|
4050
|
+
prop = formatSenseablePropName(prop.data);
|
|
4051
|
+
return [value, [new InstructionBase("setprop", prop, this.target, value)]];
|
|
4052
|
+
}
|
|
4053
|
+
debugString() {
|
|
4054
|
+
return "SettableEntry";
|
|
4055
|
+
}
|
|
4056
|
+
toMlogString() {
|
|
4057
|
+
return "[macro SettableEntry]";
|
|
4058
|
+
}
|
|
4059
|
+
}
|
|
4060
|
+
class Settable extends ObjectValue {
|
|
4061
|
+
constructor(target) {
|
|
4062
|
+
super({});
|
|
4063
|
+
this.target = target;
|
|
4064
|
+
}
|
|
4065
|
+
get(scope, key, out) {
|
|
4066
|
+
const entry = new SettableEntry(this.target, key);
|
|
4067
|
+
if (out)
|
|
4068
|
+
return entry.eval(scope, out);
|
|
4069
|
+
return [entry, []];
|
|
4070
|
+
}
|
|
4071
|
+
}
|
|
4072
|
+
|
|
4073
|
+
class SyncLock extends ObjectValue {
|
|
4074
|
+
constructor(value) {
|
|
4075
|
+
super({
|
|
4076
|
+
value,
|
|
4077
|
+
sendToClients: new MacroFunction(() => {
|
|
4078
|
+
return [null, [new InstructionBase("sync", this.value)]];
|
|
4079
|
+
}),
|
|
4080
|
+
});
|
|
4081
|
+
this.value = value;
|
|
4082
|
+
}
|
|
4083
|
+
}
|
|
4084
|
+
class SyncLockConstructor extends MacroFunction {
|
|
4085
|
+
constructor() {
|
|
4086
|
+
super((scope, out, init) => {
|
|
4087
|
+
const inst = [];
|
|
4088
|
+
const value = StoreValue.from(scope, out, EMutability.mutable);
|
|
4089
|
+
if (init) {
|
|
4090
|
+
if (!(init instanceof StoreValue || init instanceof LiteralValue)) {
|
|
4091
|
+
throw new CompilerError("Expected a store value or a literal value");
|
|
4092
|
+
}
|
|
4093
|
+
pipeInsts(value["="](scope, init), inst);
|
|
4094
|
+
}
|
|
4095
|
+
return [new SyncLock(value), inst];
|
|
4096
|
+
});
|
|
4097
|
+
}
|
|
4098
|
+
preCall(scope, out) {
|
|
4099
|
+
if (out)
|
|
4100
|
+
return [out];
|
|
4101
|
+
}
|
|
4102
|
+
}
|
|
4103
|
+
|
|
4104
|
+
class Effect extends ObjectValue {
|
|
4105
|
+
constructor() {
|
|
4106
|
+
const data = createOverloadNamespace({
|
|
4107
|
+
overloads: {
|
|
4108
|
+
warn: { args: ["x", "y"] },
|
|
4109
|
+
cross: { args: ["x", "y"] },
|
|
4110
|
+
blockFall: { args: ["x", "y", "data"] },
|
|
4111
|
+
placeBlock: { args: ["x", "y", "size"] },
|
|
4112
|
+
placeBlockSpark: { args: ["x", "y", "size"] },
|
|
4113
|
+
breakBlock: { args: ["x", "y", "size"] },
|
|
4114
|
+
spawn: { args: ["x", "y"] },
|
|
4115
|
+
trail: { args: ["x", "y", "color", "size"], named: "options" },
|
|
4116
|
+
breakPop: { args: ["x", "y", "color", "size"], named: "options" },
|
|
4117
|
+
breakProp: { args: ["x", "y", "color", "size"], named: "options" },
|
|
4118
|
+
smokeCloud: { args: ["x", "y", "color"] },
|
|
4119
|
+
vapor: { args: ["x", "y", "color"] },
|
|
4120
|
+
hit: { args: ["x", "y", "color"] },
|
|
4121
|
+
hitSquare: { args: ["x", "y", "color"] },
|
|
4122
|
+
shootSmall: { args: ["x", "y", "color", "rotation"], named: "options" },
|
|
4123
|
+
shootBig: { args: ["x", "y", "color", "rotation"], named: "options" },
|
|
4124
|
+
smokeSmall: { args: ["x", "y", "rotation"] },
|
|
4125
|
+
smokeBig: { args: ["x", "y", "rotation"] },
|
|
4126
|
+
smokeColor: { args: ["x", "y", "color", "rotation"], named: "options" },
|
|
4127
|
+
smokeSquare: {
|
|
4128
|
+
args: ["x", "y", "color", "rotation"],
|
|
4129
|
+
named: "options",
|
|
4130
|
+
},
|
|
4131
|
+
smokeSquareBig: {
|
|
4132
|
+
args: ["x", "y", "color", "rotation"],
|
|
4133
|
+
named: "options",
|
|
4134
|
+
},
|
|
4135
|
+
spark: { args: ["x", "y", "color"] },
|
|
4136
|
+
sparkBig: { args: ["x", "y", "color"] },
|
|
4137
|
+
sparkShoot: { args: ["x", "y", "color", "rotation"], named: "options" },
|
|
4138
|
+
sparkShootBig: {
|
|
4139
|
+
args: ["x", "y", "color", "rotation"],
|
|
4140
|
+
named: "options",
|
|
4141
|
+
},
|
|
4142
|
+
drill: { args: ["x", "y", "color"] },
|
|
4143
|
+
drillBig: { args: ["x", "y", "color"] },
|
|
4144
|
+
lightBlock: { args: ["x", "y", "color", "size"], named: "options" },
|
|
4145
|
+
explosion: { args: ["x", "y", "size"] },
|
|
4146
|
+
smokePuff: { args: ["x", "y", "color"] },
|
|
4147
|
+
sparkExplosion: { args: ["x", "y", "color"] },
|
|
4148
|
+
crossExplosion: { args: ["x", "y", "color", "size"], named: "options" },
|
|
4149
|
+
wave: { args: ["x", "y", "color", "size"], named: "options" },
|
|
4150
|
+
bubble: { args: ["x", "y"] },
|
|
4151
|
+
},
|
|
4152
|
+
handler(scope, overload, out, ...args) {
|
|
4153
|
+
const params = ["0", "0", "2", "%ffaaff", ""];
|
|
4154
|
+
params[0] = args[0];
|
|
4155
|
+
params[1] = args[1];
|
|
4156
|
+
switch (overload) {
|
|
4157
|
+
case "blockFall":
|
|
4158
|
+
params[4] = args[2];
|
|
4159
|
+
break;
|
|
4160
|
+
case "placeBlock":
|
|
4161
|
+
case "smokeSmall":
|
|
4162
|
+
case "smokeBig":
|
|
4163
|
+
case "explosion":
|
|
4164
|
+
params[2] = args[2];
|
|
4165
|
+
break;
|
|
4166
|
+
case "trail":
|
|
4167
|
+
case "breakPop":
|
|
4168
|
+
case "breakProp":
|
|
4169
|
+
case "shootSmall":
|
|
4170
|
+
case "shootBig":
|
|
4171
|
+
case "smokeColor":
|
|
4172
|
+
case "smokeSquare":
|
|
4173
|
+
case "smokeSquareBig":
|
|
4174
|
+
case "sparkShoot":
|
|
4175
|
+
case "sparkShootBig":
|
|
4176
|
+
case "lightBlock":
|
|
4177
|
+
case "crossExplosion":
|
|
4178
|
+
case "wave":
|
|
4179
|
+
params[3] = args[2];
|
|
4180
|
+
params[2] = args[3];
|
|
4181
|
+
break;
|
|
4182
|
+
case "smokeCloud":
|
|
4183
|
+
case "vapor":
|
|
4184
|
+
case "hit":
|
|
4185
|
+
case "hitSquare":
|
|
4186
|
+
case "spark":
|
|
4187
|
+
case "sparkBig":
|
|
4188
|
+
case "drill":
|
|
4189
|
+
case "drillBig":
|
|
4190
|
+
case "smokePuff":
|
|
4191
|
+
case "sparkExplosion":
|
|
4192
|
+
params[3] = args[2];
|
|
4193
|
+
break;
|
|
4194
|
+
}
|
|
4195
|
+
return [
|
|
4196
|
+
null,
|
|
4197
|
+
[
|
|
4198
|
+
new InstructionBase("effect", overload === "breakPop" ? "breakProp" : overload, ...params),
|
|
4199
|
+
],
|
|
4200
|
+
];
|
|
4201
|
+
},
|
|
4202
|
+
});
|
|
4203
|
+
super(data);
|
|
4204
|
+
}
|
|
4205
|
+
}
|
|
4206
|
+
|
|
4207
|
+
class LocalePrint extends MacroFunction {
|
|
4208
|
+
constructor() {
|
|
4209
|
+
super((scope, out, name) => {
|
|
4210
|
+
return [null, [new InstructionBase("localeprint", name)]];
|
|
4211
|
+
});
|
|
4212
|
+
}
|
|
4213
|
+
}
|
|
4214
|
+
|
|
4215
|
+
class WeatherSense extends MacroFunction {
|
|
4216
|
+
constructor() {
|
|
4217
|
+
super((scope, out, weather) => {
|
|
4218
|
+
const state = StoreValue.from(scope, out);
|
|
4219
|
+
return [state, [new InstructionBase("weathersense", state, weather)]];
|
|
4220
|
+
});
|
|
4221
|
+
}
|
|
4222
|
+
}
|
|
4223
|
+
|
|
4224
|
+
class WeatherSet extends MacroFunction {
|
|
4225
|
+
constructor() {
|
|
4226
|
+
super((scope, out, weather, state) => {
|
|
4227
|
+
return [null, [new InstructionBase("weatherset", weather, state)]];
|
|
4228
|
+
});
|
|
4229
|
+
}
|
|
4230
|
+
}
|
|
4231
|
+
|
|
4232
|
+
class PlaySound extends ObjectValue {
|
|
4233
|
+
constructor() {
|
|
4234
|
+
const data = createOverloadNamespace({
|
|
4235
|
+
overloads: {
|
|
4236
|
+
positional: {
|
|
4237
|
+
named: "options",
|
|
4238
|
+
args: ["sound", "volume", "pitch", "x", "y", "limit"],
|
|
4239
|
+
},
|
|
4240
|
+
global: {
|
|
4241
|
+
named: "options",
|
|
4242
|
+
args: ["sound", "volume", "pitch", "pan", "limit"],
|
|
4243
|
+
},
|
|
4244
|
+
},
|
|
4245
|
+
//playsound false @sfx-pew 12 0.1 globalyespan @thisx @thisy true
|
|
4246
|
+
// wait 1
|
|
4247
|
+
handler(scope, overload, out, ...args) {
|
|
4248
|
+
let sound = "0";
|
|
4249
|
+
let volume = "0";
|
|
4250
|
+
let pitch = "0";
|
|
4251
|
+
let pan = "0";
|
|
4252
|
+
let x = "0";
|
|
4253
|
+
let y = "0";
|
|
4254
|
+
let limit = "0";
|
|
4255
|
+
// let volume =
|
|
4256
|
+
switch (overload) {
|
|
4257
|
+
case "positional": {
|
|
4258
|
+
// playsound true id volume pitch _pan x y limit
|
|
4259
|
+
[sound, volume, pitch, x, y, limit] = args;
|
|
4260
|
+
break;
|
|
4261
|
+
}
|
|
4262
|
+
case "global": {
|
|
4263
|
+
// playsound false id volume pitch pan _x _y limit
|
|
4264
|
+
[sound, volume, pitch, pan, limit] = args;
|
|
4265
|
+
break;
|
|
4266
|
+
}
|
|
4267
|
+
}
|
|
4268
|
+
return [
|
|
4269
|
+
null,
|
|
4270
|
+
[
|
|
4271
|
+
new InstructionBase("playsound", String(overload === "positional"), sound, volume, pitch, pan, x, y, limit),
|
|
4272
|
+
],
|
|
4273
|
+
];
|
|
4274
|
+
},
|
|
4275
|
+
});
|
|
4276
|
+
super(data);
|
|
4277
|
+
}
|
|
4278
|
+
}
|
|
4279
|
+
|
|
4280
|
+
class PrintChar extends MacroFunction {
|
|
4281
|
+
constructor() {
|
|
4282
|
+
super((scope, out, character) => {
|
|
4283
|
+
return [null, [new InstructionBase("printchar", character)]];
|
|
4284
|
+
});
|
|
4285
|
+
}
|
|
4286
|
+
}
|
|
4287
|
+
|
|
4288
|
+
class UnpackColor extends MacroFunction {
|
|
4289
|
+
constructor() {
|
|
4290
|
+
super((scope, out, color) => {
|
|
4291
|
+
if (!color) {
|
|
4292
|
+
throw new CompilerError(`Missing argument: color`);
|
|
4293
|
+
}
|
|
4294
|
+
const outR = StoreValue.from(scope, extractDestrucuringOut(out, "r"));
|
|
4295
|
+
const outG = StoreValue.from(scope, extractDestrucuringOut(out, "g"));
|
|
4296
|
+
const outB = StoreValue.from(scope, extractDestrucuringOut(out, "b"));
|
|
4297
|
+
const outA = StoreValue.from(scope, extractDestrucuringOut(out, "a"));
|
|
4298
|
+
return [
|
|
4299
|
+
new ObjectValue({
|
|
4300
|
+
r: outR,
|
|
4301
|
+
g: outG,
|
|
4302
|
+
b: outB,
|
|
4303
|
+
a: outA,
|
|
4304
|
+
}),
|
|
4305
|
+
[new InstructionBase("unpackcolor", outR, outG, outB, outA, color)],
|
|
4306
|
+
];
|
|
4307
|
+
});
|
|
4308
|
+
}
|
|
4309
|
+
}
|
|
4310
|
+
|
|
4311
|
+
class Format extends MacroFunction {
|
|
4312
|
+
constructor() {
|
|
4313
|
+
super((scope, out, value) => {
|
|
4314
|
+
return [null, [new InstructionBase("format", value)]];
|
|
4315
|
+
});
|
|
4316
|
+
}
|
|
4317
|
+
}
|
|
4318
|
+
|
|
4319
|
+
class Query extends ObjectValue {
|
|
4320
|
+
constructor() {
|
|
4321
|
+
const data = createOverloadNamespace({
|
|
4322
|
+
overloads: {
|
|
4323
|
+
circle: {
|
|
4324
|
+
named: "options",
|
|
4325
|
+
args: ["type", { key: "team", default: "null" }, "x", "y", "radius"],
|
|
4326
|
+
},
|
|
4327
|
+
rect: {
|
|
4328
|
+
named: "options",
|
|
4329
|
+
args: [
|
|
4330
|
+
"type",
|
|
4331
|
+
{ key: "team", default: "null" },
|
|
4332
|
+
"x",
|
|
4333
|
+
"y",
|
|
4334
|
+
"width",
|
|
4335
|
+
"height",
|
|
4336
|
+
],
|
|
4337
|
+
},
|
|
4338
|
+
},
|
|
4339
|
+
handler(scope, overload, out, ...args) {
|
|
4340
|
+
if (overload === "circle") {
|
|
4341
|
+
const [type, team, x, y, radius] = args;
|
|
4342
|
+
return [
|
|
4343
|
+
null,
|
|
4344
|
+
[
|
|
4345
|
+
new InstructionBase("query", "circle", type, team, x, y, radius, "0"),
|
|
4346
|
+
],
|
|
4347
|
+
];
|
|
4348
|
+
}
|
|
4349
|
+
const [type, team, x, y, width, height] = args;
|
|
4350
|
+
return [
|
|
4351
|
+
null,
|
|
4352
|
+
[
|
|
4353
|
+
new InstructionBase("query", "rect", type, team, x, y, width, height),
|
|
4354
|
+
],
|
|
4355
|
+
];
|
|
4356
|
+
},
|
|
4357
|
+
});
|
|
4358
|
+
super(data);
|
|
4359
|
+
}
|
|
4360
|
+
}
|
|
4361
|
+
|
|
4362
|
+
class SpawnBullet extends MacroFunction {
|
|
4363
|
+
constructor() {
|
|
4364
|
+
super((scope, out, options) => {
|
|
4365
|
+
assertIsObjectMacro(options, "The options");
|
|
4366
|
+
const { from, index, x, y, rotation, team, owner, damage, velocityScl, lifeScl, aimX, aimY, } = options.data;
|
|
4367
|
+
assertIsRuntimeValue(from, "from");
|
|
4368
|
+
assertIsRuntimeValue(x, "x");
|
|
4369
|
+
assertIsRuntimeValue(y, "y");
|
|
4370
|
+
assertIsRuntimeValue(rotation, "rotation");
|
|
4371
|
+
assertIsRuntimeValue(team, "team");
|
|
4372
|
+
const output = StoreValue.from(scope, out);
|
|
4373
|
+
return [
|
|
4374
|
+
output,
|
|
4375
|
+
[
|
|
4376
|
+
new InstructionBase("bullet", output, from, index ?? "0", x, y, rotation, team, owner ?? "null", damage ?? "-1", velocityScl ?? "1", lifeScl ?? "1", aimX ?? "-1", aimY ?? "-1"),
|
|
4377
|
+
],
|
|
4378
|
+
];
|
|
4379
|
+
});
|
|
4380
|
+
}
|
|
4381
|
+
}
|
|
4382
|
+
|
|
4383
|
+
class PlayMusic extends MacroFunction {
|
|
4384
|
+
constructor() {
|
|
4385
|
+
super((scope, out, name, interrupt) => {
|
|
4386
|
+
assertIsRuntimeValue(name, "name");
|
|
4387
|
+
return [
|
|
4388
|
+
null,
|
|
4389
|
+
[new InstructionBase("playmusic", name, interrupt ?? "true")],
|
|
4390
|
+
];
|
|
4391
|
+
});
|
|
4392
|
+
}
|
|
4393
|
+
}
|
|
4394
|
+
|
|
3490
4395
|
class MemoryEntry extends BaseValue {
|
|
3491
4396
|
constructor(scope, mem, prop) {
|
|
3492
4397
|
super();
|
|
@@ -3518,7 +4423,7 @@ class MemoryEntry extends BaseValue {
|
|
|
3518
4423
|
debugString() {
|
|
3519
4424
|
return "MemoryEntry";
|
|
3520
4425
|
}
|
|
3521
|
-
|
|
4426
|
+
toMlogString() {
|
|
3522
4427
|
return '"[macro MemoryEntry]"';
|
|
3523
4428
|
}
|
|
3524
4429
|
}
|
|
@@ -3547,33 +4452,108 @@ class MemoryMacro extends ObjectValue {
|
|
|
3547
4452
|
return super.hasProperty(scope, prop);
|
|
3548
4453
|
}
|
|
3549
4454
|
debugString() {
|
|
3550
|
-
return `Memory("${this.cell.
|
|
4455
|
+
return `Memory("${this.cell.toMlogString()}")`;
|
|
3551
4456
|
}
|
|
3552
|
-
|
|
4457
|
+
toMlogString() {
|
|
3553
4458
|
return '"[macro Memory]"';
|
|
3554
4459
|
}
|
|
3555
4460
|
}
|
|
3556
4461
|
class MemoryBuilder extends MacroFunction {
|
|
3557
4462
|
constructor() {
|
|
3558
4463
|
super((scope, out, cell, size = new LiteralValue(64)) => {
|
|
3559
|
-
if (!(cell instanceof
|
|
3560
|
-
throw new CompilerError("Memory cell must be a
|
|
4464
|
+
if (!(cell instanceof StoreValue))
|
|
4465
|
+
throw new CompilerError("Memory cell must be a store value.");
|
|
3561
4466
|
if (!(size instanceof LiteralValue && size.isNumber()) &&
|
|
3562
4467
|
!(size instanceof StoreValue))
|
|
3563
4468
|
throw new CompilerError("The memory size must be a number literal or a store.");
|
|
3564
|
-
if (size.mutability === EMutability.constant
|
|
4469
|
+
if (size.mutability === EMutability.constant ||
|
|
4470
|
+
size.mutability === EMutability.immutable) {
|
|
3565
4471
|
return [new MemoryMacro(cell, size), []];
|
|
3566
4472
|
}
|
|
3567
4473
|
const name = extractOutName(out) ?? scope.makeTempName();
|
|
3568
4474
|
const store = new StoreValue(`${name}.&len`);
|
|
3569
4475
|
const [, inst] = store["="](scope, size);
|
|
3570
|
-
store.mutability = EMutability.
|
|
4476
|
+
store.mutability = EMutability.immutable;
|
|
3571
4477
|
return [new MemoryMacro(cell, store), inst];
|
|
3572
4478
|
});
|
|
3573
4479
|
}
|
|
3574
4480
|
}
|
|
3575
4481
|
|
|
3576
|
-
|
|
4482
|
+
class QueriesMacro extends ObjectValue {
|
|
4483
|
+
constructor() {
|
|
4484
|
+
super({
|
|
4485
|
+
at: new MacroFunction((scope, out, index) => {
|
|
4486
|
+
if (!index) {
|
|
4487
|
+
throw new CompilerError("Expected index argument for queries.at()");
|
|
4488
|
+
}
|
|
4489
|
+
const outValue = StoreValue.from(scope, out);
|
|
4490
|
+
return [
|
|
4491
|
+
outValue,
|
|
4492
|
+
[new InstructionBase("read", outValue, "@queries", index)],
|
|
4493
|
+
];
|
|
4494
|
+
}),
|
|
4495
|
+
});
|
|
4496
|
+
this.queriesStore = new StoreValue("@queries", EMutability.readonly);
|
|
4497
|
+
}
|
|
4498
|
+
get(scope, key, out) {
|
|
4499
|
+
if (super.hasProperty(scope, key))
|
|
4500
|
+
return super.get(scope, key, out);
|
|
4501
|
+
if (key instanceof LiteralValue && key.isString()) {
|
|
4502
|
+
if (key.data === "length") {
|
|
4503
|
+
const outValue = StoreValue.from(scope, out);
|
|
4504
|
+
return [
|
|
4505
|
+
outValue,
|
|
4506
|
+
[new InstructionBase("sensor", outValue, "@queries", "@size")],
|
|
4507
|
+
];
|
|
4508
|
+
}
|
|
4509
|
+
if (key.data === "value") {
|
|
4510
|
+
return [this.queriesStore, []];
|
|
4511
|
+
}
|
|
4512
|
+
}
|
|
4513
|
+
if ((key instanceof LiteralValue && key.isNumber()) ||
|
|
4514
|
+
!(key instanceof LiteralValue)) {
|
|
4515
|
+
const outValue = StoreValue.from(scope, out);
|
|
4516
|
+
return [
|
|
4517
|
+
outValue,
|
|
4518
|
+
[new InstructionBase("read", outValue, "@queries", key)],
|
|
4519
|
+
];
|
|
4520
|
+
}
|
|
4521
|
+
throw new CompilerError(`The member [${key.debugString()}] is not present in QueriesView`);
|
|
4522
|
+
}
|
|
4523
|
+
hasProperty(scope, prop) {
|
|
4524
|
+
if ((prop instanceof LiteralValue &&
|
|
4525
|
+
(prop.isNumber() || prop.data === "length" || prop.data === "value")) ||
|
|
4526
|
+
!(prop instanceof LiteralValue)) {
|
|
4527
|
+
return true;
|
|
4528
|
+
}
|
|
4529
|
+
return super.hasProperty(scope, prop);
|
|
4530
|
+
}
|
|
4531
|
+
debugString() {
|
|
4532
|
+
return "QueriesView";
|
|
4533
|
+
}
|
|
4534
|
+
toMlogString() {
|
|
4535
|
+
return '"[macro QueriesView]"';
|
|
4536
|
+
}
|
|
4537
|
+
}
|
|
4538
|
+
|
|
4539
|
+
const dynamicVars = [
|
|
4540
|
+
"unit",
|
|
4541
|
+
"tick",
|
|
4542
|
+
"time",
|
|
4543
|
+
"second",
|
|
4544
|
+
"minute",
|
|
4545
|
+
"waveNumber",
|
|
4546
|
+
"waveTime",
|
|
4547
|
+
"server",
|
|
4548
|
+
"client",
|
|
4549
|
+
"clientLocale",
|
|
4550
|
+
"clientUnit",
|
|
4551
|
+
"clientName",
|
|
4552
|
+
"clientTeam",
|
|
4553
|
+
"clientMobile",
|
|
4554
|
+
"clientMusicPlaying",
|
|
4555
|
+
"clientCurrentMusic",
|
|
4556
|
+
];
|
|
3577
4557
|
class NamespaceMacro extends ObjectValue {
|
|
3578
4558
|
constructor({ changeCasing = false } = {}) {
|
|
3579
4559
|
super();
|
|
@@ -3585,11 +4565,15 @@ class NamespaceMacro extends ObjectValue {
|
|
|
3585
4565
|
if (!(key instanceof LiteralValue) || !key.isString())
|
|
3586
4566
|
throw new CompilerError("Cannot use dynamic properties on namespace macros");
|
|
3587
4567
|
const symbolName = this.changeCasing ? camelToDashCase(key.data) : key.data;
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
4568
|
+
if (dynamicVars.includes(symbolName)) {
|
|
4569
|
+
return [
|
|
4570
|
+
new StoreValue(`@${symbolName}`, EMutability.readonly, {
|
|
4571
|
+
volatile: true,
|
|
4572
|
+
}),
|
|
4573
|
+
[],
|
|
4574
|
+
];
|
|
4575
|
+
}
|
|
4576
|
+
return [new StoreValue(`@${symbolName}`, EMutability.constant), []];
|
|
3593
4577
|
}
|
|
3594
4578
|
hasProperty(scope, prop) {
|
|
3595
4579
|
return prop instanceof LiteralValue && prop.isString();
|
|
@@ -3598,16 +4582,59 @@ class NamespaceMacro extends ObjectValue {
|
|
|
3598
4582
|
class VarsNamespace extends NamespaceMacro {
|
|
3599
4583
|
constructor() {
|
|
3600
4584
|
super();
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
4585
|
+
}
|
|
4586
|
+
}
|
|
4587
|
+
class ColorsNamespace extends NamespaceMacro {
|
|
4588
|
+
constructor() {
|
|
4589
|
+
super();
|
|
4590
|
+
}
|
|
4591
|
+
get(scope, key, out) {
|
|
4592
|
+
if (!(key instanceof LiteralValue) || !key.isString())
|
|
4593
|
+
return super.get(scope, key, out);
|
|
4594
|
+
const plainName = key.data;
|
|
4595
|
+
const name = `@color${plainName[0].toUpperCase()}${plainName.slice(1)}`;
|
|
4596
|
+
const result = new StoreValue(name, EMutability.constant);
|
|
4597
|
+
return [result, []];
|
|
4598
|
+
}
|
|
4599
|
+
}
|
|
4600
|
+
class SoundsNamespace extends NamespaceMacro {
|
|
4601
|
+
constructor() {
|
|
4602
|
+
super();
|
|
4603
|
+
}
|
|
4604
|
+
get(scope, key, out) {
|
|
4605
|
+
if (!(key instanceof LiteralValue) || !key.isString())
|
|
4606
|
+
return super.get(scope, key, out);
|
|
4607
|
+
const result = new StoreValue(`@sfx-${key.data}`, EMutability.constant);
|
|
4608
|
+
return [result, []];
|
|
4609
|
+
}
|
|
4610
|
+
}
|
|
4611
|
+
class StatusEffectsNamespace extends NamespaceMacro {
|
|
4612
|
+
constructor() {
|
|
4613
|
+
super({ changeCasing: true });
|
|
4614
|
+
}
|
|
4615
|
+
get(scope, key, out) {
|
|
4616
|
+
if (!(key instanceof LiteralValue) || !key.isString())
|
|
4617
|
+
return super.get(scope, key, out);
|
|
4618
|
+
const name = `@status-${camelToDashCase(key.data)}`;
|
|
4619
|
+
const result = new StoreValue(name, EMutability.constant);
|
|
4620
|
+
return [result, []];
|
|
4621
|
+
}
|
|
4622
|
+
}
|
|
4623
|
+
class PVarsNamespace extends NamespaceMacro {
|
|
4624
|
+
constructor() {
|
|
4625
|
+
super();
|
|
4626
|
+
}
|
|
4627
|
+
get(scope, key, out) {
|
|
4628
|
+
if (key instanceof LiteralValue && key.data === "queries") {
|
|
4629
|
+
return [new QueriesMacro(), []];
|
|
4630
|
+
}
|
|
4631
|
+
return super.get(scope, key, out);
|
|
3605
4632
|
}
|
|
3606
4633
|
}
|
|
3607
4634
|
|
|
3608
4635
|
/**
|
|
3609
|
-
* The size of a dynamic array iteration item.
|
|
3610
|
-
*
|
|
4636
|
+
* The size of a dynamic array iteration item. Includes the set counter
|
|
4637
|
+
* instruction
|
|
3611
4638
|
*/
|
|
3612
4639
|
const itemSize = 2;
|
|
3613
4640
|
class DynamicArray extends ObjectValue {
|
|
@@ -3762,8 +4789,8 @@ class DynamicArray extends ObjectValue {
|
|
|
3762
4789
|
this.scope = scope;
|
|
3763
4790
|
this.name = name;
|
|
3764
4791
|
this.values = values;
|
|
3765
|
-
this.getterTemp = new
|
|
3766
|
-
this.setterTemp = new
|
|
4792
|
+
this.getterTemp = new StoreValue(getterName);
|
|
4793
|
+
this.setterTemp = new StoreValue(setterName);
|
|
3767
4794
|
this.returnTemp = new StoreValue(returnName);
|
|
3768
4795
|
if (dynamic)
|
|
3769
4796
|
this.lengthStore = lengthStore;
|
|
@@ -3847,7 +4874,7 @@ class DynamicArray extends ObjectValue {
|
|
|
3847
4874
|
const id = this.dynamic ? "DynamicArray" : "MutableArray";
|
|
3848
4875
|
return `${id}("${this.name}")`;
|
|
3849
4876
|
}
|
|
3850
|
-
|
|
4877
|
+
toMlogString() {
|
|
3851
4878
|
const id = this.dynamic ? "DynamicArray" : "MutableArray";
|
|
3852
4879
|
return `"[macro ${id}]"`;
|
|
3853
4880
|
}
|
|
@@ -3878,7 +4905,7 @@ class DynamicArrayEntry extends BaseValue {
|
|
|
3878
4905
|
? out
|
|
3879
4906
|
: out
|
|
3880
4907
|
? getterTemp
|
|
3881
|
-
:
|
|
4908
|
+
: StoreValue.from(scope);
|
|
3882
4909
|
// used in checked mode, jumps to this address
|
|
3883
4910
|
// if the index is out of bounds
|
|
3884
4911
|
const failAddr = this.failAddress ?? new LiteralValue(null);
|
|
@@ -3949,7 +4976,7 @@ class DynamicArrayEntry extends BaseValue {
|
|
|
3949
4976
|
const name = this.array.dynamic ? "DynamicArray" : "MutableArray";
|
|
3950
4977
|
return `${name}Entry`;
|
|
3951
4978
|
}
|
|
3952
|
-
|
|
4979
|
+
toMlogString() {
|
|
3953
4980
|
const name = this.array.dynamic ? "DynamicArray" : "MutableArray";
|
|
3954
4981
|
return `"[macro ${name}Entry]"`;
|
|
3955
4982
|
}
|
|
@@ -3972,7 +4999,7 @@ class DynamicArrayConstructor extends MacroFunction {
|
|
|
3972
4999
|
throw new CompilerError("The dynamic array initializer must be an array macro or a number literal");
|
|
3973
5000
|
}
|
|
3974
5001
|
for (let i = 0; i < length; i++) {
|
|
3975
|
-
values.push(new
|
|
5002
|
+
values.push(new StoreValue(`${name}->${i}`));
|
|
3976
5003
|
}
|
|
3977
5004
|
if (init instanceof ObjectValue) {
|
|
3978
5005
|
const length = init.data.length.data;
|
|
@@ -4013,17 +5040,234 @@ class Unchecked extends MacroFunction {
|
|
|
4013
5040
|
}
|
|
4014
5041
|
}
|
|
4015
5042
|
|
|
5043
|
+
class GetBuildings extends MacroFunction {
|
|
5044
|
+
constructor() {
|
|
5045
|
+
super(() => [new BuildingsMacro(), []]);
|
|
5046
|
+
}
|
|
5047
|
+
}
|
|
5048
|
+
class BuildingsMacro extends ObjectValue {
|
|
5049
|
+
hasProperty(scope, prop) {
|
|
5050
|
+
if (isKeyBuildingName(prop)) {
|
|
5051
|
+
return true;
|
|
5052
|
+
}
|
|
5053
|
+
return super.hasProperty(scope, prop);
|
|
5054
|
+
}
|
|
5055
|
+
get(scope, key, out) {
|
|
5056
|
+
if (super.hasProperty(scope, key))
|
|
5057
|
+
return super.get(scope, key, out);
|
|
5058
|
+
if (!isKeyBuildingName(key)) {
|
|
5059
|
+
throw new CompilerError(`[${key.debugString()}] is not a valid building name`);
|
|
5060
|
+
}
|
|
5061
|
+
return [new StoreValue(key.data, EMutability.constant), []];
|
|
5062
|
+
}
|
|
5063
|
+
}
|
|
5064
|
+
function isKeyBuildingName(key) {
|
|
5065
|
+
if (!(key instanceof LiteralValue) || !key.isString())
|
|
5066
|
+
return false;
|
|
5067
|
+
const regex = /^\w+$/;
|
|
5068
|
+
return regex.test(key.data);
|
|
5069
|
+
}
|
|
5070
|
+
|
|
4016
5071
|
class GetGlobal extends MacroFunction {
|
|
4017
5072
|
constructor(mutability) {
|
|
4018
5073
|
super((scope, out, name) => {
|
|
4019
5074
|
if (!(name instanceof LiteralValue) || !name.isString())
|
|
4020
5075
|
throw new CompilerError("The name parameter must be a string literal.");
|
|
4021
|
-
const result = new
|
|
5076
|
+
const result = new StoreValue(name.data, mutability);
|
|
4022
5077
|
return [result, []];
|
|
4023
5078
|
});
|
|
4024
5079
|
}
|
|
4025
5080
|
}
|
|
4026
5081
|
|
|
5082
|
+
class GetColor extends MacroFunction {
|
|
5083
|
+
constructor() {
|
|
5084
|
+
super((scope, out, color) => {
|
|
5085
|
+
if (!(color instanceof LiteralValue) || !color.isString())
|
|
5086
|
+
throw new CompilerError("The color parameter must be a string literal.");
|
|
5087
|
+
return [new StoreValue(`%${color.data}`, EMutability.constant), []];
|
|
5088
|
+
});
|
|
5089
|
+
}
|
|
5090
|
+
}
|
|
5091
|
+
|
|
5092
|
+
class MarkerConstructor extends ObjectValue {
|
|
5093
|
+
constructor() {
|
|
5094
|
+
const data = createOverloadNamespace({
|
|
5095
|
+
overloads: {
|
|
5096
|
+
of: { args: ["id"] },
|
|
5097
|
+
shapeText: {
|
|
5098
|
+
named: "options",
|
|
5099
|
+
args: ["id", "x", "y", "replace"],
|
|
5100
|
+
},
|
|
5101
|
+
point: {
|
|
5102
|
+
named: "options",
|
|
5103
|
+
args: ["id", "x", "y", "replace"],
|
|
5104
|
+
},
|
|
5105
|
+
shape: {
|
|
5106
|
+
named: "options",
|
|
5107
|
+
args: ["id", "x", "y", "replace"],
|
|
5108
|
+
},
|
|
5109
|
+
line: {
|
|
5110
|
+
named: "options",
|
|
5111
|
+
args: ["id", "x", "y", "replace"],
|
|
5112
|
+
},
|
|
5113
|
+
text: {
|
|
5114
|
+
named: "options",
|
|
5115
|
+
args: ["id", "x", "y", "replace"],
|
|
5116
|
+
},
|
|
5117
|
+
texture: {
|
|
5118
|
+
named: "options",
|
|
5119
|
+
args: ["id", "x", "y", "replace"],
|
|
5120
|
+
},
|
|
5121
|
+
quad: {
|
|
5122
|
+
named: "options",
|
|
5123
|
+
args: ["id", "x", "y", "replace"],
|
|
5124
|
+
},
|
|
5125
|
+
},
|
|
5126
|
+
handler(scope, overload, out, ...args) {
|
|
5127
|
+
// makemarker id line x y replace
|
|
5128
|
+
const inst = [];
|
|
5129
|
+
let id = args[0];
|
|
5130
|
+
const rest = args.slice(1);
|
|
5131
|
+
if (!(id instanceof LiteralValue)) {
|
|
5132
|
+
// prevent mutations to the source
|
|
5133
|
+
// from affecting the object's internal value
|
|
5134
|
+
const store = StoreValue.from(scope);
|
|
5135
|
+
pipeInsts(store["="](scope, id), inst);
|
|
5136
|
+
id = store;
|
|
5137
|
+
}
|
|
5138
|
+
const marker = new MarkerMacro(id);
|
|
5139
|
+
if (overload !== "of") {
|
|
5140
|
+
inst.push(new InstructionBase("makemarker", overload, id, ...rest));
|
|
5141
|
+
}
|
|
5142
|
+
return [marker, inst];
|
|
5143
|
+
},
|
|
5144
|
+
});
|
|
5145
|
+
super(data);
|
|
5146
|
+
}
|
|
5147
|
+
}
|
|
5148
|
+
class MarkerMacro extends ObjectValue {
|
|
5149
|
+
constructor(id) {
|
|
5150
|
+
super({
|
|
5151
|
+
remove: new MacroFunction(() => [null, [setmarker(id, "remove")]]),
|
|
5152
|
+
world: new MarkerMacroSetter(id, "world"),
|
|
5153
|
+
minimap: new MarkerMacroSetter(id, "minimap"),
|
|
5154
|
+
light: new MarkerMacroSetter(id, "light"),
|
|
5155
|
+
autoscale: new MarkerMacroSetter(id, "autoscale"),
|
|
5156
|
+
pos: new MarkerMacroSetter(id, "pos", ["x", "y"]),
|
|
5157
|
+
endPos: new MarkerMacroSetter(id, "endPos", ["x", "y"]),
|
|
5158
|
+
drawLayer: new MarkerMacroSetter(id, "drawLayer"),
|
|
5159
|
+
color: new MarkerMacroSetter(id, "color"),
|
|
5160
|
+
radius: new MarkerMacroSetter(id, "radius"),
|
|
5161
|
+
stroke: new MarkerMacroSetter(id, "stroke"),
|
|
5162
|
+
rotation: new MarkerMacroSetter(id, "rotation"),
|
|
5163
|
+
shape: new MarkerMacroSetter(id, "shape", ["sides", "fill", "outline"]),
|
|
5164
|
+
arc: new MarkerMacroSetter(id, "arc", ["start", "end"]),
|
|
5165
|
+
flushText: new MacroFunction((scope, out, options) => {
|
|
5166
|
+
assertIsObjectMacro(options, "options");
|
|
5167
|
+
const [fetch] = assertObjectFields(options, ["fetch"]);
|
|
5168
|
+
return [null, [setmarker(id, "flushText", fetch)]];
|
|
5169
|
+
}),
|
|
5170
|
+
fontSize: new MarkerMacroSetter(id, "fontSize"),
|
|
5171
|
+
textAlign: new MarkerMacroSetter(id, "textAlign"),
|
|
5172
|
+
lineAlign: new MarkerMacroSetter(id, "lineAlign"),
|
|
5173
|
+
textHeight: new MarkerMacroSetter(id, "textHeight"),
|
|
5174
|
+
outline: new MarkerMacroSetter(id, "outline"),
|
|
5175
|
+
labelFlags: new MarkerMacroSetter(id, "labelFlags", [
|
|
5176
|
+
"background",
|
|
5177
|
+
"outline",
|
|
5178
|
+
]),
|
|
5179
|
+
texture: new MarkerMacroSetter(id, "texture", [], args => ["0", ...args]),
|
|
5180
|
+
flushTexture: new MacroFunction(() => [
|
|
5181
|
+
null,
|
|
5182
|
+
[setmarker(id, "texture", "1")],
|
|
5183
|
+
]),
|
|
5184
|
+
textureSize: new MarkerMacroSetter(id, "textureSize", [
|
|
5185
|
+
"width",
|
|
5186
|
+
"height",
|
|
5187
|
+
]),
|
|
5188
|
+
posi: new MarkerMacroSetter(id, "posi", ["index", "x", "y"]),
|
|
5189
|
+
uvi: new MarkerMacroSetter(id, "uvi", ["index", "x", "y"]),
|
|
5190
|
+
colori: new MarkerMacroSetter(id, "colori", ["index", "color"]),
|
|
5191
|
+
});
|
|
5192
|
+
}
|
|
5193
|
+
}
|
|
5194
|
+
class MarkerMacroSetter extends VoidValue {
|
|
5195
|
+
constructor(id, prop, keys = [], modifyArgs) {
|
|
5196
|
+
super();
|
|
5197
|
+
this.id = id;
|
|
5198
|
+
this.prop = prop;
|
|
5199
|
+
this.keys = keys;
|
|
5200
|
+
this.modifyArgs = modifyArgs;
|
|
5201
|
+
}
|
|
5202
|
+
"="(scope, value) {
|
|
5203
|
+
const inst = [];
|
|
5204
|
+
let args = [];
|
|
5205
|
+
if (this.keys.length === 0) {
|
|
5206
|
+
args.push(value);
|
|
5207
|
+
}
|
|
5208
|
+
else {
|
|
5209
|
+
for (const key of this.keys) {
|
|
5210
|
+
const member = pipeInsts(value.get(scope, new LiteralValue(key)), inst);
|
|
5211
|
+
args.push(member);
|
|
5212
|
+
}
|
|
5213
|
+
}
|
|
5214
|
+
if (this.modifyArgs) {
|
|
5215
|
+
args = this.modifyArgs(args);
|
|
5216
|
+
}
|
|
5217
|
+
inst.push(setmarker(this.id, this.prop, ...args));
|
|
5218
|
+
return [value, inst];
|
|
5219
|
+
}
|
|
5220
|
+
}
|
|
5221
|
+
function setmarker(id, prop, ...args) {
|
|
5222
|
+
return new InstructionBase("setmarker", prop, id, ...args);
|
|
5223
|
+
}
|
|
5224
|
+
|
|
5225
|
+
class StringView extends ObjectValue {
|
|
5226
|
+
constructor(string) {
|
|
5227
|
+
super();
|
|
5228
|
+
this.string = string;
|
|
5229
|
+
}
|
|
5230
|
+
get(scope, key, out) {
|
|
5231
|
+
if (super.hasProperty(scope, key))
|
|
5232
|
+
return super.get(scope, key, out);
|
|
5233
|
+
if (key instanceof LiteralValue) {
|
|
5234
|
+
if (key.data === "length") {
|
|
5235
|
+
return this.string.get(scope, key, out);
|
|
5236
|
+
}
|
|
5237
|
+
if (!key.isNumber())
|
|
5238
|
+
throw new CompilerError(`The member [${key.debugString()}] is not present in [${this.debugString()}]`);
|
|
5239
|
+
if (this.string instanceof LiteralValue && this.string.isString()) {
|
|
5240
|
+
const char = this.string.data.charCodeAt(key.data);
|
|
5241
|
+
return [new LiteralValue(char), []];
|
|
5242
|
+
}
|
|
5243
|
+
}
|
|
5244
|
+
const outValue = StoreValue.from(scope, out);
|
|
5245
|
+
return [
|
|
5246
|
+
outValue,
|
|
5247
|
+
[new InstructionBase("read", outValue, this.string, key)],
|
|
5248
|
+
];
|
|
5249
|
+
}
|
|
5250
|
+
hasProperty(scope, prop) {
|
|
5251
|
+
if ((prop instanceof LiteralValue && prop.isNumber()) ||
|
|
5252
|
+
prop instanceof StoreValue)
|
|
5253
|
+
return true;
|
|
5254
|
+
return super.hasProperty(scope, prop);
|
|
5255
|
+
}
|
|
5256
|
+
debugString() {
|
|
5257
|
+
return `StringView(${this.string.debugString()})`;
|
|
5258
|
+
}
|
|
5259
|
+
toMlogString() {
|
|
5260
|
+
return '"[macro StringView]"';
|
|
5261
|
+
}
|
|
5262
|
+
}
|
|
5263
|
+
class StringViewBuilder extends MacroFunction {
|
|
5264
|
+
constructor() {
|
|
5265
|
+
super((scope, out, string) => {
|
|
5266
|
+
return [new StringView(string), []];
|
|
5267
|
+
});
|
|
5268
|
+
}
|
|
5269
|
+
}
|
|
5270
|
+
|
|
4027
5271
|
class Asm extends MacroFunction {
|
|
4028
5272
|
constructor() {
|
|
4029
5273
|
super((scope, out, stringsArray, ...values) => {
|
|
@@ -4055,7 +5299,7 @@ function formatInstructions(args) {
|
|
|
4055
5299
|
const segments = arg.split("\n");
|
|
4056
5300
|
const params = [...buffer, segments[0]];
|
|
4057
5301
|
if (validateInstructionArgs(params)) {
|
|
4058
|
-
instructions.push(new
|
|
5302
|
+
instructions.push(new AsmInstruction(...params));
|
|
4059
5303
|
buffer = [];
|
|
4060
5304
|
}
|
|
4061
5305
|
// iterates over all segments except the first and last
|
|
@@ -4067,18 +5311,21 @@ function formatInstructions(args) {
|
|
|
4067
5311
|
const trimmed = segments[i].trim();
|
|
4068
5312
|
if (trimmed.length == 0)
|
|
4069
5313
|
continue;
|
|
4070
|
-
instructions.push(new
|
|
5314
|
+
instructions.push(new AsmInstruction(trimmed));
|
|
4071
5315
|
}
|
|
4072
5316
|
if (segments.length > 1) {
|
|
4073
5317
|
buffer.push(segments[segments.length - 1]);
|
|
4074
5318
|
}
|
|
4075
5319
|
}
|
|
4076
5320
|
if (buffer.length > 0 && validateInstructionArgs(buffer)) {
|
|
4077
|
-
instructions.push(new
|
|
5321
|
+
instructions.push(new AsmInstruction(...buffer));
|
|
4078
5322
|
}
|
|
4079
5323
|
return instructions;
|
|
4080
5324
|
}
|
|
4081
|
-
/**
|
|
5325
|
+
/**
|
|
5326
|
+
* Determines if an asm line should be generated and trims it at the start and
|
|
5327
|
+
* end
|
|
5328
|
+
*/
|
|
4082
5329
|
function validateInstructionArgs(args) {
|
|
4083
5330
|
if (args.length === 0)
|
|
4084
5331
|
return false;
|
|
@@ -4097,11 +5344,29 @@ function validateInstructionArgs(args) {
|
|
|
4097
5344
|
args[args.length - 1] = last.trimEnd();
|
|
4098
5345
|
return true;
|
|
4099
5346
|
}
|
|
5347
|
+
class AsmInstruction extends InstructionBase {
|
|
5348
|
+
constructor(...args) {
|
|
5349
|
+
super(...args);
|
|
5350
|
+
const [first] = args;
|
|
5351
|
+
if (typeof first === "string") {
|
|
5352
|
+
this.ignoredByParser = /^\s*(#|\S+:)/.test(first);
|
|
5353
|
+
}
|
|
5354
|
+
}
|
|
5355
|
+
toString() {
|
|
5356
|
+
return formatInstructionArgs(this.args).join("");
|
|
5357
|
+
}
|
|
5358
|
+
}
|
|
4100
5359
|
|
|
4101
5360
|
/**
|
|
4102
|
-
*
|
|
5361
|
+
* Creates the global scope of the user's script, contains all built-ins that
|
|
5362
|
+
* are not privileged
|
|
4103
5363
|
*/
|
|
4104
|
-
function
|
|
5364
|
+
function createGlobalScope() {
|
|
5365
|
+
const scope = new Scope({
|
|
5366
|
+
builtInModules: {
|
|
5367
|
+
[worldModuleName]: createWordModule(),
|
|
5368
|
+
},
|
|
5369
|
+
});
|
|
4105
5370
|
scope.hardSet("undefined", new LiteralValue(null));
|
|
4106
5371
|
// namespaces
|
|
4107
5372
|
scope.hardSet("ControlKind", new NamespaceMacro());
|
|
@@ -4112,19 +5377,30 @@ function initScope(scope) {
|
|
|
4112
5377
|
scope.hardSet("Units", new NamespaceMacro({ changeCasing: true }));
|
|
4113
5378
|
scope.hardSet("LAccess", new NamespaceMacro());
|
|
4114
5379
|
scope.hardSet("Blocks", new NamespaceMacro({ changeCasing: true }));
|
|
5380
|
+
scope.hardSet("Colors", new ColorsNamespace());
|
|
5381
|
+
scope.hardSet("StatusEffects", new StatusEffectsNamespace());
|
|
4115
5382
|
// helper methods
|
|
4116
5383
|
scope.hardSet("getBuilding", new GetGlobal(EMutability.constant));
|
|
5384
|
+
scope.hardSet("getBuildings", new GetBuildings());
|
|
4117
5385
|
scope.hardSet("getVar", new GetGlobal(EMutability.mutable));
|
|
5386
|
+
scope.hardSet("getColor", new GetColor());
|
|
4118
5387
|
scope.hardSet("concat", new Concat());
|
|
4119
5388
|
scope.hardSet("asm", new Asm());
|
|
4120
5389
|
scope.hardSet("Math", new MlogMath());
|
|
4121
5390
|
scope.hardSet("Memory", new MemoryBuilder());
|
|
5391
|
+
scope.hardSet("MemoryView", new MemoryBuilder());
|
|
5392
|
+
scope.hardSet("StringView", new StringViewBuilder());
|
|
4122
5393
|
scope.hardSet("MutableArray", new DynamicArrayConstructor(false));
|
|
4123
5394
|
scope.hardSet("DynamicArray", new DynamicArrayConstructor(true));
|
|
4124
5395
|
scope.hardSet("unchecked", new Unchecked());
|
|
5396
|
+
scope.hardSet("Align", new NamespaceMacro());
|
|
5397
|
+
scope.hardSet("Weathers", new NamespaceMacro({ changeCasing: true }));
|
|
5398
|
+
scope.hardSet("Sounds", new SoundsNamespace());
|
|
4125
5399
|
// commands
|
|
4126
5400
|
scope.hardSet("draw", new Draw());
|
|
4127
5401
|
scope.hardSet("print", new Print());
|
|
5402
|
+
scope.hardSet("format", new Format());
|
|
5403
|
+
scope.hardSet("printChar", new PrintChar());
|
|
4128
5404
|
scope.hardSet("printFlush", new PrintFlush());
|
|
4129
5405
|
scope.hardSet("drawFlush", new DrawFlush());
|
|
4130
5406
|
scope.hardSet("getLink", new GetLink());
|
|
@@ -4134,33 +5410,50 @@ function initScope(scope) {
|
|
|
4134
5410
|
scope.hardSet("wait", new Wait());
|
|
4135
5411
|
scope.hardSet("lookup", new Lookup());
|
|
4136
5412
|
scope.hardSet("packColor", new PackColor());
|
|
5413
|
+
scope.hardSet("unpackColor", new UnpackColor());
|
|
4137
5414
|
scope.hardSet("endScript", new End());
|
|
4138
5415
|
scope.hardSet("stopScript", new Stop());
|
|
4139
5416
|
scope.hardSet("unitBind", new UnitBind());
|
|
4140
5417
|
scope.hardSet("unitControl", new UnitControl());
|
|
4141
5418
|
scope.hardSet("unitRadar", new UnitRadar());
|
|
4142
5419
|
scope.hardSet("unitLocate", new UnitLocate());
|
|
4143
|
-
// world processor commands
|
|
4144
|
-
scope.hardSet("getBlock", new GetBlock());
|
|
4145
|
-
scope.hardSet("setBlock", new SetBlock());
|
|
4146
|
-
scope.hardSet("spawnUnit", new SpawnUnit());
|
|
4147
|
-
scope.hardSet("applyStatus", new ApplyStatus());
|
|
4148
|
-
scope.hardSet("spawnWave", new SpawnWave());
|
|
4149
|
-
scope.hardSet("setRule", new SetRule());
|
|
4150
|
-
scope.hardSet("flushMessage", new FlushMessage());
|
|
4151
|
-
scope.hardSet("cutscene", new Cutscene());
|
|
4152
|
-
scope.hardSet("explosion", new Explosion());
|
|
4153
5420
|
scope.hardSet("setRate", new SetRate());
|
|
4154
|
-
scope
|
|
4155
|
-
|
|
4156
|
-
|
|
5421
|
+
return scope;
|
|
5422
|
+
}
|
|
5423
|
+
function createWordModule() {
|
|
5424
|
+
const module = new ObjectValue({
|
|
5425
|
+
PVars: new PVarsNamespace(),
|
|
5426
|
+
getBlock: new GetBlock(),
|
|
5427
|
+
setBlock: new SetBlock(),
|
|
5428
|
+
spawnUnit: new SpawnUnit(),
|
|
5429
|
+
spawnBullet: new SpawnBullet(),
|
|
5430
|
+
applyStatus: new ApplyStatus(),
|
|
5431
|
+
spawnWave: new SpawnWave(),
|
|
5432
|
+
setRule: new SetRule(),
|
|
5433
|
+
flushMessage: new FlushMessage(),
|
|
5434
|
+
cutscene: new Cutscene(),
|
|
5435
|
+
explosion: new Explosion(),
|
|
5436
|
+
setRate: new SetRate(),
|
|
5437
|
+
fetch: new Fetch(),
|
|
5438
|
+
getFlag: new GetFlag(),
|
|
5439
|
+
setFlag: new SetFlag(),
|
|
5440
|
+
setProp: new SetProp(),
|
|
5441
|
+
SyncLock: new SyncLockConstructor(),
|
|
5442
|
+
effect: new Effect(),
|
|
5443
|
+
localePrint: new LocalePrint(),
|
|
5444
|
+
Marker: new MarkerConstructor(),
|
|
5445
|
+
senseWeather: new WeatherSense(),
|
|
5446
|
+
setWeather: new WeatherSet(),
|
|
5447
|
+
playSound: new PlaySound(),
|
|
5448
|
+
playMusic: new PlayMusic(),
|
|
5449
|
+
query: new Query(),
|
|
5450
|
+
});
|
|
5451
|
+
return module;
|
|
4157
5452
|
}
|
|
4158
5453
|
|
|
4159
5454
|
class Compiler {
|
|
4160
|
-
constructor({
|
|
5455
|
+
constructor({ compactNames = false, sourcemap = false, } = {}) {
|
|
4161
5456
|
this.handlers = handlers;
|
|
4162
|
-
this.usingStack = !!stackName;
|
|
4163
|
-
this.stackName = stackName;
|
|
4164
5457
|
this.compactNames = compactNames;
|
|
4165
5458
|
this.sourcemap = sourcemap;
|
|
4166
5459
|
}
|
|
@@ -4169,8 +5462,10 @@ class Compiler {
|
|
|
4169
5462
|
let sourcemaps;
|
|
4170
5463
|
try {
|
|
4171
5464
|
const program = this.parse(script);
|
|
4172
|
-
const
|
|
4173
|
-
|
|
5465
|
+
const globalScope = createGlobalScope();
|
|
5466
|
+
// this allows the user to shadow global variables inside
|
|
5467
|
+
// the script, since it is treated as a module
|
|
5468
|
+
const scope = globalScope.createScope();
|
|
4174
5469
|
const valueInst = this.handle(scope, program);
|
|
4175
5470
|
if (needsEndInstruction(valueInst[1], scope)) {
|
|
4176
5471
|
valueInst[1].push(new EndInstruction());
|
|
@@ -4193,18 +5488,20 @@ class Compiler {
|
|
|
4193
5488
|
let i = 0;
|
|
4194
5489
|
for (const inst of valueInst[1]) {
|
|
4195
5490
|
inst.resolve(i);
|
|
4196
|
-
if (!inst.hidden)
|
|
5491
|
+
if (!inst.hidden && !inst.ignoredByParser)
|
|
4197
5492
|
i++;
|
|
4198
5493
|
}
|
|
4199
5494
|
}
|
|
4200
5495
|
serialize(resLines) {
|
|
4201
5496
|
const [, inst] = resLines;
|
|
5497
|
+
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
|
4202
5498
|
return inst.filter(l => !l.hidden).join("\n");
|
|
4203
5499
|
}
|
|
4204
5500
|
parse(script) {
|
|
4205
|
-
return
|
|
5501
|
+
return parse(script, {
|
|
4206
5502
|
ranges: true,
|
|
4207
5503
|
plugins: ["typescript"],
|
|
5504
|
+
sourceType: "module",
|
|
4208
5505
|
});
|
|
4209
5506
|
}
|
|
4210
5507
|
mapSources(valueInst) {
|
|
@@ -4215,11 +5512,11 @@ class Compiler {
|
|
|
4215
5512
|
}
|
|
4216
5513
|
return result;
|
|
4217
5514
|
}
|
|
4218
|
-
handle(scope, node, handler = this.handlers[node.type], out) {
|
|
5515
|
+
handle(scope, node, handler = this.handlers[node.type], out, arg) {
|
|
4219
5516
|
try {
|
|
4220
5517
|
if (!handler)
|
|
4221
5518
|
throw new CompilerError("Missing handler for " + node.type);
|
|
4222
|
-
const result = handler(this, scope, node, out,
|
|
5519
|
+
const result = handler(this, scope, node, out, arg);
|
|
4223
5520
|
if (this.sourcemap)
|
|
4224
5521
|
return appendSourceLocations(result, node);
|
|
4225
5522
|
return result;
|
|
@@ -4232,14 +5529,14 @@ class Compiler {
|
|
|
4232
5529
|
}
|
|
4233
5530
|
}
|
|
4234
5531
|
/** Handles the node and asserts that it resolves to a value. */
|
|
4235
|
-
handleValue(scope, node, handler = this.handlers[node.type], out) {
|
|
4236
|
-
const result = this.handle(scope, node, handler, out);
|
|
5532
|
+
handleValue(scope, node, handler = this.handlers[node.type], out, arg) {
|
|
5533
|
+
const result = this.handle(scope, node, handler, out, arg);
|
|
4237
5534
|
if (!result[0])
|
|
4238
5535
|
throw new CompilerError(`This node (${node.type}) did not return a value`, node);
|
|
4239
5536
|
return result;
|
|
4240
5537
|
}
|
|
4241
|
-
handleEval(scope, node, out) {
|
|
4242
|
-
const [res, inst] = this.handleValue(scope, node, undefined, out);
|
|
5538
|
+
handleEval(scope, node, out, arg) {
|
|
5539
|
+
const [res, inst] = this.handleValue(scope, node, undefined, out, arg);
|
|
4243
5540
|
const [evaluated, evaluatedInst] = res.eval(scope, out);
|
|
4244
5541
|
const result = [evaluated, [...inst, ...evaluatedInst]];
|
|
4245
5542
|
if (this.sourcemap)
|
|
@@ -4249,12 +5546,10 @@ class Compiler {
|
|
|
4249
5546
|
/**
|
|
4250
5547
|
* Handles many nodes in order.
|
|
4251
5548
|
*
|
|
4252
|
-
* The usage of this method over a regular loop over an array of nodes
|
|
4253
|
-
*
|
|
4254
|
-
*
|
|
4255
|
-
* handler methods ({@link handle}, {@link handleEval}
|
|
5549
|
+
* The usage of this method over a regular loop over an array of nodes is only
|
|
5550
|
+
* required if the code inside the loop generates instructions that are not
|
|
5551
|
+
* tracked by the compiler handler methods ({@link handle}, {@link handleEval}
|
|
4256
5552
|
* and {@link handleMany})
|
|
4257
|
-
*
|
|
4258
5553
|
*/
|
|
4259
5554
|
handleMany(scope, nodes, handler) {
|
|
4260
5555
|
const lines = [];
|
|
@@ -4285,5 +5580,4 @@ function needsEndInstruction(inst, scope) {
|
|
|
4285
5580
|
return scope.inst.length > 0;
|
|
4286
5581
|
}
|
|
4287
5582
|
|
|
4288
|
-
|
|
4289
|
-
exports.CompilerError = CompilerError;
|
|
5583
|
+
export { Compiler as C, CompilerError as a };
|