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
@@ -641,6 +641,24 @@ var index$3 = /*#__PURE__*/Object.freeze({
641
641
  resolve: resolve
642
642
  });
643
643
 
644
+ const Nothing = Symbol.for("jsonpath.nothing");
645
+
646
+ /**
647
+ * ValueType for JSONPath function expression tye system.
648
+ */
649
+
650
+ /**
651
+ * Object passed to FilterExpression.evaluate().
652
+ */
653
+
654
+ /**
655
+ * A type predicate for an object with a string property.
656
+ */
657
+ function hasStringKey(value, key) {
658
+ return isObject(value) && Object.hasOwn(value, key);
659
+ }
660
+ const KEY_MARK = "\x02";
661
+
644
662
  /**
645
663
  * The pair of a JSON value and its location found in the target JSON value.
646
664
  */
@@ -658,7 +676,7 @@ class JSONPathNode {
658
676
  get path() {
659
677
  return (
660
678
  // eslint-disable-next-line prefer-template
661
- "$" + this.location.map(s => isString(s) ? `['${s}']` : `[${s}]`).join("")
679
+ "$" + this.location.map(s => isString(s) ? this.decode_name_location(s) : `[${s}]`).join("")
662
680
  );
663
681
  }
664
682
 
@@ -671,6 +689,9 @@ class JSONPathNode {
671
689
  }
672
690
  return new JSONPointer(JSONPointer.encode(this.location.map(String)));
673
691
  }
692
+ decode_name_location(name) {
693
+ return name.startsWith(KEY_MARK) ? `[~'${name.slice(1).replaceAll("'", "\\'")}']` : `['${name.replaceAll("'", "\\'")}']`;
694
+ }
674
695
  }
675
696
 
676
697
  /**
@@ -750,23 +771,6 @@ class JSONPathNodeList {
750
771
  }
751
772
  }
752
773
 
753
- const Nothing = Symbol.for("jsonpath.nothing");
754
-
755
- /**
756
- * ValueType for JSONPath function expression tye system.
757
- */
758
-
759
- /**
760
- * Object passed to FilterExpression.evaluate().
761
- */
762
-
763
- /**
764
- * A type predicate for an object with a string property.
765
- */
766
- function hasStringKey(value, key) {
767
- return isObject(value) && Object.hasOwn(value, key);
768
- }
769
-
770
774
  /**
771
775
  * Base class for all filter expressions.
772
776
  */
