@gabrielbryk/jq-ts 1.3.6 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/index.cjs +1445 -654
- package/dist/index.d.cts +85 -5
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +85 -5
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1439 -650
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -41,7 +41,6 @@ var RuntimeError = class extends BaseError {
|
|
|
41
41
|
super("runtime", message, span);
|
|
42
42
|
}
|
|
43
43
|
};
|
|
44
|
-
|
|
45
44
|
//#endregion
|
|
46
45
|
//#region src/tokens.ts
|
|
47
46
|
const keywordKinds = {
|
|
@@ -65,7 +64,6 @@ const keywordKinds = {
|
|
|
65
64
|
label: "Label",
|
|
66
65
|
break: "Break"
|
|
67
66
|
};
|
|
68
|
-
|
|
69
67
|
//#endregion
|
|
70
68
|
//#region src/lexer.ts
|
|
71
69
|
/**
|
|
@@ -379,7 +377,6 @@ const lex = (text) => {
|
|
|
379
377
|
}
|
|
380
378
|
};
|
|
381
379
|
const isHexDigit = (ch) => !!ch && (ch >= "0" && ch <= "9" || ch >= "a" && ch <= "f" || ch >= "A" && ch <= "F");
|
|
382
|
-
|
|
383
380
|
//#endregion
|
|
384
381
|
//#region src/parser.ts
|
|
385
382
|
/**
|
|
@@ -446,19 +443,93 @@ var Parser = class {
|
|
|
446
443
|
}
|
|
447
444
|
let expr = this.parsePipe(allowComma);
|
|
448
445
|
while (this.match("As")) {
|
|
449
|
-
const
|
|
450
|
-
this.consume("Pipe", "Expected \"|\" after
|
|
446
|
+
const pattern = this.parseBindingPattern();
|
|
447
|
+
this.consume("Pipe", "Expected \"|\" after binding pattern");
|
|
451
448
|
const body = this.parseBinding(allowComma);
|
|
452
449
|
expr = {
|
|
453
450
|
kind: "As",
|
|
454
451
|
bind: expr,
|
|
455
|
-
|
|
452
|
+
pattern,
|
|
456
453
|
body,
|
|
457
454
|
span: spanBetween(expr.span, body.span)
|
|
458
455
|
};
|
|
459
456
|
}
|
|
460
457
|
return expr;
|
|
461
458
|
}
|
|
459
|
+
parseBindingPattern() {
|
|
460
|
+
if (this.match("Variable")) {
|
|
461
|
+
const token = this.previous();
|
|
462
|
+
return {
|
|
463
|
+
kind: "VariablePattern",
|
|
464
|
+
name: String(token.value),
|
|
465
|
+
span: token.span
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
if (this.match("LBracket")) {
|
|
469
|
+
const start = this.previous();
|
|
470
|
+
const items = [];
|
|
471
|
+
if (!this.match("RBracket")) {
|
|
472
|
+
do
|
|
473
|
+
items.push(this.parseBindingPattern());
|
|
474
|
+
while (this.match("Comma"));
|
|
475
|
+
this.consume("RBracket", "Expected \"]\" after array binding pattern");
|
|
476
|
+
}
|
|
477
|
+
return {
|
|
478
|
+
kind: "ArrayPattern",
|
|
479
|
+
items,
|
|
480
|
+
span: spanBetween(start.span, this.previous().span)
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
if (this.match("LBrace")) {
|
|
484
|
+
const start = this.previous();
|
|
485
|
+
const entries = [];
|
|
486
|
+
if (!this.match("RBrace")) {
|
|
487
|
+
do
|
|
488
|
+
entries.push(this.parseObjectBindingPatternEntry());
|
|
489
|
+
while (this.match("Comma"));
|
|
490
|
+
this.consume("RBrace", "Expected \"}\" after object binding pattern");
|
|
491
|
+
}
|
|
492
|
+
return {
|
|
493
|
+
kind: "ObjectPattern",
|
|
494
|
+
entries,
|
|
495
|
+
span: spanBetween(start.span, this.previous().span)
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
throw this.error(this.peek(), "Expected variable or destructuring pattern after \"as\"");
|
|
499
|
+
}
|
|
500
|
+
parseObjectBindingPatternEntry() {
|
|
501
|
+
if (this.match("Variable")) {
|
|
502
|
+
const token = this.previous();
|
|
503
|
+
const name = String(token.value);
|
|
504
|
+
return {
|
|
505
|
+
key: name,
|
|
506
|
+
pattern: {
|
|
507
|
+
kind: "VariablePattern",
|
|
508
|
+
name,
|
|
509
|
+
span: token.span
|
|
510
|
+
},
|
|
511
|
+
span: token.span
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
let key;
|
|
515
|
+
let keySpan;
|
|
516
|
+
if (this.match("Identifier")) {
|
|
517
|
+
const token = this.previous();
|
|
518
|
+
key = String(token.value);
|
|
519
|
+
keySpan = token.span;
|
|
520
|
+
} else if (this.match("String")) {
|
|
521
|
+
const token = this.previous();
|
|
522
|
+
key = String(token.value);
|
|
523
|
+
keySpan = token.span;
|
|
524
|
+
} else throw this.error(this.peek(), "Expected identifier, string, or variable shorthand in object binding pattern");
|
|
525
|
+
this.consume("Colon", "Expected \":\" after object binding pattern key");
|
|
526
|
+
const pattern = this.parseBindingPattern();
|
|
527
|
+
return {
|
|
528
|
+
key,
|
|
529
|
+
pattern,
|
|
530
|
+
span: spanBetween(keySpan, pattern.span)
|
|
531
|
+
};
|
|
532
|
+
}
|
|
462
533
|
parsePipe(allowComma = true) {
|
|
463
534
|
let expr = this.parseComma(allowComma);
|
|
464
535
|
while (this.match("Pipe")) {
|
|
@@ -692,10 +763,7 @@ var Parser = class {
|
|
|
692
763
|
expr = {
|
|
693
764
|
kind: "Try",
|
|
694
765
|
body: expr,
|
|
695
|
-
handler:
|
|
696
|
-
kind: "Identity",
|
|
697
|
-
span: op.span
|
|
698
|
-
},
|
|
766
|
+
handler: void 0,
|
|
699
767
|
span: spanBetween(expr.span, op.span)
|
|
700
768
|
};
|
|
701
769
|
continue;
|
|
@@ -753,8 +821,8 @@ var Parser = class {
|
|
|
753
821
|
parseReduce(start) {
|
|
754
822
|
const source = this.parsePipe();
|
|
755
823
|
this.consume("As", "Expected \"as\" after reduce source");
|
|
756
|
-
const
|
|
757
|
-
this.consume("LParen", "Expected \"(\" after
|
|
824
|
+
const pattern = this.parseBindingPattern();
|
|
825
|
+
this.consume("LParen", "Expected \"(\" after binding pattern");
|
|
758
826
|
const init = this.parseComma();
|
|
759
827
|
this.consume("Semicolon", "Expected \";\" after init");
|
|
760
828
|
const update = this.parseComma();
|
|
@@ -762,7 +830,7 @@ var Parser = class {
|
|
|
762
830
|
return {
|
|
763
831
|
kind: "Reduce",
|
|
764
832
|
source,
|
|
765
|
-
|
|
833
|
+
pattern,
|
|
766
834
|
init,
|
|
767
835
|
update,
|
|
768
836
|
span: spanBetween(start.span, end.span)
|
|
@@ -771,8 +839,8 @@ var Parser = class {
|
|
|
771
839
|
parseForeach(start) {
|
|
772
840
|
const source = this.parsePipe();
|
|
773
841
|
this.consume("As", "Expected \"as\" after foreach source");
|
|
774
|
-
const
|
|
775
|
-
this.consume("LParen", "Expected \"(\" after
|
|
842
|
+
const pattern = this.parseBindingPattern();
|
|
843
|
+
this.consume("LParen", "Expected \"(\" after binding pattern");
|
|
776
844
|
const init = this.parseComma();
|
|
777
845
|
this.consume("Semicolon", "Expected \";\" after init");
|
|
778
846
|
const update = this.parseComma();
|
|
@@ -782,7 +850,7 @@ var Parser = class {
|
|
|
782
850
|
return {
|
|
783
851
|
kind: "Foreach",
|
|
784
852
|
source,
|
|
785
|
-
|
|
853
|
+
pattern,
|
|
786
854
|
init,
|
|
787
855
|
update,
|
|
788
856
|
extract,
|
|
@@ -1004,10 +1072,7 @@ var Parser = class {
|
|
|
1004
1072
|
expr = {
|
|
1005
1073
|
kind: "Try",
|
|
1006
1074
|
body: expr,
|
|
1007
|
-
handler:
|
|
1008
|
-
kind: "Identity",
|
|
1009
|
-
span: op.span
|
|
1010
|
-
},
|
|
1075
|
+
handler: void 0,
|
|
1011
1076
|
span: spanBetween(expr.span, op.span)
|
|
1012
1077
|
};
|
|
1013
1078
|
continue;
|
|
@@ -1153,7 +1218,6 @@ const spanBetween = (a, b) => ({
|
|
|
1153
1218
|
start: Math.min(a.start, b.start),
|
|
1154
1219
|
end: Math.max(a.end, b.end)
|
|
1155
1220
|
});
|
|
1156
|
-
|
|
1157
1221
|
//#endregion
|
|
1158
1222
|
//#region src/builtins/registry.ts
|
|
1159
1223
|
const builtins = {};
|
|
@@ -1164,7 +1228,6 @@ const registerBuiltin = (spec) => {
|
|
|
1164
1228
|
const registerBuiltins = (specs) => {
|
|
1165
1229
|
for (const spec of specs) registerBuiltin(spec);
|
|
1166
1230
|
};
|
|
1167
|
-
|
|
1168
1231
|
//#endregion
|
|
1169
1232
|
//#region src/value.ts
|
|
1170
1233
|
/**
|
|
@@ -1191,7 +1254,7 @@ const valueEquals = (a, b) => {
|
|
|
1191
1254
|
for (let i = 0; i < a.length; i += 1) if (!valueEquals(a[i], b[i])) return false;
|
|
1192
1255
|
return true;
|
|
1193
1256
|
}
|
|
1194
|
-
if (isPlainObject(a) && isPlainObject(b)) {
|
|
1257
|
+
if (isPlainObject$1(a) && isPlainObject$1(b)) {
|
|
1195
1258
|
const aKeys = Object.keys(a).sort();
|
|
1196
1259
|
const bKeys = Object.keys(b).sort();
|
|
1197
1260
|
if (aKeys.length !== bKeys.length) return false;
|
|
@@ -1228,7 +1291,7 @@ const compareValues = (a, b) => {
|
|
|
1228
1291
|
}
|
|
1229
1292
|
return a.length < b.length ? -1 : 1;
|
|
1230
1293
|
}
|
|
1231
|
-
if (isPlainObject(a) && isPlainObject(b)) {
|
|
1294
|
+
if (isPlainObject$1(a) && isPlainObject$1(b)) {
|
|
1232
1295
|
const aKeys = Object.keys(a).sort();
|
|
1233
1296
|
const bKeys = Object.keys(b).sort();
|
|
1234
1297
|
const len = Math.min(aKeys.length, bKeys.length);
|
|
@@ -1262,7 +1325,7 @@ const typeRank = (value) => {
|
|
|
1262
1325
|
* @param value - The value to check.
|
|
1263
1326
|
* @returns `true` if `value` is a non-null object (and not an array).
|
|
1264
1327
|
*/
|
|
1265
|
-
const isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1328
|
+
const isPlainObject$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1266
1329
|
/**
|
|
1267
1330
|
* Checks if a value is a JSON array.
|
|
1268
1331
|
*
|
|
@@ -1285,7 +1348,6 @@ const describeType = (value) => {
|
|
|
1285
1348
|
if (Array.isArray(value)) return "array";
|
|
1286
1349
|
return "object";
|
|
1287
1350
|
};
|
|
1288
|
-
|
|
1289
1351
|
//#endregion
|
|
1290
1352
|
//#region src/builtins/utils.ts
|
|
1291
1353
|
const emit$1 = (value, span, tracker) => {
|
|
@@ -1307,7 +1369,6 @@ const stableStringify = (value) => {
|
|
|
1307
1369
|
if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
|
|
1308
1370
|
return `{${Object.keys(value).sort().map((k) => `${JSON.stringify(k)}:${stableStringify(value[k])}`).join(",")}}`;
|
|
1309
1371
|
};
|
|
1310
|
-
|
|
1311
1372
|
//#endregion
|
|
1312
1373
|
//#region src/builtins/std.ts
|
|
1313
1374
|
const toJqString = (value) => {
|
|
@@ -1353,9 +1414,87 @@ const stdBuiltins = [
|
|
|
1353
1414
|
if (typeof input === "string") yield emit$1(Array.from(input).length, span, tracker);
|
|
1354
1415
|
else if (Array.isArray(input)) yield emit$1(input.length, span, tracker);
|
|
1355
1416
|
else if (input !== null && typeof input === "object") yield emit$1(Object.keys(input).length, span, tracker);
|
|
1417
|
+
else if (typeof input === "number") yield emit$1(Math.abs(input), span, tracker);
|
|
1356
1418
|
else throw new RuntimeError(`Cannot take length of ${describeType(input)}`, span);
|
|
1357
1419
|
}
|
|
1358
1420
|
},
|
|
1421
|
+
{
|
|
1422
|
+
name: "arrays",
|
|
1423
|
+
arity: 0,
|
|
1424
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
1425
|
+
if (Array.isArray(input)) yield emit$1(input, span, tracker);
|
|
1426
|
+
}
|
|
1427
|
+
},
|
|
1428
|
+
{
|
|
1429
|
+
name: "objects",
|
|
1430
|
+
arity: 0,
|
|
1431
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
1432
|
+
if (isPlainObject$1(input)) yield emit$1(input, span, tracker);
|
|
1433
|
+
}
|
|
1434
|
+
},
|
|
1435
|
+
{
|
|
1436
|
+
name: "iterables",
|
|
1437
|
+
arity: 0,
|
|
1438
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
1439
|
+
if (Array.isArray(input) || isPlainObject$1(input)) yield emit$1(input, span, tracker);
|
|
1440
|
+
}
|
|
1441
|
+
},
|
|
1442
|
+
{
|
|
1443
|
+
name: "booleans",
|
|
1444
|
+
arity: 0,
|
|
1445
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
1446
|
+
if (typeof input === "boolean") yield emit$1(input, span, tracker);
|
|
1447
|
+
}
|
|
1448
|
+
},
|
|
1449
|
+
{
|
|
1450
|
+
name: "numbers",
|
|
1451
|
+
arity: 0,
|
|
1452
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
1453
|
+
if (typeof input === "number") yield emit$1(input, span, tracker);
|
|
1454
|
+
}
|
|
1455
|
+
},
|
|
1456
|
+
{
|
|
1457
|
+
name: "strings",
|
|
1458
|
+
arity: 0,
|
|
1459
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
1460
|
+
if (typeof input === "string") yield emit$1(input, span, tracker);
|
|
1461
|
+
}
|
|
1462
|
+
},
|
|
1463
|
+
{
|
|
1464
|
+
name: "nulls",
|
|
1465
|
+
arity: 0,
|
|
1466
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
1467
|
+
if (input === null) yield emit$1(input, span, tracker);
|
|
1468
|
+
}
|
|
1469
|
+
},
|
|
1470
|
+
{
|
|
1471
|
+
name: "values",
|
|
1472
|
+
arity: 0,
|
|
1473
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
1474
|
+
if (input !== null) yield emit$1(input, span, tracker);
|
|
1475
|
+
}
|
|
1476
|
+
},
|
|
1477
|
+
{
|
|
1478
|
+
name: "scalars",
|
|
1479
|
+
arity: 0,
|
|
1480
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
1481
|
+
if (input === null || typeof input !== "object") yield emit$1(input, span, tracker);
|
|
1482
|
+
}
|
|
1483
|
+
},
|
|
1484
|
+
{
|
|
1485
|
+
name: "finites",
|
|
1486
|
+
arity: 0,
|
|
1487
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
1488
|
+
if (typeof input === "number" && Number.isFinite(input)) yield emit$1(input, span, tracker);
|
|
1489
|
+
}
|
|
1490
|
+
},
|
|
1491
|
+
{
|
|
1492
|
+
name: "normals",
|
|
1493
|
+
arity: 0,
|
|
1494
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
1495
|
+
if (typeof input === "number" && Number.isFinite(input) && input !== 0) yield emit$1(input, span, tracker);
|
|
1496
|
+
}
|
|
1497
|
+
},
|
|
1359
1498
|
{
|
|
1360
1499
|
name: "empty",
|
|
1361
1500
|
arity: 0,
|
|
@@ -1378,7 +1517,7 @@ const stdBuiltins = [
|
|
|
1378
1517
|
{
|
|
1379
1518
|
name: "walk",
|
|
1380
1519
|
arity: 1,
|
|
1381
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
1520
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
1382
1521
|
const f = args[0];
|
|
1383
1522
|
const walkRec = function* (curr) {
|
|
1384
1523
|
tracker.step(span);
|
|
@@ -1387,7 +1526,7 @@ const stdBuiltins = [
|
|
|
1387
1526
|
const newArr = [];
|
|
1388
1527
|
for (const item of curr) for (const walkedItem of walkRec(item)) newArr.push(walkedItem);
|
|
1389
1528
|
newStruct = newArr;
|
|
1390
|
-
} else if (isPlainObject(curr)) {
|
|
1529
|
+
} else if (isPlainObject$1(curr)) {
|
|
1391
1530
|
const newObj = {};
|
|
1392
1531
|
const keys = Object.keys(curr).sort();
|
|
1393
1532
|
let objValid = true;
|
|
@@ -1408,23 +1547,27 @@ const stdBuiltins = [
|
|
|
1408
1547
|
if (!objValid) return;
|
|
1409
1548
|
newStruct = newObj;
|
|
1410
1549
|
}
|
|
1411
|
-
yield* evaluate
|
|
1550
|
+
yield* evaluate(f, newStruct, env, tracker);
|
|
1412
1551
|
};
|
|
1413
1552
|
yield* walkRec(input);
|
|
1414
1553
|
}
|
|
1415
1554
|
}
|
|
1416
1555
|
];
|
|
1417
|
-
|
|
1418
1556
|
//#endregion
|
|
1419
1557
|
//#region src/builtins/errors.ts
|
|
1420
1558
|
const errorBuiltins = [{
|
|
1559
|
+
name: "error",
|
|
1560
|
+
arity: 0,
|
|
1561
|
+
apply: function* (_input, _args, _env, _tracker, _eval, span) {
|
|
1562
|
+
throw new RuntimeError("null", span);
|
|
1563
|
+
}
|
|
1564
|
+
}, {
|
|
1421
1565
|
name: "error",
|
|
1422
1566
|
arity: 1,
|
|
1423
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
1424
|
-
for (const msg of evaluate
|
|
1567
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
1568
|
+
for (const msg of evaluate(args[0], input, env, tracker)) throw new RuntimeError(typeof msg === "string" ? msg : stableStringify(msg), span);
|
|
1425
1569
|
}
|
|
1426
1570
|
}];
|
|
1427
|
-
|
|
1428
1571
|
//#endregion
|
|
1429
1572
|
//#region src/builtins/strings.ts
|
|
1430
1573
|
const checkContains = (a, b) => {
|
|
@@ -1432,7 +1575,7 @@ const checkContains = (a, b) => {
|
|
|
1432
1575
|
if (typeof a !== typeof b) return false;
|
|
1433
1576
|
if (typeof a === "string" && typeof b === "string") return a.includes(b);
|
|
1434
1577
|
if (Array.isArray(a) && Array.isArray(b)) return b.every((bItem) => a.some((aItem) => checkContains(aItem, bItem)));
|
|
1435
|
-
if (isPlainObject(a) && isPlainObject(b)) {
|
|
1578
|
+
if (isPlainObject$1(a) && isPlainObject$1(b)) {
|
|
1436
1579
|
const keys = Object.keys(b);
|
|
1437
1580
|
for (const key of keys) {
|
|
1438
1581
|
if (!Object.prototype.hasOwnProperty.call(a, key)) return false;
|
|
@@ -1448,9 +1591,9 @@ const stringBuiltins = [
|
|
|
1448
1591
|
{
|
|
1449
1592
|
name: "split",
|
|
1450
1593
|
arity: 1,
|
|
1451
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
1594
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
1452
1595
|
if (typeof input !== "string") throw new RuntimeError("split input must be a string", span);
|
|
1453
|
-
const sepGen = evaluate
|
|
1596
|
+
const sepGen = evaluate(args[0], input, env, tracker);
|
|
1454
1597
|
for (const sep of sepGen) {
|
|
1455
1598
|
if (typeof sep !== "string") throw new RuntimeError("split separator must be a string", span);
|
|
1456
1599
|
yield emit$1(input.split(sep), span, tracker);
|
|
@@ -1460,16 +1603,15 @@ const stringBuiltins = [
|
|
|
1460
1603
|
{
|
|
1461
1604
|
name: "join",
|
|
1462
1605
|
arity: 1,
|
|
1463
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
1606
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
1464
1607
|
if (!Array.isArray(input)) throw new RuntimeError("join input must be an array", span);
|
|
1465
|
-
const sepGen = evaluate
|
|
1608
|
+
const sepGen = evaluate(args[0], input, env, tracker);
|
|
1466
1609
|
for (const sep of sepGen) {
|
|
1467
1610
|
if (typeof sep !== "string") throw new RuntimeError("join separator must be a string", span);
|
|
1468
1611
|
const parts = [];
|
|
1469
|
-
for (const item of input)
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
}
|
|
1612
|
+
for (const item of input) if (item === null) parts.push("");
|
|
1613
|
+
else if (typeof item === "string" || typeof item === "number" || typeof item === "boolean") parts.push(String(item));
|
|
1614
|
+
else throw new RuntimeError(`join cannot join ${describeType(item)}`, span);
|
|
1473
1615
|
yield emit$1(parts.join(sep), span, tracker);
|
|
1474
1616
|
}
|
|
1475
1617
|
}
|
|
@@ -1477,9 +1619,9 @@ const stringBuiltins = [
|
|
|
1477
1619
|
{
|
|
1478
1620
|
name: "startswith",
|
|
1479
1621
|
arity: 1,
|
|
1480
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
1622
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
1481
1623
|
if (typeof input !== "string") throw new RuntimeError("startswith input must be a string", span);
|
|
1482
|
-
const prefixGen = evaluate
|
|
1624
|
+
const prefixGen = evaluate(args[0], input, env, tracker);
|
|
1483
1625
|
for (const prefix of prefixGen) {
|
|
1484
1626
|
if (typeof prefix !== "string") throw new RuntimeError("startswith prefix must be a string", span);
|
|
1485
1627
|
yield emit$1(input.startsWith(prefix), span, tracker);
|
|
@@ -1489,9 +1631,9 @@ const stringBuiltins = [
|
|
|
1489
1631
|
{
|
|
1490
1632
|
name: "endswith",
|
|
1491
1633
|
arity: 1,
|
|
1492
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
1634
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
1493
1635
|
if (typeof input !== "string") throw new RuntimeError("endswith input must be a string", span);
|
|
1494
|
-
const suffixGen = evaluate
|
|
1636
|
+
const suffixGen = evaluate(args[0], input, env, tracker);
|
|
1495
1637
|
for (const suffix of suffixGen) {
|
|
1496
1638
|
if (typeof suffix !== "string") throw new RuntimeError("endswith suffix must be a string", span);
|
|
1497
1639
|
yield emit$1(input.endsWith(suffix), span, tracker);
|
|
@@ -1501,16 +1643,16 @@ const stringBuiltins = [
|
|
|
1501
1643
|
{
|
|
1502
1644
|
name: "contains",
|
|
1503
1645
|
arity: 1,
|
|
1504
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
1505
|
-
const bGen = evaluate
|
|
1646
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
1647
|
+
const bGen = evaluate(args[0], input, env, tracker);
|
|
1506
1648
|
for (const b of bGen) yield emit$1(checkContains(input, b), span, tracker);
|
|
1507
1649
|
}
|
|
1508
1650
|
},
|
|
1509
1651
|
{
|
|
1510
1652
|
name: "index",
|
|
1511
1653
|
arity: 1,
|
|
1512
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
1513
|
-
const searchGen = evaluate
|
|
1654
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
1655
|
+
const searchGen = evaluate(args[0], input, env, tracker);
|
|
1514
1656
|
for (const search of searchGen) if (Array.isArray(input)) {
|
|
1515
1657
|
let foundIndex = null;
|
|
1516
1658
|
for (let i = 0; i < input.length; i++) {
|
|
@@ -1532,8 +1674,8 @@ const stringBuiltins = [
|
|
|
1532
1674
|
{
|
|
1533
1675
|
name: "rindex",
|
|
1534
1676
|
arity: 1,
|
|
1535
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
1536
|
-
const searchGen = evaluate
|
|
1677
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
1678
|
+
const searchGen = evaluate(args[0], input, env, tracker);
|
|
1537
1679
|
for (const search of searchGen) if (Array.isArray(input)) {
|
|
1538
1680
|
let foundIndex = null;
|
|
1539
1681
|
for (let i = input.length - 1; i >= 0; i--) {
|
|
@@ -1555,8 +1697,8 @@ const stringBuiltins = [
|
|
|
1555
1697
|
{
|
|
1556
1698
|
name: "indices",
|
|
1557
1699
|
arity: 1,
|
|
1558
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
1559
|
-
const searchGen = evaluate
|
|
1700
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
1701
|
+
const searchGen = evaluate(args[0], input, env, tracker);
|
|
1560
1702
|
for (const search of searchGen) {
|
|
1561
1703
|
const indices = [];
|
|
1562
1704
|
if (Array.isArray(input)) for (let i = 0; i < input.length; i++) {
|
|
@@ -1594,6 +1736,14 @@ const stringBuiltins = [
|
|
|
1594
1736
|
yield emit$1(Array.from(input).map((c) => c.codePointAt(0)), span, tracker);
|
|
1595
1737
|
}
|
|
1596
1738
|
},
|
|
1739
|
+
{
|
|
1740
|
+
name: "utf8bytelength",
|
|
1741
|
+
arity: 0,
|
|
1742
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
1743
|
+
if (typeof input !== "string") throw new RuntimeError("utf8bytelength expects string", span);
|
|
1744
|
+
yield emit$1(utf8ByteLength(input), span, tracker);
|
|
1745
|
+
}
|
|
1746
|
+
},
|
|
1597
1747
|
{
|
|
1598
1748
|
name: "implode",
|
|
1599
1749
|
arity: 0,
|
|
@@ -1610,9 +1760,9 @@ const stringBuiltins = [
|
|
|
1610
1760
|
{
|
|
1611
1761
|
name: "ltrimstr",
|
|
1612
1762
|
arity: 1,
|
|
1613
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
1763
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
1614
1764
|
if (typeof input !== "string") throw new RuntimeError("ltrimstr expects string", span);
|
|
1615
|
-
const prefixGen = evaluate
|
|
1765
|
+
const prefixGen = evaluate(args[0], input, env, tracker);
|
|
1616
1766
|
for (const prefix of prefixGen) {
|
|
1617
1767
|
if (typeof prefix !== "string") throw new RuntimeError("ltrimstr prefix must be a string", span);
|
|
1618
1768
|
if (input.startsWith(prefix)) yield emit$1(input.slice(prefix.length), span, tracker);
|
|
@@ -1621,195 +1771,575 @@ const stringBuiltins = [
|
|
|
1621
1771
|
}
|
|
1622
1772
|
},
|
|
1623
1773
|
{
|
|
1624
|
-
name: "
|
|
1774
|
+
name: "trimstr",
|
|
1625
1775
|
arity: 1,
|
|
1626
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
1627
|
-
if (typeof input !== "string") throw new RuntimeError("
|
|
1628
|
-
const
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
if (
|
|
1632
|
-
|
|
1776
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
1777
|
+
if (typeof input !== "string") throw new RuntimeError("trimstr expects string", span);
|
|
1778
|
+
for (const trim of evaluate(args[0], input, env, tracker)) {
|
|
1779
|
+
if (typeof trim !== "string") throw new RuntimeError("trimstr argument must be string", span);
|
|
1780
|
+
let result = input;
|
|
1781
|
+
if (result.startsWith(trim)) result = result.slice(trim.length);
|
|
1782
|
+
if (result.endsWith(trim)) result = result.slice(0, result.length - trim.length);
|
|
1783
|
+
yield emit$1(result, span, tracker);
|
|
1633
1784
|
}
|
|
1634
1785
|
}
|
|
1635
1786
|
},
|
|
1636
1787
|
{
|
|
1637
|
-
name: "
|
|
1788
|
+
name: "trim",
|
|
1638
1789
|
arity: 0,
|
|
1639
1790
|
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
1640
|
-
if (typeof input !== "string") throw new RuntimeError("
|
|
1641
|
-
yield emit$1(input.
|
|
1791
|
+
if (typeof input !== "string") throw new RuntimeError("trim expects string", span);
|
|
1792
|
+
yield emit$1(input.trim(), span, tracker);
|
|
1642
1793
|
}
|
|
1643
1794
|
},
|
|
1644
1795
|
{
|
|
1645
|
-
name: "
|
|
1796
|
+
name: "ltrim",
|
|
1646
1797
|
arity: 0,
|
|
1647
1798
|
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
1648
|
-
if (typeof input !== "string") throw new RuntimeError("
|
|
1649
|
-
yield emit$1(input.
|
|
1799
|
+
if (typeof input !== "string") throw new RuntimeError("ltrim expects string", span);
|
|
1800
|
+
yield emit$1(input.trimStart(), span, tracker);
|
|
1650
1801
|
}
|
|
1651
|
-
}
|
|
1652
|
-
];
|
|
1653
|
-
|
|
1654
|
-
//#endregion
|
|
1655
|
-
//#region src/builtins/collections.ts
|
|
1656
|
-
function sortStable(arr, compare$1) {
|
|
1657
|
-
return arr.map((item, index) => ({
|
|
1658
|
-
item,
|
|
1659
|
-
index
|
|
1660
|
-
})).sort((a, b) => {
|
|
1661
|
-
const cmp = compare$1(a.item, b.item);
|
|
1662
|
-
return cmp !== 0 ? cmp : a.index - b.index;
|
|
1663
|
-
}).map((p) => p.item);
|
|
1664
|
-
}
|
|
1665
|
-
const collectionBuiltins = [
|
|
1802
|
+
},
|
|
1666
1803
|
{
|
|
1667
|
-
name: "
|
|
1804
|
+
name: "rtrim",
|
|
1668
1805
|
arity: 0,
|
|
1669
1806
|
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
1670
|
-
if (
|
|
1671
|
-
|
|
1672
|
-
else throw new RuntimeError(`keys expects an array or object`, span);
|
|
1673
|
-
}
|
|
1674
|
-
},
|
|
1675
|
-
{
|
|
1676
|
-
name: "has",
|
|
1677
|
-
arity: 1,
|
|
1678
|
-
apply: function* (input, args, env, tracker, evaluate$1, span) {
|
|
1679
|
-
const keyFilter = args[0];
|
|
1680
|
-
for (const key of evaluate$1(keyFilter, input, env, tracker)) if (Array.isArray(input)) {
|
|
1681
|
-
const idx = ensureIndex(key);
|
|
1682
|
-
yield emit$1(idx !== void 0 && idx >= 0 && idx < input.length, span, tracker);
|
|
1683
|
-
} else if (input !== null && typeof input === "object") {
|
|
1684
|
-
let keyStr;
|
|
1685
|
-
if (typeof key === "string") keyStr = key;
|
|
1686
|
-
else if (typeof key === "number") keyStr = key.toString();
|
|
1687
|
-
else throw new RuntimeError(`has() key must be string or number for object input`, span);
|
|
1688
|
-
yield emit$1(Object.prototype.hasOwnProperty.call(input, keyStr), span, tracker);
|
|
1689
|
-
} else throw new RuntimeError(`has() expects an array or object input`, span);
|
|
1690
|
-
}
|
|
1691
|
-
},
|
|
1692
|
-
{
|
|
1693
|
-
name: "map",
|
|
1694
|
-
arity: 1,
|
|
1695
|
-
apply: function* (input, args, env, tracker, evaluate$1, span) {
|
|
1696
|
-
if (!Array.isArray(input)) throw new RuntimeError("map expects an array", span);
|
|
1697
|
-
const result = [];
|
|
1698
|
-
const filter = args[0];
|
|
1699
|
-
for (const item of input) {
|
|
1700
|
-
tracker.step(span);
|
|
1701
|
-
for (const output of evaluate$1(filter, item, env, tracker)) result.push(output);
|
|
1702
|
-
}
|
|
1703
|
-
yield emit$1(result, span, tracker);
|
|
1807
|
+
if (typeof input !== "string") throw new RuntimeError("rtrim expects string", span);
|
|
1808
|
+
yield emit$1(input.trimEnd(), span, tracker);
|
|
1704
1809
|
}
|
|
1705
1810
|
},
|
|
1706
1811
|
{
|
|
1707
|
-
name: "
|
|
1812
|
+
name: "rtrimstr",
|
|
1708
1813
|
arity: 1,
|
|
1709
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1814
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
1815
|
+
if (typeof input !== "string") throw new RuntimeError("rtrimstr expects string", span);
|
|
1816
|
+
const suffixGen = evaluate(args[0], input, env, tracker);
|
|
1817
|
+
for (const suffix of suffixGen) {
|
|
1818
|
+
if (typeof suffix !== "string") throw new RuntimeError("rtrimstr suffix must be a string", span);
|
|
1819
|
+
if (input.endsWith(suffix)) yield emit$1(input.slice(0, input.length - suffix.length), span, tracker);
|
|
1820
|
+
else yield emit$1(input, span, tracker);
|
|
1714
1821
|
}
|
|
1715
1822
|
}
|
|
1716
1823
|
},
|
|
1717
1824
|
{
|
|
1718
|
-
name: "
|
|
1825
|
+
name: "ascii_downcase",
|
|
1719
1826
|
arity: 0,
|
|
1720
1827
|
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
1721
|
-
if (
|
|
1722
|
-
|
|
1723
|
-
tracker.step(span);
|
|
1724
|
-
yield emit$1(sorted, span, tracker);
|
|
1725
|
-
}
|
|
1726
|
-
},
|
|
1727
|
-
{
|
|
1728
|
-
name: "sort_by",
|
|
1729
|
-
arity: 1,
|
|
1730
|
-
apply: function* (input, args, env, tracker, evaluate$1, span) {
|
|
1731
|
-
if (!Array.isArray(input)) throw new RuntimeError("sort_by expects an array", span);
|
|
1732
|
-
const filter = args[0];
|
|
1733
|
-
const pairs = [];
|
|
1734
|
-
for (const item of input) {
|
|
1735
|
-
tracker.step(span);
|
|
1736
|
-
const keys = Array.from(evaluate$1(filter, item, env, tracker));
|
|
1737
|
-
if (keys.length !== 1) throw new RuntimeError("sort_by key expression must return exactly one value", span);
|
|
1738
|
-
pairs.push({
|
|
1739
|
-
val: item,
|
|
1740
|
-
key: keys[0]
|
|
1741
|
-
});
|
|
1742
|
-
}
|
|
1743
|
-
yield emit$1(sortStable(pairs, (a, b) => compareValues(a.key, b.key)).map((p) => p.val), span, tracker);
|
|
1828
|
+
if (typeof input !== "string") throw new RuntimeError("ascii_downcase expects string", span);
|
|
1829
|
+
yield emit$1(input.replace(/[A-Z]/g, (char) => char.toLowerCase()), span, tracker);
|
|
1744
1830
|
}
|
|
1745
1831
|
},
|
|
1746
1832
|
{
|
|
1747
|
-
name: "
|
|
1833
|
+
name: "ascii_upcase",
|
|
1748
1834
|
arity: 0,
|
|
1749
1835
|
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
1750
|
-
if (
|
|
1751
|
-
|
|
1752
|
-
const result = [];
|
|
1753
|
-
for (const item of input) {
|
|
1754
|
-
tracker.step(span);
|
|
1755
|
-
if (!seen.some((s) => valueEquals(s, item))) {
|
|
1756
|
-
seen.push(item);
|
|
1757
|
-
result.push(item);
|
|
1758
|
-
}
|
|
1759
|
-
}
|
|
1760
|
-
yield emit$1(result, span, tracker);
|
|
1761
|
-
}
|
|
1762
|
-
},
|
|
1763
|
-
{
|
|
1764
|
-
name: "unique_by",
|
|
1765
|
-
arity: 1,
|
|
1766
|
-
apply: function* (input, args, env, tracker, evaluate$1, span) {
|
|
1767
|
-
if (!Array.isArray(input)) throw new RuntimeError("unique_by expects an array", span);
|
|
1768
|
-
const filter = args[0];
|
|
1769
|
-
const seenKeys = [];
|
|
1770
|
-
const result = [];
|
|
1771
|
-
for (const item of input) {
|
|
1772
|
-
tracker.step(span);
|
|
1773
|
-
const keys = Array.from(evaluate$1(filter, item, env, tracker));
|
|
1774
|
-
if (keys.length !== 1) throw new RuntimeError("unique_by key expression must return exactly one value", span);
|
|
1775
|
-
const key = keys[0];
|
|
1776
|
-
if (!seenKeys.some((s) => valueEquals(s, key))) {
|
|
1777
|
-
seenKeys.push(key);
|
|
1778
|
-
result.push(item);
|
|
1779
|
-
}
|
|
1780
|
-
}
|
|
1781
|
-
yield emit$1(result, span, tracker);
|
|
1836
|
+
if (typeof input !== "string") throw new RuntimeError("ascii_upcase expects string", span);
|
|
1837
|
+
yield emit$1(input.replace(/[a-z]/g, (char) => char.toUpperCase()), span, tracker);
|
|
1782
1838
|
}
|
|
1783
1839
|
},
|
|
1784
1840
|
{
|
|
1785
|
-
name: "
|
|
1841
|
+
name: "tojson",
|
|
1786
1842
|
arity: 0,
|
|
1787
1843
|
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
1788
|
-
|
|
1789
|
-
key: i,
|
|
1790
|
-
value: v
|
|
1791
|
-
})), span, tracker);
|
|
1792
|
-
else if (input !== null && typeof input === "object") yield emit$1(Object.keys(input).sort().map((k) => ({
|
|
1793
|
-
key: k,
|
|
1794
|
-
value: input[k]
|
|
1795
|
-
})), span, tracker);
|
|
1796
|
-
else throw new RuntimeError("to_entries expects array or object", span);
|
|
1844
|
+
yield emit$1(stableStringify(input), span, tracker);
|
|
1797
1845
|
}
|
|
1798
1846
|
},
|
|
1799
1847
|
{
|
|
1800
|
-
name: "
|
|
1848
|
+
name: "fromjson",
|
|
1801
1849
|
arity: 0,
|
|
1802
1850
|
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
1803
|
-
if (
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1851
|
+
if (typeof input !== "string") throw new RuntimeError("fromjson expects string", span);
|
|
1852
|
+
try {
|
|
1853
|
+
yield emit$1(JSON.parse(input), span, tracker);
|
|
1854
|
+
} catch {
|
|
1855
|
+
throw new RuntimeError("fromjson could not parse JSON", span);
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
];
|
|
1860
|
+
const utf8ByteLength = (input) => {
|
|
1861
|
+
let length = 0;
|
|
1862
|
+
for (const char of input) {
|
|
1863
|
+
const codePoint = char.codePointAt(0);
|
|
1864
|
+
if (codePoint <= 127) length += 1;
|
|
1865
|
+
else if (codePoint <= 2047) length += 2;
|
|
1866
|
+
else if (codePoint <= 65535) length += 3;
|
|
1867
|
+
else length += 4;
|
|
1868
|
+
}
|
|
1869
|
+
return length;
|
|
1870
|
+
};
|
|
1871
|
+
//#endregion
|
|
1872
|
+
//#region src/eval/path_eval.ts
|
|
1873
|
+
/**
|
|
1874
|
+
* Resolves properties paths for assignment or `path()` built-in.
|
|
1875
|
+
* Returns an array of paths (arrays of strings/numbers) for valid locations.
|
|
1876
|
+
*
|
|
1877
|
+
* @param node - The AST node describing trailing field access/indexing.
|
|
1878
|
+
* @param input - The current input value.
|
|
1879
|
+
* @param env - The environment.
|
|
1880
|
+
* @param tracker - Limits tracker.
|
|
1881
|
+
* @param evaluate - Recursive evaluator.
|
|
1882
|
+
*/
|
|
1883
|
+
const evaluatePath = function* (node, input, env, tracker, evaluate) {
|
|
1884
|
+
switch (node.kind) {
|
|
1885
|
+
case "Identity":
|
|
1886
|
+
yield [];
|
|
1887
|
+
return;
|
|
1888
|
+
case "FieldAccess":
|
|
1889
|
+
for (const parent of evaluatePath(node.target, input, env, tracker, evaluate)) yield [...parent, node.field];
|
|
1890
|
+
return;
|
|
1891
|
+
case "IndexAccess": {
|
|
1892
|
+
const parentPaths = Array.from(evaluatePath(node.target, input, env, tracker, evaluate));
|
|
1893
|
+
const output = evaluate(node.index, input, env, tracker);
|
|
1894
|
+
for (const keyVal of output) {
|
|
1895
|
+
let key;
|
|
1896
|
+
if (typeof keyVal === "string") key = keyVal;
|
|
1897
|
+
else if (typeof keyVal === "number" && Number.isInteger(keyVal)) key = keyVal;
|
|
1898
|
+
else throw new RuntimeError("Path index must be string or integer", node.span);
|
|
1899
|
+
for (const parent of parentPaths) yield [...parent, key];
|
|
1900
|
+
}
|
|
1901
|
+
return;
|
|
1902
|
+
}
|
|
1903
|
+
case "Pipe": {
|
|
1904
|
+
const leftPaths = Array.from(evaluatePath(node.left, input, env, tracker, evaluate));
|
|
1905
|
+
for (const p of leftPaths) {
|
|
1906
|
+
const val = getPath(input, p) ?? null;
|
|
1907
|
+
for (const q of evaluatePath(node.right, val, env, tracker, evaluate)) yield [...p, ...q];
|
|
1908
|
+
}
|
|
1909
|
+
return;
|
|
1910
|
+
}
|
|
1911
|
+
case "Comma":
|
|
1912
|
+
yield* evaluatePath(node.left, input, env, tracker, evaluate);
|
|
1913
|
+
yield* evaluatePath(node.right, input, env, tracker, evaluate);
|
|
1914
|
+
return;
|
|
1915
|
+
case "Iterate": {
|
|
1916
|
+
const parentPaths = Array.from(evaluatePath(node.target, input, env, tracker, evaluate));
|
|
1917
|
+
for (const p of parentPaths) {
|
|
1918
|
+
const val = getPath(input, p);
|
|
1919
|
+
if (Array.isArray(val)) for (let i = 0; i < val.length; i++) yield [...p, i];
|
|
1920
|
+
else if (val !== null && typeof val === "object") for (const key of Object.keys(val)) yield [...p, key];
|
|
1921
|
+
else {
|
|
1922
|
+
if (val === null) continue;
|
|
1923
|
+
throw new RuntimeError(`Cannot iterate over ${typeof val}`, node.span);
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
return;
|
|
1927
|
+
}
|
|
1928
|
+
case "Call":
|
|
1929
|
+
if (node.name === "select") {
|
|
1930
|
+
const conds = evaluate(node.args[0], input, env, tracker);
|
|
1931
|
+
let matched = false;
|
|
1932
|
+
for (const c of conds) if (c !== null && c !== false) matched = true;
|
|
1933
|
+
if (matched) yield [];
|
|
1934
|
+
return;
|
|
1935
|
+
}
|
|
1936
|
+
throw new RuntimeError(`Function ${node.name} not supported in path expression`, node.span);
|
|
1937
|
+
case "Slice": {
|
|
1938
|
+
const parentPaths = Array.from(evaluatePath(node.target, input, env, tracker, evaluate));
|
|
1939
|
+
const startRes = node.start ? Array.from(evaluate(node.start, input, env, tracker)) : [null];
|
|
1940
|
+
const endRes = node.end ? Array.from(evaluate(node.end, input, env, tracker)) : [null];
|
|
1941
|
+
for (const startVal of startRes) for (const endVal of endRes) {
|
|
1942
|
+
const sliceSpec = {
|
|
1943
|
+
start: typeof startVal === "number" ? startVal : null,
|
|
1944
|
+
end: typeof endVal === "number" ? endVal : null
|
|
1945
|
+
};
|
|
1946
|
+
for (const p of parentPaths) yield [...p, sliceSpec];
|
|
1947
|
+
}
|
|
1948
|
+
return;
|
|
1949
|
+
}
|
|
1950
|
+
case "Try":
|
|
1951
|
+
try {
|
|
1952
|
+
yield* evaluatePath(node.body, input, env, tracker, evaluate);
|
|
1953
|
+
} catch (e) {
|
|
1954
|
+
if (!(e instanceof RuntimeError)) throw e;
|
|
1955
|
+
}
|
|
1956
|
+
return;
|
|
1957
|
+
case "Var":
|
|
1958
|
+
yield [];
|
|
1959
|
+
return;
|
|
1960
|
+
default: throw new RuntimeError("Invalid path expression", node.span);
|
|
1961
|
+
}
|
|
1962
|
+
};
|
|
1963
|
+
//#endregion
|
|
1964
|
+
//#region src/builtins/paths.ts
|
|
1965
|
+
function* traversePaths(root, currentPath, span, tracker) {
|
|
1966
|
+
tracker.step(span);
|
|
1967
|
+
if (Array.isArray(root)) for (let i = 0; i < root.length; i++) {
|
|
1968
|
+
const path = [...currentPath, i];
|
|
1969
|
+
yield emit$1(path, span, tracker);
|
|
1970
|
+
yield* traversePaths(root[i], path, span, tracker);
|
|
1971
|
+
}
|
|
1972
|
+
else if (isPlainObject$1(root)) {
|
|
1973
|
+
const keys = Object.keys(root).sort();
|
|
1974
|
+
for (const key of keys) {
|
|
1975
|
+
const path = [...currentPath, key];
|
|
1976
|
+
yield emit$1(path, span, tracker);
|
|
1977
|
+
yield* traversePaths(root[key], path, span, tracker);
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
const isPath = (val) => {
|
|
1982
|
+
if (!Array.isArray(val)) return false;
|
|
1983
|
+
return val.every((p) => typeof p === "string" || typeof p === "number" && Number.isInteger(p) || isPlainObject$1(p) && ("start" in p || "end" in p));
|
|
1984
|
+
};
|
|
1985
|
+
const ensurePath = (val, span) => {
|
|
1986
|
+
if (isPath(val)) return val;
|
|
1987
|
+
throw new RuntimeError("Path must be an array of strings, integers, or slice objects", span);
|
|
1988
|
+
};
|
|
1989
|
+
const getPath = (root, path) => {
|
|
1990
|
+
let curr = root;
|
|
1991
|
+
for (const part of path) {
|
|
1992
|
+
if (curr === null) return void 0;
|
|
1993
|
+
if (typeof part === "string" && isPlainObject$1(curr)) {
|
|
1994
|
+
if (!Object.prototype.hasOwnProperty.call(curr, part)) return void 0;
|
|
1995
|
+
curr = curr[part];
|
|
1996
|
+
} else if (typeof part === "number" && Array.isArray(curr)) {
|
|
1997
|
+
if (part < 0 || part >= curr.length) return void 0;
|
|
1998
|
+
curr = curr[part];
|
|
1999
|
+
} else return;
|
|
2000
|
+
}
|
|
2001
|
+
return curr;
|
|
2002
|
+
};
|
|
2003
|
+
const updatePath = (root, path, updateFn, span, depth = 0) => {
|
|
2004
|
+
if (path.length === 0) return updateFn(root);
|
|
2005
|
+
const [head, ...tail] = path;
|
|
2006
|
+
if (typeof head === "string") {
|
|
2007
|
+
let obj = {};
|
|
2008
|
+
if (isPlainObject$1(root)) obj = { ...root };
|
|
2009
|
+
else if (root === null) obj = {};
|
|
2010
|
+
else throw new RuntimeError(`Cannot index ${describeType(root)} with string "${head}"`, span);
|
|
2011
|
+
const newVal = updatePath((Object.prototype.hasOwnProperty.call(obj, head) ? obj[head] : void 0) ?? null, tail, updateFn, span, depth + 1);
|
|
2012
|
+
if (newVal === void 0) delete obj[head];
|
|
2013
|
+
else obj[head] = newVal;
|
|
2014
|
+
return obj;
|
|
2015
|
+
}
|
|
2016
|
+
if (typeof head === "number") {
|
|
2017
|
+
let arr = [];
|
|
2018
|
+
if (Array.isArray(root)) arr = [...root];
|
|
2019
|
+
else if (root === null) arr = [];
|
|
2020
|
+
else throw new RuntimeError(`Cannot index ${describeType(root)} with number ${head}`, span);
|
|
2021
|
+
const idx = head < 0 ? arr.length + head : head;
|
|
2022
|
+
if (idx < 0) throw new RuntimeError("Invalid negative index", span);
|
|
2023
|
+
const newVal = updatePath((idx < arr.length ? arr[idx] : null) ?? null, tail, updateFn, span, depth + 1);
|
|
2024
|
+
if (newVal === void 0) if (idx >= arr.length) {
|
|
2025
|
+
while (arr.length < idx) arr.push(null);
|
|
2026
|
+
arr.push(newVal);
|
|
2027
|
+
} else arr[idx] = newVal;
|
|
2028
|
+
else if (idx >= arr.length) {
|
|
2029
|
+
while (arr.length < idx) arr.push(null);
|
|
2030
|
+
arr.push(newVal);
|
|
2031
|
+
} else arr[idx] = newVal;
|
|
2032
|
+
return arr;
|
|
2033
|
+
}
|
|
2034
|
+
if (typeof head === "object" && head !== null) {
|
|
2035
|
+
if (!Array.isArray(root)) throw new RuntimeError(`Cannot slice ${describeType(root)}`, span);
|
|
2036
|
+
const arr = [...root];
|
|
2037
|
+
const start = head.start ?? 0;
|
|
2038
|
+
const end = head.end ?? arr.length;
|
|
2039
|
+
const newSliceVal = updateFn(arr.slice(start, end));
|
|
2040
|
+
if (!Array.isArray(newSliceVal)) throw new RuntimeError("Assignment to a slice must be an array", span);
|
|
2041
|
+
arr.splice(start, end - start, ...newSliceVal);
|
|
2042
|
+
return arr;
|
|
2043
|
+
}
|
|
2044
|
+
throw new RuntimeError(`Path segment must be string, integer, or slice object`, span);
|
|
2045
|
+
};
|
|
2046
|
+
const deletePaths = (root, paths, span) => {
|
|
2047
|
+
if (paths.some((p) => p.length === 0)) return null;
|
|
2048
|
+
if (isPlainObject$1(root)) {
|
|
2049
|
+
const result = { ...root };
|
|
2050
|
+
const relevantPaths = paths.filter((p) => p.length > 0 && typeof p[0] === "string");
|
|
2051
|
+
const byKey = {};
|
|
2052
|
+
for (const p of relevantPaths) {
|
|
2053
|
+
const key = p[0];
|
|
2054
|
+
if (!byKey[key]) byKey[key] = [];
|
|
2055
|
+
byKey[key].push(p.slice(1));
|
|
2056
|
+
}
|
|
2057
|
+
for (const key of Object.keys(byKey)) {
|
|
2058
|
+
const tails = byKey[key];
|
|
2059
|
+
if (tails.some((t) => t.length === 0)) delete result[key];
|
|
2060
|
+
else result[key] = deletePaths(root[key], tails, span);
|
|
2061
|
+
}
|
|
2062
|
+
return result;
|
|
2063
|
+
}
|
|
2064
|
+
if (Array.isArray(root)) {
|
|
2065
|
+
const relevantPaths = paths.filter((p) => p.length > 0 && typeof p[0] === "number");
|
|
2066
|
+
const actions = {};
|
|
2067
|
+
for (const p of relevantPaths) {
|
|
2068
|
+
let idx = p[0];
|
|
2069
|
+
if (idx < 0) idx = root.length + idx;
|
|
2070
|
+
if (idx < 0 || idx >= root.length) continue;
|
|
2071
|
+
if (!actions[idx]) actions[idx] = [];
|
|
2072
|
+
actions[idx].push(p.slice(1));
|
|
2073
|
+
}
|
|
2074
|
+
const finalArr = [];
|
|
2075
|
+
for (let i = 0; i < root.length; i++) {
|
|
2076
|
+
const tails = actions[i];
|
|
2077
|
+
if (tails) if (tails.some((t) => t.length === 0)) continue;
|
|
2078
|
+
else finalArr.push(deletePaths(root[i], tails, span));
|
|
2079
|
+
else finalArr.push(root[i]);
|
|
2080
|
+
}
|
|
2081
|
+
return finalArr;
|
|
2082
|
+
}
|
|
2083
|
+
return root;
|
|
2084
|
+
};
|
|
2085
|
+
const pathBuiltins = [
|
|
2086
|
+
{
|
|
2087
|
+
name: "paths",
|
|
2088
|
+
arity: 0,
|
|
2089
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
2090
|
+
yield* traversePaths(input, [], span, tracker);
|
|
2091
|
+
}
|
|
2092
|
+
},
|
|
2093
|
+
{
|
|
2094
|
+
name: "paths",
|
|
2095
|
+
arity: 1,
|
|
2096
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2097
|
+
for (const pathVal of traversePaths(input, [], span, tracker)) {
|
|
2098
|
+
const path = ensurePath(pathVal, span);
|
|
2099
|
+
const value = getPath(input, path) ?? null;
|
|
2100
|
+
let matched = false;
|
|
2101
|
+
for (const result of evaluate(args[0], value, env, tracker)) if (result !== null && result !== false) {
|
|
2102
|
+
matched = true;
|
|
2103
|
+
break;
|
|
2104
|
+
}
|
|
2105
|
+
if (matched) yield emit$1(path, span, tracker);
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
},
|
|
2109
|
+
{
|
|
2110
|
+
name: "getpath",
|
|
2111
|
+
arity: 1,
|
|
2112
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2113
|
+
for (const pathVal of evaluate(args[0], input, env, tracker)) yield emit$1(getPath(input, ensurePath(pathVal, span)) ?? null, span, tracker);
|
|
2114
|
+
}
|
|
2115
|
+
},
|
|
2116
|
+
{
|
|
2117
|
+
name: "setpath",
|
|
2118
|
+
arity: 2,
|
|
2119
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2120
|
+
const paths = Array.from(evaluate(args[0], input, env, tracker));
|
|
2121
|
+
const values = Array.from(evaluate(args[1], input, env, tracker));
|
|
2122
|
+
for (const pathVal of paths) {
|
|
2123
|
+
const path = ensurePath(pathVal, span);
|
|
2124
|
+
for (const val of values) yield emit$1(updatePath(input, path, () => val, span) ?? null, span, tracker);
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
},
|
|
2128
|
+
{
|
|
2129
|
+
name: "delpaths",
|
|
2130
|
+
arity: 1,
|
|
2131
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2132
|
+
for (const pathsVal of evaluate(args[0], input, env, tracker)) {
|
|
2133
|
+
if (!Array.isArray(pathsVal)) throw new RuntimeError("delpaths expects an array of paths", span);
|
|
2134
|
+
yield emit$1(deletePaths(input, pathsVal.map((p) => ensurePath(p, span)), span), span, tracker);
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
},
|
|
2138
|
+
{
|
|
2139
|
+
name: "path",
|
|
2140
|
+
arity: 1,
|
|
2141
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2142
|
+
for (const p of evaluatePath(args[0], input, env, tracker, evaluate)) yield emit$1(p, span, tracker);
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
];
|
|
2146
|
+
//#endregion
|
|
2147
|
+
//#region src/builtins/collections.ts
|
|
2148
|
+
function sortStable(arr, compare) {
|
|
2149
|
+
return arr.map((item, index) => ({
|
|
2150
|
+
item,
|
|
2151
|
+
index
|
|
2152
|
+
})).sort((a, b) => {
|
|
2153
|
+
const cmp = compare(a.item, b.item);
|
|
2154
|
+
return cmp !== 0 ? cmp : a.index - b.index;
|
|
2155
|
+
}).map((p) => p.item);
|
|
2156
|
+
}
|
|
2157
|
+
const collectionBuiltins = [
|
|
2158
|
+
{
|
|
2159
|
+
name: "keys",
|
|
2160
|
+
arity: 0,
|
|
2161
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
2162
|
+
if (Array.isArray(input)) yield emit$1(Array.from({ length: input.length }, (_, i) => i), span, tracker);
|
|
2163
|
+
else if (input !== null && typeof input === "object") yield emit$1(Object.keys(input).sort(), span, tracker);
|
|
2164
|
+
else throw new RuntimeError(`keys expects an array or object`, span);
|
|
2165
|
+
}
|
|
2166
|
+
},
|
|
2167
|
+
{
|
|
2168
|
+
name: "has",
|
|
2169
|
+
arity: 1,
|
|
2170
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2171
|
+
const keyFilter = args[0];
|
|
2172
|
+
for (const key of evaluate(keyFilter, input, env, tracker)) if (Array.isArray(input)) {
|
|
2173
|
+
const idx = ensureIndex(key);
|
|
2174
|
+
yield emit$1(idx !== void 0 && idx >= 0 && idx < input.length, span, tracker);
|
|
2175
|
+
} else if (input !== null && typeof input === "object") {
|
|
2176
|
+
let keyStr;
|
|
2177
|
+
if (typeof key === "string") keyStr = key;
|
|
2178
|
+
else if (typeof key === "number") keyStr = key.toString();
|
|
2179
|
+
else throw new RuntimeError(`has() key must be string or number for object input`, span);
|
|
2180
|
+
yield emit$1(Object.prototype.hasOwnProperty.call(input, keyStr), span, tracker);
|
|
2181
|
+
} else throw new RuntimeError(`has() expects an array or object input`, span);
|
|
2182
|
+
}
|
|
2183
|
+
},
|
|
2184
|
+
{
|
|
2185
|
+
name: "map",
|
|
2186
|
+
arity: 1,
|
|
2187
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2188
|
+
if (!Array.isArray(input)) throw new RuntimeError("map expects an array", span);
|
|
2189
|
+
const result = [];
|
|
2190
|
+
const filter = args[0];
|
|
2191
|
+
for (const item of input) {
|
|
2192
|
+
tracker.step(span);
|
|
2193
|
+
for (const output of evaluate(filter, item, env, tracker)) result.push(output);
|
|
2194
|
+
}
|
|
2195
|
+
yield emit$1(result, span, tracker);
|
|
2196
|
+
}
|
|
2197
|
+
},
|
|
2198
|
+
{
|
|
2199
|
+
name: "map_values",
|
|
2200
|
+
arity: 1,
|
|
2201
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2202
|
+
const filter = args[0];
|
|
2203
|
+
if (Array.isArray(input)) {
|
|
2204
|
+
const result = [];
|
|
2205
|
+
for (const item of input) {
|
|
2206
|
+
tracker.step(span);
|
|
2207
|
+
for (const output of evaluate(filter, item, env, tracker)) result.push(output);
|
|
2208
|
+
}
|
|
2209
|
+
yield emit$1(result, span, tracker);
|
|
2210
|
+
return;
|
|
2211
|
+
}
|
|
2212
|
+
if (input !== null && typeof input === "object") {
|
|
2213
|
+
const result = {};
|
|
2214
|
+
for (const key of Object.keys(input)) {
|
|
2215
|
+
tracker.step(span);
|
|
2216
|
+
let last;
|
|
2217
|
+
let found = false;
|
|
2218
|
+
for (const output of evaluate(filter, input[key], env, tracker)) {
|
|
2219
|
+
last = output;
|
|
2220
|
+
found = true;
|
|
2221
|
+
}
|
|
2222
|
+
if (found) result[key] = last;
|
|
2223
|
+
}
|
|
2224
|
+
yield emit$1(result, span, tracker);
|
|
2225
|
+
return;
|
|
2226
|
+
}
|
|
2227
|
+
throw new RuntimeError("map_values expects an array or object", span);
|
|
2228
|
+
}
|
|
2229
|
+
},
|
|
2230
|
+
{
|
|
2231
|
+
name: "select",
|
|
2232
|
+
arity: 1,
|
|
2233
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2234
|
+
const filter = args[0];
|
|
2235
|
+
for (const res of evaluate(filter, input, env, tracker)) if (isTruthy(res)) {
|
|
2236
|
+
yield emit$1(input, span, tracker);
|
|
2237
|
+
return;
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
},
|
|
2241
|
+
{
|
|
2242
|
+
name: "sort",
|
|
2243
|
+
arity: 0,
|
|
2244
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
2245
|
+
if (!Array.isArray(input)) throw new RuntimeError("sort expects an array", span);
|
|
2246
|
+
const sorted = sortStable(input, (a, b) => compareValues(a, b));
|
|
2247
|
+
tracker.step(span);
|
|
2248
|
+
yield emit$1(sorted, span, tracker);
|
|
2249
|
+
}
|
|
2250
|
+
},
|
|
2251
|
+
{
|
|
2252
|
+
name: "sort_by",
|
|
2253
|
+
arity: 1,
|
|
2254
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2255
|
+
if (!Array.isArray(input)) throw new RuntimeError("sort_by expects an array", span);
|
|
2256
|
+
const filter = args[0];
|
|
2257
|
+
const pairs = [];
|
|
2258
|
+
for (const item of input) {
|
|
2259
|
+
tracker.step(span);
|
|
2260
|
+
const keys = Array.from(evaluate(filter, item, env, tracker));
|
|
2261
|
+
if (keys.length !== 1) throw new RuntimeError("sort_by key expression must return exactly one value", span);
|
|
2262
|
+
pairs.push({
|
|
2263
|
+
val: item,
|
|
2264
|
+
key: keys[0]
|
|
2265
|
+
});
|
|
2266
|
+
}
|
|
2267
|
+
yield emit$1(sortStable(pairs, (a, b) => compareValues(a.key, b.key)).map((p) => p.val), span, tracker);
|
|
2268
|
+
}
|
|
2269
|
+
},
|
|
2270
|
+
{
|
|
2271
|
+
name: "unique",
|
|
2272
|
+
arity: 0,
|
|
2273
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
2274
|
+
if (!Array.isArray(input)) throw new RuntimeError("unique expects an array", span);
|
|
2275
|
+
const seen = [];
|
|
2276
|
+
const result = [];
|
|
2277
|
+
for (const item of input) {
|
|
2278
|
+
tracker.step(span);
|
|
2279
|
+
if (!seen.some((s) => valueEquals(s, item))) {
|
|
2280
|
+
seen.push(item);
|
|
2281
|
+
result.push(item);
|
|
2282
|
+
}
|
|
2283
|
+
}
|
|
2284
|
+
yield emit$1(result, span, tracker);
|
|
2285
|
+
}
|
|
2286
|
+
},
|
|
2287
|
+
{
|
|
2288
|
+
name: "unique_by",
|
|
2289
|
+
arity: 1,
|
|
2290
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2291
|
+
if (!Array.isArray(input)) throw new RuntimeError("unique_by expects an array", span);
|
|
2292
|
+
const filter = args[0];
|
|
2293
|
+
const seenKeys = [];
|
|
2294
|
+
const result = [];
|
|
2295
|
+
for (const item of input) {
|
|
2296
|
+
tracker.step(span);
|
|
2297
|
+
const keys = Array.from(evaluate(filter, item, env, tracker));
|
|
2298
|
+
if (keys.length !== 1) throw new RuntimeError("unique_by key expression must return exactly one value", span);
|
|
2299
|
+
const key = keys[0];
|
|
2300
|
+
if (!seenKeys.some((s) => valueEquals(s, key))) {
|
|
2301
|
+
seenKeys.push(key);
|
|
2302
|
+
result.push(item);
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
yield emit$1(result, span, tracker);
|
|
2306
|
+
}
|
|
2307
|
+
},
|
|
2308
|
+
{
|
|
2309
|
+
name: "to_entries",
|
|
2310
|
+
arity: 0,
|
|
2311
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
2312
|
+
if (Array.isArray(input)) yield emit$1(input.map((v, i) => ({
|
|
2313
|
+
key: i,
|
|
2314
|
+
value: v
|
|
2315
|
+
})), span, tracker);
|
|
2316
|
+
else if (input !== null && typeof input === "object") yield emit$1(Object.keys(input).sort().map((k) => ({
|
|
2317
|
+
key: k,
|
|
2318
|
+
value: input[k]
|
|
2319
|
+
})), span, tracker);
|
|
2320
|
+
else throw new RuntimeError("to_entries expects array or object", span);
|
|
2321
|
+
}
|
|
2322
|
+
},
|
|
2323
|
+
{
|
|
2324
|
+
name: "from_entries",
|
|
2325
|
+
arity: 0,
|
|
2326
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
2327
|
+
if (!Array.isArray(input)) throw new RuntimeError("from_entries expects an array", span);
|
|
2328
|
+
const result = {};
|
|
2329
|
+
for (const item of input) {
|
|
2330
|
+
tracker.step(span);
|
|
2331
|
+
if (item === null || typeof item !== "object" || Array.isArray(item)) throw new RuntimeError("from_entries expects array of objects", span);
|
|
2332
|
+
const obj = item;
|
|
2333
|
+
const key = getEntryField(obj, [
|
|
2334
|
+
"key",
|
|
2335
|
+
"Key",
|
|
2336
|
+
"name",
|
|
2337
|
+
"Name"
|
|
2338
|
+
]);
|
|
2339
|
+
const value = getEntryField(obj, ["value", "Value"]);
|
|
2340
|
+
if (key === void 0 || value === void 0) throw new RuntimeError("from_entries items must have key/name and value fields", span);
|
|
1811
2341
|
if (typeof key !== "string") throw new RuntimeError("from_entries object keys must be strings", span);
|
|
1812
|
-
result[key] =
|
|
2342
|
+
result[key] = value;
|
|
1813
2343
|
}
|
|
1814
2344
|
yield emit$1(result, span, tracker);
|
|
1815
2345
|
}
|
|
@@ -1817,7 +2347,7 @@ const collectionBuiltins = [
|
|
|
1817
2347
|
{
|
|
1818
2348
|
name: "with_entries",
|
|
1819
2349
|
arity: 1,
|
|
1820
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2350
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
1821
2351
|
let entries;
|
|
1822
2352
|
if (Array.isArray(input)) entries = input.map((v, i) => ({
|
|
1823
2353
|
key: i,
|
|
@@ -1832,7 +2362,7 @@ const collectionBuiltins = [
|
|
|
1832
2362
|
const filter = args[0];
|
|
1833
2363
|
for (const entry of entries) {
|
|
1834
2364
|
tracker.step(span);
|
|
1835
|
-
for (const outVar of evaluate
|
|
2365
|
+
for (const outVar of evaluate(filter, entry, env, tracker)) transformed.push(outVar);
|
|
1836
2366
|
}
|
|
1837
2367
|
const result = {};
|
|
1838
2368
|
for (const item of transformed) {
|
|
@@ -1849,13 +2379,13 @@ const collectionBuiltins = [
|
|
|
1849
2379
|
{
|
|
1850
2380
|
name: "group_by",
|
|
1851
2381
|
arity: 1,
|
|
1852
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2382
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
1853
2383
|
if (!Array.isArray(input)) throw new RuntimeError("group_by expects an array", span);
|
|
1854
2384
|
const pairs = [];
|
|
1855
2385
|
const filter = args[0];
|
|
1856
2386
|
for (const item of input) {
|
|
1857
2387
|
tracker.step(span);
|
|
1858
|
-
const keys = Array.from(evaluate
|
|
2388
|
+
const keys = Array.from(evaluate(filter, item, env, tracker));
|
|
1859
2389
|
if (keys.length !== 1) throw new RuntimeError("group_by key expression must return exactly one value", span);
|
|
1860
2390
|
pairs.push({
|
|
1861
2391
|
val: item,
|
|
@@ -1889,6 +2419,46 @@ const collectionBuiltins = [
|
|
|
1889
2419
|
yield emit$1([...input].reverse(), span, tracker);
|
|
1890
2420
|
}
|
|
1891
2421
|
},
|
|
2422
|
+
{
|
|
2423
|
+
name: "in",
|
|
2424
|
+
arity: 1,
|
|
2425
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2426
|
+
for (const container of evaluate(args[0], input, env, tracker)) {
|
|
2427
|
+
if (Array.isArray(container)) {
|
|
2428
|
+
if (typeof input !== "number") throw new RuntimeError(`Cannot check whether array has a ${typeof input} key`, span);
|
|
2429
|
+
const idx = ensureIndex(input);
|
|
2430
|
+
yield emit$1(idx !== void 0 && idx >= 0 && idx < container.length, span, tracker);
|
|
2431
|
+
continue;
|
|
2432
|
+
}
|
|
2433
|
+
if (container !== null && typeof container === "object") {
|
|
2434
|
+
if (typeof input !== "string") throw new RuntimeError(`Cannot check whether object has a ${typeof input} key`, span);
|
|
2435
|
+
yield emit$1(Object.prototype.hasOwnProperty.call(container, input), span, tracker);
|
|
2436
|
+
continue;
|
|
2437
|
+
}
|
|
2438
|
+
throw new RuntimeError("in expects an array or object argument", span);
|
|
2439
|
+
}
|
|
2440
|
+
}
|
|
2441
|
+
},
|
|
2442
|
+
{
|
|
2443
|
+
name: "del",
|
|
2444
|
+
arity: 1,
|
|
2445
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2446
|
+
yield emit$1(deletePaths(input, Array.from(evaluatePath(args[0], input, env, tracker, evaluate)), span), span, tracker);
|
|
2447
|
+
}
|
|
2448
|
+
},
|
|
2449
|
+
{
|
|
2450
|
+
name: "pick",
|
|
2451
|
+
arity: 1,
|
|
2452
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2453
|
+
const paths = Array.from(evaluatePath(args[0], input, env, tracker, evaluate));
|
|
2454
|
+
let result = null;
|
|
2455
|
+
for (const path of paths) {
|
|
2456
|
+
const value = getPath(input, path);
|
|
2457
|
+
result = updatePath(result, path, () => value ?? null, span) ?? null;
|
|
2458
|
+
}
|
|
2459
|
+
yield emit$1(result, span, tracker);
|
|
2460
|
+
}
|
|
2461
|
+
},
|
|
1892
2462
|
{
|
|
1893
2463
|
name: "flatten",
|
|
1894
2464
|
arity: 0,
|
|
@@ -1906,9 +2476,9 @@ const collectionBuiltins = [
|
|
|
1906
2476
|
{
|
|
1907
2477
|
name: "flatten",
|
|
1908
2478
|
arity: 1,
|
|
1909
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2479
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
1910
2480
|
if (!Array.isArray(input)) throw new RuntimeError("flatten expects an array", span);
|
|
1911
|
-
const depths = evaluate
|
|
2481
|
+
const depths = evaluate(args[0], input, env, tracker);
|
|
1912
2482
|
for (const depthVal of depths) {
|
|
1913
2483
|
if (typeof depthVal !== "number") throw new RuntimeError("flatten depth must be a number", span);
|
|
1914
2484
|
const flattenDepth = (arr, d) => {
|
|
@@ -1962,9 +2532,9 @@ const collectionBuiltins = [
|
|
|
1962
2532
|
{
|
|
1963
2533
|
name: "bsearch",
|
|
1964
2534
|
arity: 1,
|
|
1965
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2535
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
1966
2536
|
if (!Array.isArray(input)) throw new RuntimeError("bsearch expects an array", span);
|
|
1967
|
-
const targetGen = evaluate
|
|
2537
|
+
const targetGen = evaluate(args[0], input, env, tracker);
|
|
1968
2538
|
for (const target of targetGen) {
|
|
1969
2539
|
let low = 0;
|
|
1970
2540
|
let high = input.length - 1;
|
|
@@ -2013,9 +2583,9 @@ const collectionBuiltins = [
|
|
|
2013
2583
|
{
|
|
2014
2584
|
name: "combinations",
|
|
2015
2585
|
arity: 1,
|
|
2016
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2586
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2017
2587
|
if (!Array.isArray(input)) throw new RuntimeError("combinations expects an array", span);
|
|
2018
|
-
const nGen = evaluate
|
|
2588
|
+
const nGen = evaluate(args[0], input, env, tracker);
|
|
2019
2589
|
for (const nVal of nGen) {
|
|
2020
2590
|
if (typeof nVal !== "number") throw new RuntimeError("combinations(n) expects n to be number", span);
|
|
2021
2591
|
if (nVal === 0) {
|
|
@@ -2043,278 +2613,23 @@ const collectionBuiltins = [
|
|
|
2043
2613
|
{
|
|
2044
2614
|
name: "inside",
|
|
2045
2615
|
arity: 1,
|
|
2046
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2047
|
-
const bGen = evaluate
|
|
2616
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2617
|
+
const bGen = evaluate(args[0], input, env, tracker);
|
|
2048
2618
|
for (const b of bGen) yield emit$1(checkContains(b, input), span, tracker);
|
|
2049
2619
|
}
|
|
2050
2620
|
}
|
|
2051
2621
|
];
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
/**
|
|
2056
|
-
* Resolves properties paths for assignment or `path()` built-in.
|
|
2057
|
-
* Returns an array of paths (arrays of strings/numbers) for valid locations.
|
|
2058
|
-
*
|
|
2059
|
-
* @param node - The AST node describing trailing field access/indexing.
|
|
2060
|
-
* @param input - The current input value.
|
|
2061
|
-
* @param env - The environment.
|
|
2062
|
-
* @param tracker - Limits tracker.
|
|
2063
|
-
* @param evaluate - Recursive evaluator.
|
|
2064
|
-
*/
|
|
2065
|
-
const evaluatePath = function* (node, input, env, tracker, evaluate$1) {
|
|
2066
|
-
switch (node.kind) {
|
|
2067
|
-
case "Identity":
|
|
2068
|
-
yield [];
|
|
2069
|
-
return;
|
|
2070
|
-
case "FieldAccess":
|
|
2071
|
-
for (const parent of evaluatePath(node.target, input, env, tracker, evaluate$1)) yield [...parent, node.field];
|
|
2072
|
-
return;
|
|
2073
|
-
case "IndexAccess": {
|
|
2074
|
-
const parentPaths = Array.from(evaluatePath(node.target, input, env, tracker, evaluate$1));
|
|
2075
|
-
const output = evaluate$1(node.index, input, env, tracker);
|
|
2076
|
-
for (const keyVal of output) {
|
|
2077
|
-
let key;
|
|
2078
|
-
if (typeof keyVal === "string") key = keyVal;
|
|
2079
|
-
else if (typeof keyVal === "number" && Number.isInteger(keyVal)) key = keyVal;
|
|
2080
|
-
else throw new RuntimeError("Path index must be string or integer", node.span);
|
|
2081
|
-
for (const parent of parentPaths) yield [...parent, key];
|
|
2082
|
-
}
|
|
2083
|
-
return;
|
|
2084
|
-
}
|
|
2085
|
-
case "Pipe": {
|
|
2086
|
-
const leftPaths = Array.from(evaluatePath(node.left, input, env, tracker, evaluate$1));
|
|
2087
|
-
for (const p of leftPaths) {
|
|
2088
|
-
const val = getPath(input, p) ?? null;
|
|
2089
|
-
for (const q of evaluatePath(node.right, val, env, tracker, evaluate$1)) yield [...p, ...q];
|
|
2090
|
-
}
|
|
2091
|
-
return;
|
|
2092
|
-
}
|
|
2093
|
-
case "Comma":
|
|
2094
|
-
yield* evaluatePath(node.left, input, env, tracker, evaluate$1);
|
|
2095
|
-
yield* evaluatePath(node.right, input, env, tracker, evaluate$1);
|
|
2096
|
-
return;
|
|
2097
|
-
case "Iterate": {
|
|
2098
|
-
const parentPaths = Array.from(evaluatePath(node.target, input, env, tracker, evaluate$1));
|
|
2099
|
-
for (const p of parentPaths) {
|
|
2100
|
-
const val = getPath(input, p);
|
|
2101
|
-
if (Array.isArray(val)) for (let i = 0; i < val.length; i++) yield [...p, i];
|
|
2102
|
-
else if (val !== null && typeof val === "object") for (const key of Object.keys(val)) yield [...p, key];
|
|
2103
|
-
else {
|
|
2104
|
-
if (val === null) continue;
|
|
2105
|
-
throw new RuntimeError(`Cannot iterate over ${typeof val}`, node.span);
|
|
2106
|
-
}
|
|
2107
|
-
}
|
|
2108
|
-
return;
|
|
2109
|
-
}
|
|
2110
|
-
case "Call":
|
|
2111
|
-
if (node.name === "select") {
|
|
2112
|
-
const conds = evaluate$1(node.args[0], input, env, tracker);
|
|
2113
|
-
let matched = false;
|
|
2114
|
-
for (const c of conds) if (c !== null && c !== false) matched = true;
|
|
2115
|
-
if (matched) yield [];
|
|
2116
|
-
return;
|
|
2117
|
-
}
|
|
2118
|
-
throw new RuntimeError(`Function ${node.name} not supported in path expression`, node.span);
|
|
2119
|
-
case "Slice": {
|
|
2120
|
-
const parentPaths = Array.from(evaluatePath(node.target, input, env, tracker, evaluate$1));
|
|
2121
|
-
const startRes = node.start ? Array.from(evaluate$1(node.start, input, env, tracker)) : [null];
|
|
2122
|
-
const endRes = node.end ? Array.from(evaluate$1(node.end, input, env, tracker)) : [null];
|
|
2123
|
-
for (const startVal of startRes) for (const endVal of endRes) {
|
|
2124
|
-
const sliceSpec = {
|
|
2125
|
-
start: typeof startVal === "number" ? startVal : null,
|
|
2126
|
-
end: typeof endVal === "number" ? endVal : null
|
|
2127
|
-
};
|
|
2128
|
-
for (const p of parentPaths) yield [...p, sliceSpec];
|
|
2129
|
-
}
|
|
2130
|
-
return;
|
|
2131
|
-
}
|
|
2132
|
-
case "Try":
|
|
2133
|
-
try {
|
|
2134
|
-
yield* evaluatePath(node.body, input, env, tracker, evaluate$1);
|
|
2135
|
-
} catch (e) {
|
|
2136
|
-
if (!(e instanceof RuntimeError)) throw e;
|
|
2137
|
-
}
|
|
2138
|
-
return;
|
|
2139
|
-
case "Var":
|
|
2140
|
-
yield [];
|
|
2141
|
-
return;
|
|
2142
|
-
default: throw new RuntimeError("Invalid path expression", node.span);
|
|
2143
|
-
}
|
|
2144
|
-
};
|
|
2145
|
-
|
|
2146
|
-
//#endregion
|
|
2147
|
-
//#region src/builtins/paths.ts
|
|
2148
|
-
function* traversePaths(root, currentPath, span, tracker) {
|
|
2149
|
-
tracker.step(span);
|
|
2150
|
-
if (root === null || typeof root !== "object" || Array.isArray(root) && root.length === 0 || isPlainObject(root) && Object.keys(root).length === 0) {
|
|
2151
|
-
yield emit$1([...currentPath], span, tracker);
|
|
2152
|
-
return;
|
|
2153
|
-
}
|
|
2154
|
-
if (Array.isArray(root)) for (let i = 0; i < root.length; i++) yield* traversePaths(root[i], [...currentPath, i], span, tracker);
|
|
2155
|
-
else if (isPlainObject(root)) {
|
|
2156
|
-
const keys = Object.keys(root).sort();
|
|
2157
|
-
for (const key of keys) yield* traversePaths(root[key], [...currentPath, key], span, tracker);
|
|
2158
|
-
}
|
|
2159
|
-
}
|
|
2160
|
-
const isPath = (val) => {
|
|
2161
|
-
if (!Array.isArray(val)) return false;
|
|
2162
|
-
return val.every((p) => typeof p === "string" || typeof p === "number" && Number.isInteger(p) || isPlainObject(p) && ("start" in p || "end" in p));
|
|
2163
|
-
};
|
|
2164
|
-
const ensurePath = (val, span) => {
|
|
2165
|
-
if (isPath(val)) return val;
|
|
2166
|
-
throw new RuntimeError("Path must be an array of strings, integers, or slice objects", span);
|
|
2167
|
-
};
|
|
2168
|
-
const getPath = (root, path) => {
|
|
2169
|
-
let curr = root;
|
|
2170
|
-
for (const part of path) {
|
|
2171
|
-
if (curr === null) return void 0;
|
|
2172
|
-
if (typeof part === "string" && isPlainObject(curr)) {
|
|
2173
|
-
if (!Object.prototype.hasOwnProperty.call(curr, part)) return void 0;
|
|
2174
|
-
curr = curr[part];
|
|
2175
|
-
} else if (typeof part === "number" && Array.isArray(curr)) {
|
|
2176
|
-
if (part < 0 || part >= curr.length) return void 0;
|
|
2177
|
-
curr = curr[part];
|
|
2178
|
-
} else return;
|
|
2179
|
-
}
|
|
2180
|
-
return curr;
|
|
2181
|
-
};
|
|
2182
|
-
const updatePath = (root, path, updateFn, span, depth = 0) => {
|
|
2183
|
-
if (path.length === 0) return updateFn(root);
|
|
2184
|
-
const [head, ...tail] = path;
|
|
2185
|
-
if (typeof head === "string") {
|
|
2186
|
-
let obj = {};
|
|
2187
|
-
if (isPlainObject(root)) obj = { ...root };
|
|
2188
|
-
else if (root === null) obj = {};
|
|
2189
|
-
else throw new RuntimeError(`Cannot index ${describeType(root)} with string "${head}"`, span);
|
|
2190
|
-
const newVal = updatePath((Object.prototype.hasOwnProperty.call(obj, head) ? obj[head] : void 0) ?? null, tail, updateFn, span, depth + 1);
|
|
2191
|
-
if (newVal === void 0) delete obj[head];
|
|
2192
|
-
else obj[head] = newVal;
|
|
2193
|
-
return obj;
|
|
2194
|
-
}
|
|
2195
|
-
if (typeof head === "number") {
|
|
2196
|
-
let arr = [];
|
|
2197
|
-
if (Array.isArray(root)) arr = [...root];
|
|
2198
|
-
else if (root === null) arr = [];
|
|
2199
|
-
else throw new RuntimeError(`Cannot index ${describeType(root)} with number ${head}`, span);
|
|
2200
|
-
const idx = head < 0 ? arr.length + head : head;
|
|
2201
|
-
if (idx < 0) throw new RuntimeError("Invalid negative index", span);
|
|
2202
|
-
const newVal = updatePath((idx < arr.length ? arr[idx] : null) ?? null, tail, updateFn, span, depth + 1);
|
|
2203
|
-
if (newVal === void 0) if (idx >= arr.length) {
|
|
2204
|
-
while (arr.length < idx) arr.push(null);
|
|
2205
|
-
arr.push(newVal);
|
|
2206
|
-
} else arr[idx] = newVal;
|
|
2207
|
-
else if (idx >= arr.length) {
|
|
2208
|
-
while (arr.length < idx) arr.push(null);
|
|
2209
|
-
arr.push(newVal);
|
|
2210
|
-
} else arr[idx] = newVal;
|
|
2211
|
-
return arr;
|
|
2212
|
-
}
|
|
2213
|
-
if (typeof head === "object" && head !== null) {
|
|
2214
|
-
if (!Array.isArray(root)) throw new RuntimeError(`Cannot slice ${describeType(root)}`, span);
|
|
2215
|
-
const arr = [...root];
|
|
2216
|
-
const start = head.start ?? 0;
|
|
2217
|
-
const end = head.end ?? arr.length;
|
|
2218
|
-
const newSliceVal = updateFn(arr.slice(start, end));
|
|
2219
|
-
if (!Array.isArray(newSliceVal)) throw new RuntimeError("Assignment to a slice must be an array", span);
|
|
2220
|
-
arr.splice(start, end - start, ...newSliceVal);
|
|
2221
|
-
return arr;
|
|
2222
|
-
}
|
|
2223
|
-
throw new RuntimeError(`Path segment must be string, integer, or slice object`, span);
|
|
2224
|
-
};
|
|
2225
|
-
const deletePaths = (root, paths, span) => {
|
|
2226
|
-
if (paths.some((p) => p.length === 0)) return null;
|
|
2227
|
-
if (isPlainObject(root)) {
|
|
2228
|
-
const result = { ...root };
|
|
2229
|
-
const relevantPaths = paths.filter((p) => p.length > 0 && typeof p[0] === "string");
|
|
2230
|
-
const byKey = {};
|
|
2231
|
-
for (const p of relevantPaths) {
|
|
2232
|
-
const key = p[0];
|
|
2233
|
-
if (!byKey[key]) byKey[key] = [];
|
|
2234
|
-
byKey[key].push(p.slice(1));
|
|
2235
|
-
}
|
|
2236
|
-
for (const key of Object.keys(byKey)) {
|
|
2237
|
-
const tails = byKey[key];
|
|
2238
|
-
if (tails.some((t) => t.length === 0)) delete result[key];
|
|
2239
|
-
else result[key] = deletePaths(root[key], tails, span);
|
|
2240
|
-
}
|
|
2241
|
-
return result;
|
|
2242
|
-
}
|
|
2243
|
-
if (Array.isArray(root)) {
|
|
2244
|
-
const relevantPaths = paths.filter((p) => p.length > 0 && typeof p[0] === "number");
|
|
2245
|
-
const actions = {};
|
|
2246
|
-
for (const p of relevantPaths) {
|
|
2247
|
-
let idx = p[0];
|
|
2248
|
-
if (idx < 0) idx = root.length + idx;
|
|
2249
|
-
if (idx < 0 || idx >= root.length) continue;
|
|
2250
|
-
if (!actions[idx]) actions[idx] = [];
|
|
2251
|
-
actions[idx].push(p.slice(1));
|
|
2252
|
-
}
|
|
2253
|
-
const finalArr = [];
|
|
2254
|
-
for (let i = 0; i < root.length; i++) {
|
|
2255
|
-
const tails = actions[i];
|
|
2256
|
-
if (tails) if (tails.some((t) => t.length === 0)) continue;
|
|
2257
|
-
else finalArr.push(deletePaths(root[i], tails, span));
|
|
2258
|
-
else finalArr.push(root[i]);
|
|
2259
|
-
}
|
|
2260
|
-
return finalArr;
|
|
2261
|
-
}
|
|
2262
|
-
return root;
|
|
2263
|
-
};
|
|
2264
|
-
const pathBuiltins = [
|
|
2265
|
-
{
|
|
2266
|
-
name: "paths",
|
|
2267
|
-
arity: 0,
|
|
2268
|
-
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
2269
|
-
yield* traversePaths(input, [], span, tracker);
|
|
2270
|
-
}
|
|
2271
|
-
},
|
|
2272
|
-
{
|
|
2273
|
-
name: "getpath",
|
|
2274
|
-
arity: 1,
|
|
2275
|
-
apply: function* (input, args, env, tracker, evaluate$1, span) {
|
|
2276
|
-
for (const pathVal of evaluate$1(args[0], input, env, tracker)) yield emit$1(getPath(input, ensurePath(pathVal, span)) ?? null, span, tracker);
|
|
2277
|
-
}
|
|
2278
|
-
},
|
|
2279
|
-
{
|
|
2280
|
-
name: "setpath",
|
|
2281
|
-
arity: 2,
|
|
2282
|
-
apply: function* (input, args, env, tracker, evaluate$1, span) {
|
|
2283
|
-
const paths = Array.from(evaluate$1(args[0], input, env, tracker));
|
|
2284
|
-
const values = Array.from(evaluate$1(args[1], input, env, tracker));
|
|
2285
|
-
for (const pathVal of paths) {
|
|
2286
|
-
const path = ensurePath(pathVal, span);
|
|
2287
|
-
for (const val of values) yield emit$1(updatePath(input, path, () => val, span) ?? null, span, tracker);
|
|
2288
|
-
}
|
|
2289
|
-
}
|
|
2290
|
-
},
|
|
2291
|
-
{
|
|
2292
|
-
name: "delpaths",
|
|
2293
|
-
arity: 1,
|
|
2294
|
-
apply: function* (input, args, env, tracker, evaluate$1, span) {
|
|
2295
|
-
for (const pathsVal of evaluate$1(args[0], input, env, tracker)) {
|
|
2296
|
-
if (!Array.isArray(pathsVal)) throw new RuntimeError("delpaths expects an array of paths", span);
|
|
2297
|
-
yield emit$1(deletePaths(input, pathsVal.map((p) => ensurePath(p, span)), span), span, tracker);
|
|
2298
|
-
}
|
|
2299
|
-
}
|
|
2300
|
-
},
|
|
2301
|
-
{
|
|
2302
|
-
name: "path",
|
|
2303
|
-
arity: 1,
|
|
2304
|
-
apply: function* (input, args, env, tracker, evaluate$1, span) {
|
|
2305
|
-
for (const p of evaluatePath(args[0], input, env, tracker, evaluate$1)) yield emit$1(p, span, tracker);
|
|
2306
|
-
}
|
|
2307
|
-
}
|
|
2308
|
-
];
|
|
2309
|
-
|
|
2622
|
+
const getEntryField = (obj, names) => {
|
|
2623
|
+
for (const name of names) if (Object.prototype.hasOwnProperty.call(obj, name)) return obj[name];
|
|
2624
|
+
};
|
|
2310
2625
|
//#endregion
|
|
2311
2626
|
//#region src/builtins/iterators.ts
|
|
2312
2627
|
const iteratorBuiltins = [
|
|
2313
2628
|
{
|
|
2314
2629
|
name: "range",
|
|
2315
2630
|
arity: 1,
|
|
2316
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2317
|
-
const ends = evaluate
|
|
2631
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2632
|
+
const ends = evaluate(args[0], input, env, tracker);
|
|
2318
2633
|
for (const end of ends) {
|
|
2319
2634
|
if (typeof end !== "number") throw new RuntimeError("range expects numbers", span);
|
|
2320
2635
|
for (let i = 0; i < end; i++) yield emit$1(i, span, tracker);
|
|
@@ -2324,9 +2639,9 @@ const iteratorBuiltins = [
|
|
|
2324
2639
|
{
|
|
2325
2640
|
name: "range",
|
|
2326
2641
|
arity: 2,
|
|
2327
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2328
|
-
const starts = Array.from(evaluate
|
|
2329
|
-
const ends = Array.from(evaluate
|
|
2642
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2643
|
+
const starts = Array.from(evaluate(args[0], input, env, tracker));
|
|
2644
|
+
const ends = Array.from(evaluate(args[1], input, env, tracker));
|
|
2330
2645
|
for (const start of starts) for (const end of ends) {
|
|
2331
2646
|
if (typeof start !== "number" || typeof end !== "number") throw new RuntimeError("range expects numbers", span);
|
|
2332
2647
|
if (start < end) for (let i = start; i < end; i++) yield emit$1(i, span, tracker);
|
|
@@ -2336,10 +2651,10 @@ const iteratorBuiltins = [
|
|
|
2336
2651
|
{
|
|
2337
2652
|
name: "range",
|
|
2338
2653
|
arity: 3,
|
|
2339
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2340
|
-
const starts = Array.from(evaluate
|
|
2341
|
-
const ends = Array.from(evaluate
|
|
2342
|
-
const steps = Array.from(evaluate
|
|
2654
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2655
|
+
const starts = Array.from(evaluate(args[0], input, env, tracker));
|
|
2656
|
+
const ends = Array.from(evaluate(args[1], input, env, tracker));
|
|
2657
|
+
const steps = Array.from(evaluate(args[2], input, env, tracker));
|
|
2343
2658
|
for (const start of starts) for (const end of ends) for (const step of steps) {
|
|
2344
2659
|
if (typeof start !== "number" || typeof end !== "number" || typeof step !== "number") throw new RuntimeError("range expects numbers", span);
|
|
2345
2660
|
if (step === 0) throw new RuntimeError("range step cannot be zero", span);
|
|
@@ -2351,12 +2666,12 @@ const iteratorBuiltins = [
|
|
|
2351
2666
|
{
|
|
2352
2667
|
name: "limit",
|
|
2353
2668
|
arity: 2,
|
|
2354
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2355
|
-
const limits = evaluate
|
|
2669
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2670
|
+
const limits = evaluate(args[0], input, env, tracker);
|
|
2356
2671
|
for (const n of limits) {
|
|
2357
2672
|
if (typeof n !== "number") throw new RuntimeError("limit expects number", span);
|
|
2358
2673
|
let count = 0;
|
|
2359
|
-
if (n > 0) for (const val of evaluate
|
|
2674
|
+
if (n > 0) for (const val of evaluate(args[1], input, env, tracker)) {
|
|
2360
2675
|
yield val;
|
|
2361
2676
|
count++;
|
|
2362
2677
|
if (count >= n) break;
|
|
@@ -2364,38 +2679,78 @@ const iteratorBuiltins = [
|
|
|
2364
2679
|
}
|
|
2365
2680
|
}
|
|
2366
2681
|
},
|
|
2682
|
+
{
|
|
2683
|
+
name: "skip",
|
|
2684
|
+
arity: 2,
|
|
2685
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2686
|
+
for (const n of evaluate(args[0], input, env, tracker)) {
|
|
2687
|
+
if (typeof n !== "number") throw new RuntimeError("skip expects number", span);
|
|
2688
|
+
let count = 0;
|
|
2689
|
+
for (const val of evaluate(args[1], input, env, tracker)) {
|
|
2690
|
+
if (count >= n) yield val;
|
|
2691
|
+
count++;
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
},
|
|
2696
|
+
{
|
|
2697
|
+
name: "first",
|
|
2698
|
+
arity: 0,
|
|
2699
|
+
apply: function* (input) {
|
|
2700
|
+
if (Array.isArray(input) && input.length > 0) yield input[0];
|
|
2701
|
+
}
|
|
2702
|
+
},
|
|
2367
2703
|
{
|
|
2368
2704
|
name: "first",
|
|
2369
2705
|
arity: 1,
|
|
2370
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2371
|
-
for (const val of evaluate
|
|
2706
|
+
apply: function* (input, args, env, tracker, evaluate) {
|
|
2707
|
+
for (const val of evaluate(args[0], input, env, tracker)) {
|
|
2372
2708
|
yield val;
|
|
2373
2709
|
break;
|
|
2374
2710
|
}
|
|
2375
2711
|
}
|
|
2376
2712
|
},
|
|
2713
|
+
{
|
|
2714
|
+
name: "last",
|
|
2715
|
+
arity: 0,
|
|
2716
|
+
apply: function* (input) {
|
|
2717
|
+
if (Array.isArray(input) && input.length > 0) yield input[input.length - 1];
|
|
2718
|
+
}
|
|
2719
|
+
},
|
|
2377
2720
|
{
|
|
2378
2721
|
name: "last",
|
|
2379
2722
|
arity: 1,
|
|
2380
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2723
|
+
apply: function* (input, args, env, tracker, evaluate) {
|
|
2381
2724
|
let lastVal;
|
|
2382
2725
|
let found = false;
|
|
2383
|
-
for (const val of evaluate
|
|
2726
|
+
for (const val of evaluate(args[0], input, env, tracker)) {
|
|
2384
2727
|
lastVal = val;
|
|
2385
2728
|
found = true;
|
|
2386
2729
|
}
|
|
2387
2730
|
if (found) yield lastVal;
|
|
2388
2731
|
}
|
|
2389
2732
|
},
|
|
2733
|
+
{
|
|
2734
|
+
name: "nth",
|
|
2735
|
+
arity: 1,
|
|
2736
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2737
|
+
if (!Array.isArray(input)) throw new RuntimeError("nth expects array input", span);
|
|
2738
|
+
for (const n of evaluate(args[0], input, env, tracker)) {
|
|
2739
|
+
if (typeof n !== "number") throw new RuntimeError("nth expects number", span);
|
|
2740
|
+
const idx = Math.trunc(n);
|
|
2741
|
+
if (idx >= 0 && idx < input.length) yield input[idx];
|
|
2742
|
+
}
|
|
2743
|
+
}
|
|
2744
|
+
},
|
|
2390
2745
|
{
|
|
2391
2746
|
name: "nth",
|
|
2392
2747
|
arity: 2,
|
|
2393
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2394
|
-
const indices = evaluate
|
|
2748
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2749
|
+
const indices = evaluate(args[0], input, env, tracker);
|
|
2395
2750
|
for (const n of indices) {
|
|
2396
2751
|
if (typeof n !== "number") throw new RuntimeError("nth expects number", span);
|
|
2397
2752
|
let count = 0;
|
|
2398
|
-
for (const val of evaluate
|
|
2753
|
+
for (const val of evaluate(args[1], input, env, tracker)) {
|
|
2399
2754
|
if (count === n) {
|
|
2400
2755
|
yield val;
|
|
2401
2756
|
break;
|
|
@@ -2408,67 +2763,161 @@ const iteratorBuiltins = [
|
|
|
2408
2763
|
{
|
|
2409
2764
|
name: "isempty",
|
|
2410
2765
|
arity: 1,
|
|
2411
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2766
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2412
2767
|
let empty = true;
|
|
2413
|
-
for (const _ of evaluate
|
|
2768
|
+
for (const _ of evaluate(args[0], input, env, tracker)) {
|
|
2414
2769
|
empty = false;
|
|
2415
2770
|
break;
|
|
2416
2771
|
}
|
|
2417
2772
|
yield emit$1(empty, span, tracker);
|
|
2418
2773
|
}
|
|
2419
2774
|
},
|
|
2775
|
+
{
|
|
2776
|
+
name: "all",
|
|
2777
|
+
arity: 0,
|
|
2778
|
+
apply: function* (input, _args, _env, tracker, _evaluate, span) {
|
|
2779
|
+
if (!Array.isArray(input)) throw new RuntimeError("all expects an array", span);
|
|
2780
|
+
yield emit$1(input.every(isTruthy), span, tracker);
|
|
2781
|
+
}
|
|
2782
|
+
},
|
|
2420
2783
|
{
|
|
2421
2784
|
name: "all",
|
|
2422
2785
|
arity: 1,
|
|
2423
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2786
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2787
|
+
if (!Array.isArray(input)) throw new RuntimeError("all expects an array", span);
|
|
2424
2788
|
let result = true;
|
|
2425
|
-
for (const
|
|
2426
|
-
|
|
2427
|
-
|
|
2789
|
+
for (const item of input) {
|
|
2790
|
+
let itemResult = false;
|
|
2791
|
+
for (const val of evaluate(args[0], item, env, tracker)) if (isTruthy(val)) {
|
|
2792
|
+
itemResult = true;
|
|
2793
|
+
break;
|
|
2794
|
+
}
|
|
2795
|
+
if (!itemResult) {
|
|
2796
|
+
result = false;
|
|
2797
|
+
break;
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
yield emit$1(result, span, tracker);
|
|
2801
|
+
}
|
|
2802
|
+
},
|
|
2803
|
+
{
|
|
2804
|
+
name: "all",
|
|
2805
|
+
arity: 2,
|
|
2806
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2807
|
+
let result = true;
|
|
2808
|
+
for (const item of evaluate(args[0], input, env, tracker)) {
|
|
2809
|
+
let itemResult = false;
|
|
2810
|
+
for (const condition of evaluate(args[1], item, env, tracker)) if (isTruthy(condition)) {
|
|
2811
|
+
itemResult = true;
|
|
2812
|
+
break;
|
|
2813
|
+
}
|
|
2814
|
+
if (!itemResult) {
|
|
2815
|
+
result = false;
|
|
2816
|
+
break;
|
|
2817
|
+
}
|
|
2428
2818
|
}
|
|
2429
2819
|
yield emit$1(result, span, tracker);
|
|
2430
2820
|
}
|
|
2431
2821
|
},
|
|
2822
|
+
{
|
|
2823
|
+
name: "any",
|
|
2824
|
+
arity: 0,
|
|
2825
|
+
apply: function* (input, _args, _env, tracker, _evaluate, span) {
|
|
2826
|
+
if (!Array.isArray(input)) throw new RuntimeError("any expects an array", span);
|
|
2827
|
+
yield emit$1(input.some(isTruthy), span, tracker);
|
|
2828
|
+
}
|
|
2829
|
+
},
|
|
2432
2830
|
{
|
|
2433
2831
|
name: "any",
|
|
2434
2832
|
arity: 1,
|
|
2435
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2833
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2834
|
+
if (!Array.isArray(input)) throw new RuntimeError("any expects an array", span);
|
|
2436
2835
|
let result = false;
|
|
2437
|
-
for (const
|
|
2438
|
-
|
|
2439
|
-
|
|
2836
|
+
for (const item of input) {
|
|
2837
|
+
for (const val of evaluate(args[0], item, env, tracker)) if (isTruthy(val)) {
|
|
2838
|
+
result = true;
|
|
2839
|
+
break;
|
|
2840
|
+
}
|
|
2841
|
+
if (result) break;
|
|
2842
|
+
}
|
|
2843
|
+
yield emit$1(result, span, tracker);
|
|
2844
|
+
}
|
|
2845
|
+
},
|
|
2846
|
+
{
|
|
2847
|
+
name: "any",
|
|
2848
|
+
arity: 2,
|
|
2849
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2850
|
+
let result = false;
|
|
2851
|
+
for (const item of evaluate(args[0], input, env, tracker)) {
|
|
2852
|
+
for (const condition of evaluate(args[1], item, env, tracker)) if (isTruthy(condition)) {
|
|
2853
|
+
result = true;
|
|
2854
|
+
break;
|
|
2855
|
+
}
|
|
2856
|
+
if (result) break;
|
|
2440
2857
|
}
|
|
2441
2858
|
yield emit$1(result, span, tracker);
|
|
2442
2859
|
}
|
|
2443
2860
|
},
|
|
2861
|
+
{
|
|
2862
|
+
name: "recurse",
|
|
2863
|
+
arity: 0,
|
|
2864
|
+
apply: function* (input, _args, _env, tracker, _evaluate, span) {
|
|
2865
|
+
const rec = function* (curr) {
|
|
2866
|
+
yield emit$1(curr, span, tracker);
|
|
2867
|
+
if (Array.isArray(curr)) for (const item of curr) yield* rec(item);
|
|
2868
|
+
else if (curr !== null && typeof curr === "object") {
|
|
2869
|
+
const keys = Object.keys(curr).sort();
|
|
2870
|
+
for (const key of keys) yield* rec(curr[key]);
|
|
2871
|
+
}
|
|
2872
|
+
};
|
|
2873
|
+
yield* rec(input);
|
|
2874
|
+
}
|
|
2875
|
+
},
|
|
2444
2876
|
{
|
|
2445
2877
|
name: "recurse",
|
|
2446
2878
|
arity: 1,
|
|
2447
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2879
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2448
2880
|
const rec = function* (curr) {
|
|
2449
2881
|
yield emit$1(curr, span, tracker);
|
|
2450
|
-
const nexts = evaluate
|
|
2882
|
+
const nexts = evaluate(args[0], curr, env, tracker);
|
|
2451
2883
|
for (const next of nexts) yield* rec(next);
|
|
2452
2884
|
};
|
|
2453
2885
|
yield* rec(input);
|
|
2454
2886
|
}
|
|
2455
2887
|
},
|
|
2888
|
+
{
|
|
2889
|
+
name: "recurse",
|
|
2890
|
+
arity: 2,
|
|
2891
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2892
|
+
const stepExpr = args[0];
|
|
2893
|
+
const conditionExpr = args[1];
|
|
2894
|
+
const rec = function* (curr) {
|
|
2895
|
+
yield emit$1(curr, span, tracker);
|
|
2896
|
+
for (const condition of evaluate(conditionExpr, curr, env, tracker)) {
|
|
2897
|
+
if (!isTruthy(condition)) return;
|
|
2898
|
+
break;
|
|
2899
|
+
}
|
|
2900
|
+
for (const next of evaluate(stepExpr, curr, env, tracker)) yield* rec(next);
|
|
2901
|
+
};
|
|
2902
|
+
yield* rec(input);
|
|
2903
|
+
}
|
|
2904
|
+
},
|
|
2456
2905
|
{
|
|
2457
2906
|
name: "while",
|
|
2458
2907
|
arity: 2,
|
|
2459
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2908
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2460
2909
|
const condExpr = args[0];
|
|
2461
2910
|
const updateExpr = args[1];
|
|
2462
2911
|
const rec = function* (curr) {
|
|
2463
2912
|
tracker.step(span);
|
|
2464
2913
|
let condMatches = false;
|
|
2465
|
-
for (const c of evaluate
|
|
2914
|
+
for (const c of evaluate(condExpr, curr, env, tracker)) if (isTruthy(c)) {
|
|
2466
2915
|
condMatches = true;
|
|
2467
2916
|
break;
|
|
2468
2917
|
}
|
|
2469
2918
|
if (condMatches) {
|
|
2470
2919
|
yield emit$1(curr, span, tracker);
|
|
2471
|
-
for (const next of evaluate
|
|
2920
|
+
for (const next of evaluate(updateExpr, curr, env, tracker)) yield* rec(next);
|
|
2472
2921
|
}
|
|
2473
2922
|
};
|
|
2474
2923
|
yield* rec(input);
|
|
@@ -2477,18 +2926,18 @@ const iteratorBuiltins = [
|
|
|
2477
2926
|
{
|
|
2478
2927
|
name: "until",
|
|
2479
2928
|
arity: 2,
|
|
2480
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2929
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2481
2930
|
const condExpr = args[0];
|
|
2482
2931
|
const updateExpr = args[1];
|
|
2483
2932
|
const rec = function* (curr) {
|
|
2484
2933
|
tracker.step(span);
|
|
2485
2934
|
let condMatches = false;
|
|
2486
|
-
for (const c of evaluate
|
|
2935
|
+
for (const c of evaluate(condExpr, curr, env, tracker)) if (isTruthy(c)) {
|
|
2487
2936
|
condMatches = true;
|
|
2488
2937
|
break;
|
|
2489
2938
|
}
|
|
2490
2939
|
if (condMatches) yield emit$1(curr, span, tracker);
|
|
2491
|
-
else for (const next of evaluate
|
|
2940
|
+
else for (const next of evaluate(updateExpr, curr, env, tracker)) yield* rec(next);
|
|
2492
2941
|
};
|
|
2493
2942
|
yield* rec(input);
|
|
2494
2943
|
}
|
|
@@ -2496,15 +2945,14 @@ const iteratorBuiltins = [
|
|
|
2496
2945
|
{
|
|
2497
2946
|
name: "repeat",
|
|
2498
2947
|
arity: 1,
|
|
2499
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2948
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2500
2949
|
while (true) {
|
|
2501
2950
|
tracker.step(span);
|
|
2502
|
-
yield* evaluate
|
|
2951
|
+
yield* evaluate(args[0], input, env, tracker);
|
|
2503
2952
|
}
|
|
2504
2953
|
}
|
|
2505
2954
|
}
|
|
2506
2955
|
];
|
|
2507
|
-
|
|
2508
2956
|
//#endregion
|
|
2509
2957
|
//#region src/eval/ops.ts
|
|
2510
2958
|
/**
|
|
@@ -2553,7 +3001,7 @@ function isEqual(a, b) {
|
|
|
2553
3001
|
if (a.length !== b.length) return false;
|
|
2554
3002
|
return a.every((v, i) => isEqual(v, b[i]));
|
|
2555
3003
|
}
|
|
2556
|
-
if (isPlainObject
|
|
3004
|
+
if (isPlainObject(a) && isPlainObject(b)) {
|
|
2557
3005
|
const ka = Object.keys(a).sort();
|
|
2558
3006
|
const kb = Object.keys(b).sort();
|
|
2559
3007
|
if (ka.length !== kb.length) return false;
|
|
@@ -2570,7 +3018,7 @@ function compare(a, b) {
|
|
|
2570
3018
|
if (typeof v === "number") return 2;
|
|
2571
3019
|
if (typeof v === "string") return 3;
|
|
2572
3020
|
if (Array.isArray(v)) return 4;
|
|
2573
|
-
if (isPlainObject
|
|
3021
|
+
if (isPlainObject(v)) return 5;
|
|
2574
3022
|
return 6;
|
|
2575
3023
|
};
|
|
2576
3024
|
const ta = typeOrder(a);
|
|
@@ -2586,7 +3034,7 @@ function compare(a, b) {
|
|
|
2586
3034
|
}
|
|
2587
3035
|
return a.length - b.length;
|
|
2588
3036
|
}
|
|
2589
|
-
if (isPlainObject
|
|
3037
|
+
if (isPlainObject(a) && isPlainObject(b)) {
|
|
2590
3038
|
const keysA = Object.keys(a).sort();
|
|
2591
3039
|
const keysB = Object.keys(b).sort();
|
|
2592
3040
|
for (let i = 0; i < Math.min(keysA.length, keysB.length); i++) {
|
|
@@ -2606,7 +3054,7 @@ function add(left, right, span) {
|
|
|
2606
3054
|
if (typeof left === "number" && typeof right === "number") return left + right;
|
|
2607
3055
|
if (typeof left === "string" && typeof right === "string") return left + right;
|
|
2608
3056
|
if (Array.isArray(left) && Array.isArray(right)) return [...left, ...right];
|
|
2609
|
-
if (isPlainObject
|
|
3057
|
+
if (isPlainObject(left) && isPlainObject(right)) return {
|
|
2610
3058
|
...left,
|
|
2611
3059
|
...right
|
|
2612
3060
|
};
|
|
@@ -2624,7 +3072,7 @@ function mul(left, right, span) {
|
|
|
2624
3072
|
if (typeof left === "number" && typeof right === "number") return left * right;
|
|
2625
3073
|
if (typeof left === "string" && typeof right === "number") return repeatString(left, right);
|
|
2626
3074
|
if (typeof left === "number" && typeof right === "string") return repeatString(right, left);
|
|
2627
|
-
if (isPlainObject
|
|
3075
|
+
if (isPlainObject(left) && isPlainObject(right)) return mergeDeep(left, right);
|
|
2628
3076
|
throw new RuntimeError(`Cannot multiply ${describeType(left)} by ${describeType(right)}`, span);
|
|
2629
3077
|
}
|
|
2630
3078
|
function div(left, right, span) {
|
|
@@ -2653,15 +3101,14 @@ function mergeDeep(target, source) {
|
|
|
2653
3101
|
for (const key of Object.keys(source)) {
|
|
2654
3102
|
const sVal = source[key];
|
|
2655
3103
|
const tVal = result[key];
|
|
2656
|
-
if (isPlainObject
|
|
3104
|
+
if (isPlainObject(sVal) && tVal !== void 0 && isPlainObject(tVal)) result[key] = mergeDeep(tVal, sVal);
|
|
2657
3105
|
else result[key] = sVal;
|
|
2658
3106
|
}
|
|
2659
3107
|
return result;
|
|
2660
3108
|
}
|
|
2661
|
-
function isPlainObject
|
|
3109
|
+
function isPlainObject(v) {
|
|
2662
3110
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
2663
3111
|
}
|
|
2664
|
-
|
|
2665
3112
|
//#endregion
|
|
2666
3113
|
//#region src/builtins/math.ts
|
|
2667
3114
|
const mathBuiltins = [
|
|
@@ -2719,12 +3166,22 @@ const mathBuiltins = [
|
|
|
2719
3166
|
{
|
|
2720
3167
|
name: "infinite",
|
|
2721
3168
|
arity: 0,
|
|
3169
|
+
apply: function* (_input, _args, _env, tracker, _eval, span) {
|
|
3170
|
+
yield emit$1(Infinity, span, tracker);
|
|
3171
|
+
}
|
|
3172
|
+
},
|
|
3173
|
+
{
|
|
3174
|
+
name: "nan",
|
|
3175
|
+
arity: 0,
|
|
3176
|
+
apply: function* (_input, _args, _env, tracker, _eval, span) {
|
|
3177
|
+
yield emit$1(NaN, span, tracker);
|
|
3178
|
+
}
|
|
3179
|
+
},
|
|
3180
|
+
{
|
|
3181
|
+
name: "isinfinite",
|
|
3182
|
+
arity: 0,
|
|
2722
3183
|
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
2723
|
-
|
|
2724
|
-
yield emit$1(false, span, tracker);
|
|
2725
|
-
return;
|
|
2726
|
-
}
|
|
2727
|
-
yield emit$1(!Number.isFinite(input) && !Number.isNaN(input), span, tracker);
|
|
3184
|
+
yield emit$1(typeof input === "number" && !Number.isFinite(input) && !Number.isNaN(input), span, tracker);
|
|
2728
3185
|
}
|
|
2729
3186
|
},
|
|
2730
3187
|
{
|
|
@@ -2753,6 +3210,13 @@ const mathBuiltins = [
|
|
|
2753
3210
|
yield emit$1(true, span, tracker);
|
|
2754
3211
|
}
|
|
2755
3212
|
},
|
|
3213
|
+
{
|
|
3214
|
+
name: "isnormal",
|
|
3215
|
+
arity: 0,
|
|
3216
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
3217
|
+
yield emit$1(typeof input === "number" && Number.isFinite(input) && input !== 0, span, tracker);
|
|
3218
|
+
}
|
|
3219
|
+
},
|
|
2756
3220
|
{
|
|
2757
3221
|
name: "subnormal",
|
|
2758
3222
|
arity: 0,
|
|
@@ -2795,7 +3259,7 @@ const mathBuiltins = [
|
|
|
2795
3259
|
{
|
|
2796
3260
|
name: "min_by",
|
|
2797
3261
|
arity: 1,
|
|
2798
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
3262
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2799
3263
|
if (!Array.isArray(input)) throw new RuntimeError("min_by expects an array", span);
|
|
2800
3264
|
if (input.length === 0) {
|
|
2801
3265
|
yield emit$1(null, span, tracker);
|
|
@@ -2803,12 +3267,12 @@ const mathBuiltins = [
|
|
|
2803
3267
|
}
|
|
2804
3268
|
let minItem = input[0];
|
|
2805
3269
|
let minKey;
|
|
2806
|
-
const keys0 = Array.from(evaluate
|
|
3270
|
+
const keys0 = Array.from(evaluate(args[0], minItem, env, tracker));
|
|
2807
3271
|
if (keys0.length !== 1) throw new RuntimeError("min_by key must return one value", span);
|
|
2808
3272
|
minKey = keys0[0];
|
|
2809
3273
|
for (let i = 1; i < input.length; i++) {
|
|
2810
3274
|
const item = input[i];
|
|
2811
|
-
const keys = Array.from(evaluate
|
|
3275
|
+
const keys = Array.from(evaluate(args[0], item, env, tracker));
|
|
2812
3276
|
if (keys.length !== 1) throw new RuntimeError("min_by key must return one value", span);
|
|
2813
3277
|
const key = keys[0];
|
|
2814
3278
|
if (compareValues(key, minKey) < 0) {
|
|
@@ -2822,7 +3286,7 @@ const mathBuiltins = [
|
|
|
2822
3286
|
{
|
|
2823
3287
|
name: "max_by",
|
|
2824
3288
|
arity: 1,
|
|
2825
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
3289
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2826
3290
|
if (!Array.isArray(input)) throw new RuntimeError("max_by expects an array", span);
|
|
2827
3291
|
if (input.length === 0) {
|
|
2828
3292
|
yield emit$1(null, span, tracker);
|
|
@@ -2830,12 +3294,12 @@ const mathBuiltins = [
|
|
|
2830
3294
|
}
|
|
2831
3295
|
let maxItem = input[0];
|
|
2832
3296
|
let maxKey;
|
|
2833
|
-
const keys0 = Array.from(evaluate
|
|
3297
|
+
const keys0 = Array.from(evaluate(args[0], maxItem, env, tracker));
|
|
2834
3298
|
if (keys0.length !== 1) throw new RuntimeError("max_by key must return one value", span);
|
|
2835
3299
|
maxKey = keys0[0];
|
|
2836
3300
|
for (let i = 1; i < input.length; i++) {
|
|
2837
3301
|
const item = input[i];
|
|
2838
|
-
const keys = Array.from(evaluate
|
|
3302
|
+
const keys = Array.from(evaluate(args[0], item, env, tracker));
|
|
2839
3303
|
if (keys.length !== 1) throw new RuntimeError("max_by key must return one value", span);
|
|
2840
3304
|
const key = keys[0];
|
|
2841
3305
|
if (compareValues(key, maxKey) > 0) {
|
|
@@ -2862,9 +3326,25 @@ const mathBuiltins = [
|
|
|
2862
3326
|
}
|
|
2863
3327
|
yield emit$1(acc, span, tracker);
|
|
2864
3328
|
}
|
|
3329
|
+
},
|
|
3330
|
+
{
|
|
3331
|
+
name: "add",
|
|
3332
|
+
arity: 1,
|
|
3333
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
3334
|
+
const values = Array.from(evaluate(args[0], input, env, tracker));
|
|
3335
|
+
if (values.length === 0) {
|
|
3336
|
+
yield emit$1(null, span, tracker);
|
|
3337
|
+
return;
|
|
3338
|
+
}
|
|
3339
|
+
let acc = values[0];
|
|
3340
|
+
for (let i = 1; i < values.length; i++) {
|
|
3341
|
+
tracker.step(span);
|
|
3342
|
+
acc = add(acc, values[i], span);
|
|
3343
|
+
}
|
|
3344
|
+
yield emit$1(acc, span, tracker);
|
|
3345
|
+
}
|
|
2865
3346
|
}
|
|
2866
3347
|
];
|
|
2867
|
-
|
|
2868
3348
|
//#endregion
|
|
2869
3349
|
//#region src/builtins/index.ts
|
|
2870
3350
|
const registerAllBuiltins = () => {
|
|
@@ -2876,11 +3356,9 @@ const registerAllBuiltins = () => {
|
|
|
2876
3356
|
registerBuiltins(iteratorBuiltins);
|
|
2877
3357
|
registerBuiltins(mathBuiltins);
|
|
2878
3358
|
};
|
|
2879
|
-
|
|
2880
3359
|
//#endregion
|
|
2881
3360
|
//#region src/builtins.ts
|
|
2882
3361
|
registerAllBuiltins();
|
|
2883
|
-
|
|
2884
3362
|
//#endregion
|
|
2885
3363
|
//#region src/validate.ts
|
|
2886
3364
|
/**
|
|
@@ -2891,57 +3369,57 @@ registerAllBuiltins();
|
|
|
2891
3369
|
* @throws {ValidationError} If validation fails.
|
|
2892
3370
|
*/
|
|
2893
3371
|
const validate = (node) => {
|
|
2894
|
-
visit(node, []);
|
|
3372
|
+
visit$1(node, []);
|
|
2895
3373
|
};
|
|
2896
|
-
const visit = (node, scope) => {
|
|
3374
|
+
const visit$1 = (node, scope) => {
|
|
2897
3375
|
switch (node.kind) {
|
|
2898
3376
|
case "Identity":
|
|
2899
3377
|
case "Literal":
|
|
2900
3378
|
case "Var": return;
|
|
2901
3379
|
case "FieldAccess":
|
|
2902
|
-
visit(node.target, scope);
|
|
3380
|
+
visit$1(node.target, scope);
|
|
2903
3381
|
return;
|
|
2904
3382
|
case "IndexAccess":
|
|
2905
|
-
visit(node.target, scope);
|
|
2906
|
-
visit(node.index, scope);
|
|
3383
|
+
visit$1(node.target, scope);
|
|
3384
|
+
visit$1(node.index, scope);
|
|
2907
3385
|
return;
|
|
2908
3386
|
case "Array":
|
|
2909
|
-
node.items.forEach((n) => visit(n, scope));
|
|
3387
|
+
node.items.forEach((n) => visit$1(n, scope));
|
|
2910
3388
|
return;
|
|
2911
3389
|
case "Object":
|
|
2912
3390
|
node.entries.forEach((entry) => {
|
|
2913
|
-
if (entry.key.kind === "KeyExpr") visit(entry.key.expr, scope);
|
|
2914
|
-
visit(entry.value, scope);
|
|
3391
|
+
if (entry.key.kind === "KeyExpr") visit$1(entry.key.expr, scope);
|
|
3392
|
+
visit$1(entry.value, scope);
|
|
2915
3393
|
});
|
|
2916
3394
|
return;
|
|
2917
3395
|
case "Pipe":
|
|
2918
3396
|
case "Comma":
|
|
2919
3397
|
case "Alt":
|
|
2920
|
-
visit(node.left, scope);
|
|
2921
|
-
visit(node.right, scope);
|
|
3398
|
+
visit$1(node.left, scope);
|
|
3399
|
+
visit$1(node.right, scope);
|
|
2922
3400
|
return;
|
|
2923
3401
|
case "Binary":
|
|
2924
3402
|
case "Bool":
|
|
2925
|
-
visit(node.left, scope);
|
|
2926
|
-
visit(node.right, scope);
|
|
3403
|
+
visit$1(node.left, scope);
|
|
3404
|
+
visit$1(node.right, scope);
|
|
2927
3405
|
return;
|
|
2928
3406
|
case "Unary":
|
|
2929
|
-
visit(node.expr, scope);
|
|
3407
|
+
visit$1(node.expr, scope);
|
|
2930
3408
|
return;
|
|
2931
3409
|
case "If":
|
|
2932
3410
|
node.branches.forEach((branch) => {
|
|
2933
|
-
visit(branch.cond, scope);
|
|
2934
|
-
visit(branch.then, scope);
|
|
3411
|
+
visit$1(branch.cond, scope);
|
|
3412
|
+
visit$1(branch.then, scope);
|
|
2935
3413
|
});
|
|
2936
|
-
visit(node.else, scope);
|
|
3414
|
+
visit$1(node.else, scope);
|
|
2937
3415
|
return;
|
|
2938
3416
|
case "As":
|
|
2939
|
-
visit(node.bind, scope);
|
|
2940
|
-
visit(node.body, scope);
|
|
3417
|
+
visit$1(node.bind, scope);
|
|
3418
|
+
visit$1(node.body, scope);
|
|
2941
3419
|
return;
|
|
2942
3420
|
case "Call": {
|
|
2943
3421
|
for (let i = scope.length - 1; i >= 0; i--) if (scope[i].has(node.name)) {
|
|
2944
|
-
for (const arg of node.args) visit(arg, scope);
|
|
3422
|
+
for (const arg of node.args) visit$1(arg, scope);
|
|
2945
3423
|
return;
|
|
2946
3424
|
}
|
|
2947
3425
|
const specs = builtins[node.name];
|
|
@@ -2950,51 +3428,50 @@ const visit = (node, scope) => {
|
|
|
2950
3428
|
const arities = specs.map((s) => s.arity).join(" or ");
|
|
2951
3429
|
throw new ValidationError(`Function ${node.name} expects ${arities} arguments, but got ${node.args.length}`, node.span);
|
|
2952
3430
|
}
|
|
2953
|
-
for (const arg of node.args) visit(arg, scope);
|
|
3431
|
+
for (const arg of node.args) visit$1(arg, scope);
|
|
2954
3432
|
return;
|
|
2955
3433
|
}
|
|
2956
3434
|
case "Assignment":
|
|
2957
|
-
visit(node.left, scope);
|
|
2958
|
-
visit(node.right, scope);
|
|
3435
|
+
visit$1(node.left, scope);
|
|
3436
|
+
visit$1(node.right, scope);
|
|
2959
3437
|
return;
|
|
2960
3438
|
case "Def": {
|
|
2961
3439
|
const bodyScope = new Set(node.args);
|
|
2962
3440
|
bodyScope.add(node.name);
|
|
2963
|
-
visit(node.body, [...scope, bodyScope]);
|
|
3441
|
+
visit$1(node.body, [...scope, bodyScope]);
|
|
2964
3442
|
const nextScope = new Set([node.name]);
|
|
2965
|
-
visit(node.next, [...scope, nextScope]);
|
|
3443
|
+
visit$1(node.next, [...scope, nextScope]);
|
|
2966
3444
|
return;
|
|
2967
3445
|
}
|
|
2968
3446
|
case "Reduce":
|
|
2969
|
-
visit(node.source, scope);
|
|
2970
|
-
visit(node.init, scope);
|
|
2971
|
-
visit(node.update, scope);
|
|
3447
|
+
visit$1(node.source, scope);
|
|
3448
|
+
visit$1(node.init, scope);
|
|
3449
|
+
visit$1(node.update, scope);
|
|
2972
3450
|
return;
|
|
2973
3451
|
case "Foreach":
|
|
2974
|
-
visit(node.source, scope);
|
|
2975
|
-
visit(node.init, scope);
|
|
2976
|
-
visit(node.update, scope);
|
|
2977
|
-
if (node.extract) visit(node.extract, scope);
|
|
3452
|
+
visit$1(node.source, scope);
|
|
3453
|
+
visit$1(node.init, scope);
|
|
3454
|
+
visit$1(node.update, scope);
|
|
3455
|
+
if (node.extract) visit$1(node.extract, scope);
|
|
2978
3456
|
return;
|
|
2979
3457
|
case "Try":
|
|
2980
|
-
visit(node.body, scope);
|
|
2981
|
-
if (node.handler) visit(node.handler, scope);
|
|
3458
|
+
visit$1(node.body, scope);
|
|
3459
|
+
if (node.handler) visit$1(node.handler, scope);
|
|
2982
3460
|
return;
|
|
2983
3461
|
case "Recurse":
|
|
2984
3462
|
case "Iterate":
|
|
2985
3463
|
case "Break": return;
|
|
2986
3464
|
case "Label":
|
|
2987
|
-
visit(node.body, scope);
|
|
3465
|
+
visit$1(node.body, scope);
|
|
2988
3466
|
return;
|
|
2989
3467
|
case "Slice":
|
|
2990
|
-
visit(node.target, scope);
|
|
2991
|
-
if (node.start) visit(node.start, scope);
|
|
2992
|
-
if (node.end) visit(node.end, scope);
|
|
3468
|
+
visit$1(node.target, scope);
|
|
3469
|
+
if (node.start) visit$1(node.start, scope);
|
|
3470
|
+
if (node.end) visit$1(node.end, scope);
|
|
2993
3471
|
return;
|
|
2994
3472
|
default: return node;
|
|
2995
3473
|
}
|
|
2996
3474
|
};
|
|
2997
|
-
|
|
2998
3475
|
//#endregion
|
|
2999
3476
|
//#region src/eval/break.ts
|
|
3000
3477
|
/**
|
|
@@ -3008,7 +3485,6 @@ var BreakSignal = class BreakSignal extends Error {
|
|
|
3008
3485
|
Object.setPrototypeOf(this, BreakSignal.prototype);
|
|
3009
3486
|
}
|
|
3010
3487
|
};
|
|
3011
|
-
|
|
3012
3488
|
//#endregion
|
|
3013
3489
|
//#region src/limits.ts
|
|
3014
3490
|
const DEFAULT_LIMITS = {
|
|
@@ -3069,7 +3545,6 @@ var LimitTracker = class {
|
|
|
3069
3545
|
if (this.outputs > this.limits.maxOutputs) throw new RuntimeError("Output limit exceeded", span);
|
|
3070
3546
|
}
|
|
3071
3547
|
};
|
|
3072
|
-
|
|
3073
3548
|
//#endregion
|
|
3074
3549
|
//#region src/eval/env.ts
|
|
3075
3550
|
/**
|
|
@@ -3079,7 +3554,26 @@ var LimitTracker = class {
|
|
|
3079
3554
|
const getVar = (env, name) => {
|
|
3080
3555
|
for (let i = env.length - 1; i >= 0; i--) if (env[i].vars.has(name)) return env[i].vars.get(name);
|
|
3081
3556
|
};
|
|
3082
|
-
|
|
3557
|
+
/**
|
|
3558
|
+
* Binds jq `as` destructuring patterns into a variable map.
|
|
3559
|
+
*/
|
|
3560
|
+
const bindPattern = (pattern, value, bindings) => {
|
|
3561
|
+
switch (pattern.kind) {
|
|
3562
|
+
case "VariablePattern":
|
|
3563
|
+
bindings.set(pattern.name, value);
|
|
3564
|
+
return;
|
|
3565
|
+
case "ArrayPattern":
|
|
3566
|
+
if (!Array.isArray(value)) throw new RuntimeError(`Cannot index ${describeType(value)} with number`, pattern.span);
|
|
3567
|
+
pattern.items.forEach((item, index) => {
|
|
3568
|
+
bindPattern(item, index < value.length ? value[index] : null, bindings);
|
|
3569
|
+
});
|
|
3570
|
+
return;
|
|
3571
|
+
case "ObjectPattern":
|
|
3572
|
+
if (!isPlainObject$1(value)) throw new RuntimeError(`Cannot index ${describeType(value)} with string`, pattern.span);
|
|
3573
|
+
for (const entry of pattern.entries) bindPattern(entry.pattern, Object.prototype.hasOwnProperty.call(value, entry.key) ? value[entry.key] : null, bindings);
|
|
3574
|
+
return;
|
|
3575
|
+
}
|
|
3576
|
+
};
|
|
3083
3577
|
//#endregion
|
|
3084
3578
|
//#region src/eval/common.ts
|
|
3085
3579
|
/**
|
|
@@ -3111,7 +3605,6 @@ const toIndex = (value, span) => {
|
|
|
3111
3605
|
}
|
|
3112
3606
|
throw new RuntimeError("Expected numeric index", span);
|
|
3113
3607
|
};
|
|
3114
|
-
|
|
3115
3608
|
//#endregion
|
|
3116
3609
|
//#region src/eval/assignment.ts
|
|
3117
3610
|
/**
|
|
@@ -3127,15 +3620,15 @@ const toIndex = (value, span) => {
|
|
|
3127
3620
|
* @param tracker - Limits tracker.
|
|
3128
3621
|
* @param evaluate - Recursive evaluator.
|
|
3129
3622
|
*/
|
|
3130
|
-
const evalAssignment = function* (node, input, env, tracker, evaluate
|
|
3131
|
-
const paths = Array.from(evaluatePath(node.left, input, env, tracker, evaluate
|
|
3623
|
+
const evalAssignment = function* (node, input, env, tracker, evaluate) {
|
|
3624
|
+
const paths = Array.from(evaluatePath(node.left, input, env, tracker, evaluate));
|
|
3132
3625
|
paths.sort((a, b) => compareValues(a, b) * -1);
|
|
3133
3626
|
if (paths.length === 0) {
|
|
3134
3627
|
yield emit(input, node.span, tracker);
|
|
3135
3628
|
return;
|
|
3136
3629
|
}
|
|
3137
3630
|
if (node.op === "=") {
|
|
3138
|
-
const rhsValues = Array.from(evaluate
|
|
3631
|
+
const rhsValues = Array.from(evaluate(node.right, input, env, tracker));
|
|
3139
3632
|
if (rhsValues.length === 0) return;
|
|
3140
3633
|
for (const rhsVal of rhsValues) {
|
|
3141
3634
|
let current = input;
|
|
@@ -3144,9 +3637,9 @@ const evalAssignment = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3144
3637
|
}
|
|
3145
3638
|
return;
|
|
3146
3639
|
}
|
|
3147
|
-
yield* applyUpdates(input, paths, 0, node.op, node.right, input, env, tracker, evaluate
|
|
3640
|
+
yield* applyUpdates(input, paths, 0, node.op, node.right, input, env, tracker, evaluate);
|
|
3148
3641
|
};
|
|
3149
|
-
function* applyUpdates(current, paths, index, op, rhsNode, contextInput, env, tracker, evaluate
|
|
3642
|
+
function* applyUpdates(current, paths, index, op, rhsNode, contextInput, env, tracker, evaluate) {
|
|
3150
3643
|
if (index >= paths.length) {
|
|
3151
3644
|
yield current;
|
|
3152
3645
|
return;
|
|
@@ -3154,9 +3647,9 @@ function* applyUpdates(current, paths, index, op, rhsNode, contextInput, env, tr
|
|
|
3154
3647
|
const path = paths[index];
|
|
3155
3648
|
const oldValue = getPath(current, path) ?? null;
|
|
3156
3649
|
let newValues = [];
|
|
3157
|
-
if (op === "|=") newValues = Array.from(evaluate
|
|
3650
|
+
if (op === "|=") newValues = Array.from(evaluate(rhsNode, oldValue, env, tracker));
|
|
3158
3651
|
else {
|
|
3159
|
-
const rhsResults = Array.from(evaluate
|
|
3652
|
+
const rhsResults = Array.from(evaluate(rhsNode, contextInput, env, tracker));
|
|
3160
3653
|
for (const rhs of rhsResults) {
|
|
3161
3654
|
let res;
|
|
3162
3655
|
switch (op) {
|
|
@@ -3184,12 +3677,11 @@ function* applyUpdates(current, paths, index, op, rhsNode, contextInput, env, tr
|
|
|
3184
3677
|
}
|
|
3185
3678
|
}
|
|
3186
3679
|
if (newValues.length === 0) {
|
|
3187
|
-
yield* applyUpdates(deletePaths(current, [path], rhsNode.span), paths, index + 1, op, rhsNode, contextInput, env, tracker, evaluate
|
|
3680
|
+
yield* applyUpdates(deletePaths(current, [path], rhsNode.span), paths, index + 1, op, rhsNode, contextInput, env, tracker, evaluate);
|
|
3188
3681
|
return;
|
|
3189
3682
|
}
|
|
3190
|
-
for (const val of newValues) yield* applyUpdates(updatePath(current, path, () => val, rhsNode.span) ?? current, paths, index + 1, op, rhsNode, contextInput, env, tracker, evaluate
|
|
3683
|
+
for (const val of newValues) yield* applyUpdates(updatePath(current, path, () => val, rhsNode.span) ?? current, paths, index + 1, op, rhsNode, contextInput, env, tracker, evaluate);
|
|
3191
3684
|
}
|
|
3192
|
-
|
|
3193
3685
|
//#endregion
|
|
3194
3686
|
//#region src/eval/iterators.ts
|
|
3195
3687
|
/**
|
|
@@ -3201,14 +3693,14 @@ function* applyUpdates(current, paths, index, op, rhsNode, contextInput, env, tr
|
|
|
3201
3693
|
* @param tracker - Limits tracker.
|
|
3202
3694
|
* @param evaluate - Recursive evaluator.
|
|
3203
3695
|
*/
|
|
3204
|
-
const evalIterate = function* (node, input, env, tracker, evaluate
|
|
3205
|
-
for (const container of evaluate
|
|
3696
|
+
const evalIterate = function* (node, input, env, tracker, evaluate) {
|
|
3697
|
+
for (const container of evaluate(node.target, input, env, tracker)) {
|
|
3206
3698
|
if (container === null) continue;
|
|
3207
3699
|
if (isValueArray(container)) {
|
|
3208
3700
|
for (const item of container) yield emit(item, node.span, tracker);
|
|
3209
3701
|
continue;
|
|
3210
3702
|
}
|
|
3211
|
-
if (isPlainObject(container)) {
|
|
3703
|
+
if (isPlainObject$1(container)) {
|
|
3212
3704
|
const keys = Object.keys(container).sort();
|
|
3213
3705
|
for (const key of keys) yield emit(container[key], node.span, tracker);
|
|
3214
3706
|
continue;
|
|
@@ -3227,18 +3719,20 @@ const evalIterate = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3227
3719
|
* @param tracker - Limits tracker.
|
|
3228
3720
|
* @param evaluate - Recursive evaluator.
|
|
3229
3721
|
*/
|
|
3230
|
-
const evalReduce = function* (node, input, env, tracker, evaluate
|
|
3231
|
-
const initValues = Array.from(evaluate
|
|
3722
|
+
const evalReduce = function* (node, input, env, tracker, evaluate) {
|
|
3723
|
+
const initValues = Array.from(evaluate(node.init, input, env, tracker));
|
|
3232
3724
|
if (initValues.length !== 1) throw new RuntimeError("Reduce init must single value", node.init.span);
|
|
3233
3725
|
let acc = initValues[0];
|
|
3234
|
-
for (const item of evaluate
|
|
3726
|
+
for (const item of evaluate(node.source, input, env, tracker)) {
|
|
3235
3727
|
tracker.step(node.span);
|
|
3728
|
+
const vars = /* @__PURE__ */ new Map();
|
|
3729
|
+
bindPattern(node.pattern, item, vars);
|
|
3236
3730
|
const newFrame = {
|
|
3237
|
-
vars
|
|
3731
|
+
vars,
|
|
3238
3732
|
funcs: /* @__PURE__ */ new Map()
|
|
3239
3733
|
};
|
|
3240
3734
|
const newEnv = [...env, newFrame];
|
|
3241
|
-
const updates = Array.from(evaluate
|
|
3735
|
+
const updates = Array.from(evaluate(node.update, acc, newEnv, tracker));
|
|
3242
3736
|
if (updates.length !== 1) throw new RuntimeError("Reduce update must produce single value", node.update.span);
|
|
3243
3737
|
acc = updates[0];
|
|
3244
3738
|
}
|
|
@@ -3255,21 +3749,23 @@ const evalReduce = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3255
3749
|
* @param tracker - Limits tracker.
|
|
3256
3750
|
* @param evaluate - Recursive evaluator.
|
|
3257
3751
|
*/
|
|
3258
|
-
const evalForeach = function* (node, input, env, tracker, evaluate
|
|
3259
|
-
const initValues = Array.from(evaluate
|
|
3752
|
+
const evalForeach = function* (node, input, env, tracker, evaluate) {
|
|
3753
|
+
const initValues = Array.from(evaluate(node.init, input, env, tracker));
|
|
3260
3754
|
if (initValues.length !== 1) throw new RuntimeError("Foreach init must single value", node.init.span);
|
|
3261
3755
|
let acc = initValues[0];
|
|
3262
|
-
for (const item of evaluate
|
|
3756
|
+
for (const item of evaluate(node.source, input, env, tracker)) {
|
|
3263
3757
|
tracker.step(node.span);
|
|
3758
|
+
const vars = /* @__PURE__ */ new Map();
|
|
3759
|
+
bindPattern(node.pattern, item, vars);
|
|
3264
3760
|
const newFrame = {
|
|
3265
|
-
vars
|
|
3761
|
+
vars,
|
|
3266
3762
|
funcs: /* @__PURE__ */ new Map()
|
|
3267
3763
|
};
|
|
3268
3764
|
const newEnv = [...env, newFrame];
|
|
3269
|
-
const updates = Array.from(evaluate
|
|
3765
|
+
const updates = Array.from(evaluate(node.update, acc, newEnv, tracker));
|
|
3270
3766
|
if (updates.length !== 1) throw new RuntimeError("Foreach update must produce single value", node.update.span);
|
|
3271
3767
|
acc = updates[0];
|
|
3272
|
-
if (node.extract) for (const extracted of evaluate
|
|
3768
|
+
if (node.extract) for (const extracted of evaluate(node.extract, acc, newEnv, tracker)) yield emit(extracted, node.span, tracker);
|
|
3273
3769
|
else yield emit(acc, node.span, tracker);
|
|
3274
3770
|
}
|
|
3275
3771
|
};
|
|
@@ -3283,17 +3779,16 @@ const evalForeach = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3283
3779
|
* @param tracker - Limits tracker.
|
|
3284
3780
|
* @param evaluate - Recursive evaluator.
|
|
3285
3781
|
*/
|
|
3286
|
-
const evalRecurse = function* (node, input, env, tracker, evaluate
|
|
3782
|
+
const evalRecurse = function* (node, input, env, tracker, evaluate) {
|
|
3287
3783
|
yield emit(input, node.span, tracker);
|
|
3288
3784
|
const children = [];
|
|
3289
3785
|
if (isValueArray(input)) children.push(...input);
|
|
3290
|
-
else if (isPlainObject(input)) {
|
|
3786
|
+
else if (isPlainObject$1(input)) {
|
|
3291
3787
|
const keys = Object.keys(input).sort();
|
|
3292
3788
|
for (const key of keys) children.push(input[key]);
|
|
3293
3789
|
}
|
|
3294
|
-
for (const child of children) yield* evalRecurse(node, child, env, tracker, evaluate
|
|
3790
|
+
for (const child of children) yield* evalRecurse(node, child, env, tracker, evaluate);
|
|
3295
3791
|
};
|
|
3296
|
-
|
|
3297
3792
|
//#endregion
|
|
3298
3793
|
//#region src/eval/access.ts
|
|
3299
3794
|
/**
|
|
@@ -3305,13 +3800,13 @@ const evalRecurse = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3305
3800
|
* @param tracker - Limits tracker.
|
|
3306
3801
|
* @param evaluate - Recursive evaluator.
|
|
3307
3802
|
*/
|
|
3308
|
-
const evalField = function* (node, input, env, tracker, evaluate
|
|
3309
|
-
for (const container of evaluate
|
|
3803
|
+
const evalField = function* (node, input, env, tracker, evaluate) {
|
|
3804
|
+
for (const container of evaluate(node.target, input, env, tracker)) {
|
|
3310
3805
|
if (container === null) {
|
|
3311
3806
|
yield emit(null, node.span, tracker);
|
|
3312
3807
|
continue;
|
|
3313
3808
|
}
|
|
3314
|
-
if (isPlainObject(container)) {
|
|
3809
|
+
if (isPlainObject$1(container)) {
|
|
3315
3810
|
yield emit(Object.prototype.hasOwnProperty.call(container, node.field) ? container[node.field] : null, node.span, tracker);
|
|
3316
3811
|
continue;
|
|
3317
3812
|
}
|
|
@@ -3328,9 +3823,9 @@ const evalField = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3328
3823
|
* @param tracker - Limits tracker.
|
|
3329
3824
|
* @param evaluate - Recursive evaluator.
|
|
3330
3825
|
*/
|
|
3331
|
-
const evalIndex = function* (node, input, env, tracker, evaluate
|
|
3332
|
-
const indexValues = Array.from(evaluate
|
|
3333
|
-
for (const container of evaluate
|
|
3826
|
+
const evalIndex = function* (node, input, env, tracker, evaluate) {
|
|
3827
|
+
const indexValues = Array.from(evaluate(node.index, input, env, tracker));
|
|
3828
|
+
for (const container of evaluate(node.target, input, env, tracker)) {
|
|
3334
3829
|
if (container === null) {
|
|
3335
3830
|
yield emit(null, node.span, tracker);
|
|
3336
3831
|
continue;
|
|
@@ -3348,7 +3843,7 @@ const evalIndex = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3348
3843
|
}
|
|
3349
3844
|
continue;
|
|
3350
3845
|
}
|
|
3351
|
-
if (isPlainObject(container)) {
|
|
3846
|
+
if (isPlainObject$1(container)) {
|
|
3352
3847
|
for (const keyValue of indexValues) {
|
|
3353
3848
|
if (typeof keyValue !== "string") throw new RuntimeError(`Cannot index object with ${describeType(keyValue)}`, node.span);
|
|
3354
3849
|
yield emit(Object.prototype.hasOwnProperty.call(container, keyValue) ? container[keyValue] : null, node.span, tracker);
|
|
@@ -3368,25 +3863,26 @@ const evalIndex = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3368
3863
|
* @param tracker - Limits tracker.
|
|
3369
3864
|
* @param evaluate - Recursive evaluator.
|
|
3370
3865
|
*/
|
|
3371
|
-
const evalSlice = function* (node, input, env, tracker, evaluate
|
|
3372
|
-
for (const target of evaluate
|
|
3866
|
+
const evalSlice = function* (node, input, env, tracker, evaluate) {
|
|
3867
|
+
for (const target of evaluate(node.target, input, env, tracker)) {
|
|
3373
3868
|
if (typeof target !== "string" && !Array.isArray(target)) throw new RuntimeError("Slice expected string or array", node.span);
|
|
3374
3869
|
const starts = [];
|
|
3375
|
-
if (node.start) for (const s of evaluate
|
|
3870
|
+
if (node.start) for (const s of evaluate(node.start, input, env, tracker)) {
|
|
3376
3871
|
if (typeof s !== "number") throw new RuntimeError("Slice start must be number", node.span);
|
|
3377
3872
|
starts.push(s);
|
|
3378
3873
|
}
|
|
3379
3874
|
else starts.push(0);
|
|
3380
3875
|
const ends = [];
|
|
3381
|
-
if (node.end) for (const e of evaluate
|
|
3876
|
+
if (node.end) for (const e of evaluate(node.end, input, env, tracker)) {
|
|
3382
3877
|
if (typeof e !== "number") throw new RuntimeError("Slice end must be number", node.span);
|
|
3383
3878
|
ends.push(e);
|
|
3384
3879
|
}
|
|
3385
3880
|
else ends.push(target.length);
|
|
3386
|
-
for (const s of starts) for (const e of ends) yield emit(target.slice(s, e), node.span, tracker);
|
|
3881
|
+
for (const s of starts) for (const e of ends) yield emit(target.slice(normalizeSliceStart(s), normalizeSliceEnd(e)), node.span, tracker);
|
|
3387
3882
|
}
|
|
3388
3883
|
};
|
|
3389
|
-
|
|
3884
|
+
const normalizeSliceStart = (value) => Math.floor(value);
|
|
3885
|
+
const normalizeSliceEnd = (value) => Math.ceil(value);
|
|
3390
3886
|
//#endregion
|
|
3391
3887
|
//#region src/eval/constructors.ts
|
|
3392
3888
|
/**
|
|
@@ -3401,9 +3897,9 @@ const evalSlice = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3401
3897
|
* @param tracker - Limits tracker.
|
|
3402
3898
|
* @param evaluate - Recursive evaluator.
|
|
3403
3899
|
*/
|
|
3404
|
-
const buildArray = function* (node, input, env, tracker, evaluate
|
|
3900
|
+
const buildArray = function* (node, input, env, tracker, evaluate) {
|
|
3405
3901
|
const result = [];
|
|
3406
|
-
for (const itemNode of node.items) for (const itemVal of evaluate
|
|
3902
|
+
for (const itemNode of node.items) for (const itemVal of evaluate(itemNode, input, env, tracker)) result.push(itemVal);
|
|
3407
3903
|
yield result;
|
|
3408
3904
|
};
|
|
3409
3905
|
/**
|
|
@@ -3418,10 +3914,10 @@ const buildArray = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3418
3914
|
* @param tracker - Limits tracker.
|
|
3419
3915
|
* @param evaluate - Recursive evaluator.
|
|
3420
3916
|
*/
|
|
3421
|
-
const buildObjects = function* (node, input, env, tracker, evaluate
|
|
3422
|
-
yield* fillObject(node.entries, 0, {}, input, env, tracker, evaluate
|
|
3917
|
+
const buildObjects = function* (node, input, env, tracker, evaluate) {
|
|
3918
|
+
yield* fillObject(node.entries, 0, {}, input, env, tracker, evaluate);
|
|
3423
3919
|
};
|
|
3424
|
-
function* fillObject(entries, index, current, input, env, tracker, evaluate
|
|
3920
|
+
function* fillObject(entries, index, current, input, env, tracker, evaluate) {
|
|
3425
3921
|
if (index >= entries.length) {
|
|
3426
3922
|
yield { ...current };
|
|
3427
3923
|
return;
|
|
@@ -3430,17 +3926,16 @@ function* fillObject(entries, index, current, input, env, tracker, evaluate$1) {
|
|
|
3430
3926
|
let keys = [];
|
|
3431
3927
|
if (entry.key.kind === "KeyIdentifier") keys = [entry.key.name];
|
|
3432
3928
|
else if (entry.key.kind === "KeyString") keys = [entry.key.value];
|
|
3433
|
-
else for (const k of evaluate
|
|
3929
|
+
else for (const k of evaluate(entry.key.expr, input, env, tracker)) {
|
|
3434
3930
|
if (typeof k !== "string") throw new RuntimeError("Object key must be a string", entry.key.span);
|
|
3435
3931
|
keys.push(k);
|
|
3436
3932
|
}
|
|
3437
|
-
for (const key of keys) for (const val of evaluate
|
|
3933
|
+
for (const key of keys) for (const val of evaluate(entry.value, input, env, tracker)) {
|
|
3438
3934
|
current[key] = val;
|
|
3439
|
-
yield* fillObject(entries, index + 1, current, input, env, tracker, evaluate
|
|
3935
|
+
yield* fillObject(entries, index + 1, current, input, env, tracker, evaluate);
|
|
3440
3936
|
delete current[key];
|
|
3441
3937
|
}
|
|
3442
3938
|
}
|
|
3443
|
-
|
|
3444
3939
|
//#endregion
|
|
3445
3940
|
//#region src/eval/functions.ts
|
|
3446
3941
|
/**
|
|
@@ -3458,7 +3953,7 @@ function* fillObject(entries, index, current, input, env, tracker, evaluate$1) {
|
|
|
3458
3953
|
* @param tracker - Limits tracker.
|
|
3459
3954
|
* @param evaluate - Recursive evaluator.
|
|
3460
3955
|
*/
|
|
3461
|
-
const evalCall = function* (node, input, env, tracker, evaluate
|
|
3956
|
+
const evalCall = function* (node, input, env, tracker, evaluate) {
|
|
3462
3957
|
for (let i = env.length - 1; i >= 0; i--) {
|
|
3463
3958
|
const funcs = env[i].funcs.get(node.name);
|
|
3464
3959
|
if (funcs) {
|
|
@@ -3480,7 +3975,7 @@ const evalCall = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3480
3975
|
newFrame.funcs.set(argName, argDefs);
|
|
3481
3976
|
}
|
|
3482
3977
|
const newStack = [...def.closure, newFrame];
|
|
3483
|
-
yield* evaluate
|
|
3978
|
+
yield* evaluate(def.body, input, newStack, tracker);
|
|
3484
3979
|
return;
|
|
3485
3980
|
}
|
|
3486
3981
|
}
|
|
@@ -3489,7 +3984,7 @@ const evalCall = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3489
3984
|
if (!specs) throw new RuntimeError(`Unknown function: ${node.name}`, node.span);
|
|
3490
3985
|
const builtin = specs.find((s) => s.arity === node.args.length);
|
|
3491
3986
|
if (!builtin) throw new RuntimeError(`Function ${node.name} does not accept ${node.args.length} arguments`, node.span);
|
|
3492
|
-
yield* builtin.apply(input, node.args, env, tracker, evaluate
|
|
3987
|
+
yield* builtin.apply(input, node.args, env, tracker, evaluate, node.span);
|
|
3493
3988
|
};
|
|
3494
3989
|
/**
|
|
3495
3990
|
* Defines a new function in the environment.
|
|
@@ -3503,7 +3998,7 @@ const evalCall = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3503
3998
|
* @param tracker - Limits tracker.
|
|
3504
3999
|
* @param evaluate - Recursive evaluator.
|
|
3505
4000
|
*/
|
|
3506
|
-
const evalDef = function* (node, input, env, tracker, evaluate
|
|
4001
|
+
const evalDef = function* (node, input, env, tracker, evaluate) {
|
|
3507
4002
|
const newFrame = {
|
|
3508
4003
|
vars: /* @__PURE__ */ new Map(),
|
|
3509
4004
|
funcs: /* @__PURE__ */ new Map()
|
|
@@ -3518,9 +4013,8 @@ const evalDef = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3518
4013
|
newFrame.funcs.set(node.name, currentDefs);
|
|
3519
4014
|
const newStack = [...env, newFrame];
|
|
3520
4015
|
funDef.closure = newStack;
|
|
3521
|
-
yield* evaluate
|
|
4016
|
+
yield* evaluate(node.next, input, newStack, tracker);
|
|
3522
4017
|
};
|
|
3523
|
-
|
|
3524
4018
|
//#endregion
|
|
3525
4019
|
//#region src/eval/control_flow.ts
|
|
3526
4020
|
/**
|
|
@@ -3532,17 +4026,17 @@ const evalDef = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3532
4026
|
* @param tracker - Limits tracker.
|
|
3533
4027
|
* @param evaluate - Recursive evaluator.
|
|
3534
4028
|
*/
|
|
3535
|
-
const evalIf = function* (node, input, env, tracker, evaluate
|
|
3536
|
-
yield* evalIfBranch(node, 0, input, env, tracker, evaluate
|
|
4029
|
+
const evalIf = function* (node, input, env, tracker, evaluate) {
|
|
4030
|
+
yield* evalIfBranch(node, 0, input, env, tracker, evaluate);
|
|
3537
4031
|
};
|
|
3538
|
-
function* evalIfBranch(node, branchIndex, input, env, tracker, evaluate
|
|
4032
|
+
function* evalIfBranch(node, branchIndex, input, env, tracker, evaluate) {
|
|
3539
4033
|
if (branchIndex >= node.branches.length) {
|
|
3540
|
-
yield* evaluate
|
|
4034
|
+
yield* evaluate(node.else, input, env, tracker);
|
|
3541
4035
|
return;
|
|
3542
4036
|
}
|
|
3543
4037
|
const branch = node.branches[branchIndex];
|
|
3544
|
-
for (const cond of evaluate
|
|
3545
|
-
else yield* evalIfBranch(node, branchIndex + 1, input, env, tracker, evaluate
|
|
4038
|
+
for (const cond of evaluate(branch.cond, input, env, tracker)) if (isTruthy(cond)) yield* evaluate(branch.then, input, env, tracker);
|
|
4039
|
+
else yield* evalIfBranch(node, branchIndex + 1, input, env, tracker, evaluate);
|
|
3546
4040
|
}
|
|
3547
4041
|
/**
|
|
3548
4042
|
* Evaluates a `try-catch` expression.
|
|
@@ -3553,12 +4047,12 @@ function* evalIfBranch(node, branchIndex, input, env, tracker, evaluate$1) {
|
|
|
3553
4047
|
* @param tracker - Limits tracker.
|
|
3554
4048
|
* @param evaluate - Recursive evaluator.
|
|
3555
4049
|
*/
|
|
3556
|
-
const evalTry = function* (node, input, env, tracker, evaluate
|
|
4050
|
+
const evalTry = function* (node, input, env, tracker, evaluate) {
|
|
3557
4051
|
try {
|
|
3558
|
-
yield* evaluate
|
|
4052
|
+
yield* evaluate(node.body, input, env, tracker);
|
|
3559
4053
|
} catch (err) {
|
|
3560
4054
|
if (err instanceof RuntimeError) {
|
|
3561
|
-
if (node.handler) yield* evaluate
|
|
4055
|
+
if (node.handler) yield* evaluate(node.handler, err.message, env, tracker);
|
|
3562
4056
|
} else throw err;
|
|
3563
4057
|
}
|
|
3564
4058
|
};
|
|
@@ -3571,9 +4065,9 @@ const evalTry = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3571
4065
|
* @param tracker - Limits tracker.
|
|
3572
4066
|
* @param evaluate - Recursive evaluator.
|
|
3573
4067
|
*/
|
|
3574
|
-
const evalLabel = function* (node, input, env, tracker, evaluate
|
|
4068
|
+
const evalLabel = function* (node, input, env, tracker, evaluate) {
|
|
3575
4069
|
try {
|
|
3576
|
-
yield* evaluate
|
|
4070
|
+
yield* evaluate(node.body, input, env, tracker);
|
|
3577
4071
|
} catch (e) {
|
|
3578
4072
|
if (e instanceof BreakSignal) {
|
|
3579
4073
|
if (e.label === node.label) return;
|
|
@@ -3581,7 +4075,6 @@ const evalLabel = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3581
4075
|
throw e;
|
|
3582
4076
|
}
|
|
3583
4077
|
};
|
|
3584
|
-
|
|
3585
4078
|
//#endregion
|
|
3586
4079
|
//#region src/eval/dispatch.ts
|
|
3587
4080
|
/**
|
|
@@ -3702,8 +4195,10 @@ function* evaluate(node, input, env, tracker) {
|
|
|
3702
4195
|
case "As": {
|
|
3703
4196
|
const values = Array.from(evaluate(node.bind, input, env, tracker));
|
|
3704
4197
|
for (const val of values) {
|
|
4198
|
+
const vars = /* @__PURE__ */ new Map();
|
|
4199
|
+
bindPattern(node.pattern, val, vars);
|
|
3705
4200
|
const newFrame = {
|
|
3706
|
-
vars
|
|
4201
|
+
vars,
|
|
3707
4202
|
funcs: /* @__PURE__ */ new Map()
|
|
3708
4203
|
};
|
|
3709
4204
|
const newEnv = [...env, newFrame];
|
|
@@ -3722,7 +4217,301 @@ function* evaluate(node, input, env, tracker) {
|
|
|
3722
4217
|
tracker.exit();
|
|
3723
4218
|
}
|
|
3724
4219
|
}
|
|
3725
|
-
|
|
4220
|
+
//#endregion
|
|
4221
|
+
//#region src/compat.ts
|
|
4222
|
+
const semanticWarnings = {
|
|
4223
|
+
unique: "jq sorts unique results; jq-ts preserves first-seen order for determinism.",
|
|
4224
|
+
unique_by: "jq sorts unique_by results by key; jq-ts preserves first-seen order.",
|
|
4225
|
+
to_entries: "jq preserves object insertion order; jq-ts sorts object keys deterministically.",
|
|
4226
|
+
with_entries: "jq preserves object insertion order; jq-ts processes object keys in sorted order.",
|
|
4227
|
+
tostring: "jq stringifies objects in input key order; jq-ts uses stable sorted-key stringification.",
|
|
4228
|
+
tojson: "jq tojson preserves input object order; jq-ts uses stable sorted-key stringification.",
|
|
4229
|
+
infinite: "jq serializes infinite as a finite JSON number; jq-ts returns JavaScript Infinity internally.",
|
|
4230
|
+
normal: "jq documents isnormal; jq-ts exposes normal with approximate JavaScript number semantics.",
|
|
4231
|
+
subnormal: "jq documents subnormal classification; jq-ts currently approximates this as false."
|
|
4232
|
+
};
|
|
4233
|
+
const specialVariableWarnings = {
|
|
4234
|
+
ENV: "jq $ENV is an environment snapshot; jq-ts does not populate it unless the caller injects an ENV variable.",
|
|
4235
|
+
__loc__: "jq $__loc__ reports source location metadata; jq-ts does not populate it unless the caller injects a __loc__ variable.",
|
|
4236
|
+
JQ_BUILD_CONFIGURATION: "jq $JQ_BUILD_CONFIGURATION reports jq build metadata; jq-ts does not populate it unless the caller injects a JQ_BUILD_CONFIGURATION variable.",
|
|
4237
|
+
ARGS: "jq $ARGS is populated from CLI arguments; jq-ts does not populate it unless the caller injects an ARGS variable."
|
|
4238
|
+
};
|
|
4239
|
+
const syntaxWarningsByKind = { Slice: "jq slice bounds have jq-specific numeric coercion; jq-ts uses JavaScript slice semantics." };
|
|
4240
|
+
/**
|
|
4241
|
+
* Checks whether a jq expression can be parsed and statically validated by jq-ts.
|
|
4242
|
+
*
|
|
4243
|
+
* This is a syntax/subset check only. It does not prove that runtime behavior
|
|
4244
|
+
* matches the jq binary for every possible input.
|
|
4245
|
+
*/
|
|
4246
|
+
const checkCompatibility = (source) => {
|
|
4247
|
+
try {
|
|
4248
|
+
validate(parse(source));
|
|
4249
|
+
return {
|
|
4250
|
+
compatible: true,
|
|
4251
|
+
findings: []
|
|
4252
|
+
};
|
|
4253
|
+
} catch (err) {
|
|
4254
|
+
const finding = toFinding(err);
|
|
4255
|
+
if (finding) return {
|
|
4256
|
+
compatible: false,
|
|
4257
|
+
findings: [finding]
|
|
4258
|
+
};
|
|
4259
|
+
throw err;
|
|
4260
|
+
}
|
|
4261
|
+
};
|
|
4262
|
+
/**
|
|
4263
|
+
* Checks jq-ts compatibility and reports known semantic differences from jq.
|
|
4264
|
+
*/
|
|
4265
|
+
const analyzeCompatibility = (source) => {
|
|
4266
|
+
const check = checkCompatibility(source);
|
|
4267
|
+
if (!check.compatible) return {
|
|
4268
|
+
...check,
|
|
4269
|
+
warnings: []
|
|
4270
|
+
};
|
|
4271
|
+
const warnings = collectWarnings(parse(source));
|
|
4272
|
+
return {
|
|
4273
|
+
compatible: true,
|
|
4274
|
+
findings: warnings,
|
|
4275
|
+
warnings
|
|
4276
|
+
};
|
|
4277
|
+
};
|
|
4278
|
+
/**
|
|
4279
|
+
* Compares jq-ts execution against a caller-provided jq runner or jq result.
|
|
4280
|
+
*
|
|
4281
|
+
* The function is safe to export from the isolate-safe library because it does
|
|
4282
|
+
* not spawn the jq binary itself. Tests and development tools can pass a runner
|
|
4283
|
+
* that shells out to jq.
|
|
4284
|
+
*/
|
|
4285
|
+
const compareWithJq = (source, input, jq, options = {}) => {
|
|
4286
|
+
const analysis = analyzeCompatibility(source);
|
|
4287
|
+
const jqTs = runJqTs(source, input, options);
|
|
4288
|
+
const jqResult = typeof jq === "function" ? runExternalJq(jq, source, input) : jq;
|
|
4289
|
+
const findings = [...analysis.findings];
|
|
4290
|
+
if (!jqTs.ok) findings.push({
|
|
4291
|
+
severity: "error",
|
|
4292
|
+
stage: jqTs.stage ?? "runtime",
|
|
4293
|
+
category: "runtime-error",
|
|
4294
|
+
message: jqTs.error
|
|
4295
|
+
});
|
|
4296
|
+
if (!jqResult.ok) findings.push({
|
|
4297
|
+
severity: "error",
|
|
4298
|
+
stage: "compare",
|
|
4299
|
+
category: "jq-error",
|
|
4300
|
+
message: jqResult.error
|
|
4301
|
+
});
|
|
4302
|
+
const equivalent = jqTs.ok && jqResult.ok ? outputStreamsEqual(jqTs.outputs, jqResult.outputs) : null;
|
|
4303
|
+
if (equivalent === false) findings.push({
|
|
4304
|
+
severity: "error",
|
|
4305
|
+
stage: "compare",
|
|
4306
|
+
category: "output-mismatch",
|
|
4307
|
+
message: "jq-ts output stream does not match jq output stream."
|
|
4308
|
+
});
|
|
4309
|
+
return {
|
|
4310
|
+
compatible: analysis.compatible && jqTs.ok,
|
|
4311
|
+
equivalent,
|
|
4312
|
+
analysis,
|
|
4313
|
+
jqTs,
|
|
4314
|
+
jq: jqResult,
|
|
4315
|
+
findings
|
|
4316
|
+
};
|
|
4317
|
+
};
|
|
4318
|
+
const runJqTs = (source, input, options) => {
|
|
4319
|
+
try {
|
|
4320
|
+
const ast = parse(source);
|
|
4321
|
+
validate(ast);
|
|
4322
|
+
return {
|
|
4323
|
+
ok: true,
|
|
4324
|
+
outputs: runAst(ast, input, options)
|
|
4325
|
+
};
|
|
4326
|
+
} catch (err) {
|
|
4327
|
+
const jqTsError = asJqTsError(err);
|
|
4328
|
+
return {
|
|
4329
|
+
ok: false,
|
|
4330
|
+
error: err instanceof Error ? err.message : String(err),
|
|
4331
|
+
stage: jqTsError?.kind
|
|
4332
|
+
};
|
|
4333
|
+
}
|
|
4334
|
+
};
|
|
4335
|
+
const runExternalJq = (runner, source, input) => {
|
|
4336
|
+
try {
|
|
4337
|
+
const result = runner(source, input);
|
|
4338
|
+
if (Array.isArray(result)) return {
|
|
4339
|
+
ok: true,
|
|
4340
|
+
outputs: result
|
|
4341
|
+
};
|
|
4342
|
+
return result;
|
|
4343
|
+
} catch (err) {
|
|
4344
|
+
return {
|
|
4345
|
+
ok: false,
|
|
4346
|
+
error: err instanceof Error ? err.message : String(err),
|
|
4347
|
+
stage: "compare"
|
|
4348
|
+
};
|
|
4349
|
+
}
|
|
4350
|
+
};
|
|
4351
|
+
const outputStreamsEqual = (left, right) => left.length === right.length && left.every((value, index) => valueEquals(value, right[index]));
|
|
4352
|
+
const toFinding = (err) => {
|
|
4353
|
+
const jqTsError = asJqTsError(err);
|
|
4354
|
+
if (!jqTsError) return null;
|
|
4355
|
+
return {
|
|
4356
|
+
severity: "error",
|
|
4357
|
+
stage: jqTsError.kind,
|
|
4358
|
+
category: classifyError(jqTsError),
|
|
4359
|
+
message: jqTsError.message,
|
|
4360
|
+
span: jqTsError.span
|
|
4361
|
+
};
|
|
4362
|
+
};
|
|
4363
|
+
const asJqTsError = (err) => {
|
|
4364
|
+
if (err instanceof LexError || err instanceof ParseError || err instanceof ValidationError || err instanceof RuntimeError) return err;
|
|
4365
|
+
return null;
|
|
4366
|
+
};
|
|
4367
|
+
const classifyError = (err) => {
|
|
4368
|
+
if (err.kind === "parse" || err.kind === "lex") return "unsupported-syntax";
|
|
4369
|
+
if (err.kind === "runtime") return "runtime-error";
|
|
4370
|
+
if (err.message.startsWith("Unknown function:")) return isIntentionalExclusion(err.message) ? "intentional-exclusion" : "unsupported-builtin";
|
|
4371
|
+
if (err.message.includes("expects") && err.message.includes("arguments")) return "arity-mismatch";
|
|
4372
|
+
return "unsupported-syntax";
|
|
4373
|
+
};
|
|
4374
|
+
const isIntentionalExclusion = (message) => [
|
|
4375
|
+
"now",
|
|
4376
|
+
"input",
|
|
4377
|
+
"inputs",
|
|
4378
|
+
"env"
|
|
4379
|
+
].some((name) => message === `Unknown function: ${name}`);
|
|
4380
|
+
const collectWarnings = (node) => {
|
|
4381
|
+
const warnings = [];
|
|
4382
|
+
visit(node, (current) => {
|
|
4383
|
+
const syntaxWarning = syntaxWarningsByKind[current.kind];
|
|
4384
|
+
if (syntaxWarning) warnings.push({
|
|
4385
|
+
severity: "warning",
|
|
4386
|
+
stage: "validate",
|
|
4387
|
+
category: "input-dependent",
|
|
4388
|
+
message: syntaxWarning,
|
|
4389
|
+
span: current.span
|
|
4390
|
+
});
|
|
4391
|
+
if (current.kind === "Call") {
|
|
4392
|
+
const warning = semanticWarnings[current.name];
|
|
4393
|
+
if (warning) warnings.push({
|
|
4394
|
+
severity: "warning",
|
|
4395
|
+
stage: "validate",
|
|
4396
|
+
category: "semantic-deviation",
|
|
4397
|
+
message: warning,
|
|
4398
|
+
span: current.span
|
|
4399
|
+
});
|
|
4400
|
+
}
|
|
4401
|
+
if (current.kind === "Var") {
|
|
4402
|
+
const warning = specialVariableWarnings[current.name];
|
|
4403
|
+
if (warning) warnings.push({
|
|
4404
|
+
severity: "warning",
|
|
4405
|
+
stage: "validate",
|
|
4406
|
+
category: current.name === "ENV" ? "intentional-exclusion" : "semantic-deviation",
|
|
4407
|
+
message: warning,
|
|
4408
|
+
span: current.span
|
|
4409
|
+
});
|
|
4410
|
+
}
|
|
4411
|
+
});
|
|
4412
|
+
return dedupeFindings(warnings);
|
|
4413
|
+
};
|
|
4414
|
+
const dedupeFindings = (findings) => {
|
|
4415
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4416
|
+
const result = [];
|
|
4417
|
+
for (const finding of findings) {
|
|
4418
|
+
const key = `${finding.category}:${finding.message}:${finding.span?.start ?? ""}:${finding.span?.end ?? ""}`;
|
|
4419
|
+
if (seen.has(key)) continue;
|
|
4420
|
+
seen.add(key);
|
|
4421
|
+
result.push(finding);
|
|
4422
|
+
}
|
|
4423
|
+
return result;
|
|
4424
|
+
};
|
|
4425
|
+
const visit = (node, callback) => {
|
|
4426
|
+
callback(node);
|
|
4427
|
+
switch (node.kind) {
|
|
4428
|
+
case "Identity":
|
|
4429
|
+
case "Literal":
|
|
4430
|
+
case "Var":
|
|
4431
|
+
case "Recurse":
|
|
4432
|
+
case "Break": return;
|
|
4433
|
+
case "FieldAccess":
|
|
4434
|
+
visit(node.target, callback);
|
|
4435
|
+
return;
|
|
4436
|
+
case "IndexAccess":
|
|
4437
|
+
visit(node.target, callback);
|
|
4438
|
+
visit(node.index, callback);
|
|
4439
|
+
return;
|
|
4440
|
+
case "Iterate":
|
|
4441
|
+
visit(node.target, callback);
|
|
4442
|
+
return;
|
|
4443
|
+
case "Slice":
|
|
4444
|
+
visit(node.target, callback);
|
|
4445
|
+
if (node.start) visit(node.start, callback);
|
|
4446
|
+
if (node.end) visit(node.end, callback);
|
|
4447
|
+
return;
|
|
4448
|
+
case "Array":
|
|
4449
|
+
node.items.forEach((item) => visit(item, callback));
|
|
4450
|
+
return;
|
|
4451
|
+
case "Object":
|
|
4452
|
+
node.entries.forEach((entry) => visitObjectEntry(entry, callback));
|
|
4453
|
+
return;
|
|
4454
|
+
case "Pipe":
|
|
4455
|
+
case "Comma":
|
|
4456
|
+
case "Alt":
|
|
4457
|
+
case "Binary":
|
|
4458
|
+
case "Bool":
|
|
4459
|
+
visit(node.left, callback);
|
|
4460
|
+
visit(node.right, callback);
|
|
4461
|
+
return;
|
|
4462
|
+
case "Unary":
|
|
4463
|
+
visit(node.expr, callback);
|
|
4464
|
+
return;
|
|
4465
|
+
case "If":
|
|
4466
|
+
node.branches.forEach((branch) => {
|
|
4467
|
+
visit(branch.cond, callback);
|
|
4468
|
+
visit(branch.then, callback);
|
|
4469
|
+
});
|
|
4470
|
+
visit(node.else, callback);
|
|
4471
|
+
return;
|
|
4472
|
+
case "As":
|
|
4473
|
+
visit(node.bind, callback);
|
|
4474
|
+
visit(node.body, callback);
|
|
4475
|
+
return;
|
|
4476
|
+
case "Call":
|
|
4477
|
+
node.args.forEach((arg) => visit(arg, callback));
|
|
4478
|
+
return;
|
|
4479
|
+
case "Reduce":
|
|
4480
|
+
visit(node.source, callback);
|
|
4481
|
+
visit(node.init, callback);
|
|
4482
|
+
visit(node.update, callback);
|
|
4483
|
+
return;
|
|
4484
|
+
case "Foreach":
|
|
4485
|
+
visit(node.source, callback);
|
|
4486
|
+
visit(node.init, callback);
|
|
4487
|
+
visit(node.update, callback);
|
|
4488
|
+
if (node.extract) visit(node.extract, callback);
|
|
4489
|
+
return;
|
|
4490
|
+
case "Try":
|
|
4491
|
+
visit(node.body, callback);
|
|
4492
|
+
if (node.handler) visit(node.handler, callback);
|
|
4493
|
+
return;
|
|
4494
|
+
case "Assignment":
|
|
4495
|
+
visit(node.left, callback);
|
|
4496
|
+
visit(node.right, callback);
|
|
4497
|
+
return;
|
|
4498
|
+
case "Def":
|
|
4499
|
+
visit(node.body, callback);
|
|
4500
|
+
visit(node.next, callback);
|
|
4501
|
+
return;
|
|
4502
|
+
case "Label":
|
|
4503
|
+
visit(node.body, callback);
|
|
4504
|
+
return;
|
|
4505
|
+
default: return node;
|
|
4506
|
+
}
|
|
4507
|
+
};
|
|
4508
|
+
const visitObjectEntry = (entry, callback) => {
|
|
4509
|
+
visitObjectKey(entry.key, callback);
|
|
4510
|
+
visit(entry.value, callback);
|
|
4511
|
+
};
|
|
4512
|
+
const visitObjectKey = (key, callback) => {
|
|
4513
|
+
if (key.kind === "KeyExpr") visit(key.expr, callback);
|
|
4514
|
+
};
|
|
3726
4515
|
//#endregion
|
|
3727
4516
|
//#region src/index.ts
|
|
3728
4517
|
/**
|
|
@@ -3742,7 +4531,7 @@ const run = (source, input, options = {}) => {
|
|
|
3742
4531
|
validate(ast);
|
|
3743
4532
|
return runAst(ast, input, options);
|
|
3744
4533
|
};
|
|
3745
|
-
|
|
3746
4534
|
//#endregion
|
|
3747
|
-
export { LexError, LimitTracker, ParseError, RuntimeError, ValidationError, parse, resolveLimits, run, runAst, validate };
|
|
4535
|
+
export { LexError, LimitTracker, ParseError, RuntimeError, ValidationError, analyzeCompatibility, checkCompatibility, compareWithJq, parse, resolveLimits, run, runAst, validate };
|
|
4536
|
+
|
|
3748
4537
|
//# sourceMappingURL=index.mjs.map
|