@creationix/rex 0.3.1 → 0.4.1
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/package.json +1 -1
- package/rex-cli.js +615 -269
- package/rex-cli.ts +32 -11
- package/rex-repl.js +582 -256
- package/rex-repl.ts +4 -3
- package/rex.js +310 -180
- package/rex.ohm +50 -22
- package/rex.ohm-bundle.cjs +1 -1
- package/rex.ohm-bundle.d.ts +26 -8
- package/rex.ohm-bundle.js +1 -1
- package/rex.ts +342 -201
- package/rexc-interpreter.ts +262 -85
package/rex.js
CHANGED
|
@@ -10,31 +10,32 @@ 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",
|
|
36
|
+
size: "sz"
|
|
35
37
|
};
|
|
36
|
-
var
|
|
37
|
-
var DOMAIN_DIGIT_INDEX = new Map(Array.from(DIGITS).map((char, index) => [char, index]));
|
|
38
|
+
var KEYWORD_OPCODES = new Set(["boolean", "number", "string", "array", "object", "size"]);
|
|
38
39
|
var BINARY_TO_OPCODE = {
|
|
39
40
|
add: "add",
|
|
40
41
|
sub: "sub",
|
|
@@ -163,11 +164,11 @@ function encodeDecimal(significand, power) {
|
|
|
163
164
|
function encodeNumberNode(node) {
|
|
164
165
|
const numberValue = node.value;
|
|
165
166
|
if (Number.isNaN(numberValue))
|
|
166
|
-
return "
|
|
167
|
+
return "nan'";
|
|
167
168
|
if (numberValue === Infinity)
|
|
168
|
-
return "
|
|
169
|
+
return "inf'";
|
|
169
170
|
if (numberValue === -Infinity)
|
|
170
|
-
return "
|
|
171
|
+
return "nif'";
|
|
171
172
|
if (Number.isInteger(numberValue)) {
|
|
172
173
|
const { base, exp } = splitDecimal(numberValue);
|
|
173
174
|
if (exp >= 0 && exp <= 4)
|
|
@@ -199,7 +200,7 @@ function encodeNumberNode(node) {
|
|
|
199
200
|
return encodeDecimal(significand, power);
|
|
200
201
|
}
|
|
201
202
|
function encodeOpcode(opcode) {
|
|
202
|
-
return `${
|
|
203
|
+
return `${OPCODE_IDS[opcode]}%`;
|
|
203
204
|
}
|
|
204
205
|
function encodeCallParts(parts) {
|
|
205
206
|
return `(${parts.join("")})`;
|
|
@@ -216,7 +217,7 @@ function addOptionalPrefix(encoded) {
|
|
|
216
217
|
let payload = encoded;
|
|
217
218
|
if (encoded.startsWith("?(") || encoded.startsWith("!(") || encoded.startsWith("|(") || encoded.startsWith("&(") || encoded.startsWith(">(") || encoded.startsWith("<(") || encoded.startsWith("#(")) {
|
|
218
219
|
payload = encoded.slice(2, -1);
|
|
219
|
-
} else if (encoded.startsWith(">[") || encoded.startsWith(">{")) {
|
|
220
|
+
} else if (encoded.startsWith(">[") || encoded.startsWith(">{") || encoded.startsWith("<[") || encoded.startsWith("<{") || encoded.startsWith("#[") || encoded.startsWith("#{")) {
|
|
220
221
|
payload = encoded.slice(2, -1);
|
|
221
222
|
} else if (encoded.startsWith("[") || encoded.startsWith("{") || encoded.startsWith("(")) {
|
|
222
223
|
payload = encoded.slice(1, -1);
|
|
@@ -227,7 +228,7 @@ function addOptionalPrefix(encoded) {
|
|
|
227
228
|
}
|
|
228
229
|
function encodeBlockExpression(block) {
|
|
229
230
|
if (block.length === 0)
|
|
230
|
-
return "
|
|
231
|
+
return "un'";
|
|
231
232
|
if (block.length === 1)
|
|
232
233
|
return encodeNode(block[0]);
|
|
233
234
|
return encodeCallParts([encodeOpcode("do"), ...block.map((node) => encodeNode(node))]);
|
|
@@ -244,9 +245,13 @@ function encodeConditionalElse(elseBranch) {
|
|
|
244
245
|
};
|
|
245
246
|
return encodeNode(nested);
|
|
246
247
|
}
|
|
248
|
+
function encodeDomainLookup(shortCode, tag) {
|
|
249
|
+
return `${shortCode}${tag}`;
|
|
250
|
+
}
|
|
247
251
|
function encodeNavigation(node) {
|
|
248
252
|
const domainRefs = activeEncodeOptions?.domainRefs;
|
|
249
|
-
|
|
253
|
+
const domainOpcodes = activeEncodeOptions?.domainOpcodes;
|
|
254
|
+
if ((domainRefs || domainOpcodes) && node.target.type === "identifier") {
|
|
250
255
|
const staticPath = [node.target.name];
|
|
251
256
|
for (const segment of node.segments) {
|
|
252
257
|
if (segment.type !== "static")
|
|
@@ -255,14 +260,17 @@ function encodeNavigation(node) {
|
|
|
255
260
|
}
|
|
256
261
|
for (let pathLength = staticPath.length;pathLength >= 1; pathLength -= 1) {
|
|
257
262
|
const dottedName = staticPath.slice(0, pathLength).join(".");
|
|
258
|
-
const
|
|
259
|
-
|
|
263
|
+
const refCode = domainRefs?.[dottedName];
|
|
264
|
+
const opcodeCode = domainOpcodes?.[dottedName];
|
|
265
|
+
const shortCode = refCode ?? opcodeCode;
|
|
266
|
+
if (shortCode === undefined)
|
|
260
267
|
continue;
|
|
268
|
+
const tag = refCode !== undefined ? "'" : "%";
|
|
261
269
|
const consumedStaticSegments = pathLength - 1;
|
|
262
270
|
if (consumedStaticSegments === node.segments.length) {
|
|
263
|
-
return
|
|
271
|
+
return encodeDomainLookup(shortCode, tag);
|
|
264
272
|
}
|
|
265
|
-
const parts2 = [
|
|
273
|
+
const parts2 = [encodeDomainLookup(shortCode, tag)];
|
|
266
274
|
for (const segment of node.segments.slice(consumedStaticSegments)) {
|
|
267
275
|
if (segment.type === "static")
|
|
268
276
|
parts2.push(encodeBareOrLengthString(segment.key));
|
|
@@ -288,9 +296,12 @@ function encodeWhile(node) {
|
|
|
288
296
|
}
|
|
289
297
|
function encodeFor(node) {
|
|
290
298
|
const body = addOptionalPrefix(encodeBlockExpression(node.body));
|
|
291
|
-
if (node.binding.type === "binding:
|
|
299
|
+
if (node.binding.type === "binding:bareIn") {
|
|
292
300
|
return `>(${encodeNode(node.binding.source)}${body})`;
|
|
293
301
|
}
|
|
302
|
+
if (node.binding.type === "binding:bareOf") {
|
|
303
|
+
return `<(${encodeNode(node.binding.source)}${body})`;
|
|
304
|
+
}
|
|
294
305
|
if (node.binding.type === "binding:valueIn") {
|
|
295
306
|
return `>(${encodeNode(node.binding.source)}${node.binding.value}$${body})`;
|
|
296
307
|
}
|
|
@@ -301,30 +312,47 @@ function encodeFor(node) {
|
|
|
301
312
|
}
|
|
302
313
|
function encodeArrayComprehension(node) {
|
|
303
314
|
const body = addOptionalPrefix(encodeNode(node.body));
|
|
304
|
-
if (node.binding.type === "binding:
|
|
315
|
+
if (node.binding.type === "binding:bareIn") {
|
|
305
316
|
return `>[${encodeNode(node.binding.source)}${body}]`;
|
|
306
317
|
}
|
|
318
|
+
if (node.binding.type === "binding:bareOf") {
|
|
319
|
+
return `<[${encodeNode(node.binding.source)}${body}]`;
|
|
320
|
+
}
|
|
307
321
|
if (node.binding.type === "binding:valueIn") {
|
|
308
322
|
return `>[${encodeNode(node.binding.source)}${node.binding.value}$${body}]`;
|
|
309
323
|
}
|
|
310
324
|
if (node.binding.type === "binding:keyValueIn") {
|
|
311
325
|
return `>[${encodeNode(node.binding.source)}${node.binding.key}$${node.binding.value}$${body}]`;
|
|
312
326
|
}
|
|
313
|
-
return
|
|
327
|
+
return `<[${encodeNode(node.binding.source)}${node.binding.key}$${body}]`;
|
|
314
328
|
}
|
|
315
329
|
function encodeObjectComprehension(node) {
|
|
316
330
|
const key = addOptionalPrefix(encodeNode(node.key));
|
|
317
331
|
const value = addOptionalPrefix(encodeNode(node.value));
|
|
318
|
-
if (node.binding.type === "binding:
|
|
332
|
+
if (node.binding.type === "binding:bareIn") {
|
|
319
333
|
return `>{${encodeNode(node.binding.source)}${key}${value}}`;
|
|
320
334
|
}
|
|
335
|
+
if (node.binding.type === "binding:bareOf") {
|
|
336
|
+
return `<{${encodeNode(node.binding.source)}${key}${value}}`;
|
|
337
|
+
}
|
|
321
338
|
if (node.binding.type === "binding:valueIn") {
|
|
322
339
|
return `>{${encodeNode(node.binding.source)}${node.binding.value}$${key}${value}}`;
|
|
323
340
|
}
|
|
324
341
|
if (node.binding.type === "binding:keyValueIn") {
|
|
325
342
|
return `>{${encodeNode(node.binding.source)}${node.binding.key}$${node.binding.value}$${key}${value}}`;
|
|
326
343
|
}
|
|
327
|
-
return
|
|
344
|
+
return `<{${encodeNode(node.binding.source)}${node.binding.key}$${key}${value}}`;
|
|
345
|
+
}
|
|
346
|
+
function encodeWhileArrayComprehension(node) {
|
|
347
|
+
const cond = encodeNode(node.condition);
|
|
348
|
+
const body = addOptionalPrefix(encodeNode(node.body));
|
|
349
|
+
return `#[${cond}${body}]`;
|
|
350
|
+
}
|
|
351
|
+
function encodeWhileObjectComprehension(node) {
|
|
352
|
+
const cond = encodeNode(node.condition);
|
|
353
|
+
const key = addOptionalPrefix(encodeNode(node.key));
|
|
354
|
+
const value = addOptionalPrefix(encodeNode(node.value));
|
|
355
|
+
return `#{${cond}${key}${value}}`;
|
|
328
356
|
}
|
|
329
357
|
var activeEncodeOptions;
|
|
330
358
|
function encodeNode(node) {
|
|
@@ -334,7 +362,10 @@ function encodeNode(node) {
|
|
|
334
362
|
case "identifier": {
|
|
335
363
|
const domainRef = activeEncodeOptions?.domainRefs?.[node.name];
|
|
336
364
|
if (domainRef !== undefined)
|
|
337
|
-
return `${
|
|
365
|
+
return `${domainRef}'`;
|
|
366
|
+
const domainOpcode = activeEncodeOptions?.domainOpcodes?.[node.name];
|
|
367
|
+
if (domainOpcode !== undefined)
|
|
368
|
+
return `${domainOpcode}%`;
|
|
338
369
|
return `${node.name}$`;
|
|
339
370
|
}
|
|
340
371
|
case "self":
|
|
@@ -347,11 +378,11 @@ function encodeNode(node) {
|
|
|
347
378
|
return `${encodeUint(node.depth - 1)}@`;
|
|
348
379
|
}
|
|
349
380
|
case "boolean":
|
|
350
|
-
return node.value ? "
|
|
381
|
+
return node.value ? "tr'" : "fl'";
|
|
351
382
|
case "null":
|
|
352
|
-
return "
|
|
383
|
+
return "nl'";
|
|
353
384
|
case "undefined":
|
|
354
|
-
return "
|
|
385
|
+
return "un'";
|
|
355
386
|
case "number":
|
|
356
387
|
return encodeNumberNode(node);
|
|
357
388
|
case "string":
|
|
@@ -362,12 +393,16 @@ function encodeNode(node) {
|
|
|
362
393
|
}
|
|
363
394
|
case "arrayComprehension":
|
|
364
395
|
return encodeArrayComprehension(node);
|
|
396
|
+
case "whileArrayComprehension":
|
|
397
|
+
return encodeWhileArrayComprehension(node);
|
|
365
398
|
case "object": {
|
|
366
399
|
const body = node.entries.map(({ key, value }) => `${encodeNode(key)}${addOptionalPrefix(encodeNode(value))}`).join("");
|
|
367
400
|
return `{${body}}`;
|
|
368
401
|
}
|
|
369
402
|
case "objectComprehension":
|
|
370
403
|
return encodeObjectComprehension(node);
|
|
404
|
+
case "whileObjectComprehension":
|
|
405
|
+
return encodeWhileObjectComprehension(node);
|
|
371
406
|
case "key":
|
|
372
407
|
return encodeBareOrLengthString(node.name);
|
|
373
408
|
case "group":
|
|
@@ -377,6 +412,10 @@ function encodeNode(node) {
|
|
|
377
412
|
return `~${encodeNode(node.value)}`;
|
|
378
413
|
if (node.op === "neg")
|
|
379
414
|
return encodeCallParts([encodeOpcode("neg"), encodeNode(node.value)]);
|
|
415
|
+
if (node.op === "logicalNot") {
|
|
416
|
+
const val = encodeNode(node.value);
|
|
417
|
+
return `!(${val}tr')`;
|
|
418
|
+
}
|
|
380
419
|
return encodeCallParts([encodeOpcode("not"), encodeNode(node.value)]);
|
|
381
420
|
case "binary":
|
|
382
421
|
if (node.op === "and") {
|
|
@@ -395,12 +434,19 @@ function encodeNode(node) {
|
|
|
395
434
|
}).join("");
|
|
396
435
|
return `|(${body})`;
|
|
397
436
|
}
|
|
437
|
+
if (node.op === "nor") {
|
|
438
|
+
const left = encodeNode(node.left);
|
|
439
|
+
const right = addOptionalPrefix(encodeNode(node.right));
|
|
440
|
+
return `!(${left}${right})`;
|
|
441
|
+
}
|
|
398
442
|
return encodeCallParts([
|
|
399
443
|
encodeOpcode(BINARY_TO_OPCODE[node.op]),
|
|
400
444
|
encodeNode(node.left),
|
|
401
445
|
encodeNode(node.right)
|
|
402
446
|
]);
|
|
403
447
|
case "assign": {
|
|
448
|
+
if (node.op === ":=")
|
|
449
|
+
return `/${encodeNode(node.place)}${addOptionalPrefix(encodeNode(node.value))}`;
|
|
404
450
|
if (node.op === "=")
|
|
405
451
|
return `=${encodeNode(node.place)}${addOptionalPrefix(encodeNode(node.value))}`;
|
|
406
452
|
const opcode = ASSIGN_COMPOUND_TO_OPCODE[node.op];
|
|
@@ -411,8 +457,12 @@ function encodeNode(node) {
|
|
|
411
457
|
}
|
|
412
458
|
case "navigation":
|
|
413
459
|
return encodeNavigation(node);
|
|
414
|
-
case "call":
|
|
460
|
+
case "call": {
|
|
461
|
+
if (node.callee.type === "identifier" && KEYWORD_OPCODES.has(node.callee.name)) {
|
|
462
|
+
return encodeCallParts([encodeOpcode(node.callee.name), ...node.args.map((arg) => encodeNode(arg))]);
|
|
463
|
+
}
|
|
415
464
|
return encodeCallParts([encodeNode(node.callee), ...node.args.map((arg) => encodeNode(arg))]);
|
|
465
|
+
}
|
|
416
466
|
case "conditional": {
|
|
417
467
|
const opener = node.head === "when" ? "?(" : "!(";
|
|
418
468
|
const cond = encodeNode(node.condition);
|
|
@@ -420,6 +470,8 @@ function encodeNode(node) {
|
|
|
420
470
|
const elseExpr = node.elseBranch ? addOptionalPrefix(encodeConditionalElse(node.elseBranch)) : "";
|
|
421
471
|
return `${opener}${cond}${thenExpr}${elseExpr})`;
|
|
422
472
|
}
|
|
473
|
+
case "range":
|
|
474
|
+
return encodeCallParts([encodeOpcode("range"), encodeNode(node.from), encodeNode(node.to)]);
|
|
423
475
|
case "for":
|
|
424
476
|
return encodeFor(node);
|
|
425
477
|
case "while":
|
|
@@ -595,78 +647,55 @@ function domainRefsFromConfig(config) {
|
|
|
595
647
|
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
596
648
|
throw new Error("Domain config must be an object");
|
|
597
649
|
}
|
|
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)`);
|
|
650
|
+
const configObj = config;
|
|
651
|
+
const domainRefs = {};
|
|
652
|
+
const domainOpcodes = {};
|
|
653
|
+
const dataSection = configObj.data;
|
|
654
|
+
if (dataSection && typeof dataSection === "object" && !Array.isArray(dataSection)) {
|
|
655
|
+
mapConfigEntries(dataSection, domainRefs);
|
|
614
656
|
}
|
|
615
|
-
|
|
616
|
-
|
|
657
|
+
const functionsSection = configObj.functions;
|
|
658
|
+
if (functionsSection && typeof functionsSection === "object" && !Array.isArray(functionsSection)) {
|
|
659
|
+
mapConfigEntries(functionsSection, domainOpcodes);
|
|
617
660
|
}
|
|
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;
|
|
661
|
+
return { domainRefs, domainOpcodes };
|
|
632
662
|
}
|
|
633
663
|
function mapConfigEntries(entries, refs) {
|
|
634
664
|
const sourceKindByRoot = new Map;
|
|
635
665
|
for (const root of Object.keys(refs)) {
|
|
636
666
|
sourceKindByRoot.set(root, "explicit");
|
|
637
667
|
}
|
|
638
|
-
for (const [
|
|
668
|
+
for (const [shortCode, rawEntry] of Object.entries(entries)) {
|
|
639
669
|
const entry = rawEntry;
|
|
640
670
|
if (!entry || typeof entry !== "object")
|
|
641
671
|
continue;
|
|
642
672
|
if (!Array.isArray(entry.names))
|
|
643
673
|
continue;
|
|
644
|
-
const refId = decodeDomainRefKey(refText);
|
|
645
674
|
for (const rawName of entry.names) {
|
|
646
675
|
if (typeof rawName !== "string")
|
|
647
676
|
continue;
|
|
648
|
-
const
|
|
649
|
-
if (
|
|
650
|
-
throw new Error(`Conflicting refs for '${rawName}': ${
|
|
677
|
+
const existingRef = refs[rawName];
|
|
678
|
+
if (existingRef !== undefined && existingRef !== shortCode) {
|
|
679
|
+
throw new Error(`Conflicting refs for '${rawName}': ${existingRef} vs ${shortCode}`);
|
|
651
680
|
}
|
|
652
|
-
refs[rawName] =
|
|
681
|
+
refs[rawName] = shortCode;
|
|
653
682
|
const root = rawName.split(".")[0];
|
|
654
683
|
if (!root)
|
|
655
684
|
continue;
|
|
656
685
|
const currentKind = rawName.includes(".") ? "implicit" : "explicit";
|
|
657
686
|
const existing = refs[root];
|
|
658
687
|
if (existing !== undefined) {
|
|
659
|
-
if (existing ===
|
|
688
|
+
if (existing === shortCode)
|
|
660
689
|
continue;
|
|
661
690
|
const existingKind = sourceKindByRoot.get(root) ?? "explicit";
|
|
662
691
|
if (currentKind === "explicit") {
|
|
663
|
-
throw new Error(`Conflicting refs for '${root}': ${existing} vs ${
|
|
692
|
+
throw new Error(`Conflicting refs for '${root}': ${existing} vs ${shortCode}`);
|
|
664
693
|
}
|
|
665
694
|
if (existingKind === "explicit")
|
|
666
695
|
continue;
|
|
667
696
|
continue;
|
|
668
697
|
}
|
|
669
|
-
refs[root] =
|
|
698
|
+
refs[root] = shortCode;
|
|
670
699
|
sourceKindByRoot.set(root, currentKind);
|
|
671
700
|
}
|
|
672
701
|
}
|
|
@@ -950,6 +979,21 @@ function dropBindingNames(env, binding) {
|
|
|
950
979
|
clearBinding(env, binding.key);
|
|
951
980
|
}
|
|
952
981
|
}
|
|
982
|
+
function optimizeBinding(binding, sourceEnv, currentDepth) {
|
|
983
|
+
const source = optimizeNode(binding.source, sourceEnv, currentDepth);
|
|
984
|
+
switch (binding.type) {
|
|
985
|
+
case "binding:bareIn":
|
|
986
|
+
return { type: "binding:bareIn", source };
|
|
987
|
+
case "binding:bareOf":
|
|
988
|
+
return { type: "binding:bareOf", source };
|
|
989
|
+
case "binding:valueIn":
|
|
990
|
+
return { type: "binding:valueIn", value: binding.value, source };
|
|
991
|
+
case "binding:keyValueIn":
|
|
992
|
+
return { type: "binding:keyValueIn", key: binding.key, value: binding.value, source };
|
|
993
|
+
case "binding:keyOf":
|
|
994
|
+
return { type: "binding:keyOf", key: binding.key, source };
|
|
995
|
+
}
|
|
996
|
+
}
|
|
953
997
|
function collectReads(node, out) {
|
|
954
998
|
switch (node.type) {
|
|
955
999
|
case "identifier":
|
|
@@ -972,11 +1016,20 @@ function collectReads(node, out) {
|
|
|
972
1016
|
collectReads(node.binding.source, out);
|
|
973
1017
|
collectReads(node.body, out);
|
|
974
1018
|
return;
|
|
1019
|
+
case "whileArrayComprehension":
|
|
1020
|
+
collectReads(node.condition, out);
|
|
1021
|
+
collectReads(node.body, out);
|
|
1022
|
+
return;
|
|
975
1023
|
case "objectComprehension":
|
|
976
1024
|
collectReads(node.binding.source, out);
|
|
977
1025
|
collectReads(node.key, out);
|
|
978
1026
|
collectReads(node.value, out);
|
|
979
1027
|
return;
|
|
1028
|
+
case "whileObjectComprehension":
|
|
1029
|
+
collectReads(node.condition, out);
|
|
1030
|
+
collectReads(node.key, out);
|
|
1031
|
+
collectReads(node.value, out);
|
|
1032
|
+
return;
|
|
980
1033
|
case "unary":
|
|
981
1034
|
collectReads(node.value, out);
|
|
982
1035
|
return;
|
|
@@ -1013,6 +1066,10 @@ function collectReads(node, out) {
|
|
|
1013
1066
|
for (const part of node.body)
|
|
1014
1067
|
collectReads(part, out);
|
|
1015
1068
|
return;
|
|
1069
|
+
case "range":
|
|
1070
|
+
collectReads(node.from, out);
|
|
1071
|
+
collectReads(node.to, out);
|
|
1072
|
+
return;
|
|
1016
1073
|
case "program":
|
|
1017
1074
|
for (const part of node.body)
|
|
1018
1075
|
collectReads(part, out);
|
|
@@ -1057,6 +1114,8 @@ function isPureNode(node) {
|
|
|
1057
1114
|
return node.op !== "delete" && isPureNode(node.value);
|
|
1058
1115
|
case "binary":
|
|
1059
1116
|
return isPureNode(node.left) && isPureNode(node.right);
|
|
1117
|
+
case "range":
|
|
1118
|
+
return isPureNode(node.from) && isPureNode(node.to);
|
|
1060
1119
|
default:
|
|
1061
1120
|
return false;
|
|
1062
1121
|
}
|
|
@@ -1121,6 +1180,8 @@ function hasIdentifierRead(node, name, asPlace = false) {
|
|
|
1121
1180
|
return hasIdentifierRead(node.value, name, node.op === "delete");
|
|
1122
1181
|
case "binary":
|
|
1123
1182
|
return hasIdentifierRead(node.left, name) || hasIdentifierRead(node.right, name);
|
|
1183
|
+
case "range":
|
|
1184
|
+
return hasIdentifierRead(node.from, name) || hasIdentifierRead(node.to, name);
|
|
1124
1185
|
case "assign":
|
|
1125
1186
|
return hasIdentifierRead(node.place, name, true) || hasIdentifierRead(node.value, name);
|
|
1126
1187
|
default:
|
|
@@ -1143,6 +1204,8 @@ function countIdentifierReads(node, name, asPlace = false) {
|
|
|
1143
1204
|
return countIdentifierReads(node.value, name, node.op === "delete");
|
|
1144
1205
|
case "binary":
|
|
1145
1206
|
return countIdentifierReads(node.left, name) + countIdentifierReads(node.right, name);
|
|
1207
|
+
case "range":
|
|
1208
|
+
return countIdentifierReads(node.from, name) + countIdentifierReads(node.to, name);
|
|
1146
1209
|
case "assign":
|
|
1147
1210
|
return countIdentifierReads(node.place, name, true) + countIdentifierReads(node.value, name);
|
|
1148
1211
|
default:
|
|
@@ -1197,6 +1260,12 @@ function replaceIdentifier(node, name, replacement, asPlace = false) {
|
|
|
1197
1260
|
place: replaceIdentifier(node.place, name, replacement, true),
|
|
1198
1261
|
value: replaceIdentifier(node.value, name, replacement)
|
|
1199
1262
|
};
|
|
1263
|
+
case "range":
|
|
1264
|
+
return {
|
|
1265
|
+
type: "range",
|
|
1266
|
+
from: replaceIdentifier(node.from, name, replacement),
|
|
1267
|
+
to: replaceIdentifier(node.to, name, replacement)
|
|
1268
|
+
};
|
|
1200
1269
|
default:
|
|
1201
1270
|
return node;
|
|
1202
1271
|
}
|
|
@@ -1341,6 +1410,9 @@ function foldUnary(op, value) {
|
|
|
1341
1410
|
return ~value;
|
|
1342
1411
|
return;
|
|
1343
1412
|
}
|
|
1413
|
+
if (op === "logicalNot") {
|
|
1414
|
+
return value === undefined ? true : undefined;
|
|
1415
|
+
}
|
|
1344
1416
|
return;
|
|
1345
1417
|
}
|
|
1346
1418
|
function foldBinary(op, left, right) {
|
|
@@ -1511,6 +1583,12 @@ function optimizeNode(node, env, currentDepth, asPlace = false) {
|
|
|
1511
1583
|
}
|
|
1512
1584
|
return { type: "binary", op: node.op, left, right };
|
|
1513
1585
|
}
|
|
1586
|
+
case "range":
|
|
1587
|
+
return {
|
|
1588
|
+
type: "range",
|
|
1589
|
+
from: optimizeNode(node.from, env, currentDepth),
|
|
1590
|
+
to: optimizeNode(node.to, env, currentDepth)
|
|
1591
|
+
};
|
|
1514
1592
|
case "navigation": {
|
|
1515
1593
|
const target = optimizeNode(node.target, env, currentDepth);
|
|
1516
1594
|
const segments = node.segments.map((segment) => segment.type === "static" ? segment : { type: "dynamic", key: optimizeNode(segment.key, env, currentDepth) });
|
|
@@ -1621,31 +1699,7 @@ function optimizeNode(node, env, currentDepth, asPlace = false) {
|
|
|
1621
1699
|
}
|
|
1622
1700
|
case "for": {
|
|
1623
1701
|
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
|
-
})();
|
|
1702
|
+
const binding = optimizeBinding(node.binding, sourceEnv, currentDepth);
|
|
1649
1703
|
const bodyEnv = cloneOptimizeEnv(env);
|
|
1650
1704
|
dropBindingNames(bodyEnv, binding);
|
|
1651
1705
|
return {
|
|
@@ -1656,20 +1710,7 @@ function optimizeNode(node, env, currentDepth, asPlace = false) {
|
|
|
1656
1710
|
}
|
|
1657
1711
|
case "arrayComprehension": {
|
|
1658
1712
|
const sourceEnv = cloneOptimizeEnv(env);
|
|
1659
|
-
const binding =
|
|
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
|
-
};
|
|
1713
|
+
const binding = optimizeBinding(node.binding, sourceEnv, currentDepth);
|
|
1673
1714
|
const bodyEnv = cloneOptimizeEnv(env);
|
|
1674
1715
|
dropBindingNames(bodyEnv, binding);
|
|
1675
1716
|
return {
|
|
@@ -1678,22 +1719,15 @@ function optimizeNode(node, env, currentDepth, asPlace = false) {
|
|
|
1678
1719
|
body: optimizeNode(node.body, bodyEnv, currentDepth + 1)
|
|
1679
1720
|
};
|
|
1680
1721
|
}
|
|
1722
|
+
case "whileArrayComprehension":
|
|
1723
|
+
return {
|
|
1724
|
+
type: "whileArrayComprehension",
|
|
1725
|
+
condition: optimizeNode(node.condition, env, currentDepth),
|
|
1726
|
+
body: optimizeNode(node.body, env, currentDepth + 1)
|
|
1727
|
+
};
|
|
1681
1728
|
case "objectComprehension": {
|
|
1682
1729
|
const sourceEnv = cloneOptimizeEnv(env);
|
|
1683
|
-
const binding =
|
|
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
|
-
};
|
|
1730
|
+
const binding = optimizeBinding(node.binding, sourceEnv, currentDepth);
|
|
1697
1731
|
const bodyEnv = cloneOptimizeEnv(env);
|
|
1698
1732
|
dropBindingNames(bodyEnv, binding);
|
|
1699
1733
|
return {
|
|
@@ -1703,6 +1737,13 @@ function optimizeNode(node, env, currentDepth, asPlace = false) {
|
|
|
1703
1737
|
value: optimizeNode(node.value, bodyEnv, currentDepth + 1)
|
|
1704
1738
|
};
|
|
1705
1739
|
}
|
|
1740
|
+
case "whileObjectComprehension":
|
|
1741
|
+
return {
|
|
1742
|
+
type: "whileObjectComprehension",
|
|
1743
|
+
condition: optimizeNode(node.condition, env, currentDepth),
|
|
1744
|
+
key: optimizeNode(node.key, env, currentDepth + 1),
|
|
1745
|
+
value: optimizeNode(node.value, env, currentDepth + 1)
|
|
1746
|
+
};
|
|
1706
1747
|
default:
|
|
1707
1748
|
return node;
|
|
1708
1749
|
}
|
|
@@ -1754,6 +1795,10 @@ function collectLocalBindings(node, locals) {
|
|
|
1754
1795
|
collectLocalBindings(node.left, locals);
|
|
1755
1796
|
collectLocalBindings(node.right, locals);
|
|
1756
1797
|
return;
|
|
1798
|
+
case "range":
|
|
1799
|
+
collectLocalBindings(node.from, locals);
|
|
1800
|
+
collectLocalBindings(node.to, locals);
|
|
1801
|
+
return;
|
|
1757
1802
|
case "conditional":
|
|
1758
1803
|
collectLocalBindings(node.condition, locals);
|
|
1759
1804
|
for (const part of node.thenBlock)
|
|
@@ -1770,11 +1815,20 @@ function collectLocalBindings(node, locals) {
|
|
|
1770
1815
|
collectLocalBindingFromBinding(node.binding, locals);
|
|
1771
1816
|
collectLocalBindings(node.body, locals);
|
|
1772
1817
|
return;
|
|
1818
|
+
case "whileArrayComprehension":
|
|
1819
|
+
collectLocalBindings(node.condition, locals);
|
|
1820
|
+
collectLocalBindings(node.body, locals);
|
|
1821
|
+
return;
|
|
1773
1822
|
case "objectComprehension":
|
|
1774
1823
|
collectLocalBindingFromBinding(node.binding, locals);
|
|
1775
1824
|
collectLocalBindings(node.key, locals);
|
|
1776
1825
|
collectLocalBindings(node.value, locals);
|
|
1777
1826
|
return;
|
|
1827
|
+
case "whileObjectComprehension":
|
|
1828
|
+
collectLocalBindings(node.condition, locals);
|
|
1829
|
+
collectLocalBindings(node.key, locals);
|
|
1830
|
+
collectLocalBindings(node.value, locals);
|
|
1831
|
+
return;
|
|
1778
1832
|
default:
|
|
1779
1833
|
return;
|
|
1780
1834
|
}
|
|
@@ -1866,6 +1920,10 @@ function collectNameFrequencies(node, locals, frequencies, order, nextOrder) {
|
|
|
1866
1920
|
collectNameFrequencies(node.left, locals, frequencies, order, nextOrder);
|
|
1867
1921
|
collectNameFrequencies(node.right, locals, frequencies, order, nextOrder);
|
|
1868
1922
|
return;
|
|
1923
|
+
case "range":
|
|
1924
|
+
collectNameFrequencies(node.from, locals, frequencies, order, nextOrder);
|
|
1925
|
+
collectNameFrequencies(node.to, locals, frequencies, order, nextOrder);
|
|
1926
|
+
return;
|
|
1869
1927
|
case "conditional":
|
|
1870
1928
|
collectNameFrequencies(node.condition, locals, frequencies, order, nextOrder);
|
|
1871
1929
|
for (const part of node.thenBlock)
|
|
@@ -1882,11 +1940,20 @@ function collectNameFrequencies(node, locals, frequencies, order, nextOrder) {
|
|
|
1882
1940
|
collectNameFrequenciesBinding(node.binding, locals, frequencies, order, nextOrder);
|
|
1883
1941
|
collectNameFrequencies(node.body, locals, frequencies, order, nextOrder);
|
|
1884
1942
|
return;
|
|
1943
|
+
case "whileArrayComprehension":
|
|
1944
|
+
collectNameFrequencies(node.condition, locals, frequencies, order, nextOrder);
|
|
1945
|
+
collectNameFrequencies(node.body, locals, frequencies, order, nextOrder);
|
|
1946
|
+
return;
|
|
1885
1947
|
case "objectComprehension":
|
|
1886
1948
|
collectNameFrequenciesBinding(node.binding, locals, frequencies, order, nextOrder);
|
|
1887
1949
|
collectNameFrequencies(node.key, locals, frequencies, order, nextOrder);
|
|
1888
1950
|
collectNameFrequencies(node.value, locals, frequencies, order, nextOrder);
|
|
1889
1951
|
return;
|
|
1952
|
+
case "whileObjectComprehension":
|
|
1953
|
+
collectNameFrequencies(node.condition, locals, frequencies, order, nextOrder);
|
|
1954
|
+
collectNameFrequencies(node.key, locals, frequencies, order, nextOrder);
|
|
1955
|
+
collectNameFrequencies(node.value, locals, frequencies, order, nextOrder);
|
|
1956
|
+
return;
|
|
1890
1957
|
default:
|
|
1891
1958
|
return;
|
|
1892
1959
|
}
|
|
@@ -1961,6 +2028,12 @@ function renameLocalNames(node, map) {
|
|
|
1961
2028
|
left: renameLocalNames(node.left, map),
|
|
1962
2029
|
right: renameLocalNames(node.right, map)
|
|
1963
2030
|
};
|
|
2031
|
+
case "range":
|
|
2032
|
+
return {
|
|
2033
|
+
type: "range",
|
|
2034
|
+
from: renameLocalNames(node.from, map),
|
|
2035
|
+
to: renameLocalNames(node.to, map)
|
|
2036
|
+
};
|
|
1964
2037
|
case "assign": {
|
|
1965
2038
|
const place = node.place.type === "identifier" && map.has(node.place.name) ? { type: "identifier", name: map.get(node.place.name) } : renameLocalNames(node.place, map);
|
|
1966
2039
|
return {
|
|
@@ -1990,6 +2063,12 @@ function renameLocalNames(node, map) {
|
|
|
1990
2063
|
binding: renameLocalNamesBinding(node.binding, map),
|
|
1991
2064
|
body: renameLocalNames(node.body, map)
|
|
1992
2065
|
};
|
|
2066
|
+
case "whileArrayComprehension":
|
|
2067
|
+
return {
|
|
2068
|
+
type: "whileArrayComprehension",
|
|
2069
|
+
condition: renameLocalNames(node.condition, map),
|
|
2070
|
+
body: renameLocalNames(node.body, map)
|
|
2071
|
+
};
|
|
1993
2072
|
case "objectComprehension":
|
|
1994
2073
|
return {
|
|
1995
2074
|
type: "objectComprehension",
|
|
@@ -1997,34 +2076,31 @@ function renameLocalNames(node, map) {
|
|
|
1997
2076
|
key: renameLocalNames(node.key, map),
|
|
1998
2077
|
value: renameLocalNames(node.value, map)
|
|
1999
2078
|
};
|
|
2079
|
+
case "whileObjectComprehension":
|
|
2080
|
+
return {
|
|
2081
|
+
type: "whileObjectComprehension",
|
|
2082
|
+
condition: renameLocalNames(node.condition, map),
|
|
2083
|
+
key: renameLocalNames(node.key, map),
|
|
2084
|
+
value: renameLocalNames(node.value, map)
|
|
2085
|
+
};
|
|
2000
2086
|
default:
|
|
2001
2087
|
return node;
|
|
2002
2088
|
}
|
|
2003
2089
|
}
|
|
2004
2090
|
function renameLocalNamesBinding(binding, map) {
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
type: "binding:
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
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
|
-
};
|
|
2091
|
+
const source = renameLocalNames(binding.source, map);
|
|
2092
|
+
switch (binding.type) {
|
|
2093
|
+
case "binding:bareIn":
|
|
2094
|
+
return { type: "binding:bareIn", source };
|
|
2095
|
+
case "binding:bareOf":
|
|
2096
|
+
return { type: "binding:bareOf", source };
|
|
2097
|
+
case "binding:valueIn":
|
|
2098
|
+
return { type: "binding:valueIn", value: map.get(binding.value) ?? binding.value, source };
|
|
2099
|
+
case "binding:keyValueIn":
|
|
2100
|
+
return { type: "binding:keyValueIn", key: map.get(binding.key) ?? binding.key, value: map.get(binding.value) ?? binding.value, source };
|
|
2101
|
+
case "binding:keyOf":
|
|
2102
|
+
return { type: "binding:keyOf", key: map.get(binding.key) ?? binding.key, source };
|
|
2022
2103
|
}
|
|
2023
|
-
return {
|
|
2024
|
-
type: "binding:keyOf",
|
|
2025
|
-
key: map.get(binding.key) ?? binding.key,
|
|
2026
|
-
source: renameLocalNames(binding.source, map)
|
|
2027
|
-
};
|
|
2028
2104
|
}
|
|
2029
2105
|
function renameLocalNamesElse(elseBranch, map) {
|
|
2030
2106
|
if (elseBranch.type === "else") {
|
|
@@ -2071,9 +2147,9 @@ function compile(source, options) {
|
|
|
2071
2147
|
let lowered = options?.optimize ? optimizeIR(ir) : ir;
|
|
2072
2148
|
if (options?.minifyNames)
|
|
2073
2149
|
lowered = minifyLocalNamesIR(lowered);
|
|
2074
|
-
const
|
|
2150
|
+
const domainMaps = options?.domainConfig ? domainRefsFromConfig(options.domainConfig) : undefined;
|
|
2075
2151
|
return encodeIR(lowered, {
|
|
2076
|
-
|
|
2152
|
+
...domainMaps,
|
|
2077
2153
|
dedupeValues: options?.dedupeValues,
|
|
2078
2154
|
dedupeMinBytes: options?.dedupeMinBytes
|
|
2079
2155
|
});
|
|
@@ -2199,6 +2275,9 @@ semantics.addOperation("toIR", {
|
|
|
2199
2275
|
ExistenceExpr_or(left, _or, right) {
|
|
2200
2276
|
return { type: "binary", op: "or", left: left.toIR(), right: right.toIR() };
|
|
2201
2277
|
},
|
|
2278
|
+
ExistenceExpr_nor(left, _nor, right) {
|
|
2279
|
+
return { type: "binary", op: "nor", left: left.toIR(), right: right.toIR() };
|
|
2280
|
+
},
|
|
2202
2281
|
BitExpr_and(left, _op, right) {
|
|
2203
2282
|
return { type: "binary", op: "bitAnd", left: left.toIR(), right: right.toIR() };
|
|
2204
2283
|
},
|
|
@@ -2208,6 +2287,9 @@ semantics.addOperation("toIR", {
|
|
|
2208
2287
|
BitExpr_or(left, _op, right) {
|
|
2209
2288
|
return { type: "binary", op: "bitOr", left: left.toIR(), right: right.toIR() };
|
|
2210
2289
|
},
|
|
2290
|
+
RangeExpr_range(left, _op, right) {
|
|
2291
|
+
return { type: "range", from: left.toIR(), to: right.toIR() };
|
|
2292
|
+
},
|
|
2211
2293
|
CompareExpr_binary(left, op, right) {
|
|
2212
2294
|
const map = {
|
|
2213
2295
|
"==": "eq",
|
|
@@ -2248,6 +2330,9 @@ semantics.addOperation("toIR", {
|
|
|
2248
2330
|
UnaryExpr_not(_op, value) {
|
|
2249
2331
|
return { type: "unary", op: "not", value: value.toIR() };
|
|
2250
2332
|
},
|
|
2333
|
+
UnaryExpr_logicalNot(_not, value) {
|
|
2334
|
+
return { type: "unary", op: "logicalNot", value: value.toIR() };
|
|
2335
|
+
},
|
|
2251
2336
|
UnaryExpr_delete(_del, place) {
|
|
2252
2337
|
return { type: "unary", op: "delete", value: place.toIR() };
|
|
2253
2338
|
},
|
|
@@ -2301,14 +2386,6 @@ semantics.addOperation("toIR", {
|
|
|
2301
2386
|
ConditionalElse_else(_else, block) {
|
|
2302
2387
|
return { type: "else", block: block.toIR() };
|
|
2303
2388
|
},
|
|
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
2389
|
WhileExpr(_while, condition, _do, block, _end) {
|
|
2313
2390
|
return {
|
|
2314
2391
|
type: "while",
|
|
@@ -2323,30 +2400,44 @@ semantics.addOperation("toIR", {
|
|
|
2323
2400
|
body: block.toIR()
|
|
2324
2401
|
};
|
|
2325
2402
|
},
|
|
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
2403
|
Array_empty(_open, _close) {
|
|
2334
2404
|
return { type: "array", items: [] };
|
|
2335
2405
|
},
|
|
2336
|
-
|
|
2406
|
+
Array_forComprehension(_open, body, _for, binding, _close) {
|
|
2337
2407
|
return {
|
|
2338
2408
|
type: "arrayComprehension",
|
|
2339
2409
|
binding: binding.toIR(),
|
|
2340
2410
|
body: body.toIR()
|
|
2341
2411
|
};
|
|
2342
2412
|
},
|
|
2413
|
+
Array_whileComprehension(_open, body, _while, condition, _close) {
|
|
2414
|
+
return {
|
|
2415
|
+
type: "whileArrayComprehension",
|
|
2416
|
+
condition: condition.toIR(),
|
|
2417
|
+
body: body.toIR()
|
|
2418
|
+
};
|
|
2419
|
+
},
|
|
2420
|
+
Array_inComprehension(_open, body, _in, source, _close) {
|
|
2421
|
+
return {
|
|
2422
|
+
type: "arrayComprehension",
|
|
2423
|
+
binding: { type: "binding:bareIn", source: source.toIR() },
|
|
2424
|
+
body: body.toIR()
|
|
2425
|
+
};
|
|
2426
|
+
},
|
|
2427
|
+
Array_ofComprehension(_open, body, _of, source, _close) {
|
|
2428
|
+
return {
|
|
2429
|
+
type: "arrayComprehension",
|
|
2430
|
+
binding: { type: "binding:bareOf", source: source.toIR() },
|
|
2431
|
+
body: body.toIR()
|
|
2432
|
+
};
|
|
2433
|
+
},
|
|
2343
2434
|
Array_values(_open, items, _close) {
|
|
2344
2435
|
return { type: "array", items: normalizeList(items.toIR()) };
|
|
2345
2436
|
},
|
|
2346
2437
|
Object_empty(_open, _close) {
|
|
2347
2438
|
return { type: "object", entries: [] };
|
|
2348
2439
|
},
|
|
2349
|
-
|
|
2440
|
+
Object_forComprehension(_open, key, _colon, value, _for, binding, _close) {
|
|
2350
2441
|
return {
|
|
2351
2442
|
type: "objectComprehension",
|
|
2352
2443
|
binding: binding.toIR(),
|
|
@@ -2354,6 +2445,30 @@ semantics.addOperation("toIR", {
|
|
|
2354
2445
|
value: value.toIR()
|
|
2355
2446
|
};
|
|
2356
2447
|
},
|
|
2448
|
+
Object_whileComprehension(_open, key, _colon, value, _while, condition, _close) {
|
|
2449
|
+
return {
|
|
2450
|
+
type: "whileObjectComprehension",
|
|
2451
|
+
condition: condition.toIR(),
|
|
2452
|
+
key: key.toIR(),
|
|
2453
|
+
value: value.toIR()
|
|
2454
|
+
};
|
|
2455
|
+
},
|
|
2456
|
+
Object_inComprehension(_open, key, _colon, value, _in, source, _close) {
|
|
2457
|
+
return {
|
|
2458
|
+
type: "objectComprehension",
|
|
2459
|
+
binding: { type: "binding:bareIn", source: source.toIR() },
|
|
2460
|
+
key: key.toIR(),
|
|
2461
|
+
value: value.toIR()
|
|
2462
|
+
};
|
|
2463
|
+
},
|
|
2464
|
+
Object_ofComprehension(_open, key, _colon, value, _of, source, _close) {
|
|
2465
|
+
return {
|
|
2466
|
+
type: "objectComprehension",
|
|
2467
|
+
binding: { type: "binding:bareOf", source: source.toIR() },
|
|
2468
|
+
key: key.toIR(),
|
|
2469
|
+
value: value.toIR()
|
|
2470
|
+
};
|
|
2471
|
+
},
|
|
2357
2472
|
Object_pairs(_open, pairs, _close) {
|
|
2358
2473
|
return {
|
|
2359
2474
|
type: "object",
|
|
@@ -2382,6 +2497,18 @@ semantics.addOperation("toIR", {
|
|
|
2382
2497
|
source: source.toIR()
|
|
2383
2498
|
};
|
|
2384
2499
|
},
|
|
2500
|
+
IterBinding_bareIn(_in, source) {
|
|
2501
|
+
return {
|
|
2502
|
+
type: "binding:bareIn",
|
|
2503
|
+
source: source.toIR()
|
|
2504
|
+
};
|
|
2505
|
+
},
|
|
2506
|
+
IterBinding_bareOf(_of, source) {
|
|
2507
|
+
return {
|
|
2508
|
+
type: "binding:bareOf",
|
|
2509
|
+
source: source.toIR()
|
|
2510
|
+
};
|
|
2511
|
+
},
|
|
2385
2512
|
Pair(key, _colon, value) {
|
|
2386
2513
|
return { key: key.toIR(), value: value.toIR() };
|
|
2387
2514
|
},
|
|
@@ -2445,6 +2572,9 @@ semantics.addOperation("toIR", {
|
|
|
2445
2572
|
BooleanKw(_kw) {
|
|
2446
2573
|
return { type: "identifier", name: "boolean" };
|
|
2447
2574
|
},
|
|
2575
|
+
SizeKw(_kw) {
|
|
2576
|
+
return { type: "identifier", name: "size" };
|
|
2577
|
+
},
|
|
2448
2578
|
identifier(_a, _b) {
|
|
2449
2579
|
return { type: "identifier", name: this.sourceString };
|
|
2450
2580
|
},
|