@@ -1169,7 +1173,8 @@ let TokenKind = /*#__PURE__*/function (TokenKind) {
1169
1173
  TokenKind["AND"] = "TOKEN_AND";
1170
1174
  TokenKind["COLON"] = "TOKEN_COLON";
1171
1175
  TokenKind["COMMA"] = "TOKEN_COMMA";
1172
- TokenKind["CURRENT"] = "TOKEN_CURRENT_NODE";
1176
+ TokenKind["CURRENT"] = "TOKEN_CURRENT_VALUE";
1177
+ TokenKind["CURRENT_KEY"] = "TOKEN_CURRENT_KEY";
1173
1178
  TokenKind["DDOT"] = "TOKEN_DDOT";
1174
1179
  TokenKind["DOT"] = "TOKEN_DOT";
1175
1180
  TokenKind["DOUBLE_QUOTE_STRING"] = "TOKEN_DOUBLE_QUOTE_STRING";
@@ -1183,7 +1188,10 @@ let TokenKind = /*#__PURE__*/function (TokenKind) {
1183
1188
  TokenKind["GT"] = "TOKEN_GT";
1184
1189
  TokenKind["INDEX"] = "TOKEN_INDEX";
1185
1190
  TokenKind["KEY"] = "TOKEN_KEY";
1191
+ TokenKind["KEY_DOUBLE_QUOTE_STRING"] = "TOKEN_KEY_DOUBLE_QUOTE_STRING";
1192
+ TokenKind["KEY_SINGLE_QUOTE_STRING"] = "TOKEN_KEY_SINGLE_QUOTE_STRING";
1186
1193
  TokenKind["KEYS"] = "TOKEN_KEYS";
1194
+ TokenKind["KEYS_FILTER"] = "TOKEN_KEYS_FILTER";
1187
1195
  TokenKind["LBRACKET"] = "TOKEN_LBRACKET";
1188
1196
  TokenKind["LE"] = "TOKEN_LE";
1189
1197
  TokenKind["LG"] = "TOKEN_LG";
@@ -1264,6 +1272,7 @@ const indexPattern = /-?\d+/y;
1264
1272
  const intPattern = /-?[0-9]+/y;
1265
1273
  const namePattern = /[\u0080-\uFFFFa-zA-Z_][\u0080-\uFFFFa-zA-Z0-9_-]*/y;
1266
1274
  const whitespace = new Set([" ", "\n", "\t", "\r"]);
1275
+ const nameFirstPattern = /[\u0080-\uFFFFa-zA-Z_]/; // don't set sticky bit
1267
1276
 
1268
1277
  /**
1269
1278
  * JSONPath lexical scanner.
@@ -1336,6 +1345,11 @@ class Lexer {
1336
1345
  if (ch) this.backup();
1337
1346
  return ch;
1338
1347
  }
1348
+ peekMatch(pattern) {
1349
+ const ch = this.next();
1350
+ if (ch) this.backup();
1351
+ return pattern.test(ch);
1352
+ }
1339
1353
  accept(valid) {
1340
1354
  const ch = this.next();
1341
1355
  if (valid.has(ch)) return true;
@@ -1465,10 +1479,28 @@ function lexDescendantSelection(l) {
1465
1479
  l.emit(TokenKind.NAME);
1466
1480
  return lexSegment;
1467
1481
  }
1468
- if (!l.environment.strict && l.acceptMatchRun(l.environment.keysPattern)) {
1469
- // Non-standard keys selector
1470
- l.emit(TokenKind.KEYS);
1471
- return lexSegment;
1482
+ if (!l.environment.strict) {
1483
+ // We're effectively disabling the _key selector_ and _keys filter selector_ if a
1484
+ // custom _keys selector_ is set.
1485
+ if (l.environment.keysPattern.source === "~" && l.peek() === "~") {
1486
+ l.next();
1487
+ if (l.peekMatch(nameFirstPattern)) {
1488
+ // Non-standard key selector
1489
+ l.ignore(); // ignore ~
1490
+ l.acceptMatchRun(namePattern);
1491
+ l.emit(TokenKind.KEY);
1492
+ return lexSegment;
1493
+ } else {
1494
+ // Non-standard keys selector
1495
+ l.emit(TokenKind.KEYS);
1496
+ return lexSegment;
1497
+ }
1498
+ } else if (l.acceptMatchRun(l.environment.keysPattern)) {
1499
+ // NOTE: A custom keys pattern does not play well with other non-standard key selectors.
1500
+ // We leave this here for backwards compatibility.
1501
+ l.emit(TokenKind.KEYS);
1502
+ return lexSegment;
1503
+ }
1472
1504
  }
1473
1505
  const ch = l.next();
1474
1506
  switch (ch) {
@@ -1493,9 +1525,28 @@ function lexDotSelector(l) {
1493
1525
  l.error("unexpected whitespace after dot");
1494
1526
  return null;
1495
1527
  }
1496
- if (!l.environment.strict && l.acceptMatchRun(l.environment.keysPattern)) {
1497
- l.emit(TokenKind.KEYS);
1498
- return lexSegment;
1528
+ if (!l.environment.strict) {
1529
+ // We're effectively disabling the _key selector_ and _keys filter selector_ if a
1530
+ // custom _keys selector_ is set.
1531
+ if (l.environment.keysPattern.source === "~" && l.peek() === "~") {
1532
+ l.next();
1533
+ if (l.peekMatch(nameFirstPattern)) {
1534
+ // Non-standard key selector
1535
+ l.ignore(); // ignore ~
1536
+ l.acceptMatchRun(namePattern);
1537
+ l.emit(TokenKind.KEY);
1538
+ return lexSegment;
1539
+ } else {
1540
+ // Non-standard keys selector
1541
+ l.emit(TokenKind.KEYS);
1542
+ return lexSegment;
1543
+ }
1544
+ } else if (l.acceptMatchRun(l.environment.keysPattern)) {
1545
+ // NOTE: A custom keys pattern does not play well with other non-standard key selectors.
1546
+ // We leave this here for backwards compatibility.
1547
+ l.emit(TokenKind.KEYS);
1548
+ return lexSegment;
1549
+ }
1499
1550
  }
1500
1551
  if (l.acceptMatchRun(namePattern)) {
1501
1552
  l.emit(TokenKind.NAME);
@@ -1518,8 +1569,25 @@ function lexInsideBracketedSelection(l) {
1518
1569
  continue;
1519
1570
  }
1520
1571
  if (!l.environment.strict && l.acceptMatchRun(l.environment.keysPattern)) {
1521
- l.emit(TokenKind.KEYS);
1522
- continue;
1572
+ switch (l.peek()) {
1573
+ case "'":
1574
+ l.ignore(); // ~
1575
+ l.next();
1576
+ return lexSingleQuoteKeyString(l);
1577
+ case '"':
1578
+ l.ignore(); // ~
1579
+ l.next();
1580
+ return lexDoubleQuoteKeyString(l);
1581
+ case "?":
1582
+ l.ignore(); // ~
1583
+ l.next();
1584
+ l.emit(TokenKind.KEYS_FILTER);
1585
+ l.filterLevel += 1;
1586
+ return lexInsideFilter;
1587
+ default:
1588
+ l.emit(TokenKind.KEYS);
1589
+ continue;
1590
+ }
1523
1591
  }
1524
1592
  const ch = l.next();
1525
1593
  switch (ch) {
@@ -1606,7 +1674,7 @@ function lexInsideFilter(l) {
1606
1674
  l.emit(TokenKind.CURRENT);
1607
1675
  return lexSegment;
1608
1676
  case "#":
1609
- l.emit(TokenKind.KEY);
1677
+ l.emit(TokenKind.CURRENT_KEY);
1610
1678
  return lexSegment;
1611
1679
  case ".":
1612
1680
  l.backup();
@@ -1706,8 +1774,7 @@ function lexInsideFilter(l) {
1706
1774
  * @param state - The state function to return control to.
1707
1775
  * @returns String tokenizing state function.
1708
1776
  */
1709
- function makeLexString(quote, state) {
1710
- // eslint-disable-next-line sonarjs/cognitive-complexity
1777
+ function makeLexString(quote, state, token_kind) {
1711
1778
  function _lexString(l) {
1712
1779
  l.ignore();
1713
1780
  if (l.peek() === quote) {
@@ -1733,7 +1800,7 @@ function makeLexString(quote, state) {
1733
1800
  }
1734
1801
  if (ch === quote) {
1735
1802
  l.backup();
1736
- l.emit(quote === "'" ? TokenKind.SINGLE_QUOTE_STRING : TokenKind.DOUBLE_QUOTE_STRING);
1803
+ l.emit(token_kind);
1737
1804
  l.next();
1738
1805
  l.ignore();
1739
1806
  return state;
@@ -1742,10 +1809,12 @@ function makeLexString(quote, state) {
1742
1809
  }
1743
1810
  return _lexString;
1744
1811
  }
1745
- const lexSingleQuoteStringInsideBracketSelection = makeLexString("'", lexInsideBracketedSelection);
1746
- const lexDoubleQuoteStringInsideBracketSelection = makeLexString('"', lexInsideBracketedSelection);
1747
- const lexSingleQuoteStringInsideFilterExpression = makeLexString("'", lexInsideFilter);
1748
- const lexDoubleQuoteStringInsideFilterExpression = makeLexString('"', lexInsideFilter);
1812
+ const lexSingleQuoteStringInsideBracketSelection = makeLexString("'", lexInsideBracketedSelection, TokenKind.SINGLE_QUOTE_STRING);
1813
+ const lexDoubleQuoteStringInsideBracketSelection = makeLexString('"', lexInsideBracketedSelection, TokenKind.DOUBLE_QUOTE_STRING);
1814
+ const lexSingleQuoteStringInsideFilterExpression = makeLexString("'", lexInsideFilter, TokenKind.SINGLE_QUOTE_STRING);
1815
+ const lexDoubleQuoteStringInsideFilterExpression = makeLexString('"', lexInsideFilter, TokenKind.DOUBLE_QUOTE_STRING);
1816
+ const lexSingleQuoteKeyString = makeLexString("'", lexInsideBracketedSelection, TokenKind.KEY_SINGLE_QUOTE_STRING);
1817
+ const lexDoubleQuoteKeyString = makeLexString('"', lexInsideBracketedSelection, TokenKind.KEY_DOUBLE_QUOTE_STRING);
1749
1818
 
1750
1819
  /**
1751
1820
  * Base class for all JSONPath segments and selectors.
@@ -1786,7 +1855,7 @@ class NameSelector extends JSONPathSelector {
1786
1855
  resolve(nodes) {
1787
1856
  const rv = [];
1788
1857
  for (const node of nodes) {
1789
- if (hasStringKey(node.value, this.name)) {
1858
+ if (!isArray(node.value) && hasStringKey(node.value, this.name)) {
1790
1859
  rv.push(new JSONPathNode(node.value[this.name], node.location.concat(this.name), node.root));
1791
1860
  }
1792
1861
  }
@@ -1794,7 +1863,7 @@ class NameSelector extends JSONPathSelector {
1794
1863
  }
1795
1864
  *lazyResolve(nodes) {
1796
1865
  for (const node of nodes) {
1797
- if (hasStringKey(node.value, this.name)) {
1866
+ if (!isArray(node.value) && hasStringKey(node.value, this.name)) {
1798
1867
  yield new JSONPathNode(node.value[this.name], node.location.concat(this.name), node.root);
1799
1868
  }
1800
1869
  }
@@ -1870,7 +1939,7 @@ class SliceSelector extends JSONPathSelector {
1870
1939
  *lazyResolve(nodes) {
1871
1940
  for (const node of nodes) {
1872
1941
  if (!isArray(node.value)) continue;
1873
- for (const [i, value] of this.slice(node.value, this.start, this.stop, this.step)) {
1942
+ for (const [i, value] of this.lazySlice(node.value, this.start, this.stop, this.step)) {
1874
1943
  yield new JSONPathNode(value, node.location.concat(i), node.root);
1875
1944
  }
1876
1945
  }
@@ -1933,6 +2002,45 @@ class SliceSelector extends JSONPathSelector {
1933
2002
  }
1934
2003
  return slicedArray;
1935
2004
  }
2005
+
2006
+ // eslint-disable-next-line sonarjs/cognitive-complexity
2007
+ *lazySlice(arr, start, stop, step) {
2008
+ if (!arr.length) return;
2009
+
2010
+ // Handle negative and undefined start values
2011
+ if (start === undefined || start === null) {
2012
+ start = step && step < 0 ? arr.length - 1 : 0;
2013
+ } else if (start < 0) {
2014
+ start = Math.max(arr.length + start, 0);
2015
+ } else {
2016
+ start = Math.min(start, arr.length - 1);
2017
+ }
2018
+
2019
+ // Handle negative and undefined stop values
2020
+ if (stop === undefined || stop === null) {
2021
+ stop = step && step < 0 ? -1 : arr.length;
2022
+ } else if (stop < 0) {
2023
+ stop = Math.max(arr.length + stop, -1);
2024
+ } else {
2025
+ stop = Math.min(stop, arr.length);
2026
+ }
2027
+
2028
+ // Perform the slice
2029
+ if (step === undefined) {
2030
+ // Default to a step of 1
2031
+ for (let i = start; i < stop; i += 1) {
2032
+ yield [i, arr[i]];
2033
+ }
2034
+ } else if (step > 0) {
2035
+ for (let i = start; i < stop; i += step) {
2036
+ yield [i, arr[i]];
2037
+ }
2038
+ } else if (step < 0) {
2039
+ for (let i = start; i > stop; i += step) {
2040
+ yield [i, arr[i]];
2041
+ }
2042
+ }
2043
+ }
1936
2044
  }
1937
2045
  class WildcardSelector extends JSONPathSelector {
1938
2046
  constructor(environment, token) {
@@ -1999,8 +2107,6 @@ class RecursiveDescentSegment extends JSONPathSelector {
1999
2107
  }
2000
2108
  }
2001
2109
  }
2002
-
2003
- // console.log(JSON.stringify(rv.map((n: any) => n.value)));
2004
2110
  return this.selector.resolve(rv);
2005
2111
  }
2006
2112
  *lazyResolve(nodes) {
@@ -2357,6 +2463,38 @@ class CurrentKey extends FilterExpression {
2357
2463
  }
2358
2464
  }
2359
2465
 
2466
+ class KeySelector extends JSONPathSelector {
2467
+ constructor(environment, token, key) {
2468
+ let shorthand = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
2469
+ super(environment, token);
2470
+ this.environment = environment;
2471
+ this.token = token;
2472
+ this.key = key;
2473
+ this.shorthand = shorthand;
2474
+ }
2475
+ resolve(nodes) {
2476
+ const rv = [];
2477
+ for (const node of nodes) {
2478
+ if (node.value instanceof String || isArray(node.value)) continue;
2479
+ if (isObject(node.value) && hasStringKey(node.value, this.key)) {
2480
+ rv.push(new JSONPathNode(this.key, node.location.concat(`${KEY_MARK}${this.key}`), node.root));
2481
+ }
2482
+ }
2483
+ return rv;
2484
+ }
2485
+ *lazyResolve(nodes) {
2486
+ for (const node of nodes) {
2487
+ if (node.value instanceof String || isArray(node.value)) continue;
2488
+ if (isObject(node.value) && hasStringKey(node.value, this.key)) {
2489
+ yield new JSONPathNode(this.key, node.location.concat(`${KEY_MARK}${this.key}`), node.root);
2490
+ }
2491
+ }
2492
+ }
2493
+ toString() {
2494
+ return this.shorthand ? `[~'${this.key.replaceAll("'", "\\'")}']` : `~'${this.key.replaceAll("'", "\\'")}'`;
2495
+ }
2496
+ }
2497
+
2360
2498
  /**
2361
2499
  * Object property name selector or array index selector.
2362
2500
  */
@@ -2373,10 +2511,8 @@ class KeysSelector extends JSONPathSelector {
2373
2511
  for (const node of nodes) {
2374
2512
  if (node.value instanceof String || isArray(node.value)) continue;
2375
2513
  if (isObject(node.value)) {
2376
- let i = 0;
2377
2514
  for (const [key, _] of this.environment.entries(node.value)) {
2378
- rv.push(new JSONPathNode(key, node.location.concat("[~]", `[${i}]`), node.root));
2379
- i++;
2515
+ rv.push(new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root));
2380
2516
  }
2381
2517
  }
2382
2518
  }
@@ -2386,10 +2522,8 @@ class KeysSelector extends JSONPathSelector {
2386
2522
  for (const node of nodes) {
2387
2523
  if (node.value instanceof String || isArray(node.value)) continue;
2388
2524
  if (isObject(node.value)) {
2389
- let i = 0;
2390
2525
  for (const [key, _] of this.environment.entries(node.value)) {
2391
- yield new JSONPathNode(key, node.location.concat("[~]", `[${i}]`), node.root);
2392
- i++;
2526
+ yield new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root);
2393
2527
  }
2394
2528
  }
2395
2529
  }
@@ -2398,6 +2532,56 @@ class KeysSelector extends JSONPathSelector {
2398
2532
  return this.shorthand ? "[~]" : "~";
2399
2533
  }
2400
2534
  }
2535
+ class KeysFilterSelector extends JSONPathSelector {
2536
+ constructor(environment, token, expression) {
2537
+ super(environment, token);
2538
+ this.environment = environment;
2539
+ this.token = token;
2540
+ this.expression = expression;
2541
+ }
2542
+ resolve(nodes) {
2543
+ const rv = [];
2544
+ for (const node of nodes) {
2545
+ if (node.value instanceof String || isArray(node.value)) continue;
2546
+ if (isObject(node.value)) {
2547
+ for (const [key, value] of this.environment.entries(node.value)) {
2548
+ const filterContext = {
2549
+ environment: this.environment,
2550
+ currentValue: value,
2551
+ rootValue: node.root,
2552
+ currentKey: key
2553
+ };
2554
+ if (this.expression.evaluate(filterContext)) {
2555
+ rv.push(new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root));
2556
+ }
2557
+ }
2558
+ }
2559
+ }
2560
+ return rv;
2561
+ }
2562
+ *lazyResolve(nodes) {
2563
+ for (const node of nodes) {
2564
+ if (node.value instanceof String || isArray(node.value)) continue;
2565
+ if (isObject(node.value)) {
2566
+ for (const [key, value] of this.environment.entries(node.value)) {
2567
+ const filterContext = {
2568
+ environment: this.environment,
2569
+ currentValue: value,
2570
+ rootValue: node.root,
2571
+ lazy: true,
2572
+ currentKey: key
2573
+ };
2574
+ if (this.expression.evaluate(filterContext)) {
2575
+ yield new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root);
2576
+ }
2577
+ }
2578
+ }
2579
+ }
2580
+ }
2581
+ toString() {
2582
+ return `~?${this.expression.toString()}`;
2583
+ }
2584
+ }
2401
2585
 
2402
2586
  const PRECEDENCE_LOWEST = 1;
2403
2587
  const PRECEDENCE_LOGICAL_OR = 4;
@@ -2414,7 +2598,7 @@ const COMPARISON_OPERATORS = new Set(["==", ">=", ">", "<=", "<", "!="]);
2414
2598
  class Parser {
2415
2599
  constructor(environment) {
2416
2600
  this.environment = environment;
2417
- 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]]);
2601
+ 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]]);
2418
2602
  }
