@creationix/rex 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -2
- package/rex-cli.js +730 -285
- package/rex-cli.ts +34 -13
- package/rex-repl.js +695 -270
- package/rex-repl.ts +39 -5
- package/rex.js +370 -188
- package/rex.ohm +73 -25
- package/rex.ohm-bundle.cjs +1 -1
- package/rex.ohm-bundle.d.ts +31 -8
- package/rex.ohm-bundle.js +1 -1
- package/rex.ts +390 -206
- package/rexc-interpreter.ts +277 -90
package/rex-cli.js
CHANGED
|
@@ -100,12 +100,32 @@ function encodeBareOrLengthString(value) {
|
|
|
100
100
|
return `${value}:`;
|
|
101
101
|
return `${encodeUint(byteLength(value))},${value}`;
|
|
102
102
|
}
|
|
103
|
+
function splitDecimal(num) {
|
|
104
|
+
const match = num.toExponential().match(DEC_PARTS);
|
|
105
|
+
if (!match)
|
|
106
|
+
throw new Error(`Failed to split decimal for ${num}`);
|
|
107
|
+
const [, b1, b2 = "", e1] = match;
|
|
108
|
+
const base = Number.parseInt(b1 + b2, 10);
|
|
109
|
+
const exp = Number.parseInt(e1, 10) - b2.length;
|
|
110
|
+
return { base, exp };
|
|
111
|
+
}
|
|
112
|
+
function encodeDecimal(significand, power) {
|
|
113
|
+
return `${encodeZigzag(power)}*${encodeInt(significand)}`;
|
|
114
|
+
}
|
|
103
115
|
function encodeNumberNode(node) {
|
|
104
116
|
const numberValue = node.value;
|
|
105
|
-
if (
|
|
106
|
-
|
|
107
|
-
if (
|
|
108
|
-
return
|
|
117
|
+
if (Number.isNaN(numberValue))
|
|
118
|
+
return "nan'";
|
|
119
|
+
if (numberValue === Infinity)
|
|
120
|
+
return "inf'";
|
|
121
|
+
if (numberValue === -Infinity)
|
|
122
|
+
return "nif'";
|
|
123
|
+
if (Number.isInteger(numberValue)) {
|
|
124
|
+
const { base, exp } = splitDecimal(numberValue);
|
|
125
|
+
if (exp >= 0 && exp <= 4)
|
|
126
|
+
return encodeInt(numberValue);
|
|
127
|
+
return encodeDecimal(base, exp);
|
|
128
|
+
}
|
|
109
129
|
const raw = node.raw.toLowerCase();
|
|
110
130
|
const sign = raw.startsWith("-") ? -1 : 1;
|
|
111
131
|
const unsigned = sign < 0 ? raw.slice(1) : raw;
|
|
@@ -128,10 +148,10 @@ function encodeNumberNode(node) {
|
|
|
128
148
|
significand /= 10;
|
|
129
149
|
power += 1;
|
|
130
150
|
}
|
|
131
|
-
return
|
|
151
|
+
return encodeDecimal(significand, power);
|
|
132
152
|
}
|
|
133
153
|
function encodeOpcode(opcode) {
|
|
134
|
-
return `${
|
|
154
|
+
return `${OPCODE_IDS[opcode]}%`;
|
|
135
155
|
}
|
|
136
156
|
function encodeCallParts(parts) {
|
|
137
157
|
return `(${parts.join("")})`;
|
|
@@ -148,7 +168,7 @@ function addOptionalPrefix(encoded) {
|
|
|
148
168
|
let payload = encoded;
|
|
149
169
|
if (encoded.startsWith("?(") || encoded.startsWith("!(") || encoded.startsWith("|(") || encoded.startsWith("&(") || encoded.startsWith(">(") || encoded.startsWith("<(") || encoded.startsWith("#(")) {
|
|
150
170
|
payload = encoded.slice(2, -1);
|
|
151
|
-
} else if (encoded.startsWith(">[") || encoded.startsWith(">{")) {
|
|
171
|
+
} else if (encoded.startsWith(">[") || encoded.startsWith(">{") || encoded.startsWith("<[") || encoded.startsWith("<{") || encoded.startsWith("#[") || encoded.startsWith("#{")) {
|
|
152
172
|
payload = encoded.slice(2, -1);
|
|
153
173
|
} else if (encoded.startsWith("[") || encoded.startsWith("{") || encoded.startsWith("(")) {
|
|
154
174
|
payload = encoded.slice(1, -1);
|
|
@@ -159,7 +179,7 @@ function addOptionalPrefix(encoded) {
|
|
|
159
179
|
}
|
|
160
180
|
function encodeBlockExpression(block) {
|
|
161
181
|
if (block.length === 0)
|
|
162
|
-
return "
|
|
182
|
+
return "un'";
|
|
163
183
|
if (block.length === 1)
|
|
164
184
|
return encodeNode(block[0]);
|
|
165
185
|
return encodeCallParts([encodeOpcode("do"), ...block.map((node) => encodeNode(node))]);
|
|
@@ -176,9 +196,13 @@ function encodeConditionalElse(elseBranch) {
|
|
|
176
196
|
};
|
|
177
197
|
return encodeNode(nested);
|
|
178
198
|
}
|
|
199
|
+
function encodeDomainLookup(shortCode, tag) {
|
|
200
|
+
return `${shortCode}${tag}`;
|
|
201
|
+
}
|
|
179
202
|
function encodeNavigation(node) {
|
|
180
203
|
const domainRefs = activeEncodeOptions?.domainRefs;
|
|
181
|
-
|
|
204
|
+
const domainOpcodes = activeEncodeOptions?.domainOpcodes;
|
|
205
|
+
if ((domainRefs || domainOpcodes) && node.target.type === "identifier") {
|
|
182
206
|
const staticPath = [node.target.name];
|
|
183
207
|
for (const segment of node.segments) {
|
|
184
208
|
if (segment.type !== "static")
|
|
@@ -187,14 +211,17 @@ function encodeNavigation(node) {
|
|
|
187
211
|
}
|
|
188
212
|
for (let pathLength = staticPath.length;pathLength >= 1; pathLength -= 1) {
|
|
189
213
|
const dottedName = staticPath.slice(0, pathLength).join(".");
|
|
190
|
-
const
|
|
191
|
-
|
|
214
|
+
const refCode = domainRefs?.[dottedName];
|
|
215
|
+
const opcodeCode = domainOpcodes?.[dottedName];
|
|
216
|
+
const shortCode = refCode ?? opcodeCode;
|
|
217
|
+
if (shortCode === undefined)
|
|
192
218
|
continue;
|
|
219
|
+
const tag = refCode !== undefined ? "'" : "%";
|
|
193
220
|
const consumedStaticSegments = pathLength - 1;
|
|
194
221
|
if (consumedStaticSegments === node.segments.length) {
|
|
195
|
-
return
|
|
222
|
+
return encodeDomainLookup(shortCode, tag);
|
|
196
223
|
}
|
|
197
|
-
const parts2 = [
|
|
224
|
+
const parts2 = [encodeDomainLookup(shortCode, tag)];
|
|
198
225
|
for (const segment of node.segments.slice(consumedStaticSegments)) {
|
|
199
226
|
if (segment.type === "static")
|
|
200
227
|
parts2.push(encodeBareOrLengthString(segment.key));
|
|
@@ -220,9 +247,12 @@ function encodeWhile(node) {
|
|
|
220
247
|
}
|
|
221
248
|
function encodeFor(node) {
|
|
222
249
|
const body = addOptionalPrefix(encodeBlockExpression(node.body));
|
|
223
|
-
if (node.binding.type === "binding:
|
|
250
|
+
if (node.binding.type === "binding:bareIn") {
|
|
224
251
|
return `>(${encodeNode(node.binding.source)}${body})`;
|
|
225
252
|
}
|
|
253
|
+
if (node.binding.type === "binding:bareOf") {
|
|
254
|
+
return `<(${encodeNode(node.binding.source)}${body})`;
|
|
255
|
+
}
|
|
226
256
|
if (node.binding.type === "binding:valueIn") {
|
|
227
257
|
return `>(${encodeNode(node.binding.source)}${node.binding.value}$${body})`;
|
|
228
258
|
}
|
|
@@ -233,30 +263,47 @@ function encodeFor(node) {
|
|
|
233
263
|
}
|
|
234
264
|
function encodeArrayComprehension(node) {
|
|
235
265
|
const body = addOptionalPrefix(encodeNode(node.body));
|
|
236
|
-
if (node.binding.type === "binding:
|
|
266
|
+
if (node.binding.type === "binding:bareIn") {
|
|
237
267
|
return `>[${encodeNode(node.binding.source)}${body}]`;
|
|
238
268
|
}
|
|
269
|
+
if (node.binding.type === "binding:bareOf") {
|
|
270
|
+
return `<[${encodeNode(node.binding.source)}${body}]`;
|
|
271
|
+
}
|
|
239
272
|
if (node.binding.type === "binding:valueIn") {
|
|
240
273
|
return `>[${encodeNode(node.binding.source)}${node.binding.value}$${body}]`;
|
|
241
274
|
}
|
|
242
275
|
if (node.binding.type === "binding:keyValueIn") {
|
|
243
276
|
return `>[${encodeNode(node.binding.source)}${node.binding.key}$${node.binding.value}$${body}]`;
|
|
244
277
|
}
|
|
245
|
-
return
|
|
278
|
+
return `<[${encodeNode(node.binding.source)}${node.binding.key}$${body}]`;
|
|
246
279
|
}
|
|
247
280
|
function encodeObjectComprehension(node) {
|
|
248
281
|
const key = addOptionalPrefix(encodeNode(node.key));
|
|
249
282
|
const value = addOptionalPrefix(encodeNode(node.value));
|
|
250
|
-
if (node.binding.type === "binding:
|
|
283
|
+
if (node.binding.type === "binding:bareIn") {
|
|
251
284
|
return `>{${encodeNode(node.binding.source)}${key}${value}}`;
|
|
252
285
|
}
|
|
286
|
+
if (node.binding.type === "binding:bareOf") {
|
|
287
|
+
return `<{${encodeNode(node.binding.source)}${key}${value}}`;
|
|
288
|
+
}
|
|
253
289
|
if (node.binding.type === "binding:valueIn") {
|
|
254
290
|
return `>{${encodeNode(node.binding.source)}${node.binding.value}$${key}${value}}`;
|
|
255
291
|
}
|
|
256
292
|
if (node.binding.type === "binding:keyValueIn") {
|
|
257
293
|
return `>{${encodeNode(node.binding.source)}${node.binding.key}$${node.binding.value}$${key}${value}}`;
|
|
258
294
|
}
|
|
259
|
-
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}}`;
|
|
260
307
|
}
|
|
261
308
|
function encodeNode(node) {
|
|
262
309
|
switch (node.type) {
|
|
@@ -265,7 +312,10 @@ function encodeNode(node) {
|
|
|
265
312
|
case "identifier": {
|
|
266
313
|
const domainRef = activeEncodeOptions?.domainRefs?.[node.name];
|
|
267
314
|
if (domainRef !== undefined)
|
|
268
|
-
return `${
|
|
315
|
+
return `${domainRef}'`;
|
|
316
|
+
const domainOpcode = activeEncodeOptions?.domainOpcodes?.[node.name];
|
|
317
|
+
if (domainOpcode !== undefined)
|
|
318
|
+
return `${domainOpcode}%`;
|
|
269
319
|
return `${node.name}$`;
|
|
270
320
|
}
|
|
271
321
|
case "self":
|
|
@@ -278,11 +328,11 @@ function encodeNode(node) {
|
|
|
278
328
|
return `${encodeUint(node.depth - 1)}@`;
|
|
279
329
|
}
|
|
280
330
|
case "boolean":
|
|
281
|
-
return node.value ? "
|
|
331
|
+
return node.value ? "tr'" : "fl'";
|
|
282
332
|
case "null":
|
|
283
|
-
return "
|
|
333
|
+
return "nl'";
|
|
284
334
|
case "undefined":
|
|
285
|
-
return "
|
|
335
|
+
return "un'";
|
|
286
336
|
case "number":
|
|
287
337
|
return encodeNumberNode(node);
|
|
288
338
|
case "string":
|
|
@@ -293,12 +343,16 @@ function encodeNode(node) {
|
|
|
293
343
|
}
|
|
294
344
|
case "arrayComprehension":
|
|
295
345
|
return encodeArrayComprehension(node);
|
|
346
|
+
case "whileArrayComprehension":
|
|
347
|
+
return encodeWhileArrayComprehension(node);
|
|
296
348
|
case "object": {
|
|
297
349
|
const body = node.entries.map(({ key, value }) => `${encodeNode(key)}${addOptionalPrefix(encodeNode(value))}`).join("");
|
|
298
350
|
return `{${body}}`;
|
|
299
351
|
}
|
|
300
352
|
case "objectComprehension":
|
|
301
353
|
return encodeObjectComprehension(node);
|
|
354
|
+
case "whileObjectComprehension":
|
|
355
|
+
return encodeWhileObjectComprehension(node);
|
|
302
356
|
case "key":
|
|
303
357
|
return encodeBareOrLengthString(node.name);
|
|
304
358
|
case "group":
|
|
@@ -308,6 +362,10 @@ function encodeNode(node) {
|
|
|
308
362
|
return `~${encodeNode(node.value)}`;
|
|
309
363
|
if (node.op === "neg")
|
|
310
364
|
return encodeCallParts([encodeOpcode("neg"), encodeNode(node.value)]);
|
|
365
|
+
if (node.op === "logicalNot") {
|
|
366
|
+
const val = encodeNode(node.value);
|
|
367
|
+
return `!(${val}tr')`;
|
|
368
|
+
}
|
|
311
369
|
return encodeCallParts([encodeOpcode("not"), encodeNode(node.value)]);
|
|
312
370
|
case "binary":
|
|
313
371
|
if (node.op === "and") {
|
|
@@ -326,12 +384,19 @@ function encodeNode(node) {
|
|
|
326
384
|
}).join("");
|
|
327
385
|
return `|(${body})`;
|
|
328
386
|
}
|
|
387
|
+
if (node.op === "nor") {
|
|
388
|
+
const left = encodeNode(node.left);
|
|
389
|
+
const right = addOptionalPrefix(encodeNode(node.right));
|
|
390
|
+
return `!(${left}${right})`;
|
|
391
|
+
}
|
|
329
392
|
return encodeCallParts([
|
|
330
393
|
encodeOpcode(BINARY_TO_OPCODE[node.op]),
|
|
331
394
|
encodeNode(node.left),
|
|
332
395
|
encodeNode(node.right)
|
|
333
396
|
]);
|
|
334
397
|
case "assign": {
|
|
398
|
+
if (node.op === ":=")
|
|
399
|
+
return `/${encodeNode(node.place)}${addOptionalPrefix(encodeNode(node.value))}`;
|
|
335
400
|
if (node.op === "=")
|
|
336
401
|
return `=${encodeNode(node.place)}${addOptionalPrefix(encodeNode(node.value))}`;
|
|
337
402
|
const opcode = ASSIGN_COMPOUND_TO_OPCODE[node.op];
|
|
@@ -342,8 +407,12 @@ function encodeNode(node) {
|
|
|
342
407
|
}
|
|
343
408
|
case "navigation":
|
|
344
409
|
return encodeNavigation(node);
|
|
345
|
-
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
|
+
}
|
|
346
414
|
return encodeCallParts([encodeNode(node.callee), ...node.args.map((arg) => encodeNode(arg))]);
|
|
415
|
+
}
|
|
347
416
|
case "conditional": {
|
|
348
417
|
const opener = node.head === "when" ? "?(" : "!(";
|
|
349
418
|
const cond = encodeNode(node.condition);
|
|
@@ -351,6 +420,8 @@ function encodeNode(node) {
|
|
|
351
420
|
const elseExpr = node.elseBranch ? addOptionalPrefix(encodeConditionalElse(node.elseBranch)) : "";
|
|
352
421
|
return `${opener}${cond}${thenExpr}${elseExpr})`;
|
|
353
422
|
}
|
|
423
|
+
case "range":
|
|
424
|
+
return encodeCallParts([encodeOpcode("range"), encodeNode(node.from), encodeNode(node.to)]);
|
|
354
425
|
case "for":
|
|
355
426
|
return encodeFor(node);
|
|
356
427
|
case "while":
|
|
@@ -429,6 +500,18 @@ function isPlainObject(value) {
|
|
|
429
500
|
function isBareKeyName(key) {
|
|
430
501
|
return /^[A-Za-z_][A-Za-z0-9_-]*$/.test(key);
|
|
431
502
|
}
|
|
503
|
+
function isNumericKey(key) {
|
|
504
|
+
if (key === "")
|
|
505
|
+
return false;
|
|
506
|
+
return String(Number(key)) === key && Number.isFinite(Number(key));
|
|
507
|
+
}
|
|
508
|
+
function stringifyKey(key) {
|
|
509
|
+
if (isBareKeyName(key))
|
|
510
|
+
return key;
|
|
511
|
+
if (isNumericKey(key))
|
|
512
|
+
return key;
|
|
513
|
+
return stringifyString(key);
|
|
514
|
+
}
|
|
432
515
|
function stringifyString(value) {
|
|
433
516
|
return JSON.stringify(value);
|
|
434
517
|
}
|
|
@@ -440,8 +523,12 @@ function stringifyInline(value) {
|
|
|
440
523
|
if (typeof value === "boolean")
|
|
441
524
|
return value ? "true" : "false";
|
|
442
525
|
if (typeof value === "number") {
|
|
443
|
-
if (
|
|
444
|
-
|
|
526
|
+
if (Number.isNaN(value))
|
|
527
|
+
return "nan";
|
|
528
|
+
if (value === Infinity)
|
|
529
|
+
return "inf";
|
|
530
|
+
if (value === -Infinity)
|
|
531
|
+
return "-inf";
|
|
445
532
|
return String(value);
|
|
446
533
|
}
|
|
447
534
|
if (typeof value === "string")
|
|
@@ -455,7 +542,7 @@ function stringifyInline(value) {
|
|
|
455
542
|
const entries = Object.entries(value);
|
|
456
543
|
if (entries.length === 0)
|
|
457
544
|
return "{}";
|
|
458
|
-
const body = entries.map(([key, item]) => `${
|
|
545
|
+
const body = entries.map(([key, item]) => `${stringifyKey(key)}: ${stringifyInline(item)}`).join(" ");
|
|
459
546
|
return `{${body}}`;
|
|
460
547
|
}
|
|
461
548
|
throw new Error(`Rex stringify() cannot encode value of type ${typeof value}`);
|
|
@@ -492,7 +579,7 @@ ${indent}]`;
|
|
|
492
579
|
if (entries.length === 0)
|
|
493
580
|
return "{}";
|
|
494
581
|
const lines = entries.map(([key, item]) => {
|
|
495
|
-
const keyText =
|
|
582
|
+
const keyText = stringifyKey(key);
|
|
496
583
|
const rendered = stringifyPretty(item, depth + 1, indentSize, maxWidth);
|
|
497
584
|
return `${childIndent}${keyText}: ${rendered}`;
|
|
498
585
|
});
|
|
@@ -510,78 +597,55 @@ function domainRefsFromConfig(config) {
|
|
|
510
597
|
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
511
598
|
throw new Error("Domain config must be an object");
|
|
512
599
|
}
|
|
513
|
-
const
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
return refs;
|
|
520
|
-
}
|
|
521
|
-
function decodeDomainRefKey(refText) {
|
|
522
|
-
if (!refText)
|
|
523
|
-
throw new Error("Domain ref key cannot be empty");
|
|
524
|
-
if (!/^[0-9A-Za-z_-]+$/.test(refText)) {
|
|
525
|
-
throw new Error(`Invalid domain ref key '${refText}' (must use base64 alphabet 0-9a-zA-Z-_)`);
|
|
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);
|
|
526
606
|
}
|
|
527
|
-
|
|
528
|
-
|
|
607
|
+
const functionsSection = configObj.functions;
|
|
608
|
+
if (functionsSection && typeof functionsSection === "object" && !Array.isArray(functionsSection)) {
|
|
609
|
+
mapConfigEntries(functionsSection, domainOpcodes);
|
|
529
610
|
}
|
|
530
|
-
|
|
531
|
-
throw new Error(`Invalid domain ref key '${refText}' (reserved by core language)`);
|
|
532
|
-
}
|
|
533
|
-
let value = 0;
|
|
534
|
-
for (const char of refText) {
|
|
535
|
-
const digit = DOMAIN_DIGIT_INDEX.get(char);
|
|
536
|
-
if (digit === undefined)
|
|
537
|
-
throw new Error(`Invalid domain ref key '${refText}'`);
|
|
538
|
-
value = value * 64 + digit;
|
|
539
|
-
if (value > Number.MAX_SAFE_INTEGER) {
|
|
540
|
-
throw new Error(`Invalid domain ref key '${refText}' (must fit in 53 bits)`);
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
if (value < FIRST_NON_RESERVED_REF) {
|
|
544
|
-
throw new Error(`Invalid domain ref key '${refText}' (maps to reserved id ${value})`);
|
|
545
|
-
}
|
|
546
|
-
return value;
|
|
611
|
+
return { domainRefs, domainOpcodes };
|
|
547
612
|
}
|
|
548
613
|
function mapConfigEntries(entries, refs) {
|
|
549
614
|
const sourceKindByRoot = new Map;
|
|
550
615
|
for (const root of Object.keys(refs)) {
|
|
551
616
|
sourceKindByRoot.set(root, "explicit");
|
|
552
617
|
}
|
|
553
|
-
for (const [
|
|
618
|
+
for (const [shortCode, rawEntry] of Object.entries(entries)) {
|
|
554
619
|
const entry = rawEntry;
|
|
555
620
|
if (!entry || typeof entry !== "object")
|
|
556
621
|
continue;
|
|
557
622
|
if (!Array.isArray(entry.names))
|
|
558
623
|
continue;
|
|
559
|
-
const refId = decodeDomainRefKey(refText);
|
|
560
624
|
for (const rawName of entry.names) {
|
|
561
625
|
if (typeof rawName !== "string")
|
|
562
626
|
continue;
|
|
563
|
-
const
|
|
564
|
-
if (
|
|
565
|
-
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}`);
|
|
566
630
|
}
|
|
567
|
-
refs[rawName] =
|
|
631
|
+
refs[rawName] = shortCode;
|
|
568
632
|
const root = rawName.split(".")[0];
|
|
569
633
|
if (!root)
|
|
570
634
|
continue;
|
|
571
635
|
const currentKind = rawName.includes(".") ? "implicit" : "explicit";
|
|
572
636
|
const existing = refs[root];
|
|
573
637
|
if (existing !== undefined) {
|
|
574
|
-
if (existing ===
|
|
638
|
+
if (existing === shortCode)
|
|
575
639
|
continue;
|
|
576
640
|
const existingKind = sourceKindByRoot.get(root) ?? "explicit";
|
|
577
641
|
if (currentKind === "explicit") {
|
|
578
|
-
throw new Error(`Conflicting refs for '${root}': ${existing} vs ${
|
|
642
|
+
throw new Error(`Conflicting refs for '${root}': ${existing} vs ${shortCode}`);
|
|
579
643
|
}
|
|
580
644
|
if (existingKind === "explicit")
|
|
581
645
|
continue;
|
|
582
646
|
continue;
|
|
583
647
|
}
|
|
584
|
-
refs[root] =
|
|
648
|
+
refs[root] = shortCode;
|
|
585
649
|
sourceKindByRoot.set(root, currentKind);
|
|
586
650
|
}
|
|
587
651
|
}
|
|
@@ -863,6 +927,21 @@ function dropBindingNames(env, binding) {
|
|
|
863
927
|
clearBinding(env, binding.key);
|
|
864
928
|
}
|
|
865
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
|
+
}
|
|
866
945
|
function collectReads(node, out) {
|
|
867
946
|
switch (node.type) {
|
|
868
947
|
case "identifier":
|
|
@@ -885,11 +964,20 @@ function collectReads(node, out) {
|
|
|
885
964
|
collectReads(node.binding.source, out);
|
|
886
965
|
collectReads(node.body, out);
|
|
887
966
|
return;
|
|
967
|
+
case "whileArrayComprehension":
|
|
968
|
+
collectReads(node.condition, out);
|
|
969
|
+
collectReads(node.body, out);
|
|
970
|
+
return;
|
|
888
971
|
case "objectComprehension":
|
|
889
972
|
collectReads(node.binding.source, out);
|
|
890
973
|
collectReads(node.key, out);
|
|
891
974
|
collectReads(node.value, out);
|
|
892
975
|
return;
|
|
976
|
+
case "whileObjectComprehension":
|
|
977
|
+
collectReads(node.condition, out);
|
|
978
|
+
collectReads(node.key, out);
|
|
979
|
+
collectReads(node.value, out);
|
|
980
|
+
return;
|
|
893
981
|
case "unary":
|
|
894
982
|
collectReads(node.value, out);
|
|
895
983
|
return;
|
|
@@ -926,6 +1014,10 @@ function collectReads(node, out) {
|
|
|
926
1014
|
for (const part of node.body)
|
|
927
1015
|
collectReads(part, out);
|
|
928
1016
|
return;
|
|
1017
|
+
case "range":
|
|
1018
|
+
collectReads(node.from, out);
|
|
1019
|
+
collectReads(node.to, out);
|
|
1020
|
+
return;
|
|
929
1021
|
case "program":
|
|
930
1022
|
for (const part of node.body)
|
|
931
1023
|
collectReads(part, out);
|
|
@@ -970,6 +1062,8 @@ function isPureNode(node) {
|
|
|
970
1062
|
return node.op !== "delete" && isPureNode(node.value);
|
|
971
1063
|
case "binary":
|
|
972
1064
|
return isPureNode(node.left) && isPureNode(node.right);
|
|
1065
|
+
case "range":
|
|
1066
|
+
return isPureNode(node.from) && isPureNode(node.to);
|
|
973
1067
|
default:
|
|
974
1068
|
return false;
|
|
975
1069
|
}
|
|
@@ -1034,6 +1128,8 @@ function hasIdentifierRead(node, name, asPlace = false) {
|
|
|
1034
1128
|
return hasIdentifierRead(node.value, name, node.op === "delete");
|
|
1035
1129
|
case "binary":
|
|
1036
1130
|
return hasIdentifierRead(node.left, name) || hasIdentifierRead(node.right, name);
|
|
1131
|
+
case "range":
|
|
1132
|
+
return hasIdentifierRead(node.from, name) || hasIdentifierRead(node.to, name);
|
|
1037
1133
|
case "assign":
|
|
1038
1134
|
return hasIdentifierRead(node.place, name, true) || hasIdentifierRead(node.value, name);
|
|
1039
1135
|
default:
|
|
@@ -1056,6 +1152,8 @@ function countIdentifierReads(node, name, asPlace = false) {
|
|
|
1056
1152
|
return countIdentifierReads(node.value, name, node.op === "delete");
|
|
1057
1153
|
case "binary":
|
|
1058
1154
|
return countIdentifierReads(node.left, name) + countIdentifierReads(node.right, name);
|
|
1155
|
+
case "range":
|
|
1156
|
+
return countIdentifierReads(node.from, name) + countIdentifierReads(node.to, name);
|
|
1059
1157
|
case "assign":
|
|
1060
1158
|
return countIdentifierReads(node.place, name, true) + countIdentifierReads(node.value, name);
|
|
1061
1159
|
default:
|
|
@@ -1110,6 +1208,12 @@ function replaceIdentifier(node, name, replacement, asPlace = false) {
|
|
|
1110
1208
|
place: replaceIdentifier(node.place, name, replacement, true),
|
|
1111
1209
|
value: replaceIdentifier(node.value, name, replacement)
|
|
1112
1210
|
};
|
|
1211
|
+
case "range":
|
|
1212
|
+
return {
|
|
1213
|
+
type: "range",
|
|
1214
|
+
from: replaceIdentifier(node.from, name, replacement),
|
|
1215
|
+
to: replaceIdentifier(node.to, name, replacement)
|
|
1216
|
+
};
|
|
1113
1217
|
default:
|
|
1114
1218
|
return node;
|
|
1115
1219
|
}
|
|
@@ -1150,7 +1254,16 @@ function inlineAdjacentPureAssignments(block) {
|
|
|
1150
1254
|
return out;
|
|
1151
1255
|
}
|
|
1152
1256
|
function toNumberNode(value) {
|
|
1153
|
-
|
|
1257
|
+
let raw;
|
|
1258
|
+
if (Number.isNaN(value))
|
|
1259
|
+
raw = "nan";
|
|
1260
|
+
else if (value === Infinity)
|
|
1261
|
+
raw = "inf";
|
|
1262
|
+
else if (value === -Infinity)
|
|
1263
|
+
raw = "-inf";
|
|
1264
|
+
else
|
|
1265
|
+
raw = String(value);
|
|
1266
|
+
return { type: "number", raw, value };
|
|
1154
1267
|
}
|
|
1155
1268
|
function toStringNode(value) {
|
|
1156
1269
|
return { type: "string", raw: JSON.stringify(value) };
|
|
@@ -1162,7 +1275,7 @@ function toLiteralNode(value) {
|
|
|
1162
1275
|
return { type: "null" };
|
|
1163
1276
|
if (typeof value === "boolean")
|
|
1164
1277
|
return { type: "boolean", value };
|
|
1165
|
-
if (typeof value === "number"
|
|
1278
|
+
if (typeof value === "number")
|
|
1166
1279
|
return toNumberNode(value);
|
|
1167
1280
|
if (typeof value === "string")
|
|
1168
1281
|
return toStringNode(value);
|
|
@@ -1245,6 +1358,9 @@ function foldUnary(op, value) {
|
|
|
1245
1358
|
return ~value;
|
|
1246
1359
|
return;
|
|
1247
1360
|
}
|
|
1361
|
+
if (op === "logicalNot") {
|
|
1362
|
+
return value === undefined ? true : undefined;
|
|
1363
|
+
}
|
|
1248
1364
|
return;
|
|
1249
1365
|
}
|
|
1250
1366
|
function foldBinary(op, left, right) {
|
|
@@ -1415,6 +1531,12 @@ function optimizeNode(node, env, currentDepth, asPlace = false) {
|
|
|
1415
1531
|
}
|
|
1416
1532
|
return { type: "binary", op: node.op, left, right };
|
|
1417
1533
|
}
|
|
1534
|
+
case "range":
|
|
1535
|
+
return {
|
|
1536
|
+
type: "range",
|
|
1537
|
+
from: optimizeNode(node.from, env, currentDepth),
|
|
1538
|
+
to: optimizeNode(node.to, env, currentDepth)
|
|
1539
|
+
};
|
|
1418
1540
|
case "navigation": {
|
|
1419
1541
|
const target = optimizeNode(node.target, env, currentDepth);
|
|
1420
1542
|
const segments = node.segments.map((segment) => segment.type === "static" ? segment : { type: "dynamic", key: optimizeNode(segment.key, env, currentDepth) });
|
|
@@ -1525,31 +1647,7 @@ function optimizeNode(node, env, currentDepth, asPlace = false) {
|
|
|
1525
1647
|
}
|
|
1526
1648
|
case "for": {
|
|
1527
1649
|
const sourceEnv = cloneOptimizeEnv(env);
|
|
1528
|
-
const binding = (
|
|
1529
|
-
if (node.binding.type === "binding:expr") {
|
|
1530
|
-
return { type: "binding:expr", source: optimizeNode(node.binding.source, sourceEnv, currentDepth) };
|
|
1531
|
-
}
|
|
1532
|
-
if (node.binding.type === "binding:valueIn") {
|
|
1533
|
-
return {
|
|
1534
|
-
type: "binding:valueIn",
|
|
1535
|
-
value: node.binding.value,
|
|
1536
|
-
source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
|
|
1537
|
-
};
|
|
1538
|
-
}
|
|
1539
|
-
if (node.binding.type === "binding:keyValueIn") {
|
|
1540
|
-
return {
|
|
1541
|
-
type: "binding:keyValueIn",
|
|
1542
|
-
key: node.binding.key,
|
|
1543
|
-
value: node.binding.value,
|
|
1544
|
-
source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
|
|
1545
|
-
};
|
|
1546
|
-
}
|
|
1547
|
-
return {
|
|
1548
|
-
type: "binding:keyOf",
|
|
1549
|
-
key: node.binding.key,
|
|
1550
|
-
source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
|
|
1551
|
-
};
|
|
1552
|
-
})();
|
|
1650
|
+
const binding = optimizeBinding(node.binding, sourceEnv, currentDepth);
|
|
1553
1651
|
const bodyEnv = cloneOptimizeEnv(env);
|
|
1554
1652
|
dropBindingNames(bodyEnv, binding);
|
|
1555
1653
|
return {
|
|
@@ -1560,20 +1658,7 @@ function optimizeNode(node, env, currentDepth, asPlace = false) {
|
|
|
1560
1658
|
}
|
|
1561
1659
|
case "arrayComprehension": {
|
|
1562
1660
|
const sourceEnv = cloneOptimizeEnv(env);
|
|
1563
|
-
const binding =
|
|
1564
|
-
type: "binding:valueIn",
|
|
1565
|
-
value: node.binding.value,
|
|
1566
|
-
source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
|
|
1567
|
-
} : node.binding.type === "binding:keyValueIn" ? {
|
|
1568
|
-
type: "binding:keyValueIn",
|
|
1569
|
-
key: node.binding.key,
|
|
1570
|
-
value: node.binding.value,
|
|
1571
|
-
source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
|
|
1572
|
-
} : {
|
|
1573
|
-
type: "binding:keyOf",
|
|
1574
|
-
key: node.binding.key,
|
|
1575
|
-
source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
|
|
1576
|
-
};
|
|
1661
|
+
const binding = optimizeBinding(node.binding, sourceEnv, currentDepth);
|
|
1577
1662
|
const bodyEnv = cloneOptimizeEnv(env);
|
|
1578
1663
|
dropBindingNames(bodyEnv, binding);
|
|
1579
1664
|
return {
|
|
@@ -1582,22 +1667,15 @@ function optimizeNode(node, env, currentDepth, asPlace = false) {
|
|
|
1582
1667
|
body: optimizeNode(node.body, bodyEnv, currentDepth + 1)
|
|
1583
1668
|
};
|
|
1584
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
|
+
};
|
|
1585
1676
|
case "objectComprehension": {
|
|
1586
1677
|
const sourceEnv = cloneOptimizeEnv(env);
|
|
1587
|
-
const binding =
|
|
1588
|
-
type: "binding:valueIn",
|
|
1589
|
-
value: node.binding.value,
|
|
1590
|
-
source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
|
|
1591
|
-
} : node.binding.type === "binding:keyValueIn" ? {
|
|
1592
|
-
type: "binding:keyValueIn",
|
|
1593
|
-
key: node.binding.key,
|
|
1594
|
-
value: node.binding.value,
|
|
1595
|
-
source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
|
|
1596
|
-
} : {
|
|
1597
|
-
type: "binding:keyOf",
|
|
1598
|
-
key: node.binding.key,
|
|
1599
|
-
source: optimizeNode(node.binding.source, sourceEnv, currentDepth)
|
|
1600
|
-
};
|
|
1678
|
+
const binding = optimizeBinding(node.binding, sourceEnv, currentDepth);
|
|
1601
1679
|
const bodyEnv = cloneOptimizeEnv(env);
|
|
1602
1680
|
dropBindingNames(bodyEnv, binding);
|
|
1603
1681
|
return {
|
|
@@ -1607,6 +1685,13 @@ function optimizeNode(node, env, currentDepth, asPlace = false) {
|
|
|
1607
1685
|
value: optimizeNode(node.value, bodyEnv, currentDepth + 1)
|
|
1608
1686
|
};
|
|
1609
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
|
+
};
|
|
1610
1695
|
default:
|
|
1611
1696
|
return node;
|
|
1612
1697
|
}
|
|
@@ -1658,6 +1743,10 @@ function collectLocalBindings(node, locals) {
|
|
|
1658
1743
|
collectLocalBindings(node.left, locals);
|
|
1659
1744
|
collectLocalBindings(node.right, locals);
|
|
1660
1745
|
return;
|
|
1746
|
+
case "range":
|
|
1747
|
+
collectLocalBindings(node.from, locals);
|
|
1748
|
+
collectLocalBindings(node.to, locals);
|
|
1749
|
+
return;
|
|
1661
1750
|
case "conditional":
|
|
1662
1751
|
collectLocalBindings(node.condition, locals);
|
|
1663
1752
|
for (const part of node.thenBlock)
|
|
@@ -1674,11 +1763,20 @@ function collectLocalBindings(node, locals) {
|
|
|
1674
1763
|
collectLocalBindingFromBinding(node.binding, locals);
|
|
1675
1764
|
collectLocalBindings(node.body, locals);
|
|
1676
1765
|
return;
|
|
1766
|
+
case "whileArrayComprehension":
|
|
1767
|
+
collectLocalBindings(node.condition, locals);
|
|
1768
|
+
collectLocalBindings(node.body, locals);
|
|
1769
|
+
return;
|
|
1677
1770
|
case "objectComprehension":
|
|
1678
1771
|
collectLocalBindingFromBinding(node.binding, locals);
|
|
1679
1772
|
collectLocalBindings(node.key, locals);
|
|
1680
1773
|
collectLocalBindings(node.value, locals);
|
|
1681
1774
|
return;
|
|
1775
|
+
case "whileObjectComprehension":
|
|
1776
|
+
collectLocalBindings(node.condition, locals);
|
|
1777
|
+
collectLocalBindings(node.key, locals);
|
|
1778
|
+
collectLocalBindings(node.value, locals);
|
|
1779
|
+
return;
|
|
1682
1780
|
default:
|
|
1683
1781
|
return;
|
|
1684
1782
|
}
|
|
@@ -1770,6 +1868,10 @@ function collectNameFrequencies(node, locals, frequencies, order, nextOrder) {
|
|
|
1770
1868
|
collectNameFrequencies(node.left, locals, frequencies, order, nextOrder);
|
|
1771
1869
|
collectNameFrequencies(node.right, locals, frequencies, order, nextOrder);
|
|
1772
1870
|
return;
|
|
1871
|
+
case "range":
|
|
1872
|
+
collectNameFrequencies(node.from, locals, frequencies, order, nextOrder);
|
|
1873
|
+
collectNameFrequencies(node.to, locals, frequencies, order, nextOrder);
|
|
1874
|
+
return;
|
|
1773
1875
|
case "conditional":
|
|
1774
1876
|
collectNameFrequencies(node.condition, locals, frequencies, order, nextOrder);
|
|
1775
1877
|
for (const part of node.thenBlock)
|
|
@@ -1786,11 +1888,20 @@ function collectNameFrequencies(node, locals, frequencies, order, nextOrder) {
|
|
|
1786
1888
|
collectNameFrequenciesBinding(node.binding, locals, frequencies, order, nextOrder);
|
|
1787
1889
|
collectNameFrequencies(node.body, locals, frequencies, order, nextOrder);
|
|
1788
1890
|
return;
|
|
1891
|
+
case "whileArrayComprehension":
|
|
1892
|
+
collectNameFrequencies(node.condition, locals, frequencies, order, nextOrder);
|
|
1893
|
+
collectNameFrequencies(node.body, locals, frequencies, order, nextOrder);
|
|
1894
|
+
return;
|
|
1789
1895
|
case "objectComprehension":
|
|
1790
1896
|
collectNameFrequenciesBinding(node.binding, locals, frequencies, order, nextOrder);
|
|
1791
1897
|
collectNameFrequencies(node.key, locals, frequencies, order, nextOrder);
|
|
1792
1898
|
collectNameFrequencies(node.value, locals, frequencies, order, nextOrder);
|
|
1793
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;
|
|
1794
1905
|
default:
|
|
1795
1906
|
return;
|
|
1796
1907
|
}
|
|
@@ -1865,6 +1976,12 @@ function renameLocalNames(node, map) {
|
|
|
1865
1976
|
left: renameLocalNames(node.left, map),
|
|
1866
1977
|
right: renameLocalNames(node.right, map)
|
|
1867
1978
|
};
|
|
1979
|
+
case "range":
|
|
1980
|
+
return {
|
|
1981
|
+
type: "range",
|
|
1982
|
+
from: renameLocalNames(node.from, map),
|
|
1983
|
+
to: renameLocalNames(node.to, map)
|
|
1984
|
+
};
|
|
1868
1985
|
case "assign": {
|
|
1869
1986
|
const place = node.place.type === "identifier" && map.has(node.place.name) ? { type: "identifier", name: map.get(node.place.name) } : renameLocalNames(node.place, map);
|
|
1870
1987
|
return {
|
|
@@ -1894,6 +2011,12 @@ function renameLocalNames(node, map) {
|
|
|
1894
2011
|
binding: renameLocalNamesBinding(node.binding, map),
|
|
1895
2012
|
body: renameLocalNames(node.body, map)
|
|
1896
2013
|
};
|
|
2014
|
+
case "whileArrayComprehension":
|
|
2015
|
+
return {
|
|
2016
|
+
type: "whileArrayComprehension",
|
|
2017
|
+
condition: renameLocalNames(node.condition, map),
|
|
2018
|
+
body: renameLocalNames(node.body, map)
|
|
2019
|
+
};
|
|
1897
2020
|
case "objectComprehension":
|
|
1898
2021
|
return {
|
|
1899
2022
|
type: "objectComprehension",
|
|
@@ -1901,34 +2024,31 @@ function renameLocalNames(node, map) {
|
|
|
1901
2024
|
key: renameLocalNames(node.key, map),
|
|
1902
2025
|
value: renameLocalNames(node.value, map)
|
|
1903
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
|
+
};
|
|
1904
2034
|
default:
|
|
1905
2035
|
return node;
|
|
1906
2036
|
}
|
|
1907
2037
|
}
|
|
1908
2038
|
function renameLocalNamesBinding(binding, map) {
|
|
1909
|
-
|
|
1910
|
-
|
|
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 };
|
|
1911
2051
|
}
|
|
1912
|
-
if (binding.type === "binding:valueIn") {
|
|
1913
|
-
return {
|
|
1914
|
-
type: "binding:valueIn",
|
|
1915
|
-
value: map.get(binding.value) ?? binding.value,
|
|
1916
|
-
source: renameLocalNames(binding.source, map)
|
|
1917
|
-
};
|
|
1918
|
-
}
|
|
1919
|
-
if (binding.type === "binding:keyValueIn") {
|
|
1920
|
-
return {
|
|
1921
|
-
type: "binding:keyValueIn",
|
|
1922
|
-
key: map.get(binding.key) ?? binding.key,
|
|
1923
|
-
value: map.get(binding.value) ?? binding.value,
|
|
1924
|
-
source: renameLocalNames(binding.source, map)
|
|
1925
|
-
};
|
|
1926
|
-
}
|
|
1927
|
-
return {
|
|
1928
|
-
type: "binding:keyOf",
|
|
1929
|
-
key: map.get(binding.key) ?? binding.key,
|
|
1930
|
-
source: renameLocalNames(binding.source, map)
|
|
1931
|
-
};
|
|
1932
2052
|
}
|
|
1933
2053
|
function renameLocalNamesElse(elseBranch, map) {
|
|
1934
2054
|
if (elseBranch.type === "else") {
|
|
@@ -1975,14 +2095,20 @@ function compile(source, options) {
|
|
|
1975
2095
|
let lowered = options?.optimize ? optimizeIR(ir) : ir;
|
|
1976
2096
|
if (options?.minifyNames)
|
|
1977
2097
|
lowered = minifyLocalNamesIR(lowered);
|
|
1978
|
-
const
|
|
2098
|
+
const domainMaps = options?.domainConfig ? domainRefsFromConfig(options.domainConfig) : undefined;
|
|
1979
2099
|
return encodeIR(lowered, {
|
|
1980
|
-
|
|
2100
|
+
...domainMaps,
|
|
1981
2101
|
dedupeValues: options?.dedupeValues,
|
|
1982
2102
|
dedupeMinBytes: options?.dedupeMinBytes
|
|
1983
2103
|
});
|
|
1984
2104
|
}
|
|
1985
2105
|
function parseNumber(raw) {
|
|
2106
|
+
if (raw === "nan")
|
|
2107
|
+
return NaN;
|
|
2108
|
+
if (raw === "inf")
|
|
2109
|
+
return Infinity;
|
|
2110
|
+
if (raw === "-inf")
|
|
2111
|
+
return -Infinity;
|
|
1986
2112
|
if (/^-?0x/i.test(raw))
|
|
1987
2113
|
return parseInt(raw, 16);
|
|
1988
2114
|
if (/^-?0b/i.test(raw)) {
|
|
@@ -2054,7 +2180,7 @@ function buildPostfix(base, steps) {
|
|
|
2054
2180
|
flushSegments();
|
|
2055
2181
|
return current;
|
|
2056
2182
|
}
|
|
2057
|
-
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;
|
|
2058
2184
|
var init_rex = __esm(() => {
|
|
2059
2185
|
require2 = createRequire(import.meta.url);
|
|
2060
2186
|
rexGrammarModule = require2("./rex.ohm-bundle.cjs");
|
|
@@ -2062,30 +2188,32 @@ var init_rex = __esm(() => {
|
|
|
2062
2188
|
grammar = rexGrammar;
|
|
2063
2189
|
semantics = rexGrammar.createSemantics();
|
|
2064
2190
|
OPCODE_IDS = {
|
|
2065
|
-
do:
|
|
2066
|
-
add:
|
|
2067
|
-
sub:
|
|
2068
|
-
mul:
|
|
2069
|
-
div:
|
|
2070
|
-
eq:
|
|
2071
|
-
neq:
|
|
2072
|
-
lt:
|
|
2073
|
-
lte:
|
|
2074
|
-
gt:
|
|
2075
|
-
gte:
|
|
2076
|
-
and:
|
|
2077
|
-
or:
|
|
2078
|
-
xor:
|
|
2079
|
-
not:
|
|
2080
|
-
boolean:
|
|
2081
|
-
number:
|
|
2082
|
-
string:
|
|
2083
|
-
array:
|
|
2084
|
-
object:
|
|
2085
|
-
mod:
|
|
2086
|
-
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"
|
|
2087
2215
|
};
|
|
2088
|
-
|
|
2216
|
+
KEYWORD_OPCODES = new Set(["boolean", "number", "string", "array", "object", "size"]);
|
|
2089
2217
|
BINARY_TO_OPCODE = {
|
|
2090
2218
|
add: "add",
|
|
2091
2219
|
sub: "sub",
|
|
@@ -2114,6 +2242,7 @@ var init_rex = __esm(() => {
|
|
|
2114
2242
|
"|=": "or",
|
|
2115
2243
|
"^=": "xor"
|
|
2116
2244
|
};
|
|
2245
|
+
DEC_PARTS = /^(-?\d)(?:\.(\d+))?e([+-]\d+)$/;
|
|
2117
2246
|
DIGIT_SET = new Set(DIGITS.split(""));
|
|
2118
2247
|
DIGIT_INDEX = new Map(Array.from(DIGITS).map((char, index) => [char, index]));
|
|
2119
2248
|
semantics.addOperation("toIR", {
|
|
@@ -2159,6 +2288,9 @@ var init_rex = __esm(() => {
|
|
|
2159
2288
|
ExistenceExpr_or(left, _or, right) {
|
|
2160
2289
|
return { type: "binary", op: "or", left: left.toIR(), right: right.toIR() };
|
|
2161
2290
|
},
|
|
2291
|
+
ExistenceExpr_nor(left, _nor, right) {
|
|
2292
|
+
return { type: "binary", op: "nor", left: left.toIR(), right: right.toIR() };
|
|
2293
|
+
},
|
|
2162
2294
|
BitExpr_and(left, _op, right) {
|
|
2163
2295
|
return { type: "binary", op: "bitAnd", left: left.toIR(), right: right.toIR() };
|
|
2164
2296
|
},
|
|
@@ -2168,6 +2300,9 @@ var init_rex = __esm(() => {
|
|
|
2168
2300
|
BitExpr_or(left, _op, right) {
|
|
2169
2301
|
return { type: "binary", op: "bitOr", left: left.toIR(), right: right.toIR() };
|
|
2170
2302
|
},
|
|
2303
|
+
RangeExpr_range(left, _op, right) {
|
|
2304
|
+
return { type: "range", from: left.toIR(), to: right.toIR() };
|
|
2305
|
+
},
|
|
2171
2306
|
CompareExpr_binary(left, op, right) {
|
|
2172
2307
|
const map = {
|
|
2173
2308
|
"==": "eq",
|
|
@@ -2208,6 +2343,9 @@ var init_rex = __esm(() => {
|
|
|
2208
2343
|
UnaryExpr_not(_op, value) {
|
|
2209
2344
|
return { type: "unary", op: "not", value: value.toIR() };
|
|
2210
2345
|
},
|
|
2346
|
+
UnaryExpr_logicalNot(_not, value) {
|
|
2347
|
+
return { type: "unary", op: "logicalNot", value: value.toIR() };
|
|
2348
|
+
},
|
|
2211
2349
|
UnaryExpr_delete(_del, place) {
|
|
2212
2350
|
return { type: "unary", op: "delete", value: place.toIR() };
|
|
2213
2351
|
},
|
|
@@ -2261,14 +2399,6 @@ var init_rex = __esm(() => {
|
|
|
2261
2399
|
ConditionalElse_else(_else, block) {
|
|
2262
2400
|
return { type: "else", block: block.toIR() };
|
|
2263
2401
|
},
|
|
2264
|
-
DoExpr(_do, block, _end) {
|
|
2265
|
-
const body = block.toIR();
|
|
2266
|
-
if (body.length === 0)
|
|
2267
|
-
return { type: "undefined" };
|
|
2268
|
-
if (body.length === 1)
|
|
2269
|
-
return body[0];
|
|
2270
|
-
return { type: "program", body };
|
|
2271
|
-
},
|
|
2272
2402
|
WhileExpr(_while, condition, _do, block, _end) {
|
|
2273
2403
|
return {
|
|
2274
2404
|
type: "while",
|
|
@@ -2283,30 +2413,44 @@ var init_rex = __esm(() => {
|
|
|
2283
2413
|
body: block.toIR()
|
|
2284
2414
|
};
|
|
2285
2415
|
},
|
|
2286
|
-
BindingExpr(iterOrExpr) {
|
|
2287
|
-
const node = iterOrExpr.toIR();
|
|
2288
|
-
if (typeof node === "object" && node && "type" in node && String(node.type).startsWith("binding:")) {
|
|
2289
|
-
return node;
|
|
2290
|
-
}
|
|
2291
|
-
return { type: "binding:expr", source: node };
|
|
2292
|
-
},
|
|
2293
2416
|
Array_empty(_open, _close) {
|
|
2294
2417
|
return { type: "array", items: [] };
|
|
2295
2418
|
},
|
|
2296
|
-
|
|
2419
|
+
Array_forComprehension(_open, body, _for, binding, _close) {
|
|
2297
2420
|
return {
|
|
2298
2421
|
type: "arrayComprehension",
|
|
2299
2422
|
binding: binding.toIR(),
|
|
2300
2423
|
body: body.toIR()
|
|
2301
2424
|
};
|
|
2302
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
|
+
},
|
|
2303
2447
|
Array_values(_open, items, _close) {
|
|
2304
2448
|
return { type: "array", items: normalizeList(items.toIR()) };
|
|
2305
2449
|
},
|
|
2306
2450
|
Object_empty(_open, _close) {
|
|
2307
2451
|
return { type: "object", entries: [] };
|
|
2308
2452
|
},
|
|
2309
|
-
|
|
2453
|
+
Object_forComprehension(_open, key, _colon, value, _for, binding, _close) {
|
|
2310
2454
|
return {
|
|
2311
2455
|
type: "objectComprehension",
|
|
2312
2456
|
binding: binding.toIR(),
|
|
@@ -2314,6 +2458,30 @@ var init_rex = __esm(() => {
|
|
|
2314
2458
|
value: value.toIR()
|
|
2315
2459
|
};
|
|
2316
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
|
+
},
|
|
2317
2485
|
Object_pairs(_open, pairs, _close) {
|
|
2318
2486
|
return {
|
|
2319
2487
|
type: "object",
|
|
@@ -2342,6 +2510,18 @@ var init_rex = __esm(() => {
|
|
|
2342
2510
|
source: source.toIR()
|
|
2343
2511
|
};
|
|
2344
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
|
+
},
|
|
2345
2525
|
Pair(key, _colon, value) {
|
|
2346
2526
|
return { key: key.toIR(), value: value.toIR() };
|
|
2347
2527
|
},
|
|
@@ -2405,6 +2585,9 @@ var init_rex = __esm(() => {
|
|
|
2405
2585
|
BooleanKw(_kw) {
|
|
2406
2586
|
return { type: "identifier", name: "boolean" };
|
|
2407
2587
|
},
|
|
2588
|
+
SizeKw(_kw) {
|
|
2589
|
+
return { type: "identifier", name: "size" };
|
|
2590
|
+
},
|
|
2408
2591
|
identifier(_a, _b) {
|
|
2409
2592
|
return { type: "identifier", name: this.sourceString };
|
|
2410
2593
|
},
|
|
@@ -2443,7 +2626,6 @@ class CursorInterpreter {
|
|
|
2443
2626
|
pos = 0;
|
|
2444
2627
|
state;
|
|
2445
2628
|
selfStack;
|
|
2446
|
-
opcodeMarkers;
|
|
2447
2629
|
pointerCache = new Map;
|
|
2448
2630
|
gasLimit;
|
|
2449
2631
|
gas = 0;
|
|
@@ -2458,25 +2640,22 @@ class CursorInterpreter {
|
|
|
2458
2640
|
this.state = {
|
|
2459
2641
|
vars: ctx.vars ?? {},
|
|
2460
2642
|
refs: {
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2643
|
+
tr: true,
|
|
2644
|
+
fl: false,
|
|
2645
|
+
nl: null,
|
|
2646
|
+
un: undefined,
|
|
2647
|
+
nan: NaN,
|
|
2648
|
+
inf: Infinity,
|
|
2649
|
+
nif: -Infinity,
|
|
2650
|
+
...ctx.refs
|
|
2466
2651
|
}
|
|
2467
2652
|
};
|
|
2468
2653
|
this.selfStack = ctx.selfStack && ctx.selfStack.length > 0 ? [...ctx.selfStack] : [initialSelf];
|
|
2469
2654
|
this.gasLimit = ctx.gasLimit ?? 0;
|
|
2470
|
-
this.opcodeMarkers = Array.from({ length: 256 }, (_, id) => ({ __opcode: id }));
|
|
2471
|
-
for (const [idText, value] of Object.entries(ctx.refs ?? {})) {
|
|
2472
|
-
const id = Number(idText);
|
|
2473
|
-
if (Number.isInteger(id))
|
|
2474
|
-
this.state.refs[id] = value;
|
|
2475
|
-
}
|
|
2476
2655
|
if (ctx.opcodes) {
|
|
2477
|
-
for (const [
|
|
2656
|
+
for (const [key, op] of Object.entries(ctx.opcodes)) {
|
|
2478
2657
|
if (op)
|
|
2479
|
-
this.customOpcodes.set(
|
|
2658
|
+
this.customOpcodes.set(key, op);
|
|
2480
2659
|
}
|
|
2481
2660
|
}
|
|
2482
2661
|
}
|
|
@@ -2537,6 +2716,22 @@ class CursorInterpreter {
|
|
|
2537
2716
|
const end = this.pos;
|
|
2538
2717
|
return { start, end, value: decodePrefix(this.text, start, end), raw: this.text.slice(start, end) };
|
|
2539
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
|
+
}
|
|
2540
2735
|
ensure(char) {
|
|
2541
2736
|
if (this.text[this.pos] !== char)
|
|
2542
2737
|
throw new Error(`Expected '${char}' at ${this.pos}`);
|
|
@@ -2577,36 +2772,34 @@ class CursorInterpreter {
|
|
|
2577
2772
|
const significand = this.evalValue();
|
|
2578
2773
|
if (typeof significand !== "number")
|
|
2579
2774
|
throw new Error("Decimal significand must be numeric");
|
|
2580
|
-
return significand
|
|
2775
|
+
return parseFloat(`${significand}e${power}`);
|
|
2581
2776
|
}
|
|
2582
2777
|
case ":":
|
|
2583
2778
|
this.pos += 1;
|
|
2584
2779
|
return prefix.raw;
|
|
2585
2780
|
case "%":
|
|
2586
2781
|
this.pos += 1;
|
|
2587
|
-
return
|
|
2782
|
+
return { __opcode: prefix.raw };
|
|
2588
2783
|
case "@":
|
|
2589
2784
|
this.pos += 1;
|
|
2590
2785
|
return this.readSelf(prefix.value);
|
|
2591
2786
|
case "'":
|
|
2592
2787
|
this.pos += 1;
|
|
2593
|
-
return this.state.refs[prefix.
|
|
2788
|
+
return this.state.refs[prefix.raw];
|
|
2594
2789
|
case "$":
|
|
2595
2790
|
this.pos += 1;
|
|
2596
2791
|
return this.state.vars[prefix.raw];
|
|
2597
2792
|
case ",": {
|
|
2598
2793
|
this.pos += 1;
|
|
2599
2794
|
const start = this.pos;
|
|
2600
|
-
const end = start
|
|
2601
|
-
if (end > this.text.length)
|
|
2602
|
-
throw new Error("String container overflows input");
|
|
2795
|
+
const end = this.advanceByBytes(start, prefix.value);
|
|
2603
2796
|
const value = this.text.slice(start, end);
|
|
2604
2797
|
this.pos = end;
|
|
2605
2798
|
return value;
|
|
2606
2799
|
}
|
|
2607
2800
|
case "^": {
|
|
2608
2801
|
this.pos += 1;
|
|
2609
|
-
const target = this.pos
|
|
2802
|
+
const target = this.advanceByBytes(this.pos, prefix.value);
|
|
2610
2803
|
if (this.pointerCache.has(target))
|
|
2611
2804
|
return this.pointerCache.get(target);
|
|
2612
2805
|
const save = this.pos;
|
|
@@ -2623,6 +2816,14 @@ class CursorInterpreter {
|
|
|
2623
2816
|
this.writePlace(place, value);
|
|
2624
2817
|
return value;
|
|
2625
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
|
+
}
|
|
2626
2827
|
case "~": {
|
|
2627
2828
|
this.pos += 1;
|
|
2628
2829
|
const place = this.readPlace();
|
|
@@ -2650,7 +2851,7 @@ class CursorInterpreter {
|
|
|
2650
2851
|
case "<":
|
|
2651
2852
|
return this.evalLoopLike(tag);
|
|
2652
2853
|
case "#":
|
|
2653
|
-
return this.
|
|
2854
|
+
return this.evalWhileLike();
|
|
2654
2855
|
default:
|
|
2655
2856
|
throw new Error(`Unexpected tag '${tag}' at ${this.pos}`);
|
|
2656
2857
|
}
|
|
@@ -2863,15 +3064,11 @@ class CursorInterpreter {
|
|
|
2863
3064
|
return entries.map(([key]) => ({ key, value: key }));
|
|
2864
3065
|
return entries.map(([key, value]) => ({ key, value }));
|
|
2865
3066
|
}
|
|
2866
|
-
if (typeof iterable === "
|
|
2867
|
-
const
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
else
|
|
2872
|
-
out.push({ key: index, value: index + 1 });
|
|
2873
|
-
}
|
|
2874
|
-
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 }));
|
|
2875
3072
|
}
|
|
2876
3073
|
return [];
|
|
2877
3074
|
}
|
|
@@ -2916,21 +3113,32 @@ class CursorInterpreter {
|
|
|
2916
3113
|
}
|
|
2917
3114
|
return last;
|
|
2918
3115
|
}
|
|
2919
|
-
|
|
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 === "[" ? "]" : "}";
|
|
2920
3122
|
this.pos += 1;
|
|
2921
|
-
this.ensure("(");
|
|
2922
3123
|
const condStart = this.pos;
|
|
2923
3124
|
const condValue = this.evalValue();
|
|
2924
3125
|
const bodyStart = this.pos;
|
|
2925
|
-
const bodyValueCount = 1;
|
|
3126
|
+
const bodyValueCount = open === "{" ? 2 : 1;
|
|
2926
3127
|
let cursor = bodyStart;
|
|
2927
3128
|
for (let index = 0;index < bodyValueCount; index += 1) {
|
|
2928
3129
|
cursor = this.skipValueFrom(cursor);
|
|
2929
3130
|
}
|
|
2930
3131
|
const bodyEnd = cursor;
|
|
2931
3132
|
this.pos = bodyEnd;
|
|
2932
|
-
this.ensure(
|
|
3133
|
+
this.ensure(close);
|
|
2933
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) {
|
|
2934
3142
|
let last = undefined;
|
|
2935
3143
|
let currentCond = condValue;
|
|
2936
3144
|
while (isDefined(currentCond)) {
|
|
@@ -2951,6 +3159,58 @@ class CursorInterpreter {
|
|
|
2951
3159
|
this.pos = afterClose;
|
|
2952
3160
|
return last;
|
|
2953
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
|
+
}
|
|
2954
3214
|
evalArrayComprehension(iterable, varA, varB, bodyStart, bodyEnd, keysOnly) {
|
|
2955
3215
|
const items = this.iterate(iterable, keysOnly);
|
|
2956
3216
|
const out = [];
|
|
@@ -3019,25 +3279,39 @@ class CursorInterpreter {
|
|
|
3019
3279
|
case OPCODES.do:
|
|
3020
3280
|
return args.length ? args[args.length - 1] : undefined;
|
|
3021
3281
|
case OPCODES.add:
|
|
3282
|
+
if (args[0] === undefined || args[1] === undefined)
|
|
3283
|
+
return;
|
|
3022
3284
|
if (typeof args[0] === "string" || typeof args[1] === "string") {
|
|
3023
|
-
return String(args[0]
|
|
3285
|
+
return String(args[0]) + String(args[1]);
|
|
3024
3286
|
}
|
|
3025
|
-
return Number(args[0]
|
|
3287
|
+
return Number(args[0]) + Number(args[1]);
|
|
3026
3288
|
case OPCODES.sub:
|
|
3027
|
-
|
|
3289
|
+
if (args[0] === undefined || args[1] === undefined)
|
|
3290
|
+
return;
|
|
3291
|
+
return Number(args[0]) - Number(args[1]);
|
|
3028
3292
|
case OPCODES.mul:
|
|
3029
|
-
|
|
3293
|
+
if (args[0] === undefined || args[1] === undefined)
|
|
3294
|
+
return;
|
|
3295
|
+
return Number(args[0]) * Number(args[1]);
|
|
3030
3296
|
case OPCODES.div:
|
|
3031
|
-
|
|
3297
|
+
if (args[0] === undefined || args[1] === undefined)
|
|
3298
|
+
return;
|
|
3299
|
+
return Number(args[0]) / Number(args[1]);
|
|
3032
3300
|
case OPCODES.mod:
|
|
3033
|
-
|
|
3301
|
+
if (args[0] === undefined || args[1] === undefined)
|
|
3302
|
+
return;
|
|
3303
|
+
return Number(args[0]) % Number(args[1]);
|
|
3034
3304
|
case OPCODES.neg:
|
|
3035
|
-
|
|
3305
|
+
if (args[0] === undefined)
|
|
3306
|
+
return;
|
|
3307
|
+
return -Number(args[0]);
|
|
3036
3308
|
case OPCODES.not: {
|
|
3037
3309
|
const value = args[0];
|
|
3310
|
+
if (value === undefined)
|
|
3311
|
+
return;
|
|
3038
3312
|
if (typeof value === "boolean")
|
|
3039
3313
|
return !value;
|
|
3040
|
-
return ~Number(value
|
|
3314
|
+
return ~Number(value);
|
|
3041
3315
|
}
|
|
3042
3316
|
case OPCODES.and: {
|
|
3043
3317
|
const [a, b] = args;
|
|
@@ -3079,6 +3353,25 @@ class CursorInterpreter {
|
|
|
3079
3353
|
return Array.isArray(args[0]) ? args[0] : undefined;
|
|
3080
3354
|
case OPCODES.object:
|
|
3081
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
|
+
}
|
|
3082
3375
|
default:
|
|
3083
3376
|
throw new Error(`Unknown opcode ${id}`);
|
|
3084
3377
|
}
|
|
@@ -3088,10 +3381,59 @@ class CursorInterpreter {
|
|
|
3088
3381
|
for (const key of keys) {
|
|
3089
3382
|
if (current === undefined || current === null)
|
|
3090
3383
|
return;
|
|
3091
|
-
current = current
|
|
3384
|
+
current = this.readProperty(current, key);
|
|
3385
|
+
if (current === undefined)
|
|
3386
|
+
return;
|
|
3092
3387
|
}
|
|
3093
3388
|
return current;
|
|
3094
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
|
+
}
|
|
3095
3437
|
readPlace() {
|
|
3096
3438
|
this.skipNonCode();
|
|
3097
3439
|
const direct = this.readRootVarOrRefIfPresent();
|
|
@@ -3146,13 +3488,13 @@ class CursorInterpreter {
|
|
|
3146
3488
|
}
|
|
3147
3489
|
this.pos += 1;
|
|
3148
3490
|
return {
|
|
3149
|
-
root:
|
|
3491
|
+
root: prefix.raw,
|
|
3150
3492
|
isRef: tag === "'"
|
|
3151
3493
|
};
|
|
3152
3494
|
}
|
|
3153
3495
|
writePlace(place, value) {
|
|
3154
3496
|
const rootTable = place.isRef ? this.state.refs : this.state.vars;
|
|
3155
|
-
const rootKey =
|
|
3497
|
+
const rootKey = place.root;
|
|
3156
3498
|
if (place.keys.length === 0) {
|
|
3157
3499
|
rootTable[rootKey] = value;
|
|
3158
3500
|
return;
|
|
@@ -3163,17 +3505,48 @@ class CursorInterpreter {
|
|
|
3163
3505
|
rootTable[rootKey] = target;
|
|
3164
3506
|
}
|
|
3165
3507
|
for (let index = 0;index < place.keys.length - 1; index += 1) {
|
|
3166
|
-
const key =
|
|
3167
|
-
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];
|
|
3168
3521
|
if (!next || typeof next !== "object")
|
|
3169
|
-
target[
|
|
3170
|
-
target = target[
|
|
3522
|
+
target[prop] = {};
|
|
3523
|
+
target = target[prop];
|
|
3171
3524
|
}
|
|
3172
|
-
|
|
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;
|
|
3173
3546
|
}
|
|
3174
3547
|
deletePlace(place) {
|
|
3175
3548
|
const rootTable = place.isRef ? this.state.refs : this.state.vars;
|
|
3176
|
-
const rootKey =
|
|
3549
|
+
const rootKey = place.root;
|
|
3177
3550
|
if (place.keys.length === 0) {
|
|
3178
3551
|
delete rootTable[rootKey];
|
|
3179
3552
|
return;
|
|
@@ -3182,11 +3555,29 @@ class CursorInterpreter {
|
|
|
3182
3555
|
if (!target || typeof target !== "object")
|
|
3183
3556
|
return;
|
|
3184
3557
|
for (let index = 0;index < place.keys.length - 1; index += 1) {
|
|
3185
|
-
|
|
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)];
|
|
3186
3569
|
if (!target || typeof target !== "object")
|
|
3187
3570
|
return;
|
|
3188
3571
|
}
|
|
3189
|
-
|
|
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)];
|
|
3190
3581
|
}
|
|
3191
3582
|
skipValue() {
|
|
3192
3583
|
this.pos = this.skipValueFrom(this.pos);
|
|
@@ -3202,12 +3593,12 @@ class CursorInterpreter {
|
|
|
3202
3593
|
return startPos;
|
|
3203
3594
|
}
|
|
3204
3595
|
if (tag === ",") {
|
|
3205
|
-
this.pos
|
|
3596
|
+
this.pos = this.advanceByBytes(this.pos + 1, prefix.value);
|
|
3206
3597
|
const end2 = this.pos;
|
|
3207
3598
|
this.pos = save;
|
|
3208
3599
|
return end2;
|
|
3209
3600
|
}
|
|
3210
|
-
if (tag === "=") {
|
|
3601
|
+
if (tag === "=" || tag === "/") {
|
|
3211
3602
|
this.pos += 1;
|
|
3212
3603
|
this.skipValue();
|
|
3213
3604
|
this.skipValue();
|
|
@@ -3242,7 +3633,8 @@ class CursorInterpreter {
|
|
|
3242
3633
|
if (opener && "([{".includes(opener)) {
|
|
3243
3634
|
const close = opener === "(" ? ")" : opener === "[" ? "]" : "}";
|
|
3244
3635
|
if (prefix.value > 0) {
|
|
3245
|
-
this.pos
|
|
3636
|
+
const bodyEnd = this.advanceByBytes(this.pos + 1, prefix.value);
|
|
3637
|
+
this.pos = bodyEnd + 1;
|
|
3246
3638
|
const end3 = this.pos;
|
|
3247
3639
|
this.pos = save;
|
|
3248
3640
|
return end3;
|
|
@@ -3278,28 +3670,30 @@ var init_rexc_interpreter = __esm(() => {
|
|
|
3278
3670
|
init_rex();
|
|
3279
3671
|
digitMap = new Map(Array.from(DIGITS2).map((char, index) => [char, index]));
|
|
3280
3672
|
OPCODES = {
|
|
3281
|
-
do:
|
|
3282
|
-
add:
|
|
3283
|
-
sub:
|
|
3284
|
-
mul:
|
|
3285
|
-
div:
|
|
3286
|
-
eq:
|
|
3287
|
-
neq:
|
|
3288
|
-
lt:
|
|
3289
|
-
lte:
|
|
3290
|
-
gt:
|
|
3291
|
-
gte:
|
|
3292
|
-
and:
|
|
3293
|
-
or:
|
|
3294
|
-
xor:
|
|
3295
|
-
not:
|
|
3296
|
-
boolean:
|
|
3297
|
-
number:
|
|
3298
|
-
string:
|
|
3299
|
-
array:
|
|
3300
|
-
object:
|
|
3301
|
-
mod:
|
|
3302
|
-
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"
|
|
3303
3697
|
};
|
|
3304
3698
|
});
|
|
3305
3699
|
|
|
@@ -3310,6 +3704,7 @@ __export(exports_rex_repl, {
|
|
|
3310
3704
|
isIncomplete: () => isIncomplete,
|
|
3311
3705
|
highlightRexc: () => highlightRexc,
|
|
3312
3706
|
highlightLine: () => highlightLine,
|
|
3707
|
+
highlightJSON: () => highlightJSON,
|
|
3313
3708
|
formatVarState: () => formatVarState
|
|
3314
3709
|
});
|
|
3315
3710
|
import * as readline from "node:readline";
|
|
@@ -3421,6 +3816,7 @@ function highlightRexc(text) {
|
|
|
3421
3816
|
break;
|
|
3422
3817
|
}
|
|
3423
3818
|
case "=":
|
|
3819
|
+
case "/":
|
|
3424
3820
|
case "~":
|
|
3425
3821
|
out += C.red + prefix + tag + C.reset;
|
|
3426
3822
|
i++;
|
|
@@ -3460,6 +3856,32 @@ function highlightRexc(text) {
|
|
|
3460
3856
|
}
|
|
3461
3857
|
return out;
|
|
3462
3858
|
}
|
|
3859
|
+
function highlightJSON(json) {
|
|
3860
|
+
let result = "";
|
|
3861
|
+
let lastIndex = 0;
|
|
3862
|
+
JSON_TOKEN_RE.lastIndex = 0;
|
|
3863
|
+
for (const m of json.matchAll(JSON_TOKEN_RE)) {
|
|
3864
|
+
result += json.slice(lastIndex, m.index);
|
|
3865
|
+
const text = m[0];
|
|
3866
|
+
const g = m.groups;
|
|
3867
|
+
if (g.key) {
|
|
3868
|
+
result += C.cyan + g.key + C.reset + ":";
|
|
3869
|
+
} else if (g.string) {
|
|
3870
|
+
result += C.green + text + C.reset;
|
|
3871
|
+
} else if (g.number) {
|
|
3872
|
+
result += C.yellow + text + C.reset;
|
|
3873
|
+
} else if (g.bool) {
|
|
3874
|
+
result += C.yellow + text + C.reset;
|
|
3875
|
+
} else if (g.null) {
|
|
3876
|
+
result += C.dim + text + C.reset;
|
|
3877
|
+
} else {
|
|
3878
|
+
result += text;
|
|
3879
|
+
}
|
|
3880
|
+
lastIndex = m.index + text.length;
|
|
3881
|
+
}
|
|
3882
|
+
result += json.slice(lastIndex);
|
|
3883
|
+
return result;
|
|
3884
|
+
}
|
|
3463
3885
|
function stripStringsAndComments(source) {
|
|
3464
3886
|
return source.replace(/\/\*[\s\S]*?\*\/|\/\/[^\n]*|"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/g, (m) => " ".repeat(m.length));
|
|
3465
3887
|
}
|
|
@@ -3497,7 +3919,7 @@ function isIncomplete(buffer) {
|
|
|
3497
3919
|
const trimmed = buffer.trimEnd();
|
|
3498
3920
|
if (/[+\-*/%&|^=<>]$/.test(trimmed))
|
|
3499
3921
|
return true;
|
|
3500
|
-
if (/\b(?:and|or|do|in|of)\s*$/.test(trimmed))
|
|
3922
|
+
if (/\b(?:and|or|nor|do|in|of)\s*$/.test(trimmed))
|
|
3501
3923
|
return true;
|
|
3502
3924
|
return false;
|
|
3503
3925
|
}
|
|
@@ -3707,7 +4129,7 @@ async function startRepl() {
|
|
|
3707
4129
|
const ir = parseToIR(source);
|
|
3708
4130
|
const lowered = state.optimize ? optimizeIR(ir) : ir;
|
|
3709
4131
|
if (state.showIR) {
|
|
3710
|
-
console.log(`${C.dim} IR
|
|
4132
|
+
console.log(`${C.dim} IR:${C.reset} ${highlightJSON(JSON.stringify(lowered))}`);
|
|
3711
4133
|
}
|
|
3712
4134
|
const rexc = compile(source, { optimize: state.optimize });
|
|
3713
4135
|
if (state.showRexc) {
|
|
@@ -3739,7 +4161,7 @@ async function startRepl() {
|
|
|
3739
4161
|
});
|
|
3740
4162
|
rl.prompt();
|
|
3741
4163
|
}
|
|
3742
|
-
var req, version, C, TOKEN_RE, REXC_DIGITS, KEYWORDS, GAS_LIMIT = 1e7;
|
|
4164
|
+
var req, version, C, TOKEN_RE, REXC_DIGITS, JSON_TOKEN_RE, KEYWORDS, GAS_LIMIT = 1e7;
|
|
3743
4165
|
var init_rex_repl = __esm(() => {
|
|
3744
4166
|
init_rex();
|
|
3745
4167
|
init_rexc_interpreter();
|
|
@@ -3757,8 +4179,9 @@ var init_rex_repl = __esm(() => {
|
|
|
3757
4179
|
gray: "\x1B[90m",
|
|
3758
4180
|
boldBlue: "\x1B[1;34m"
|
|
3759
4181
|
};
|
|
3760
|
-
TOKEN_RE = /(?<blockComment>\/\*[\s\S]*?(?:\*\/|$))|(?<lineComment>\/\/[^\n]*)|(?<dstring>"(?:[^"\\]|\\.)*"?)|(?<sstring>'(?:[^'\\]|\\.)*'?)|(?<keyword>\b(?:when|unless|while|for|do|end|in|of|and|or|else|break|continue|delete|self)(?![a-zA-Z0-9_-]))|(?<literal>\b(?:true|false|null|undefined)(?![a-zA-Z0-9_-]))|(?<typePred>\b(?:string|number|object|array|boolean)(?![a-zA-Z0-9_-]))|(?<num>\b(?:0x[0-9a-fA-F]+|0b[01]+|(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?)\b)/g;
|
|
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;
|
|
3761
4183
|
REXC_DIGITS = new Set("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_");
|
|
4184
|
+
JSON_TOKEN_RE = /(?<key>"(?:[^"\\]|\\.)*")\s*:|(?<string>"(?:[^"\\]|\\.)*")|(?<number>-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?)\b|(?<bool>true|false)|(?<null>null)|(?<brace>[{}[\]])|(?<punct>[:,])/g;
|
|
3762
4185
|
KEYWORDS = [
|
|
3763
4186
|
"when",
|
|
3764
4187
|
"unless",
|
|
@@ -3770,6 +4193,7 @@ var init_rex_repl = __esm(() => {
|
|
|
3770
4193
|
"of",
|
|
3771
4194
|
"and",
|
|
3772
4195
|
"or",
|
|
4196
|
+
"nor",
|
|
3773
4197
|
"else",
|
|
3774
4198
|
"break",
|
|
3775
4199
|
"continue",
|
|
@@ -3779,6 +4203,8 @@ var init_rex_repl = __esm(() => {
|
|
|
3779
4203
|
"false",
|
|
3780
4204
|
"null",
|
|
3781
4205
|
"undefined",
|
|
4206
|
+
"nan",
|
|
4207
|
+
"inf",
|
|
3782
4208
|
"string",
|
|
3783
4209
|
"number",
|
|
3784
4210
|
"object",
|
|
@@ -3794,6 +4220,7 @@ import { dirname, resolve } from "node:path";
|
|
|
3794
4220
|
import { readFile, writeFile } from "node:fs/promises";
|
|
3795
4221
|
function parseArgs(argv) {
|
|
3796
4222
|
const options = {
|
|
4223
|
+
sources: [],
|
|
3797
4224
|
compile: false,
|
|
3798
4225
|
ir: false,
|
|
3799
4226
|
minifyNames: false,
|
|
@@ -3839,7 +4266,7 @@ function parseArgs(argv) {
|
|
|
3839
4266
|
const value = argv[index + 1];
|
|
3840
4267
|
if (!value)
|
|
3841
4268
|
throw new Error("Missing value for --expr");
|
|
3842
|
-
options.expr
|
|
4269
|
+
options.sources.push({ type: "expr", value });
|
|
3843
4270
|
index += 1;
|
|
3844
4271
|
continue;
|
|
3845
4272
|
}
|
|
@@ -3847,7 +4274,7 @@ function parseArgs(argv) {
|
|
|
3847
4274
|
const value = argv[index + 1];
|
|
3848
4275
|
if (!value)
|
|
3849
4276
|
throw new Error("Missing value for --file");
|
|
3850
|
-
options.file
|
|
4277
|
+
options.sources.push({ type: "file", path: value });
|
|
3851
4278
|
index += 1;
|
|
3852
4279
|
continue;
|
|
3853
4280
|
}
|
|
@@ -3860,9 +4287,7 @@ function parseArgs(argv) {
|
|
|
3860
4287
|
continue;
|
|
3861
4288
|
}
|
|
3862
4289
|
if (!arg.startsWith("-")) {
|
|
3863
|
-
|
|
3864
|
-
throw new Error("Multiple file arguments provided");
|
|
3865
|
-
options.file = arg;
|
|
4290
|
+
options.sources.push({ type: "file", path: arg });
|
|
3866
4291
|
continue;
|
|
3867
4292
|
}
|
|
3868
4293
|
throw new Error(`Unknown option: ${arg}`);
|
|
@@ -3880,11 +4305,18 @@ function usage() {
|
|
|
3880
4305
|
" cat input.rex | rex Evaluate from stdin",
|
|
3881
4306
|
" rex -c input.rex Compile to rexc bytecode",
|
|
3882
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
|
+
"",
|
|
3883
4312
|
"Input:",
|
|
3884
4313
|
" <file> Evaluate/compile a Rex source file",
|
|
3885
4314
|
" -e, --expr <source> Evaluate/compile an inline expression",
|
|
3886
4315
|
" -f, --file <path> Evaluate/compile source from a file",
|
|
3887
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
|
+
"",
|
|
3888
4320
|
"Output mode:",
|
|
3889
4321
|
" (default) Evaluate and output result as JSON",
|
|
3890
4322
|
" -c, --compile Compile to rexc bytecode",
|
|
@@ -3909,12 +4341,17 @@ async function readStdin() {
|
|
|
3909
4341
|
return Buffer.concat(chunks).toString("utf8");
|
|
3910
4342
|
}
|
|
3911
4343
|
async function resolveSource(options) {
|
|
3912
|
-
if (options.
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
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
|
+
}
|
|
3918
4355
|
if (!process.stdin.isTTY) {
|
|
3919
4356
|
const piped = await readStdin();
|
|
3920
4357
|
if (piped.trim().length > 0)
|
|
@@ -3922,6 +4359,13 @@ async function resolveSource(options) {
|
|
|
3922
4359
|
}
|
|
3923
4360
|
throw new Error("No input provided. Use a file path, --expr, or pipe source via stdin.");
|
|
3924
4361
|
}
|
|
4362
|
+
function findFirstFilePath(sources) {
|
|
4363
|
+
for (const seg of sources) {
|
|
4364
|
+
if (seg.type === "file")
|
|
4365
|
+
return seg.path;
|
|
4366
|
+
}
|
|
4367
|
+
return;
|
|
4368
|
+
}
|
|
3925
4369
|
async function loadDomainConfigFromFolder(folderPath) {
|
|
3926
4370
|
const configPath = resolve(folderPath, ".config.rex");
|
|
3927
4371
|
try {
|
|
@@ -3933,7 +4377,8 @@ async function loadDomainConfigFromFolder(folderPath) {
|
|
|
3933
4377
|
}
|
|
3934
4378
|
}
|
|
3935
4379
|
async function resolveDomainConfig(options) {
|
|
3936
|
-
const
|
|
4380
|
+
const filePath = findFirstFilePath(options.sources);
|
|
4381
|
+
const baseFolder = filePath ? dirname(resolve(filePath)) : process.cwd();
|
|
3937
4382
|
return loadDomainConfigFromFolder(baseFolder);
|
|
3938
4383
|
}
|
|
3939
4384
|
async function main() {
|
|
@@ -3942,7 +4387,7 @@ async function main() {
|
|
|
3942
4387
|
console.log(usage());
|
|
3943
4388
|
return;
|
|
3944
4389
|
}
|
|
3945
|
-
const hasSource = options.
|
|
4390
|
+
const hasSource = options.sources.length > 0 || !process.stdin.isTTY;
|
|
3946
4391
|
if (!hasSource && !options.compile && !options.ir) {
|
|
3947
4392
|
const { startRepl: startRepl2 } = await Promise.resolve().then(() => (init_rex_repl(), exports_rex_repl));
|
|
3948
4393
|
await startRepl2();
|
|
@@ -3962,7 +4407,7 @@ async function main() {
|
|
|
3962
4407
|
});
|
|
3963
4408
|
} else {
|
|
3964
4409
|
const { value } = evaluateSource(source);
|
|
3965
|
-
output =
|
|
4410
|
+
output = stringify(value);
|
|
3966
4411
|
}
|
|
3967
4412
|
if (options.out) {
|
|
3968
4413
|
await writeFile(options.out, `${output}
|