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.
Files changed (79) hide show
  1. package/README.md +2 -2
  2. package/dist/{index.cjs → Compiler-7GMG4MDr.js} +1746 -452
  3. package/dist/Compiler.d.ts +8 -12
  4. package/dist/CompilerError.d.ts +13 -3
  5. package/dist/Scope.d.ts +8 -8
  6. package/dist/bin/files.d.ts +18 -0
  7. package/dist/bin.mjs +170 -0
  8. package/dist/handlers/Import.d.ts +5 -0
  9. package/dist/handlers/Literal.d.ts +2 -2
  10. package/dist/handlers/Typescript.d.ts +2 -2
  11. package/dist/handlers/index.d.ts +1 -0
  12. package/dist/index.mjs +3 -4267
  13. package/dist/instructions/AddressResolver.d.ts +4 -4
  14. package/dist/instructions/ControlInstructions.d.ts +3 -3
  15. package/dist/instructions/InstructionBase.d.ts +1 -0
  16. package/dist/instructions/JumpInstruction.d.ts +5 -5
  17. package/dist/instructions/index.d.ts +0 -1
  18. package/dist/macros/DynamicArray.d.ts +4 -4
  19. package/dist/macros/Function.d.ts +2 -2
  20. package/dist/macros/GetBuildings.d.ts +4 -0
  21. package/dist/macros/GetColor.d.ts +4 -0
  22. package/dist/macros/Marker.d.ts +4 -0
  23. package/dist/macros/Namespace.d.ts +16 -0
  24. package/dist/macros/Queries.d.ts +10 -0
  25. package/dist/macros/StringView.d.ts +4 -0
  26. package/dist/macros/commands/Effect.d.ts +4 -0
  27. package/dist/macros/commands/Format.d.ts +4 -0
  28. package/dist/macros/commands/LocalePrint.d.ts +4 -0
  29. package/dist/macros/commands/PlayMusic.d.ts +4 -0
  30. package/dist/macros/commands/PlaySound.d.ts +4 -0
  31. package/dist/macros/commands/PrintChar.d.ts +4 -0
  32. package/dist/macros/commands/Query.d.ts +4 -0
  33. package/dist/macros/commands/SetProp.d.ts +4 -0
  34. package/dist/macros/commands/SpawnBullet.d.ts +4 -0
  35. package/dist/macros/commands/Sync.d.ts +6 -0
  36. package/dist/macros/commands/UnpackColor.d.ts +4 -0
  37. package/dist/macros/commands/WeatherSense.d.ts +4 -0
  38. package/dist/macros/commands/WeatherSet.d.ts +4 -0
  39. package/dist/macros/commands/index.d.ts +13 -0
  40. package/dist/macros/index.d.ts +7 -1
  41. package/dist/macros/util.d.ts +5 -2
  42. package/dist/modules.d.ts +8 -0
  43. package/dist/operators.d.ts +1 -1
  44. package/dist/types.d.ts +84 -59
  45. package/dist/utils/constants.d.ts +9 -4
  46. package/dist/utils/general.d.ts +8 -9
  47. package/dist/utils/instruction.d.ts +5 -1
  48. package/dist/utils/value.d.ts +14 -5
  49. package/dist/values/AssignmentValue.d.ts +1 -1
  50. package/dist/values/BaseValue.d.ts +4 -2
  51. package/dist/values/DestructuringValue.d.ts +5 -7
  52. package/dist/values/FunctionValue.d.ts +8 -6
  53. package/dist/values/JumpOutValue.d.ts +4 -4
  54. package/dist/values/LazyValue.d.ts +3 -3
  55. package/dist/values/LiteralValue.d.ts +2 -3
  56. package/dist/values/ObjectValue.d.ts +6 -11
  57. package/dist/values/StoreValue.d.ts +10 -5
  58. package/dist/values/VoidValue.d.ts +2 -1
  59. package/dist/values/index.d.ts +0 -1
  60. package/lib/commands.d.ts +776 -730
  61. package/lib/eslib/lib.es2015.collection.d.ts +50 -39
  62. package/lib/eslib/lib.es2015.core.d.ts +59 -0
  63. package/lib/eslib/lib.es2015.d.ts +0 -3
  64. package/lib/eslib/lib.es2015.generator.d.ts +54 -53
  65. package/lib/eslib/lib.es2015.iterable.d.ts +9 -6
  66. package/lib/eslib/lib.es5.d.ts +53 -6
  67. package/lib/globals.d.ts +309 -18
  68. package/lib/helpers.d.ts +86 -52
  69. package/lib/kinds.d.ts +42 -7
  70. package/lib/macros.d.ts +53 -18
  71. package/lib/traits.d.ts +308 -146
  72. package/lib/util.d.ts +144 -48
  73. package/lib/world.d.ts +1197 -0
  74. package/package.json +27 -35
  75. package/dist/bin.cjs +0 -111
  76. package/dist/initScope.d.ts +0 -5
  77. package/dist/instructions/ZeroSpaceInstruction.d.ts +0 -4
  78. package/dist/values/SenseableValue.d.ts +0 -24
  79. /package/dist/{bin.d.ts → bin/index.d.ts} +0 -0
