json-p3 1.2.0 → 1.3.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.
@@ -1,5 +1,5 @@
1
1
  /*
2
- * json-p3 version 1.2.0
2
+ * json-p3 version 1.3.0
3
3
  * https://github.com/jg-rp/json-p3
4
4
  *
5
5
  * MIT License
@@ -639,6 +639,24 @@ var index$3 = /*#__PURE__*/Object.freeze({
639
639
  resolve: resolve
640
640
  });
641
641
 
642
+ const Nothing = Symbol.for("jsonpath.nothing");
643
+
644
+ /**
645
+ * ValueType for JSONPath function expression tye system.
646
+ */
647
+
648
+ /**
649
+ * Object passed to FilterExpression.evaluate().
650
+ */
651
+
652
+ /**
653
+ * A type predicate for an object with a string property.
654
+ */
655
+ function hasStringKey(value, key) {
656
+ return isObject(value) && Object.hasOwn(value, key);
657
+ }
658
+ const KEY_MARK = "\x02";
659
+
642
660
  /**
643
661
  * The pair of a JSON value and its location found in the target JSON value.
644
662
  */
@@ -656,7 +674,7 @@ class JSONPathNode {
656
674
  get path() {
657
675
  return (
658
676
  // eslint-disable-next-line prefer-template
659
- "$" + this.location.map(s => isString(s) ? `['${s}']` : `[${s}]`).join("")
677
+ "$" + this.location.map(s => isString(s) ? this.decode_name_location(s) : `[${s}]`).join("")
660
678
  );
661
679
  }
662
680
 
@@ -669,6 +687,9 @@ class JSONPathNode {
669
687
  }
670
688
  return new JSONPointer(JSONPointer.encode(this.location.map(String)));
671
689
  }
690
+ decode_name_location(name) {
691
+ return name.startsWith(KEY_MARK) ? `[~'${name.slice(1).replaceAll("'", "\\'")}']` : `['${name.replaceAll("'", "\\'")}']`;
692
+ }
672
693
  }
673
694
 
674
695
  /**
@@ -748,23 +769,6 @@ class JSONPathNodeList {
748
769
  }
749
770
  }
750
771
 
751
- const Nothing = Symbol.for("jsonpath.nothing");
752
-
753
- /**
754
- * ValueType for JSONPath function expression tye system.
755
- */
756
-
757
- /**
758
- * Object passed to FilterExpression.evaluate().
759
- */
760
-
761
- /**
762
- * A type predicate for an object with a string property.
763
- */
764
- function hasStringKey(value, key) {
765
- return isObject(value) && Object.hasOwn(value, key);
766
- }
767
-
768
772
  /**
769
773
  * Base class for all filter expressions.
770
774
  */