2419
2603
  parse(stream) {
2420
2604
  if (stream.current.kind === TokenKind.ROOT) stream.next();
@@ -2446,6 +2630,8 @@ class Parser {
2446
2630
  return new NameSelector(this.environment, stream.current, stream.current.value, true);
2447
2631
  case TokenKind.WILD:
2448
2632
  return new WildcardSelector(this.environment, stream.current, true);
2633
+ case TokenKind.KEY:
2634
+ return new KeySelector(this.environment, stream.current, stream.current.value, true);
2449
2635
  case TokenKind.KEYS:
2450
2636
  return new KeysSelector(this.environment, stream.current, true);
2451
2637
  case TokenKind.DDOT:
@@ -2541,6 +2727,13 @@ class Parser {
2541
2727
  case TokenKind.WILD:
2542
2728
  items.push(new WildcardSelector(this.environment, stream.current));
2543
2729
  break;
2730
+ case TokenKind.KEY_SINGLE_QUOTE_STRING:
2731
+ case TokenKind.KEY_DOUBLE_QUOTE_STRING:
2732
+ items.push(new KeySelector(this.environment, stream.current, this.decodeString(stream.current, true), false));
2733
+ break;
2734
+ case TokenKind.KEYS_FILTER:
2735
+ items.push(this.parseFilter(stream, true));
2736
+ break;
2544
2737
  case TokenKind.KEYS:
2545
2738
  items.push(new KeysSelector(this.environment, stream.current));
2546
2739
  break;
@@ -2561,6 +2754,7 @@ class Parser {
2561
2754
  return new BracketedSelection(this.environment, token, items);
2562
2755
  }
2563
2756
  parseFilter(stream) {
2757
+ let keys = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
2564
2758
  const tok = stream.next();
2565
2759
  const expr = this.parseFilterExpression(stream);
2566
2760
  if (expr instanceof FunctionExtension) {
@@ -2569,7 +2763,7 @@ class Parser {
2569
2763
  throw new JSONPathTypeError(`result of ${expr.name}() must be compared`, expr.token);
2570
2764
  }
2571
2765
  }
2572
- return new FilterSelector(this.environment, tok, new LogicalExpression(tok, expr));
2766
+ return keys ? new KeysFilterSelector(this.environment, tok, new LogicalExpression(tok, expr)) : new FilterSelector(this.environment, tok, new LogicalExpression(tok, expr));
2573
2767
  }
2574
2768
  parseBoolean(stream) {
2575
2769
  if (stream.current.kind === TokenKind.FALSE) return new BooleanLiteral(stream.current, false);
@@ -2604,13 +2798,20 @@ class Parser {
2604
2798
  return new InfixExpression(tok, left, operator, right);
2605
2799
  }
2606
2800
  parseGroupedExpression(stream) {
2607
- stream.next();
2801
+ if (stream.peek.kind === TokenKind.RPAREN) {
2802
+ throw new JSONPathSyntaxError(`empty paren expression`, stream.current);
2803
+ }
2804
+ stream.next(); // eat open paren
2805
+
2608
2806
  let expr = this.parseFilterExpression(stream);
2609
2807
  stream.next();
2610
2808
  while (stream.current.kind !== TokenKind.RPAREN) {
2611
2809
  if (stream.current.kind === TokenKind.EOF) {
2612
2810
  throw new JSONPathSyntaxError("unbalanced parentheses", stream.current);
2613
2811
  }
2812
+ if (!BINARY_OPERATORS.has(stream.current.kind)) {
2813
+ throw new JSONPathSyntaxError(`expected an expression, found '${stream.current.value}'`, stream.current);
2814
+ }
2614
2815
  expr = this.parseInfixExpression(stream, expr);
2615
2816
  }
2616
2817
  stream.expect(TokenKind.RPAREN);
@@ -2666,7 +2867,7 @@ class Parser {
2666
2867
  msg = "end of expression";
2667
2868
  break;
2668
2869
  default:
2669
- msg = `'${stream.current.value}`;
2870
+ msg = `'${stream.current.value}'`;
2670
2871
  }
2671
2872
  throw new JSONPathSyntaxError(`unexpected ${msg}`, stream.current);
2672
2873
  }
@@ -2990,6 +3191,7 @@ var index$1 = /*#__PURE__*/Object.freeze({
2990
3191
  JSONPathRecursionLimitError: JSONPathRecursionLimitError,
2991
3192
  JSONPathSyntaxError: JSONPathSyntaxError,
2992
3193
  JSONPathTypeError: JSONPathTypeError,
3194
+ KEY_MARK: KEY_MARK,
2993
3195
  Nothing: Nothing,
2994
3196
  Token: Token,
2995
3197
  TokenKind: TokenKind,
@@ -3482,7 +3684,7 @@ var index = /*#__PURE__*/Object.freeze({
3482
3684
  apply: apply
3483
3685
  });
3484
3686
 
3485
- const version = "1.2.0";
3687
+ const version = "1.3.0";
3486
3688
 
3487
3689
  exports.DEFAULT_ENVIRONMENT = DEFAULT_ENVIRONMENT;
3488
3690
  exports.FunctionExpressionType = FunctionExpressionType;