@gabrielbryk/jq-ts 1.1.0 → 1.2.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/dist/index.cjs CHANGED
@@ -61,11 +61,22 @@ const keywordKinds = {
61
61
  reduce: "Reduce",
62
62
  foreach: "Foreach",
63
63
  try: "Try",
64
- catch: "Catch"
64
+ catch: "Catch",
65
+ def: "Def",
66
+ label: "Label",
67
+ break: "Break"
65
68
  };
66
69
 
67
70
  //#endregion
68
71
  //#region src/lexer.ts
72
+ /**
73
+ * Tokenizes the input jq string into a list of AST tokens.
74
+ * Handles string interpolation, comments, and operator grouping.
75
+ *
76
+ * @param text - The source code to tokenize.
77
+ * @returns An array of {@link Token}, including an EOF token at the end.
78
+ * @throws {@link LexError} if an invalid character or sequence is encountered.
79
+ */
69
80
  const lex = (text) => {
70
81
  const tokens = [];
71
82
  const length = text.length;
@@ -87,6 +98,7 @@ const lex = (text) => {
87
98
  const isDigit = (ch) => !!ch && ch >= "0" && ch <= "9";
88
99
  const isIdentifierStart = (ch) => !!ch && (ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z" || ch === "_");
89
100
  const isIdentifierPart = (ch) => isIdentifierStart(ch) || isDigit(ch);
101
+ const modeStack = [0];
90
102
  while (pos < length) {
91
103
  const ch = peek();
92
104
  if (isWhitespace(ch)) {
@@ -98,10 +110,27 @@ const lex = (text) => {
98
110
  continue;
99
111
  }
100
112
  const start = pos;
113
+ if (ch === ")") {
114
+ if (modeStack.length > 1) {
115
+ advance();
116
+ const endPos = readString(start, false);
117
+ const value = readStringValue(start, start + 1, endPos - (peek(-1) === "\"" ? 1 : 2));
118
+ if (text[endPos - 1] === "\"") {
119
+ modeStack.pop();
120
+ pushToken("StringEnd", start, endPos, value);
121
+ } else pushToken("StringMiddle", start, endPos, value);
122
+ continue;
123
+ }
124
+ }
101
125
  switch (ch) {
102
126
  case "\"": {
103
- const endPos = readString(start);
104
- pushToken("String", start, endPos, readStringValue(start, start + 1, endPos - 1));
127
+ const endPos = readString(start, true);
128
+ const isInterp = text.substring(endPos - 2, endPos) === "\\(";
129
+ const value = readStringValue(start, start + 1, endPos - (isInterp ? 2 : 1));
130
+ if (isInterp) {
131
+ modeStack.push(1);
132
+ pushToken("StringStart", start, endPos, value);
133
+ } else pushToken("String", start, endPos, value);
105
134
  continue;
106
135
  }
107
136
  case ".":
@@ -119,15 +148,28 @@ const lex = (text) => {
119
148
  advance();
120
149
  pushToken("Comma", start, pos);
121
150
  continue;
151
+ case "?":
152
+ advance();
153
+ pushToken("Question", start, pos);
154
+ continue;
122
155
  case "|":
123
156
  advance();
124
- pushToken("Pipe", start, pos);
157
+ if (peek() === "=") {
158
+ advance();
159
+ pushToken("BarEq", start, pos);
160
+ } else pushToken("Pipe", start, pos);
125
161
  continue;
126
162
  case "/":
127
163
  advance();
128
164
  if (peek() === "/") {
129
165
  advance();
130
- pushToken("Alt", start, pos);
166
+ if (peek() === "=") {
167
+ advance();
168
+ pushToken("AltEq", start, pos);
169
+ } else pushToken("Alt", start, pos);
170
+ } else if (peek() === "=") {
171
+ advance();
172
+ pushToken("SlashEq", start, pos);
131
173
  } else pushToken("Slash", start, pos);
132
174
  continue;
133
175
  case "(":
@@ -160,26 +202,38 @@ const lex = (text) => {
160
202
  continue;
161
203
  case "+":
162
204
  advance();
163
- pushToken("Plus", start, pos);
205
+ if (peek() === "=") {
206
+ advance();
207
+ pushToken("PlusEq", start, pos);
208
+ } else pushToken("Plus", start, pos);
164
209
  continue;
165
210
  case "-":
166
211
  advance();
167
- pushToken("Minus", start, pos);
212
+ if (peek() === "=") {
213
+ advance();
214
+ pushToken("MinusEq", start, pos);
215
+ } else pushToken("Minus", start, pos);
168
216
  continue;
169
217
  case "*":
170
218
  advance();
171
- pushToken("Star", start, pos);
219
+ if (peek() === "=") {
220
+ advance();
221
+ pushToken("StarEq", start, pos);
222
+ } else pushToken("Star", start, pos);
172
223
  continue;
173
224
  case "%":
174
225
  advance();
175
- pushToken("Percent", start, pos);
226
+ if (peek() === "=") {
227
+ advance();
228
+ pushToken("PercentEq", start, pos);
229
+ } else pushToken("Percent", start, pos);
176
230
  continue;
177
231
  case "=":
178
232
  advance();
179
233
  if (peek() === "=") {
180
234
  advance();
181
235
  pushToken("EqualEqual", start, pos);
182
- } else throw new LexError("Unexpected \"=\" (only \"==\" supported)", makeSpan(start, pos));
236
+ } else pushToken("Eq", start, pos);
183
237
  continue;
184
238
  case "!":
185
239
  advance();
@@ -247,19 +301,23 @@ const lex = (text) => {
247
301
  }
248
302
  return pos;
249
303
  }
250
- function readString(tokenStart) {
251
- advance();
304
+ function readString(tokenStart, openQuote) {
305
+ if (openQuote) advance();
252
306
  while (pos < length) {
253
307
  const current = advance();
254
308
  if (current === "\"") return pos;
255
309
  if (current === "\\") {
310
+ if (peek() === "(") {
311
+ advance();
312
+ return pos;
313
+ }
256
314
  const esc = advance();
257
315
  if (!esc) break;
258
316
  if ("\"\\/bfnrt".includes(esc)) continue;
259
317
  if (esc === "u") {
260
- for (let i = 0; i < 4; i += 1) {
261
- const hex = advance();
262
- if (!hex || !isHexDigit(hex)) throw new LexError("Invalid Unicode escape in string literal", makeSpan(tokenStart, pos));
318
+ for (let i = 0; i < 4; i++) {
319
+ const h = advance();
320
+ if (!h || !isHexDigit(h)) throw new LexError("Invalid Unicode escape", makeSpan(tokenStart, pos));
263
321
  }
264
322
  continue;
265
323
  }
@@ -275,41 +333,44 @@ const lex = (text) => {
275
333
  const ch = text[i];
276
334
  if (ch === "\\") {
277
335
  const next = text[i + 1];
278
- switch (next) {
279
- case "\"":
280
- case "\\":
281
- case "/":
282
- result += next;
283
- i += 2;
284
- break;
285
- case "b":
286
- result += "\b";
287
- i += 2;
288
- break;
289
- case "f":
290
- result += "\f";
291
- i += 2;
292
- break;
293
- case "n":
294
- result += "\n";
295
- i += 2;
296
- break;
297
- case "r":
298
- result += "\r";
299
- i += 2;
300
- break;
301
- case "t":
302
- result += " ";
303
- i += 2;
304
- break;
305
- case "u": {
306
- const hex = text.slice(i + 2, i + 6);
307
- result += String.fromCharCode(Number.parseInt(hex, 16));
308
- i += 6;
309
- break;
336
+ if (!next) throw new LexError("Unexpected end of input", makeSpan(tokenStart, pos));
337
+ if ("\"\\/bfnrt".includes(next)) {
338
+ switch (next) {
339
+ case "\"":
340
+ result += "\"";
341
+ break;
342
+ case "\\":
343
+ result += "\\";
344
+ break;
345
+ case "/":
346
+ result += "/";
347
+ break;
348
+ case "b":
349
+ result += "\b";
350
+ break;
351
+ case "f":
352
+ result += "\f";
353
+ break;
354
+ case "n":
355
+ result += "\n";
356
+ break;
357
+ case "r":
358
+ result += "\r";
359
+ break;
360
+ case "t":
361
+ result += " ";
362
+ break;
310
363
  }
311
- default: throw new LexError(`Invalid escape sequence "\\${next}"`, makeSpan(tokenStart, tokenStart + (i - innerStart) + 2));
364
+ i += 2;
365
+ continue;
366
+ }
367
+ if (next === "u") {
368
+ const hex = text.slice(i + 2, i + 6);
369
+ result += String.fromCharCode(parseInt(hex, 16));
370
+ i += 6;
371
+ continue;
312
372
  }
373
+ throw new LexError(`Invalid escape sequence "\\${next}"`, makeSpan(tokenStart, pos));
313
374
  } else {
314
375
  result += ch;
315
376
  i += 1;
@@ -339,16 +400,56 @@ var Parser = class {
339
400
  this.tokens = tokens;
340
401
  }
341
402
  parseFilter() {
342
- const expr = this.parseBinding();
403
+ const expr = this.parseDef();
343
404
  this.consume("EOF", "Expected end of expression");
344
405
  return expr;
345
406
  }
346
- parseBinding() {
347
- let expr = this.parseComma();
407
+ parseDef(allowComma = true) {
408
+ if (this.match("Def")) {
409
+ const startSpan = this.previous().span;
410
+ const nameToken = this.consume("Identifier", "Expected function name");
411
+ const name = String(nameToken.value);
412
+ const args = [];
413
+ if (this.match("LParen")) {
414
+ if (!this.check("RParen")) do {
415
+ const argToken = this.consume("Identifier", "Expected argument name");
416
+ args.push(String(argToken.value));
417
+ } while (this.match("Semicolon"));
418
+ this.consume("RParen", "Expected \")\" after arguments");
419
+ }
420
+ this.consume("Colon", "Expected \":\" after function signature");
421
+ const body = this.parseDef(true);
422
+ this.consume("Semicolon", "Expected \";\" after function body");
423
+ const next = this.parseDef(allowComma);
424
+ return {
425
+ kind: "Def",
426
+ name,
427
+ args,
428
+ body,
429
+ next,
430
+ span: spanBetween(startSpan, next.span)
431
+ };
432
+ }
433
+ return this.parseBinding(allowComma);
434
+ }
435
+ parseBinding(allowComma = true) {
436
+ if (this.match("Label")) {
437
+ const start = this.previous();
438
+ const varToken = this.consume("Variable", "Expected variable name after \"label\"");
439
+ this.consume("Pipe", "Expected \"|\" after label");
440
+ const body = this.parseBinding(allowComma);
441
+ return {
442
+ kind: "Label",
443
+ label: String(varToken.value),
444
+ body,
445
+ span: spanBetween(start.span, body.span)
446
+ };
447
+ }
448
+ let expr = allowComma ? this.parseComma() : this.parseAssignment();
348
449
  while (this.match("As")) {
349
450
  const varToken = this.consume("Variable", "Expected variable name after \"as\"");
350
451
  this.consume("Pipe", "Expected \"|\" after variable binding");
351
- const body = this.parseBinding();
452
+ const body = this.parseBinding(allowComma);
352
453
  expr = {
353
454
  kind: "As",
354
455
  bind: expr,
@@ -360,9 +461,9 @@ var Parser = class {
360
461
  return expr;
361
462
  }
362
463
  parseComma() {
363
- let expr = this.parsePipe();
464
+ let expr = this.parseAssignment();
364
465
  while (this.match("Comma")) {
365
- const right = this.parsePipe();
466
+ const right = this.parseAssignment();
366
467
  expr = {
367
468
  kind: "Comma",
368
469
  left: expr,
@@ -372,6 +473,49 @@ var Parser = class {
372
473
  }
373
474
  return expr;
374
475
  }
476
+ parseAssignment() {
477
+ const expr = this.parsePipe();
478
+ if (this.match("Eq") || this.match("BarEq") || this.match("PlusEq") || this.match("MinusEq") || this.match("StarEq") || this.match("SlashEq") || this.match("PercentEq") || this.match("AltEq")) {
479
+ const opToken = this.previous();
480
+ const right = this.parseAssignment();
481
+ let op;
482
+ switch (opToken.kind) {
483
+ case "Eq":
484
+ op = "=";
485
+ break;
486
+ case "BarEq":
487
+ op = "|=";
488
+ break;
489
+ case "PlusEq":
490
+ op = "+=";
491
+ break;
492
+ case "MinusEq":
493
+ op = "-=";
494
+ break;
495
+ case "StarEq":
496
+ op = "*=";
497
+ break;
498
+ case "SlashEq":
499
+ op = "/=";
500
+ break;
501
+ case "PercentEq":
502
+ op = "%=";
503
+ break;
504
+ case "AltEq":
505
+ op = "//=";
506
+ break;
507
+ default: throw new Error(`Unknown assignment op: ${opToken.kind}`);
508
+ }
509
+ return {
510
+ kind: "Assignment",
511
+ op,
512
+ left: expr,
513
+ right,
514
+ span: spanBetween(expr.span, right.span)
515
+ };
516
+ }
517
+ return expr;
518
+ }
375
519
  parsePipe() {
376
520
  let expr = this.parsePipeOperand();
377
521
  while (this.match("Pipe")) {
@@ -508,6 +652,19 @@ var Parser = class {
508
652
  continue;
509
653
  }
510
654
  if (this.match("LBracket")) {
655
+ if (this.match("Colon")) {
656
+ let end = null;
657
+ if (!this.check("RBracket")) end = this.parsePipe();
658
+ const close = this.consume("RBracket", "Expected \"]\" after slice");
659
+ expr = {
660
+ kind: "Slice",
661
+ target: expr,
662
+ start: null,
663
+ end,
664
+ span: spanBetween(expr.span, close.span)
665
+ };
666
+ continue;
667
+ }
511
668
  if (this.match("RBracket")) {
512
669
  const close = this.previous();
513
670
  expr = {
@@ -518,6 +675,19 @@ var Parser = class {
518
675
  continue;
519
676
  }
520
677
  const index = this.parsePipe();
678
+ if (this.match("Colon")) {
679
+ let end = null;
680
+ if (!this.check("RBracket")) end = this.parsePipe();
681
+ const close = this.consume("RBracket", "Expected \"]\" after slice");
682
+ expr = {
683
+ kind: "Slice",
684
+ target: expr,
685
+ start: index,
686
+ end,
687
+ span: spanBetween(expr.span, close.span)
688
+ };
689
+ continue;
690
+ }
521
691
  const closing = this.consume("RBracket", "Expected \"]\" after index expression");
522
692
  expr = {
523
693
  kind: "IndexAccess",
@@ -527,6 +697,19 @@ var Parser = class {
527
697
  };
528
698
  continue;
529
699
  }
700
+ if (this.match("Question")) {
701
+ const op = this.previous();
702
+ expr = {
703
+ kind: "Try",
704
+ body: expr,
705
+ handler: {
706
+ kind: "Identity",
707
+ span: op.span
708
+ },
709
+ span: spanBetween(expr.span, op.span)
710
+ };
711
+ continue;
712
+ }
530
713
  break;
531
714
  }
532
715
  return expr;
@@ -538,6 +721,7 @@ var Parser = class {
538
721
  if (this.match("False")) return this.literalNode(false, this.previous().span);
539
722
  if (this.match("Number")) return this.literalNode(Number(this.previous().value), this.previous().span);
540
723
  if (this.match("String")) return this.literalNode(String(this.previous().value), this.previous().span);
724
+ if (this.match("StringStart")) return this.parseStringInterpolation(this.previous());
541
725
  if (this.match("Variable")) {
542
726
  const token = this.previous();
543
727
  return {
@@ -550,7 +734,7 @@ var Parser = class {
550
734
  if (this.match("If")) return this.parseIf(this.previous());
551
735
  if (this.match("LParen")) {
552
736
  const start = this.previous();
553
- const expr = this.parseComma();
737
+ const expr = this.parseDef();
554
738
  const close = this.consume("RParen", "Expected \")\" after expression");
555
739
  expr.span = spanBetween(start.span, close.span);
556
740
  return expr;
@@ -564,8 +748,21 @@ var Parser = class {
564
748
  kind: "Recurse",
565
749
  span: this.previous().span
566
750
  };
751
+ if (this.match("DotDot")) return {
752
+ kind: "Recurse",
753
+ span: this.previous().span
754
+ };
755
+ if (this.match("Break")) return this.parseBreak(this.previous());
567
756
  throw this.error(this.peek(), "Unexpected token");
568
757
  }
758
+ parseBreak(start) {
759
+ const varToken = this.consume("Variable", "Expected variable after \"break\"");
760
+ return {
761
+ kind: "Break",
762
+ label: String(varToken.value),
763
+ span: spanBetween(start.span, varToken.span)
764
+ };
765
+ }
569
766
  parseReduce(start) {
570
767
  const source = this.parsePipe();
571
768
  this.consume("As", "Expected \"as\" after reduce source");
@@ -606,11 +803,11 @@ var Parser = class {
606
803
  };
607
804
  }
608
805
  parseTry(start) {
609
- const body = this.parseComma();
806
+ const body = this.parseDef(true);
610
807
  let handler;
611
808
  let endSpan = body.span;
612
809
  if (this.match("Catch")) {
613
- handler = this.parseComma();
810
+ handler = this.parseDef(true);
614
811
  endSpan = handler.span;
615
812
  }
616
813
  return {
@@ -622,24 +819,24 @@ var Parser = class {
622
819
  }
623
820
  parseIf(start) {
624
821
  const branches = [];
625
- const firstCond = this.parsePipe();
822
+ const firstCond = this.parseDef(true);
626
823
  this.consume("Then", "Expected \"then\" after condition");
627
- const firstThen = this.parsePipe();
824
+ const firstThen = this.parseDef(true);
628
825
  branches.push({
629
826
  cond: firstCond,
630
827
  then: firstThen
631
828
  });
632
829
  while (this.match("Elif")) {
633
- const cond = this.parsePipe();
830
+ const cond = this.parseDef(true);
634
831
  this.consume("Then", "Expected \"then\" after elif condition");
635
- const thenBranch = this.parsePipe();
832
+ const thenBranch = this.parseDef(true);
636
833
  branches.push({
637
834
  cond,
638
835
  then: thenBranch
639
836
  });
640
837
  }
641
838
  this.consume("Else", "Expected \"else\" in if expression");
642
- const elseBranch = this.parsePipe();
839
+ const elseBranch = this.parseDef(true);
643
840
  const endToken = this.consume("End", "Expected \"end\" to close if expression");
644
841
  return {
645
842
  kind: "If",
@@ -650,26 +847,36 @@ var Parser = class {
650
847
  }
651
848
  parseArray(start) {
652
849
  const items = [];
653
- if (!this.check("RBracket")) do
654
- items.push(this.parsePipe());
655
- while (this.match("Comma"));
656
- const end = this.consume("RBracket", "Expected \"]\" after array literal");
850
+ if (!this.match("RBracket")) {
851
+ do
852
+ items.push(this.parseDef(false));
853
+ while (this.match("Comma"));
854
+ this.consume("RBracket", "Expected \"]\" after array elements");
855
+ }
657
856
  return {
658
857
  kind: "Array",
659
858
  items,
660
- span: spanBetween(start.span, end.span)
859
+ span: spanBetween(start.span, this.previous().span)
661
860
  };
662
861
  }
663
862
  parseObject(start) {
664
863
  const entries = [];
665
- if (!this.check("RBrace")) do
666
- entries.push(this.parseObjectEntry());
667
- while (this.match("Comma"));
668
- const end = this.consume("RBrace", "Expected \"}\" after object literal");
864
+ if (!this.match("RBrace")) {
865
+ do {
866
+ const key = this.parseObjectKey();
867
+ this.consume("Colon", "Expected \":\" after object key");
868
+ const value = this.parseDef(false);
869
+ entries.push({
870
+ key,
871
+ value
872
+ });
873
+ } while (this.match("Comma"));
874
+ this.consume("RBrace", "Expected \"}\" after object entries");
875
+ }
669
876
  return {
670
877
  kind: "Object",
671
878
  entries,
672
- span: spanBetween(start.span, end.span)
879
+ span: spanBetween(start.span, this.previous().span)
673
880
  };
674
881
  }
675
882
  parseObjectEntry() {
@@ -677,7 +884,7 @@ var Parser = class {
677
884
  this.consume("Colon", "Expected \":\" after object key");
678
885
  return {
679
886
  key,
680
- value: this.parsePipe()
887
+ value: this.parseDef(false)
681
888
  };
682
889
  }
683
890
  parseObjectKey() {
@@ -741,6 +948,19 @@ var Parser = class {
741
948
  continue;
742
949
  }
743
950
  if (this.match("LBracket")) {
951
+ if (this.match("Colon")) {
952
+ let end = null;
953
+ if (!this.check("RBracket")) end = this.parsePipe();
954
+ const close = this.consume("RBracket", "Expected \"]\" after slice");
955
+ expr = {
956
+ kind: "Slice",
957
+ target: expr,
958
+ start: null,
959
+ end,
960
+ span: spanBetween(expr.span, close.span)
961
+ };
962
+ continue;
963
+ }
744
964
  if (this.match("RBracket")) {
745
965
  const close = this.previous();
746
966
  expr = {
@@ -751,6 +971,19 @@ var Parser = class {
751
971
  continue;
752
972
  }
753
973
  const index = this.parsePipe();
974
+ if (this.match("Colon")) {
975
+ let end = null;
976
+ if (!this.check("RBracket")) end = this.parsePipe();
977
+ const close = this.consume("RBracket", "Expected \"]\" after slice");
978
+ expr = {
979
+ kind: "Slice",
980
+ target: expr,
981
+ start: index,
982
+ end,
983
+ span: spanBetween(expr.span, close.span)
984
+ };
985
+ continue;
986
+ }
754
987
  const closing = this.consume("RBracket", "Expected \"]\" after index expression");
755
988
  expr = {
756
989
  kind: "IndexAccess",
@@ -760,6 +993,19 @@ var Parser = class {
760
993
  };
761
994
  continue;
762
995
  }
996
+ if (this.match("Question")) {
997
+ const op = this.previous();
998
+ expr = {
999
+ kind: "Try",
1000
+ body: expr,
1001
+ handler: {
1002
+ kind: "Identity",
1003
+ span: op.span
1004
+ },
1005
+ span: spanBetween(expr.span, op.span)
1006
+ };
1007
+ continue;
1008
+ }
763
1009
  break;
764
1010
  }
765
1011
  return expr;
@@ -786,7 +1032,7 @@ var Parser = class {
786
1032
  const args = [];
787
1033
  if (this.check("RParen")) return args;
788
1034
  do
789
- args.push(this.parsePipe());
1035
+ args.push(this.parseDef(true));
790
1036
  while (this.match("Semicolon"));
791
1037
  return args;
792
1038
  }
@@ -806,6 +1052,53 @@ var Parser = class {
806
1052
  span: spanBetween(left.span, right.span)
807
1053
  };
808
1054
  }
1055
+ parseStringInterpolation(start) {
1056
+ const parts = [{
1057
+ kind: "Literal",
1058
+ value: String(start.value),
1059
+ span: start.span
1060
+ }];
1061
+ while (true) {
1062
+ const expr = this.parseDef();
1063
+ parts.push({
1064
+ kind: "Pipe",
1065
+ left: expr,
1066
+ right: {
1067
+ kind: "Call",
1068
+ name: "tostring",
1069
+ args: [],
1070
+ span: expr.span
1071
+ },
1072
+ span: expr.span
1073
+ });
1074
+ if (this.match("StringMiddle")) {
1075
+ const token = this.previous();
1076
+ parts.push({
1077
+ kind: "Literal",
1078
+ value: String(token.value),
1079
+ span: token.span
1080
+ });
1081
+ continue;
1082
+ }
1083
+ if (this.match("StringEnd")) {
1084
+ const token = this.previous();
1085
+ parts.push({
1086
+ kind: "Literal",
1087
+ value: String(token.value),
1088
+ span: token.span
1089
+ });
1090
+ break;
1091
+ }
1092
+ throw this.error(this.peek(), "Expected closing paren of interpolation or continuation");
1093
+ }
1094
+ return parts.reduce((acc, curr) => ({
1095
+ kind: "Binary",
1096
+ op: "+",
1097
+ left: acc,
1098
+ right: curr,
1099
+ span: spanBetween(acc.span, curr.span)
1100
+ }));
1101
+ }
809
1102
  match(kind) {
810
1103
  if (this.check(kind)) {
811
1104
  this.advance();
@@ -855,6 +1148,17 @@ const spanBetween = (a, b) => ({
855
1148
  end: Math.max(a.end, b.end)
856
1149
  });
857
1150
 
1151
+ //#endregion
1152
+ //#region src/builtins/registry.ts
1153
+ const builtins = {};
1154
+ const registerBuiltin = (spec) => {
1155
+ if (!builtins[spec.name]) builtins[spec.name] = [];
1156
+ builtins[spec.name].push(spec);
1157
+ };
1158
+ const registerBuiltins = (specs) => {
1159
+ for (const spec of specs) registerBuiltin(spec);
1160
+ };
1161
+
858
1162
  //#endregion
859
1163
  //#region src/value.ts
860
1164
  /**
@@ -881,7 +1185,7 @@ const valueEquals = (a, b) => {
881
1185
  for (let i = 0; i < a.length; i += 1) if (!valueEquals(a[i], b[i])) return false;
882
1186
  return true;
883
1187
  }
884
- if (isPlainObject$1(a) && isPlainObject$1(b)) {
1188
+ if (isPlainObject(a) && isPlainObject(b)) {
885
1189
  const aKeys = Object.keys(a).sort();
886
1190
  const bKeys = Object.keys(b).sort();
887
1191
  if (aKeys.length !== bKeys.length) return false;
@@ -918,7 +1222,7 @@ const compareValues = (a, b) => {
918
1222
  }
919
1223
  return a.length < b.length ? -1 : 1;
920
1224
  }
921
- if (isPlainObject$1(a) && isPlainObject$1(b)) {
1225
+ if (isPlainObject(a) && isPlainObject(b)) {
922
1226
  const aKeys = Object.keys(a).sort();
923
1227
  const bKeys = Object.keys(b).sort();
924
1228
  const len = Math.min(aKeys.length, bKeys.length);
@@ -952,7 +1256,14 @@ const typeRank = (value) => {
952
1256
  * @param value - The value to check.
953
1257
  * @returns `true` if `value` is a non-null object (and not an array).
954
1258
  */
955
- const isPlainObject$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1259
+ const isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1260
+ /**
1261
+ * Checks if a value is a JSON array.
1262
+ *
1263
+ * @param value - The value to check.
1264
+ * @returns `true` if `value` is an array.
1265
+ */
1266
+ const isValueArray = (value) => Array.isArray(value);
956
1267
  /**
957
1268
  * Returns the type name of a value as a string (e.g., "null", "boolean", "number", "string", "array", "object").
958
1269
  * This corresponds to the output of the `type` builtin.
@@ -960,7 +1271,7 @@ const isPlainObject$1 = (value) => typeof value === "object" && value !== null &
960
1271
  * @param value - The value to inspect.
961
1272
  * @returns The lower-case type name.
962
1273
  */
963
- const describeType$1 = (value) => {
1274
+ const describeType = (value) => {
964
1275
  if (value === null) return "null";
965
1276
  if (typeof value === "boolean") return "boolean";
966
1277
  if (typeof value === "number") return "number";
@@ -970,7 +1281,7 @@ const describeType$1 = (value) => {
970
1281
  };
971
1282
 
972
1283
  //#endregion
973
- //#region src/builtins.ts
1284
+ //#region src/builtins/utils.ts
974
1285
  const emit$1 = (value, span, tracker) => {
975
1286
  tracker.emit(span);
976
1287
  return value;
@@ -987,22 +1298,25 @@ const stableStringify = (value) => {
987
1298
  if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
988
1299
  return `{${Object.keys(value).sort().map((k) => `${JSON.stringify(k)}:${stableStringify(value[k])}`).join(",")}}`;
989
1300
  };
990
- const builtins = {
991
- type: {
1301
+
1302
+ //#endregion
1303
+ //#region src/builtins/std.ts
1304
+ const stdBuiltins = [
1305
+ {
992
1306
  name: "type",
993
1307
  arity: 0,
994
1308
  apply: function* (input, _args, _env, tracker, _eval, span) {
995
- yield emit$1(describeType$1(input), span, tracker);
1309
+ yield emit$1(describeType(input), span, tracker);
996
1310
  }
997
1311
  },
998
- tostring: {
1312
+ {
999
1313
  name: "tostring",
1000
1314
  arity: 0,
1001
1315
  apply: function* (input, _args, _env, tracker, _eval, span) {
1002
1316
  yield emit$1(stableStringify(input), span, tracker);
1003
1317
  }
1004
1318
  },
1005
- tonumber: {
1319
+ {
1006
1320
  name: "tonumber",
1007
1321
  arity: 0,
1008
1322
  apply: function* (input, _args, _env, tracker, _eval, span) {
@@ -1016,20 +1330,49 @@ const builtins = {
1016
1330
  yield emit$1(num, span, tracker);
1017
1331
  return;
1018
1332
  }
1019
- throw new RuntimeError(`Cannot convert ${describeType$1(input)} to number`, span);
1333
+ throw new RuntimeError(`Cannot convert ${describeType(input)} to number`, span);
1020
1334
  }
1021
1335
  },
1022
- length: {
1336
+ {
1023
1337
  name: "length",
1024
1338
  arity: 0,
1025
1339
  apply: function* (input, _args, _env, tracker, _eval, span) {
1026
1340
  if (typeof input === "string") yield emit$1(Array.from(input).length, span, tracker);
1027
1341
  else if (Array.isArray(input)) yield emit$1(input.length, span, tracker);
1028
1342
  else if (input !== null && typeof input === "object") yield emit$1(Object.keys(input).length, span, tracker);
1029
- else throw new RuntimeError(`Cannot take length of ${describeType$1(input)}`, span);
1343
+ else throw new RuntimeError(`Cannot take length of ${describeType(input)}`, span);
1030
1344
  }
1031
1345
  },
1032
- keys: {
1346
+ {
1347
+ name: "empty",
1348
+ arity: 0,
1349
+ apply: function* () {}
1350
+ }
1351
+ ];
1352
+
1353
+ //#endregion
1354
+ //#region src/builtins/errors.ts
1355
+ const errorBuiltins = [{
1356
+ name: "error",
1357
+ arity: 1,
1358
+ apply: function* (input, args, env, tracker, evaluate$1, span) {
1359
+ for (const msg of evaluate$1(args[0], input, env, tracker)) throw new RuntimeError(typeof msg === "string" ? msg : stableStringify(msg), span);
1360
+ }
1361
+ }];
1362
+
1363
+ //#endregion
1364
+ //#region src/builtins/collections.ts
1365
+ function sortStable(arr, compare$1) {
1366
+ return arr.map((item, index) => ({
1367
+ item,
1368
+ index
1369
+ })).sort((a, b) => {
1370
+ const cmp = compare$1(a.item, b.item);
1371
+ return cmp !== 0 ? cmp : a.index - b.index;
1372
+ }).map((p) => p.item);
1373
+ }
1374
+ const collectionBuiltins = [
1375
+ {
1033
1376
  name: "keys",
1034
1377
  arity: 0,
1035
1378
  apply: function* (input, _args, _env, tracker, _eval, span) {
@@ -1038,7 +1381,7 @@ const builtins = {
1038
1381
  else throw new RuntimeError(`keys expects an array or object`, span);
1039
1382
  }
1040
1383
  },
1041
- has: {
1384
+ {
1042
1385
  name: "has",
1043
1386
  arity: 1,
1044
1387
  apply: function* (input, args, env, tracker, evaluate$1, span) {
@@ -1055,14 +1398,7 @@ const builtins = {
1055
1398
  } else throw new RuntimeError(`has() expects an array or object input`, span);
1056
1399
  }
1057
1400
  },
1058
- error: {
1059
- name: "error",
1060
- arity: 1,
1061
- apply: function* (input, args, env, tracker, evaluate$1, span) {
1062
- for (const msg of evaluate$1(args[0], input, env, tracker)) throw new RuntimeError(typeof msg === "string" ? msg : stableStringify(msg), span);
1063
- }
1064
- },
1065
- map: {
1401
+ {
1066
1402
  name: "map",
1067
1403
  arity: 1,
1068
1404
  apply: function* (input, args, env, tracker, evaluate$1, span) {
@@ -1076,7 +1412,7 @@ const builtins = {
1076
1412
  yield emit$1(result, span, tracker);
1077
1413
  }
1078
1414
  },
1079
- select: {
1415
+ {
1080
1416
  name: "select",
1081
1417
  arity: 1,
1082
1418
  apply: function* (input, args, env, tracker, evaluate$1, span) {
@@ -1087,7 +1423,7 @@ const builtins = {
1087
1423
  }
1088
1424
  }
1089
1425
  },
1090
- sort: {
1426
+ {
1091
1427
  name: "sort",
1092
1428
  arity: 0,
1093
1429
  apply: function* (input, _args, _env, tracker, _eval, span) {
@@ -1097,7 +1433,7 @@ const builtins = {
1097
1433
  yield emit$1(sorted, span, tracker);
1098
1434
  }
1099
1435
  },
1100
- sort_by: {
1436
+ {
1101
1437
  name: "sort_by",
1102
1438
  arity: 1,
1103
1439
  apply: function* (input, args, env, tracker, evaluate$1, span) {
@@ -1116,7 +1452,7 @@ const builtins = {
1116
1452
  yield emit$1(sortStable(pairs, (a, b) => compareValues(a.key, b.key)).map((p) => p.val), span, tracker);
1117
1453
  }
1118
1454
  },
1119
- unique: {
1455
+ {
1120
1456
  name: "unique",
1121
1457
  arity: 0,
1122
1458
  apply: function* (input, _args, _env, tracker, _eval, span) {
@@ -1133,7 +1469,7 @@ const builtins = {
1133
1469
  yield emit$1(result, span, tracker);
1134
1470
  }
1135
1471
  },
1136
- unique_by: {
1472
+ {
1137
1473
  name: "unique_by",
1138
1474
  arity: 1,
1139
1475
  apply: function* (input, args, env, tracker, evaluate$1, span) {
@@ -1154,7 +1490,7 @@ const builtins = {
1154
1490
  yield emit$1(result, span, tracker);
1155
1491
  }
1156
1492
  },
1157
- to_entries: {
1493
+ {
1158
1494
  name: "to_entries",
1159
1495
  arity: 0,
1160
1496
  apply: function* (input, _args, _env, tracker, _eval, span) {
@@ -1169,7 +1505,7 @@ const builtins = {
1169
1505
  else throw new RuntimeError("to_entries expects array or object", span);
1170
1506
  }
1171
1507
  },
1172
- from_entries: {
1508
+ {
1173
1509
  name: "from_entries",
1174
1510
  arity: 0,
1175
1511
  apply: function* (input, _args, _env, tracker, _eval, span) {
@@ -1187,7 +1523,7 @@ const builtins = {
1187
1523
  yield emit$1(result, span, tracker);
1188
1524
  }
1189
1525
  },
1190
- with_entries: {
1526
+ {
1191
1527
  name: "with_entries",
1192
1528
  arity: 1,
1193
1529
  apply: function* (input, args, env, tracker, evaluate$1, span) {
@@ -1218,8 +1554,30 @@ const builtins = {
1218
1554
  }
1219
1555
  yield emit$1(result, span, tracker);
1220
1556
  }
1221
- },
1222
- split: {
1557
+ }
1558
+ ];
1559
+
1560
+ //#endregion
1561
+ //#region src/builtins/strings.ts
1562
+ const checkContains = (a, b) => {
1563
+ if (a === b) return true;
1564
+ if (typeof a !== typeof b) return false;
1565
+ if (typeof a === "string" && typeof b === "string") return a.includes(b);
1566
+ if (Array.isArray(a) && Array.isArray(b)) return b.every((bItem) => a.some((aItem) => checkContains(aItem, bItem)));
1567
+ if (isPlainObject(a) && isPlainObject(b)) {
1568
+ const keys = Object.keys(b);
1569
+ for (const key of keys) {
1570
+ if (!Object.prototype.hasOwnProperty.call(a, key)) return false;
1571
+ const valA = a[key];
1572
+ const valB = b[key];
1573
+ if (!checkContains(valA, valB)) return false;
1574
+ }
1575
+ return true;
1576
+ }
1577
+ return valueEquals(a, b);
1578
+ };
1579
+ const stringBuiltins = [
1580
+ {
1223
1581
  name: "split",
1224
1582
  arity: 1,
1225
1583
  apply: function* (input, args, env, tracker, evaluate$1, span) {
@@ -1231,7 +1589,7 @@ const builtins = {
1231
1589
  }
1232
1590
  }
1233
1591
  },
1234
- join: {
1592
+ {
1235
1593
  name: "join",
1236
1594
  arity: 1,
1237
1595
  apply: function* (input, args, env, tracker, evaluate$1, span) {
@@ -1241,14 +1599,14 @@ const builtins = {
1241
1599
  if (typeof sep !== "string") throw new RuntimeError("join separator must be a string", span);
1242
1600
  const parts = [];
1243
1601
  for (const item of input) {
1244
- if (typeof item !== "string") throw new RuntimeError(`join expects strings, but got ${describeType$1(item)}`, span);
1602
+ if (typeof item !== "string") throw new RuntimeError(`join expects strings, but got ${describeType(item)}`, span);
1245
1603
  parts.push(item);
1246
1604
  }
1247
1605
  yield emit$1(parts.join(sep), span, tracker);
1248
1606
  }
1249
1607
  }
1250
1608
  },
1251
- startswith: {
1609
+ {
1252
1610
  name: "startswith",
1253
1611
  arity: 1,
1254
1612
  apply: function* (input, args, env, tracker, evaluate$1, span) {
@@ -1260,7 +1618,7 @@ const builtins = {
1260
1618
  }
1261
1619
  }
1262
1620
  },
1263
- endswith: {
1621
+ {
1264
1622
  name: "endswith",
1265
1623
  arity: 1,
1266
1624
  apply: function* (input, args, env, tracker, evaluate$1, span) {
@@ -1272,7 +1630,7 @@ const builtins = {
1272
1630
  }
1273
1631
  }
1274
1632
  },
1275
- contains: {
1633
+ {
1276
1634
  name: "contains",
1277
1635
  arity: 1,
1278
1636
  apply: function* (input, args, env, tracker, evaluate$1, span) {
@@ -1280,72 +1638,189 @@ const builtins = {
1280
1638
  for (const b of bGen) yield emit$1(checkContains(input, b), span, tracker);
1281
1639
  }
1282
1640
  },
1283
- paths: {
1284
- name: "paths",
1285
- arity: 0,
1286
- apply: function* (input, _args, _env, tracker, _eval, span) {
1287
- yield* traversePaths(input, [], span, tracker);
1641
+ {
1642
+ name: "index",
1643
+ arity: 1,
1644
+ apply: function* (input, args, env, tracker, evaluate$1, span) {
1645
+ const searchGen = evaluate$1(args[0], input, env, tracker);
1646
+ for (const search of searchGen) if (Array.isArray(input)) {
1647
+ let foundIndex = null;
1648
+ for (let i = 0; i < input.length; i++) {
1649
+ const val = input[i];
1650
+ if (val !== void 0 && valueEquals(val, search)) {
1651
+ foundIndex = i;
1652
+ break;
1653
+ }
1654
+ }
1655
+ yield emit$1(foundIndex, span, tracker);
1656
+ } else if (typeof input === "string") {
1657
+ if (typeof search !== "string") throw new RuntimeError("index expects string search when input is string", span);
1658
+ const idx = input.indexOf(search);
1659
+ yield emit$1(idx === -1 ? null : idx, span, tracker);
1660
+ } else if (input === null) yield emit$1(null, span, tracker);
1661
+ else throw new RuntimeError("index expects string or array", span);
1288
1662
  }
1289
1663
  },
1290
- getpath: {
1291
- name: "getpath",
1664
+ {
1665
+ name: "rindex",
1292
1666
  arity: 1,
1293
1667
  apply: function* (input, args, env, tracker, evaluate$1, span) {
1294
- for (const pathVal of evaluate$1(args[0], input, env, tracker)) yield emit$1(getPath(input, ensurePath(pathVal, span)) ?? null, span, tracker);
1668
+ const searchGen = evaluate$1(args[0], input, env, tracker);
1669
+ for (const search of searchGen) if (Array.isArray(input)) {
1670
+ let foundIndex = null;
1671
+ for (let i = input.length - 1; i >= 0; i--) {
1672
+ const val = input[i];
1673
+ if (val !== void 0 && valueEquals(val, search)) {
1674
+ foundIndex = i;
1675
+ break;
1676
+ }
1677
+ }
1678
+ yield emit$1(foundIndex, span, tracker);
1679
+ } else if (typeof input === "string") {
1680
+ if (typeof search !== "string") throw new RuntimeError("rindex expects string search when input is string", span);
1681
+ const idx = input.lastIndexOf(search);
1682
+ yield emit$1(idx === -1 ? null : idx, span, tracker);
1683
+ } else if (input === null) yield emit$1(null, span, tracker);
1684
+ else throw new RuntimeError("rindex expects string or array", span);
1295
1685
  }
1296
1686
  },
1297
- setpath: {
1298
- name: "setpath",
1299
- arity: 2,
1687
+ {
1688
+ name: "indices",
1689
+ arity: 1,
1300
1690
  apply: function* (input, args, env, tracker, evaluate$1, span) {
1301
- const paths = Array.from(evaluate$1(args[0], input, env, tracker));
1302
- const values = Array.from(evaluate$1(args[1], input, env, tracker));
1303
- for (const pathVal of paths) {
1304
- const path = ensurePath(pathVal, span);
1305
- for (const val of values) yield emit$1(updatePath(input, path, () => val, span) ?? null, span, tracker);
1691
+ const searchGen = evaluate$1(args[0], input, env, tracker);
1692
+ for (const search of searchGen) {
1693
+ const indices = [];
1694
+ if (Array.isArray(input)) for (let i = 0; i < input.length; i++) {
1695
+ const val = input[i];
1696
+ if (val !== void 0 && valueEquals(val, search)) indices.push(i);
1697
+ }
1698
+ else if (typeof input === "string") {
1699
+ if (typeof search !== "string") throw new RuntimeError("indices expects string", span);
1700
+ if (search.length === 0) {} else {
1701
+ let pos = 0;
1702
+ while (pos < input.length) {
1703
+ const idx = input.indexOf(search, pos);
1704
+ if (idx === -1) break;
1705
+ indices.push(idx);
1706
+ pos = idx + 1;
1707
+ pos = idx + search.length;
1708
+ }
1709
+ }
1710
+ } else {
1711
+ if (input === null) {
1712
+ yield emit$1(null, span, tracker);
1713
+ continue;
1714
+ }
1715
+ throw new RuntimeError("indices expects string or array", span);
1716
+ }
1717
+ yield emit$1(indices, span, tracker);
1306
1718
  }
1307
1719
  }
1308
1720
  },
1309
- delpaths: {
1310
- name: "delpaths",
1311
- arity: 1,
1312
- apply: function* (input, args, env, tracker, evaluate$1, span) {
1313
- for (const pathsVal of evaluate$1(args[0], input, env, tracker)) {
1314
- if (!Array.isArray(pathsVal)) throw new RuntimeError("delpaths expects an array of paths", span);
1315
- yield emit$1(deletePaths(input, pathsVal.map((p) => ensurePath(p, span)), span), span, tracker);
1721
+ {
1722
+ name: "explode",
1723
+ arity: 0,
1724
+ apply: function* (input, _args, _env, tracker, _eval, span) {
1725
+ if (typeof input !== "string") throw new RuntimeError("explode expects string", span);
1726
+ yield emit$1(Array.from(input).map((c) => c.codePointAt(0)), span, tracker);
1727
+ }
1728
+ },
1729
+ {
1730
+ name: "implode",
1731
+ arity: 0,
1732
+ apply: function* (input, _args, _env, tracker, _eval, span) {
1733
+ if (!Array.isArray(input)) throw new RuntimeError("implode expects array", span);
1734
+ const chars = [];
1735
+ for (const item of input) {
1736
+ if (typeof item !== "number") throw new RuntimeError("implode item must be number", span);
1737
+ chars.push(String.fromCodePoint(item));
1316
1738
  }
1739
+ yield emit$1(chars.join(""), span, tracker);
1317
1740
  }
1318
1741
  }
1319
- };
1320
- const checkContains = (a, b) => {
1321
- if (a === b) return true;
1322
- if (typeof a !== typeof b) return false;
1323
- if (typeof a === "string" && typeof b === "string") return a.includes(b);
1324
- if (Array.isArray(a) && Array.isArray(b)) return b.every((bItem) => a.some((aItem) => checkContains(aItem, bItem)));
1325
- if (isPlainObject$1(a) && isPlainObject$1(b)) {
1326
- const keys = Object.keys(b);
1327
- for (const key of keys) {
1328
- if (!Object.prototype.hasOwnProperty.call(a, key)) return false;
1329
- const valA = a[key];
1330
- const valB = b[key];
1331
- if (!checkContains(valA, valB)) return false;
1742
+ ];
1743
+
1744
+ //#endregion
1745
+ //#region src/eval/path_eval.ts
1746
+ /**
1747
+ * Resolves properties paths for assignment or `path()` built-in.
1748
+ * Returns an array of paths (arrays of strings/numbers) for valid locations.
1749
+ *
1750
+ * @param node - The AST node describing trailing field access/indexing.
1751
+ * @param input - The current input value.
1752
+ * @param env - The environment.
1753
+ * @param tracker - Limits tracker.
1754
+ * @param evaluate - Recursive evaluator.
1755
+ */
1756
+ const evaluatePath = function* (node, input, env, tracker, evaluate$1) {
1757
+ switch (node.kind) {
1758
+ case "Identity":
1759
+ yield [];
1760
+ return;
1761
+ case "FieldAccess":
1762
+ for (const parent of evaluatePath(node.target, input, env, tracker, evaluate$1)) yield [...parent, node.field];
1763
+ return;
1764
+ case "IndexAccess": {
1765
+ const parentPaths = Array.from(evaluatePath(node.target, input, env, tracker, evaluate$1));
1766
+ const output = evaluate$1(node.index, input, env, tracker);
1767
+ for (const keyVal of output) {
1768
+ let key;
1769
+ if (typeof keyVal === "string") key = keyVal;
1770
+ else if (typeof keyVal === "number" && Number.isInteger(keyVal)) key = keyVal;
1771
+ else throw new RuntimeError("Path index must be string or integer", node.span);
1772
+ for (const parent of parentPaths) yield [...parent, key];
1773
+ }
1774
+ return;
1332
1775
  }
1333
- return true;
1776
+ case "Pipe": {
1777
+ const leftPaths = Array.from(evaluatePath(node.left, input, env, tracker, evaluate$1));
1778
+ for (const p of leftPaths) {
1779
+ const val = getPath(input, p) ?? null;
1780
+ for (const q of evaluatePath(node.right, val, env, tracker, evaluate$1)) yield [...p, ...q];
1781
+ }
1782
+ return;
1783
+ }
1784
+ case "Comma":
1785
+ yield* evaluatePath(node.left, input, env, tracker, evaluate$1);
1786
+ yield* evaluatePath(node.right, input, env, tracker, evaluate$1);
1787
+ return;
1788
+ case "Iterate": {
1789
+ const parentPaths = Array.from(evaluatePath(node.target, input, env, tracker, evaluate$1));
1790
+ for (const p of parentPaths) {
1791
+ const val = getPath(input, p);
1792
+ if (Array.isArray(val)) for (let i = 0; i < val.length; i++) yield [...p, i];
1793
+ else if (val !== null && typeof val === "object") for (const key of Object.keys(val)) yield [...p, key];
1794
+ else {
1795
+ if (val === null) continue;
1796
+ throw new RuntimeError(`Cannot iterate over ${typeof val}`, node.span);
1797
+ }
1798
+ }
1799
+ return;
1800
+ }
1801
+ case "Call":
1802
+ if (node.name === "select") {
1803
+ const conds = evaluate$1(node.args[0], input, env, tracker);
1804
+ let matched = false;
1805
+ for (const c of conds) if (c !== null && c !== false) matched = true;
1806
+ if (matched) yield [];
1807
+ return;
1808
+ }
1809
+ throw new RuntimeError(`Function ${node.name} not supported in path expression`, node.span);
1810
+ default: throw new RuntimeError("Invalid path expression", node.span);
1334
1811
  }
1335
- return valueEquals(a, b);
1336
1812
  };
1337
- /**
1338
- * Recursively traverses a JSON value to find all paths to leaf nodes.
1339
- * Yields paths as arrays of strings/numbers.
1340
- */
1813
+
1814
+ //#endregion
1815
+ //#region src/builtins/paths.ts
1341
1816
  function* traversePaths(root, currentPath, span, tracker) {
1342
1817
  tracker.step(span);
1343
- if (root === null || typeof root !== "object" || Array.isArray(root) && root.length === 0 || isPlainObject$1(root) && Object.keys(root).length === 0) {
1818
+ if (root === null || typeof root !== "object" || Array.isArray(root) && root.length === 0 || isPlainObject(root) && Object.keys(root).length === 0) {
1344
1819
  yield emit$1([...currentPath], span, tracker);
1345
1820
  return;
1346
1821
  }
1347
1822
  if (Array.isArray(root)) for (let i = 0; i < root.length; i++) yield* traversePaths(root[i], [...currentPath, i], span, tracker);
1348
- else if (isPlainObject$1(root)) {
1823
+ else if (isPlainObject(root)) {
1349
1824
  const keys = Object.keys(root).sort();
1350
1825
  for (const key of keys) yield* traversePaths(root[key], [...currentPath, key], span, tracker);
1351
1826
  }
@@ -1362,7 +1837,7 @@ const getPath = (root, path) => {
1362
1837
  let curr = root;
1363
1838
  for (const part of path) {
1364
1839
  if (curr === null) return void 0;
1365
- if (typeof part === "string" && isPlainObject$1(curr)) {
1840
+ if (typeof part === "string" && isPlainObject(curr)) {
1366
1841
  if (!Object.prototype.hasOwnProperty.call(curr, part)) return void 0;
1367
1842
  curr = curr[part];
1368
1843
  } else if (typeof part === "number" && Array.isArray(curr)) {
@@ -1372,18 +1847,14 @@ const getPath = (root, path) => {
1372
1847
  }
1373
1848
  return curr;
1374
1849
  };
1375
- /**
1376
- * Immutably updates a value at a given path.
1377
- * Creates intermediate objects/arrays as needed.
1378
- */
1379
1850
  const updatePath = (root, path, updateFn, span, depth = 0) => {
1380
1851
  if (path.length === 0) return updateFn(root);
1381
1852
  const [head, ...tail] = path;
1382
1853
  if (typeof head === "string") {
1383
1854
  let obj = {};
1384
- if (isPlainObject$1(root)) obj = { ...root };
1855
+ if (isPlainObject(root)) obj = { ...root };
1385
1856
  else if (root === null) obj = {};
1386
- else throw new RuntimeError(`Cannot index ${describeType$1(root)} with string "${head}"`, span);
1857
+ else throw new RuntimeError(`Cannot index ${describeType(root)} with string "${head}"`, span);
1387
1858
  const newVal = updatePath((Object.prototype.hasOwnProperty.call(obj, head) ? obj[head] : void 0) ?? null, tail, updateFn, span, depth + 1);
1388
1859
  if (newVal === void 0) delete obj[head];
1389
1860
  else obj[head] = newVal;
@@ -1393,7 +1864,7 @@ const updatePath = (root, path, updateFn, span, depth = 0) => {
1393
1864
  let arr = [];
1394
1865
  if (Array.isArray(root)) arr = [...root];
1395
1866
  else if (root === null) arr = [];
1396
- else throw new RuntimeError(`Cannot index ${describeType$1(root)} with number ${head}`, span);
1867
+ else throw new RuntimeError(`Cannot index ${describeType(root)} with number ${head}`, span);
1397
1868
  const idx = head < 0 ? arr.length + head : head;
1398
1869
  if (idx < 0) throw new RuntimeError("Invalid negative index", span);
1399
1870
  const newVal = updatePath((idx < arr.length ? arr[idx] : null) ?? null, tail, updateFn, span, depth + 1);
@@ -1411,7 +1882,7 @@ const updatePath = (root, path, updateFn, span, depth = 0) => {
1411
1882
  };
1412
1883
  const deletePaths = (root, paths, span) => {
1413
1884
  if (paths.some((p) => p.length === 0)) return null;
1414
- if (isPlainObject$1(root)) {
1885
+ if (isPlainObject(root)) {
1415
1886
  const result = { ...root };
1416
1887
  const relevantPaths = paths.filter((p) => p.length > 0 && typeof p[0] === "string");
1417
1888
  const byKey = {};
@@ -1448,17 +1919,403 @@ const deletePaths = (root, paths, span) => {
1448
1919
  }
1449
1920
  return root;
1450
1921
  };
1451
- function sortStable(arr, compare) {
1452
- return arr.map((item, index) => ({
1453
- item,
1454
- index
1455
- })).sort((a, b) => {
1456
- const cmp = compare(a.item, b.item);
1457
- return cmp !== 0 ? cmp : a.index - b.index;
1458
- }).map((p) => p.item);
1459
- }
1460
-
1461
- //#endregion
1922
+ const pathBuiltins = [
1923
+ {
1924
+ name: "paths",
1925
+ arity: 0,
1926
+ apply: function* (input, _args, _env, tracker, _eval, span) {
1927
+ yield* traversePaths(input, [], span, tracker);
1928
+ }
1929
+ },
1930
+ {
1931
+ name: "getpath",
1932
+ arity: 1,
1933
+ apply: function* (input, args, env, tracker, evaluate$1, span) {
1934
+ for (const pathVal of evaluate$1(args[0], input, env, tracker)) yield emit$1(getPath(input, ensurePath(pathVal, span)) ?? null, span, tracker);
1935
+ }
1936
+ },
1937
+ {
1938
+ name: "setpath",
1939
+ arity: 2,
1940
+ apply: function* (input, args, env, tracker, evaluate$1, span) {
1941
+ const paths = Array.from(evaluate$1(args[0], input, env, tracker));
1942
+ const values = Array.from(evaluate$1(args[1], input, env, tracker));
1943
+ for (const pathVal of paths) {
1944
+ const path = ensurePath(pathVal, span);
1945
+ for (const val of values) yield emit$1(updatePath(input, path, () => val, span) ?? null, span, tracker);
1946
+ }
1947
+ }
1948
+ },
1949
+ {
1950
+ name: "delpaths",
1951
+ arity: 1,
1952
+ apply: function* (input, args, env, tracker, evaluate$1, span) {
1953
+ for (const pathsVal of evaluate$1(args[0], input, env, tracker)) {
1954
+ if (!Array.isArray(pathsVal)) throw new RuntimeError("delpaths expects an array of paths", span);
1955
+ yield emit$1(deletePaths(input, pathsVal.map((p) => ensurePath(p, span)), span), span, tracker);
1956
+ }
1957
+ }
1958
+ },
1959
+ {
1960
+ name: "path",
1961
+ arity: 1,
1962
+ apply: function* (input, args, env, tracker, evaluate$1, span) {
1963
+ for (const p of evaluatePath(args[0], input, env, tracker, evaluate$1)) yield emit$1(p, span, tracker);
1964
+ }
1965
+ }
1966
+ ];
1967
+
1968
+ //#endregion
1969
+ //#region src/builtins/iterators.ts
1970
+ const iteratorBuiltins = [
1971
+ {
1972
+ name: "range",
1973
+ arity: 1,
1974
+ apply: function* (input, args, env, tracker, evaluate$1, span) {
1975
+ const ends = evaluate$1(args[0], input, env, tracker);
1976
+ for (const end of ends) {
1977
+ if (typeof end !== "number") throw new RuntimeError("range expects numbers", span);
1978
+ for (let i = 0; i < end; i++) yield emit$1(i, span, tracker);
1979
+ }
1980
+ }
1981
+ },
1982
+ {
1983
+ name: "range",
1984
+ arity: 2,
1985
+ apply: function* (input, args, env, tracker, evaluate$1, span) {
1986
+ const starts = Array.from(evaluate$1(args[0], input, env, tracker));
1987
+ const ends = Array.from(evaluate$1(args[1], input, env, tracker));
1988
+ for (const start of starts) for (const end of ends) {
1989
+ if (typeof start !== "number" || typeof end !== "number") throw new RuntimeError("range expects numbers", span);
1990
+ if (start < end) for (let i = start; i < end; i++) yield emit$1(i, span, tracker);
1991
+ }
1992
+ }
1993
+ },
1994
+ {
1995
+ name: "range",
1996
+ arity: 3,
1997
+ apply: function* (input, args, env, tracker, evaluate$1, span) {
1998
+ const starts = Array.from(evaluate$1(args[0], input, env, tracker));
1999
+ const ends = Array.from(evaluate$1(args[1], input, env, tracker));
2000
+ const steps = Array.from(evaluate$1(args[2], input, env, tracker));
2001
+ for (const start of starts) for (const end of ends) for (const step of steps) {
2002
+ if (typeof start !== "number" || typeof end !== "number" || typeof step !== "number") throw new RuntimeError("range expects numbers", span);
2003
+ if (step === 0) throw new RuntimeError("range step cannot be zero", span);
2004
+ if (step > 0) for (let i = start; i < end; i += step) yield emit$1(i, span, tracker);
2005
+ else for (let i = start; i > end; i += step) yield emit$1(i, span, tracker);
2006
+ }
2007
+ }
2008
+ },
2009
+ {
2010
+ name: "limit",
2011
+ arity: 2,
2012
+ apply: function* (input, args, env, tracker, evaluate$1, span) {
2013
+ const limits = evaluate$1(args[0], input, env, tracker);
2014
+ for (const n of limits) {
2015
+ if (typeof n !== "number") throw new RuntimeError("limit expects number", span);
2016
+ let count = 0;
2017
+ if (n > 0) for (const val of evaluate$1(args[1], input, env, tracker)) {
2018
+ yield val;
2019
+ count++;
2020
+ if (count >= n) break;
2021
+ }
2022
+ }
2023
+ }
2024
+ },
2025
+ {
2026
+ name: "first",
2027
+ arity: 1,
2028
+ apply: function* (input, args, env, tracker, evaluate$1) {
2029
+ for (const val of evaluate$1(args[0], input, env, tracker)) {
2030
+ yield val;
2031
+ break;
2032
+ }
2033
+ }
2034
+ },
2035
+ {
2036
+ name: "last",
2037
+ arity: 1,
2038
+ apply: function* (input, args, env, tracker, evaluate$1) {
2039
+ let lastVal;
2040
+ let found = false;
2041
+ for (const val of evaluate$1(args[0], input, env, tracker)) {
2042
+ lastVal = val;
2043
+ found = true;
2044
+ }
2045
+ if (found) yield lastVal;
2046
+ }
2047
+ },
2048
+ {
2049
+ name: "nth",
2050
+ arity: 2,
2051
+ apply: function* (input, args, env, tracker, evaluate$1, span) {
2052
+ const indices = evaluate$1(args[0], input, env, tracker);
2053
+ for (const n of indices) {
2054
+ if (typeof n !== "number") throw new RuntimeError("nth expects number", span);
2055
+ let count = 0;
2056
+ for (const val of evaluate$1(args[1], input, env, tracker)) {
2057
+ if (count === n) {
2058
+ yield val;
2059
+ break;
2060
+ }
2061
+ count++;
2062
+ }
2063
+ }
2064
+ }
2065
+ },
2066
+ {
2067
+ name: "isempty",
2068
+ arity: 1,
2069
+ apply: function* (input, args, env, tracker, evaluate$1, span) {
2070
+ let empty = true;
2071
+ for (const _ of evaluate$1(args[0], input, env, tracker)) {
2072
+ empty = false;
2073
+ break;
2074
+ }
2075
+ yield emit$1(empty, span, tracker);
2076
+ }
2077
+ },
2078
+ {
2079
+ name: "all",
2080
+ arity: 1,
2081
+ apply: function* (input, args, env, tracker, evaluate$1, span) {
2082
+ let result = true;
2083
+ for (const val of evaluate$1(args[0], input, env, tracker)) if (!isTruthy(val)) {
2084
+ result = false;
2085
+ break;
2086
+ }
2087
+ yield emit$1(result, span, tracker);
2088
+ }
2089
+ },
2090
+ {
2091
+ name: "any",
2092
+ arity: 1,
2093
+ apply: function* (input, args, env, tracker, evaluate$1, span) {
2094
+ let result = false;
2095
+ for (const val of evaluate$1(args[0], input, env, tracker)) if (isTruthy(val)) {
2096
+ result = true;
2097
+ break;
2098
+ }
2099
+ yield emit$1(result, span, tracker);
2100
+ }
2101
+ },
2102
+ {
2103
+ name: "recurse",
2104
+ arity: 1,
2105
+ apply: function* (input, args, env, tracker, evaluate$1, span) {
2106
+ const rec = function* (curr) {
2107
+ yield emit$1(curr, span, tracker);
2108
+ const nexts = evaluate$1(args[0], curr, env, tracker);
2109
+ for (const next of nexts) yield* rec(next);
2110
+ };
2111
+ yield* rec(input);
2112
+ }
2113
+ }
2114
+ ];
2115
+
2116
+ //#endregion
2117
+ //#region src/builtins/math.ts
2118
+ const mathBuiltins = [
2119
+ {
2120
+ name: "floor",
2121
+ arity: 0,
2122
+ apply: function* (input, _args, _env, tracker, _eval, span) {
2123
+ if (typeof input !== "number") throw new RuntimeError("floor expects number", span);
2124
+ yield emit$1(Math.floor(input), span, tracker);
2125
+ }
2126
+ },
2127
+ {
2128
+ name: "ceil",
2129
+ arity: 0,
2130
+ apply: function* (input, _args, _env, tracker, _eval, span) {
2131
+ if (typeof input !== "number") throw new RuntimeError("ceil expects number", span);
2132
+ yield emit$1(Math.ceil(input), span, tracker);
2133
+ }
2134
+ },
2135
+ {
2136
+ name: "round",
2137
+ arity: 0,
2138
+ apply: function* (input, _args, _env, tracker, _eval, span) {
2139
+ if (typeof input !== "number") throw new RuntimeError("round expects number", span);
2140
+ yield emit$1(Math.round(input), span, tracker);
2141
+ }
2142
+ },
2143
+ {
2144
+ name: "abs",
2145
+ arity: 0,
2146
+ apply: function* (input, _args, _env, tracker, _eval, span) {
2147
+ if (typeof input !== "number") throw new RuntimeError("abs expects number", span);
2148
+ yield emit$1(Math.abs(input), span, tracker);
2149
+ }
2150
+ },
2151
+ {
2152
+ name: "sqrt",
2153
+ arity: 0,
2154
+ apply: function* (input, _args, _env, tracker, _eval, span) {
2155
+ if (typeof input !== "number") throw new RuntimeError("sqrt expects number", span);
2156
+ yield emit$1(Math.sqrt(input), span, tracker);
2157
+ }
2158
+ },
2159
+ {
2160
+ name: "isnan",
2161
+ arity: 0,
2162
+ apply: function* (input, _args, _env, tracker, _eval, span) {
2163
+ if (typeof input !== "number") {
2164
+ yield emit$1(false, span, tracker);
2165
+ return;
2166
+ }
2167
+ yield emit$1(Number.isNaN(input), span, tracker);
2168
+ }
2169
+ },
2170
+ {
2171
+ name: "infinite",
2172
+ arity: 0,
2173
+ apply: function* (input, _args, _env, tracker, _eval, span) {
2174
+ if (typeof input !== "number") {
2175
+ yield emit$1(false, span, tracker);
2176
+ return;
2177
+ }
2178
+ yield emit$1(!Number.isFinite(input) && !Number.isNaN(input), span, tracker);
2179
+ }
2180
+ },
2181
+ {
2182
+ name: "isfinite",
2183
+ arity: 0,
2184
+ apply: function* (input, _args, _env, tracker, _eval, span) {
2185
+ if (typeof input !== "number") {
2186
+ yield emit$1(true, span, tracker);
2187
+ return;
2188
+ }
2189
+ yield emit$1(Number.isFinite(input), span, tracker);
2190
+ }
2191
+ },
2192
+ {
2193
+ name: "normal",
2194
+ arity: 0,
2195
+ apply: function* (input, _args, _env, tracker, _eval, span) {
2196
+ if (typeof input !== "number") {
2197
+ yield emit$1(false, span, tracker);
2198
+ return;
2199
+ }
2200
+ if (!Number.isFinite(input) || Number.isNaN(input) || input === 0) {
2201
+ yield emit$1(false, span, tracker);
2202
+ return;
2203
+ }
2204
+ yield emit$1(true, span, tracker);
2205
+ }
2206
+ },
2207
+ {
2208
+ name: "subnormal",
2209
+ arity: 0,
2210
+ apply: function* (input, _args, _env, tracker, _eval, span) {
2211
+ if (typeof input !== "number") {
2212
+ yield emit$1(false, span, tracker);
2213
+ return;
2214
+ }
2215
+ yield emit$1(false, span, tracker);
2216
+ }
2217
+ },
2218
+ {
2219
+ name: "min",
2220
+ arity: 0,
2221
+ apply: function* (input, _args, _env, tracker, _eval, span) {
2222
+ if (!Array.isArray(input)) throw new RuntimeError("min expects an array", span);
2223
+ if (input.length === 0) {
2224
+ yield emit$1(null, span, tracker);
2225
+ return;
2226
+ }
2227
+ let minVal = input[0];
2228
+ for (let i = 1; i < input.length; i++) if (compareValues(input[i], minVal) < 0) minVal = input[i];
2229
+ yield emit$1(minVal, span, tracker);
2230
+ }
2231
+ },
2232
+ {
2233
+ name: "max",
2234
+ arity: 0,
2235
+ apply: function* (input, _args, _env, tracker, _eval, span) {
2236
+ if (!Array.isArray(input)) throw new RuntimeError("max expects an array", span);
2237
+ if (input.length === 0) {
2238
+ yield emit$1(null, span, tracker);
2239
+ return;
2240
+ }
2241
+ let maxVal = input[0];
2242
+ for (let i = 1; i < input.length; i++) if (compareValues(input[i], maxVal) > 0) maxVal = input[i];
2243
+ yield emit$1(maxVal, span, tracker);
2244
+ }
2245
+ },
2246
+ {
2247
+ name: "min_by",
2248
+ arity: 1,
2249
+ apply: function* (input, args, env, tracker, evaluate$1, span) {
2250
+ if (!Array.isArray(input)) throw new RuntimeError("min_by expects an array", span);
2251
+ if (input.length === 0) {
2252
+ yield emit$1(null, span, tracker);
2253
+ return;
2254
+ }
2255
+ let minItem = input[0];
2256
+ let minKey;
2257
+ const keys0 = Array.from(evaluate$1(args[0], minItem, env, tracker));
2258
+ if (keys0.length !== 1) throw new RuntimeError("min_by key must return one value", span);
2259
+ minKey = keys0[0];
2260
+ for (let i = 1; i < input.length; i++) {
2261
+ const item = input[i];
2262
+ const keys = Array.from(evaluate$1(args[0], item, env, tracker));
2263
+ if (keys.length !== 1) throw new RuntimeError("min_by key must return one value", span);
2264
+ const key = keys[0];
2265
+ if (compareValues(key, minKey) < 0) {
2266
+ minKey = key;
2267
+ minItem = item;
2268
+ }
2269
+ }
2270
+ yield emit$1(minItem, span, tracker);
2271
+ }
2272
+ },
2273
+ {
2274
+ name: "max_by",
2275
+ arity: 1,
2276
+ apply: function* (input, args, env, tracker, evaluate$1, span) {
2277
+ if (!Array.isArray(input)) throw new RuntimeError("max_by expects an array", span);
2278
+ if (input.length === 0) {
2279
+ yield emit$1(null, span, tracker);
2280
+ return;
2281
+ }
2282
+ let maxItem = input[0];
2283
+ let maxKey;
2284
+ const keys0 = Array.from(evaluate$1(args[0], maxItem, env, tracker));
2285
+ if (keys0.length !== 1) throw new RuntimeError("max_by key must return one value", span);
2286
+ maxKey = keys0[0];
2287
+ for (let i = 1; i < input.length; i++) {
2288
+ const item = input[i];
2289
+ const keys = Array.from(evaluate$1(args[0], item, env, tracker));
2290
+ if (keys.length !== 1) throw new RuntimeError("max_by key must return one value", span);
2291
+ const key = keys[0];
2292
+ if (compareValues(key, maxKey) > 0) {
2293
+ maxKey = key;
2294
+ maxItem = item;
2295
+ }
2296
+ }
2297
+ yield emit$1(maxItem, span, tracker);
2298
+ }
2299
+ }
2300
+ ];
2301
+
2302
+ //#endregion
2303
+ //#region src/builtins/index.ts
2304
+ const registerAllBuiltins = () => {
2305
+ registerBuiltins(stdBuiltins);
2306
+ registerBuiltins(errorBuiltins);
2307
+ registerBuiltins(collectionBuiltins);
2308
+ registerBuiltins(stringBuiltins);
2309
+ registerBuiltins(pathBuiltins);
2310
+ registerBuiltins(iteratorBuiltins);
2311
+ registerBuiltins(mathBuiltins);
2312
+ };
2313
+
2314
+ //#endregion
2315
+ //#region src/builtins.ts
2316
+ registerAllBuiltins();
2317
+
2318
+ //#endregion
1462
2319
  //#region src/validate.ts
1463
2320
  /**
1464
2321
  * Validates the AST for correctness and supported features.
@@ -1468,82 +2325,124 @@ function sortStable(arr, compare) {
1468
2325
  * @throws {ValidationError} If validation fails.
1469
2326
  */
1470
2327
  const validate = (node) => {
1471
- visit(node);
2328
+ visit(node, []);
1472
2329
  };
1473
- const visit = (node) => {
2330
+ const visit = (node, scope) => {
1474
2331
  switch (node.kind) {
1475
2332
  case "Identity":
1476
2333
  case "Literal":
1477
2334
  case "Var": return;
1478
2335
  case "FieldAccess":
1479
- visit(node.target);
2336
+ visit(node.target, scope);
1480
2337
  return;
1481
2338
  case "IndexAccess":
1482
- visit(node.target);
1483
- visit(node.index);
2339
+ visit(node.target, scope);
2340
+ visit(node.index, scope);
1484
2341
  return;
1485
2342
  case "Array":
1486
- node.items.forEach(visit);
2343
+ node.items.forEach((n) => visit(n, scope));
1487
2344
  return;
1488
2345
  case "Object":
1489
2346
  node.entries.forEach((entry) => {
1490
- if (entry.key.kind === "KeyExpr") visit(entry.key.expr);
1491
- visit(entry.value);
2347
+ if (entry.key.kind === "KeyExpr") visit(entry.key.expr, scope);
2348
+ visit(entry.value, scope);
1492
2349
  });
1493
2350
  return;
1494
2351
  case "Pipe":
1495
2352
  case "Comma":
1496
2353
  case "Alt":
1497
- visit(node.left);
1498
- visit(node.right);
2354
+ visit(node.left, scope);
2355
+ visit(node.right, scope);
1499
2356
  return;
1500
2357
  case "Binary":
1501
2358
  case "Bool":
1502
- visit(node.left);
1503
- visit(node.right);
2359
+ visit(node.left, scope);
2360
+ visit(node.right, scope);
1504
2361
  return;
1505
2362
  case "Unary":
1506
- visit(node.expr);
2363
+ visit(node.expr, scope);
1507
2364
  return;
1508
2365
  case "If":
1509
2366
  node.branches.forEach((branch) => {
1510
- visit(branch.cond);
1511
- visit(branch.then);
2367
+ visit(branch.cond, scope);
2368
+ visit(branch.then, scope);
1512
2369
  });
1513
- visit(node.else);
2370
+ visit(node.else, scope);
1514
2371
  return;
1515
2372
  case "As":
1516
- visit(node.bind);
1517
- visit(node.body);
2373
+ visit(node.bind, scope);
2374
+ visit(node.body, scope);
1518
2375
  return;
1519
2376
  case "Call": {
1520
- const builtin = builtins[node.name];
1521
- if (!builtin) throw new ValidationError(`Unknown function: ${node.name}`, node.span);
1522
- if (builtin.arity !== node.args.length) throw new ValidationError(`Function ${node.name} expects ${builtin.arity} arguments, but got ${node.args.length}`, node.span);
1523
- node.args.forEach(visit);
2377
+ for (let i = scope.length - 1; i >= 0; i--) if (scope[i].has(node.name)) {
2378
+ for (const arg of node.args) visit(arg, scope);
2379
+ return;
2380
+ }
2381
+ const specs = builtins[node.name];
2382
+ if (!specs || specs.length === 0) throw new ValidationError(`Unknown function: ${node.name}`, node.span);
2383
+ if (!specs.find((s) => s.arity === node.args.length)) {
2384
+ const arities = specs.map((s) => s.arity).join(" or ");
2385
+ throw new ValidationError(`Function ${node.name} expects ${arities} arguments, but got ${node.args.length}`, node.span);
2386
+ }
2387
+ for (const arg of node.args) visit(arg, scope);
2388
+ return;
2389
+ }
2390
+ case "Assignment":
2391
+ visit(node.left, scope);
2392
+ visit(node.right, scope);
2393
+ return;
2394
+ case "Def": {
2395
+ const bodyScope = new Set(node.args);
2396
+ bodyScope.add(node.name);
2397
+ visit(node.body, [...scope, bodyScope]);
2398
+ const nextScope = new Set([node.name]);
2399
+ visit(node.next, [...scope, nextScope]);
1524
2400
  return;
1525
2401
  }
1526
2402
  case "Reduce":
1527
- visit(node.source);
1528
- visit(node.init);
1529
- visit(node.update);
2403
+ visit(node.source, scope);
2404
+ visit(node.init, scope);
2405
+ visit(node.update, scope);
1530
2406
  return;
1531
2407
  case "Foreach":
1532
- visit(node.source);
1533
- visit(node.init);
1534
- visit(node.update);
1535
- if (node.extract) visit(node.extract);
2408
+ visit(node.source, scope);
2409
+ visit(node.init, scope);
2410
+ visit(node.update, scope);
2411
+ if (node.extract) visit(node.extract, scope);
1536
2412
  return;
1537
2413
  case "Try":
1538
- visit(node.body);
1539
- if (node.handler) visit(node.handler);
2414
+ visit(node.body, scope);
2415
+ if (node.handler) visit(node.handler, scope);
1540
2416
  return;
1541
2417
  case "Recurse":
1542
- case "Iterate": return;
2418
+ case "Iterate":
2419
+ case "Break": return;
2420
+ case "Label":
2421
+ visit(node.body, scope);
2422
+ return;
2423
+ case "Slice":
2424
+ visit(node.target, scope);
2425
+ if (node.start) visit(node.start, scope);
2426
+ if (node.end) visit(node.end, scope);
2427
+ return;
1543
2428
  default: return node;
1544
2429
  }
1545
2430
  };
1546
2431
 
2432
+ //#endregion
2433
+ //#region src/eval/break.ts
2434
+ /**
2435
+ * Internal error class used to implement control flow breaks (`break $label`).
2436
+ * Caught by `evalLabel` or `evalForeach`/`evalReduce` if labels match.
2437
+ */
2438
+ var BreakSignal = class BreakSignal extends Error {
2439
+ constructor(label) {
2440
+ super(`Break: ${label}`);
2441
+ this.label = label;
2442
+ Object.setPrototypeOf(this, BreakSignal.prototype);
2443
+ }
2444
+ };
2445
+
1547
2446
  //#endregion
1548
2447
  //#region src/limits.ts
1549
2448
  const DEFAULT_LIMITS = {
@@ -1606,167 +2505,298 @@ var LimitTracker = class {
1606
2505
  };
1607
2506
 
1608
2507
  //#endregion
1609
- //#region src/eval.ts
1610
- const runAst = (ast, input, options = {}) => {
1611
- const tracker = new LimitTracker(resolveLimits(options.limits));
1612
- const env = [/* @__PURE__ */ new Map()];
1613
- return Array.from(evaluate(ast, input, env, tracker));
2508
+ //#region src/eval/ops.ts
2509
+ /**
2510
+ * Applies a unary negation (`-`).
2511
+ *
2512
+ * @param value - The value to negate.
2513
+ * @param span - Source span for errors.
2514
+ * @returns The negated value.
2515
+ */
2516
+ const applyUnaryNeg = (value, span) => {
2517
+ if (typeof value === "number") return -value;
2518
+ throw new RuntimeError(`Invalid operand for unary -: ${describeType(value)}`, span);
1614
2519
  };
1615
2520
  /**
1616
- * Evaluates an AST node against an input value.
1617
- * This is a generator function that yields results lazily.
2521
+ * Applies a binary operator.
1618
2522
  *
1619
- * @param node - The AST node to evaluate.
1620
- * @param input - The current input value (context).
1621
- * @param env - The variable environment stack.
1622
- * @param tracker - Limits tracker for cycle/output limits.
2523
+ * Supports arithmetic (`+`, `-`, `*`, `/`, `%`) and comparators.
2524
+ *
2525
+ * @param op - The operator string.
2526
+ * @param left - The left operand.
2527
+ * @param right - The right operand.
2528
+ * @param span - Source span for errors.
2529
+ * @returns The result of the operation.
1623
2530
  */
1624
- function* evaluate(node, input, env, tracker) {
1625
- tracker.step(node.span);
1626
- tracker.enter(node.span);
1627
- try {
1628
- switch (node.kind) {
1629
- case "Identity":
1630
- yield emit(input, node.span, tracker);
1631
- return;
1632
- case "Literal":
1633
- yield emit(node.value, node.span, tracker);
1634
- return;
1635
- case "Var":
1636
- yield emit(getVar(env, node.name, node.span), node.span, tracker);
1637
- return;
1638
- case "FieldAccess":
1639
- yield* evalField(node, input, env, tracker);
1640
- return;
1641
- case "IndexAccess":
1642
- yield* evalIndex(node, input, env, tracker);
1643
- return;
1644
- case "Array":
1645
- yield emit(buildArray(node, input, env, tracker), node.span, tracker);
1646
- return;
1647
- case "Object":
1648
- yield* buildObjects(node, input, env, tracker);
1649
- return;
1650
- case "Pipe":
1651
- for (const left of evaluate(node.left, input, env, tracker)) yield* evaluate(node.right, left, env, tracker);
1652
- return;
1653
- case "Comma":
1654
- yield* evaluate(node.left, input, env, tracker);
1655
- yield* evaluate(node.right, input, env, tracker);
1656
- return;
1657
- case "Alt": {
1658
- const valid = Array.from(evaluate(node.left, input, env, tracker)).filter((v) => v !== null && v !== false);
1659
- if (valid.length > 0) for (const v of valid) yield v;
1660
- else yield* evaluate(node.right, input, env, tracker);
1661
- return;
1662
- }
1663
- case "Unary":
1664
- if (node.op === "Not") for (const value of evaluate(node.expr, input, env, tracker)) yield emit(!isTruthy(value), node.span, tracker);
1665
- else for (const value of evaluate(node.expr, input, env, tracker)) yield emit(applyUnaryNeg(value, node.span), node.span, tracker);
1666
- return;
1667
- case "Binary":
1668
- yield* evalBinary(node, input, env, tracker);
1669
- return;
1670
- case "Bool":
1671
- if (node.op === "Or") for (const left of evaluate(node.left, input, env, tracker)) if (isTruthy(left)) yield emit(true, node.span, tracker);
1672
- else for (const right of evaluate(node.right, input, env, tracker)) yield emit(isTruthy(right), node.span, tracker);
1673
- else for (const left of evaluate(node.left, input, env, tracker)) if (!isTruthy(left)) yield emit(false, node.span, tracker);
1674
- else for (const right of evaluate(node.right, input, env, tracker)) yield emit(isTruthy(right), node.span, tracker);
1675
- return;
1676
- case "If":
1677
- for (const branch of node.branches) {
1678
- const condValues = Array.from(evaluate(branch.cond, input, env, tracker));
1679
- if (condValues.length > 1) throw new RuntimeError("Condition produced multiple values", branch.cond.span);
1680
- if (condValues.length === 1 && isTruthy(condValues[0])) {
1681
- yield* evaluate(branch.then, input, env, tracker);
1682
- return;
1683
- }
1684
- }
1685
- yield* evaluate(node.else, input, env, tracker);
1686
- return;
1687
- case "As": {
1688
- const boundValues = Array.from(evaluate(node.bind, input, env, tracker));
1689
- for (const value of boundValues) {
1690
- pushBinding(env, node.name, value);
1691
- try {
1692
- yield* evaluate(node.body, input, env, tracker);
1693
- } finally {
1694
- popBinding(env);
1695
- }
1696
- }
1697
- return;
1698
- }
1699
- case "Call": {
1700
- const builtin = builtins[node.name];
1701
- if (!builtin) throw new RuntimeError(`Unknown function: ${node.name}/${node.args.length}`, node.span);
1702
- yield* builtin.apply(input, node.args, env, tracker, evaluate, node.span);
1703
- return;
1704
- }
1705
- case "Reduce":
1706
- yield* evalReduce(node, input, env, tracker);
1707
- return;
1708
- case "Foreach":
1709
- yield* evalForeach(node, input, env, tracker);
1710
- return;
1711
- case "Try":
1712
- yield* evalTry(node, input, env, tracker);
1713
- return;
1714
- case "Recurse":
1715
- yield* evalRecurse(node, input, env, tracker);
1716
- return;
1717
- case "Iterate":
1718
- yield* evalIterate(node, input, env, tracker);
1719
- return;
2531
+ const applyBinaryOp = (op, left, right, span) => {
2532
+ switch (op) {
2533
+ case "+": return add(left, right, span);
2534
+ case "-": return sub(left, right, span);
2535
+ case "*": return mul(left, right, span);
2536
+ case "/": return div(left, right, span);
2537
+ case "%": return mod(left, right, span);
2538
+ case "Eq": return isEqual(left, right);
2539
+ case "Neq": return !isEqual(left, right);
2540
+ case "Lt": return compare(left, right) < 0;
2541
+ case "Lte": return compare(left, right) <= 0;
2542
+ case "Gt": return compare(left, right) > 0;
2543
+ case "Gte": return compare(left, right) >= 0;
2544
+ default: throw new RuntimeError(`Unknown binary operator: ${op}`, span);
2545
+ }
2546
+ };
2547
+ function isEqual(a, b) {
2548
+ if (a === b) return true;
2549
+ if (a === null || b === null) return false;
2550
+ if (typeof a !== typeof b) return false;
2551
+ if (Array.isArray(a) && Array.isArray(b)) {
2552
+ if (a.length !== b.length) return false;
2553
+ return a.every((v, i) => isEqual(v, b[i]));
2554
+ }
2555
+ if (isPlainObject$1(a) && isPlainObject$1(b)) {
2556
+ const ka = Object.keys(a).sort();
2557
+ const kb = Object.keys(b).sort();
2558
+ if (ka.length !== kb.length) return false;
2559
+ if (!ka.every((k, i) => k === kb[i])) return false;
2560
+ return ka.every((k) => isEqual(a[k], b[k]));
2561
+ }
2562
+ return false;
2563
+ }
2564
+ function compare(a, b) {
2565
+ if (a === b) return 0;
2566
+ const typeOrder = (v) => {
2567
+ if (v === null) return 0;
2568
+ if (typeof v === "boolean") return 1;
2569
+ if (typeof v === "number") return 2;
2570
+ if (typeof v === "string") return 3;
2571
+ if (Array.isArray(v)) return 4;
2572
+ if (isPlainObject$1(v)) return 5;
2573
+ return 6;
2574
+ };
2575
+ const ta = typeOrder(a);
2576
+ const tb = typeOrder(b);
2577
+ if (ta !== tb) return ta - tb;
2578
+ if (typeof a === "boolean" && typeof b === "boolean") return (a ? 1 : 0) - (b ? 1 : 0);
2579
+ if (typeof a === "number" && typeof b === "number") return a - b;
2580
+ if (typeof a === "string" && typeof b === "string") return a < b ? -1 : 1;
2581
+ if (Array.isArray(a) && Array.isArray(b)) {
2582
+ for (let i = 0; i < Math.min(a.length, b.length); i++) {
2583
+ const c = compare(a[i], b[i]);
2584
+ if (c !== 0) return c;
1720
2585
  }
1721
- } finally {
1722
- tracker.exit();
2586
+ return a.length - b.length;
2587
+ }
2588
+ if (isPlainObject$1(a) && isPlainObject$1(b)) {
2589
+ const keysA = Object.keys(a).sort();
2590
+ const keysB = Object.keys(b).sort();
2591
+ for (let i = 0; i < Math.min(keysA.length, keysB.length); i++) {
2592
+ const kA = keysA[i];
2593
+ const kB = keysB[i];
2594
+ if (kA !== kB) return kA < kB ? -1 : 1;
2595
+ const c = compare(a[kA], b[kB]);
2596
+ if (c !== 0) return c;
2597
+ }
2598
+ return keysA.length - keysB.length;
2599
+ }
2600
+ return 0;
2601
+ }
2602
+ function add(left, right, span) {
2603
+ if (left === null && right === null) return null;
2604
+ if (left === null && typeof right === "number") return right;
2605
+ if (typeof left === "number" && right === null) return left;
2606
+ if (typeof left === "number" && typeof right === "number") return left + right;
2607
+ if (typeof left === "string" && typeof right === "string") return left + right;
2608
+ if (Array.isArray(left) && Array.isArray(right)) return [...left, ...right];
2609
+ if (isPlainObject$1(left) && isPlainObject$1(right)) return {
2610
+ ...left,
2611
+ ...right
2612
+ };
2613
+ throw new RuntimeError(`Cannot add ${describeType(left)} and ${describeType(right)}`, span);
2614
+ }
2615
+ function sub(left, right, span) {
2616
+ if (typeof left === "number" && typeof right === "number") return left - right;
2617
+ if (Array.isArray(left) && Array.isArray(right)) {
2618
+ const toRemove = new Set(right.map(stableStringify));
2619
+ return left.filter((x) => !toRemove.has(stableStringify(x)));
1723
2620
  }
2621
+ throw new RuntimeError(`Cannot subtract ${describeType(right)} from ${describeType(left)}`, span);
2622
+ }
2623
+ function mul(left, right, span) {
2624
+ if (typeof left === "number" && typeof right === "number") return left * right;
2625
+ if (typeof left === "string" && typeof right === "number") return repeatString(left, right);
2626
+ if (typeof left === "number" && typeof right === "string") return repeatString(right, left);
2627
+ if (isPlainObject$1(left) && isPlainObject$1(right)) return mergeDeep(left, right);
2628
+ throw new RuntimeError(`Cannot multiply ${describeType(left)} by ${describeType(right)}`, span);
2629
+ }
2630
+ function div(left, right, span) {
2631
+ if (typeof left === "number" && typeof right === "number") {
2632
+ if (right === 0) throw new RuntimeError("Division by zero", span);
2633
+ return left / right;
2634
+ }
2635
+ if (typeof left === "string" && typeof right === "string") return left.split(right);
2636
+ throw new RuntimeError(`Cannot divide ${describeType(left)} by ${describeType(right)}`, span);
2637
+ }
2638
+ function mod(left, right, span) {
2639
+ if (typeof left === "number" && typeof right === "number") {
2640
+ if (right === 0) throw new RuntimeError("Modulo by zero", span);
2641
+ return left % right;
2642
+ }
2643
+ throw new RuntimeError(`Cannot modulo ${describeType(left)} by ${describeType(right)}`, span);
2644
+ }
2645
+ function repeatString(str, count) {
2646
+ if (count <= 0) return null;
2647
+ const n = Math.floor(count);
2648
+ if (n <= 0) return null;
2649
+ return str.repeat(n);
2650
+ }
2651
+ function mergeDeep(target, source) {
2652
+ const result = { ...target };
2653
+ for (const key of Object.keys(source)) {
2654
+ const sVal = source[key];
2655
+ const tVal = result[key];
2656
+ if (isPlainObject$1(sVal) && tVal !== void 0 && isPlainObject$1(tVal)) result[key] = mergeDeep(tVal, sVal);
2657
+ else result[key] = sVal;
2658
+ }
2659
+ return result;
2660
+ }
2661
+ function isPlainObject$1(v) {
2662
+ return typeof v === "object" && v !== null && !Array.isArray(v);
1724
2663
  }
2664
+
2665
+ //#endregion
2666
+ //#region src/eval/env.ts
2667
+ /**
2668
+ * Retrieves a variable value from the environment stack.
2669
+ * Searches from the current (top) frame down to the global frame.
2670
+ */
2671
+ const getVar = (env, name) => {
2672
+ for (let i = env.length - 1; i >= 0; i--) if (env[i].vars.has(name)) return env[i].vars.get(name);
2673
+ };
2674
+ /**
2675
+ * Bounds a value to a variable name in the current scope frame.
2676
+ */
2677
+ const pushBinding = (env, name, value) => {
2678
+ env[env.length - 1].vars.set(name, value);
2679
+ };
2680
+ /**
2681
+ * Removes a variable binding from the current scope frame.
2682
+ */
2683
+ const popBinding = (env, name) => {
2684
+ env[env.length - 1].vars.delete(name);
2685
+ };
2686
+
2687
+ //#endregion
2688
+ //#region src/eval/common.ts
2689
+ /**
2690
+ * Emits a value, recording it in the limits tracker.
2691
+ *
2692
+ * @param value - The value to yield.
2693
+ * @param span - The source span responsible for this value.
2694
+ * @param tracker - The limits tracker.
2695
+ * @returns The value itself.
2696
+ */
1725
2697
  const emit = (value, span, tracker) => {
1726
2698
  tracker.emit(span);
1727
2699
  return value;
1728
2700
  };
1729
- const evalField = function* (node, input, env, tracker) {
1730
- for (const container of evaluate(node.target, input, env, tracker)) {
1731
- if (container === null) {
1732
- yield emit(null, node.span, tracker);
1733
- continue;
1734
- }
1735
- if (isPlainObject(container)) {
1736
- yield emit(Object.prototype.hasOwnProperty.call(container, node.field) ? container[node.field] : null, node.span, tracker);
1737
- continue;
2701
+ /**
2702
+ * Ensures a value is an integer. Throws a RuntimeError otherwise.
2703
+ *
2704
+ * @param value - The value to check.
2705
+ * @param span - To report error.
2706
+ * @returns The integer value.
2707
+ */
2708
+ const ensureInteger = (value, span) => {
2709
+ if (typeof value === "number" && Number.isInteger(value)) return value;
2710
+ throw new RuntimeError("Expected integer", span);
2711
+ };
2712
+
2713
+ //#endregion
2714
+ //#region src/eval/assignment.ts
2715
+ /**
2716
+ * Evaluates assignment and update operators (`=`, `|=`, `+=`, etc.).
2717
+ *
2718
+ * Implements the complex path update logic of jq.
2719
+ * It first resolves the path(s) on the left-hand side, then computes new values,
2720
+ * and finally reconstructs the object with the updated values.
2721
+ *
2722
+ * @param node - The assignment AST node.
2723
+ * @param input - The current input value.
2724
+ * @param env - The environment.
2725
+ * @param tracker - Limits tracker.
2726
+ * @param evaluate - Recursive evaluator.
2727
+ */
2728
+ const evalAssignment = function* (node, input, env, tracker, evaluate$1) {
2729
+ const paths = Array.from(evaluatePath(node.left, input, env, tracker, evaluate$1));
2730
+ if (paths.length === 0) {
2731
+ yield emit(input, node.span, tracker);
2732
+ return;
2733
+ }
2734
+ if (node.op === "=") {
2735
+ const rhsValues = Array.from(evaluate$1(node.right, input, env, tracker));
2736
+ if (rhsValues.length === 0) return;
2737
+ for (const rhsVal of rhsValues) {
2738
+ let current = input;
2739
+ for (const path of paths) current = updatePath(current, path, () => rhsVal, node.span) ?? current;
2740
+ yield emit(current, node.span, tracker);
1738
2741
  }
1739
- throw new RuntimeError(`Cannot index ${describeType(container)} with string`, node.span);
2742
+ return;
1740
2743
  }
2744
+ yield* applyUpdates(input, paths, 0, node.op, node.right, input, env, tracker, evaluate$1);
1741
2745
  };
1742
- const evalIndex = function* (node, input, env, tracker) {
1743
- const indexValues = Array.from(evaluate(node.index, input, env, tracker));
1744
- for (const container of evaluate(node.target, input, env, tracker)) {
1745
- if (container === null) {
1746
- yield emit(null, node.span, tracker);
1747
- continue;
1748
- }
1749
- if (isValueArray(container)) {
1750
- for (const idxValue of indexValues) {
1751
- const index = ensureInteger(idxValue, node.span);
1752
- const resolved = index < 0 ? container.length + index : index;
1753
- if (resolved < 0 || resolved >= container.length) yield emit(null, node.span, tracker);
1754
- else yield emit(container[resolved], node.span, tracker);
2746
+ function* applyUpdates(current, paths, index, op, rhsNode, contextInput, env, tracker, evaluate$1) {
2747
+ if (index >= paths.length) {
2748
+ yield current;
2749
+ return;
2750
+ }
2751
+ const path = paths[index];
2752
+ const oldValue = getPath(current, path) ?? null;
2753
+ let newValues = [];
2754
+ if (op === "|=") newValues = Array.from(evaluate$1(rhsNode, oldValue, env, tracker));
2755
+ else {
2756
+ const rhsResults = Array.from(evaluate$1(rhsNode, contextInput, env, tracker));
2757
+ for (const rhs of rhsResults) {
2758
+ let res;
2759
+ switch (op) {
2760
+ case "+=":
2761
+ res = applyBinaryOp("+", oldValue, rhs, rhsNode.span);
2762
+ break;
2763
+ case "-=":
2764
+ res = applyBinaryOp("-", oldValue, rhs, rhsNode.span);
2765
+ break;
2766
+ case "*=":
2767
+ res = applyBinaryOp("*", oldValue, rhs, rhsNode.span);
2768
+ break;
2769
+ case "/=":
2770
+ res = applyBinaryOp("/", oldValue, rhs, rhsNode.span);
2771
+ break;
2772
+ case "%=":
2773
+ res = applyBinaryOp("%", oldValue, rhs, rhsNode.span);
2774
+ break;
2775
+ case "//=":
2776
+ res = oldValue !== null && oldValue !== false ? oldValue : rhs;
2777
+ break;
2778
+ default: throw new RuntimeError(`Unknown assignment op: ${op}`, rhsNode.span);
1755
2779
  }
1756
- continue;
2780
+ newValues.push(res);
1757
2781
  }
1758
- if (isPlainObject(container)) {
1759
- for (const keyValue of indexValues) {
1760
- if (typeof keyValue !== "string") throw new RuntimeError(`Cannot index object with ${describeType(keyValue)}`, node.span);
1761
- yield emit(Object.prototype.hasOwnProperty.call(container, keyValue) ? container[keyValue] : null, node.span, tracker);
1762
- }
1763
- continue;
1764
- }
1765
- throw new RuntimeError(`Cannot index ${describeType(container)}`, node.span);
1766
2782
  }
1767
- };
1768
- const evalIterate = function* (node, input, env, tracker) {
1769
- for (const container of evaluate(node.target, input, env, tracker)) {
2783
+ if (newValues.length === 0) return;
2784
+ for (const val of newValues) yield* applyUpdates(updatePath(current, path, () => val, rhsNode.span) ?? current, paths, index + 1, op, rhsNode, contextInput, env, tracker, evaluate$1);
2785
+ }
2786
+
2787
+ //#endregion
2788
+ //#region src/eval/iterators.ts
2789
+ /**
2790
+ * Iterates over the values of an array or object (`.[]`).
2791
+ *
2792
+ * @param node - The iterate AST node.
2793
+ * @param input - The current input value.
2794
+ * @param env - The environment.
2795
+ * @param tracker - Limits tracker.
2796
+ * @param evaluate - Recursive evaluator.
2797
+ */
2798
+ const evalIterate = function* (node, input, env, tracker, evaluate$1) {
2799
+ for (const container of evaluate$1(node.target, input, env, tracker)) {
1770
2800
  if (container === null) continue;
1771
2801
  if (isValueArray(container)) {
1772
2802
  for (const item of container) yield emit(item, node.span, tracker);
@@ -1780,232 +2810,508 @@ const evalIterate = function* (node, input, env, tracker) {
1780
2810
  throw new RuntimeError(`Cannot iterate over ${describeType(container)}`, node.span);
1781
2811
  }
1782
2812
  };
1783
- const evalReduce = function* (node, input, env, tracker) {
1784
- const initValues = Array.from(evaluate(node.init, input, env, tracker));
2813
+ /**
2814
+ * Executes a `reduce` operation.
2815
+ *
2816
+ * `reduce inputs as $var (init; update)`
2817
+ *
2818
+ * @param node - The reduce AST node.
2819
+ * @param input - The current input value.
2820
+ * @param env - The environment.
2821
+ * @param tracker - Limits tracker.
2822
+ * @param evaluate - Recursive evaluator.
2823
+ */
2824
+ const evalReduce = function* (node, input, env, tracker, evaluate$1) {
2825
+ const initValues = Array.from(evaluate$1(node.init, input, env, tracker));
1785
2826
  if (initValues.length !== 1) throw new RuntimeError("Reduce init must single value", node.init.span);
1786
2827
  let acc = initValues[0];
1787
- for (const item of evaluate(node.source, input, env, tracker)) {
2828
+ for (const item of evaluate$1(node.source, input, env, tracker)) {
1788
2829
  tracker.step(node.span);
1789
2830
  pushBinding(env, node.var, item);
1790
2831
  try {
1791
- const updates = Array.from(evaluate(node.update, acc, env, tracker));
2832
+ const updates = Array.from(evaluate$1(node.update, acc, env, tracker));
1792
2833
  if (updates.length !== 1) throw new RuntimeError("Reduce update must produce single value", node.update.span);
1793
2834
  acc = updates[0];
1794
2835
  } finally {
1795
- popBinding(env);
2836
+ popBinding(env, node.var);
1796
2837
  }
1797
2838
  }
1798
2839
  yield emit(acc, node.span, tracker);
1799
2840
  };
1800
- const evalForeach = function* (node, input, env, tracker) {
1801
- const initValues = Array.from(evaluate(node.init, input, env, tracker));
2841
+ /**
2842
+ * Executes a `foreach` operation.
2843
+ *
2844
+ * `foreach inputs as $var (init; update; extract)`
2845
+ *
2846
+ * @param node - The foreach AST node.
2847
+ * @param input - The current input value.
2848
+ * @param env - The environment.
2849
+ * @param tracker - Limits tracker.
2850
+ * @param evaluate - Recursive evaluator.
2851
+ */
2852
+ const evalForeach = function* (node, input, env, tracker, evaluate$1) {
2853
+ const initValues = Array.from(evaluate$1(node.init, input, env, tracker));
1802
2854
  if (initValues.length !== 1) throw new RuntimeError("Foreach init must single value", node.init.span);
1803
2855
  let acc = initValues[0];
1804
- for (const item of evaluate(node.source, input, env, tracker)) {
2856
+ for (const item of evaluate$1(node.source, input, env, tracker)) {
1805
2857
  tracker.step(node.span);
1806
2858
  pushBinding(env, node.var, item);
1807
2859
  try {
1808
- const updates = Array.from(evaluate(node.update, acc, env, tracker));
2860
+ const updates = Array.from(evaluate$1(node.update, acc, env, tracker));
1809
2861
  if (updates.length !== 1) throw new RuntimeError("Foreach update must produce single value", node.update.span);
1810
2862
  acc = updates[0];
1811
- if (node.extract) for (const extracted of evaluate(node.extract, acc, env, tracker)) yield emit(extracted, node.span, tracker);
2863
+ if (node.extract) for (const extracted of evaluate$1(node.extract, acc, env, tracker)) yield emit(extracted, node.span, tracker);
1812
2864
  else yield emit(acc, node.span, tracker);
1813
2865
  } finally {
1814
- popBinding(env);
2866
+ popBinding(env, node.var);
1815
2867
  }
1816
2868
  }
1817
2869
  };
1818
- const evalTry = function* (node, input, env, tracker) {
1819
- try {
1820
- yield* evaluate(node.body, input, env, tracker);
1821
- } catch (err) {
1822
- if (err instanceof RuntimeError) {
1823
- if (node.handler) yield* evaluate(node.handler, err.message, env, tracker);
1824
- } else throw err;
1825
- }
1826
- };
1827
- const evalRecurse = function* (node, input, env, tracker) {
2870
+ /**
2871
+ * Evaluates the recursive operator `..`.
2872
+ * Deprecated node type kept for compatibility; currently implements `..` logic.
2873
+ *
2874
+ * @param node - The recurse AST node.
2875
+ * @param input - The current input value.
2876
+ * @param env - The environment.
2877
+ * @param tracker - Limits tracker.
2878
+ * @param evaluate - Recursive evaluator.
2879
+ */
2880
+ const evalRecurse = function* (node, input, env, tracker, evaluate$1) {
1828
2881
  yield emit(input, node.span, tracker);
1829
- tracker.step(node.span);
1830
- if (isValueArray(input)) for (const item of input) yield* evaluate(node, item, env, tracker);
2882
+ const children = [];
2883
+ if (isValueArray(input)) children.push(...input);
1831
2884
  else if (isPlainObject(input)) {
1832
2885
  const keys = Object.keys(input).sort();
1833
- for (const key of keys) yield* evaluate(node, input[key], env, tracker);
2886
+ for (const key of keys) children.push(input[key]);
1834
2887
  }
2888
+ for (const child of children) yield* evalRecurse(node, child, env, tracker, evaluate$1);
1835
2889
  };
1836
- const buildArray = (node, input, env, tracker) => {
1837
- const result = [];
1838
- node.items.forEach((item) => {
1839
- for (const value of evaluate(item, input, env, tracker)) result.push(value);
1840
- });
1841
- return result;
1842
- };
1843
- const buildObjects = function* (node, input, env, tracker) {
1844
- if (node.entries.length === 0) {
1845
- yield emit({}, node.span, tracker);
1846
- return;
1847
- }
1848
- let partials = [{}];
1849
- for (const entry of node.entries) {
1850
- const keys = resolveObjectKeys(entry.key, input, env, tracker);
1851
- const values = Array.from(evaluate(entry.value, input, env, tracker));
1852
- const next = [];
1853
- partials.forEach((partial) => {
1854
- keys.forEach((key) => {
1855
- values.forEach((value) => {
1856
- next.push(extendRecord(partial, key, value));
1857
- });
1858
- });
1859
- });
1860
- partials = next;
1861
- if (partials.length === 0) return;
2890
+
2891
+ //#endregion
2892
+ //#region src/eval/access.ts
2893
+ /**
2894
+ * Evaluates field access (`.foo`).
2895
+ *
2896
+ * @param node - The field access AST node.
2897
+ * @param input - The current input value.
2898
+ * @param env - The environment.
2899
+ * @param tracker - Limits tracker.
2900
+ * @param evaluate - Recursive evaluator.
2901
+ */
2902
+ const evalField = function* (node, input, env, tracker, evaluate$1) {
2903
+ for (const container of evaluate$1(node.target, input, env, tracker)) {
2904
+ if (container === null) {
2905
+ yield emit(null, node.span, tracker);
2906
+ continue;
2907
+ }
2908
+ if (isPlainObject(container)) {
2909
+ yield emit(Object.prototype.hasOwnProperty.call(container, node.field) ? container[node.field] : null, node.span, tracker);
2910
+ continue;
2911
+ }
2912
+ throw new RuntimeError(`Cannot index ${describeType(container)} with string`, node.span);
1862
2913
  }
1863
- for (const obj of partials) yield emit(obj, node.span, tracker);
1864
- };
1865
- const resolveObjectKeys = (key, input, env, tracker) => {
1866
- if (key.kind === "KeyIdentifier") return [key.name];
1867
- if (key.kind === "KeyString") return [key.value];
1868
- return Array.from(evaluate(key.expr, input, env, tracker)).map((value) => {
1869
- if (typeof value !== "string") throw new RuntimeError("Object key expression must produce strings", key.span);
1870
- return value;
1871
- });
1872
2914
  };
1873
- const evalBinary = function* (node, input, env, tracker) {
1874
- const leftValues = Array.from(evaluate(node.left, input, env, tracker));
1875
- const rightValues = Array.from(evaluate(node.right, input, env, tracker));
1876
- for (const left of leftValues) for (const right of rightValues) yield emit(applyBinaryOp(node.op, left, right, node.span), node.span, tracker);
1877
- };
1878
- const applyBinaryOp = (op, left, right, span) => {
1879
- switch (op) {
1880
- case "+": return applyPlus(left, right, span);
1881
- case "-": return applyMinus(left, right, span);
1882
- case "*": return applyMultiply(left, right, span);
1883
- case "/": return applyDivide(left, right, span);
1884
- case "%": return applyModulo(left, right, span);
1885
- case "Eq": return valueEquals(left, right);
1886
- case "Neq": return !valueEquals(left, right);
1887
- case "Lt": return compareValues(left, right) < 0;
1888
- case "Lte": return compareValues(left, right) <= 0;
1889
- case "Gt": return compareValues(left, right) > 0;
1890
- case "Gte": return compareValues(left, right) >= 0;
1891
- default: throw new RuntimeError(`Unsupported operator ${op}`, span);
2915
+ /**
2916
+ * Evaluates index access (`.[0]`, `.["foo"]`).
2917
+ * Supports negative indices for arrays.
2918
+ *
2919
+ * @param node - The index access AST node.
2920
+ * @param input - The current input value.
2921
+ * @param env - The environment.
2922
+ * @param tracker - Limits tracker.
2923
+ * @param evaluate - Recursive evaluator.
2924
+ */
2925
+ const evalIndex = function* (node, input, env, tracker, evaluate$1) {
2926
+ const indexValues = Array.from(evaluate$1(node.index, input, env, tracker));
2927
+ for (const container of evaluate$1(node.target, input, env, tracker)) {
2928
+ if (container === null) {
2929
+ yield emit(null, node.span, tracker);
2930
+ continue;
2931
+ }
2932
+ if (isValueArray(container)) {
2933
+ for (const idxValue of indexValues) {
2934
+ const index = ensureInteger(idxValue, node.span);
2935
+ const resolved = index < 0 ? container.length + index : index;
2936
+ if (resolved < 0 || resolved >= container.length) yield emit(null, node.span, tracker);
2937
+ else yield emit(container[resolved], node.span, tracker);
2938
+ }
2939
+ continue;
2940
+ }
2941
+ if (isPlainObject(container)) {
2942
+ for (const keyValue of indexValues) {
2943
+ if (typeof keyValue !== "string") throw new RuntimeError(`Cannot index object with ${describeType(keyValue)}`, node.span);
2944
+ yield emit(Object.prototype.hasOwnProperty.call(container, keyValue) ? container[keyValue] : null, node.span, tracker);
2945
+ }
2946
+ continue;
2947
+ }
2948
+ throw new RuntimeError(`Cannot index ${describeType(container)}`, node.span);
1892
2949
  }
1893
2950
  };
1894
- const applyUnaryNeg = (value, span) => {
1895
- if (typeof value === "number") return -value;
1896
- throw new RuntimeError("Unary \"-\" expects a number", span);
1897
- };
1898
- const applyPlus = (left, right, span) => {
1899
- if (left === null) return cloneValue(right);
1900
- if (right === null) return cloneValue(left);
1901
- if (typeof left === "number" && typeof right === "number") return left + right;
1902
- if (typeof left === "string" && typeof right === "string") return left + right;
1903
- if (isValueArray(left) && isValueArray(right)) return [...left, ...right];
1904
- if (isPlainObject(left) && isPlainObject(right)) return mergeShallowObjects(left, right);
1905
- throw new RuntimeError(`Cannot add ${describeType(left)} and ${describeType(right)}`, span);
1906
- };
1907
- const applyMinus = (left, right, span) => {
1908
- if (typeof left === "number" && typeof right === "number") return left - right;
1909
- if (isValueArray(left) && isValueArray(right)) return left.filter((item) => !right.some((candidate) => valueEquals(item, candidate)));
1910
- throw new RuntimeError(`Cannot subtract ${describeType(right)} from ${describeType(left)}`, span);
1911
- };
1912
- const applyMultiply = (left, right, span) => {
1913
- if (typeof left === "number" && typeof right === "number") return left * right;
1914
- if (typeof left === "string" && typeof right === "number") return repeatString(left, right, span);
1915
- if (typeof right === "string" && typeof left === "number") return repeatString(right, left, span);
1916
- if (isPlainObject(left) && isPlainObject(right)) return deepMergeObjects(left, right);
1917
- throw new RuntimeError(`Cannot multiply ${describeType(left)} and ${describeType(right)}`, span);
1918
- };
1919
- const applyDivide = (left, right, span) => {
1920
- if (typeof left !== "number" || typeof right !== "number") throw new RuntimeError("Division expects two numbers", span);
1921
- if (right === 0) throw new RuntimeError("Division by zero", span);
1922
- return left / right;
1923
- };
1924
- const applyModulo = (left, right, span) => {
1925
- if (typeof left !== "number" || typeof right !== "number") throw new RuntimeError("Modulo expects two numbers", span);
1926
- if (right === 0) throw new RuntimeError("Modulo by zero", span);
1927
- return left % right;
2951
+ /**
2952
+ * Evaluates array slicing (`.[start:end]`).
2953
+ * Supports optional start/end (defaults to 0/length) and negative indices (via JS slice).
2954
+ *
2955
+ * @param node - The slice AST node.
2956
+ * @param input - The current input value.
2957
+ * @param env - The environment.
2958
+ * @param tracker - Limits tracker.
2959
+ * @param evaluate - Recursive evaluator.
2960
+ */
2961
+ const evalSlice = function* (node, input, env, tracker, evaluate$1) {
2962
+ for (const target of evaluate$1(node.target, input, env, tracker)) {
2963
+ if (typeof target !== "string" && !Array.isArray(target)) throw new RuntimeError("Slice expected string or array", node.span);
2964
+ const starts = [];
2965
+ if (node.start) for (const s of evaluate$1(node.start, input, env, tracker)) {
2966
+ if (typeof s !== "number") throw new RuntimeError("Slice start must be number", node.span);
2967
+ starts.push(s);
2968
+ }
2969
+ else starts.push(0);
2970
+ const ends = [];
2971
+ if (node.end) for (const e of evaluate$1(node.end, input, env, tracker)) {
2972
+ if (typeof e !== "number") throw new RuntimeError("Slice end must be number", node.span);
2973
+ ends.push(e);
2974
+ }
2975
+ else ends.push(target.length);
2976
+ for (const s of starts) for (const e of ends) yield emit(target.slice(s, e), node.span, tracker);
2977
+ }
1928
2978
  };
1929
- const repeatString = (text, countValue, span) => {
1930
- const count = ensureInteger(countValue, span);
1931
- if (count < 0) throw new RuntimeError("String repeat expects non-negative count", span);
1932
- return text.repeat(count);
2979
+
2980
+ //#endregion
2981
+ //#region src/eval/constructors.ts
2982
+ /**
2983
+ * constructs arrays from the input.
2984
+ *
2985
+ * `[.foo, .bar]` -> `[foo_val, bar_val]`
2986
+ * It computes the Cartesian product of all items in the array definition.
2987
+ *
2988
+ * @param node - The array definition AST node.
2989
+ * @param input - The current input value.
2990
+ * @param env - The environment.
2991
+ * @param tracker - Limits tracker.
2992
+ * @param evaluate - Recursive evaluator.
2993
+ */
2994
+ const buildArray = function* (node, input, env, tracker, evaluate$1) {
2995
+ const result = [];
2996
+ for (const itemNode of node.items) for (const itemVal of evaluate$1(itemNode, input, env, tracker)) result.push(itemVal);
2997
+ yield result;
1933
2998
  };
1934
- const deepMergeObjects = (left, right) => {
1935
- const result = {};
1936
- Object.keys(left).forEach((key) => {
1937
- const leftValue = left[key];
1938
- if (leftValue !== void 0) result[key] = cloneValue(leftValue);
1939
- });
1940
- Object.keys(right).forEach((key) => {
1941
- const existing = result[key];
1942
- const rightValue = right[key];
1943
- if (rightValue === void 0) return;
1944
- if (existing !== void 0 && isPlainObject(existing) && isPlainObject(rightValue)) result[key] = deepMergeObjects(existing, rightValue);
1945
- else result[key] = cloneValue(rightValue);
1946
- });
1947
- return result;
2999
+ /**
3000
+ * constructs objects from the input.
3001
+ *
3002
+ * `{a: .foo, b: .bar}`
3003
+ * Computes the Cartesian product of all key-value pairs.
3004
+ *
3005
+ * @param node - The object definition AST node.
3006
+ * @param input - The current input value.
3007
+ * @param env - The environment.
3008
+ * @param tracker - Limits tracker.
3009
+ * @param evaluate - Recursive evaluator.
3010
+ */
3011
+ const buildObjects = function* (node, input, env, tracker, evaluate$1) {
3012
+ yield* fillObject(node.entries, 0, {}, input, env, tracker, evaluate$1);
1948
3013
  };
1949
- const cloneValue = (value) => {
1950
- if (isValueArray(value)) return value.map((item) => cloneValue(item));
1951
- if (isPlainObject(value)) {
1952
- const result = {};
1953
- Object.keys(value).forEach((key) => {
1954
- const child = value[key];
1955
- if (child !== void 0) result[key] = cloneValue(child);
1956
- });
1957
- return result;
3014
+ function* fillObject(entries, index, current, input, env, tracker, evaluate$1) {
3015
+ if (index >= entries.length) {
3016
+ yield { ...current };
3017
+ return;
1958
3018
  }
1959
- return value;
1960
- };
1961
- const getVar = (env, name, span) => {
1962
- for (let i = env.length - 1; i >= 0; i -= 1) {
1963
- const frame = env[i];
1964
- if (frame && frame.has(name)) return frame.get(name);
3019
+ const entry = entries[index];
3020
+ let keys = [];
3021
+ if (entry.key.kind === "KeyIdentifier") keys = [entry.key.name];
3022
+ else if (entry.key.kind === "KeyString") keys = [entry.key.value];
3023
+ else for (const k of evaluate$1(entry.key.expr, input, env, tracker)) {
3024
+ if (typeof k !== "string") throw new RuntimeError("Object key must be a string", entry.key.span);
3025
+ keys.push(k);
1965
3026
  }
1966
- throw new RuntimeError(`Unbound variable: $${name}`, span);
1967
- };
1968
- const pushBinding = (env, name, value) => {
1969
- env.push(new Map([[name, value]]));
3027
+ for (const key of keys) for (const val of evaluate$1(entry.value, input, env, tracker)) {
3028
+ current[key] = val;
3029
+ yield* fillObject(entries, index + 1, current, input, env, tracker, evaluate$1);
3030
+ delete current[key];
3031
+ }
3032
+ }
3033
+
3034
+ //#endregion
3035
+ //#region src/eval/functions.ts
3036
+ /**
3037
+ * Evaluates a function call.
3038
+ *
3039
+ * Checks for:
3040
+ * 1. User-defined functions in the current environment stack (local scope).
3041
+ * 2. Standard library built-ins.
3042
+ *
3043
+ * Handles closure creation for arguments and recursion.
3044
+ *
3045
+ * @param node - The call AST node.
3046
+ * @param input - The current input value.
3047
+ * @param env - The environment.
3048
+ * @param tracker - Limits tracker.
3049
+ * @param evaluate - Recursive evaluator.
3050
+ */
3051
+ const evalCall = function* (node, input, env, tracker, evaluate$1) {
3052
+ for (let i = env.length - 1; i >= 0; i--) {
3053
+ const funcs = env[i].funcs.get(node.name);
3054
+ if (funcs) {
3055
+ const def = funcs.find((f) => f.args.length === node.args.length);
3056
+ if (def) {
3057
+ const newFrame = {
3058
+ vars: /* @__PURE__ */ new Map(),
3059
+ funcs: /* @__PURE__ */ new Map()
3060
+ };
3061
+ for (let j = 0; j < node.args.length; j++) {
3062
+ const argName = def.args[j];
3063
+ const argBody = node.args[j];
3064
+ const argDefs = newFrame.funcs.get(argName) || [];
3065
+ argDefs.push({
3066
+ args: [],
3067
+ body: argBody,
3068
+ closure: env
3069
+ });
3070
+ newFrame.funcs.set(argName, argDefs);
3071
+ }
3072
+ const newStack = [...def.closure, newFrame];
3073
+ yield* evaluate$1(def.body, input, newStack, tracker);
3074
+ return;
3075
+ }
3076
+ }
3077
+ }
3078
+ const specs = builtins[node.name];
3079
+ if (!specs) throw new RuntimeError(`Unknown function: ${node.name}`, node.span);
3080
+ const builtin = specs.find((s) => s.arity === node.args.length);
3081
+ if (!builtin) throw new RuntimeError(`Function ${node.name} does not accept ${node.args.length} arguments`, node.span);
3082
+ yield* builtin.apply(input, node.args, env, tracker, evaluate$1, node.span);
1970
3083
  };
1971
- const popBinding = (env) => {
1972
- env.pop();
3084
+ /**
3085
+ * Defines a new function in the environment.
3086
+ *
3087
+ * Adds the function definition to the current scope and executes the `next` expression
3088
+ * with the updated environment.
3089
+ *
3090
+ * @param node - The function definition AST node.
3091
+ * @param input - The current input value.
3092
+ * @param env - The environment.
3093
+ * @param tracker - Limits tracker.
3094
+ * @param evaluate - Recursive evaluator.
3095
+ */
3096
+ const evalDef = function* (node, input, env, tracker, evaluate$1) {
3097
+ const newFrame = {
3098
+ vars: /* @__PURE__ */ new Map(),
3099
+ funcs: /* @__PURE__ */ new Map()
3100
+ };
3101
+ const currentDefs = newFrame.funcs.get(node.name) || [];
3102
+ const funDef = {
3103
+ args: node.args,
3104
+ body: node.body,
3105
+ closure: []
3106
+ };
3107
+ currentDefs.push(funDef);
3108
+ newFrame.funcs.set(node.name, currentDefs);
3109
+ const newStack = [...env, newFrame];
3110
+ funDef.closure = newStack;
3111
+ yield* evaluate$1(node.next, input, newStack, tracker);
1973
3112
  };
1974
- const ensureInteger = (value, span) => {
1975
- if (typeof value !== "number" || !Number.isInteger(value)) throw new RuntimeError("Index must be an integer number", span);
1976
- return value;
3113
+
3114
+ //#endregion
3115
+ //#region src/eval/control_flow.ts
3116
+ /**
3117
+ * Evaluates an `if-then-else` expression.
3118
+ *
3119
+ * @param node - The If AST node.
3120
+ * @param input - The current input value.
3121
+ * @param env - The environment.
3122
+ * @param tracker - Limits tracker.
3123
+ * @param evaluate - Recursive evaluator.
3124
+ */
3125
+ const evalIf = function* (node, input, env, tracker, evaluate$1) {
3126
+ yield* evalIfBranch(node, 0, input, env, tracker, evaluate$1);
1977
3127
  };
1978
- const extendRecord = (base, key, value) => {
1979
- const next = {};
1980
- Object.keys(base).forEach((existingKey) => {
1981
- const existingValue = base[existingKey];
1982
- if (existingValue !== void 0) next[existingKey] = existingValue;
1983
- });
1984
- next[key] = value;
1985
- return next;
3128
+ function* evalIfBranch(node, branchIndex, input, env, tracker, evaluate$1) {
3129
+ if (branchIndex >= node.branches.length) {
3130
+ yield* evaluate$1(node.else, input, env, tracker);
3131
+ return;
3132
+ }
3133
+ const branch = node.branches[branchIndex];
3134
+ for (const cond of evaluate$1(branch.cond, input, env, tracker)) if (isTruthy(cond)) yield* evaluate$1(branch.then, input, env, tracker);
3135
+ else yield* evalIfBranch(node, branchIndex + 1, input, env, tracker, evaluate$1);
3136
+ }
3137
+ /**
3138
+ * Evaluates a `try-catch` expression.
3139
+ *
3140
+ * @param node - The Try AST node.
3141
+ * @param input - The current input value.
3142
+ * @param env - The environment.
3143
+ * @param tracker - Limits tracker.
3144
+ * @param evaluate - Recursive evaluator.
3145
+ */
3146
+ const evalTry = function* (node, input, env, tracker, evaluate$1) {
3147
+ try {
3148
+ yield* evaluate$1(node.body, input, env, tracker);
3149
+ } catch (err) {
3150
+ if (err instanceof RuntimeError) {
3151
+ if (node.handler) yield* evaluate$1(node.handler, err.message, env, tracker);
3152
+ } else throw err;
3153
+ }
1986
3154
  };
1987
- const mergeShallowObjects = (left, right) => {
1988
- const result = {};
1989
- Object.keys(left).forEach((key) => {
1990
- const leftValue = left[key];
1991
- if (leftValue !== void 0) result[key] = leftValue;
1992
- });
1993
- Object.keys(right).forEach((key) => {
1994
- const rightValue = right[key];
1995
- if (rightValue !== void 0) result[key] = rightValue;
1996
- });
1997
- return result;
3155
+ /**
3156
+ * Evaluates a `label` expression, establishing a target for `break`.
3157
+ *
3158
+ * @param node - The Label AST node.
3159
+ * @param input - The current input value.
3160
+ * @param env - The environment.
3161
+ * @param tracker - Limits tracker.
3162
+ * @param evaluate - Recursive evaluator.
3163
+ */
3164
+ const evalLabel = function* (node, input, env, tracker, evaluate$1) {
3165
+ try {
3166
+ yield* evaluate$1(node.body, input, env, tracker);
3167
+ } catch (e) {
3168
+ if (e instanceof BreakSignal) {
3169
+ if (e.label === node.label) return;
3170
+ }
3171
+ throw e;
3172
+ }
1998
3173
  };
1999
- const isValueArray = (value) => Array.isArray(value);
2000
- const isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2001
- const describeType = (value) => {
2002
- if (value === null) return "null";
2003
- if (typeof value === "boolean") return "boolean";
2004
- if (typeof value === "number") return "number";
2005
- if (typeof value === "string") return "string";
2006
- if (Array.isArray(value)) return "array";
2007
- return "object";
3174
+
3175
+ //#endregion
3176
+ //#region src/eval/dispatch.ts
3177
+ /**
3178
+ * Runs a jq AST against an input value.
3179
+ *
3180
+ * @param ast - The parsed AST.
3181
+ * @param input - The initial input value (JSON).
3182
+ * @param options - Execution options.
3183
+ * @returns An array of all values yielded by the filter.
3184
+ */
3185
+ const runAst = (ast, input, options = {}) => {
3186
+ const tracker = new LimitTracker(resolveLimits(options.limits));
3187
+ const env = [{
3188
+ vars: /* @__PURE__ */ new Map(),
3189
+ funcs: /* @__PURE__ */ new Map()
3190
+ }];
3191
+ return Array.from(evaluate(ast, input, env, tracker));
2008
3192
  };
3193
+ /**
3194
+ * The core evaluation generator.
3195
+ *
3196
+ * This function dispatches execution to specific handlers based on the AST node kind.
3197
+ * It uses a generator to support jq's streaming nature (backtracking and multiple outputs).
3198
+ *
3199
+ * @param node - The current AST node to evaluate.
3200
+ * @param input - The current input value (context).
3201
+ * @param env - The current environment (variables and functions).
3202
+ * @param tracker - The limit tracker for safety.
3203
+ * @yields The output values produced by the filter.
3204
+ */
3205
+ function* evaluate(node, input, env, tracker) {
3206
+ if (!node) throw new Error("evaluate called with undefined node");
3207
+ tracker.step(node.span);
3208
+ tracker.enter(node.span);
3209
+ try {
3210
+ switch (node.kind) {
3211
+ case "Identity":
3212
+ yield emit(input, node.span, tracker);
3213
+ return;
3214
+ case "Literal":
3215
+ yield emit(node.value, node.span, tracker);
3216
+ return;
3217
+ case "Var": {
3218
+ const val = getVar(env, node.name);
3219
+ if (val === void 0) throw new RuntimeError(`Undefined variable: ${node.name}`, node.span);
3220
+ yield emit(val, node.span, tracker);
3221
+ return;
3222
+ }
3223
+ case "FieldAccess":
3224
+ yield* evalField(node, input, env, tracker, evaluate);
3225
+ return;
3226
+ case "IndexAccess":
3227
+ yield* evalIndex(node, input, env, tracker, evaluate);
3228
+ return;
3229
+ case "Slice":
3230
+ yield* evalSlice(node, input, env, tracker, evaluate);
3231
+ return;
3232
+ case "Array":
3233
+ yield* buildArray(node, input, env, tracker, evaluate);
3234
+ return;
3235
+ case "Object":
3236
+ yield* buildObjects(node, input, env, tracker, evaluate);
3237
+ return;
3238
+ case "Label":
3239
+ yield* evalLabel(node, input, env, tracker, evaluate);
3240
+ return;
3241
+ case "Break": throw new BreakSignal(node.label);
3242
+ case "Pipe":
3243
+ for (const leftVal of evaluate(node.left, input, env, tracker)) yield* evaluate(node.right, leftVal, env, tracker);
3244
+ return;
3245
+ case "Comma":
3246
+ yield* evaluate(node.left, input, env, tracker);
3247
+ yield* evaluate(node.right, input, env, tracker);
3248
+ return;
3249
+ case "Alt": {
3250
+ const valid = Array.from(evaluate(node.left, input, env, tracker)).filter((v) => v !== null && v !== false);
3251
+ if (valid.length > 0) for (const v of valid) yield v;
3252
+ else yield* evaluate(node.right, input, env, tracker);
3253
+ return;
3254
+ }
3255
+ case "Unary":
3256
+ if (node.op === "Not") for (const value of evaluate(node.expr, input, env, tracker)) yield emit(!isTruthy(value), node.span, tracker);
3257
+ else for (const value of evaluate(node.expr, input, env, tracker)) yield emit(applyUnaryNeg(value, node.span), node.span, tracker);
3258
+ return;
3259
+ case "Bool":
3260
+ for (const l of evaluate(node.left, input, env, tracker)) if (node.op === "And") if (!isTruthy(l)) yield emit(false, node.span, tracker);
3261
+ else for (const r of evaluate(node.right, input, env, tracker)) yield emit(isTruthy(l) && isTruthy(r), node.span, tracker);
3262
+ else if (isTruthy(l)) yield emit(true, node.span, tracker);
3263
+ else for (const r of evaluate(node.right, input, env, tracker)) yield emit(isTruthy(l) || isTruthy(r), node.span, tracker);
3264
+ return;
3265
+ case "Binary": {
3266
+ const leftRes = Array.from(evaluate(node.left, input, env, tracker));
3267
+ const rightRes = Array.from(evaluate(node.right, input, env, tracker));
3268
+ for (const l of leftRes) for (const r of rightRes) yield emit(applyBinaryOp(node.op, l, r, node.span), node.span, tracker);
3269
+ return;
3270
+ }
3271
+ case "If":
3272
+ yield* evalIf(node, input, env, tracker, evaluate);
3273
+ return;
3274
+ case "Try":
3275
+ yield* evalTry(node, input, env, tracker, evaluate);
3276
+ return;
3277
+ case "Recurse":
3278
+ yield* evalRecurse(node, input, env, tracker, evaluate);
3279
+ return;
3280
+ case "Iterate":
3281
+ yield* evalIterate(node, input, env, tracker, evaluate);
3282
+ return;
3283
+ case "Assignment":
3284
+ yield* evalAssignment(node, input, env, tracker, evaluate);
3285
+ return;
3286
+ case "Reduce":
3287
+ yield* evalReduce(node, input, env, tracker, evaluate);
3288
+ return;
3289
+ case "Foreach":
3290
+ yield* evalForeach(node, input, env, tracker, evaluate);
3291
+ return;
3292
+ case "As": {
3293
+ const values = Array.from(evaluate(node.bind, input, env, tracker));
3294
+ for (const val of values) {
3295
+ const newFrame = {
3296
+ vars: new Map([[node.name, val]]),
3297
+ funcs: /* @__PURE__ */ new Map()
3298
+ };
3299
+ const newEnv = [...env, newFrame];
3300
+ yield* evaluate(node.body, input, newEnv, tracker);
3301
+ }
3302
+ return;
3303
+ }
3304
+ case "Call":
3305
+ yield* evalCall(node, input, env, tracker, evaluate);
3306
+ return;
3307
+ case "Def":
3308
+ yield* evalDef(node, input, env, tracker, evaluate);
3309
+ return;
3310
+ }
3311
+ } finally {
3312
+ tracker.exit();
3313
+ }
3314
+ }
2009
3315
 
2010
3316
  //#endregion
2011
3317
  //#region src/index.ts