@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/rex-cli.js CHANGED
@@ -1,30 +1,3978 @@
1
- #!/usr/bin/env node
2
-
3
- import { spawn } from "node:child_process";
4
- import { dirname, join } from "node:path";
5
- import { fileURLToPath } from "node:url";
6
-
7
- const here = dirname(fileURLToPath(import.meta.url));
8
- const compileScript = join(here, "rex-compile.js");
9
- const passthroughArgs = process.argv.slice(2);
10
-
11
- const child = spawn(process.execPath, [compileScript, ...passthroughArgs], {
12
- stdio: "inherit",
13
- });
14
-
15
- child.on("error", (error) => {
16
- const message = error instanceof Error ? error.message : String(error);
17
- console.error("rex: failed to launch runtime.");
18
- console.error("Use Node.js or Bun.");
19
- console.error(`Details: ${message}`);
20
- process.exit(1);
21
- });
22
-
23
- child.on("exit", (code, signal) => {
24
- if (typeof code === "number") process.exit(code);
25
- if (signal) {
26
- console.error(`rex: process terminated by signal ${signal}`);
27
- process.exit(1);
28
- }
29
- process.exit(1);
30
- });
1
+ #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
3
+ var __export = (target, all) => {
4
+ for (var name in all)
5
+ __defProp(target, name, {
6
+ get: all[name],
7
+ enumerable: true,
8
+ configurable: true,
9
+ set: (newValue) => all[name] = () => newValue
10
+ });
11
+ };
12
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
13
+
14
+ // rex.ts
15
+ import { createRequire } from "node:module";
16
+ function byteLength(value) {
17
+ return Buffer.byteLength(value, "utf8");
18
+ }
19
+ function encodeUint(value) {
20
+ if (!Number.isInteger(value) || value < 0)
21
+ throw new Error(`Cannot encode non-uint value: ${value}`);
22
+ if (value === 0)
23
+ return "";
24
+ let current = value;
25
+ let out = "";
26
+ while (current > 0) {
27
+ const digit = current % 64;
28
+ out = `${DIGITS[digit]}${out}`;
29
+ current = Math.floor(current / 64);
30
+ }
31
+ return out;
32
+ }
33
+ function encodeZigzag(value) {
34
+ if (!Number.isInteger(value))
35
+ throw new Error(`Cannot zigzag non-integer: ${value}`);
36
+ const encoded = value >= 0 ? value * 2 : -value * 2 - 1;
37
+ return encodeUint(encoded);
38
+ }
39
+ function encodeInt(value) {
40
+ return `${encodeZigzag(value)}+`;
41
+ }
42
+ function canUseBareString(value) {
43
+ for (const char of value) {
44
+ if (!DIGITS.includes(char))
45
+ return false;
46
+ }
47
+ return true;
48
+ }
49
+ function decodeStringLiteral(raw) {
50
+ const quote = raw[0];
51
+ if (quote !== '"' && quote !== "'" || raw[raw.length - 1] !== quote) {
52
+ throw new Error(`Invalid string literal: ${raw}`);
53
+ }
54
+ let out = "";
55
+ for (let index = 1;index < raw.length - 1; index += 1) {
56
+ const char = raw[index];
57
+ if (char !== "\\") {
58
+ out += char;
59
+ continue;
60
+ }
61
+ index += 1;
62
+ const esc = raw[index];
63
+ if (esc === undefined)
64
+ throw new Error(`Invalid escape sequence in ${raw}`);
65
+ if (esc === "n")
66
+ out += `
67
+ `;
68
+ else if (esc === "r")
69
+ out += "\r";
70
+ else if (esc === "t")
71
+ out += "\t";
72
+ else if (esc === "b")
73
+ out += "\b";
74
+ else if (esc === "f")
75
+ out += "\f";
76
+ else if (esc === "v")
77
+ out += "\v";
78
+ else if (esc === "0")
79
+ out += "\x00";
80
+ else if (esc === "x") {
81
+ const hex = raw.slice(index + 1, index + 3);
82
+ if (!/^[0-9a-fA-F]{2}$/.test(hex))
83
+ throw new Error(`Invalid hex escape in ${raw}`);
84
+ out += String.fromCodePoint(parseInt(hex, 16));
85
+ index += 2;
86
+ } else if (esc === "u") {
87
+ const hex = raw.slice(index + 1, index + 5);
88
+ if (!/^[0-9a-fA-F]{4}$/.test(hex))
89
+ throw new Error(`Invalid unicode escape in ${raw}`);
90
+ out += String.fromCodePoint(parseInt(hex, 16));
91
+ index += 4;
92
+ } else {
93
+ out += esc;
94
+ }
95
+ }
96
+ return out;
97
+ }
98
+ function encodeBareOrLengthString(value) {
99
+ if (canUseBareString(value))
100
+ return `${value}:`;
101
+ return `${encodeUint(byteLength(value))},${value}`;
102
+ }
103
+ function encodeNumberNode(node) {
104
+ const numberValue = node.value;
105
+ if (!Number.isFinite(numberValue))
106
+ throw new Error(`Cannot encode non-finite number: ${node.raw}`);
107
+ if (Number.isInteger(numberValue))
108
+ return encodeInt(numberValue);
109
+ const raw = node.raw.toLowerCase();
110
+ const sign = raw.startsWith("-") ? -1 : 1;
111
+ const unsigned = sign < 0 ? raw.slice(1) : raw;
112
+ const splitExp = unsigned.split("e");
113
+ const mantissaText = splitExp[0];
114
+ const exponentText = splitExp[1] ?? "0";
115
+ if (!mantissaText)
116
+ throw new Error(`Invalid decimal literal: ${node.raw}`);
117
+ const exponent = Number(exponentText);
118
+ if (!Number.isInteger(exponent))
119
+ throw new Error(`Invalid decimal exponent: ${node.raw}`);
120
+ const dotIndex = mantissaText.indexOf(".");
121
+ const decimals = dotIndex === -1 ? 0 : mantissaText.length - dotIndex - 1;
122
+ const digits = mantissaText.replace(".", "");
123
+ if (!/^\d+$/.test(digits))
124
+ throw new Error(`Invalid decimal digits: ${node.raw}`);
125
+ let significand = Number(digits) * sign;
126
+ let power = exponent - decimals;
127
+ while (significand !== 0 && significand % 10 === 0) {
128
+ significand /= 10;
129
+ power += 1;
130
+ }
131
+ return `${encodeZigzag(power)}*${encodeInt(significand)}`;
132
+ }
133
+ function encodeOpcode(opcode) {
134
+ return `${encodeUint(OPCODE_IDS[opcode])}%`;
135
+ }
136
+ function encodeCallParts(parts) {
137
+ return `(${parts.join("")})`;
138
+ }
139
+ function needsOptionalPrefix(encoded) {
140
+ const first = encoded[0];
141
+ if (!first)
142
+ return false;
143
+ return first === "[" || first === "{" || first === "(" || first === "=" || first === "~" || first === "?" || first === "!" || first === "|" || first === "&" || first === ">" || first === "<" || first === "#";
144
+ }
145
+ function addOptionalPrefix(encoded) {
146
+ if (!needsOptionalPrefix(encoded))
147
+ return encoded;
148
+ let payload = encoded;
149
+ if (encoded.startsWith("?(") || encoded.startsWith("!(") || encoded.startsWith("|(") || encoded.startsWith("&(") || encoded.startsWith(">(") || encoded.startsWith("<(") || encoded.startsWith("#(")) {
150
+ payload = encoded.slice(2, -1);
151
+ } else if (encoded.startsWith(">[") || encoded.startsWith(">{")) {
152
+ payload = encoded.slice(2, -1);
153
+ } else if (encoded.startsWith("[") || encoded.startsWith("{") || encoded.startsWith("(")) {
154
+ payload = encoded.slice(1, -1);
155
+ } else if (encoded.startsWith("=") || encoded.startsWith("~")) {
156
+ payload = encoded.slice(1);
157
+ }
158
+ return `${encodeUint(byteLength(payload))}${encoded}`;
159
+ }
160
+ function encodeBlockExpression(block) {
161
+ if (block.length === 0)
162
+ return "4'";
163
+ if (block.length === 1)
164
+ return encodeNode(block[0]);
165
+ return encodeCallParts([encodeOpcode("do"), ...block.map((node) => encodeNode(node))]);
166
+ }
167
+ function encodeConditionalElse(elseBranch) {
168
+ if (elseBranch.type === "else")
169
+ return encodeBlockExpression(elseBranch.block);
170
+ const nested = {
171
+ type: "conditional",
172
+ head: elseBranch.head,
173
+ condition: elseBranch.condition,
174
+ thenBlock: elseBranch.thenBlock,
175
+ elseBranch: elseBranch.elseBranch
176
+ };
177
+ return encodeNode(nested);
178
+ }
179
+ function encodeNavigation(node) {
180
+ const domainRefs = activeEncodeOptions?.domainRefs;
181
+ if (domainRefs && node.target.type === "identifier") {
182
+ const staticPath = [node.target.name];
183
+ for (const segment of node.segments) {
184
+ if (segment.type !== "static")
185
+ break;
186
+ staticPath.push(segment.key);
187
+ }
188
+ for (let pathLength = staticPath.length;pathLength >= 1; pathLength -= 1) {
189
+ const dottedName = staticPath.slice(0, pathLength).join(".");
190
+ const domainRef = domainRefs[dottedName];
191
+ if (domainRef === undefined)
192
+ continue;
193
+ const consumedStaticSegments = pathLength - 1;
194
+ if (consumedStaticSegments === node.segments.length) {
195
+ return `${encodeUint(domainRef)}'`;
196
+ }
197
+ const parts2 = [`${encodeUint(domainRef)}'`];
198
+ for (const segment of node.segments.slice(consumedStaticSegments)) {
199
+ if (segment.type === "static")
200
+ parts2.push(encodeBareOrLengthString(segment.key));
201
+ else
202
+ parts2.push(encodeNode(segment.key));
203
+ }
204
+ return encodeCallParts(parts2);
205
+ }
206
+ }
207
+ const parts = [encodeNode(node.target)];
208
+ for (const segment of node.segments) {
209
+ if (segment.type === "static")
210
+ parts.push(encodeBareOrLengthString(segment.key));
211
+ else
212
+ parts.push(encodeNode(segment.key));
213
+ }
214
+ return encodeCallParts(parts);
215
+ }
216
+ function encodeWhile(node) {
217
+ const cond = encodeNode(node.condition);
218
+ const body = addOptionalPrefix(encodeBlockExpression(node.body));
219
+ return `#(${cond}${body})`;
220
+ }
221
+ function encodeFor(node) {
222
+ const body = addOptionalPrefix(encodeBlockExpression(node.body));
223
+ if (node.binding.type === "binding:expr") {
224
+ return `>(${encodeNode(node.binding.source)}${body})`;
225
+ }
226
+ if (node.binding.type === "binding:valueIn") {
227
+ return `>(${encodeNode(node.binding.source)}${node.binding.value}$${body})`;
228
+ }
229
+ if (node.binding.type === "binding:keyValueIn") {
230
+ return `>(${encodeNode(node.binding.source)}${node.binding.key}$${node.binding.value}$${body})`;
231
+ }
232
+ return `<(${encodeNode(node.binding.source)}${node.binding.key}$${body})`;
233
+ }
234
+ function encodeArrayComprehension(node) {
235
+ const body = addOptionalPrefix(encodeNode(node.body));
236
+ if (node.binding.type === "binding:expr") {
237
+ return `>[${encodeNode(node.binding.source)}${body}]`;
238
+ }
239
+ if (node.binding.type === "binding:valueIn") {
240
+ return `>[${encodeNode(node.binding.source)}${node.binding.value}$${body}]`;
241
+ }
242
+ if (node.binding.type === "binding:keyValueIn") {
243
+ return `>[${encodeNode(node.binding.source)}${node.binding.key}$${node.binding.value}$${body}]`;
244
+ }
245
+ return `>[${encodeNode(node.binding.source)}${node.binding.key}$${body}]`;
246
+ }
247
+ function encodeObjectComprehension(node) {
248
+ const key = addOptionalPrefix(encodeNode(node.key));
249
+ const value = addOptionalPrefix(encodeNode(node.value));
250
+ if (node.binding.type === "binding:expr") {
251
+ return `>{${encodeNode(node.binding.source)}${key}${value}}`;
252
+ }
253
+ if (node.binding.type === "binding:valueIn") {
254
+ return `>{${encodeNode(node.binding.source)}${node.binding.value}$${key}${value}}`;
255
+ }
256
+ if (node.binding.type === "binding:keyValueIn") {
257
+ return `>{${encodeNode(node.binding.source)}${node.binding.key}$${node.binding.value}$${key}${value}}`;
258
+ }
259
+ return `>{${encodeNode(node.binding.source)}${node.binding.key}$${key}${value}}`;
260
+ }
261
+ function encodeNode(node) {
262
+ switch (node.type) {
263
+ case "program":
264
+ return encodeBlockExpression(node.body);
265
+ case "identifier": {
266
+ const domainRef = activeEncodeOptions?.domainRefs?.[node.name];
267
+ if (domainRef !== undefined)
268
+ return `${encodeUint(domainRef)}'`;
269
+ return `${node.name}$`;
270
+ }
271
+ case "self":
272
+ return "@";
273
+ case "selfDepth": {
274
+ if (!Number.isInteger(node.depth) || node.depth < 1)
275
+ throw new Error(`Invalid self depth: ${node.depth}`);
276
+ if (node.depth === 1)
277
+ return "@";
278
+ return `${encodeUint(node.depth - 1)}@`;
279
+ }
280
+ case "boolean":
281
+ return node.value ? "1'" : "2'";
282
+ case "null":
283
+ return "3'";
284
+ case "undefined":
285
+ return "4'";
286
+ case "number":
287
+ return encodeNumberNode(node);
288
+ case "string":
289
+ return encodeBareOrLengthString(decodeStringLiteral(node.raw));
290
+ case "array": {
291
+ const body = node.items.map((item) => addOptionalPrefix(encodeNode(item))).join("");
292
+ return `[${body}]`;
293
+ }
294
+ case "arrayComprehension":
295
+ return encodeArrayComprehension(node);
296
+ case "object": {
297
+ const body = node.entries.map(({ key, value }) => `${encodeNode(key)}${addOptionalPrefix(encodeNode(value))}`).join("");
298
+ return `{${body}}`;
299
+ }
300
+ case "objectComprehension":
301
+ return encodeObjectComprehension(node);
302
+ case "key":
303
+ return encodeBareOrLengthString(node.name);
304
+ case "group":
305
+ return encodeNode(node.expression);
306
+ case "unary":
307
+ if (node.op === "delete")
308
+ return `~${encodeNode(node.value)}`;
309
+ if (node.op === "neg")
310
+ return encodeCallParts([encodeOpcode("neg"), encodeNode(node.value)]);
311
+ return encodeCallParts([encodeOpcode("not"), encodeNode(node.value)]);
312
+ case "binary":
313
+ if (node.op === "and") {
314
+ const operands = collectLogicalChain(node, "and");
315
+ const body = operands.map((operand, index) => {
316
+ const encoded = encodeNode(operand);
317
+ return index === 0 ? encoded : addOptionalPrefix(encoded);
318
+ }).join("");
319
+ return `&(${body})`;
320
+ }
321
+ if (node.op === "or") {
322
+ const operands = collectLogicalChain(node, "or");
323
+ const body = operands.map((operand, index) => {
324
+ const encoded = encodeNode(operand);
325
+ return index === 0 ? encoded : addOptionalPrefix(encoded);
326
+ }).join("");
327
+ return `|(${body})`;
328
+ }
329
+ return encodeCallParts([
330
+ encodeOpcode(BINARY_TO_OPCODE[node.op]),
331
+ encodeNode(node.left),
332
+ encodeNode(node.right)
333
+ ]);
334
+ case "assign": {
335
+ if (node.op === "=")
336
+ return `=${encodeNode(node.place)}${addOptionalPrefix(encodeNode(node.value))}`;
337
+ const opcode = ASSIGN_COMPOUND_TO_OPCODE[node.op];
338
+ if (!opcode)
339
+ throw new Error(`Unsupported assignment op: ${node.op}`);
340
+ const computedValue = encodeCallParts([encodeOpcode(opcode), encodeNode(node.place), encodeNode(node.value)]);
341
+ return `=${encodeNode(node.place)}${addOptionalPrefix(computedValue)}`;
342
+ }
343
+ case "navigation":
344
+ return encodeNavigation(node);
345
+ case "call":
346
+ return encodeCallParts([encodeNode(node.callee), ...node.args.map((arg) => encodeNode(arg))]);
347
+ case "conditional": {
348
+ const opener = node.head === "when" ? "?(" : "!(";
349
+ const cond = encodeNode(node.condition);
350
+ const thenExpr = addOptionalPrefix(encodeBlockExpression(node.thenBlock));
351
+ const elseExpr = node.elseBranch ? addOptionalPrefix(encodeConditionalElse(node.elseBranch)) : "";
352
+ return `${opener}${cond}${thenExpr}${elseExpr})`;
353
+ }
354
+ case "for":
355
+ return encodeFor(node);
356
+ case "while":
357
+ return encodeWhile(node);
358
+ case "break":
359
+ return ";";
360
+ case "continue":
361
+ return "1;";
362
+ default: {
363
+ const exhaustive = node;
364
+ throw new Error(`Unsupported IR node ${exhaustive.type ?? "unknown"}`);
365
+ }
366
+ }
367
+ }
368
+ function collectLogicalChain(node, op) {
369
+ if (node.type !== "binary" || node.op !== op)
370
+ return [node];
371
+ return [...collectLogicalChain(node.left, op), ...collectLogicalChain(node.right, op)];
372
+ }
373
+ function parseToIR(source) {
374
+ const match = grammar.match(source);
375
+ if (!match.succeeded()) {
376
+ const failure = match;
377
+ throw new Error(failure.message ?? "Parse failed");
378
+ }
379
+ return semantics(match).toIR();
380
+ }
381
+ function parseDataNode(node) {
382
+ switch (node.type) {
383
+ case "group":
384
+ return parseDataNode(node.expression);
385
+ case "program": {
386
+ if (node.body.length === 1)
387
+ return parseDataNode(node.body[0]);
388
+ if (node.body.length === 0)
389
+ return;
390
+ throw new Error("Rex parse() expects a single data expression");
391
+ }
392
+ case "undefined":
393
+ return;
394
+ case "null":
395
+ return null;
396
+ case "boolean":
397
+ return node.value;
398
+ case "number":
399
+ return node.value;
400
+ case "string":
401
+ return decodeStringLiteral(node.raw);
402
+ case "array":
403
+ return node.items.map((item) => parseDataNode(item));
404
+ case "object": {
405
+ const out = {};
406
+ for (const entry of node.entries) {
407
+ const keyNode = entry.key;
408
+ let key;
409
+ if (keyNode.type === "key")
410
+ key = keyNode.name;
411
+ else {
412
+ const keyValue = parseDataNode(keyNode);
413
+ key = String(keyValue);
414
+ }
415
+ out[key] = parseDataNode(entry.value);
416
+ }
417
+ return out;
418
+ }
419
+ default:
420
+ throw new Error(`Rex parse() only supports data expressions. Found: ${node.type}`);
421
+ }
422
+ }
423
+ function isPlainObject(value) {
424
+ if (!value || typeof value !== "object" || Array.isArray(value))
425
+ return false;
426
+ const proto = Object.getPrototypeOf(value);
427
+ return proto === Object.prototype || proto === null;
428
+ }
429
+ function isBareKeyName(key) {
430
+ return /^[A-Za-z_][A-Za-z0-9_-]*$/.test(key);
431
+ }
432
+ function stringifyString(value) {
433
+ return JSON.stringify(value);
434
+ }
435
+ function stringifyInline(value) {
436
+ if (value === undefined)
437
+ return "undefined";
438
+ if (value === null)
439
+ return "null";
440
+ if (typeof value === "boolean")
441
+ return value ? "true" : "false";
442
+ if (typeof value === "number") {
443
+ if (!Number.isFinite(value))
444
+ throw new Error("Rex stringify() cannot encode non-finite numbers");
445
+ return String(value);
446
+ }
447
+ if (typeof value === "string")
448
+ return stringifyString(value);
449
+ if (Array.isArray(value)) {
450
+ if (value.length === 0)
451
+ return "[]";
452
+ return `[${value.map((item) => stringifyInline(item)).join(" ")}]`;
453
+ }
454
+ if (isPlainObject(value)) {
455
+ const entries = Object.entries(value);
456
+ if (entries.length === 0)
457
+ return "{}";
458
+ const body = entries.map(([key, item]) => `${isBareKeyName(key) ? key : stringifyString(key)}: ${stringifyInline(item)}`).join(" ");
459
+ return `{${body}}`;
460
+ }
461
+ throw new Error(`Rex stringify() cannot encode value of type ${typeof value}`);
462
+ }
463
+ function fitsInline(rendered, depth, indentSize, maxWidth) {
464
+ if (rendered.includes(`
465
+ `))
466
+ return false;
467
+ return depth * indentSize + rendered.length <= maxWidth;
468
+ }
469
+ function stringifyPretty(value, depth, indentSize, maxWidth) {
470
+ const inline = stringifyInline(value);
471
+ if (fitsInline(inline, depth, indentSize, maxWidth))
472
+ return inline;
473
+ const indent = " ".repeat(depth * indentSize);
474
+ const childIndent = " ".repeat((depth + 1) * indentSize);
475
+ if (Array.isArray(value)) {
476
+ if (value.length === 0)
477
+ return "[]";
478
+ const lines = value.map((item) => {
479
+ const rendered = stringifyPretty(item, depth + 1, indentSize, maxWidth);
480
+ if (!rendered.includes(`
481
+ `))
482
+ return `${childIndent}${rendered}`;
483
+ return `${childIndent}${rendered}`;
484
+ });
485
+ return `[
486
+ ${lines.join(`
487
+ `)}
488
+ ${indent}]`;
489
+ }
490
+ if (isPlainObject(value)) {
491
+ const entries = Object.entries(value);
492
+ if (entries.length === 0)
493
+ return "{}";
494
+ const lines = entries.map(([key, item]) => {
495
+ const keyText = isBareKeyName(key) ? key : stringifyString(key);
496
+ const rendered = stringifyPretty(item, depth + 1, indentSize, maxWidth);
497
+ return `${childIndent}${keyText}: ${rendered}`;
498
+ });
499
+ return `{
500
+ ${lines.join(`
501
+ `)}
502
+ ${indent}}`;
503
+ }
504
+ return inline;
505
+ }
506
+ function parse(source) {
507
+ return parseDataNode(parseToIR(source));
508
+ }
509
+ function domainRefsFromConfig(config) {
510
+ if (!config || typeof config !== "object" || Array.isArray(config)) {
511
+ throw new Error("Domain config must be an object");
512
+ }
513
+ const refs = {};
514
+ for (const section of Object.values(config)) {
515
+ if (!section || typeof section !== "object" || Array.isArray(section))
516
+ continue;
517
+ mapConfigEntries(section, refs);
518
+ }
519
+ return refs;
520
+ }
521
+ function decodeDomainRefKey(refText) {
522
+ if (!refText)
523
+ throw new Error("Domain ref key cannot be empty");
524
+ if (!/^[0-9A-Za-z_-]+$/.test(refText)) {
525
+ throw new Error(`Invalid domain ref key '${refText}' (must use base64 alphabet 0-9a-zA-Z-_)`);
526
+ }
527
+ if (refText.length > 1 && refText[0] === "0") {
528
+ throw new Error(`Invalid domain ref key '${refText}' (leading zeroes are not allowed)`);
529
+ }
530
+ if (/^[1-9]$/.test(refText)) {
531
+ throw new Error(`Invalid domain ref key '${refText}' (reserved by core language)`);
532
+ }
533
+ let value = 0;
534
+ for (const char of refText) {
535
+ const digit = DOMAIN_DIGIT_INDEX.get(char);
536
+ if (digit === undefined)
537
+ throw new Error(`Invalid domain ref key '${refText}'`);
538
+ value = value * 64 + digit;
539
+ if (value > Number.MAX_SAFE_INTEGER) {
540
+ throw new Error(`Invalid domain ref key '${refText}' (must fit in 53 bits)`);
541
+ }
542
+ }
543
+ if (value < FIRST_NON_RESERVED_REF) {
544
+ throw new Error(`Invalid domain ref key '${refText}' (maps to reserved id ${value})`);
545
+ }
546
+ return value;
547
+ }
548
+ function mapConfigEntries(entries, refs) {
549
+ const sourceKindByRoot = new Map;
550
+ for (const root of Object.keys(refs)) {
551
+ sourceKindByRoot.set(root, "explicit");
552
+ }
553
+ for (const [refText, rawEntry] of Object.entries(entries)) {
554
+ const entry = rawEntry;
555
+ if (!entry || typeof entry !== "object")
556
+ continue;
557
+ if (!Array.isArray(entry.names))
558
+ continue;
559
+ const refId = decodeDomainRefKey(refText);
560
+ for (const rawName of entry.names) {
561
+ if (typeof rawName !== "string")
562
+ continue;
563
+ const existingNameRef = refs[rawName];
564
+ if (existingNameRef !== undefined && existingNameRef !== refId) {
565
+ throw new Error(`Conflicting refs for '${rawName}': ${existingNameRef} vs ${refId}`);
566
+ }
567
+ refs[rawName] = refId;
568
+ const root = rawName.split(".")[0];
569
+ if (!root)
570
+ continue;
571
+ const currentKind = rawName.includes(".") ? "implicit" : "explicit";
572
+ const existing = refs[root];
573
+ if (existing !== undefined) {
574
+ if (existing === refId)
575
+ continue;
576
+ const existingKind = sourceKindByRoot.get(root) ?? "explicit";
577
+ if (currentKind === "explicit") {
578
+ throw new Error(`Conflicting refs for '${root}': ${existing} vs ${refId}`);
579
+ }
580
+ if (existingKind === "explicit")
581
+ continue;
582
+ continue;
583
+ }
584
+ refs[root] = refId;
585
+ sourceKindByRoot.set(root, currentKind);
586
+ }
587
+ }
588
+ }
589
+ function stringify(value, options) {
590
+ const indent = options?.indent ?? 2;
591
+ const maxWidth = options?.maxWidth ?? 80;
592
+ if (!Number.isInteger(indent) || indent < 0)
593
+ throw new Error("Rex stringify() indent must be a non-negative integer");
594
+ if (!Number.isInteger(maxWidth) || maxWidth < 20)
595
+ throw new Error("Rex stringify() maxWidth must be an integer >= 20");
596
+ return stringifyPretty(value, 0, indent, maxWidth);
597
+ }
598
+ function readPrefixAt(text, start) {
599
+ let index = start;
600
+ while (index < text.length && DIGIT_SET.has(text[index]))
601
+ index += 1;
602
+ const raw = text.slice(start, index);
603
+ let value = 0;
604
+ for (const char of raw) {
605
+ const digit = DIGIT_INDEX.get(char);
606
+ if (digit === undefined)
607
+ throw new Error(`Invalid prefix in encoded stream at ${start}`);
608
+ value = value * 64 + digit;
609
+ }
610
+ return { end: index, raw, value };
611
+ }
612
+ function parsePlaceEnd(text, start, out) {
613
+ if (text[start] === "(") {
614
+ let index2 = start + 1;
615
+ while (index2 < text.length && text[index2] !== ")") {
616
+ index2 = parseValueEnd(text, index2, out).end;
617
+ }
618
+ if (text[index2] !== ")")
619
+ throw new Error(`Unterminated place at ${start}`);
620
+ return index2 + 1;
621
+ }
622
+ const prefix = readPrefixAt(text, start);
623
+ const tag = text[prefix.end];
624
+ if (tag !== "$" && tag !== "'")
625
+ throw new Error(`Invalid place at ${start}`);
626
+ let index = prefix.end + 1;
627
+ if (text[index] !== "(")
628
+ return index;
629
+ index += 1;
630
+ while (index < text.length && text[index] !== ")") {
631
+ index = parseValueEnd(text, index, out).end;
632
+ }
633
+ if (text[index] !== ")")
634
+ throw new Error(`Unterminated place at ${start}`);
635
+ return index + 1;
636
+ }
637
+ function parseValueEnd(text, start, out) {
638
+ const prefix = readPrefixAt(text, start);
639
+ const tag = text[prefix.end];
640
+ if (!tag)
641
+ throw new Error(`Unexpected end of encoded stream at ${start}`);
642
+ if (tag === ",") {
643
+ const strStart = prefix.end + 1;
644
+ const strEnd = strStart + prefix.value;
645
+ if (strEnd > text.length)
646
+ throw new Error(`String overflows encoded stream at ${start}`);
647
+ const raw = text.slice(start, strEnd);
648
+ if (Buffer.byteLength(text.slice(strStart, strEnd), "utf8") !== prefix.value) {
649
+ throw new Error(`Non-ASCII length-string not currently dedupe-safe at ${start}`);
650
+ }
651
+ const span2 = { start, end: strEnd, raw };
652
+ if (out)
653
+ out.push(span2);
654
+ return span2;
655
+ }
656
+ if (tag === "=") {
657
+ const placeEnd = parsePlaceEnd(text, prefix.end + 1, out);
658
+ const valueEnd = parseValueEnd(text, placeEnd, out).end;
659
+ const span2 = { start, end: valueEnd, raw: text.slice(start, valueEnd) };
660
+ if (out)
661
+ out.push(span2);
662
+ return span2;
663
+ }
664
+ if (tag === "~") {
665
+ const placeEnd = parsePlaceEnd(text, prefix.end + 1, out);
666
+ const span2 = { start, end: placeEnd, raw: text.slice(start, placeEnd) };
667
+ if (out)
668
+ out.push(span2);
669
+ return span2;
670
+ }
671
+ if (tag === "(" || tag === "[" || tag === "{") {
672
+ const close = tag === "(" ? ")" : tag === "[" ? "]" : "}";
673
+ let index = prefix.end + 1;
674
+ while (index < text.length && text[index] !== close) {
675
+ index = parseValueEnd(text, index, out).end;
676
+ }
677
+ if (text[index] !== close)
678
+ throw new Error(`Unterminated container at ${start}`);
679
+ const span2 = { start, end: index + 1, raw: text.slice(start, index + 1) };
680
+ if (out)
681
+ out.push(span2);
682
+ return span2;
683
+ }
684
+ if (tag === "?" || tag === "!" || tag === "|" || tag === "&") {
685
+ if (text[prefix.end + 1] !== "(")
686
+ throw new Error(`Expected '(' after '${tag}' at ${start}`);
687
+ let index = prefix.end + 2;
688
+ while (index < text.length && text[index] !== ")") {
689
+ index = parseValueEnd(text, index, out).end;
690
+ }
691
+ if (text[index] !== ")")
692
+ throw new Error(`Unterminated flow container at ${start}`);
693
+ const span2 = { start, end: index + 1, raw: text.slice(start, index + 1) };
694
+ if (out)
695
+ out.push(span2);
696
+ return span2;
697
+ }
698
+ if (tag === ">" || tag === "<") {
699
+ const open = text[prefix.end + 1];
700
+ if (open !== "(" && open !== "[" && open !== "{")
701
+ throw new Error(`Invalid loop opener at ${start}`);
702
+ const close = open === "(" ? ")" : open === "[" ? "]" : "}";
703
+ let index = prefix.end + 2;
704
+ while (index < text.length && text[index] !== close) {
705
+ index = parseValueEnd(text, index, out).end;
706
+ }
707
+ if (text[index] !== close)
708
+ throw new Error(`Unterminated loop container at ${start}`);
709
+ const span2 = { start, end: index + 1, raw: text.slice(start, index + 1) };
710
+ if (out)
711
+ out.push(span2);
712
+ return span2;
713
+ }
714
+ const span = { start, end: prefix.end + 1, raw: text.slice(start, prefix.end + 1) };
715
+ if (out)
716
+ out.push(span);
717
+ return span;
718
+ }
719
+ function gatherEncodedValueSpans(text) {
720
+ const spans = [];
721
+ let index = 0;
722
+ while (index < text.length) {
723
+ const span = parseValueEnd(text, index, spans);
724
+ index = span.end;
725
+ }
726
+ return spans;
727
+ }
728
+ function buildPointerToken(pointerStart, targetStart) {
729
+ let offset = targetStart - (pointerStart + 1);
730
+ if (offset < 0)
731
+ return;
732
+ for (let guard = 0;guard < 8; guard += 1) {
733
+ const prefix = encodeUint(offset);
734
+ const recalculated = targetStart - (pointerStart + prefix.length + 1);
735
+ if (recalculated === offset)
736
+ return `${prefix}^`;
737
+ offset = recalculated;
738
+ if (offset < 0)
739
+ return;
740
+ }
741
+ return;
742
+ }
743
+ function buildDedupeCandidateTable(encoded, minBytes) {
744
+ const spans = gatherEncodedValueSpans(encoded);
745
+ const table = new Map;
746
+ for (const span of spans) {
747
+ const sizeBytes = span.raw.length;
748
+ if (sizeBytes < minBytes)
749
+ continue;
750
+ const prefix = readPrefixAt(encoded, span.start);
751
+ const tag = encoded[prefix.end];
752
+ if (tag !== "{" && tag !== "[" && tag !== "," && tag !== ":")
753
+ continue;
754
+ const offsetFromEnd = encoded.length - span.end;
755
+ const entry = {
756
+ span,
757
+ sizeBytes,
758
+ offsetFromEnd
759
+ };
760
+ if (!table.has(span.raw))
761
+ table.set(span.raw, []);
762
+ table.get(span.raw).push(entry);
763
+ }
764
+ return table;
765
+ }
766
+ function dedupeLargeEncodedValues(encoded, minBytes = 4) {
767
+ const effectiveMinBytes = Math.max(1, minBytes);
768
+ let current = encoded;
769
+ while (true) {
770
+ const groups = buildDedupeCandidateTable(current, effectiveMinBytes);
771
+ let replaced = false;
772
+ for (const [value, occurrences] of groups.entries()) {
773
+ if (occurrences.length < 2)
774
+ continue;
775
+ const canonical = occurrences[occurrences.length - 1];
776
+ for (let index = occurrences.length - 2;index >= 0; index -= 1) {
777
+ const occurrence = occurrences[index];
778
+ if (occurrence.span.end > canonical.span.start)
779
+ continue;
780
+ if (current.slice(occurrence.span.start, occurrence.span.end) !== value)
781
+ continue;
782
+ const canonicalCurrentStart = current.length - canonical.offsetFromEnd - canonical.sizeBytes;
783
+ const pointerToken = buildPointerToken(occurrence.span.start, canonicalCurrentStart);
784
+ if (!pointerToken)
785
+ continue;
786
+ if (pointerToken.length >= occurrence.sizeBytes)
787
+ continue;
788
+ current = `${current.slice(0, occurrence.span.start)}${pointerToken}${current.slice(occurrence.span.end)}`;
789
+ replaced = true;
790
+ break;
791
+ }
792
+ if (replaced)
793
+ break;
794
+ }
795
+ if (!replaced)
796
+ return current;
797
+ }
798
+ }
799
+ function encodeIR(node, options) {
800
+ const previous = activeEncodeOptions;
801
+ activeEncodeOptions = options;
802
+ try {
803
+ const encoded = encodeNode(node);
804
+ if (options?.dedupeValues) {
805
+ return dedupeLargeEncodedValues(encoded, options.dedupeMinBytes ?? 4);
806
+ }
807
+ return encoded;
808
+ } finally {
809
+ activeEncodeOptions = previous;
810
+ }
811
+ }
812
+ function cloneNode(node) {
813
+ return structuredClone(node);
814
+ }
815
+ function emptyOptimizeEnv() {
816
+ return { constants: {}, selfCaptures: {} };
817
+ }
818
+ function cloneOptimizeEnv(env) {
819
+ return {
820
+ constants: { ...env.constants },
821
+ selfCaptures: { ...env.selfCaptures }
822
+ };
823
+ }
824
+ function clearOptimizeEnv(env) {
825
+ for (const key of Object.keys(env.constants))
826
+ delete env.constants[key];
827
+ for (const key of Object.keys(env.selfCaptures))
828
+ delete env.selfCaptures[key];
829
+ }
830
+ function clearBinding(env, name) {
831
+ delete env.constants[name];
832
+ delete env.selfCaptures[name];
833
+ }
834
+ function selfTargetFromNode(node, currentDepth) {
835
+ if (node.type === "self")
836
+ return currentDepth;
837
+ if (node.type === "selfDepth") {
838
+ const target = currentDepth - (node.depth - 1);
839
+ if (target >= 1)
840
+ return target;
841
+ }
842
+ return;
843
+ }
844
+ function selfNodeFromTarget(targetDepth, currentDepth) {
845
+ const relDepth = currentDepth - targetDepth + 1;
846
+ if (!Number.isInteger(relDepth) || relDepth < 1)
847
+ return;
848
+ if (relDepth === 1)
849
+ return { type: "self" };
850
+ return { type: "selfDepth", depth: relDepth };
851
+ }
852
+ function dropBindingNames(env, binding) {
853
+ if (binding.type === "binding:valueIn") {
854
+ clearBinding(env, binding.value);
855
+ return;
856
+ }
857
+ if (binding.type === "binding:keyValueIn") {
858
+ clearBinding(env, binding.key);
859
+ clearBinding(env, binding.value);
860
+ return;
861
+ }
862
+ if (binding.type === "binding:keyOf") {
863
+ clearBinding(env, binding.key);
864
+ }
865
+ }
866
+ function collectReads(node, out) {
867
+ switch (node.type) {
868
+ case "identifier":
869
+ out.add(node.name);
870
+ return;
871
+ case "group":
872
+ collectReads(node.expression, out);
873
+ return;
874
+ case "array":
875
+ for (const item of node.items)
876
+ collectReads(item, out);
877
+ return;
878
+ case "object":
879
+ for (const entry of node.entries) {
880
+ collectReads(entry.key, out);
881
+ collectReads(entry.value, out);
882
+ }
883
+ return;
884
+ case "arrayComprehension":
885
+ collectReads(node.binding.source, out);
886
+ collectReads(node.body, out);
887
+ return;
888
+ case "objectComprehension":
889
+ collectReads(node.binding.source, out);
890
+ collectReads(node.key, out);
891
+ collectReads(node.value, out);
892
+ return;
893
+ case "unary":
894
+ collectReads(node.value, out);
895
+ return;
896
+ case "binary":
897
+ collectReads(node.left, out);
898
+ collectReads(node.right, out);
899
+ return;
900
+ case "assign":
901
+ if (!(node.op === "=" && node.place.type === "identifier"))
902
+ collectReads(node.place, out);
903
+ collectReads(node.value, out);
904
+ return;
905
+ case "navigation":
906
+ collectReads(node.target, out);
907
+ for (const segment of node.segments) {
908
+ if (segment.type === "dynamic")
909
+ collectReads(segment.key, out);
910
+ }
911
+ return;
912
+ case "call":
913
+ collectReads(node.callee, out);
914
+ for (const arg of node.args)
915
+ collectReads(arg, out);
916
+ return;
917
+ case "conditional":
918
+ collectReads(node.condition, out);
919
+ for (const part of node.thenBlock)
920
+ collectReads(part, out);
921
+ if (node.elseBranch)
922
+ collectReadsElse(node.elseBranch, out);
923
+ return;
924
+ case "for":
925
+ collectReads(node.binding.source, out);
926
+ for (const part of node.body)
927
+ collectReads(part, out);
928
+ return;
929
+ case "program":
930
+ for (const part of node.body)
931
+ collectReads(part, out);
932
+ return;
933
+ default:
934
+ return;
935
+ }
936
+ }
937
+ function collectReadsElse(elseBranch, out) {
938
+ if (elseBranch.type === "else") {
939
+ for (const part of elseBranch.block)
940
+ collectReads(part, out);
941
+ return;
942
+ }
943
+ collectReads(elseBranch.condition, out);
944
+ for (const part of elseBranch.thenBlock)
945
+ collectReads(part, out);
946
+ if (elseBranch.elseBranch)
947
+ collectReadsElse(elseBranch.elseBranch, out);
948
+ }
949
+ function isPureNode(node) {
950
+ switch (node.type) {
951
+ case "identifier":
952
+ case "self":
953
+ case "selfDepth":
954
+ case "boolean":
955
+ case "null":
956
+ case "undefined":
957
+ case "number":
958
+ case "string":
959
+ case "key":
960
+ return true;
961
+ case "group":
962
+ return isPureNode(node.expression);
963
+ case "array":
964
+ return node.items.every((item) => isPureNode(item));
965
+ case "object":
966
+ return node.entries.every((entry) => isPureNode(entry.key) && isPureNode(entry.value));
967
+ case "navigation":
968
+ return isPureNode(node.target) && node.segments.every((segment) => segment.type === "static" || isPureNode(segment.key));
969
+ case "unary":
970
+ return node.op !== "delete" && isPureNode(node.value);
971
+ case "binary":
972
+ return isPureNode(node.left) && isPureNode(node.right);
973
+ default:
974
+ return false;
975
+ }
976
+ }
977
+ function eliminateDeadAssignments(block) {
978
+ const needed = new Set;
979
+ const out = [];
980
+ for (let index = block.length - 1;index >= 0; index -= 1) {
981
+ const node = block[index];
982
+ if (node.type === "conditional") {
983
+ let rewritten = node;
984
+ if (node.condition.type === "assign" && node.condition.op === "=" && node.condition.place.type === "identifier") {
985
+ const name = node.condition.place.name;
986
+ const branchReads = new Set;
987
+ for (const part of node.thenBlock)
988
+ collectReads(part, branchReads);
989
+ if (node.elseBranch)
990
+ collectReadsElse(node.elseBranch, branchReads);
991
+ if (!needed.has(name) && !branchReads.has(name)) {
992
+ rewritten = {
993
+ type: "conditional",
994
+ head: node.head,
995
+ condition: node.condition.value,
996
+ thenBlock: node.thenBlock,
997
+ elseBranch: node.elseBranch
998
+ };
999
+ }
1000
+ }
1001
+ collectReads(rewritten, needed);
1002
+ out.push(rewritten);
1003
+ continue;
1004
+ }
1005
+ if (node.type === "assign" && node.op === "=" && node.place.type === "identifier") {
1006
+ collectReads(node.value, needed);
1007
+ const name = node.place.name;
1008
+ const canDrop = !needed.has(name) && isPureNode(node.value);
1009
+ needed.delete(name);
1010
+ if (canDrop)
1011
+ continue;
1012
+ out.push(node);
1013
+ continue;
1014
+ }
1015
+ collectReads(node, needed);
1016
+ out.push(node);
1017
+ }
1018
+ out.reverse();
1019
+ return out;
1020
+ }
1021
+ function hasIdentifierRead(node, name, asPlace = false) {
1022
+ if (node.type === "identifier")
1023
+ return !asPlace && node.name === name;
1024
+ switch (node.type) {
1025
+ case "group":
1026
+ return hasIdentifierRead(node.expression, name);
1027
+ case "array":
1028
+ return node.items.some((item) => hasIdentifierRead(item, name));
1029
+ case "object":
1030
+ return node.entries.some((entry) => hasIdentifierRead(entry.key, name) || hasIdentifierRead(entry.value, name));
1031
+ case "navigation":
1032
+ return hasIdentifierRead(node.target, name) || node.segments.some((segment) => segment.type === "dynamic" && hasIdentifierRead(segment.key, name));
1033
+ case "unary":
1034
+ return hasIdentifierRead(node.value, name, node.op === "delete");
1035
+ case "binary":
1036
+ return hasIdentifierRead(node.left, name) || hasIdentifierRead(node.right, name);
1037
+ case "assign":
1038
+ return hasIdentifierRead(node.place, name, true) || hasIdentifierRead(node.value, name);
1039
+ default:
1040
+ return false;
1041
+ }
1042
+ }
1043
+ function countIdentifierReads(node, name, asPlace = false) {
1044
+ if (node.type === "identifier")
1045
+ return !asPlace && node.name === name ? 1 : 0;
1046
+ switch (node.type) {
1047
+ case "group":
1048
+ return countIdentifierReads(node.expression, name);
1049
+ case "array":
1050
+ return node.items.reduce((sum, item) => sum + countIdentifierReads(item, name), 0);
1051
+ case "object":
1052
+ return node.entries.reduce((sum, entry) => sum + countIdentifierReads(entry.key, name) + countIdentifierReads(entry.value, name), 0);
1053
+ case "navigation":
1054
+ return countIdentifierReads(node.target, name) + node.segments.reduce((sum, segment) => sum + (segment.type === "dynamic" ? countIdentifierReads(segment.key, name) : 0), 0);
1055
+ case "unary":
1056
+ return countIdentifierReads(node.value, name, node.op === "delete");
1057
+ case "binary":
1058
+ return countIdentifierReads(node.left, name) + countIdentifierReads(node.right, name);
1059
+ case "assign":
1060
+ return countIdentifierReads(node.place, name, true) + countIdentifierReads(node.value, name);
1061
+ default:
1062
+ return 0;
1063
+ }
1064
+ }
1065
+ function replaceIdentifier(node, name, replacement, asPlace = false) {
1066
+ if (node.type === "identifier") {
1067
+ if (!asPlace && node.name === name)
1068
+ return cloneNode(replacement);
1069
+ return node;
1070
+ }
1071
+ switch (node.type) {
1072
+ case "group":
1073
+ return {
1074
+ type: "group",
1075
+ expression: replaceIdentifier(node.expression, name, replacement)
1076
+ };
1077
+ case "array":
1078
+ return { type: "array", items: node.items.map((item) => replaceIdentifier(item, name, replacement)) };
1079
+ case "object":
1080
+ return {
1081
+ type: "object",
1082
+ entries: node.entries.map((entry) => ({
1083
+ key: replaceIdentifier(entry.key, name, replacement),
1084
+ value: replaceIdentifier(entry.value, name, replacement)
1085
+ }))
1086
+ };
1087
+ case "navigation":
1088
+ return {
1089
+ type: "navigation",
1090
+ target: replaceIdentifier(node.target, name, replacement),
1091
+ segments: node.segments.map((segment) => segment.type === "static" ? segment : { type: "dynamic", key: replaceIdentifier(segment.key, name, replacement) })
1092
+ };
1093
+ case "unary":
1094
+ return {
1095
+ type: "unary",
1096
+ op: node.op,
1097
+ value: replaceIdentifier(node.value, name, replacement, node.op === "delete")
1098
+ };
1099
+ case "binary":
1100
+ return {
1101
+ type: "binary",
1102
+ op: node.op,
1103
+ left: replaceIdentifier(node.left, name, replacement),
1104
+ right: replaceIdentifier(node.right, name, replacement)
1105
+ };
1106
+ case "assign":
1107
+ return {
1108
+ type: "assign",
1109
+ op: node.op,
1110
+ place: replaceIdentifier(node.place, name, replacement, true),
1111
+ value: replaceIdentifier(node.value, name, replacement)
1112
+ };
1113
+ default:
1114
+ return node;
1115
+ }
1116
+ }
1117
+ function isSafeInlineTargetNode(node) {
1118
+ if (isPureNode(node))
1119
+ return true;
1120
+ if (node.type === "assign" && node.op === "=") {
1121
+ return isPureNode(node.place) && isPureNode(node.value);
1122
+ }
1123
+ return false;
1124
+ }
1125
+ function inlineAdjacentPureAssignments(block) {
1126
+ const out = [...block];
1127
+ let changed = true;
1128
+ while (changed) {
1129
+ changed = false;
1130
+ for (let index = 0;index < out.length - 1; index += 1) {
1131
+ const current = out[index];
1132
+ if (current.type !== "assign" || current.op !== "=" || current.place.type !== "identifier")
1133
+ continue;
1134
+ if (!isPureNode(current.value))
1135
+ continue;
1136
+ const name = current.place.name;
1137
+ if (hasIdentifierRead(current.value, name))
1138
+ continue;
1139
+ const next = out[index + 1];
1140
+ if (!isSafeInlineTargetNode(next))
1141
+ continue;
1142
+ if (countIdentifierReads(next, name) !== 1)
1143
+ continue;
1144
+ out[index + 1] = replaceIdentifier(next, name, current.value);
1145
+ out.splice(index, 1);
1146
+ changed = true;
1147
+ break;
1148
+ }
1149
+ }
1150
+ return out;
1151
+ }
1152
+ function toNumberNode(value) {
1153
+ return { type: "number", raw: String(value), value };
1154
+ }
1155
+ function toStringNode(value) {
1156
+ return { type: "string", raw: JSON.stringify(value) };
1157
+ }
1158
+ function toLiteralNode(value) {
1159
+ if (value === undefined)
1160
+ return { type: "undefined" };
1161
+ if (value === null)
1162
+ return { type: "null" };
1163
+ if (typeof value === "boolean")
1164
+ return { type: "boolean", value };
1165
+ if (typeof value === "number" && Number.isFinite(value))
1166
+ return toNumberNode(value);
1167
+ if (typeof value === "string")
1168
+ return toStringNode(value);
1169
+ if (Array.isArray(value)) {
1170
+ const items = [];
1171
+ for (const item of value) {
1172
+ const lowered = toLiteralNode(item);
1173
+ if (!lowered)
1174
+ return;
1175
+ items.push(lowered);
1176
+ }
1177
+ return { type: "array", items };
1178
+ }
1179
+ if (value && typeof value === "object") {
1180
+ const entries = [];
1181
+ for (const [key, entryValue] of Object.entries(value)) {
1182
+ const loweredValue = toLiteralNode(entryValue);
1183
+ if (!loweredValue)
1184
+ return;
1185
+ entries.push({ key: { type: "key", name: key }, value: loweredValue });
1186
+ }
1187
+ return { type: "object", entries };
1188
+ }
1189
+ return;
1190
+ }
1191
+ function constValue(node) {
1192
+ switch (node.type) {
1193
+ case "undefined":
1194
+ return;
1195
+ case "null":
1196
+ return null;
1197
+ case "boolean":
1198
+ return node.value;
1199
+ case "number":
1200
+ return node.value;
1201
+ case "string":
1202
+ return decodeStringLiteral(node.raw);
1203
+ case "key":
1204
+ return node.name;
1205
+ case "array": {
1206
+ const out = [];
1207
+ for (const item of node.items) {
1208
+ const value = constValue(item);
1209
+ if (value === undefined && item.type !== "undefined")
1210
+ return;
1211
+ out.push(value);
1212
+ }
1213
+ return out;
1214
+ }
1215
+ case "object": {
1216
+ const out = {};
1217
+ for (const entry of node.entries) {
1218
+ const key = constValue(entry.key);
1219
+ if (key === undefined && entry.key.type !== "undefined")
1220
+ return;
1221
+ const value = constValue(entry.value);
1222
+ if (value === undefined && entry.value.type !== "undefined")
1223
+ return;
1224
+ out[String(key)] = value;
1225
+ }
1226
+ return out;
1227
+ }
1228
+ default:
1229
+ return;
1230
+ }
1231
+ }
1232
+ function isDefinedValue(value) {
1233
+ return value !== undefined;
1234
+ }
1235
+ function foldUnary(op, value) {
1236
+ if (op === "neg") {
1237
+ if (typeof value !== "number")
1238
+ return;
1239
+ return -value;
1240
+ }
1241
+ if (op === "not") {
1242
+ if (typeof value === "boolean")
1243
+ return !value;
1244
+ if (typeof value === "number")
1245
+ return ~value;
1246
+ return;
1247
+ }
1248
+ return;
1249
+ }
1250
+ function foldBinary(op, left, right) {
1251
+ if (op === "add" || op === "sub" || op === "mul" || op === "div" || op === "mod") {
1252
+ if (typeof left !== "number" || typeof right !== "number")
1253
+ return;
1254
+ if (op === "add")
1255
+ return left + right;
1256
+ if (op === "sub")
1257
+ return left - right;
1258
+ if (op === "mul")
1259
+ return left * right;
1260
+ if (op === "div")
1261
+ return left / right;
1262
+ return left % right;
1263
+ }
1264
+ if (op === "bitAnd" || op === "bitOr" || op === "bitXor") {
1265
+ if (typeof left !== "number" || typeof right !== "number")
1266
+ return;
1267
+ if (op === "bitAnd")
1268
+ return left & right;
1269
+ if (op === "bitOr")
1270
+ return left | right;
1271
+ return left ^ right;
1272
+ }
1273
+ if (op === "eq")
1274
+ return left === right ? left : undefined;
1275
+ if (op === "neq")
1276
+ return left !== right ? left : undefined;
1277
+ if (op === "gt" || op === "gte" || op === "lt" || op === "lte") {
1278
+ if (typeof left !== "number" || typeof right !== "number")
1279
+ return;
1280
+ if (op === "gt")
1281
+ return left > right ? left : undefined;
1282
+ if (op === "gte")
1283
+ return left >= right ? left : undefined;
1284
+ if (op === "lt")
1285
+ return left < right ? left : undefined;
1286
+ return left <= right ? left : undefined;
1287
+ }
1288
+ if (op === "and")
1289
+ return isDefinedValue(left) ? right : undefined;
1290
+ if (op === "or")
1291
+ return isDefinedValue(left) ? left : right;
1292
+ return;
1293
+ }
1294
+ function optimizeElse(elseBranch, env, currentDepth) {
1295
+ if (!elseBranch)
1296
+ return;
1297
+ if (elseBranch.type === "else") {
1298
+ return { type: "else", block: optimizeBlock(elseBranch.block, cloneOptimizeEnv(env), currentDepth) };
1299
+ }
1300
+ const optimizedCondition = optimizeNode(elseBranch.condition, env, currentDepth);
1301
+ const foldedCondition = constValue(optimizedCondition);
1302
+ if (foldedCondition !== undefined || optimizedCondition.type === "undefined") {
1303
+ const passes = elseBranch.head === "when" ? isDefinedValue(foldedCondition) : !isDefinedValue(foldedCondition);
1304
+ if (passes) {
1305
+ return {
1306
+ type: "else",
1307
+ block: optimizeBlock(elseBranch.thenBlock, cloneOptimizeEnv(env), currentDepth)
1308
+ };
1309
+ }
1310
+ return optimizeElse(elseBranch.elseBranch, env, currentDepth);
1311
+ }
1312
+ return {
1313
+ type: "elseChain",
1314
+ head: elseBranch.head,
1315
+ condition: optimizedCondition,
1316
+ thenBlock: optimizeBlock(elseBranch.thenBlock, cloneOptimizeEnv(env), currentDepth),
1317
+ elseBranch: optimizeElse(elseBranch.elseBranch, cloneOptimizeEnv(env), currentDepth)
1318
+ };
1319
+ }
1320
+ function optimizeBlock(block, env, currentDepth) {
1321
+ const out = [];
1322
+ for (const node of block) {
1323
+ const optimized = optimizeNode(node, env, currentDepth);
1324
+ out.push(optimized);
1325
+ if (optimized.type === "break" || optimized.type === "continue")
1326
+ break;
1327
+ if (optimized.type === "assign" && optimized.op === "=" && optimized.place.type === "identifier") {
1328
+ const selfTarget = selfTargetFromNode(optimized.value, currentDepth);
1329
+ if (selfTarget !== undefined) {
1330
+ env.selfCaptures[optimized.place.name] = selfTarget;
1331
+ delete env.constants[optimized.place.name];
1332
+ continue;
1333
+ }
1334
+ const folded = constValue(optimized.value);
1335
+ if (folded !== undefined || optimized.value.type === "undefined") {
1336
+ env.constants[optimized.place.name] = cloneNode(optimized.value);
1337
+ delete env.selfCaptures[optimized.place.name];
1338
+ } else {
1339
+ clearBinding(env, optimized.place.name);
1340
+ }
1341
+ continue;
1342
+ }
1343
+ if (optimized.type === "unary" && optimized.op === "delete" && optimized.value.type === "identifier") {
1344
+ clearBinding(env, optimized.value.name);
1345
+ continue;
1346
+ }
1347
+ if (optimized.type === "assign" && optimized.place.type === "identifier") {
1348
+ clearBinding(env, optimized.place.name);
1349
+ continue;
1350
+ }
1351
+ if (optimized.type === "assign" || optimized.type === "for" || optimized.type === "call") {
1352
+ clearOptimizeEnv(env);
1353
+ }
1354
+ }
1355
+ return inlineAdjacentPureAssignments(eliminateDeadAssignments(out));
1356
+ }
1357
+ function optimizeNode(node, env, currentDepth, asPlace = false) {
1358
+ switch (node.type) {
1359
+ case "program": {
1360
+ const body = optimizeBlock(node.body, cloneOptimizeEnv(env), currentDepth);
1361
+ if (body.length === 0)
1362
+ return { type: "undefined" };
1363
+ if (body.length === 1)
1364
+ return body[0];
1365
+ return { type: "program", body };
1366
+ }
1367
+ case "identifier": {
1368
+ if (asPlace)
1369
+ return node;
1370
+ const selfTarget = env.selfCaptures[node.name];
1371
+ if (selfTarget !== undefined) {
1372
+ const rewritten = selfNodeFromTarget(selfTarget, currentDepth);
1373
+ if (rewritten)
1374
+ return rewritten;
1375
+ }
1376
+ const replacement = env.constants[node.name];
1377
+ return replacement ? cloneNode(replacement) : node;
1378
+ }
1379
+ case "group": {
1380
+ return optimizeNode(node.expression, env, currentDepth);
1381
+ }
1382
+ case "array": {
1383
+ return { type: "array", items: node.items.map((item) => optimizeNode(item, env, currentDepth)) };
1384
+ }
1385
+ case "object": {
1386
+ return {
1387
+ type: "object",
1388
+ entries: node.entries.map((entry) => ({
1389
+ key: optimizeNode(entry.key, env, currentDepth),
1390
+ value: optimizeNode(entry.value, env, currentDepth)
1391
+ }))
1392
+ };
1393
+ }
1394
+ case "unary": {
1395
+ const value = optimizeNode(node.value, env, currentDepth, node.op === "delete");
1396
+ const foldedValue = constValue(value);
1397
+ if (foldedValue !== undefined || value.type === "undefined") {
1398
+ const folded = foldUnary(node.op, foldedValue);
1399
+ const literal = folded === undefined ? undefined : toLiteralNode(folded);
1400
+ if (literal)
1401
+ return literal;
1402
+ }
1403
+ return { type: "unary", op: node.op, value };
1404
+ }
1405
+ case "binary": {
1406
+ const left = optimizeNode(node.left, env, currentDepth);
1407
+ const right = optimizeNode(node.right, env, currentDepth);
1408
+ const leftValue = constValue(left);
1409
+ const rightValue = constValue(right);
1410
+ if ((leftValue !== undefined || left.type === "undefined") && (rightValue !== undefined || right.type === "undefined")) {
1411
+ const folded = foldBinary(node.op, leftValue, rightValue);
1412
+ const literal = folded === undefined ? undefined : toLiteralNode(folded);
1413
+ if (literal)
1414
+ return literal;
1415
+ }
1416
+ return { type: "binary", op: node.op, left, right };
1417
+ }
1418
+ case "navigation": {
1419
+ const target = optimizeNode(node.target, env, currentDepth);
1420
+ const segments = node.segments.map((segment) => segment.type === "static" ? segment : { type: "dynamic", key: optimizeNode(segment.key, env, currentDepth) });
1421
+ const targetValue = constValue(target);
1422
+ if (targetValue !== undefined || target.type === "undefined") {
1423
+ let current = targetValue;
1424
+ let foldable = true;
1425
+ for (const segment of segments) {
1426
+ if (!foldable)
1427
+ break;
1428
+ const key = segment.type === "static" ? segment.key : constValue(segment.key);
1429
+ if (segment.type === "dynamic" && key === undefined && segment.key.type !== "undefined") {
1430
+ foldable = false;
1431
+ break;
1432
+ }
1433
+ if (current === null || current === undefined) {
1434
+ current = undefined;
1435
+ continue;
1436
+ }
1437
+ current = current[String(key)];
1438
+ }
1439
+ if (foldable) {
1440
+ const literal = toLiteralNode(current);
1441
+ if (literal)
1442
+ return literal;
1443
+ }
1444
+ }
1445
+ return {
1446
+ type: "navigation",
1447
+ target,
1448
+ segments
1449
+ };
1450
+ }
1451
+ case "call": {
1452
+ return {
1453
+ type: "call",
1454
+ callee: optimizeNode(node.callee, env, currentDepth),
1455
+ args: node.args.map((arg) => optimizeNode(arg, env, currentDepth))
1456
+ };
1457
+ }
1458
+ case "assign": {
1459
+ return {
1460
+ type: "assign",
1461
+ op: node.op,
1462
+ place: optimizeNode(node.place, env, currentDepth, true),
1463
+ value: optimizeNode(node.value, env, currentDepth)
1464
+ };
1465
+ }
1466
+ case "conditional": {
1467
+ const condition = optimizeNode(node.condition, env, currentDepth);
1468
+ const thenEnv = cloneOptimizeEnv(env);
1469
+ if (condition.type === "assign" && condition.op === "=" && condition.place.type === "identifier") {
1470
+ thenEnv.selfCaptures[condition.place.name] = currentDepth;
1471
+ delete thenEnv.constants[condition.place.name];
1472
+ }
1473
+ const conditionValue = constValue(condition);
1474
+ if (conditionValue !== undefined || condition.type === "undefined") {
1475
+ const passes = node.head === "when" ? isDefinedValue(conditionValue) : !isDefinedValue(conditionValue);
1476
+ if (passes) {
1477
+ const thenBlock2 = optimizeBlock(node.thenBlock, thenEnv, currentDepth);
1478
+ if (thenBlock2.length === 0)
1479
+ return { type: "undefined" };
1480
+ if (thenBlock2.length === 1)
1481
+ return thenBlock2[0];
1482
+ return { type: "program", body: thenBlock2 };
1483
+ }
1484
+ if (!node.elseBranch)
1485
+ return { type: "undefined" };
1486
+ const loweredElse = optimizeElse(node.elseBranch, cloneOptimizeEnv(env), currentDepth);
1487
+ if (!loweredElse)
1488
+ return { type: "undefined" };
1489
+ if (loweredElse.type === "else") {
1490
+ if (loweredElse.block.length === 0)
1491
+ return { type: "undefined" };
1492
+ if (loweredElse.block.length === 1)
1493
+ return loweredElse.block[0];
1494
+ return { type: "program", body: loweredElse.block };
1495
+ }
1496
+ return {
1497
+ type: "conditional",
1498
+ head: loweredElse.head,
1499
+ condition: loweredElse.condition,
1500
+ thenBlock: loweredElse.thenBlock,
1501
+ elseBranch: loweredElse.elseBranch
1502
+ };
1503
+ }
1504
+ const thenBlock = optimizeBlock(node.thenBlock, thenEnv, currentDepth);
1505
+ const elseBranch = optimizeElse(node.elseBranch, cloneOptimizeEnv(env), currentDepth);
1506
+ let finalCondition = condition;
1507
+ if (condition.type === "assign" && condition.op === "=" && condition.place.type === "identifier") {
1508
+ const name = condition.place.name;
1509
+ const reads = new Set;
1510
+ for (const part of thenBlock)
1511
+ collectReads(part, reads);
1512
+ if (elseBranch)
1513
+ collectReadsElse(elseBranch, reads);
1514
+ if (!reads.has(name)) {
1515
+ finalCondition = condition.value;
1516
+ }
1517
+ }
1518
+ return {
1519
+ type: "conditional",
1520
+ head: node.head,
1521
+ condition: finalCondition,
1522
+ thenBlock,
1523
+ elseBranch
1524
+ };
1525
+ }
1526
+ case "for": {
1527
+ const sourceEnv = cloneOptimizeEnv(env);
1528
+ const binding = (() => {
1529
+ if (node.binding.type === "binding:expr") {
1530
+ return { type: "binding:expr", source: optimizeNode(node.binding.source, sourceEnv, currentDepth) };
1531
+ }
1532
+ if (node.binding.type === "binding:valueIn") {
1533
+ return {
1534
+ type: "binding:valueIn",
1535
+ value: node.binding.value,
1536
+ source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
1537
+ };
1538
+ }
1539
+ if (node.binding.type === "binding:keyValueIn") {
1540
+ return {
1541
+ type: "binding:keyValueIn",
1542
+ key: node.binding.key,
1543
+ value: node.binding.value,
1544
+ source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
1545
+ };
1546
+ }
1547
+ return {
1548
+ type: "binding:keyOf",
1549
+ key: node.binding.key,
1550
+ source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
1551
+ };
1552
+ })();
1553
+ const bodyEnv = cloneOptimizeEnv(env);
1554
+ dropBindingNames(bodyEnv, binding);
1555
+ return {
1556
+ type: "for",
1557
+ binding,
1558
+ body: optimizeBlock(node.body, bodyEnv, currentDepth + 1)
1559
+ };
1560
+ }
1561
+ case "arrayComprehension": {
1562
+ const sourceEnv = cloneOptimizeEnv(env);
1563
+ const binding = node.binding.type === "binding:expr" ? { type: "binding:expr", source: optimizeNode(node.binding.source, sourceEnv, currentDepth) } : node.binding.type === "binding:valueIn" ? {
1564
+ type: "binding:valueIn",
1565
+ value: node.binding.value,
1566
+ source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
1567
+ } : node.binding.type === "binding:keyValueIn" ? {
1568
+ type: "binding:keyValueIn",
1569
+ key: node.binding.key,
1570
+ value: node.binding.value,
1571
+ source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
1572
+ } : {
1573
+ type: "binding:keyOf",
1574
+ key: node.binding.key,
1575
+ source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
1576
+ };
1577
+ const bodyEnv = cloneOptimizeEnv(env);
1578
+ dropBindingNames(bodyEnv, binding);
1579
+ return {
1580
+ type: "arrayComprehension",
1581
+ binding,
1582
+ body: optimizeNode(node.body, bodyEnv, currentDepth + 1)
1583
+ };
1584
+ }
1585
+ case "objectComprehension": {
1586
+ const sourceEnv = cloneOptimizeEnv(env);
1587
+ const binding = node.binding.type === "binding:expr" ? { type: "binding:expr", source: optimizeNode(node.binding.source, sourceEnv, currentDepth) } : node.binding.type === "binding:valueIn" ? {
1588
+ type: "binding:valueIn",
1589
+ value: node.binding.value,
1590
+ source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
1591
+ } : node.binding.type === "binding:keyValueIn" ? {
1592
+ type: "binding:keyValueIn",
1593
+ key: node.binding.key,
1594
+ value: node.binding.value,
1595
+ source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
1596
+ } : {
1597
+ type: "binding:keyOf",
1598
+ key: node.binding.key,
1599
+ source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
1600
+ };
1601
+ const bodyEnv = cloneOptimizeEnv(env);
1602
+ dropBindingNames(bodyEnv, binding);
1603
+ return {
1604
+ type: "objectComprehension",
1605
+ binding,
1606
+ key: optimizeNode(node.key, bodyEnv, currentDepth + 1),
1607
+ value: optimizeNode(node.value, bodyEnv, currentDepth + 1)
1608
+ };
1609
+ }
1610
+ default:
1611
+ return node;
1612
+ }
1613
+ }
1614
+ function optimizeIR(node) {
1615
+ return optimizeNode(node, emptyOptimizeEnv(), 1);
1616
+ }
1617
+ function collectLocalBindings(node, locals) {
1618
+ switch (node.type) {
1619
+ case "assign":
1620
+ if (node.place.type === "identifier")
1621
+ locals.add(node.place.name);
1622
+ collectLocalBindings(node.place, locals);
1623
+ collectLocalBindings(node.value, locals);
1624
+ return;
1625
+ case "program":
1626
+ for (const part of node.body)
1627
+ collectLocalBindings(part, locals);
1628
+ return;
1629
+ case "group":
1630
+ collectLocalBindings(node.expression, locals);
1631
+ return;
1632
+ case "array":
1633
+ for (const item of node.items)
1634
+ collectLocalBindings(item, locals);
1635
+ return;
1636
+ case "object":
1637
+ for (const entry of node.entries) {
1638
+ collectLocalBindings(entry.key, locals);
1639
+ collectLocalBindings(entry.value, locals);
1640
+ }
1641
+ return;
1642
+ case "navigation":
1643
+ collectLocalBindings(node.target, locals);
1644
+ for (const segment of node.segments) {
1645
+ if (segment.type === "dynamic")
1646
+ collectLocalBindings(segment.key, locals);
1647
+ }
1648
+ return;
1649
+ case "call":
1650
+ collectLocalBindings(node.callee, locals);
1651
+ for (const arg of node.args)
1652
+ collectLocalBindings(arg, locals);
1653
+ return;
1654
+ case "unary":
1655
+ collectLocalBindings(node.value, locals);
1656
+ return;
1657
+ case "binary":
1658
+ collectLocalBindings(node.left, locals);
1659
+ collectLocalBindings(node.right, locals);
1660
+ return;
1661
+ case "conditional":
1662
+ collectLocalBindings(node.condition, locals);
1663
+ for (const part of node.thenBlock)
1664
+ collectLocalBindings(part, locals);
1665
+ if (node.elseBranch)
1666
+ collectLocalBindingsElse(node.elseBranch, locals);
1667
+ return;
1668
+ case "for":
1669
+ collectLocalBindingFromBinding(node.binding, locals);
1670
+ for (const part of node.body)
1671
+ collectLocalBindings(part, locals);
1672
+ return;
1673
+ case "arrayComprehension":
1674
+ collectLocalBindingFromBinding(node.binding, locals);
1675
+ collectLocalBindings(node.body, locals);
1676
+ return;
1677
+ case "objectComprehension":
1678
+ collectLocalBindingFromBinding(node.binding, locals);
1679
+ collectLocalBindings(node.key, locals);
1680
+ collectLocalBindings(node.value, locals);
1681
+ return;
1682
+ default:
1683
+ return;
1684
+ }
1685
+ }
1686
+ function collectLocalBindingFromBinding(binding, locals) {
1687
+ if (binding.type === "binding:valueIn") {
1688
+ locals.add(binding.value);
1689
+ collectLocalBindings(binding.source, locals);
1690
+ return;
1691
+ }
1692
+ if (binding.type === "binding:keyValueIn") {
1693
+ locals.add(binding.key);
1694
+ locals.add(binding.value);
1695
+ collectLocalBindings(binding.source, locals);
1696
+ return;
1697
+ }
1698
+ if (binding.type === "binding:keyOf") {
1699
+ locals.add(binding.key);
1700
+ collectLocalBindings(binding.source, locals);
1701
+ return;
1702
+ }
1703
+ collectLocalBindings(binding.source, locals);
1704
+ }
1705
+ function collectLocalBindingsElse(elseBranch, locals) {
1706
+ if (elseBranch.type === "else") {
1707
+ for (const part of elseBranch.block)
1708
+ collectLocalBindings(part, locals);
1709
+ return;
1710
+ }
1711
+ collectLocalBindings(elseBranch.condition, locals);
1712
+ for (const part of elseBranch.thenBlock)
1713
+ collectLocalBindings(part, locals);
1714
+ if (elseBranch.elseBranch)
1715
+ collectLocalBindingsElse(elseBranch.elseBranch, locals);
1716
+ }
1717
+ function bumpNameFrequency(name, locals, frequencies, order, nextOrder) {
1718
+ if (!locals.has(name))
1719
+ return;
1720
+ if (!order.has(name)) {
1721
+ order.set(name, nextOrder.value);
1722
+ nextOrder.value += 1;
1723
+ }
1724
+ frequencies.set(name, (frequencies.get(name) ?? 0) + 1);
1725
+ }
1726
+ function collectNameFrequencies(node, locals, frequencies, order, nextOrder) {
1727
+ switch (node.type) {
1728
+ case "identifier":
1729
+ bumpNameFrequency(node.name, locals, frequencies, order, nextOrder);
1730
+ return;
1731
+ case "assign":
1732
+ if (node.place.type === "identifier")
1733
+ bumpNameFrequency(node.place.name, locals, frequencies, order, nextOrder);
1734
+ collectNameFrequencies(node.place, locals, frequencies, order, nextOrder);
1735
+ collectNameFrequencies(node.value, locals, frequencies, order, nextOrder);
1736
+ return;
1737
+ case "program":
1738
+ for (const part of node.body)
1739
+ collectNameFrequencies(part, locals, frequencies, order, nextOrder);
1740
+ return;
1741
+ case "group":
1742
+ collectNameFrequencies(node.expression, locals, frequencies, order, nextOrder);
1743
+ return;
1744
+ case "array":
1745
+ for (const item of node.items)
1746
+ collectNameFrequencies(item, locals, frequencies, order, nextOrder);
1747
+ return;
1748
+ case "object":
1749
+ for (const entry of node.entries) {
1750
+ collectNameFrequencies(entry.key, locals, frequencies, order, nextOrder);
1751
+ collectNameFrequencies(entry.value, locals, frequencies, order, nextOrder);
1752
+ }
1753
+ return;
1754
+ case "navigation":
1755
+ collectNameFrequencies(node.target, locals, frequencies, order, nextOrder);
1756
+ for (const segment of node.segments) {
1757
+ if (segment.type === "dynamic")
1758
+ collectNameFrequencies(segment.key, locals, frequencies, order, nextOrder);
1759
+ }
1760
+ return;
1761
+ case "call":
1762
+ collectNameFrequencies(node.callee, locals, frequencies, order, nextOrder);
1763
+ for (const arg of node.args)
1764
+ collectNameFrequencies(arg, locals, frequencies, order, nextOrder);
1765
+ return;
1766
+ case "unary":
1767
+ collectNameFrequencies(node.value, locals, frequencies, order, nextOrder);
1768
+ return;
1769
+ case "binary":
1770
+ collectNameFrequencies(node.left, locals, frequencies, order, nextOrder);
1771
+ collectNameFrequencies(node.right, locals, frequencies, order, nextOrder);
1772
+ return;
1773
+ case "conditional":
1774
+ collectNameFrequencies(node.condition, locals, frequencies, order, nextOrder);
1775
+ for (const part of node.thenBlock)
1776
+ collectNameFrequencies(part, locals, frequencies, order, nextOrder);
1777
+ if (node.elseBranch)
1778
+ collectNameFrequenciesElse(node.elseBranch, locals, frequencies, order, nextOrder);
1779
+ return;
1780
+ case "for":
1781
+ collectNameFrequenciesBinding(node.binding, locals, frequencies, order, nextOrder);
1782
+ for (const part of node.body)
1783
+ collectNameFrequencies(part, locals, frequencies, order, nextOrder);
1784
+ return;
1785
+ case "arrayComprehension":
1786
+ collectNameFrequenciesBinding(node.binding, locals, frequencies, order, nextOrder);
1787
+ collectNameFrequencies(node.body, locals, frequencies, order, nextOrder);
1788
+ return;
1789
+ case "objectComprehension":
1790
+ collectNameFrequenciesBinding(node.binding, locals, frequencies, order, nextOrder);
1791
+ collectNameFrequencies(node.key, locals, frequencies, order, nextOrder);
1792
+ collectNameFrequencies(node.value, locals, frequencies, order, nextOrder);
1793
+ return;
1794
+ default:
1795
+ return;
1796
+ }
1797
+ }
1798
+ function collectNameFrequenciesBinding(binding, locals, frequencies, order, nextOrder) {
1799
+ if (binding.type === "binding:valueIn") {
1800
+ bumpNameFrequency(binding.value, locals, frequencies, order, nextOrder);
1801
+ collectNameFrequencies(binding.source, locals, frequencies, order, nextOrder);
1802
+ return;
1803
+ }
1804
+ if (binding.type === "binding:keyValueIn") {
1805
+ bumpNameFrequency(binding.key, locals, frequencies, order, nextOrder);
1806
+ bumpNameFrequency(binding.value, locals, frequencies, order, nextOrder);
1807
+ collectNameFrequencies(binding.source, locals, frequencies, order, nextOrder);
1808
+ return;
1809
+ }
1810
+ if (binding.type === "binding:keyOf") {
1811
+ bumpNameFrequency(binding.key, locals, frequencies, order, nextOrder);
1812
+ collectNameFrequencies(binding.source, locals, frequencies, order, nextOrder);
1813
+ return;
1814
+ }
1815
+ collectNameFrequencies(binding.source, locals, frequencies, order, nextOrder);
1816
+ }
1817
+ function collectNameFrequenciesElse(elseBranch, locals, frequencies, order, nextOrder) {
1818
+ if (elseBranch.type === "else") {
1819
+ for (const part of elseBranch.block)
1820
+ collectNameFrequencies(part, locals, frequencies, order, nextOrder);
1821
+ return;
1822
+ }
1823
+ collectNameFrequencies(elseBranch.condition, locals, frequencies, order, nextOrder);
1824
+ for (const part of elseBranch.thenBlock)
1825
+ collectNameFrequencies(part, locals, frequencies, order, nextOrder);
1826
+ if (elseBranch.elseBranch)
1827
+ collectNameFrequenciesElse(elseBranch.elseBranch, locals, frequencies, order, nextOrder);
1828
+ }
1829
+ function renameLocalNames(node, map) {
1830
+ switch (node.type) {
1831
+ case "identifier":
1832
+ return map.has(node.name) ? { type: "identifier", name: map.get(node.name) } : node;
1833
+ case "program":
1834
+ return { type: "program", body: node.body.map((part) => renameLocalNames(part, map)) };
1835
+ case "group":
1836
+ return { type: "group", expression: renameLocalNames(node.expression, map) };
1837
+ case "array":
1838
+ return { type: "array", items: node.items.map((item) => renameLocalNames(item, map)) };
1839
+ case "object":
1840
+ return {
1841
+ type: "object",
1842
+ entries: node.entries.map((entry) => ({
1843
+ key: renameLocalNames(entry.key, map),
1844
+ value: renameLocalNames(entry.value, map)
1845
+ }))
1846
+ };
1847
+ case "navigation":
1848
+ return {
1849
+ type: "navigation",
1850
+ target: renameLocalNames(node.target, map),
1851
+ segments: node.segments.map((segment) => segment.type === "static" ? segment : { type: "dynamic", key: renameLocalNames(segment.key, map) })
1852
+ };
1853
+ case "call":
1854
+ return {
1855
+ type: "call",
1856
+ callee: renameLocalNames(node.callee, map),
1857
+ args: node.args.map((arg) => renameLocalNames(arg, map))
1858
+ };
1859
+ case "unary":
1860
+ return { type: "unary", op: node.op, value: renameLocalNames(node.value, map) };
1861
+ case "binary":
1862
+ return {
1863
+ type: "binary",
1864
+ op: node.op,
1865
+ left: renameLocalNames(node.left, map),
1866
+ right: renameLocalNames(node.right, map)
1867
+ };
1868
+ case "assign": {
1869
+ const place = node.place.type === "identifier" && map.has(node.place.name) ? { type: "identifier", name: map.get(node.place.name) } : renameLocalNames(node.place, map);
1870
+ return {
1871
+ type: "assign",
1872
+ op: node.op,
1873
+ place,
1874
+ value: renameLocalNames(node.value, map)
1875
+ };
1876
+ }
1877
+ case "conditional":
1878
+ return {
1879
+ type: "conditional",
1880
+ head: node.head,
1881
+ condition: renameLocalNames(node.condition, map),
1882
+ thenBlock: node.thenBlock.map((part) => renameLocalNames(part, map)),
1883
+ elseBranch: node.elseBranch ? renameLocalNamesElse(node.elseBranch, map) : undefined
1884
+ };
1885
+ case "for":
1886
+ return {
1887
+ type: "for",
1888
+ binding: renameLocalNamesBinding(node.binding, map),
1889
+ body: node.body.map((part) => renameLocalNames(part, map))
1890
+ };
1891
+ case "arrayComprehension":
1892
+ return {
1893
+ type: "arrayComprehension",
1894
+ binding: renameLocalNamesBinding(node.binding, map),
1895
+ body: renameLocalNames(node.body, map)
1896
+ };
1897
+ case "objectComprehension":
1898
+ return {
1899
+ type: "objectComprehension",
1900
+ binding: renameLocalNamesBinding(node.binding, map),
1901
+ key: renameLocalNames(node.key, map),
1902
+ value: renameLocalNames(node.value, map)
1903
+ };
1904
+ default:
1905
+ return node;
1906
+ }
1907
+ }
1908
+ function renameLocalNamesBinding(binding, map) {
1909
+ if (binding.type === "binding:expr") {
1910
+ return { type: "binding:expr", source: renameLocalNames(binding.source, map) };
1911
+ }
1912
+ if (binding.type === "binding:valueIn") {
1913
+ return {
1914
+ type: "binding:valueIn",
1915
+ value: map.get(binding.value) ?? binding.value,
1916
+ source: renameLocalNames(binding.source, map)
1917
+ };
1918
+ }
1919
+ if (binding.type === "binding:keyValueIn") {
1920
+ return {
1921
+ type: "binding:keyValueIn",
1922
+ key: map.get(binding.key) ?? binding.key,
1923
+ value: map.get(binding.value) ?? binding.value,
1924
+ source: renameLocalNames(binding.source, map)
1925
+ };
1926
+ }
1927
+ return {
1928
+ type: "binding:keyOf",
1929
+ key: map.get(binding.key) ?? binding.key,
1930
+ source: renameLocalNames(binding.source, map)
1931
+ };
1932
+ }
1933
+ function renameLocalNamesElse(elseBranch, map) {
1934
+ if (elseBranch.type === "else") {
1935
+ return {
1936
+ type: "else",
1937
+ block: elseBranch.block.map((part) => renameLocalNames(part, map))
1938
+ };
1939
+ }
1940
+ return {
1941
+ type: "elseChain",
1942
+ head: elseBranch.head,
1943
+ condition: renameLocalNames(elseBranch.condition, map),
1944
+ thenBlock: elseBranch.thenBlock.map((part) => renameLocalNames(part, map)),
1945
+ elseBranch: elseBranch.elseBranch ? renameLocalNamesElse(elseBranch.elseBranch, map) : undefined
1946
+ };
1947
+ }
1948
+ function minifyLocalNamesIR(node) {
1949
+ const locals = new Set;
1950
+ collectLocalBindings(node, locals);
1951
+ if (locals.size === 0)
1952
+ return node;
1953
+ const frequencies = new Map;
1954
+ const order = new Map;
1955
+ collectNameFrequencies(node, locals, frequencies, order, { value: 0 });
1956
+ const ranked = Array.from(locals).sort((a, b) => {
1957
+ const freqA = frequencies.get(a) ?? 0;
1958
+ const freqB = frequencies.get(b) ?? 0;
1959
+ if (freqA !== freqB)
1960
+ return freqB - freqA;
1961
+ const orderA = order.get(a) ?? Number.MAX_SAFE_INTEGER;
1962
+ const orderB = order.get(b) ?? Number.MAX_SAFE_INTEGER;
1963
+ if (orderA !== orderB)
1964
+ return orderA - orderB;
1965
+ return a.localeCompare(b);
1966
+ });
1967
+ const renameMap = new Map;
1968
+ ranked.forEach((name, index) => {
1969
+ renameMap.set(name, encodeUint(index));
1970
+ });
1971
+ return renameLocalNames(node, renameMap);
1972
+ }
1973
+ function compile(source, options) {
1974
+ const ir = parseToIR(source);
1975
+ let lowered = options?.optimize ? optimizeIR(ir) : ir;
1976
+ if (options?.minifyNames)
1977
+ lowered = minifyLocalNamesIR(lowered);
1978
+ const domainRefs = options?.domainConfig ? domainRefsFromConfig(options.domainConfig) : undefined;
1979
+ return encodeIR(lowered, {
1980
+ domainRefs,
1981
+ dedupeValues: options?.dedupeValues,
1982
+ dedupeMinBytes: options?.dedupeMinBytes
1983
+ });
1984
+ }
1985
+ function parseNumber(raw) {
1986
+ if (/^-?0x/i.test(raw))
1987
+ return parseInt(raw, 16);
1988
+ if (/^-?0b/i.test(raw)) {
1989
+ const isNegative = raw.startsWith("-");
1990
+ const digits = raw.replace(/^-?0b/i, "");
1991
+ const value = parseInt(digits, 2);
1992
+ return isNegative ? -value : value;
1993
+ }
1994
+ return Number(raw);
1995
+ }
1996
+ function collectStructured(value, out) {
1997
+ if (Array.isArray(value)) {
1998
+ for (const part of value)
1999
+ collectStructured(part, out);
2000
+ return;
2001
+ }
2002
+ if (!value || typeof value !== "object")
2003
+ return;
2004
+ if ("type" in value || "key" in value && "value" in value) {
2005
+ out.push(value);
2006
+ }
2007
+ }
2008
+ function normalizeList(value) {
2009
+ const out = [];
2010
+ collectStructured(value, out);
2011
+ return out;
2012
+ }
2013
+ function collectPostfixSteps(value, out) {
2014
+ if (Array.isArray(value)) {
2015
+ for (const part of value)
2016
+ collectPostfixSteps(part, out);
2017
+ return;
2018
+ }
2019
+ if (!value || typeof value !== "object")
2020
+ return;
2021
+ if ("kind" in value)
2022
+ out.push(value);
2023
+ }
2024
+ function normalizePostfixSteps(value) {
2025
+ const out = [];
2026
+ collectPostfixSteps(value, out);
2027
+ return out;
2028
+ }
2029
+ function buildPostfix(base, steps) {
2030
+ let current = base;
2031
+ let pendingSegments = [];
2032
+ const flushSegments = () => {
2033
+ if (pendingSegments.length === 0)
2034
+ return;
2035
+ current = {
2036
+ type: "navigation",
2037
+ target: current,
2038
+ segments: pendingSegments
2039
+ };
2040
+ pendingSegments = [];
2041
+ };
2042
+ for (const step of steps) {
2043
+ if (step.kind === "navStatic") {
2044
+ pendingSegments.push({ type: "static", key: step.key });
2045
+ continue;
2046
+ }
2047
+ if (step.kind === "navDynamic") {
2048
+ pendingSegments.push({ type: "dynamic", key: step.key });
2049
+ continue;
2050
+ }
2051
+ flushSegments();
2052
+ current = { type: "call", callee: current, args: step.args };
2053
+ }
2054
+ flushSegments();
2055
+ return current;
2056
+ }
2057
+ var require2, rexGrammarModule, rexGrammar, grammar, semantics, DIGITS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_", OPCODE_IDS, FIRST_NON_RESERVED_REF = 5, DOMAIN_DIGIT_INDEX, BINARY_TO_OPCODE, ASSIGN_COMPOUND_TO_OPCODE, activeEncodeOptions, DIGIT_SET, DIGIT_INDEX;
2058
+ var init_rex = __esm(() => {
2059
+ require2 = createRequire(import.meta.url);
2060
+ rexGrammarModule = require2("./rex.ohm-bundle.cjs");
2061
+ rexGrammar = rexGrammarModule?.default ?? rexGrammarModule;
2062
+ grammar = rexGrammar;
2063
+ semantics = rexGrammar.createSemantics();
2064
+ OPCODE_IDS = {
2065
+ do: 0,
2066
+ add: 1,
2067
+ sub: 2,
2068
+ mul: 3,
2069
+ div: 4,
2070
+ eq: 5,
2071
+ neq: 6,
2072
+ lt: 7,
2073
+ lte: 8,
2074
+ gt: 9,
2075
+ gte: 10,
2076
+ and: 11,
2077
+ or: 12,
2078
+ xor: 13,
2079
+ not: 14,
2080
+ boolean: 15,
2081
+ number: 16,
2082
+ string: 17,
2083
+ array: 18,
2084
+ object: 19,
2085
+ mod: 20,
2086
+ neg: 21
2087
+ };
2088
+ DOMAIN_DIGIT_INDEX = new Map(Array.from(DIGITS).map((char, index) => [char, index]));
2089
+ BINARY_TO_OPCODE = {
2090
+ add: "add",
2091
+ sub: "sub",
2092
+ mul: "mul",
2093
+ div: "div",
2094
+ mod: "mod",
2095
+ bitAnd: "and",
2096
+ bitOr: "or",
2097
+ bitXor: "xor",
2098
+ and: "and",
2099
+ or: "or",
2100
+ eq: "eq",
2101
+ neq: "neq",
2102
+ gt: "gt",
2103
+ gte: "gte",
2104
+ lt: "lt",
2105
+ lte: "lte"
2106
+ };
2107
+ ASSIGN_COMPOUND_TO_OPCODE = {
2108
+ "+=": "add",
2109
+ "-=": "sub",
2110
+ "*=": "mul",
2111
+ "/=": "div",
2112
+ "%=": "mod",
2113
+ "&=": "and",
2114
+ "|=": "or",
2115
+ "^=": "xor"
2116
+ };
2117
+ DIGIT_SET = new Set(DIGITS.split(""));
2118
+ DIGIT_INDEX = new Map(Array.from(DIGITS).map((char, index) => [char, index]));
2119
+ semantics.addOperation("toIR", {
2120
+ _iter(...children) {
2121
+ return children.map((child) => child.toIR());
2122
+ },
2123
+ _terminal() {
2124
+ return this.sourceString;
2125
+ },
2126
+ _nonterminal(...children) {
2127
+ if (children.length === 1 && children[0])
2128
+ return children[0].toIR();
2129
+ return children.map((child) => child.toIR());
2130
+ },
2131
+ Program(expressions) {
2132
+ const body = normalizeList(expressions.toIR());
2133
+ if (body.length === 1)
2134
+ return body[0];
2135
+ return { type: "program", body };
2136
+ },
2137
+ Block(expressions) {
2138
+ return normalizeList(expressions.toIR());
2139
+ },
2140
+ Elements(first, separatorsAndItems, maybeTrailingComma, maybeEmpty) {
2141
+ return normalizeList([
2142
+ first.toIR(),
2143
+ separatorsAndItems.toIR(),
2144
+ maybeTrailingComma.toIR(),
2145
+ maybeEmpty.toIR()
2146
+ ]);
2147
+ },
2148
+ AssignExpr_assign(place, op, value) {
2149
+ return {
2150
+ type: "assign",
2151
+ op: op.sourceString,
2152
+ place: place.toIR(),
2153
+ value: value.toIR()
2154
+ };
2155
+ },
2156
+ ExistenceExpr_and(left, _and, right) {
2157
+ return { type: "binary", op: "and", left: left.toIR(), right: right.toIR() };
2158
+ },
2159
+ ExistenceExpr_or(left, _or, right) {
2160
+ return { type: "binary", op: "or", left: left.toIR(), right: right.toIR() };
2161
+ },
2162
+ BitExpr_and(left, _op, right) {
2163
+ return { type: "binary", op: "bitAnd", left: left.toIR(), right: right.toIR() };
2164
+ },
2165
+ BitExpr_xor(left, _op, right) {
2166
+ return { type: "binary", op: "bitXor", left: left.toIR(), right: right.toIR() };
2167
+ },
2168
+ BitExpr_or(left, _op, right) {
2169
+ return { type: "binary", op: "bitOr", left: left.toIR(), right: right.toIR() };
2170
+ },
2171
+ CompareExpr_binary(left, op, right) {
2172
+ const map = {
2173
+ "==": "eq",
2174
+ "!=": "neq",
2175
+ ">": "gt",
2176
+ ">=": "gte",
2177
+ "<": "lt",
2178
+ "<=": "lte"
2179
+ };
2180
+ const mapped = map[op.sourceString];
2181
+ if (!mapped)
2182
+ throw new Error(`Unsupported compare op: ${op.sourceString}`);
2183
+ return { type: "binary", op: mapped, left: left.toIR(), right: right.toIR() };
2184
+ },
2185
+ AddExpr_add(left, _op, right) {
2186
+ return { type: "binary", op: "add", left: left.toIR(), right: right.toIR() };
2187
+ },
2188
+ AddExpr_sub(left, _op, right) {
2189
+ return { type: "binary", op: "sub", left: left.toIR(), right: right.toIR() };
2190
+ },
2191
+ MulExpr_mul(left, _op, right) {
2192
+ return { type: "binary", op: "mul", left: left.toIR(), right: right.toIR() };
2193
+ },
2194
+ MulExpr_div(left, _op, right) {
2195
+ return { type: "binary", op: "div", left: left.toIR(), right: right.toIR() };
2196
+ },
2197
+ MulExpr_mod(left, _op, right) {
2198
+ return { type: "binary", op: "mod", left: left.toIR(), right: right.toIR() };
2199
+ },
2200
+ UnaryExpr_neg(_op, value) {
2201
+ const lowered = value.toIR();
2202
+ if (lowered.type === "number") {
2203
+ const raw = lowered.raw.startsWith("-") ? lowered.raw.slice(1) : `-${lowered.raw}`;
2204
+ return { type: "number", raw, value: -lowered.value };
2205
+ }
2206
+ return { type: "unary", op: "neg", value: lowered };
2207
+ },
2208
+ UnaryExpr_not(_op, value) {
2209
+ return { type: "unary", op: "not", value: value.toIR() };
2210
+ },
2211
+ UnaryExpr_delete(_del, place) {
2212
+ return { type: "unary", op: "delete", value: place.toIR() };
2213
+ },
2214
+ PostfixExpr_chain(base, tails) {
2215
+ return buildPostfix(base.toIR(), normalizePostfixSteps(tails.toIR()));
2216
+ },
2217
+ Place(base, tails) {
2218
+ return buildPostfix(base.toIR(), normalizePostfixSteps(tails.toIR()));
2219
+ },
2220
+ PlaceTail_navStatic(_dot, key) {
2221
+ return { kind: "navStatic", key: key.sourceString };
2222
+ },
2223
+ PlaceTail_navDynamic(_dotOpen, key, _close) {
2224
+ return { kind: "navDynamic", key: key.toIR() };
2225
+ },
2226
+ PostfixTail_navStatic(_dot, key) {
2227
+ return { kind: "navStatic", key: key.sourceString };
2228
+ },
2229
+ PostfixTail_navDynamic(_dotOpen, key, _close) {
2230
+ return { kind: "navDynamic", key: key.toIR() };
2231
+ },
2232
+ PostfixTail_callEmpty(_open, _close) {
2233
+ return { kind: "call", args: [] };
2234
+ },
2235
+ PostfixTail_call(_open, args, _close) {
2236
+ return { kind: "call", args: normalizeList(args.toIR()) };
2237
+ },
2238
+ ConditionalExpr(head, condition, _do, thenBlock, elseBranch, _end) {
2239
+ const nextElse = elseBranch.children[0];
2240
+ return {
2241
+ type: "conditional",
2242
+ head: head.toIR(),
2243
+ condition: condition.toIR(),
2244
+ thenBlock: thenBlock.toIR(),
2245
+ elseBranch: nextElse ? nextElse.toIR() : undefined
2246
+ };
2247
+ },
2248
+ ConditionalHead(_kw) {
2249
+ return this.sourceString;
2250
+ },
2251
+ ConditionalElse_elseChain(_else, head, condition, _do, thenBlock, elseBranch) {
2252
+ const nextElse = elseBranch.children[0];
2253
+ return {
2254
+ type: "elseChain",
2255
+ head: head.toIR(),
2256
+ condition: condition.toIR(),
2257
+ thenBlock: thenBlock.toIR(),
2258
+ elseBranch: nextElse ? nextElse.toIR() : undefined
2259
+ };
2260
+ },
2261
+ ConditionalElse_else(_else, block) {
2262
+ return { type: "else", block: block.toIR() };
2263
+ },
2264
+ DoExpr(_do, block, _end) {
2265
+ const body = block.toIR();
2266
+ if (body.length === 0)
2267
+ return { type: "undefined" };
2268
+ if (body.length === 1)
2269
+ return body[0];
2270
+ return { type: "program", body };
2271
+ },
2272
+ WhileExpr(_while, condition, _do, block, _end) {
2273
+ return {
2274
+ type: "while",
2275
+ condition: condition.toIR(),
2276
+ body: block.toIR()
2277
+ };
2278
+ },
2279
+ ForExpr(_for, binding, _do, block, _end) {
2280
+ return {
2281
+ type: "for",
2282
+ binding: binding.toIR(),
2283
+ body: block.toIR()
2284
+ };
2285
+ },
2286
+ BindingExpr(iterOrExpr) {
2287
+ const node = iterOrExpr.toIR();
2288
+ if (typeof node === "object" && node && "type" in node && String(node.type).startsWith("binding:")) {
2289
+ return node;
2290
+ }
2291
+ return { type: "binding:expr", source: node };
2292
+ },
2293
+ Array_empty(_open, _close) {
2294
+ return { type: "array", items: [] };
2295
+ },
2296
+ Array_comprehension(_open, binding, _semi, body, _close) {
2297
+ return {
2298
+ type: "arrayComprehension",
2299
+ binding: binding.toIR(),
2300
+ body: body.toIR()
2301
+ };
2302
+ },
2303
+ Array_values(_open, items, _close) {
2304
+ return { type: "array", items: normalizeList(items.toIR()) };
2305
+ },
2306
+ Object_empty(_open, _close) {
2307
+ return { type: "object", entries: [] };
2308
+ },
2309
+ Object_comprehension(_open, binding, _semi, key, _colon, value, _close) {
2310
+ return {
2311
+ type: "objectComprehension",
2312
+ binding: binding.toIR(),
2313
+ key: key.toIR(),
2314
+ value: value.toIR()
2315
+ };
2316
+ },
2317
+ Object_pairs(_open, pairs, _close) {
2318
+ return {
2319
+ type: "object",
2320
+ entries: normalizeList(pairs.toIR())
2321
+ };
2322
+ },
2323
+ IterBinding_keyValueIn(key, _comma, value, _in, source) {
2324
+ return {
2325
+ type: "binding:keyValueIn",
2326
+ key: key.sourceString,
2327
+ value: value.sourceString,
2328
+ source: source.toIR()
2329
+ };
2330
+ },
2331
+ IterBinding_valueIn(value, _in, source) {
2332
+ return {
2333
+ type: "binding:valueIn",
2334
+ value: value.sourceString,
2335
+ source: source.toIR()
2336
+ };
2337
+ },
2338
+ IterBinding_keyOf(key, _of, source) {
2339
+ return {
2340
+ type: "binding:keyOf",
2341
+ key: key.sourceString,
2342
+ source: source.toIR()
2343
+ };
2344
+ },
2345
+ Pair(key, _colon, value) {
2346
+ return { key: key.toIR(), value: value.toIR() };
2347
+ },
2348
+ ObjKey_bare(key) {
2349
+ return { type: "key", name: key.sourceString };
2350
+ },
2351
+ ObjKey_number(num) {
2352
+ return num.toIR();
2353
+ },
2354
+ ObjKey_string(str) {
2355
+ return str.toIR();
2356
+ },
2357
+ ObjKey_computed(_open, expr, _close) {
2358
+ return expr.toIR();
2359
+ },
2360
+ BreakKw(_kw) {
2361
+ return { type: "break" };
2362
+ },
2363
+ ContinueKw(_kw) {
2364
+ return { type: "continue" };
2365
+ },
2366
+ SelfExpr_depth(_self, _at, depth) {
2367
+ const value = depth.toIR();
2368
+ if (value.type !== "number" || !Number.isInteger(value.value) || value.value < 1) {
2369
+ throw new Error("self depth must be a positive integer literal");
2370
+ }
2371
+ if (value.value === 1)
2372
+ return { type: "self" };
2373
+ return { type: "selfDepth", depth: value.value };
2374
+ },
2375
+ SelfExpr_plain(selfKw) {
2376
+ return selfKw.toIR();
2377
+ },
2378
+ SelfKw(_kw) {
2379
+ return { type: "self" };
2380
+ },
2381
+ TrueKw(_kw) {
2382
+ return { type: "boolean", value: true };
2383
+ },
2384
+ FalseKw(_kw) {
2385
+ return { type: "boolean", value: false };
2386
+ },
2387
+ NullKw(_kw) {
2388
+ return { type: "null" };
2389
+ },
2390
+ UndefinedKw(_kw) {
2391
+ return { type: "undefined" };
2392
+ },
2393
+ StringKw(_kw) {
2394
+ return { type: "identifier", name: "string" };
2395
+ },
2396
+ NumberKw(_kw) {
2397
+ return { type: "identifier", name: "number" };
2398
+ },
2399
+ ObjectKw(_kw) {
2400
+ return { type: "identifier", name: "object" };
2401
+ },
2402
+ ArrayKw(_kw) {
2403
+ return { type: "identifier", name: "array" };
2404
+ },
2405
+ BooleanKw(_kw) {
2406
+ return { type: "identifier", name: "boolean" };
2407
+ },
2408
+ identifier(_a, _b) {
2409
+ return { type: "identifier", name: this.sourceString };
2410
+ },
2411
+ String(_value) {
2412
+ return { type: "string", raw: this.sourceString };
2413
+ },
2414
+ Number(_value) {
2415
+ return { type: "number", raw: this.sourceString, value: parseNumber(this.sourceString) };
2416
+ },
2417
+ PrimaryExpr_group(_open, expr, _close) {
2418
+ return { type: "group", expression: expr.toIR() };
2419
+ }
2420
+ });
2421
+ });
2422
+
2423
+ // rexc-interpreter.ts
2424
+ function decodePrefix(text, start, end) {
2425
+ let value = 0;
2426
+ for (let index = start;index < end; index += 1) {
2427
+ const digit = digitMap.get(text[index] ?? "") ?? -1;
2428
+ if (digit < 0)
2429
+ throw new Error(`Invalid digit '${text[index]}'`);
2430
+ value = value * 64 + digit;
2431
+ }
2432
+ return value;
2433
+ }
2434
+ function decodeZigzag(value) {
2435
+ return value % 2 === 0 ? value / 2 : -(value + 1) / 2;
2436
+ }
2437
+ function isDefined(value) {
2438
+ return value !== undefined;
2439
+ }
2440
+
2441
+ class CursorInterpreter {
2442
+ text;
2443
+ pos = 0;
2444
+ state;
2445
+ selfStack;
2446
+ opcodeMarkers;
2447
+ pointerCache = new Map;
2448
+ gasLimit;
2449
+ gas = 0;
2450
+ tick() {
2451
+ if (this.gasLimit && ++this.gas > this.gasLimit) {
2452
+ throw new Error("Gas limit exceeded (too many loop iterations)");
2453
+ }
2454
+ }
2455
+ constructor(text, ctx = {}) {
2456
+ const initialSelf = ctx.selfStack && ctx.selfStack.length > 0 ? ctx.selfStack[ctx.selfStack.length - 1] : ctx.self;
2457
+ this.text = text;
2458
+ this.state = {
2459
+ vars: ctx.vars ?? {},
2460
+ refs: {
2461
+ 0: ctx.refs?.[0],
2462
+ 1: ctx.refs?.[1] ?? true,
2463
+ 2: ctx.refs?.[2] ?? false,
2464
+ 3: ctx.refs?.[3] ?? null,
2465
+ 4: ctx.refs?.[4] ?? undefined
2466
+ }
2467
+ };
2468
+ this.selfStack = ctx.selfStack && ctx.selfStack.length > 0 ? [...ctx.selfStack] : [initialSelf];
2469
+ this.gasLimit = ctx.gasLimit ?? 0;
2470
+ this.opcodeMarkers = Array.from({ length: 256 }, (_, id) => ({ __opcode: id }));
2471
+ for (const [idText, value] of Object.entries(ctx.refs ?? {})) {
2472
+ const id = Number(idText);
2473
+ if (Number.isInteger(id))
2474
+ this.state.refs[id] = value;
2475
+ }
2476
+ if (ctx.opcodes) {
2477
+ for (const [idText, op] of Object.entries(ctx.opcodes)) {
2478
+ if (op)
2479
+ this.customOpcodes.set(Number(idText), op);
2480
+ }
2481
+ }
2482
+ }
2483
+ customOpcodes = new Map;
2484
+ readSelf(depthPrefix) {
2485
+ const depth = depthPrefix + 1;
2486
+ const index = this.selfStack.length - depth;
2487
+ if (index < 0)
2488
+ return;
2489
+ return this.selfStack[index];
2490
+ }
2491
+ get runtimeState() {
2492
+ return this.state;
2493
+ }
2494
+ evaluateTopLevel() {
2495
+ this.skipNonCode();
2496
+ if (this.pos >= this.text.length)
2497
+ return;
2498
+ const value = this.evalValue();
2499
+ this.skipNonCode();
2500
+ if (this.pos < this.text.length)
2501
+ throw new Error(`Unexpected trailing input at ${this.pos}`);
2502
+ return value;
2503
+ }
2504
+ skipNonCode() {
2505
+ while (this.pos < this.text.length) {
2506
+ const ch = this.text[this.pos];
2507
+ if (ch === " " || ch === "\t" || ch === `
2508
+ ` || ch === "\r") {
2509
+ this.pos += 1;
2510
+ continue;
2511
+ }
2512
+ if (ch === "/" && this.text[this.pos + 1] === "/") {
2513
+ this.pos += 2;
2514
+ while (this.pos < this.text.length && this.text[this.pos] !== `
2515
+ `)
2516
+ this.pos += 1;
2517
+ continue;
2518
+ }
2519
+ if (ch === "/" && this.text[this.pos + 1] === "*") {
2520
+ this.pos += 2;
2521
+ while (this.pos < this.text.length) {
2522
+ if (this.text[this.pos] === "*" && this.text[this.pos + 1] === "/") {
2523
+ this.pos += 2;
2524
+ break;
2525
+ }
2526
+ this.pos += 1;
2527
+ }
2528
+ continue;
2529
+ }
2530
+ break;
2531
+ }
2532
+ }
2533
+ readPrefix() {
2534
+ const start = this.pos;
2535
+ while (this.pos < this.text.length && digitMap.has(this.text[this.pos] ?? ""))
2536
+ this.pos += 1;
2537
+ const end = this.pos;
2538
+ return { start, end, value: decodePrefix(this.text, start, end), raw: this.text.slice(start, end) };
2539
+ }
2540
+ ensure(char) {
2541
+ if (this.text[this.pos] !== char)
2542
+ throw new Error(`Expected '${char}' at ${this.pos}`);
2543
+ this.pos += 1;
2544
+ }
2545
+ hasMoreBefore(close) {
2546
+ const save = this.pos;
2547
+ this.skipNonCode();
2548
+ const more = this.pos < this.text.length && this.text[this.pos] !== close;
2549
+ this.pos = save;
2550
+ return more;
2551
+ }
2552
+ readBindingVarIfPresent() {
2553
+ const save = this.pos;
2554
+ this.skipNonCode();
2555
+ const prefix = this.readPrefix();
2556
+ const tag = this.text[this.pos];
2557
+ if (tag === "$" && prefix.raw.length > 0) {
2558
+ this.pos += 1;
2559
+ return prefix.raw;
2560
+ }
2561
+ this.pos = save;
2562
+ return;
2563
+ }
2564
+ evalValue() {
2565
+ this.skipNonCode();
2566
+ const prefix = this.readPrefix();
2567
+ const tag = this.text[this.pos];
2568
+ if (!tag)
2569
+ throw new Error("Unexpected end of input");
2570
+ switch (tag) {
2571
+ case "+":
2572
+ this.pos += 1;
2573
+ return decodeZigzag(prefix.value);
2574
+ case "*": {
2575
+ this.pos += 1;
2576
+ const power = decodeZigzag(prefix.value);
2577
+ const significand = this.evalValue();
2578
+ if (typeof significand !== "number")
2579
+ throw new Error("Decimal significand must be numeric");
2580
+ return significand * 10 ** power;
2581
+ }
2582
+ case ":":
2583
+ this.pos += 1;
2584
+ return prefix.raw;
2585
+ case "%":
2586
+ this.pos += 1;
2587
+ return this.opcodeMarkers[prefix.value] ?? { __opcode: prefix.value };
2588
+ case "@":
2589
+ this.pos += 1;
2590
+ return this.readSelf(prefix.value);
2591
+ case "'":
2592
+ this.pos += 1;
2593
+ return this.state.refs[prefix.value];
2594
+ case "$":
2595
+ this.pos += 1;
2596
+ return this.state.vars[prefix.raw];
2597
+ case ",": {
2598
+ this.pos += 1;
2599
+ const start = this.pos;
2600
+ const end = start + prefix.value;
2601
+ if (end > this.text.length)
2602
+ throw new Error("String container overflows input");
2603
+ const value = this.text.slice(start, end);
2604
+ this.pos = end;
2605
+ return value;
2606
+ }
2607
+ case "^": {
2608
+ this.pos += 1;
2609
+ const target = this.pos + prefix.value;
2610
+ if (this.pointerCache.has(target))
2611
+ return this.pointerCache.get(target);
2612
+ const save = this.pos;
2613
+ this.pos = target;
2614
+ const value = this.evalValue();
2615
+ this.pos = save;
2616
+ this.pointerCache.set(target, value);
2617
+ return value;
2618
+ }
2619
+ case "=": {
2620
+ this.pos += 1;
2621
+ const place = this.readPlace();
2622
+ const value = this.evalValue();
2623
+ this.writePlace(place, value);
2624
+ return value;
2625
+ }
2626
+ case "~": {
2627
+ this.pos += 1;
2628
+ const place = this.readPlace();
2629
+ this.deletePlace(place);
2630
+ return;
2631
+ }
2632
+ case ";": {
2633
+ this.pos += 1;
2634
+ const kind = prefix.value % 2 === 0 ? "break" : "continue";
2635
+ const depth = Math.floor(prefix.value / 2) + 1;
2636
+ return { kind, depth };
2637
+ }
2638
+ case "(":
2639
+ return this.evalCall(prefix.value);
2640
+ case "[":
2641
+ return this.evalArray(prefix.value);
2642
+ case "{":
2643
+ return this.evalObject(prefix.value);
2644
+ case "?":
2645
+ case "!":
2646
+ case "|":
2647
+ case "&":
2648
+ return this.evalFlowParen(tag);
2649
+ case ">":
2650
+ case "<":
2651
+ return this.evalLoopLike(tag);
2652
+ case "#":
2653
+ return this.evalWhileLoop();
2654
+ default:
2655
+ throw new Error(`Unexpected tag '${tag}' at ${this.pos}`);
2656
+ }
2657
+ }
2658
+ evalCall(_prefix) {
2659
+ this.ensure("(");
2660
+ this.skipNonCode();
2661
+ if (this.text[this.pos] === ")") {
2662
+ this.pos += 1;
2663
+ return;
2664
+ }
2665
+ const callee = this.evalValue();
2666
+ const args = [];
2667
+ while (true) {
2668
+ this.skipNonCode();
2669
+ if (this.text[this.pos] === ")")
2670
+ break;
2671
+ args.push(this.evalValue());
2672
+ }
2673
+ this.ensure(")");
2674
+ if (typeof callee === "object" && callee && "__opcode" in callee) {
2675
+ return this.applyOpcode(callee.__opcode, args);
2676
+ }
2677
+ return this.navigate(callee, args);
2678
+ }
2679
+ evalArray(_prefix) {
2680
+ this.ensure("[");
2681
+ this.skipNonCode();
2682
+ this.skipIndexHeaderIfPresent();
2683
+ const out = [];
2684
+ while (true) {
2685
+ this.skipNonCode();
2686
+ if (this.text[this.pos] === "]")
2687
+ break;
2688
+ out.push(this.evalValue());
2689
+ }
2690
+ this.ensure("]");
2691
+ return out;
2692
+ }
2693
+ evalObject(_prefix) {
2694
+ this.ensure("{");
2695
+ this.skipNonCode();
2696
+ this.skipIndexHeaderIfPresent();
2697
+ const out = {};
2698
+ while (true) {
2699
+ this.skipNonCode();
2700
+ if (this.text[this.pos] === "}")
2701
+ break;
2702
+ const key = this.evalValue();
2703
+ const value = this.evalValue();
2704
+ out[String(key)] = value;
2705
+ }
2706
+ this.ensure("}");
2707
+ return out;
2708
+ }
2709
+ skipIndexHeaderIfPresent() {
2710
+ const save = this.pos;
2711
+ const countPrefix = this.readPrefix();
2712
+ if (this.text[this.pos] !== "#") {
2713
+ this.pos = save;
2714
+ return;
2715
+ }
2716
+ this.pos += 1;
2717
+ const widthChar = this.text[this.pos];
2718
+ const width = widthChar ? digitMap.get(widthChar) ?? 0 : 0;
2719
+ if (widthChar)
2720
+ this.pos += 1;
2721
+ const skipLen = countPrefix.value * width;
2722
+ this.pos += skipLen;
2723
+ }
2724
+ evalFlowParen(tag) {
2725
+ this.pos += 1;
2726
+ this.ensure("(");
2727
+ if (tag === "?") {
2728
+ const cond = this.evalValue();
2729
+ if (isDefined(cond)) {
2730
+ this.selfStack.push(cond);
2731
+ const thenValue = this.evalValue();
2732
+ this.selfStack.pop();
2733
+ if (this.hasMoreBefore(")"))
2734
+ this.skipValue();
2735
+ this.ensure(")");
2736
+ return thenValue;
2737
+ }
2738
+ this.skipValue();
2739
+ let elseValue = undefined;
2740
+ if (this.hasMoreBefore(")"))
2741
+ elseValue = this.evalValue();
2742
+ this.ensure(")");
2743
+ return elseValue;
2744
+ }
2745
+ if (tag === "!") {
2746
+ const cond = this.evalValue();
2747
+ if (!isDefined(cond)) {
2748
+ const thenValue = this.evalValue();
2749
+ if (this.hasMoreBefore(")"))
2750
+ this.skipValue();
2751
+ this.ensure(")");
2752
+ return thenValue;
2753
+ }
2754
+ this.skipValue();
2755
+ let elseValue = undefined;
2756
+ if (this.hasMoreBefore(")")) {
2757
+ this.selfStack.push(cond);
2758
+ elseValue = this.evalValue();
2759
+ this.selfStack.pop();
2760
+ }
2761
+ this.ensure(")");
2762
+ return elseValue;
2763
+ }
2764
+ if (tag === "|") {
2765
+ let result2 = undefined;
2766
+ while (this.hasMoreBefore(")")) {
2767
+ if (isDefined(result2))
2768
+ this.skipValue();
2769
+ else
2770
+ result2 = this.evalValue();
2771
+ }
2772
+ this.ensure(")");
2773
+ return result2;
2774
+ }
2775
+ let result = undefined;
2776
+ while (this.hasMoreBefore(")")) {
2777
+ if (!isDefined(result) && result !== undefined) {
2778
+ this.skipValue();
2779
+ continue;
2780
+ }
2781
+ result = this.evalValue();
2782
+ if (!isDefined(result)) {
2783
+ while (this.hasMoreBefore(")"))
2784
+ this.skipValue();
2785
+ break;
2786
+ }
2787
+ }
2788
+ this.ensure(")");
2789
+ return result;
2790
+ }
2791
+ evalLoopLike(tag) {
2792
+ this.pos += 1;
2793
+ const open = this.text[this.pos];
2794
+ if (!open || !"([{".includes(open))
2795
+ throw new Error(`Expected loop opener after '${tag}'`);
2796
+ const close = open === "(" ? ")" : open === "[" ? "]" : "}";
2797
+ this.pos += 1;
2798
+ const iterable = this.evalValue();
2799
+ const afterIterable = this.pos;
2800
+ const bodyValueCount = open === "{" ? 2 : 1;
2801
+ let varA;
2802
+ let varB;
2803
+ let bodyStart = afterIterable;
2804
+ let bodyEnd = afterIterable;
2805
+ let matched = false;
2806
+ for (const bindingCount of [2, 1, 0]) {
2807
+ this.pos = afterIterable;
2808
+ const vars = [];
2809
+ let bindingsOk = true;
2810
+ for (let index = 0;index < bindingCount; index += 1) {
2811
+ const binding = this.readBindingVarIfPresent();
2812
+ if (!binding) {
2813
+ bindingsOk = false;
2814
+ break;
2815
+ }
2816
+ vars.push(binding);
2817
+ }
2818
+ if (!bindingsOk)
2819
+ continue;
2820
+ const candidateBodyStart = this.pos;
2821
+ let cursor = candidateBodyStart;
2822
+ let bodyOk = true;
2823
+ for (let index = 0;index < bodyValueCount; index += 1) {
2824
+ const next = this.skipValueFrom(cursor);
2825
+ if (next <= cursor) {
2826
+ bodyOk = false;
2827
+ break;
2828
+ }
2829
+ cursor = next;
2830
+ }
2831
+ if (!bodyOk)
2832
+ continue;
2833
+ this.pos = cursor;
2834
+ this.skipNonCode();
2835
+ if (this.text[this.pos] !== close)
2836
+ continue;
2837
+ varA = vars[0];
2838
+ varB = vars[1];
2839
+ bodyStart = candidateBodyStart;
2840
+ bodyEnd = cursor;
2841
+ matched = true;
2842
+ break;
2843
+ }
2844
+ if (!matched)
2845
+ throw new Error(`Invalid loop/comprehension body before '${close}' at ${this.pos}`);
2846
+ this.pos = bodyEnd;
2847
+ this.ensure(close);
2848
+ if (open === "[")
2849
+ return this.evalArrayComprehension(iterable, varA, varB, bodyStart, bodyEnd, tag === "<");
2850
+ if (open === "{")
2851
+ return this.evalObjectComprehension(iterable, varA, varB, bodyStart, bodyEnd, tag === "<");
2852
+ return this.evalForLoop(iterable, varA, varB, bodyStart, bodyEnd, tag === "<");
2853
+ }
2854
+ iterate(iterable, keysOnly) {
2855
+ if (Array.isArray(iterable)) {
2856
+ if (keysOnly)
2857
+ return iterable.map((_value, index) => ({ key: index, value: index }));
2858
+ return iterable.map((value, index) => ({ key: index, value }));
2859
+ }
2860
+ if (iterable && typeof iterable === "object") {
2861
+ const entries = Object.entries(iterable);
2862
+ if (keysOnly)
2863
+ return entries.map(([key]) => ({ key, value: key }));
2864
+ return entries.map(([key, value]) => ({ key, value }));
2865
+ }
2866
+ if (typeof iterable === "number" && Number.isFinite(iterable) && iterable > 0) {
2867
+ const out = [];
2868
+ for (let index = 0;index < Math.floor(iterable); index += 1) {
2869
+ if (keysOnly)
2870
+ out.push({ key: index, value: index });
2871
+ else
2872
+ out.push({ key: index, value: index + 1 });
2873
+ }
2874
+ return out;
2875
+ }
2876
+ return [];
2877
+ }
2878
+ evalBodySlice(start, end) {
2879
+ const save = this.pos;
2880
+ this.pos = start;
2881
+ const value = this.evalValue();
2882
+ this.pos = end;
2883
+ this.pos = save;
2884
+ return value;
2885
+ }
2886
+ handleLoopControl(value) {
2887
+ if (value && typeof value === "object" && "kind" in value && "depth" in value) {
2888
+ return value;
2889
+ }
2890
+ return;
2891
+ }
2892
+ evalForLoop(iterable, varA, varB, bodyStart, bodyEnd, keysOnly) {
2893
+ const items = this.iterate(iterable, keysOnly);
2894
+ let last = undefined;
2895
+ for (const item of items) {
2896
+ this.tick();
2897
+ const currentSelf = keysOnly ? item.key : item.value;
2898
+ this.selfStack.push(currentSelf);
2899
+ if (varA && varB) {
2900
+ this.state.vars[varA] = item.key;
2901
+ this.state.vars[varB] = keysOnly ? item.key : item.value;
2902
+ } else if (varA) {
2903
+ this.state.vars[varA] = keysOnly ? item.key : item.value;
2904
+ }
2905
+ last = this.evalBodySlice(bodyStart, bodyEnd);
2906
+ this.selfStack.pop();
2907
+ const control = this.handleLoopControl(last);
2908
+ if (!control)
2909
+ continue;
2910
+ if (control.depth > 1)
2911
+ return { kind: control.kind, depth: control.depth - 1 };
2912
+ if (control.kind === "break")
2913
+ return;
2914
+ last = undefined;
2915
+ continue;
2916
+ }
2917
+ return last;
2918
+ }
2919
+ evalWhileLoop() {
2920
+ this.pos += 1;
2921
+ this.ensure("(");
2922
+ const condStart = this.pos;
2923
+ const condValue = this.evalValue();
2924
+ const bodyStart = this.pos;
2925
+ const bodyValueCount = 1;
2926
+ let cursor = bodyStart;
2927
+ for (let index = 0;index < bodyValueCount; index += 1) {
2928
+ cursor = this.skipValueFrom(cursor);
2929
+ }
2930
+ const bodyEnd = cursor;
2931
+ this.pos = bodyEnd;
2932
+ this.ensure(")");
2933
+ const afterClose = this.pos;
2934
+ let last = undefined;
2935
+ let currentCond = condValue;
2936
+ while (isDefined(currentCond)) {
2937
+ this.tick();
2938
+ this.selfStack.push(currentCond);
2939
+ last = this.evalBodySlice(bodyStart, bodyEnd);
2940
+ this.selfStack.pop();
2941
+ const control = this.handleLoopControl(last);
2942
+ if (control) {
2943
+ if (control.depth > 1)
2944
+ return { kind: control.kind, depth: control.depth - 1 };
2945
+ if (control.kind === "break")
2946
+ return;
2947
+ last = undefined;
2948
+ }
2949
+ currentCond = this.evalBodySlice(condStart, bodyStart);
2950
+ }
2951
+ this.pos = afterClose;
2952
+ return last;
2953
+ }
2954
+ evalArrayComprehension(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 value = this.evalBodySlice(bodyStart, bodyEnd);
2968
+ this.selfStack.pop();
2969
+ const control = this.handleLoopControl(value);
2970
+ if (control) {
2971
+ if (control.depth > 1)
2972
+ return { kind: control.kind, depth: control.depth - 1 };
2973
+ if (control.kind === "break")
2974
+ break;
2975
+ continue;
2976
+ }
2977
+ if (isDefined(value))
2978
+ out.push(value);
2979
+ }
2980
+ return out;
2981
+ }
2982
+ evalObjectComprehension(iterable, varA, varB, bodyStart, bodyEnd, keysOnly) {
2983
+ const items = this.iterate(iterable, keysOnly);
2984
+ const out = {};
2985
+ for (const item of items) {
2986
+ this.tick();
2987
+ const currentSelf = keysOnly ? item.key : item.value;
2988
+ this.selfStack.push(currentSelf);
2989
+ if (varA && varB) {
2990
+ this.state.vars[varA] = item.key;
2991
+ this.state.vars[varB] = keysOnly ? item.key : item.value;
2992
+ } else if (varA) {
2993
+ this.state.vars[varA] = keysOnly ? item.key : item.value;
2994
+ }
2995
+ const save = this.pos;
2996
+ this.pos = bodyStart;
2997
+ const key = this.evalValue();
2998
+ const value = this.evalValue();
2999
+ this.pos = save;
3000
+ this.selfStack.pop();
3001
+ const control = this.handleLoopControl(value);
3002
+ if (control) {
3003
+ if (control.depth > 1)
3004
+ return { kind: control.kind, depth: control.depth - 1 };
3005
+ if (control.kind === "break")
3006
+ break;
3007
+ continue;
3008
+ }
3009
+ if (isDefined(value))
3010
+ out[String(key)] = value;
3011
+ }
3012
+ return out;
3013
+ }
3014
+ applyOpcode(id, args) {
3015
+ const custom = this.customOpcodes.get(id);
3016
+ if (custom)
3017
+ return custom(args, this.state);
3018
+ switch (id) {
3019
+ case OPCODES.do:
3020
+ return args.length ? args[args.length - 1] : undefined;
3021
+ case OPCODES.add:
3022
+ if (typeof args[0] === "string" || typeof args[1] === "string") {
3023
+ return String(args[0] ?? "") + String(args[1] ?? "");
3024
+ }
3025
+ return Number(args[0] ?? 0) + Number(args[1] ?? 0);
3026
+ case OPCODES.sub:
3027
+ return Number(args[0] ?? 0) - Number(args[1] ?? 0);
3028
+ case OPCODES.mul:
3029
+ return Number(args[0] ?? 0) * Number(args[1] ?? 0);
3030
+ case OPCODES.div:
3031
+ return Number(args[0] ?? 0) / Number(args[1] ?? 0);
3032
+ case OPCODES.mod:
3033
+ return Number(args[0] ?? 0) % Number(args[1] ?? 0);
3034
+ case OPCODES.neg:
3035
+ return -Number(args[0] ?? 0);
3036
+ case OPCODES.not: {
3037
+ const value = args[0];
3038
+ if (typeof value === "boolean")
3039
+ return !value;
3040
+ return ~Number(value ?? 0);
3041
+ }
3042
+ case OPCODES.and: {
3043
+ const [a, b] = args;
3044
+ if (typeof a === "boolean" || typeof b === "boolean")
3045
+ return Boolean(a) && Boolean(b);
3046
+ return Number(a ?? 0) & Number(b ?? 0);
3047
+ }
3048
+ case OPCODES.or: {
3049
+ const [a, b] = args;
3050
+ if (typeof a === "boolean" || typeof b === "boolean")
3051
+ return Boolean(a) || Boolean(b);
3052
+ return Number(a ?? 0) | Number(b ?? 0);
3053
+ }
3054
+ case OPCODES.xor: {
3055
+ const [a, b] = args;
3056
+ if (typeof a === "boolean" || typeof b === "boolean")
3057
+ return Boolean(a) !== Boolean(b);
3058
+ return Number(a ?? 0) ^ Number(b ?? 0);
3059
+ }
3060
+ case OPCODES.eq:
3061
+ return args[0] === args[1] ? args[0] : undefined;
3062
+ case OPCODES.neq:
3063
+ return args[0] !== args[1] ? args[0] : undefined;
3064
+ case OPCODES.gt:
3065
+ return Number(args[0]) > Number(args[1]) ? args[0] : undefined;
3066
+ case OPCODES.gte:
3067
+ return Number(args[0]) >= Number(args[1]) ? args[0] : undefined;
3068
+ case OPCODES.lt:
3069
+ return Number(args[0]) < Number(args[1]) ? args[0] : undefined;
3070
+ case OPCODES.lte:
3071
+ return Number(args[0]) <= Number(args[1]) ? args[0] : undefined;
3072
+ case OPCODES.boolean:
3073
+ return typeof args[0] === "boolean" ? args[0] : undefined;
3074
+ case OPCODES.number:
3075
+ return typeof args[0] === "number" ? args[0] : undefined;
3076
+ case OPCODES.string:
3077
+ return typeof args[0] === "string" ? args[0] : undefined;
3078
+ case OPCODES.array:
3079
+ return Array.isArray(args[0]) ? args[0] : undefined;
3080
+ case OPCODES.object:
3081
+ return args[0] && typeof args[0] === "object" && !Array.isArray(args[0]) ? args[0] : undefined;
3082
+ default:
3083
+ throw new Error(`Unknown opcode ${id}`);
3084
+ }
3085
+ }
3086
+ navigate(base, keys) {
3087
+ let current = base;
3088
+ for (const key of keys) {
3089
+ if (current === undefined || current === null)
3090
+ return;
3091
+ current = current[String(key)];
3092
+ }
3093
+ return current;
3094
+ }
3095
+ readPlace() {
3096
+ this.skipNonCode();
3097
+ const direct = this.readRootVarOrRefIfPresent();
3098
+ if (direct) {
3099
+ const keys = [];
3100
+ this.skipNonCode();
3101
+ if (this.text[this.pos] === "(") {
3102
+ this.pos += 1;
3103
+ while (true) {
3104
+ this.skipNonCode();
3105
+ if (this.text[this.pos] === ")")
3106
+ break;
3107
+ keys.push(this.evalValue());
3108
+ }
3109
+ this.pos += 1;
3110
+ }
3111
+ return {
3112
+ root: direct.root,
3113
+ keys,
3114
+ isRef: direct.isRef
3115
+ };
3116
+ }
3117
+ if (this.text[this.pos] === "(") {
3118
+ this.pos += 1;
3119
+ this.skipNonCode();
3120
+ const rootFromNav = this.readRootVarOrRefIfPresent();
3121
+ if (!rootFromNav)
3122
+ throw new Error(`Invalid place root at ${this.pos}`);
3123
+ const keys = [];
3124
+ while (true) {
3125
+ this.skipNonCode();
3126
+ if (this.text[this.pos] === ")")
3127
+ break;
3128
+ keys.push(this.evalValue());
3129
+ }
3130
+ this.pos += 1;
3131
+ return {
3132
+ root: rootFromNav.root,
3133
+ keys,
3134
+ isRef: rootFromNav.isRef
3135
+ };
3136
+ }
3137
+ throw new Error(`Invalid place at ${this.pos}`);
3138
+ }
3139
+ readRootVarOrRefIfPresent() {
3140
+ const save = this.pos;
3141
+ const prefix = this.readPrefix();
3142
+ const tag = this.text[this.pos];
3143
+ if (tag !== "$" && tag !== "'") {
3144
+ this.pos = save;
3145
+ return;
3146
+ }
3147
+ this.pos += 1;
3148
+ return {
3149
+ root: tag === "$" ? prefix.raw : prefix.value,
3150
+ isRef: tag === "'"
3151
+ };
3152
+ }
3153
+ writePlace(place, value) {
3154
+ const rootTable = place.isRef ? this.state.refs : this.state.vars;
3155
+ const rootKey = String(place.root);
3156
+ if (place.keys.length === 0) {
3157
+ rootTable[rootKey] = value;
3158
+ return;
3159
+ }
3160
+ let target = rootTable[rootKey];
3161
+ if (!target || typeof target !== "object") {
3162
+ target = {};
3163
+ rootTable[rootKey] = target;
3164
+ }
3165
+ for (let index = 0;index < place.keys.length - 1; index += 1) {
3166
+ const key = String(place.keys[index]);
3167
+ const next = target[key];
3168
+ if (!next || typeof next !== "object")
3169
+ target[key] = {};
3170
+ target = target[key];
3171
+ }
3172
+ target[String(place.keys[place.keys.length - 1])] = value;
3173
+ }
3174
+ deletePlace(place) {
3175
+ const rootTable = place.isRef ? this.state.refs : this.state.vars;
3176
+ const rootKey = String(place.root);
3177
+ if (place.keys.length === 0) {
3178
+ delete rootTable[rootKey];
3179
+ return;
3180
+ }
3181
+ let target = rootTable[rootKey];
3182
+ if (!target || typeof target !== "object")
3183
+ return;
3184
+ for (let index = 0;index < place.keys.length - 1; index += 1) {
3185
+ target = target[String(place.keys[index])];
3186
+ if (!target || typeof target !== "object")
3187
+ return;
3188
+ }
3189
+ delete target[String(place.keys[place.keys.length - 1])];
3190
+ }
3191
+ skipValue() {
3192
+ this.pos = this.skipValueFrom(this.pos);
3193
+ }
3194
+ skipValueFrom(startPos) {
3195
+ const save = this.pos;
3196
+ this.pos = startPos;
3197
+ this.skipNonCode();
3198
+ const prefix = this.readPrefix();
3199
+ const tag = this.text[this.pos];
3200
+ if (!tag) {
3201
+ this.pos = save;
3202
+ return startPos;
3203
+ }
3204
+ if (tag === ",") {
3205
+ this.pos += 1 + prefix.value;
3206
+ const end2 = this.pos;
3207
+ this.pos = save;
3208
+ return end2;
3209
+ }
3210
+ if (tag === "=") {
3211
+ this.pos += 1;
3212
+ this.skipValue();
3213
+ this.skipValue();
3214
+ const end2 = this.pos;
3215
+ this.pos = save;
3216
+ return end2;
3217
+ }
3218
+ if (tag === "~") {
3219
+ this.pos += 1;
3220
+ this.skipValue();
3221
+ const end2 = this.pos;
3222
+ this.pos = save;
3223
+ return end2;
3224
+ }
3225
+ if (tag === "*") {
3226
+ this.pos += 1;
3227
+ this.skipValue();
3228
+ const end2 = this.pos;
3229
+ this.pos = save;
3230
+ return end2;
3231
+ }
3232
+ if ("+:%$@'^;".includes(tag)) {
3233
+ this.pos += 1;
3234
+ const end2 = this.pos;
3235
+ this.pos = save;
3236
+ return end2;
3237
+ }
3238
+ if (tag === "?" || tag === "!" || tag === "|" || tag === "&" || tag === ">" || tag === "<" || tag === "#") {
3239
+ this.pos += 1;
3240
+ }
3241
+ const opener = this.text[this.pos];
3242
+ if (opener && "([{".includes(opener)) {
3243
+ const close = opener === "(" ? ")" : opener === "[" ? "]" : "}";
3244
+ if (prefix.value > 0) {
3245
+ this.pos += 1 + prefix.value + 1;
3246
+ const end3 = this.pos;
3247
+ this.pos = save;
3248
+ return end3;
3249
+ }
3250
+ this.pos += 1;
3251
+ while (true) {
3252
+ this.skipNonCode();
3253
+ if (this.text[this.pos] === close)
3254
+ break;
3255
+ this.skipValue();
3256
+ }
3257
+ this.pos += 1;
3258
+ const end2 = this.pos;
3259
+ this.pos = save;
3260
+ return end2;
3261
+ }
3262
+ this.pos += 1;
3263
+ const end = this.pos;
3264
+ this.pos = save;
3265
+ return end;
3266
+ }
3267
+ }
3268
+ function evaluateRexc(text, ctx = {}) {
3269
+ const interpreter = new CursorInterpreter(text, ctx);
3270
+ const value = interpreter.evaluateTopLevel();
3271
+ return { value, state: interpreter.runtimeState };
3272
+ }
3273
+ function evaluateSource(source, ctx = {}) {
3274
+ return evaluateRexc(compile(source), ctx);
3275
+ }
3276
+ var DIGITS2 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_", digitMap, OPCODES;
3277
+ var init_rexc_interpreter = __esm(() => {
3278
+ init_rex();
3279
+ digitMap = new Map(Array.from(DIGITS2).map((char, index) => [char, index]));
3280
+ OPCODES = {
3281
+ do: 0,
3282
+ add: 1,
3283
+ sub: 2,
3284
+ mul: 3,
3285
+ div: 4,
3286
+ eq: 5,
3287
+ neq: 6,
3288
+ lt: 7,
3289
+ lte: 8,
3290
+ gt: 9,
3291
+ gte: 10,
3292
+ and: 11,
3293
+ or: 12,
3294
+ xor: 13,
3295
+ not: 14,
3296
+ boolean: 15,
3297
+ number: 16,
3298
+ string: 17,
3299
+ array: 18,
3300
+ object: 19,
3301
+ mod: 20,
3302
+ neg: 21
3303
+ };
3304
+ });
3305
+
3306
+ // rex-repl.ts
3307
+ var exports_rex_repl = {};
3308
+ __export(exports_rex_repl, {
3309
+ startRepl: () => startRepl,
3310
+ isIncomplete: () => isIncomplete,
3311
+ highlightRexc: () => highlightRexc,
3312
+ highlightLine: () => highlightLine,
3313
+ formatVarState: () => formatVarState
3314
+ });
3315
+ import * as readline from "node:readline";
3316
+ import { createRequire as createRequire2 } from "node:module";
3317
+ function highlightLine(line) {
3318
+ let result = "";
3319
+ let lastIndex = 0;
3320
+ TOKEN_RE.lastIndex = 0;
3321
+ for (const m of line.matchAll(TOKEN_RE)) {
3322
+ result += line.slice(lastIndex, m.index);
3323
+ const text = m[0];
3324
+ const g = m.groups;
3325
+ if (g.blockComment || g.lineComment) {
3326
+ result += C.gray + text + C.reset;
3327
+ } else if (g.dstring || g.sstring) {
3328
+ result += C.green + text + C.reset;
3329
+ } else if (g.keyword) {
3330
+ result += C.boldBlue + text + C.reset;
3331
+ } else if (g.literal) {
3332
+ result += C.yellow + text + C.reset;
3333
+ } else if (g.typePred) {
3334
+ result += C.cyan + text + C.reset;
3335
+ } else if (g.num) {
3336
+ result += C.cyan + text + C.reset;
3337
+ } else {
3338
+ result += text;
3339
+ }
3340
+ lastIndex = m.index + text.length;
3341
+ }
3342
+ result += line.slice(lastIndex);
3343
+ return result;
3344
+ }
3345
+ function highlightRexc(text) {
3346
+ let out = "";
3347
+ let i = 0;
3348
+ function readPrefix() {
3349
+ const start = i;
3350
+ while (i < text.length && REXC_DIGITS.has(text[i]))
3351
+ i++;
3352
+ return text.slice(start, i);
3353
+ }
3354
+ while (i < text.length) {
3355
+ const ch = text[i];
3356
+ if (ch === " " || ch === "\t" || ch === `
3357
+ ` || ch === "\r") {
3358
+ out += ch;
3359
+ i++;
3360
+ continue;
3361
+ }
3362
+ if (ch === "/" && text[i + 1] === "/") {
3363
+ const start = i;
3364
+ i += 2;
3365
+ while (i < text.length && text[i] !== `
3366
+ `)
3367
+ i++;
3368
+ out += C.gray + text.slice(start, i) + C.reset;
3369
+ continue;
3370
+ }
3371
+ if (ch === "/" && text[i + 1] === "*") {
3372
+ const start = i;
3373
+ i += 2;
3374
+ while (i < text.length && !(text[i] === "*" && text[i + 1] === "/"))
3375
+ i++;
3376
+ if (i < text.length)
3377
+ i += 2;
3378
+ out += C.gray + text.slice(start, i) + C.reset;
3379
+ continue;
3380
+ }
3381
+ const prefix = readPrefix();
3382
+ if (i >= text.length) {
3383
+ out += prefix;
3384
+ break;
3385
+ }
3386
+ const tag = text[i];
3387
+ switch (tag) {
3388
+ case "+":
3389
+ case "*":
3390
+ out += C.cyan + prefix + tag + C.reset;
3391
+ i++;
3392
+ break;
3393
+ case ":":
3394
+ out += C.dim + prefix + tag + C.reset;
3395
+ i++;
3396
+ break;
3397
+ case "%":
3398
+ out += C.boldBlue + prefix + tag + C.reset;
3399
+ i++;
3400
+ break;
3401
+ case "$":
3402
+ out += C.yellow + prefix + tag + C.reset;
3403
+ i++;
3404
+ break;
3405
+ case "@":
3406
+ out += C.yellow + prefix + tag + C.reset;
3407
+ i++;
3408
+ break;
3409
+ case "'":
3410
+ out += C.dim + prefix + tag + C.reset;
3411
+ i++;
3412
+ break;
3413
+ case ",": {
3414
+ i++;
3415
+ let len = 0;
3416
+ for (const ch2 of prefix)
3417
+ len = len * 64 + (REXC_DIGITS.has(ch2) ? "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_".indexOf(ch2) : 0);
3418
+ const content = text.slice(i, i + len);
3419
+ i += len;
3420
+ out += C.green + prefix + "," + content + C.reset;
3421
+ break;
3422
+ }
3423
+ case "=":
3424
+ case "~":
3425
+ out += C.red + prefix + tag + C.reset;
3426
+ i++;
3427
+ break;
3428
+ case "?":
3429
+ case "!":
3430
+ case "|":
3431
+ case "&":
3432
+ case ">":
3433
+ case "<":
3434
+ case "#":
3435
+ out += C.boldBlue + prefix + tag + C.reset;
3436
+ i++;
3437
+ break;
3438
+ case ";":
3439
+ out += C.boldBlue + prefix + tag + C.reset;
3440
+ i++;
3441
+ break;
3442
+ case "^":
3443
+ out += C.dim + prefix + tag + C.reset;
3444
+ i++;
3445
+ break;
3446
+ case "(":
3447
+ case ")":
3448
+ case "[":
3449
+ case "]":
3450
+ case "{":
3451
+ case "}":
3452
+ out += C.dim + prefix + C.reset + tag;
3453
+ i++;
3454
+ break;
3455
+ default:
3456
+ out += prefix + tag;
3457
+ i++;
3458
+ break;
3459
+ }
3460
+ }
3461
+ return out;
3462
+ }
3463
+ function stripStringsAndComments(source) {
3464
+ return source.replace(/\/\*[\s\S]*?\*\/|\/\/[^\n]*|"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/g, (m) => " ".repeat(m.length));
3465
+ }
3466
+ function countWord(text, word) {
3467
+ const re = new RegExp(`\\b${word}(?![a-zA-Z0-9_-])`, "g");
3468
+ return (text.match(re) || []).length;
3469
+ }
3470
+ function isIncomplete(buffer) {
3471
+ try {
3472
+ if (grammar.match(buffer).succeeded())
3473
+ return false;
3474
+ } catch {}
3475
+ const stripped = stripStringsAndComments(buffer);
3476
+ let parens = 0, brackets = 0, braces = 0;
3477
+ for (const ch of stripped) {
3478
+ if (ch === "(")
3479
+ parens++;
3480
+ else if (ch === ")")
3481
+ parens--;
3482
+ else if (ch === "[")
3483
+ brackets++;
3484
+ else if (ch === "]")
3485
+ brackets--;
3486
+ else if (ch === "{")
3487
+ braces++;
3488
+ else if (ch === "}")
3489
+ braces--;
3490
+ }
3491
+ if (parens > 0 || brackets > 0 || braces > 0)
3492
+ return true;
3493
+ const doCount = countWord(stripped, "do");
3494
+ const endCount = countWord(stripped, "end");
3495
+ if (doCount > endCount)
3496
+ return true;
3497
+ const trimmed = buffer.trimEnd();
3498
+ if (/[+\-*/%&|^=<>]$/.test(trimmed))
3499
+ return true;
3500
+ if (/\b(?:and|or|do|in|of)\s*$/.test(trimmed))
3501
+ return true;
3502
+ return false;
3503
+ }
3504
+ function formatResult(value) {
3505
+ let text;
3506
+ try {
3507
+ text = stringify(value, { maxWidth: 60 });
3508
+ } catch {
3509
+ text = String(value);
3510
+ }
3511
+ return `${C.gray}→${C.reset} ${highlightLine(text)}`;
3512
+ }
3513
+ function formatVarState(vars) {
3514
+ const entries = Object.entries(vars);
3515
+ if (entries.length === 0)
3516
+ return "";
3517
+ const MAX_LINE = 70;
3518
+ const MAX_VALUE = 30;
3519
+ const parts = [];
3520
+ let totalLen = 0;
3521
+ for (const [key, val] of entries) {
3522
+ let valStr;
3523
+ try {
3524
+ valStr = stringify(val, { maxWidth: MAX_VALUE });
3525
+ } catch {
3526
+ valStr = String(val);
3527
+ }
3528
+ if (valStr.length > MAX_VALUE) {
3529
+ valStr = valStr.slice(0, MAX_VALUE - 1) + "…";
3530
+ }
3531
+ const part = `${key} = ${valStr}`;
3532
+ if (totalLen + part.length + 2 > MAX_LINE && parts.length > 0) {
3533
+ parts.push("…");
3534
+ break;
3535
+ }
3536
+ parts.push(part);
3537
+ totalLen += part.length + 2;
3538
+ }
3539
+ return `${C.dim} ${parts.join(", ")}${C.reset}`;
3540
+ }
3541
+ function completer(state) {
3542
+ return (line) => {
3543
+ const match = line.match(/[a-zA-Z_][a-zA-Z0-9_.-]*$/);
3544
+ const partial = match ? match[0] : "";
3545
+ if (!partial)
3546
+ return [[], ""];
3547
+ const varNames = Object.keys(state.vars);
3548
+ const all = [...new Set([...KEYWORDS, ...varNames])];
3549
+ const hits = all.filter((w) => w.startsWith(partial));
3550
+ return [hits, partial];
3551
+ };
3552
+ }
3553
+ function handleDotCommand(cmd, state, rl) {
3554
+ function toggleLabel(on) {
3555
+ return on ? `${C.green}on${C.reset}` : `${C.dim}off${C.reset}`;
3556
+ }
3557
+ switch (cmd) {
3558
+ case ".help":
3559
+ console.log([
3560
+ `${C.boldBlue}Rex REPL Commands:${C.reset}`,
3561
+ " .help Show this help message",
3562
+ " .vars Show all current variables",
3563
+ " .clear Clear all variables",
3564
+ " .ir Toggle showing IR JSON after parsing",
3565
+ " .rexc Toggle showing compiled rexc before execution",
3566
+ " .opt Toggle IR optimizations",
3567
+ " .exit Exit the REPL",
3568
+ "",
3569
+ "Enter Rex expressions to evaluate them.",
3570
+ "Multi-line: open brackets or do/end blocks continue on the next line.",
3571
+ "Ctrl-C cancels multi-line input.",
3572
+ "Ctrl-D exits."
3573
+ ].join(`
3574
+ `));
3575
+ return true;
3576
+ case ".ir":
3577
+ state.showIR = !state.showIR;
3578
+ console.log(`${C.dim} IR display: ${toggleLabel(state.showIR)}${C.reset}`);
3579
+ return true;
3580
+ case ".rexc":
3581
+ state.showRexc = !state.showRexc;
3582
+ console.log(`${C.dim} Rexc display: ${toggleLabel(state.showRexc)}${C.reset}`);
3583
+ return true;
3584
+ case ".opt":
3585
+ state.optimize = !state.optimize;
3586
+ console.log(`${C.dim} Optimizations: ${toggleLabel(state.optimize)}${C.reset}`);
3587
+ return true;
3588
+ case ".vars": {
3589
+ const entries = Object.entries(state.vars);
3590
+ if (entries.length === 0) {
3591
+ console.log(`${C.dim} (no variables)${C.reset}`);
3592
+ } else {
3593
+ for (const [key, val] of entries) {
3594
+ let valStr;
3595
+ try {
3596
+ valStr = stringify(val, { maxWidth: 60 });
3597
+ } catch {
3598
+ valStr = String(val);
3599
+ }
3600
+ console.log(` ${key} = ${highlightLine(valStr)}`);
3601
+ }
3602
+ }
3603
+ return true;
3604
+ }
3605
+ case ".clear":
3606
+ state.vars = {};
3607
+ state.refs = {};
3608
+ console.log(`${C.dim} Variables cleared.${C.reset}`);
3609
+ return true;
3610
+ case ".exit":
3611
+ rl.close();
3612
+ return true;
3613
+ default:
3614
+ if (cmd.startsWith(".")) {
3615
+ console.log(`${C.red} Unknown command: ${cmd}. Type .help for available commands.${C.reset}`);
3616
+ return true;
3617
+ }
3618
+ return false;
3619
+ }
3620
+ }
3621
+ async function startRepl() {
3622
+ const state = { vars: {}, refs: {}, showIR: false, showRexc: false, optimize: false };
3623
+ let multiLineBuffer = "";
3624
+ const PRIMARY_PROMPT = "rex> ";
3625
+ const CONT_PROMPT = "... ";
3626
+ const STYLED_PRIMARY = `${C.boldBlue}rex${C.reset}> `;
3627
+ const STYLED_CONT = `${C.dim}...${C.reset} `;
3628
+ let currentPrompt = PRIMARY_PROMPT;
3629
+ let styledPrompt = STYLED_PRIMARY;
3630
+ console.log(`${C.boldBlue}Rex${C.reset} v${version} — type ${C.dim}.help${C.reset} for commands`);
3631
+ const rl = readline.createInterface({
3632
+ input: process.stdin,
3633
+ output: process.stdout,
3634
+ prompt: PRIMARY_PROMPT,
3635
+ historySize: 500,
3636
+ completer: completer(state),
3637
+ terminal: true
3638
+ });
3639
+ process.stdin.on("keypress", () => {
3640
+ process.nextTick(() => {
3641
+ if (!rl.line && rl.line !== "")
3642
+ return;
3643
+ readline.clearLine(process.stdout, 0);
3644
+ readline.cursorTo(process.stdout, 0);
3645
+ process.stdout.write(styledPrompt + highlightLine(rl.line));
3646
+ readline.cursorTo(process.stdout, currentPrompt.length + rl.cursor);
3647
+ });
3648
+ });
3649
+ process.stdin.on("keypress", (_ch, key) => {
3650
+ if (key?.ctrl && key.name === "l") {
3651
+ readline.cursorTo(process.stdout, 0, 0);
3652
+ readline.clearScreenDown(process.stdout);
3653
+ rl.prompt();
3654
+ }
3655
+ });
3656
+ rl.on("SIGINT", () => {
3657
+ if (multiLineBuffer) {
3658
+ multiLineBuffer = "";
3659
+ currentPrompt = PRIMARY_PROMPT;
3660
+ styledPrompt = STYLED_PRIMARY;
3661
+ rl.setPrompt(PRIMARY_PROMPT);
3662
+ process.stdout.write(`
3663
+ `);
3664
+ rl.prompt();
3665
+ } else {
3666
+ console.log();
3667
+ rl.close();
3668
+ }
3669
+ });
3670
+ function resetPrompt() {
3671
+ currentPrompt = PRIMARY_PROMPT;
3672
+ styledPrompt = STYLED_PRIMARY;
3673
+ rl.setPrompt(PRIMARY_PROMPT);
3674
+ rl.prompt();
3675
+ }
3676
+ rl.on("line", (line) => {
3677
+ const trimmed = line.trim();
3678
+ if (!multiLineBuffer && trimmed.startsWith(".")) {
3679
+ if (handleDotCommand(trimmed, state, rl)) {
3680
+ rl.prompt();
3681
+ return;
3682
+ }
3683
+ }
3684
+ multiLineBuffer += (multiLineBuffer ? `
3685
+ ` : "") + line;
3686
+ if (multiLineBuffer.trim() === "") {
3687
+ multiLineBuffer = "";
3688
+ rl.prompt();
3689
+ return;
3690
+ }
3691
+ if (isIncomplete(multiLineBuffer)) {
3692
+ currentPrompt = CONT_PROMPT;
3693
+ styledPrompt = STYLED_CONT;
3694
+ rl.setPrompt(CONT_PROMPT);
3695
+ rl.prompt();
3696
+ return;
3697
+ }
3698
+ const source = multiLineBuffer;
3699
+ multiLineBuffer = "";
3700
+ const match = grammar.match(source);
3701
+ if (!match.succeeded()) {
3702
+ console.log(`${C.red} ${match.message}${C.reset}`);
3703
+ resetPrompt();
3704
+ return;
3705
+ }
3706
+ try {
3707
+ const ir = parseToIR(source);
3708
+ const lowered = state.optimize ? optimizeIR(ir) : ir;
3709
+ if (state.showIR) {
3710
+ console.log(`${C.dim} IR: ${JSON.stringify(lowered)}${C.reset}`);
3711
+ }
3712
+ const rexc = compile(source, { optimize: state.optimize });
3713
+ if (state.showRexc) {
3714
+ console.log(`${C.dim} rexc:${C.reset} ${highlightRexc(rexc)}`);
3715
+ }
3716
+ const result = evaluateRexc(rexc, {
3717
+ vars: { ...state.vars },
3718
+ refs: { ...state.refs },
3719
+ gasLimit: GAS_LIMIT
3720
+ });
3721
+ state.vars = result.state.vars;
3722
+ state.refs = result.state.refs;
3723
+ console.log(formatResult(result.value));
3724
+ const varLine = formatVarState(state.vars);
3725
+ if (varLine)
3726
+ console.log(varLine);
3727
+ } catch (error) {
3728
+ const message = error instanceof Error ? error.message : String(error);
3729
+ if (message.includes("Gas limit exceeded")) {
3730
+ console.log(`${C.yellow} ${message}${C.reset}`);
3731
+ } else {
3732
+ console.log(`${C.red} Error: ${message}${C.reset}`);
3733
+ }
3734
+ }
3735
+ resetPrompt();
3736
+ });
3737
+ rl.on("close", () => {
3738
+ process.exit(0);
3739
+ });
3740
+ rl.prompt();
3741
+ }
3742
+ var req, version, C, TOKEN_RE, REXC_DIGITS, KEYWORDS, GAS_LIMIT = 1e7;
3743
+ var init_rex_repl = __esm(() => {
3744
+ init_rex();
3745
+ init_rexc_interpreter();
3746
+ req = createRequire2(import.meta.url);
3747
+ ({ version } = req("./package.json"));
3748
+ C = {
3749
+ reset: "\x1B[0m",
3750
+ bold: "\x1B[1m",
3751
+ dim: "\x1B[2m",
3752
+ red: "\x1B[31m",
3753
+ green: "\x1B[32m",
3754
+ yellow: "\x1B[33m",
3755
+ blue: "\x1B[34m",
3756
+ cyan: "\x1B[36m",
3757
+ gray: "\x1B[90m",
3758
+ boldBlue: "\x1B[1;34m"
3759
+ };
3760
+ 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;
3761
+ REXC_DIGITS = new Set("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_");
3762
+ KEYWORDS = [
3763
+ "when",
3764
+ "unless",
3765
+ "while",
3766
+ "for",
3767
+ "do",
3768
+ "end",
3769
+ "in",
3770
+ "of",
3771
+ "and",
3772
+ "or",
3773
+ "else",
3774
+ "break",
3775
+ "continue",
3776
+ "delete",
3777
+ "self",
3778
+ "true",
3779
+ "false",
3780
+ "null",
3781
+ "undefined",
3782
+ "string",
3783
+ "number",
3784
+ "object",
3785
+ "array",
3786
+ "boolean"
3787
+ ];
3788
+ });
3789
+
3790
+ // rex-cli.ts
3791
+ init_rex();
3792
+ init_rexc_interpreter();
3793
+ import { dirname, resolve } from "node:path";
3794
+ import { readFile, writeFile } from "node:fs/promises";
3795
+ function parseArgs(argv) {
3796
+ const options = {
3797
+ compile: false,
3798
+ ir: false,
3799
+ minifyNames: false,
3800
+ dedupeValues: false,
3801
+ help: false
3802
+ };
3803
+ for (let index = 0;index < argv.length; index += 1) {
3804
+ const arg = argv[index];
3805
+ if (!arg)
3806
+ continue;
3807
+ if (arg === "--help" || arg === "-h") {
3808
+ options.help = true;
3809
+ continue;
3810
+ }
3811
+ if (arg === "--compile" || arg === "-c") {
3812
+ options.compile = true;
3813
+ continue;
3814
+ }
3815
+ if (arg === "--ir") {
3816
+ options.ir = true;
3817
+ continue;
3818
+ }
3819
+ if (arg === "--minify-names" || arg === "-m") {
3820
+ options.minifyNames = true;
3821
+ continue;
3822
+ }
3823
+ if (arg === "--dedupe-values") {
3824
+ options.dedupeValues = true;
3825
+ continue;
3826
+ }
3827
+ if (arg === "--dedupe-min-bytes") {
3828
+ const value = argv[index + 1];
3829
+ if (!value)
3830
+ throw new Error("Missing value for --dedupe-min-bytes");
3831
+ const parsed = Number(value);
3832
+ if (!Number.isInteger(parsed) || parsed < 1)
3833
+ throw new Error("--dedupe-min-bytes must be a positive integer");
3834
+ options.dedupeMinBytes = parsed;
3835
+ index += 1;
3836
+ continue;
3837
+ }
3838
+ if (arg === "--expr" || arg === "-e") {
3839
+ const value = argv[index + 1];
3840
+ if (!value)
3841
+ throw new Error("Missing value for --expr");
3842
+ options.expr = value;
3843
+ index += 1;
3844
+ continue;
3845
+ }
3846
+ if (arg === "--file" || arg === "-f") {
3847
+ const value = argv[index + 1];
3848
+ if (!value)
3849
+ throw new Error("Missing value for --file");
3850
+ options.file = value;
3851
+ index += 1;
3852
+ continue;
3853
+ }
3854
+ if (arg === "--out" || arg === "-o") {
3855
+ const value = argv[index + 1];
3856
+ if (!value)
3857
+ throw new Error("Missing value for --out");
3858
+ options.out = value;
3859
+ index += 1;
3860
+ continue;
3861
+ }
3862
+ if (!arg.startsWith("-")) {
3863
+ if (options.file)
3864
+ throw new Error("Multiple file arguments provided");
3865
+ options.file = arg;
3866
+ continue;
3867
+ }
3868
+ throw new Error(`Unknown option: ${arg}`);
3869
+ }
3870
+ return options;
3871
+ }
3872
+ function usage() {
3873
+ return [
3874
+ "Rex expression language CLI.",
3875
+ "",
3876
+ "Usage:",
3877
+ " rex Start interactive REPL",
3878
+ " rex input.rex Evaluate a Rex script (JSON output)",
3879
+ " rex --expr '1 + 2' Evaluate an inline expression",
3880
+ " cat input.rex | rex Evaluate from stdin",
3881
+ " rex -c input.rex Compile to rexc bytecode",
3882
+ "",
3883
+ "Input:",
3884
+ " <file> Evaluate/compile a Rex source file",
3885
+ " -e, --expr <source> Evaluate/compile an inline expression",
3886
+ " -f, --file <path> Evaluate/compile source from a file",
3887
+ "",
3888
+ "Output mode:",
3889
+ " (default) Evaluate and output result as JSON",
3890
+ " -c, --compile Compile to rexc bytecode",
3891
+ " --ir Output lowered IR as JSON",
3892
+ "",
3893
+ "Compile options:",
3894
+ " -m, --minify-names Minify local variable names",
3895
+ " --dedupe-values Deduplicate large repeated values",
3896
+ " --dedupe-min-bytes <n> Minimum bytes for dedupe (default: 4)",
3897
+ "",
3898
+ "General:",
3899
+ " -o, --out <path> Write output to file instead of stdout",
3900
+ " -h, --help Show this message"
3901
+ ].join(`
3902
+ `);
3903
+ }
3904
+ async function readStdin() {
3905
+ const chunks = [];
3906
+ for await (const chunk of process.stdin) {
3907
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
3908
+ }
3909
+ return Buffer.concat(chunks).toString("utf8");
3910
+ }
3911
+ async function resolveSource(options) {
3912
+ if (options.expr && options.file)
3913
+ throw new Error("Use only one of --expr, --file, or a positional file path");
3914
+ if (options.expr)
3915
+ return options.expr;
3916
+ if (options.file)
3917
+ return readFile(options.file, "utf8");
3918
+ if (!process.stdin.isTTY) {
3919
+ const piped = await readStdin();
3920
+ if (piped.trim().length > 0)
3921
+ return piped;
3922
+ }
3923
+ throw new Error("No input provided. Use a file path, --expr, or pipe source via stdin.");
3924
+ }
3925
+ async function loadDomainConfigFromFolder(folderPath) {
3926
+ const configPath = resolve(folderPath, ".config.rex");
3927
+ try {
3928
+ return parse(await readFile(configPath, "utf8"));
3929
+ } catch (error) {
3930
+ if (error.code === "ENOENT")
3931
+ return;
3932
+ throw error;
3933
+ }
3934
+ }
3935
+ async function resolveDomainConfig(options) {
3936
+ const baseFolder = options.file ? dirname(resolve(options.file)) : process.cwd();
3937
+ return loadDomainConfigFromFolder(baseFolder);
3938
+ }
3939
+ async function main() {
3940
+ const options = parseArgs(process.argv.slice(2));
3941
+ if (options.help) {
3942
+ console.log(usage());
3943
+ return;
3944
+ }
3945
+ const hasSource = options.expr || options.file || !process.stdin.isTTY;
3946
+ if (!hasSource && !options.compile && !options.ir) {
3947
+ const { startRepl: startRepl2 } = await Promise.resolve().then(() => (init_rex_repl(), exports_rex_repl));
3948
+ await startRepl2();
3949
+ return;
3950
+ }
3951
+ const source = await resolveSource(options);
3952
+ let output;
3953
+ if (options.ir) {
3954
+ output = JSON.stringify(parseToIR(source), null, 2);
3955
+ } else if (options.compile) {
3956
+ const domainConfig = await resolveDomainConfig(options);
3957
+ output = compile(source, {
3958
+ minifyNames: options.minifyNames,
3959
+ dedupeValues: options.dedupeValues,
3960
+ dedupeMinBytes: options.dedupeMinBytes,
3961
+ domainConfig
3962
+ });
3963
+ } else {
3964
+ const { value } = evaluateSource(source);
3965
+ output = JSON.stringify(value, null, 2);
3966
+ }
3967
+ if (options.out) {
3968
+ await writeFile(options.out, `${output}
3969
+ `, "utf8");
3970
+ return;
3971
+ }
3972
+ console.log(output);
3973
+ }
3974
+ await main().catch((error) => {
3975
+ const message = error instanceof Error ? error.message : String(error);
3976
+ console.error(`rex: ${message}`);
3977
+ process.exit(1);
3978
+ });