@creationix/rex 0.1.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -62
- package/package.json +7 -6
- package/rex-cli.js +3978 -30
- package/{rex-compile.ts → rex-cli.ts} +200 -206
- package/{rex-compile.js → rex-repl.js} +1576 -195
- package/rex-repl.ts +545 -0
- package/rex.js +151 -48
- package/rex.ohm +11 -0
- package/rex.ohm-bundle.cjs +1 -1
- package/rex.ohm-bundle.d.ts +3 -0
- package/rex.ohm-bundle.js +1 -1
- package/rex.ts +2536 -2422
- package/rexc-interpreter.ts +916 -848
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// rex-repl.ts
|
|
2
|
+
import * as readline from "node:readline";
|
|
3
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
4
|
+
|
|
1
5
|
// rex.ts
|
|
2
6
|
import { createRequire } from "node:module";
|
|
3
7
|
var require2 = createRequire(import.meta.url);
|
|
@@ -33,19 +37,8 @@ var OPCODE_IDS = {
|
|
|
33
37
|
mod: 20,
|
|
34
38
|
neg: 21
|
|
35
39
|
};
|
|
36
|
-
var
|
|
37
|
-
|
|
38
|
-
if (!overrides || Object.keys(overrides).length === 0) {
|
|
39
|
-
return Object.keys(registeredDomainRefs).length > 0 ? { ...registeredDomainRefs } : undefined;
|
|
40
|
-
}
|
|
41
|
-
const merged = { ...registeredDomainRefs };
|
|
42
|
-
for (const [name, refId] of Object.entries(overrides)) {
|
|
43
|
-
if (!Number.isInteger(refId) || refId < 0)
|
|
44
|
-
throw new Error(`Invalid domain extension ref id for '${name}': ${refId}`);
|
|
45
|
-
merged[name] = refId;
|
|
46
|
-
}
|
|
47
|
-
return merged;
|
|
48
|
-
}
|
|
40
|
+
var FIRST_NON_RESERVED_REF = 5;
|
|
41
|
+
var DOMAIN_DIGIT_INDEX = new Map(Array.from(DIGITS).map((char, index) => [char, index]));
|
|
49
42
|
var BINARY_TO_OPCODE = {
|
|
50
43
|
add: "add",
|
|
51
44
|
sub: "sub",
|
|
@@ -198,13 +191,13 @@ function needsOptionalPrefix(encoded) {
|
|
|
198
191
|
const first = encoded[0];
|
|
199
192
|
if (!first)
|
|
200
193
|
return false;
|
|
201
|
-
return first === "[" || first === "{" || first === "(" || first === "=" || first === "~" || first === "?" || first === "!" || first === "|" || first === "&" || first === ">" || first === "<";
|
|
194
|
+
return first === "[" || first === "{" || first === "(" || first === "=" || first === "~" || first === "?" || first === "!" || first === "|" || first === "&" || first === ">" || first === "<" || first === "#";
|
|
202
195
|
}
|
|
203
196
|
function addOptionalPrefix(encoded) {
|
|
204
197
|
if (!needsOptionalPrefix(encoded))
|
|
205
198
|
return encoded;
|
|
206
199
|
let payload = encoded;
|
|
207
|
-
if (encoded.startsWith("?(") || encoded.startsWith("!(") || encoded.startsWith("|(") || encoded.startsWith("&(") || encoded.startsWith(">(") || encoded.startsWith("<(")) {
|
|
200
|
+
if (encoded.startsWith("?(") || encoded.startsWith("!(") || encoded.startsWith("|(") || encoded.startsWith("&(") || encoded.startsWith(">(") || encoded.startsWith("<(") || encoded.startsWith("#(")) {
|
|
208
201
|
payload = encoded.slice(2, -1);
|
|
209
202
|
} else if (encoded.startsWith(">[") || encoded.startsWith(">{")) {
|
|
210
203
|
payload = encoded.slice(2, -1);
|
|
@@ -235,6 +228,33 @@ function encodeConditionalElse(elseBranch) {
|
|
|
235
228
|
return encodeNode(nested);
|
|
236
229
|
}
|
|
237
230
|
function encodeNavigation(node) {
|
|
231
|
+
const domainRefs = activeEncodeOptions?.domainRefs;
|
|
232
|
+
if (domainRefs && node.target.type === "identifier") {
|
|
233
|
+
const staticPath = [node.target.name];
|
|
234
|
+
for (const segment of node.segments) {
|
|
235
|
+
if (segment.type !== "static")
|
|
236
|
+
break;
|
|
237
|
+
staticPath.push(segment.key);
|
|
238
|
+
}
|
|
239
|
+
for (let pathLength = staticPath.length;pathLength >= 1; pathLength -= 1) {
|
|
240
|
+
const dottedName = staticPath.slice(0, pathLength).join(".");
|
|
241
|
+
const domainRef = domainRefs[dottedName];
|
|
242
|
+
if (domainRef === undefined)
|
|
243
|
+
continue;
|
|
244
|
+
const consumedStaticSegments = pathLength - 1;
|
|
245
|
+
if (consumedStaticSegments === node.segments.length) {
|
|
246
|
+
return `${encodeUint(domainRef)}'`;
|
|
247
|
+
}
|
|
248
|
+
const parts2 = [`${encodeUint(domainRef)}'`];
|
|
249
|
+
for (const segment of node.segments.slice(consumedStaticSegments)) {
|
|
250
|
+
if (segment.type === "static")
|
|
251
|
+
parts2.push(encodeBareOrLengthString(segment.key));
|
|
252
|
+
else
|
|
253
|
+
parts2.push(encodeNode(segment.key));
|
|
254
|
+
}
|
|
255
|
+
return encodeCallParts(parts2);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
238
258
|
const parts = [encodeNode(node.target)];
|
|
239
259
|
for (const segment of node.segments) {
|
|
240
260
|
if (segment.type === "static")
|
|
@@ -244,6 +264,11 @@ function encodeNavigation(node) {
|
|
|
244
264
|
}
|
|
245
265
|
return encodeCallParts(parts);
|
|
246
266
|
}
|
|
267
|
+
function encodeWhile(node) {
|
|
268
|
+
const cond = encodeNode(node.condition);
|
|
269
|
+
const body = addOptionalPrefix(encodeBlockExpression(node.body));
|
|
270
|
+
return `#(${cond}${body})`;
|
|
271
|
+
}
|
|
247
272
|
function encodeFor(node) {
|
|
248
273
|
const body = addOptionalPrefix(encodeBlockExpression(node.body));
|
|
249
274
|
if (node.binding.type === "binding:expr") {
|
|
@@ -380,6 +405,8 @@ function encodeNode(node) {
|
|
|
380
405
|
}
|
|
381
406
|
case "for":
|
|
382
407
|
return encodeFor(node);
|
|
408
|
+
case "while":
|
|
409
|
+
return encodeWhile(node);
|
|
383
410
|
case "break":
|
|
384
411
|
return ";";
|
|
385
412
|
case "continue":
|
|
@@ -403,6 +430,178 @@ function parseToIR(source) {
|
|
|
403
430
|
}
|
|
404
431
|
return semantics(match).toIR();
|
|
405
432
|
}
|
|
433
|
+
function isPlainObject(value) {
|
|
434
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
435
|
+
return false;
|
|
436
|
+
const proto = Object.getPrototypeOf(value);
|
|
437
|
+
return proto === Object.prototype || proto === null;
|
|
438
|
+
}
|
|
439
|
+
function isBareKeyName(key) {
|
|
440
|
+
return /^[A-Za-z_][A-Za-z0-9_-]*$/.test(key);
|
|
441
|
+
}
|
|
442
|
+
function stringifyString(value) {
|
|
443
|
+
return JSON.stringify(value);
|
|
444
|
+
}
|
|
445
|
+
function stringifyInline(value) {
|
|
446
|
+
if (value === undefined)
|
|
447
|
+
return "undefined";
|
|
448
|
+
if (value === null)
|
|
449
|
+
return "null";
|
|
450
|
+
if (typeof value === "boolean")
|
|
451
|
+
return value ? "true" : "false";
|
|
452
|
+
if (typeof value === "number") {
|
|
453
|
+
if (!Number.isFinite(value))
|
|
454
|
+
throw new Error("Rex stringify() cannot encode non-finite numbers");
|
|
455
|
+
return String(value);
|
|
456
|
+
}
|
|
457
|
+
if (typeof value === "string")
|
|
458
|
+
return stringifyString(value);
|
|
459
|
+
if (Array.isArray(value)) {
|
|
460
|
+
if (value.length === 0)
|
|
461
|
+
return "[]";
|
|
462
|
+
return `[${value.map((item) => stringifyInline(item)).join(" ")}]`;
|
|
463
|
+
}
|
|
464
|
+
if (isPlainObject(value)) {
|
|
465
|
+
const entries = Object.entries(value);
|
|
466
|
+
if (entries.length === 0)
|
|
467
|
+
return "{}";
|
|
468
|
+
const body = entries.map(([key, item]) => `${isBareKeyName(key) ? key : stringifyString(key)}: ${stringifyInline(item)}`).join(" ");
|
|
469
|
+
return `{${body}}`;
|
|
470
|
+
}
|
|
471
|
+
throw new Error(`Rex stringify() cannot encode value of type ${typeof value}`);
|
|
472
|
+
}
|
|
473
|
+
function fitsInline(rendered, depth, indentSize, maxWidth) {
|
|
474
|
+
if (rendered.includes(`
|
|
475
|
+
`))
|
|
476
|
+
return false;
|
|
477
|
+
return depth * indentSize + rendered.length <= maxWidth;
|
|
478
|
+
}
|
|
479
|
+
function stringifyPretty(value, depth, indentSize, maxWidth) {
|
|
480
|
+
const inline = stringifyInline(value);
|
|
481
|
+
if (fitsInline(inline, depth, indentSize, maxWidth))
|
|
482
|
+
return inline;
|
|
483
|
+
const indent = " ".repeat(depth * indentSize);
|
|
484
|
+
const childIndent = " ".repeat((depth + 1) * indentSize);
|
|
485
|
+
if (Array.isArray(value)) {
|
|
486
|
+
if (value.length === 0)
|
|
487
|
+
return "[]";
|
|
488
|
+
const lines = value.map((item) => {
|
|
489
|
+
const rendered = stringifyPretty(item, depth + 1, indentSize, maxWidth);
|
|
490
|
+
if (!rendered.includes(`
|
|
491
|
+
`))
|
|
492
|
+
return `${childIndent}${rendered}`;
|
|
493
|
+
return `${childIndent}${rendered}`;
|
|
494
|
+
});
|
|
495
|
+
return `[
|
|
496
|
+
${lines.join(`
|
|
497
|
+
`)}
|
|
498
|
+
${indent}]`;
|
|
499
|
+
}
|
|
500
|
+
if (isPlainObject(value)) {
|
|
501
|
+
const entries = Object.entries(value);
|
|
502
|
+
if (entries.length === 0)
|
|
503
|
+
return "{}";
|
|
504
|
+
const lines = entries.map(([key, item]) => {
|
|
505
|
+
const keyText = isBareKeyName(key) ? key : stringifyString(key);
|
|
506
|
+
const rendered = stringifyPretty(item, depth + 1, indentSize, maxWidth);
|
|
507
|
+
return `${childIndent}${keyText}: ${rendered}`;
|
|
508
|
+
});
|
|
509
|
+
return `{
|
|
510
|
+
${lines.join(`
|
|
511
|
+
`)}
|
|
512
|
+
${indent}}`;
|
|
513
|
+
}
|
|
514
|
+
return inline;
|
|
515
|
+
}
|
|
516
|
+
function domainRefsFromConfig(config) {
|
|
517
|
+
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
518
|
+
throw new Error("Domain config must be an object");
|
|
519
|
+
}
|
|
520
|
+
const refs = {};
|
|
521
|
+
for (const section of Object.values(config)) {
|
|
522
|
+
if (!section || typeof section !== "object" || Array.isArray(section))
|
|
523
|
+
continue;
|
|
524
|
+
mapConfigEntries(section, refs);
|
|
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
|
+
}
|
|
549
|
+
}
|
|
550
|
+
if (value < FIRST_NON_RESERVED_REF) {
|
|
551
|
+
throw new Error(`Invalid domain ref key '${refText}' (maps to reserved id ${value})`);
|
|
552
|
+
}
|
|
553
|
+
return value;
|
|
554
|
+
}
|
|
555
|
+
function mapConfigEntries(entries, refs) {
|
|
556
|
+
const sourceKindByRoot = new Map;
|
|
557
|
+
for (const root of Object.keys(refs)) {
|
|
558
|
+
sourceKindByRoot.set(root, "explicit");
|
|
559
|
+
}
|
|
560
|
+
for (const [refText, rawEntry] of Object.entries(entries)) {
|
|
561
|
+
const entry = rawEntry;
|
|
562
|
+
if (!entry || typeof entry !== "object")
|
|
563
|
+
continue;
|
|
564
|
+
if (!Array.isArray(entry.names))
|
|
565
|
+
continue;
|
|
566
|
+
const refId = decodeDomainRefKey(refText);
|
|
567
|
+
for (const rawName of entry.names) {
|
|
568
|
+
if (typeof rawName !== "string")
|
|
569
|
+
continue;
|
|
570
|
+
const existingNameRef = refs[rawName];
|
|
571
|
+
if (existingNameRef !== undefined && existingNameRef !== refId) {
|
|
572
|
+
throw new Error(`Conflicting refs for '${rawName}': ${existingNameRef} vs ${refId}`);
|
|
573
|
+
}
|
|
574
|
+
refs[rawName] = refId;
|
|
575
|
+
const root = rawName.split(".")[0];
|
|
576
|
+
if (!root)
|
|
577
|
+
continue;
|
|
578
|
+
const currentKind = rawName.includes(".") ? "implicit" : "explicit";
|
|
579
|
+
const existing = refs[root];
|
|
580
|
+
if (existing !== undefined) {
|
|
581
|
+
if (existing === refId)
|
|
582
|
+
continue;
|
|
583
|
+
const existingKind = sourceKindByRoot.get(root) ?? "explicit";
|
|
584
|
+
if (currentKind === "explicit") {
|
|
585
|
+
throw new Error(`Conflicting refs for '${root}': ${existing} vs ${refId}`);
|
|
586
|
+
}
|
|
587
|
+
if (existingKind === "explicit")
|
|
588
|
+
continue;
|
|
589
|
+
continue;
|
|
590
|
+
}
|
|
591
|
+
refs[root] = refId;
|
|
592
|
+
sourceKindByRoot.set(root, currentKind);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
function stringify(value, options) {
|
|
597
|
+
const indent = options?.indent ?? 2;
|
|
598
|
+
const maxWidth = options?.maxWidth ?? 80;
|
|
599
|
+
if (!Number.isInteger(indent) || indent < 0)
|
|
600
|
+
throw new Error("Rex stringify() indent must be a non-negative integer");
|
|
601
|
+
if (!Number.isInteger(maxWidth) || maxWidth < 20)
|
|
602
|
+
throw new Error("Rex stringify() maxWidth must be an integer >= 20");
|
|
603
|
+
return stringifyPretty(value, 0, indent, maxWidth);
|
|
604
|
+
}
|
|
406
605
|
var DIGIT_SET = new Set(DIGITS.split(""));
|
|
407
606
|
var DIGIT_INDEX = new Map(Array.from(DIGITS).map((char, index) => [char, index]));
|
|
408
607
|
function readPrefixAt(text, start) {
|
|
@@ -1284,12 +1483,12 @@ function optimizeNode(node, env, currentDepth, asPlace = false) {
|
|
|
1284
1483
|
if (conditionValue !== undefined || condition.type === "undefined") {
|
|
1285
1484
|
const passes = node.head === "when" ? isDefinedValue(conditionValue) : !isDefinedValue(conditionValue);
|
|
1286
1485
|
if (passes) {
|
|
1287
|
-
const
|
|
1288
|
-
if (
|
|
1486
|
+
const thenBlock2 = optimizeBlock(node.thenBlock, thenEnv, currentDepth);
|
|
1487
|
+
if (thenBlock2.length === 0)
|
|
1289
1488
|
return { type: "undefined" };
|
|
1290
|
-
if (
|
|
1291
|
-
return
|
|
1292
|
-
return { type: "program", body:
|
|
1489
|
+
if (thenBlock2.length === 1)
|
|
1490
|
+
return thenBlock2[0];
|
|
1491
|
+
return { type: "program", body: thenBlock2 };
|
|
1293
1492
|
}
|
|
1294
1493
|
if (!node.elseBranch)
|
|
1295
1494
|
return { type: "undefined" };
|
|
@@ -1311,12 +1510,26 @@ function optimizeNode(node, env, currentDepth, asPlace = false) {
|
|
|
1311
1510
|
elseBranch: loweredElse.elseBranch
|
|
1312
1511
|
};
|
|
1313
1512
|
}
|
|
1513
|
+
const thenBlock = optimizeBlock(node.thenBlock, thenEnv, currentDepth);
|
|
1514
|
+
const elseBranch = optimizeElse(node.elseBranch, cloneOptimizeEnv(env), currentDepth);
|
|
1515
|
+
let finalCondition = condition;
|
|
1516
|
+
if (condition.type === "assign" && condition.op === "=" && condition.place.type === "identifier") {
|
|
1517
|
+
const name = condition.place.name;
|
|
1518
|
+
const reads = new Set;
|
|
1519
|
+
for (const part of thenBlock)
|
|
1520
|
+
collectReads(part, reads);
|
|
1521
|
+
if (elseBranch)
|
|
1522
|
+
collectReadsElse(elseBranch, reads);
|
|
1523
|
+
if (!reads.has(name)) {
|
|
1524
|
+
finalCondition = condition.value;
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1314
1527
|
return {
|
|
1315
1528
|
type: "conditional",
|
|
1316
1529
|
head: node.head,
|
|
1317
|
-
condition,
|
|
1318
|
-
thenBlock
|
|
1319
|
-
elseBranch
|
|
1530
|
+
condition: finalCondition,
|
|
1531
|
+
thenBlock,
|
|
1532
|
+
elseBranch
|
|
1320
1533
|
};
|
|
1321
1534
|
}
|
|
1322
1535
|
case "for": {
|
|
@@ -1771,8 +1984,9 @@ function compile(source, options) {
|
|
|
1771
1984
|
let lowered = options?.optimize ? optimizeIR(ir) : ir;
|
|
1772
1985
|
if (options?.minifyNames)
|
|
1773
1986
|
lowered = minifyLocalNamesIR(lowered);
|
|
1987
|
+
const domainRefs = options?.domainConfig ? domainRefsFromConfig(options.domainConfig) : undefined;
|
|
1774
1988
|
return encodeIR(lowered, {
|
|
1775
|
-
domainRefs
|
|
1989
|
+
domainRefs,
|
|
1776
1990
|
dedupeValues: options?.dedupeValues,
|
|
1777
1991
|
dedupeMinBytes: options?.dedupeMinBytes
|
|
1778
1992
|
});
|
|
@@ -2002,6 +2216,13 @@ semantics.addOperation("toIR", {
|
|
|
2002
2216
|
return body[0];
|
|
2003
2217
|
return { type: "program", body };
|
|
2004
2218
|
},
|
|
2219
|
+
WhileExpr(_while, condition, _do, block, _end) {
|
|
2220
|
+
return {
|
|
2221
|
+
type: "while",
|
|
2222
|
+
condition: condition.toIR(),
|
|
2223
|
+
body: block.toIR()
|
|
2224
|
+
};
|
|
2225
|
+
},
|
|
2005
2226
|
ForExpr(_for, binding, _do, block, _end) {
|
|
2006
2227
|
return {
|
|
2007
2228
|
type: "for",
|
|
@@ -2145,196 +2366,1356 @@ semantics.addOperation("toIR", {
|
|
|
2145
2366
|
}
|
|
2146
2367
|
});
|
|
2147
2368
|
|
|
2148
|
-
//
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2369
|
+
// rexc-interpreter.ts
|
|
2370
|
+
var DIGITS2 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_";
|
|
2371
|
+
var digitMap = new Map(Array.from(DIGITS2).map((char, index) => [char, index]));
|
|
2372
|
+
var OPCODES = {
|
|
2373
|
+
do: 0,
|
|
2374
|
+
add: 1,
|
|
2375
|
+
sub: 2,
|
|
2376
|
+
mul: 3,
|
|
2377
|
+
div: 4,
|
|
2378
|
+
eq: 5,
|
|
2379
|
+
neq: 6,
|
|
2380
|
+
lt: 7,
|
|
2381
|
+
lte: 8,
|
|
2382
|
+
gt: 9,
|
|
2383
|
+
gte: 10,
|
|
2384
|
+
and: 11,
|
|
2385
|
+
or: 12,
|
|
2386
|
+
xor: 13,
|
|
2387
|
+
not: 14,
|
|
2388
|
+
boolean: 15,
|
|
2389
|
+
number: 16,
|
|
2390
|
+
string: 17,
|
|
2391
|
+
array: 18,
|
|
2392
|
+
object: 19,
|
|
2393
|
+
mod: 20,
|
|
2394
|
+
neg: 21
|
|
2395
|
+
};
|
|
2396
|
+
function decodePrefix(text, start, end) {
|
|
2397
|
+
let value = 0;
|
|
2398
|
+
for (let index = start;index < end; index += 1) {
|
|
2399
|
+
const digit = digitMap.get(text[index] ?? "") ?? -1;
|
|
2400
|
+
if (digit < 0)
|
|
2401
|
+
throw new Error(`Invalid digit '${text[index]}'`);
|
|
2402
|
+
value = value * 64 + digit;
|
|
2403
|
+
}
|
|
2404
|
+
return value;
|
|
2405
|
+
}
|
|
2406
|
+
function decodeZigzag(value) {
|
|
2407
|
+
return value % 2 === 0 ? value / 2 : -(value + 1) / 2;
|
|
2408
|
+
}
|
|
2409
|
+
function isDefined(value) {
|
|
2410
|
+
return value !== undefined;
|
|
2411
|
+
}
|
|
2412
|
+
|
|
2413
|
+
class CursorInterpreter {
|
|
2414
|
+
text;
|
|
2415
|
+
pos = 0;
|
|
2416
|
+
state;
|
|
2417
|
+
selfStack;
|
|
2418
|
+
opcodeMarkers;
|
|
2419
|
+
pointerCache = new Map;
|
|
2420
|
+
gasLimit;
|
|
2421
|
+
gas = 0;
|
|
2422
|
+
tick() {
|
|
2423
|
+
if (this.gasLimit && ++this.gas > this.gasLimit) {
|
|
2424
|
+
throw new Error("Gas limit exceeded (too many loop iterations)");
|
|
2166
2425
|
}
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2426
|
+
}
|
|
2427
|
+
constructor(text, ctx = {}) {
|
|
2428
|
+
const initialSelf = ctx.selfStack && ctx.selfStack.length > 0 ? ctx.selfStack[ctx.selfStack.length - 1] : ctx.self;
|
|
2429
|
+
this.text = text;
|
|
2430
|
+
this.state = {
|
|
2431
|
+
vars: ctx.vars ?? {},
|
|
2432
|
+
refs: {
|
|
2433
|
+
0: ctx.refs?.[0],
|
|
2434
|
+
1: ctx.refs?.[1] ?? true,
|
|
2435
|
+
2: ctx.refs?.[2] ?? false,
|
|
2436
|
+
3: ctx.refs?.[3] ?? null,
|
|
2437
|
+
4: ctx.refs?.[4] ?? undefined
|
|
2438
|
+
}
|
|
2439
|
+
};
|
|
2440
|
+
this.selfStack = ctx.selfStack && ctx.selfStack.length > 0 ? [...ctx.selfStack] : [initialSelf];
|
|
2441
|
+
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;
|
|
2170
2447
|
}
|
|
2171
|
-
if (
|
|
2172
|
-
|
|
2173
|
-
|
|
2448
|
+
if (ctx.opcodes) {
|
|
2449
|
+
for (const [idText, op] of Object.entries(ctx.opcodes)) {
|
|
2450
|
+
if (op)
|
|
2451
|
+
this.customOpcodes.set(Number(idText), op);
|
|
2452
|
+
}
|
|
2174
2453
|
}
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2454
|
+
}
|
|
2455
|
+
customOpcodes = new Map;
|
|
2456
|
+
readSelf(depthPrefix) {
|
|
2457
|
+
const depth = depthPrefix + 1;
|
|
2458
|
+
const index = this.selfStack.length - depth;
|
|
2459
|
+
if (index < 0)
|
|
2460
|
+
return;
|
|
2461
|
+
return this.selfStack[index];
|
|
2462
|
+
}
|
|
2463
|
+
get runtimeState() {
|
|
2464
|
+
return this.state;
|
|
2465
|
+
}
|
|
2466
|
+
evaluateTopLevel() {
|
|
2467
|
+
this.skipNonCode();
|
|
2468
|
+
if (this.pos >= this.text.length)
|
|
2469
|
+
return;
|
|
2470
|
+
const value = this.evalValue();
|
|
2471
|
+
this.skipNonCode();
|
|
2472
|
+
if (this.pos < this.text.length)
|
|
2473
|
+
throw new Error(`Unexpected trailing input at ${this.pos}`);
|
|
2474
|
+
return value;
|
|
2475
|
+
}
|
|
2476
|
+
skipNonCode() {
|
|
2477
|
+
while (this.pos < this.text.length) {
|
|
2478
|
+
const ch = this.text[this.pos];
|
|
2479
|
+
if (ch === " " || ch === "\t" || ch === `
|
|
2480
|
+
` || ch === "\r") {
|
|
2481
|
+
this.pos += 1;
|
|
2482
|
+
continue;
|
|
2483
|
+
}
|
|
2484
|
+
if (ch === "/" && this.text[this.pos + 1] === "/") {
|
|
2485
|
+
this.pos += 2;
|
|
2486
|
+
while (this.pos < this.text.length && this.text[this.pos] !== `
|
|
2487
|
+
`)
|
|
2488
|
+
this.pos += 1;
|
|
2489
|
+
continue;
|
|
2490
|
+
}
|
|
2491
|
+
if (ch === "/" && this.text[this.pos + 1] === "*") {
|
|
2492
|
+
this.pos += 2;
|
|
2493
|
+
while (this.pos < this.text.length) {
|
|
2494
|
+
if (this.text[this.pos] === "*" && this.text[this.pos + 1] === "/") {
|
|
2495
|
+
this.pos += 2;
|
|
2496
|
+
break;
|
|
2497
|
+
}
|
|
2498
|
+
this.pos += 1;
|
|
2499
|
+
}
|
|
2500
|
+
continue;
|
|
2501
|
+
}
|
|
2502
|
+
break;
|
|
2178
2503
|
}
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2504
|
+
}
|
|
2505
|
+
readPrefix() {
|
|
2506
|
+
const start = this.pos;
|
|
2507
|
+
while (this.pos < this.text.length && digitMap.has(this.text[this.pos] ?? ""))
|
|
2508
|
+
this.pos += 1;
|
|
2509
|
+
const end = this.pos;
|
|
2510
|
+
return { start, end, value: decodePrefix(this.text, start, end), raw: this.text.slice(start, end) };
|
|
2511
|
+
}
|
|
2512
|
+
ensure(char) {
|
|
2513
|
+
if (this.text[this.pos] !== char)
|
|
2514
|
+
throw new Error(`Expected '${char}' at ${this.pos}`);
|
|
2515
|
+
this.pos += 1;
|
|
2516
|
+
}
|
|
2517
|
+
hasMoreBefore(close) {
|
|
2518
|
+
const save = this.pos;
|
|
2519
|
+
this.skipNonCode();
|
|
2520
|
+
const more = this.pos < this.text.length && this.text[this.pos] !== close;
|
|
2521
|
+
this.pos = save;
|
|
2522
|
+
return more;
|
|
2523
|
+
}
|
|
2524
|
+
readBindingVarIfPresent() {
|
|
2525
|
+
const save = this.pos;
|
|
2526
|
+
this.skipNonCode();
|
|
2527
|
+
const prefix = this.readPrefix();
|
|
2528
|
+
const tag = this.text[this.pos];
|
|
2529
|
+
if (tag === "$" && prefix.raw.length > 0) {
|
|
2530
|
+
this.pos += 1;
|
|
2531
|
+
return prefix.raw;
|
|
2189
2532
|
}
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2533
|
+
this.pos = save;
|
|
2534
|
+
return;
|
|
2535
|
+
}
|
|
2536
|
+
evalValue() {
|
|
2537
|
+
this.skipNonCode();
|
|
2538
|
+
const prefix = this.readPrefix();
|
|
2539
|
+
const tag = this.text[this.pos];
|
|
2540
|
+
if (!tag)
|
|
2541
|
+
throw new Error("Unexpected end of input");
|
|
2542
|
+
switch (tag) {
|
|
2543
|
+
case "+":
|
|
2544
|
+
this.pos += 1;
|
|
2545
|
+
return decodeZigzag(prefix.value);
|
|
2546
|
+
case "*": {
|
|
2547
|
+
this.pos += 1;
|
|
2548
|
+
const power = decodeZigzag(prefix.value);
|
|
2549
|
+
const significand = this.evalValue();
|
|
2550
|
+
if (typeof significand !== "number")
|
|
2551
|
+
throw new Error("Decimal significand must be numeric");
|
|
2552
|
+
return significand * 10 ** power;
|
|
2553
|
+
}
|
|
2554
|
+
case ":":
|
|
2555
|
+
this.pos += 1;
|
|
2556
|
+
return prefix.raw;
|
|
2557
|
+
case "%":
|
|
2558
|
+
this.pos += 1;
|
|
2559
|
+
return this.opcodeMarkers[prefix.value] ?? { __opcode: prefix.value };
|
|
2560
|
+
case "@":
|
|
2561
|
+
this.pos += 1;
|
|
2562
|
+
return this.readSelf(prefix.value);
|
|
2563
|
+
case "'":
|
|
2564
|
+
this.pos += 1;
|
|
2565
|
+
return this.state.refs[prefix.value];
|
|
2566
|
+
case "$":
|
|
2567
|
+
this.pos += 1;
|
|
2568
|
+
return this.state.vars[prefix.raw];
|
|
2569
|
+
case ",": {
|
|
2570
|
+
this.pos += 1;
|
|
2571
|
+
const start = this.pos;
|
|
2572
|
+
const end = start + prefix.value;
|
|
2573
|
+
if (end > this.text.length)
|
|
2574
|
+
throw new Error("String container overflows input");
|
|
2575
|
+
const value = this.text.slice(start, end);
|
|
2576
|
+
this.pos = end;
|
|
2577
|
+
return value;
|
|
2578
|
+
}
|
|
2579
|
+
case "^": {
|
|
2580
|
+
this.pos += 1;
|
|
2581
|
+
const target = this.pos + prefix.value;
|
|
2582
|
+
if (this.pointerCache.has(target))
|
|
2583
|
+
return this.pointerCache.get(target);
|
|
2584
|
+
const save = this.pos;
|
|
2585
|
+
this.pos = target;
|
|
2586
|
+
const value = this.evalValue();
|
|
2587
|
+
this.pos = save;
|
|
2588
|
+
this.pointerCache.set(target, value);
|
|
2589
|
+
return value;
|
|
2590
|
+
}
|
|
2591
|
+
case "=": {
|
|
2592
|
+
this.pos += 1;
|
|
2593
|
+
const place = this.readPlace();
|
|
2594
|
+
const value = this.evalValue();
|
|
2595
|
+
this.writePlace(place, value);
|
|
2596
|
+
return value;
|
|
2597
|
+
}
|
|
2598
|
+
case "~": {
|
|
2599
|
+
this.pos += 1;
|
|
2600
|
+
const place = this.readPlace();
|
|
2601
|
+
this.deletePlace(place);
|
|
2602
|
+
return;
|
|
2603
|
+
}
|
|
2604
|
+
case ";": {
|
|
2605
|
+
this.pos += 1;
|
|
2606
|
+
const kind = prefix.value % 2 === 0 ? "break" : "continue";
|
|
2607
|
+
const depth = Math.floor(prefix.value / 2) + 1;
|
|
2608
|
+
return { kind, depth };
|
|
2609
|
+
}
|
|
2610
|
+
case "(":
|
|
2611
|
+
return this.evalCall(prefix.value);
|
|
2612
|
+
case "[":
|
|
2613
|
+
return this.evalArray(prefix.value);
|
|
2614
|
+
case "{":
|
|
2615
|
+
return this.evalObject(prefix.value);
|
|
2616
|
+
case "?":
|
|
2617
|
+
case "!":
|
|
2618
|
+
case "|":
|
|
2619
|
+
case "&":
|
|
2620
|
+
return this.evalFlowParen(tag);
|
|
2621
|
+
case ">":
|
|
2622
|
+
case "<":
|
|
2623
|
+
return this.evalLoopLike(tag);
|
|
2624
|
+
case "#":
|
|
2625
|
+
return this.evalWhileLoop();
|
|
2626
|
+
default:
|
|
2627
|
+
throw new Error(`Unexpected tag '${tag}' at ${this.pos}`);
|
|
2628
|
+
}
|
|
2629
|
+
}
|
|
2630
|
+
evalCall(_prefix) {
|
|
2631
|
+
this.ensure("(");
|
|
2632
|
+
this.skipNonCode();
|
|
2633
|
+
if (this.text[this.pos] === ")") {
|
|
2634
|
+
this.pos += 1;
|
|
2635
|
+
return;
|
|
2197
2636
|
}
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2637
|
+
const callee = this.evalValue();
|
|
2638
|
+
const args = [];
|
|
2639
|
+
while (true) {
|
|
2640
|
+
this.skipNonCode();
|
|
2641
|
+
if (this.text[this.pos] === ")")
|
|
2642
|
+
break;
|
|
2643
|
+
args.push(this.evalValue());
|
|
2644
|
+
}
|
|
2645
|
+
this.ensure(")");
|
|
2646
|
+
if (typeof callee === "object" && callee && "__opcode" in callee) {
|
|
2647
|
+
return this.applyOpcode(callee.__opcode, args);
|
|
2648
|
+
}
|
|
2649
|
+
return this.navigate(callee, args);
|
|
2650
|
+
}
|
|
2651
|
+
evalArray(_prefix) {
|
|
2652
|
+
this.ensure("[");
|
|
2653
|
+
this.skipNonCode();
|
|
2654
|
+
this.skipIndexHeaderIfPresent();
|
|
2655
|
+
const out = [];
|
|
2656
|
+
while (true) {
|
|
2657
|
+
this.skipNonCode();
|
|
2658
|
+
if (this.text[this.pos] === "]")
|
|
2659
|
+
break;
|
|
2660
|
+
out.push(this.evalValue());
|
|
2661
|
+
}
|
|
2662
|
+
this.ensure("]");
|
|
2663
|
+
return out;
|
|
2664
|
+
}
|
|
2665
|
+
evalObject(_prefix) {
|
|
2666
|
+
this.ensure("{");
|
|
2667
|
+
this.skipNonCode();
|
|
2668
|
+
this.skipIndexHeaderIfPresent();
|
|
2669
|
+
const out = {};
|
|
2670
|
+
while (true) {
|
|
2671
|
+
this.skipNonCode();
|
|
2672
|
+
if (this.text[this.pos] === "}")
|
|
2673
|
+
break;
|
|
2674
|
+
const key = this.evalValue();
|
|
2675
|
+
const value = this.evalValue();
|
|
2676
|
+
out[String(key)] = value;
|
|
2677
|
+
}
|
|
2678
|
+
this.ensure("}");
|
|
2679
|
+
return out;
|
|
2680
|
+
}
|
|
2681
|
+
skipIndexHeaderIfPresent() {
|
|
2682
|
+
const save = this.pos;
|
|
2683
|
+
const countPrefix = this.readPrefix();
|
|
2684
|
+
if (this.text[this.pos] !== "#") {
|
|
2685
|
+
this.pos = save;
|
|
2686
|
+
return;
|
|
2687
|
+
}
|
|
2688
|
+
this.pos += 1;
|
|
2689
|
+
const widthChar = this.text[this.pos];
|
|
2690
|
+
const width = widthChar ? digitMap.get(widthChar) ?? 0 : 0;
|
|
2691
|
+
if (widthChar)
|
|
2692
|
+
this.pos += 1;
|
|
2693
|
+
const skipLen = countPrefix.value * width;
|
|
2694
|
+
this.pos += skipLen;
|
|
2695
|
+
}
|
|
2696
|
+
evalFlowParen(tag) {
|
|
2697
|
+
this.pos += 1;
|
|
2698
|
+
this.ensure("(");
|
|
2699
|
+
if (tag === "?") {
|
|
2700
|
+
const cond = this.evalValue();
|
|
2701
|
+
if (isDefined(cond)) {
|
|
2702
|
+
this.selfStack.push(cond);
|
|
2703
|
+
const thenValue = this.evalValue();
|
|
2704
|
+
this.selfStack.pop();
|
|
2705
|
+
if (this.hasMoreBefore(")"))
|
|
2706
|
+
this.skipValue();
|
|
2707
|
+
this.ensure(")");
|
|
2708
|
+
return thenValue;
|
|
2205
2709
|
}
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2710
|
+
this.skipValue();
|
|
2711
|
+
let elseValue = undefined;
|
|
2712
|
+
if (this.hasMoreBefore(")"))
|
|
2713
|
+
elseValue = this.evalValue();
|
|
2714
|
+
this.ensure(")");
|
|
2715
|
+
return elseValue;
|
|
2716
|
+
}
|
|
2717
|
+
if (tag === "!") {
|
|
2718
|
+
const cond = this.evalValue();
|
|
2719
|
+
if (!isDefined(cond)) {
|
|
2720
|
+
const thenValue = this.evalValue();
|
|
2721
|
+
if (this.hasMoreBefore(")"))
|
|
2722
|
+
this.skipValue();
|
|
2723
|
+
this.ensure(")");
|
|
2724
|
+
return thenValue;
|
|
2725
|
+
}
|
|
2726
|
+
this.skipValue();
|
|
2727
|
+
let elseValue = undefined;
|
|
2728
|
+
if (this.hasMoreBefore(")")) {
|
|
2729
|
+
this.selfStack.push(cond);
|
|
2730
|
+
elseValue = this.evalValue();
|
|
2731
|
+
this.selfStack.pop();
|
|
2732
|
+
}
|
|
2733
|
+
this.ensure(")");
|
|
2734
|
+
return elseValue;
|
|
2735
|
+
}
|
|
2736
|
+
if (tag === "|") {
|
|
2737
|
+
let result2 = undefined;
|
|
2738
|
+
while (this.hasMoreBefore(")")) {
|
|
2739
|
+
if (isDefined(result2))
|
|
2740
|
+
this.skipValue();
|
|
2741
|
+
else
|
|
2742
|
+
result2 = this.evalValue();
|
|
2743
|
+
}
|
|
2744
|
+
this.ensure(")");
|
|
2745
|
+
return result2;
|
|
2746
|
+
}
|
|
2747
|
+
let result = undefined;
|
|
2748
|
+
while (this.hasMoreBefore(")")) {
|
|
2749
|
+
if (!isDefined(result) && result !== undefined) {
|
|
2750
|
+
this.skipValue();
|
|
2751
|
+
continue;
|
|
2752
|
+
}
|
|
2753
|
+
result = this.evalValue();
|
|
2754
|
+
if (!isDefined(result)) {
|
|
2755
|
+
while (this.hasMoreBefore(")"))
|
|
2756
|
+
this.skipValue();
|
|
2757
|
+
break;
|
|
2758
|
+
}
|
|
2759
|
+
}
|
|
2760
|
+
this.ensure(")");
|
|
2761
|
+
return result;
|
|
2762
|
+
}
|
|
2763
|
+
evalLoopLike(tag) {
|
|
2764
|
+
this.pos += 1;
|
|
2765
|
+
const open = this.text[this.pos];
|
|
2766
|
+
if (!open || !"([{".includes(open))
|
|
2767
|
+
throw new Error(`Expected loop opener after '${tag}'`);
|
|
2768
|
+
const close = open === "(" ? ")" : open === "[" ? "]" : "}";
|
|
2769
|
+
this.pos += 1;
|
|
2770
|
+
const iterable = this.evalValue();
|
|
2771
|
+
const afterIterable = this.pos;
|
|
2772
|
+
const bodyValueCount = open === "{" ? 2 : 1;
|
|
2773
|
+
let varA;
|
|
2774
|
+
let varB;
|
|
2775
|
+
let bodyStart = afterIterable;
|
|
2776
|
+
let bodyEnd = afterIterable;
|
|
2777
|
+
let matched = false;
|
|
2778
|
+
for (const bindingCount of [2, 1, 0]) {
|
|
2779
|
+
this.pos = afterIterable;
|
|
2780
|
+
const vars = [];
|
|
2781
|
+
let bindingsOk = true;
|
|
2782
|
+
for (let index = 0;index < bindingCount; index += 1) {
|
|
2783
|
+
const binding = this.readBindingVarIfPresent();
|
|
2784
|
+
if (!binding) {
|
|
2785
|
+
bindingsOk = false;
|
|
2786
|
+
break;
|
|
2787
|
+
}
|
|
2788
|
+
vars.push(binding);
|
|
2789
|
+
}
|
|
2790
|
+
if (!bindingsOk)
|
|
2791
|
+
continue;
|
|
2792
|
+
const candidateBodyStart = this.pos;
|
|
2793
|
+
let cursor = candidateBodyStart;
|
|
2794
|
+
let bodyOk = true;
|
|
2795
|
+
for (let index = 0;index < bodyValueCount; index += 1) {
|
|
2796
|
+
const next = this.skipValueFrom(cursor);
|
|
2797
|
+
if (next <= cursor) {
|
|
2798
|
+
bodyOk = false;
|
|
2799
|
+
break;
|
|
2800
|
+
}
|
|
2801
|
+
cursor = next;
|
|
2802
|
+
}
|
|
2803
|
+
if (!bodyOk)
|
|
2804
|
+
continue;
|
|
2805
|
+
this.pos = cursor;
|
|
2806
|
+
this.skipNonCode();
|
|
2807
|
+
if (this.text[this.pos] !== close)
|
|
2808
|
+
continue;
|
|
2809
|
+
varA = vars[0];
|
|
2810
|
+
varB = vars[1];
|
|
2811
|
+
bodyStart = candidateBodyStart;
|
|
2812
|
+
bodyEnd = cursor;
|
|
2813
|
+
matched = true;
|
|
2814
|
+
break;
|
|
2815
|
+
}
|
|
2816
|
+
if (!matched)
|
|
2817
|
+
throw new Error(`Invalid loop/comprehension body before '${close}' at ${this.pos}`);
|
|
2818
|
+
this.pos = bodyEnd;
|
|
2819
|
+
this.ensure(close);
|
|
2820
|
+
if (open === "[")
|
|
2821
|
+
return this.evalArrayComprehension(iterable, varA, varB, bodyStart, bodyEnd, tag === "<");
|
|
2822
|
+
if (open === "{")
|
|
2823
|
+
return this.evalObjectComprehension(iterable, varA, varB, bodyStart, bodyEnd, tag === "<");
|
|
2824
|
+
return this.evalForLoop(iterable, varA, varB, bodyStart, bodyEnd, tag === "<");
|
|
2825
|
+
}
|
|
2826
|
+
iterate(iterable, keysOnly) {
|
|
2827
|
+
if (Array.isArray(iterable)) {
|
|
2828
|
+
if (keysOnly)
|
|
2829
|
+
return iterable.map((_value, index) => ({ key: index, value: index }));
|
|
2830
|
+
return iterable.map((value, index) => ({ key: index, value }));
|
|
2831
|
+
}
|
|
2832
|
+
if (iterable && typeof iterable === "object") {
|
|
2833
|
+
const entries = Object.entries(iterable);
|
|
2834
|
+
if (keysOnly)
|
|
2835
|
+
return entries.map(([key]) => ({ key, value: key }));
|
|
2836
|
+
return entries.map(([key, value]) => ({ key, value }));
|
|
2837
|
+
}
|
|
2838
|
+
if (typeof iterable === "number" && Number.isFinite(iterable) && iterable > 0) {
|
|
2839
|
+
const out = [];
|
|
2840
|
+
for (let index = 0;index < Math.floor(iterable); index += 1) {
|
|
2841
|
+
if (keysOnly)
|
|
2842
|
+
out.push({ key: index, value: index });
|
|
2843
|
+
else
|
|
2844
|
+
out.push({ key: index, value: index + 1 });
|
|
2845
|
+
}
|
|
2846
|
+
return out;
|
|
2847
|
+
}
|
|
2848
|
+
return [];
|
|
2849
|
+
}
|
|
2850
|
+
evalBodySlice(start, end) {
|
|
2851
|
+
const save = this.pos;
|
|
2852
|
+
this.pos = start;
|
|
2853
|
+
const value = this.evalValue();
|
|
2854
|
+
this.pos = end;
|
|
2855
|
+
this.pos = save;
|
|
2856
|
+
return value;
|
|
2857
|
+
}
|
|
2858
|
+
handleLoopControl(value) {
|
|
2859
|
+
if (value && typeof value === "object" && "kind" in value && "depth" in value) {
|
|
2860
|
+
return value;
|
|
2861
|
+
}
|
|
2862
|
+
return;
|
|
2863
|
+
}
|
|
2864
|
+
evalForLoop(iterable, varA, varB, bodyStart, bodyEnd, keysOnly) {
|
|
2865
|
+
const items = this.iterate(iterable, keysOnly);
|
|
2866
|
+
let last = undefined;
|
|
2867
|
+
for (const item of items) {
|
|
2868
|
+
this.tick();
|
|
2869
|
+
const currentSelf = keysOnly ? item.key : item.value;
|
|
2870
|
+
this.selfStack.push(currentSelf);
|
|
2871
|
+
if (varA && varB) {
|
|
2872
|
+
this.state.vars[varA] = item.key;
|
|
2873
|
+
this.state.vars[varB] = keysOnly ? item.key : item.value;
|
|
2874
|
+
} else if (varA) {
|
|
2875
|
+
this.state.vars[varA] = keysOnly ? item.key : item.value;
|
|
2876
|
+
}
|
|
2877
|
+
last = this.evalBodySlice(bodyStart, bodyEnd);
|
|
2878
|
+
this.selfStack.pop();
|
|
2879
|
+
const control = this.handleLoopControl(last);
|
|
2880
|
+
if (!control)
|
|
2881
|
+
continue;
|
|
2882
|
+
if (control.depth > 1)
|
|
2883
|
+
return { kind: control.kind, depth: control.depth - 1 };
|
|
2884
|
+
if (control.kind === "break")
|
|
2885
|
+
return;
|
|
2886
|
+
last = undefined;
|
|
2213
2887
|
continue;
|
|
2214
2888
|
}
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2889
|
+
return last;
|
|
2890
|
+
}
|
|
2891
|
+
evalWhileLoop() {
|
|
2892
|
+
this.pos += 1;
|
|
2893
|
+
this.ensure("(");
|
|
2894
|
+
const condStart = this.pos;
|
|
2895
|
+
const condValue = this.evalValue();
|
|
2896
|
+
const bodyStart = this.pos;
|
|
2897
|
+
const bodyValueCount = 1;
|
|
2898
|
+
let cursor = bodyStart;
|
|
2899
|
+
for (let index = 0;index < bodyValueCount; index += 1) {
|
|
2900
|
+
cursor = this.skipValueFrom(cursor);
|
|
2901
|
+
}
|
|
2902
|
+
const bodyEnd = cursor;
|
|
2903
|
+
this.pos = bodyEnd;
|
|
2904
|
+
this.ensure(")");
|
|
2905
|
+
const afterClose = this.pos;
|
|
2906
|
+
let last = undefined;
|
|
2907
|
+
let currentCond = condValue;
|
|
2908
|
+
while (isDefined(currentCond)) {
|
|
2909
|
+
this.tick();
|
|
2910
|
+
this.selfStack.push(currentCond);
|
|
2911
|
+
last = this.evalBodySlice(bodyStart, bodyEnd);
|
|
2912
|
+
this.selfStack.pop();
|
|
2913
|
+
const control = this.handleLoopControl(last);
|
|
2914
|
+
if (control) {
|
|
2915
|
+
if (control.depth > 1)
|
|
2916
|
+
return { kind: control.kind, depth: control.depth - 1 };
|
|
2917
|
+
if (control.kind === "break")
|
|
2918
|
+
return;
|
|
2919
|
+
last = undefined;
|
|
2920
|
+
}
|
|
2921
|
+
currentCond = this.evalBodySlice(condStart, bodyStart);
|
|
2922
|
+
}
|
|
2923
|
+
this.pos = afterClose;
|
|
2924
|
+
return last;
|
|
2925
|
+
}
|
|
2926
|
+
evalArrayComprehension(iterable, varA, varB, bodyStart, bodyEnd, keysOnly) {
|
|
2927
|
+
const items = this.iterate(iterable, keysOnly);
|
|
2928
|
+
const out = [];
|
|
2929
|
+
for (const item of items) {
|
|
2930
|
+
this.tick();
|
|
2931
|
+
const currentSelf = keysOnly ? item.key : item.value;
|
|
2932
|
+
this.selfStack.push(currentSelf);
|
|
2933
|
+
if (varA && varB) {
|
|
2934
|
+
this.state.vars[varA] = item.key;
|
|
2935
|
+
this.state.vars[varB] = keysOnly ? item.key : item.value;
|
|
2936
|
+
} else if (varA) {
|
|
2937
|
+
this.state.vars[varA] = keysOnly ? item.key : item.value;
|
|
2938
|
+
}
|
|
2939
|
+
const value = this.evalBodySlice(bodyStart, bodyEnd);
|
|
2940
|
+
this.selfStack.pop();
|
|
2941
|
+
const control = this.handleLoopControl(value);
|
|
2942
|
+
if (control) {
|
|
2943
|
+
if (control.depth > 1)
|
|
2944
|
+
return { kind: control.kind, depth: control.depth - 1 };
|
|
2945
|
+
if (control.kind === "break")
|
|
2946
|
+
break;
|
|
2947
|
+
continue;
|
|
2948
|
+
}
|
|
2949
|
+
if (isDefined(value))
|
|
2950
|
+
out.push(value);
|
|
2951
|
+
}
|
|
2952
|
+
return out;
|
|
2953
|
+
}
|
|
2954
|
+
evalObjectComprehension(iterable, varA, varB, bodyStart, bodyEnd, keysOnly) {
|
|
2955
|
+
const items = this.iterate(iterable, keysOnly);
|
|
2956
|
+
const out = {};
|
|
2957
|
+
for (const item of items) {
|
|
2958
|
+
this.tick();
|
|
2959
|
+
const currentSelf = keysOnly ? item.key : item.value;
|
|
2960
|
+
this.selfStack.push(currentSelf);
|
|
2961
|
+
if (varA && varB) {
|
|
2962
|
+
this.state.vars[varA] = item.key;
|
|
2963
|
+
this.state.vars[varB] = keysOnly ? item.key : item.value;
|
|
2964
|
+
} else if (varA) {
|
|
2965
|
+
this.state.vars[varA] = keysOnly ? item.key : item.value;
|
|
2966
|
+
}
|
|
2967
|
+
const save = this.pos;
|
|
2968
|
+
this.pos = bodyStart;
|
|
2969
|
+
const key = this.evalValue();
|
|
2970
|
+
const value = this.evalValue();
|
|
2971
|
+
this.pos = save;
|
|
2972
|
+
this.selfStack.pop();
|
|
2973
|
+
const control = this.handleLoopControl(value);
|
|
2974
|
+
if (control) {
|
|
2975
|
+
if (control.depth > 1)
|
|
2976
|
+
return { kind: control.kind, depth: control.depth - 1 };
|
|
2977
|
+
if (control.kind === "break")
|
|
2978
|
+
break;
|
|
2979
|
+
continue;
|
|
2980
|
+
}
|
|
2981
|
+
if (isDefined(value))
|
|
2982
|
+
out[String(key)] = value;
|
|
2983
|
+
}
|
|
2984
|
+
return out;
|
|
2985
|
+
}
|
|
2986
|
+
applyOpcode(id, args) {
|
|
2987
|
+
const custom = this.customOpcodes.get(id);
|
|
2988
|
+
if (custom)
|
|
2989
|
+
return custom(args, this.state);
|
|
2990
|
+
switch (id) {
|
|
2991
|
+
case OPCODES.do:
|
|
2992
|
+
return args.length ? args[args.length - 1] : undefined;
|
|
2993
|
+
case OPCODES.add:
|
|
2994
|
+
if (typeof args[0] === "string" || typeof args[1] === "string") {
|
|
2995
|
+
return String(args[0] ?? "") + String(args[1] ?? "");
|
|
2996
|
+
}
|
|
2997
|
+
return Number(args[0] ?? 0) + Number(args[1] ?? 0);
|
|
2998
|
+
case OPCODES.sub:
|
|
2999
|
+
return Number(args[0] ?? 0) - Number(args[1] ?? 0);
|
|
3000
|
+
case OPCODES.mul:
|
|
3001
|
+
return Number(args[0] ?? 0) * Number(args[1] ?? 0);
|
|
3002
|
+
case OPCODES.div:
|
|
3003
|
+
return Number(args[0] ?? 0) / Number(args[1] ?? 0);
|
|
3004
|
+
case OPCODES.mod:
|
|
3005
|
+
return Number(args[0] ?? 0) % Number(args[1] ?? 0);
|
|
3006
|
+
case OPCODES.neg:
|
|
3007
|
+
return -Number(args[0] ?? 0);
|
|
3008
|
+
case OPCODES.not: {
|
|
3009
|
+
const value = args[0];
|
|
3010
|
+
if (typeof value === "boolean")
|
|
3011
|
+
return !value;
|
|
3012
|
+
return ~Number(value ?? 0);
|
|
3013
|
+
}
|
|
3014
|
+
case OPCODES.and: {
|
|
3015
|
+
const [a, b] = args;
|
|
3016
|
+
if (typeof a === "boolean" || typeof b === "boolean")
|
|
3017
|
+
return Boolean(a) && Boolean(b);
|
|
3018
|
+
return Number(a ?? 0) & Number(b ?? 0);
|
|
3019
|
+
}
|
|
3020
|
+
case OPCODES.or: {
|
|
3021
|
+
const [a, b] = args;
|
|
3022
|
+
if (typeof a === "boolean" || typeof b === "boolean")
|
|
3023
|
+
return Boolean(a) || Boolean(b);
|
|
3024
|
+
return Number(a ?? 0) | Number(b ?? 0);
|
|
3025
|
+
}
|
|
3026
|
+
case OPCODES.xor: {
|
|
3027
|
+
const [a, b] = args;
|
|
3028
|
+
if (typeof a === "boolean" || typeof b === "boolean")
|
|
3029
|
+
return Boolean(a) !== Boolean(b);
|
|
3030
|
+
return Number(a ?? 0) ^ Number(b ?? 0);
|
|
3031
|
+
}
|
|
3032
|
+
case OPCODES.eq:
|
|
3033
|
+
return args[0] === args[1] ? args[0] : undefined;
|
|
3034
|
+
case OPCODES.neq:
|
|
3035
|
+
return args[0] !== args[1] ? args[0] : undefined;
|
|
3036
|
+
case OPCODES.gt:
|
|
3037
|
+
return Number(args[0]) > Number(args[1]) ? args[0] : undefined;
|
|
3038
|
+
case OPCODES.gte:
|
|
3039
|
+
return Number(args[0]) >= Number(args[1]) ? args[0] : undefined;
|
|
3040
|
+
case OPCODES.lt:
|
|
3041
|
+
return Number(args[0]) < Number(args[1]) ? args[0] : undefined;
|
|
3042
|
+
case OPCODES.lte:
|
|
3043
|
+
return Number(args[0]) <= Number(args[1]) ? args[0] : undefined;
|
|
3044
|
+
case OPCODES.boolean:
|
|
3045
|
+
return typeof args[0] === "boolean" ? args[0] : undefined;
|
|
3046
|
+
case OPCODES.number:
|
|
3047
|
+
return typeof args[0] === "number" ? args[0] : undefined;
|
|
3048
|
+
case OPCODES.string:
|
|
3049
|
+
return typeof args[0] === "string" ? args[0] : undefined;
|
|
3050
|
+
case OPCODES.array:
|
|
3051
|
+
return Array.isArray(args[0]) ? args[0] : undefined;
|
|
3052
|
+
case OPCODES.object:
|
|
3053
|
+
return args[0] && typeof args[0] === "object" && !Array.isArray(args[0]) ? args[0] : undefined;
|
|
3054
|
+
default:
|
|
3055
|
+
throw new Error(`Unknown opcode ${id}`);
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
navigate(base, keys) {
|
|
3059
|
+
let current = base;
|
|
3060
|
+
for (const key of keys) {
|
|
3061
|
+
if (current === undefined || current === null)
|
|
3062
|
+
return;
|
|
3063
|
+
current = current[String(key)];
|
|
3064
|
+
}
|
|
3065
|
+
return current;
|
|
3066
|
+
}
|
|
3067
|
+
readPlace() {
|
|
3068
|
+
this.skipNonCode();
|
|
3069
|
+
const direct = this.readRootVarOrRefIfPresent();
|
|
3070
|
+
if (direct) {
|
|
3071
|
+
const keys = [];
|
|
3072
|
+
this.skipNonCode();
|
|
3073
|
+
if (this.text[this.pos] === "(") {
|
|
3074
|
+
this.pos += 1;
|
|
3075
|
+
while (true) {
|
|
3076
|
+
this.skipNonCode();
|
|
3077
|
+
if (this.text[this.pos] === ")")
|
|
3078
|
+
break;
|
|
3079
|
+
keys.push(this.evalValue());
|
|
3080
|
+
}
|
|
3081
|
+
this.pos += 1;
|
|
3082
|
+
}
|
|
3083
|
+
return {
|
|
3084
|
+
root: direct.root,
|
|
3085
|
+
keys,
|
|
3086
|
+
isRef: direct.isRef
|
|
3087
|
+
};
|
|
3088
|
+
}
|
|
3089
|
+
if (this.text[this.pos] === "(") {
|
|
3090
|
+
this.pos += 1;
|
|
3091
|
+
this.skipNonCode();
|
|
3092
|
+
const rootFromNav = this.readRootVarOrRefIfPresent();
|
|
3093
|
+
if (!rootFromNav)
|
|
3094
|
+
throw new Error(`Invalid place root at ${this.pos}`);
|
|
3095
|
+
const keys = [];
|
|
3096
|
+
while (true) {
|
|
3097
|
+
this.skipNonCode();
|
|
3098
|
+
if (this.text[this.pos] === ")")
|
|
3099
|
+
break;
|
|
3100
|
+
keys.push(this.evalValue());
|
|
3101
|
+
}
|
|
3102
|
+
this.pos += 1;
|
|
3103
|
+
return {
|
|
3104
|
+
root: rootFromNav.root,
|
|
3105
|
+
keys,
|
|
3106
|
+
isRef: rootFromNav.isRef
|
|
3107
|
+
};
|
|
3108
|
+
}
|
|
3109
|
+
throw new Error(`Invalid place at ${this.pos}`);
|
|
3110
|
+
}
|
|
3111
|
+
readRootVarOrRefIfPresent() {
|
|
3112
|
+
const save = this.pos;
|
|
3113
|
+
const prefix = this.readPrefix();
|
|
3114
|
+
const tag = this.text[this.pos];
|
|
3115
|
+
if (tag !== "$" && tag !== "'") {
|
|
3116
|
+
this.pos = save;
|
|
3117
|
+
return;
|
|
3118
|
+
}
|
|
3119
|
+
this.pos += 1;
|
|
3120
|
+
return {
|
|
3121
|
+
root: tag === "$" ? prefix.raw : prefix.value,
|
|
3122
|
+
isRef: tag === "'"
|
|
3123
|
+
};
|
|
3124
|
+
}
|
|
3125
|
+
writePlace(place, value) {
|
|
3126
|
+
const rootTable = place.isRef ? this.state.refs : this.state.vars;
|
|
3127
|
+
const rootKey = String(place.root);
|
|
3128
|
+
if (place.keys.length === 0) {
|
|
3129
|
+
rootTable[rootKey] = value;
|
|
3130
|
+
return;
|
|
3131
|
+
}
|
|
3132
|
+
let target = rootTable[rootKey];
|
|
3133
|
+
if (!target || typeof target !== "object") {
|
|
3134
|
+
target = {};
|
|
3135
|
+
rootTable[rootKey] = target;
|
|
3136
|
+
}
|
|
3137
|
+
for (let index = 0;index < place.keys.length - 1; index += 1) {
|
|
3138
|
+
const key = String(place.keys[index]);
|
|
3139
|
+
const next = target[key];
|
|
3140
|
+
if (!next || typeof next !== "object")
|
|
3141
|
+
target[key] = {};
|
|
3142
|
+
target = target[key];
|
|
3143
|
+
}
|
|
3144
|
+
target[String(place.keys[place.keys.length - 1])] = value;
|
|
3145
|
+
}
|
|
3146
|
+
deletePlace(place) {
|
|
3147
|
+
const rootTable = place.isRef ? this.state.refs : this.state.vars;
|
|
3148
|
+
const rootKey = String(place.root);
|
|
3149
|
+
if (place.keys.length === 0) {
|
|
3150
|
+
delete rootTable[rootKey];
|
|
3151
|
+
return;
|
|
3152
|
+
}
|
|
3153
|
+
let target = rootTable[rootKey];
|
|
3154
|
+
if (!target || typeof target !== "object")
|
|
3155
|
+
return;
|
|
3156
|
+
for (let index = 0;index < place.keys.length - 1; index += 1) {
|
|
3157
|
+
target = target[String(place.keys[index])];
|
|
3158
|
+
if (!target || typeof target !== "object")
|
|
3159
|
+
return;
|
|
3160
|
+
}
|
|
3161
|
+
delete target[String(place.keys[place.keys.length - 1])];
|
|
3162
|
+
}
|
|
3163
|
+
skipValue() {
|
|
3164
|
+
this.pos = this.skipValueFrom(this.pos);
|
|
3165
|
+
}
|
|
3166
|
+
skipValueFrom(startPos) {
|
|
3167
|
+
const save = this.pos;
|
|
3168
|
+
this.pos = startPos;
|
|
3169
|
+
this.skipNonCode();
|
|
3170
|
+
const prefix = this.readPrefix();
|
|
3171
|
+
const tag = this.text[this.pos];
|
|
3172
|
+
if (!tag) {
|
|
3173
|
+
this.pos = save;
|
|
3174
|
+
return startPos;
|
|
3175
|
+
}
|
|
3176
|
+
if (tag === ",") {
|
|
3177
|
+
this.pos += 1 + prefix.value;
|
|
3178
|
+
const end2 = this.pos;
|
|
3179
|
+
this.pos = save;
|
|
3180
|
+
return end2;
|
|
3181
|
+
}
|
|
3182
|
+
if (tag === "=") {
|
|
3183
|
+
this.pos += 1;
|
|
3184
|
+
this.skipValue();
|
|
3185
|
+
this.skipValue();
|
|
3186
|
+
const end2 = this.pos;
|
|
3187
|
+
this.pos = save;
|
|
3188
|
+
return end2;
|
|
3189
|
+
}
|
|
3190
|
+
if (tag === "~") {
|
|
3191
|
+
this.pos += 1;
|
|
3192
|
+
this.skipValue();
|
|
3193
|
+
const end2 = this.pos;
|
|
3194
|
+
this.pos = save;
|
|
3195
|
+
return end2;
|
|
3196
|
+
}
|
|
3197
|
+
if (tag === "*") {
|
|
3198
|
+
this.pos += 1;
|
|
3199
|
+
this.skipValue();
|
|
3200
|
+
const end2 = this.pos;
|
|
3201
|
+
this.pos = save;
|
|
3202
|
+
return end2;
|
|
3203
|
+
}
|
|
3204
|
+
if ("+:%$@'^;".includes(tag)) {
|
|
3205
|
+
this.pos += 1;
|
|
3206
|
+
const end2 = this.pos;
|
|
3207
|
+
this.pos = save;
|
|
3208
|
+
return end2;
|
|
3209
|
+
}
|
|
3210
|
+
if (tag === "?" || tag === "!" || tag === "|" || tag === "&" || tag === ">" || tag === "<" || tag === "#") {
|
|
3211
|
+
this.pos += 1;
|
|
3212
|
+
}
|
|
3213
|
+
const opener = this.text[this.pos];
|
|
3214
|
+
if (opener && "([{".includes(opener)) {
|
|
3215
|
+
const close = opener === "(" ? ")" : opener === "[" ? "]" : "}";
|
|
3216
|
+
if (prefix.value > 0) {
|
|
3217
|
+
this.pos += 1 + prefix.value + 1;
|
|
3218
|
+
const end3 = this.pos;
|
|
3219
|
+
this.pos = save;
|
|
3220
|
+
return end3;
|
|
3221
|
+
}
|
|
3222
|
+
this.pos += 1;
|
|
3223
|
+
while (true) {
|
|
3224
|
+
this.skipNonCode();
|
|
3225
|
+
if (this.text[this.pos] === close)
|
|
3226
|
+
break;
|
|
3227
|
+
this.skipValue();
|
|
3228
|
+
}
|
|
3229
|
+
this.pos += 1;
|
|
3230
|
+
const end2 = this.pos;
|
|
3231
|
+
this.pos = save;
|
|
3232
|
+
return end2;
|
|
3233
|
+
}
|
|
3234
|
+
this.pos += 1;
|
|
3235
|
+
const end = this.pos;
|
|
3236
|
+
this.pos = save;
|
|
3237
|
+
return end;
|
|
3238
|
+
}
|
|
3239
|
+
}
|
|
3240
|
+
function evaluateRexc(text, ctx = {}) {
|
|
3241
|
+
const interpreter = new CursorInterpreter(text, ctx);
|
|
3242
|
+
const value = interpreter.evaluateTopLevel();
|
|
3243
|
+
return { value, state: interpreter.runtimeState };
|
|
3244
|
+
}
|
|
3245
|
+
|
|
3246
|
+
// rex-repl.ts
|
|
3247
|
+
var req = createRequire2(import.meta.url);
|
|
3248
|
+
var { version } = req("./package.json");
|
|
3249
|
+
var C = {
|
|
3250
|
+
reset: "\x1B[0m",
|
|
3251
|
+
bold: "\x1B[1m",
|
|
3252
|
+
dim: "\x1B[2m",
|
|
3253
|
+
red: "\x1B[31m",
|
|
3254
|
+
green: "\x1B[32m",
|
|
3255
|
+
yellow: "\x1B[33m",
|
|
3256
|
+
blue: "\x1B[34m",
|
|
3257
|
+
cyan: "\x1B[36m",
|
|
3258
|
+
gray: "\x1B[90m",
|
|
3259
|
+
boldBlue: "\x1B[1;34m"
|
|
3260
|
+
};
|
|
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;
|
|
3262
|
+
function highlightLine(line) {
|
|
3263
|
+
let result = "";
|
|
3264
|
+
let lastIndex = 0;
|
|
3265
|
+
TOKEN_RE.lastIndex = 0;
|
|
3266
|
+
for (const m of line.matchAll(TOKEN_RE)) {
|
|
3267
|
+
result += line.slice(lastIndex, m.index);
|
|
3268
|
+
const text = m[0];
|
|
3269
|
+
const g = m.groups;
|
|
3270
|
+
if (g.blockComment || g.lineComment) {
|
|
3271
|
+
result += C.gray + text + C.reset;
|
|
3272
|
+
} else if (g.dstring || g.sstring) {
|
|
3273
|
+
result += C.green + text + C.reset;
|
|
3274
|
+
} else if (g.keyword) {
|
|
3275
|
+
result += C.boldBlue + text + C.reset;
|
|
3276
|
+
} else if (g.literal) {
|
|
3277
|
+
result += C.yellow + text + C.reset;
|
|
3278
|
+
} else if (g.typePred) {
|
|
3279
|
+
result += C.cyan + text + C.reset;
|
|
3280
|
+
} else if (g.num) {
|
|
3281
|
+
result += C.cyan + text + C.reset;
|
|
3282
|
+
} else {
|
|
3283
|
+
result += text;
|
|
3284
|
+
}
|
|
3285
|
+
lastIndex = m.index + text.length;
|
|
3286
|
+
}
|
|
3287
|
+
result += line.slice(lastIndex);
|
|
3288
|
+
return result;
|
|
3289
|
+
}
|
|
3290
|
+
var REXC_DIGITS = new Set("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_");
|
|
3291
|
+
function highlightRexc(text) {
|
|
3292
|
+
let out = "";
|
|
3293
|
+
let i = 0;
|
|
3294
|
+
function readPrefix() {
|
|
3295
|
+
const start = i;
|
|
3296
|
+
while (i < text.length && REXC_DIGITS.has(text[i]))
|
|
3297
|
+
i++;
|
|
3298
|
+
return text.slice(start, i);
|
|
3299
|
+
}
|
|
3300
|
+
while (i < text.length) {
|
|
3301
|
+
const ch = text[i];
|
|
3302
|
+
if (ch === " " || ch === "\t" || ch === `
|
|
3303
|
+
` || ch === "\r") {
|
|
3304
|
+
out += ch;
|
|
3305
|
+
i++;
|
|
2221
3306
|
continue;
|
|
2222
3307
|
}
|
|
2223
|
-
if (
|
|
2224
|
-
const
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
3308
|
+
if (ch === "/" && text[i + 1] === "/") {
|
|
3309
|
+
const start = i;
|
|
3310
|
+
i += 2;
|
|
3311
|
+
while (i < text.length && text[i] !== `
|
|
3312
|
+
`)
|
|
3313
|
+
i++;
|
|
3314
|
+
out += C.gray + text.slice(start, i) + C.reset;
|
|
2229
3315
|
continue;
|
|
2230
3316
|
}
|
|
2231
|
-
if (
|
|
2232
|
-
const
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
3317
|
+
if (ch === "/" && text[i + 1] === "*") {
|
|
3318
|
+
const start = i;
|
|
3319
|
+
i += 2;
|
|
3320
|
+
while (i < text.length && !(text[i] === "*" && text[i + 1] === "/"))
|
|
3321
|
+
i++;
|
|
3322
|
+
if (i < text.length)
|
|
3323
|
+
i += 2;
|
|
3324
|
+
out += C.gray + text.slice(start, i) + C.reset;
|
|
2237
3325
|
continue;
|
|
2238
3326
|
}
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
3327
|
+
const prefix = readPrefix();
|
|
3328
|
+
if (i >= text.length) {
|
|
3329
|
+
out += prefix;
|
|
3330
|
+
break;
|
|
3331
|
+
}
|
|
3332
|
+
const tag = text[i];
|
|
3333
|
+
switch (tag) {
|
|
3334
|
+
case "+":
|
|
3335
|
+
case "*":
|
|
3336
|
+
out += C.cyan + prefix + tag + C.reset;
|
|
3337
|
+
i++;
|
|
3338
|
+
break;
|
|
3339
|
+
case ":":
|
|
3340
|
+
out += C.dim + prefix + tag + C.reset;
|
|
3341
|
+
i++;
|
|
3342
|
+
break;
|
|
3343
|
+
case "%":
|
|
3344
|
+
out += C.boldBlue + prefix + tag + C.reset;
|
|
3345
|
+
i++;
|
|
3346
|
+
break;
|
|
3347
|
+
case "$":
|
|
3348
|
+
out += C.yellow + prefix + tag + C.reset;
|
|
3349
|
+
i++;
|
|
3350
|
+
break;
|
|
3351
|
+
case "@":
|
|
3352
|
+
out += C.yellow + prefix + tag + C.reset;
|
|
3353
|
+
i++;
|
|
3354
|
+
break;
|
|
3355
|
+
case "'":
|
|
3356
|
+
out += C.dim + prefix + tag + C.reset;
|
|
3357
|
+
i++;
|
|
3358
|
+
break;
|
|
3359
|
+
case ",": {
|
|
3360
|
+
i++;
|
|
3361
|
+
let len = 0;
|
|
3362
|
+
for (const ch2 of prefix)
|
|
3363
|
+
len = len * 64 + (REXC_DIGITS.has(ch2) ? "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_".indexOf(ch2) : 0);
|
|
3364
|
+
const content = text.slice(i, i + len);
|
|
3365
|
+
i += len;
|
|
3366
|
+
out += C.green + prefix + "," + content + C.reset;
|
|
3367
|
+
break;
|
|
3368
|
+
}
|
|
3369
|
+
case "=":
|
|
3370
|
+
case "~":
|
|
3371
|
+
out += C.red + prefix + tag + C.reset;
|
|
3372
|
+
i++;
|
|
3373
|
+
break;
|
|
3374
|
+
case "?":
|
|
3375
|
+
case "!":
|
|
3376
|
+
case "|":
|
|
3377
|
+
case "&":
|
|
3378
|
+
case ">":
|
|
3379
|
+
case "<":
|
|
3380
|
+
case "#":
|
|
3381
|
+
out += C.boldBlue + prefix + tag + C.reset;
|
|
3382
|
+
i++;
|
|
3383
|
+
break;
|
|
3384
|
+
case ";":
|
|
3385
|
+
out += C.boldBlue + prefix + tag + C.reset;
|
|
3386
|
+
i++;
|
|
3387
|
+
break;
|
|
3388
|
+
case "^":
|
|
3389
|
+
out += C.dim + prefix + tag + C.reset;
|
|
3390
|
+
i++;
|
|
3391
|
+
break;
|
|
3392
|
+
case "(":
|
|
3393
|
+
case ")":
|
|
3394
|
+
case "[":
|
|
3395
|
+
case "]":
|
|
3396
|
+
case "{":
|
|
3397
|
+
case "}":
|
|
3398
|
+
out += C.dim + prefix + C.reset + tag;
|
|
3399
|
+
i++;
|
|
3400
|
+
break;
|
|
3401
|
+
default:
|
|
3402
|
+
out += prefix + tag;
|
|
3403
|
+
i++;
|
|
3404
|
+
break;
|
|
3405
|
+
}
|
|
3406
|
+
}
|
|
3407
|
+
return out;
|
|
3408
|
+
}
|
|
3409
|
+
function stripStringsAndComments(source) {
|
|
3410
|
+
return source.replace(/\/\*[\s\S]*?\*\/|\/\/[^\n]*|"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/g, (m) => " ".repeat(m.length));
|
|
3411
|
+
}
|
|
3412
|
+
function countWord(text, word) {
|
|
3413
|
+
const re = new RegExp(`\\b${word}(?![a-zA-Z0-9_-])`, "g");
|
|
3414
|
+
return (text.match(re) || []).length;
|
|
2267
3415
|
}
|
|
2268
|
-
|
|
2269
|
-
const chunks = [];
|
|
2270
|
-
for await (const chunk of process.stdin) {
|
|
2271
|
-
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
2272
|
-
}
|
|
2273
|
-
return Buffer.concat(chunks).toString("utf8");
|
|
2274
|
-
}
|
|
2275
|
-
async function resolveSource(options) {
|
|
2276
|
-
if (options.expr && options.file)
|
|
2277
|
-
throw new Error("Use only one of --expr or --file");
|
|
2278
|
-
if (options.expr)
|
|
2279
|
-
return options.expr;
|
|
2280
|
-
if (options.file)
|
|
2281
|
-
return readFile(options.file, "utf8");
|
|
2282
|
-
if (!process.stdin.isTTY) {
|
|
2283
|
-
const piped = await readStdin();
|
|
2284
|
-
if (piped.trim().length > 0)
|
|
2285
|
-
return piped;
|
|
2286
|
-
}
|
|
2287
|
-
throw new Error("No input provided. Use --expr, --file, or pipe source via stdin.");
|
|
2288
|
-
}
|
|
2289
|
-
async function loadDomainRefsFromFolder(folderPath) {
|
|
2290
|
-
const schemaPath = resolve(folderPath, "rex-domain.json");
|
|
2291
|
-
let parsed;
|
|
3416
|
+
function isIncomplete(buffer) {
|
|
2292
3417
|
try {
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
3418
|
+
if (grammar.match(buffer).succeeded())
|
|
3419
|
+
return false;
|
|
3420
|
+
} catch {}
|
|
3421
|
+
const stripped = stripStringsAndComments(buffer);
|
|
3422
|
+
let parens = 0, brackets = 0, braces = 0;
|
|
3423
|
+
for (const ch of stripped) {
|
|
3424
|
+
if (ch === "(")
|
|
3425
|
+
parens++;
|
|
3426
|
+
else if (ch === ")")
|
|
3427
|
+
parens--;
|
|
3428
|
+
else if (ch === "[")
|
|
3429
|
+
brackets++;
|
|
3430
|
+
else if (ch === "]")
|
|
3431
|
+
brackets--;
|
|
3432
|
+
else if (ch === "{")
|
|
3433
|
+
braces++;
|
|
3434
|
+
else if (ch === "}")
|
|
3435
|
+
braces--;
|
|
3436
|
+
}
|
|
3437
|
+
if (parens > 0 || brackets > 0 || braces > 0)
|
|
3438
|
+
return true;
|
|
3439
|
+
const doCount = countWord(stripped, "do");
|
|
3440
|
+
const endCount = countWord(stripped, "end");
|
|
3441
|
+
if (doCount > endCount)
|
|
3442
|
+
return true;
|
|
3443
|
+
const trimmed = buffer.trimEnd();
|
|
3444
|
+
if (/[+\-*/%&|^=<>]$/.test(trimmed))
|
|
3445
|
+
return true;
|
|
3446
|
+
if (/\b(?:and|or|do|in|of)\s*$/.test(trimmed))
|
|
3447
|
+
return true;
|
|
3448
|
+
return false;
|
|
3449
|
+
}
|
|
3450
|
+
function formatResult(value) {
|
|
3451
|
+
let text;
|
|
3452
|
+
try {
|
|
3453
|
+
text = stringify(value, { maxWidth: 60 });
|
|
3454
|
+
} catch {
|
|
3455
|
+
text = String(value);
|
|
2307
3456
|
}
|
|
2308
|
-
return
|
|
3457
|
+
return `${C.gray}→${C.reset} ${highlightLine(text)}`;
|
|
2309
3458
|
}
|
|
2310
|
-
|
|
2311
|
-
const
|
|
2312
|
-
|
|
2313
|
-
|
|
3459
|
+
function formatVarState(vars) {
|
|
3460
|
+
const entries = Object.entries(vars);
|
|
3461
|
+
if (entries.length === 0)
|
|
3462
|
+
return "";
|
|
3463
|
+
const MAX_LINE = 70;
|
|
3464
|
+
const MAX_VALUE = 30;
|
|
3465
|
+
const parts = [];
|
|
3466
|
+
let totalLen = 0;
|
|
3467
|
+
for (const [key, val] of entries) {
|
|
3468
|
+
let valStr;
|
|
3469
|
+
try {
|
|
3470
|
+
valStr = stringify(val, { maxWidth: MAX_VALUE });
|
|
3471
|
+
} catch {
|
|
3472
|
+
valStr = String(val);
|
|
3473
|
+
}
|
|
3474
|
+
if (valStr.length > MAX_VALUE) {
|
|
3475
|
+
valStr = valStr.slice(0, MAX_VALUE - 1) + "…";
|
|
3476
|
+
}
|
|
3477
|
+
const part = `${key} = ${valStr}`;
|
|
3478
|
+
if (totalLen + part.length + 2 > MAX_LINE && parts.length > 0) {
|
|
3479
|
+
parts.push("…");
|
|
3480
|
+
break;
|
|
3481
|
+
}
|
|
3482
|
+
parts.push(part);
|
|
3483
|
+
totalLen += part.length + 2;
|
|
3484
|
+
}
|
|
3485
|
+
return `${C.dim} ${parts.join(", ")}${C.reset}`;
|
|
3486
|
+
}
|
|
3487
|
+
var KEYWORDS = [
|
|
3488
|
+
"when",
|
|
3489
|
+
"unless",
|
|
3490
|
+
"while",
|
|
3491
|
+
"for",
|
|
3492
|
+
"do",
|
|
3493
|
+
"end",
|
|
3494
|
+
"in",
|
|
3495
|
+
"of",
|
|
3496
|
+
"and",
|
|
3497
|
+
"or",
|
|
3498
|
+
"else",
|
|
3499
|
+
"break",
|
|
3500
|
+
"continue",
|
|
3501
|
+
"delete",
|
|
3502
|
+
"self",
|
|
3503
|
+
"true",
|
|
3504
|
+
"false",
|
|
3505
|
+
"null",
|
|
3506
|
+
"undefined",
|
|
3507
|
+
"string",
|
|
3508
|
+
"number",
|
|
3509
|
+
"object",
|
|
3510
|
+
"array",
|
|
3511
|
+
"boolean"
|
|
3512
|
+
];
|
|
3513
|
+
function completer(state) {
|
|
3514
|
+
return (line) => {
|
|
3515
|
+
const match = line.match(/[a-zA-Z_][a-zA-Z0-9_.-]*$/);
|
|
3516
|
+
const partial = match ? match[0] : "";
|
|
3517
|
+
if (!partial)
|
|
3518
|
+
return [[], ""];
|
|
3519
|
+
const varNames = Object.keys(state.vars);
|
|
3520
|
+
const all = [...new Set([...KEYWORDS, ...varNames])];
|
|
3521
|
+
const hits = all.filter((w) => w.startsWith(partial));
|
|
3522
|
+
return [hits, partial];
|
|
3523
|
+
};
|
|
2314
3524
|
}
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
3525
|
+
function handleDotCommand(cmd, state, rl) {
|
|
3526
|
+
function toggleLabel(on) {
|
|
3527
|
+
return on ? `${C.green}on${C.reset}` : `${C.dim}off${C.reset}`;
|
|
3528
|
+
}
|
|
3529
|
+
switch (cmd) {
|
|
3530
|
+
case ".help":
|
|
3531
|
+
console.log([
|
|
3532
|
+
`${C.boldBlue}Rex REPL Commands:${C.reset}`,
|
|
3533
|
+
" .help Show this help message",
|
|
3534
|
+
" .vars Show all current variables",
|
|
3535
|
+
" .clear Clear all variables",
|
|
3536
|
+
" .ir Toggle showing IR JSON after parsing",
|
|
3537
|
+
" .rexc Toggle showing compiled rexc before execution",
|
|
3538
|
+
" .opt Toggle IR optimizations",
|
|
3539
|
+
" .exit Exit the REPL",
|
|
3540
|
+
"",
|
|
3541
|
+
"Enter Rex expressions to evaluate them.",
|
|
3542
|
+
"Multi-line: open brackets or do/end blocks continue on the next line.",
|
|
3543
|
+
"Ctrl-C cancels multi-line input.",
|
|
3544
|
+
"Ctrl-D exits."
|
|
3545
|
+
].join(`
|
|
3546
|
+
`));
|
|
3547
|
+
return true;
|
|
3548
|
+
case ".ir":
|
|
3549
|
+
state.showIR = !state.showIR;
|
|
3550
|
+
console.log(`${C.dim} IR display: ${toggleLabel(state.showIR)}${C.reset}`);
|
|
3551
|
+
return true;
|
|
3552
|
+
case ".rexc":
|
|
3553
|
+
state.showRexc = !state.showRexc;
|
|
3554
|
+
console.log(`${C.dim} Rexc display: ${toggleLabel(state.showRexc)}${C.reset}`);
|
|
3555
|
+
return true;
|
|
3556
|
+
case ".opt":
|
|
3557
|
+
state.optimize = !state.optimize;
|
|
3558
|
+
console.log(`${C.dim} Optimizations: ${toggleLabel(state.optimize)}${C.reset}`);
|
|
3559
|
+
return true;
|
|
3560
|
+
case ".vars": {
|
|
3561
|
+
const entries = Object.entries(state.vars);
|
|
3562
|
+
if (entries.length === 0) {
|
|
3563
|
+
console.log(`${C.dim} (no variables)${C.reset}`);
|
|
3564
|
+
} else {
|
|
3565
|
+
for (const [key, val] of entries) {
|
|
3566
|
+
let valStr;
|
|
3567
|
+
try {
|
|
3568
|
+
valStr = stringify(val, { maxWidth: 60 });
|
|
3569
|
+
} catch {
|
|
3570
|
+
valStr = String(val);
|
|
3571
|
+
}
|
|
3572
|
+
console.log(` ${key} = ${highlightLine(valStr)}`);
|
|
3573
|
+
}
|
|
3574
|
+
}
|
|
3575
|
+
return true;
|
|
3576
|
+
}
|
|
3577
|
+
case ".clear":
|
|
3578
|
+
state.vars = {};
|
|
3579
|
+
state.refs = {};
|
|
3580
|
+
console.log(`${C.dim} Variables cleared.${C.reset}`);
|
|
3581
|
+
return true;
|
|
3582
|
+
case ".exit":
|
|
3583
|
+
rl.close();
|
|
3584
|
+
return true;
|
|
3585
|
+
default:
|
|
3586
|
+
if (cmd.startsWith(".")) {
|
|
3587
|
+
console.log(`${C.red} Unknown command: ${cmd}. Type .help for available commands.${C.reset}`);
|
|
3588
|
+
return true;
|
|
3589
|
+
}
|
|
3590
|
+
return false;
|
|
2333
3591
|
}
|
|
2334
|
-
console.log(output);
|
|
2335
3592
|
}
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
3593
|
+
var GAS_LIMIT = 1e7;
|
|
3594
|
+
async function startRepl() {
|
|
3595
|
+
const state = { vars: {}, refs: {}, showIR: false, showRexc: false, optimize: false };
|
|
3596
|
+
let multiLineBuffer = "";
|
|
3597
|
+
const PRIMARY_PROMPT = "rex> ";
|
|
3598
|
+
const CONT_PROMPT = "... ";
|
|
3599
|
+
const STYLED_PRIMARY = `${C.boldBlue}rex${C.reset}> `;
|
|
3600
|
+
const STYLED_CONT = `${C.dim}...${C.reset} `;
|
|
3601
|
+
let currentPrompt = PRIMARY_PROMPT;
|
|
3602
|
+
let styledPrompt = STYLED_PRIMARY;
|
|
3603
|
+
console.log(`${C.boldBlue}Rex${C.reset} v${version} — type ${C.dim}.help${C.reset} for commands`);
|
|
3604
|
+
const rl = readline.createInterface({
|
|
3605
|
+
input: process.stdin,
|
|
3606
|
+
output: process.stdout,
|
|
3607
|
+
prompt: PRIMARY_PROMPT,
|
|
3608
|
+
historySize: 500,
|
|
3609
|
+
completer: completer(state),
|
|
3610
|
+
terminal: true
|
|
3611
|
+
});
|
|
3612
|
+
process.stdin.on("keypress", () => {
|
|
3613
|
+
process.nextTick(() => {
|
|
3614
|
+
if (!rl.line && rl.line !== "")
|
|
3615
|
+
return;
|
|
3616
|
+
readline.clearLine(process.stdout, 0);
|
|
3617
|
+
readline.cursorTo(process.stdout, 0);
|
|
3618
|
+
process.stdout.write(styledPrompt + highlightLine(rl.line));
|
|
3619
|
+
readline.cursorTo(process.stdout, currentPrompt.length + rl.cursor);
|
|
3620
|
+
});
|
|
3621
|
+
});
|
|
3622
|
+
process.stdin.on("keypress", (_ch, key) => {
|
|
3623
|
+
if (key?.ctrl && key.name === "l") {
|
|
3624
|
+
readline.cursorTo(process.stdout, 0, 0);
|
|
3625
|
+
readline.clearScreenDown(process.stdout);
|
|
3626
|
+
rl.prompt();
|
|
3627
|
+
}
|
|
3628
|
+
});
|
|
3629
|
+
rl.on("SIGINT", () => {
|
|
3630
|
+
if (multiLineBuffer) {
|
|
3631
|
+
multiLineBuffer = "";
|
|
3632
|
+
currentPrompt = PRIMARY_PROMPT;
|
|
3633
|
+
styledPrompt = STYLED_PRIMARY;
|
|
3634
|
+
rl.setPrompt(PRIMARY_PROMPT);
|
|
3635
|
+
process.stdout.write(`
|
|
3636
|
+
`);
|
|
3637
|
+
rl.prompt();
|
|
3638
|
+
} else {
|
|
3639
|
+
console.log();
|
|
3640
|
+
rl.close();
|
|
3641
|
+
}
|
|
3642
|
+
});
|
|
3643
|
+
function resetPrompt() {
|
|
3644
|
+
currentPrompt = PRIMARY_PROMPT;
|
|
3645
|
+
styledPrompt = STYLED_PRIMARY;
|
|
3646
|
+
rl.setPrompt(PRIMARY_PROMPT);
|
|
3647
|
+
rl.prompt();
|
|
3648
|
+
}
|
|
3649
|
+
rl.on("line", (line) => {
|
|
3650
|
+
const trimmed = line.trim();
|
|
3651
|
+
if (!multiLineBuffer && trimmed.startsWith(".")) {
|
|
3652
|
+
if (handleDotCommand(trimmed, state, rl)) {
|
|
3653
|
+
rl.prompt();
|
|
3654
|
+
return;
|
|
3655
|
+
}
|
|
3656
|
+
}
|
|
3657
|
+
multiLineBuffer += (multiLineBuffer ? `
|
|
3658
|
+
` : "") + line;
|
|
3659
|
+
if (multiLineBuffer.trim() === "") {
|
|
3660
|
+
multiLineBuffer = "";
|
|
3661
|
+
rl.prompt();
|
|
3662
|
+
return;
|
|
3663
|
+
}
|
|
3664
|
+
if (isIncomplete(multiLineBuffer)) {
|
|
3665
|
+
currentPrompt = CONT_PROMPT;
|
|
3666
|
+
styledPrompt = STYLED_CONT;
|
|
3667
|
+
rl.setPrompt(CONT_PROMPT);
|
|
3668
|
+
rl.prompt();
|
|
3669
|
+
return;
|
|
3670
|
+
}
|
|
3671
|
+
const source = multiLineBuffer;
|
|
3672
|
+
multiLineBuffer = "";
|
|
3673
|
+
const match = grammar.match(source);
|
|
3674
|
+
if (!match.succeeded()) {
|
|
3675
|
+
console.log(`${C.red} ${match.message}${C.reset}`);
|
|
3676
|
+
resetPrompt();
|
|
3677
|
+
return;
|
|
3678
|
+
}
|
|
3679
|
+
try {
|
|
3680
|
+
const ir = parseToIR(source);
|
|
3681
|
+
const lowered = state.optimize ? optimizeIR(ir) : ir;
|
|
3682
|
+
if (state.showIR) {
|
|
3683
|
+
console.log(`${C.dim} IR: ${JSON.stringify(lowered)}${C.reset}`);
|
|
3684
|
+
}
|
|
3685
|
+
const rexc = compile(source, { optimize: state.optimize });
|
|
3686
|
+
if (state.showRexc) {
|
|
3687
|
+
console.log(`${C.dim} rexc:${C.reset} ${highlightRexc(rexc)}`);
|
|
3688
|
+
}
|
|
3689
|
+
const result = evaluateRexc(rexc, {
|
|
3690
|
+
vars: { ...state.vars },
|
|
3691
|
+
refs: { ...state.refs },
|
|
3692
|
+
gasLimit: GAS_LIMIT
|
|
3693
|
+
});
|
|
3694
|
+
state.vars = result.state.vars;
|
|
3695
|
+
state.refs = result.state.refs;
|
|
3696
|
+
console.log(formatResult(result.value));
|
|
3697
|
+
const varLine = formatVarState(state.vars);
|
|
3698
|
+
if (varLine)
|
|
3699
|
+
console.log(varLine);
|
|
3700
|
+
} catch (error) {
|
|
3701
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3702
|
+
if (message.includes("Gas limit exceeded")) {
|
|
3703
|
+
console.log(`${C.yellow} ${message}${C.reset}`);
|
|
3704
|
+
} else {
|
|
3705
|
+
console.log(`${C.red} Error: ${message}${C.reset}`);
|
|
3706
|
+
}
|
|
3707
|
+
}
|
|
3708
|
+
resetPrompt();
|
|
3709
|
+
});
|
|
3710
|
+
rl.on("close", () => {
|
|
3711
|
+
process.exit(0);
|
|
3712
|
+
});
|
|
3713
|
+
rl.prompt();
|
|
3714
|
+
}
|
|
3715
|
+
export {
|
|
3716
|
+
startRepl,
|
|
3717
|
+
isIncomplete,
|
|
3718
|
+
highlightRexc,
|
|
3719
|
+
highlightLine,
|
|
3720
|
+
formatVarState
|
|
3721
|
+
};
|