@@ -1167,7 +1171,8 @@ let TokenKind = /*#__PURE__*/function (TokenKind) {
1167
1171
  TokenKind["AND"] = "TOKEN_AND";
1168
1172
  TokenKind["COLON"] = "TOKEN_COLON";
1169
1173
  TokenKind["COMMA"] = "TOKEN_COMMA";
1170
- TokenKind["CURRENT"] = "TOKEN_CURRENT_NODE";
1174
+ TokenKind["CURRENT"] = "TOKEN_CURRENT_VALUE";
1175
+ TokenKind["CURRENT_KEY"] = "TOKEN_CURRENT_KEY";
1171
1176
  TokenKind["DDOT"] = "TOKEN_DDOT";
1172
1177
  TokenKind["DOT"] = "TOKEN_DOT";
1173
1178
  TokenKind["DOUBLE_QUOTE_STRING"] = "TOKEN_DOUBLE_QUOTE_STRING";
@@ -1181,7 +1186,10 @@ let TokenKind = /*#__PURE__*/function (TokenKind) {
1181
1186
  TokenKind["GT"] = "TOKEN_GT";
1182
1187
  TokenKind["INDEX"] = "TOKEN_INDEX";
1183
1188
  TokenKind["KEY"] = "TOKEN_KEY";
1189
+ TokenKind["KEY_DOUBLE_QUOTE_STRING"] = "TOKEN_KEY_DOUBLE_QUOTE_STRING";
1190
+ TokenKind["KEY_SINGLE_QUOTE_STRING"] = "TOKEN_KEY_SINGLE_QUOTE_STRING";
1184
1191
  TokenKind["KEYS"] = "TOKEN_KEYS";
1192
+ TokenKind["KEYS_FILTER"] = "TOKEN_KEYS_FILTER";
1185
1193
  TokenKind["LBRACKET"] = "TOKEN_LBRACKET";
1186
1194
  TokenKind["LE"] = "TOKEN_LE";
1187
1195
  TokenKind["LG"] = "TOKEN_LG";
@@ -1262,6 +1270,7 @@ const indexPattern = /-?\d+/y;
1262
1270
  const intPattern = /-?[0-9]+/y;
1263
1271
  const namePattern = /[\u0080-\uFFFFa-zA-Z_][\u0080-\uFFFFa-zA-Z0-9_-]*/y;
1264
1272
  const whitespace = new Set([" ", "\n", "\t", "\r"]);
1273
+ const nameFirstPattern = /[\u0080-\uFFFFa-zA-Z_]/; // don't set sticky bit
1265
1274
 
1266
1275
  /**
1267
1276
  * JSONPath lexical scanner.
@@ -1334,6 +1343,11 @@ class Lexer {
1334
1343
  if (ch) this.backup();
1335
1344
  return ch;
1336
1345
  }
1346
+ peekMatch(pattern) {
1347
+ const ch = this.next();
1348
+ if (ch) this.backup();
1349
+ return pattern.test(ch);
1350
+ }
1337
1351
  accept(valid) {
1338
1352
  const ch = this.next();
1339
1353
  if (valid.has(ch)) return true;
@@ -1463,10 +1477,28 @@ function lexDescendantSelection(l) {
1463
1477
  l.emit(TokenKind.NAME);
1464
1478
  return lexSegment;
1465
1479
  }
1466
- if (!l.environment.strict && l.acceptMatchRun(l.environment.keysPattern)) {
1467
- // Non-standard keys selector
1468
- l.emit(TokenKind.KEYS);
1469
- return lexSegment;
1480
+ if (!l.environment.strict) {
1481
+ // We're effectively disabling the _key selector_ and _keys filter selector_ if a
1482
+ // custom _keys selector_ is set.
1483
+ if (l.environment.keysPattern.source === "~" && l.peek() === "~") {
1484
+ l.next();
1485
+ if (l.peekMatch(nameFirstPattern)) {
1486
+ // Non-standard key selector
1487
+ l.ignore(); // ignore ~
1488
+ l.acceptMatchRun(namePattern);
1489
+ l.emit(TokenKind.KEY);
1490
+ return lexSegment;
1491
+ } else {
1492
+ // Non-standard keys selector
1493
+ l.emit(TokenKind.KEYS);
1494
+ return lexSegment;
1495
+ }
1496
+ } else if (l.acceptMatchRun(l.environment.keysPattern)) {
1497
+ // NOTE: A custom keys pattern does not play well with other non-standard key selectors.
1498
+ // We leave this here for backwards compatibility.
1499
+ l.emit(TokenKind.KEYS);
1500
+ return lexSegment;
1501
+ }
1470
1502
  }
1471
1503
  const ch = l.next();
1472
1504
  switch (ch) {
@@ -1491,9 +1523,28 @@ function lexDotSelector(l) {
1491
1523
  l.error("unexpected whitespace after dot");
1492
1524
  return null;
1493
1525
  }
1494
- if (!l.environment.strict && l.acceptMatchRun(l.environment.keysPattern)) {
1495
- l.emit(TokenKind.KEYS);
1496
- return lexSegment;
1526
+ if (!l.environment.strict) {
1527
+ // We're effectively disabling the _key selector_ and _keys filter selector_ if a
1528
+ // custom _keys selector_ is set.
1529
+ if (l.environment.keysPattern.source === "~" && l.peek() === "~") {
1530
+ l.next();
1531
+ if (l.peekMatch(nameFirstPattern)) {
1532
+ // Non-standard key selector
1533
+ l.ignore(); // ignore ~
1534
+ l.acceptMatchRun(namePattern);
1535
+ l.emit(TokenKind.KEY);
1536
+ return lexSegment;
1537
+ } else {
1538
+ // Non-standard keys selector
1539
+ l.emit(TokenKind.KEYS);
1540
+ return lexSegment;
1541
+ }
1542
+ } else if (l.acceptMatchRun(l.environment.keysPattern)) {
1543
+ // NOTE: A custom keys pattern does not play well with other non-standard key selectors.
1544
+ // We leave this here for backwards compatibility.
1545
+ l.emit(TokenKind.KEYS);
1546
+ return lexSegment;
1547
+ }
1497
1548
  }
1498
1549
  if (l.acceptMatchRun(namePattern)) {
1499
1550
  l.emit(TokenKind.NAME);
@@ -1516,8 +1567,25 @@ function lexInsideBracketedSelection(l) {
1516
1567
  continue;
1517
1568
  }
1518
1569
  if (!l.environment.strict && l.acceptMatchRun(l.environment.keysPattern)) {
1519
- l.emit(TokenKind.KEYS);
1520
- continue;
1570
+ switch (l.peek()) {
1571
+ case "'":
1572
+ l.ignore(); // ~
1573
+ l.next();
1574
+ return lexSingleQuoteKeyString(l);
1575
+ case '"':
1576
+ l.ignore(); // ~
1577
+ l.next();
1578
+ return lexDoubleQuoteKeyString(l);
1579
+ case "?":
1580
+ l.ignore(); // ~
1581
+ l.next();
1582
+ l.emit(TokenKind.KEYS_FILTER);
1583
+ l.filterLevel += 1;
1584
+ return lexInsideFilter;
1585
+ default:
1586
+ l.emit(TokenKind.KEYS);
1587
+ continue;
1588
+ }
1521
1589
  }
1522
1590
  const ch = l.next();
1523
1591
  switch (ch) {
@@ -1604,7 +1672,7 @@ function lexInsideFilter(l) {
1604
1672
  l.emit(TokenKind.CURRENT);
1605
1673
  return lexSegment;
1606
1674
  case "#":
1607
- l.emit(TokenKind.KEY);
1675
+ l.emit(TokenKind.CURRENT_KEY);
1608
1676
  return lexSegment;
1609
1677
  case ".":
1610
1678
  l.backup();
@@ -1704,8 +1772,7 @@ function lexInsideFilter(l) {
1704
1772
  * @param state - The state function to return control to.
1705
1773
  * @returns String tokenizing state function.
1706
1774
  */
1707
- function makeLexString(quote, state) {
1708
- // eslint-disable-next-line sonarjs/cognitive-complexity
1775
+ function makeLexString(quote, state, token_kind) {
1709
1776
  function _lexString(l) {
1710
1777
  l.ignore();
1711
1778
  if (l.peek() === quote) {
@@ -1731,7 +1798,7 @@ function makeLexString(quote, state) {
1731
1798
  }
1732
1799
  if (ch === quote) {
1733
1800
  l.backup();
1734
- l.emit(quote === "'" ? TokenKind.SINGLE_QUOTE_STRING : TokenKind.DOUBLE_QUOTE_STRING);
1801
+ l.emit(token_kind);
1735
1802
  l.next();
1736
1803
  l.ignore();
1737
1804
  return state;
@@ -1740,10 +1807,12 @@ function makeLexString(quote, state) {
1740
1807
  }
1741
1808
  return _lexString;
1742
1809
  }
1743
- const lexSingleQuoteStringInsideBracketSelection = makeLexString("'", lexInsideBracketedSelection);
1744
- const lexDoubleQuoteStringInsideBracketSelection = makeLexString('"', lexInsideBracketedSelection);
1745
- const lexSingleQuoteStringInsideFilterExpression = makeLexString("'", lexInsideFilter);
1746
- const lexDoubleQuoteStringInsideFilterExpression = makeLexString('"', lexInsideFilter);
1810
+ const lexSingleQuoteStringInsideBracketSelection = makeLexString("'", lexInsideBracketedSelection, TokenKind.SINGLE_QUOTE_STRING);
1811
+ const lexDoubleQuoteStringInsideBracketSelection = makeLexString('"', lexInsideBracketedSelection, TokenKind.DOUBLE_QUOTE_STRING);
1812
+ const lexSingleQuoteStringInsideFilterExpression = makeLexString("'", lexInsideFilter, TokenKind.SINGLE_QUOTE_STRING);
1813
+ const lexDoubleQuoteStringInsideFilterExpression = makeLexString('"', lexInsideFilter, TokenKind.DOUBLE_QUOTE_STRING);
1814
+ const lexSingleQuoteKeyString = makeLexString("'", lexInsideBracketedSelection, TokenKind.KEY_SINGLE_QUOTE_STRING);
1815
+ const lexDoubleQuoteKeyString = makeLexString('"', lexInsideBracketedSelection, TokenKind.KEY_DOUBLE_QUOTE_STRING);
1747
1816
 
1748
1817
  /**
1749
1818
  * Base class for all JSONPath segments and selectors.
@@ -1784,7 +1853,7 @@ class NameSelector extends JSONPathSelector {
1784
1853
  resolve(nodes) {
1785
1854
  const rv = [];
1786
1855
  for (const node of nodes) {
1787
- if (hasStringKey(node.value, this.name)) {
1856
+ if (!isArray(node.value) && hasStringKey(node.value, this.name)) {
1788
1857
  rv.push(new JSONPathNode(node.value[this.name], node.location.concat(this.name), node.root));
1789
1858
  }
1790
1859
  }
@@ -1792,7 +1861,7 @@ class NameSelector extends JSONPathSelector {
1792
1861
  }
1793
1862
  *lazyResolve(nodes) {
1794
1863
  for (const node of nodes) {
1795
- if (hasStringKey(node.value, this.name)) {
1864
+ if (!isArray(node.value) && hasStringKey(node.value, this.name)) {
1796
1865
  yield new JSONPathNode(node.value[this.name], node.location.concat(this.name), node.root);
1797
1866
  }
1798
1867
  }
@@ -1868,7 +1937,7 @@ class SliceSelector extends JSONPathSelector {
1868
1937
  *lazyResolve(nodes) {
1869
1938
  for (const node of nodes) {
1870
1939
  if (!isArray(node.value)) continue;
1871
- for (const [i, value] of this.slice(node.value, this.start, this.stop, this.step)) {
1940
+ for (const [i, value] of this.lazySlice(node.value, this.start, this.stop, this.step)) {
1872
1941
  yield new JSONPathNode(value, node.location.concat(i), node.root);
1873
1942
  }
1874
1943
  }
@@ -1931,6 +2000,45 @@ class SliceSelector extends JSONPathSelector {
1931
2000
  }
1932
2001
  return slicedArray;
1933
2002
  }
2003
+
2004
+ // eslint-disable-next-line sonarjs/cognitive-complexity
2005
+ *lazySlice(arr, start, stop, step) {
2006
+ if (!arr.length) return;
2007
+
2008
+ // Handle negative and undefined start values
2009
+ if (start === undefined || start === null) {
2010
+ start = step && step < 0 ? arr.length - 1 : 0;
2011
+ } else if (start < 0) {
2012
+ start = Math.max(arr.length + start, 0);
2013
+ } else {
2014
+ start = Math.min(start, arr.length - 1);
2015
+ }
2016
+
2017
+ // Handle negative and undefined stop values
2018
+ if (stop === undefined || stop === null) {
2019
+ stop = step && step < 0 ? -1 : arr.length;
2020
+ } else if (stop < 0) {
2021
+ stop = Math.max(arr.length + stop, -1);
2022
+ } else {
2023
+ stop = Math.min(stop, arr.length);
2024
+ }
2025
+
2026
+ // Perform the slice
2027
+ if (step === undefined) {
2028
+ // Default to a step of 1
2029
+ for (let i = start; i < stop; i += 1) {
2030
+ yield [i, arr[i]];
2031
+ }
2032
+ } else if (step > 0) {
2033
+ for (let i = start; i < stop; i += step) {
2034
+ yield [i, arr[i]];
2035
+ }
2036
+ } else if (step < 0) {
2037
+ for (let i = start; i > stop; i += step) {
2038
+ yield [i, arr[i]];
2039
+ }
2040
+ }
2041
+ }
1934
2042
  }
1935
2043
  class WildcardSelector extends JSONPathSelector {
1936
2044
  constructor(environment, token) {
@@ -1997,8 +2105,6 @@ class RecursiveDescentSegment extends JSONPathSelector {
1997
2105
  }
1998
2106
  }
1999
2107
  }
2000
-
2001
- // console.log(JSON.stringify(rv.map((n: any) => n.value)));
2002
2108
  return this.selector.resolve(rv);
2003
2109
  }
2004
2110
  *lazyResolve(nodes) {
@@ -2355,6 +2461,38 @@ class CurrentKey extends FilterExpression {
2355
2461
  }
2356
2462
  }
2357
2463
 
2464
+ class KeySelector extends JSONPathSelector {
2465
+ constructor(environment, token, key) {
2466
+ let shorthand = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
2467
+ super(environment, token);
2468
+ this.environment = environment;
2469
+ this.token = token;
2470
+ this.key = key;
2471
+ this.shorthand = shorthand;
2472
+ }
2473
+ resolve(nodes) {
2474
+ const rv = [];
2475
+ for (const node of nodes) {
2476
+ if (node.value instanceof String || isArray(node.value)) continue;
2477
+ if (isObject(node.value) && hasStringKey(node.value, this.key)) {
2478
+ rv.push(new JSONPathNode(this.key, node.location.concat(`${KEY_MARK}${this.key}`), node.root));
2479
+ }
2480
+ }
2481
+ return rv;
2482
+ }
2483
+ *lazyResolve(nodes) {
2484
+ for (const node of nodes) {
2485
+ if (node.value instanceof String || isArray(node.value)) continue;
2486
+ if (isObject(node.value) && hasStringKey(node.value, this.key)) {
2487
+ yield new JSONPathNode(this.key, node.location.concat(`${KEY_MARK}${this.key}`), node.root);
2488
+ }
2489
+ }
2490
+ }
2491
+ toString() {
2492
+ return this.shorthand ? `[~'${this.key.replaceAll("'", "\\'")}']` : `~'${this.key.replaceAll("'", "\\'")}'`;
2493
+ }
2494
+ }
2495
+
2358
2496
  /**
2359
2497
  * Object property name selector or array index selector.
2360
2498
  */
@@ -2371,10 +2509,8 @@ class KeysSelector extends JSONPathSelector {
2371
2509
  for (const node of nodes) {
2372
2510
  if (node.value instanceof String || isArray(node.value)) continue;
2373
2511
  if (isObject(node.value)) {
2374
- let i = 0;
2375
2512
  for (const [key, _] of this.environment.entries(node.value)) {
2376
- rv.push(new JSONPathNode(key, node.location.concat("[~]", `[${i}]`), node.root));
2377
- i++;
2513
+ rv.push(new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root));
2378
2514
  }
2379
2515
  }
2380
2516
  }
@@ -2384,10 +2520,8 @@ class KeysSelector extends JSONPathSelector {
2384
2520
  for (const node of nodes) {
2385
2521
  if (node.value instanceof String || isArray(node.value)) continue;
2386
2522
  if (isObject(node.value)) {
2387
- let i = 0;
2388
2523
  for (const [key, _] of this.environment.entries(node.value)) {
2389
- yield new JSONPathNode(key, node.location.concat("[~]", `[${i}]`), node.root);
2390
- i++;
2524
+ yield new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root);
2391
2525
  }
2392
2526
  }
2393
2527
  }
@@ -2396,6 +2530,56 @@ class KeysSelector extends JSONPathSelector {
2396
2530
  return this.shorthand ? "[~]" : "~";
2397
2531
  }
2398
2532
  }
2533
+ class KeysFilterSelector extends JSONPathSelector {
2534
+ constructor(environment, token, expression) {
2535
+ super(environment, token);
2536
+ this.environment = environment;
2537
+ this.token = token;
2538
+ this.expression = expression;
2539
+ }
2540
+ resolve(nodes) {
2541
+ const rv = [];
2542
+ for (const node of nodes) {
2543
+ if (node.value instanceof String || isArray(node.value)) continue;
2544
+ if (isObject(node.value)) {
2545
+ for (const [key, value] of this.environment.entries(node.value)) {
2546
+ const filterContext = {
2547
+ environment: this.environment,
2548
+ currentValue: value,
2549
+ rootValue: node.root,
2550
+ currentKey: key
2551
+ };
2552
+ if (this.expression.evaluate(filterContext)) {
2553
+ rv.push(new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root));
2554
+ }
2555
+ }
2556
+ }
2557
+ }
2558
+ return rv;
2559
+ }
2560
+ *lazyResolve(nodes) {
2561
+ for (const node of nodes) {
2562
+ if (node.value instanceof String || isArray(node.value)) continue;
2563
+ if (isObject(node.value)) {
2564
+ for (const [key, value] of this.environment.entries(node.value)) {
2565
+ const filterContext = {
2566
+ environment: this.environment,
2567
+ currentValue: value,
2568
+ rootValue: node.root,
2569
+ lazy: true,
2570
+ currentKey: key
2571
+ };
2572
+ if (this.expression.evaluate(filterContext)) {
2573
+ yield new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root);
2574
+ }
2575
+ }
2576
+ }
2577
+ }
2578
+ }
2579
+ toString() {
2580
+ return `~?${this.expression.toString()}`;
2581
+ }
2582
+ }
2399
2583
 
2400
2584
  const PRECEDENCE_LOWEST = 1;
2401
2585
  const PRECEDENCE_LOGICAL_OR = 4;
@@ -2412,7 +2596,7 @@ const COMPARISON_OPERATORS = new Set(["==", ">=", ">", "<=", "<", "!="]);
2412
2596
  class Parser {
2413
2597
  constructor(environment) {
2414
2598
  this.environment = environment;
2415
- this.tokenMap = new Map([[TokenKind.FALSE, this.parseBoolean], [TokenKind.NUMBER, this.parseNumber], [TokenKind.LPAREN, this.parseGroupedExpression], [TokenKind.NOT, this.parsePrefixExpression], [TokenKind.NULL, this.parseNull], [TokenKind.ROOT, this.parseRootQuery], [TokenKind.CURRENT, this.parseRelativeQuery], [TokenKind.SINGLE_QUOTE_STRING, this.parseString], [TokenKind.DOUBLE_QUOTE_STRING, this.parseString], [TokenKind.TRUE, this.parseBoolean], [TokenKind.FUNCTION, this.parseFunction], [TokenKind.KEY, this.parseCurrentKey], [TokenKind.KEY, this.parseCurrentKey]]);
2599
+ this.tokenMap = new Map([[TokenKind.FALSE, this.parseBoolean], [TokenKind.NUMBER, this.parseNumber], [TokenKind.LPAREN, this.parseGroupedExpression], [TokenKind.NOT, this.parsePrefixExpression], [TokenKind.NULL, this.parseNull], [TokenKind.ROOT, this.parseRootQuery], [TokenKind.CURRENT, this.parseRelativeQuery], [TokenKind.SINGLE_QUOTE_STRING, this.parseString], [TokenKind.DOUBLE_QUOTE_STRING, this.parseString], [TokenKind.TRUE, this.parseBoolean], [TokenKind.FUNCTION, this.parseFunction], [TokenKind.CURRENT_KEY, this.parseCurrentKey]]);
2416
2600
  }
2417
2601
  parse(stream) {
2418
2602
  if (stream.current.kind === TokenKind.ROOT) stream.next();
@@ -2444,6 +2628,8 @@ class Parser {
2444
2628
  return new NameSelector(this.environment, stream.current, stream.current.value, true);
2445
2629
  case TokenKind.WILD:
2446
2630
  return new WildcardSelector(this.environment, stream.current, true);
2631
+ case TokenKind.KEY:
2632
+ return new KeySelector(this.environment, stream.current, stream.current.value, true);
2447
2633
  case TokenKind.KEYS:
2448
2634
  return new KeysSelector(this.environment, stream.current, true);
2449
2635
  case TokenKind.DDOT:
@@ -2539,6 +2725,13 @@ class Parser {
2539
2725
  case TokenKind.WILD:
2540
2726
  items.push(new WildcardSelector(this.environment, stream.current));
2541
2727
  break;
2728
+ case TokenKind.KEY_SINGLE_QUOTE_STRING:
2729
+ case TokenKind.KEY_DOUBLE_QUOTE_STRING:
2730
+ items.push(new KeySelector(this.environment, stream.current, this.decodeString(stream.current, true), false));
2731
+ break;
2732
+ case TokenKind.KEYS_FILTER:
2733
+ items.push(this.parseFilter(stream, true));
2734
+ break;
2542
2735
  case TokenKind.KEYS:
2543
2736
  items.push(new KeysSelector(this.environment, stream.current));
2544
2737
  break;
@@ -2559,6 +2752,7 @@ class Parser {
2559
2752
  return new BracketedSelection(this.environment, token, items);
2560
2753
  }
2561
2754
  parseFilter(stream) {
2755
+ let keys = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
2562
2756
  const tok = stream.next();
2563
2757
  const expr = this.parseFilterExpression(stream);
2564
2758
  if (expr instanceof FunctionExtension) {
@@ -2567,7 +2761,7 @@ class Parser {
2567
2761
  throw new JSONPathTypeError(`result of ${expr.name}() must be compared`, expr.token);
2568
2762
  }
2569
2763
  }
2570
- return new FilterSelector(this.environment, tok, new LogicalExpression(tok, expr));
2764
+ return keys ? new KeysFilterSelector(this.environment, tok, new LogicalExpression(tok, expr)) : new FilterSelector(this.environment, tok, new LogicalExpression(tok, expr));
2571
2765
  }
2572
2766
  parseBoolean(stream) {
2573
2767
  if (stream.current.kind === TokenKind.FALSE) return new BooleanLiteral(stream.current, false);
@@ -2602,13 +2796,20 @@ class Parser {
2602
2796
  return new InfixExpression(tok, left, operator, right);
2603
2797
  }
2604
2798
  parseGroupedExpression(stream) {
2605
- stream.next();
2799
+ if (stream.peek.kind === TokenKind.RPAREN) {
2800
+ throw new JSONPathSyntaxError(`empty paren expression`, stream.current);
2801
+ }
2802
+ stream.next(); // eat open paren
2803
+
2606
2804
  let expr = this.parseFilterExpression(stream);
2607
2805
  stream.next();
2608
2806
  while (stream.current.kind !== TokenKind.RPAREN) {
2609
2807
  if (stream.current.kind === TokenKind.EOF) {
2610
2808
  throw new JSONPathSyntaxError("unbalanced parentheses", stream.current);
2611
2809
  }
2810
+ if (!BINARY_OPERATORS.has(stream.current.kind)) {
2811
+ throw new JSONPathSyntaxError(`expected an expression, found '${stream.current.value}'`, stream.current);
2812
+ }
2612
2813
  expr = this.parseInfixExpression(stream, expr);
2613
2814
  }
2614
2815
  stream.expect(TokenKind.RPAREN);
@@ -2664,7 +2865,7 @@ class Parser {
2664
2865
  msg = "end of expression";
2665
2866
  break;
2666
2867
  default:
2667
- msg = `'${stream.current.value}`;
2868
+ msg = `'${stream.current.value}'`;
2668
2869
  }
2669
2870
  throw new JSONPathSyntaxError(`unexpected ${msg}`, stream.current);
2670
2871
  }
@@ -2988,6 +3189,7 @@ var index$1 = /*#__PURE__*/Object.freeze({
2988
3189
  JSONPathRecursionLimitError: JSONPathRecursionLimitError,
2989
3190
  JSONPathSyntaxError: JSONPathSyntaxError,
2990
3191
  JSONPathTypeError: JSONPathTypeError,
3192
+ KEY_MARK: KEY_MARK,
2991
3193
  Nothing: Nothing,
2992
3194
  Token: Token,
2993
3195
  TokenKind: TokenKind,
@@ -3480,6 +3682,6 @@ var index = /*#__PURE__*/Object.freeze({
3480
3682
  apply: apply
3481
3683
  });
3482
3684
 
3483
- const version = "1.2.0";
3685
+ const version = "1.3.0";
3484
3686
 
3485
3687
  export { DEFAULT_ENVIRONMENT, FunctionExpressionType, JSONPatch, JSONPatchError, JSONPatchTestFailure, JSONPath, JSONPathEnvironment, JSONPathError, JSONPathIndexError, JSONPathLexerError, JSONPathNode, JSONPathNodeList, JSONPathRecursionLimitError, JSONPathSyntaxError, JSONPathTypeError, JSONPointer, Nothing, RelativeJSONPointer, Token, TokenKind, UNDEFINED, apply, compile, index as jsonpatch, index$1 as jsonpath, index$3 as jsonpointer, lazyQuery, query, resolve, version };