@creationix/rex 0.3.0 → 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 +3 -2
- package/rex-cli.js +730 -285
- package/rex-cli.ts +34 -13
- package/rex-repl.js +695 -270
- package/rex-repl.ts +39 -5
- package/rex.js +370 -188
- package/rex.ohm +73 -25
- package/rex.ohm-bundle.cjs +1 -1
- package/rex.ohm-bundle.d.ts +31 -8
- package/rex.ohm-bundle.js +1 -1
- package/rex.ts +390 -206
- package/rexc-interpreter.ts +277 -90
package/rex-repl.js
CHANGED
|
@@ -14,31 +14,32 @@ function byteLength(value) {
|
|
|
14
14
|
return Buffer.byteLength(value, "utf8");
|
|
15
15
|
}
|
|
16
16
|
var OPCODE_IDS = {
|
|
17
|
-
do:
|
|
18
|
-
add:
|
|
19
|
-
sub:
|
|
20
|
-
mul:
|
|
21
|
-
div:
|
|
22
|
-
eq:
|
|
23
|
-
neq:
|
|
24
|
-
lt:
|
|
25
|
-
lte:
|
|
26
|
-
gt:
|
|
27
|
-
gte:
|
|
28
|
-
and:
|
|
29
|
-
or:
|
|
30
|
-
xor:
|
|
31
|
-
not:
|
|
32
|
-
boolean:
|
|
33
|
-
number:
|
|
34
|
-
string:
|
|
35
|
-
array:
|
|
36
|
-
object:
|
|
37
|
-
mod:
|
|
38
|
-
neg:
|
|
17
|
+
do: "",
|
|
18
|
+
add: "ad",
|
|
19
|
+
sub: "sb",
|
|
20
|
+
mul: "ml",
|
|
21
|
+
div: "dv",
|
|
22
|
+
eq: "eq",
|
|
23
|
+
neq: "nq",
|
|
24
|
+
lt: "lt",
|
|
25
|
+
lte: "le",
|
|
26
|
+
gt: "gt",
|
|
27
|
+
gte: "ge",
|
|
28
|
+
and: "an",
|
|
29
|
+
or: "or",
|
|
30
|
+
xor: "xr",
|
|
31
|
+
not: "nt",
|
|
32
|
+
boolean: "bt",
|
|
33
|
+
number: "nm",
|
|
34
|
+
string: "st",
|
|
35
|
+
array: "ar",
|
|
36
|
+
object: "ob",
|
|
37
|
+
mod: "md",
|
|
38
|
+
neg: "ng",
|
|
39
|
+
range: "rn",
|
|
40
|
+
size: "sz"
|
|
39
41
|
};
|
|
40
|
-
var
|
|
41
|
-
var DOMAIN_DIGIT_INDEX = new Map(Array.from(DIGITS).map((char, index) => [char, index]));
|
|
42
|
+
var KEYWORD_OPCODES = new Set(["boolean", "number", "string", "array", "object", "size"]);
|
|
42
43
|
var BINARY_TO_OPCODE = {
|
|
43
44
|
add: "add",
|
|
44
45
|
sub: "sub",
|
|
@@ -151,12 +152,33 @@ function encodeBareOrLengthString(value) {
|
|
|
151
152
|
return `${value}:`;
|
|
152
153
|
return `${encodeUint(byteLength(value))},${value}`;
|
|
153
154
|
}
|
|
155
|
+
var DEC_PARTS = /^(-?\d)(?:\.(\d+))?e([+-]\d+)$/;
|
|
156
|
+
function splitDecimal(num) {
|
|
157
|
+
const match = num.toExponential().match(DEC_PARTS);
|
|
158
|
+
if (!match)
|
|
159
|
+
throw new Error(`Failed to split decimal for ${num}`);
|
|
160
|
+
const [, b1, b2 = "", e1] = match;
|
|
161
|
+
const base = Number.parseInt(b1 + b2, 10);
|
|
162
|
+
const exp = Number.parseInt(e1, 10) - b2.length;
|
|
163
|
+
return { base, exp };
|
|
164
|
+
}
|
|
165
|
+
function encodeDecimal(significand, power) {
|
|
166
|
+
return `${encodeZigzag(power)}*${encodeInt(significand)}`;
|
|
167
|
+
}
|
|
154
168
|
function encodeNumberNode(node) {
|
|
155
169
|
const numberValue = node.value;
|
|
156
|
-
if (
|
|
157
|
-
|
|
158
|
-
if (
|
|
159
|
-
return
|
|
170
|
+
if (Number.isNaN(numberValue))
|
|
171
|
+
return "nan'";
|
|
172
|
+
if (numberValue === Infinity)
|
|
173
|
+
return "inf'";
|
|
174
|
+
if (numberValue === -Infinity)
|
|
175
|
+
return "nif'";
|
|
176
|
+
if (Number.isInteger(numberValue)) {
|
|
177
|
+
const { base, exp } = splitDecimal(numberValue);
|
|
178
|
+
if (exp >= 0 && exp <= 4)
|
|
179
|
+
return encodeInt(numberValue);
|
|
180
|
+
return encodeDecimal(base, exp);
|
|
181
|
+
}
|
|
160
182
|
const raw = node.raw.toLowerCase();
|
|
161
183
|
const sign = raw.startsWith("-") ? -1 : 1;
|
|
162
184
|
const unsigned = sign < 0 ? raw.slice(1) : raw;
|
|
@@ -179,10 +201,10 @@ function encodeNumberNode(node) {
|
|
|
179
201
|
significand /= 10;
|
|
180
202
|
power += 1;
|
|
181
203
|
}
|
|
182
|
-
return
|
|
204
|
+
return encodeDecimal(significand, power);
|
|
183
205
|
}
|
|
184
206
|
function encodeOpcode(opcode) {
|
|
185
|
-
return `${
|
|
207
|
+
return `${OPCODE_IDS[opcode]}%`;
|
|
186
208
|
}
|
|
187
209
|
function encodeCallParts(parts) {
|
|
188
210
|
return `(${parts.join("")})`;
|
|
@@ -199,7 +221,7 @@ function addOptionalPrefix(encoded) {
|
|
|
199
221
|
let payload = encoded;
|
|
200
222
|
if (encoded.startsWith("?(") || encoded.startsWith("!(") || encoded.startsWith("|(") || encoded.startsWith("&(") || encoded.startsWith(">(") || encoded.startsWith("<(") || encoded.startsWith("#(")) {
|
|
201
223
|
payload = encoded.slice(2, -1);
|
|
202
|
-
} else if (encoded.startsWith(">[") || encoded.startsWith(">{")) {
|
|
224
|
+
} else if (encoded.startsWith(">[") || encoded.startsWith(">{") || encoded.startsWith("<[") || encoded.startsWith("<{") || encoded.startsWith("#[") || encoded.startsWith("#{")) {
|
|
203
225
|
payload = encoded.slice(2, -1);
|
|
204
226
|
} else if (encoded.startsWith("[") || encoded.startsWith("{") || encoded.startsWith("(")) {
|
|
205
227
|
payload = encoded.slice(1, -1);
|
|
@@ -210,7 +232,7 @@ function addOptionalPrefix(encoded) {
|
|
|
210
232
|
}
|
|
211
233
|
function encodeBlockExpression(block) {
|
|
212
234
|
if (block.length === 0)
|
|
213
|
-
return "
|
|
235
|
+
return "un'";
|
|
214
236
|
if (block.length === 1)
|
|
215
237
|
return encodeNode(block[0]);
|
|
216
238
|
return encodeCallParts([encodeOpcode("do"), ...block.map((node) => encodeNode(node))]);
|
|
@@ -227,9 +249,13 @@ function encodeConditionalElse(elseBranch) {
|
|
|
227
249
|
};
|
|
228
250
|
return encodeNode(nested);
|
|
229
251
|
}
|
|
252
|
+
function encodeDomainLookup(shortCode, tag) {
|
|
253
|
+
return `${shortCode}${tag}`;
|
|
254
|
+
}
|
|
230
255
|
function encodeNavigation(node) {
|
|
231
256
|
const domainRefs = activeEncodeOptions?.domainRefs;
|
|
232
|
-
|
|
257
|
+
const domainOpcodes = activeEncodeOptions?.domainOpcodes;
|
|
258
|
+
if ((domainRefs || domainOpcodes) && node.target.type === "identifier") {
|
|
233
259
|
const staticPath = [node.target.name];
|
|
234
260
|
for (const segment of node.segments) {
|
|
235
261
|
if (segment.type !== "static")
|
|
@@ -238,14 +264,17 @@ function encodeNavigation(node) {
|
|
|
238
264
|
}
|
|
239
265
|
for (let pathLength = staticPath.length;pathLength >= 1; pathLength -= 1) {
|
|
240
266
|
const dottedName = staticPath.slice(0, pathLength).join(".");
|
|
241
|
-
const
|
|
242
|
-
|
|
267
|
+
const refCode = domainRefs?.[dottedName];
|
|
268
|
+
const opcodeCode = domainOpcodes?.[dottedName];
|
|
269
|
+
const shortCode = refCode ?? opcodeCode;
|
|
270
|
+
if (shortCode === undefined)
|
|
243
271
|
continue;
|
|
272
|
+
const tag = refCode !== undefined ? "'" : "%";
|
|
244
273
|
const consumedStaticSegments = pathLength - 1;
|
|
245
274
|
if (consumedStaticSegments === node.segments.length) {
|
|
246
|
-
return
|
|
275
|
+
return encodeDomainLookup(shortCode, tag);
|
|
247
276
|
}
|
|
248
|
-
const parts2 = [
|
|
277
|
+
const parts2 = [encodeDomainLookup(shortCode, tag)];
|
|
249
278
|
for (const segment of node.segments.slice(consumedStaticSegments)) {
|
|
250
279
|
if (segment.type === "static")
|
|
251
280
|
parts2.push(encodeBareOrLengthString(segment.key));
|
|
@@ -271,9 +300,12 @@ function encodeWhile(node) {
|
|
|
271
300
|
}
|
|
272
301
|
function encodeFor(node) {
|
|
273
302
|
const body = addOptionalPrefix(encodeBlockExpression(node.body));
|
|
274
|
-
if (node.binding.type === "binding:
|
|
303
|
+
if (node.binding.type === "binding:bareIn") {
|
|
275
304
|
return `>(${encodeNode(node.binding.source)}${body})`;
|
|
276
305
|
}
|
|
306
|
+
if (node.binding.type === "binding:bareOf") {
|
|
307
|
+
return `<(${encodeNode(node.binding.source)}${body})`;
|
|
308
|
+
}
|
|
277
309
|
if (node.binding.type === "binding:valueIn") {
|
|
278
310
|
return `>(${encodeNode(node.binding.source)}${node.binding.value}$${body})`;
|
|
279
311
|
}
|
|
@@ -284,30 +316,47 @@ function encodeFor(node) {
|
|
|
284
316
|
}
|
|
285
317
|
function encodeArrayComprehension(node) {
|
|
286
318
|
const body = addOptionalPrefix(encodeNode(node.body));
|
|
287
|
-
if (node.binding.type === "binding:
|
|
319
|
+
if (node.binding.type === "binding:bareIn") {
|
|
288
320
|
return `>[${encodeNode(node.binding.source)}${body}]`;
|
|
289
321
|
}
|
|
322
|
+
if (node.binding.type === "binding:bareOf") {
|
|
323
|
+
return `<[${encodeNode(node.binding.source)}${body}]`;
|
|
324
|
+
}
|
|
290
325
|
if (node.binding.type === "binding:valueIn") {
|
|
291
326
|
return `>[${encodeNode(node.binding.source)}${node.binding.value}$${body}]`;
|
|
292
327
|
}
|
|
293
328
|
if (node.binding.type === "binding:keyValueIn") {
|
|
294
329
|
return `>[${encodeNode(node.binding.source)}${node.binding.key}$${node.binding.value}$${body}]`;
|
|
295
330
|
}
|
|
296
|
-
return
|
|
331
|
+
return `<[${encodeNode(node.binding.source)}${node.binding.key}$${body}]`;
|
|
297
332
|
}
|
|
298
333
|
function encodeObjectComprehension(node) {
|
|
299
334
|
const key = addOptionalPrefix(encodeNode(node.key));
|
|
300
335
|
const value = addOptionalPrefix(encodeNode(node.value));
|
|
301
|
-
if (node.binding.type === "binding:
|
|
336
|
+
if (node.binding.type === "binding:bareIn") {
|
|
302
337
|
return `>{${encodeNode(node.binding.source)}${key}${value}}`;
|
|
303
338
|
}
|
|
339
|
+
if (node.binding.type === "binding:bareOf") {
|
|
340
|
+
return `<{${encodeNode(node.binding.source)}${key}${value}}`;
|
|
341
|
+
}
|
|
304
342
|
if (node.binding.type === "binding:valueIn") {
|
|
305
343
|
return `>{${encodeNode(node.binding.source)}${node.binding.value}$${key}${value}}`;
|
|
306
344
|
}
|
|
307
345
|
if (node.binding.type === "binding:keyValueIn") {
|
|
308
346
|
return `>{${encodeNode(node.binding.source)}${node.binding.key}$${node.binding.value}$${key}${value}}`;
|
|
309
347
|
}
|
|
310
|
-
return
|
|
348
|
+
return `<{${encodeNode(node.binding.source)}${node.binding.key}$${key}${value}}`;
|
|
349
|
+
}
|
|
350
|
+
function encodeWhileArrayComprehension(node) {
|
|
351
|
+
const cond = encodeNode(node.condition);
|
|
352
|
+
const body = addOptionalPrefix(encodeNode(node.body));
|
|
353
|
+
return `#[${cond}${body}]`;
|
|
354
|
+
}
|
|
355
|
+
function encodeWhileObjectComprehension(node) {
|
|
356
|
+
const cond = encodeNode(node.condition);
|
|
357
|
+
const key = addOptionalPrefix(encodeNode(node.key));
|
|
358
|
+
const value = addOptionalPrefix(encodeNode(node.value));
|
|
359
|
+
return `#{${cond}${key}${value}}`;
|
|
311
360
|
}
|
|
312
361
|
var activeEncodeOptions;
|
|
313
362
|
function encodeNode(node) {
|
|
@@ -317,7 +366,10 @@ function encodeNode(node) {
|
|
|
317
366
|
case "identifier": {
|
|
318
367
|
const domainRef = activeEncodeOptions?.domainRefs?.[node.name];
|
|
319
368
|
if (domainRef !== undefined)
|
|
320
|
-
return `${
|
|
369
|
+
return `${domainRef}'`;
|
|
370
|
+
const domainOpcode = activeEncodeOptions?.domainOpcodes?.[node.name];
|
|
371
|
+
if (domainOpcode !== undefined)
|
|
372
|
+
return `${domainOpcode}%`;
|
|
321
373
|
return `${node.name}$`;
|
|
322
374
|
}
|
|
323
375
|
case "self":
|
|
@@ -330,11 +382,11 @@ function encodeNode(node) {
|
|
|
330
382
|
return `${encodeUint(node.depth - 1)}@`;
|
|
331
383
|
}
|
|
332
384
|
case "boolean":
|
|
333
|
-
return node.value ? "
|
|
385
|
+
return node.value ? "tr'" : "fl'";
|
|
334
386
|
case "null":
|
|
335
|
-
return "
|
|
387
|
+
return "nl'";
|
|
336
388
|
case "undefined":
|
|
337
|
-
return "
|
|
389
|
+
return "un'";
|
|
338
390
|
case "number":
|
|
339
391
|
return encodeNumberNode(node);
|
|
340
392
|
case "string":
|
|
@@ -345,12 +397,16 @@ function encodeNode(node) {
|
|
|
345
397
|
}
|
|
346
398
|
case "arrayComprehension":
|
|
347
399
|
return encodeArrayComprehension(node);
|
|
400
|
+
case "whileArrayComprehension":
|
|
401
|
+
return encodeWhileArrayComprehension(node);
|
|
348
402
|
case "object": {
|
|
349
403
|
const body = node.entries.map(({ key, value }) => `${encodeNode(key)}${addOptionalPrefix(encodeNode(value))}`).join("");
|
|
350
404
|
return `{${body}}`;
|
|
351
405
|
}
|
|
352
406
|
case "objectComprehension":
|
|
353
407
|
return encodeObjectComprehension(node);
|
|
408
|
+
case "whileObjectComprehension":
|
|
409
|
+
return encodeWhileObjectComprehension(node);
|
|
354
410
|
case "key":
|
|
355
411
|
return encodeBareOrLengthString(node.name);
|
|
356
412
|
case "group":
|
|
@@ -360,6 +416,10 @@ function encodeNode(node) {
|
|
|
360
416
|
return `~${encodeNode(node.value)}`;
|
|
361
417
|
if (node.op === "neg")
|
|
362
418
|
return encodeCallParts([encodeOpcode("neg"), encodeNode(node.value)]);
|
|
419
|
+
if (node.op === "logicalNot") {
|
|
420
|
+
const val = encodeNode(node.value);
|
|
421
|
+
return `!(${val}tr')`;
|
|
422
|
+
}
|
|
363
423
|
return encodeCallParts([encodeOpcode("not"), encodeNode(node.value)]);
|
|
364
424
|
case "binary":
|
|
365
425
|
if (node.op === "and") {
|
|
@@ -378,12 +438,19 @@ function encodeNode(node) {
|
|
|
378
438
|
}).join("");
|
|
379
439
|
return `|(${body})`;
|
|
380
440
|
}
|
|
441
|
+
if (node.op === "nor") {
|
|
442
|
+
const left = encodeNode(node.left);
|
|
443
|
+
const right = addOptionalPrefix(encodeNode(node.right));
|
|
444
|
+
return `!(${left}${right})`;
|
|
445
|
+
}
|
|
381
446
|
return encodeCallParts([
|
|
382
447
|
encodeOpcode(BINARY_TO_OPCODE[node.op]),
|
|
383
448
|
encodeNode(node.left),
|
|
384
449
|
encodeNode(node.right)
|
|
385
450
|
]);
|
|
386
451
|
case "assign": {
|
|
452
|
+
if (node.op === ":=")
|
|
453
|
+
return `/${encodeNode(node.place)}${addOptionalPrefix(encodeNode(node.value))}`;
|
|
387
454
|
if (node.op === "=")
|
|
388
455
|
return `=${encodeNode(node.place)}${addOptionalPrefix(encodeNode(node.value))}`;
|
|
389
456
|
const opcode = ASSIGN_COMPOUND_TO_OPCODE[node.op];
|
|
@@ -394,8 +461,12 @@ function encodeNode(node) {
|
|
|
394
461
|
}
|
|
395
462
|
case "navigation":
|
|
396
463
|
return encodeNavigation(node);
|
|
397
|
-
case "call":
|
|
464
|
+
case "call": {
|
|
465
|
+
if (node.callee.type === "identifier" && KEYWORD_OPCODES.has(node.callee.name)) {
|
|
466
|
+
return encodeCallParts([encodeOpcode(node.callee.name), ...node.args.map((arg) => encodeNode(arg))]);
|
|
467
|
+
}
|
|
398
468
|
return encodeCallParts([encodeNode(node.callee), ...node.args.map((arg) => encodeNode(arg))]);
|
|
469
|
+
}
|
|
399
470
|
case "conditional": {
|
|
400
471
|
const opener = node.head === "when" ? "?(" : "!(";
|
|
401
472
|
const cond = encodeNode(node.condition);
|
|
@@ -403,6 +474,8 @@ function encodeNode(node) {
|
|
|
403
474
|
const elseExpr = node.elseBranch ? addOptionalPrefix(encodeConditionalElse(node.elseBranch)) : "";
|
|
404
475
|
return `${opener}${cond}${thenExpr}${elseExpr})`;
|
|
405
476
|
}
|
|
477
|
+
case "range":
|
|
478
|
+
return encodeCallParts([encodeOpcode("range"), encodeNode(node.from), encodeNode(node.to)]);
|
|
406
479
|
case "for":
|
|
407
480
|
return encodeFor(node);
|
|
408
481
|
case "while":
|
|
@@ -439,6 +512,18 @@ function isPlainObject(value) {
|
|
|
439
512
|
function isBareKeyName(key) {
|
|
440
513
|
return /^[A-Za-z_][A-Za-z0-9_-]*$/.test(key);
|
|
441
514
|
}
|
|
515
|
+
function isNumericKey(key) {
|
|
516
|
+
if (key === "")
|
|
517
|
+
return false;
|
|
518
|
+
return String(Number(key)) === key && Number.isFinite(Number(key));
|
|
519
|
+
}
|
|
520
|
+
function stringifyKey(key) {
|
|
521
|
+
if (isBareKeyName(key))
|
|
522
|
+
return key;
|
|
523
|
+
if (isNumericKey(key))
|
|
524
|
+
return key;
|
|
525
|
+
return stringifyString(key);
|
|
526
|
+
}
|
|
442
527
|
function stringifyString(value) {
|
|
443
528
|
return JSON.stringify(value);
|
|
444
529
|
}
|
|
@@ -450,8 +535,12 @@ function stringifyInline(value) {
|
|
|
450
535
|
if (typeof value === "boolean")
|
|
451
536
|
return value ? "true" : "false";
|
|
452
537
|
if (typeof value === "number") {
|
|
453
|
-
if (
|
|
454
|
-
|
|
538
|
+
if (Number.isNaN(value))
|
|
539
|
+
return "nan";
|
|
540
|
+
if (value === Infinity)
|
|
541
|
+
return "inf";
|
|
542
|
+
if (value === -Infinity)
|
|
543
|
+
return "-inf";
|
|
455
544
|
return String(value);
|
|
456
545
|
}
|
|
457
546
|
if (typeof value === "string")
|
|
@@ -465,7 +554,7 @@ function stringifyInline(value) {
|
|
|
465
554
|
const entries = Object.entries(value);
|
|
466
555
|
if (entries.length === 0)
|
|
467
556
|
return "{}";
|
|
468
|
-
const body = entries.map(([key, item]) => `${
|
|
557
|
+
const body = entries.map(([key, item]) => `${stringifyKey(key)}: ${stringifyInline(item)}`).join(" ");
|
|
469
558
|
return `{${body}}`;
|
|
470
559
|
}
|
|
471
560
|
throw new Error(`Rex stringify() cannot encode value of type ${typeof value}`);
|
|
@@ -502,7 +591,7 @@ ${indent}]`;
|
|
|
502
591
|
if (entries.length === 0)
|
|
503
592
|
return "{}";
|
|
504
593
|
const lines = entries.map(([key, item]) => {
|
|
505
|
-
const keyText =
|
|
594
|
+
const keyText = stringifyKey(key);
|
|
506
595
|
const rendered = stringifyPretty(item, depth + 1, indentSize, maxWidth);
|
|
507
596
|
return `${childIndent}${keyText}: ${rendered}`;
|
|
508
597
|
});
|
|
@@ -517,78 +606,55 @@ function domainRefsFromConfig(config) {
|
|
|
517
606
|
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
518
607
|
throw new Error("Domain config must be an object");
|
|
519
608
|
}
|
|
520
|
-
const
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
return refs;
|
|
527
|
-
}
|
|
528
|
-
function decodeDomainRefKey(refText) {
|
|
529
|
-
if (!refText)
|
|
530
|
-
throw new Error("Domain ref key cannot be empty");
|
|
531
|
-
if (!/^[0-9A-Za-z_-]+$/.test(refText)) {
|
|
532
|
-
throw new Error(`Invalid domain ref key '${refText}' (must use base64 alphabet 0-9a-zA-Z-_)`);
|
|
533
|
-
}
|
|
534
|
-
if (refText.length > 1 && refText[0] === "0") {
|
|
535
|
-
throw new Error(`Invalid domain ref key '${refText}' (leading zeroes are not allowed)`);
|
|
536
|
-
}
|
|
537
|
-
if (/^[1-9]$/.test(refText)) {
|
|
538
|
-
throw new Error(`Invalid domain ref key '${refText}' (reserved by core language)`);
|
|
539
|
-
}
|
|
540
|
-
let value = 0;
|
|
541
|
-
for (const char of refText) {
|
|
542
|
-
const digit = DOMAIN_DIGIT_INDEX.get(char);
|
|
543
|
-
if (digit === undefined)
|
|
544
|
-
throw new Error(`Invalid domain ref key '${refText}'`);
|
|
545
|
-
value = value * 64 + digit;
|
|
546
|
-
if (value > Number.MAX_SAFE_INTEGER) {
|
|
547
|
-
throw new Error(`Invalid domain ref key '${refText}' (must fit in 53 bits)`);
|
|
548
|
-
}
|
|
609
|
+
const configObj = config;
|
|
610
|
+
const domainRefs = {};
|
|
611
|
+
const domainOpcodes = {};
|
|
612
|
+
const dataSection = configObj.data;
|
|
613
|
+
if (dataSection && typeof dataSection === "object" && !Array.isArray(dataSection)) {
|
|
614
|
+
mapConfigEntries(dataSection, domainRefs);
|
|
549
615
|
}
|
|
550
|
-
|
|
551
|
-
|
|
616
|
+
const functionsSection = configObj.functions;
|
|
617
|
+
if (functionsSection && typeof functionsSection === "object" && !Array.isArray(functionsSection)) {
|
|
618
|
+
mapConfigEntries(functionsSection, domainOpcodes);
|
|
552
619
|
}
|
|
553
|
-
return
|
|
620
|
+
return { domainRefs, domainOpcodes };
|
|
554
621
|
}
|
|
555
622
|
function mapConfigEntries(entries, refs) {
|
|
556
623
|
const sourceKindByRoot = new Map;
|
|
557
624
|
for (const root of Object.keys(refs)) {
|
|
558
625
|
sourceKindByRoot.set(root, "explicit");
|
|
559
626
|
}
|
|
560
|
-
for (const [
|
|
627
|
+
for (const [shortCode, rawEntry] of Object.entries(entries)) {
|
|
561
628
|
const entry = rawEntry;
|
|
562
629
|
if (!entry || typeof entry !== "object")
|
|
563
630
|
continue;
|
|
564
631
|
if (!Array.isArray(entry.names))
|
|
565
632
|
continue;
|
|
566
|
-
const refId = decodeDomainRefKey(refText);
|
|
567
633
|
for (const rawName of entry.names) {
|
|
568
634
|
if (typeof rawName !== "string")
|
|
569
635
|
continue;
|
|
570
|
-
const
|
|
571
|
-
if (
|
|
572
|
-
throw new Error(`Conflicting refs for '${rawName}': ${
|
|
636
|
+
const existingRef = refs[rawName];
|
|
637
|
+
if (existingRef !== undefined && existingRef !== shortCode) {
|
|
638
|
+
throw new Error(`Conflicting refs for '${rawName}': ${existingRef} vs ${shortCode}`);
|
|
573
639
|
}
|
|
574
|
-
refs[rawName] =
|
|
640
|
+
refs[rawName] = shortCode;
|
|
575
641
|
const root = rawName.split(".")[0];
|
|
576
642
|
if (!root)
|
|
577
643
|
continue;
|
|
578
644
|
const currentKind = rawName.includes(".") ? "implicit" : "explicit";
|
|
579
645
|
const existing = refs[root];
|
|
580
646
|
if (existing !== undefined) {
|
|
581
|
-
if (existing ===
|
|
647
|
+
if (existing === shortCode)
|
|
582
648
|
continue;
|
|
583
649
|
const existingKind = sourceKindByRoot.get(root) ?? "explicit";
|
|
584
650
|
if (currentKind === "explicit") {
|
|
585
|
-
throw new Error(`Conflicting refs for '${root}': ${existing} vs ${
|
|
651
|
+
throw new Error(`Conflicting refs for '${root}': ${existing} vs ${shortCode}`);
|
|
586
652
|
}
|
|
587
653
|
if (existingKind === "explicit")
|
|
588
654
|
continue;
|
|
589
655
|
continue;
|
|
590
656
|
}
|
|
591
|
-
refs[root] =
|
|
657
|
+
refs[root] = shortCode;
|
|
592
658
|
sourceKindByRoot.set(root, currentKind);
|
|
593
659
|
}
|
|
594
660
|
}
|
|
@@ -872,6 +938,21 @@ function dropBindingNames(env, binding) {
|
|
|
872
938
|
clearBinding(env, binding.key);
|
|
873
939
|
}
|
|
874
940
|
}
|
|
941
|
+
function optimizeBinding(binding, sourceEnv, currentDepth) {
|
|
942
|
+
const source = optimizeNode(binding.source, sourceEnv, currentDepth);
|
|
943
|
+
switch (binding.type) {
|
|
944
|
+
case "binding:bareIn":
|
|
945
|
+
return { type: "binding:bareIn", source };
|
|
946
|
+
case "binding:bareOf":
|
|
947
|
+
return { type: "binding:bareOf", source };
|
|
948
|
+
case "binding:valueIn":
|
|
949
|
+
return { type: "binding:valueIn", value: binding.value, source };
|
|
950
|
+
case "binding:keyValueIn":
|
|
951
|
+
return { type: "binding:keyValueIn", key: binding.key, value: binding.value, source };
|
|
952
|
+
case "binding:keyOf":
|
|
953
|
+
return { type: "binding:keyOf", key: binding.key, source };
|
|
954
|
+
}
|
|
955
|
+
}
|
|
875
956
|
function collectReads(node, out) {
|
|
876
957
|
switch (node.type) {
|
|
877
958
|
case "identifier":
|
|
@@ -894,11 +975,20 @@ function collectReads(node, out) {
|
|
|
894
975
|
collectReads(node.binding.source, out);
|
|
895
976
|
collectReads(node.body, out);
|
|
896
977
|
return;
|
|
978
|
+
case "whileArrayComprehension":
|
|
979
|
+
collectReads(node.condition, out);
|
|
980
|
+
collectReads(node.body, out);
|
|
981
|
+
return;
|
|
897
982
|
case "objectComprehension":
|
|
898
983
|
collectReads(node.binding.source, out);
|
|
899
984
|
collectReads(node.key, out);
|
|
900
985
|
collectReads(node.value, out);
|
|
901
986
|
return;
|
|
987
|
+
case "whileObjectComprehension":
|
|
988
|
+
collectReads(node.condition, out);
|
|
989
|
+
collectReads(node.key, out);
|
|
990
|
+
collectReads(node.value, out);
|
|
991
|
+
return;
|
|
902
992
|
case "unary":
|
|
903
993
|
collectReads(node.value, out);
|
|
904
994
|
return;
|
|
@@ -935,6 +1025,10 @@ function collectReads(node, out) {
|
|
|
935
1025
|
for (const part of node.body)
|
|
936
1026
|
collectReads(part, out);
|
|
937
1027
|
return;
|
|
1028
|
+
case "range":
|
|
1029
|
+
collectReads(node.from, out);
|
|
1030
|
+
collectReads(node.to, out);
|
|
1031
|
+
return;
|
|
938
1032
|
case "program":
|
|
939
1033
|
for (const part of node.body)
|
|
940
1034
|
collectReads(part, out);
|
|
@@ -979,6 +1073,8 @@ function isPureNode(node) {
|
|
|
979
1073
|
return node.op !== "delete" && isPureNode(node.value);
|
|
980
1074
|
case "binary":
|
|
981
1075
|
return isPureNode(node.left) && isPureNode(node.right);
|
|
1076
|
+
case "range":
|
|
1077
|
+
return isPureNode(node.from) && isPureNode(node.to);
|
|
982
1078
|
default:
|
|
983
1079
|
return false;
|
|
984
1080
|
}
|
|
@@ -1043,6 +1139,8 @@ function hasIdentifierRead(node, name, asPlace = false) {
|
|
|
1043
1139
|
return hasIdentifierRead(node.value, name, node.op === "delete");
|
|
1044
1140
|
case "binary":
|
|
1045
1141
|
return hasIdentifierRead(node.left, name) || hasIdentifierRead(node.right, name);
|
|
1142
|
+
case "range":
|
|
1143
|
+
return hasIdentifierRead(node.from, name) || hasIdentifierRead(node.to, name);
|
|
1046
1144
|
case "assign":
|
|
1047
1145
|
return hasIdentifierRead(node.place, name, true) || hasIdentifierRead(node.value, name);
|
|
1048
1146
|
default:
|
|
@@ -1065,6 +1163,8 @@ function countIdentifierReads(node, name, asPlace = false) {
|
|
|
1065
1163
|
return countIdentifierReads(node.value, name, node.op === "delete");
|
|
1066
1164
|
case "binary":
|
|
1067
1165
|
return countIdentifierReads(node.left, name) + countIdentifierReads(node.right, name);
|
|
1166
|
+
case "range":
|
|
1167
|
+
return countIdentifierReads(node.from, name) + countIdentifierReads(node.to, name);
|
|
1068
1168
|
case "assign":
|
|
1069
1169
|
return countIdentifierReads(node.place, name, true) + countIdentifierReads(node.value, name);
|
|
1070
1170
|
default:
|
|
@@ -1119,6 +1219,12 @@ function replaceIdentifier(node, name, replacement, asPlace = false) {
|
|
|
1119
1219
|
place: replaceIdentifier(node.place, name, replacement, true),
|
|
1120
1220
|
value: replaceIdentifier(node.value, name, replacement)
|
|
1121
1221
|
};
|
|
1222
|
+
case "range":
|
|
1223
|
+
return {
|
|
1224
|
+
type: "range",
|
|
1225
|
+
from: replaceIdentifier(node.from, name, replacement),
|
|
1226
|
+
to: replaceIdentifier(node.to, name, replacement)
|
|
1227
|
+
};
|
|
1122
1228
|
default:
|
|
1123
1229
|
return node;
|
|
1124
1230
|
}
|
|
@@ -1159,7 +1265,16 @@ function inlineAdjacentPureAssignments(block) {
|
|
|
1159
1265
|
return out;
|
|
1160
1266
|
}
|
|
1161
1267
|
function toNumberNode(value) {
|
|
1162
|
-
|
|
1268
|
+
let raw;
|
|
1269
|
+
if (Number.isNaN(value))
|
|
1270
|
+
raw = "nan";
|
|
1271
|
+
else if (value === Infinity)
|
|
1272
|
+
raw = "inf";
|
|
1273
|
+
else if (value === -Infinity)
|
|
1274
|
+
raw = "-inf";
|
|
1275
|
+
else
|
|
1276
|
+
raw = String(value);
|
|
1277
|
+
return { type: "number", raw, value };
|
|
1163
1278
|
}
|
|
1164
1279
|
function toStringNode(value) {
|
|
1165
1280
|
return { type: "string", raw: JSON.stringify(value) };
|
|
@@ -1171,7 +1286,7 @@ function toLiteralNode(value) {
|
|
|
1171
1286
|
return { type: "null" };
|
|
1172
1287
|
if (typeof value === "boolean")
|
|
1173
1288
|
return { type: "boolean", value };
|
|
1174
|
-
if (typeof value === "number"
|
|
1289
|
+
if (typeof value === "number")
|
|
1175
1290
|
return toNumberNode(value);
|
|
1176
1291
|
if (typeof value === "string")
|
|
1177
1292
|
return toStringNode(value);
|
|
@@ -1254,6 +1369,9 @@ function foldUnary(op, value) {
|
|
|
1254
1369
|
return ~value;
|
|
1255
1370
|
return;
|
|
1256
1371
|
}
|
|
1372
|
+
if (op === "logicalNot") {
|
|
1373
|
+
return value === undefined ? true : undefined;
|
|
1374
|
+
}
|
|
1257
1375
|
return;
|
|
1258
1376
|
}
|
|
1259
1377
|
function foldBinary(op, left, right) {
|
|
@@ -1424,6 +1542,12 @@ function optimizeNode(node, env, currentDepth, asPlace = false) {
|
|
|
1424
1542
|
}
|
|
1425
1543
|
return { type: "binary", op: node.op, left, right };
|
|
1426
1544
|
}
|
|
1545
|
+
case "range":
|
|
1546
|
+
return {
|
|
1547
|
+
type: "range",
|
|
1548
|
+
from: optimizeNode(node.from, env, currentDepth),
|
|
1549
|
+
to: optimizeNode(node.to, env, currentDepth)
|
|
1550
|
+
};
|
|
1427
1551
|
case "navigation": {
|
|
1428
1552
|
const target = optimizeNode(node.target, env, currentDepth);
|
|
1429
1553
|
const segments = node.segments.map((segment) => segment.type === "static" ? segment : { type: "dynamic", key: optimizeNode(segment.key, env, currentDepth) });
|
|
@@ -1534,31 +1658,7 @@ function optimizeNode(node, env, currentDepth, asPlace = false) {
|
|
|
1534
1658
|
}
|
|
1535
1659
|
case "for": {
|
|
1536
1660
|
const sourceEnv = cloneOptimizeEnv(env);
|
|
1537
|
-
const binding = (
|
|
1538
|
-
if (node.binding.type === "binding:expr") {
|
|
1539
|
-
return { type: "binding:expr", source: optimizeNode(node.binding.source, sourceEnv, currentDepth) };
|
|
1540
|
-
}
|
|
1541
|
-
if (node.binding.type === "binding:valueIn") {
|
|
1542
|
-
return {
|
|
1543
|
-
type: "binding:valueIn",
|
|
1544
|
-
value: node.binding.value,
|
|
1545
|
-
source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
|
|
1546
|
-
};
|
|
1547
|
-
}
|
|
1548
|
-
if (node.binding.type === "binding:keyValueIn") {
|
|
1549
|
-
return {
|
|
1550
|
-
type: "binding:keyValueIn",
|
|
1551
|
-
key: node.binding.key,
|
|
1552
|
-
value: node.binding.value,
|
|
1553
|
-
source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
|
|
1554
|
-
};
|
|
1555
|
-
}
|
|
1556
|
-
return {
|
|
1557
|
-
type: "binding:keyOf",
|
|
1558
|
-
key: node.binding.key,
|
|
1559
|
-
source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
|
|
1560
|
-
};
|
|
1561
|
-
})();
|
|
1661
|
+
const binding = optimizeBinding(node.binding, sourceEnv, currentDepth);
|
|
1562
1662
|
const bodyEnv = cloneOptimizeEnv(env);
|
|
1563
1663
|
dropBindingNames(bodyEnv, binding);
|
|
1564
1664
|
return {
|
|
@@ -1569,20 +1669,7 @@ function optimizeNode(node, env, currentDepth, asPlace = false) {
|
|
|
1569
1669
|
}
|
|
1570
1670
|
case "arrayComprehension": {
|
|
1571
1671
|
const sourceEnv = cloneOptimizeEnv(env);
|
|
1572
|
-
const binding =
|
|
1573
|
-
type: "binding:valueIn",
|
|
1574
|
-
value: node.binding.value,
|
|
1575
|
-
source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
|
|
1576
|
-
} : node.binding.type === "binding:keyValueIn" ? {
|
|
1577
|
-
type: "binding:keyValueIn",
|
|
1578
|
-
key: node.binding.key,
|
|
1579
|
-
value: node.binding.value,
|
|
1580
|
-
source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
|
|
1581
|
-
} : {
|
|
1582
|
-
type: "binding:keyOf",
|
|
1583
|
-
key: node.binding.key,
|
|
1584
|
-
source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
|
|
1585
|
-
};
|
|
1672
|
+
const binding = optimizeBinding(node.binding, sourceEnv, currentDepth);
|
|
1586
1673
|
const bodyEnv = cloneOptimizeEnv(env);
|
|
1587
1674
|
dropBindingNames(bodyEnv, binding);
|
|
1588
1675
|
return {
|
|
@@ -1591,22 +1678,15 @@ function optimizeNode(node, env, currentDepth, asPlace = false) {
|
|
|
1591
1678
|
body: optimizeNode(node.body, bodyEnv, currentDepth + 1)
|
|
1592
1679
|
};
|
|
1593
1680
|
}
|
|
1681
|
+
case "whileArrayComprehension":
|
|
1682
|
+
return {
|
|
1683
|
+
type: "whileArrayComprehension",
|
|
1684
|
+
condition: optimizeNode(node.condition, env, currentDepth),
|
|
1685
|
+
body: optimizeNode(node.body, env, currentDepth + 1)
|
|
1686
|
+
};
|
|
1594
1687
|
case "objectComprehension": {
|
|
1595
1688
|
const sourceEnv = cloneOptimizeEnv(env);
|
|
1596
|
-
const binding =
|
|
1597
|
-
type: "binding:valueIn",
|
|
1598
|
-
value: node.binding.value,
|
|
1599
|
-
source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
|
|
1600
|
-
} : node.binding.type === "binding:keyValueIn" ? {
|
|
1601
|
-
type: "binding:keyValueIn",
|
|
1602
|
-
key: node.binding.key,
|
|
1603
|
-
value: node.binding.value,
|
|
1604
|
-
source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
|
|
1605
|
-
} : {
|
|
1606
|
-
type: "binding:keyOf",
|
|
1607
|
-
key: node.binding.key,
|
|
1608
|
-
source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
|
|
1609
|
-
};
|
|
1689
|
+
const binding = optimizeBinding(node.binding, sourceEnv, currentDepth);
|
|
1610
1690
|
const bodyEnv = cloneOptimizeEnv(env);
|
|
1611
1691
|
dropBindingNames(bodyEnv, binding);
|
|
1612
1692
|
return {
|
|
@@ -1616,6 +1696,13 @@ function optimizeNode(node, env, currentDepth, asPlace = false) {
|
|
|
1616
1696
|
value: optimizeNode(node.value, bodyEnv, currentDepth + 1)
|
|
1617
1697
|
};
|
|
1618
1698
|
}
|
|
1699
|
+
case "whileObjectComprehension":
|
|
1700
|
+
return {
|
|
1701
|
+
type: "whileObjectComprehension",
|
|
1702
|
+
condition: optimizeNode(node.condition, env, currentDepth),
|
|
1703
|
+
key: optimizeNode(node.key, env, currentDepth + 1),
|
|
1704
|
+
value: optimizeNode(node.value, env, currentDepth + 1)
|
|
1705
|
+
};
|
|
1619
1706
|
default:
|
|
1620
1707
|
return node;
|
|
1621
1708
|
}
|
|
@@ -1667,6 +1754,10 @@ function collectLocalBindings(node, locals) {
|
|
|
1667
1754
|
collectLocalBindings(node.left, locals);
|
|
1668
1755
|
collectLocalBindings(node.right, locals);
|
|
1669
1756
|
return;
|
|
1757
|
+
case "range":
|
|
1758
|
+
collectLocalBindings(node.from, locals);
|
|
1759
|
+
collectLocalBindings(node.to, locals);
|
|
1760
|
+
return;
|
|
1670
1761
|
case "conditional":
|
|
1671
1762
|
collectLocalBindings(node.condition, locals);
|
|
1672
1763
|
for (const part of node.thenBlock)
|
|
@@ -1683,11 +1774,20 @@ function collectLocalBindings(node, locals) {
|
|
|
1683
1774
|
collectLocalBindingFromBinding(node.binding, locals);
|
|
1684
1775
|
collectLocalBindings(node.body, locals);
|
|
1685
1776
|
return;
|
|
1777
|
+
case "whileArrayComprehension":
|
|
1778
|
+
collectLocalBindings(node.condition, locals);
|
|
1779
|
+
collectLocalBindings(node.body, locals);
|
|
1780
|
+
return;
|
|
1686
1781
|
case "objectComprehension":
|
|
1687
1782
|
collectLocalBindingFromBinding(node.binding, locals);
|
|
1688
1783
|
collectLocalBindings(node.key, locals);
|
|
1689
1784
|
collectLocalBindings(node.value, locals);
|
|
1690
1785
|
return;
|
|
1786
|
+
case "whileObjectComprehension":
|
|
1787
|
+
collectLocalBindings(node.condition, locals);
|
|
1788
|
+
collectLocalBindings(node.key, locals);
|
|
1789
|
+
collectLocalBindings(node.value, locals);
|
|
1790
|
+
return;
|
|
1691
1791
|
default:
|
|
1692
1792
|
return;
|
|
1693
1793
|
}
|
|
@@ -1779,6 +1879,10 @@ function collectNameFrequencies(node, locals, frequencies, order, nextOrder) {
|
|
|
1779
1879
|
collectNameFrequencies(node.left, locals, frequencies, order, nextOrder);
|
|
1780
1880
|
collectNameFrequencies(node.right, locals, frequencies, order, nextOrder);
|
|
1781
1881
|
return;
|
|
1882
|
+
case "range":
|
|
1883
|
+
collectNameFrequencies(node.from, locals, frequencies, order, nextOrder);
|
|
1884
|
+
collectNameFrequencies(node.to, locals, frequencies, order, nextOrder);
|
|
1885
|
+
return;
|
|
1782
1886
|
case "conditional":
|
|
1783
1887
|
collectNameFrequencies(node.condition, locals, frequencies, order, nextOrder);
|
|
1784
1888
|
for (const part of node.thenBlock)
|
|
@@ -1795,11 +1899,20 @@ function collectNameFrequencies(node, locals, frequencies, order, nextOrder) {
|
|
|
1795
1899
|
collectNameFrequenciesBinding(node.binding, locals, frequencies, order, nextOrder);
|
|
1796
1900
|
collectNameFrequencies(node.body, locals, frequencies, order, nextOrder);
|
|
1797
1901
|
return;
|
|
1902
|
+
case "whileArrayComprehension":
|
|
1903
|
+
collectNameFrequencies(node.condition, locals, frequencies, order, nextOrder);
|
|
1904
|
+
collectNameFrequencies(node.body, locals, frequencies, order, nextOrder);
|
|
1905
|
+
return;
|
|
1798
1906
|
case "objectComprehension":
|
|
1799
1907
|
collectNameFrequenciesBinding(node.binding, locals, frequencies, order, nextOrder);
|
|
1800
1908
|
collectNameFrequencies(node.key, locals, frequencies, order, nextOrder);
|
|
1801
1909
|
collectNameFrequencies(node.value, locals, frequencies, order, nextOrder);
|
|
1802
1910
|
return;
|
|
1911
|
+
case "whileObjectComprehension":
|
|
1912
|
+
collectNameFrequencies(node.condition, locals, frequencies, order, nextOrder);
|
|
1913
|
+
collectNameFrequencies(node.key, locals, frequencies, order, nextOrder);
|
|
1914
|
+
collectNameFrequencies(node.value, locals, frequencies, order, nextOrder);
|
|
1915
|
+
return;
|
|
1803
1916
|
default:
|
|
1804
1917
|
return;
|
|
1805
1918
|
}
|
|
@@ -1874,6 +1987,12 @@ function renameLocalNames(node, map) {
|
|
|
1874
1987
|
left: renameLocalNames(node.left, map),
|
|
1875
1988
|
right: renameLocalNames(node.right, map)
|
|
1876
1989
|
};
|
|
1990
|
+
case "range":
|
|
1991
|
+
return {
|
|
1992
|
+
type: "range",
|
|
1993
|
+
from: renameLocalNames(node.from, map),
|
|
1994
|
+
to: renameLocalNames(node.to, map)
|
|
1995
|
+
};
|
|
1877
1996
|
case "assign": {
|
|
1878
1997
|
const place = node.place.type === "identifier" && map.has(node.place.name) ? { type: "identifier", name: map.get(node.place.name) } : renameLocalNames(node.place, map);
|
|
1879
1998
|
return {
|
|
@@ -1903,6 +2022,12 @@ function renameLocalNames(node, map) {
|
|
|
1903
2022
|
binding: renameLocalNamesBinding(node.binding, map),
|
|
1904
2023
|
body: renameLocalNames(node.body, map)
|
|
1905
2024
|
};
|
|
2025
|
+
case "whileArrayComprehension":
|
|
2026
|
+
return {
|
|
2027
|
+
type: "whileArrayComprehension",
|
|
2028
|
+
condition: renameLocalNames(node.condition, map),
|
|
2029
|
+
body: renameLocalNames(node.body, map)
|
|
2030
|
+
};
|
|
1906
2031
|
case "objectComprehension":
|
|
1907
2032
|
return {
|
|
1908
2033
|
type: "objectComprehension",
|
|
@@ -1910,34 +2035,31 @@ function renameLocalNames(node, map) {
|
|
|
1910
2035
|
key: renameLocalNames(node.key, map),
|
|
1911
2036
|
value: renameLocalNames(node.value, map)
|
|
1912
2037
|
};
|
|
2038
|
+
case "whileObjectComprehension":
|
|
2039
|
+
return {
|
|
2040
|
+
type: "whileObjectComprehension",
|
|
2041
|
+
condition: renameLocalNames(node.condition, map),
|
|
2042
|
+
key: renameLocalNames(node.key, map),
|
|
2043
|
+
value: renameLocalNames(node.value, map)
|
|
2044
|
+
};
|
|
1913
2045
|
default:
|
|
1914
2046
|
return node;
|
|
1915
2047
|
}
|
|
1916
2048
|
}
|
|
1917
2049
|
function renameLocalNamesBinding(binding, map) {
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
type: "binding:
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
type: "binding:keyValueIn",
|
|
1931
|
-
key: map.get(binding.key) ?? binding.key,
|
|
1932
|
-
value: map.get(binding.value) ?? binding.value,
|
|
1933
|
-
source: renameLocalNames(binding.source, map)
|
|
1934
|
-
};
|
|
2050
|
+
const source = renameLocalNames(binding.source, map);
|
|
2051
|
+
switch (binding.type) {
|
|
2052
|
+
case "binding:bareIn":
|
|
2053
|
+
return { type: "binding:bareIn", source };
|
|
2054
|
+
case "binding:bareOf":
|
|
2055
|
+
return { type: "binding:bareOf", source };
|
|
2056
|
+
case "binding:valueIn":
|
|
2057
|
+
return { type: "binding:valueIn", value: map.get(binding.value) ?? binding.value, source };
|
|
2058
|
+
case "binding:keyValueIn":
|
|
2059
|
+
return { type: "binding:keyValueIn", key: map.get(binding.key) ?? binding.key, value: map.get(binding.value) ?? binding.value, source };
|
|
2060
|
+
case "binding:keyOf":
|
|
2061
|
+
return { type: "binding:keyOf", key: map.get(binding.key) ?? binding.key, source };
|
|
1935
2062
|
}
|
|
1936
|
-
return {
|
|
1937
|
-
type: "binding:keyOf",
|
|
1938
|
-
key: map.get(binding.key) ?? binding.key,
|
|
1939
|
-
source: renameLocalNames(binding.source, map)
|
|
1940
|
-
};
|
|
1941
2063
|
}
|
|
1942
2064
|
function renameLocalNamesElse(elseBranch, map) {
|
|
1943
2065
|
if (elseBranch.type === "else") {
|
|
@@ -1984,14 +2106,20 @@ function compile(source, options) {
|
|
|
1984
2106
|
let lowered = options?.optimize ? optimizeIR(ir) : ir;
|
|
1985
2107
|
if (options?.minifyNames)
|
|
1986
2108
|
lowered = minifyLocalNamesIR(lowered);
|
|
1987
|
-
const
|
|
2109
|
+
const domainMaps = options?.domainConfig ? domainRefsFromConfig(options.domainConfig) : undefined;
|
|
1988
2110
|
return encodeIR(lowered, {
|
|
1989
|
-
|
|
2111
|
+
...domainMaps,
|
|
1990
2112
|
dedupeValues: options?.dedupeValues,
|
|
1991
2113
|
dedupeMinBytes: options?.dedupeMinBytes
|
|
1992
2114
|
});
|
|
1993
2115
|
}
|
|
1994
2116
|
function parseNumber(raw) {
|
|
2117
|
+
if (raw === "nan")
|
|
2118
|
+
return NaN;
|
|
2119
|
+
if (raw === "inf")
|
|
2120
|
+
return Infinity;
|
|
2121
|
+
if (raw === "-inf")
|
|
2122
|
+
return -Infinity;
|
|
1995
2123
|
if (/^-?0x/i.test(raw))
|
|
1996
2124
|
return parseInt(raw, 16);
|
|
1997
2125
|
if (/^-?0b/i.test(raw)) {
|
|
@@ -2106,6 +2234,9 @@ semantics.addOperation("toIR", {
|
|
|
2106
2234
|
ExistenceExpr_or(left, _or, right) {
|
|
2107
2235
|
return { type: "binary", op: "or", left: left.toIR(), right: right.toIR() };
|
|
2108
2236
|
},
|
|
2237
|
+
ExistenceExpr_nor(left, _nor, right) {
|
|
2238
|
+
return { type: "binary", op: "nor", left: left.toIR(), right: right.toIR() };
|
|
2239
|
+
},
|
|
2109
2240
|
BitExpr_and(left, _op, right) {
|
|
2110
2241
|
return { type: "binary", op: "bitAnd", left: left.toIR(), right: right.toIR() };
|
|
2111
2242
|
},
|
|
@@ -2115,6 +2246,9 @@ semantics.addOperation("toIR", {
|
|
|
2115
2246
|
BitExpr_or(left, _op, right) {
|
|
2116
2247
|
return { type: "binary", op: "bitOr", left: left.toIR(), right: right.toIR() };
|
|
2117
2248
|
},
|
|
2249
|
+
RangeExpr_range(left, _op, right) {
|
|
2250
|
+
return { type: "range", from: left.toIR(), to: right.toIR() };
|
|
2251
|
+
},
|
|
2118
2252
|
CompareExpr_binary(left, op, right) {
|
|
2119
2253
|
const map = {
|
|
2120
2254
|
"==": "eq",
|
|
@@ -2155,6 +2289,9 @@ semantics.addOperation("toIR", {
|
|
|
2155
2289
|
UnaryExpr_not(_op, value) {
|
|
2156
2290
|
return { type: "unary", op: "not", value: value.toIR() };
|
|
2157
2291
|
},
|
|
2292
|
+
UnaryExpr_logicalNot(_not, value) {
|
|
2293
|
+
return { type: "unary", op: "logicalNot", value: value.toIR() };
|
|
2294
|
+
},
|
|
2158
2295
|
UnaryExpr_delete(_del, place) {
|
|
2159
2296
|
return { type: "unary", op: "delete", value: place.toIR() };
|
|
2160
2297
|
},
|
|
@@ -2208,14 +2345,6 @@ semantics.addOperation("toIR", {
|
|
|
2208
2345
|
ConditionalElse_else(_else, block) {
|
|
2209
2346
|
return { type: "else", block: block.toIR() };
|
|
2210
2347
|
},
|
|
2211
|
-
DoExpr(_do, block, _end) {
|
|
2212
|
-
const body = block.toIR();
|
|
2213
|
-
if (body.length === 0)
|
|
2214
|
-
return { type: "undefined" };
|
|
2215
|
-
if (body.length === 1)
|
|
2216
|
-
return body[0];
|
|
2217
|
-
return { type: "program", body };
|
|
2218
|
-
},
|
|
2219
2348
|
WhileExpr(_while, condition, _do, block, _end) {
|
|
2220
2349
|
return {
|
|
2221
2350
|
type: "while",
|
|
@@ -2230,30 +2359,44 @@ semantics.addOperation("toIR", {
|
|
|
2230
2359
|
body: block.toIR()
|
|
2231
2360
|
};
|
|
2232
2361
|
},
|
|
2233
|
-
BindingExpr(iterOrExpr) {
|
|
2234
|
-
const node = iterOrExpr.toIR();
|
|
2235
|
-
if (typeof node === "object" && node && "type" in node && String(node.type).startsWith("binding:")) {
|
|
2236
|
-
return node;
|
|
2237
|
-
}
|
|
2238
|
-
return { type: "binding:expr", source: node };
|
|
2239
|
-
},
|
|
2240
2362
|
Array_empty(_open, _close) {
|
|
2241
2363
|
return { type: "array", items: [] };
|
|
2242
2364
|
},
|
|
2243
|
-
|
|
2365
|
+
Array_forComprehension(_open, body, _for, binding, _close) {
|
|
2244
2366
|
return {
|
|
2245
2367
|
type: "arrayComprehension",
|
|
2246
2368
|
binding: binding.toIR(),
|
|
2247
2369
|
body: body.toIR()
|
|
2248
2370
|
};
|
|
2249
2371
|
},
|
|
2372
|
+
Array_whileComprehension(_open, body, _while, condition, _close) {
|
|
2373
|
+
return {
|
|
2374
|
+
type: "whileArrayComprehension",
|
|
2375
|
+
condition: condition.toIR(),
|
|
2376
|
+
body: body.toIR()
|
|
2377
|
+
};
|
|
2378
|
+
},
|
|
2379
|
+
Array_inComprehension(_open, body, _in, source, _close) {
|
|
2380
|
+
return {
|
|
2381
|
+
type: "arrayComprehension",
|
|
2382
|
+
binding: { type: "binding:bareIn", source: source.toIR() },
|
|
2383
|
+
body: body.toIR()
|
|
2384
|
+
};
|
|
2385
|
+
},
|
|
2386
|
+
Array_ofComprehension(_open, body, _of, source, _close) {
|
|
2387
|
+
return {
|
|
2388
|
+
type: "arrayComprehension",
|
|
2389
|
+
binding: { type: "binding:bareOf", source: source.toIR() },
|
|
2390
|
+
body: body.toIR()
|
|
2391
|
+
};
|
|
2392
|
+
},
|
|
2250
2393
|
Array_values(_open, items, _close) {
|
|
2251
2394
|
return { type: "array", items: normalizeList(items.toIR()) };
|
|
2252
2395
|
},
|
|
2253
2396
|
Object_empty(_open, _close) {
|
|
2254
2397
|
return { type: "object", entries: [] };
|
|
2255
2398
|
},
|
|
2256
|
-
|
|
2399
|
+
Object_forComprehension(_open, key, _colon, value, _for, binding, _close) {
|
|
2257
2400
|
return {
|
|
2258
2401
|
type: "objectComprehension",
|
|
2259
2402
|
binding: binding.toIR(),
|
|
@@ -2261,6 +2404,30 @@ semantics.addOperation("toIR", {
|
|
|
2261
2404
|
value: value.toIR()
|
|
2262
2405
|
};
|
|
2263
2406
|
},
|
|
2407
|
+
Object_whileComprehension(_open, key, _colon, value, _while, condition, _close) {
|
|
2408
|
+
return {
|
|
2409
|
+
type: "whileObjectComprehension",
|
|
2410
|
+
condition: condition.toIR(),
|
|
2411
|
+
key: key.toIR(),
|
|
2412
|
+
value: value.toIR()
|
|
2413
|
+
};
|
|
2414
|
+
},
|
|
2415
|
+
Object_inComprehension(_open, key, _colon, value, _in, source, _close) {
|
|
2416
|
+
return {
|
|
2417
|
+
type: "objectComprehension",
|
|
2418
|
+
binding: { type: "binding:bareIn", source: source.toIR() },
|
|
2419
|
+
key: key.toIR(),
|
|
2420
|
+
value: value.toIR()
|
|
2421
|
+
};
|
|
2422
|
+
},
|
|
2423
|
+
Object_ofComprehension(_open, key, _colon, value, _of, source, _close) {
|
|
2424
|
+
return {
|
|
2425
|
+
type: "objectComprehension",
|
|
2426
|
+
binding: { type: "binding:bareOf", source: source.toIR() },
|
|
2427
|
+
key: key.toIR(),
|
|
2428
|
+
value: value.toIR()
|
|
2429
|
+
};
|
|
2430
|
+
},
|
|
2264
2431
|
Object_pairs(_open, pairs, _close) {
|
|
2265
2432
|
return {
|
|
2266
2433
|
type: "object",
|
|
@@ -2289,6 +2456,18 @@ semantics.addOperation("toIR", {
|
|
|
2289
2456
|
source: source.toIR()
|
|
2290
2457
|
};
|
|
2291
2458
|
},
|
|
2459
|
+
IterBinding_bareIn(_in, source) {
|
|
2460
|
+
return {
|
|
2461
|
+
type: "binding:bareIn",
|
|
2462
|
+
source: source.toIR()
|
|
2463
|
+
};
|
|
2464
|
+
},
|
|
2465
|
+
IterBinding_bareOf(_of, source) {
|
|
2466
|
+
return {
|
|
2467
|
+
type: "binding:bareOf",
|
|
2468
|
+
source: source.toIR()
|
|
2469
|
+
};
|
|
2470
|
+
},
|
|
2292
2471
|
Pair(key, _colon, value) {
|
|
2293
2472
|
return { key: key.toIR(), value: value.toIR() };
|
|
2294
2473
|
},
|
|
@@ -2352,6 +2531,9 @@ semantics.addOperation("toIR", {
|
|
|
2352
2531
|
BooleanKw(_kw) {
|
|
2353
2532
|
return { type: "identifier", name: "boolean" };
|
|
2354
2533
|
},
|
|
2534
|
+
SizeKw(_kw) {
|
|
2535
|
+
return { type: "identifier", name: "size" };
|
|
2536
|
+
},
|
|
2355
2537
|
identifier(_a, _b) {
|
|
2356
2538
|
return { type: "identifier", name: this.sourceString };
|
|
2357
2539
|
},
|
|
@@ -2370,28 +2552,30 @@ semantics.addOperation("toIR", {
|
|
|
2370
2552
|
var DIGITS2 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_";
|
|
2371
2553
|
var digitMap = new Map(Array.from(DIGITS2).map((char, index) => [char, index]));
|
|
2372
2554
|
var OPCODES = {
|
|
2373
|
-
do:
|
|
2374
|
-
add:
|
|
2375
|
-
sub:
|
|
2376
|
-
mul:
|
|
2377
|
-
div:
|
|
2378
|
-
eq:
|
|
2379
|
-
neq:
|
|
2380
|
-
lt:
|
|
2381
|
-
lte:
|
|
2382
|
-
gt:
|
|
2383
|
-
gte:
|
|
2384
|
-
and:
|
|
2385
|
-
or:
|
|
2386
|
-
xor:
|
|
2387
|
-
not:
|
|
2388
|
-
boolean:
|
|
2389
|
-
number:
|
|
2390
|
-
string:
|
|
2391
|
-
array:
|
|
2392
|
-
object:
|
|
2393
|
-
mod:
|
|
2394
|
-
neg:
|
|
2555
|
+
do: "",
|
|
2556
|
+
add: "ad",
|
|
2557
|
+
sub: "sb",
|
|
2558
|
+
mul: "ml",
|
|
2559
|
+
div: "dv",
|
|
2560
|
+
eq: "eq",
|
|
2561
|
+
neq: "nq",
|
|
2562
|
+
lt: "lt",
|
|
2563
|
+
lte: "le",
|
|
2564
|
+
gt: "gt",
|
|
2565
|
+
gte: "ge",
|
|
2566
|
+
and: "an",
|
|
2567
|
+
or: "or",
|
|
2568
|
+
xor: "xr",
|
|
2569
|
+
not: "nt",
|
|
2570
|
+
boolean: "bt",
|
|
2571
|
+
number: "nm",
|
|
2572
|
+
string: "st",
|
|
2573
|
+
array: "ar",
|
|
2574
|
+
object: "ob",
|
|
2575
|
+
mod: "md",
|
|
2576
|
+
neg: "ng",
|
|
2577
|
+
range: "rn",
|
|
2578
|
+
size: "sz"
|
|
2395
2579
|
};
|
|
2396
2580
|
function decodePrefix(text, start, end) {
|
|
2397
2581
|
let value = 0;
|
|
@@ -2415,7 +2599,6 @@ class CursorInterpreter {
|
|
|
2415
2599
|
pos = 0;
|
|
2416
2600
|
state;
|
|
2417
2601
|
selfStack;
|
|
2418
|
-
opcodeMarkers;
|
|
2419
2602
|
pointerCache = new Map;
|
|
2420
2603
|
gasLimit;
|
|
2421
2604
|
gas = 0;
|
|
@@ -2430,25 +2613,22 @@ class CursorInterpreter {
|
|
|
2430
2613
|
this.state = {
|
|
2431
2614
|
vars: ctx.vars ?? {},
|
|
2432
2615
|
refs: {
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2616
|
+
tr: true,
|
|
2617
|
+
fl: false,
|
|
2618
|
+
nl: null,
|
|
2619
|
+
un: undefined,
|
|
2620
|
+
nan: NaN,
|
|
2621
|
+
inf: Infinity,
|
|
2622
|
+
nif: -Infinity,
|
|
2623
|
+
...ctx.refs
|
|
2438
2624
|
}
|
|
2439
2625
|
};
|
|
2440
2626
|
this.selfStack = ctx.selfStack && ctx.selfStack.length > 0 ? [...ctx.selfStack] : [initialSelf];
|
|
2441
2627
|
this.gasLimit = ctx.gasLimit ?? 0;
|
|
2442
|
-
this.opcodeMarkers = Array.from({ length: 256 }, (_, id) => ({ __opcode: id }));
|
|
2443
|
-
for (const [idText, value] of Object.entries(ctx.refs ?? {})) {
|
|
2444
|
-
const id = Number(idText);
|
|
2445
|
-
if (Number.isInteger(id))
|
|
2446
|
-
this.state.refs[id] = value;
|
|
2447
|
-
}
|
|
2448
2628
|
if (ctx.opcodes) {
|
|
2449
|
-
for (const [
|
|
2629
|
+
for (const [key, op] of Object.entries(ctx.opcodes)) {
|
|
2450
2630
|
if (op)
|
|
2451
|
-
this.customOpcodes.set(
|
|
2631
|
+
this.customOpcodes.set(key, op);
|
|
2452
2632
|
}
|
|
2453
2633
|
}
|
|
2454
2634
|
}
|
|
@@ -2509,6 +2689,22 @@ class CursorInterpreter {
|
|
|
2509
2689
|
const end = this.pos;
|
|
2510
2690
|
return { start, end, value: decodePrefix(this.text, start, end), raw: this.text.slice(start, end) };
|
|
2511
2691
|
}
|
|
2692
|
+
advanceByBytes(start, byteCount) {
|
|
2693
|
+
if (byteCount <= 0)
|
|
2694
|
+
return start;
|
|
2695
|
+
let bytes = 0;
|
|
2696
|
+
let index = start;
|
|
2697
|
+
for (const char of this.text.slice(start)) {
|
|
2698
|
+
const charBytes = Buffer.byteLength(char);
|
|
2699
|
+
if (bytes + charBytes > byteCount)
|
|
2700
|
+
break;
|
|
2701
|
+
bytes += charBytes;
|
|
2702
|
+
index += char.length;
|
|
2703
|
+
if (bytes === byteCount)
|
|
2704
|
+
return index;
|
|
2705
|
+
}
|
|
2706
|
+
throw new Error("String container overflows input");
|
|
2707
|
+
}
|
|
2512
2708
|
ensure(char) {
|
|
2513
2709
|
if (this.text[this.pos] !== char)
|
|
2514
2710
|
throw new Error(`Expected '${char}' at ${this.pos}`);
|
|
@@ -2549,36 +2745,34 @@ class CursorInterpreter {
|
|
|
2549
2745
|
const significand = this.evalValue();
|
|
2550
2746
|
if (typeof significand !== "number")
|
|
2551
2747
|
throw new Error("Decimal significand must be numeric");
|
|
2552
|
-
return significand
|
|
2748
|
+
return parseFloat(`${significand}e${power}`);
|
|
2553
2749
|
}
|
|
2554
2750
|
case ":":
|
|
2555
2751
|
this.pos += 1;
|
|
2556
2752
|
return prefix.raw;
|
|
2557
2753
|
case "%":
|
|
2558
2754
|
this.pos += 1;
|
|
2559
|
-
return
|
|
2755
|
+
return { __opcode: prefix.raw };
|
|
2560
2756
|
case "@":
|
|
2561
2757
|
this.pos += 1;
|
|
2562
2758
|
return this.readSelf(prefix.value);
|
|
2563
2759
|
case "'":
|
|
2564
2760
|
this.pos += 1;
|
|
2565
|
-
return this.state.refs[prefix.
|
|
2761
|
+
return this.state.refs[prefix.raw];
|
|
2566
2762
|
case "$":
|
|
2567
2763
|
this.pos += 1;
|
|
2568
2764
|
return this.state.vars[prefix.raw];
|
|
2569
2765
|
case ",": {
|
|
2570
2766
|
this.pos += 1;
|
|
2571
2767
|
const start = this.pos;
|
|
2572
|
-
const end = start
|
|
2573
|
-
if (end > this.text.length)
|
|
2574
|
-
throw new Error("String container overflows input");
|
|
2768
|
+
const end = this.advanceByBytes(start, prefix.value);
|
|
2575
2769
|
const value = this.text.slice(start, end);
|
|
2576
2770
|
this.pos = end;
|
|
2577
2771
|
return value;
|
|
2578
2772
|
}
|
|
2579
2773
|
case "^": {
|
|
2580
2774
|
this.pos += 1;
|
|
2581
|
-
const target = this.pos
|
|
2775
|
+
const target = this.advanceByBytes(this.pos, prefix.value);
|
|
2582
2776
|
if (this.pointerCache.has(target))
|
|
2583
2777
|
return this.pointerCache.get(target);
|
|
2584
2778
|
const save = this.pos;
|
|
@@ -2595,6 +2789,14 @@ class CursorInterpreter {
|
|
|
2595
2789
|
this.writePlace(place, value);
|
|
2596
2790
|
return value;
|
|
2597
2791
|
}
|
|
2792
|
+
case "/": {
|
|
2793
|
+
this.pos += 1;
|
|
2794
|
+
const place = this.readPlace();
|
|
2795
|
+
const oldValue = this.readPlaceValue(place);
|
|
2796
|
+
const newValue = this.evalValue();
|
|
2797
|
+
this.writePlace(place, newValue);
|
|
2798
|
+
return oldValue;
|
|
2799
|
+
}
|
|
2598
2800
|
case "~": {
|
|
2599
2801
|
this.pos += 1;
|
|
2600
2802
|
const place = this.readPlace();
|
|
@@ -2622,7 +2824,7 @@ class CursorInterpreter {
|
|
|
2622
2824
|
case "<":
|
|
2623
2825
|
return this.evalLoopLike(tag);
|
|
2624
2826
|
case "#":
|
|
2625
|
-
return this.
|
|
2827
|
+
return this.evalWhileLike();
|
|
2626
2828
|
default:
|
|
2627
2829
|
throw new Error(`Unexpected tag '${tag}' at ${this.pos}`);
|
|
2628
2830
|
}
|
|
@@ -2835,15 +3037,11 @@ class CursorInterpreter {
|
|
|
2835
3037
|
return entries.map(([key]) => ({ key, value: key }));
|
|
2836
3038
|
return entries.map(([key, value]) => ({ key, value }));
|
|
2837
3039
|
}
|
|
2838
|
-
if (typeof iterable === "
|
|
2839
|
-
const
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
else
|
|
2844
|
-
out.push({ key: index, value: index + 1 });
|
|
2845
|
-
}
|
|
2846
|
-
return out;
|
|
3040
|
+
if (typeof iterable === "string") {
|
|
3041
|
+
const entries = Array.from(iterable);
|
|
3042
|
+
if (keysOnly)
|
|
3043
|
+
return entries.map((_value, index) => ({ key: index, value: index }));
|
|
3044
|
+
return entries.map((value, index) => ({ key: index, value }));
|
|
2847
3045
|
}
|
|
2848
3046
|
return [];
|
|
2849
3047
|
}
|
|
@@ -2888,21 +3086,32 @@ class CursorInterpreter {
|
|
|
2888
3086
|
}
|
|
2889
3087
|
return last;
|
|
2890
3088
|
}
|
|
2891
|
-
|
|
3089
|
+
evalWhileLike() {
|
|
3090
|
+
this.pos += 1;
|
|
3091
|
+
const open = this.text[this.pos];
|
|
3092
|
+
if (!open || !"([{".includes(open))
|
|
3093
|
+
throw new Error(`Expected opener after '#' at ${this.pos}`);
|
|
3094
|
+
const close = open === "(" ? ")" : open === "[" ? "]" : "}";
|
|
2892
3095
|
this.pos += 1;
|
|
2893
|
-
this.ensure("(");
|
|
2894
3096
|
const condStart = this.pos;
|
|
2895
3097
|
const condValue = this.evalValue();
|
|
2896
3098
|
const bodyStart = this.pos;
|
|
2897
|
-
const bodyValueCount = 1;
|
|
3099
|
+
const bodyValueCount = open === "{" ? 2 : 1;
|
|
2898
3100
|
let cursor = bodyStart;
|
|
2899
3101
|
for (let index = 0;index < bodyValueCount; index += 1) {
|
|
2900
3102
|
cursor = this.skipValueFrom(cursor);
|
|
2901
3103
|
}
|
|
2902
3104
|
const bodyEnd = cursor;
|
|
2903
3105
|
this.pos = bodyEnd;
|
|
2904
|
-
this.ensure(
|
|
3106
|
+
this.ensure(close);
|
|
2905
3107
|
const afterClose = this.pos;
|
|
3108
|
+
if (open === "[")
|
|
3109
|
+
return this.evalWhileArrayComprehension(condStart, bodyStart, bodyEnd, afterClose, condValue);
|
|
3110
|
+
if (open === "{")
|
|
3111
|
+
return this.evalWhileObjectComprehension(condStart, bodyStart, bodyEnd, afterClose, condValue);
|
|
3112
|
+
return this.evalWhileLoop(condStart, bodyStart, bodyEnd, afterClose, condValue);
|
|
3113
|
+
}
|
|
3114
|
+
evalWhileLoop(condStart, bodyStart, bodyEnd, afterClose, condValue) {
|
|
2906
3115
|
let last = undefined;
|
|
2907
3116
|
let currentCond = condValue;
|
|
2908
3117
|
while (isDefined(currentCond)) {
|
|
@@ -2923,6 +3132,58 @@ class CursorInterpreter {
|
|
|
2923
3132
|
this.pos = afterClose;
|
|
2924
3133
|
return last;
|
|
2925
3134
|
}
|
|
3135
|
+
evalWhileArrayComprehension(condStart, bodyStart, bodyEnd, afterClose, condValue) {
|
|
3136
|
+
const out = [];
|
|
3137
|
+
let currentCond = condValue;
|
|
3138
|
+
while (isDefined(currentCond)) {
|
|
3139
|
+
this.tick();
|
|
3140
|
+
this.selfStack.push(currentCond);
|
|
3141
|
+
const value = this.evalBodySlice(bodyStart, bodyEnd);
|
|
3142
|
+
this.selfStack.pop();
|
|
3143
|
+
const control = this.handleLoopControl(value);
|
|
3144
|
+
if (control) {
|
|
3145
|
+
if (control.depth > 1)
|
|
3146
|
+
return { kind: control.kind, depth: control.depth - 1 };
|
|
3147
|
+
if (control.kind === "break")
|
|
3148
|
+
break;
|
|
3149
|
+
currentCond = this.evalBodySlice(condStart, bodyStart);
|
|
3150
|
+
continue;
|
|
3151
|
+
}
|
|
3152
|
+
if (isDefined(value))
|
|
3153
|
+
out.push(value);
|
|
3154
|
+
currentCond = this.evalBodySlice(condStart, bodyStart);
|
|
3155
|
+
}
|
|
3156
|
+
this.pos = afterClose;
|
|
3157
|
+
return out;
|
|
3158
|
+
}
|
|
3159
|
+
evalWhileObjectComprehension(condStart, bodyStart, bodyEnd, afterClose, condValue) {
|
|
3160
|
+
const result = {};
|
|
3161
|
+
let currentCond = condValue;
|
|
3162
|
+
while (isDefined(currentCond)) {
|
|
3163
|
+
this.tick();
|
|
3164
|
+
this.selfStack.push(currentCond);
|
|
3165
|
+
const save = this.pos;
|
|
3166
|
+
this.pos = bodyStart;
|
|
3167
|
+
const key = this.evalValue();
|
|
3168
|
+
const value = this.evalValue();
|
|
3169
|
+
this.pos = save;
|
|
3170
|
+
this.selfStack.pop();
|
|
3171
|
+
const control = this.handleLoopControl(value);
|
|
3172
|
+
if (control) {
|
|
3173
|
+
if (control.depth > 1)
|
|
3174
|
+
return { kind: control.kind, depth: control.depth - 1 };
|
|
3175
|
+
if (control.kind === "break")
|
|
3176
|
+
break;
|
|
3177
|
+
currentCond = this.evalBodySlice(condStart, bodyStart);
|
|
3178
|
+
continue;
|
|
3179
|
+
}
|
|
3180
|
+
if (isDefined(value))
|
|
3181
|
+
result[String(key)] = value;
|
|
3182
|
+
currentCond = this.evalBodySlice(condStart, bodyStart);
|
|
3183
|
+
}
|
|
3184
|
+
this.pos = afterClose;
|
|
3185
|
+
return result;
|
|
3186
|
+
}
|
|
2926
3187
|
evalArrayComprehension(iterable, varA, varB, bodyStart, bodyEnd, keysOnly) {
|
|
2927
3188
|
const items = this.iterate(iterable, keysOnly);
|
|
2928
3189
|
const out = [];
|
|
@@ -2991,25 +3252,39 @@ class CursorInterpreter {
|
|
|
2991
3252
|
case OPCODES.do:
|
|
2992
3253
|
return args.length ? args[args.length - 1] : undefined;
|
|
2993
3254
|
case OPCODES.add:
|
|
3255
|
+
if (args[0] === undefined || args[1] === undefined)
|
|
3256
|
+
return;
|
|
2994
3257
|
if (typeof args[0] === "string" || typeof args[1] === "string") {
|
|
2995
|
-
return String(args[0]
|
|
3258
|
+
return String(args[0]) + String(args[1]);
|
|
2996
3259
|
}
|
|
2997
|
-
return Number(args[0]
|
|
3260
|
+
return Number(args[0]) + Number(args[1]);
|
|
2998
3261
|
case OPCODES.sub:
|
|
2999
|
-
|
|
3262
|
+
if (args[0] === undefined || args[1] === undefined)
|
|
3263
|
+
return;
|
|
3264
|
+
return Number(args[0]) - Number(args[1]);
|
|
3000
3265
|
case OPCODES.mul:
|
|
3001
|
-
|
|
3266
|
+
if (args[0] === undefined || args[1] === undefined)
|
|
3267
|
+
return;
|
|
3268
|
+
return Number(args[0]) * Number(args[1]);
|
|
3002
3269
|
case OPCODES.div:
|
|
3003
|
-
|
|
3270
|
+
if (args[0] === undefined || args[1] === undefined)
|
|
3271
|
+
return;
|
|
3272
|
+
return Number(args[0]) / Number(args[1]);
|
|
3004
3273
|
case OPCODES.mod:
|
|
3005
|
-
|
|
3274
|
+
if (args[0] === undefined || args[1] === undefined)
|
|
3275
|
+
return;
|
|
3276
|
+
return Number(args[0]) % Number(args[1]);
|
|
3006
3277
|
case OPCODES.neg:
|
|
3007
|
-
|
|
3278
|
+
if (args[0] === undefined)
|
|
3279
|
+
return;
|
|
3280
|
+
return -Number(args[0]);
|
|
3008
3281
|
case OPCODES.not: {
|
|
3009
3282
|
const value = args[0];
|
|
3283
|
+
if (value === undefined)
|
|
3284
|
+
return;
|
|
3010
3285
|
if (typeof value === "boolean")
|
|
3011
3286
|
return !value;
|
|
3012
|
-
return ~Number(value
|
|
3287
|
+
return ~Number(value);
|
|
3013
3288
|
}
|
|
3014
3289
|
case OPCODES.and: {
|
|
3015
3290
|
const [a, b] = args;
|
|
@@ -3051,6 +3326,25 @@ class CursorInterpreter {
|
|
|
3051
3326
|
return Array.isArray(args[0]) ? args[0] : undefined;
|
|
3052
3327
|
case OPCODES.object:
|
|
3053
3328
|
return args[0] && typeof args[0] === "object" && !Array.isArray(args[0]) ? args[0] : undefined;
|
|
3329
|
+
case OPCODES.range: {
|
|
3330
|
+
const from = Number(args[0]);
|
|
3331
|
+
const to = Number(args[1]);
|
|
3332
|
+
const step = to >= from ? 1 : -1;
|
|
3333
|
+
const out = [];
|
|
3334
|
+
for (let v = from;step > 0 ? v <= to : v >= to; v += step)
|
|
3335
|
+
out.push(v);
|
|
3336
|
+
return out;
|
|
3337
|
+
}
|
|
3338
|
+
case OPCODES.size: {
|
|
3339
|
+
const target = args[0];
|
|
3340
|
+
if (Array.isArray(target))
|
|
3341
|
+
return target.length;
|
|
3342
|
+
if (typeof target === "string")
|
|
3343
|
+
return Array.from(target).length;
|
|
3344
|
+
if (target && typeof target === "object")
|
|
3345
|
+
return Object.keys(target).length;
|
|
3346
|
+
return;
|
|
3347
|
+
}
|
|
3054
3348
|
default:
|
|
3055
3349
|
throw new Error(`Unknown opcode ${id}`);
|
|
3056
3350
|
}
|
|
@@ -3060,10 +3354,59 @@ class CursorInterpreter {
|
|
|
3060
3354
|
for (const key of keys) {
|
|
3061
3355
|
if (current === undefined || current === null)
|
|
3062
3356
|
return;
|
|
3063
|
-
current = current
|
|
3357
|
+
current = this.readProperty(current, key);
|
|
3358
|
+
if (current === undefined)
|
|
3359
|
+
return;
|
|
3064
3360
|
}
|
|
3065
3361
|
return current;
|
|
3066
3362
|
}
|
|
3363
|
+
readProperty(target, key) {
|
|
3364
|
+
const index = this.parseIndexKey(key);
|
|
3365
|
+
if (Array.isArray(target)) {
|
|
3366
|
+
if (index === undefined)
|
|
3367
|
+
return;
|
|
3368
|
+
return target[index];
|
|
3369
|
+
}
|
|
3370
|
+
if (typeof target === "string") {
|
|
3371
|
+
if (index === undefined)
|
|
3372
|
+
return;
|
|
3373
|
+
return Array.from(target)[index];
|
|
3374
|
+
}
|
|
3375
|
+
if (this.isPlainObject(target)) {
|
|
3376
|
+
const prop = String(key);
|
|
3377
|
+
if (!Object.prototype.hasOwnProperty.call(target, prop))
|
|
3378
|
+
return;
|
|
3379
|
+
return target[prop];
|
|
3380
|
+
}
|
|
3381
|
+
return;
|
|
3382
|
+
}
|
|
3383
|
+
canWriteProperty(target, key) {
|
|
3384
|
+
const index = this.parseIndexKey(key);
|
|
3385
|
+
if (Array.isArray(target)) {
|
|
3386
|
+
if (index === undefined)
|
|
3387
|
+
return;
|
|
3388
|
+
return { kind: "array", index };
|
|
3389
|
+
}
|
|
3390
|
+
if (this.isPlainObject(target))
|
|
3391
|
+
return { kind: "object" };
|
|
3392
|
+
return;
|
|
3393
|
+
}
|
|
3394
|
+
parseIndexKey(key) {
|
|
3395
|
+
if (typeof key === "number" && Number.isInteger(key) && key >= 0)
|
|
3396
|
+
return key;
|
|
3397
|
+
if (typeof key !== "string" || key.length === 0)
|
|
3398
|
+
return;
|
|
3399
|
+
if (!/^(0|[1-9]\d*)$/.test(key))
|
|
3400
|
+
return;
|
|
3401
|
+
const index = Number(key);
|
|
3402
|
+
return Number.isSafeInteger(index) ? index : undefined;
|
|
3403
|
+
}
|
|
3404
|
+
isPlainObject(value) {
|
|
3405
|
+
if (!value || typeof value !== "object")
|
|
3406
|
+
return false;
|
|
3407
|
+
const proto = Object.getPrototypeOf(value);
|
|
3408
|
+
return proto === Object.prototype || proto === null;
|
|
3409
|
+
}
|
|
3067
3410
|
readPlace() {
|
|
3068
3411
|
this.skipNonCode();
|
|
3069
3412
|
const direct = this.readRootVarOrRefIfPresent();
|
|
@@ -3118,13 +3461,13 @@ class CursorInterpreter {
|
|
|
3118
3461
|
}
|
|
3119
3462
|
this.pos += 1;
|
|
3120
3463
|
return {
|
|
3121
|
-
root:
|
|
3464
|
+
root: prefix.raw,
|
|
3122
3465
|
isRef: tag === "'"
|
|
3123
3466
|
};
|
|
3124
3467
|
}
|
|
3125
3468
|
writePlace(place, value) {
|
|
3126
3469
|
const rootTable = place.isRef ? this.state.refs : this.state.vars;
|
|
3127
|
-
const rootKey =
|
|
3470
|
+
const rootKey = place.root;
|
|
3128
3471
|
if (place.keys.length === 0) {
|
|
3129
3472
|
rootTable[rootKey] = value;
|
|
3130
3473
|
return;
|
|
@@ -3135,17 +3478,48 @@ class CursorInterpreter {
|
|
|
3135
3478
|
rootTable[rootKey] = target;
|
|
3136
3479
|
}
|
|
3137
3480
|
for (let index = 0;index < place.keys.length - 1; index += 1) {
|
|
3138
|
-
const key =
|
|
3139
|
-
const
|
|
3481
|
+
const key = place.keys[index];
|
|
3482
|
+
const access2 = this.canWriteProperty(target, key);
|
|
3483
|
+
if (!access2)
|
|
3484
|
+
return;
|
|
3485
|
+
if (access2.kind === "array") {
|
|
3486
|
+
const next2 = target[access2.index];
|
|
3487
|
+
if (!next2 || typeof next2 !== "object")
|
|
3488
|
+
target[access2.index] = {};
|
|
3489
|
+
target = target[access2.index];
|
|
3490
|
+
continue;
|
|
3491
|
+
}
|
|
3492
|
+
const prop = String(key);
|
|
3493
|
+
const next = target[prop];
|
|
3140
3494
|
if (!next || typeof next !== "object")
|
|
3141
|
-
target[
|
|
3142
|
-
target = target[
|
|
3495
|
+
target[prop] = {};
|
|
3496
|
+
target = target[prop];
|
|
3497
|
+
}
|
|
3498
|
+
const lastKey = place.keys[place.keys.length - 1];
|
|
3499
|
+
const access = this.canWriteProperty(target, lastKey);
|
|
3500
|
+
if (!access)
|
|
3501
|
+
return;
|
|
3502
|
+
if (access.kind === "array") {
|
|
3503
|
+
target[access.index] = value;
|
|
3504
|
+
return;
|
|
3505
|
+
}
|
|
3506
|
+
target[String(lastKey)] = value;
|
|
3507
|
+
}
|
|
3508
|
+
readPlaceValue(place) {
|
|
3509
|
+
const rootTable = place.isRef ? this.state.refs : this.state.vars;
|
|
3510
|
+
let current = rootTable[place.root];
|
|
3511
|
+
for (const key of place.keys) {
|
|
3512
|
+
if (current === undefined || current === null)
|
|
3513
|
+
return;
|
|
3514
|
+
current = this.readProperty(current, key);
|
|
3515
|
+
if (current === undefined)
|
|
3516
|
+
return;
|
|
3143
3517
|
}
|
|
3144
|
-
|
|
3518
|
+
return current;
|
|
3145
3519
|
}
|
|
3146
3520
|
deletePlace(place) {
|
|
3147
3521
|
const rootTable = place.isRef ? this.state.refs : this.state.vars;
|
|
3148
|
-
const rootKey =
|
|
3522
|
+
const rootKey = place.root;
|
|
3149
3523
|
if (place.keys.length === 0) {
|
|
3150
3524
|
delete rootTable[rootKey];
|
|
3151
3525
|
return;
|
|
@@ -3154,11 +3528,29 @@ class CursorInterpreter {
|
|
|
3154
3528
|
if (!target || typeof target !== "object")
|
|
3155
3529
|
return;
|
|
3156
3530
|
for (let index = 0;index < place.keys.length - 1; index += 1) {
|
|
3157
|
-
|
|
3531
|
+
const key = place.keys[index];
|
|
3532
|
+
const access2 = this.canWriteProperty(target, key);
|
|
3533
|
+
if (!access2)
|
|
3534
|
+
return;
|
|
3535
|
+
if (access2.kind === "array") {
|
|
3536
|
+
target = target[access2.index];
|
|
3537
|
+
if (!target || typeof target !== "object")
|
|
3538
|
+
return;
|
|
3539
|
+
continue;
|
|
3540
|
+
}
|
|
3541
|
+
target = target[String(key)];
|
|
3158
3542
|
if (!target || typeof target !== "object")
|
|
3159
3543
|
return;
|
|
3160
3544
|
}
|
|
3161
|
-
|
|
3545
|
+
const lastKey = place.keys[place.keys.length - 1];
|
|
3546
|
+
const access = this.canWriteProperty(target, lastKey);
|
|
3547
|
+
if (!access)
|
|
3548
|
+
return;
|
|
3549
|
+
if (access.kind === "array") {
|
|
3550
|
+
delete target[access.index];
|
|
3551
|
+
return;
|
|
3552
|
+
}
|
|
3553
|
+
delete target[String(lastKey)];
|
|
3162
3554
|
}
|
|
3163
3555
|
skipValue() {
|
|
3164
3556
|
this.pos = this.skipValueFrom(this.pos);
|
|
@@ -3174,12 +3566,12 @@ class CursorInterpreter {
|
|
|
3174
3566
|
return startPos;
|
|
3175
3567
|
}
|
|
3176
3568
|
if (tag === ",") {
|
|
3177
|
-
this.pos
|
|
3569
|
+
this.pos = this.advanceByBytes(this.pos + 1, prefix.value);
|
|
3178
3570
|
const end2 = this.pos;
|
|
3179
3571
|
this.pos = save;
|
|
3180
3572
|
return end2;
|
|
3181
3573
|
}
|
|
3182
|
-
if (tag === "=") {
|
|
3574
|
+
if (tag === "=" || tag === "/") {
|
|
3183
3575
|
this.pos += 1;
|
|
3184
3576
|
this.skipValue();
|
|
3185
3577
|
this.skipValue();
|
|
@@ -3214,7 +3606,8 @@ class CursorInterpreter {
|
|
|
3214
3606
|
if (opener && "([{".includes(opener)) {
|
|
3215
3607
|
const close = opener === "(" ? ")" : opener === "[" ? "]" : "}";
|
|
3216
3608
|
if (prefix.value > 0) {
|
|
3217
|
-
this.pos
|
|
3609
|
+
const bodyEnd = this.advanceByBytes(this.pos + 1, prefix.value);
|
|
3610
|
+
this.pos = bodyEnd + 1;
|
|
3218
3611
|
const end3 = this.pos;
|
|
3219
3612
|
this.pos = save;
|
|
3220
3613
|
return end3;
|
|
@@ -3258,7 +3651,7 @@ var C = {
|
|
|
3258
3651
|
gray: "\x1B[90m",
|
|
3259
3652
|
boldBlue: "\x1B[1;34m"
|
|
3260
3653
|
};
|
|
3261
|
-
var TOKEN_RE = /(?<blockComment>\/\*[\s\S]*?(?:\*\/|$))|(?<lineComment>\/\/[^\n]*)|(?<dstring>"(?:[^"\\]|\\.)*"?)|(?<sstring>'(?:[^'\\]|\\.)*'?)|(?<keyword>\b(?:when|unless|while|for|do|end|in|of|and|or|else|break|continue|delete|self)(?![a-zA-Z0-9_-]))|(?<literal>\b(?:true|false|null|undefined)(?![a-zA-Z0-9_-]))|(?<typePred>\b(?:string|number|object|array|boolean)(?![a-zA-Z0-9_-]))|(?<num>\b(?:0x[0-9a-fA-F]+|0b[01]+|(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?)\b)/g;
|
|
3654
|
+
var TOKEN_RE = /(?<blockComment>\/\*[\s\S]*?(?:\*\/|$))|(?<lineComment>\/\/[^\n]*)|(?<dstring>"(?:[^"\\]|\\.)*"?)|(?<sstring>'(?:[^'\\]|\\.)*'?)|(?<keyword>\b(?:when|unless|while|for|do|end|in|of|and|or|nor|else|break|continue|delete|self)(?![a-zA-Z0-9_-]))|(?<literal>\b(?:true|false|null|undefined|nan)(?![a-zA-Z0-9_-])|-?\binf\b)|(?<typePred>\b(?:string|number|object|array|boolean)(?![a-zA-Z0-9_-]))|(?<num>\b(?:0x[0-9a-fA-F]+|0b[01]+|(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?)\b)/g;
|
|
3262
3655
|
function highlightLine(line) {
|
|
3263
3656
|
let result = "";
|
|
3264
3657
|
let lastIndex = 0;
|
|
@@ -3367,6 +3760,7 @@ function highlightRexc(text) {
|
|
|
3367
3760
|
break;
|
|
3368
3761
|
}
|
|
3369
3762
|
case "=":
|
|
3763
|
+
case "/":
|
|
3370
3764
|
case "~":
|
|
3371
3765
|
out += C.red + prefix + tag + C.reset;
|
|
3372
3766
|
i++;
|
|
@@ -3406,6 +3800,33 @@ function highlightRexc(text) {
|
|
|
3406
3800
|
}
|
|
3407
3801
|
return out;
|
|
3408
3802
|
}
|
|
3803
|
+
var JSON_TOKEN_RE = /(?<key>"(?:[^"\\]|\\.)*")\s*:|(?<string>"(?:[^"\\]|\\.)*")|(?<number>-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?)\b|(?<bool>true|false)|(?<null>null)|(?<brace>[{}[\]])|(?<punct>[:,])/g;
|
|
3804
|
+
function highlightJSON(json) {
|
|
3805
|
+
let result = "";
|
|
3806
|
+
let lastIndex = 0;
|
|
3807
|
+
JSON_TOKEN_RE.lastIndex = 0;
|
|
3808
|
+
for (const m of json.matchAll(JSON_TOKEN_RE)) {
|
|
3809
|
+
result += json.slice(lastIndex, m.index);
|
|
3810
|
+
const text = m[0];
|
|
3811
|
+
const g = m.groups;
|
|
3812
|
+
if (g.key) {
|
|
3813
|
+
result += C.cyan + g.key + C.reset + ":";
|
|
3814
|
+
} else if (g.string) {
|
|
3815
|
+
result += C.green + text + C.reset;
|
|
3816
|
+
} else if (g.number) {
|
|
3817
|
+
result += C.yellow + text + C.reset;
|
|
3818
|
+
} else if (g.bool) {
|
|
3819
|
+
result += C.yellow + text + C.reset;
|
|
3820
|
+
} else if (g.null) {
|
|
3821
|
+
result += C.dim + text + C.reset;
|
|
3822
|
+
} else {
|
|
3823
|
+
result += text;
|
|
3824
|
+
}
|
|
3825
|
+
lastIndex = m.index + text.length;
|
|
3826
|
+
}
|
|
3827
|
+
result += json.slice(lastIndex);
|
|
3828
|
+
return result;
|
|
3829
|
+
}
|
|
3409
3830
|
function stripStringsAndComments(source) {
|
|
3410
3831
|
return source.replace(/\/\*[\s\S]*?\*\/|\/\/[^\n]*|"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/g, (m) => " ".repeat(m.length));
|
|
3411
3832
|
}
|
|
@@ -3443,7 +3864,7 @@ function isIncomplete(buffer) {
|
|
|
3443
3864
|
const trimmed = buffer.trimEnd();
|
|
3444
3865
|
if (/[+\-*/%&|^=<>]$/.test(trimmed))
|
|
3445
3866
|
return true;
|
|
3446
|
-
if (/\b(?:and|or|do|in|of)\s*$/.test(trimmed))
|
|
3867
|
+
if (/\b(?:and|or|nor|do|in|of)\s*$/.test(trimmed))
|
|
3447
3868
|
return true;
|
|
3448
3869
|
return false;
|
|
3449
3870
|
}
|
|
@@ -3495,6 +3916,7 @@ var KEYWORDS = [
|
|
|
3495
3916
|
"of",
|
|
3496
3917
|
"and",
|
|
3497
3918
|
"or",
|
|
3919
|
+
"nor",
|
|
3498
3920
|
"else",
|
|
3499
3921
|
"break",
|
|
3500
3922
|
"continue",
|
|
@@ -3504,6 +3926,8 @@ var KEYWORDS = [
|
|
|
3504
3926
|
"false",
|
|
3505
3927
|
"null",
|
|
3506
3928
|
"undefined",
|
|
3929
|
+
"nan",
|
|
3930
|
+
"inf",
|
|
3507
3931
|
"string",
|
|
3508
3932
|
"number",
|
|
3509
3933
|
"object",
|
|
@@ -3680,7 +4104,7 @@ async function startRepl() {
|
|
|
3680
4104
|
const ir = parseToIR(source);
|
|
3681
4105
|
const lowered = state.optimize ? optimizeIR(ir) : ir;
|
|
3682
4106
|
if (state.showIR) {
|
|
3683
|
-
console.log(`${C.dim} IR
|
|
4107
|
+
console.log(`${C.dim} IR:${C.reset} ${highlightJSON(JSON.stringify(lowered))}`);
|
|
3684
4108
|
}
|
|
3685
4109
|
const rexc = compile(source, { optimize: state.optimize });
|
|
3686
4110
|
if (state.showRexc) {
|
|
@@ -3717,5 +4141,6 @@ export {
|
|
|
3717
4141
|
isIncomplete,
|
|
3718
4142
|
highlightRexc,
|
|
3719
4143
|
highlightLine,
|
|
4144
|
+
highlightJSON,
|
|
3720
4145
|
formatVarState
|
|
3721
4146
|
};
|