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