@creationix/rex 0.3.1 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/rex-cli.js +615 -269
- package/rex-cli.ts +32 -11
- package/rex-repl.js +582 -256
- package/rex-repl.ts +4 -3
- package/rex.js +310 -180
- package/rex.ohm +50 -22
- package/rex.ohm-bundle.cjs +1 -1
- package/rex.ohm-bundle.d.ts +26 -8
- package/rex.ohm-bundle.js +1 -1
- package/rex.ts +342 -201
- package/rexc-interpreter.ts +262 -85
package/rex-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 "
|
|
118
|
+
return "nan'";
|
|
119
119
|
if (numberValue === Infinity)
|
|
120
|
-
return "
|
|
120
|
+
return "inf'";
|
|
121
121
|
if (numberValue === -Infinity)
|
|
122
|
-
return "
|
|
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 `${
|
|
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 "
|
|
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
|
-
|
|
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
|
|
211
|
-
|
|
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
|
|
222
|
+
return encodeDomainLookup(shortCode, tag);
|
|
216
223
|
}
|
|
217
|
-
const parts2 = [
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
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
|
|
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 `${
|
|
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 ? "
|
|
331
|
+
return node.value ? "tr'" : "fl'";
|
|
302
332
|
case "null":
|
|
303
|
-
return "
|
|
333
|
+
return "nl'";
|
|
304
334
|
case "undefined":
|
|
305
|
-
return "
|
|
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
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
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
|
-
|
|
556
|
-
|
|
557
|
-
|
|
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
|
|
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 [
|
|
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
|
|
600
|
-
if (
|
|
601
|
-
throw new Error(`Conflicting refs for '${rawName}': ${
|
|
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] =
|
|
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 ===
|
|
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 ${
|
|
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] =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
type: "binding:
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
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
|
|
2098
|
+
const domainMaps = options?.domainConfig ? domainRefsFromConfig(options.domainConfig) : undefined;
|
|
2024
2099
|
return encodeIR(lowered, {
|
|
2025
|
-
|
|
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,
|
|
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:
|
|
2117
|
-
add:
|
|
2118
|
-
sub:
|
|
2119
|
-
mul:
|
|
2120
|
-
div:
|
|
2121
|
-
eq:
|
|
2122
|
-
neq:
|
|
2123
|
-
lt:
|
|
2124
|
-
lte:
|
|
2125
|
-
gt:
|
|
2126
|
-
gte:
|
|
2127
|
-
and:
|
|
2128
|
-
or:
|
|
2129
|
-
xor:
|
|
2130
|
-
not:
|
|
2131
|
-
boolean:
|
|
2132
|
-
number:
|
|
2133
|
-
string:
|
|
2134
|
-
array:
|
|
2135
|
-
object:
|
|
2136
|
-
mod:
|
|
2137
|
-
neg:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
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 [
|
|
2656
|
+
for (const [key, op] of Object.entries(ctx.opcodes)) {
|
|
2533
2657
|
if (op)
|
|
2534
|
-
this.customOpcodes.set(
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
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 === "
|
|
2922
|
-
const
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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 =
|
|
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 =
|
|
3236
|
-
const
|
|
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[
|
|
3239
|
-
target = target[
|
|
3522
|
+
target[prop] = {};
|
|
3523
|
+
target = target[prop];
|
|
3240
3524
|
}
|
|
3241
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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:
|
|
3351
|
-
add:
|
|
3352
|
-
sub:
|
|
3353
|
-
mul:
|
|
3354
|
-
div:
|
|
3355
|
-
eq:
|
|
3356
|
-
neq:
|
|
3357
|
-
lt:
|
|
3358
|
-
lte:
|
|
3359
|
-
gt:
|
|
3360
|
-
gte:
|
|
3361
|
-
and:
|
|
3362
|
-
or:
|
|
3363
|
-
xor:
|
|
3364
|
-
not:
|
|
3365
|
-
boolean:
|
|
3366
|
-
number:
|
|
3367
|
-
string:
|
|
3368
|
-
array:
|
|
3369
|
-
object:
|
|
3370
|
-
mod:
|
|
3371
|
-
neg:
|
|
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
|
|
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
|
|
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
|
-
|
|
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.
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
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
|
|
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.
|
|
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();
|