package/dist/index.mjs CHANGED
@@ -1,4267 +1,3 @@
1
- import { parse } from '@babel/parser';
2
- import * as es from '@babel/types';
3
-
4
- class CompilerError extends Error {
5
- constructor(message, source) {
6
- super(message);
7
- this.loc = source;
8
- }
9
- get loc() {
10
- return this._loc;
11
- }
12
- set loc(value) {
13
- if (!value)
14
- return;
15
- if ("type" in value) {
16
- this._loc = value.loc;
17
- }
18
- else if ("start" in value) {
19
- this._loc = value;
20
- }
21
- else {
22
- this._loc = {
23
- start: value,
24
- end: value,
25
- };
26
- }
27
- }
28
- static from(error, source) {
29
- let message;
30
- if (error && typeof error === "object" && "loc" in error) {
31
- const err = error;
32
- message = err.toString();
33
- source ??= err.loc;
34
- }
35
- else if (error instanceof Error) {
36
- message = error.message;
37
- }
38
- else if (typeof error === "string") {
39
- message = error;
40
- }
41
- else {
42
- message = `Unknown error: ${error}`;
43
- }
44
- const result = new CompilerError(message, source);
45
- result.inner = error;
46
- return result;
47
- }
48
- }
49
-
50
- var EInstIntent;
51
- (function (EInstIntent) {
52
- EInstIntent[EInstIntent["none"] = 0] = "none";
53
- EInstIntent[EInstIntent["break"] = 1] = "break";
54
- EInstIntent[EInstIntent["continue"] = 2] = "continue";
55
- EInstIntent[EInstIntent["return"] = 3] = "return";
56
- /**
57
- * This is used by the `end` and `stop` instructions.
58
- * Means that the processor will restart or shutdown.
59
- */
60
- EInstIntent[EInstIntent["exit"] = 4] = "exit";
61
- })(EInstIntent || (EInstIntent = {}));
62
- /** Defines the possible types of mutability of a value */
63
- var EMutability;
64
- (function (EMutability) {
65
- /**
66
- * The value can be changed by the user's code,
67
- * by the mlog runtime, or us
68
- */
69
- EMutability[EMutability["mutable"] = 0] = "mutable";
70
- /**
71
- * The value cannot be directly assigned,
72
- * but it's still not safe from mutations coming from the runtime.
73
- */
74
- EMutability[EMutability["readonly"] = 1] = "readonly";
75
- /** Constant values, won't change during execution */
76
- EMutability[EMutability["constant"] = 2] = "constant";
77
- /** An immutable value that hasn't been initialized yet */
78
- EMutability[EMutability["init"] = 3] = "init";
79
- })(EMutability || (EMutability = {}));
80
-
81
- const arithmeticBinaryOperators = [
82
- "*",
83
- "**",
84
- "+",
85
- "-",
86
- "/",
87
- "%",
88
- ];
89
- const comparisonBinaryOperators = [
90
- "!=",
91
- "!==",
92
- "<",
93
- "<=",
94
- "==",
95
- "===",
96
- ">",
97
- ">=",
98
- ];
99
- const bitwiseBinaryOperators = [
100
- "&",
101
- "<<",
102
- ">>",
103
- ">>>",
104
- "^",
105
- "|",
106
- ];
107
- const binaryOperators = [
108
- ...arithmeticBinaryOperators,
109
- ...comparisonBinaryOperators,
110
- ...bitwiseBinaryOperators,
111
- "instanceof",
112
- "in",
113
- ];
114
- const logicalOperators = ["&&", "??", "||"];
115
- const arithmeticAssignmentOperators = [
116
- "%=",
117
- "&=",
118
- "*=",
119
- "**=",
120
- "+=",
121
- "-=",
122
- "/=",
123
- ];
124
- const logicalAssignmentOperators = ["&&=", "||=", "??="];
125
- const bitwiseAssignmentOperators = [
126
- "<<=",
127
- ">>=",
128
- ">>>=",
129
- "^=",
130
- "|=",
131
- ];
132
- const assignmentOperators = [
133
- ...arithmeticAssignmentOperators,
134
- ...logicalAssignmentOperators,
135
- ...bitwiseAssignmentOperators,
136
- "=",
137
- ];
138
- const leftRightOperators = [
139
- ...binaryOperators,
140
- ...logicalOperators,
141
- ...assignmentOperators,
142
- ];
143
- const unaryOperators = [
144
- "!",
145
- "u+",
146
- "u-",
147
- "delete",
148
- "typeof",
149
- "void",
150
- "~",
151
- ];
152
- const updateOperators = ["++", "--"];
153
- const singleOperators = [...unaryOperators, ...updateOperators];
154
- const operators = [...leftRightOperators, ...singleOperators];
155
- const operatorMap$2 = {
156
- "==": "equal",
157
- "===": "strictEqual",
158
- "!=": "notEqual",
159
- "<": "lessThan",
160
- ">": "greaterThan",
161
- "<=": "lessThanEq",
162
- ">=": "greaterThanEq",
163
- "+": "add",
164
- "-": "sub",
165
- "*": "mul",
166
- "/": "div",
167
- "%": "mod",
168
- "**": "pow",
169
- "|": "or",
170
- "&": "and",
171
- "^": "xor",
172
- ">>": "shr",
173
- ">>>": "shr",
174
- "<<": "shl",
175
- "&&": "land",
176
- "||": "or",
177
- };
178
- const orderIndependentOperators = [
179
- "!=",
180
- "!==",
181
- "&&",
182
- "&",
183
- "*",
184
- "+",
185
- "==",
186
- "===",
187
- "|",
188
- "||",
189
- ];
190
-
191
- class VoidValue {
192
- constructor() {
193
- this.mutability = EMutability.mutable;
194
- this.macro = false;
195
- }
196
- eval(_scope, _out) {
197
- throw new CompilerError(`[${this.debugString()}] cannot be evaluated into a value.`);
198
- }
199
- call(_scope, _args, _out) {
200
- throw new CompilerError(`[${this.debugString()}] is not callable.`);
201
- }
202
- get(_scope, name) {
203
- throw new CompilerError(`The member [${name.debugString()}] does not exist in [${this.debugString()}]`);
204
- }
205
- hasProperty(_scope, _prop) {
206
- return false;
207
- }
208
- preCall(_scope, _out) {
209
- return;
210
- }
211
- postCall(_scope) { }
212
- toOut() {
213
- return this;
214
- }
215
- debugString() {
216
- return "VoidValue";
217
- }
218
- toString() {
219
- return '"[macro VoidValue]"';
220
- }
221
- }
222
- for (const key of operators) {
223
- VoidValue.prototype[key] = function () {
224
- console.log(this);
225
- throw new CompilerError(`The operator '${key}' is not defined for [${this.debugString()}].`);
226
- };
227
- }
228
-
229
- class InstructionBase {
230
- get hidden() {
231
- return this._hidden;
232
- }
233
- set hidden(value) {
234
- this._hidden = value;
235
- }
236
- constructor(...args) {
237
- this.intent = EInstIntent.none;
238
- this.alwaysRuns = true;
239
- this._hidden = false;
240
- this.args = args;
241
- }
242
- resolve(_i) { }
243
- toString() {
244
- return this.args.filter(arg => arg).join(" ");
245
- }
246
- }
247
-
248
- class OperationInstruction extends InstructionBase {
249
- constructor(kind, temp, left, right) {
250
- super("op", kind, temp, left, right);
251
- }
252
- }
253
-
254
- /**
255
- * The prefix for internal variables inside the compiler output
256
- */
257
- const internalPrefix = "&";
258
- const discardedName = `${internalPrefix}_`;
259
- const itemNames = [
260
- "copper",
261
- "lead",
262
- "metaglass",
263
- "graphite",
264
- "sand",
265
- "coal",
266
- "titanium",
267
- "thorium",
268
- "scrap",
269
- "silicon",
270
- "plastanium",
271
- "phaseFabric",
272
- "surgeAlloy",
273
- "sporePod",
274
- "blastCompound",
275
- "pyratite",
276
- "beryllium",
277
- "tungsten",
278
- "oxide",
279
- "carbide",
280
- "fissileMatter",
281
- "dormantCyst",
282
- ];
283
- /** The name of a special processor variable, controls the instruction pointer */
284
- const counterName = "@counter";
285
-
286
- /**
287
- * Returns a string that has the format: [line]:[column].
288
- *
289
- * If `name` is provided, and it is a string, the resulting string will
290
- * have the format: [name]:[line]:[column].
291
- */
292
- function nodeName(node, name) {
293
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
294
- const { line, column } = node.loc.start;
295
- if (typeof name === "string")
296
- return `${name}:${line}:${column}`;
297
- return `${line}:${column}`;
298
- }
299
- /**
300
- * Converts a camel case string into a dash case one.
301
- *
302
- * As an example `camelCase` becomes `camel-case`
303
- * @example
304
- * ```
305
- * camelToDashCase("camelCase") // returns "camel-case"
306
- * ```
307
- */
308
- function camelToDashCase(name) {
309
- return name.replace(/[A-Z]/g, str => `-${str.toLowerCase()}`);
310
- }
311
- /**
312
- * A more type safe version of `Object.assign`
313
- */
314
- // eslint-disable-next-line @typescript-eslint/ban-types
315
- function assign(obj, props) {
316
- return Object.assign(obj, props);
317
- }
318
- /** Lazily computes and caches an expression */
319
- function lazy(fn) {
320
- let value;
321
- return () => (value ??= fn());
322
- }
323
-
324
- function appendSourceLocations(valueInst, node) {
325
- for (const inst of valueInst[1]) {
326
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
327
- inst.source ??= node.loc;
328
- }
329
- return valueInst;
330
- }
331
- function withAlwaysRuns(valueInst, value) {
332
- valueInst[1].forEach(inst => (inst.alwaysRuns = value));
333
- return valueInst;
334
- }
335
- /** Removes jump instructions that point right to the next line */
336
- function hideRedundantJumps(inst) {
337
- for (let i = inst.length - 1; i >= 0; i--) {
338
- const instruction = inst[i];
339
- if (!(instruction instanceof JumpInstruction))
340
- continue;
341
- const { address } = instruction;
342
- let searchIndex = i + 1;
343
- let current = inst[searchIndex];
344
- while (current?.hidden) {
345
- if (current instanceof AddressResolver &&
346
- current.bonds.includes(address)) {
347
- // only gets assigned if `current` is an adress resolver
348
- // that resolves the jump adress
349
- // and that is also right next to the jump,
350
- // without any actual instructions in between
351
- instruction.hidden = true;
352
- break;
353
- }
354
- searchIndex++;
355
- current = inst[searchIndex];
356
- }
357
- }
358
- }
359
- /** A helper that appends the instructions from `value` into `inst` and returns it's `IValue` instance */
360
- function pipeInsts(value, inst) {
361
- inst.push(...value[1]);
362
- return value[0];
363
- }
364
- function usesAddressResolver(resolver, instructions) {
365
- for (let i = 0; i < instructions.length; i++) {
366
- const inst = instructions[i];
367
- if (inst instanceof JumpInstruction &&
368
- resolver.bonds.includes(inst.address))
369
- return true;
370
- }
371
- return false;
372
- }
373
-
374
- function isTemplateObjectArray(value) {
375
- return (value instanceof ObjectValue &&
376
- value.data.length instanceof LiteralValue &&
377
- value.data.length.isNumber() &&
378
- value.data.raw instanceof ObjectValue &&
379
- value.data.raw.data.length instanceof LiteralValue &&
380
- value.data.raw.data.length.isNumber());
381
- }
382
- function extractOutName(out) {
383
- if (!out || typeof out === "string")
384
- return out;
385
- return out.name;
386
- }
387
- function extractDestrucuringOut(out, field) {
388
- if (isDiscardedOut(out))
389
- return discardedName;
390
- if (!(out instanceof DestructuringValue))
391
- return;
392
- return out.fields[field] ?? discardedName;
393
- }
394
- /** Asserts that `value` is a `LiteralValue` that contains a string */
395
- function assertStringLiteral(value, name) {
396
- if (!(value instanceof LiteralValue) || !value.isString())
397
- throw new CompilerError(`${name} must be a string literal`);
398
- }
399
- /** Asserts that `value` is a string literal that is part of a given set of `members`*/
400
- function assertLiteralOneOf(value, members, name) {
401
- assertStringLiteral(value, name);
402
- if (!members.includes(value.data))
403
- throw new CompilerError(`${name} must be one of: ${members
404
- .slice(0, -1)
405
- .map(member => `"${member}"`)
406
- .join(", ")} or "${members[members.length - 1]}"`);
407
- }
408
- /** Asserts that `value` is either a store or a literal */
409
- function assertIsRuntimeValue(value, name) {
410
- if (!(value instanceof LiteralValue) && !(value instanceof StoreValue))
411
- throw new CompilerError(`${name} must be a valid runtime value`);
412
- }
413
- /** Asserts that `value` is an `ObjectValue` */
414
- function assertIsObjectMacro(value, name) {
415
- if (!(value instanceof ObjectValue))
416
- throw new CompilerError(`${name} must be an object macro`);
417
- }
418
- /** Asserts that `value` is an `ObjectValue` that has a length property */
419
- function assertIsArrayMacro(value, name) {
420
- if (value instanceof ObjectValue) {
421
- const { length } = value.data;
422
- if (length instanceof LiteralValue && length.isNumber())
423
- return;
424
- }
425
- throw new CompilerError(`${name} must be an array macro`);
426
- }
427
- /** Asserts that all of the fields described are present on `value`*/
428
- function assertObjectFields(value, fields) {
429
- const result = [];
430
- for (const field of fields) {
431
- const param = typeof field === "object" ? field : { key: field };
432
- const item = value.data[param.key];
433
- if (item) {
434
- param.validate?.(item);
435
- result.push(item);
436
- continue;
437
- }
438
- if (param.default == undefined)
439
- throw new CompilerError(`The field "${param.name ?? param.key}" must be present`);
440
- result.push(param.default);
441
- }
442
- return result;
443
- }
444
- function isDiscardedOut(out) {
445
- if (!out)
446
- return false;
447
- if (typeof out === "string")
448
- return out === discardedName;
449
- return out.name === discardedName;
450
- }
451
-
452
- class SetInstruction extends InstructionBase {
453
- constructor(store, value) {
454
- super("set", store, value);
455
- }
456
- get store() {
457
- return this.args[1];
458
- }
459
- get value() {
460
- return this.args[2];
461
- }
462
- get hidden() {
463
- const { store, value } = this;
464
- return (this._hidden ||
465
- // assignment to discarded value
466
- store.name === discardedName ||
467
- // self assignment of a store
468
- (!!store.name &&
469
- store instanceof StoreValue &&
470
- value instanceof StoreValue &&
471
- store.name === value.name));
472
- }
473
- }
474
-
475
- class EndInstruction extends InstructionBase {
476
- constructor() {
477
- super("end");
478
- this.intent = EInstIntent.exit;
479
- }
480
- }
481
-
482
- class AddressResolver extends InstructionBase {
483
- get hidden() {
484
- return true;
485
- }
486
- set hidden(value) { }
487
- constructor(...bonds) {
488
- super();
489
- this.bonds = bonds;
490
- }
491
- resolve(i) {
492
- for (const literal of this.bonds)
493
- literal.data = i;
494
- }
495
- bind(bond) {
496
- this.bonds.push(bond);
497
- }
498
- bindBreak(scope) {
499
- scope.break = this;
500
- return this;
501
- }
502
- bindContinue(scope) {
503
- scope.continue = this;
504
- return this;
505
- }
506
- }
507
-
508
- var EJumpKind;
509
- (function (EJumpKind) {
510
- EJumpKind["Equal"] = "equal";
511
- EJumpKind["NotEqual"] = "notEqual";
512
- EJumpKind["LessThan"] = "lessThan";
513
- EJumpKind["LessThanEq"] = "lessThanEq";
514
- EJumpKind["GreaterThan"] = "greaterThan";
515
- EJumpKind["GreaterThanEq"] = "greaterThanEq";
516
- EJumpKind["StrictEqual"] = "strictEqual";
517
- EJumpKind["Always"] = "always";
518
- })(EJumpKind || (EJumpKind = {}));
519
- class JumpInstruction extends InstructionBase {
520
- constructor(address, kind, left = null, right = null) {
521
- super("jump", address, kind, left, right);
522
- this.address = address;
523
- }
524
- /**
525
- * Works with the `JumpOutValue` class to determine
526
- * wether a jump should be added if the expression wasn't jump compressed.
527
- */
528
- static or(test, out) {
529
- if (test === out)
530
- return [];
531
- return [
532
- new JumpInstruction(out.address, out.whenTrue ? EJumpKind.NotEqual : EJumpKind.Equal, test, new LiteralValue(0)),
533
- ];
534
- }
535
- }
536
-
537
- class SetCounterInstruction extends InstructionBase {
538
- constructor(value) {
539
- super("set", counterName, value);
540
- }
541
- }
542
-
543
- class ZeroSpaceInstruction extends InstructionBase {
544
- toString() {
545
- return this.args.join("");
546
- }
547
- }
548
-
549
- class BreakInstruction extends JumpInstruction {
550
- constructor(address) {
551
- super(address, EJumpKind.Always);
552
- this.intent = EInstIntent.break;
553
- }
554
- }
555
- class ContinueInstruction extends JumpInstruction {
556
- constructor(address) {
557
- super(address, EJumpKind.Always);
558
- this.intent = EInstIntent.continue;
559
- }
560
- }
561
-
562
- const operatorMap$1 = {
563
- "!=": EJumpKind.NotEqual,
564
- "<": EJumpKind.LessThan,
565
- "<=": EJumpKind.LessThanEq,
566
- "==": EJumpKind.Equal,
567
- "===": EJumpKind.StrictEqual,
568
- ">": EJumpKind.GreaterThan,
569
- ">=": EJumpKind.GreaterThanEq,
570
- };
571
- const invertedOperatorMap = {
572
- "==": operatorMap$1["!="],
573
- "!=": operatorMap$1["=="],
574
- "!==": operatorMap$1["==="],
575
- ">": operatorMap$1["<="],
576
- "<": operatorMap$1[">="],
577
- ">=": operatorMap$1["<"],
578
- "<=": operatorMap$1[">"],
579
- };
580
- class JumpOutValue extends VoidValue {
581
- constructor(node, address, whenTrue) {
582
- super();
583
- this.node = node;
584
- this.address = address;
585
- this.whenTrue = whenTrue;
586
- this.macro = true;
587
- }
588
- canHandle(operator) {
589
- if (this.whenTrue)
590
- return operator in operatorMap$1;
591
- return operator in invertedOperatorMap;
592
- }
593
- handle(operator, left, right) {
594
- let kind;
595
- if (this.whenTrue) {
596
- if (operator === "!==")
597
- throw new CompilerError('Unsupported jump compression operator "!=="');
598
- kind = operatorMap$1[operator];
599
- }
600
- else {
601
- if (operator === "===")
602
- throw new CompilerError('Unsupported jump compression operator "==="');
603
- kind = invertedOperatorMap[operator];
604
- }
605
- const jump = new JumpInstruction(this.address, kind, left, right);
606
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
607
- jump.source = this.node.loc;
608
- return jump;
609
- }
610
- eval(_scope, _out) {
611
- return [this, []];
612
- }
613
- debugString() {
614
- return "JumpOutValue";
615
- }
616
- toString() {
617
- return '"[macro JumpOutValue]"';
618
- }
619
- }
620
-
621
- class BaseValue extends VoidValue {
622
- "u-"(scope, out) {
623
- const [that, inst] = this.eval(scope);
624
- const temp = StoreValue.from(scope, out);
625
- return [
626
- temp,
627
- [
628
- ...inst,
629
- new OperationInstruction("sub", temp, new LiteralValue(0), that),
630
- ],
631
- ];
632
- }
633
- "u+"(scope, out) {
634
- const inst = [];
635
- const value = pipeInsts(this.eval(scope), inst);
636
- const zero = new LiteralValue(0);
637
- const result = pipeInsts(zero["+"](scope, value, out), inst);
638
- return [result, inst];
639
- }
640
- "!"(scope, out) {
641
- const inst = [];
642
- const falseLiteral = new LiteralValue(0);
643
- const that = pipeInsts(this.eval(scope), inst);
644
- const result = pipeInsts(that["=="](scope, falseLiteral, out), inst);
645
- return [result, inst];
646
- }
647
- "~"(scope, out) {
648
- const [that, inst] = this.eval(scope);
649
- const temp = StoreValue.from(scope, out);
650
- return [temp, [...inst, new OperationInstruction("not", temp, that, null)]];
651
- }
652
- // requires special handling
653
- // the handler should give an object value to allow the lazy evaluation
654
- "??"(scope, other, out) {
655
- const result = SenseableValue.from(scope, out, EMutability.mutable);
656
- const nullLiteral = new LiteralValue(null);
657
- const endAdress = new LiteralValue(null);
658
- const [left, leftInst] = this.eval(scope, result);
659
- const [right, rightInst] = other.eval(scope, result);
660
- const [test, testInst] = left["==="](scope, nullLiteral);
661
- return [
662
- result,
663
- [
664
- ...leftInst,
665
- ...result["="](scope, left)[1],
666
- ...testInst,
667
- new JumpInstruction(endAdress, EJumpKind.Equal, test, new LiteralValue(0)),
668
- ...rightInst,
669
- ...result["="](scope, right)[1],
670
- new AddressResolver(endAdress),
671
- ],
672
- ];
673
- }
674
- "!=="(scope, other, out) {
675
- if (out instanceof JumpOutValue && out.canHandle("!==")) {
676
- const [left, leftInst] = this.eval(scope);
677
- const [right, rightInst] = other.eval(scope);
678
- return [out, [...leftInst, ...rightInst, out.handle("!==", left, right)]];
679
- }
680
- const [equal, equalInst] = this["==="](scope, other);
681
- const [result, resultInst] = equal["!"](scope, out);
682
- return [result, [...equalInst, ...resultInst]];
683
- }
684
- }
685
- for (const key in operatorMap$2) {
686
- const kind = operatorMap$2[key];
687
- BaseValue.prototype[key] = function (scope, value, out) {
688
- const [left, leftInst] = this.eval(scope);
689
- const [right, rightInst] = value.eval(scope);
690
- if (out instanceof JumpOutValue && out.canHandle(key)) {
691
- const jump = out.handle(key, left, right);
692
- return [out, [...leftInst, ...rightInst, jump]];
693
- }
694
- const temp = StoreValue.from(scope, out);
695
- return [
696
- temp,
697
- [
698
- ...leftInst,
699
- ...rightInst,
700
- new OperationInstruction(kind, temp, left, right),
701
- ],
702
- ];
703
- };
704
- }
705
- const assignmentToBinary = {
706
- "+=": "+",
707
- "-=": "-",
708
- "*=": "*",
709
- "/=": "/",
710
- "%=": "%",
711
- "**=": "**",
712
- "|=": "|",
713
- "&=": "&",
714
- "^=": "^",
715
- ">>=": ">>",
716
- ">>>=": ">>>",
717
- "<<=": "<<",
718
- "&&=": "&&",
719
- "||=": "||",
720
- "??=": "??",
721
- };
722
- for (const k in assignmentToBinary) {
723
- const op = k;
724
- BaseValue.prototype[op] = function (scope, value) {
725
- const [opValue, opInst] = this[assignmentToBinary[op]](scope, value, this.toOut());
726
- const [retValue, retInst] = this["="](scope, opValue);
727
- return [retValue, [...opInst, ...retInst]];
728
- };
729
- }
730
- for (const key of updateOperators) {
731
- BaseValue.prototype[key] = function (scope, prefix, out) {
732
- let [ret, inst] = this.eval(scope);
733
- if (!prefix && !isDiscardedOut(out)) {
734
- const temp = StoreValue.from(scope, out);
735
- pipeInsts(temp["="](scope, ret), inst);
736
- ret = temp;
737
- }
738
- const kind = key === "++" ? "+=" : "-=";
739
- pipeInsts(this[kind](scope, new LiteralValue(1)), inst);
740
- return [ret, inst];
741
- };
742
- }
743
-
744
- const literalMethods = {
745
- length: function () {
746
- if (!this.isString())
747
- throw new CompilerError("Length method only works on string literal values.");
748
- return new LiteralValue(this.data.length);
749
- },
750
- };
751
- class LiteralValue extends BaseValue {
752
- constructor(data) {
753
- super();
754
- this.mutability = EMutability.constant;
755
- this.data = data;
756
- this.name = this.toString();
757
- }
758
- eval(_scope) {
759
- return [this, []];
760
- }
761
- toString() {
762
- return JSON.stringify(this.data);
763
- }
764
- get(scope, name) {
765
- if (!(name instanceof LiteralValue && name.isString()))
766
- return super.get(scope, name);
767
- const method = literalMethods[name.data];
768
- if (!method)
769
- throw new CompilerError(`The member [${name.debugString()}] does not exist on literal values.`);
770
- return [method.apply(this, [scope]), []];
771
- }
772
- hasProperty(scope, prop) {
773
- if (this.isString() && prop instanceof LiteralValue && prop.isString())
774
- return Object.prototype.hasOwnProperty.call(literalMethods, prop.data);
775
- return false;
776
- }
777
- get num() {
778
- if (this.data === null)
779
- return 0;
780
- if (typeof this.data === "string")
781
- return 1;
782
- return this.data;
783
- }
784
- "??"(scope, other, out) {
785
- if (this.data === null)
786
- return other.eval(scope, out);
787
- return [this, []];
788
- }
789
- typeof() {
790
- return [new LiteralValue("literal"), []];
791
- }
792
- isString() {
793
- return typeof this.data === "string";
794
- }
795
- isNumber() {
796
- return typeof this.data === "number";
797
- }
798
- debugString() {
799
- return this.toString();
800
- }
801
- }
802
- const operatorMap = {
803
- "==": (a, b) => +(a == b),
804
- "===": (a, b) => +(a === b),
805
- "!=": (a, b) => +(a != b),
806
- "!==": (a, b) => +(a !== b),
807
- "<": (a, b) => +(a < b),
808
- ">": (a, b) => +(a > b),
809
- "<=": (a, b) => +(a <= b),
810
- ">=": (a, b) => +(a >= b),
811
- "+": (a, b) => a + b,
812
- "-": (a, b) => a - b,
813
- "*": (a, b) => a * b,
814
- "/": (a, b) => a / b,
815
- "%": (a, b) => a % b,
816
- "**": (a, b) => a ** b,
817
- "|": (a, b) => a | b,
818
- "&": (a, b) => a & b,
819
- "^": (a, b) => a ^ b,
820
- ">>": (a, b) => a >> b,
821
- ">>>": (a, b) => a >> b,
822
- "<<": (a, b) => a << b,
823
- "&&": (a, b) => +(a && b),
824
- "||": (a, b) => +(a || b),
825
- };
826
- for (const k in operatorMap) {
827
- const key = k;
828
- const fn = operatorMap[key];
829
- LiteralValue.prototype[key] = function (scope, value, out) {
830
- if (key === "&&") {
831
- if (this.data)
832
- return [value, []];
833
- return [new LiteralValue(0), []];
834
- }
835
- if (key === "||") {
836
- if (!this.data)
837
- return [value, []];
838
- return [new LiteralValue(1), []];
839
- }
840
- if (!(value instanceof LiteralValue)) {
841
- return BaseValue.prototype[key].apply(this, [scope, value, out]);
842
- }
843
- return [new LiteralValue(fn(this.data, value.data)), []];
844
- };
845
- }
846
- const unaryOperatorMap = {
847
- "!": v => +!v,
848
- "~": v => ~v,
849
- "u-": v => -v,
850
- "u+": v => +v,
851
- };
852
- for (const key in unaryOperatorMap) {
853
- LiteralValue.prototype[key] = function () {
854
- const fn = unaryOperatorMap[key];
855
- return [new LiteralValue(fn(this.data)), []];
856
- };
857
- }
858
-
859
- /**
860
- * `StoreValue` represents values unknown at compile time,
861
- * mostly used with mutable variables and temporary values
862
- */
863
- class StoreValue extends BaseValue {
864
- constructor(name, mutability = EMutability.mutable) {
865
- super();
866
- this.name = name;
867
- this.mutability = mutability;
868
- }
869
- static from(scope, out, mutability = EMutability.mutable) {
870
- if (out instanceof StoreValue)
871
- return out;
872
- const name = typeof out === "string" ? out : scope.makeTempName();
873
- return new StoreValue(name, mutability);
874
- }
875
- static out(scope, out, mutability = EMutability.mutable) {
876
- if (!out || typeof out === "string") {
877
- return new StoreValue(out ?? scope.makeTempName(), mutability);
878
- }
879
- return out;
880
- }
881
- typeof() {
882
- return [new LiteralValue("store"), []];
883
- }
884
- "="(scope, value) {
885
- if (this.mutability !== EMutability.mutable &&
886
- this.mutability !== EMutability.init)
887
- throw new CompilerError(`Cannot assign to immutable value: [${this.debugString()}].`);
888
- if (compareStores(this, value))
889
- return [this, []];
890
- const [evalValue, evalInst] = value.eval(scope, this);
891
- if (evalValue.macro)
892
- throw new CompilerError(`Cannot assign a macro to a store (attempted to assign [${evalValue.debugString()}] to [${this.debugString()}])`);
893
- return [evalValue, [...evalInst, new SetInstruction(this, evalValue)]];
894
- }
895
- "=="(scope, value, out) {
896
- if (compareStores(this, value))
897
- return [new LiteralValue(1), []];
898
- return super["=="](scope, value, out);
899
- }
900
- "==="(scope, value, out) {
901
- if (compareStores(this, value))
902
- return [new LiteralValue(1), []];
903
- return super["==="](scope, value, out);
904
- }
905
- "!="(scope, value, out) {
906
- if (compareStores(this, value))
907
- return [new LiteralValue(0), []];
908
- return super["!="](scope, value, out);
909
- }
910
- "!=="(scope, value, out) {
911
- if (compareStores(this, value))
912
- return [new LiteralValue(0), []];
913
- return super["!=="](scope, value, out);
914
- }
915
- eval(_scope) {
916
- return [this, []];
917
- }
918
- debugString() {
919
- return `StoreValue("${this.name}")`;
920
- }
921
- toString() {
922
- return this.name;
923
- }
924
- }
925
- function compareStores(left, right) {
926
- return right instanceof StoreValue && right.name === left.name;
927
- }
928
-
929
- class ObjectValue extends VoidValue {
930
- constructor(data = {}) {
931
- super();
932
- this.mutability = EMutability.constant;
933
- this.macro = true;
934
- this.data = data;
935
- }
936
- static fromArray(items, initialData) {
937
- const data = {
938
- ...initialData,
939
- length: new LiteralValue(items.length),
940
- };
941
- items.forEach((item, i) => {
942
- if (item)
943
- data[i] = item;
944
- });
945
- return new ObjectValue(data);
946
- }
947
- typeof(scope, out) {
948
- const { $typeof } = this.data;
949
- if ($typeof)
950
- return $typeof.call(scope, [], out);
951
- return [new LiteralValue("object"), []];
952
- }
953
- get(scope, key, out) {
954
- if (key instanceof LiteralValue && (key.isNumber() || key.isString())) {
955
- // avoids naming collisions with keys like
956
- // constructor or toString
957
- if (Object.prototype.hasOwnProperty.call(this.data, key.data)) {
958
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
959
- return [this.data[key.data], []];
960
- }
961
- }
962
- const { $get } = this.data;
963
- if (!$get)
964
- throw new CompilerError(`The member [${key.debugString()}] is not present in [${this.debugString()}]`);
965
- return $get.call(scope, [key], out);
966
- }
967
- hasProperty(scope, prop) {
968
- if (prop instanceof LiteralValue && (prop.isNumber() || prop.isString())) {
969
- const hasMember = Object.prototype.hasOwnProperty.call(this.data, prop.data);
970
- return hasMember;
971
- }
972
- return false;
973
- }
974
- eval(scope, out) {
975
- const { $eval } = this.data;
976
- if (!$eval)
977
- return [this, []];
978
- return $eval.call(scope, [], out);
979
- }
980
- call(scope, args, out) {
981
- const { $call } = this.data;
982
- if (!$call)
983
- return super.call(scope, args, out);
984
- return $call.call(scope, args, out);
985
- }
986
- "??"(scope, other, out) {
987
- const $ = this.data["$??"];
988
- if ($)
989
- return $.call(scope, [other], out);
990
- return [this, []];
991
- }
992
- debugString() {
993
- if (this.name)
994
- return `ObjectValue("${this.name}")`;
995
- return "ObjectValue";
996
- }
997
- toString() {
998
- return '"[macro ObjectValue]"';
999
- }
1000
- }
1001
- for (const op of leftRightOperators) {
1002
- if (op === "??")
1003
- continue;
1004
- ObjectValue.prototype[op] = function (scope, value, out) {
1005
- const $ = this.data[`$${op}`];
1006
- if (!$)
1007
- return VoidValue.prototype[op].apply(this, [scope, value, out]);
1008
- return $.call(scope, [value], out);
1009
- };
1010
- }
1011
- for (const op of unaryOperators) {
1012
- if (op === "typeof")
1013
- continue;
1014
- ObjectValue.prototype[op] = function (scope, out) {
1015
- const $ = this.data[`$${op}`];
1016
- if (!$)
1017
- return VoidValue.prototype[op].apply(this, [scope, out]);
1018
- return $.call(scope, [], out);
1019
- };
1020
- }
1021
- for (const op of updateOperators) {
1022
- ObjectValue.prototype[op] = function (scope, prefix, out) {
1023
- const $ = this.data[`$${op}`];
1024
- if (!$)
1025
- return VoidValue.prototype[op].apply(this, [scope, prefix, out]);
1026
- return $.call(scope, [new LiteralValue(+prefix)], out);
1027
- };
1028
- }
1029
-
1030
- /**
1031
- * Properties that should return a new `SenseableValue`
1032
- */
1033
- const senseableProps = ["controller"];
1034
- /**
1035
- * Senseable values are "objects" in the mlog runtime that
1036
- * have special properties that can be accessed
1037
- * via the `sense` instruction.
1038
- *
1039
- * This class extends `StoreValue` to allow dynamic behavior
1040
- * that is not possible with object macros.
1041
- *
1042
- * Note that these values are constant by default, so make sure
1043
- * to set that field to `EMutability.mutable` if you want them to be assignable. Example:
1044
- * ```
1045
- * const value = new SenseableValue(name, EMutability.mutable);
1046
- * ```
1047
- */
1048
- class SenseableValue extends StoreValue {
1049
- constructor(name, mutability = EMutability.constant) {
1050
- super(name, mutability);
1051
- }
1052
- static from(scope, out, mutability = EMutability.constant) {
1053
- if (out instanceof SenseableValue)
1054
- return out;
1055
- const name = typeof out === "string" ? out : scope.makeTempName();
1056
- return new SenseableValue(name, mutability);
1057
- }
1058
- static out(scope, out, mutability = EMutability.mutable) {
1059
- if (!out || typeof out === "string") {
1060
- return new SenseableValue(out ?? scope.makeTempName(), mutability);
1061
- }
1062
- return out;
1063
- }
1064
- get(scope, prop, out) {
1065
- const mutability = EMutability.readonly;
1066
- if (prop instanceof LiteralValue && prop.isString()) {
1067
- const name = itemNames.includes(prop.data)
1068
- ? camelToDashCase(prop.data)
1069
- : prop.data;
1070
- const result = senseableProps.includes(prop.data)
1071
- ? SenseableValue.from(scope, out, mutability)
1072
- : StoreValue.from(scope, out, mutability);
1073
- return [
1074
- result,
1075
- [new InstructionBase("sensor", result, this, `@${name}`)],
1076
- ];
1077
- }
1078
- if (prop instanceof StoreValue) {
1079
- const temp = SenseableValue.from(scope, out, mutability);
1080
- return [temp, [new InstructionBase("sensor", temp, this, prop)]];
1081
- }
1082
- throw new CompilerError(`The property [${prop.debugString()}] cannot be sensed`);
1083
- }
1084
- debugString() {
1085
- return `SenseableValue("${this.name}")`;
1086
- }
1087
- hasProperty(scope, prop) {
1088
- return ((prop instanceof LiteralValue && prop.isString()) ||
1089
- prop instanceof StoreValue);
1090
- }
1091
- }
1092
-
1093
- /**
1094
- * Specific case class, required on the ArrayPattern and ObjectPattern handlers
1095
- * in order to keep the separation of concerns and make the code more modular.
1096
- *
1097
- * When it is assign a value, it will get it's own properties and recursively assign
1098
- * each of them to their counterparts on the right hand value.
1099
- */
1100
- class DestructuringValue extends VoidValue {
1101
- constructor(members) {
1102
- super();
1103
- this.members = members;
1104
- this.macro = true;
1105
- this.fields = {};
1106
- for (const [key, { value }] of this.members) {
1107
- if (key instanceof LiteralValue && (key.isString() || key.isNumber())) {
1108
- this.fields[key.data] = value;
1109
- }
1110
- }
1111
- }
1112
- "="(scope, right) {
1113
- const inst = [];
1114
- for (const [key, { value, handler }] of this.members) {
1115
- pipeInsts(handler(() => right.get(scope, key, value), () => right.hasProperty(scope, key), scope), inst);
1116
- }
1117
- return [right, inst];
1118
- }
1119
- eval(_scope) {
1120
- return [this, []];
1121
- }
1122
- debugString() {
1123
- return "DestructuringValue";
1124
- }
1125
- toString() {
1126
- return '"[macro DestructuringValue]"';
1127
- }
1128
- }
1129
-
1130
- /**
1131
- * This class is used to handle some cases where the value
1132
- * can't be directly evaluated by the node handler
1133
- */
1134
- class LazyValue extends VoidValue {
1135
- constructor(evalutate) {
1136
- super();
1137
- this.eval = evalutate;
1138
- }
1139
- debugString() {
1140
- return "LazyValue";
1141
- }
1142
- toString() {
1143
- return '"[macro LazyValue]"';
1144
- }
1145
- }
1146
-
1147
- class AssignmentValue extends VoidValue {
1148
- constructor(left, right) {
1149
- super();
1150
- this.left = left;
1151
- this.right = right;
1152
- this.name = left.name;
1153
- }
1154
- "="(scope, value, out) {
1155
- const inst = [];
1156
- const input = pipeInsts(value["??"](scope, this.right, this.left.toOut()), inst);
1157
- const output = pipeInsts(this.left["="](scope, input, out), inst);
1158
- return [output, inst];
1159
- }
1160
- toOut() {
1161
- return this.left.toOut();
1162
- }
1163
- debugString() {
1164
- return "AssignmentValue";
1165
- }
1166
- toString() {
1167
- return '"[macro AssignmentValue]"';
1168
- }
1169
- }
1170
-
1171
- class Scope {
1172
- constructor(values, parent = null, stacked = false, ntemp = 0, name = "", inst = [], operationCache = {}, cacheDependencies = {}) {
1173
- this.parent = parent;
1174
- this.stacked = stacked;
1175
- this.ntemp = ntemp;
1176
- this.name = name;
1177
- this.inst = inst;
1178
- this.operationCache = operationCache;
1179
- this.cacheDependencies = cacheDependencies;
1180
- // Only `unchecked` is supposed to change this
1181
- this.checkIndexes = true;
1182
- this.data = values;
1183
- }
1184
- copy() {
1185
- const scope = new Scope({ ...this.data }, this.parent, this.stacked, this.ntemp, this.name, this.inst, { ...this.operationCache }, { ...this.cacheDependencies });
1186
- scope.break = this.break;
1187
- scope.continue = this.continue;
1188
- scope.function = this.function;
1189
- return scope;
1190
- }
1191
- createScope() {
1192
- const scope = this.copy();
1193
- scope.data = {};
1194
- scope.operationCache = {};
1195
- scope.cacheDependencies = {};
1196
- scope.parent = this;
1197
- return scope;
1198
- }
1199
- createFunction(name, stacked = this.stacked) {
1200
- return new Scope({}, this, stacked, 0, name, this.inst);
1201
- }
1202
- has(identifier) {
1203
- if (identifier in this.data)
1204
- return true;
1205
- if (this.parent)
1206
- return this.parent.has(identifier);
1207
- return false;
1208
- }
1209
- get(identifier) {
1210
- const value = this.data[identifier];
1211
- if (value)
1212
- return value;
1213
- if (this.parent)
1214
- return this.parent.get(identifier);
1215
- throw new CompilerError(`${identifier} is not declared.`);
1216
- }
1217
- set(name, value) {
1218
- if (name in this.data)
1219
- throw new CompilerError(`${name} is already declared.`);
1220
- return this.hardSet(name, value);
1221
- }
1222
- hardSet(name, value) {
1223
- if (!name)
1224
- throw new CompilerError("Values in a scope must have an identifier");
1225
- this.data[name] = value;
1226
- return value;
1227
- }
1228
- make(identifier, name) {
1229
- const value = assign(new SenseableValue(name), {
1230
- mutability: EMutability.mutable,
1231
- });
1232
- return this.set(identifier, value);
1233
- }
1234
- makeTempName() {
1235
- let result = `${internalPrefix}t${this.ntemp}`;
1236
- if (this.name)
1237
- result += `:${this.name}`;
1238
- this.ntemp++;
1239
- return result;
1240
- }
1241
- addCachedOperation(op, result, left, right) {
1242
- if (!left.name || !result.name || (right && !right.name))
1243
- return;
1244
- const id = formatCacheId(op, left, right);
1245
- this.operationCache[id] = result;
1246
- this.clearDependentCache(result);
1247
- addCacheDependency(this, left.name, id);
1248
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1249
- if (right)
1250
- addCacheDependency(this, right.name, id);
1251
- addCacheDependency(this, result.name, id);
1252
- }
1253
- getCachedOperation(op, left, right) {
1254
- const id = formatCacheId(op, left, right);
1255
- const result = this.operationCache[id];
1256
- if (result)
1257
- return result;
1258
- if (this.parent)
1259
- return this.parent.getCachedOperation(op, left, right);
1260
- }
1261
- clearDependentCache(value) {
1262
- if (!value.name)
1263
- return;
1264
- const dependents = this.cacheDependencies[value.name];
1265
- if (dependents) {
1266
- for (const id of dependents) {
1267
- delete this.operationCache[id];
1268
- }
1269
- }
1270
- if (this.parent)
1271
- this.parent.clearDependentCache(value);
1272
- }
1273
- }
1274
- function formatCacheId(op, left, right) {
1275
- if (right)
1276
- return `{${op}}{${left.name}}{${right.name}}`;
1277
- return `{${op}}{${left.name}}`;
1278
- }
1279
- function addCacheDependency(scope, name, id) {
1280
- (scope.cacheDependencies[name] ??= []).push(id);
1281
- }
1282
-
1283
- const Program = (c, scope, node) => {
1284
- return c.handleMany(scope ?? new Scope({}), node.body);
1285
- };
1286
-
1287
- const VariableDeclaration = (c, scope, node) => {
1288
- return c.handleMany(scope, node.declarations, child => VariableDeclarator(c, scope, child, undefined, node.kind));
1289
- };
1290
- const VariableDeclarator = (c, scope, node, out, kind = "let") => {
1291
- const { init } = node;
1292
- const [value, inst] = Declare(c, scope, node.id, kind);
1293
- if (init) {
1294
- const [result, resultInst] = c.handleEval(scope, init, value.out);
1295
- pipeInsts(value.handler(result, resultInst), inst);
1296
- }
1297
- else {
1298
- pipeInsts(value.handler(null, []), inst);
1299
- }
1300
- return [null, inst];
1301
- };
1302
- const Declare = (c, scope, node, kind) => {
1303
- return c.handle(scope, node, () => {
1304
- switch (node.type) {
1305
- case "Identifier":
1306
- return DeclareIdentifier(c, scope, node, kind);
1307
- case "ArrayPattern":
1308
- return DeclareArrayPattern(c, scope, node, kind);
1309
- case "ObjectPattern":
1310
- return DeclareObjectPattern(c, scope, node, kind);
1311
- case "AssignmentPattern":
1312
- return DeclareAssignmentPattern(c, scope, node, kind);
1313
- default:
1314
- throw new CompilerError(`Unsupported declaration type: ${node.type}`, node);
1315
- }
1316
- });
1317
- };
1318
- const DeclareIdentifier = (c, scope, node, kind) => {
1319
- const { name: identifier } = node;
1320
- const name = nodeName(node, !c.compactNames && identifier);
1321
- const out = new SenseableValue(name, kind === "const" ? EMutability.init : EMutability.mutable);
1322
- const declarationValue = new DeclarationValue({
1323
- c,
1324
- scope,
1325
- node,
1326
- out,
1327
- handler(init, inst) {
1328
- if (kind === "const" && !init)
1329
- throw new CompilerError("Constants must be initialized.", node);
1330
- if (kind === "const" && init?.mutability === EMutability.constant) {
1331
- scope.set(identifier, init);
1332
- return [null, inst];
1333
- }
1334
- else {
1335
- const value = scope.make(identifier, name);
1336
- if (init) {
1337
- if (init.macro)
1338
- throw new CompilerError("Macro values must be held by constants", node);
1339
- pipeInsts(value["="](scope, init), inst);
1340
- }
1341
- if (kind === "const")
1342
- value.mutability = EMutability.constant;
1343
- return [null, inst];
1344
- }
1345
- },
1346
- });
1347
- return [declarationValue, []];
1348
- };
1349
- const DeclareArrayPattern = (c, scope, node, kind) => {
1350
- const members = new Map();
1351
- const { elements } = node;
1352
- const inst = [];
1353
- for (let i = 0; i < elements.length; i++) {
1354
- const element = elements[i];
1355
- if (!element)
1356
- continue;
1357
- const value = pipeInsts(Declare(c, scope, element, kind), inst);
1358
- members.set(new LiteralValue(i), {
1359
- value: value.out,
1360
- handler(get, propExists, scope) {
1361
- const [init, inst] = c.handle(scope, element, () => {
1362
- let [init, initInst] = propExists()
1363
- ? get()
1364
- : [value.defaultInit ? new LiteralValue(null) : null, []];
1365
- if (!init)
1366
- throw new CompilerError(`The target object does not have a value at index ${i}`, element);
1367
- const getDefault = value.defaultInit;
1368
- if (getDefault) {
1369
- init = pipeInsts(init["??"](scope, new LazyValue(() => getDefault()), value.out), initInst);
1370
- }
1371
- return [init, initInst];
1372
- });
1373
- return value.handler(init, inst);
1374
- },
1375
- });
1376
- }
1377
- const out = new DestructuringValue(members);
1378
- const value = new DeclarationValue({
1379
- c,
1380
- scope,
1381
- node,
1382
- out,
1383
- handler(init, initInst) {
1384
- if (!(init instanceof ObjectValue))
1385
- throw new CompilerError("The value being destructured must be an object value");
1386
- pipeInsts(out["="](scope, init), initInst);
1387
- return [null, initInst];
1388
- },
1389
- });
1390
- return [value, inst];
1391
- };
1392
- const DeclareObjectPattern = (c, scope, node, kind) => {
1393
- const members = new Map();
1394
- const { properties } = node;
1395
- const propertiesInst = c.handleMany(scope, properties, prop => {
1396
- if (prop.type === "RestElement")
1397
- throw new CompilerError("The rest operator is not supported", prop);
1398
- const { key, value: propValue } = prop;
1399
- const keyInst = key.type === "Identifier" && !prop.computed
1400
- ? [new LiteralValue(key.name), []]
1401
- : c.handleEval(scope, prop.key);
1402
- const [value, valueInst] = Declare(c, scope, propValue, kind);
1403
- members.set(keyInst[0], {
1404
- value: value.out,
1405
- handler(get, propExists, scope) {
1406
- const [init, inst] = c.handle(scope, prop, () => {
1407
- let [init, initInst] = propExists() || !value.defaultInit
1408
- ? get()
1409
- : [new LiteralValue(null), []];
1410
- const getDefault = value.defaultInit;
1411
- if (getDefault) {
1412
- init = pipeInsts(init["??"](scope, new LazyValue(() => getDefault()), value.out), initInst);
1413
- }
1414
- return [init, [...keyInst[1], ...initInst]];
1415
- });
1416
- return value.handler(init, inst);
1417
- },
1418
- });
1419
- return [null, valueInst];
1420
- });
1421
- const out = new DestructuringValue(members);
1422
- const declarationValue = new DeclarationValue({
1423
- c,
1424
- scope,
1425
- node,
1426
- out,
1427
- handler(init, initInst) {
1428
- if (!init)
1429
- throw new CompilerError("Cannot use object destructuring without an initializer", node);
1430
- pipeInsts(out["="](scope, init), initInst);
1431
- return [null, initInst];
1432
- },
1433
- });
1434
- return [declarationValue, propertiesInst[1]];
1435
- };
1436
- const DeclareAssignmentPattern = (c, scope, node, kind) => {
1437
- const [value, inst] = Declare(c, scope, node.left, kind);
1438
- value.defaultInit = () => c.handleEval(scope, node.right, value.out);
1439
- return [value, inst];
1440
- };
1441
- class DeclarationValue extends VoidValue {
1442
- constructor({ c, scope, node, out, handler, }) {
1443
- super();
1444
- this.out = out;
1445
- this.c = c;
1446
- this.scope = scope;
1447
- this.node = node;
1448
- this.handle = handler;
1449
- }
1450
- handler(init, inst) {
1451
- return this.c.handle(this.scope, this.node, () => this.handle(init, inst));
1452
- }
1453
- debugString() {
1454
- return "DeclarationValue";
1455
- }
1456
- toString() {
1457
- return '"[macro DeclarationValue]"';
1458
- }
1459
- }
1460
-
1461
- const Literal = (_c, scope, node) => [new LiteralValue(node.value), []];
1462
- const NumericLiteral = Literal;
1463
- const StringLiteral = Literal;
1464
- const NullLiteral = () => {
1465
- throw new CompilerError("`null` is no longer supported, use `undefined` instead");
1466
- };
1467
- const BooleanLiteral = (_c, scope, node) => [new LiteralValue(+node.value), []];
1468
-
1469
- const LRExpression = (c, scope, node, out) => {
1470
- const [left, leftInst] = c.handleEval(scope, node.left);
1471
- const [right, rightInst] = c.handleEval(scope, node.right);
1472
- if (!(left instanceof LiteralValue) || !(right instanceof LiteralValue)) {
1473
- const cachedResult = scope.getCachedOperation(node.operator, left, right);
1474
- if (cachedResult)
1475
- return [cachedResult, [...leftInst, ...rightInst]];
1476
- // because a * b is the same as b * a
1477
- if (orderIndependentOperators.includes(node.operator)) {
1478
- const cachedResult = scope.getCachedOperation(node.operator, right, left);
1479
- if (cachedResult)
1480
- return [cachedResult, [...leftInst, ...rightInst]];
1481
- }
1482
- }
1483
- const [op, opInst] = left[node.operator](scope, right, out);
1484
- if (!(op instanceof LiteralValue)) {
1485
- scope.addCachedOperation(node.operator, op, left, right);
1486
- }
1487
- return [op, [...leftInst, ...rightInst, ...opInst]];
1488
- };
1489
- const BinaryExpression = LRExpression;
1490
- const LogicalExpression = (c, scope, node, out) => {
1491
- if (node.operator !== "??")
1492
- return LRExpression(c, scope, node, out);
1493
- const other = new LazyValue((scope, out) => c.handleEval(scope, node.right, out));
1494
- const [left, leftInst] = c.handleValue(scope, node.left);
1495
- const [result, resultInst] = left["??"](scope, other, out);
1496
- return [result, [...leftInst, ...resultInst]];
1497
- };
1498
- const AssignmentExpression = (c, scope, node) => {
1499
- const [left, leftInst] = c.handleValue(scope, node.left);
1500
- const leftOutput = left.toOut();
1501
- const [right, rightInst] = node.operator !== "??="
1502
- ? c.handleEval(scope, node.right, node.operator === "=" ? leftOutput : undefined)
1503
- : [
1504
- new LazyValue((scope, out) => c.handleEval(scope, node.right, out)),
1505
- [],
1506
- ];
1507
- const [op, opInst] = left[node.operator](scope, right);
1508
- scope.clearDependentCache(left);
1509
- scope.clearDependentCache(leftOutput);
1510
- return [op, [...leftInst, ...rightInst, ...opInst]];
1511
- };
1512
- const UnaryExpression = (c, scope, { argument, operator }, out) => {
1513
- const [arg, argInst] = c.handleEval(scope, argument);
1514
- const cachedResult = scope.getCachedOperation(operator, arg);
1515
- if (cachedResult)
1516
- return [cachedResult, argInst];
1517
- const operatorId = operator == "+" || operator == "-" ? `u${operator}` : operator;
1518
- if (operatorId === "throw")
1519
- throw new CompilerError("throw operator is not supported");
1520
- const [op, opInst] = arg[operatorId](scope, out);
1521
- scope.addCachedOperation(operator, op, arg);
1522
- return [op, [...argInst, ...opInst]];
1523
- };
1524
- const UpdateExpression = (c, scope, { argument, operator, prefix }, out) => {
1525
- const [arg, argInst] = c.handleValue(scope, argument);
1526
- scope.clearDependentCache(arg);
1527
- const [op, opInst] = arg[operator](scope, prefix, out);
1528
- return [op, [...argInst, ...opInst]];
1529
- };
1530
- const ConditionalExpression = (c, scope, node, out) => {
1531
- const alternateStartAdress = new LiteralValue(null);
1532
- const endExpressionAdress = new LiteralValue(null);
1533
- const testOut = new JumpOutValue(node, alternateStartAdress, false);
1534
- const [test, testInst] = c.handleEval(scope, node.test, testOut);
1535
- if (test instanceof LiteralValue) {
1536
- if (test.data)
1537
- return c.handleEval(scope, node.consequent, out);
1538
- return c.handleEval(scope, node.alternate, out);
1539
- }
1540
- const result = SenseableValue.from(scope, out, EMutability.mutable);
1541
- const consequent = c.handleEval(scope, node.consequent, result);
1542
- const alternate = c.handleEval(scope, node.alternate, result);
1543
- return [
1544
- result,
1545
- [
1546
- ...testInst,
1547
- ...JumpInstruction.or(test, testOut),
1548
- ...consequent[1],
1549
- ...result["="](scope, consequent[0])[1],
1550
- new JumpInstruction(endExpressionAdress, EJumpKind.Always),
1551
- new AddressResolver(alternateStartAdress),
1552
- ...alternate[1],
1553
- ...result["="](scope, alternate[0])[1],
1554
- new AddressResolver(endExpressionAdress),
1555
- ],
1556
- ];
1557
- };
1558
- const SequenceExpression = (c, scope, node, out) => {
1559
- const { expressions } = node;
1560
- const inst = [];
1561
- // compute every expression except the last one
1562
- for (let i = 0; i < expressions.length - 1; i++) {
1563
- pipeInsts(c.handleEval(scope, expressions[i], discardedName), inst);
1564
- }
1565
- const value = pipeInsts(c.handleEval(scope, expressions[expressions.length - 1], out), inst);
1566
- return [value, inst];
1567
- };
1568
-
1569
- const Identifier = (c, scope, node) => {
1570
- return [scope.get(node.name), []];
1571
- };
1572
-
1573
- const ExpressionStatement = (c, scope, node) => {
1574
- return c.handle(scope, node.expression, undefined, discardedName);
1575
- };
1576
- const BreakStatement = (_, scope, node) => {
1577
- const label = node.label?.name;
1578
- const target = label ? findScopeLabel(scope, label) : scope;
1579
- const addr = new LiteralValue(null);
1580
- target.break.bind(addr);
1581
- return [null, [new BreakInstruction(addr)]];
1582
- };
1583
- const ContinueStatement = (_, scope, node) => {
1584
- const addr = new LiteralValue(null);
1585
- const label = node.label?.name;
1586
- const target = label ? findScopeLabel(scope, label) : scope;
1587
- target.continue.bind(addr);
1588
- return [null, [new ContinueInstruction(addr)]];
1589
- };
1590
- const ReturnStatement = (c, scope, node, out) => {
1591
- const { argument } = node;
1592
- const [arg, argInst] = argument
1593
- ? [new LazyValue((scope, out) => c.handleEval(scope, argument, out)), []]
1594
- : [new LiteralValue(null), []];
1595
- const [ret, retInst] = scope.function.return(scope, arg, out);
1596
- return [ret, [...argInst, ...retInst]];
1597
- };
1598
- const EmptyStatement = () => [null, []];
1599
- const LabeledStatement = (c, scope, node) => {
1600
- const inner = scope.createScope();
1601
- inner.label = node.label.name;
1602
- const end = new LiteralValue(null);
1603
- const endAdress = new AddressResolver(end).bindBreak(inner);
1604
- const [, bodyInst] = c.handle(inner, node.body);
1605
- if (usesAddressResolver(endAdress, bodyInst))
1606
- bodyInst.push(endAdress);
1607
- return [null, bodyInst];
1608
- };
1609
- function findScopeLabel(scope, label) {
1610
- let current = scope;
1611
- while (current !== null) {
1612
- if (current.label === label)
1613
- return current;
1614
- current = current.parent;
1615
- }
1616
- throw new CompilerError(`Could not find label "${label}"`);
1617
- }
1618
-
1619
- const IfStatement = (c, scope, node) => {
1620
- const inst = [];
1621
- const endIfAddr = new LiteralValue(null);
1622
- const testOut = createJumpOut(c, scope, node, endIfAddr);
1623
- const mergedJumps = testOut.whenTrue;
1624
- const test = pipeInsts(c.handleEval(scope, node.test, testOut), inst);
1625
- if (test instanceof LiteralValue) {
1626
- if (test.data)
1627
- return [null, c.handle(scope, node.consequent)[1]];
1628
- else if (node.alternate)
1629
- return [null, c.handle(scope, node.alternate)[1]];
1630
- return [null, []];
1631
- }
1632
- const consequent = !mergedJumps
1633
- ? withAlwaysRuns(c.handle(scope, node.consequent), false)
1634
- : [null, []];
1635
- inst.push(...JumpInstruction.or(test, testOut), ...consequent[1]);
1636
- if (!node.alternate) {
1637
- inst.push(new AddressResolver(endIfAddr));
1638
- }
1639
- else {
1640
- const endElseAddr = new LiteralValue(null);
1641
- const needsJump = !endsWithControlFlow(consequent[1]) && !mergedJumps;
1642
- if (needsJump)
1643
- inst.push(new JumpInstruction(endElseAddr, EJumpKind.Always));
1644
- inst.push(new AddressResolver(endIfAddr), ...withAlwaysRuns(c.handle(scope, node.alternate), false)[1]);
1645
- if (needsJump)
1646
- inst.push(new AddressResolver(endElseAddr));
1647
- }
1648
- return [null, inst];
1649
- };
1650
- /**
1651
- * Returns `true` if the instruction array ends with an instruction
1652
- * that already changes the control flow of the script (like `jump x always`, `end` , `stop`, etc)
1653
- */
1654
- function endsWithControlFlow(inst) {
1655
- for (let i = inst.length - 1; i >= 0; i--) {
1656
- const instruction = inst[i];
1657
- if (instruction instanceof AddressResolver)
1658
- return false;
1659
- if (instruction.hidden)
1660
- continue;
1661
- return instruction.intent !== EInstIntent.none;
1662
- }
1663
- return false;
1664
- }
1665
- function createJumpOut(c, scope, node, endIfAddr) {
1666
- const jumpNode = asMergeableJumpNode(node.consequent);
1667
- if (!jumpNode)
1668
- return new JumpOutValue(node, endIfAddr, false);
1669
- const jump = c.handle(scope, jumpNode)[1][0];
1670
- return new JumpOutValue(node, jump.address, true);
1671
- }
1672
- function asMergeableJumpNode(body) {
1673
- if (body.type === "ContinueStatement" || body.type === "BreakStatement")
1674
- return body;
1675
- if (body.type !== "BlockStatement")
1676
- return;
1677
- if (body.body.length == 0)
1678
- return;
1679
- return asMergeableJumpNode(body.body[0]);
1680
- }
1681
-
1682
- const SwitchStatement = (c, scope, node) => {
1683
- const innerScope = scope.createScope();
1684
- const [ref, refInst] = c.handleEval(scope, node.discriminant);
1685
- const inst = [];
1686
- const endAdress = new LiteralValue(null);
1687
- const endLine = new AddressResolver(endAdress).bindBreak(innerScope);
1688
- const caseJumps = [];
1689
- let defaultJump;
1690
- // if there is a case that is guaranteed to run
1691
- let constantCase = false;
1692
- let onlyConstantTests = true;
1693
- for (const scase of node.cases) {
1694
- const bodyInst = lazy(() => c.handleMany(createCaseScope(innerScope), scase.consequent)[1]);
1695
- const bodyAdress = new LiteralValue(null);
1696
- const bodyLine = new AddressResolver(bodyAdress);
1697
- const canFallInto = !endsWithoutFalltrough(inst, endLine);
1698
- let includeJump = !constantCase;
1699
- const includeBody = !constantCase || canFallInto;
1700
- if (!scase.test) {
1701
- if (includeJump) {
1702
- [defaultJump] = c.handle(innerScope, scase, () => [
1703
- null,
1704
- [new JumpInstruction(bodyAdress, EJumpKind.Always)],
1705
- ])[1];
1706
- }
1707
- if (includeBody)
1708
- inst.push(bodyLine, ...bodyInst());
1709
- continue;
1710
- }
1711
- const [test, testInst] = c.handleEval(innerScope, scase.test);
1712
- if (onlyConstantTests)
1713
- onlyConstantTests = test.mutability === EMutability.constant;
1714
- let jump;
1715
- // check if it can be evaluated at compile time
1716
- // the scope copy prevents unecessary increases in the temp counter
1717
- const [comp] = ref["=="](innerScope.copy(), test);
1718
- if (comp instanceof LiteralValue) {
1719
- // whether other cases can falltrough and reach this one
1720
- const noFallInto = !canFallInto;
1721
- const isLastCase = scase === node.cases[node.cases.length - 1];
1722
- if (!comp.data) {
1723
- // this case is not accessible in any way
1724
- if (noFallInto)
1725
- continue;
1726
- includeJump = false;
1727
- }
1728
- else {
1729
- // this case is only accessible via the comparison jump
1730
- // and all the other cases will never run
1731
- if (noFallInto &&
1732
- onlyConstantTests &&
1733
- (isLastCase || endsWithoutFalltrough(bodyInst(), endLine))) {
1734
- return [null, [...bodyInst(), endLine]];
1735
- }
1736
- jump = c.handle(innerScope, scase, () => [
1737
- null,
1738
- [new JumpInstruction(bodyAdress, EJumpKind.Always)],
1739
- ])[1];
1740
- constantCase = true;
1741
- }
1742
- }
1743
- // makes sourcemapping for the jump more specific
1744
- jump ??= c.handle(innerScope, scase, () => [
1745
- null,
1746
- [new JumpInstruction(bodyAdress, EJumpKind.StrictEqual, ref, test)],
1747
- ])[1];
1748
- if (includeJump)
1749
- caseJumps.push(...testInst, ...jump);
1750
- if (includeBody)
1751
- inst.push(bodyLine, ...bodyInst());
1752
- }
1753
- // ensures that the processor exits
1754
- // the switch if no cases match
1755
- defaultJump ??= new JumpInstruction(endAdress, EJumpKind.Always);
1756
- if (caseJumps.length > 0) {
1757
- withAlwaysRuns([null, [...caseJumps.slice(1), defaultJump]], false);
1758
- withAlwaysRuns([null, inst], false);
1759
- }
1760
- return [
1761
- null,
1762
- [
1763
- ...refInst,
1764
- ...caseJumps,
1765
- ...(caseJumps.length > 0 && !constantCase ? [defaultJump] : []),
1766
- ...inst,
1767
- ...(usesEndLine(endLine, inst, defaultJump) ? [endLine] : []),
1768
- ],
1769
- ];
1770
- };
1771
- function endsWithoutFalltrough(inst, endLine) {
1772
- if (inst.length === 0)
1773
- return true;
1774
- for (let i = inst.length - 1; i >= 0; i--) {
1775
- const instruction = inst[i];
1776
- // this means that there is an instruction trying to reference
1777
- // the final function `set @counter` instruction
1778
- // of course this generates an unecessary
1779
- // `set @counter` in cases where a control flow
1780
- // structure contains a fallback return statement
1781
- // TODO: fix this with static analysis
1782
- if (instruction instanceof AddressResolver)
1783
- return false;
1784
- if (instruction.hidden)
1785
- continue;
1786
- return (instruction instanceof SetCounterInstruction ||
1787
- instruction.intent === EInstIntent.return ||
1788
- (instruction instanceof BreakInstruction &&
1789
- endLine.bonds.includes(instruction.address)));
1790
- }
1791
- return false;
1792
- }
1793
- function usesEndLine(endLine, inst, defaultJump) {
1794
- if (endLine.bonds.includes(defaultJump.address))
1795
- return true;
1796
- return usesAddressResolver(endLine, inst);
1797
- }
1798
- function createCaseScope(scope) {
1799
- // prevents the operation cache from propagating
1800
- // but also allows the case to declare variables
1801
- // that can be used by other cases
1802
- const child = scope.createScope();
1803
- child.data = scope.data;
1804
- return child;
1805
- }
1806
-
1807
- const WhileStatement = (c, scope, node) => {
1808
- const startLoopAddr = new LiteralValue(null);
1809
- const endLoopAddr = new LiteralValue(null);
1810
- const hasEmptyBody = isEmptyBody(node.body);
1811
- const testOut = hasEmptyBody
1812
- ? new JumpOutValue(node, startLoopAddr, true)
1813
- : new JumpOutValue(node, endLoopAddr, false);
1814
- const [test, testLines] = c.handleEval(scope, node.test, testOut);
1815
- const childScope = scope.createScope();
1816
- const startLoopLine = new AddressResolver(startLoopAddr).bindContinue(childScope);
1817
- const endLoopLine = new AddressResolver(endLoopAddr).bindBreak(childScope);
1818
- if (scope.label) {
1819
- startLoopLine.bindContinue(scope);
1820
- }
1821
- if (test instanceof LiteralValue) {
1822
- if (!test.data)
1823
- return [null, []];
1824
- const bodyInst = c.handle(childScope, node.body)[1];
1825
- return [
1826
- null,
1827
- [
1828
- startLoopLine,
1829
- ...bodyInst,
1830
- new JumpInstruction(startLoopAddr, EJumpKind.Always),
1831
- ...(usesAddressResolver(endLoopLine, bodyInst) ? [endLoopLine] : []),
1832
- ],
1833
- ];
1834
- }
1835
- return [
1836
- null,
1837
- [
1838
- startLoopLine,
1839
- ...testLines,
1840
- ...JumpInstruction.or(test, testOut),
1841
- ...withAlwaysRuns(c.handle(childScope, node.body), false)[1],
1842
- ...(hasEmptyBody
1843
- ? []
1844
- : [new JumpInstruction(startLoopAddr, EJumpKind.Always), endLoopLine]),
1845
- ],
1846
- ];
1847
- };
1848
- const DoWhileStatement = (c, scope, node) => {
1849
- const startLoopAddr = new LiteralValue(null);
1850
- const endLoopAddr = new LiteralValue(null);
1851
- const testOut = new JumpOutValue(node, startLoopAddr, true);
1852
- const [test, testLines] = c.handleEval(scope, node.test, testOut);
1853
- const childScope = scope.createScope();
1854
- const startLoopLine = new AddressResolver(startLoopAddr).bindContinue(childScope);
1855
- const endLoopLine = new AddressResolver(endLoopAddr).bindBreak(childScope);
1856
- if (scope.label) {
1857
- startLoopLine.bindContinue(scope);
1858
- }
1859
- const [, bodyLines] = c.handle(childScope, node.body);
1860
- if (test instanceof LiteralValue) {
1861
- if (!test.data)
1862
- return [null, bodyLines];
1863
- return [
1864
- null,
1865
- [
1866
- startLoopLine,
1867
- ...bodyLines,
1868
- new JumpInstruction(startLoopAddr, EJumpKind.Always),
1869
- ...(usesAddressResolver(endLoopLine, bodyLines) ? [endLoopLine] : []),
1870
- ],
1871
- ];
1872
- }
1873
- return [
1874
- null,
1875
- [
1876
- startLoopLine,
1877
- ...bodyLines,
1878
- ...testLines,
1879
- ...JumpInstruction.or(test, testOut),
1880
- ...(usesAddressResolver(endLoopLine, bodyLines) ? [endLoopLine] : []),
1881
- ],
1882
- ];
1883
- };
1884
- function isEmptyBody(node) {
1885
- if (node.type === "EmptyStatement")
1886
- return true;
1887
- if (node.type === "BlockStatement")
1888
- return node.body.length === 0;
1889
- return false;
1890
- }
1891
-
1892
- const ForStatement = (c, parentScope, node) => {
1893
- const startLoopAddr = new LiteralValue(null);
1894
- // continue statements jump here to run the loop update lines
1895
- const beforeEndAddr = new LiteralValue(null);
1896
- const endLoopAddr = new LiteralValue(null);
1897
- const scope = parentScope.createScope();
1898
- const testOut = new JumpOutValue(node, endLoopAddr, false);
1899
- const initInst = node.init ? c.handle(scope, node.init)[1] : [];
1900
- const [test, testLines] = node.test
1901
- ? c.handleEval(scope, node.test, testOut)
1902
- : [new LiteralValue(1), []];
1903
- const updateLines = node.update
1904
- ? c.handle(scope, node.update, undefined, discardedName)[1]
1905
- : [];
1906
- const startLoopLine = new AddressResolver(startLoopAddr);
1907
- const endLoopLine = new AddressResolver(endLoopAddr).bindBreak(scope);
1908
- const beforeEndLine = new AddressResolver(beforeEndAddr).bindContinue(scope);
1909
- if (parentScope.label) {
1910
- beforeEndLine.bindContinue(parentScope);
1911
- }
1912
- if (test instanceof LiteralValue && !test.data) {
1913
- return [null, [...initInst]];
1914
- }
1915
- const isInfiniteLoop = test instanceof LiteralValue;
1916
- return [
1917
- null,
1918
- [
1919
- ...initInst,
1920
- startLoopLine,
1921
- ...testLines,
1922
- ...(isInfiniteLoop ? [] : JumpInstruction.or(test, testOut)),
1923
- ...withAlwaysRuns(c.handle(scope, node.body), false)[1],
1924
- beforeEndLine,
1925
- ...updateLines,
1926
- new JumpInstruction(startLoopAddr, EJumpKind.Always),
1927
- ...(isInfiniteLoop ? [] : [endLoopLine]),
1928
- ],
1929
- ];
1930
- };
1931
-
1932
- class FunctionValue extends VoidValue {
1933
- constructor({ scope, params, body, c, out, }) {
1934
- super();
1935
- this.mutability = EMutability.constant;
1936
- this.macro = true;
1937
- this.paramValues = [];
1938
- this.paramNames = new Map();
1939
- this.destructuringKeyData = new Map();
1940
- this.paramOuts = [];
1941
- this.minimumArgumentCount = 0;
1942
- this.maximumArgumentCount = 0;
1943
- this.bundled = false;
1944
- this.initialized = false;
1945
- this.scope = scope;
1946
- this.body = body;
1947
- this.c = c;
1948
- this.params = params;
1949
- this.name = extractOutName(out) ?? scope.makeTempName();
1950
- }
1951
- typeof() {
1952
- return [new LiteralValue("function"), []];
1953
- }
1954
- initScope() {
1955
- this.childScope = this.scope.createFunction(this.name);
1956
- this.childScope.function = this;
1957
- for (let i = 0; i < this.params.length; i++) {
1958
- const value = this.unwrapParameter(this.params[i]);
1959
- this.paramValues.push(value);
1960
- this.paramOuts.push(value.toOut());
1961
- if (!(value instanceof AssignmentValue))
1962
- this.minimumArgumentCount = i + 1;
1963
- }
1964
- this.callSize = this.paramValues.length + 2;
1965
- this.maximumArgumentCount = this.params.length;
1966
- }
1967
- ensureInit() {
1968
- if (this.initialized)
1969
- return;
1970
- this.initialized = true;
1971
- this.initScope();
1972
- this.addr = new LiteralValue(null);
1973
- this.temp = new SenseableValue(`${internalPrefix}f${this.name}`, EMutability.mutable);
1974
- this.ret = new StoreValue(`${internalPrefix}r${this.name}`);
1975
- this.inst = [
1976
- new AddressResolver(this.addr),
1977
- ...this.c.handle(this.childScope, this.body)[1],
1978
- ];
1979
- if (!endsWithReturn(this, this.inst)) {
1980
- pipeInsts(this.temp["="](this.scope, new LiteralValue(null)), this.inst);
1981
- this.inst.push(assign(new SetCounterInstruction(this.ret), {
1982
- intent: EInstIntent.return,
1983
- intentSource: this,
1984
- }));
1985
- }
1986
- }
1987
- normalReturn(scope, arg) {
1988
- const inst = [];
1989
- if (arg) {
1990
- const value = pipeInsts(arg.eval(scope, this.temp), inst);
1991
- pipeInsts(this.temp["="](scope, value), inst);
1992
- }
1993
- const returnInst = assign(new SetCounterInstruction(this.ret), {
1994
- intent: EInstIntent.return,
1995
- intentSource: this,
1996
- });
1997
- return [null, [...inst, returnInst]];
1998
- }
1999
- // TODO: use static analysis to determine if
2000
- // we can safely skip the temp value assignment
2001
- normalCall(scope, args, out) {
2002
- if (!this.bundled)
2003
- this.childScope.inst.push(...this.inst);
2004
- this.bundled = true;
2005
- const callAddressLiteral = new LiteralValue(null);
2006
- // the value will be stored somewhere
2007
- // no need to save it in a temporary variable
2008
- const temp = out
2009
- ? this.temp
2010
- : SenseableValue.from(scope, undefined, EMutability.mutable);
2011
- const inst = this.paramValues
2012
- .map((param, i) => param["="](scope, args[i] ?? new LiteralValue(null))[1])
2013
- .reduce((s, c) => s.concat(c), [])
2014
- .concat(...this.ret["="](scope, callAddressLiteral)[1], new JumpInstruction(this.addr, EJumpKind.Always), new AddressResolver(callAddressLiteral),
2015
- // ensures that functions can be called multiple times inside expressions
2016
- ...temp["="](scope, this.temp)[1]);
2017
- return [temp, inst];
2018
- }
2019
- inlineReturn(scope, arg) {
2020
- if (!this.inlineEnd)
2021
- throw new CompilerError("Error during inline attempt: missing inline end adress");
2022
- const inst = [];
2023
- if (arg) {
2024
- const value = pipeInsts(arg.eval(scope, this.inlineTemp), inst);
2025
- pipeInsts(this.inlineTemp["="](scope, value), inst);
2026
- }
2027
- const jump = assign(new JumpInstruction(this.inlineEnd, EJumpKind.Always), {
2028
- intent: EInstIntent.return,
2029
- intentSource: this,
2030
- });
2031
- return [null, [...inst, jump]];
2032
- }
2033
- inlineCall(scope, args, out) {
2034
- // create return value
2035
- this.inlineTemp = SenseableValue.from(this.childScope, out, EMutability.mutable);
2036
- // make a copy of the function scope
2037
- const fnScope = this.childScope.copy();
2038
- this.tryingInline = true;
2039
- let inst = [];
2040
- // hard set variables within the function scope
2041
- for (let i = 0; i < this.paramValues.length; i++) {
2042
- inst.push(...this.hardSetParameter(fnScope, this.paramValues[i], args[i]));
2043
- }
2044
- this.inlineEnd = new LiteralValue(null);
2045
- inst.push(...this.c.handle(fnScope, this.body)[1]);
2046
- this.tryingInline = false;
2047
- const returnIndex = inst.findIndex(i => i.alwaysRuns &&
2048
- i.intent === EInstIntent.return &&
2049
- i.intentSource === this);
2050
- if (returnIndex !== -1)
2051
- inst = inst.slice(0, returnIndex + 1);
2052
- inst.push(new AddressResolver(this.inlineEnd));
2053
- // return the function value
2054
- return [this.inlineTemp, inst];
2055
- }
2056
- call(scope, args, out) {
2057
- this.ensureInit();
2058
- const min = this.minimumArgumentCount;
2059
- const max = this.maximumArgumentCount;
2060
- if (args.length < min || args.length > max) {
2061
- if (min !== max)
2062
- throw new CompilerError(`Cannot call: expected ${min}-${max} arguments but got: ${args.length}`);
2063
- throw new CompilerError(`Cannot call: expected ${min} arguments but got: ${args.length}`);
2064
- }
2065
- validateParameters(args, this.paramValues);
2066
- const inlineCall = this.inlineCall(scope, args, out);
2067
- hideRedundantJumps(inlineCall[1]);
2068
- const inlineSize = inlineCall[1].filter(i => !i.hidden).length;
2069
- if (this.inline || inlineSize <= this.callSize)
2070
- return inlineCall;
2071
- return this.normalCall(scope, args, out);
2072
- }
2073
- return(scope, arg) {
2074
- this.ensureInit();
2075
- if (arg && arg.macro) {
2076
- this.inline = true;
2077
- return [null, []];
2078
- }
2079
- if (this.inline || this.tryingInline)
2080
- return this.inlineReturn(scope, arg);
2081
- return this.normalReturn(scope, arg);
2082
- }
2083
- eval(_scope) {
2084
- return [this, []];
2085
- }
2086
- preCall() {
2087
- this.ensureInit();
2088
- if (this.inline || this.tryingInline)
2089
- return;
2090
- return this.paramOuts;
2091
- }
2092
- debugString() {
2093
- return `FunctionValue("${this.name}")`;
2094
- }
2095
- toString() {
2096
- return '"[macro FunctionValue]"';
2097
- }
2098
- unwrapParameter(param) {
2099
- switch (param.type) {
2100
- case "Identifier": {
2101
- const name = nodeName(param, !this.c.compactNames && param.name);
2102
- const value = new SenseableValue(name, EMutability.mutable);
2103
- this.childScope.set(param.name, value);
2104
- this.paramNames.set(value, param.name);
2105
- return value;
2106
- }
2107
- case "AssignmentPattern": {
2108
- const left = this.unwrapParameter(param.left);
2109
- const [rightValue, rightInst] = this.c.handleEval(this.childScope, param.right, left);
2110
- const right = new LazyValue(scope => {
2111
- if (this.inline || this.tryingInline) {
2112
- return this.c.handleEval(scope, param.right, left);
2113
- }
2114
- return [rightValue, [...rightInst]];
2115
- });
2116
- return new AssignmentValue(left, right);
2117
- }
2118
- case "ObjectPattern": {
2119
- const members = new Map();
2120
- for (const prop of param.properties) {
2121
- if (prop.type === "RestElement")
2122
- throw new CompilerError("Rest parameters are not supported");
2123
- const { key: propKey, value: propValue } = prop;
2124
- const propInst = [];
2125
- const key = propKey.type === "Identifier" && !prop.computed
2126
- ? new LiteralValue(propKey.name)
2127
- : pipeInsts(this.c.handleEval(this.childScope, propKey), propInst);
2128
- const value = this.unwrapParameter(propValue);
2129
- const hasDefault = value instanceof AssignmentValue;
2130
- this.destructuringKeyData.set(key, { inst: propInst, value });
2131
- members.set(key, {
2132
- value: value.toOut(),
2133
- handler: (get, propExists, scope) => {
2134
- return this.c.handle(scope, prop, () => {
2135
- const inst = [...propInst];
2136
- if (propExists() || !hasDefault) {
2137
- const input = pipeInsts(get(), inst);
2138
- // assigns the output to the target value
2139
- const output = pipeInsts(value["="](scope, input), inst);
2140
- return [output, inst];
2141
- }
2142
- const result = pipeInsts(value["="](scope, new LiteralValue(null)), inst);
2143
- return [result, inst];
2144
- });
2145
- },
2146
- });
2147
- }
2148
- return new DestructuringValue(members);
2149
- }
2150
- case "ArrayPattern": {
2151
- const members = new Map();
2152
- for (let i = 0; i < param.elements.length; i++) {
2153
- const element = param.elements[i];
2154
- if (!element)
2155
- continue;
2156
- const value = this.unwrapParameter(element);
2157
- const hasDefault = value instanceof AssignmentValue;
2158
- if (!value)
2159
- throw new CompilerError("Destructuring element must resolve to a value", element);
2160
- const key = new LiteralValue(i);
2161
- this.destructuringKeyData.set(key, { inst: [], value });
2162
- members.set(key, {
2163
- value: value.toOut(),
2164
- handler: (get, propExists, scope) => {
2165
- return this.c.handle(scope, element, () => {
2166
- if (propExists() || !hasDefault) {
2167
- const inst = get();
2168
- // assigns the output to the target value
2169
- pipeInsts(value["="](scope, inst[0]), inst[1]);
2170
- return inst;
2171
- }
2172
- return value["="](scope, new LiteralValue(null));
2173
- });
2174
- },
2175
- });
2176
- }
2177
- return new DestructuringValue(members);
2178
- }
2179
- default:
2180
- throw new CompilerError(`Unsupported function parameter type: ${param.type}`);
2181
- }
2182
- }
2183
- hardSetParameter(scope, param, value = new LiteralValue(null)) {
2184
- if (param instanceof SenseableValue) {
2185
- const name = this.paramNames.get(param);
2186
- scope.hardSet(name, value);
2187
- return [];
2188
- }
2189
- if (param instanceof AssignmentValue) {
2190
- const [result, inst] = value["??"](scope, param.right, param.left);
2191
- inst.push(...this.hardSetParameter(scope, param.left, result));
2192
- return inst;
2193
- }
2194
- const inst = [];
2195
- for (const [key, { value: out }] of param.members) {
2196
- const data = this.destructuringKeyData.get(key);
2197
- if (!data)
2198
- continue;
2199
- inst.push(...data.inst);
2200
- const hasDefault = data.value instanceof AssignmentValue;
2201
- const input = value.hasProperty(scope, key) || !hasDefault
2202
- ? pipeInsts(value.get(scope, key, out), inst)
2203
- : new LiteralValue(null);
2204
- inst.push(...this.hardSetParameter(scope, data.value, input));
2205
- }
2206
- return inst;
2207
- }
2208
- }
2209
- function endsWithReturn(fun, inst) {
2210
- for (let i = inst.length - 1; i >= 0; i--) {
2211
- const instruction = inst[i];
2212
- // this means that there is an instruction trying to reference
2213
- // the final function `set @counter` instruction
2214
- // of course this generates an unecessary
2215
- // `set @counter` in cases where a control flow
2216
- // structure contains a fallback return statement
2217
- // TODO: fix this with static analysis
2218
- if (instruction instanceof AddressResolver)
2219
- return false;
2220
- if (instruction.hidden)
2221
- continue;
2222
- return (instruction.intent === EInstIntent.exit ||
2223
- (instruction.intent === EInstIntent.return &&
2224
- instruction.intentSource === fun));
2225
- }
2226
- return false;
2227
- }
2228
- function validateParameters(args, params) {
2229
- for (let i = 0; i < args.length; i++) {
2230
- const arg = args[i];
2231
- const param = params[i];
2232
- if (arg instanceof StoreValue &&
2233
- param instanceof AssignmentValue &&
2234
- param.left.macro) {
2235
- throw new CompilerError("Cannot pass a store to a function parameter that has a macro as a default value.");
2236
- }
2237
- }
2238
- }
2239
-
2240
- function handleFunctionNode(c, scope, node, out) {
2241
- let { params, body } = node;
2242
- if (es.isExpression(body)) {
2243
- const { loc } = body;
2244
- const statement = es.returnStatement(body);
2245
- statement.loc = loc;
2246
- body = es.blockStatement([statement]);
2247
- body.loc = loc;
2248
- }
2249
- return [
2250
- new FunctionValue({
2251
- scope,
2252
- params: params,
2253
- body,
2254
- c,
2255
- out,
2256
- }),
2257
- [],
2258
- ];
2259
- }
2260
- const ArrowFunctionExpression = (c, scope, node, out) => {
2261
- return handleFunctionNode(c, scope, node, out);
2262
- };
2263
- const FunctionDeclaration = (c, scope, node) => {
2264
- const identifier = node.id.name;
2265
- const name = nodeName(node, !c.compactNames && identifier);
2266
- const functionIns = handleFunctionNode(c, scope, node, name);
2267
- scope.set(identifier, functionIns[0]);
2268
- return functionIns;
2269
- };
2270
- const FunctionExpression = ArrowFunctionExpression;
2271
- const ObjectMethod = ArrowFunctionExpression;
2272
-
2273
- const CallExpression = (c, scope, node, out) => {
2274
- const inst = [];
2275
- const callee = pipeInsts(c.handleEval(scope, node.callee), inst);
2276
- const paramOuts = callee.preCall(scope, out);
2277
- const args = node.arguments.map((node, index) => {
2278
- const out = paramOuts?.[index];
2279
- return pipeInsts(c.handleEval(scope, node, out), inst);
2280
- });
2281
- const callValue = pipeInsts(callee.call(scope, args, out), inst);
2282
- callee.postCall(scope);
2283
- return [callValue, inst];
2284
- };
2285
- const NewExpression = CallExpression;
2286
- const TaggedTemplateExpression = (c, scope, node, out) => {
2287
- const [tag, tagInst] = c.handleEval(scope, node.tag);
2288
- const template = node.quasi;
2289
- const stringsObject = ObjectValue.fromArray(template.quasis.map(quasi => new LiteralValue(quasi.value.cooked ?? "")), {
2290
- raw: ObjectValue.fromArray(template.quasis.map(quasi => new LiteralValue(quasi.value.raw))),
2291
- });
2292
- const expressions = [];
2293
- const expressionInsts = [];
2294
- template.expressions.forEach(expression => {
2295
- const value = pipeInsts(c.handleEval(scope, expression), expressionInsts);
2296
- expressions.push(value);
2297
- });
2298
- const [result, resultInst] = tag.call(scope, [stringsObject, ...expressions], out);
2299
- return [result, [...expressionInsts, ...tagInst, ...resultInst]];
2300
- };
2301
-
2302
- const ObjectExpression = (c, scope, node, out) => {
2303
- const data = {};
2304
- const inst = [];
2305
- for (const prop of node.properties) {
2306
- if (prop.type === "SpreadElement")
2307
- throw new CompilerError("Cannot handle spread element", prop);
2308
- if (prop.computed)
2309
- throw new CompilerError("Cannot handle computed property.", prop);
2310
- const { key } = prop;
2311
- const value = prop.type === "ObjectProperty" ? prop.value : prop;
2312
- let index;
2313
- if (key.type === "Identifier") {
2314
- index = key.name;
2315
- }
2316
- else if (key.type === "StringLiteral" || key.type === "NumericLiteral") {
2317
- index = `${key.value}`;
2318
- }
2319
- else {
2320
- throw new CompilerError(`Unsupported object key type: ${key.type}`, key);
2321
- }
2322
- const memberOut = extractDestrucuringOut(out, index);
2323
- const member = pipeInsts(c.handleEval(scope, value, memberOut), inst);
2324
- data[index] = member;
2325
- }
2326
- return [new ObjectValue(data), inst];
2327
- };
2328
- const ArrayExpression = (c, scope, node, out) => {
2329
- const items = [];
2330
- const inst = [];
2331
- node.elements.forEach((element, i) => {
2332
- if (!element) {
2333
- items.push(undefined);
2334
- return;
2335
- }
2336
- const value = pipeInsts(c.handleEval(scope, element, extractDestrucuringOut(out, i)), inst);
2337
- items.push(value);
2338
- });
2339
- return [ObjectValue.fromArray(items), inst];
2340
- };
2341
- const MemberExpression = (c, scope, node, out, optional = false) => {
2342
- const [prop, propInst] = node.computed
2343
- ? c.handleEval(scope, node.property)
2344
- : [new LiteralValue(node.property.name), []];
2345
- if (!out) {
2346
- const [obj, objInst] = c.handleEval(scope, node.object);
2347
- if (optional && obj instanceof LiteralValue && obj.data === null)
2348
- return [obj, [...objInst, ...propInst]];
2349
- const [got, gotInst] = obj.get(scope, prop);
2350
- return [got, [...objInst, ...propInst, ...gotInst]];
2351
- }
2352
- const temp = SenseableValue.out(scope, out);
2353
- const objOut = new DestructuringValue(new Map([
2354
- [
2355
- prop,
2356
- {
2357
- value: temp,
2358
- // a direct assignment should NOT happen
2359
- handler: () => {
2360
- throw new CompilerError("Unexpected destructuring assignment");
2361
- },
2362
- },
2363
- ],
2364
- ]));
2365
- const [obj, objInst] = c.handleEval(scope, node.object, objOut);
2366
- if (optional && obj instanceof LiteralValue && obj.data === null)
2367
- return [obj, [...objInst, ...propInst]];
2368
- const [got, gotInst] = obj.get(scope, prop, temp);
2369
- return [got, [...objInst, ...propInst, ...gotInst]];
2370
- };
2371
- const ArrayPattern = (c, scope, node) => {
2372
- const inst = [];
2373
- const members = new Map();
2374
- for (let i = 0; i < node.elements.length; i++) {
2375
- const element = node.elements[i];
2376
- if (!element)
2377
- continue;
2378
- const value = pipeInsts(c.handleValue(scope, element), inst);
2379
- const hasDefault = value instanceof AssignmentValue;
2380
- members.set(new LiteralValue(i), {
2381
- value: value.toOut(),
2382
- handler(get, propExists, scope) {
2383
- return c.handle(scope, element, () => {
2384
- if (propExists() || !hasDefault) {
2385
- const inst = get();
2386
- // assigns the output to the target value
2387
- pipeInsts(value["="](scope, inst[0]), inst[1]);
2388
- return inst;
2389
- }
2390
- return value["="](scope, new LiteralValue(null));
2391
- });
2392
- },
2393
- });
2394
- }
2395
- return [new DestructuringValue(members), inst];
2396
- };
2397
- const ObjectPattern = (c, scope, node) => {
2398
- const inst = [];
2399
- const members = new Map();
2400
- for (const prop of node.properties) {
2401
- if (prop.type === "RestElement")
2402
- throw new CompilerError("The rest operator is not supported");
2403
- if (!prop)
2404
- continue;
2405
- const propKey = prop.key;
2406
- const [key, keyInst] = propKey.type === "Identifier" && !prop.computed
2407
- ? [new LiteralValue(propKey.name), []]
2408
- : c.handleEval(scope, propKey);
2409
- const value = pipeInsts(c.handleValue(scope, prop.value), inst);
2410
- const hasDefault = value instanceof AssignmentValue;
2411
- members.set(key, {
2412
- value: value.toOut(),
2413
- handler(get, propExists, scope) {
2414
- return c.handle(scope, prop, () => {
2415
- const inst = keyInst;
2416
- if (propExists() || !hasDefault) {
2417
- const input = pipeInsts(get(), inst);
2418
- // assigns the output to the target value
2419
- const output = pipeInsts(value["="](scope, input), inst);
2420
- return [output, inst];
2421
- }
2422
- const result = pipeInsts(value["="](scope, new LiteralValue(null)), inst);
2423
- return [result, inst];
2424
- });
2425
- },
2426
- });
2427
- }
2428
- return [new DestructuringValue(members), inst];
2429
- };
2430
- // the mlog runtime already does this check for us
2431
- const OptionalMemberExpression = (c, scope, node, out) => MemberExpression(c, scope, node, out, true);
2432
- const AssignmentPattern = (c, scope, node) => {
2433
- const [left, inst] = c.handleValue(scope, node.left);
2434
- const right = new LazyValue((_, out) => c.handleEval(scope, node.right, out));
2435
- return [new AssignmentValue(left, right), inst];
2436
- };
2437
-
2438
- const TemplateLiteral = () => {
2439
- throw new CompilerError("Template strings can only be used in tagged template literals. To inline mlog code use the asm function.");
2440
- };
2441
-
2442
- const BlockStatement = (c, scope, node) => {
2443
- return c.handleMany(scope.createScope(), node.body);
2444
- };
2445
-
2446
- const File = (c, scope, node) => {
2447
- return c.handle(scope ?? new Scope({}), node.program);
2448
- };
2449
-
2450
- const TypeCastExpression = (c, scope, node, out) => {
2451
- return c.handle(scope, node.expression, undefined, out);
2452
- };
2453
- const TSAsExpression = TypeCastExpression;
2454
- const TSTypeAssertion = TypeCastExpression;
2455
- const IgnoredHandler = () => [null, []];
2456
- const TSInterfaceDeclaration = IgnoredHandler;
2457
- const TSTypeAliasDeclaration = IgnoredHandler;
2458
- const TSEnumDeclaration = (c, scope, node) => {
2459
- if (!node.const)
2460
- throw new CompilerError("All enums must be declared with the const keyword before enum");
2461
- let counter = 0;
2462
- let lastType = "number";
2463
- const data = {};
2464
- for (const member of node.members) {
2465
- if (lastType === "string" && !member.initializer)
2466
- throw new CompilerError("This enum member must be initialized", member);
2467
- const [value] = member.initializer
2468
- ? c.handleEval(scope, member.initializer)
2469
- : [new LiteralValue(counter)];
2470
- if (!(value instanceof LiteralValue))
2471
- throw new CompilerError("Enum members must contain literal values", member);
2472
- if (value.isNumber()) {
2473
- lastType = "number";
2474
- counter = value.data + 1;
2475
- }
2476
- else {
2477
- lastType = "string";
2478
- }
2479
- const name = member.id.type === "Identifier" ? member.id.name : member.id.value;
2480
- data[name] = value;
2481
- }
2482
- const value = new ObjectValue(data);
2483
- value.name = nodeName(node, !c.compactNames && node.id.name);
2484
- scope.set(node.id.name, value);
2485
- return [null, []];
2486
- };
2487
- const TSNonNullExpression = (c, scope, node, out) => {
2488
- return c.handle(scope, node.expression, undefined, out);
2489
- };
2490
- const TSSatisfiesExpression = (c, scope, node, out) => c.handle(scope, node.expression, undefined, out);
2491
-
2492
- var handlers = /*#__PURE__*/Object.freeze({
2493
- __proto__: null,
2494
- ArrayExpression: ArrayExpression,
2495
- ArrayPattern: ArrayPattern,
2496
- ArrowFunctionExpression: ArrowFunctionExpression,
2497
- AssignmentExpression: AssignmentExpression,
2498
- AssignmentPattern: AssignmentPattern,
2499
- BinaryExpression: BinaryExpression,
2500
- BlockStatement: BlockStatement,
2501
- BooleanLiteral: BooleanLiteral,
2502
- BreakStatement: BreakStatement,
2503
- CallExpression: CallExpression,
2504
- ConditionalExpression: ConditionalExpression,
2505
- ContinueStatement: ContinueStatement,
2506
- DoWhileStatement: DoWhileStatement,
2507
- EmptyStatement: EmptyStatement,
2508
- ExpressionStatement: ExpressionStatement,
2509
- File: File,
2510
- ForStatement: ForStatement,
2511
- FunctionDeclaration: FunctionDeclaration,
2512
- FunctionExpression: FunctionExpression,
2513
- Identifier: Identifier,
2514
- IfStatement: IfStatement,
2515
- LRExpression: LRExpression,
2516
- LabeledStatement: LabeledStatement,
2517
- LogicalExpression: LogicalExpression,
2518
- MemberExpression: MemberExpression,
2519
- NewExpression: NewExpression,
2520
- NullLiteral: NullLiteral,
2521
- NumericLiteral: NumericLiteral,
2522
- ObjectExpression: ObjectExpression,
2523
- ObjectMethod: ObjectMethod,
2524
- ObjectPattern: ObjectPattern,
2525
- OptionalMemberExpression: OptionalMemberExpression,
2526
- Program: Program,
2527
- ReturnStatement: ReturnStatement,
2528
- SequenceExpression: SequenceExpression,
2529
- StringLiteral: StringLiteral,
2530
- SwitchStatement: SwitchStatement,
2531
- TSAsExpression: TSAsExpression,
2532
- TSEnumDeclaration: TSEnumDeclaration,
2533
- TSInterfaceDeclaration: TSInterfaceDeclaration,
2534
- TSNonNullExpression: TSNonNullExpression,
2535
- TSSatisfiesExpression: TSSatisfiesExpression,
2536
- TSTypeAliasDeclaration: TSTypeAliasDeclaration,
2537
- TSTypeAssertion: TSTypeAssertion,
2538
- TaggedTemplateExpression: TaggedTemplateExpression,
2539
- TemplateLiteral: TemplateLiteral,
2540
- UnaryExpression: UnaryExpression,
2541
- UpdateExpression: UpdateExpression,
2542
- VariableDeclaration: VariableDeclaration,
2543
- VariableDeclarator: VariableDeclarator,
2544
- WhileStatement: WhileStatement
2545
- });
2546
-
2547
- class MacroFunction extends VoidValue {
2548
- constructor(fn, paramOuts) {
2549
- super();
2550
- this.paramOuts = paramOuts;
2551
- this.macro = true;
2552
- this.mutability = EMutability.constant;
2553
- this.fn = fn;
2554
- }
2555
- call(scope, args, out) {
2556
- return this.fn.apply(this, [scope, out, ...args]);
2557
- }
2558
- eval(_scope) {
2559
- return [this, []];
2560
- }
2561
- preCall(_scope, _out) {
2562
- return this.paramOuts;
2563
- }
2564
- debugString() {
2565
- return "MacroFunction";
2566
- }
2567
- toString() {
2568
- return '"[macro MacroFunction]"';
2569
- }
2570
- }
2571
-
2572
- /* eslint-disable @typescript-eslint/no-non-null-assertion */
2573
- const toRadians = (degrees) => (degrees * Math.PI) / 180;
2574
- const toDegrees = (radians) => (radians * 180) / Math.PI;
2575
- const mathOperations = {
2576
- max: (a, b) => Math.max(a, b),
2577
- min: (a, b) => Math.min(a, b),
2578
- angle: (x, y) => {
2579
- const angle = toDegrees(Math.atan2(y, x));
2580
- return angle < 0 ? angle + 360 : angle;
2581
- },
2582
- len: (a, b) => Math.sqrt(a ** 2 + b ** 2),
2583
- noise: null,
2584
- abs: a => Math.abs(a),
2585
- log: a => Math.log(a),
2586
- log10: a => Math.log10(a),
2587
- sin: a => Math.sin(toRadians(a)),
2588
- cos: a => Math.cos(toRadians(a)),
2589
- tan: a => Math.tan(toRadians(a)),
2590
- floor: a => Math.floor(a),
2591
- ceil: a => Math.ceil(a),
2592
- sqrt: a => Math.sqrt(a),
2593
- asin: a => toDegrees(Math.asin(a)),
2594
- acos: a => toDegrees(Math.acos(a)),
2595
- atan: a => toDegrees(Math.atan(a)),
2596
- // yes, this doesn't work with negative numbers
2597
- // but the game also implements it this way.
2598
- idiv: (a, b) => Math.floor(a / b),
2599
- pow: (a, b) => Math.pow(a, b),
2600
- rand: null,
2601
- };
2602
- const orderIndependentOperations = ["max", "min", "len"];
2603
- function createMacroMathOperations() {
2604
- const macroMathOperations = {
2605
- PI: new StoreValue("@pi", EMutability.constant),
2606
- E: new StoreValue("@e", EMutability.constant),
2607
- degToRad: new StoreValue("@degToRad", EMutability.constant),
2608
- radToDeg: new StoreValue("@radToDeg", EMutability.constant),
2609
- };
2610
- for (const key in mathOperations) {
2611
- const fn = mathOperations[key];
2612
- macroMathOperations[key] = new MacroFunction((scope, out, a, b) => {
2613
- const argumentCount = +!!a + +!!b;
2614
- if (fn && argumentCount != fn.length) {
2615
- throw new CompilerError(`Expected ${fn.length} arguments, but got ${argumentCount}`);
2616
- }
2617
- const cacheKey = getCacheKey(key);
2618
- if (b) {
2619
- if (fn && a instanceof LiteralValue && b instanceof LiteralValue) {
2620
- if (!a.isNumber() || !b.isNumber())
2621
- throw new CompilerError("Cannot do math operation with non-numerical literals.");
2622
- return [new LiteralValue(fn(a.num, b.num)), []];
2623
- }
2624
- const cachedResult = scope.getCachedOperation(cacheKey, a, b);
2625
- if (cachedResult)
2626
- return [cachedResult, []];
2627
- // max, min and len do not change if the arguments change in order
2628
- if (orderIndependentOperations.includes(key)) {
2629
- const cachedResult = scope.getCachedOperation(cacheKey, b, a);
2630
- if (cachedResult)
2631
- return [cachedResult, []];
2632
- }
2633
- const temp = StoreValue.from(scope, out);
2634
- scope.addCachedOperation(cacheKey, temp, a, b);
2635
- return [temp, [new OperationInstruction(key, temp, a, b)]];
2636
- }
2637
- if (fn && a instanceof LiteralValue) {
2638
- if (!a.isNumber())
2639
- throw new CompilerError("Cannot do math operation with non-numerical literal.");
2640
- return [new LiteralValue(fn(a.num)), []];
2641
- }
2642
- const cachedResult = scope.getCachedOperation(cacheKey, a);
2643
- if (cachedResult)
2644
- return [cachedResult, []];
2645
- const temp = StoreValue.from(scope, out);
2646
- // ensures that operations like `rand` are not cached
2647
- if (mathOperations[key])
2648
- scope.addCachedOperation(cacheKey, temp, a);
2649
- return [temp, [new OperationInstruction(key, temp, a, null)]];
2650
- });
2651
- }
2652
- return macroMathOperations;
2653
- }
2654
- function getCacheKey(key) {
2655
- if (key === "pow")
2656
- return "**";
2657
- return key;
2658
- }
2659
- class MlogMath extends ObjectValue {
2660
- constructor() {
2661
- super(createMacroMathOperations());
2662
- }
2663
- }
2664
- // op angle result a b
2665
- // op len result a b
2666
-
2667
- class Concat extends MacroFunction {
2668
- constructor() {
2669
- super((scope, out, ...values) => {
2670
- const [first] = values;
2671
- if (!isTemplateObjectArray(first)) {
2672
- assertLiterals(values, "Concat arguments must be all literal values");
2673
- let text = "";
2674
- for (const value of values)
2675
- text += value.data;
2676
- return [new LiteralValue(text), []];
2677
- }
2678
- let text = "";
2679
- const [, ...interpolated] = values;
2680
- assertLiterals(interpolated, "Interpolated values in concat must be literals");
2681
- for (let i = 0; i < values.length; i++) {
2682
- const [string] = first.get(scope, new LiteralValue(i));
2683
- text += string.data;
2684
- if (i < interpolated.length)
2685
- text += interpolated[i].data;
2686
- }
2687
- return [new LiteralValue(text), []];
2688
- });
2689
- }
2690
- }
2691
- function assertLiterals(values, message) {
2692
- for (const value of values) {
2693
- if (!(value instanceof LiteralValue))
2694
- throw new CompilerError(message);
2695
- }
2696
- }
2697
-
2698
- /** Used to create namespaces that contain multiple methods that map into instructions */
2699
- function createOverloadNamespace({ overloads, handler, }) {
2700
- const result = {};
2701
- for (const key in overloads) {
2702
- const { named, args } = overloads[key];
2703
- if (named) {
2704
- result[key] = new MacroFunction((scope, out, options) => {
2705
- assertIsObjectMacro(options, named);
2706
- return handler(scope, key, out, ...assertObjectFields(options, args));
2707
- });
2708
- continue;
2709
- }
2710
- result[key] = new MacroFunction((scope, out, ...params) => {
2711
- const handlerParams = [];
2712
- // validate the paramters
2713
- for (let i = 0; i < args.length; i++) {
2714
- let arg = args[i];
2715
- if (typeof arg === "string")
2716
- arg = { key: arg };
2717
- const param = params[i];
2718
- if (!param) {
2719
- if (arg.default == undefined)
2720
- throw new CompilerError(`Missing argument: ${arg.name ?? arg.key}`);
2721
- handlerParams.push(arg.default);
2722
- continue;
2723
- }
2724
- arg.validate?.(param);
2725
- handlerParams.push(param);
2726
- }
2727
- return handler(scope, key, out, ...handlerParams);
2728
- });
2729
- }
2730
- return result;
2731
- }
2732
-
2733
- class Draw extends ObjectValue {
2734
- constructor() {
2735
- const data = createOverloadNamespace({
2736
- overloads: {
2737
- clear: {
2738
- args: ["r", "g", "b"],
2739
- },
2740
- color: {
2741
- args: ["r", "g", "b", { key: "a", default: "" }],
2742
- },
2743
- col: {
2744
- args: ["rgbaData"],
2745
- },
2746
- stroke: {
2747
- args: ["width"],
2748
- },
2749
- line: {
2750
- named: "options",
2751
- args: ["x", "y", "x2", "y2"],
2752
- },
2753
- rect: {
2754
- named: "options",
2755
- args: ["x", "y", "width", "height"],
2756
- },
2757
- lineRect: { named: "options", args: ["x", "y", "width", "height"] },
2758
- linePoly: {
2759
- named: "options",
2760
- args: ["x", "y", "sides", "radius", "rotation"],
2761
- },
2762
- poly: {
2763
- named: "options",
2764
- args: ["x", "y", "sides", "radius", "rotation"],
2765
- },
2766
- triangle: {
2767
- named: "options",
2768
- args: ["x", "y", "x2", "y2", "x3", "y3"],
2769
- },
2770
- image: {
2771
- named: "options",
2772
- args: ["x", "y", "image", "size", "rotation"],
2773
- },
2774
- },
2775
- handler(scope, overload, out, ...args) {
2776
- return [null, [new InstructionBase("draw", overload, ...args)]];
2777
- },
2778
- });
2779
- super(data);
2780
- }
2781
- }
2782
-
2783
- class Print extends MacroFunction {
2784
- constructor() {
2785
- super((scope, out, ...values) => {
2786
- const [first] = values;
2787
- const inst = [];
2788
- if (!isTemplateObjectArray(first)) {
2789
- for (const value of values) {
2790
- inst.push(new InstructionBase("print", value));
2791
- }
2792
- return [null, inst];
2793
- }
2794
- // `first` is likely a template strings array
2795
- // maybe this should be checked in another way?
2796
- const { length } = first.data;
2797
- for (let i = 1; i < values.length; i++) {
2798
- const [string] = first.get(scope, new LiteralValue(i - 1));
2799
- if (string.data)
2800
- inst.push(new InstructionBase("print", string));
2801
- inst.push(new InstructionBase("print", values[i]));
2802
- }
2803
- const [tail] = first.get(scope, new LiteralValue(length.data - 1));
2804
- if (tail.data)
2805
- inst.push(new InstructionBase("print", tail));
2806
- return [null, inst];
2807
- });
2808
- }
2809
- }
2810
-
2811
- const defaultTargetName$1 = "message1";
2812
- class PrintFlush extends MacroFunction {
2813
- constructor() {
2814
- super((scope, out, target) => {
2815
- if (!target)
2816
- return [null, [new InstructionBase("printflush", defaultTargetName$1)]];
2817
- if (!(target instanceof SenseableValue))
2818
- throw new CompilerError("The printflush target must be a senseable value");
2819
- return [null, [new InstructionBase("printflush", target)]];
2820
- });
2821
- }
2822
- }
2823
-
2824
- const defaultTargetName = "display1";
2825
- class DrawFlush extends MacroFunction {
2826
- constructor() {
2827
- super((scope, out, target) => {
2828
- if (!target)
2829
- return [null, [new InstructionBase("drawflush", defaultTargetName)]];
2830
- if (!(target instanceof SenseableValue))
2831
- throw new CompilerError("The drawflush target must be a senseable value");
2832
- return [null, [new InstructionBase("drawflush", target)]];
2833
- });
2834
- }
2835
- }
2836
-
2837
- class GetBlock extends ObjectValue {
2838
- constructor() {
2839
- const data = createOverloadNamespace({
2840
- overloads: {
2841
- floor: { args: ["x", "y"] },
2842
- ore: { args: ["x", "y"] },
2843
- block: { args: ["x", "y"] },
2844
- building: { args: ["x", "y"] },
2845
- },
2846
- handler(scope, overload, out, x, y) {
2847
- const output = overload === "building"
2848
- ? SenseableValue.from(scope, out, EMutability.constant)
2849
- : StoreValue.from(scope, out, EMutability.constant);
2850
- return [
2851
- output,
2852
- [new InstructionBase("getblock", overload, output, x, y)],
2853
- ];
2854
- },
2855
- });
2856
- super(data);
2857
- }
2858
- }
2859
-
2860
- class GetLink extends MacroFunction {
2861
- constructor() {
2862
- super((scope, out, index) => {
2863
- if (!(index instanceof StoreValue) &&
2864
- (!(index instanceof LiteralValue) || !index.isNumber()))
2865
- throw new CompilerError("The getlink index must be a number literal or a store");
2866
- const outBuild = SenseableValue.from(scope, out, EMutability.constant);
2867
- return [outBuild, [new InstructionBase("getlink", outBuild, index)]];
2868
- });
2869
- }
2870
- }
2871
-
2872
- class Control extends ObjectValue {
2873
- constructor() {
2874
- super(createOverloadNamespace({
2875
- overloads: {
2876
- enabled: { args: ["building", "value"] },
2877
- shoot: {
2878
- named: "options",
2879
- args: ["building", "x", "y", "shoot"],
2880
- },
2881
- shootp: {
2882
- named: "options",
2883
- args: ["building", "unit", "shoot"],
2884
- },
2885
- config: { args: ["building", "value"] },
2886
- color: { args: ["building", "rgbaData"] },
2887
- },
2888
- handler(scope, overload, out, ...args) {
2889
- return [null, [new InstructionBase("control", overload, ...args)]];
2890
- },
2891
- }));
2892
- }
2893
- }
2894
-
2895
- const validRadarFilters = [
2896
- "any",
2897
- "enemy",
2898
- "ally",
2899
- "player",
2900
- "attacker",
2901
- "flying",
2902
- "boss",
2903
- "ground",
2904
- ];
2905
- const validRadarSorts = [
2906
- "distance",
2907
- "health",
2908
- "shield",
2909
- "armor",
2910
- "maxHealth",
2911
- ];
2912
- class Radar extends MacroFunction {
2913
- constructor() {
2914
- super((scope, out, options) => {
2915
- assertIsObjectMacro(options, "The radar options");
2916
- const { building, filters, order, sort } = options.data;
2917
- if (!(building instanceof SenseableValue))
2918
- throw new CompilerError("The building must a senseable value");
2919
- assertIsArrayMacro(filters, "filters");
2920
- const { length } = filters.data;
2921
- if (!(length instanceof LiteralValue) || !length.isNumber())
2922
- throw new CompilerError("The length of an array macro must be");
2923
- if (length.data !== 3)
2924
- throw new CompilerError("The filters array must have 3 items");
2925
- // data is not an array
2926
- const { 0: filter1, 1: filter2, 2: filter3 } = filters.data;
2927
- assertLiteralOneOf(filter1, validRadarFilters, "The first filter");
2928
- assertLiteralOneOf(filter2, validRadarFilters, "The second filter");
2929
- assertLiteralOneOf(filter3, validRadarFilters, "The third filter");
2930
- if (!(order instanceof LiteralValue || order instanceof StoreValue))
2931
- throw new CompilerError("The radar order must be a literal or a store");
2932
- assertLiteralOneOf(sort, validRadarSorts, "The radar sort");
2933
- const outUnit = SenseableValue.from(scope, out, EMutability.constant);
2934
- return [
2935
- outUnit,
2936
- [
2937
- new InstructionBase("radar", filter1.data, filter2.data, filter3.data, sort.data, building, order, outUnit),
2938
- ],
2939
- ];
2940
- });
2941
- }
2942
- }
2943
-
2944
- class Sensor extends MacroFunction {
2945
- constructor() {
2946
- super((scope, out, property, target) => {
2947
- if (!(property instanceof StoreValue))
2948
- throw new CompilerError("The sensor property must be a store");
2949
- if (!(target instanceof SenseableValue))
2950
- throw new CompilerError("The sensor target must be a senseable value");
2951
- const temp = SenseableValue.from(scope, out, EMutability.readonly);
2952
- return [temp, [new InstructionBase("sensor", temp, target, property)]];
2953
- });
2954
- }
2955
- }
2956
-
2957
- class Wait extends MacroFunction {
2958
- constructor() {
2959
- super((scope, out, seconds) => {
2960
- if (!(seconds instanceof StoreValue) &&
2961
- !(seconds instanceof LiteralValue && seconds.isNumber()))
2962
- throw new CompilerError("The wait seconds must be either a number literal or a store");
2963
- return [null, [new InstructionBase("wait", seconds)]];
2964
- });
2965
- }
2966
- }
2967
-
2968
- class Lookup extends ObjectValue {
2969
- constructor() {
2970
- const data = createOverloadNamespace({
2971
- overloads: {
2972
- block: { args: ["index"] },
2973
- unit: { args: ["index"] },
2974
- item: { args: ["index"] },
2975
- liquid: { args: ["index"] },
2976
- },
2977
- handler(scope, overload, out, index) {
2978
- const output = StoreValue.from(scope, out);
2979
- return [
2980
- output,
2981
- [new InstructionBase("lookup", overload, output, index)],
2982
- ];
2983
- },
2984
- });
2985
- super(data);
2986
- }
2987
- }
2988
-
2989
- class PackColor extends MacroFunction {
2990
- constructor() {
2991
- super((scope, out, ...args) => {
2992
- if (args.length !== 4) {
2993
- throw new CompilerError(`Expected 4 arguments, received ${args.length}`);
2994
- }
2995
- if (!args.every((value) => value instanceof StoreValue ||
2996
- (value instanceof LiteralValue && value.isNumber()))) {
2997
- throw new CompilerError("packColor arguments must be either stores or number literals");
2998
- }
2999
- const output = StoreValue.from(scope, out);
3000
- return [output, [new InstructionBase("packcolor", output, ...args)]];
3001
- });
3002
- }
3003
- }
3004
-
3005
- class End extends MacroFunction {
3006
- constructor() {
3007
- super(() => [null, [new EndInstruction()]]);
3008
- }
3009
- }
3010
-
3011
- class SetBlock extends ObjectValue {
3012
- constructor() {
3013
- const data = createOverloadNamespace({
3014
- overloads: {
3015
- floor: { args: ["x", "y", "to"] },
3016
- ore: { args: ["x", "y", "to"] },
3017
- block: {
3018
- named: "options",
3019
- args: ["x", "y", "to", "team", { key: "rotation", default: "0" }],
3020
- },
3021
- },
3022
- handler(scope, overload, out, x, y, to, team, rotation) {
3023
- return [
3024
- null,
3025
- [
3026
- new InstructionBase("setblock", overload, to, x, y, team ?? "@derelict", rotation ?? "0"),
3027
- ],
3028
- ];
3029
- },
3030
- });
3031
- super(data);
3032
- }
3033
- }
3034
-
3035
- class SpawnUnit extends MacroFunction {
3036
- constructor() {
3037
- super((scope, out, options) => {
3038
- assertIsObjectMacro(options, "The options");
3039
- const { type, x, y, team, rotation } = options.data;
3040
- assertIsRuntimeValue(type, "type");
3041
- assertIsRuntimeValue(x, "x");
3042
- assertIsRuntimeValue(y, "y");
3043
- assertIsRuntimeValue(team, "team");
3044
- const output = SenseableValue.from(scope, out, EMutability.constant);
3045
- return [
3046
- output,
3047
- [
3048
- new InstructionBase("spawn", type, x, y, rotation ?? "0", team, output),
3049
- ],
3050
- ];
3051
- });
3052
- }
3053
- }
3054
-
3055
- class Stop extends MacroFunction {
3056
- constructor() {
3057
- super(() => [
3058
- null,
3059
- [
3060
- assign(new InstructionBase("stop"), {
3061
- intent: EInstIntent.exit,
3062
- }),
3063
- ],
3064
- ]);
3065
- }
3066
- }
3067
-
3068
- class UnitBind extends MacroFunction {
3069
- constructor() {
3070
- super((scope, out, unit) => {
3071
- if (!(unit instanceof StoreValue))
3072
- throw new CompilerError("The unitBind unit must be a store");
3073
- return [null, [new InstructionBase("ubind", unit)]];
3074
- });
3075
- }
3076
- }
3077
-
3078
- class UnitControl extends ObjectValue {
3079
- constructor() {
3080
- const data = createOverloadNamespace({
3081
- overloads: {
3082
- idle: { args: [] },
3083
- stop: { args: [] },
3084
- move: { args: ["x", "y"] },
3085
- approach: { named: "options", args: ["x", "y", "radius"] },
3086
- boost: { args: ["enable"] },
3087
- pathfind: { args: [] },
3088
- target: { named: "options", args: ["x", "y", "shoot"] },
3089
- targetp: { named: "options", args: ["unit", "shoot"] },
3090
- itemDrop: { args: ["target", "amount"] },
3091
- itemTake: { args: ["target", "item", "amount"] },
3092
- payDrop: { args: [] },
3093
- payTake: { named: "options", args: ["takeUnits"] },
3094
- payEnter: { args: [] },
3095
- mine: { args: ["x", "y"] },
3096
- flag: { args: ["value"] },
3097
- build: {
3098
- named: "options",
3099
- args: ["x", "y", "block", "rotation", { key: "config", default: "" }],
3100
- },
3101
- getBlock: {
3102
- args: ["x", "y"],
3103
- },
3104
- within: {
3105
- named: "options",
3106
- args: ["x", "y", "radius"],
3107
- },
3108
- unbind: {
3109
- args: [],
3110
- },
3111
- },
3112
- handler(scope, overload, out, ...args) {
3113
- let result = null;
3114
- let extraArgs = [];
3115
- switch (overload) {
3116
- case "getBlock": {
3117
- const outType = StoreValue.from(scope, extractDestrucuringOut(out, 0));
3118
- const outBuilding = SenseableValue.from(scope, extractDestrucuringOut(out, 1));
3119
- const outFloor = StoreValue.from(scope, extractDestrucuringOut(out, 2));
3120
- result = ObjectValue.fromArray([outType, outBuilding, outFloor]);
3121
- extraArgs = [outType, outBuilding, outFloor];
3122
- break;
3123
- }
3124
- case "within": {
3125
- result = StoreValue.from(scope, out);
3126
- extraArgs = [result, new LiteralValue(0)];
3127
- break;
3128
- }
3129
- }
3130
- return [
3131
- result,
3132
- [new InstructionBase("ucontrol", overload, ...args, ...extraArgs)],
3133
- ];
3134
- },
3135
- });
3136
- super(data);
3137
- }
3138
- }
3139
-
3140
- class UnitRadar extends MacroFunction {
3141
- constructor() {
3142
- super((scope, out, options) => {
3143
- assertIsObjectMacro(options, "The radar options");
3144
- const { filters, order, sort } = options.data;
3145
- assertIsArrayMacro(filters, "filters");
3146
- const { length } = filters.data;
3147
- if (!(length instanceof LiteralValue) || !length.isNumber())
3148
- throw new CompilerError("The length of an array macro must be");
3149
- if (length.data !== 3)
3150
- throw new CompilerError("The filters array must have 3 items");
3151
- // data is not an array
3152
- const { 0: filter1, 1: filter2, 2: filter3 } = filters.data;
3153
- assertLiteralOneOf(filter1, validRadarFilters, "The first filter");
3154
- assertLiteralOneOf(filter2, validRadarFilters, "The second filter");
3155
- assertLiteralOneOf(filter3, validRadarFilters, "The third filter");
3156
- if (!(order instanceof LiteralValue || order instanceof StoreValue))
3157
- throw new CompilerError("The radar order must be a literal or a store");
3158
- assertLiteralOneOf(sort, validRadarSorts, "The radar sort");
3159
- const outUnit = SenseableValue.from(scope, out, EMutability.constant);
3160
- return [
3161
- outUnit,
3162
- [
3163
- new InstructionBase("uradar", filter1.data, filter2.data, filter3.data, sort.data, "0", // I don't know why, but mindustry requires this extra parameter
3164
- order, outUnit),
3165
- ],
3166
- ];
3167
- });
3168
- }
3169
- }
3170
-
3171
- const validBuildingGroups = [
3172
- "core",
3173
- "storage",
3174
- "generator",
3175
- "turret",
3176
- "factory",
3177
- "repair",
3178
- "rally",
3179
- "battery",
3180
- "reactor",
3181
- ];
3182
- class UnitLocate extends ObjectValue {
3183
- constructor() {
3184
- const data = createOverloadNamespace({
3185
- overloads: {
3186
- ore: {
3187
- args: ["ore"],
3188
- },
3189
- building: { named: "options", args: ["group", "enemy"] },
3190
- spawn: {
3191
- args: [],
3192
- },
3193
- damaged: {
3194
- args: [],
3195
- },
3196
- },
3197
- handler(scope, overload, out, ...args) {
3198
- const outFound = StoreValue.from(scope, extractDestrucuringOut(out, 0));
3199
- const outX = StoreValue.from(scope, extractDestrucuringOut(out, 1));
3200
- const outY = StoreValue.from(scope, extractDestrucuringOut(out, 2));
3201
- const outBuilding = SenseableValue.from(scope, overload === "ore" ? discardedName : extractDestrucuringOut(out, 3));
3202
- const outArgs = [outX, outY, outFound, outBuilding];
3203
- let inputArgs = [];
3204
- switch (overload) {
3205
- case "ore": {
3206
- const [ore] = args;
3207
- inputArgs = [overload, "core", "true", ore];
3208
- break;
3209
- }
3210
- case "building": {
3211
- const [group, enemy] = args;
3212
- if (!(group instanceof LiteralValue))
3213
- throw new CompilerError("The building group must be a string literal");
3214
- assertLiteralOneOf(group, validBuildingGroups, "The building group");
3215
- inputArgs = [overload, group.data, enemy, "@copper"];
3216
- break;
3217
- }
3218
- case "spawn":
3219
- case "damaged": {
3220
- inputArgs = [overload, "core", "true", "@copper"];
3221
- break;
3222
- }
3223
- }
3224
- return [
3225
- ObjectValue.fromArray([
3226
- outFound,
3227
- outX,
3228
- outY,
3229
- ...(overload !== "ore" ? [outBuilding] : []),
3230
- ]),
3231
- [new InstructionBase("ulocate", ...inputArgs, ...outArgs)],
3232
- ];
3233
- },
3234
- });
3235
- super(data);
3236
- }
3237
- }
3238
-
3239
- const validEffects = [
3240
- "burning",
3241
- "freezing",
3242
- "unmoving",
3243
- "wet",
3244
- "melting",
3245
- "sapped",
3246
- "electrified",
3247
- "spore-slowed",
3248
- "tarred",
3249
- "overdrive",
3250
- "overclock",
3251
- "boss",
3252
- "shocked",
3253
- "blasted",
3254
- ];
3255
- class ApplyStatus extends ObjectValue {
3256
- constructor() {
3257
- const data = createOverloadNamespace({
3258
- overloads: {
3259
- apply: {
3260
- args: ["status", "unit", { key: "duration", default: "10" }],
3261
- },
3262
- clear: { args: ["status", "unit"] },
3263
- },
3264
- handler(scope, overload, out, effect, unit, duration) {
3265
- if (!(effect instanceof LiteralValue))
3266
- throw new CompilerError("The status effect must be a string literal");
3267
- assertLiteralOneOf(effect, validEffects, "The status effect");
3268
- return [
3269
- null,
3270
- [
3271
- new InstructionBase("status", String(overload !== "apply"), effect.data, unit, duration),
3272
- ],
3273
- ];
3274
- },
3275
- });
3276
- super(data);
3277
- }
3278
- }
3279
-
3280
- class SpawnWave extends MacroFunction {
3281
- constructor() {
3282
- super((scope, out, natural, x, y) => {
3283
- if (!(natural instanceof LiteralValue) ||
3284
- (natural.data !== 1 && natural.data !== 0))
3285
- throw new CompilerError("The 'natural' argument must be a boolean literal");
3286
- return [
3287
- null,
3288
- [
3289
- new InstructionBase("spawnwave", x ?? "10", y ?? "10", String(!!natural.data)),
3290
- ],
3291
- ];
3292
- });
3293
- }
3294
- }
3295
-
3296
- class SetRule extends ObjectValue {
3297
- constructor() {
3298
- const data = createOverloadNamespace({
3299
- overloads: {
3300
- currentWaveTime: { args: ["seconds"] },
3301
- waveTimer: { args: ["enabled"] },
3302
- waves: { args: ["enabled"] },
3303
- wave: { args: ["number"] },
3304
- waveSpacing: { args: ["seconds"] },
3305
- waveSending: { args: ["enabled"] },
3306
- attackMode: { args: ["enabled"] },
3307
- enemyCoreBuildRadius: { args: ["radius"] },
3308
- dropZoneRadius: { args: ["radius"] },
3309
- unitCap: { args: ["cap"] },
3310
- mapArea: { named: "options", args: ["x", "y", "width", "height"] },
3311
- lighting: { args: ["enabled"] },
3312
- ambientLight: { args: ["rgbaData"] },
3313
- solarMultiplier: { args: ["multiplier"] },
3314
- buildSpeed: { args: ["team", "multiplier"] },
3315
- unitBuildSpeed: { args: ["team", "multiplier"] },
3316
- unitCost: { args: ["team", "multiplier"] },
3317
- unitDamage: { args: ["team", "multiplier"] },
3318
- blockHealth: { args: ["team", "multiplier"] },
3319
- blockDamage: { args: ["team", "multiplier"] },
3320
- rtsMinWeight: { args: ["team", "value"] },
3321
- rtsMinSquad: { args: ["team", "value"] },
3322
- },
3323
- handler(scope, overload, out, ...args) {
3324
- const params = ["10", "0", "0", "100", "100"];
3325
- switch (overload) {
3326
- case "mapArea": {
3327
- const [x, y, width, height] = args;
3328
- params[1] = x;
3329
- params[2] = y;
3330
- params[3] = width;
3331
- params[4] = height;
3332
- break;
3333
- }
3334
- case "buildSpeed":
3335
- case "unitBuildSpeed":
3336
- case "unitCost":
3337
- case "unitDamage":
3338
- case "blockHealth":
3339
- case "blockDamage":
3340
- case "rtsMinWeight":
3341
- case "rtsMinSquad": {
3342
- const [team, value] = args;
3343
- params[1] = team;
3344
- params[0] = value;
3345
- break;
3346
- }
3347
- default:
3348
- params[0] = args[0]; // the general value
3349
- }
3350
- return [null, [new InstructionBase("setrule", overload, ...params)]];
3351
- },
3352
- });
3353
- super(data);
3354
- }
3355
- }
3356
-
3357
- class FlushMessage extends ObjectValue {
3358
- constructor() {
3359
- const data = createOverloadNamespace({
3360
- overloads: {
3361
- notify: { args: [] },
3362
- mission: { args: [] },
3363
- announce: { args: ["duration"] },
3364
- toast: { args: ["duration"] },
3365
- },
3366
- handler(scope, overload, out, duration) {
3367
- return [null, [new InstructionBase("message", overload, duration)]];
3368
- },
3369
- });
3370
- super(data);
3371
- }
3372
- }
3373
-
3374
- class Cutscene extends ObjectValue {
3375
- constructor() {
3376
- const data = createOverloadNamespace({
3377
- overloads: {
3378
- pan: { named: "options", args: ["x", "y", "speed"] },
3379
- zoom: { args: ["level"] },
3380
- stop: { args: [] },
3381
- },
3382
- handler(scope, overload, out, ...args) {
3383
- const params = Object.assign(["100", "100", "0.06", "0"], args);
3384
- return [null, [new InstructionBase("cutscene", overload, ...params)]];
3385
- },
3386
- });
3387
- super(data);
3388
- }
3389
- }
3390
-
3391
- class Explosion extends MacroFunction {
3392
- constructor() {
3393
- super((scope, out, options) => {
3394
- assertIsObjectMacro(options, "The options");
3395
- const params = assertObjectFields(options, [
3396
- "team",
3397
- "x",
3398
- "y",
3399
- "radius",
3400
- "damage",
3401
- "air",
3402
- "ground",
3403
- "pierce",
3404
- ]);
3405
- return [null, [new InstructionBase("explosion", ...params)]];
3406
- });
3407
- }
3408
- }
3409
-
3410
- class SetRate extends MacroFunction {
3411
- constructor() {
3412
- super((scope, out, ipt) => [null, [new InstructionBase("setrate", ipt)]]);
3413
- }
3414
- }
3415
-
3416
- class Fetch extends ObjectValue {
3417
- constructor() {
3418
- const data = createOverloadNamespace({
3419
- overloads: {
3420
- unit: { args: ["team", "index"] },
3421
- unitCount: { args: ["team"] },
3422
- player: { args: ["team", "index"] },
3423
- playerCount: { args: ["team"] },
3424
- core: { args: ["team", "index"] },
3425
- coreCount: { args: ["team"] },
3426
- build: { args: ["team", "index", "block"] },
3427
- buildCount: { args: ["team", "block"] },
3428
- },
3429
- handler(scope, overload, out, team, ...rest) {
3430
- const output = SenseableValue.from(scope, out, EMutability.constant);
3431
- const params = ["0", "@conveyor"];
3432
- if (overload === "buildCount") {
3433
- params[1] = rest[0];
3434
- }
3435
- else {
3436
- Object.assign(params, rest);
3437
- }
3438
- return [
3439
- output,
3440
- [new InstructionBase("fetch", overload, output, team, ...params)],
3441
- ];
3442
- },
3443
- });
3444
- super(data);
3445
- }
3446
- }
3447
-
3448
- class GetFlag extends MacroFunction {
3449
- constructor() {
3450
- super((scope, out, flag) => {
3451
- const result = StoreValue.from(scope, out);
3452
- return [result, [new InstructionBase("getflag", result, flag)]];
3453
- });
3454
- }
3455
- }
3456
-
3457
- class SetFlag extends MacroFunction {
3458
- constructor() {
3459
- super((scope, out, flag, value) => {
3460
- if (!flag)
3461
- throw new CompilerError("Missing flag argument");
3462
- if (!value)
3463
- throw new CompilerError("Missing flag value");
3464
- return [null, [new InstructionBase("setflag", flag, value)]];
3465
- });
3466
- }
3467
- }
3468
-
3469
- class MemoryEntry extends BaseValue {
3470
- constructor(scope, mem, prop) {
3471
- super();
3472
- this.scope = scope;
3473
- this.mem = mem;
3474
- this.prop = prop;
3475
- this.macro = true;
3476
- this.store = null;
3477
- }
3478
- eval(scope, out) {
3479
- if (this.store)
3480
- return [this.store, []];
3481
- const temp = (this.store = StoreValue.from(scope, out));
3482
- return [
3483
- temp,
3484
- [new InstructionBase("read", temp, this.mem.cell, this.prop)],
3485
- ];
3486
- }
3487
- "="(scope, value) {
3488
- const [data, dataInst] = value.eval(scope);
3489
- return [
3490
- data,
3491
- [
3492
- ...dataInst,
3493
- new InstructionBase("write", data, this.mem.cell, this.prop),
3494
- ],
3495
- ];
3496
- }
3497
- debugString() {
3498
- return "MemoryEntry";
3499
- }
3500
- toString() {
3501
- return '"[macro MemoryEntry]"';
3502
- }
3503
- }
3504
- class MemoryMacro extends ObjectValue {
3505
- constructor(cell, size) {
3506
- super({
3507
- length: size,
3508
- size,
3509
- });
3510
- this.cell = cell;
3511
- }
3512
- get(scope, key, out) {
3513
- if (super.hasProperty(scope, key))
3514
- return super.get(scope, key, out);
3515
- if (key instanceof LiteralValue && !key.isNumber())
3516
- throw new CompilerError(`The member [${key.debugString()}] is not present in [${this.debugString()}]`);
3517
- const entry = new MemoryEntry(scope, this, key);
3518
- if (out)
3519
- return entry.eval(scope, out);
3520
- return [entry, []];
3521
- }
3522
- hasProperty(scope, prop) {
3523
- if ((prop instanceof LiteralValue && prop.isNumber()) ||
3524
- prop instanceof StoreValue)
3525
- return true;
3526
- return super.hasProperty(scope, prop);
3527
- }
3528
- debugString() {
3529
- return `Memory("${this.cell.toString()}")`;
3530
- }
3531
- toString() {
3532
- return '"[macro Memory]"';
3533
- }
3534
- }
3535
- class MemoryBuilder extends MacroFunction {
3536
- constructor() {
3537
- super((scope, out, cell, size = new LiteralValue(64)) => {
3538
- if (!(cell instanceof SenseableValue))
3539
- throw new CompilerError("Memory cell must be a senseable value.");
3540
- if (!(size instanceof LiteralValue && size.isNumber()) &&
3541
- !(size instanceof StoreValue))
3542
- throw new CompilerError("The memory size must be a number literal or a store.");
3543
- if (size.mutability === EMutability.constant) {
3544
- return [new MemoryMacro(cell, size), []];
3545
- }
3546
- const name = extractOutName(out) ?? scope.makeTempName();
3547
- const store = new StoreValue(`${name}.&len`);
3548
- const [, inst] = store["="](scope, size);
3549
- store.mutability = EMutability.constant;
3550
- return [new MemoryMacro(cell, store), inst];
3551
- });
3552
- }
3553
- }
3554
-
3555
- const dynamicVars = ["time", "tick"];
3556
- class NamespaceMacro extends ObjectValue {
3557
- constructor({ changeCasing = false } = {}) {
3558
- super();
3559
- this.changeCasing = changeCasing;
3560
- }
3561
- get(scope, key, out) {
3562
- if (super.hasProperty(scope, key))
3563
- return super.get(scope, key, out);
3564
- if (!(key instanceof LiteralValue) || !key.isString())
3565
- throw new CompilerError("Cannot use dynamic properties on namespace macros");
3566
- const symbolName = this.changeCasing ? camelToDashCase(key.data) : key.data;
3567
- const mutability = dynamicVars.includes(symbolName)
3568
- ? EMutability.readonly
3569
- : EMutability.constant;
3570
- const result = new StoreValue(`@${symbolName}`, mutability);
3571
- return [result, []];
3572
- }
3573
- hasProperty(scope, prop) {
3574
- return prop instanceof LiteralValue && prop.isString();
3575
- }
3576
- }
3577
- class VarsNamespace extends NamespaceMacro {
3578
- constructor() {
3579
- super();
3580
- Object.assign(this.data, {
3581
- unit: new SenseableValue("@unit"),
3582
- this: new SenseableValue("@this"),
3583
- });
3584
- }
3585
- }
3586
-
3587
- /**
3588
- * The size of a dynamic array iteration item.
3589
- * Includes the set counter instruction
3590
- */
3591
- const itemSize = 2;
3592
- class DynamicArray extends ObjectValue {
3593
- constructor(scope, name, values, dynamic) {
3594
- const getterName = `${name}.&read`;
3595
- const setterName = `${name}.&write`;
3596
- const returnName = `${name}.&rt`;
3597
- const lengthName = getLengthName(name);
3598
- const lengthStore = new StoreValue(lengthName);
3599
- const sizeValue = new LiteralValue(values.length);
3600
- super({
3601
- fill: new MacroFunction((scope, out, value) => {
3602
- if (!value)
3603
- throw new CompilerError("Missing argument: value");
3604
- this.initFill();
3605
- const inst = [];
3606
- const returnAddress = new LiteralValue(null);
3607
- pipeInsts(values[0]["="](scope, value), inst);
3608
- pipeInsts(this.returnTemp["="](scope, returnAddress), inst);
3609
- inst.push(new JumpInstruction(this.fillAddr, EJumpKind.Always));
3610
- inst.push(new AddressResolver(returnAddress));
3611
- if (this.lengthStore) {
3612
- pipeInsts(this.lengthStore["="](scope, new LiteralValue(values.length)), inst);
3613
- }
3614
- return [null, inst];
3615
- }, [values[0]]),
3616
- at: new MacroFunction((scope, out, index) => {
3617
- const inst = [];
3618
- const isLiteral = index instanceof LiteralValue;
3619
- if (isLiteral) {
3620
- assertBounds(index, -values.length, values.length - 1);
3621
- }
3622
- const indexTemp = isLiteral ? index : StoreValue.from(scope);
3623
- const address = new LiteralValue(null);
3624
- if (!isLiteral) {
3625
- pipeInsts(indexTemp["="](scope, index), inst);
3626
- inst.push(new JumpInstruction(address, EJumpKind.GreaterThanEq, indexTemp, new LiteralValue(0)));
3627
- }
3628
- const finalIndex = isLiteral && index.data >= 0
3629
- ? index
3630
- : pipeInsts(indexTemp["+"](scope, this.lengthStore ?? sizeValue, indexTemp), inst);
3631
- if (!isLiteral)
3632
- inst.push(new AddressResolver(address));
3633
- const entry = new DynamicArrayEntry({
3634
- scope,
3635
- array: this,
3636
- index: finalIndex,
3637
- checked: scope.checkIndexes,
3638
- });
3639
- const result = pipeInsts(entry.eval(scope, out), inst);
3640
- return [result, inst];
3641
- }),
3642
- size: sizeValue,
3643
- ...(dynamic
3644
- ? {
3645
- length: new StoreValue(lengthName, EMutability.readonly),
3646
- push: new MacroFunction((scope, out, item) => {
3647
- const checked = scope.checkIndexes;
3648
- const entry = new DynamicArrayEntry({
3649
- scope,
3650
- array: this,
3651
- index: lengthStore,
3652
- checked,
3653
- });
3654
- const [, inst] = entry["="](scope, item);
3655
- return [new LiteralValue(null), inst];
3656
- }, [setterName]),
3657
- pop: new MacroFunction((scope, out) => {
3658
- const checked = scope.checkIndexes;
3659
- const inst = [];
3660
- const index = pipeInsts(lengthStore["-"](scope, new LiteralValue(1)), inst);
3661
- const failAddress = new LiteralValue(null);
3662
- const entry = new DynamicArrayEntry({
3663
- scope,
3664
- array: this,
3665
- index,
3666
- checked,
3667
- newLength: index,
3668
- failAddress,
3669
- });
3670
- const nullLiteral = new LiteralValue(null);
3671
- const hasOut = !isDiscardedOut(out);
3672
- const result = hasOut
3673
- ? pipeInsts(entry.eval(scope, out), inst)
3674
- : nullLiteral;
3675
- // only add checks if the scope is checked and
3676
- // aren't any previous checks
3677
- entry.checked = checked && !hasOut;
3678
- pipeInsts(entry["="](scope, nullLiteral), inst);
3679
- inst.push(new AddressResolver(failAddress));
3680
- return [result, inst];
3681
- }),
3682
- removeAt: new MacroFunction((scope, out, index) => {
3683
- if (!index)
3684
- throw new CompilerError("Missing index argument");
3685
- this.initRemoveAt();
3686
- if (index instanceof LiteralValue) {
3687
- assertBounds(index, 0, values.length - 1);
3688
- }
3689
- const checked = scope.checkIndexes;
3690
- const hasOut = !isDiscardedOut(out);
3691
- const { returnTemp } = this;
3692
- const inst = [];
3693
- let result = new LiteralValue(null);
3694
- const returnAdress = new LiteralValue(null);
3695
- const failAddress = new LiteralValue(null);
3696
- const upperBound = new JumpInstruction(failAddress, EJumpKind.GreaterThanEq, index, lengthStore);
3697
- if (hasOut) {
3698
- const entry = new DynamicArrayEntry({
3699
- scope,
3700
- array: this,
3701
- checked: scope.checkIndexes,
3702
- index,
3703
- failAddress,
3704
- upperBound,
3705
- });
3706
- if (index instanceof LiteralValue) {
3707
- const value = pipeInsts(entry.eval(scope), inst);
3708
- result = StoreValue.from(scope, out);
3709
- pipeInsts(result["="](scope, value), inst);
3710
- }
3711
- else {
3712
- result = pipeInsts(entry.eval(scope, out), inst);
3713
- }
3714
- }
3715
- if (checked) {
3716
- if (!hasOut && !(index instanceof LiteralValue)) {
3717
- inst.push(new JumpInstruction(failAddress, EJumpKind.LessThan, index, new LiteralValue(0)));
3718
- }
3719
- if (!hasOut || index instanceof LiteralValue)
3720
- inst.push(upperBound);
3721
- }
3722
- pipeInsts(returnTemp["="](scope, returnAdress), inst);
3723
- const counter = new StoreValue(counterName);
3724
- inst.push(new InstructionBase("op", "add", counter, this.removeAtAddr, index));
3725
- inst.push(new AddressResolver(returnAdress));
3726
- pipeInsts(lengthStore["--"](scope, true), inst);
3727
- inst.push(new AddressResolver(failAddress));
3728
- return [result, inst];
3729
- }),
3730
- }
3731
- : {}),
3732
- });
3733
- this.scope = scope;
3734
- this.name = name;
3735
- this.values = values;
3736
- this.dynamic = dynamic;
3737
- this.bundledSetter = false;
3738
- this.bundledGetter = false;
3739
- this.bundledRemoveAt = false;
3740
- this.bundledFill = false;
3741
- this.scope = scope;
3742
- this.name = name;
3743
- this.values = values;
3744
- this.getterTemp = new SenseableValue(getterName, EMutability.mutable);
3745
- this.setterTemp = new SenseableValue(setterName, EMutability.mutable);
3746
- this.returnTemp = new StoreValue(returnName);
3747
- if (dynamic)
3748
- this.lengthStore = lengthStore;
3749
- this.getterAddr = new LiteralValue(null);
3750
- this.setterAddr = new LiteralValue(null);
3751
- this.removeAtAddr = new LiteralValue(null);
3752
- this.fillAddr = new LiteralValue(null);
3753
- }
3754
- get(scope, index, out) {
3755
- if (super.hasProperty(scope, index))
3756
- return super.get(scope, index, out);
3757
- const { values } = this;
3758
- if (index instanceof LiteralValue) {
3759
- assertBounds(index, 0, values.length - 1);
3760
- }
3761
- const entry = new DynamicArrayEntry({
3762
- scope,
3763
- array: this,
3764
- index,
3765
- checked: scope.checkIndexes,
3766
- });
3767
- if (out)
3768
- return entry.eval(scope, out);
3769
- return [entry, []];
3770
- }
3771
- hasProperty(scope, prop) {
3772
- if (prop instanceof LiteralValue && prop.isNumber()) {
3773
- return prop.data >= 0 && prop.data < this.values.length;
3774
- }
3775
- if (prop instanceof StoreValue)
3776
- return true;
3777
- return super.hasProperty(scope, prop);
3778
- }
3779
- initGetter() {
3780
- if (this.bundledGetter)
3781
- return;
3782
- this.bundledGetter = true;
3783
- this.scope.inst.push(new AddressResolver(this.getterAddr));
3784
- for (const value of this.values) {
3785
- this.scope.inst.push(new SetInstruction(this.getterTemp, value), new SetCounterInstruction(this.returnTemp));
3786
- }
3787
- }
3788
- initSetter() {
3789
- if (this.bundledSetter)
3790
- return;
3791
- this.bundledSetter = true;
3792
- this.scope.inst.push(new AddressResolver(this.setterAddr));
3793
- for (const value of this.values) {
3794
- this.scope.inst.push(new SetInstruction(value, this.setterTemp), new SetCounterInstruction(this.returnTemp));
3795
- }
3796
- }
3797
- initRemoveAt() {
3798
- if (this.bundledRemoveAt)
3799
- return;
3800
- this.bundledRemoveAt = true;
3801
- const { inst } = this.scope;
3802
- inst.push(new AddressResolver(this.removeAtAddr));
3803
- for (let i = 0; i < this.values.length - 1; i++) {
3804
- const value = this.values[i];
3805
- const next = this.values[i + 1];
3806
- pipeInsts(value["="](this.scope, next), inst);
3807
- }
3808
- const last = this.values[this.values.length - 1];
3809
- pipeInsts(last["="](this.scope, new LiteralValue(null)), inst);
3810
- inst.push(new SetCounterInstruction(this.returnTemp));
3811
- }
3812
- initFill() {
3813
- if (this.bundledFill)
3814
- return;
3815
- this.bundledFill = true;
3816
- const { inst } = this.scope;
3817
- inst.push(new AddressResolver(this.fillAddr));
3818
- const firstValue = this.values[0];
3819
- for (let i = 1; i < this.values.length; i++) {
3820
- const value = this.values[i];
3821
- pipeInsts(value["="](this.scope, firstValue), inst);
3822
- }
3823
- inst.push(new SetCounterInstruction(this.returnTemp));
3824
- }
3825
- debugString() {
3826
- const id = this.dynamic ? "DynamicArray" : "MutableArray";
3827
- return `${id}("${this.name}")`;
3828
- }
3829
- toString() {
3830
- const id = this.dynamic ? "DynamicArray" : "MutableArray";
3831
- return `"[macro ${id}]"`;
3832
- }
3833
- }
3834
- class DynamicArrayEntry extends BaseValue {
3835
- constructor({ array, checked, index, scope, newLength, failAddress, upperBound, }) {
3836
- super();
3837
- this.macro = true;
3838
- this.array = array;
3839
- this.scope = scope;
3840
- this.checked = checked;
3841
- this.index = index;
3842
- this.newLength = newLength;
3843
- this.failAddress = failAddress;
3844
- this.upperBound = upperBound;
3845
- }
3846
- eval(scope, out) {
3847
- const { checked, index } = this;
3848
- if (index instanceof LiteralValue) {
3849
- return [this.array.values[index.data], []];
3850
- }
3851
- this.array.initGetter();
3852
- const inst = [];
3853
- const { getterTemp, values, getterAddr, returnTemp } = this.array;
3854
- // the value will be stored somewhere
3855
- // no need to save it in a temporary variable
3856
- const temp = out instanceof StoreValue
3857
- ? out
3858
- : out
3859
- ? getterTemp
3860
- : SenseableValue.from(scope, undefined, EMutability.mutable);
3861
- // used in checked mode, jumps to this address
3862
- // if the index is out of bounds
3863
- const failAddr = this.failAddress ?? new LiteralValue(null);
3864
- if (checked) {
3865
- inst.push(...temp["="](scope, new LiteralValue(null))[1], new JumpInstruction(failAddr, EJumpKind.LessThan, index, new LiteralValue(0)), this.upperBound ??
3866
- new JumpInstruction(failAddr, EJumpKind.GreaterThan, index, new LiteralValue(values.length - 1)));
3867
- }
3868
- const returnAdress = new LiteralValue(null);
3869
- pipeInsts(returnTemp["="](scope, returnAdress), inst);
3870
- const counter = new StoreValue(counterName);
3871
- const doubleIndex = pipeInsts(index["*"](scope, new LiteralValue(itemSize)), inst);
3872
- const line = pipeInsts(getterAddr["+"](scope, doubleIndex, counter), inst);
3873
- pipeInsts(counter["="](scope, line), inst);
3874
- inst.push(new AddressResolver(returnAdress));
3875
- // without this you can't access the array twice inside the same expression
3876
- pipeInsts(temp["="](scope, getterTemp), inst);
3877
- if (checked && !this.failAddress) {
3878
- inst.push(new AddressResolver(failAddr));
3879
- }
3880
- return [temp, inst];
3881
- }
3882
- "="(scope, value) {
3883
- const { checked, index } = this;
3884
- const { setterTemp, values, setterAddr, returnTemp, lengthStore } = this.array;
3885
- const inst = [];
3886
- // where to jump in checked mode if the index is out of range
3887
- const failAddress = this.failAddress ?? new LiteralValue(null);
3888
- if (index instanceof LiteralValue) {
3889
- const member = values[index.data];
3890
- pipeInsts(member["="](scope, value), inst);
3891
- }
3892
- else {
3893
- this.array.initSetter();
3894
- // used in both checked and unchecked modes
3895
- // indicates where to jump after success
3896
- const returnAdress = new LiteralValue(null);
3897
- if (checked) {
3898
- inst.push(new JumpInstruction(failAddress, EJumpKind.LessThan, index, new LiteralValue(0)), this.upperBound ??
3899
- new JumpInstruction(failAddress, EJumpKind.GreaterThan, index, new LiteralValue(values.length - 1)));
3900
- }
3901
- pipeInsts(setterTemp["="](scope, value), inst);
3902
- pipeInsts(returnTemp["="](scope, returnAdress), inst);
3903
- const counter = new StoreValue(counterName);
3904
- const doubleIndex = pipeInsts(index["*"](scope, new LiteralValue(itemSize)), inst);
3905
- const line = pipeInsts(setterAddr["+"](scope, doubleIndex, counter), inst);
3906
- pipeInsts(counter["="](scope, line), inst);
3907
- inst.push(new AddressResolver(returnAdress));
3908
- }
3909
- if (lengthStore) {
3910
- if (this.newLength) {
3911
- pipeInsts(lengthStore["="](scope, this.newLength), inst);
3912
- }
3913
- else {
3914
- const indexIsLength = index.name === lengthStore.name;
3915
- const len = pipeInsts(index["+"](scope, new LiteralValue(1), indexIsLength ? lengthStore : undefined), inst);
3916
- if (!indexIsLength)
3917
- inst.push(new OperationInstruction("max", lengthStore, len, lengthStore));
3918
- }
3919
- }
3920
- if (!this.failAddress)
3921
- inst.push(new AddressResolver(failAddress));
3922
- return [value, inst];
3923
- }
3924
- toOut() {
3925
- return this.array.setterTemp;
3926
- }
3927
- debugString() {
3928
- const name = this.array.dynamic ? "DynamicArray" : "MutableArray";
3929
- return `${name}Entry`;
3930
- }
3931
- toString() {
3932
- const name = this.array.dynamic ? "DynamicArray" : "MutableArray";
3933
- return `"[macro ${name}Entry]"`;
3934
- }
3935
- }
3936
- class DynamicArrayConstructor extends MacroFunction {
3937
- constructor(dynamic) {
3938
- super((scope, out, init) => {
3939
- const name = extractOutName(out) ?? scope.makeTempName();
3940
- const inst = [];
3941
- const values = [];
3942
- let length;
3943
- if (init instanceof LiteralValue && typeof init.data === "number") {
3944
- length = init.data;
3945
- }
3946
- else if (init instanceof ObjectValue) {
3947
- assertIsArrayMacro(init, "The array initializer");
3948
- length = init.data.length.data;
3949
- }
3950
- else {
3951
- throw new CompilerError("The dynamic array initializer must be an array macro or a number literal");
3952
- }
3953
- for (let i = 0; i < length; i++) {
3954
- values.push(new SenseableValue(`${name}->${i}`, EMutability.mutable));
3955
- }
3956
- if (init instanceof ObjectValue) {
3957
- const length = init.data.length.data;
3958
- for (let i = 0; i < length; i++) {
3959
- const value = pipeInsts(init.get(scope, new LiteralValue(i), values[i]), inst);
3960
- pipeInsts(values[i]["="](scope, value), inst);
3961
- }
3962
- }
3963
- if (dynamic) {
3964
- const lengthStore = new StoreValue(getLengthName(name));
3965
- pipeInsts(lengthStore["="](scope, new LiteralValue(init instanceof ObjectValue ? length : 0)), inst);
3966
- }
3967
- return [new DynamicArray(scope, name, values, dynamic), inst];
3968
- });
3969
- }
3970
- }
3971
- const getLengthName = (name) => `${name}.&len`;
3972
- function assertBounds(
3973
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
3974
- index, min, max) {
3975
- if (!index.isNumber())
3976
- throw new CompilerError("The index must be a store or a number literal");
3977
- if (index.data < min || index.data > max)
3978
- throw new CompilerError(`The index "${index.data}" is out of bounds: [${min}, ${max}]`);
3979
- }
3980
-
3981
- class Unchecked extends MacroFunction {
3982
- constructor() {
3983
- super((scope, out, expression) => [expression, []]);
3984
- }
3985
- preCall(scope, out) {
3986
- scope.checkIndexes = false;
3987
- if (out)
3988
- return [out];
3989
- }
3990
- postCall(scope) {
3991
- scope.checkIndexes = true;
3992
- }
3993
- }
3994
-
3995
- class GetGlobal extends MacroFunction {
3996
- constructor(mutability) {
3997
- super((scope, out, name) => {
3998
- if (!(name instanceof LiteralValue) || !name.isString())
3999
- throw new CompilerError("The name parameter must be a string literal.");
4000
- const result = new SenseableValue(name.data, mutability);
4001
- return [result, []];
4002
- });
4003
- }
4004
- }
4005
-
4006
- class Asm extends MacroFunction {
4007
- constructor() {
4008
- super((scope, out, stringsArray, ...values) => {
4009
- if (!isTemplateObjectArray(stringsArray))
4010
- throw new CompilerError("Expected to receive a template strings array");
4011
- const args = [];
4012
- for (let i = 0; i < values.length; i++) {
4013
- const [item] = stringsArray.get(scope, new LiteralValue(i));
4014
- args.push(item.data);
4015
- args.push(values[i]);
4016
- }
4017
- const { length } = stringsArray.data;
4018
- const [tail] = stringsArray.get(scope, new LiteralValue(length.data - 1));
4019
- args.push(tail.data);
4020
- return [null, formatInstructions(args)];
4021
- });
4022
- }
4023
- }
4024
- /** Splits multiline asm calls and formats each line. */
4025
- function formatInstructions(args) {
4026
- const instructions = [];
4027
- let buffer = [];
4028
- for (let i = 0; i < args.length; i++) {
4029
- const arg = args[i];
4030
- if (typeof arg !== "string" || !arg.includes("\n")) {
4031
- buffer.push(arg);
4032
- continue;
4033
- }
4034
- const segments = arg.split("\n");
4035
- const params = [...buffer, segments[0]];
4036
- if (validateInstructionArgs(params)) {
4037
- instructions.push(new ZeroSpaceInstruction(...params));
4038
- buffer = [];
4039
- }
4040
- // iterates over all segments except the first and last
4041
- // the first segment was already processed above
4042
- // the last segment will be added to the buffer because there might
4043
- // be an interpolation after it
4044
- // and putting it in the buffer makes sure it has a proper output format
4045
- for (let i = 1; i < segments.length - 1; i++) {
4046
- const trimmed = segments[i].trim();
4047
- if (trimmed.length == 0)
4048
- continue;
4049
- instructions.push(new ZeroSpaceInstruction(trimmed));
4050
- }
4051
- if (segments.length > 1) {
4052
- buffer.push(segments[segments.length - 1]);
4053
- }
4054
- }
4055
- if (buffer.length > 0 && validateInstructionArgs(buffer)) {
4056
- instructions.push(new ZeroSpaceInstruction(...buffer));
4057
- }
4058
- return instructions;
4059
- }
4060
- /** Determines if an asm line should be generated and trims it at the start and end */
4061
- function validateInstructionArgs(args) {
4062
- if (args.length === 0)
4063
- return false;
4064
- if (args.length === 1) {
4065
- const item = args[0];
4066
- if (typeof item !== "string")
4067
- return true;
4068
- args[0] = item.trim();
4069
- return args[0].length !== 0;
4070
- }
4071
- const first = args[0];
4072
- const last = args[args.length - 1];
4073
- if (typeof first === "string")
4074
- args[0] = first.trimStart();
4075
- if (typeof last === "string")
4076
- args[args.length - 1] = last.trimEnd();
4077
- return true;
4078
- }
4079
-
4080
- /**
4081
- * Adds all the compiler globals to `scope`
4082
- */
4083
- function initScope(scope) {
4084
- scope.hardSet("undefined", new LiteralValue(null));
4085
- // namespaces
4086
- scope.hardSet("ControlKind", new NamespaceMacro());
4087
- scope.hardSet("Vars", new VarsNamespace());
4088
- scope.hardSet("Teams", new NamespaceMacro());
4089
- scope.hardSet("Items", new NamespaceMacro({ changeCasing: true }));
4090
- scope.hardSet("Liquids", new NamespaceMacro());
4091
- scope.hardSet("Units", new NamespaceMacro({ changeCasing: true }));
4092
- scope.hardSet("LAccess", new NamespaceMacro());
4093
- scope.hardSet("Blocks", new NamespaceMacro({ changeCasing: true }));
4094
- // helper methods
4095
- scope.hardSet("getBuilding", new GetGlobal(EMutability.constant));
4096
- scope.hardSet("getVar", new GetGlobal(EMutability.mutable));
4097
- scope.hardSet("concat", new Concat());
4098
- scope.hardSet("asm", new Asm());
4099
- scope.hardSet("Math", new MlogMath());
4100
- scope.hardSet("Memory", new MemoryBuilder());
4101
- scope.hardSet("MutableArray", new DynamicArrayConstructor(false));
4102
- scope.hardSet("DynamicArray", new DynamicArrayConstructor(true));
4103
- scope.hardSet("unchecked", new Unchecked());
4104
- // commands
4105
- scope.hardSet("draw", new Draw());
4106
- scope.hardSet("print", new Print());
4107
- scope.hardSet("printFlush", new PrintFlush());
4108
- scope.hardSet("drawFlush", new DrawFlush());
4109
- scope.hardSet("getLink", new GetLink());
4110
- scope.hardSet("control", new Control());
4111
- scope.hardSet("radar", new Radar());
4112
- scope.hardSet("sensor", new Sensor());
4113
- scope.hardSet("wait", new Wait());
4114
- scope.hardSet("lookup", new Lookup());
4115
- scope.hardSet("packColor", new PackColor());
4116
- scope.hardSet("endScript", new End());
4117
- scope.hardSet("stopScript", new Stop());
4118
- scope.hardSet("unitBind", new UnitBind());
4119
- scope.hardSet("unitControl", new UnitControl());
4120
- scope.hardSet("unitRadar", new UnitRadar());
4121
- scope.hardSet("unitLocate", new UnitLocate());
4122
- // world processor commands
4123
- scope.hardSet("getBlock", new GetBlock());
4124
- scope.hardSet("setBlock", new SetBlock());
4125
- scope.hardSet("spawnUnit", new SpawnUnit());
4126
- scope.hardSet("applyStatus", new ApplyStatus());
4127
- scope.hardSet("spawnWave", new SpawnWave());
4128
- scope.hardSet("setRule", new SetRule());
4129
- scope.hardSet("flushMessage", new FlushMessage());
4130
- scope.hardSet("cutscene", new Cutscene());
4131
- scope.hardSet("explosion", new Explosion());
4132
- scope.hardSet("setRate", new SetRate());
4133
- scope.hardSet("fetch", new Fetch());
4134
- scope.hardSet("getFlag", new GetFlag());
4135
- scope.hardSet("setFlag", new SetFlag());
4136
- }
4137
-
4138
- class Compiler {
4139
- constructor({ stackName, compactNames = false, sourcemap = false, } = {}) {
4140
- this.handlers = handlers;
4141
- this.usingStack = !!stackName;
4142
- this.stackName = stackName;
4143
- this.compactNames = compactNames;
4144
- this.sourcemap = sourcemap;
4145
- }
4146
- compile(script) {
4147
- let output;
4148
- let sourcemaps;
4149
- try {
4150
- const program = this.parse(script);
4151
- const scope = new Scope({});
4152
- initScope(scope);
4153
- const valueInst = this.handle(scope, program);
4154
- if (needsEndInstruction(valueInst[1], scope)) {
4155
- valueInst[1].push(new EndInstruction());
4156
- }
4157
- valueInst[1].push(...scope.inst);
4158
- hideRedundantJumps(valueInst[1]);
4159
- this.resolve(valueInst);
4160
- output = this.serialize(valueInst) + "\n";
4161
- if (this.sourcemap) {
4162
- sourcemaps = this.mapSources(valueInst);
4163
- }
4164
- }
4165
- catch (error) {
4166
- const err = error instanceof CompilerError ? error : CompilerError.from(error);
4167
- return [null, err];
4168
- }
4169
- return [output, null, sourcemaps];
4170
- }
4171
- resolve(valueInst) {
4172
- let i = 0;
4173
- for (const inst of valueInst[1]) {
4174
- inst.resolve(i);
4175
- if (!inst.hidden)
4176
- i++;
4177
- }
4178
- }
4179
- serialize(resLines) {
4180
- const [, inst] = resLines;
4181
- return inst.filter(l => !l.hidden).join("\n");
4182
- }
4183
- parse(script) {
4184
- return parse(script, {
4185
- ranges: true,
4186
- plugins: ["typescript"],
4187
- });
4188
- }
4189
- mapSources(valueInst) {
4190
- const result = [];
4191
- for (const inst of valueInst[1]) {
4192
- if (!inst.hidden)
4193
- result.push(inst.source);
4194
- }
4195
- return result;
4196
- }
4197
- handle(scope, node, handler = this.handlers[node.type], out) {
4198
- try {
4199
- if (!handler)
4200
- throw new CompilerError("Missing handler for " + node.type);
4201
- const result = handler(this, scope, node, out, null);
4202
- if (this.sourcemap)
4203
- return appendSourceLocations(result, node);
4204
- return result;
4205
- }
4206
- catch (error) {
4207
- if (error instanceof CompilerError) {
4208
- error.loc ??= node.loc;
4209
- }
4210
- throw error;
4211
- }
4212
- }
4213
- /** Handles the node and asserts that it resolves to a value. */
4214
- handleValue(scope, node, handler = this.handlers[node.type], out) {
4215
- const result = this.handle(scope, node, handler, out);
4216
- if (!result[0])
4217
- throw new CompilerError(`This node (${node.type}) did not return a value`, node);
4218
- return result;
4219
- }
4220
- handleEval(scope, node, out) {
4221
- const [res, inst] = this.handleValue(scope, node, undefined, out);
4222
- const [evaluated, evaluatedInst] = res.eval(scope, out);
4223
- const result = [evaluated, [...inst, ...evaluatedInst]];
4224
- if (this.sourcemap)
4225
- return appendSourceLocations(result, node);
4226
- return result;
4227
- }
4228
- /**
4229
- * Handles many nodes in order.
4230
- *
4231
- * The usage of this method over a regular loop over an array of nodes
4232
- * is only required if the code inside the loop generates
4233
- * instructions that are not tracked by the compiler
4234
- * handler methods ({@link handle}, {@link handleEval}
4235
- * and {@link handleMany})
4236
- *
4237
- */
4238
- handleMany(scope, nodes, handler) {
4239
- const lines = [];
4240
- for (const node of hoistedFunctionNodes(nodes)) {
4241
- pipeInsts(this.handle(scope, node, handler && (() => handler(node))), lines);
4242
- }
4243
- return [null, lines];
4244
- }
4245
- }
4246
- function* hoistedFunctionNodes(nodes) {
4247
- // sorting is O(n long n) while this is just O(n)
4248
- // besides, it's better not to modify the node array
4249
- for (const node of nodes) {
4250
- if (node.type === "FunctionDeclaration") {
4251
- yield node;
4252
- }
4253
- }
4254
- for (const node of nodes) {
4255
- if (node.type !== "FunctionDeclaration") {
4256
- yield node;
4257
- }
4258
- }
4259
- }
4260
- function needsEndInstruction(inst, scope) {
4261
- const last = inst[inst.length - 1];
4262
- if (last instanceof AddressResolver)
4263
- return true;
4264
- return scope.inst.length > 0;
4265
- }
4266
-
4267
- export { Compiler, CompilerError };
1
+ export { C as Compiler, a as CompilerError } from './Compiler-7GMG4MDr.js';
2
+ import '@babel/parser';
3
+ import '@babel/types';