@creationix/rex 0.3.1 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/rex-cli.js CHANGED
@@ -115,11 +115,11 @@ function encodeDecimal(significand, power) {
115
115
  function encodeNumberNode(node) {
116
116
  const numberValue = node.value;
117
117
  if (Number.isNaN(numberValue))
118
- return "5'";
118
+ return "nan'";
119
119
  if (numberValue === Infinity)
120
- return "6'";
120
+ return "inf'";
121
121
  if (numberValue === -Infinity)
122
- return "7'";
122
+ return "nif'";
123
123
  if (Number.isInteger(numberValue)) {
124
124
  const { base, exp } = splitDecimal(numberValue);
125
125
  if (exp >= 0 && exp <= 4)
@@ -151,7 +151,7 @@ function encodeNumberNode(node) {
151
151
  return encodeDecimal(significand, power);
152
152
  }
153
153
  function encodeOpcode(opcode) {
154
- return `${encodeUint(OPCODE_IDS[opcode])}%`;
154
+ return `${OPCODE_IDS[opcode]}%`;
155
155
  }
156
156
  function encodeCallParts(parts) {
157
157
  return `(${parts.join("")})`;
@@ -168,7 +168,7 @@ function addOptionalPrefix(encoded) {
168
168
  let payload = encoded;
169
169
  if (encoded.startsWith("?(") || encoded.startsWith("!(") || encoded.startsWith("|(") || encoded.startsWith("&(") || encoded.startsWith(">(") || encoded.startsWith("<(") || encoded.startsWith("#(")) {
170
170
  payload = encoded.slice(2, -1);
171
- } else if (encoded.startsWith(">[") || encoded.startsWith(">{")) {
171
+ } else if (encoded.startsWith(">[") || encoded.startsWith(">{") || encoded.startsWith("<[") || encoded.startsWith("<{") || encoded.startsWith("#[") || encoded.startsWith("#{")) {
172
172
  payload = encoded.slice(2, -1);
173
173
  } else if (encoded.startsWith("[") || encoded.startsWith("{") || encoded.startsWith("(")) {
174
174
  payload = encoded.slice(1, -1);
@@ -179,7 +179,7 @@ function addOptionalPrefix(encoded) {
179
179
  }
180
180
  function encodeBlockExpression(block) {
181
181
  if (block.length === 0)
182
- return "4'";
182
+ return "un'";
183
183
  if (block.length === 1)
184
184
  return encodeNode(block[0]);
185
185
  return encodeCallParts([encodeOpcode("do"), ...block.map((node) => encodeNode(node))]);
@@ -196,9 +196,13 @@ function encodeConditionalElse(elseBranch) {
196
196
  };
197
197
  return encodeNode(nested);
198
198
  }
199
+ function encodeDomainLookup(shortCode, tag) {
200
+ return `${shortCode}${tag}`;
201
+ }
199
202
  function encodeNavigation(node) {
200
203
  const domainRefs = activeEncodeOptions?.domainRefs;
201
- if (domainRefs && node.target.type === "identifier") {
204
+ const domainOpcodes = activeEncodeOptions?.domainOpcodes;
205
+ if ((domainRefs || domainOpcodes) && node.target.type === "identifier") {
202
206
  const staticPath = [node.target.name];
203
207
  for (const segment of node.segments) {
204
208
  if (segment.type !== "static")
@@ -207,14 +211,17 @@ function encodeNavigation(node) {
207
211
  }
208
212
  for (let pathLength = staticPath.length;pathLength >= 1; pathLength -= 1) {
209
213
  const dottedName = staticPath.slice(0, pathLength).join(".");
210
- const domainRef = domainRefs[dottedName];
211
- if (domainRef === undefined)
214
+ const refCode = domainRefs?.[dottedName];
215
+ const opcodeCode = domainOpcodes?.[dottedName];
216
+ const shortCode = refCode ?? opcodeCode;
217
+ if (shortCode === undefined)
212
218
  continue;
219
+ const tag = refCode !== undefined ? "'" : "%";
213
220
  const consumedStaticSegments = pathLength - 1;
214
221
  if (consumedStaticSegments === node.segments.length) {
215
- return `${encodeUint(domainRef)}'`;
222
+ return encodeDomainLookup(shortCode, tag);
216
223
  }
217
- const parts2 = [`${encodeUint(domainRef)}'`];
224
+ const parts2 = [encodeDomainLookup(shortCode, tag)];
218
225
  for (const segment of node.segments.slice(consumedStaticSegments)) {
219
226
  if (segment.type === "static")
220
227
  parts2.push(encodeBareOrLengthString(segment.key));
@@ -240,9 +247,12 @@ function encodeWhile(node) {
240
247
  }
241
248
  function encodeFor(node) {
242
249
  const body = addOptionalPrefix(encodeBlockExpression(node.body));
243
- if (node.binding.type === "binding:expr") {
250
+ if (node.binding.type === "binding:bareIn") {
244
251
  return `>(${encodeNode(node.binding.source)}${body})`;
245
252
  }
253
+ if (node.binding.type === "binding:bareOf") {
254
+ return `<(${encodeNode(node.binding.source)}${body})`;
255
+ }
246
256
  if (node.binding.type === "binding:valueIn") {
247
257
  return `>(${encodeNode(node.binding.source)}${node.binding.value}$${body})`;
248
258
  }
@@ -253,30 +263,47 @@ function encodeFor(node) {
253
263
  }
254
264
  function encodeArrayComprehension(node) {
255
265
  const body = addOptionalPrefix(encodeNode(node.body));
256
- if (node.binding.type === "binding:expr") {
266
+ if (node.binding.type === "binding:bareIn") {
257
267
  return `>[${encodeNode(node.binding.source)}${body}]`;
258
268
  }
269
+ if (node.binding.type === "binding:bareOf") {
270
+ return `<[${encodeNode(node.binding.source)}${body}]`;
271
+ }
259
272
  if (node.binding.type === "binding:valueIn") {
260
273
  return `>[${encodeNode(node.binding.source)}${node.binding.value}$${body}]`;
261
274
  }
262
275
  if (node.binding.type === "binding:keyValueIn") {
263
276
  return `>[${encodeNode(node.binding.source)}${node.binding.key}$${node.binding.value}$${body}]`;
264
277
  }
265
- return `>[${encodeNode(node.binding.source)}${node.binding.key}$${body}]`;
278
+ return `<[${encodeNode(node.binding.source)}${node.binding.key}$${body}]`;
266
279
  }
267
280
  function encodeObjectComprehension(node) {
268
281
  const key = addOptionalPrefix(encodeNode(node.key));
269
282
  const value = addOptionalPrefix(encodeNode(node.value));
270
- if (node.binding.type === "binding:expr") {
283
+ if (node.binding.type === "binding:bareIn") {
271
284
  return `>{${encodeNode(node.binding.source)}${key}${value}}`;
272
285
  }
286
+ if (node.binding.type === "binding:bareOf") {
287
+ return `<{${encodeNode(node.binding.source)}${key}${value}}`;
288
+ }
273
289
  if (node.binding.type === "binding:valueIn") {
274
290
  return `>{${encodeNode(node.binding.source)}${node.binding.value}$${key}${value}}`;
275
291
  }
276
292
  if (node.binding.type === "binding:keyValueIn") {
277
293
  return `>{${encodeNode(node.binding.source)}${node.binding.key}$${node.binding.value}$${key}${value}}`;
278
294
  }
279
- return `>{${encodeNode(node.binding.source)}${node.binding.key}$${key}${value}}`;
295
+ return `<{${encodeNode(node.binding.source)}${node.binding.key}$${key}${value}}`;
296
+ }
297
+ function encodeWhileArrayComprehension(node) {
298
+ const cond = encodeNode(node.condition);
299
+ const body = addOptionalPrefix(encodeNode(node.body));
300
+ return `#[${cond}${body}]`;
301
+ }
302
+ function encodeWhileObjectComprehension(node) {
303
+ const cond = encodeNode(node.condition);
304
+ const key = addOptionalPrefix(encodeNode(node.key));
305
+ const value = addOptionalPrefix(encodeNode(node.value));
306
+ return `#{${cond}${key}${value}}`;
280
307
  }
281
308
  function encodeNode(node) {
282
309
  switch (node.type) {
@@ -285,7 +312,10 @@ function encodeNode(node) {
285
312
  case "identifier": {
286
313
  const domainRef = activeEncodeOptions?.domainRefs?.[node.name];
287
314
  if (domainRef !== undefined)
288
- return `${encodeUint(domainRef)}'`;
315
+ return `${domainRef}'`;
316
+ const domainOpcode = activeEncodeOptions?.domainOpcodes?.[node.name];
317
+ if (domainOpcode !== undefined)
318
+ return `${domainOpcode}%`;
289
319
  return `${node.name}$`;
290
320
  }
291
321
  case "self":
@@ -298,11 +328,11 @@ function encodeNode(node) {
298
328
  return `${encodeUint(node.depth - 1)}@`;
299
329
  }
300
330
  case "boolean":
301
- return node.value ? "1'" : "2'";
331
+ return node.value ? "tr'" : "fl'";
302
332
  case "null":
303
- return "3'";
333
+ return "nl'";
304
334
  case "undefined":
305
- return "4'";
335
+ return "un'";
306
336
  case "number":
307
337
  return encodeNumberNode(node);
308
338
  case "string":
@@ -313,12 +343,16 @@ function encodeNode(node) {
313
343
  }
314
344
  case "arrayComprehension":
315
345
  return encodeArrayComprehension(node);
346
+ case "whileArrayComprehension":
347
+ return encodeWhileArrayComprehension(node);
316
348
  case "object": {
317
349
  const body = node.entries.map(({ key, value }) => `${encodeNode(key)}${addOptionalPrefix(encodeNode(value))}`).join("");
318
350
  return `{${body}}`;
319
351
  }
320
352
  case "objectComprehension":
321
353
  return encodeObjectComprehension(node);
354
+ case "whileObjectComprehension":
355
+ return encodeWhileObjectComprehension(node);
322
356
  case "key":
323
357
  return encodeBareOrLengthString(node.name);
324
358
  case "group":
@@ -328,6 +362,10 @@ function encodeNode(node) {
328
362
  return `~${encodeNode(node.value)}`;
329
363
  if (node.op === "neg")
330
364
  return encodeCallParts([encodeOpcode("neg"), encodeNode(node.value)]);
365
+ if (node.op === "logicalNot") {
366
+ const val = encodeNode(node.value);
367
+ return `!(${val}tr')`;
368
+ }
331
369
  return encodeCallParts([encodeOpcode("not"), encodeNode(node.value)]);
332
370
  case "binary":
333
371
  if (node.op === "and") {
@@ -346,12 +384,19 @@ function encodeNode(node) {
346
384
  }).join("");
347
385
  return `|(${body})`;
348
386
  }
387
+ if (node.op === "nor") {
388
+ const left = encodeNode(node.left);
389
+ const right = addOptionalPrefix(encodeNode(node.right));
390
+ return `!(${left}${right})`;
391
+ }
349
392
  return encodeCallParts([
350
393
  encodeOpcode(BINARY_TO_OPCODE[node.op]),
351
394
  encodeNode(node.left),
352
395
  encodeNode(node.right)
353
396
  ]);
354
397
  case "assign": {
398
+ if (node.op === ":=")
399
+ return `/${encodeNode(node.place)}${addOptionalPrefix(encodeNode(node.value))}`;
355
400
  if (node.op === "=")
356
401
  return `=${encodeNode(node.place)}${addOptionalPrefix(encodeNode(node.value))}`;
357
402
  const opcode = ASSIGN_COMPOUND_TO_OPCODE[node.op];
@@ -362,8 +407,12 @@ function encodeNode(node) {
362
407
  }
363
408
  case "navigation":
364
409
  return encodeNavigation(node);
365
- case "call":
410
+ case "call": {
411
+ if (node.callee.type === "identifier" && KEYWORD_OPCODES.has(node.callee.name)) {
412
+ return encodeCallParts([encodeOpcode(node.callee.name), ...node.args.map((arg) => encodeNode(arg))]);
413
+ }
366
414
  return encodeCallParts([encodeNode(node.callee), ...node.args.map((arg) => encodeNode(arg))]);
415
+ }
367
416
  case "conditional": {
368
417
  const opener = node.head === "when" ? "?(" : "!(";
369
418
  const cond = encodeNode(node.condition);
@@ -371,6 +420,8 @@ function encodeNode(node) {
371
420
  const elseExpr = node.elseBranch ? addOptionalPrefix(encodeConditionalElse(node.elseBranch)) : "";
372
421
  return `${opener}${cond}${thenExpr}${elseExpr})`;
373
422
  }
423
+ case "range":
424
+ return encodeCallParts([encodeOpcode("range"), encodeNode(node.from), encodeNode(node.to)]);
374
425
  case "for":
375
426
  return encodeFor(node);
376
427
  case "while":
@@ -546,78 +597,55 @@ function domainRefsFromConfig(config) {
546
597
  if (!config || typeof config !== "object" || Array.isArray(config)) {
547
598
  throw new Error("Domain config must be an object");
548
599
  }
549
- const refs = {};
550
- for (const section of Object.values(config)) {
551
- if (!section || typeof section !== "object" || Array.isArray(section))
552
- continue;
553
- mapConfigEntries(section, refs);
600
+ const configObj = config;
601
+ const domainRefs = {};
602
+ const domainOpcodes = {};
603
+ const dataSection = configObj.data;
604
+ if (dataSection && typeof dataSection === "object" && !Array.isArray(dataSection)) {
605
+ mapConfigEntries(dataSection, domainRefs);
554
606
  }
555
- return refs;
556
- }
557
- function decodeDomainRefKey(refText) {
558
- if (!refText)
559
- throw new Error("Domain ref key cannot be empty");
560
- if (!/^[0-9A-Za-z_-]+$/.test(refText)) {
561
- throw new Error(`Invalid domain ref key '${refText}' (must use base64 alphabet 0-9a-zA-Z-_)`);
562
- }
563
- if (refText.length > 1 && refText[0] === "0") {
564
- throw new Error(`Invalid domain ref key '${refText}' (leading zeroes are not allowed)`);
565
- }
566
- if (/^[1-9]$/.test(refText)) {
567
- throw new Error(`Invalid domain ref key '${refText}' (reserved by core language)`);
568
- }
569
- let value = 0;
570
- for (const char of refText) {
571
- const digit = DOMAIN_DIGIT_INDEX.get(char);
572
- if (digit === undefined)
573
- throw new Error(`Invalid domain ref key '${refText}'`);
574
- value = value * 64 + digit;
575
- if (value > Number.MAX_SAFE_INTEGER) {
576
- throw new Error(`Invalid domain ref key '${refText}' (must fit in 53 bits)`);
577
- }
578
- }
579
- if (value < FIRST_NON_RESERVED_REF) {
580
- throw new Error(`Invalid domain ref key '${refText}' (maps to reserved id ${value})`);
607
+ const functionsSection = configObj.functions;
608
+ if (functionsSection && typeof functionsSection === "object" && !Array.isArray(functionsSection)) {
609
+ mapConfigEntries(functionsSection, domainOpcodes);
581
610
  }
582
- return value;
611
+ return { domainRefs, domainOpcodes };
583
612
  }
584
613
  function mapConfigEntries(entries, refs) {
585
614
  const sourceKindByRoot = new Map;
586
615
  for (const root of Object.keys(refs)) {
587
616
  sourceKindByRoot.set(root, "explicit");
588
617
  }
589
- for (const [refText, rawEntry] of Object.entries(entries)) {
618
+ for (const [shortCode, rawEntry] of Object.entries(entries)) {
590
619
  const entry = rawEntry;
591
620
  if (!entry || typeof entry !== "object")
592
621
  continue;
593
622
  if (!Array.isArray(entry.names))
594
623
  continue;
595
- const refId = decodeDomainRefKey(refText);
596
624
  for (const rawName of entry.names) {
597
625
  if (typeof rawName !== "string")
598
626
  continue;
599
- const existingNameRef = refs[rawName];
600
- if (existingNameRef !== undefined && existingNameRef !== refId) {
601
- throw new Error(`Conflicting refs for '${rawName}': ${existingNameRef} vs ${refId}`);
627
+ const existingRef = refs[rawName];
628
+ if (existingRef !== undefined && existingRef !== shortCode) {
629
+ throw new Error(`Conflicting refs for '${rawName}': ${existingRef} vs ${shortCode}`);
602
630
  }
603
- refs[rawName] = refId;
631
+ refs[rawName] = shortCode;
604
632
  const root = rawName.split(".")[0];
605
633
  if (!root)
606
634
  continue;
607
635
  const currentKind = rawName.includes(".") ? "implicit" : "explicit";
608
636
  const existing = refs[root];
609
637
  if (existing !== undefined) {
610
- if (existing === refId)
638
+ if (existing === shortCode)
611
639
  continue;
612
640
  const existingKind = sourceKindByRoot.get(root) ?? "explicit";
613
641
  if (currentKind === "explicit") {
614
- throw new Error(`Conflicting refs for '${root}': ${existing} vs ${refId}`);
642
+ throw new Error(`Conflicting refs for '${root}': ${existing} vs ${shortCode}`);
615
643
  }
616
644
  if (existingKind === "explicit")
617
645
  continue;
618
646
  continue;
619
647
  }
620
- refs[root] = refId;
648
+ refs[root] = shortCode;
621
649
  sourceKindByRoot.set(root, currentKind);
622
650
  }
623
651
  }
@@ -899,6 +927,21 @@ function dropBindingNames(env, binding) {
899
927
  clearBinding(env, binding.key);
900
928
  }
901
929
  }
930
+ function optimizeBinding(binding, sourceEnv, currentDepth) {
931
+ const source = optimizeNode(binding.source, sourceEnv, currentDepth);
932
+ switch (binding.type) {
933
+ case "binding:bareIn":
934
+ return { type: "binding:bareIn", source };
935
+ case "binding:bareOf":
936
+ return { type: "binding:bareOf", source };
937
+ case "binding:valueIn":
938
+ return { type: "binding:valueIn", value: binding.value, source };
939
+ case "binding:keyValueIn":
940
+ return { type: "binding:keyValueIn", key: binding.key, value: binding.value, source };
941
+ case "binding:keyOf":
942
+ return { type: "binding:keyOf", key: binding.key, source };
943
+ }
944
+ }
902
945
  function collectReads(node, out) {
903
946
  switch (node.type) {
904
947
  case "identifier":
@@ -921,11 +964,20 @@ function collectReads(node, out) {
921
964
  collectReads(node.binding.source, out);
922
965
  collectReads(node.body, out);
923
966
  return;
967
+ case "whileArrayComprehension":
968
+ collectReads(node.condition, out);
969
+ collectReads(node.body, out);
970
+ return;
924
971
  case "objectComprehension":
925
972
  collectReads(node.binding.source, out);
926
973
  collectReads(node.key, out);
927
974
  collectReads(node.value, out);
928
975
  return;
976
+ case "whileObjectComprehension":
977
+ collectReads(node.condition, out);
978
+ collectReads(node.key, out);
979
+ collectReads(node.value, out);
980
+ return;
929
981
  case "unary":
930
982
  collectReads(node.value, out);
931
983
  return;
@@ -962,6 +1014,10 @@ function collectReads(node, out) {
962
1014
  for (const part of node.body)
963
1015
  collectReads(part, out);
964
1016
  return;
1017
+ case "range":
1018
+ collectReads(node.from, out);
1019
+ collectReads(node.to, out);
1020
+ return;
965
1021
  case "program":
966
1022
  for (const part of node.body)
967
1023
  collectReads(part, out);
@@ -1006,6 +1062,8 @@ function isPureNode(node) {
1006
1062
  return node.op !== "delete" && isPureNode(node.value);
1007
1063
  case "binary":
1008
1064
  return isPureNode(node.left) && isPureNode(node.right);
1065
+ case "range":
1066
+ return isPureNode(node.from) && isPureNode(node.to);
1009
1067
  default:
1010
1068
  return false;
1011
1069
  }
@@ -1070,6 +1128,8 @@ function hasIdentifierRead(node, name, asPlace = false) {
1070
1128
  return hasIdentifierRead(node.value, name, node.op === "delete");
1071
1129
  case "binary":
1072
1130
  return hasIdentifierRead(node.left, name) || hasIdentifierRead(node.right, name);
1131
+ case "range":
1132
+ return hasIdentifierRead(node.from, name) || hasIdentifierRead(node.to, name);
1073
1133
  case "assign":
1074
1134
  return hasIdentifierRead(node.place, name, true) || hasIdentifierRead(node.value, name);
1075
1135
  default:
@@ -1092,6 +1152,8 @@ function countIdentifierReads(node, name, asPlace = false) {
1092
1152
  return countIdentifierReads(node.value, name, node.op === "delete");
1093
1153
  case "binary":
1094
1154
  return countIdentifierReads(node.left, name) + countIdentifierReads(node.right, name);
1155
+ case "range":
1156
+ return countIdentifierReads(node.from, name) + countIdentifierReads(node.to, name);
1095
1157
  case "assign":
1096
1158
  return countIdentifierReads(node.place, name, true) + countIdentifierReads(node.value, name);
1097
1159
  default:
@@ -1146,6 +1208,12 @@ function replaceIdentifier(node, name, replacement, asPlace = false) {
1146
1208
  place: replaceIdentifier(node.place, name, replacement, true),
1147
1209
  value: replaceIdentifier(node.value, name, replacement)
1148
1210
  };
1211
+ case "range":
1212
+ return {
1213
+ type: "range",
1214
+ from: replaceIdentifier(node.from, name, replacement),
1215
+ to: replaceIdentifier(node.to, name, replacement)
1216
+ };
1149
1217
  default:
1150
1218
  return node;
1151
1219
  }
@@ -1290,6 +1358,9 @@ function foldUnary(op, value) {
1290
1358
  return ~value;
1291
1359
  return;
1292
1360
  }
1361
+ if (op === "logicalNot") {
1362
+ return value === undefined ? true : undefined;
1363
+ }
1293
1364
  return;
1294
1365
  }
1295
1366
  function foldBinary(op, left, right) {
@@ -1460,6 +1531,12 @@ function optimizeNode(node, env, currentDepth, asPlace = false) {
1460
1531
  }
1461
1532
  return { type: "binary", op: node.op, left, right };
1462
1533
  }
1534
+ case "range":
1535
+ return {
1536
+ type: "range",
1537
+ from: optimizeNode(node.from, env, currentDepth),
1538
+ to: optimizeNode(node.to, env, currentDepth)
1539
+ };
1463
1540
  case "navigation": {
1464
1541
  const target = optimizeNode(node.target, env, currentDepth);
1465
1542
  const segments = node.segments.map((segment) => segment.type === "static" ? segment : { type: "dynamic", key: optimizeNode(segment.key, env, currentDepth) });
@@ -1570,31 +1647,7 @@ function optimizeNode(node, env, currentDepth, asPlace = false) {
1570
1647
  }
1571
1648
  case "for": {
1572
1649
  const sourceEnv = cloneOptimizeEnv(env);
1573
- const binding = (() => {
1574
- if (node.binding.type === "binding:expr") {
1575
- return { type: "binding:expr", source: optimizeNode(node.binding.source, sourceEnv, currentDepth) };
1576
- }
1577
- if (node.binding.type === "binding:valueIn") {
1578
- return {
1579
- type: "binding:valueIn",
1580
- value: node.binding.value,
1581
- source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
1582
- };
1583
- }
1584
- if (node.binding.type === "binding:keyValueIn") {
1585
- return {
1586
- type: "binding:keyValueIn",
1587
- key: node.binding.key,
1588
- value: node.binding.value,
1589
- source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
1590
- };
1591
- }
1592
- return {
1593
- type: "binding:keyOf",
1594
- key: node.binding.key,
1595
- source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
1596
- };
1597
- })();
1650
+ const binding = optimizeBinding(node.binding, sourceEnv, currentDepth);
1598
1651
  const bodyEnv = cloneOptimizeEnv(env);
1599
1652
  dropBindingNames(bodyEnv, binding);
1600
1653
  return {
@@ -1605,20 +1658,7 @@ function optimizeNode(node, env, currentDepth, asPlace = false) {
1605
1658
  }
1606
1659
  case "arrayComprehension": {
1607
1660
  const sourceEnv = cloneOptimizeEnv(env);
1608
- const binding = node.binding.type === "binding:expr" ? { type: "binding:expr", source: optimizeNode(node.binding.source, sourceEnv, currentDepth) } : node.binding.type === "binding:valueIn" ? {
1609
- type: "binding:valueIn",
1610
- value: node.binding.value,
1611
- source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
1612
- } : node.binding.type === "binding:keyValueIn" ? {
1613
- type: "binding:keyValueIn",
1614
- key: node.binding.key,
1615
- value: node.binding.value,
1616
- source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
1617
- } : {
1618
- type: "binding:keyOf",
1619
- key: node.binding.key,
1620
- source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
1621
- };
1661
+ const binding = optimizeBinding(node.binding, sourceEnv, currentDepth);
1622
1662
  const bodyEnv = cloneOptimizeEnv(env);
1623
1663
  dropBindingNames(bodyEnv, binding);
1624
1664
  return {
@@ -1627,22 +1667,15 @@ function optimizeNode(node, env, currentDepth, asPlace = false) {
1627
1667
  body: optimizeNode(node.body, bodyEnv, currentDepth + 1)
1628
1668
  };
1629
1669
  }
1670
+ case "whileArrayComprehension":
1671
+ return {
1672
+ type: "whileArrayComprehension",
1673
+ condition: optimizeNode(node.condition, env, currentDepth),
1674
+ body: optimizeNode(node.body, env, currentDepth + 1)
1675
+ };
1630
1676
  case "objectComprehension": {
1631
1677
  const sourceEnv = cloneOptimizeEnv(env);
1632
- const binding = node.binding.type === "binding:expr" ? { type: "binding:expr", source: optimizeNode(node.binding.source, sourceEnv, currentDepth) } : node.binding.type === "binding:valueIn" ? {
1633
- type: "binding:valueIn",
1634
- value: node.binding.value,
1635
- source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
1636
- } : node.binding.type === "binding:keyValueIn" ? {
1637
- type: "binding:keyValueIn",
1638
- key: node.binding.key,
1639
- value: node.binding.value,
1640
- source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
1641
- } : {
1642
- type: "binding:keyOf",
1643
- key: node.binding.key,
1644
- source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
1645
- };
1678
+ const binding = optimizeBinding(node.binding, sourceEnv, currentDepth);
1646
1679
  const bodyEnv = cloneOptimizeEnv(env);
1647
1680
  dropBindingNames(bodyEnv, binding);
1648
1681
  return {
@@ -1652,6 +1685,13 @@ function optimizeNode(node, env, currentDepth, asPlace = false) {
1652
1685
  value: optimizeNode(node.value, bodyEnv, currentDepth + 1)
1653
1686
  };
1654
1687
  }
1688
+ case "whileObjectComprehension":
1689
+ return {
1690
+ type: "whileObjectComprehension",
1691
+ condition: optimizeNode(node.condition, env, currentDepth),
1692
+ key: optimizeNode(node.key, env, currentDepth + 1),
1693
+ value: optimizeNode(node.value, env, currentDepth + 1)
1694
+ };
1655
1695
  default:
1656
1696
  return node;
1657
1697
  }
@@ -1703,6 +1743,10 @@ function collectLocalBindings(node, locals) {
1703
1743
  collectLocalBindings(node.left, locals);
1704
1744
  collectLocalBindings(node.right, locals);
1705
1745
  return;
1746
+ case "range":
1747
+ collectLocalBindings(node.from, locals);
1748
+ collectLocalBindings(node.to, locals);
1749
+ return;
1706
1750
  case "conditional":
1707
1751
  collectLocalBindings(node.condition, locals);
1708
1752
  for (const part of node.thenBlock)
@@ -1719,11 +1763,20 @@ function collectLocalBindings(node, locals) {
1719
1763
  collectLocalBindingFromBinding(node.binding, locals);
1720
1764
  collectLocalBindings(node.body, locals);
1721
1765
  return;
1766
+ case "whileArrayComprehension":
1767
+ collectLocalBindings(node.condition, locals);
1768
+ collectLocalBindings(node.body, locals);
1769
+ return;
1722
1770
  case "objectComprehension":
1723
1771
  collectLocalBindingFromBinding(node.binding, locals);
1724
1772
  collectLocalBindings(node.key, locals);
1725
1773
  collectLocalBindings(node.value, locals);
1726
1774
  return;
1775
+ case "whileObjectComprehension":
1776
+ collectLocalBindings(node.condition, locals);
1777
+ collectLocalBindings(node.key, locals);
1778
+ collectLocalBindings(node.value, locals);
1779
+ return;
1727
1780
  default:
1728
1781
  return;
1729
1782
  }
@@ -1815,6 +1868,10 @@ function collectNameFrequencies(node, locals, frequencies, order, nextOrder) {
1815
1868
  collectNameFrequencies(node.left, locals, frequencies, order, nextOrder);
1816
1869
  collectNameFrequencies(node.right, locals, frequencies, order, nextOrder);
1817
1870
  return;
1871
+ case "range":
1872
+ collectNameFrequencies(node.from, locals, frequencies, order, nextOrder);
1873
+ collectNameFrequencies(node.to, locals, frequencies, order, nextOrder);
1874
+ return;
1818
1875
  case "conditional":
1819
1876
  collectNameFrequencies(node.condition, locals, frequencies, order, nextOrder);
1820
1877
  for (const part of node.thenBlock)
@@ -1831,11 +1888,20 @@ function collectNameFrequencies(node, locals, frequencies, order, nextOrder) {
1831
1888
  collectNameFrequenciesBinding(node.binding, locals, frequencies, order, nextOrder);
1832
1889
  collectNameFrequencies(node.body, locals, frequencies, order, nextOrder);
1833
1890
  return;
1891
+ case "whileArrayComprehension":
1892
+ collectNameFrequencies(node.condition, locals, frequencies, order, nextOrder);
1893
+ collectNameFrequencies(node.body, locals, frequencies, order, nextOrder);
1894
+ return;
1834
1895
  case "objectComprehension":
1835
1896
  collectNameFrequenciesBinding(node.binding, locals, frequencies, order, nextOrder);
1836
1897
  collectNameFrequencies(node.key, locals, frequencies, order, nextOrder);
1837
1898
  collectNameFrequencies(node.value, locals, frequencies, order, nextOrder);
1838
1899
  return;
1900
+ case "whileObjectComprehension":
1901
+ collectNameFrequencies(node.condition, locals, frequencies, order, nextOrder);
1902
+ collectNameFrequencies(node.key, locals, frequencies, order, nextOrder);
1903
+ collectNameFrequencies(node.value, locals, frequencies, order, nextOrder);
1904
+ return;
1839
1905
  default:
1840
1906
  return;
1841
1907
  }
@@ -1910,6 +1976,12 @@ function renameLocalNames(node, map) {
1910
1976
  left: renameLocalNames(node.left, map),
1911
1977
  right: renameLocalNames(node.right, map)
1912
1978
  };
1979
+ case "range":
1980
+ return {
1981
+ type: "range",
1982
+ from: renameLocalNames(node.from, map),
1983
+ to: renameLocalNames(node.to, map)
1984
+ };
1913
1985
  case "assign": {
1914
1986
  const place = node.place.type === "identifier" && map.has(node.place.name) ? { type: "identifier", name: map.get(node.place.name) } : renameLocalNames(node.place, map);
1915
1987
  return {
@@ -1939,6 +2011,12 @@ function renameLocalNames(node, map) {
1939
2011
  binding: renameLocalNamesBinding(node.binding, map),
1940
2012
  body: renameLocalNames(node.body, map)
1941
2013
  };
2014
+ case "whileArrayComprehension":
2015
+ return {
2016
+ type: "whileArrayComprehension",
2017
+ condition: renameLocalNames(node.condition, map),
2018
+ body: renameLocalNames(node.body, map)
2019
+ };
1942
2020
  case "objectComprehension":
1943
2021
  return {
1944
2022
  type: "objectComprehension",
@@ -1946,34 +2024,31 @@ function renameLocalNames(node, map) {
1946
2024
  key: renameLocalNames(node.key, map),
1947
2025
  value: renameLocalNames(node.value, map)
1948
2026
  };
2027
+ case "whileObjectComprehension":
2028
+ return {
2029
+ type: "whileObjectComprehension",
2030
+ condition: renameLocalNames(node.condition, map),
2031
+ key: renameLocalNames(node.key, map),
2032
+ value: renameLocalNames(node.value, map)
2033
+ };
1949
2034
  default:
1950
2035
  return node;
1951
2036
  }
1952
2037
  }
1953
2038
  function renameLocalNamesBinding(binding, map) {
1954
- if (binding.type === "binding:expr") {
1955
- return { type: "binding:expr", source: renameLocalNames(binding.source, map) };
1956
- }
1957
- if (binding.type === "binding:valueIn") {
1958
- return {
1959
- type: "binding:valueIn",
1960
- value: map.get(binding.value) ?? binding.value,
1961
- source: renameLocalNames(binding.source, map)
1962
- };
1963
- }
1964
- if (binding.type === "binding:keyValueIn") {
1965
- return {
1966
- type: "binding:keyValueIn",
1967
- key: map.get(binding.key) ?? binding.key,
1968
- value: map.get(binding.value) ?? binding.value,
1969
- source: renameLocalNames(binding.source, map)
1970
- };
2039
+ const source = renameLocalNames(binding.source, map);
2040
+ switch (binding.type) {
2041
+ case "binding:bareIn":
2042
+ return { type: "binding:bareIn", source };
2043
+ case "binding:bareOf":
2044
+ return { type: "binding:bareOf", source };
2045
+ case "binding:valueIn":
2046
+ return { type: "binding:valueIn", value: map.get(binding.value) ?? binding.value, source };
2047
+ case "binding:keyValueIn":
2048
+ return { type: "binding:keyValueIn", key: map.get(binding.key) ?? binding.key, value: map.get(binding.value) ?? binding.value, source };
2049
+ case "binding:keyOf":
2050
+ return { type: "binding:keyOf", key: map.get(binding.key) ?? binding.key, source };
1971
2051
  }
1972
- return {
1973
- type: "binding:keyOf",
1974
- key: map.get(binding.key) ?? binding.key,
1975
- source: renameLocalNames(binding.source, map)
1976
- };
1977
2052
  }
1978
2053
  function renameLocalNamesElse(elseBranch, map) {
1979
2054
  if (elseBranch.type === "else") {
@@ -2020,9 +2095,9 @@ function compile(source, options) {
2020
2095
  let lowered = options?.optimize ? optimizeIR(ir) : ir;
2021
2096
  if (options?.minifyNames)
2022
2097
  lowered = minifyLocalNamesIR(lowered);
2023
- const domainRefs = options?.domainConfig ? domainRefsFromConfig(options.domainConfig) : undefined;
2098
+ const domainMaps = options?.domainConfig ? domainRefsFromConfig(options.domainConfig) : undefined;
2024
2099
  return encodeIR(lowered, {
2025
- domainRefs,
2100
+ ...domainMaps,
2026
2101
  dedupeValues: options?.dedupeValues,
2027
2102
  dedupeMinBytes: options?.dedupeMinBytes
2028
2103
  });
@@ -2105,7 +2180,7 @@ function buildPostfix(base, steps) {
2105
2180
  flushSegments();
2106
2181
  return current;
2107
2182
  }
2108
- var require2, rexGrammarModule, rexGrammar, grammar, semantics, DIGITS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_", OPCODE_IDS, FIRST_NON_RESERVED_REF = 8, DOMAIN_DIGIT_INDEX, BINARY_TO_OPCODE, ASSIGN_COMPOUND_TO_OPCODE, DEC_PARTS, activeEncodeOptions, DIGIT_SET, DIGIT_INDEX;
2183
+ var require2, rexGrammarModule, rexGrammar, grammar, semantics, DIGITS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_", OPCODE_IDS, KEYWORD_OPCODES, BINARY_TO_OPCODE, ASSIGN_COMPOUND_TO_OPCODE, DEC_PARTS, activeEncodeOptions, DIGIT_SET, DIGIT_INDEX;
2109
2184
  var init_rex = __esm(() => {
2110
2185
  require2 = createRequire(import.meta.url);
2111
2186
  rexGrammarModule = require2("./rex.ohm-bundle.cjs");
@@ -2113,30 +2188,32 @@ var init_rex = __esm(() => {
2113
2188
  grammar = rexGrammar;
2114
2189
  semantics = rexGrammar.createSemantics();
2115
2190
  OPCODE_IDS = {
2116
- do: 0,
2117
- add: 1,
2118
- sub: 2,
2119
- mul: 3,
2120
- div: 4,
2121
- eq: 5,
2122
- neq: 6,
2123
- lt: 7,
2124
- lte: 8,
2125
- gt: 9,
2126
- gte: 10,
2127
- and: 11,
2128
- or: 12,
2129
- xor: 13,
2130
- not: 14,
2131
- boolean: 15,
2132
- number: 16,
2133
- string: 17,
2134
- array: 18,
2135
- object: 19,
2136
- mod: 20,
2137
- neg: 21
2191
+ do: "",
2192
+ add: "ad",
2193
+ sub: "sb",
2194
+ mul: "ml",
2195
+ div: "dv",
2196
+ eq: "eq",
2197
+ neq: "nq",
2198
+ lt: "lt",
2199
+ lte: "le",
2200
+ gt: "gt",
2201
+ gte: "ge",
2202
+ and: "an",
2203
+ or: "or",
2204
+ xor: "xr",
2205
+ not: "nt",
2206
+ boolean: "bt",
2207
+ number: "nm",
2208
+ string: "st",
2209
+ array: "ar",
2210
+ object: "ob",
2211
+ mod: "md",
2212
+ neg: "ng",
2213
+ range: "rn",
2214
+ size: "sz"
2138
2215
  };
2139
- DOMAIN_DIGIT_INDEX = new Map(Array.from(DIGITS).map((char, index) => [char, index]));
2216
+ KEYWORD_OPCODES = new Set(["boolean", "number", "string", "array", "object", "size"]);
2140
2217
  BINARY_TO_OPCODE = {
2141
2218
  add: "add",
2142
2219
  sub: "sub",
@@ -2211,6 +2288,9 @@ var init_rex = __esm(() => {
2211
2288
  ExistenceExpr_or(left, _or, right) {
2212
2289
  return { type: "binary", op: "or", left: left.toIR(), right: right.toIR() };
2213
2290
  },
2291
+ ExistenceExpr_nor(left, _nor, right) {
2292
+ return { type: "binary", op: "nor", left: left.toIR(), right: right.toIR() };
2293
+ },
2214
2294
  BitExpr_and(left, _op, right) {
2215
2295
  return { type: "binary", op: "bitAnd", left: left.toIR(), right: right.toIR() };
2216
2296
  },
@@ -2220,6 +2300,9 @@ var init_rex = __esm(() => {
2220
2300
  BitExpr_or(left, _op, right) {
2221
2301
  return { type: "binary", op: "bitOr", left: left.toIR(), right: right.toIR() };
2222
2302
  },
2303
+ RangeExpr_range(left, _op, right) {
2304
+ return { type: "range", from: left.toIR(), to: right.toIR() };
2305
+ },
2223
2306
  CompareExpr_binary(left, op, right) {
2224
2307
  const map = {
2225
2308
  "==": "eq",
@@ -2260,6 +2343,9 @@ var init_rex = __esm(() => {
2260
2343
  UnaryExpr_not(_op, value) {
2261
2344
  return { type: "unary", op: "not", value: value.toIR() };
2262
2345
  },
2346
+ UnaryExpr_logicalNot(_not, value) {
2347
+ return { type: "unary", op: "logicalNot", value: value.toIR() };
2348
+ },
2263
2349
  UnaryExpr_delete(_del, place) {
2264
2350
  return { type: "unary", op: "delete", value: place.toIR() };
2265
2351
  },
@@ -2313,14 +2399,6 @@ var init_rex = __esm(() => {
2313
2399
  ConditionalElse_else(_else, block) {
2314
2400
  return { type: "else", block: block.toIR() };
2315
2401
  },
2316
- DoExpr(_do, block, _end) {
2317
- const body = block.toIR();
2318
- if (body.length === 0)
2319
- return { type: "undefined" };
2320
- if (body.length === 1)
2321
- return body[0];
2322
- return { type: "program", body };
2323
- },
2324
2402
  WhileExpr(_while, condition, _do, block, _end) {
2325
2403
  return {
2326
2404
  type: "while",
@@ -2335,30 +2413,44 @@ var init_rex = __esm(() => {
2335
2413
  body: block.toIR()
2336
2414
  };
2337
2415
  },
2338
- BindingExpr(iterOrExpr) {
2339
- const node = iterOrExpr.toIR();
2340
- if (typeof node === "object" && node && "type" in node && String(node.type).startsWith("binding:")) {
2341
- return node;
2342
- }
2343
- return { type: "binding:expr", source: node };
2344
- },
2345
2416
  Array_empty(_open, _close) {
2346
2417
  return { type: "array", items: [] };
2347
2418
  },
2348
- Array_comprehension(_open, binding, _semi, body, _close) {
2419
+ Array_forComprehension(_open, body, _for, binding, _close) {
2349
2420
  return {
2350
2421
  type: "arrayComprehension",
2351
2422
  binding: binding.toIR(),
2352
2423
  body: body.toIR()
2353
2424
  };
2354
2425
  },
2426
+ Array_whileComprehension(_open, body, _while, condition, _close) {
2427
+ return {
2428
+ type: "whileArrayComprehension",
2429
+ condition: condition.toIR(),
2430
+ body: body.toIR()
2431
+ };
2432
+ },
2433
+ Array_inComprehension(_open, body, _in, source, _close) {
2434
+ return {
2435
+ type: "arrayComprehension",
2436
+ binding: { type: "binding:bareIn", source: source.toIR() },
2437
+ body: body.toIR()
2438
+ };
2439
+ },
2440
+ Array_ofComprehension(_open, body, _of, source, _close) {
2441
+ return {
2442
+ type: "arrayComprehension",
2443
+ binding: { type: "binding:bareOf", source: source.toIR() },
2444
+ body: body.toIR()
2445
+ };
2446
+ },
2355
2447
  Array_values(_open, items, _close) {
2356
2448
  return { type: "array", items: normalizeList(items.toIR()) };
2357
2449
  },
2358
2450
  Object_empty(_open, _close) {
2359
2451
  return { type: "object", entries: [] };
2360
2452
  },
2361
- Object_comprehension(_open, binding, _semi, key, _colon, value, _close) {
2453
+ Object_forComprehension(_open, key, _colon, value, _for, binding, _close) {
2362
2454
  return {
2363
2455
  type: "objectComprehension",
2364
2456
  binding: binding.toIR(),
@@ -2366,6 +2458,30 @@ var init_rex = __esm(() => {
2366
2458
  value: value.toIR()
2367
2459
  };
2368
2460
  },
2461
+ Object_whileComprehension(_open, key, _colon, value, _while, condition, _close) {
2462
+ return {
2463
+ type: "whileObjectComprehension",
2464
+ condition: condition.toIR(),
2465
+ key: key.toIR(),
2466
+ value: value.toIR()
2467
+ };
2468
+ },
2469
+ Object_inComprehension(_open, key, _colon, value, _in, source, _close) {
2470
+ return {
2471
+ type: "objectComprehension",
2472
+ binding: { type: "binding:bareIn", source: source.toIR() },
2473
+ key: key.toIR(),
2474
+ value: value.toIR()
2475
+ };
2476
+ },
2477
+ Object_ofComprehension(_open, key, _colon, value, _of, source, _close) {
2478
+ return {
2479
+ type: "objectComprehension",
2480
+ binding: { type: "binding:bareOf", source: source.toIR() },
2481
+ key: key.toIR(),
2482
+ value: value.toIR()
2483
+ };
2484
+ },
2369
2485
  Object_pairs(_open, pairs, _close) {
2370
2486
  return {
2371
2487
  type: "object",
@@ -2394,6 +2510,18 @@ var init_rex = __esm(() => {
2394
2510
  source: source.toIR()
2395
2511
  };
2396
2512
  },
2513
+ IterBinding_bareIn(_in, source) {
2514
+ return {
2515
+ type: "binding:bareIn",
2516
+ source: source.toIR()
2517
+ };
2518
+ },
2519
+ IterBinding_bareOf(_of, source) {
2520
+ return {
2521
+ type: "binding:bareOf",
2522
+ source: source.toIR()
2523
+ };
2524
+ },
2397
2525
  Pair(key, _colon, value) {
2398
2526
  return { key: key.toIR(), value: value.toIR() };
2399
2527
  },
@@ -2457,6 +2585,9 @@ var init_rex = __esm(() => {
2457
2585
  BooleanKw(_kw) {
2458
2586
  return { type: "identifier", name: "boolean" };
2459
2587
  },
2588
+ SizeKw(_kw) {
2589
+ return { type: "identifier", name: "size" };
2590
+ },
2460
2591
  identifier(_a, _b) {
2461
2592
  return { type: "identifier", name: this.sourceString };
2462
2593
  },
@@ -2495,7 +2626,6 @@ class CursorInterpreter {
2495
2626
  pos = 0;
2496
2627
  state;
2497
2628
  selfStack;
2498
- opcodeMarkers;
2499
2629
  pointerCache = new Map;
2500
2630
  gasLimit;
2501
2631
  gas = 0;
@@ -2510,28 +2640,22 @@ class CursorInterpreter {
2510
2640
  this.state = {
2511
2641
  vars: ctx.vars ?? {},
2512
2642
  refs: {
2513
- 0: ctx.refs?.[0],
2514
- 1: ctx.refs?.[1] ?? true,
2515
- 2: ctx.refs?.[2] ?? false,
2516
- 3: ctx.refs?.[3] ?? null,
2517
- 4: ctx.refs?.[4] ?? undefined,
2518
- 5: ctx.refs?.[5] ?? NaN,
2519
- 6: ctx.refs?.[6] ?? Infinity,
2520
- 7: ctx.refs?.[7] ?? -Infinity
2643
+ tr: true,
2644
+ fl: false,
2645
+ nl: null,
2646
+ un: undefined,
2647
+ nan: NaN,
2648
+ inf: Infinity,
2649
+ nif: -Infinity,
2650
+ ...ctx.refs
2521
2651
  }
2522
2652
  };
2523
2653
  this.selfStack = ctx.selfStack && ctx.selfStack.length > 0 ? [...ctx.selfStack] : [initialSelf];
2524
2654
  this.gasLimit = ctx.gasLimit ?? 0;
2525
- this.opcodeMarkers = Array.from({ length: 256 }, (_, id) => ({ __opcode: id }));
2526
- for (const [idText, value] of Object.entries(ctx.refs ?? {})) {
2527
- const id = Number(idText);
2528
- if (Number.isInteger(id))
2529
- this.state.refs[id] = value;
2530
- }
2531
2655
  if (ctx.opcodes) {
2532
- for (const [idText, op] of Object.entries(ctx.opcodes)) {
2656
+ for (const [key, op] of Object.entries(ctx.opcodes)) {
2533
2657
  if (op)
2534
- this.customOpcodes.set(Number(idText), op);
2658
+ this.customOpcodes.set(key, op);
2535
2659
  }
2536
2660
  }
2537
2661
  }
@@ -2592,6 +2716,22 @@ class CursorInterpreter {
2592
2716
  const end = this.pos;
2593
2717
  return { start, end, value: decodePrefix(this.text, start, end), raw: this.text.slice(start, end) };
2594
2718
  }
2719
+ advanceByBytes(start, byteCount) {
2720
+ if (byteCount <= 0)
2721
+ return start;
2722
+ let bytes = 0;
2723
+ let index = start;
2724
+ for (const char of this.text.slice(start)) {
2725
+ const charBytes = Buffer.byteLength(char);
2726
+ if (bytes + charBytes > byteCount)
2727
+ break;
2728
+ bytes += charBytes;
2729
+ index += char.length;
2730
+ if (bytes === byteCount)
2731
+ return index;
2732
+ }
2733
+ throw new Error("String container overflows input");
2734
+ }
2595
2735
  ensure(char) {
2596
2736
  if (this.text[this.pos] !== char)
2597
2737
  throw new Error(`Expected '${char}' at ${this.pos}`);
@@ -2632,36 +2772,34 @@ class CursorInterpreter {
2632
2772
  const significand = this.evalValue();
2633
2773
  if (typeof significand !== "number")
2634
2774
  throw new Error("Decimal significand must be numeric");
2635
- return significand * 10 ** power;
2775
+ return parseFloat(`${significand}e${power}`);
2636
2776
  }
2637
2777
  case ":":
2638
2778
  this.pos += 1;
2639
2779
  return prefix.raw;
2640
2780
  case "%":
2641
2781
  this.pos += 1;
2642
- return this.opcodeMarkers[prefix.value] ?? { __opcode: prefix.value };
2782
+ return { __opcode: prefix.raw };
2643
2783
  case "@":
2644
2784
  this.pos += 1;
2645
2785
  return this.readSelf(prefix.value);
2646
2786
  case "'":
2647
2787
  this.pos += 1;
2648
- return this.state.refs[prefix.value];
2788
+ return this.state.refs[prefix.raw];
2649
2789
  case "$":
2650
2790
  this.pos += 1;
2651
2791
  return this.state.vars[prefix.raw];
2652
2792
  case ",": {
2653
2793
  this.pos += 1;
2654
2794
  const start = this.pos;
2655
- const end = start + prefix.value;
2656
- if (end > this.text.length)
2657
- throw new Error("String container overflows input");
2795
+ const end = this.advanceByBytes(start, prefix.value);
2658
2796
  const value = this.text.slice(start, end);
2659
2797
  this.pos = end;
2660
2798
  return value;
2661
2799
  }
2662
2800
  case "^": {
2663
2801
  this.pos += 1;
2664
- const target = this.pos + prefix.value;
2802
+ const target = this.advanceByBytes(this.pos, prefix.value);
2665
2803
  if (this.pointerCache.has(target))
2666
2804
  return this.pointerCache.get(target);
2667
2805
  const save = this.pos;
@@ -2678,6 +2816,14 @@ class CursorInterpreter {
2678
2816
  this.writePlace(place, value);
2679
2817
  return value;
2680
2818
  }
2819
+ case "/": {
2820
+ this.pos += 1;
2821
+ const place = this.readPlace();
2822
+ const oldValue = this.readPlaceValue(place);
2823
+ const newValue = this.evalValue();
2824
+ this.writePlace(place, newValue);
2825
+ return oldValue;
2826
+ }
2681
2827
  case "~": {
2682
2828
  this.pos += 1;
2683
2829
  const place = this.readPlace();
@@ -2705,7 +2851,7 @@ class CursorInterpreter {
2705
2851
  case "<":
2706
2852
  return this.evalLoopLike(tag);
2707
2853
  case "#":
2708
- return this.evalWhileLoop();
2854
+ return this.evalWhileLike();
2709
2855
  default:
2710
2856
  throw new Error(`Unexpected tag '${tag}' at ${this.pos}`);
2711
2857
  }
@@ -2918,15 +3064,11 @@ class CursorInterpreter {
2918
3064
  return entries.map(([key]) => ({ key, value: key }));
2919
3065
  return entries.map(([key, value]) => ({ key, value }));
2920
3066
  }
2921
- if (typeof iterable === "number" && Number.isFinite(iterable) && iterable > 0) {
2922
- const out = [];
2923
- for (let index = 0;index < Math.floor(iterable); index += 1) {
2924
- if (keysOnly)
2925
- out.push({ key: index, value: index });
2926
- else
2927
- out.push({ key: index, value: index + 1 });
2928
- }
2929
- return out;
3067
+ if (typeof iterable === "string") {
3068
+ const entries = Array.from(iterable);
3069
+ if (keysOnly)
3070
+ return entries.map((_value, index) => ({ key: index, value: index }));
3071
+ return entries.map((value, index) => ({ key: index, value }));
2930
3072
  }
2931
3073
  return [];
2932
3074
  }
@@ -2971,21 +3113,32 @@ class CursorInterpreter {
2971
3113
  }
2972
3114
  return last;
2973
3115
  }
2974
- evalWhileLoop() {
3116
+ evalWhileLike() {
3117
+ this.pos += 1;
3118
+ const open = this.text[this.pos];
3119
+ if (!open || !"([{".includes(open))
3120
+ throw new Error(`Expected opener after '#' at ${this.pos}`);
3121
+ const close = open === "(" ? ")" : open === "[" ? "]" : "}";
2975
3122
  this.pos += 1;
2976
- this.ensure("(");
2977
3123
  const condStart = this.pos;
2978
3124
  const condValue = this.evalValue();
2979
3125
  const bodyStart = this.pos;
2980
- const bodyValueCount = 1;
3126
+ const bodyValueCount = open === "{" ? 2 : 1;
2981
3127
  let cursor = bodyStart;
2982
3128
  for (let index = 0;index < bodyValueCount; index += 1) {
2983
3129
  cursor = this.skipValueFrom(cursor);
2984
3130
  }
2985
3131
  const bodyEnd = cursor;
2986
3132
  this.pos = bodyEnd;
2987
- this.ensure(")");
3133
+ this.ensure(close);
2988
3134
  const afterClose = this.pos;
3135
+ if (open === "[")
3136
+ return this.evalWhileArrayComprehension(condStart, bodyStart, bodyEnd, afterClose, condValue);
3137
+ if (open === "{")
3138
+ return this.evalWhileObjectComprehension(condStart, bodyStart, bodyEnd, afterClose, condValue);
3139
+ return this.evalWhileLoop(condStart, bodyStart, bodyEnd, afterClose, condValue);
3140
+ }
3141
+ evalWhileLoop(condStart, bodyStart, bodyEnd, afterClose, condValue) {
2989
3142
  let last = undefined;
2990
3143
  let currentCond = condValue;
2991
3144
  while (isDefined(currentCond)) {
@@ -3006,6 +3159,58 @@ class CursorInterpreter {
3006
3159
  this.pos = afterClose;
3007
3160
  return last;
3008
3161
  }
3162
+ evalWhileArrayComprehension(condStart, bodyStart, bodyEnd, afterClose, condValue) {
3163
+ const out = [];
3164
+ let currentCond = condValue;
3165
+ while (isDefined(currentCond)) {
3166
+ this.tick();
3167
+ this.selfStack.push(currentCond);
3168
+ const value = this.evalBodySlice(bodyStart, bodyEnd);
3169
+ this.selfStack.pop();
3170
+ const control = this.handleLoopControl(value);
3171
+ if (control) {
3172
+ if (control.depth > 1)
3173
+ return { kind: control.kind, depth: control.depth - 1 };
3174
+ if (control.kind === "break")
3175
+ break;
3176
+ currentCond = this.evalBodySlice(condStart, bodyStart);
3177
+ continue;
3178
+ }
3179
+ if (isDefined(value))
3180
+ out.push(value);
3181
+ currentCond = this.evalBodySlice(condStart, bodyStart);
3182
+ }
3183
+ this.pos = afterClose;
3184
+ return out;
3185
+ }
3186
+ evalWhileObjectComprehension(condStart, bodyStart, bodyEnd, afterClose, condValue) {
3187
+ const result = {};
3188
+ let currentCond = condValue;
3189
+ while (isDefined(currentCond)) {
3190
+ this.tick();
3191
+ this.selfStack.push(currentCond);
3192
+ const save = this.pos;
3193
+ this.pos = bodyStart;
3194
+ const key = this.evalValue();
3195
+ const value = this.evalValue();
3196
+ this.pos = save;
3197
+ this.selfStack.pop();
3198
+ const control = this.handleLoopControl(value);
3199
+ if (control) {
3200
+ if (control.depth > 1)
3201
+ return { kind: control.kind, depth: control.depth - 1 };
3202
+ if (control.kind === "break")
3203
+ break;
3204
+ currentCond = this.evalBodySlice(condStart, bodyStart);
3205
+ continue;
3206
+ }
3207
+ if (isDefined(value))
3208
+ result[String(key)] = value;
3209
+ currentCond = this.evalBodySlice(condStart, bodyStart);
3210
+ }
3211
+ this.pos = afterClose;
3212
+ return result;
3213
+ }
3009
3214
  evalArrayComprehension(iterable, varA, varB, bodyStart, bodyEnd, keysOnly) {
3010
3215
  const items = this.iterate(iterable, keysOnly);
3011
3216
  const out = [];
@@ -3148,6 +3353,25 @@ class CursorInterpreter {
3148
3353
  return Array.isArray(args[0]) ? args[0] : undefined;
3149
3354
  case OPCODES.object:
3150
3355
  return args[0] && typeof args[0] === "object" && !Array.isArray(args[0]) ? args[0] : undefined;
3356
+ case OPCODES.range: {
3357
+ const from = Number(args[0]);
3358
+ const to = Number(args[1]);
3359
+ const step = to >= from ? 1 : -1;
3360
+ const out = [];
3361
+ for (let v = from;step > 0 ? v <= to : v >= to; v += step)
3362
+ out.push(v);
3363
+ return out;
3364
+ }
3365
+ case OPCODES.size: {
3366
+ const target = args[0];
3367
+ if (Array.isArray(target))
3368
+ return target.length;
3369
+ if (typeof target === "string")
3370
+ return Array.from(target).length;
3371
+ if (target && typeof target === "object")
3372
+ return Object.keys(target).length;
3373
+ return;
3374
+ }
3151
3375
  default:
3152
3376
  throw new Error(`Unknown opcode ${id}`);
3153
3377
  }
@@ -3157,10 +3381,59 @@ class CursorInterpreter {
3157
3381
  for (const key of keys) {
3158
3382
  if (current === undefined || current === null)
3159
3383
  return;
3160
- current = current[String(key)];
3384
+ current = this.readProperty(current, key);
3385
+ if (current === undefined)
3386
+ return;
3161
3387
  }
3162
3388
  return current;
3163
3389
  }
3390
+ readProperty(target, key) {
3391
+ const index = this.parseIndexKey(key);
3392
+ if (Array.isArray(target)) {
3393
+ if (index === undefined)
3394
+ return;
3395
+ return target[index];
3396
+ }
3397
+ if (typeof target === "string") {
3398
+ if (index === undefined)
3399
+ return;
3400
+ return Array.from(target)[index];
3401
+ }
3402
+ if (this.isPlainObject(target)) {
3403
+ const prop = String(key);
3404
+ if (!Object.prototype.hasOwnProperty.call(target, prop))
3405
+ return;
3406
+ return target[prop];
3407
+ }
3408
+ return;
3409
+ }
3410
+ canWriteProperty(target, key) {
3411
+ const index = this.parseIndexKey(key);
3412
+ if (Array.isArray(target)) {
3413
+ if (index === undefined)
3414
+ return;
3415
+ return { kind: "array", index };
3416
+ }
3417
+ if (this.isPlainObject(target))
3418
+ return { kind: "object" };
3419
+ return;
3420
+ }
3421
+ parseIndexKey(key) {
3422
+ if (typeof key === "number" && Number.isInteger(key) && key >= 0)
3423
+ return key;
3424
+ if (typeof key !== "string" || key.length === 0)
3425
+ return;
3426
+ if (!/^(0|[1-9]\d*)$/.test(key))
3427
+ return;
3428
+ const index = Number(key);
3429
+ return Number.isSafeInteger(index) ? index : undefined;
3430
+ }
3431
+ isPlainObject(value) {
3432
+ if (!value || typeof value !== "object")
3433
+ return false;
3434
+ const proto = Object.getPrototypeOf(value);
3435
+ return proto === Object.prototype || proto === null;
3436
+ }
3164
3437
  readPlace() {
3165
3438
  this.skipNonCode();
3166
3439
  const direct = this.readRootVarOrRefIfPresent();
@@ -3215,13 +3488,13 @@ class CursorInterpreter {
3215
3488
  }
3216
3489
  this.pos += 1;
3217
3490
  return {
3218
- root: tag === "$" ? prefix.raw : prefix.value,
3491
+ root: prefix.raw,
3219
3492
  isRef: tag === "'"
3220
3493
  };
3221
3494
  }
3222
3495
  writePlace(place, value) {
3223
3496
  const rootTable = place.isRef ? this.state.refs : this.state.vars;
3224
- const rootKey = String(place.root);
3497
+ const rootKey = place.root;
3225
3498
  if (place.keys.length === 0) {
3226
3499
  rootTable[rootKey] = value;
3227
3500
  return;
@@ -3232,17 +3505,48 @@ class CursorInterpreter {
3232
3505
  rootTable[rootKey] = target;
3233
3506
  }
3234
3507
  for (let index = 0;index < place.keys.length - 1; index += 1) {
3235
- const key = String(place.keys[index]);
3236
- const next = target[key];
3508
+ const key = place.keys[index];
3509
+ const access2 = this.canWriteProperty(target, key);
3510
+ if (!access2)
3511
+ return;
3512
+ if (access2.kind === "array") {
3513
+ const next2 = target[access2.index];
3514
+ if (!next2 || typeof next2 !== "object")
3515
+ target[access2.index] = {};
3516
+ target = target[access2.index];
3517
+ continue;
3518
+ }
3519
+ const prop = String(key);
3520
+ const next = target[prop];
3237
3521
  if (!next || typeof next !== "object")
3238
- target[key] = {};
3239
- target = target[key];
3522
+ target[prop] = {};
3523
+ target = target[prop];
3240
3524
  }
3241
- target[String(place.keys[place.keys.length - 1])] = value;
3525
+ const lastKey = place.keys[place.keys.length - 1];
3526
+ const access = this.canWriteProperty(target, lastKey);
3527
+ if (!access)
3528
+ return;
3529
+ if (access.kind === "array") {
3530
+ target[access.index] = value;
3531
+ return;
3532
+ }
3533
+ target[String(lastKey)] = value;
3534
+ }
3535
+ readPlaceValue(place) {
3536
+ const rootTable = place.isRef ? this.state.refs : this.state.vars;
3537
+ let current = rootTable[place.root];
3538
+ for (const key of place.keys) {
3539
+ if (current === undefined || current === null)
3540
+ return;
3541
+ current = this.readProperty(current, key);
3542
+ if (current === undefined)
3543
+ return;
3544
+ }
3545
+ return current;
3242
3546
  }
3243
3547
  deletePlace(place) {
3244
3548
  const rootTable = place.isRef ? this.state.refs : this.state.vars;
3245
- const rootKey = String(place.root);
3549
+ const rootKey = place.root;
3246
3550
  if (place.keys.length === 0) {
3247
3551
  delete rootTable[rootKey];
3248
3552
  return;
@@ -3251,11 +3555,29 @@ class CursorInterpreter {
3251
3555
  if (!target || typeof target !== "object")
3252
3556
  return;
3253
3557
  for (let index = 0;index < place.keys.length - 1; index += 1) {
3254
- target = target[String(place.keys[index])];
3558
+ const key = place.keys[index];
3559
+ const access2 = this.canWriteProperty(target, key);
3560
+ if (!access2)
3561
+ return;
3562
+ if (access2.kind === "array") {
3563
+ target = target[access2.index];
3564
+ if (!target || typeof target !== "object")
3565
+ return;
3566
+ continue;
3567
+ }
3568
+ target = target[String(key)];
3255
3569
  if (!target || typeof target !== "object")
3256
3570
  return;
3257
3571
  }
3258
- delete target[String(place.keys[place.keys.length - 1])];
3572
+ const lastKey = place.keys[place.keys.length - 1];
3573
+ const access = this.canWriteProperty(target, lastKey);
3574
+ if (!access)
3575
+ return;
3576
+ if (access.kind === "array") {
3577
+ delete target[access.index];
3578
+ return;
3579
+ }
3580
+ delete target[String(lastKey)];
3259
3581
  }
3260
3582
  skipValue() {
3261
3583
  this.pos = this.skipValueFrom(this.pos);
@@ -3271,12 +3593,12 @@ class CursorInterpreter {
3271
3593
  return startPos;
3272
3594
  }
3273
3595
  if (tag === ",") {
3274
- this.pos += 1 + prefix.value;
3596
+ this.pos = this.advanceByBytes(this.pos + 1, prefix.value);
3275
3597
  const end2 = this.pos;
3276
3598
  this.pos = save;
3277
3599
  return end2;
3278
3600
  }
3279
- if (tag === "=") {
3601
+ if (tag === "=" || tag === "/") {
3280
3602
  this.pos += 1;
3281
3603
  this.skipValue();
3282
3604
  this.skipValue();
@@ -3311,7 +3633,8 @@ class CursorInterpreter {
3311
3633
  if (opener && "([{".includes(opener)) {
3312
3634
  const close = opener === "(" ? ")" : opener === "[" ? "]" : "}";
3313
3635
  if (prefix.value > 0) {
3314
- this.pos += 1 + prefix.value + 1;
3636
+ const bodyEnd = this.advanceByBytes(this.pos + 1, prefix.value);
3637
+ this.pos = bodyEnd + 1;
3315
3638
  const end3 = this.pos;
3316
3639
  this.pos = save;
3317
3640
  return end3;
@@ -3347,28 +3670,30 @@ var init_rexc_interpreter = __esm(() => {
3347
3670
  init_rex();
3348
3671
  digitMap = new Map(Array.from(DIGITS2).map((char, index) => [char, index]));
3349
3672
  OPCODES = {
3350
- do: 0,
3351
- add: 1,
3352
- sub: 2,
3353
- mul: 3,
3354
- div: 4,
3355
- eq: 5,
3356
- neq: 6,
3357
- lt: 7,
3358
- lte: 8,
3359
- gt: 9,
3360
- gte: 10,
3361
- and: 11,
3362
- or: 12,
3363
- xor: 13,
3364
- not: 14,
3365
- boolean: 15,
3366
- number: 16,
3367
- string: 17,
3368
- array: 18,
3369
- object: 19,
3370
- mod: 20,
3371
- neg: 21
3673
+ do: "",
3674
+ add: "ad",
3675
+ sub: "sb",
3676
+ mul: "ml",
3677
+ div: "dv",
3678
+ eq: "eq",
3679
+ neq: "nq",
3680
+ lt: "lt",
3681
+ lte: "le",
3682
+ gt: "gt",
3683
+ gte: "ge",
3684
+ and: "an",
3685
+ or: "or",
3686
+ xor: "xr",
3687
+ not: "nt",
3688
+ boolean: "bt",
3689
+ number: "nm",
3690
+ string: "st",
3691
+ array: "ar",
3692
+ object: "ob",
3693
+ mod: "md",
3694
+ neg: "ng",
3695
+ range: "rn",
3696
+ size: "sz"
3372
3697
  };
3373
3698
  });
3374
3699
 
@@ -3491,6 +3816,7 @@ function highlightRexc(text) {
3491
3816
  break;
3492
3817
  }
3493
3818
  case "=":
3819
+ case "/":
3494
3820
  case "~":
3495
3821
  out += C.red + prefix + tag + C.reset;
3496
3822
  i++;
@@ -3593,7 +3919,7 @@ function isIncomplete(buffer) {
3593
3919
  const trimmed = buffer.trimEnd();
3594
3920
  if (/[+\-*/%&|^=<>]$/.test(trimmed))
3595
3921
  return true;
3596
- if (/\b(?:and|or|do|in|of)\s*$/.test(trimmed))
3922
+ if (/\b(?:and|or|nor|do|in|of)\s*$/.test(trimmed))
3597
3923
  return true;
3598
3924
  return false;
3599
3925
  }
@@ -3853,7 +4179,7 @@ var init_rex_repl = __esm(() => {
3853
4179
  gray: "\x1B[90m",
3854
4180
  boldBlue: "\x1B[1;34m"
3855
4181
  };
3856
- 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|nan)(?![a-zA-Z0-9_-])|-?\binf\b)|(?<typePred>\b(?:string|number|object|array|boolean)(?![a-zA-Z0-9_-]))|(?<num>\b(?:0x[0-9a-fA-F]+|0b[01]+|(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?)\b)/g;
4182
+ TOKEN_RE = /(?<blockComment>\/\*[\s\S]*?(?:\*\/|$))|(?<lineComment>\/\/[^\n]*)|(?<dstring>"(?:[^"\\]|\\.)*"?)|(?<sstring>'(?:[^'\\]|\\.)*'?)|(?<keyword>\b(?:when|unless|while|for|do|end|in|of|and|or|nor|else|break|continue|delete|self)(?![a-zA-Z0-9_-]))|(?<literal>\b(?:true|false|null|undefined|nan)(?![a-zA-Z0-9_-])|-?\binf\b)|(?<typePred>\b(?:string|number|object|array|boolean)(?![a-zA-Z0-9_-]))|(?<num>\b(?:0x[0-9a-fA-F]+|0b[01]+|(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?)\b)/g;
3857
4183
  REXC_DIGITS = new Set("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_");
3858
4184
  JSON_TOKEN_RE = /(?<key>"(?:[^"\\]|\\.)*")\s*:|(?<string>"(?:[^"\\]|\\.)*")|(?<number>-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?)\b|(?<bool>true|false)|(?<null>null)|(?<brace>[{}[\]])|(?<punct>[:,])/g;
3859
4185
  KEYWORDS = [
@@ -3867,6 +4193,7 @@ var init_rex_repl = __esm(() => {
3867
4193
  "of",
3868
4194
  "and",
3869
4195
  "or",
4196
+ "nor",
3870
4197
  "else",
3871
4198
  "break",
3872
4199
  "continue",
@@ -3893,6 +4220,7 @@ import { dirname, resolve } from "node:path";
3893
4220
  import { readFile, writeFile } from "node:fs/promises";
3894
4221
  function parseArgs(argv) {
3895
4222
  const options = {
4223
+ sources: [],
3896
4224
  compile: false,
3897
4225
  ir: false,
3898
4226
  minifyNames: false,
@@ -3938,7 +4266,7 @@ function parseArgs(argv) {
3938
4266
  const value = argv[index + 1];
3939
4267
  if (!value)
3940
4268
  throw new Error("Missing value for --expr");
3941
- options.expr = value;
4269
+ options.sources.push({ type: "expr", value });
3942
4270
  index += 1;
3943
4271
  continue;
3944
4272
  }
@@ -3946,7 +4274,7 @@ function parseArgs(argv) {
3946
4274
  const value = argv[index + 1];
3947
4275
  if (!value)
3948
4276
  throw new Error("Missing value for --file");
3949
- options.file = value;
4277
+ options.sources.push({ type: "file", path: value });
3950
4278
  index += 1;
3951
4279
  continue;
3952
4280
  }
@@ -3959,9 +4287,7 @@ function parseArgs(argv) {
3959
4287
  continue;
3960
4288
  }
3961
4289
  if (!arg.startsWith("-")) {
3962
- if (options.file)
3963
- throw new Error("Multiple file arguments provided");
3964
- options.file = arg;
4290
+ options.sources.push({ type: "file", path: arg });
3965
4291
  continue;
3966
4292
  }
3967
4293
  throw new Error(`Unknown option: ${arg}`);
@@ -3979,11 +4305,18 @@ function usage() {
3979
4305
  " cat input.rex | rex Evaluate from stdin",
3980
4306
  " rex -c input.rex Compile to rexc bytecode",
3981
4307
  "",
4308
+ " Sources are concatenated in order, so flags and files can be mixed:",
4309
+ " rex -e 'max = 200' primes.rex Set max before running script",
4310
+ " rex primes.rex -e '42' Run script, then evaluate 42",
4311
+ "",
3982
4312
  "Input:",
3983
4313
  " <file> Evaluate/compile a Rex source file",
3984
4314
  " -e, --expr <source> Evaluate/compile an inline expression",
3985
4315
  " -f, --file <path> Evaluate/compile source from a file",
3986
4316
  "",
4317
+ " Multiple -e and -f flags (and positional files) can be combined.",
4318
+ " They are concatenated in the order they appear on the command line.",
4319
+ "",
3987
4320
  "Output mode:",
3988
4321
  " (default) Evaluate and output result as JSON",
3989
4322
  " -c, --compile Compile to rexc bytecode",
@@ -4008,12 +4341,17 @@ async function readStdin() {
4008
4341
  return Buffer.concat(chunks).toString("utf8");
4009
4342
  }
4010
4343
  async function resolveSource(options) {
4011
- if (options.expr && options.file)
4012
- throw new Error("Use only one of --expr, --file, or a positional file path");
4013
- if (options.expr)
4014
- return options.expr;
4015
- if (options.file)
4016
- return readFile(options.file, "utf8");
4344
+ if (options.sources.length > 0) {
4345
+ const parts = [];
4346
+ for (const seg of options.sources) {
4347
+ if (seg.type === "expr")
4348
+ parts.push(seg.value);
4349
+ else
4350
+ parts.push(await readFile(seg.path, "utf8"));
4351
+ }
4352
+ return parts.join(`
4353
+ `);
4354
+ }
4017
4355
  if (!process.stdin.isTTY) {
4018
4356
  const piped = await readStdin();
4019
4357
  if (piped.trim().length > 0)
@@ -4021,6 +4359,13 @@ async function resolveSource(options) {
4021
4359
  }
4022
4360
  throw new Error("No input provided. Use a file path, --expr, or pipe source via stdin.");
4023
4361
  }
4362
+ function findFirstFilePath(sources) {
4363
+ for (const seg of sources) {
4364
+ if (seg.type === "file")
4365
+ return seg.path;
4366
+ }
4367
+ return;
4368
+ }
4024
4369
  async function loadDomainConfigFromFolder(folderPath) {
4025
4370
  const configPath = resolve(folderPath, ".config.rex");
4026
4371
  try {
@@ -4032,7 +4377,8 @@ async function loadDomainConfigFromFolder(folderPath) {
4032
4377
  }
4033
4378
  }
4034
4379
  async function resolveDomainConfig(options) {
4035
- const baseFolder = options.file ? dirname(resolve(options.file)) : process.cwd();
4380
+ const filePath = findFirstFilePath(options.sources);
4381
+ const baseFolder = filePath ? dirname(resolve(filePath)) : process.cwd();
4036
4382
  return loadDomainConfigFromFolder(baseFolder);
4037
4383
  }
4038
4384
  async function main() {
@@ -4041,7 +4387,7 @@ async function main() {
4041
4387
  console.log(usage());
4042
4388
  return;
4043
4389
  }
4044
- const hasSource = options.expr || options.file || !process.stdin.isTTY;
4390
+ const hasSource = options.sources.length > 0 || !process.stdin.isTTY;
4045
4391
  if (!hasSource && !options.compile && !options.ir) {
4046
4392
  const { startRepl: startRepl2 } = await Promise.resolve().then(() => (init_rex_repl(), exports_rex_repl));
4047
4393
  await startRepl2();