@gabrielbryk/jq-ts 1.3.6 → 1.5.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 +2375 -1024
- package/dist/index.d.cts +118 -6
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +118 -6
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2220 -871
- 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,435 +1771,103 @@ 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
|
-
const obj = item;
|
|
1809
|
-
if (!("key" in obj) || !("value" in obj)) throw new RuntimeError("from_entries items must have \"key\" and \"value\"", span);
|
|
1810
|
-
const key = obj["key"];
|
|
1811
|
-
if (typeof key !== "string") throw new RuntimeError("from_entries object keys must be strings", span);
|
|
1812
|
-
result[key] = obj["value"];
|
|
1813
|
-
}
|
|
1814
|
-
yield emit$1(result, span, tracker);
|
|
1815
|
-
}
|
|
1816
|
-
},
|
|
1817
|
-
{
|
|
1818
|
-
name: "with_entries",
|
|
1819
|
-
arity: 1,
|
|
1820
|
-
apply: function* (input, args, env, tracker, evaluate$1, span) {
|
|
1821
|
-
let entries;
|
|
1822
|
-
if (Array.isArray(input)) entries = input.map((v, i) => ({
|
|
1823
|
-
key: i,
|
|
1824
|
-
value: v
|
|
1825
|
-
}));
|
|
1826
|
-
else if (input !== null && typeof input === "object") entries = Object.keys(input).sort().map((k) => ({
|
|
1827
|
-
key: k,
|
|
1828
|
-
value: input[k]
|
|
1829
|
-
}));
|
|
1830
|
-
else throw new RuntimeError("with_entries expects array or object", span);
|
|
1831
|
-
const transformed = [];
|
|
1832
|
-
const filter = args[0];
|
|
1833
|
-
for (const entry of entries) {
|
|
1834
|
-
tracker.step(span);
|
|
1835
|
-
for (const outVar of evaluate$1(filter, entry, env, tracker)) transformed.push(outVar);
|
|
1836
|
-
}
|
|
1837
|
-
const result = {};
|
|
1838
|
-
for (const item of transformed) {
|
|
1839
|
-
if (item === null || typeof item !== "object" || Array.isArray(item)) throw new RuntimeError("with_entries filter must produce objects", span);
|
|
1840
|
-
const obj = item;
|
|
1841
|
-
if (!("key" in obj) || !("value" in obj)) throw new RuntimeError("with_entries items must have \"key\" and \"value\"", span);
|
|
1842
|
-
const key = obj["key"];
|
|
1843
|
-
if (typeof key !== "string") throw new RuntimeError("with_entries keys must be strings", span);
|
|
1844
|
-
result[key] = obj["value"];
|
|
1845
|
-
}
|
|
1846
|
-
yield emit$1(result, span, tracker);
|
|
1847
|
-
}
|
|
1848
|
-
},
|
|
1849
|
-
{
|
|
1850
|
-
name: "group_by",
|
|
1851
|
-
arity: 1,
|
|
1852
|
-
apply: function* (input, args, env, tracker, evaluate$1, span) {
|
|
1853
|
-
if (!Array.isArray(input)) throw new RuntimeError("group_by expects an array", span);
|
|
1854
|
-
const pairs = [];
|
|
1855
|
-
const filter = args[0];
|
|
1856
|
-
for (const item of input) {
|
|
1857
|
-
tracker.step(span);
|
|
1858
|
-
const keys = Array.from(evaluate$1(filter, item, env, tracker));
|
|
1859
|
-
if (keys.length !== 1) throw new RuntimeError("group_by key expression must return exactly one value", span);
|
|
1860
|
-
pairs.push({
|
|
1861
|
-
val: item,
|
|
1862
|
-
key: keys[0]
|
|
1863
|
-
});
|
|
1864
|
-
}
|
|
1865
|
-
const sorted = sortStable(pairs, (a, b) => compareValues(a.key, b.key));
|
|
1866
|
-
const groups = [];
|
|
1867
|
-
if (sorted.length > 0) {
|
|
1868
|
-
let currentGroup = [sorted[0].val];
|
|
1869
|
-
let currentKey = sorted[0].key;
|
|
1870
|
-
for (let i = 1; i < sorted.length; i++) {
|
|
1871
|
-
const pair = sorted[i];
|
|
1872
|
-
if (compareValues(pair.key, currentKey) === 0) currentGroup.push(pair.val);
|
|
1873
|
-
else {
|
|
1874
|
-
groups.push(currentGroup);
|
|
1875
|
-
currentGroup = [pair.val];
|
|
1876
|
-
currentKey = pair.key;
|
|
1877
|
-
}
|
|
1878
|
-
}
|
|
1879
|
-
groups.push(currentGroup);
|
|
1880
|
-
}
|
|
1881
|
-
yield emit$1(groups, span, tracker);
|
|
1882
|
-
}
|
|
1883
|
-
},
|
|
1884
|
-
{
|
|
1885
|
-
name: "reverse",
|
|
1886
|
-
arity: 0,
|
|
1887
|
-
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
1888
|
-
if (!Array.isArray(input)) throw new RuntimeError("reverse expects an array", span);
|
|
1889
|
-
yield emit$1([...input].reverse(), span, tracker);
|
|
1890
|
-
}
|
|
1891
|
-
},
|
|
1892
|
-
{
|
|
1893
|
-
name: "flatten",
|
|
1894
|
-
arity: 0,
|
|
1895
|
-
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
1896
|
-
if (!Array.isArray(input)) throw new RuntimeError("flatten expects an array", span);
|
|
1897
|
-
const flattenRec = (arr) => {
|
|
1898
|
-
let res = [];
|
|
1899
|
-
for (const item of arr) if (Array.isArray(item)) res = res.concat(flattenRec(item));
|
|
1900
|
-
else res.push(item);
|
|
1901
|
-
return res;
|
|
1902
|
-
};
|
|
1903
|
-
yield emit$1(flattenRec(input), span, tracker);
|
|
1904
|
-
}
|
|
1905
|
-
},
|
|
1906
|
-
{
|
|
1907
|
-
name: "flatten",
|
|
1908
|
-
arity: 1,
|
|
1909
|
-
apply: function* (input, args, env, tracker, evaluate$1, span) {
|
|
1910
|
-
if (!Array.isArray(input)) throw new RuntimeError("flatten expects an array", span);
|
|
1911
|
-
const depths = evaluate$1(args[0], input, env, tracker);
|
|
1912
|
-
for (const depthVal of depths) {
|
|
1913
|
-
if (typeof depthVal !== "number") throw new RuntimeError("flatten depth must be a number", span);
|
|
1914
|
-
const flattenDepth = (arr, d) => {
|
|
1915
|
-
if (d <= 0) return arr;
|
|
1916
|
-
let res = [];
|
|
1917
|
-
for (const item of arr) if (Array.isArray(item)) res = res.concat(flattenDepth(item, d - 1));
|
|
1918
|
-
else res.push(item);
|
|
1919
|
-
return res;
|
|
1920
|
-
};
|
|
1921
|
-
yield emit$1(flattenDepth(input, depthVal), span, tracker);
|
|
1922
|
-
}
|
|
1923
|
-
}
|
|
1924
|
-
},
|
|
1925
|
-
{
|
|
1926
|
-
name: "keys_unsorted",
|
|
1927
|
-
arity: 0,
|
|
1928
|
-
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
1929
|
-
if (Array.isArray(input)) yield emit$1(Array.from({ length: input.length }, (_, i) => i), span, tracker);
|
|
1930
|
-
else if (input !== null && typeof input === "object") yield emit$1(Object.keys(input), span, tracker);
|
|
1931
|
-
else throw new RuntimeError(`keys_unsorted expects an array or object`, span);
|
|
1932
|
-
}
|
|
1933
|
-
},
|
|
1934
|
-
{
|
|
1935
|
-
name: "transpose",
|
|
1936
|
-
arity: 0,
|
|
1937
|
-
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
1938
|
-
if (!Array.isArray(input)) throw new RuntimeError("transpose expects an array", span);
|
|
1939
|
-
const arr = input;
|
|
1940
|
-
if (arr.length === 0) {
|
|
1941
|
-
yield emit$1([], span, tracker);
|
|
1942
|
-
return;
|
|
1943
|
-
}
|
|
1944
|
-
let maxLen = 0;
|
|
1945
|
-
for (const row of arr) {
|
|
1946
|
-
if (!Array.isArray(row)) throw new RuntimeError("transpose input must be array of arrays", span);
|
|
1947
|
-
if (row.length > maxLen) maxLen = row.length;
|
|
1948
|
-
}
|
|
1949
|
-
const result = [];
|
|
1950
|
-
for (let j = 0; j < maxLen; j++) {
|
|
1951
|
-
const newRow = [];
|
|
1952
|
-
for (let i = 0; i < arr.length; i++) {
|
|
1953
|
-
const row = arr[i];
|
|
1954
|
-
const val = j < row.length ? row[j] : null;
|
|
1955
|
-
newRow.push(val);
|
|
1956
|
-
}
|
|
1957
|
-
result.push(newRow);
|
|
1958
|
-
}
|
|
1959
|
-
yield emit$1(result, span, tracker);
|
|
1960
|
-
}
|
|
1961
|
-
},
|
|
1962
|
-
{
|
|
1963
|
-
name: "bsearch",
|
|
1964
|
-
arity: 1,
|
|
1965
|
-
apply: function* (input, args, env, tracker, evaluate$1, span) {
|
|
1966
|
-
if (!Array.isArray(input)) throw new RuntimeError("bsearch expects an array", span);
|
|
1967
|
-
const targetGen = evaluate$1(args[0], input, env, tracker);
|
|
1968
|
-
for (const target of targetGen) {
|
|
1969
|
-
let low = 0;
|
|
1970
|
-
let high = input.length - 1;
|
|
1971
|
-
let idx = -1;
|
|
1972
|
-
while (low <= high) {
|
|
1973
|
-
const mid = Math.floor((low + high) / 2);
|
|
1974
|
-
const cmp = compareValues(input[mid], target);
|
|
1975
|
-
if (cmp === 0) {
|
|
1976
|
-
idx = mid;
|
|
1977
|
-
break;
|
|
1978
|
-
} else if (cmp < 0) low = mid + 1;
|
|
1979
|
-
else high = mid - 1;
|
|
1980
|
-
}
|
|
1981
|
-
if (idx !== -1) yield emit$1(idx, span, tracker);
|
|
1982
|
-
else yield emit$1(-low - 1, span, tracker);
|
|
1983
|
-
}
|
|
1984
|
-
}
|
|
1985
|
-
},
|
|
1986
|
-
{
|
|
1987
|
-
name: "combinations",
|
|
1988
|
-
arity: 0,
|
|
1989
|
-
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
1990
|
-
if (!Array.isArray(input)) throw new RuntimeError("combinations expects an array", span);
|
|
1991
|
-
if (input.some((x) => !Array.isArray(x))) throw new RuntimeError("combinations input must be array of arrays", span);
|
|
1992
|
-
const arrays = input;
|
|
1993
|
-
if (arrays.length === 0) {
|
|
1994
|
-
yield emit$1([], span, tracker);
|
|
1995
|
-
return;
|
|
1996
|
-
}
|
|
1997
|
-
const helper = function* (idx, current) {
|
|
1998
|
-
if (idx === arrays.length) {
|
|
1999
|
-
yield [...current];
|
|
2000
|
-
return;
|
|
2001
|
-
}
|
|
2002
|
-
const arr = arrays[idx];
|
|
2003
|
-
if (arr.length === 0) return;
|
|
2004
|
-
for (const item of arr) {
|
|
2005
|
-
current.push(item);
|
|
2006
|
-
yield* helper(idx + 1, current);
|
|
2007
|
-
current.pop();
|
|
2008
|
-
}
|
|
2009
|
-
};
|
|
2010
|
-
for (const combo of helper(0, [])) yield emit$1(combo, span, tracker);
|
|
2011
|
-
}
|
|
2012
|
-
},
|
|
2013
|
-
{
|
|
2014
|
-
name: "combinations",
|
|
2015
|
-
arity: 1,
|
|
2016
|
-
apply: function* (input, args, env, tracker, evaluate$1, span) {
|
|
2017
|
-
if (!Array.isArray(input)) throw new RuntimeError("combinations expects an array", span);
|
|
2018
|
-
const nGen = evaluate$1(args[0], input, env, tracker);
|
|
2019
|
-
for (const nVal of nGen) {
|
|
2020
|
-
if (typeof nVal !== "number") throw new RuntimeError("combinations(n) expects n to be number", span);
|
|
2021
|
-
if (nVal === 0) {
|
|
2022
|
-
yield emit$1([], span, tracker);
|
|
2023
|
-
continue;
|
|
2024
|
-
}
|
|
2025
|
-
const arrays = [];
|
|
2026
|
-
for (let i = 0; i < nVal; i++) arrays.push(input);
|
|
2027
|
-
const helper = function* (idx, current) {
|
|
2028
|
-
if (idx === arrays.length) {
|
|
2029
|
-
yield [...current];
|
|
2030
|
-
return;
|
|
2031
|
-
}
|
|
2032
|
-
const arr = arrays[idx];
|
|
2033
|
-
for (const item of arr) {
|
|
2034
|
-
current.push(item);
|
|
2035
|
-
yield* helper(idx + 1, current);
|
|
2036
|
-
current.pop();
|
|
2037
|
-
}
|
|
2038
|
-
};
|
|
2039
|
-
if (input.length === 0 && nVal > 0) {} else for (const combo of helper(0, [])) yield emit$1(combo, span, tracker);
|
|
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);
|
|
2040
1856
|
}
|
|
2041
1857
|
}
|
|
2042
|
-
},
|
|
2043
|
-
{
|
|
2044
|
-
name: "inside",
|
|
2045
|
-
arity: 1,
|
|
2046
|
-
apply: function* (input, args, env, tracker, evaluate$1, span) {
|
|
2047
|
-
const bGen = evaluate$1(args[0], input, env, tracker);
|
|
2048
|
-
for (const b of bGen) yield emit$1(checkContains(b, input), span, tracker);
|
|
2049
|
-
}
|
|
2050
1858
|
}
|
|
2051
1859
|
];
|
|
2052
|
-
|
|
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
|
+
};
|
|
2053
1871
|
//#endregion
|
|
2054
1872
|
//#region src/eval/path_eval.ts
|
|
2055
1873
|
/**
|
|
@@ -2062,17 +1880,17 @@ const collectionBuiltins = [
|
|
|
2062
1880
|
* @param tracker - Limits tracker.
|
|
2063
1881
|
* @param evaluate - Recursive evaluator.
|
|
2064
1882
|
*/
|
|
2065
|
-
const evaluatePath = function* (node, input, env, tracker, evaluate
|
|
1883
|
+
const evaluatePath = function* (node, input, env, tracker, evaluate) {
|
|
2066
1884
|
switch (node.kind) {
|
|
2067
1885
|
case "Identity":
|
|
2068
1886
|
yield [];
|
|
2069
1887
|
return;
|
|
2070
1888
|
case "FieldAccess":
|
|
2071
|
-
for (const parent of evaluatePath(node.target, input, env, tracker, evaluate
|
|
1889
|
+
for (const parent of evaluatePath(node.target, input, env, tracker, evaluate)) yield [...parent, node.field];
|
|
2072
1890
|
return;
|
|
2073
1891
|
case "IndexAccess": {
|
|
2074
|
-
const parentPaths = Array.from(evaluatePath(node.target, input, env, tracker, evaluate
|
|
2075
|
-
const output = evaluate
|
|
1892
|
+
const parentPaths = Array.from(evaluatePath(node.target, input, env, tracker, evaluate));
|
|
1893
|
+
const output = evaluate(node.index, input, env, tracker);
|
|
2076
1894
|
for (const keyVal of output) {
|
|
2077
1895
|
let key;
|
|
2078
1896
|
if (typeof keyVal === "string") key = keyVal;
|
|
@@ -2083,19 +1901,19 @@ const evaluatePath = function* (node, input, env, tracker, evaluate$1) {
|
|
|
2083
1901
|
return;
|
|
2084
1902
|
}
|
|
2085
1903
|
case "Pipe": {
|
|
2086
|
-
const leftPaths = Array.from(evaluatePath(node.left, input, env, tracker, evaluate
|
|
1904
|
+
const leftPaths = Array.from(evaluatePath(node.left, input, env, tracker, evaluate));
|
|
2087
1905
|
for (const p of leftPaths) {
|
|
2088
1906
|
const val = getPath(input, p) ?? null;
|
|
2089
|
-
for (const q of evaluatePath(node.right, val, env, tracker, evaluate
|
|
1907
|
+
for (const q of evaluatePath(node.right, val, env, tracker, evaluate)) yield [...p, ...q];
|
|
2090
1908
|
}
|
|
2091
1909
|
return;
|
|
2092
1910
|
}
|
|
2093
1911
|
case "Comma":
|
|
2094
|
-
yield* evaluatePath(node.left, input, env, tracker, evaluate
|
|
2095
|
-
yield* evaluatePath(node.right, input, env, tracker, evaluate
|
|
1912
|
+
yield* evaluatePath(node.left, input, env, tracker, evaluate);
|
|
1913
|
+
yield* evaluatePath(node.right, input, env, tracker, evaluate);
|
|
2096
1914
|
return;
|
|
2097
1915
|
case "Iterate": {
|
|
2098
|
-
const parentPaths = Array.from(evaluatePath(node.target, input, env, tracker, evaluate
|
|
1916
|
+
const parentPaths = Array.from(evaluatePath(node.target, input, env, tracker, evaluate));
|
|
2099
1917
|
for (const p of parentPaths) {
|
|
2100
1918
|
const val = getPath(input, p);
|
|
2101
1919
|
if (Array.isArray(val)) for (let i = 0; i < val.length; i++) yield [...p, i];
|
|
@@ -2109,7 +1927,7 @@ const evaluatePath = function* (node, input, env, tracker, evaluate$1) {
|
|
|
2109
1927
|
}
|
|
2110
1928
|
case "Call":
|
|
2111
1929
|
if (node.name === "select") {
|
|
2112
|
-
const conds = evaluate
|
|
1930
|
+
const conds = evaluate(node.args[0], input, env, tracker);
|
|
2113
1931
|
let matched = false;
|
|
2114
1932
|
for (const c of conds) if (c !== null && c !== false) matched = true;
|
|
2115
1933
|
if (matched) yield [];
|
|
@@ -2117,9 +1935,9 @@ const evaluatePath = function* (node, input, env, tracker, evaluate$1) {
|
|
|
2117
1935
|
}
|
|
2118
1936
|
throw new RuntimeError(`Function ${node.name} not supported in path expression`, node.span);
|
|
2119
1937
|
case "Slice": {
|
|
2120
|
-
const parentPaths = Array.from(evaluatePath(node.target, input, env, tracker, evaluate
|
|
2121
|
-
const startRes = node.start ? Array.from(evaluate
|
|
2122
|
-
const endRes = node.end ? Array.from(evaluate
|
|
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];
|
|
2123
1941
|
for (const startVal of startRes) for (const endVal of endRes) {
|
|
2124
1942
|
const sliceSpec = {
|
|
2125
1943
|
start: typeof startVal === "number" ? startVal : null,
|
|
@@ -2131,7 +1949,7 @@ const evaluatePath = function* (node, input, env, tracker, evaluate$1) {
|
|
|
2131
1949
|
}
|
|
2132
1950
|
case "Try":
|
|
2133
1951
|
try {
|
|
2134
|
-
yield* evaluatePath(node.body, input, env, tracker, evaluate
|
|
1952
|
+
yield* evaluatePath(node.body, input, env, tracker, evaluate);
|
|
2135
1953
|
} catch (e) {
|
|
2136
1954
|
if (!(e instanceof RuntimeError)) throw e;
|
|
2137
1955
|
}
|
|
@@ -2142,24 +1960,27 @@ const evaluatePath = function* (node, input, env, tracker, evaluate$1) {
|
|
|
2142
1960
|
default: throw new RuntimeError("Invalid path expression", node.span);
|
|
2143
1961
|
}
|
|
2144
1962
|
};
|
|
2145
|
-
|
|
2146
1963
|
//#endregion
|
|
2147
1964
|
//#region src/builtins/paths.ts
|
|
2148
1965
|
function* traversePaths(root, currentPath, span, tracker) {
|
|
2149
1966
|
tracker.step(span);
|
|
2150
|
-
if (
|
|
2151
|
-
|
|
2152
|
-
|
|
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);
|
|
2153
1971
|
}
|
|
2154
|
-
if (
|
|
2155
|
-
else if (isPlainObject(root)) {
|
|
1972
|
+
else if (isPlainObject$1(root)) {
|
|
2156
1973
|
const keys = Object.keys(root).sort();
|
|
2157
|
-
for (const key of keys)
|
|
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
|
+
}
|
|
2158
1979
|
}
|
|
2159
1980
|
}
|
|
2160
1981
|
const isPath = (val) => {
|
|
2161
1982
|
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));
|
|
1983
|
+
return val.every((p) => typeof p === "string" || typeof p === "number" && Number.isInteger(p) || isPlainObject$1(p) && ("start" in p || "end" in p));
|
|
2163
1984
|
};
|
|
2164
1985
|
const ensurePath = (val, span) => {
|
|
2165
1986
|
if (isPath(val)) return val;
|
|
@@ -2169,7 +1990,7 @@ const getPath = (root, path) => {
|
|
|
2169
1990
|
let curr = root;
|
|
2170
1991
|
for (const part of path) {
|
|
2171
1992
|
if (curr === null) return void 0;
|
|
2172
|
-
if (typeof part === "string" && isPlainObject(curr)) {
|
|
1993
|
+
if (typeof part === "string" && isPlainObject$1(curr)) {
|
|
2173
1994
|
if (!Object.prototype.hasOwnProperty.call(curr, part)) return void 0;
|
|
2174
1995
|
curr = curr[part];
|
|
2175
1996
|
} else if (typeof part === "number" && Array.isArray(curr)) {
|
|
@@ -2184,7 +2005,7 @@ const updatePath = (root, path, updateFn, span, depth = 0) => {
|
|
|
2184
2005
|
const [head, ...tail] = path;
|
|
2185
2006
|
if (typeof head === "string") {
|
|
2186
2007
|
let obj = {};
|
|
2187
|
-
if (isPlainObject(root)) obj = { ...root };
|
|
2008
|
+
if (isPlainObject$1(root)) obj = { ...root };
|
|
2188
2009
|
else if (root === null) obj = {};
|
|
2189
2010
|
else throw new RuntimeError(`Cannot index ${describeType(root)} with string "${head}"`, span);
|
|
2190
2011
|
const newVal = updatePath((Object.prototype.hasOwnProperty.call(obj, head) ? obj[head] : void 0) ?? null, tail, updateFn, span, depth + 1);
|
|
@@ -2224,7 +2045,7 @@ const updatePath = (root, path, updateFn, span, depth = 0) => {
|
|
|
2224
2045
|
};
|
|
2225
2046
|
const deletePaths = (root, paths, span) => {
|
|
2226
2047
|
if (paths.some((p) => p.length === 0)) return null;
|
|
2227
|
-
if (isPlainObject(root)) {
|
|
2048
|
+
if (isPlainObject$1(root)) {
|
|
2228
2049
|
const result = { ...root };
|
|
2229
2050
|
const relevantPaths = paths.filter((p) => p.length > 0 && typeof p[0] === "string");
|
|
2230
2051
|
const byKey = {};
|
|
@@ -2269,19 +2090,35 @@ const pathBuiltins = [
|
|
|
2269
2090
|
yield* traversePaths(input, [], span, tracker);
|
|
2270
2091
|
}
|
|
2271
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
|
+
},
|
|
2272
2109
|
{
|
|
2273
2110
|
name: "getpath",
|
|
2274
2111
|
arity: 1,
|
|
2275
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2276
|
-
for (const pathVal of evaluate
|
|
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);
|
|
2277
2114
|
}
|
|
2278
2115
|
},
|
|
2279
2116
|
{
|
|
2280
2117
|
name: "setpath",
|
|
2281
2118
|
arity: 2,
|
|
2282
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2283
|
-
const paths = Array.from(evaluate
|
|
2284
|
-
const values = Array.from(evaluate
|
|
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));
|
|
2285
2122
|
for (const pathVal of paths) {
|
|
2286
2123
|
const path = ensurePath(pathVal, span);
|
|
2287
2124
|
for (const val of values) yield emit$1(updatePath(input, path, () => val, span) ?? null, span, tracker);
|
|
@@ -2291,8 +2128,8 @@ const pathBuiltins = [
|
|
|
2291
2128
|
{
|
|
2292
2129
|
name: "delpaths",
|
|
2293
2130
|
arity: 1,
|
|
2294
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2295
|
-
for (const pathsVal of evaluate
|
|
2131
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2132
|
+
for (const pathsVal of evaluate(args[0], input, env, tracker)) {
|
|
2296
2133
|
if (!Array.isArray(pathsVal)) throw new RuntimeError("delpaths expects an array of paths", span);
|
|
2297
2134
|
yield emit$1(deletePaths(input, pathsVal.map((p) => ensurePath(p, span)), span), span, tracker);
|
|
2298
2135
|
}
|
|
@@ -2301,210 +2138,821 @@ const pathBuiltins = [
|
|
|
2301
2138
|
{
|
|
2302
2139
|
name: "path",
|
|
2303
2140
|
arity: 1,
|
|
2304
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2305
|
-
for (const p of evaluatePath(args[0], input, env, tracker, evaluate
|
|
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);
|
|
2306
2143
|
}
|
|
2307
2144
|
}
|
|
2308
2145
|
];
|
|
2309
|
-
|
|
2310
2146
|
//#endregion
|
|
2311
|
-
//#region src/builtins/
|
|
2312
|
-
|
|
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 = [
|
|
2313
2158
|
{
|
|
2314
|
-
name: "
|
|
2315
|
-
arity:
|
|
2316
|
-
apply: function* (input,
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
for (let i = 0; i < end; i++) yield emit$1(i, span, tracker);
|
|
2321
|
-
}
|
|
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);
|
|
2322
2165
|
}
|
|
2323
2166
|
},
|
|
2324
2167
|
{
|
|
2325
|
-
name: "
|
|
2326
|
-
arity:
|
|
2327
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2328
|
-
const
|
|
2329
|
-
const
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
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);
|
|
2334
2182
|
}
|
|
2335
2183
|
},
|
|
2336
2184
|
{
|
|
2337
|
-
name: "
|
|
2338
|
-
arity:
|
|
2339
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2340
|
-
|
|
2341
|
-
const
|
|
2342
|
-
const
|
|
2343
|
-
for (const
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
if (step > 0) for (let i = start; i < end; i += step) yield emit$1(i, span, tracker);
|
|
2347
|
-
else for (let i = start; i > end; i += step) yield emit$1(i, span, tracker);
|
|
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);
|
|
2348
2194
|
}
|
|
2195
|
+
yield emit$1(result, span, tracker);
|
|
2349
2196
|
}
|
|
2350
2197
|
},
|
|
2351
2198
|
{
|
|
2352
|
-
name: "
|
|
2353
|
-
arity:
|
|
2354
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2355
|
-
const
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
count++;
|
|
2362
|
-
if (count >= n) break;
|
|
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);
|
|
2363
2208
|
}
|
|
2209
|
+
yield emit$1(result, span, tracker);
|
|
2210
|
+
return;
|
|
2364
2211
|
}
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
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;
|
|
2374
2226
|
}
|
|
2227
|
+
throw new RuntimeError("map_values expects an array or object", span);
|
|
2375
2228
|
}
|
|
2376
2229
|
},
|
|
2377
2230
|
{
|
|
2378
|
-
name: "
|
|
2231
|
+
name: "select",
|
|
2379
2232
|
arity: 1,
|
|
2380
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
found = true;
|
|
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;
|
|
2386
2238
|
}
|
|
2387
|
-
if (found) yield lastVal;
|
|
2388
2239
|
}
|
|
2389
2240
|
},
|
|
2390
2241
|
{
|
|
2391
|
-
name: "
|
|
2392
|
-
arity:
|
|
2393
|
-
apply: function* (input,
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
for (const val of evaluate$1(args[1], input, env, tracker)) {
|
|
2399
|
-
if (count === n) {
|
|
2400
|
-
yield val;
|
|
2401
|
-
break;
|
|
2402
|
-
}
|
|
2403
|
-
count++;
|
|
2404
|
-
}
|
|
2405
|
-
}
|
|
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);
|
|
2406
2249
|
}
|
|
2407
2250
|
},
|
|
2408
2251
|
{
|
|
2409
|
-
name: "
|
|
2252
|
+
name: "sort_by",
|
|
2410
2253
|
arity: 1,
|
|
2411
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
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
|
+
});
|
|
2416
2266
|
}
|
|
2417
|
-
yield emit$1(
|
|
2267
|
+
yield emit$1(sortStable(pairs, (a, b) => compareValues(a.key, b.key)).map((p) => p.val), span, tracker);
|
|
2418
2268
|
}
|
|
2419
2269
|
},
|
|
2420
2270
|
{
|
|
2421
|
-
name: "
|
|
2422
|
-
arity:
|
|
2423
|
-
apply: function* (input,
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
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
|
+
}
|
|
2428
2283
|
}
|
|
2429
2284
|
yield emit$1(result, span, tracker);
|
|
2430
2285
|
}
|
|
2431
2286
|
},
|
|
2432
2287
|
{
|
|
2433
|
-
name: "
|
|
2288
|
+
name: "unique_by",
|
|
2434
2289
|
arity: 1,
|
|
2435
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
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
|
+
}
|
|
2440
2304
|
}
|
|
2441
2305
|
yield emit$1(result, span, tracker);
|
|
2442
2306
|
}
|
|
2443
2307
|
},
|
|
2444
2308
|
{
|
|
2445
|
-
name: "
|
|
2446
|
-
arity:
|
|
2447
|
-
apply: function* (input,
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
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);
|
|
2454
2321
|
}
|
|
2455
2322
|
},
|
|
2456
2323
|
{
|
|
2457
|
-
name: "
|
|
2458
|
-
arity:
|
|
2459
|
-
apply: function* (input,
|
|
2460
|
-
|
|
2461
|
-
const
|
|
2462
|
-
const
|
|
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) {
|
|
2463
2330
|
tracker.step(span);
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
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);
|
|
2341
|
+
if (typeof key !== "string") throw new RuntimeError("from_entries object keys must be strings", span);
|
|
2342
|
+
result[key] = value;
|
|
2343
|
+
}
|
|
2344
|
+
yield emit$1(result, span, tracker);
|
|
2475
2345
|
}
|
|
2476
2346
|
},
|
|
2477
2347
|
{
|
|
2478
|
-
name: "
|
|
2479
|
-
arity:
|
|
2480
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2348
|
+
name: "with_entries",
|
|
2349
|
+
arity: 1,
|
|
2350
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2351
|
+
let entries;
|
|
2352
|
+
if (Array.isArray(input)) entries = input.map((v, i) => ({
|
|
2353
|
+
key: i,
|
|
2354
|
+
value: v
|
|
2355
|
+
}));
|
|
2356
|
+
else if (input !== null && typeof input === "object") entries = Object.keys(input).sort().map((k) => ({
|
|
2357
|
+
key: k,
|
|
2358
|
+
value: input[k]
|
|
2359
|
+
}));
|
|
2360
|
+
else throw new RuntimeError("with_entries expects array or object", span);
|
|
2361
|
+
const transformed = [];
|
|
2362
|
+
const filter = args[0];
|
|
2363
|
+
for (const entry of entries) {
|
|
2484
2364
|
tracker.step(span);
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2365
|
+
for (const outVar of evaluate(filter, entry, env, tracker)) transformed.push(outVar);
|
|
2366
|
+
}
|
|
2367
|
+
const result = {};
|
|
2368
|
+
for (const item of transformed) {
|
|
2369
|
+
if (item === null || typeof item !== "object" || Array.isArray(item)) throw new RuntimeError("with_entries filter must produce objects", span);
|
|
2370
|
+
const obj = item;
|
|
2371
|
+
if (!("key" in obj) || !("value" in obj)) throw new RuntimeError("with_entries items must have \"key\" and \"value\"", span);
|
|
2372
|
+
const key = obj["key"];
|
|
2373
|
+
if (typeof key !== "string") throw new RuntimeError("with_entries keys must be strings", span);
|
|
2374
|
+
result[key] = obj["value"];
|
|
2375
|
+
}
|
|
2376
|
+
yield emit$1(result, span, tracker);
|
|
2494
2377
|
}
|
|
2495
2378
|
},
|
|
2496
2379
|
{
|
|
2497
|
-
name: "
|
|
2380
|
+
name: "group_by",
|
|
2498
2381
|
arity: 1,
|
|
2499
|
-
apply: function* (input, args, env, tracker, evaluate
|
|
2500
|
-
|
|
2382
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2383
|
+
if (!Array.isArray(input)) throw new RuntimeError("group_by expects an array", span);
|
|
2384
|
+
const pairs = [];
|
|
2385
|
+
const filter = args[0];
|
|
2386
|
+
for (const item of input) {
|
|
2501
2387
|
tracker.step(span);
|
|
2502
|
-
|
|
2388
|
+
const keys = Array.from(evaluate(filter, item, env, tracker));
|
|
2389
|
+
if (keys.length !== 1) throw new RuntimeError("group_by key expression must return exactly one value", span);
|
|
2390
|
+
pairs.push({
|
|
2391
|
+
val: item,
|
|
2392
|
+
key: keys[0]
|
|
2393
|
+
});
|
|
2394
|
+
}
|
|
2395
|
+
const sorted = sortStable(pairs, (a, b) => compareValues(a.key, b.key));
|
|
2396
|
+
const groups = [];
|
|
2397
|
+
if (sorted.length > 0) {
|
|
2398
|
+
let currentGroup = [sorted[0].val];
|
|
2399
|
+
let currentKey = sorted[0].key;
|
|
2400
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
2401
|
+
const pair = sorted[i];
|
|
2402
|
+
if (compareValues(pair.key, currentKey) === 0) currentGroup.push(pair.val);
|
|
2403
|
+
else {
|
|
2404
|
+
groups.push(currentGroup);
|
|
2405
|
+
currentGroup = [pair.val];
|
|
2406
|
+
currentKey = pair.key;
|
|
2407
|
+
}
|
|
2408
|
+
}
|
|
2409
|
+
groups.push(currentGroup);
|
|
2503
2410
|
}
|
|
2411
|
+
yield emit$1(groups, span, tracker);
|
|
2504
2412
|
}
|
|
2505
|
-
}
|
|
2413
|
+
},
|
|
2414
|
+
{
|
|
2415
|
+
name: "reverse",
|
|
2416
|
+
arity: 0,
|
|
2417
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
2418
|
+
if (!Array.isArray(input)) throw new RuntimeError("reverse expects an array", span);
|
|
2419
|
+
yield emit$1([...input].reverse(), span, tracker);
|
|
2420
|
+
}
|
|
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
|
+
},
|
|
2462
|
+
{
|
|
2463
|
+
name: "flatten",
|
|
2464
|
+
arity: 0,
|
|
2465
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
2466
|
+
if (!Array.isArray(input)) throw new RuntimeError("flatten expects an array", span);
|
|
2467
|
+
const flattenRec = (arr) => {
|
|
2468
|
+
let res = [];
|
|
2469
|
+
for (const item of arr) if (Array.isArray(item)) res = res.concat(flattenRec(item));
|
|
2470
|
+
else res.push(item);
|
|
2471
|
+
return res;
|
|
2472
|
+
};
|
|
2473
|
+
yield emit$1(flattenRec(input), span, tracker);
|
|
2474
|
+
}
|
|
2475
|
+
},
|
|
2476
|
+
{
|
|
2477
|
+
name: "flatten",
|
|
2478
|
+
arity: 1,
|
|
2479
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2480
|
+
if (!Array.isArray(input)) throw new RuntimeError("flatten expects an array", span);
|
|
2481
|
+
const depths = evaluate(args[0], input, env, tracker);
|
|
2482
|
+
for (const depthVal of depths) {
|
|
2483
|
+
if (typeof depthVal !== "number") throw new RuntimeError("flatten depth must be a number", span);
|
|
2484
|
+
const flattenDepth = (arr, d) => {
|
|
2485
|
+
if (d <= 0) return arr;
|
|
2486
|
+
let res = [];
|
|
2487
|
+
for (const item of arr) if (Array.isArray(item)) res = res.concat(flattenDepth(item, d - 1));
|
|
2488
|
+
else res.push(item);
|
|
2489
|
+
return res;
|
|
2490
|
+
};
|
|
2491
|
+
yield emit$1(flattenDepth(input, depthVal), span, tracker);
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
},
|
|
2495
|
+
{
|
|
2496
|
+
name: "keys_unsorted",
|
|
2497
|
+
arity: 0,
|
|
2498
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
2499
|
+
if (Array.isArray(input)) yield emit$1(Array.from({ length: input.length }, (_, i) => i), span, tracker);
|
|
2500
|
+
else if (input !== null && typeof input === "object") yield emit$1(Object.keys(input), span, tracker);
|
|
2501
|
+
else throw new RuntimeError(`keys_unsorted expects an array or object`, span);
|
|
2502
|
+
}
|
|
2503
|
+
},
|
|
2504
|
+
{
|
|
2505
|
+
name: "transpose",
|
|
2506
|
+
arity: 0,
|
|
2507
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
2508
|
+
if (!Array.isArray(input)) throw new RuntimeError("transpose expects an array", span);
|
|
2509
|
+
const arr = input;
|
|
2510
|
+
if (arr.length === 0) {
|
|
2511
|
+
yield emit$1([], span, tracker);
|
|
2512
|
+
return;
|
|
2513
|
+
}
|
|
2514
|
+
let maxLen = 0;
|
|
2515
|
+
for (const row of arr) {
|
|
2516
|
+
if (!Array.isArray(row)) throw new RuntimeError("transpose input must be array of arrays", span);
|
|
2517
|
+
if (row.length > maxLen) maxLen = row.length;
|
|
2518
|
+
}
|
|
2519
|
+
const result = [];
|
|
2520
|
+
for (let j = 0; j < maxLen; j++) {
|
|
2521
|
+
const newRow = [];
|
|
2522
|
+
for (let i = 0; i < arr.length; i++) {
|
|
2523
|
+
const row = arr[i];
|
|
2524
|
+
const val = j < row.length ? row[j] : null;
|
|
2525
|
+
newRow.push(val);
|
|
2526
|
+
}
|
|
2527
|
+
result.push(newRow);
|
|
2528
|
+
}
|
|
2529
|
+
yield emit$1(result, span, tracker);
|
|
2530
|
+
}
|
|
2531
|
+
},
|
|
2532
|
+
{
|
|
2533
|
+
name: "bsearch",
|
|
2534
|
+
arity: 1,
|
|
2535
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2536
|
+
if (!Array.isArray(input)) throw new RuntimeError("bsearch expects an array", span);
|
|
2537
|
+
const targetGen = evaluate(args[0], input, env, tracker);
|
|
2538
|
+
for (const target of targetGen) {
|
|
2539
|
+
let low = 0;
|
|
2540
|
+
let high = input.length - 1;
|
|
2541
|
+
let idx = -1;
|
|
2542
|
+
while (low <= high) {
|
|
2543
|
+
const mid = Math.floor((low + high) / 2);
|
|
2544
|
+
const cmp = compareValues(input[mid], target);
|
|
2545
|
+
if (cmp === 0) {
|
|
2546
|
+
idx = mid;
|
|
2547
|
+
break;
|
|
2548
|
+
} else if (cmp < 0) low = mid + 1;
|
|
2549
|
+
else high = mid - 1;
|
|
2550
|
+
}
|
|
2551
|
+
if (idx !== -1) yield emit$1(idx, span, tracker);
|
|
2552
|
+
else yield emit$1(-low - 1, span, tracker);
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
},
|
|
2556
|
+
{
|
|
2557
|
+
name: "combinations",
|
|
2558
|
+
arity: 0,
|
|
2559
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
2560
|
+
if (!Array.isArray(input)) throw new RuntimeError("combinations expects an array", span);
|
|
2561
|
+
if (input.some((x) => !Array.isArray(x))) throw new RuntimeError("combinations input must be array of arrays", span);
|
|
2562
|
+
const arrays = input;
|
|
2563
|
+
if (arrays.length === 0) {
|
|
2564
|
+
yield emit$1([], span, tracker);
|
|
2565
|
+
return;
|
|
2566
|
+
}
|
|
2567
|
+
const helper = function* (idx, current) {
|
|
2568
|
+
if (idx === arrays.length) {
|
|
2569
|
+
yield [...current];
|
|
2570
|
+
return;
|
|
2571
|
+
}
|
|
2572
|
+
const arr = arrays[idx];
|
|
2573
|
+
if (arr.length === 0) return;
|
|
2574
|
+
for (const item of arr) {
|
|
2575
|
+
current.push(item);
|
|
2576
|
+
yield* helper(idx + 1, current);
|
|
2577
|
+
current.pop();
|
|
2578
|
+
}
|
|
2579
|
+
};
|
|
2580
|
+
for (const combo of helper(0, [])) yield emit$1(combo, span, tracker);
|
|
2581
|
+
}
|
|
2582
|
+
},
|
|
2583
|
+
{
|
|
2584
|
+
name: "combinations",
|
|
2585
|
+
arity: 1,
|
|
2586
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2587
|
+
if (!Array.isArray(input)) throw new RuntimeError("combinations expects an array", span);
|
|
2588
|
+
const nGen = evaluate(args[0], input, env, tracker);
|
|
2589
|
+
for (const nVal of nGen) {
|
|
2590
|
+
if (typeof nVal !== "number") throw new RuntimeError("combinations(n) expects n to be number", span);
|
|
2591
|
+
if (nVal === 0) {
|
|
2592
|
+
yield emit$1([], span, tracker);
|
|
2593
|
+
continue;
|
|
2594
|
+
}
|
|
2595
|
+
const arrays = [];
|
|
2596
|
+
for (let i = 0; i < nVal; i++) arrays.push(input);
|
|
2597
|
+
const helper = function* (idx, current) {
|
|
2598
|
+
if (idx === arrays.length) {
|
|
2599
|
+
yield [...current];
|
|
2600
|
+
return;
|
|
2601
|
+
}
|
|
2602
|
+
const arr = arrays[idx];
|
|
2603
|
+
for (const item of arr) {
|
|
2604
|
+
current.push(item);
|
|
2605
|
+
yield* helper(idx + 1, current);
|
|
2606
|
+
current.pop();
|
|
2607
|
+
}
|
|
2608
|
+
};
|
|
2609
|
+
if (input.length === 0 && nVal > 0) {} else for (const combo of helper(0, [])) yield emit$1(combo, span, tracker);
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2612
|
+
},
|
|
2613
|
+
{
|
|
2614
|
+
name: "inside",
|
|
2615
|
+
arity: 1,
|
|
2616
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2617
|
+
const bGen = evaluate(args[0], input, env, tracker);
|
|
2618
|
+
for (const b of bGen) yield emit$1(checkContains(b, input), span, tracker);
|
|
2619
|
+
}
|
|
2620
|
+
}
|
|
2621
|
+
];
|
|
2622
|
+
const getEntryField = (obj, names) => {
|
|
2623
|
+
for (const name of names) if (Object.prototype.hasOwnProperty.call(obj, name)) return obj[name];
|
|
2624
|
+
};
|
|
2625
|
+
//#endregion
|
|
2626
|
+
//#region src/builtins/iterators.ts
|
|
2627
|
+
const iteratorBuiltins = [
|
|
2628
|
+
{
|
|
2629
|
+
name: "range",
|
|
2630
|
+
arity: 1,
|
|
2631
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2632
|
+
const ends = evaluate(args[0], input, env, tracker);
|
|
2633
|
+
for (const end of ends) {
|
|
2634
|
+
if (typeof end !== "number") throw new RuntimeError("range expects numbers", span);
|
|
2635
|
+
for (let i = 0; i < end; i++) yield emit$1(i, span, tracker);
|
|
2636
|
+
}
|
|
2637
|
+
}
|
|
2638
|
+
},
|
|
2639
|
+
{
|
|
2640
|
+
name: "range",
|
|
2641
|
+
arity: 2,
|
|
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));
|
|
2645
|
+
for (const start of starts) for (const end of ends) {
|
|
2646
|
+
if (typeof start !== "number" || typeof end !== "number") throw new RuntimeError("range expects numbers", span);
|
|
2647
|
+
if (start < end) for (let i = start; i < end; i++) yield emit$1(i, span, tracker);
|
|
2648
|
+
}
|
|
2649
|
+
}
|
|
2650
|
+
},
|
|
2651
|
+
{
|
|
2652
|
+
name: "range",
|
|
2653
|
+
arity: 3,
|
|
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));
|
|
2658
|
+
for (const start of starts) for (const end of ends) for (const step of steps) {
|
|
2659
|
+
if (typeof start !== "number" || typeof end !== "number" || typeof step !== "number") throw new RuntimeError("range expects numbers", span);
|
|
2660
|
+
if (step === 0) throw new RuntimeError("range step cannot be zero", span);
|
|
2661
|
+
if (step > 0) for (let i = start; i < end; i += step) yield emit$1(i, span, tracker);
|
|
2662
|
+
else for (let i = start; i > end; i += step) yield emit$1(i, span, tracker);
|
|
2663
|
+
}
|
|
2664
|
+
}
|
|
2665
|
+
},
|
|
2666
|
+
{
|
|
2667
|
+
name: "limit",
|
|
2668
|
+
arity: 2,
|
|
2669
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2670
|
+
const limits = evaluate(args[0], input, env, tracker);
|
|
2671
|
+
for (const n of limits) {
|
|
2672
|
+
if (typeof n !== "number") throw new RuntimeError("limit expects number", span);
|
|
2673
|
+
let count = 0;
|
|
2674
|
+
if (n > 0) for (const val of evaluate(args[1], input, env, tracker)) {
|
|
2675
|
+
yield val;
|
|
2676
|
+
count++;
|
|
2677
|
+
if (count >= n) break;
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
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
|
+
},
|
|
2703
|
+
{
|
|
2704
|
+
name: "first",
|
|
2705
|
+
arity: 1,
|
|
2706
|
+
apply: function* (input, args, env, tracker, evaluate) {
|
|
2707
|
+
for (const val of evaluate(args[0], input, env, tracker)) {
|
|
2708
|
+
yield val;
|
|
2709
|
+
break;
|
|
2710
|
+
}
|
|
2711
|
+
}
|
|
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
|
+
},
|
|
2720
|
+
{
|
|
2721
|
+
name: "last",
|
|
2722
|
+
arity: 1,
|
|
2723
|
+
apply: function* (input, args, env, tracker, evaluate) {
|
|
2724
|
+
let lastVal;
|
|
2725
|
+
let found = false;
|
|
2726
|
+
for (const val of evaluate(args[0], input, env, tracker)) {
|
|
2727
|
+
lastVal = val;
|
|
2728
|
+
found = true;
|
|
2729
|
+
}
|
|
2730
|
+
if (found) yield lastVal;
|
|
2731
|
+
}
|
|
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
|
+
},
|
|
2745
|
+
{
|
|
2746
|
+
name: "nth",
|
|
2747
|
+
arity: 2,
|
|
2748
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2749
|
+
const indices = evaluate(args[0], input, env, tracker);
|
|
2750
|
+
for (const n of indices) {
|
|
2751
|
+
if (typeof n !== "number") throw new RuntimeError("nth expects number", span);
|
|
2752
|
+
let count = 0;
|
|
2753
|
+
for (const val of evaluate(args[1], input, env, tracker)) {
|
|
2754
|
+
if (count === n) {
|
|
2755
|
+
yield val;
|
|
2756
|
+
break;
|
|
2757
|
+
}
|
|
2758
|
+
count++;
|
|
2759
|
+
}
|
|
2760
|
+
}
|
|
2761
|
+
}
|
|
2762
|
+
},
|
|
2763
|
+
{
|
|
2764
|
+
name: "isempty",
|
|
2765
|
+
arity: 1,
|
|
2766
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2767
|
+
let empty = true;
|
|
2768
|
+
for (const _ of evaluate(args[0], input, env, tracker)) {
|
|
2769
|
+
empty = false;
|
|
2770
|
+
break;
|
|
2771
|
+
}
|
|
2772
|
+
yield emit$1(empty, span, tracker);
|
|
2773
|
+
}
|
|
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
|
+
},
|
|
2783
|
+
{
|
|
2784
|
+
name: "all",
|
|
2785
|
+
arity: 1,
|
|
2786
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2787
|
+
if (!Array.isArray(input)) throw new RuntimeError("all expects an array", span);
|
|
2788
|
+
let result = true;
|
|
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
|
+
}
|
|
2818
|
+
}
|
|
2819
|
+
yield emit$1(result, span, tracker);
|
|
2820
|
+
}
|
|
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
|
+
},
|
|
2830
|
+
{
|
|
2831
|
+
name: "any",
|
|
2832
|
+
arity: 1,
|
|
2833
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2834
|
+
if (!Array.isArray(input)) throw new RuntimeError("any expects an array", span);
|
|
2835
|
+
let result = false;
|
|
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;
|
|
2857
|
+
}
|
|
2858
|
+
yield emit$1(result, span, tracker);
|
|
2859
|
+
}
|
|
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
|
+
},
|
|
2876
|
+
{
|
|
2877
|
+
name: "recurse",
|
|
2878
|
+
arity: 1,
|
|
2879
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2880
|
+
const rec = function* (curr) {
|
|
2881
|
+
yield emit$1(curr, span, tracker);
|
|
2882
|
+
const nexts = evaluate(args[0], curr, env, tracker);
|
|
2883
|
+
for (const next of nexts) yield* rec(next);
|
|
2884
|
+
};
|
|
2885
|
+
yield* rec(input);
|
|
2886
|
+
}
|
|
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
|
+
},
|
|
2905
|
+
{
|
|
2906
|
+
name: "while",
|
|
2907
|
+
arity: 2,
|
|
2908
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2909
|
+
const condExpr = args[0];
|
|
2910
|
+
const updateExpr = args[1];
|
|
2911
|
+
const rec = function* (curr) {
|
|
2912
|
+
tracker.step(span);
|
|
2913
|
+
let condMatches = false;
|
|
2914
|
+
for (const c of evaluate(condExpr, curr, env, tracker)) if (isTruthy(c)) {
|
|
2915
|
+
condMatches = true;
|
|
2916
|
+
break;
|
|
2917
|
+
}
|
|
2918
|
+
if (condMatches) {
|
|
2919
|
+
yield emit$1(curr, span, tracker);
|
|
2920
|
+
for (const next of evaluate(updateExpr, curr, env, tracker)) yield* rec(next);
|
|
2921
|
+
}
|
|
2922
|
+
};
|
|
2923
|
+
yield* rec(input);
|
|
2924
|
+
}
|
|
2925
|
+
},
|
|
2926
|
+
{
|
|
2927
|
+
name: "until",
|
|
2928
|
+
arity: 2,
|
|
2929
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2930
|
+
const condExpr = args[0];
|
|
2931
|
+
const updateExpr = args[1];
|
|
2932
|
+
const rec = function* (curr) {
|
|
2933
|
+
tracker.step(span);
|
|
2934
|
+
let condMatches = false;
|
|
2935
|
+
for (const c of evaluate(condExpr, curr, env, tracker)) if (isTruthy(c)) {
|
|
2936
|
+
condMatches = true;
|
|
2937
|
+
break;
|
|
2938
|
+
}
|
|
2939
|
+
if (condMatches) yield emit$1(curr, span, tracker);
|
|
2940
|
+
else for (const next of evaluate(updateExpr, curr, env, tracker)) yield* rec(next);
|
|
2941
|
+
};
|
|
2942
|
+
yield* rec(input);
|
|
2943
|
+
}
|
|
2944
|
+
},
|
|
2945
|
+
{
|
|
2946
|
+
name: "repeat",
|
|
2947
|
+
arity: 1,
|
|
2948
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
2949
|
+
while (true) {
|
|
2950
|
+
tracker.step(span);
|
|
2951
|
+
yield* evaluate(args[0], input, env, tracker);
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
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,218 +3101,784 @@ 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 = [
|
|
2668
3115
|
{
|
|
2669
|
-
name: "floor",
|
|
3116
|
+
name: "floor",
|
|
3117
|
+
arity: 0,
|
|
3118
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
3119
|
+
if (typeof input !== "number") throw new RuntimeError("floor expects number", span);
|
|
3120
|
+
yield emit$1(Math.floor(input), span, tracker);
|
|
3121
|
+
}
|
|
3122
|
+
},
|
|
3123
|
+
{
|
|
3124
|
+
name: "ceil",
|
|
3125
|
+
arity: 0,
|
|
3126
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
3127
|
+
if (typeof input !== "number") throw new RuntimeError("ceil expects number", span);
|
|
3128
|
+
yield emit$1(Math.ceil(input), span, tracker);
|
|
3129
|
+
}
|
|
3130
|
+
},
|
|
3131
|
+
{
|
|
3132
|
+
name: "round",
|
|
3133
|
+
arity: 0,
|
|
3134
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
3135
|
+
if (typeof input !== "number") throw new RuntimeError("round expects number", span);
|
|
3136
|
+
yield emit$1(Math.round(input), span, tracker);
|
|
3137
|
+
}
|
|
3138
|
+
},
|
|
3139
|
+
{
|
|
3140
|
+
name: "abs",
|
|
3141
|
+
arity: 0,
|
|
3142
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
3143
|
+
if (typeof input !== "number") throw new RuntimeError("abs expects number", span);
|
|
3144
|
+
yield emit$1(Math.abs(input), span, tracker);
|
|
3145
|
+
}
|
|
3146
|
+
},
|
|
3147
|
+
{
|
|
3148
|
+
name: "sqrt",
|
|
3149
|
+
arity: 0,
|
|
3150
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
3151
|
+
if (typeof input !== "number") throw new RuntimeError("sqrt expects number", span);
|
|
3152
|
+
yield emit$1(Math.sqrt(input), span, tracker);
|
|
3153
|
+
}
|
|
3154
|
+
},
|
|
3155
|
+
{
|
|
3156
|
+
name: "isnan",
|
|
3157
|
+
arity: 0,
|
|
3158
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
3159
|
+
if (typeof input !== "number") {
|
|
3160
|
+
yield emit$1(false, span, tracker);
|
|
3161
|
+
return;
|
|
3162
|
+
}
|
|
3163
|
+
yield emit$1(Number.isNaN(input), span, tracker);
|
|
3164
|
+
}
|
|
3165
|
+
},
|
|
3166
|
+
{
|
|
3167
|
+
name: "infinite",
|
|
2670
3168
|
arity: 0,
|
|
2671
|
-
apply: function* (
|
|
2672
|
-
|
|
2673
|
-
yield emit$1(Math.floor(input), span, tracker);
|
|
3169
|
+
apply: function* (_input, _args, _env, tracker, _eval, span) {
|
|
3170
|
+
yield emit$1(Infinity, span, tracker);
|
|
2674
3171
|
}
|
|
2675
3172
|
},
|
|
2676
3173
|
{
|
|
2677
|
-
name: "
|
|
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",
|
|
2678
3182
|
arity: 0,
|
|
2679
3183
|
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
2680
|
-
|
|
2681
|
-
yield emit$1(Math.ceil(input), span, tracker);
|
|
3184
|
+
yield emit$1(typeof input === "number" && !Number.isFinite(input) && !Number.isNaN(input), span, tracker);
|
|
2682
3185
|
}
|
|
2683
3186
|
},
|
|
2684
3187
|
{
|
|
2685
|
-
name: "
|
|
3188
|
+
name: "isfinite",
|
|
2686
3189
|
arity: 0,
|
|
2687
3190
|
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
2688
|
-
if (typeof input !== "number")
|
|
2689
|
-
|
|
3191
|
+
if (typeof input !== "number") {
|
|
3192
|
+
yield emit$1(true, span, tracker);
|
|
3193
|
+
return;
|
|
3194
|
+
}
|
|
3195
|
+
yield emit$1(Number.isFinite(input), span, tracker);
|
|
2690
3196
|
}
|
|
2691
3197
|
},
|
|
2692
3198
|
{
|
|
2693
|
-
name: "
|
|
3199
|
+
name: "normal",
|
|
2694
3200
|
arity: 0,
|
|
2695
3201
|
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
2696
|
-
if (typeof input !== "number")
|
|
2697
|
-
|
|
3202
|
+
if (typeof input !== "number") {
|
|
3203
|
+
yield emit$1(false, span, tracker);
|
|
3204
|
+
return;
|
|
3205
|
+
}
|
|
3206
|
+
if (!Number.isFinite(input) || Number.isNaN(input) || input === 0) {
|
|
3207
|
+
yield emit$1(false, span, tracker);
|
|
3208
|
+
return;
|
|
3209
|
+
}
|
|
3210
|
+
yield emit$1(true, span, tracker);
|
|
2698
3211
|
}
|
|
2699
3212
|
},
|
|
2700
3213
|
{
|
|
2701
|
-
name: "
|
|
3214
|
+
name: "isnormal",
|
|
2702
3215
|
arity: 0,
|
|
2703
3216
|
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
2704
|
-
|
|
2705
|
-
yield emit$1(Math.sqrt(input), span, tracker);
|
|
3217
|
+
yield emit$1(typeof input === "number" && Number.isFinite(input) && input !== 0, span, tracker);
|
|
2706
3218
|
}
|
|
2707
3219
|
},
|
|
2708
3220
|
{
|
|
2709
|
-
name: "
|
|
3221
|
+
name: "subnormal",
|
|
2710
3222
|
arity: 0,
|
|
2711
3223
|
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
2712
3224
|
if (typeof input !== "number") {
|
|
2713
3225
|
yield emit$1(false, span, tracker);
|
|
2714
3226
|
return;
|
|
2715
3227
|
}
|
|
2716
|
-
yield emit$1(
|
|
3228
|
+
yield emit$1(false, span, tracker);
|
|
3229
|
+
}
|
|
3230
|
+
},
|
|
3231
|
+
{
|
|
3232
|
+
name: "min",
|
|
3233
|
+
arity: 0,
|
|
3234
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
3235
|
+
if (!Array.isArray(input)) throw new RuntimeError("min expects an array", span);
|
|
3236
|
+
if (input.length === 0) {
|
|
3237
|
+
yield emit$1(null, span, tracker);
|
|
3238
|
+
return;
|
|
3239
|
+
}
|
|
3240
|
+
let minVal = input[0];
|
|
3241
|
+
for (let i = 1; i < input.length; i++) if (compareValues(input[i], minVal) < 0) minVal = input[i];
|
|
3242
|
+
yield emit$1(minVal, span, tracker);
|
|
3243
|
+
}
|
|
3244
|
+
},
|
|
3245
|
+
{
|
|
3246
|
+
name: "max",
|
|
3247
|
+
arity: 0,
|
|
3248
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
3249
|
+
if (!Array.isArray(input)) throw new RuntimeError("max expects an array", span);
|
|
3250
|
+
if (input.length === 0) {
|
|
3251
|
+
yield emit$1(null, span, tracker);
|
|
3252
|
+
return;
|
|
3253
|
+
}
|
|
3254
|
+
let maxVal = input[0];
|
|
3255
|
+
for (let i = 1; i < input.length; i++) if (compareValues(input[i], maxVal) > 0) maxVal = input[i];
|
|
3256
|
+
yield emit$1(maxVal, span, tracker);
|
|
3257
|
+
}
|
|
3258
|
+
},
|
|
3259
|
+
{
|
|
3260
|
+
name: "min_by",
|
|
3261
|
+
arity: 1,
|
|
3262
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
3263
|
+
if (!Array.isArray(input)) throw new RuntimeError("min_by expects an array", span);
|
|
3264
|
+
if (input.length === 0) {
|
|
3265
|
+
yield emit$1(null, span, tracker);
|
|
3266
|
+
return;
|
|
3267
|
+
}
|
|
3268
|
+
let minItem = input[0];
|
|
3269
|
+
let minKey;
|
|
3270
|
+
const keys0 = Array.from(evaluate(args[0], minItem, env, tracker));
|
|
3271
|
+
if (keys0.length !== 1) throw new RuntimeError("min_by key must return one value", span);
|
|
3272
|
+
minKey = keys0[0];
|
|
3273
|
+
for (let i = 1; i < input.length; i++) {
|
|
3274
|
+
const item = input[i];
|
|
3275
|
+
const keys = Array.from(evaluate(args[0], item, env, tracker));
|
|
3276
|
+
if (keys.length !== 1) throw new RuntimeError("min_by key must return one value", span);
|
|
3277
|
+
const key = keys[0];
|
|
3278
|
+
if (compareValues(key, minKey) < 0) {
|
|
3279
|
+
minKey = key;
|
|
3280
|
+
minItem = item;
|
|
3281
|
+
}
|
|
3282
|
+
}
|
|
3283
|
+
yield emit$1(minItem, span, tracker);
|
|
3284
|
+
}
|
|
3285
|
+
},
|
|
3286
|
+
{
|
|
3287
|
+
name: "max_by",
|
|
3288
|
+
arity: 1,
|
|
3289
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
3290
|
+
if (!Array.isArray(input)) throw new RuntimeError("max_by expects an array", span);
|
|
3291
|
+
if (input.length === 0) {
|
|
3292
|
+
yield emit$1(null, span, tracker);
|
|
3293
|
+
return;
|
|
3294
|
+
}
|
|
3295
|
+
let maxItem = input[0];
|
|
3296
|
+
let maxKey;
|
|
3297
|
+
const keys0 = Array.from(evaluate(args[0], maxItem, env, tracker));
|
|
3298
|
+
if (keys0.length !== 1) throw new RuntimeError("max_by key must return one value", span);
|
|
3299
|
+
maxKey = keys0[0];
|
|
3300
|
+
for (let i = 1; i < input.length; i++) {
|
|
3301
|
+
const item = input[i];
|
|
3302
|
+
const keys = Array.from(evaluate(args[0], item, env, tracker));
|
|
3303
|
+
if (keys.length !== 1) throw new RuntimeError("max_by key must return one value", span);
|
|
3304
|
+
const key = keys[0];
|
|
3305
|
+
if (compareValues(key, maxKey) > 0) {
|
|
3306
|
+
maxKey = key;
|
|
3307
|
+
maxItem = item;
|
|
3308
|
+
}
|
|
3309
|
+
}
|
|
3310
|
+
yield emit$1(maxItem, span, tracker);
|
|
3311
|
+
}
|
|
3312
|
+
},
|
|
3313
|
+
{
|
|
3314
|
+
name: "add",
|
|
3315
|
+
arity: 0,
|
|
3316
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
3317
|
+
if (!Array.isArray(input)) throw new RuntimeError("add expects an array", span);
|
|
3318
|
+
if (input.length === 0) {
|
|
3319
|
+
yield emit$1(null, span, tracker);
|
|
3320
|
+
return;
|
|
3321
|
+
}
|
|
3322
|
+
let acc = input[0];
|
|
3323
|
+
for (let i = 1; i < input.length; i++) {
|
|
3324
|
+
tracker.step(span);
|
|
3325
|
+
acc = add(acc, input[i], span);
|
|
3326
|
+
}
|
|
3327
|
+
yield emit$1(acc, span, tracker);
|
|
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
|
+
}
|
|
3346
|
+
}
|
|
3347
|
+
];
|
|
3348
|
+
//#endregion
|
|
3349
|
+
//#region src/builtins/dates.ts
|
|
3350
|
+
/**
|
|
3351
|
+
* Date/time builtins matching the jq binary (jq 1.7+/1.8).
|
|
3352
|
+
*
|
|
3353
|
+
* jq represents time in two forms:
|
|
3354
|
+
* - a number of seconds since the Unix epoch (possibly fractional), and
|
|
3355
|
+
* - a "broken down time": an 8-element array
|
|
3356
|
+
* [ year, month(0-11), mday, hour, minute, second,
|
|
3357
|
+
* weekday(0-6, Sun=0), yearday(0-365) ].
|
|
3358
|
+
* The second slot carries the fractional part of the input, and `year` is
|
|
3359
|
+
* the full year (NOT year-1900). This mirrors `1425599507 | gmtime ==
|
|
3360
|
+
* [2015,2,5,23,51,47,4,63]`.
|
|
3361
|
+
*
|
|
3362
|
+
* `now` is the only clock-dependent builtin; it reads the instant injected via
|
|
3363
|
+
* `options.now` and throws when none was supplied (jq-ts never reads the host
|
|
3364
|
+
* clock on its own). Every other builtin here is a pure function of its input.
|
|
3365
|
+
*
|
|
3366
|
+
* Known deviation: `strftime`'s `%s` specifier is computed as the UTC epoch of
|
|
3367
|
+
* the broken-down time, whereas glibc's `%s` uses the host timezone. jq-ts
|
|
3368
|
+
* favors determinism/portability here; every other specifier matches jq.
|
|
3369
|
+
*/
|
|
3370
|
+
const WEEKDAY_ABBR = [
|
|
3371
|
+
"Sun",
|
|
3372
|
+
"Mon",
|
|
3373
|
+
"Tue",
|
|
3374
|
+
"Wed",
|
|
3375
|
+
"Thu",
|
|
3376
|
+
"Fri",
|
|
3377
|
+
"Sat"
|
|
3378
|
+
];
|
|
3379
|
+
const WEEKDAY_FULL = [
|
|
3380
|
+
"Sunday",
|
|
3381
|
+
"Monday",
|
|
3382
|
+
"Tuesday",
|
|
3383
|
+
"Wednesday",
|
|
3384
|
+
"Thursday",
|
|
3385
|
+
"Friday",
|
|
3386
|
+
"Saturday"
|
|
3387
|
+
];
|
|
3388
|
+
const MONTH_ABBR = [
|
|
3389
|
+
"Jan",
|
|
3390
|
+
"Feb",
|
|
3391
|
+
"Mar",
|
|
3392
|
+
"Apr",
|
|
3393
|
+
"May",
|
|
3394
|
+
"Jun",
|
|
3395
|
+
"Jul",
|
|
3396
|
+
"Aug",
|
|
3397
|
+
"Sep",
|
|
3398
|
+
"Oct",
|
|
3399
|
+
"Nov",
|
|
3400
|
+
"Dec"
|
|
3401
|
+
];
|
|
3402
|
+
const MONTH_FULL = [
|
|
3403
|
+
"January",
|
|
3404
|
+
"February",
|
|
3405
|
+
"March",
|
|
3406
|
+
"April",
|
|
3407
|
+
"May",
|
|
3408
|
+
"June",
|
|
3409
|
+
"July",
|
|
3410
|
+
"August",
|
|
3411
|
+
"September",
|
|
3412
|
+
"October",
|
|
3413
|
+
"November",
|
|
3414
|
+
"December"
|
|
3415
|
+
];
|
|
3416
|
+
const MS_PER_DAY = 864e5;
|
|
3417
|
+
/**
|
|
3418
|
+
* `Date.UTC` / `new Date(y, …)` map years 0–99 to 1900–1999 (a legacy JS
|
|
3419
|
+
* quirk). jq uses the literal year, so these helpers correct it via
|
|
3420
|
+
* `setUTCFullYear` / `setFullYear`, which accept the true year.
|
|
3421
|
+
*/
|
|
3422
|
+
const utcMillis = (year, mon, mday, hour = 0, min = 0, sec = 0) => {
|
|
3423
|
+
const ms = Date.UTC(year, mon, mday, hour, min, sec);
|
|
3424
|
+
if (year >= 0 && year <= 99) {
|
|
3425
|
+
const d = new Date(ms);
|
|
3426
|
+
d.setUTCFullYear(year);
|
|
3427
|
+
return d.getTime();
|
|
3428
|
+
}
|
|
3429
|
+
return ms;
|
|
3430
|
+
};
|
|
3431
|
+
const localMillis = (year, mon, mday, hour = 0, min = 0, sec = 0) => {
|
|
3432
|
+
const d = new Date(year, mon, mday, hour, min, sec);
|
|
3433
|
+
if (year >= 0 && year <= 99) d.setFullYear(year);
|
|
3434
|
+
return d.getTime();
|
|
3435
|
+
};
|
|
3436
|
+
const pad = (value, width, fill = "0") => String(Math.trunc(value)).padStart(width, fill);
|
|
3437
|
+
const requireNumber = (input, fn, span) => {
|
|
3438
|
+
if (typeof input !== "number") throw new RuntimeError(`${fn}() requires numeric inputs`, span);
|
|
3439
|
+
return input;
|
|
3440
|
+
};
|
|
3441
|
+
const requireString = (input, fn, span) => {
|
|
3442
|
+
if (typeof input !== "string") throw new RuntimeError(`${fn} requires a string, got ${describeType(input)}`, span);
|
|
3443
|
+
return input;
|
|
3444
|
+
};
|
|
3445
|
+
/** Day-of-year (0-based), computed with date-only UTC math so DST can't skew it. */
|
|
3446
|
+
const yearDay = (year, mon, mday) => Math.round((utcMillis(year, mon, mday) - utcMillis(year, 0, 1)) / MS_PER_DAY);
|
|
3447
|
+
/** Converts epoch seconds into broken-down time fields (UTC or local). */
|
|
3448
|
+
const fieldsFromEpoch = (epochSeconds, utc) => {
|
|
3449
|
+
const whole = Math.floor(epochSeconds);
|
|
3450
|
+
const frac = epochSeconds - whole;
|
|
3451
|
+
const d = /* @__PURE__ */ new Date(whole * 1e3);
|
|
3452
|
+
const year = utc ? d.getUTCFullYear() : d.getFullYear();
|
|
3453
|
+
const mon = utc ? d.getUTCMonth() : d.getMonth();
|
|
3454
|
+
const mday = utc ? d.getUTCDate() : d.getDate();
|
|
3455
|
+
const hour = utc ? d.getUTCHours() : d.getHours();
|
|
3456
|
+
const min = utc ? d.getUTCMinutes() : d.getMinutes();
|
|
3457
|
+
const sec = utc ? d.getUTCSeconds() : d.getSeconds();
|
|
3458
|
+
const wday = utc ? d.getUTCDay() : d.getDay();
|
|
3459
|
+
return {
|
|
3460
|
+
year,
|
|
3461
|
+
mon,
|
|
3462
|
+
mday,
|
|
3463
|
+
hour,
|
|
3464
|
+
min,
|
|
3465
|
+
sec: sec + frac,
|
|
3466
|
+
wday,
|
|
3467
|
+
yday: yearDay(year, mon, mday)
|
|
3468
|
+
};
|
|
3469
|
+
};
|
|
3470
|
+
const fieldsToArray = (f) => [
|
|
3471
|
+
f.year,
|
|
3472
|
+
f.mon,
|
|
3473
|
+
f.mday,
|
|
3474
|
+
f.hour,
|
|
3475
|
+
f.min,
|
|
3476
|
+
f.sec,
|
|
3477
|
+
f.wday,
|
|
3478
|
+
f.yday
|
|
3479
|
+
];
|
|
3480
|
+
/**
|
|
3481
|
+
* Parses a jq broken-down time array into fields. Missing trailing slots
|
|
3482
|
+
* default to 0 (jq is lenient: `[1,2,3] | mktime` works); present-but-non-number
|
|
3483
|
+
* slots are an error. wday/yday are derived from the calendar date when absent.
|
|
3484
|
+
*/
|
|
3485
|
+
const fieldsFromArray = (input, fn, span) => {
|
|
3486
|
+
if (!Array.isArray(input)) throw new RuntimeError(`${fn} requires array inputs`, span);
|
|
3487
|
+
const at = (index) => {
|
|
3488
|
+
if (index >= input.length) return 0;
|
|
3489
|
+
const raw = input[index];
|
|
3490
|
+
if (typeof raw !== "number") throw new RuntimeError(`${fn} requires an array of numbers`, span);
|
|
3491
|
+
return raw;
|
|
3492
|
+
};
|
|
3493
|
+
const year = at(0);
|
|
3494
|
+
const mon = at(1);
|
|
3495
|
+
const mday = at(2);
|
|
3496
|
+
return {
|
|
3497
|
+
year,
|
|
3498
|
+
mon,
|
|
3499
|
+
mday,
|
|
3500
|
+
hour: at(3),
|
|
3501
|
+
min: at(4),
|
|
3502
|
+
sec: at(5),
|
|
3503
|
+
wday: input.length > 6 && typeof input[6] === "number" ? input[6] : new Date(utcMillis(year, mon, mday)).getUTCDay(),
|
|
3504
|
+
yday: input.length > 7 && typeof input[7] === "number" ? input[7] : yearDay(year, mon, mday)
|
|
3505
|
+
};
|
|
3506
|
+
};
|
|
3507
|
+
/** Converts broken-down time fields back into epoch seconds (mktime). */
|
|
3508
|
+
const epochFromFields = (f, utc) => {
|
|
3509
|
+
const wholeSec = Math.floor(f.sec);
|
|
3510
|
+
const frac = f.sec - wholeSec;
|
|
3511
|
+
return (utc ? utcMillis(f.year, f.mon, f.mday, f.hour, f.min, wholeSec) : localMillis(f.year, f.mon, f.mday, f.hour, f.min, wholeSec)) / 1e3 + frac;
|
|
3512
|
+
};
|
|
3513
|
+
/** ISO-8601 week-numbering year and week (for %G / %V). */
|
|
3514
|
+
const isoWeek = (year, mon, mday) => {
|
|
3515
|
+
const date = new Date(utcMillis(year, mon, mday));
|
|
3516
|
+
const dayNum = (date.getUTCDay() + 6) % 7;
|
|
3517
|
+
date.setUTCDate(date.getUTCDate() - dayNum + 3);
|
|
3518
|
+
const isoYear = date.getUTCFullYear();
|
|
3519
|
+
const firstThursday = new Date(utcMillis(isoYear, 0, 4));
|
|
3520
|
+
const firstDayNum = (firstThursday.getUTCDay() + 6) % 7;
|
|
3521
|
+
firstThursday.setUTCDate(firstThursday.getUTCDate() - firstDayNum + 3);
|
|
3522
|
+
return {
|
|
3523
|
+
year: isoYear,
|
|
3524
|
+
week: 1 + Math.round((date.getTime() - firstThursday.getTime()) / (7 * MS_PER_DAY))
|
|
3525
|
+
};
|
|
3526
|
+
};
|
|
3527
|
+
const localOffsetString = (f) => {
|
|
3528
|
+
const offsetMin = -new Date(localMillis(f.year, f.mon, f.mday, f.hour, f.min, Math.floor(f.sec))).getTimezoneOffset();
|
|
3529
|
+
const sign = offsetMin >= 0 ? "+" : "-";
|
|
3530
|
+
const abs = Math.abs(offsetMin);
|
|
3531
|
+
return `${sign}${pad(Math.floor(abs / 60), 2)}${pad(abs % 60, 2)}`;
|
|
3532
|
+
};
|
|
3533
|
+
/** Best-effort local timezone abbreviation via Intl, falling back to the offset. */
|
|
3534
|
+
const localTzAbbr = (f) => {
|
|
3535
|
+
try {
|
|
3536
|
+
const tz = new Intl.DateTimeFormat("en-US", { timeZoneName: "short" }).formatToParts(new Date(localMillis(f.year, f.mon, f.mday, f.hour, f.min, Math.floor(f.sec)))).find((p) => p.type === "timeZoneName")?.value;
|
|
3537
|
+
if (tz) return tz;
|
|
3538
|
+
} catch {}
|
|
3539
|
+
return localOffsetString(f);
|
|
3540
|
+
};
|
|
3541
|
+
/** Formats broken-down time with a C `strftime`-style format. */
|
|
3542
|
+
const formatStrftime = (f, fmt, utc) => {
|
|
3543
|
+
const hour12 = f.hour % 12 === 0 ? 12 : f.hour % 12;
|
|
3544
|
+
let out = "";
|
|
3545
|
+
for (let i = 0; i < fmt.length; i++) {
|
|
3546
|
+
if (fmt[i] !== "%") {
|
|
3547
|
+
out += fmt[i];
|
|
3548
|
+
continue;
|
|
3549
|
+
}
|
|
3550
|
+
i++;
|
|
3551
|
+
const spec = fmt[i];
|
|
3552
|
+
switch (spec) {
|
|
3553
|
+
case "Y":
|
|
3554
|
+
out += String(f.year);
|
|
3555
|
+
break;
|
|
3556
|
+
case "C":
|
|
3557
|
+
out += pad(Math.floor(f.year / 100), 2);
|
|
3558
|
+
break;
|
|
3559
|
+
case "y":
|
|
3560
|
+
out += pad((f.year % 100 + 100) % 100, 2);
|
|
3561
|
+
break;
|
|
3562
|
+
case "m":
|
|
3563
|
+
out += pad(f.mon + 1, 2);
|
|
3564
|
+
break;
|
|
3565
|
+
case "d":
|
|
3566
|
+
out += pad(f.mday, 2);
|
|
3567
|
+
break;
|
|
3568
|
+
case "e":
|
|
3569
|
+
out += pad(f.mday, 2, " ");
|
|
3570
|
+
break;
|
|
3571
|
+
case "H":
|
|
3572
|
+
out += pad(f.hour, 2);
|
|
3573
|
+
break;
|
|
3574
|
+
case "k":
|
|
3575
|
+
out += pad(f.hour, 2, " ");
|
|
3576
|
+
break;
|
|
3577
|
+
case "I":
|
|
3578
|
+
out += pad(hour12, 2);
|
|
3579
|
+
break;
|
|
3580
|
+
case "l":
|
|
3581
|
+
out += pad(hour12, 2, " ");
|
|
3582
|
+
break;
|
|
3583
|
+
case "M":
|
|
3584
|
+
out += pad(f.min, 2);
|
|
3585
|
+
break;
|
|
3586
|
+
case "S":
|
|
3587
|
+
out += pad(Math.floor(f.sec), 2);
|
|
3588
|
+
break;
|
|
3589
|
+
case "p":
|
|
3590
|
+
out += f.hour < 12 ? "AM" : "PM";
|
|
3591
|
+
break;
|
|
3592
|
+
case "P":
|
|
3593
|
+
out += f.hour < 12 ? "am" : "pm";
|
|
3594
|
+
break;
|
|
3595
|
+
case "j":
|
|
3596
|
+
out += pad(f.yday + 1, 3);
|
|
3597
|
+
break;
|
|
3598
|
+
case "a":
|
|
3599
|
+
out += WEEKDAY_ABBR[(f.wday % 7 + 7) % 7];
|
|
3600
|
+
break;
|
|
3601
|
+
case "A":
|
|
3602
|
+
out += WEEKDAY_FULL[(f.wday % 7 + 7) % 7];
|
|
3603
|
+
break;
|
|
3604
|
+
case "b":
|
|
3605
|
+
case "h":
|
|
3606
|
+
out += MONTH_ABBR[(f.mon % 12 + 12) % 12];
|
|
3607
|
+
break;
|
|
3608
|
+
case "B":
|
|
3609
|
+
out += MONTH_FULL[(f.mon % 12 + 12) % 12];
|
|
3610
|
+
break;
|
|
3611
|
+
case "u":
|
|
3612
|
+
out += String(f.wday === 0 ? 7 : f.wday);
|
|
3613
|
+
break;
|
|
3614
|
+
case "w":
|
|
3615
|
+
out += String(f.wday);
|
|
3616
|
+
break;
|
|
3617
|
+
case "V":
|
|
3618
|
+
out += pad(isoWeek(f.year, f.mon, f.mday).week, 2);
|
|
3619
|
+
break;
|
|
3620
|
+
case "G":
|
|
3621
|
+
out += String(isoWeek(f.year, f.mon, f.mday).year);
|
|
3622
|
+
break;
|
|
3623
|
+
case "Z":
|
|
3624
|
+
out += utc ? "GMT" : localTzAbbr(f);
|
|
3625
|
+
break;
|
|
3626
|
+
case "z":
|
|
3627
|
+
out += utc ? "+0000" : localOffsetString(f);
|
|
3628
|
+
break;
|
|
3629
|
+
case "s":
|
|
3630
|
+
out += String(Math.floor(epochFromFields(f, true)));
|
|
3631
|
+
break;
|
|
3632
|
+
case "T":
|
|
3633
|
+
out += `${pad(f.hour, 2)}:${pad(f.min, 2)}:${pad(Math.floor(f.sec), 2)}`;
|
|
3634
|
+
break;
|
|
3635
|
+
case "R":
|
|
3636
|
+
out += `${pad(f.hour, 2)}:${pad(f.min, 2)}`;
|
|
3637
|
+
break;
|
|
3638
|
+
case "F":
|
|
3639
|
+
out += `${String(f.year)}-${pad(f.mon + 1, 2)}-${pad(f.mday, 2)}`;
|
|
3640
|
+
break;
|
|
3641
|
+
case "D":
|
|
3642
|
+
out += `${pad(f.mon + 1, 2)}/${pad(f.mday, 2)}/${pad((f.year % 100 + 100) % 100, 2)}`;
|
|
3643
|
+
break;
|
|
3644
|
+
case "n":
|
|
3645
|
+
out += "\n";
|
|
3646
|
+
break;
|
|
3647
|
+
case "t":
|
|
3648
|
+
out += " ";
|
|
3649
|
+
break;
|
|
3650
|
+
case "%":
|
|
3651
|
+
out += "%";
|
|
3652
|
+
break;
|
|
3653
|
+
case void 0:
|
|
3654
|
+
out += "%";
|
|
3655
|
+
break;
|
|
3656
|
+
default:
|
|
3657
|
+
out += `%${spec}`;
|
|
3658
|
+
break;
|
|
3659
|
+
}
|
|
3660
|
+
}
|
|
3661
|
+
return out;
|
|
3662
|
+
};
|
|
3663
|
+
/** Parses a string into broken-down time fields using a C `strptime`-style format. */
|
|
3664
|
+
const parseStrptime = (input, fmt, span) => {
|
|
3665
|
+
let s = 0;
|
|
3666
|
+
let year = 1900;
|
|
3667
|
+
let mon = 0;
|
|
3668
|
+
let mday = 1;
|
|
3669
|
+
let hour = 0;
|
|
3670
|
+
let min = 0;
|
|
3671
|
+
let sec = 0;
|
|
3672
|
+
const fail = () => {
|
|
3673
|
+
throw new RuntimeError(`date "${input}" does not match format "${fmt}"`, span);
|
|
3674
|
+
};
|
|
3675
|
+
const readInt = (maxLen) => {
|
|
3676
|
+
const start = s;
|
|
3677
|
+
if (input[s] === "+" || input[s] === "-") s++;
|
|
3678
|
+
let digits = 0;
|
|
3679
|
+
while (s < input.length && digits < maxLen && /[0-9]/.test(input[s])) {
|
|
3680
|
+
s++;
|
|
3681
|
+
digits++;
|
|
3682
|
+
}
|
|
3683
|
+
if (digits === 0) fail();
|
|
3684
|
+
return parseInt(input.slice(start, s), 10);
|
|
3685
|
+
};
|
|
3686
|
+
const matchName = (names) => {
|
|
3687
|
+
const lower = input.slice(s).toLowerCase();
|
|
3688
|
+
let best = -1;
|
|
3689
|
+
let bestLen = 0;
|
|
3690
|
+
names.forEach((name, index) => {
|
|
3691
|
+
const n = name.toLowerCase();
|
|
3692
|
+
if (lower.startsWith(n) && n.length > bestLen) {
|
|
3693
|
+
best = index;
|
|
3694
|
+
bestLen = n.length;
|
|
3695
|
+
}
|
|
3696
|
+
});
|
|
3697
|
+
if (best < 0) fail();
|
|
3698
|
+
s += bestLen;
|
|
3699
|
+
return best;
|
|
3700
|
+
};
|
|
3701
|
+
for (let i = 0; i < fmt.length; i++) {
|
|
3702
|
+
if (fmt[i] !== "%") {
|
|
3703
|
+
if (/\s/.test(fmt[i])) while (s < input.length && /\s/.test(input[s])) s++;
|
|
3704
|
+
else {
|
|
3705
|
+
if (input[s] !== fmt[i]) fail();
|
|
3706
|
+
s++;
|
|
3707
|
+
}
|
|
3708
|
+
continue;
|
|
3709
|
+
}
|
|
3710
|
+
i++;
|
|
3711
|
+
switch (fmt[i]) {
|
|
3712
|
+
case "Y":
|
|
3713
|
+
year = readInt(4);
|
|
3714
|
+
break;
|
|
3715
|
+
case "y": {
|
|
3716
|
+
const yy = readInt(2);
|
|
3717
|
+
year = yy < 69 ? 2e3 + yy : 1900 + yy;
|
|
3718
|
+
break;
|
|
3719
|
+
}
|
|
3720
|
+
case "C":
|
|
3721
|
+
year = readInt(2) * 100;
|
|
3722
|
+
break;
|
|
3723
|
+
case "m":
|
|
3724
|
+
mon = readInt(2) - 1;
|
|
3725
|
+
break;
|
|
3726
|
+
case "d":
|
|
3727
|
+
case "e":
|
|
3728
|
+
while (s < input.length && input[s] === " ") s++;
|
|
3729
|
+
mday = readInt(2);
|
|
3730
|
+
break;
|
|
3731
|
+
case "H":
|
|
3732
|
+
case "k":
|
|
3733
|
+
case "I":
|
|
3734
|
+
case "l":
|
|
3735
|
+
while (s < input.length && input[s] === " ") s++;
|
|
3736
|
+
hour = readInt(2);
|
|
3737
|
+
break;
|
|
3738
|
+
case "M":
|
|
3739
|
+
min = readInt(2);
|
|
3740
|
+
break;
|
|
3741
|
+
case "S":
|
|
3742
|
+
sec = readInt(2);
|
|
3743
|
+
break;
|
|
3744
|
+
case "j":
|
|
3745
|
+
readInt(3);
|
|
3746
|
+
break;
|
|
3747
|
+
case "b":
|
|
3748
|
+
case "B":
|
|
3749
|
+
case "h":
|
|
3750
|
+
mon = matchName([...MONTH_ABBR, ...MONTH_FULL]) % 12;
|
|
3751
|
+
break;
|
|
3752
|
+
case "a":
|
|
3753
|
+
case "A":
|
|
3754
|
+
matchName([...WEEKDAY_ABBR, ...WEEKDAY_FULL]);
|
|
3755
|
+
break;
|
|
3756
|
+
case "p":
|
|
3757
|
+
case "P": {
|
|
3758
|
+
const ampm = input.slice(s, s + 2).toUpperCase();
|
|
3759
|
+
if (ampm === "AM") {
|
|
3760
|
+
if (hour === 12) hour = 0;
|
|
3761
|
+
s += 2;
|
|
3762
|
+
} else if (ampm === "PM") {
|
|
3763
|
+
if (hour < 12) hour += 12;
|
|
3764
|
+
s += 2;
|
|
3765
|
+
} else fail();
|
|
3766
|
+
break;
|
|
3767
|
+
}
|
|
3768
|
+
case "z":
|
|
3769
|
+
if (input[s] === "Z") s++;
|
|
3770
|
+
else {
|
|
3771
|
+
if (input[s] === "+" || input[s] === "-") s++;
|
|
3772
|
+
s += 2;
|
|
3773
|
+
if (input[s] === ":") s++;
|
|
3774
|
+
s += 2;
|
|
3775
|
+
}
|
|
3776
|
+
break;
|
|
3777
|
+
case "Z":
|
|
3778
|
+
while (s < input.length && /[A-Za-z]/.test(input[s])) s++;
|
|
3779
|
+
break;
|
|
3780
|
+
case "n":
|
|
3781
|
+
case "t":
|
|
3782
|
+
while (s < input.length && /\s/.test(input[s])) s++;
|
|
3783
|
+
break;
|
|
3784
|
+
case "%":
|
|
3785
|
+
if (input[s] !== "%") fail();
|
|
3786
|
+
s++;
|
|
3787
|
+
break;
|
|
3788
|
+
default: fail();
|
|
3789
|
+
}
|
|
3790
|
+
}
|
|
3791
|
+
return fieldsFromEpoch(utcMillis(year, mon, mday, hour, min, sec) / 1e3, true);
|
|
3792
|
+
};
|
|
3793
|
+
/** Resolves strftime input that may be either an epoch number or a broken-down array. */
|
|
3794
|
+
const fieldsForFormat = (input, fn, span, utc) => typeof input === "number" ? fieldsFromEpoch(input, utc) : fieldsFromArray(input, fn, span);
|
|
3795
|
+
const readFormat = (value, fn, span) => {
|
|
3796
|
+
if (typeof value !== "string") throw new RuntimeError(`${fn}/1 requires a string format`, span);
|
|
3797
|
+
return value;
|
|
3798
|
+
};
|
|
3799
|
+
const ISO8601 = "%Y-%m-%dT%H:%M:%SZ";
|
|
3800
|
+
const dateBuiltins = [
|
|
3801
|
+
{
|
|
3802
|
+
name: "now",
|
|
3803
|
+
arity: 0,
|
|
3804
|
+
apply: function* (_input, _args, _env, tracker, _eval, span) {
|
|
3805
|
+
yield emit$1(tracker.now(span), span, tracker);
|
|
2717
3806
|
}
|
|
2718
3807
|
},
|
|
2719
3808
|
{
|
|
2720
|
-
name: "
|
|
3809
|
+
name: "gmtime",
|
|
2721
3810
|
arity: 0,
|
|
2722
3811
|
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);
|
|
3812
|
+
yield emit$1(fieldsToArray(fieldsFromEpoch(requireNumber(input, "gmtime", span), true)), span, tracker);
|
|
2728
3813
|
}
|
|
2729
3814
|
},
|
|
2730
3815
|
{
|
|
2731
|
-
name: "
|
|
3816
|
+
name: "localtime",
|
|
2732
3817
|
arity: 0,
|
|
2733
3818
|
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
2734
|
-
|
|
2735
|
-
yield emit$1(true, span, tracker);
|
|
2736
|
-
return;
|
|
2737
|
-
}
|
|
2738
|
-
yield emit$1(Number.isFinite(input), span, tracker);
|
|
3819
|
+
yield emit$1(fieldsToArray(fieldsFromEpoch(requireNumber(input, "localtime", span), false)), span, tracker);
|
|
2739
3820
|
}
|
|
2740
3821
|
},
|
|
2741
3822
|
{
|
|
2742
|
-
name: "
|
|
3823
|
+
name: "mktime",
|
|
2743
3824
|
arity: 0,
|
|
2744
3825
|
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
2745
|
-
|
|
2746
|
-
yield emit$1(false, span, tracker);
|
|
2747
|
-
return;
|
|
2748
|
-
}
|
|
2749
|
-
if (!Number.isFinite(input) || Number.isNaN(input) || input === 0) {
|
|
2750
|
-
yield emit$1(false, span, tracker);
|
|
2751
|
-
return;
|
|
2752
|
-
}
|
|
2753
|
-
yield emit$1(true, span, tracker);
|
|
3826
|
+
yield emit$1(epochFromFields(fieldsFromArray(input, "mktime", span), true), span, tracker);
|
|
2754
3827
|
}
|
|
2755
3828
|
},
|
|
2756
3829
|
{
|
|
2757
|
-
name: "
|
|
2758
|
-
arity:
|
|
2759
|
-
apply: function* (input,
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
return;
|
|
2763
|
-
}
|
|
2764
|
-
yield emit$1(false, span, tracker);
|
|
3830
|
+
name: "strftime",
|
|
3831
|
+
arity: 1,
|
|
3832
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
3833
|
+
const fields = fieldsForFormat(input, "strftime", span, true);
|
|
3834
|
+
for (const fmt of evaluate(args[0], input, env, tracker)) yield emit$1(formatStrftime(fields, readFormat(fmt, "strftime", span), true), span, tracker);
|
|
2765
3835
|
}
|
|
2766
3836
|
},
|
|
2767
3837
|
{
|
|
2768
|
-
name: "
|
|
2769
|
-
arity:
|
|
2770
|
-
apply: function* (input,
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
yield emit$1(null, span, tracker);
|
|
2774
|
-
return;
|
|
2775
|
-
}
|
|
2776
|
-
let minVal = input[0];
|
|
2777
|
-
for (let i = 1; i < input.length; i++) if (compareValues(input[i], minVal) < 0) minVal = input[i];
|
|
2778
|
-
yield emit$1(minVal, span, tracker);
|
|
3838
|
+
name: "strflocaltime",
|
|
3839
|
+
arity: 1,
|
|
3840
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
3841
|
+
const fields = fieldsForFormat(input, "strflocaltime", span, false);
|
|
3842
|
+
for (const fmt of evaluate(args[0], input, env, tracker)) yield emit$1(formatStrftime(fields, readFormat(fmt, "strflocaltime", span), false), span, tracker);
|
|
2779
3843
|
}
|
|
2780
3844
|
},
|
|
2781
3845
|
{
|
|
2782
|
-
name: "
|
|
3846
|
+
name: "strptime",
|
|
3847
|
+
arity: 1,
|
|
3848
|
+
apply: function* (input, args, env, tracker, evaluate, span) {
|
|
3849
|
+
const str = requireString(input, "strptime", span);
|
|
3850
|
+
for (const fmt of evaluate(args[0], input, env, tracker)) yield emit$1(fieldsToArray(parseStrptime(str, readFormat(fmt, "strptime", span), span)), span, tracker);
|
|
3851
|
+
}
|
|
3852
|
+
},
|
|
3853
|
+
{
|
|
3854
|
+
name: "todate",
|
|
2783
3855
|
arity: 0,
|
|
2784
3856
|
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
2785
|
-
|
|
2786
|
-
if (input.length === 0) {
|
|
2787
|
-
yield emit$1(null, span, tracker);
|
|
2788
|
-
return;
|
|
2789
|
-
}
|
|
2790
|
-
let maxVal = input[0];
|
|
2791
|
-
for (let i = 1; i < input.length; i++) if (compareValues(input[i], maxVal) > 0) maxVal = input[i];
|
|
2792
|
-
yield emit$1(maxVal, span, tracker);
|
|
3857
|
+
yield emit$1(formatStrftime(fieldsFromEpoch(requireNumber(input, "todate", span), true), ISO8601, true), span, tracker);
|
|
2793
3858
|
}
|
|
2794
3859
|
},
|
|
2795
3860
|
{
|
|
2796
|
-
name: "
|
|
2797
|
-
arity:
|
|
2798
|
-
apply: function* (input,
|
|
2799
|
-
|
|
2800
|
-
if (input.length === 0) {
|
|
2801
|
-
yield emit$1(null, span, tracker);
|
|
2802
|
-
return;
|
|
2803
|
-
}
|
|
2804
|
-
let minItem = input[0];
|
|
2805
|
-
let minKey;
|
|
2806
|
-
const keys0 = Array.from(evaluate$1(args[0], minItem, env, tracker));
|
|
2807
|
-
if (keys0.length !== 1) throw new RuntimeError("min_by key must return one value", span);
|
|
2808
|
-
minKey = keys0[0];
|
|
2809
|
-
for (let i = 1; i < input.length; i++) {
|
|
2810
|
-
const item = input[i];
|
|
2811
|
-
const keys = Array.from(evaluate$1(args[0], item, env, tracker));
|
|
2812
|
-
if (keys.length !== 1) throw new RuntimeError("min_by key must return one value", span);
|
|
2813
|
-
const key = keys[0];
|
|
2814
|
-
if (compareValues(key, minKey) < 0) {
|
|
2815
|
-
minKey = key;
|
|
2816
|
-
minItem = item;
|
|
2817
|
-
}
|
|
2818
|
-
}
|
|
2819
|
-
yield emit$1(minItem, span, tracker);
|
|
3861
|
+
name: "todateiso8601",
|
|
3862
|
+
arity: 0,
|
|
3863
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
3864
|
+
yield emit$1(formatStrftime(fieldsFromEpoch(requireNumber(input, "todateiso8601", span), true), ISO8601, true), span, tracker);
|
|
2820
3865
|
}
|
|
2821
3866
|
},
|
|
2822
3867
|
{
|
|
2823
|
-
name: "
|
|
2824
|
-
arity:
|
|
2825
|
-
apply: function* (input,
|
|
2826
|
-
|
|
2827
|
-
if (input.length === 0) {
|
|
2828
|
-
yield emit$1(null, span, tracker);
|
|
2829
|
-
return;
|
|
2830
|
-
}
|
|
2831
|
-
let maxItem = input[0];
|
|
2832
|
-
let maxKey;
|
|
2833
|
-
const keys0 = Array.from(evaluate$1(args[0], maxItem, env, tracker));
|
|
2834
|
-
if (keys0.length !== 1) throw new RuntimeError("max_by key must return one value", span);
|
|
2835
|
-
maxKey = keys0[0];
|
|
2836
|
-
for (let i = 1; i < input.length; i++) {
|
|
2837
|
-
const item = input[i];
|
|
2838
|
-
const keys = Array.from(evaluate$1(args[0], item, env, tracker));
|
|
2839
|
-
if (keys.length !== 1) throw new RuntimeError("max_by key must return one value", span);
|
|
2840
|
-
const key = keys[0];
|
|
2841
|
-
if (compareValues(key, maxKey) > 0) {
|
|
2842
|
-
maxKey = key;
|
|
2843
|
-
maxItem = item;
|
|
2844
|
-
}
|
|
2845
|
-
}
|
|
2846
|
-
yield emit$1(maxItem, span, tracker);
|
|
3868
|
+
name: "fromdate",
|
|
3869
|
+
arity: 0,
|
|
3870
|
+
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
3871
|
+
yield emit$1(epochFromFields(parseStrptime(requireString(input, "fromdate", span), ISO8601, span), true), span, tracker);
|
|
2847
3872
|
}
|
|
2848
3873
|
},
|
|
2849
3874
|
{
|
|
2850
|
-
name: "
|
|
3875
|
+
name: "fromdateiso8601",
|
|
2851
3876
|
arity: 0,
|
|
2852
3877
|
apply: function* (input, _args, _env, tracker, _eval, span) {
|
|
2853
|
-
|
|
2854
|
-
if (input.length === 0) {
|
|
2855
|
-
yield emit$1(null, span, tracker);
|
|
2856
|
-
return;
|
|
2857
|
-
}
|
|
2858
|
-
let acc = input[0];
|
|
2859
|
-
for (let i = 1; i < input.length; i++) {
|
|
2860
|
-
tracker.step(span);
|
|
2861
|
-
acc = add(acc, input[i], span);
|
|
2862
|
-
}
|
|
2863
|
-
yield emit$1(acc, span, tracker);
|
|
3878
|
+
yield emit$1(epochFromFields(parseStrptime(requireString(input, "fromdateiso8601", span), ISO8601, span), true), span, tracker);
|
|
2864
3879
|
}
|
|
2865
3880
|
}
|
|
2866
3881
|
];
|
|
2867
|
-
|
|
2868
3882
|
//#endregion
|
|
2869
3883
|
//#region src/builtins/index.ts
|
|
2870
3884
|
const registerAllBuiltins = () => {
|
|
@@ -2875,12 +3889,11 @@ const registerAllBuiltins = () => {
|
|
|
2875
3889
|
registerBuiltins(pathBuiltins);
|
|
2876
3890
|
registerBuiltins(iteratorBuiltins);
|
|
2877
3891
|
registerBuiltins(mathBuiltins);
|
|
3892
|
+
registerBuiltins(dateBuiltins);
|
|
2878
3893
|
};
|
|
2879
|
-
|
|
2880
3894
|
//#endregion
|
|
2881
3895
|
//#region src/builtins.ts
|
|
2882
3896
|
registerAllBuiltins();
|
|
2883
|
-
|
|
2884
3897
|
//#endregion
|
|
2885
3898
|
//#region src/validate.ts
|
|
2886
3899
|
/**
|
|
@@ -2891,57 +3904,57 @@ registerAllBuiltins();
|
|
|
2891
3904
|
* @throws {ValidationError} If validation fails.
|
|
2892
3905
|
*/
|
|
2893
3906
|
const validate = (node) => {
|
|
2894
|
-
visit(node, []);
|
|
3907
|
+
visit$1(node, []);
|
|
2895
3908
|
};
|
|
2896
|
-
const visit = (node, scope) => {
|
|
3909
|
+
const visit$1 = (node, scope) => {
|
|
2897
3910
|
switch (node.kind) {
|
|
2898
3911
|
case "Identity":
|
|
2899
3912
|
case "Literal":
|
|
2900
3913
|
case "Var": return;
|
|
2901
3914
|
case "FieldAccess":
|
|
2902
|
-
visit(node.target, scope);
|
|
3915
|
+
visit$1(node.target, scope);
|
|
2903
3916
|
return;
|
|
2904
3917
|
case "IndexAccess":
|
|
2905
|
-
visit(node.target, scope);
|
|
2906
|
-
visit(node.index, scope);
|
|
3918
|
+
visit$1(node.target, scope);
|
|
3919
|
+
visit$1(node.index, scope);
|
|
2907
3920
|
return;
|
|
2908
3921
|
case "Array":
|
|
2909
|
-
node.items.forEach((n) => visit(n, scope));
|
|
3922
|
+
node.items.forEach((n) => visit$1(n, scope));
|
|
2910
3923
|
return;
|
|
2911
3924
|
case "Object":
|
|
2912
3925
|
node.entries.forEach((entry) => {
|
|
2913
|
-
if (entry.key.kind === "KeyExpr") visit(entry.key.expr, scope);
|
|
2914
|
-
visit(entry.value, scope);
|
|
3926
|
+
if (entry.key.kind === "KeyExpr") visit$1(entry.key.expr, scope);
|
|
3927
|
+
visit$1(entry.value, scope);
|
|
2915
3928
|
});
|
|
2916
3929
|
return;
|
|
2917
3930
|
case "Pipe":
|
|
2918
3931
|
case "Comma":
|
|
2919
3932
|
case "Alt":
|
|
2920
|
-
visit(node.left, scope);
|
|
2921
|
-
visit(node.right, scope);
|
|
3933
|
+
visit$1(node.left, scope);
|
|
3934
|
+
visit$1(node.right, scope);
|
|
2922
3935
|
return;
|
|
2923
3936
|
case "Binary":
|
|
2924
3937
|
case "Bool":
|
|
2925
|
-
visit(node.left, scope);
|
|
2926
|
-
visit(node.right, scope);
|
|
3938
|
+
visit$1(node.left, scope);
|
|
3939
|
+
visit$1(node.right, scope);
|
|
2927
3940
|
return;
|
|
2928
3941
|
case "Unary":
|
|
2929
|
-
visit(node.expr, scope);
|
|
3942
|
+
visit$1(node.expr, scope);
|
|
2930
3943
|
return;
|
|
2931
3944
|
case "If":
|
|
2932
3945
|
node.branches.forEach((branch) => {
|
|
2933
|
-
visit(branch.cond, scope);
|
|
2934
|
-
visit(branch.then, scope);
|
|
3946
|
+
visit$1(branch.cond, scope);
|
|
3947
|
+
visit$1(branch.then, scope);
|
|
2935
3948
|
});
|
|
2936
|
-
visit(node.else, scope);
|
|
3949
|
+
visit$1(node.else, scope);
|
|
2937
3950
|
return;
|
|
2938
3951
|
case "As":
|
|
2939
|
-
visit(node.bind, scope);
|
|
2940
|
-
visit(node.body, scope);
|
|
3952
|
+
visit$1(node.bind, scope);
|
|
3953
|
+
visit$1(node.body, scope);
|
|
2941
3954
|
return;
|
|
2942
3955
|
case "Call": {
|
|
2943
3956
|
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);
|
|
3957
|
+
for (const arg of node.args) visit$1(arg, scope);
|
|
2945
3958
|
return;
|
|
2946
3959
|
}
|
|
2947
3960
|
const specs = builtins[node.name];
|
|
@@ -2950,51 +3963,50 @@ const visit = (node, scope) => {
|
|
|
2950
3963
|
const arities = specs.map((s) => s.arity).join(" or ");
|
|
2951
3964
|
throw new ValidationError(`Function ${node.name} expects ${arities} arguments, but got ${node.args.length}`, node.span);
|
|
2952
3965
|
}
|
|
2953
|
-
for (const arg of node.args) visit(arg, scope);
|
|
3966
|
+
for (const arg of node.args) visit$1(arg, scope);
|
|
2954
3967
|
return;
|
|
2955
3968
|
}
|
|
2956
3969
|
case "Assignment":
|
|
2957
|
-
visit(node.left, scope);
|
|
2958
|
-
visit(node.right, scope);
|
|
3970
|
+
visit$1(node.left, scope);
|
|
3971
|
+
visit$1(node.right, scope);
|
|
2959
3972
|
return;
|
|
2960
3973
|
case "Def": {
|
|
2961
3974
|
const bodyScope = new Set(node.args);
|
|
2962
3975
|
bodyScope.add(node.name);
|
|
2963
|
-
visit(node.body, [...scope, bodyScope]);
|
|
3976
|
+
visit$1(node.body, [...scope, bodyScope]);
|
|
2964
3977
|
const nextScope = new Set([node.name]);
|
|
2965
|
-
visit(node.next, [...scope, nextScope]);
|
|
3978
|
+
visit$1(node.next, [...scope, nextScope]);
|
|
2966
3979
|
return;
|
|
2967
3980
|
}
|
|
2968
3981
|
case "Reduce":
|
|
2969
|
-
visit(node.source, scope);
|
|
2970
|
-
visit(node.init, scope);
|
|
2971
|
-
visit(node.update, scope);
|
|
3982
|
+
visit$1(node.source, scope);
|
|
3983
|
+
visit$1(node.init, scope);
|
|
3984
|
+
visit$1(node.update, scope);
|
|
2972
3985
|
return;
|
|
2973
3986
|
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);
|
|
3987
|
+
visit$1(node.source, scope);
|
|
3988
|
+
visit$1(node.init, scope);
|
|
3989
|
+
visit$1(node.update, scope);
|
|
3990
|
+
if (node.extract) visit$1(node.extract, scope);
|
|
2978
3991
|
return;
|
|
2979
3992
|
case "Try":
|
|
2980
|
-
visit(node.body, scope);
|
|
2981
|
-
if (node.handler) visit(node.handler, scope);
|
|
3993
|
+
visit$1(node.body, scope);
|
|
3994
|
+
if (node.handler) visit$1(node.handler, scope);
|
|
2982
3995
|
return;
|
|
2983
3996
|
case "Recurse":
|
|
2984
3997
|
case "Iterate":
|
|
2985
3998
|
case "Break": return;
|
|
2986
3999
|
case "Label":
|
|
2987
|
-
visit(node.body, scope);
|
|
4000
|
+
visit$1(node.body, scope);
|
|
2988
4001
|
return;
|
|
2989
4002
|
case "Slice":
|
|
2990
|
-
visit(node.target, scope);
|
|
2991
|
-
if (node.start) visit(node.start, scope);
|
|
2992
|
-
if (node.end) visit(node.end, scope);
|
|
4003
|
+
visit$1(node.target, scope);
|
|
4004
|
+
if (node.start) visit$1(node.start, scope);
|
|
4005
|
+
if (node.end) visit$1(node.end, scope);
|
|
2993
4006
|
return;
|
|
2994
4007
|
default: return node;
|
|
2995
4008
|
}
|
|
2996
4009
|
};
|
|
2997
|
-
|
|
2998
4010
|
//#endregion
|
|
2999
4011
|
//#region src/eval/break.ts
|
|
3000
4012
|
/**
|
|
@@ -3008,7 +4020,6 @@ var BreakSignal = class BreakSignal extends Error {
|
|
|
3008
4020
|
Object.setPrototypeOf(this, BreakSignal.prototype);
|
|
3009
4021
|
}
|
|
3010
4022
|
};
|
|
3011
|
-
|
|
3012
4023
|
//#endregion
|
|
3013
4024
|
//#region src/limits.ts
|
|
3014
4025
|
const DEFAULT_LIMITS = {
|
|
@@ -3035,8 +4046,27 @@ var LimitTracker = class {
|
|
|
3035
4046
|
steps = 0;
|
|
3036
4047
|
depth = 0;
|
|
3037
4048
|
outputs = 0;
|
|
3038
|
-
|
|
4049
|
+
/**
|
|
4050
|
+
* @param limits - Resolved execution limits.
|
|
4051
|
+
* @param nowSeconds - The wall-clock instant exposed to the `now` builtin, as
|
|
4052
|
+
* seconds since the Unix epoch (may be fractional). When `undefined`, the
|
|
4053
|
+
* `now` builtin throws rather than reading the host clock — jq-ts never
|
|
4054
|
+
* reads the real clock on its own, so date programs stay deterministic
|
|
4055
|
+
* unless the caller injects a clock via `options.now`.
|
|
4056
|
+
*/
|
|
4057
|
+
constructor(limits, nowSeconds) {
|
|
3039
4058
|
this.limits = limits;
|
|
4059
|
+
this.nowSeconds = nowSeconds;
|
|
4060
|
+
}
|
|
4061
|
+
/**
|
|
4062
|
+
* Returns the injected wall-clock instant (seconds since the Unix epoch) for
|
|
4063
|
+
* the `now` builtin.
|
|
4064
|
+
* @param span - The source span for error reporting.
|
|
4065
|
+
* @throws {RuntimeError} If no clock was injected (`options.now` was unset).
|
|
4066
|
+
*/
|
|
4067
|
+
now(span) {
|
|
4068
|
+
if (this.nowSeconds === void 0) throw new RuntimeError("now/0 requires an injected clock; pass options.now (a Date or epoch seconds)", span);
|
|
4069
|
+
return this.nowSeconds;
|
|
3040
4070
|
}
|
|
3041
4071
|
/**
|
|
3042
4072
|
* Records a single execution step (AST node visit).
|
|
@@ -3069,7 +4099,6 @@ var LimitTracker = class {
|
|
|
3069
4099
|
if (this.outputs > this.limits.maxOutputs) throw new RuntimeError("Output limit exceeded", span);
|
|
3070
4100
|
}
|
|
3071
4101
|
};
|
|
3072
|
-
|
|
3073
4102
|
//#endregion
|
|
3074
4103
|
//#region src/eval/env.ts
|
|
3075
4104
|
/**
|
|
@@ -3079,7 +4108,26 @@ var LimitTracker = class {
|
|
|
3079
4108
|
const getVar = (env, name) => {
|
|
3080
4109
|
for (let i = env.length - 1; i >= 0; i--) if (env[i].vars.has(name)) return env[i].vars.get(name);
|
|
3081
4110
|
};
|
|
3082
|
-
|
|
4111
|
+
/**
|
|
4112
|
+
* Binds jq `as` destructuring patterns into a variable map.
|
|
4113
|
+
*/
|
|
4114
|
+
const bindPattern = (pattern, value, bindings) => {
|
|
4115
|
+
switch (pattern.kind) {
|
|
4116
|
+
case "VariablePattern":
|
|
4117
|
+
bindings.set(pattern.name, value);
|
|
4118
|
+
return;
|
|
4119
|
+
case "ArrayPattern":
|
|
4120
|
+
if (!Array.isArray(value)) throw new RuntimeError(`Cannot index ${describeType(value)} with number`, pattern.span);
|
|
4121
|
+
pattern.items.forEach((item, index) => {
|
|
4122
|
+
bindPattern(item, index < value.length ? value[index] : null, bindings);
|
|
4123
|
+
});
|
|
4124
|
+
return;
|
|
4125
|
+
case "ObjectPattern":
|
|
4126
|
+
if (!isPlainObject$1(value)) throw new RuntimeError(`Cannot index ${describeType(value)} with string`, pattern.span);
|
|
4127
|
+
for (const entry of pattern.entries) bindPattern(entry.pattern, Object.prototype.hasOwnProperty.call(value, entry.key) ? value[entry.key] : null, bindings);
|
|
4128
|
+
return;
|
|
4129
|
+
}
|
|
4130
|
+
};
|
|
3083
4131
|
//#endregion
|
|
3084
4132
|
//#region src/eval/common.ts
|
|
3085
4133
|
/**
|
|
@@ -3111,7 +4159,6 @@ const toIndex = (value, span) => {
|
|
|
3111
4159
|
}
|
|
3112
4160
|
throw new RuntimeError("Expected numeric index", span);
|
|
3113
4161
|
};
|
|
3114
|
-
|
|
3115
4162
|
//#endregion
|
|
3116
4163
|
//#region src/eval/assignment.ts
|
|
3117
4164
|
/**
|
|
@@ -3127,15 +4174,15 @@ const toIndex = (value, span) => {
|
|
|
3127
4174
|
* @param tracker - Limits tracker.
|
|
3128
4175
|
* @param evaluate - Recursive evaluator.
|
|
3129
4176
|
*/
|
|
3130
|
-
const evalAssignment = function* (node, input, env, tracker, evaluate
|
|
3131
|
-
const paths = Array.from(evaluatePath(node.left, input, env, tracker, evaluate
|
|
4177
|
+
const evalAssignment = function* (node, input, env, tracker, evaluate) {
|
|
4178
|
+
const paths = Array.from(evaluatePath(node.left, input, env, tracker, evaluate));
|
|
3132
4179
|
paths.sort((a, b) => compareValues(a, b) * -1);
|
|
3133
4180
|
if (paths.length === 0) {
|
|
3134
4181
|
yield emit(input, node.span, tracker);
|
|
3135
4182
|
return;
|
|
3136
4183
|
}
|
|
3137
4184
|
if (node.op === "=") {
|
|
3138
|
-
const rhsValues = Array.from(evaluate
|
|
4185
|
+
const rhsValues = Array.from(evaluate(node.right, input, env, tracker));
|
|
3139
4186
|
if (rhsValues.length === 0) return;
|
|
3140
4187
|
for (const rhsVal of rhsValues) {
|
|
3141
4188
|
let current = input;
|
|
@@ -3144,9 +4191,9 @@ const evalAssignment = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3144
4191
|
}
|
|
3145
4192
|
return;
|
|
3146
4193
|
}
|
|
3147
|
-
yield* applyUpdates(input, paths, 0, node.op, node.right, input, env, tracker, evaluate
|
|
4194
|
+
yield* applyUpdates(input, paths, 0, node.op, node.right, input, env, tracker, evaluate);
|
|
3148
4195
|
};
|
|
3149
|
-
function* applyUpdates(current, paths, index, op, rhsNode, contextInput, env, tracker, evaluate
|
|
4196
|
+
function* applyUpdates(current, paths, index, op, rhsNode, contextInput, env, tracker, evaluate) {
|
|
3150
4197
|
if (index >= paths.length) {
|
|
3151
4198
|
yield current;
|
|
3152
4199
|
return;
|
|
@@ -3154,9 +4201,9 @@ function* applyUpdates(current, paths, index, op, rhsNode, contextInput, env, tr
|
|
|
3154
4201
|
const path = paths[index];
|
|
3155
4202
|
const oldValue = getPath(current, path) ?? null;
|
|
3156
4203
|
let newValues = [];
|
|
3157
|
-
if (op === "|=") newValues = Array.from(evaluate
|
|
4204
|
+
if (op === "|=") newValues = Array.from(evaluate(rhsNode, oldValue, env, tracker));
|
|
3158
4205
|
else {
|
|
3159
|
-
const rhsResults = Array.from(evaluate
|
|
4206
|
+
const rhsResults = Array.from(evaluate(rhsNode, contextInput, env, tracker));
|
|
3160
4207
|
for (const rhs of rhsResults) {
|
|
3161
4208
|
let res;
|
|
3162
4209
|
switch (op) {
|
|
@@ -3184,12 +4231,11 @@ function* applyUpdates(current, paths, index, op, rhsNode, contextInput, env, tr
|
|
|
3184
4231
|
}
|
|
3185
4232
|
}
|
|
3186
4233
|
if (newValues.length === 0) {
|
|
3187
|
-
yield* applyUpdates(deletePaths(current, [path], rhsNode.span), paths, index + 1, op, rhsNode, contextInput, env, tracker, evaluate
|
|
4234
|
+
yield* applyUpdates(deletePaths(current, [path], rhsNode.span), paths, index + 1, op, rhsNode, contextInput, env, tracker, evaluate);
|
|
3188
4235
|
return;
|
|
3189
4236
|
}
|
|
3190
|
-
for (const val of newValues) yield* applyUpdates(updatePath(current, path, () => val, rhsNode.span) ?? current, paths, index + 1, op, rhsNode, contextInput, env, tracker, evaluate
|
|
4237
|
+
for (const val of newValues) yield* applyUpdates(updatePath(current, path, () => val, rhsNode.span) ?? current, paths, index + 1, op, rhsNode, contextInput, env, tracker, evaluate);
|
|
3191
4238
|
}
|
|
3192
|
-
|
|
3193
4239
|
//#endregion
|
|
3194
4240
|
//#region src/eval/iterators.ts
|
|
3195
4241
|
/**
|
|
@@ -3201,14 +4247,14 @@ function* applyUpdates(current, paths, index, op, rhsNode, contextInput, env, tr
|
|
|
3201
4247
|
* @param tracker - Limits tracker.
|
|
3202
4248
|
* @param evaluate - Recursive evaluator.
|
|
3203
4249
|
*/
|
|
3204
|
-
const evalIterate = function* (node, input, env, tracker, evaluate
|
|
3205
|
-
for (const container of evaluate
|
|
4250
|
+
const evalIterate = function* (node, input, env, tracker, evaluate) {
|
|
4251
|
+
for (const container of evaluate(node.target, input, env, tracker)) {
|
|
3206
4252
|
if (container === null) continue;
|
|
3207
4253
|
if (isValueArray(container)) {
|
|
3208
4254
|
for (const item of container) yield emit(item, node.span, tracker);
|
|
3209
4255
|
continue;
|
|
3210
4256
|
}
|
|
3211
|
-
if (isPlainObject(container)) {
|
|
4257
|
+
if (isPlainObject$1(container)) {
|
|
3212
4258
|
const keys = Object.keys(container).sort();
|
|
3213
4259
|
for (const key of keys) yield emit(container[key], node.span, tracker);
|
|
3214
4260
|
continue;
|
|
@@ -3227,18 +4273,20 @@ const evalIterate = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3227
4273
|
* @param tracker - Limits tracker.
|
|
3228
4274
|
* @param evaluate - Recursive evaluator.
|
|
3229
4275
|
*/
|
|
3230
|
-
const evalReduce = function* (node, input, env, tracker, evaluate
|
|
3231
|
-
const initValues = Array.from(evaluate
|
|
4276
|
+
const evalReduce = function* (node, input, env, tracker, evaluate) {
|
|
4277
|
+
const initValues = Array.from(evaluate(node.init, input, env, tracker));
|
|
3232
4278
|
if (initValues.length !== 1) throw new RuntimeError("Reduce init must single value", node.init.span);
|
|
3233
4279
|
let acc = initValues[0];
|
|
3234
|
-
for (const item of evaluate
|
|
4280
|
+
for (const item of evaluate(node.source, input, env, tracker)) {
|
|
3235
4281
|
tracker.step(node.span);
|
|
4282
|
+
const vars = /* @__PURE__ */ new Map();
|
|
4283
|
+
bindPattern(node.pattern, item, vars);
|
|
3236
4284
|
const newFrame = {
|
|
3237
|
-
vars
|
|
4285
|
+
vars,
|
|
3238
4286
|
funcs: /* @__PURE__ */ new Map()
|
|
3239
4287
|
};
|
|
3240
4288
|
const newEnv = [...env, newFrame];
|
|
3241
|
-
const updates = Array.from(evaluate
|
|
4289
|
+
const updates = Array.from(evaluate(node.update, acc, newEnv, tracker));
|
|
3242
4290
|
if (updates.length !== 1) throw new RuntimeError("Reduce update must produce single value", node.update.span);
|
|
3243
4291
|
acc = updates[0];
|
|
3244
4292
|
}
|
|
@@ -3255,21 +4303,23 @@ const evalReduce = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3255
4303
|
* @param tracker - Limits tracker.
|
|
3256
4304
|
* @param evaluate - Recursive evaluator.
|
|
3257
4305
|
*/
|
|
3258
|
-
const evalForeach = function* (node, input, env, tracker, evaluate
|
|
3259
|
-
const initValues = Array.from(evaluate
|
|
4306
|
+
const evalForeach = function* (node, input, env, tracker, evaluate) {
|
|
4307
|
+
const initValues = Array.from(evaluate(node.init, input, env, tracker));
|
|
3260
4308
|
if (initValues.length !== 1) throw new RuntimeError("Foreach init must single value", node.init.span);
|
|
3261
4309
|
let acc = initValues[0];
|
|
3262
|
-
for (const item of evaluate
|
|
4310
|
+
for (const item of evaluate(node.source, input, env, tracker)) {
|
|
3263
4311
|
tracker.step(node.span);
|
|
4312
|
+
const vars = /* @__PURE__ */ new Map();
|
|
4313
|
+
bindPattern(node.pattern, item, vars);
|
|
3264
4314
|
const newFrame = {
|
|
3265
|
-
vars
|
|
4315
|
+
vars,
|
|
3266
4316
|
funcs: /* @__PURE__ */ new Map()
|
|
3267
4317
|
};
|
|
3268
4318
|
const newEnv = [...env, newFrame];
|
|
3269
|
-
const updates = Array.from(evaluate
|
|
4319
|
+
const updates = Array.from(evaluate(node.update, acc, newEnv, tracker));
|
|
3270
4320
|
if (updates.length !== 1) throw new RuntimeError("Foreach update must produce single value", node.update.span);
|
|
3271
4321
|
acc = updates[0];
|
|
3272
|
-
if (node.extract) for (const extracted of evaluate
|
|
4322
|
+
if (node.extract) for (const extracted of evaluate(node.extract, acc, newEnv, tracker)) yield emit(extracted, node.span, tracker);
|
|
3273
4323
|
else yield emit(acc, node.span, tracker);
|
|
3274
4324
|
}
|
|
3275
4325
|
};
|
|
@@ -3283,17 +4333,16 @@ const evalForeach = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3283
4333
|
* @param tracker - Limits tracker.
|
|
3284
4334
|
* @param evaluate - Recursive evaluator.
|
|
3285
4335
|
*/
|
|
3286
|
-
const evalRecurse = function* (node, input, env, tracker, evaluate
|
|
4336
|
+
const evalRecurse = function* (node, input, env, tracker, evaluate) {
|
|
3287
4337
|
yield emit(input, node.span, tracker);
|
|
3288
4338
|
const children = [];
|
|
3289
4339
|
if (isValueArray(input)) children.push(...input);
|
|
3290
|
-
else if (isPlainObject(input)) {
|
|
4340
|
+
else if (isPlainObject$1(input)) {
|
|
3291
4341
|
const keys = Object.keys(input).sort();
|
|
3292
4342
|
for (const key of keys) children.push(input[key]);
|
|
3293
4343
|
}
|
|
3294
|
-
for (const child of children) yield* evalRecurse(node, child, env, tracker, evaluate
|
|
4344
|
+
for (const child of children) yield* evalRecurse(node, child, env, tracker, evaluate);
|
|
3295
4345
|
};
|
|
3296
|
-
|
|
3297
4346
|
//#endregion
|
|
3298
4347
|
//#region src/eval/access.ts
|
|
3299
4348
|
/**
|
|
@@ -3305,13 +4354,13 @@ const evalRecurse = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3305
4354
|
* @param tracker - Limits tracker.
|
|
3306
4355
|
* @param evaluate - Recursive evaluator.
|
|
3307
4356
|
*/
|
|
3308
|
-
const evalField = function* (node, input, env, tracker, evaluate
|
|
3309
|
-
for (const container of evaluate
|
|
4357
|
+
const evalField = function* (node, input, env, tracker, evaluate) {
|
|
4358
|
+
for (const container of evaluate(node.target, input, env, tracker)) {
|
|
3310
4359
|
if (container === null) {
|
|
3311
4360
|
yield emit(null, node.span, tracker);
|
|
3312
4361
|
continue;
|
|
3313
4362
|
}
|
|
3314
|
-
if (isPlainObject(container)) {
|
|
4363
|
+
if (isPlainObject$1(container)) {
|
|
3315
4364
|
yield emit(Object.prototype.hasOwnProperty.call(container, node.field) ? container[node.field] : null, node.span, tracker);
|
|
3316
4365
|
continue;
|
|
3317
4366
|
}
|
|
@@ -3328,9 +4377,9 @@ const evalField = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3328
4377
|
* @param tracker - Limits tracker.
|
|
3329
4378
|
* @param evaluate - Recursive evaluator.
|
|
3330
4379
|
*/
|
|
3331
|
-
const evalIndex = function* (node, input, env, tracker, evaluate
|
|
3332
|
-
const indexValues = Array.from(evaluate
|
|
3333
|
-
for (const container of evaluate
|
|
4380
|
+
const evalIndex = function* (node, input, env, tracker, evaluate) {
|
|
4381
|
+
const indexValues = Array.from(evaluate(node.index, input, env, tracker));
|
|
4382
|
+
for (const container of evaluate(node.target, input, env, tracker)) {
|
|
3334
4383
|
if (container === null) {
|
|
3335
4384
|
yield emit(null, node.span, tracker);
|
|
3336
4385
|
continue;
|
|
@@ -3348,7 +4397,7 @@ const evalIndex = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3348
4397
|
}
|
|
3349
4398
|
continue;
|
|
3350
4399
|
}
|
|
3351
|
-
if (isPlainObject(container)) {
|
|
4400
|
+
if (isPlainObject$1(container)) {
|
|
3352
4401
|
for (const keyValue of indexValues) {
|
|
3353
4402
|
if (typeof keyValue !== "string") throw new RuntimeError(`Cannot index object with ${describeType(keyValue)}`, node.span);
|
|
3354
4403
|
yield emit(Object.prototype.hasOwnProperty.call(container, keyValue) ? container[keyValue] : null, node.span, tracker);
|
|
@@ -3368,25 +4417,26 @@ const evalIndex = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3368
4417
|
* @param tracker - Limits tracker.
|
|
3369
4418
|
* @param evaluate - Recursive evaluator.
|
|
3370
4419
|
*/
|
|
3371
|
-
const evalSlice = function* (node, input, env, tracker, evaluate
|
|
3372
|
-
for (const target of evaluate
|
|
4420
|
+
const evalSlice = function* (node, input, env, tracker, evaluate) {
|
|
4421
|
+
for (const target of evaluate(node.target, input, env, tracker)) {
|
|
3373
4422
|
if (typeof target !== "string" && !Array.isArray(target)) throw new RuntimeError("Slice expected string or array", node.span);
|
|
3374
4423
|
const starts = [];
|
|
3375
|
-
if (node.start) for (const s of evaluate
|
|
4424
|
+
if (node.start) for (const s of evaluate(node.start, input, env, tracker)) {
|
|
3376
4425
|
if (typeof s !== "number") throw new RuntimeError("Slice start must be number", node.span);
|
|
3377
4426
|
starts.push(s);
|
|
3378
4427
|
}
|
|
3379
4428
|
else starts.push(0);
|
|
3380
4429
|
const ends = [];
|
|
3381
|
-
if (node.end) for (const e of evaluate
|
|
4430
|
+
if (node.end) for (const e of evaluate(node.end, input, env, tracker)) {
|
|
3382
4431
|
if (typeof e !== "number") throw new RuntimeError("Slice end must be number", node.span);
|
|
3383
4432
|
ends.push(e);
|
|
3384
4433
|
}
|
|
3385
4434
|
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);
|
|
4435
|
+
for (const s of starts) for (const e of ends) yield emit(target.slice(normalizeSliceStart(s), normalizeSliceEnd(e)), node.span, tracker);
|
|
3387
4436
|
}
|
|
3388
4437
|
};
|
|
3389
|
-
|
|
4438
|
+
const normalizeSliceStart = (value) => Math.floor(value);
|
|
4439
|
+
const normalizeSliceEnd = (value) => Math.ceil(value);
|
|
3390
4440
|
//#endregion
|
|
3391
4441
|
//#region src/eval/constructors.ts
|
|
3392
4442
|
/**
|
|
@@ -3401,9 +4451,9 @@ const evalSlice = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3401
4451
|
* @param tracker - Limits tracker.
|
|
3402
4452
|
* @param evaluate - Recursive evaluator.
|
|
3403
4453
|
*/
|
|
3404
|
-
const buildArray = function* (node, input, env, tracker, evaluate
|
|
4454
|
+
const buildArray = function* (node, input, env, tracker, evaluate) {
|
|
3405
4455
|
const result = [];
|
|
3406
|
-
for (const itemNode of node.items) for (const itemVal of evaluate
|
|
4456
|
+
for (const itemNode of node.items) for (const itemVal of evaluate(itemNode, input, env, tracker)) result.push(itemVal);
|
|
3407
4457
|
yield result;
|
|
3408
4458
|
};
|
|
3409
4459
|
/**
|
|
@@ -3418,10 +4468,10 @@ const buildArray = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3418
4468
|
* @param tracker - Limits tracker.
|
|
3419
4469
|
* @param evaluate - Recursive evaluator.
|
|
3420
4470
|
*/
|
|
3421
|
-
const buildObjects = function* (node, input, env, tracker, evaluate
|
|
3422
|
-
yield* fillObject(node.entries, 0, {}, input, env, tracker, evaluate
|
|
4471
|
+
const buildObjects = function* (node, input, env, tracker, evaluate) {
|
|
4472
|
+
yield* fillObject(node.entries, 0, {}, input, env, tracker, evaluate);
|
|
3423
4473
|
};
|
|
3424
|
-
function* fillObject(entries, index, current, input, env, tracker, evaluate
|
|
4474
|
+
function* fillObject(entries, index, current, input, env, tracker, evaluate) {
|
|
3425
4475
|
if (index >= entries.length) {
|
|
3426
4476
|
yield { ...current };
|
|
3427
4477
|
return;
|
|
@@ -3430,17 +4480,16 @@ function* fillObject(entries, index, current, input, env, tracker, evaluate$1) {
|
|
|
3430
4480
|
let keys = [];
|
|
3431
4481
|
if (entry.key.kind === "KeyIdentifier") keys = [entry.key.name];
|
|
3432
4482
|
else if (entry.key.kind === "KeyString") keys = [entry.key.value];
|
|
3433
|
-
else for (const k of evaluate
|
|
4483
|
+
else for (const k of evaluate(entry.key.expr, input, env, tracker)) {
|
|
3434
4484
|
if (typeof k !== "string") throw new RuntimeError("Object key must be a string", entry.key.span);
|
|
3435
4485
|
keys.push(k);
|
|
3436
4486
|
}
|
|
3437
|
-
for (const key of keys) for (const val of evaluate
|
|
4487
|
+
for (const key of keys) for (const val of evaluate(entry.value, input, env, tracker)) {
|
|
3438
4488
|
current[key] = val;
|
|
3439
|
-
yield* fillObject(entries, index + 1, current, input, env, tracker, evaluate
|
|
4489
|
+
yield* fillObject(entries, index + 1, current, input, env, tracker, evaluate);
|
|
3440
4490
|
delete current[key];
|
|
3441
4491
|
}
|
|
3442
4492
|
}
|
|
3443
|
-
|
|
3444
4493
|
//#endregion
|
|
3445
4494
|
//#region src/eval/functions.ts
|
|
3446
4495
|
/**
|
|
@@ -3458,7 +4507,7 @@ function* fillObject(entries, index, current, input, env, tracker, evaluate$1) {
|
|
|
3458
4507
|
* @param tracker - Limits tracker.
|
|
3459
4508
|
* @param evaluate - Recursive evaluator.
|
|
3460
4509
|
*/
|
|
3461
|
-
const evalCall = function* (node, input, env, tracker, evaluate
|
|
4510
|
+
const evalCall = function* (node, input, env, tracker, evaluate) {
|
|
3462
4511
|
for (let i = env.length - 1; i >= 0; i--) {
|
|
3463
4512
|
const funcs = env[i].funcs.get(node.name);
|
|
3464
4513
|
if (funcs) {
|
|
@@ -3480,7 +4529,7 @@ const evalCall = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3480
4529
|
newFrame.funcs.set(argName, argDefs);
|
|
3481
4530
|
}
|
|
3482
4531
|
const newStack = [...def.closure, newFrame];
|
|
3483
|
-
yield* evaluate
|
|
4532
|
+
yield* evaluate(def.body, input, newStack, tracker);
|
|
3484
4533
|
return;
|
|
3485
4534
|
}
|
|
3486
4535
|
}
|
|
@@ -3489,7 +4538,7 @@ const evalCall = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3489
4538
|
if (!specs) throw new RuntimeError(`Unknown function: ${node.name}`, node.span);
|
|
3490
4539
|
const builtin = specs.find((s) => s.arity === node.args.length);
|
|
3491
4540
|
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
|
|
4541
|
+
yield* builtin.apply(input, node.args, env, tracker, evaluate, node.span);
|
|
3493
4542
|
};
|
|
3494
4543
|
/**
|
|
3495
4544
|
* Defines a new function in the environment.
|
|
@@ -3503,7 +4552,7 @@ const evalCall = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3503
4552
|
* @param tracker - Limits tracker.
|
|
3504
4553
|
* @param evaluate - Recursive evaluator.
|
|
3505
4554
|
*/
|
|
3506
|
-
const evalDef = function* (node, input, env, tracker, evaluate
|
|
4555
|
+
const evalDef = function* (node, input, env, tracker, evaluate) {
|
|
3507
4556
|
const newFrame = {
|
|
3508
4557
|
vars: /* @__PURE__ */ new Map(),
|
|
3509
4558
|
funcs: /* @__PURE__ */ new Map()
|
|
@@ -3518,9 +4567,8 @@ const evalDef = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3518
4567
|
newFrame.funcs.set(node.name, currentDefs);
|
|
3519
4568
|
const newStack = [...env, newFrame];
|
|
3520
4569
|
funDef.closure = newStack;
|
|
3521
|
-
yield* evaluate
|
|
4570
|
+
yield* evaluate(node.next, input, newStack, tracker);
|
|
3522
4571
|
};
|
|
3523
|
-
|
|
3524
4572
|
//#endregion
|
|
3525
4573
|
//#region src/eval/control_flow.ts
|
|
3526
4574
|
/**
|
|
@@ -3532,17 +4580,17 @@ const evalDef = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3532
4580
|
* @param tracker - Limits tracker.
|
|
3533
4581
|
* @param evaluate - Recursive evaluator.
|
|
3534
4582
|
*/
|
|
3535
|
-
const evalIf = function* (node, input, env, tracker, evaluate
|
|
3536
|
-
yield* evalIfBranch(node, 0, input, env, tracker, evaluate
|
|
4583
|
+
const evalIf = function* (node, input, env, tracker, evaluate) {
|
|
4584
|
+
yield* evalIfBranch(node, 0, input, env, tracker, evaluate);
|
|
3537
4585
|
};
|
|
3538
|
-
function* evalIfBranch(node, branchIndex, input, env, tracker, evaluate
|
|
4586
|
+
function* evalIfBranch(node, branchIndex, input, env, tracker, evaluate) {
|
|
3539
4587
|
if (branchIndex >= node.branches.length) {
|
|
3540
|
-
yield* evaluate
|
|
4588
|
+
yield* evaluate(node.else, input, env, tracker);
|
|
3541
4589
|
return;
|
|
3542
4590
|
}
|
|
3543
4591
|
const branch = node.branches[branchIndex];
|
|
3544
|
-
for (const cond of evaluate
|
|
3545
|
-
else yield* evalIfBranch(node, branchIndex + 1, input, env, tracker, evaluate
|
|
4592
|
+
for (const cond of evaluate(branch.cond, input, env, tracker)) if (isTruthy(cond)) yield* evaluate(branch.then, input, env, tracker);
|
|
4593
|
+
else yield* evalIfBranch(node, branchIndex + 1, input, env, tracker, evaluate);
|
|
3546
4594
|
}
|
|
3547
4595
|
/**
|
|
3548
4596
|
* Evaluates a `try-catch` expression.
|
|
@@ -3553,12 +4601,12 @@ function* evalIfBranch(node, branchIndex, input, env, tracker, evaluate$1) {
|
|
|
3553
4601
|
* @param tracker - Limits tracker.
|
|
3554
4602
|
* @param evaluate - Recursive evaluator.
|
|
3555
4603
|
*/
|
|
3556
|
-
const evalTry = function* (node, input, env, tracker, evaluate
|
|
4604
|
+
const evalTry = function* (node, input, env, tracker, evaluate) {
|
|
3557
4605
|
try {
|
|
3558
|
-
yield* evaluate
|
|
4606
|
+
yield* evaluate(node.body, input, env, tracker);
|
|
3559
4607
|
} catch (err) {
|
|
3560
4608
|
if (err instanceof RuntimeError) {
|
|
3561
|
-
if (node.handler) yield* evaluate
|
|
4609
|
+
if (node.handler) yield* evaluate(node.handler, err.message, env, tracker);
|
|
3562
4610
|
} else throw err;
|
|
3563
4611
|
}
|
|
3564
4612
|
};
|
|
@@ -3571,9 +4619,9 @@ const evalTry = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3571
4619
|
* @param tracker - Limits tracker.
|
|
3572
4620
|
* @param evaluate - Recursive evaluator.
|
|
3573
4621
|
*/
|
|
3574
|
-
const evalLabel = function* (node, input, env, tracker, evaluate
|
|
4622
|
+
const evalLabel = function* (node, input, env, tracker, evaluate) {
|
|
3575
4623
|
try {
|
|
3576
|
-
yield* evaluate
|
|
4624
|
+
yield* evaluate(node.body, input, env, tracker);
|
|
3577
4625
|
} catch (e) {
|
|
3578
4626
|
if (e instanceof BreakSignal) {
|
|
3579
4627
|
if (e.label === node.label) return;
|
|
@@ -3581,10 +4629,16 @@ const evalLabel = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3581
4629
|
throw e;
|
|
3582
4630
|
}
|
|
3583
4631
|
};
|
|
3584
|
-
|
|
3585
4632
|
//#endregion
|
|
3586
4633
|
//#region src/eval/dispatch.ts
|
|
3587
4634
|
/**
|
|
4635
|
+
* Normalizes the `now` option to seconds since the Unix epoch.
|
|
4636
|
+
*/
|
|
4637
|
+
const resolveNowSeconds = (now) => {
|
|
4638
|
+
if (now === void 0) return void 0;
|
|
4639
|
+
return now instanceof Date ? now.getTime() / 1e3 : now;
|
|
4640
|
+
};
|
|
4641
|
+
/**
|
|
3588
4642
|
* Runs a jq AST against an input value.
|
|
3589
4643
|
*
|
|
3590
4644
|
* @param ast - The parsed AST.
|
|
@@ -3593,7 +4647,7 @@ const evalLabel = function* (node, input, env, tracker, evaluate$1) {
|
|
|
3593
4647
|
* @returns An array of all values yielded by the filter.
|
|
3594
4648
|
*/
|
|
3595
4649
|
const runAst = (ast, input, options = {}) => {
|
|
3596
|
-
const tracker = new LimitTracker(resolveLimits(options.limits));
|
|
4650
|
+
const tracker = new LimitTracker(resolveLimits(options.limits), resolveNowSeconds(options.now));
|
|
3597
4651
|
const env = [{
|
|
3598
4652
|
vars: new Map(Object.entries(options.vars ?? {})),
|
|
3599
4653
|
funcs: /* @__PURE__ */ new Map()
|
|
@@ -3702,8 +4756,10 @@ function* evaluate(node, input, env, tracker) {
|
|
|
3702
4756
|
case "As": {
|
|
3703
4757
|
const values = Array.from(evaluate(node.bind, input, env, tracker));
|
|
3704
4758
|
for (const val of values) {
|
|
4759
|
+
const vars = /* @__PURE__ */ new Map();
|
|
4760
|
+
bindPattern(node.pattern, val, vars);
|
|
3705
4761
|
const newFrame = {
|
|
3706
|
-
vars
|
|
4762
|
+
vars,
|
|
3707
4763
|
funcs: /* @__PURE__ */ new Map()
|
|
3708
4764
|
};
|
|
3709
4765
|
const newEnv = [...env, newFrame];
|
|
@@ -3722,7 +4778,300 @@ function* evaluate(node, input, env, tracker) {
|
|
|
3722
4778
|
tracker.exit();
|
|
3723
4779
|
}
|
|
3724
4780
|
}
|
|
3725
|
-
|
|
4781
|
+
//#endregion
|
|
4782
|
+
//#region src/compat.ts
|
|
4783
|
+
const semanticWarnings = {
|
|
4784
|
+
unique: "jq sorts unique results; jq-ts preserves first-seen order for determinism.",
|
|
4785
|
+
unique_by: "jq sorts unique_by results by key; jq-ts preserves first-seen order.",
|
|
4786
|
+
to_entries: "jq preserves object insertion order; jq-ts sorts object keys deterministically.",
|
|
4787
|
+
with_entries: "jq preserves object insertion order; jq-ts processes object keys in sorted order.",
|
|
4788
|
+
tostring: "jq stringifies objects in input key order; jq-ts uses stable sorted-key stringification.",
|
|
4789
|
+
tojson: "jq tojson preserves input object order; jq-ts uses stable sorted-key stringification.",
|
|
4790
|
+
infinite: "jq serializes infinite as a finite JSON number; jq-ts returns JavaScript Infinity internally.",
|
|
4791
|
+
normal: "jq documents isnormal; jq-ts exposes normal with approximate JavaScript number semantics.",
|
|
4792
|
+
subnormal: "jq documents subnormal classification; jq-ts currently approximates this as false."
|
|
4793
|
+
};
|
|
4794
|
+
const specialVariableWarnings = {
|
|
4795
|
+
ENV: "jq $ENV is an environment snapshot; jq-ts does not populate it unless the caller injects an ENV variable.",
|
|
4796
|
+
__loc__: "jq $__loc__ reports source location metadata; jq-ts does not populate it unless the caller injects a __loc__ variable.",
|
|
4797
|
+
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.",
|
|
4798
|
+
ARGS: "jq $ARGS is populated from CLI arguments; jq-ts does not populate it unless the caller injects an ARGS variable."
|
|
4799
|
+
};
|
|
4800
|
+
const syntaxWarningsByKind = { Slice: "jq slice bounds have jq-specific numeric coercion; jq-ts uses JavaScript slice semantics." };
|
|
4801
|
+
/**
|
|
4802
|
+
* Checks whether a jq expression can be parsed and statically validated by jq-ts.
|
|
4803
|
+
*
|
|
4804
|
+
* This is a syntax/subset check only. It does not prove that runtime behavior
|
|
4805
|
+
* matches the jq binary for every possible input.
|
|
4806
|
+
*/
|
|
4807
|
+
const checkCompatibility = (source) => {
|
|
4808
|
+
try {
|
|
4809
|
+
validate(parse(source));
|
|
4810
|
+
return {
|
|
4811
|
+
compatible: true,
|
|
4812
|
+
findings: []
|
|
4813
|
+
};
|
|
4814
|
+
} catch (err) {
|
|
4815
|
+
const finding = toFinding(err);
|
|
4816
|
+
if (finding) return {
|
|
4817
|
+
compatible: false,
|
|
4818
|
+
findings: [finding]
|
|
4819
|
+
};
|
|
4820
|
+
throw err;
|
|
4821
|
+
}
|
|
4822
|
+
};
|
|
4823
|
+
/**
|
|
4824
|
+
* Checks jq-ts compatibility and reports known semantic differences from jq.
|
|
4825
|
+
*/
|
|
4826
|
+
const analyzeCompatibility = (source) => {
|
|
4827
|
+
const check = checkCompatibility(source);
|
|
4828
|
+
if (!check.compatible) return {
|
|
4829
|
+
...check,
|
|
4830
|
+
warnings: []
|
|
4831
|
+
};
|
|
4832
|
+
const warnings = collectWarnings(parse(source));
|
|
4833
|
+
return {
|
|
4834
|
+
compatible: true,
|
|
4835
|
+
findings: warnings,
|
|
4836
|
+
warnings
|
|
4837
|
+
};
|
|
4838
|
+
};
|
|
4839
|
+
/**
|
|
4840
|
+
* Compares jq-ts execution against a caller-provided jq runner or jq result.
|
|
4841
|
+
*
|
|
4842
|
+
* The function is safe to export from the isolate-safe library because it does
|
|
4843
|
+
* not spawn the jq binary itself. Tests and development tools can pass a runner
|
|
4844
|
+
* that shells out to jq.
|
|
4845
|
+
*/
|
|
4846
|
+
const compareWithJq = (source, input, jq, options = {}) => {
|
|
4847
|
+
const analysis = analyzeCompatibility(source);
|
|
4848
|
+
const jqTs = runJqTs(source, input, options);
|
|
4849
|
+
const jqResult = typeof jq === "function" ? runExternalJq(jq, source, input) : jq;
|
|
4850
|
+
const findings = [...analysis.findings];
|
|
4851
|
+
if (!jqTs.ok) findings.push({
|
|
4852
|
+
severity: "error",
|
|
4853
|
+
stage: jqTs.stage ?? "runtime",
|
|
4854
|
+
category: "runtime-error",
|
|
4855
|
+
message: jqTs.error
|
|
4856
|
+
});
|
|
4857
|
+
if (!jqResult.ok) findings.push({
|
|
4858
|
+
severity: "error",
|
|
4859
|
+
stage: "compare",
|
|
4860
|
+
category: "jq-error",
|
|
4861
|
+
message: jqResult.error
|
|
4862
|
+
});
|
|
4863
|
+
const equivalent = jqTs.ok && jqResult.ok ? outputStreamsEqual(jqTs.outputs, jqResult.outputs) : null;
|
|
4864
|
+
if (equivalent === false) findings.push({
|
|
4865
|
+
severity: "error",
|
|
4866
|
+
stage: "compare",
|
|
4867
|
+
category: "output-mismatch",
|
|
4868
|
+
message: "jq-ts output stream does not match jq output stream."
|
|
4869
|
+
});
|
|
4870
|
+
return {
|
|
4871
|
+
compatible: analysis.compatible && jqTs.ok,
|
|
4872
|
+
equivalent,
|
|
4873
|
+
analysis,
|
|
4874
|
+
jqTs,
|
|
4875
|
+
jq: jqResult,
|
|
4876
|
+
findings
|
|
4877
|
+
};
|
|
4878
|
+
};
|
|
4879
|
+
const runJqTs = (source, input, options) => {
|
|
4880
|
+
try {
|
|
4881
|
+
const ast = parse(source);
|
|
4882
|
+
validate(ast);
|
|
4883
|
+
return {
|
|
4884
|
+
ok: true,
|
|
4885
|
+
outputs: runAst(ast, input, options)
|
|
4886
|
+
};
|
|
4887
|
+
} catch (err) {
|
|
4888
|
+
const jqTsError = asJqTsError(err);
|
|
4889
|
+
return {
|
|
4890
|
+
ok: false,
|
|
4891
|
+
error: err instanceof Error ? err.message : String(err),
|
|
4892
|
+
stage: jqTsError?.kind
|
|
4893
|
+
};
|
|
4894
|
+
}
|
|
4895
|
+
};
|
|
4896
|
+
const runExternalJq = (runner, source, input) => {
|
|
4897
|
+
try {
|
|
4898
|
+
const result = runner(source, input);
|
|
4899
|
+
if (Array.isArray(result)) return {
|
|
4900
|
+
ok: true,
|
|
4901
|
+
outputs: result
|
|
4902
|
+
};
|
|
4903
|
+
return result;
|
|
4904
|
+
} catch (err) {
|
|
4905
|
+
return {
|
|
4906
|
+
ok: false,
|
|
4907
|
+
error: err instanceof Error ? err.message : String(err),
|
|
4908
|
+
stage: "compare"
|
|
4909
|
+
};
|
|
4910
|
+
}
|
|
4911
|
+
};
|
|
4912
|
+
const outputStreamsEqual = (left, right) => left.length === right.length && left.every((value, index) => valueEquals(value, right[index]));
|
|
4913
|
+
const toFinding = (err) => {
|
|
4914
|
+
const jqTsError = asJqTsError(err);
|
|
4915
|
+
if (!jqTsError) return null;
|
|
4916
|
+
return {
|
|
4917
|
+
severity: "error",
|
|
4918
|
+
stage: jqTsError.kind,
|
|
4919
|
+
category: classifyError(jqTsError),
|
|
4920
|
+
message: jqTsError.message,
|
|
4921
|
+
span: jqTsError.span
|
|
4922
|
+
};
|
|
4923
|
+
};
|
|
4924
|
+
const asJqTsError = (err) => {
|
|
4925
|
+
if (err instanceof LexError || err instanceof ParseError || err instanceof ValidationError || err instanceof RuntimeError) return err;
|
|
4926
|
+
return null;
|
|
4927
|
+
};
|
|
4928
|
+
const classifyError = (err) => {
|
|
4929
|
+
if (err.kind === "parse" || err.kind === "lex") return "unsupported-syntax";
|
|
4930
|
+
if (err.kind === "runtime") return "runtime-error";
|
|
4931
|
+
if (err.message.startsWith("Unknown function:")) return isIntentionalExclusion(err.message) ? "intentional-exclusion" : "unsupported-builtin";
|
|
4932
|
+
if (err.message.includes("expects") && err.message.includes("arguments")) return "arity-mismatch";
|
|
4933
|
+
return "unsupported-syntax";
|
|
4934
|
+
};
|
|
4935
|
+
const isIntentionalExclusion = (message) => [
|
|
4936
|
+
"input",
|
|
4937
|
+
"inputs",
|
|
4938
|
+
"env"
|
|
4939
|
+
].some((name) => message === `Unknown function: ${name}`);
|
|
4940
|
+
const collectWarnings = (node) => {
|
|
4941
|
+
const warnings = [];
|
|
4942
|
+
visit(node, (current) => {
|
|
4943
|
+
const syntaxWarning = syntaxWarningsByKind[current.kind];
|
|
4944
|
+
if (syntaxWarning) warnings.push({
|
|
4945
|
+
severity: "warning",
|
|
4946
|
+
stage: "validate",
|
|
4947
|
+
category: "input-dependent",
|
|
4948
|
+
message: syntaxWarning,
|
|
4949
|
+
span: current.span
|
|
4950
|
+
});
|
|
4951
|
+
if (current.kind === "Call") {
|
|
4952
|
+
const warning = semanticWarnings[current.name];
|
|
4953
|
+
if (warning) warnings.push({
|
|
4954
|
+
severity: "warning",
|
|
4955
|
+
stage: "validate",
|
|
4956
|
+
category: "semantic-deviation",
|
|
4957
|
+
message: warning,
|
|
4958
|
+
span: current.span
|
|
4959
|
+
});
|
|
4960
|
+
}
|
|
4961
|
+
if (current.kind === "Var") {
|
|
4962
|
+
const warning = specialVariableWarnings[current.name];
|
|
4963
|
+
if (warning) warnings.push({
|
|
4964
|
+
severity: "warning",
|
|
4965
|
+
stage: "validate",
|
|
4966
|
+
category: current.name === "ENV" ? "intentional-exclusion" : "semantic-deviation",
|
|
4967
|
+
message: warning,
|
|
4968
|
+
span: current.span
|
|
4969
|
+
});
|
|
4970
|
+
}
|
|
4971
|
+
});
|
|
4972
|
+
return dedupeFindings(warnings);
|
|
4973
|
+
};
|
|
4974
|
+
const dedupeFindings = (findings) => {
|
|
4975
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4976
|
+
const result = [];
|
|
4977
|
+
for (const finding of findings) {
|
|
4978
|
+
const key = `${finding.category}:${finding.message}:${finding.span?.start ?? ""}:${finding.span?.end ?? ""}`;
|
|
4979
|
+
if (seen.has(key)) continue;
|
|
4980
|
+
seen.add(key);
|
|
4981
|
+
result.push(finding);
|
|
4982
|
+
}
|
|
4983
|
+
return result;
|
|
4984
|
+
};
|
|
4985
|
+
const visit = (node, callback) => {
|
|
4986
|
+
callback(node);
|
|
4987
|
+
switch (node.kind) {
|
|
4988
|
+
case "Identity":
|
|
4989
|
+
case "Literal":
|
|
4990
|
+
case "Var":
|
|
4991
|
+
case "Recurse":
|
|
4992
|
+
case "Break": return;
|
|
4993
|
+
case "FieldAccess":
|
|
4994
|
+
visit(node.target, callback);
|
|
4995
|
+
return;
|
|
4996
|
+
case "IndexAccess":
|
|
4997
|
+
visit(node.target, callback);
|
|
4998
|
+
visit(node.index, callback);
|
|
4999
|
+
return;
|
|
5000
|
+
case "Iterate":
|
|
5001
|
+
visit(node.target, callback);
|
|
5002
|
+
return;
|
|
5003
|
+
case "Slice":
|
|
5004
|
+
visit(node.target, callback);
|
|
5005
|
+
if (node.start) visit(node.start, callback);
|
|
5006
|
+
if (node.end) visit(node.end, callback);
|
|
5007
|
+
return;
|
|
5008
|
+
case "Array":
|
|
5009
|
+
node.items.forEach((item) => visit(item, callback));
|
|
5010
|
+
return;
|
|
5011
|
+
case "Object":
|
|
5012
|
+
node.entries.forEach((entry) => visitObjectEntry(entry, callback));
|
|
5013
|
+
return;
|
|
5014
|
+
case "Pipe":
|
|
5015
|
+
case "Comma":
|
|
5016
|
+
case "Alt":
|
|
5017
|
+
case "Binary":
|
|
5018
|
+
case "Bool":
|
|
5019
|
+
visit(node.left, callback);
|
|
5020
|
+
visit(node.right, callback);
|
|
5021
|
+
return;
|
|
5022
|
+
case "Unary":
|
|
5023
|
+
visit(node.expr, callback);
|
|
5024
|
+
return;
|
|
5025
|
+
case "If":
|
|
5026
|
+
node.branches.forEach((branch) => {
|
|
5027
|
+
visit(branch.cond, callback);
|
|
5028
|
+
visit(branch.then, callback);
|
|
5029
|
+
});
|
|
5030
|
+
visit(node.else, callback);
|
|
5031
|
+
return;
|
|
5032
|
+
case "As":
|
|
5033
|
+
visit(node.bind, callback);
|
|
5034
|
+
visit(node.body, callback);
|
|
5035
|
+
return;
|
|
5036
|
+
case "Call":
|
|
5037
|
+
node.args.forEach((arg) => visit(arg, callback));
|
|
5038
|
+
return;
|
|
5039
|
+
case "Reduce":
|
|
5040
|
+
visit(node.source, callback);
|
|
5041
|
+
visit(node.init, callback);
|
|
5042
|
+
visit(node.update, callback);
|
|
5043
|
+
return;
|
|
5044
|
+
case "Foreach":
|
|
5045
|
+
visit(node.source, callback);
|
|
5046
|
+
visit(node.init, callback);
|
|
5047
|
+
visit(node.update, callback);
|
|
5048
|
+
if (node.extract) visit(node.extract, callback);
|
|
5049
|
+
return;
|
|
5050
|
+
case "Try":
|
|
5051
|
+
visit(node.body, callback);
|
|
5052
|
+
if (node.handler) visit(node.handler, callback);
|
|
5053
|
+
return;
|
|
5054
|
+
case "Assignment":
|
|
5055
|
+
visit(node.left, callback);
|
|
5056
|
+
visit(node.right, callback);
|
|
5057
|
+
return;
|
|
5058
|
+
case "Def":
|
|
5059
|
+
visit(node.body, callback);
|
|
5060
|
+
visit(node.next, callback);
|
|
5061
|
+
return;
|
|
5062
|
+
case "Label":
|
|
5063
|
+
visit(node.body, callback);
|
|
5064
|
+
return;
|
|
5065
|
+
default: return node;
|
|
5066
|
+
}
|
|
5067
|
+
};
|
|
5068
|
+
const visitObjectEntry = (entry, callback) => {
|
|
5069
|
+
visitObjectKey(entry.key, callback);
|
|
5070
|
+
visit(entry.value, callback);
|
|
5071
|
+
};
|
|
5072
|
+
const visitObjectKey = (key, callback) => {
|
|
5073
|
+
if (key.kind === "KeyExpr") visit(key.expr, callback);
|
|
5074
|
+
};
|
|
3726
5075
|
//#endregion
|
|
3727
5076
|
//#region src/index.ts
|
|
3728
5077
|
/**
|
|
@@ -3742,7 +5091,7 @@ const run = (source, input, options = {}) => {
|
|
|
3742
5091
|
validate(ast);
|
|
3743
5092
|
return runAst(ast, input, options);
|
|
3744
5093
|
};
|
|
3745
|
-
|
|
3746
5094
|
//#endregion
|
|
3747
|
-
export { LexError, LimitTracker, ParseError, RuntimeError, ValidationError, parse, resolveLimits, run, runAst, validate };
|
|
5095
|
+
export { LexError, LimitTracker, ParseError, RuntimeError, ValidationError, analyzeCompatibility, checkCompatibility, compareWithJq, parse, resolveLimits, run, runAst, validate };
|
|
5096
|
+
|
|
3748
5097
|
//# sourceMappingURL=index.mjs.map
|