@creationix/rex 0.3.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -0
- package/package.json +9 -6
- package/rex-cli.js +1334 -1190
- package/rex-cli.ts +268 -27
- package/rex-repl.js +1048 -1135
- package/rex-repl.ts +392 -103
- package/rex.js +290 -954
- package/rex.ohm +48 -21
- package/rex.ohm-bundle.cjs +1 -1
- package/rex.ohm-bundle.d.ts +27 -8
- package/rex.ohm-bundle.js +1 -1
- package/rex.ts +388 -218
- package/rexc-interpreter.ts +386 -88
- package/rx-cli.js +2836 -0
- package/rx-cli.ts +298 -0
package/rex.js
CHANGED
|
@@ -10,31 +10,31 @@ function byteLength(value) {
|
|
|
10
10
|
return Buffer.byteLength(value, "utf8");
|
|
11
11
|
}
|
|
12
12
|
var OPCODE_IDS = {
|
|
13
|
-
do:
|
|
14
|
-
add:
|
|
15
|
-
sub:
|
|
16
|
-
mul:
|
|
17
|
-
div:
|
|
18
|
-
eq:
|
|
19
|
-
neq:
|
|
20
|
-
lt:
|
|
21
|
-
lte:
|
|
22
|
-
gt:
|
|
23
|
-
gte:
|
|
24
|
-
and:
|
|
25
|
-
or:
|
|
26
|
-
xor:
|
|
27
|
-
not:
|
|
28
|
-
boolean:
|
|
29
|
-
number:
|
|
30
|
-
string:
|
|
31
|
-
array:
|
|
32
|
-
object:
|
|
33
|
-
mod:
|
|
34
|
-
neg:
|
|
13
|
+
do: "",
|
|
14
|
+
add: "ad",
|
|
15
|
+
sub: "sb",
|
|
16
|
+
mul: "ml",
|
|
17
|
+
div: "dv",
|
|
18
|
+
eq: "eq",
|
|
19
|
+
neq: "nq",
|
|
20
|
+
lt: "lt",
|
|
21
|
+
lte: "le",
|
|
22
|
+
gt: "gt",
|
|
23
|
+
gte: "ge",
|
|
24
|
+
and: "an",
|
|
25
|
+
or: "or",
|
|
26
|
+
xor: "xr",
|
|
27
|
+
not: "nt",
|
|
28
|
+
boolean: "bt",
|
|
29
|
+
number: "nm",
|
|
30
|
+
string: "st",
|
|
31
|
+
array: "ar",
|
|
32
|
+
object: "ob",
|
|
33
|
+
mod: "md",
|
|
34
|
+
neg: "ng",
|
|
35
|
+
range: "rn"
|
|
35
36
|
};
|
|
36
|
-
var
|
|
37
|
-
var DOMAIN_DIGIT_INDEX = new Map(Array.from(DIGITS).map((char, index) => [char, index]));
|
|
37
|
+
var KEYWORD_OPCODES = new Set(["boolean", "number", "string", "array", "object"]);
|
|
38
38
|
var BINARY_TO_OPCODE = {
|
|
39
39
|
add: "add",
|
|
40
40
|
sub: "sub",
|
|
@@ -163,11 +163,11 @@ function encodeDecimal(significand, power) {
|
|
|
163
163
|
function encodeNumberNode(node) {
|
|
164
164
|
const numberValue = node.value;
|
|
165
165
|
if (Number.isNaN(numberValue))
|
|
166
|
-
return "
|
|
166
|
+
return "nan'";
|
|
167
167
|
if (numberValue === Infinity)
|
|
168
|
-
return "
|
|
168
|
+
return "inf'";
|
|
169
169
|
if (numberValue === -Infinity)
|
|
170
|
-
return "
|
|
170
|
+
return "nif'";
|
|
171
171
|
if (Number.isInteger(numberValue)) {
|
|
172
172
|
const { base, exp } = splitDecimal(numberValue);
|
|
173
173
|
if (exp >= 0 && exp <= 4)
|
|
@@ -199,7 +199,7 @@ function encodeNumberNode(node) {
|
|
|
199
199
|
return encodeDecimal(significand, power);
|
|
200
200
|
}
|
|
201
201
|
function encodeOpcode(opcode) {
|
|
202
|
-
return `${
|
|
202
|
+
return `${OPCODE_IDS[opcode]}%`;
|
|
203
203
|
}
|
|
204
204
|
function encodeCallParts(parts) {
|
|
205
205
|
return `(${parts.join("")})`;
|
|
@@ -216,7 +216,7 @@ function addOptionalPrefix(encoded) {
|
|
|
216
216
|
let payload = encoded;
|
|
217
217
|
if (encoded.startsWith("?(") || encoded.startsWith("!(") || encoded.startsWith("|(") || encoded.startsWith("&(") || encoded.startsWith(">(") || encoded.startsWith("<(") || encoded.startsWith("#(")) {
|
|
218
218
|
payload = encoded.slice(2, -1);
|
|
219
|
-
} else if (encoded.startsWith(">[") || encoded.startsWith(">{")) {
|
|
219
|
+
} else if (encoded.startsWith(">[") || encoded.startsWith(">{") || encoded.startsWith("<[") || encoded.startsWith("<{") || encoded.startsWith("#[") || encoded.startsWith("#{")) {
|
|
220
220
|
payload = encoded.slice(2, -1);
|
|
221
221
|
} else if (encoded.startsWith("[") || encoded.startsWith("{") || encoded.startsWith("(")) {
|
|
222
222
|
payload = encoded.slice(1, -1);
|
|
@@ -227,7 +227,7 @@ function addOptionalPrefix(encoded) {
|
|
|
227
227
|
}
|
|
228
228
|
function encodeBlockExpression(block) {
|
|
229
229
|
if (block.length === 0)
|
|
230
|
-
return "
|
|
230
|
+
return "un'";
|
|
231
231
|
if (block.length === 1)
|
|
232
232
|
return encodeNode(block[0]);
|
|
233
233
|
return encodeCallParts([encodeOpcode("do"), ...block.map((node) => encodeNode(node))]);
|
|
@@ -244,9 +244,13 @@ function encodeConditionalElse(elseBranch) {
|
|
|
244
244
|
};
|
|
245
245
|
return encodeNode(nested);
|
|
246
246
|
}
|
|
247
|
+
function encodeDomainLookup(shortCode, tag) {
|
|
248
|
+
return `${shortCode}${tag}`;
|
|
249
|
+
}
|
|
247
250
|
function encodeNavigation(node) {
|
|
248
251
|
const domainRefs = activeEncodeOptions?.domainRefs;
|
|
249
|
-
|
|
252
|
+
const domainOpcodes = activeEncodeOptions?.domainOpcodes;
|
|
253
|
+
if ((domainRefs || domainOpcodes) && node.target.type === "identifier") {
|
|
250
254
|
const staticPath = [node.target.name];
|
|
251
255
|
for (const segment of node.segments) {
|
|
252
256
|
if (segment.type !== "static")
|
|
@@ -255,14 +259,17 @@ function encodeNavigation(node) {
|
|
|
255
259
|
}
|
|
256
260
|
for (let pathLength = staticPath.length;pathLength >= 1; pathLength -= 1) {
|
|
257
261
|
const dottedName = staticPath.slice(0, pathLength).join(".");
|
|
258
|
-
const
|
|
259
|
-
|
|
262
|
+
const refCode = domainRefs?.[dottedName];
|
|
263
|
+
const opcodeCode = domainOpcodes?.[dottedName];
|
|
264
|
+
const shortCode = refCode ?? opcodeCode;
|
|
265
|
+
if (shortCode === undefined)
|
|
260
266
|
continue;
|
|
267
|
+
const tag = refCode !== undefined ? "'" : "%";
|
|
261
268
|
const consumedStaticSegments = pathLength - 1;
|
|
262
269
|
if (consumedStaticSegments === node.segments.length) {
|
|
263
|
-
return
|
|
270
|
+
return encodeDomainLookup(shortCode, tag);
|
|
264
271
|
}
|
|
265
|
-
const parts2 = [
|
|
272
|
+
const parts2 = [encodeDomainLookup(shortCode, tag)];
|
|
266
273
|
for (const segment of node.segments.slice(consumedStaticSegments)) {
|
|
267
274
|
if (segment.type === "static")
|
|
268
275
|
parts2.push(encodeBareOrLengthString(segment.key));
|
|
@@ -288,9 +295,12 @@ function encodeWhile(node) {
|
|
|
288
295
|
}
|
|
289
296
|
function encodeFor(node) {
|
|
290
297
|
const body = addOptionalPrefix(encodeBlockExpression(node.body));
|
|
291
|
-
if (node.binding.type === "binding:
|
|
298
|
+
if (node.binding.type === "binding:bareIn") {
|
|
292
299
|
return `>(${encodeNode(node.binding.source)}${body})`;
|
|
293
300
|
}
|
|
301
|
+
if (node.binding.type === "binding:bareOf") {
|
|
302
|
+
return `<(${encodeNode(node.binding.source)}${body})`;
|
|
303
|
+
}
|
|
294
304
|
if (node.binding.type === "binding:valueIn") {
|
|
295
305
|
return `>(${encodeNode(node.binding.source)}${node.binding.value}$${body})`;
|
|
296
306
|
}
|
|
@@ -301,30 +311,47 @@ function encodeFor(node) {
|
|
|
301
311
|
}
|
|
302
312
|
function encodeArrayComprehension(node) {
|
|
303
313
|
const body = addOptionalPrefix(encodeNode(node.body));
|
|
304
|
-
if (node.binding.type === "binding:
|
|
314
|
+
if (node.binding.type === "binding:bareIn") {
|
|
305
315
|
return `>[${encodeNode(node.binding.source)}${body}]`;
|
|
306
316
|
}
|
|
317
|
+
if (node.binding.type === "binding:bareOf") {
|
|
318
|
+
return `<[${encodeNode(node.binding.source)}${body}]`;
|
|
319
|
+
}
|
|
307
320
|
if (node.binding.type === "binding:valueIn") {
|
|
308
321
|
return `>[${encodeNode(node.binding.source)}${node.binding.value}$${body}]`;
|
|
309
322
|
}
|
|
310
323
|
if (node.binding.type === "binding:keyValueIn") {
|
|
311
324
|
return `>[${encodeNode(node.binding.source)}${node.binding.key}$${node.binding.value}$${body}]`;
|
|
312
325
|
}
|
|
313
|
-
return
|
|
326
|
+
return `<[${encodeNode(node.binding.source)}${node.binding.key}$${body}]`;
|
|
314
327
|
}
|
|
315
328
|
function encodeObjectComprehension(node) {
|
|
316
329
|
const key = addOptionalPrefix(encodeNode(node.key));
|
|
317
330
|
const value = addOptionalPrefix(encodeNode(node.value));
|
|
318
|
-
if (node.binding.type === "binding:
|
|
331
|
+
if (node.binding.type === "binding:bareIn") {
|
|
319
332
|
return `>{${encodeNode(node.binding.source)}${key}${value}}`;
|
|
320
333
|
}
|
|
334
|
+
if (node.binding.type === "binding:bareOf") {
|
|
335
|
+
return `<{${encodeNode(node.binding.source)}${key}${value}}`;
|
|
336
|
+
}
|
|
321
337
|
if (node.binding.type === "binding:valueIn") {
|
|
322
338
|
return `>{${encodeNode(node.binding.source)}${node.binding.value}$${key}${value}}`;
|
|
323
339
|
}
|
|
324
340
|
if (node.binding.type === "binding:keyValueIn") {
|
|
325
341
|
return `>{${encodeNode(node.binding.source)}${node.binding.key}$${node.binding.value}$${key}${value}}`;
|
|
326
342
|
}
|
|
327
|
-
return
|
|
343
|
+
return `<{${encodeNode(node.binding.source)}${node.binding.key}$${key}${value}}`;
|
|
344
|
+
}
|
|
345
|
+
function encodeWhileArrayComprehension(node) {
|
|
346
|
+
const cond = encodeNode(node.condition);
|
|
347
|
+
const body = addOptionalPrefix(encodeNode(node.body));
|
|
348
|
+
return `#[${cond}${body}]`;
|
|
349
|
+
}
|
|
350
|
+
function encodeWhileObjectComprehension(node) {
|
|
351
|
+
const cond = encodeNode(node.condition);
|
|
352
|
+
const key = addOptionalPrefix(encodeNode(node.key));
|
|
353
|
+
const value = addOptionalPrefix(encodeNode(node.value));
|
|
354
|
+
return `#{${cond}${key}${value}}`;
|
|
328
355
|
}
|
|
329
356
|
var activeEncodeOptions;
|
|
330
357
|
function encodeNode(node) {
|
|
@@ -334,7 +361,10 @@ function encodeNode(node) {
|
|
|
334
361
|
case "identifier": {
|
|
335
362
|
const domainRef = activeEncodeOptions?.domainRefs?.[node.name];
|
|
336
363
|
if (domainRef !== undefined)
|
|
337
|
-
return `${
|
|
364
|
+
return `${domainRef}'`;
|
|
365
|
+
const domainOpcode = activeEncodeOptions?.domainOpcodes?.[node.name];
|
|
366
|
+
if (domainOpcode !== undefined)
|
|
367
|
+
return `${domainOpcode}%`;
|
|
338
368
|
return `${node.name}$`;
|
|
339
369
|
}
|
|
340
370
|
case "self":
|
|
@@ -347,11 +377,11 @@ function encodeNode(node) {
|
|
|
347
377
|
return `${encodeUint(node.depth - 1)}@`;
|
|
348
378
|
}
|
|
349
379
|
case "boolean":
|
|
350
|
-
return node.value ? "
|
|
380
|
+
return node.value ? "tr'" : "fl'";
|
|
351
381
|
case "null":
|
|
352
|
-
return "
|
|
382
|
+
return "nl'";
|
|
353
383
|
case "undefined":
|
|
354
|
-
return "
|
|
384
|
+
return "un'";
|
|
355
385
|
case "number":
|
|
356
386
|
return encodeNumberNode(node);
|
|
357
387
|
case "string":
|
|
@@ -362,12 +392,16 @@ function encodeNode(node) {
|
|
|
362
392
|
}
|
|
363
393
|
case "arrayComprehension":
|
|
364
394
|
return encodeArrayComprehension(node);
|
|
395
|
+
case "whileArrayComprehension":
|
|
396
|
+
return encodeWhileArrayComprehension(node);
|
|
365
397
|
case "object": {
|
|
366
398
|
const body = node.entries.map(({ key, value }) => `${encodeNode(key)}${addOptionalPrefix(encodeNode(value))}`).join("");
|
|
367
399
|
return `{${body}}`;
|
|
368
400
|
}
|
|
369
401
|
case "objectComprehension":
|
|
370
402
|
return encodeObjectComprehension(node);
|
|
403
|
+
case "whileObjectComprehension":
|
|
404
|
+
return encodeWhileObjectComprehension(node);
|
|
371
405
|
case "key":
|
|
372
406
|
return encodeBareOrLengthString(node.name);
|
|
373
407
|
case "group":
|
|
@@ -377,6 +411,10 @@ function encodeNode(node) {
|
|
|
377
411
|
return `~${encodeNode(node.value)}`;
|
|
378
412
|
if (node.op === "neg")
|
|
379
413
|
return encodeCallParts([encodeOpcode("neg"), encodeNode(node.value)]);
|
|
414
|
+
if (node.op === "logicalNot") {
|
|
415
|
+
const val = encodeNode(node.value);
|
|
416
|
+
return `!(${val}tr')`;
|
|
417
|
+
}
|
|
380
418
|
return encodeCallParts([encodeOpcode("not"), encodeNode(node.value)]);
|
|
381
419
|
case "binary":
|
|
382
420
|
if (node.op === "and") {
|
|
@@ -395,12 +433,19 @@ function encodeNode(node) {
|
|
|
395
433
|
}).join("");
|
|
396
434
|
return `|(${body})`;
|
|
397
435
|
}
|
|
436
|
+
if (node.op === "nor") {
|
|
437
|
+
const left = encodeNode(node.left);
|
|
438
|
+
const right = addOptionalPrefix(encodeNode(node.right));
|
|
439
|
+
return `!(${left}${right})`;
|
|
440
|
+
}
|
|
398
441
|
return encodeCallParts([
|
|
399
442
|
encodeOpcode(BINARY_TO_OPCODE[node.op]),
|
|
400
443
|
encodeNode(node.left),
|
|
401
444
|
encodeNode(node.right)
|
|
402
445
|
]);
|
|
403
446
|
case "assign": {
|
|
447
|
+
if (node.op === ":=")
|
|
448
|
+
return `/${encodeNode(node.place)}${addOptionalPrefix(encodeNode(node.value))}`;
|
|
404
449
|
if (node.op === "=")
|
|
405
450
|
return `=${encodeNode(node.place)}${addOptionalPrefix(encodeNode(node.value))}`;
|
|
406
451
|
const opcode = ASSIGN_COMPOUND_TO_OPCODE[node.op];
|
|
@@ -411,8 +456,12 @@ function encodeNode(node) {
|
|
|
411
456
|
}
|
|
412
457
|
case "navigation":
|
|
413
458
|
return encodeNavigation(node);
|
|
414
|
-
case "call":
|
|
459
|
+
case "call": {
|
|
460
|
+
if (node.callee.type === "identifier" && KEYWORD_OPCODES.has(node.callee.name)) {
|
|
461
|
+
return encodeCallParts([encodeOpcode(node.callee.name), ...node.args.map((arg) => encodeNode(arg))]);
|
|
462
|
+
}
|
|
415
463
|
return encodeCallParts([encodeNode(node.callee), ...node.args.map((arg) => encodeNode(arg))]);
|
|
464
|
+
}
|
|
416
465
|
case "conditional": {
|
|
417
466
|
const opener = node.head === "when" ? "?(" : "!(";
|
|
418
467
|
const cond = encodeNode(node.condition);
|
|
@@ -420,6 +469,8 @@ function encodeNode(node) {
|
|
|
420
469
|
const elseExpr = node.elseBranch ? addOptionalPrefix(encodeConditionalElse(node.elseBranch)) : "";
|
|
421
470
|
return `${opener}${cond}${thenExpr}${elseExpr})`;
|
|
422
471
|
}
|
|
472
|
+
case "range":
|
|
473
|
+
return encodeCallParts([encodeOpcode("range"), encodeNode(node.from), encodeNode(node.to)]);
|
|
423
474
|
case "for":
|
|
424
475
|
return encodeFor(node);
|
|
425
476
|
case "while":
|
|
@@ -439,11 +490,30 @@ function collectLogicalChain(node, op) {
|
|
|
439
490
|
return [node];
|
|
440
491
|
return [...collectLogicalChain(node.left, op), ...collectLogicalChain(node.right, op)];
|
|
441
492
|
}
|
|
493
|
+
function formatParseError(source, match) {
|
|
494
|
+
const message = match.message ?? "Parse failed";
|
|
495
|
+
const pos = match.getRightmostFailurePosition?.();
|
|
496
|
+
if (typeof pos !== "number" || !Number.isFinite(pos))
|
|
497
|
+
return message;
|
|
498
|
+
const safePos = Math.max(0, Math.min(source.length, pos));
|
|
499
|
+
const lineStart = source.lastIndexOf(`
|
|
500
|
+
`, safePos - 1) + 1;
|
|
501
|
+
const lineEndIndex = source.indexOf(`
|
|
502
|
+
`, safePos);
|
|
503
|
+
const lineEnd = lineEndIndex === -1 ? source.length : lineEndIndex;
|
|
504
|
+
const lineText = source.slice(lineStart, lineEnd);
|
|
505
|
+
const lineNumber = source.slice(0, lineStart).split(`
|
|
506
|
+
`).length;
|
|
507
|
+
const columnNumber = safePos - lineStart + 1;
|
|
508
|
+
const caret = `${" ".repeat(Math.max(0, columnNumber - 1))}^`;
|
|
509
|
+
return `${message}
|
|
510
|
+
${lineText}
|
|
511
|
+
${caret}`;
|
|
512
|
+
}
|
|
442
513
|
function parseToIR(source) {
|
|
443
514
|
const match = grammar.match(source);
|
|
444
515
|
if (!match.succeeded()) {
|
|
445
|
-
|
|
446
|
-
throw new Error(failure.message ?? "Parse failed");
|
|
516
|
+
throw new Error(formatParseError(source, match));
|
|
447
517
|
}
|
|
448
518
|
return semantics(match).toIR();
|
|
449
519
|
}
|
|
@@ -595,78 +665,55 @@ function domainRefsFromConfig(config) {
|
|
|
595
665
|
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
596
666
|
throw new Error("Domain config must be an object");
|
|
597
667
|
}
|
|
598
|
-
const
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
return refs;
|
|
605
|
-
}
|
|
606
|
-
function decodeDomainRefKey(refText) {
|
|
607
|
-
if (!refText)
|
|
608
|
-
throw new Error("Domain ref key cannot be empty");
|
|
609
|
-
if (!/^[0-9A-Za-z_-]+$/.test(refText)) {
|
|
610
|
-
throw new Error(`Invalid domain ref key '${refText}' (must use base64 alphabet 0-9a-zA-Z-_)`);
|
|
611
|
-
}
|
|
612
|
-
if (refText.length > 1 && refText[0] === "0") {
|
|
613
|
-
throw new Error(`Invalid domain ref key '${refText}' (leading zeroes are not allowed)`);
|
|
668
|
+
const configObj = config;
|
|
669
|
+
const domainRefs = {};
|
|
670
|
+
const domainOpcodes = {};
|
|
671
|
+
const dataSection = configObj.data;
|
|
672
|
+
if (dataSection && typeof dataSection === "object" && !Array.isArray(dataSection)) {
|
|
673
|
+
mapConfigEntries(dataSection, domainRefs);
|
|
614
674
|
}
|
|
615
|
-
|
|
616
|
-
|
|
675
|
+
const functionsSection = configObj.functions;
|
|
676
|
+
if (functionsSection && typeof functionsSection === "object" && !Array.isArray(functionsSection)) {
|
|
677
|
+
mapConfigEntries(functionsSection, domainOpcodes);
|
|
617
678
|
}
|
|
618
|
-
|
|
619
|
-
for (const char of refText) {
|
|
620
|
-
const digit = DOMAIN_DIGIT_INDEX.get(char);
|
|
621
|
-
if (digit === undefined)
|
|
622
|
-
throw new Error(`Invalid domain ref key '${refText}'`);
|
|
623
|
-
value = value * 64 + digit;
|
|
624
|
-
if (value > Number.MAX_SAFE_INTEGER) {
|
|
625
|
-
throw new Error(`Invalid domain ref key '${refText}' (must fit in 53 bits)`);
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
if (value < FIRST_NON_RESERVED_REF) {
|
|
629
|
-
throw new Error(`Invalid domain ref key '${refText}' (maps to reserved id ${value})`);
|
|
630
|
-
}
|
|
631
|
-
return value;
|
|
679
|
+
return { domainRefs, domainOpcodes };
|
|
632
680
|
}
|
|
633
681
|
function mapConfigEntries(entries, refs) {
|
|
634
682
|
const sourceKindByRoot = new Map;
|
|
635
683
|
for (const root of Object.keys(refs)) {
|
|
636
684
|
sourceKindByRoot.set(root, "explicit");
|
|
637
685
|
}
|
|
638
|
-
for (const [
|
|
686
|
+
for (const [shortCode, rawEntry] of Object.entries(entries)) {
|
|
639
687
|
const entry = rawEntry;
|
|
640
688
|
if (!entry || typeof entry !== "object")
|
|
641
689
|
continue;
|
|
642
690
|
if (!Array.isArray(entry.names))
|
|
643
691
|
continue;
|
|
644
|
-
const refId = decodeDomainRefKey(refText);
|
|
645
692
|
for (const rawName of entry.names) {
|
|
646
693
|
if (typeof rawName !== "string")
|
|
647
694
|
continue;
|
|
648
|
-
const
|
|
649
|
-
if (
|
|
650
|
-
throw new Error(`Conflicting refs for '${rawName}': ${
|
|
695
|
+
const existingRef = refs[rawName];
|
|
696
|
+
if (existingRef !== undefined && existingRef !== shortCode) {
|
|
697
|
+
throw new Error(`Conflicting refs for '${rawName}': ${existingRef} vs ${shortCode}`);
|
|
651
698
|
}
|
|
652
|
-
refs[rawName] =
|
|
699
|
+
refs[rawName] = shortCode;
|
|
653
700
|
const root = rawName.split(".")[0];
|
|
654
701
|
if (!root)
|
|
655
702
|
continue;
|
|
656
703
|
const currentKind = rawName.includes(".") ? "implicit" : "explicit";
|
|
657
704
|
const existing = refs[root];
|
|
658
705
|
if (existing !== undefined) {
|
|
659
|
-
if (existing ===
|
|
706
|
+
if (existing === shortCode)
|
|
660
707
|
continue;
|
|
661
708
|
const existingKind = sourceKindByRoot.get(root) ?? "explicit";
|
|
662
709
|
if (currentKind === "explicit") {
|
|
663
|
-
throw new Error(`Conflicting refs for '${root}': ${existing} vs ${
|
|
710
|
+
throw new Error(`Conflicting refs for '${root}': ${existing} vs ${shortCode}`);
|
|
664
711
|
}
|
|
665
712
|
if (existingKind === "explicit")
|
|
666
713
|
continue;
|
|
667
714
|
continue;
|
|
668
715
|
}
|
|
669
|
-
refs[root] =
|
|
716
|
+
refs[root] = shortCode;
|
|
670
717
|
sourceKindByRoot.set(root, currentKind);
|
|
671
718
|
}
|
|
672
719
|
}
|
|
@@ -812,20 +859,11 @@ function gatherEncodedValueSpans(text) {
|
|
|
812
859
|
}
|
|
813
860
|
return spans;
|
|
814
861
|
}
|
|
815
|
-
function buildPointerToken(pointerStart, targetStart) {
|
|
816
|
-
|
|
862
|
+
function buildPointerToken(pointerStart, targetStart, occurrenceSize) {
|
|
863
|
+
const offset = targetStart - pointerStart - occurrenceSize;
|
|
817
864
|
if (offset < 0)
|
|
818
865
|
return;
|
|
819
|
-
|
|
820
|
-
const prefix = encodeUint(offset);
|
|
821
|
-
const recalculated = targetStart - (pointerStart + prefix.length + 1);
|
|
822
|
-
if (recalculated === offset)
|
|
823
|
-
return `${prefix}^`;
|
|
824
|
-
offset = recalculated;
|
|
825
|
-
if (offset < 0)
|
|
826
|
-
return;
|
|
827
|
-
}
|
|
828
|
-
return;
|
|
866
|
+
return `${encodeUint(offset)}^`;
|
|
829
867
|
}
|
|
830
868
|
function buildDedupeCandidateTable(encoded, minBytes) {
|
|
831
869
|
const spans = gatherEncodedValueSpans(encoded);
|
|
@@ -867,7 +905,7 @@ function dedupeLargeEncodedValues(encoded, minBytes = 4) {
|
|
|
867
905
|
if (current.slice(occurrence.span.start, occurrence.span.end) !== value)
|
|
868
906
|
continue;
|
|
869
907
|
const canonicalCurrentStart = current.length - canonical.offsetFromEnd - canonical.sizeBytes;
|
|
870
|
-
const pointerToken = buildPointerToken(occurrence.span.start, canonicalCurrentStart);
|
|
908
|
+
const pointerToken = buildPointerToken(occurrence.span.start, canonicalCurrentStart, occurrence.sizeBytes);
|
|
871
909
|
if (!pointerToken)
|
|
872
910
|
continue;
|
|
873
911
|
if (pointerToken.length >= occurrence.sizeBytes)
|
|
@@ -896,819 +934,8 @@ function encodeIR(node, options) {
|
|
|
896
934
|
activeEncodeOptions = previous;
|
|
897
935
|
}
|
|
898
936
|
}
|
|
899
|
-
function cloneNode(node) {
|
|
900
|
-
return structuredClone(node);
|
|
901
|
-
}
|
|
902
|
-
function emptyOptimizeEnv() {
|
|
903
|
-
return { constants: {}, selfCaptures: {} };
|
|
904
|
-
}
|
|
905
|
-
function cloneOptimizeEnv(env) {
|
|
906
|
-
return {
|
|
907
|
-
constants: { ...env.constants },
|
|
908
|
-
selfCaptures: { ...env.selfCaptures }
|
|
909
|
-
};
|
|
910
|
-
}
|
|
911
|
-
function clearOptimizeEnv(env) {
|
|
912
|
-
for (const key of Object.keys(env.constants))
|
|
913
|
-
delete env.constants[key];
|
|
914
|
-
for (const key of Object.keys(env.selfCaptures))
|
|
915
|
-
delete env.selfCaptures[key];
|
|
916
|
-
}
|
|
917
|
-
function clearBinding(env, name) {
|
|
918
|
-
delete env.constants[name];
|
|
919
|
-
delete env.selfCaptures[name];
|
|
920
|
-
}
|
|
921
|
-
function selfTargetFromNode(node, currentDepth) {
|
|
922
|
-
if (node.type === "self")
|
|
923
|
-
return currentDepth;
|
|
924
|
-
if (node.type === "selfDepth") {
|
|
925
|
-
const target = currentDepth - (node.depth - 1);
|
|
926
|
-
if (target >= 1)
|
|
927
|
-
return target;
|
|
928
|
-
}
|
|
929
|
-
return;
|
|
930
|
-
}
|
|
931
|
-
function selfNodeFromTarget(targetDepth, currentDepth) {
|
|
932
|
-
const relDepth = currentDepth - targetDepth + 1;
|
|
933
|
-
if (!Number.isInteger(relDepth) || relDepth < 1)
|
|
934
|
-
return;
|
|
935
|
-
if (relDepth === 1)
|
|
936
|
-
return { type: "self" };
|
|
937
|
-
return { type: "selfDepth", depth: relDepth };
|
|
938
|
-
}
|
|
939
|
-
function dropBindingNames(env, binding) {
|
|
940
|
-
if (binding.type === "binding:valueIn") {
|
|
941
|
-
clearBinding(env, binding.value);
|
|
942
|
-
return;
|
|
943
|
-
}
|
|
944
|
-
if (binding.type === "binding:keyValueIn") {
|
|
945
|
-
clearBinding(env, binding.key);
|
|
946
|
-
clearBinding(env, binding.value);
|
|
947
|
-
return;
|
|
948
|
-
}
|
|
949
|
-
if (binding.type === "binding:keyOf") {
|
|
950
|
-
clearBinding(env, binding.key);
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
function collectReads(node, out) {
|
|
954
|
-
switch (node.type) {
|
|
955
|
-
case "identifier":
|
|
956
|
-
out.add(node.name);
|
|
957
|
-
return;
|
|
958
|
-
case "group":
|
|
959
|
-
collectReads(node.expression, out);
|
|
960
|
-
return;
|
|
961
|
-
case "array":
|
|
962
|
-
for (const item of node.items)
|
|
963
|
-
collectReads(item, out);
|
|
964
|
-
return;
|
|
965
|
-
case "object":
|
|
966
|
-
for (const entry of node.entries) {
|
|
967
|
-
collectReads(entry.key, out);
|
|
968
|
-
collectReads(entry.value, out);
|
|
969
|
-
}
|
|
970
|
-
return;
|
|
971
|
-
case "arrayComprehension":
|
|
972
|
-
collectReads(node.binding.source, out);
|
|
973
|
-
collectReads(node.body, out);
|
|
974
|
-
return;
|
|
975
|
-
case "objectComprehension":
|
|
976
|
-
collectReads(node.binding.source, out);
|
|
977
|
-
collectReads(node.key, out);
|
|
978
|
-
collectReads(node.value, out);
|
|
979
|
-
return;
|
|
980
|
-
case "unary":
|
|
981
|
-
collectReads(node.value, out);
|
|
982
|
-
return;
|
|
983
|
-
case "binary":
|
|
984
|
-
collectReads(node.left, out);
|
|
985
|
-
collectReads(node.right, out);
|
|
986
|
-
return;
|
|
987
|
-
case "assign":
|
|
988
|
-
if (!(node.op === "=" && node.place.type === "identifier"))
|
|
989
|
-
collectReads(node.place, out);
|
|
990
|
-
collectReads(node.value, out);
|
|
991
|
-
return;
|
|
992
|
-
case "navigation":
|
|
993
|
-
collectReads(node.target, out);
|
|
994
|
-
for (const segment of node.segments) {
|
|
995
|
-
if (segment.type === "dynamic")
|
|
996
|
-
collectReads(segment.key, out);
|
|
997
|
-
}
|
|
998
|
-
return;
|
|
999
|
-
case "call":
|
|
1000
|
-
collectReads(node.callee, out);
|
|
1001
|
-
for (const arg of node.args)
|
|
1002
|
-
collectReads(arg, out);
|
|
1003
|
-
return;
|
|
1004
|
-
case "conditional":
|
|
1005
|
-
collectReads(node.condition, out);
|
|
1006
|
-
for (const part of node.thenBlock)
|
|
1007
|
-
collectReads(part, out);
|
|
1008
|
-
if (node.elseBranch)
|
|
1009
|
-
collectReadsElse(node.elseBranch, out);
|
|
1010
|
-
return;
|
|
1011
|
-
case "for":
|
|
1012
|
-
collectReads(node.binding.source, out);
|
|
1013
|
-
for (const part of node.body)
|
|
1014
|
-
collectReads(part, out);
|
|
1015
|
-
return;
|
|
1016
|
-
case "program":
|
|
1017
|
-
for (const part of node.body)
|
|
1018
|
-
collectReads(part, out);
|
|
1019
|
-
return;
|
|
1020
|
-
default:
|
|
1021
|
-
return;
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
function collectReadsElse(elseBranch, out) {
|
|
1025
|
-
if (elseBranch.type === "else") {
|
|
1026
|
-
for (const part of elseBranch.block)
|
|
1027
|
-
collectReads(part, out);
|
|
1028
|
-
return;
|
|
1029
|
-
}
|
|
1030
|
-
collectReads(elseBranch.condition, out);
|
|
1031
|
-
for (const part of elseBranch.thenBlock)
|
|
1032
|
-
collectReads(part, out);
|
|
1033
|
-
if (elseBranch.elseBranch)
|
|
1034
|
-
collectReadsElse(elseBranch.elseBranch, out);
|
|
1035
|
-
}
|
|
1036
|
-
function isPureNode(node) {
|
|
1037
|
-
switch (node.type) {
|
|
1038
|
-
case "identifier":
|
|
1039
|
-
case "self":
|
|
1040
|
-
case "selfDepth":
|
|
1041
|
-
case "boolean":
|
|
1042
|
-
case "null":
|
|
1043
|
-
case "undefined":
|
|
1044
|
-
case "number":
|
|
1045
|
-
case "string":
|
|
1046
|
-
case "key":
|
|
1047
|
-
return true;
|
|
1048
|
-
case "group":
|
|
1049
|
-
return isPureNode(node.expression);
|
|
1050
|
-
case "array":
|
|
1051
|
-
return node.items.every((item) => isPureNode(item));
|
|
1052
|
-
case "object":
|
|
1053
|
-
return node.entries.every((entry) => isPureNode(entry.key) && isPureNode(entry.value));
|
|
1054
|
-
case "navigation":
|
|
1055
|
-
return isPureNode(node.target) && node.segments.every((segment) => segment.type === "static" || isPureNode(segment.key));
|
|
1056
|
-
case "unary":
|
|
1057
|
-
return node.op !== "delete" && isPureNode(node.value);
|
|
1058
|
-
case "binary":
|
|
1059
|
-
return isPureNode(node.left) && isPureNode(node.right);
|
|
1060
|
-
default:
|
|
1061
|
-
return false;
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
|
-
function eliminateDeadAssignments(block) {
|
|
1065
|
-
const needed = new Set;
|
|
1066
|
-
const out = [];
|
|
1067
|
-
for (let index = block.length - 1;index >= 0; index -= 1) {
|
|
1068
|
-
const node = block[index];
|
|
1069
|
-
if (node.type === "conditional") {
|
|
1070
|
-
let rewritten = node;
|
|
1071
|
-
if (node.condition.type === "assign" && node.condition.op === "=" && node.condition.place.type === "identifier") {
|
|
1072
|
-
const name = node.condition.place.name;
|
|
1073
|
-
const branchReads = new Set;
|
|
1074
|
-
for (const part of node.thenBlock)
|
|
1075
|
-
collectReads(part, branchReads);
|
|
1076
|
-
if (node.elseBranch)
|
|
1077
|
-
collectReadsElse(node.elseBranch, branchReads);
|
|
1078
|
-
if (!needed.has(name) && !branchReads.has(name)) {
|
|
1079
|
-
rewritten = {
|
|
1080
|
-
type: "conditional",
|
|
1081
|
-
head: node.head,
|
|
1082
|
-
condition: node.condition.value,
|
|
1083
|
-
thenBlock: node.thenBlock,
|
|
1084
|
-
elseBranch: node.elseBranch
|
|
1085
|
-
};
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
|
-
collectReads(rewritten, needed);
|
|
1089
|
-
out.push(rewritten);
|
|
1090
|
-
continue;
|
|
1091
|
-
}
|
|
1092
|
-
if (node.type === "assign" && node.op === "=" && node.place.type === "identifier") {
|
|
1093
|
-
collectReads(node.value, needed);
|
|
1094
|
-
const name = node.place.name;
|
|
1095
|
-
const canDrop = !needed.has(name) && isPureNode(node.value);
|
|
1096
|
-
needed.delete(name);
|
|
1097
|
-
if (canDrop)
|
|
1098
|
-
continue;
|
|
1099
|
-
out.push(node);
|
|
1100
|
-
continue;
|
|
1101
|
-
}
|
|
1102
|
-
collectReads(node, needed);
|
|
1103
|
-
out.push(node);
|
|
1104
|
-
}
|
|
1105
|
-
out.reverse();
|
|
1106
|
-
return out;
|
|
1107
|
-
}
|
|
1108
|
-
function hasIdentifierRead(node, name, asPlace = false) {
|
|
1109
|
-
if (node.type === "identifier")
|
|
1110
|
-
return !asPlace && node.name === name;
|
|
1111
|
-
switch (node.type) {
|
|
1112
|
-
case "group":
|
|
1113
|
-
return hasIdentifierRead(node.expression, name);
|
|
1114
|
-
case "array":
|
|
1115
|
-
return node.items.some((item) => hasIdentifierRead(item, name));
|
|
1116
|
-
case "object":
|
|
1117
|
-
return node.entries.some((entry) => hasIdentifierRead(entry.key, name) || hasIdentifierRead(entry.value, name));
|
|
1118
|
-
case "navigation":
|
|
1119
|
-
return hasIdentifierRead(node.target, name) || node.segments.some((segment) => segment.type === "dynamic" && hasIdentifierRead(segment.key, name));
|
|
1120
|
-
case "unary":
|
|
1121
|
-
return hasIdentifierRead(node.value, name, node.op === "delete");
|
|
1122
|
-
case "binary":
|
|
1123
|
-
return hasIdentifierRead(node.left, name) || hasIdentifierRead(node.right, name);
|
|
1124
|
-
case "assign":
|
|
1125
|
-
return hasIdentifierRead(node.place, name, true) || hasIdentifierRead(node.value, name);
|
|
1126
|
-
default:
|
|
1127
|
-
return false;
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
function countIdentifierReads(node, name, asPlace = false) {
|
|
1131
|
-
if (node.type === "identifier")
|
|
1132
|
-
return !asPlace && node.name === name ? 1 : 0;
|
|
1133
|
-
switch (node.type) {
|
|
1134
|
-
case "group":
|
|
1135
|
-
return countIdentifierReads(node.expression, name);
|
|
1136
|
-
case "array":
|
|
1137
|
-
return node.items.reduce((sum, item) => sum + countIdentifierReads(item, name), 0);
|
|
1138
|
-
case "object":
|
|
1139
|
-
return node.entries.reduce((sum, entry) => sum + countIdentifierReads(entry.key, name) + countIdentifierReads(entry.value, name), 0);
|
|
1140
|
-
case "navigation":
|
|
1141
|
-
return countIdentifierReads(node.target, name) + node.segments.reduce((sum, segment) => sum + (segment.type === "dynamic" ? countIdentifierReads(segment.key, name) : 0), 0);
|
|
1142
|
-
case "unary":
|
|
1143
|
-
return countIdentifierReads(node.value, name, node.op === "delete");
|
|
1144
|
-
case "binary":
|
|
1145
|
-
return countIdentifierReads(node.left, name) + countIdentifierReads(node.right, name);
|
|
1146
|
-
case "assign":
|
|
1147
|
-
return countIdentifierReads(node.place, name, true) + countIdentifierReads(node.value, name);
|
|
1148
|
-
default:
|
|
1149
|
-
return 0;
|
|
1150
|
-
}
|
|
1151
|
-
}
|
|
1152
|
-
function replaceIdentifier(node, name, replacement, asPlace = false) {
|
|
1153
|
-
if (node.type === "identifier") {
|
|
1154
|
-
if (!asPlace && node.name === name)
|
|
1155
|
-
return cloneNode(replacement);
|
|
1156
|
-
return node;
|
|
1157
|
-
}
|
|
1158
|
-
switch (node.type) {
|
|
1159
|
-
case "group":
|
|
1160
|
-
return {
|
|
1161
|
-
type: "group",
|
|
1162
|
-
expression: replaceIdentifier(node.expression, name, replacement)
|
|
1163
|
-
};
|
|
1164
|
-
case "array":
|
|
1165
|
-
return { type: "array", items: node.items.map((item) => replaceIdentifier(item, name, replacement)) };
|
|
1166
|
-
case "object":
|
|
1167
|
-
return {
|
|
1168
|
-
type: "object",
|
|
1169
|
-
entries: node.entries.map((entry) => ({
|
|
1170
|
-
key: replaceIdentifier(entry.key, name, replacement),
|
|
1171
|
-
value: replaceIdentifier(entry.value, name, replacement)
|
|
1172
|
-
}))
|
|
1173
|
-
};
|
|
1174
|
-
case "navigation":
|
|
1175
|
-
return {
|
|
1176
|
-
type: "navigation",
|
|
1177
|
-
target: replaceIdentifier(node.target, name, replacement),
|
|
1178
|
-
segments: node.segments.map((segment) => segment.type === "static" ? segment : { type: "dynamic", key: replaceIdentifier(segment.key, name, replacement) })
|
|
1179
|
-
};
|
|
1180
|
-
case "unary":
|
|
1181
|
-
return {
|
|
1182
|
-
type: "unary",
|
|
1183
|
-
op: node.op,
|
|
1184
|
-
value: replaceIdentifier(node.value, name, replacement, node.op === "delete")
|
|
1185
|
-
};
|
|
1186
|
-
case "binary":
|
|
1187
|
-
return {
|
|
1188
|
-
type: "binary",
|
|
1189
|
-
op: node.op,
|
|
1190
|
-
left: replaceIdentifier(node.left, name, replacement),
|
|
1191
|
-
right: replaceIdentifier(node.right, name, replacement)
|
|
1192
|
-
};
|
|
1193
|
-
case "assign":
|
|
1194
|
-
return {
|
|
1195
|
-
type: "assign",
|
|
1196
|
-
op: node.op,
|
|
1197
|
-
place: replaceIdentifier(node.place, name, replacement, true),
|
|
1198
|
-
value: replaceIdentifier(node.value, name, replacement)
|
|
1199
|
-
};
|
|
1200
|
-
default:
|
|
1201
|
-
return node;
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
function isSafeInlineTargetNode(node) {
|
|
1205
|
-
if (isPureNode(node))
|
|
1206
|
-
return true;
|
|
1207
|
-
if (node.type === "assign" && node.op === "=") {
|
|
1208
|
-
return isPureNode(node.place) && isPureNode(node.value);
|
|
1209
|
-
}
|
|
1210
|
-
return false;
|
|
1211
|
-
}
|
|
1212
|
-
function inlineAdjacentPureAssignments(block) {
|
|
1213
|
-
const out = [...block];
|
|
1214
|
-
let changed = true;
|
|
1215
|
-
while (changed) {
|
|
1216
|
-
changed = false;
|
|
1217
|
-
for (let index = 0;index < out.length - 1; index += 1) {
|
|
1218
|
-
const current = out[index];
|
|
1219
|
-
if (current.type !== "assign" || current.op !== "=" || current.place.type !== "identifier")
|
|
1220
|
-
continue;
|
|
1221
|
-
if (!isPureNode(current.value))
|
|
1222
|
-
continue;
|
|
1223
|
-
const name = current.place.name;
|
|
1224
|
-
if (hasIdentifierRead(current.value, name))
|
|
1225
|
-
continue;
|
|
1226
|
-
const next = out[index + 1];
|
|
1227
|
-
if (!isSafeInlineTargetNode(next))
|
|
1228
|
-
continue;
|
|
1229
|
-
if (countIdentifierReads(next, name) !== 1)
|
|
1230
|
-
continue;
|
|
1231
|
-
out[index + 1] = replaceIdentifier(next, name, current.value);
|
|
1232
|
-
out.splice(index, 1);
|
|
1233
|
-
changed = true;
|
|
1234
|
-
break;
|
|
1235
|
-
}
|
|
1236
|
-
}
|
|
1237
|
-
return out;
|
|
1238
|
-
}
|
|
1239
|
-
function toNumberNode(value) {
|
|
1240
|
-
let raw;
|
|
1241
|
-
if (Number.isNaN(value))
|
|
1242
|
-
raw = "nan";
|
|
1243
|
-
else if (value === Infinity)
|
|
1244
|
-
raw = "inf";
|
|
1245
|
-
else if (value === -Infinity)
|
|
1246
|
-
raw = "-inf";
|
|
1247
|
-
else
|
|
1248
|
-
raw = String(value);
|
|
1249
|
-
return { type: "number", raw, value };
|
|
1250
|
-
}
|
|
1251
|
-
function toStringNode(value) {
|
|
1252
|
-
return { type: "string", raw: JSON.stringify(value) };
|
|
1253
|
-
}
|
|
1254
|
-
function toLiteralNode(value) {
|
|
1255
|
-
if (value === undefined)
|
|
1256
|
-
return { type: "undefined" };
|
|
1257
|
-
if (value === null)
|
|
1258
|
-
return { type: "null" };
|
|
1259
|
-
if (typeof value === "boolean")
|
|
1260
|
-
return { type: "boolean", value };
|
|
1261
|
-
if (typeof value === "number")
|
|
1262
|
-
return toNumberNode(value);
|
|
1263
|
-
if (typeof value === "string")
|
|
1264
|
-
return toStringNode(value);
|
|
1265
|
-
if (Array.isArray(value)) {
|
|
1266
|
-
const items = [];
|
|
1267
|
-
for (const item of value) {
|
|
1268
|
-
const lowered = toLiteralNode(item);
|
|
1269
|
-
if (!lowered)
|
|
1270
|
-
return;
|
|
1271
|
-
items.push(lowered);
|
|
1272
|
-
}
|
|
1273
|
-
return { type: "array", items };
|
|
1274
|
-
}
|
|
1275
|
-
if (value && typeof value === "object") {
|
|
1276
|
-
const entries = [];
|
|
1277
|
-
for (const [key, entryValue] of Object.entries(value)) {
|
|
1278
|
-
const loweredValue = toLiteralNode(entryValue);
|
|
1279
|
-
if (!loweredValue)
|
|
1280
|
-
return;
|
|
1281
|
-
entries.push({ key: { type: "key", name: key }, value: loweredValue });
|
|
1282
|
-
}
|
|
1283
|
-
return { type: "object", entries };
|
|
1284
|
-
}
|
|
1285
|
-
return;
|
|
1286
|
-
}
|
|
1287
|
-
function constValue(node) {
|
|
1288
|
-
switch (node.type) {
|
|
1289
|
-
case "undefined":
|
|
1290
|
-
return;
|
|
1291
|
-
case "null":
|
|
1292
|
-
return null;
|
|
1293
|
-
case "boolean":
|
|
1294
|
-
return node.value;
|
|
1295
|
-
case "number":
|
|
1296
|
-
return node.value;
|
|
1297
|
-
case "string":
|
|
1298
|
-
return decodeStringLiteral(node.raw);
|
|
1299
|
-
case "key":
|
|
1300
|
-
return node.name;
|
|
1301
|
-
case "array": {
|
|
1302
|
-
const out = [];
|
|
1303
|
-
for (const item of node.items) {
|
|
1304
|
-
const value = constValue(item);
|
|
1305
|
-
if (value === undefined && item.type !== "undefined")
|
|
1306
|
-
return;
|
|
1307
|
-
out.push(value);
|
|
1308
|
-
}
|
|
1309
|
-
return out;
|
|
1310
|
-
}
|
|
1311
|
-
case "object": {
|
|
1312
|
-
const out = {};
|
|
1313
|
-
for (const entry of node.entries) {
|
|
1314
|
-
const key = constValue(entry.key);
|
|
1315
|
-
if (key === undefined && entry.key.type !== "undefined")
|
|
1316
|
-
return;
|
|
1317
|
-
const value = constValue(entry.value);
|
|
1318
|
-
if (value === undefined && entry.value.type !== "undefined")
|
|
1319
|
-
return;
|
|
1320
|
-
out[String(key)] = value;
|
|
1321
|
-
}
|
|
1322
|
-
return out;
|
|
1323
|
-
}
|
|
1324
|
-
default:
|
|
1325
|
-
return;
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1328
|
-
function isDefinedValue(value) {
|
|
1329
|
-
return value !== undefined;
|
|
1330
|
-
}
|
|
1331
|
-
function foldUnary(op, value) {
|
|
1332
|
-
if (op === "neg") {
|
|
1333
|
-
if (typeof value !== "number")
|
|
1334
|
-
return;
|
|
1335
|
-
return -value;
|
|
1336
|
-
}
|
|
1337
|
-
if (op === "not") {
|
|
1338
|
-
if (typeof value === "boolean")
|
|
1339
|
-
return !value;
|
|
1340
|
-
if (typeof value === "number")
|
|
1341
|
-
return ~value;
|
|
1342
|
-
return;
|
|
1343
|
-
}
|
|
1344
|
-
return;
|
|
1345
|
-
}
|
|
1346
|
-
function foldBinary(op, left, right) {
|
|
1347
|
-
if (op === "add" || op === "sub" || op === "mul" || op === "div" || op === "mod") {
|
|
1348
|
-
if (typeof left !== "number" || typeof right !== "number")
|
|
1349
|
-
return;
|
|
1350
|
-
if (op === "add")
|
|
1351
|
-
return left + right;
|
|
1352
|
-
if (op === "sub")
|
|
1353
|
-
return left - right;
|
|
1354
|
-
if (op === "mul")
|
|
1355
|
-
return left * right;
|
|
1356
|
-
if (op === "div")
|
|
1357
|
-
return left / right;
|
|
1358
|
-
return left % right;
|
|
1359
|
-
}
|
|
1360
|
-
if (op === "bitAnd" || op === "bitOr" || op === "bitXor") {
|
|
1361
|
-
if (typeof left !== "number" || typeof right !== "number")
|
|
1362
|
-
return;
|
|
1363
|
-
if (op === "bitAnd")
|
|
1364
|
-
return left & right;
|
|
1365
|
-
if (op === "bitOr")
|
|
1366
|
-
return left | right;
|
|
1367
|
-
return left ^ right;
|
|
1368
|
-
}
|
|
1369
|
-
if (op === "eq")
|
|
1370
|
-
return left === right ? left : undefined;
|
|
1371
|
-
if (op === "neq")
|
|
1372
|
-
return left !== right ? left : undefined;
|
|
1373
|
-
if (op === "gt" || op === "gte" || op === "lt" || op === "lte") {
|
|
1374
|
-
if (typeof left !== "number" || typeof right !== "number")
|
|
1375
|
-
return;
|
|
1376
|
-
if (op === "gt")
|
|
1377
|
-
return left > right ? left : undefined;
|
|
1378
|
-
if (op === "gte")
|
|
1379
|
-
return left >= right ? left : undefined;
|
|
1380
|
-
if (op === "lt")
|
|
1381
|
-
return left < right ? left : undefined;
|
|
1382
|
-
return left <= right ? left : undefined;
|
|
1383
|
-
}
|
|
1384
|
-
if (op === "and")
|
|
1385
|
-
return isDefinedValue(left) ? right : undefined;
|
|
1386
|
-
if (op === "or")
|
|
1387
|
-
return isDefinedValue(left) ? left : right;
|
|
1388
|
-
return;
|
|
1389
|
-
}
|
|
1390
|
-
function optimizeElse(elseBranch, env, currentDepth) {
|
|
1391
|
-
if (!elseBranch)
|
|
1392
|
-
return;
|
|
1393
|
-
if (elseBranch.type === "else") {
|
|
1394
|
-
return { type: "else", block: optimizeBlock(elseBranch.block, cloneOptimizeEnv(env), currentDepth) };
|
|
1395
|
-
}
|
|
1396
|
-
const optimizedCondition = optimizeNode(elseBranch.condition, env, currentDepth);
|
|
1397
|
-
const foldedCondition = constValue(optimizedCondition);
|
|
1398
|
-
if (foldedCondition !== undefined || optimizedCondition.type === "undefined") {
|
|
1399
|
-
const passes = elseBranch.head === "when" ? isDefinedValue(foldedCondition) : !isDefinedValue(foldedCondition);
|
|
1400
|
-
if (passes) {
|
|
1401
|
-
return {
|
|
1402
|
-
type: "else",
|
|
1403
|
-
block: optimizeBlock(elseBranch.thenBlock, cloneOptimizeEnv(env), currentDepth)
|
|
1404
|
-
};
|
|
1405
|
-
}
|
|
1406
|
-
return optimizeElse(elseBranch.elseBranch, env, currentDepth);
|
|
1407
|
-
}
|
|
1408
|
-
return {
|
|
1409
|
-
type: "elseChain",
|
|
1410
|
-
head: elseBranch.head,
|
|
1411
|
-
condition: optimizedCondition,
|
|
1412
|
-
thenBlock: optimizeBlock(elseBranch.thenBlock, cloneOptimizeEnv(env), currentDepth),
|
|
1413
|
-
elseBranch: optimizeElse(elseBranch.elseBranch, cloneOptimizeEnv(env), currentDepth)
|
|
1414
|
-
};
|
|
1415
|
-
}
|
|
1416
|
-
function optimizeBlock(block, env, currentDepth) {
|
|
1417
|
-
const out = [];
|
|
1418
|
-
for (const node of block) {
|
|
1419
|
-
const optimized = optimizeNode(node, env, currentDepth);
|
|
1420
|
-
out.push(optimized);
|
|
1421
|
-
if (optimized.type === "break" || optimized.type === "continue")
|
|
1422
|
-
break;
|
|
1423
|
-
if (optimized.type === "assign" && optimized.op === "=" && optimized.place.type === "identifier") {
|
|
1424
|
-
const selfTarget = selfTargetFromNode(optimized.value, currentDepth);
|
|
1425
|
-
if (selfTarget !== undefined) {
|
|
1426
|
-
env.selfCaptures[optimized.place.name] = selfTarget;
|
|
1427
|
-
delete env.constants[optimized.place.name];
|
|
1428
|
-
continue;
|
|
1429
|
-
}
|
|
1430
|
-
const folded = constValue(optimized.value);
|
|
1431
|
-
if (folded !== undefined || optimized.value.type === "undefined") {
|
|
1432
|
-
env.constants[optimized.place.name] = cloneNode(optimized.value);
|
|
1433
|
-
delete env.selfCaptures[optimized.place.name];
|
|
1434
|
-
} else {
|
|
1435
|
-
clearBinding(env, optimized.place.name);
|
|
1436
|
-
}
|
|
1437
|
-
continue;
|
|
1438
|
-
}
|
|
1439
|
-
if (optimized.type === "unary" && optimized.op === "delete" && optimized.value.type === "identifier") {
|
|
1440
|
-
clearBinding(env, optimized.value.name);
|
|
1441
|
-
continue;
|
|
1442
|
-
}
|
|
1443
|
-
if (optimized.type === "assign" && optimized.place.type === "identifier") {
|
|
1444
|
-
clearBinding(env, optimized.place.name);
|
|
1445
|
-
continue;
|
|
1446
|
-
}
|
|
1447
|
-
if (optimized.type === "assign" || optimized.type === "for" || optimized.type === "call") {
|
|
1448
|
-
clearOptimizeEnv(env);
|
|
1449
|
-
}
|
|
1450
|
-
}
|
|
1451
|
-
return inlineAdjacentPureAssignments(eliminateDeadAssignments(out));
|
|
1452
|
-
}
|
|
1453
|
-
function optimizeNode(node, env, currentDepth, asPlace = false) {
|
|
1454
|
-
switch (node.type) {
|
|
1455
|
-
case "program": {
|
|
1456
|
-
const body = optimizeBlock(node.body, cloneOptimizeEnv(env), currentDepth);
|
|
1457
|
-
if (body.length === 0)
|
|
1458
|
-
return { type: "undefined" };
|
|
1459
|
-
if (body.length === 1)
|
|
1460
|
-
return body[0];
|
|
1461
|
-
return { type: "program", body };
|
|
1462
|
-
}
|
|
1463
|
-
case "identifier": {
|
|
1464
|
-
if (asPlace)
|
|
1465
|
-
return node;
|
|
1466
|
-
const selfTarget = env.selfCaptures[node.name];
|
|
1467
|
-
if (selfTarget !== undefined) {
|
|
1468
|
-
const rewritten = selfNodeFromTarget(selfTarget, currentDepth);
|
|
1469
|
-
if (rewritten)
|
|
1470
|
-
return rewritten;
|
|
1471
|
-
}
|
|
1472
|
-
const replacement = env.constants[node.name];
|
|
1473
|
-
return replacement ? cloneNode(replacement) : node;
|
|
1474
|
-
}
|
|
1475
|
-
case "group": {
|
|
1476
|
-
return optimizeNode(node.expression, env, currentDepth);
|
|
1477
|
-
}
|
|
1478
|
-
case "array": {
|
|
1479
|
-
return { type: "array", items: node.items.map((item) => optimizeNode(item, env, currentDepth)) };
|
|
1480
|
-
}
|
|
1481
|
-
case "object": {
|
|
1482
|
-
return {
|
|
1483
|
-
type: "object",
|
|
1484
|
-
entries: node.entries.map((entry) => ({
|
|
1485
|
-
key: optimizeNode(entry.key, env, currentDepth),
|
|
1486
|
-
value: optimizeNode(entry.value, env, currentDepth)
|
|
1487
|
-
}))
|
|
1488
|
-
};
|
|
1489
|
-
}
|
|
1490
|
-
case "unary": {
|
|
1491
|
-
const value = optimizeNode(node.value, env, currentDepth, node.op === "delete");
|
|
1492
|
-
const foldedValue = constValue(value);
|
|
1493
|
-
if (foldedValue !== undefined || value.type === "undefined") {
|
|
1494
|
-
const folded = foldUnary(node.op, foldedValue);
|
|
1495
|
-
const literal = folded === undefined ? undefined : toLiteralNode(folded);
|
|
1496
|
-
if (literal)
|
|
1497
|
-
return literal;
|
|
1498
|
-
}
|
|
1499
|
-
return { type: "unary", op: node.op, value };
|
|
1500
|
-
}
|
|
1501
|
-
case "binary": {
|
|
1502
|
-
const left = optimizeNode(node.left, env, currentDepth);
|
|
1503
|
-
const right = optimizeNode(node.right, env, currentDepth);
|
|
1504
|
-
const leftValue = constValue(left);
|
|
1505
|
-
const rightValue = constValue(right);
|
|
1506
|
-
if ((leftValue !== undefined || left.type === "undefined") && (rightValue !== undefined || right.type === "undefined")) {
|
|
1507
|
-
const folded = foldBinary(node.op, leftValue, rightValue);
|
|
1508
|
-
const literal = folded === undefined ? undefined : toLiteralNode(folded);
|
|
1509
|
-
if (literal)
|
|
1510
|
-
return literal;
|
|
1511
|
-
}
|
|
1512
|
-
return { type: "binary", op: node.op, left, right };
|
|
1513
|
-
}
|
|
1514
|
-
case "navigation": {
|
|
1515
|
-
const target = optimizeNode(node.target, env, currentDepth);
|
|
1516
|
-
const segments = node.segments.map((segment) => segment.type === "static" ? segment : { type: "dynamic", key: optimizeNode(segment.key, env, currentDepth) });
|
|
1517
|
-
const targetValue = constValue(target);
|
|
1518
|
-
if (targetValue !== undefined || target.type === "undefined") {
|
|
1519
|
-
let current = targetValue;
|
|
1520
|
-
let foldable = true;
|
|
1521
|
-
for (const segment of segments) {
|
|
1522
|
-
if (!foldable)
|
|
1523
|
-
break;
|
|
1524
|
-
const key = segment.type === "static" ? segment.key : constValue(segment.key);
|
|
1525
|
-
if (segment.type === "dynamic" && key === undefined && segment.key.type !== "undefined") {
|
|
1526
|
-
foldable = false;
|
|
1527
|
-
break;
|
|
1528
|
-
}
|
|
1529
|
-
if (current === null || current === undefined) {
|
|
1530
|
-
current = undefined;
|
|
1531
|
-
continue;
|
|
1532
|
-
}
|
|
1533
|
-
current = current[String(key)];
|
|
1534
|
-
}
|
|
1535
|
-
if (foldable) {
|
|
1536
|
-
const literal = toLiteralNode(current);
|
|
1537
|
-
if (literal)
|
|
1538
|
-
return literal;
|
|
1539
|
-
}
|
|
1540
|
-
}
|
|
1541
|
-
return {
|
|
1542
|
-
type: "navigation",
|
|
1543
|
-
target,
|
|
1544
|
-
segments
|
|
1545
|
-
};
|
|
1546
|
-
}
|
|
1547
|
-
case "call": {
|
|
1548
|
-
return {
|
|
1549
|
-
type: "call",
|
|
1550
|
-
callee: optimizeNode(node.callee, env, currentDepth),
|
|
1551
|
-
args: node.args.map((arg) => optimizeNode(arg, env, currentDepth))
|
|
1552
|
-
};
|
|
1553
|
-
}
|
|
1554
|
-
case "assign": {
|
|
1555
|
-
return {
|
|
1556
|
-
type: "assign",
|
|
1557
|
-
op: node.op,
|
|
1558
|
-
place: optimizeNode(node.place, env, currentDepth, true),
|
|
1559
|
-
value: optimizeNode(node.value, env, currentDepth)
|
|
1560
|
-
};
|
|
1561
|
-
}
|
|
1562
|
-
case "conditional": {
|
|
1563
|
-
const condition = optimizeNode(node.condition, env, currentDepth);
|
|
1564
|
-
const thenEnv = cloneOptimizeEnv(env);
|
|
1565
|
-
if (condition.type === "assign" && condition.op === "=" && condition.place.type === "identifier") {
|
|
1566
|
-
thenEnv.selfCaptures[condition.place.name] = currentDepth;
|
|
1567
|
-
delete thenEnv.constants[condition.place.name];
|
|
1568
|
-
}
|
|
1569
|
-
const conditionValue = constValue(condition);
|
|
1570
|
-
if (conditionValue !== undefined || condition.type === "undefined") {
|
|
1571
|
-
const passes = node.head === "when" ? isDefinedValue(conditionValue) : !isDefinedValue(conditionValue);
|
|
1572
|
-
if (passes) {
|
|
1573
|
-
const thenBlock2 = optimizeBlock(node.thenBlock, thenEnv, currentDepth);
|
|
1574
|
-
if (thenBlock2.length === 0)
|
|
1575
|
-
return { type: "undefined" };
|
|
1576
|
-
if (thenBlock2.length === 1)
|
|
1577
|
-
return thenBlock2[0];
|
|
1578
|
-
return { type: "program", body: thenBlock2 };
|
|
1579
|
-
}
|
|
1580
|
-
if (!node.elseBranch)
|
|
1581
|
-
return { type: "undefined" };
|
|
1582
|
-
const loweredElse = optimizeElse(node.elseBranch, cloneOptimizeEnv(env), currentDepth);
|
|
1583
|
-
if (!loweredElse)
|
|
1584
|
-
return { type: "undefined" };
|
|
1585
|
-
if (loweredElse.type === "else") {
|
|
1586
|
-
if (loweredElse.block.length === 0)
|
|
1587
|
-
return { type: "undefined" };
|
|
1588
|
-
if (loweredElse.block.length === 1)
|
|
1589
|
-
return loweredElse.block[0];
|
|
1590
|
-
return { type: "program", body: loweredElse.block };
|
|
1591
|
-
}
|
|
1592
|
-
return {
|
|
1593
|
-
type: "conditional",
|
|
1594
|
-
head: loweredElse.head,
|
|
1595
|
-
condition: loweredElse.condition,
|
|
1596
|
-
thenBlock: loweredElse.thenBlock,
|
|
1597
|
-
elseBranch: loweredElse.elseBranch
|
|
1598
|
-
};
|
|
1599
|
-
}
|
|
1600
|
-
const thenBlock = optimizeBlock(node.thenBlock, thenEnv, currentDepth);
|
|
1601
|
-
const elseBranch = optimizeElse(node.elseBranch, cloneOptimizeEnv(env), currentDepth);
|
|
1602
|
-
let finalCondition = condition;
|
|
1603
|
-
if (condition.type === "assign" && condition.op === "=" && condition.place.type === "identifier") {
|
|
1604
|
-
const name = condition.place.name;
|
|
1605
|
-
const reads = new Set;
|
|
1606
|
-
for (const part of thenBlock)
|
|
1607
|
-
collectReads(part, reads);
|
|
1608
|
-
if (elseBranch)
|
|
1609
|
-
collectReadsElse(elseBranch, reads);
|
|
1610
|
-
if (!reads.has(name)) {
|
|
1611
|
-
finalCondition = condition.value;
|
|
1612
|
-
}
|
|
1613
|
-
}
|
|
1614
|
-
return {
|
|
1615
|
-
type: "conditional",
|
|
1616
|
-
head: node.head,
|
|
1617
|
-
condition: finalCondition,
|
|
1618
|
-
thenBlock,
|
|
1619
|
-
elseBranch
|
|
1620
|
-
};
|
|
1621
|
-
}
|
|
1622
|
-
case "for": {
|
|
1623
|
-
const sourceEnv = cloneOptimizeEnv(env);
|
|
1624
|
-
const binding = (() => {
|
|
1625
|
-
if (node.binding.type === "binding:expr") {
|
|
1626
|
-
return { type: "binding:expr", source: optimizeNode(node.binding.source, sourceEnv, currentDepth) };
|
|
1627
|
-
}
|
|
1628
|
-
if (node.binding.type === "binding:valueIn") {
|
|
1629
|
-
return {
|
|
1630
|
-
type: "binding:valueIn",
|
|
1631
|
-
value: node.binding.value,
|
|
1632
|
-
source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
|
|
1633
|
-
};
|
|
1634
|
-
}
|
|
1635
|
-
if (node.binding.type === "binding:keyValueIn") {
|
|
1636
|
-
return {
|
|
1637
|
-
type: "binding:keyValueIn",
|
|
1638
|
-
key: node.binding.key,
|
|
1639
|
-
value: node.binding.value,
|
|
1640
|
-
source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
|
|
1641
|
-
};
|
|
1642
|
-
}
|
|
1643
|
-
return {
|
|
1644
|
-
type: "binding:keyOf",
|
|
1645
|
-
key: node.binding.key,
|
|
1646
|
-
source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
|
|
1647
|
-
};
|
|
1648
|
-
})();
|
|
1649
|
-
const bodyEnv = cloneOptimizeEnv(env);
|
|
1650
|
-
dropBindingNames(bodyEnv, binding);
|
|
1651
|
-
return {
|
|
1652
|
-
type: "for",
|
|
1653
|
-
binding,
|
|
1654
|
-
body: optimizeBlock(node.body, bodyEnv, currentDepth + 1)
|
|
1655
|
-
};
|
|
1656
|
-
}
|
|
1657
|
-
case "arrayComprehension": {
|
|
1658
|
-
const sourceEnv = cloneOptimizeEnv(env);
|
|
1659
|
-
const binding = node.binding.type === "binding:expr" ? { type: "binding:expr", source: optimizeNode(node.binding.source, sourceEnv, currentDepth) } : node.binding.type === "binding:valueIn" ? {
|
|
1660
|
-
type: "binding:valueIn",
|
|
1661
|
-
value: node.binding.value,
|
|
1662
|
-
source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
|
|
1663
|
-
} : node.binding.type === "binding:keyValueIn" ? {
|
|
1664
|
-
type: "binding:keyValueIn",
|
|
1665
|
-
key: node.binding.key,
|
|
1666
|
-
value: node.binding.value,
|
|
1667
|
-
source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
|
|
1668
|
-
} : {
|
|
1669
|
-
type: "binding:keyOf",
|
|
1670
|
-
key: node.binding.key,
|
|
1671
|
-
source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
|
|
1672
|
-
};
|
|
1673
|
-
const bodyEnv = cloneOptimizeEnv(env);
|
|
1674
|
-
dropBindingNames(bodyEnv, binding);
|
|
1675
|
-
return {
|
|
1676
|
-
type: "arrayComprehension",
|
|
1677
|
-
binding,
|
|
1678
|
-
body: optimizeNode(node.body, bodyEnv, currentDepth + 1)
|
|
1679
|
-
};
|
|
1680
|
-
}
|
|
1681
|
-
case "objectComprehension": {
|
|
1682
|
-
const sourceEnv = cloneOptimizeEnv(env);
|
|
1683
|
-
const binding = node.binding.type === "binding:expr" ? { type: "binding:expr", source: optimizeNode(node.binding.source, sourceEnv, currentDepth) } : node.binding.type === "binding:valueIn" ? {
|
|
1684
|
-
type: "binding:valueIn",
|
|
1685
|
-
value: node.binding.value,
|
|
1686
|
-
source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
|
|
1687
|
-
} : node.binding.type === "binding:keyValueIn" ? {
|
|
1688
|
-
type: "binding:keyValueIn",
|
|
1689
|
-
key: node.binding.key,
|
|
1690
|
-
value: node.binding.value,
|
|
1691
|
-
source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
|
|
1692
|
-
} : {
|
|
1693
|
-
type: "binding:keyOf",
|
|
1694
|
-
key: node.binding.key,
|
|
1695
|
-
source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
|
|
1696
|
-
};
|
|
1697
|
-
const bodyEnv = cloneOptimizeEnv(env);
|
|
1698
|
-
dropBindingNames(bodyEnv, binding);
|
|
1699
|
-
return {
|
|
1700
|
-
type: "objectComprehension",
|
|
1701
|
-
binding,
|
|
1702
|
-
key: optimizeNode(node.key, bodyEnv, currentDepth + 1),
|
|
1703
|
-
value: optimizeNode(node.value, bodyEnv, currentDepth + 1)
|
|
1704
|
-
};
|
|
1705
|
-
}
|
|
1706
|
-
default:
|
|
1707
|
-
return node;
|
|
1708
|
-
}
|
|
1709
|
-
}
|
|
1710
937
|
function optimizeIR(node) {
|
|
1711
|
-
return
|
|
938
|
+
return node;
|
|
1712
939
|
}
|
|
1713
940
|
function collectLocalBindings(node, locals) {
|
|
1714
941
|
switch (node.type) {
|
|
@@ -1754,6 +981,10 @@ function collectLocalBindings(node, locals) {
|
|
|
1754
981
|
collectLocalBindings(node.left, locals);
|
|
1755
982
|
collectLocalBindings(node.right, locals);
|
|
1756
983
|
return;
|
|
984
|
+
case "range":
|
|
985
|
+
collectLocalBindings(node.from, locals);
|
|
986
|
+
collectLocalBindings(node.to, locals);
|
|
987
|
+
return;
|
|
1757
988
|
case "conditional":
|
|
1758
989
|
collectLocalBindings(node.condition, locals);
|
|
1759
990
|
for (const part of node.thenBlock)
|
|
@@ -1770,11 +1001,20 @@ function collectLocalBindings(node, locals) {
|
|
|
1770
1001
|
collectLocalBindingFromBinding(node.binding, locals);
|
|
1771
1002
|
collectLocalBindings(node.body, locals);
|
|
1772
1003
|
return;
|
|
1004
|
+
case "whileArrayComprehension":
|
|
1005
|
+
collectLocalBindings(node.condition, locals);
|
|
1006
|
+
collectLocalBindings(node.body, locals);
|
|
1007
|
+
return;
|
|
1773
1008
|
case "objectComprehension":
|
|
1774
1009
|
collectLocalBindingFromBinding(node.binding, locals);
|
|
1775
1010
|
collectLocalBindings(node.key, locals);
|
|
1776
1011
|
collectLocalBindings(node.value, locals);
|
|
1777
1012
|
return;
|
|
1013
|
+
case "whileObjectComprehension":
|
|
1014
|
+
collectLocalBindings(node.condition, locals);
|
|
1015
|
+
collectLocalBindings(node.key, locals);
|
|
1016
|
+
collectLocalBindings(node.value, locals);
|
|
1017
|
+
return;
|
|
1778
1018
|
default:
|
|
1779
1019
|
return;
|
|
1780
1020
|
}
|
|
@@ -1866,6 +1106,10 @@ function collectNameFrequencies(node, locals, frequencies, order, nextOrder) {
|
|
|
1866
1106
|
collectNameFrequencies(node.left, locals, frequencies, order, nextOrder);
|
|
1867
1107
|
collectNameFrequencies(node.right, locals, frequencies, order, nextOrder);
|
|
1868
1108
|
return;
|
|
1109
|
+
case "range":
|
|
1110
|
+
collectNameFrequencies(node.from, locals, frequencies, order, nextOrder);
|
|
1111
|
+
collectNameFrequencies(node.to, locals, frequencies, order, nextOrder);
|
|
1112
|
+
return;
|
|
1869
1113
|
case "conditional":
|
|
1870
1114
|
collectNameFrequencies(node.condition, locals, frequencies, order, nextOrder);
|
|
1871
1115
|
for (const part of node.thenBlock)
|
|
@@ -1882,11 +1126,20 @@ function collectNameFrequencies(node, locals, frequencies, order, nextOrder) {
|
|
|
1882
1126
|
collectNameFrequenciesBinding(node.binding, locals, frequencies, order, nextOrder);
|
|
1883
1127
|
collectNameFrequencies(node.body, locals, frequencies, order, nextOrder);
|
|
1884
1128
|
return;
|
|
1129
|
+
case "whileArrayComprehension":
|
|
1130
|
+
collectNameFrequencies(node.condition, locals, frequencies, order, nextOrder);
|
|
1131
|
+
collectNameFrequencies(node.body, locals, frequencies, order, nextOrder);
|
|
1132
|
+
return;
|
|
1885
1133
|
case "objectComprehension":
|
|
1886
1134
|
collectNameFrequenciesBinding(node.binding, locals, frequencies, order, nextOrder);
|
|
1887
1135
|
collectNameFrequencies(node.key, locals, frequencies, order, nextOrder);
|
|
1888
1136
|
collectNameFrequencies(node.value, locals, frequencies, order, nextOrder);
|
|
1889
1137
|
return;
|
|
1138
|
+
case "whileObjectComprehension":
|
|
1139
|
+
collectNameFrequencies(node.condition, locals, frequencies, order, nextOrder);
|
|
1140
|
+
collectNameFrequencies(node.key, locals, frequencies, order, nextOrder);
|
|
1141
|
+
collectNameFrequencies(node.value, locals, frequencies, order, nextOrder);
|
|
1142
|
+
return;
|
|
1890
1143
|
default:
|
|
1891
1144
|
return;
|
|
1892
1145
|
}
|
|
@@ -1961,6 +1214,12 @@ function renameLocalNames(node, map) {
|
|
|
1961
1214
|
left: renameLocalNames(node.left, map),
|
|
1962
1215
|
right: renameLocalNames(node.right, map)
|
|
1963
1216
|
};
|
|
1217
|
+
case "range":
|
|
1218
|
+
return {
|
|
1219
|
+
type: "range",
|
|
1220
|
+
from: renameLocalNames(node.from, map),
|
|
1221
|
+
to: renameLocalNames(node.to, map)
|
|
1222
|
+
};
|
|
1964
1223
|
case "assign": {
|
|
1965
1224
|
const place = node.place.type === "identifier" && map.has(node.place.name) ? { type: "identifier", name: map.get(node.place.name) } : renameLocalNames(node.place, map);
|
|
1966
1225
|
return {
|
|
@@ -1990,6 +1249,12 @@ function renameLocalNames(node, map) {
|
|
|
1990
1249
|
binding: renameLocalNamesBinding(node.binding, map),
|
|
1991
1250
|
body: renameLocalNames(node.body, map)
|
|
1992
1251
|
};
|
|
1252
|
+
case "whileArrayComprehension":
|
|
1253
|
+
return {
|
|
1254
|
+
type: "whileArrayComprehension",
|
|
1255
|
+
condition: renameLocalNames(node.condition, map),
|
|
1256
|
+
body: renameLocalNames(node.body, map)
|
|
1257
|
+
};
|
|
1993
1258
|
case "objectComprehension":
|
|
1994
1259
|
return {
|
|
1995
1260
|
type: "objectComprehension",
|
|
@@ -1997,34 +1262,31 @@ function renameLocalNames(node, map) {
|
|
|
1997
1262
|
key: renameLocalNames(node.key, map),
|
|
1998
1263
|
value: renameLocalNames(node.value, map)
|
|
1999
1264
|
};
|
|
1265
|
+
case "whileObjectComprehension":
|
|
1266
|
+
return {
|
|
1267
|
+
type: "whileObjectComprehension",
|
|
1268
|
+
condition: renameLocalNames(node.condition, map),
|
|
1269
|
+
key: renameLocalNames(node.key, map),
|
|
1270
|
+
value: renameLocalNames(node.value, map)
|
|
1271
|
+
};
|
|
2000
1272
|
default:
|
|
2001
1273
|
return node;
|
|
2002
1274
|
}
|
|
2003
1275
|
}
|
|
2004
1276
|
function renameLocalNamesBinding(binding, map) {
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
type: "binding:
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
1277
|
+
const source = renameLocalNames(binding.source, map);
|
|
1278
|
+
switch (binding.type) {
|
|
1279
|
+
case "binding:bareIn":
|
|
1280
|
+
return { type: "binding:bareIn", source };
|
|
1281
|
+
case "binding:bareOf":
|
|
1282
|
+
return { type: "binding:bareOf", source };
|
|
1283
|
+
case "binding:valueIn":
|
|
1284
|
+
return { type: "binding:valueIn", value: map.get(binding.value) ?? binding.value, source };
|
|
1285
|
+
case "binding:keyValueIn":
|
|
1286
|
+
return { type: "binding:keyValueIn", key: map.get(binding.key) ?? binding.key, value: map.get(binding.value) ?? binding.value, source };
|
|
1287
|
+
case "binding:keyOf":
|
|
1288
|
+
return { type: "binding:keyOf", key: map.get(binding.key) ?? binding.key, source };
|
|
2014
1289
|
}
|
|
2015
|
-
if (binding.type === "binding:keyValueIn") {
|
|
2016
|
-
return {
|
|
2017
|
-
type: "binding:keyValueIn",
|
|
2018
|
-
key: map.get(binding.key) ?? binding.key,
|
|
2019
|
-
value: map.get(binding.value) ?? binding.value,
|
|
2020
|
-
source: renameLocalNames(binding.source, map)
|
|
2021
|
-
};
|
|
2022
|
-
}
|
|
2023
|
-
return {
|
|
2024
|
-
type: "binding:keyOf",
|
|
2025
|
-
key: map.get(binding.key) ?? binding.key,
|
|
2026
|
-
source: renameLocalNames(binding.source, map)
|
|
2027
|
-
};
|
|
2028
1290
|
}
|
|
2029
1291
|
function renameLocalNamesElse(elseBranch, map) {
|
|
2030
1292
|
if (elseBranch.type === "else") {
|
|
@@ -2071,9 +1333,9 @@ function compile(source, options) {
|
|
|
2071
1333
|
let lowered = options?.optimize ? optimizeIR(ir) : ir;
|
|
2072
1334
|
if (options?.minifyNames)
|
|
2073
1335
|
lowered = minifyLocalNamesIR(lowered);
|
|
2074
|
-
const
|
|
1336
|
+
const domainMaps = options?.domainConfig ? domainRefsFromConfig(options.domainConfig) : undefined;
|
|
2075
1337
|
return encodeIR(lowered, {
|
|
2076
|
-
|
|
1338
|
+
...domainMaps,
|
|
2077
1339
|
dedupeValues: options?.dedupeValues,
|
|
2078
1340
|
dedupeMinBytes: options?.dedupeMinBytes
|
|
2079
1341
|
});
|
|
@@ -2199,6 +1461,9 @@ semantics.addOperation("toIR", {
|
|
|
2199
1461
|
ExistenceExpr_or(left, _or, right) {
|
|
2200
1462
|
return { type: "binary", op: "or", left: left.toIR(), right: right.toIR() };
|
|
2201
1463
|
},
|
|
1464
|
+
ExistenceExpr_nor(left, _nor, right) {
|
|
1465
|
+
return { type: "binary", op: "nor", left: left.toIR(), right: right.toIR() };
|
|
1466
|
+
},
|
|
2202
1467
|
BitExpr_and(left, _op, right) {
|
|
2203
1468
|
return { type: "binary", op: "bitAnd", left: left.toIR(), right: right.toIR() };
|
|
2204
1469
|
},
|
|
@@ -2208,6 +1473,9 @@ semantics.addOperation("toIR", {
|
|
|
2208
1473
|
BitExpr_or(left, _op, right) {
|
|
2209
1474
|
return { type: "binary", op: "bitOr", left: left.toIR(), right: right.toIR() };
|
|
2210
1475
|
},
|
|
1476
|
+
RangeExpr_range(left, _op, right) {
|
|
1477
|
+
return { type: "range", from: left.toIR(), to: right.toIR() };
|
|
1478
|
+
},
|
|
2211
1479
|
CompareExpr_binary(left, op, right) {
|
|
2212
1480
|
const map = {
|
|
2213
1481
|
"==": "eq",
|
|
@@ -2248,6 +1516,9 @@ semantics.addOperation("toIR", {
|
|
|
2248
1516
|
UnaryExpr_not(_op, value) {
|
|
2249
1517
|
return { type: "unary", op: "not", value: value.toIR() };
|
|
2250
1518
|
},
|
|
1519
|
+
UnaryExpr_logicalNot(_not, value) {
|
|
1520
|
+
return { type: "unary", op: "logicalNot", value: value.toIR() };
|
|
1521
|
+
},
|
|
2251
1522
|
UnaryExpr_delete(_del, place) {
|
|
2252
1523
|
return { type: "unary", op: "delete", value: place.toIR() };
|
|
2253
1524
|
},
|
|
@@ -2301,14 +1572,6 @@ semantics.addOperation("toIR", {
|
|
|
2301
1572
|
ConditionalElse_else(_else, block) {
|
|
2302
1573
|
return { type: "else", block: block.toIR() };
|
|
2303
1574
|
},
|
|
2304
|
-
DoExpr(_do, block, _end) {
|
|
2305
|
-
const body = block.toIR();
|
|
2306
|
-
if (body.length === 0)
|
|
2307
|
-
return { type: "undefined" };
|
|
2308
|
-
if (body.length === 1)
|
|
2309
|
-
return body[0];
|
|
2310
|
-
return { type: "program", body };
|
|
2311
|
-
},
|
|
2312
1575
|
WhileExpr(_while, condition, _do, block, _end) {
|
|
2313
1576
|
return {
|
|
2314
1577
|
type: "while",
|
|
@@ -2323,30 +1586,44 @@ semantics.addOperation("toIR", {
|
|
|
2323
1586
|
body: block.toIR()
|
|
2324
1587
|
};
|
|
2325
1588
|
},
|
|
2326
|
-
BindingExpr(iterOrExpr) {
|
|
2327
|
-
const node = iterOrExpr.toIR();
|
|
2328
|
-
if (typeof node === "object" && node && "type" in node && String(node.type).startsWith("binding:")) {
|
|
2329
|
-
return node;
|
|
2330
|
-
}
|
|
2331
|
-
return { type: "binding:expr", source: node };
|
|
2332
|
-
},
|
|
2333
1589
|
Array_empty(_open, _close) {
|
|
2334
1590
|
return { type: "array", items: [] };
|
|
2335
1591
|
},
|
|
2336
|
-
|
|
1592
|
+
Array_forComprehension(_open, body, _for, binding, _close) {
|
|
2337
1593
|
return {
|
|
2338
1594
|
type: "arrayComprehension",
|
|
2339
1595
|
binding: binding.toIR(),
|
|
2340
1596
|
body: body.toIR()
|
|
2341
1597
|
};
|
|
2342
1598
|
},
|
|
1599
|
+
Array_whileComprehension(_open, body, _while, condition, _close) {
|
|
1600
|
+
return {
|
|
1601
|
+
type: "whileArrayComprehension",
|
|
1602
|
+
condition: condition.toIR(),
|
|
1603
|
+
body: body.toIR()
|
|
1604
|
+
};
|
|
1605
|
+
},
|
|
1606
|
+
Array_inComprehension(_open, body, _in, source, _close) {
|
|
1607
|
+
return {
|
|
1608
|
+
type: "arrayComprehension",
|
|
1609
|
+
binding: { type: "binding:bareIn", source: source.toIR() },
|
|
1610
|
+
body: body.toIR()
|
|
1611
|
+
};
|
|
1612
|
+
},
|
|
1613
|
+
Array_ofComprehension(_open, body, _of, source, _close) {
|
|
1614
|
+
return {
|
|
1615
|
+
type: "arrayComprehension",
|
|
1616
|
+
binding: { type: "binding:bareOf", source: source.toIR() },
|
|
1617
|
+
body: body.toIR()
|
|
1618
|
+
};
|
|
1619
|
+
},
|
|
2343
1620
|
Array_values(_open, items, _close) {
|
|
2344
1621
|
return { type: "array", items: normalizeList(items.toIR()) };
|
|
2345
1622
|
},
|
|
2346
1623
|
Object_empty(_open, _close) {
|
|
2347
1624
|
return { type: "object", entries: [] };
|
|
2348
1625
|
},
|
|
2349
|
-
|
|
1626
|
+
Object_forComprehension(_open, key, _colon, value, _for, binding, _close) {
|
|
2350
1627
|
return {
|
|
2351
1628
|
type: "objectComprehension",
|
|
2352
1629
|
binding: binding.toIR(),
|
|
@@ -2354,6 +1631,30 @@ semantics.addOperation("toIR", {
|
|
|
2354
1631
|
value: value.toIR()
|
|
2355
1632
|
};
|
|
2356
1633
|
},
|
|
1634
|
+
Object_whileComprehension(_open, key, _colon, value, _while, condition, _close) {
|
|
1635
|
+
return {
|
|
1636
|
+
type: "whileObjectComprehension",
|
|
1637
|
+
condition: condition.toIR(),
|
|
1638
|
+
key: key.toIR(),
|
|
1639
|
+
value: value.toIR()
|
|
1640
|
+
};
|
|
1641
|
+
},
|
|
1642
|
+
Object_inComprehension(_open, key, _colon, value, _in, source, _close) {
|
|
1643
|
+
return {
|
|
1644
|
+
type: "objectComprehension",
|
|
1645
|
+
binding: { type: "binding:bareIn", source: source.toIR() },
|
|
1646
|
+
key: key.toIR(),
|
|
1647
|
+
value: value.toIR()
|
|
1648
|
+
};
|
|
1649
|
+
},
|
|
1650
|
+
Object_ofComprehension(_open, key, _colon, value, _of, source, _close) {
|
|
1651
|
+
return {
|
|
1652
|
+
type: "objectComprehension",
|
|
1653
|
+
binding: { type: "binding:bareOf", source: source.toIR() },
|
|
1654
|
+
key: key.toIR(),
|
|
1655
|
+
value: value.toIR()
|
|
1656
|
+
};
|
|
1657
|
+
},
|
|
2357
1658
|
Object_pairs(_open, pairs, _close) {
|
|
2358
1659
|
return {
|
|
2359
1660
|
type: "object",
|
|
@@ -2382,6 +1683,40 @@ semantics.addOperation("toIR", {
|
|
|
2382
1683
|
source: source.toIR()
|
|
2383
1684
|
};
|
|
2384
1685
|
},
|
|
1686
|
+
IterBinding_bareIn(_in, source) {
|
|
1687
|
+
return {
|
|
1688
|
+
type: "binding:bareIn",
|
|
1689
|
+
source: source.toIR()
|
|
1690
|
+
};
|
|
1691
|
+
},
|
|
1692
|
+
IterBinding_bareOf(_of, source) {
|
|
1693
|
+
return {
|
|
1694
|
+
type: "binding:bareOf",
|
|
1695
|
+
source: source.toIR()
|
|
1696
|
+
};
|
|
1697
|
+
},
|
|
1698
|
+
IterBindingComprehension_keyValueIn(key, _comma, value, _in, source) {
|
|
1699
|
+
return {
|
|
1700
|
+
type: "binding:keyValueIn",
|
|
1701
|
+
key: key.sourceString,
|
|
1702
|
+
value: value.sourceString,
|
|
1703
|
+
source: source.toIR()
|
|
1704
|
+
};
|
|
1705
|
+
},
|
|
1706
|
+
IterBindingComprehension_valueIn(value, _in, source) {
|
|
1707
|
+
return {
|
|
1708
|
+
type: "binding:valueIn",
|
|
1709
|
+
value: value.sourceString,
|
|
1710
|
+
source: source.toIR()
|
|
1711
|
+
};
|
|
1712
|
+
},
|
|
1713
|
+
IterBindingComprehension_keyOf(key, _of, source) {
|
|
1714
|
+
return {
|
|
1715
|
+
type: "binding:keyOf",
|
|
1716
|
+
key: key.sourceString,
|
|
1717
|
+
source: source.toIR()
|
|
1718
|
+
};
|
|
1719
|
+
},
|
|
2385
1720
|
Pair(key, _colon, value) {
|
|
2386
1721
|
return { key: key.toIR(), value: value.toIR() };
|
|
2387
1722
|
},
|
|
@@ -2467,6 +1802,7 @@ export {
|
|
|
2467
1802
|
optimizeIR,
|
|
2468
1803
|
minifyLocalNamesIR,
|
|
2469
1804
|
grammar,
|
|
1805
|
+
formatParseError,
|
|
2470
1806
|
encodeIR,
|
|
2471
1807
|
domainRefsFromConfig,
|
|
2472
1808
|
rex_default as default,
|