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