diff --git a/README.md b/README.md index 12641f9..d5dd7d8 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This repository contains an implementation of [JSONPath](http://goessner.net/art ### `Path` class -The `jsonpath2.Path.Path` class represents a JSONPath. +The `jsonpath2.path.Path` class represents a JSONPath. ```python >>> s = '{"hello":"Hello, world!"}' @@ -24,7 +24,7 @@ The `jsonpath2.Path.Path` class represents a JSONPath. ['$["hello"]'] ``` -This class is constructed with respect to the given instance of the `jsonpath2.Path.RootNode` class (viz., the `root_node` property). +This class is constructed with respect to the given instance of the `jsonpath2.nodes.root.RootNode` class (viz., the `root_node` property). #### `parse_str(strdata)` class method @@ -37,7 +37,7 @@ Parse the contents of the given file and return a new instance of this class. #### `match(root_value)` instance method Match the given JSON data structure against this instance. -For each match, yield an instance of the `jsonpath2.Node.MatchData` class. +For each match, yield an instance of the `jsonpath2.node.MatchData` class. #### `__eq__(other)` instance method @@ -53,7 +53,7 @@ The root node of the abstract syntax tree for this instance. ### `Node` abstract class -The `jsonpath2.Node.Node` class represents the abstract syntax tree for a JSONPath. +The `jsonpath2.node.Node` class represents the abstract syntax tree for a JSONPath. #### `__eq__(other)` instance method @@ -66,7 +66,7 @@ Yields the lexer tokens for the string representation of this instance. #### `match(root_value, current_value)` instance method Match the given root and current JSON data structures against this instance. -For each match, yield an instance of the `jsonpath2.Node.MatchData` class. +For each match, yield an instance of the `jsonpath2.node.MatchData` class. #### `tojsonpath()` instance method @@ -74,7 +74,7 @@ Returns the string representation of this instance. ### `MatchData` class -The `jsonpath2.Node.MatchData` class represents the JSON value and context for a JSONPath match. +The `jsonpath2.node.MatchData` class represents the JSON value and context for a JSONPath match. This class is constructed with respect to a root JSON value, a current JSON value, and an abstract syntax tree node. @@ -111,7 +111,7 @@ The abstract syntax tree node. | JSONPath Filter Expression | Description | | - | - | | `$` or `@` | nested JSONPath (returns `true` if any match exists; otherwise, returns `false`) | -| `=`, `!=`, `>`, `>=`, `<`, `<=` | binary operator, where left-hand operand is a nested JSONPath and right-right operand is a JSON value (returns `true` if any match exists; otherwise, returns `false`) | +| `=`, `!=`, `>`, `>=`, `<`, `<=` | binary operator, where left- and right-hand operands are nested JSONPaths or JSON values (returns `true` if any match exists; otherwise, returns `false`) | | `and`, `or`, `not` | Boolean operator, where operands are JSONPath filter expressions | | `(` ... `)` | parentheses | diff --git a/jsonpath2/expressions/operator.py b/jsonpath2/expressions/operator.py index 85ac2c0..3d856b3 100644 --- a/jsonpath2/expressions/operator.py +++ b/jsonpath2/expressions/operator.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- """The operator expression module.""" import json -from typing import Callable, Generator, List +from typing import Callable, Generator, List, Union from jsonpath2.expression import Expression from jsonpath2.node import Node @@ -22,33 +22,61 @@ def evaluate(self, root_value: object, current_value: object) -> bool: # pragma class BinaryOperatorExpression(OperatorExpression): """Binary operator expression.""" - def __init__(self, token: str, callback: Callable[[object, object], bool], left_node: Node, right_value: object): + def __init__(self, token: str, callback: Callable[[object, object], bool], + left_node_or_value: Union[Node, object], right_node_or_value: Union[Node, object]): """Constructor save the left right and token.""" super(BinaryOperatorExpression, self).__init__() self.token = token self.callback = callback - self.left_node = left_node - self.right_value = right_value + self.left_node_or_value = left_node_or_value + self.right_node_or_value = right_node_or_value def __jsonpath__(self) -> Generator[str, None, None]: """Return the string json path of this expression.""" - for left_node_token in self.left_node.__jsonpath__(): - yield left_node_token + if isinstance(self.left_node_or_value, Node): + for left_node_token in self.left_node_or_value.__jsonpath__(): + yield left_node_token + else: + yield json.dumps(self.left_node_or_value) + yield ' ' yield self.token yield ' ' - yield json.dumps(self.right_value) + + if isinstance(self.right_node_or_value, Node): + for right_node_token in self.right_node_or_value.__jsonpath__(): + yield right_node_token + else: + yield json.dumps(self.right_node_or_value) def evaluate(self, root_value: object, current_value: object) -> bool: """Evaluate the left and right values given the token.""" - return any( - map( - lambda left_node_match_data: self.callback( - left_node_match_data.current_value, - self.right_value - ), - self.left_node.match(root_value, current_value) + if isinstance(self.left_node_or_value, Node): + left_values = ( + left_node_match_data.current_value + for left_node_match_data + in self.left_node_or_value.match(root_value, current_value) ) + else: + left_values = [ + self.left_node_or_value, + ] + + if isinstance(self.right_node_or_value, Node): + right_values = ( + right_node_match_data.current_value + for right_node_match_data + in self.right_node_or_value.match(root_value, current_value) + ) + else: + right_values = [ + self.right_node_or_value, + ] + + return any( + self.callback(left_value, right_value) + for left_value in left_values + for right_value in right_values ) diff --git a/jsonpath2/expressions/some.py b/jsonpath2/expressions/some.py index c128718..9542f96 100644 --- a/jsonpath2/expressions/some.py +++ b/jsonpath2/expressions/some.py @@ -1,7 +1,8 @@ #!/usr/bin/python # -*- coding: utf-8 -*- """Some expression module.""" -from typing import Generator +import json +from typing import Generator, Union from jsonpath2.expression import Expression from jsonpath2.node import Node @@ -9,17 +10,21 @@ class SomeExpression(Expression): """The some expression class.""" - def __init__(self, next_node: Node): + def __init__(self, next_node_or_value: Union[Node, object]): """Save the next node.""" super(SomeExpression, self).__init__() - self.next_node = next_node + self.next_node_or_value = next_node_or_value def __jsonpath__(self) -> Generator[str, None, None]: """Return the next nodes jsonpath.""" - return self.next_node.__jsonpath__() + if isinstance(self.next_node_or_value, Node): + return self.next_node_or_value.__jsonpath__() + return [json.dumps(self.next_node_or_value)] def evaluate(self, root_value: object, current_value: object) -> bool: """Evaluate the next node.""" - for _next_node_match_data in self.next_node.match(root_value, current_value): - return True - return False + if isinstance(self.next_node_or_value, Node): + for _next_node_match_data in self.next_node_or_value.match(root_value, current_value): + return True + return False + return bool(self.next_node_or_value) diff --git a/jsonpath2/parser/JSONPath.g4 b/jsonpath2/parser/JSONPath.g4 index f422697..b7df781 100644 --- a/jsonpath2/parser/JSONPath.g4 +++ b/jsonpath2/parser/JSONPath.g4 @@ -34,6 +34,16 @@ jsonpath : ROOT_VALUE subscript? EOF ; +jsonpath_ + : ( ROOT_VALUE | CURRENT_VALUE ) subscript? + ; + +jsonpath__ + : jsonpath_ + | value + ; + + subscript : RECURSIVE_DESCENT ( subscriptableBareword | subscriptables ) subscript? | SUBSCRIPT subscriptableBareword subscript? @@ -55,6 +65,7 @@ subscriptable | sliceable | WILDCARD_SUBSCRIPT | QUESTION PAREN_LEFT expression PAREN_RIGHT + | jsonpath_ ; sliceable @@ -76,7 +87,7 @@ orExpression notExpression : NOT notExpression | PAREN_LEFT expression PAREN_RIGHT - | ( ROOT_VALUE | CURRENT_VALUE ) subscript? ( ( EQ | NE | LT | LE | GT | GE ) value )? + | jsonpath__ ( ( EQ | NE | LT | LE | GT | GE ) jsonpath__ )? ; diff --git a/jsonpath2/parser/JSONPath.interp b/jsonpath2/parser/JSONPath.interp index 35da05e..43bf851 100644 --- a/jsonpath2/parser/JSONPath.interp +++ b/jsonpath2/parser/JSONPath.interp @@ -66,6 +66,8 @@ WS rule names: jsonpath +jsonpath_ +jsonpath__ subscript subscriptables subscriptableBareword @@ -83,4 +85,4 @@ value atn: -[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 32, 169, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 3, 2, 3, 2, 5, 2, 35, 10, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 5, 3, 42, 10, 3, 3, 3, 5, 3, 45, 10, 3, 3, 3, 3, 3, 3, 3, 5, 3, 50, 10, 3, 3, 3, 3, 3, 5, 3, 54, 10, 3, 5, 3, 56, 10, 3, 3, 4, 3, 4, 3, 4, 3, 4, 7, 4, 62, 10, 4, 12, 4, 14, 4, 65, 11, 4, 3, 4, 3, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 6, 3, 6, 5, 6, 75, 10, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 5, 6, 84, 10, 6, 3, 7, 3, 7, 3, 7, 5, 7, 89, 10, 7, 3, 7, 3, 7, 3, 7, 5, 7, 94, 10, 7, 3, 8, 3, 8, 3, 9, 3, 9, 3, 9, 5, 9, 101, 10, 9, 3, 10, 3, 10, 3, 10, 5, 10, 106, 10, 10, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 5, 11, 116, 10, 11, 3, 11, 3, 11, 5, 11, 120, 10, 11, 5, 11, 122, 10, 11, 3, 12, 3, 12, 3, 13, 3, 13, 3, 13, 3, 13, 7, 13, 130, 10, 13, 12, 13, 14, 13, 133, 11, 13, 3, 13, 3, 13, 3, 13, 3, 13, 5, 13, 139, 10, 13, 3, 14, 3, 14, 3, 14, 3, 14, 3, 15, 3, 15, 3, 15, 3, 15, 7, 15, 149, 10, 15, 12, 15, 14, 15, 152, 11, 15, 3, 15, 3, 15, 3, 15, 3, 15, 5, 15, 158, 10, 15, 3, 16, 3, 16, 3, 16, 3, 16, 3, 16, 3, 16, 3, 16, 5, 16, 167, 10, 16, 3, 16, 2, 2, 17, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 2, 5, 4, 2, 7, 7, 29, 29, 4, 2, 3, 3, 5, 5, 3, 2, 9, 14, 2, 184, 2, 32, 3, 2, 2, 2, 4, 55, 3, 2, 2, 2, 6, 57, 3, 2, 2, 2, 8, 68, 3, 2, 2, 2, 10, 83, 3, 2, 2, 2, 12, 85, 3, 2, 2, 2, 14, 95, 3, 2, 2, 2, 16, 97, 3, 2, 2, 2, 18, 102, 3, 2, 2, 2, 20, 121, 3, 2, 2, 2, 22, 123, 3, 2, 2, 2, 24, 138, 3, 2, 2, 2, 26, 140, 3, 2, 2, 2, 28, 157, 3, 2, 2, 2, 30, 166, 3, 2, 2, 2, 32, 34, 7, 5, 2, 2, 33, 35, 5, 4, 3, 2, 34, 33, 3, 2, 2, 2, 34, 35, 3, 2, 2, 2, 35, 36, 3, 2, 2, 2, 36, 37, 7, 2, 2, 3, 37, 3, 3, 2, 2, 2, 38, 41, 7, 4, 2, 2, 39, 42, 5, 8, 5, 2, 40, 42, 5, 6, 4, 2, 41, 39, 3, 2, 2, 2, 41, 40, 3, 2, 2, 2, 42, 44, 3, 2, 2, 2, 43, 45, 5, 4, 3, 2, 44, 43, 3, 2, 2, 2, 44, 45, 3, 2, 2, 2, 45, 56, 3, 2, 2, 2, 46, 47, 7, 6, 2, 2, 47, 49, 5, 8, 5, 2, 48, 50, 5, 4, 3, 2, 49, 48, 3, 2, 2, 2, 49, 50, 3, 2, 2, 2, 50, 56, 3, 2, 2, 2, 51, 53, 5, 6, 4, 2, 52, 54, 5, 4, 3, 2, 53, 52, 3, 2, 2, 2, 53, 54, 3, 2, 2, 2, 54, 56, 3, 2, 2, 2, 55, 38, 3, 2, 2, 2, 55, 46, 3, 2, 2, 2, 55, 51, 3, 2, 2, 2, 56, 5, 3, 2, 2, 2, 57, 58, 7, 22, 2, 2, 58, 63, 5, 10, 6, 2, 59, 60, 7, 25, 2, 2, 60, 62, 5, 10, 6, 2, 61, 59, 3, 2, 2, 2, 62, 65, 3, 2, 2, 2, 63, 61, 3, 2, 2, 2, 63, 64, 3, 2, 2, 2, 64, 66, 3, 2, 2, 2, 65, 63, 3, 2, 2, 2, 66, 67, 7, 23, 2, 2, 67, 7, 3, 2, 2, 2, 68, 69, 9, 2, 2, 2, 69, 9, 3, 2, 2, 2, 70, 84, 7, 30, 2, 2, 71, 72, 7, 31, 2, 2, 72, 74, 6, 6, 2, 2, 73, 75, 5, 12, 7, 2, 74, 73, 3, 2, 2, 2, 74, 75, 3, 2, 2, 2, 75, 84, 3, 2, 2, 2, 76, 84, 5, 12, 7, 2, 77, 84, 7, 7, 2, 2, 78, 79, 7, 28, 2, 2, 79, 80, 7, 26, 2, 2, 80, 81, 5, 14, 8, 2, 81, 82, 7, 27, 2, 2, 82, 84, 3, 2, 2, 2, 83, 70, 3, 2, 2, 2, 83, 71, 3, 2, 2, 2, 83, 76, 3, 2, 2, 2, 83, 77, 3, 2, 2, 2, 83, 78, 3, 2, 2, 2, 84, 11, 3, 2, 2, 2, 85, 88, 7, 24, 2, 2, 86, 87, 7, 31, 2, 2, 87, 89, 6, 7, 3, 2, 88, 86, 3, 2, 2, 2, 88, 89, 3, 2, 2, 2, 89, 93, 3, 2, 2, 2, 90, 91, 7, 24, 2, 2, 91, 92, 7, 31, 2, 2, 92, 94, 6, 7, 4, 2, 93, 90, 3, 2, 2, 2, 93, 94, 3, 2, 2, 2, 94, 13, 3, 2, 2, 2, 95, 96, 5, 16, 9, 2, 96, 15, 3, 2, 2, 2, 97, 100, 5, 18, 10, 2, 98, 99, 7, 8, 2, 2, 99, 101, 5, 16, 9, 2, 100, 98, 3, 2, 2, 2, 100, 101, 3, 2, 2, 2, 101, 17, 3, 2, 2, 2, 102, 105, 5, 20, 11, 2, 103, 104, 7, 16, 2, 2, 104, 106, 5, 18, 10, 2, 105, 103, 3, 2, 2, 2, 105, 106, 3, 2, 2, 2, 106, 19, 3, 2, 2, 2, 107, 108, 7, 15, 2, 2, 108, 122, 5, 20, 11, 2, 109, 110, 7, 26, 2, 2, 110, 111, 5, 14, 8, 2, 111, 112, 7, 27, 2, 2, 112, 122, 3, 2, 2, 2, 113, 115, 9, 3, 2, 2, 114, 116, 5, 4, 3, 2, 115, 114, 3, 2, 2, 2, 115, 116, 3, 2, 2, 2, 116, 119, 3, 2, 2, 2, 117, 118, 9, 4, 2, 2, 118, 120, 5, 30, 16, 2, 119, 117, 3, 2, 2, 2, 119, 120, 3, 2, 2, 2, 120, 122, 3, 2, 2, 2, 121, 107, 3, 2, 2, 2, 121, 109, 3, 2, 2, 2, 121, 113, 3, 2, 2, 2, 122, 21, 3, 2, 2, 2, 123, 124, 5, 30, 16, 2, 124, 23, 3, 2, 2, 2, 125, 126, 7, 20, 2, 2, 126, 131, 5, 26, 14, 2, 127, 128, 7, 25, 2, 2, 128, 130, 5, 26, 14, 2, 129, 127, 3, 2, 2, 2, 130, 133, 3, 2, 2, 2, 131, 129, 3, 2, 2, 2, 131, 132, 3, 2, 2, 2, 132, 134, 3, 2, 2, 2, 133, 131, 3, 2, 2, 2, 134, 135, 7, 21, 2, 2, 135, 139, 3, 2, 2, 2, 136, 137, 7, 20, 2, 2, 137, 139, 7, 21, 2, 2, 138, 125, 3, 2, 2, 2, 138, 136, 3, 2, 2, 2, 139, 25, 3, 2, 2, 2, 140, 141, 7, 30, 2, 2, 141, 142, 7, 24, 2, 2, 142, 143, 5, 30, 16, 2, 143, 27, 3, 2, 2, 2, 144, 145, 7, 22, 2, 2, 145, 150, 5, 30, 16, 2, 146, 147, 7, 25, 2, 2, 147, 149, 5, 30, 16, 2, 148, 146, 3, 2, 2, 2, 149, 152, 3, 2, 2, 2, 150, 148, 3, 2, 2, 2, 150, 151, 3, 2, 2, 2, 151, 153, 3, 2, 2, 2, 152, 150, 3, 2, 2, 2, 153, 154, 7, 23, 2, 2, 154, 158, 3, 2, 2, 2, 155, 156, 7, 22, 2, 2, 156, 158, 7, 23, 2, 2, 157, 144, 3, 2, 2, 2, 157, 155, 3, 2, 2, 2, 158, 29, 3, 2, 2, 2, 159, 167, 7, 30, 2, 2, 160, 167, 7, 31, 2, 2, 161, 167, 5, 24, 13, 2, 162, 167, 5, 28, 15, 2, 163, 167, 7, 17, 2, 2, 164, 167, 7, 18, 2, 2, 165, 167, 7, 19, 2, 2, 166, 159, 3, 2, 2, 2, 166, 160, 3, 2, 2, 2, 166, 161, 3, 2, 2, 2, 166, 162, 3, 2, 2, 2, 166, 163, 3, 2, 2, 2, 166, 164, 3, 2, 2, 2, 166, 165, 3, 2, 2, 2, 167, 31, 3, 2, 2, 2, 23, 34, 41, 44, 49, 53, 55, 63, 74, 83, 88, 93, 100, 105, 115, 119, 121, 131, 138, 150, 157, 166] \ No newline at end of file +[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 32, 179, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, 18, 9, 18, 3, 2, 3, 2, 5, 2, 39, 10, 2, 3, 2, 3, 2, 3, 3, 3, 3, 5, 3, 45, 10, 3, 3, 4, 3, 4, 5, 4, 49, 10, 4, 3, 5, 3, 5, 3, 5, 5, 5, 54, 10, 5, 3, 5, 5, 5, 57, 10, 5, 3, 5, 3, 5, 3, 5, 5, 5, 62, 10, 5, 3, 5, 3, 5, 5, 5, 66, 10, 5, 5, 5, 68, 10, 5, 3, 6, 3, 6, 3, 6, 3, 6, 7, 6, 74, 10, 6, 12, 6, 14, 6, 77, 11, 6, 3, 6, 3, 6, 3, 7, 3, 7, 3, 8, 3, 8, 3, 8, 3, 8, 5, 8, 87, 10, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 5, 8, 97, 10, 8, 3, 9, 3, 9, 3, 9, 5, 9, 102, 10, 9, 3, 9, 3, 9, 3, 9, 5, 9, 107, 10, 9, 3, 10, 3, 10, 3, 11, 3, 11, 3, 11, 5, 11, 114, 10, 11, 3, 12, 3, 12, 3, 12, 5, 12, 119, 10, 12, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 5, 13, 130, 10, 13, 5, 13, 132, 10, 13, 3, 14, 3, 14, 3, 15, 3, 15, 3, 15, 3, 15, 7, 15, 140, 10, 15, 12, 15, 14, 15, 143, 11, 15, 3, 15, 3, 15, 3, 15, 3, 15, 5, 15, 149, 10, 15, 3, 16, 3, 16, 3, 16, 3, 16, 3, 17, 3, 17, 3, 17, 3, 17, 7, 17, 159, 10, 17, 12, 17, 14, 17, 162, 11, 17, 3, 17, 3, 17, 3, 17, 3, 17, 5, 17, 168, 10, 17, 3, 18, 3, 18, 3, 18, 3, 18, 3, 18, 3, 18, 3, 18, 5, 18, 177, 10, 18, 3, 18, 2, 2, 19, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 2, 5, 4, 2, 3, 3, 5, 5, 4, 2, 7, 7, 29, 29, 3, 2, 9, 14, 2, 194, 2, 36, 3, 2, 2, 2, 4, 42, 3, 2, 2, 2, 6, 48, 3, 2, 2, 2, 8, 67, 3, 2, 2, 2, 10, 69, 3, 2, 2, 2, 12, 80, 3, 2, 2, 2, 14, 96, 3, 2, 2, 2, 16, 98, 3, 2, 2, 2, 18, 108, 3, 2, 2, 2, 20, 110, 3, 2, 2, 2, 22, 115, 3, 2, 2, 2, 24, 131, 3, 2, 2, 2, 26, 133, 3, 2, 2, 2, 28, 148, 3, 2, 2, 2, 30, 150, 3, 2, 2, 2, 32, 167, 3, 2, 2, 2, 34, 176, 3, 2, 2, 2, 36, 38, 7, 5, 2, 2, 37, 39, 5, 8, 5, 2, 38, 37, 3, 2, 2, 2, 38, 39, 3, 2, 2, 2, 39, 40, 3, 2, 2, 2, 40, 41, 7, 2, 2, 3, 41, 3, 3, 2, 2, 2, 42, 44, 9, 2, 2, 2, 43, 45, 5, 8, 5, 2, 44, 43, 3, 2, 2, 2, 44, 45, 3, 2, 2, 2, 45, 5, 3, 2, 2, 2, 46, 49, 5, 4, 3, 2, 47, 49, 5, 34, 18, 2, 48, 46, 3, 2, 2, 2, 48, 47, 3, 2, 2, 2, 49, 7, 3, 2, 2, 2, 50, 53, 7, 4, 2, 2, 51, 54, 5, 12, 7, 2, 52, 54, 5, 10, 6, 2, 53, 51, 3, 2, 2, 2, 53, 52, 3, 2, 2, 2, 54, 56, 3, 2, 2, 2, 55, 57, 5, 8, 5, 2, 56, 55, 3, 2, 2, 2, 56, 57, 3, 2, 2, 2, 57, 68, 3, 2, 2, 2, 58, 59, 7, 6, 2, 2, 59, 61, 5, 12, 7, 2, 60, 62, 5, 8, 5, 2, 61, 60, 3, 2, 2, 2, 61, 62, 3, 2, 2, 2, 62, 68, 3, 2, 2, 2, 63, 65, 5, 10, 6, 2, 64, 66, 5, 8, 5, 2, 65, 64, 3, 2, 2, 2, 65, 66, 3, 2, 2, 2, 66, 68, 3, 2, 2, 2, 67, 50, 3, 2, 2, 2, 67, 58, 3, 2, 2, 2, 67, 63, 3, 2, 2, 2, 68, 9, 3, 2, 2, 2, 69, 70, 7, 22, 2, 2, 70, 75, 5, 14, 8, 2, 71, 72, 7, 25, 2, 2, 72, 74, 5, 14, 8, 2, 73, 71, 3, 2, 2, 2, 74, 77, 3, 2, 2, 2, 75, 73, 3, 2, 2, 2, 75, 76, 3, 2, 2, 2, 76, 78, 3, 2, 2, 2, 77, 75, 3, 2, 2, 2, 78, 79, 7, 23, 2, 2, 79, 11, 3, 2, 2, 2, 80, 81, 9, 3, 2, 2, 81, 13, 3, 2, 2, 2, 82, 97, 7, 30, 2, 2, 83, 84, 7, 31, 2, 2, 84, 86, 6, 8, 2, 2, 85, 87, 5, 16, 9, 2, 86, 85, 3, 2, 2, 2, 86, 87, 3, 2, 2, 2, 87, 97, 3, 2, 2, 2, 88, 97, 5, 16, 9, 2, 89, 97, 7, 7, 2, 2, 90, 91, 7, 28, 2, 2, 91, 92, 7, 26, 2, 2, 92, 93, 5, 18, 10, 2, 93, 94, 7, 27, 2, 2, 94, 97, 3, 2, 2, 2, 95, 97, 5, 4, 3, 2, 96, 82, 3, 2, 2, 2, 96, 83, 3, 2, 2, 2, 96, 88, 3, 2, 2, 2, 96, 89, 3, 2, 2, 2, 96, 90, 3, 2, 2, 2, 96, 95, 3, 2, 2, 2, 97, 15, 3, 2, 2, 2, 98, 101, 7, 24, 2, 2, 99, 100, 7, 31, 2, 2, 100, 102, 6, 9, 3, 2, 101, 99, 3, 2, 2, 2, 101, 102, 3, 2, 2, 2, 102, 106, 3, 2, 2, 2, 103, 104, 7, 24, 2, 2, 104, 105, 7, 31, 2, 2, 105, 107, 6, 9, 4, 2, 106, 103, 3, 2, 2, 2, 106, 107, 3, 2, 2, 2, 107, 17, 3, 2, 2, 2, 108, 109, 5, 20, 11, 2, 109, 19, 3, 2, 2, 2, 110, 113, 5, 22, 12, 2, 111, 112, 7, 8, 2, 2, 112, 114, 5, 20, 11, 2, 113, 111, 3, 2, 2, 2, 113, 114, 3, 2, 2, 2, 114, 21, 3, 2, 2, 2, 115, 118, 5, 24, 13, 2, 116, 117, 7, 16, 2, 2, 117, 119, 5, 22, 12, 2, 118, 116, 3, 2, 2, 2, 118, 119, 3, 2, 2, 2, 119, 23, 3, 2, 2, 2, 120, 121, 7, 15, 2, 2, 121, 132, 5, 24, 13, 2, 122, 123, 7, 26, 2, 2, 123, 124, 5, 18, 10, 2, 124, 125, 7, 27, 2, 2, 125, 132, 3, 2, 2, 2, 126, 129, 5, 6, 4, 2, 127, 128, 9, 4, 2, 2, 128, 130, 5, 6, 4, 2, 129, 127, 3, 2, 2, 2, 129, 130, 3, 2, 2, 2, 130, 132, 3, 2, 2, 2, 131, 120, 3, 2, 2, 2, 131, 122, 3, 2, 2, 2, 131, 126, 3, 2, 2, 2, 132, 25, 3, 2, 2, 2, 133, 134, 5, 34, 18, 2, 134, 27, 3, 2, 2, 2, 135, 136, 7, 20, 2, 2, 136, 141, 5, 30, 16, 2, 137, 138, 7, 25, 2, 2, 138, 140, 5, 30, 16, 2, 139, 137, 3, 2, 2, 2, 140, 143, 3, 2, 2, 2, 141, 139, 3, 2, 2, 2, 141, 142, 3, 2, 2, 2, 142, 144, 3, 2, 2, 2, 143, 141, 3, 2, 2, 2, 144, 145, 7, 21, 2, 2, 145, 149, 3, 2, 2, 2, 146, 147, 7, 20, 2, 2, 147, 149, 7, 21, 2, 2, 148, 135, 3, 2, 2, 2, 148, 146, 3, 2, 2, 2, 149, 29, 3, 2, 2, 2, 150, 151, 7, 30, 2, 2, 151, 152, 7, 24, 2, 2, 152, 153, 5, 34, 18, 2, 153, 31, 3, 2, 2, 2, 154, 155, 7, 22, 2, 2, 155, 160, 5, 34, 18, 2, 156, 157, 7, 25, 2, 2, 157, 159, 5, 34, 18, 2, 158, 156, 3, 2, 2, 2, 159, 162, 3, 2, 2, 2, 160, 158, 3, 2, 2, 2, 160, 161, 3, 2, 2, 2, 161, 163, 3, 2, 2, 2, 162, 160, 3, 2, 2, 2, 163, 164, 7, 23, 2, 2, 164, 168, 3, 2, 2, 2, 165, 166, 7, 22, 2, 2, 166, 168, 7, 23, 2, 2, 167, 154, 3, 2, 2, 2, 167, 165, 3, 2, 2, 2, 168, 33, 3, 2, 2, 2, 169, 177, 7, 30, 2, 2, 170, 177, 7, 31, 2, 2, 171, 177, 5, 28, 15, 2, 172, 177, 5, 32, 17, 2, 173, 177, 7, 17, 2, 2, 174, 177, 7, 18, 2, 2, 175, 177, 7, 19, 2, 2, 176, 169, 3, 2, 2, 2, 176, 170, 3, 2, 2, 2, 176, 171, 3, 2, 2, 2, 176, 172, 3, 2, 2, 2, 176, 173, 3, 2, 2, 2, 176, 174, 3, 2, 2, 2, 176, 175, 3, 2, 2, 2, 177, 35, 3, 2, 2, 2, 24, 38, 44, 48, 53, 56, 61, 65, 67, 75, 86, 96, 101, 106, 113, 118, 129, 131, 141, 148, 160, 167, 176] \ No newline at end of file diff --git a/jsonpath2/parser/JSONPathListener.py b/jsonpath2/parser/JSONPathListener.py index 1795d21..6045a59 100644 --- a/jsonpath2/parser/JSONPathListener.py +++ b/jsonpath2/parser/JSONPathListener.py @@ -17,6 +17,24 @@ def exitJsonpath(self, ctx:JSONPathParser.JsonpathContext): pass + # Enter a parse tree produced by JSONPathParser#jsonpath_. + def enterJsonpath_(self, ctx:JSONPathParser.Jsonpath_Context): + pass + + # Exit a parse tree produced by JSONPathParser#jsonpath_. + def exitJsonpath_(self, ctx:JSONPathParser.Jsonpath_Context): + pass + + + # Enter a parse tree produced by JSONPathParser#jsonpath__. + def enterJsonpath__(self, ctx:JSONPathParser.Jsonpath__Context): + pass + + # Exit a parse tree produced by JSONPathParser#jsonpath__. + def exitJsonpath__(self, ctx:JSONPathParser.Jsonpath__Context): + pass + + # Enter a parse tree produced by JSONPathParser#subscript. def enterSubscript(self, ctx:JSONPathParser.SubscriptContext): pass diff --git a/jsonpath2/parser/JSONPathParser.py b/jsonpath2/parser/JSONPathParser.py index e8c9163..61b34c8 100644 --- a/jsonpath2/parser/JSONPathParser.py +++ b/jsonpath2/parser/JSONPathParser.py @@ -8,70 +8,76 @@ def serializedATN(): with StringIO() as buf: buf.write("\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\3 ") - buf.write("\u00a9\4\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7") + buf.write("\u00b3\4\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7") buf.write("\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t\13\4\f\t\f\4\r\t\r\4\16") - buf.write("\t\16\4\17\t\17\4\20\t\20\3\2\3\2\5\2#\n\2\3\2\3\2\3\3") - buf.write("\3\3\3\3\5\3*\n\3\3\3\5\3-\n\3\3\3\3\3\3\3\5\3\62\n\3") - buf.write("\3\3\3\3\5\3\66\n\3\5\38\n\3\3\4\3\4\3\4\3\4\7\4>\n\4") - buf.write("\f\4\16\4A\13\4\3\4\3\4\3\5\3\5\3\6\3\6\3\6\3\6\5\6K\n") - buf.write("\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\5\6T\n\6\3\7\3\7\3\7\5") - buf.write("\7Y\n\7\3\7\3\7\3\7\5\7^\n\7\3\b\3\b\3\t\3\t\3\t\5\te") - buf.write("\n\t\3\n\3\n\3\n\5\nj\n\n\3\13\3\13\3\13\3\13\3\13\3\13") - buf.write("\3\13\3\13\5\13t\n\13\3\13\3\13\5\13x\n\13\5\13z\n\13") - buf.write("\3\f\3\f\3\r\3\r\3\r\3\r\7\r\u0082\n\r\f\r\16\r\u0085") - buf.write("\13\r\3\r\3\r\3\r\3\r\5\r\u008b\n\r\3\16\3\16\3\16\3\16") - buf.write("\3\17\3\17\3\17\3\17\7\17\u0095\n\17\f\17\16\17\u0098") - buf.write("\13\17\3\17\3\17\3\17\3\17\5\17\u009e\n\17\3\20\3\20\3") - buf.write("\20\3\20\3\20\3\20\3\20\5\20\u00a7\n\20\3\20\2\2\21\2") - buf.write("\4\6\b\n\f\16\20\22\24\26\30\32\34\36\2\5\4\2\7\7\35\35") - buf.write("\4\2\3\3\5\5\3\2\t\16\2\u00b8\2 \3\2\2\2\4\67\3\2\2\2") - buf.write("\69\3\2\2\2\bD\3\2\2\2\nS\3\2\2\2\fU\3\2\2\2\16_\3\2\2") - buf.write("\2\20a\3\2\2\2\22f\3\2\2\2\24y\3\2\2\2\26{\3\2\2\2\30") - buf.write("\u008a\3\2\2\2\32\u008c\3\2\2\2\34\u009d\3\2\2\2\36\u00a6") - buf.write("\3\2\2\2 \"\7\5\2\2!#\5\4\3\2\"!\3\2\2\2\"#\3\2\2\2#$") - buf.write("\3\2\2\2$%\7\2\2\3%\3\3\2\2\2&)\7\4\2\2\'*\5\b\5\2(*\5") - buf.write("\6\4\2)\'\3\2\2\2)(\3\2\2\2*,\3\2\2\2+-\5\4\3\2,+\3\2") - buf.write("\2\2,-\3\2\2\2-8\3\2\2\2./\7\6\2\2/\61\5\b\5\2\60\62\5") - buf.write("\4\3\2\61\60\3\2\2\2\61\62\3\2\2\2\628\3\2\2\2\63\65\5") - buf.write("\6\4\2\64\66\5\4\3\2\65\64\3\2\2\2\65\66\3\2\2\2\668\3") - buf.write("\2\2\2\67&\3\2\2\2\67.\3\2\2\2\67\63\3\2\2\28\5\3\2\2") - buf.write("\29:\7\26\2\2:?\5\n\6\2;<\7\31\2\2<>\5\n\6\2=;\3\2\2\2") - buf.write(">A\3\2\2\2?=\3\2\2\2?@\3\2\2\2@B\3\2\2\2A?\3\2\2\2BC\7") - buf.write("\27\2\2C\7\3\2\2\2DE\t\2\2\2E\t\3\2\2\2FT\7\36\2\2GH\7") - buf.write("\37\2\2HJ\6\6\2\2IK\5\f\7\2JI\3\2\2\2JK\3\2\2\2KT\3\2") - buf.write("\2\2LT\5\f\7\2MT\7\7\2\2NO\7\34\2\2OP\7\32\2\2PQ\5\16") - buf.write("\b\2QR\7\33\2\2RT\3\2\2\2SF\3\2\2\2SG\3\2\2\2SL\3\2\2") - buf.write("\2SM\3\2\2\2SN\3\2\2\2T\13\3\2\2\2UX\7\30\2\2VW\7\37\2") - buf.write("\2WY\6\7\3\2XV\3\2\2\2XY\3\2\2\2Y]\3\2\2\2Z[\7\30\2\2") - buf.write("[\\\7\37\2\2\\^\6\7\4\2]Z\3\2\2\2]^\3\2\2\2^\r\3\2\2\2") - buf.write("_`\5\20\t\2`\17\3\2\2\2ad\5\22\n\2bc\7\b\2\2ce\5\20\t") - buf.write("\2db\3\2\2\2de\3\2\2\2e\21\3\2\2\2fi\5\24\13\2gh\7\20") - buf.write("\2\2hj\5\22\n\2ig\3\2\2\2ij\3\2\2\2j\23\3\2\2\2kl\7\17") - buf.write("\2\2lz\5\24\13\2mn\7\32\2\2no\5\16\b\2op\7\33\2\2pz\3") - buf.write("\2\2\2qs\t\3\2\2rt\5\4\3\2sr\3\2\2\2st\3\2\2\2tw\3\2\2") - buf.write("\2uv\t\4\2\2vx\5\36\20\2wu\3\2\2\2wx\3\2\2\2xz\3\2\2\2") - buf.write("yk\3\2\2\2ym\3\2\2\2yq\3\2\2\2z\25\3\2\2\2{|\5\36\20\2") - buf.write("|\27\3\2\2\2}~\7\24\2\2~\u0083\5\32\16\2\177\u0080\7\31") - buf.write("\2\2\u0080\u0082\5\32\16\2\u0081\177\3\2\2\2\u0082\u0085") - buf.write("\3\2\2\2\u0083\u0081\3\2\2\2\u0083\u0084\3\2\2\2\u0084") - buf.write("\u0086\3\2\2\2\u0085\u0083\3\2\2\2\u0086\u0087\7\25\2") - buf.write("\2\u0087\u008b\3\2\2\2\u0088\u0089\7\24\2\2\u0089\u008b") - buf.write("\7\25\2\2\u008a}\3\2\2\2\u008a\u0088\3\2\2\2\u008b\31") - buf.write("\3\2\2\2\u008c\u008d\7\36\2\2\u008d\u008e\7\30\2\2\u008e") - buf.write("\u008f\5\36\20\2\u008f\33\3\2\2\2\u0090\u0091\7\26\2\2") - buf.write("\u0091\u0096\5\36\20\2\u0092\u0093\7\31\2\2\u0093\u0095") - buf.write("\5\36\20\2\u0094\u0092\3\2\2\2\u0095\u0098\3\2\2\2\u0096") - buf.write("\u0094\3\2\2\2\u0096\u0097\3\2\2\2\u0097\u0099\3\2\2\2") - buf.write("\u0098\u0096\3\2\2\2\u0099\u009a\7\27\2\2\u009a\u009e") - buf.write("\3\2\2\2\u009b\u009c\7\26\2\2\u009c\u009e\7\27\2\2\u009d") - buf.write("\u0090\3\2\2\2\u009d\u009b\3\2\2\2\u009e\35\3\2\2\2\u009f") - buf.write("\u00a7\7\36\2\2\u00a0\u00a7\7\37\2\2\u00a1\u00a7\5\30") - buf.write("\r\2\u00a2\u00a7\5\34\17\2\u00a3\u00a7\7\21\2\2\u00a4") - buf.write("\u00a7\7\22\2\2\u00a5\u00a7\7\23\2\2\u00a6\u009f\3\2\2") - buf.write("\2\u00a6\u00a0\3\2\2\2\u00a6\u00a1\3\2\2\2\u00a6\u00a2") - buf.write("\3\2\2\2\u00a6\u00a3\3\2\2\2\u00a6\u00a4\3\2\2\2\u00a6") - buf.write("\u00a5\3\2\2\2\u00a7\37\3\2\2\2\27\"),\61\65\67?JSX]d") - buf.write("iswy\u0083\u008a\u0096\u009d\u00a6") + buf.write("\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22\3\2\3\2") + buf.write("\5\2\'\n\2\3\2\3\2\3\3\3\3\5\3-\n\3\3\4\3\4\5\4\61\n\4") + buf.write("\3\5\3\5\3\5\5\5\66\n\5\3\5\5\59\n\5\3\5\3\5\3\5\5\5>") + buf.write("\n\5\3\5\3\5\5\5B\n\5\5\5D\n\5\3\6\3\6\3\6\3\6\7\6J\n") + buf.write("\6\f\6\16\6M\13\6\3\6\3\6\3\7\3\7\3\b\3\b\3\b\3\b\5\b") + buf.write("W\n\b\3\b\3\b\3\b\3\b\3\b\3\b\3\b\3\b\5\ba\n\b\3\t\3\t") + buf.write("\3\t\5\tf\n\t\3\t\3\t\3\t\5\tk\n\t\3\n\3\n\3\13\3\13\3") + buf.write("\13\5\13r\n\13\3\f\3\f\3\f\5\fw\n\f\3\r\3\r\3\r\3\r\3") + buf.write("\r\3\r\3\r\3\r\3\r\5\r\u0082\n\r\5\r\u0084\n\r\3\16\3") + buf.write("\16\3\17\3\17\3\17\3\17\7\17\u008c\n\17\f\17\16\17\u008f") + buf.write("\13\17\3\17\3\17\3\17\3\17\5\17\u0095\n\17\3\20\3\20\3") + buf.write("\20\3\20\3\21\3\21\3\21\3\21\7\21\u009f\n\21\f\21\16\21") + buf.write("\u00a2\13\21\3\21\3\21\3\21\3\21\5\21\u00a8\n\21\3\22") + buf.write("\3\22\3\22\3\22\3\22\3\22\3\22\5\22\u00b1\n\22\3\22\2") + buf.write("\2\23\2\4\6\b\n\f\16\20\22\24\26\30\32\34\36 \"\2\5\4") + buf.write("\2\3\3\5\5\4\2\7\7\35\35\3\2\t\16\2\u00c2\2$\3\2\2\2\4") + buf.write("*\3\2\2\2\6\60\3\2\2\2\bC\3\2\2\2\nE\3\2\2\2\fP\3\2\2") + buf.write("\2\16`\3\2\2\2\20b\3\2\2\2\22l\3\2\2\2\24n\3\2\2\2\26") + buf.write("s\3\2\2\2\30\u0083\3\2\2\2\32\u0085\3\2\2\2\34\u0094\3") + buf.write("\2\2\2\36\u0096\3\2\2\2 \u00a7\3\2\2\2\"\u00b0\3\2\2\2") + buf.write("$&\7\5\2\2%\'\5\b\5\2&%\3\2\2\2&\'\3\2\2\2\'(\3\2\2\2") + buf.write("()\7\2\2\3)\3\3\2\2\2*,\t\2\2\2+-\5\b\5\2,+\3\2\2\2,-") + buf.write("\3\2\2\2-\5\3\2\2\2.\61\5\4\3\2/\61\5\"\22\2\60.\3\2\2") + buf.write("\2\60/\3\2\2\2\61\7\3\2\2\2\62\65\7\4\2\2\63\66\5\f\7") + buf.write("\2\64\66\5\n\6\2\65\63\3\2\2\2\65\64\3\2\2\2\668\3\2\2") + buf.write("\2\679\5\b\5\28\67\3\2\2\289\3\2\2\29D\3\2\2\2:;\7\6\2") + buf.write("\2;=\5\f\7\2<>\5\b\5\2=<\3\2\2\2=>\3\2\2\2>D\3\2\2\2?") + buf.write("A\5\n\6\2@B\5\b\5\2A@\3\2\2\2AB\3\2\2\2BD\3\2\2\2C\62") + buf.write("\3\2\2\2C:\3\2\2\2C?\3\2\2\2D\t\3\2\2\2EF\7\26\2\2FK\5") + buf.write("\16\b\2GH\7\31\2\2HJ\5\16\b\2IG\3\2\2\2JM\3\2\2\2KI\3") + buf.write("\2\2\2KL\3\2\2\2LN\3\2\2\2MK\3\2\2\2NO\7\27\2\2O\13\3") + buf.write("\2\2\2PQ\t\3\2\2Q\r\3\2\2\2Ra\7\36\2\2ST\7\37\2\2TV\6") + buf.write("\b\2\2UW\5\20\t\2VU\3\2\2\2VW\3\2\2\2Wa\3\2\2\2Xa\5\20") + buf.write("\t\2Ya\7\7\2\2Z[\7\34\2\2[\\\7\32\2\2\\]\5\22\n\2]^\7") + buf.write("\33\2\2^a\3\2\2\2_a\5\4\3\2`R\3\2\2\2`S\3\2\2\2`X\3\2") + buf.write("\2\2`Y\3\2\2\2`Z\3\2\2\2`_\3\2\2\2a\17\3\2\2\2be\7\30") + buf.write("\2\2cd\7\37\2\2df\6\t\3\2ec\3\2\2\2ef\3\2\2\2fj\3\2\2") + buf.write("\2gh\7\30\2\2hi\7\37\2\2ik\6\t\4\2jg\3\2\2\2jk\3\2\2\2") + buf.write("k\21\3\2\2\2lm\5\24\13\2m\23\3\2\2\2nq\5\26\f\2op\7\b") + buf.write("\2\2pr\5\24\13\2qo\3\2\2\2qr\3\2\2\2r\25\3\2\2\2sv\5\30") + buf.write("\r\2tu\7\20\2\2uw\5\26\f\2vt\3\2\2\2vw\3\2\2\2w\27\3\2") + buf.write("\2\2xy\7\17\2\2y\u0084\5\30\r\2z{\7\32\2\2{|\5\22\n\2") + buf.write("|}\7\33\2\2}\u0084\3\2\2\2~\u0081\5\6\4\2\177\u0080\t") + buf.write("\4\2\2\u0080\u0082\5\6\4\2\u0081\177\3\2\2\2\u0081\u0082") + buf.write("\3\2\2\2\u0082\u0084\3\2\2\2\u0083x\3\2\2\2\u0083z\3\2") + buf.write("\2\2\u0083~\3\2\2\2\u0084\31\3\2\2\2\u0085\u0086\5\"\22") + buf.write("\2\u0086\33\3\2\2\2\u0087\u0088\7\24\2\2\u0088\u008d\5") + buf.write("\36\20\2\u0089\u008a\7\31\2\2\u008a\u008c\5\36\20\2\u008b") + buf.write("\u0089\3\2\2\2\u008c\u008f\3\2\2\2\u008d\u008b\3\2\2\2") + buf.write("\u008d\u008e\3\2\2\2\u008e\u0090\3\2\2\2\u008f\u008d\3") + buf.write("\2\2\2\u0090\u0091\7\25\2\2\u0091\u0095\3\2\2\2\u0092") + buf.write("\u0093\7\24\2\2\u0093\u0095\7\25\2\2\u0094\u0087\3\2\2") + buf.write("\2\u0094\u0092\3\2\2\2\u0095\35\3\2\2\2\u0096\u0097\7") + buf.write("\36\2\2\u0097\u0098\7\30\2\2\u0098\u0099\5\"\22\2\u0099") + buf.write("\37\3\2\2\2\u009a\u009b\7\26\2\2\u009b\u00a0\5\"\22\2") + buf.write("\u009c\u009d\7\31\2\2\u009d\u009f\5\"\22\2\u009e\u009c") + buf.write("\3\2\2\2\u009f\u00a2\3\2\2\2\u00a0\u009e\3\2\2\2\u00a0") + buf.write("\u00a1\3\2\2\2\u00a1\u00a3\3\2\2\2\u00a2\u00a0\3\2\2\2") + buf.write("\u00a3\u00a4\7\27\2\2\u00a4\u00a8\3\2\2\2\u00a5\u00a6") + buf.write("\7\26\2\2\u00a6\u00a8\7\27\2\2\u00a7\u009a\3\2\2\2\u00a7") + buf.write("\u00a5\3\2\2\2\u00a8!\3\2\2\2\u00a9\u00b1\7\36\2\2\u00aa") + buf.write("\u00b1\7\37\2\2\u00ab\u00b1\5\34\17\2\u00ac\u00b1\5 \21") + buf.write("\2\u00ad\u00b1\7\21\2\2\u00ae\u00b1\7\22\2\2\u00af\u00b1") + buf.write("\7\23\2\2\u00b0\u00a9\3\2\2\2\u00b0\u00aa\3\2\2\2\u00b0") + buf.write("\u00ab\3\2\2\2\u00b0\u00ac\3\2\2\2\u00b0\u00ad\3\2\2\2") + buf.write("\u00b0\u00ae\3\2\2\2\u00b0\u00af\3\2\2\2\u00b1#\3\2\2") + buf.write("\2\30&,\60\658=ACKV`ejqv\u0081\u0083\u008d\u0094\u00a0") + buf.write("\u00a7\u00b0") return buf.getvalue() @@ -98,25 +104,27 @@ class JSONPathParser ( Parser ): "QUESTION", "ID", "STRING", "NUMBER", "WS" ] RULE_jsonpath = 0 - RULE_subscript = 1 - RULE_subscriptables = 2 - RULE_subscriptableBareword = 3 - RULE_subscriptable = 4 - RULE_sliceable = 5 - RULE_expression = 6 - RULE_andExpression = 7 - RULE_orExpression = 8 - RULE_notExpression = 9 - RULE_json = 10 - RULE_obj = 11 - RULE_pair = 12 - RULE_array = 13 - RULE_value = 14 - - ruleNames = [ "jsonpath", "subscript", "subscriptables", "subscriptableBareword", - "subscriptable", "sliceable", "expression", "andExpression", - "orExpression", "notExpression", "json", "obj", "pair", - "array", "value" ] + RULE_jsonpath_ = 1 + RULE_jsonpath__ = 2 + RULE_subscript = 3 + RULE_subscriptables = 4 + RULE_subscriptableBareword = 5 + RULE_subscriptable = 6 + RULE_sliceable = 7 + RULE_expression = 8 + RULE_andExpression = 9 + RULE_orExpression = 10 + RULE_notExpression = 11 + RULE_json = 12 + RULE_obj = 13 + RULE_pair = 14 + RULE_array = 15 + RULE_value = 16 + + ruleNames = [ "jsonpath", "jsonpath_", "jsonpath__", "subscript", "subscriptables", + "subscriptableBareword", "subscriptable", "sliceable", + "expression", "andExpression", "orExpression", "notExpression", + "json", "obj", "pair", "array", "value" ] EOF = Token.EOF CURRENT_VALUE=1 @@ -195,17 +203,17 @@ def jsonpath(self): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 30 + self.state = 34 self.match(JSONPathParser.ROOT_VALUE) - self.state = 32 + self.state = 36 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << JSONPathParser.RECURSIVE_DESCENT) | (1 << JSONPathParser.SUBSCRIPT) | (1 << JSONPathParser.BRACKET_LEFT))) != 0): - self.state = 31 + self.state = 35 self.subscript() - self.state = 34 + self.state = 38 self.match(JSONPathParser.EOF) except RecognitionException as re: localctx.exception = re @@ -215,6 +223,123 @@ def jsonpath(self): self.exitRule() return localctx + class Jsonpath_Context(ParserRuleContext): + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def ROOT_VALUE(self): + return self.getToken(JSONPathParser.ROOT_VALUE, 0) + + def CURRENT_VALUE(self): + return self.getToken(JSONPathParser.CURRENT_VALUE, 0) + + def subscript(self): + return self.getTypedRuleContext(JSONPathParser.SubscriptContext,0) + + + def getRuleIndex(self): + return JSONPathParser.RULE_jsonpath_ + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterJsonpath_" ): + listener.enterJsonpath_(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitJsonpath_" ): + listener.exitJsonpath_(self) + + + + + def jsonpath_(self): + + localctx = JSONPathParser.Jsonpath_Context(self, self._ctx, self.state) + self.enterRule(localctx, 2, self.RULE_jsonpath_) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 40 + _la = self._input.LA(1) + if not(_la==JSONPathParser.CURRENT_VALUE or _la==JSONPathParser.ROOT_VALUE): + self._errHandler.recoverInline(self) + else: + self._errHandler.reportMatch(self) + self.consume() + self.state = 42 + self._errHandler.sync(self) + _la = self._input.LA(1) + if (((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << JSONPathParser.RECURSIVE_DESCENT) | (1 << JSONPathParser.SUBSCRIPT) | (1 << JSONPathParser.BRACKET_LEFT))) != 0): + self.state = 41 + self.subscript() + + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class Jsonpath__Context(ParserRuleContext): + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def jsonpath_(self): + return self.getTypedRuleContext(JSONPathParser.Jsonpath_Context,0) + + + def value(self): + return self.getTypedRuleContext(JSONPathParser.ValueContext,0) + + + def getRuleIndex(self): + return JSONPathParser.RULE_jsonpath__ + + def enterRule(self, listener:ParseTreeListener): + if hasattr( listener, "enterJsonpath__" ): + listener.enterJsonpath__(self) + + def exitRule(self, listener:ParseTreeListener): + if hasattr( listener, "exitJsonpath__" ): + listener.exitJsonpath__(self) + + + + + def jsonpath__(self): + + localctx = JSONPathParser.Jsonpath__Context(self, self._ctx, self.state) + self.enterRule(localctx, 4, self.RULE_jsonpath__) + try: + self.state = 46 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [JSONPathParser.CURRENT_VALUE, JSONPathParser.ROOT_VALUE]: + self.enterOuterAlt(localctx, 1) + self.state = 44 + self.jsonpath_() + pass + elif token in [JSONPathParser.TRUE, JSONPathParser.FALSE, JSONPathParser.NULL, JSONPathParser.BRACE_LEFT, JSONPathParser.BRACKET_LEFT, JSONPathParser.STRING, JSONPathParser.NUMBER]: + self.enterOuterAlt(localctx, 2) + self.state = 45 + self.value() + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + class SubscriptContext(ParserRuleContext): def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): @@ -256,63 +381,63 @@ def exitRule(self, listener:ParseTreeListener): def subscript(self): localctx = JSONPathParser.SubscriptContext(self, self._ctx, self.state) - self.enterRule(localctx, 2, self.RULE_subscript) + self.enterRule(localctx, 6, self.RULE_subscript) self._la = 0 # Token type try: - self.state = 53 + self.state = 65 self._errHandler.sync(self) token = self._input.LA(1) if token in [JSONPathParser.RECURSIVE_DESCENT]: self.enterOuterAlt(localctx, 1) - self.state = 36 + self.state = 48 self.match(JSONPathParser.RECURSIVE_DESCENT) - self.state = 39 + self.state = 51 self._errHandler.sync(self) token = self._input.LA(1) if token in [JSONPathParser.WILDCARD_SUBSCRIPT, JSONPathParser.ID]: - self.state = 37 + self.state = 49 self.subscriptableBareword() pass elif token in [JSONPathParser.BRACKET_LEFT]: - self.state = 38 + self.state = 50 self.subscriptables() pass else: raise NoViableAltException(self) - self.state = 42 + self.state = 54 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << JSONPathParser.RECURSIVE_DESCENT) | (1 << JSONPathParser.SUBSCRIPT) | (1 << JSONPathParser.BRACKET_LEFT))) != 0): - self.state = 41 + self.state = 53 self.subscript() pass elif token in [JSONPathParser.SUBSCRIPT]: self.enterOuterAlt(localctx, 2) - self.state = 44 + self.state = 56 self.match(JSONPathParser.SUBSCRIPT) - self.state = 45 + self.state = 57 self.subscriptableBareword() - self.state = 47 + self.state = 59 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << JSONPathParser.RECURSIVE_DESCENT) | (1 << JSONPathParser.SUBSCRIPT) | (1 << JSONPathParser.BRACKET_LEFT))) != 0): - self.state = 46 + self.state = 58 self.subscript() pass elif token in [JSONPathParser.BRACKET_LEFT]: self.enterOuterAlt(localctx, 3) - self.state = 49 + self.state = 61 self.subscriptables() - self.state = 51 + self.state = 63 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << JSONPathParser.RECURSIVE_DESCENT) | (1 << JSONPathParser.SUBSCRIPT) | (1 << JSONPathParser.BRACKET_LEFT))) != 0): - self.state = 50 + self.state = 62 self.subscript() @@ -370,27 +495,27 @@ def exitRule(self, listener:ParseTreeListener): def subscriptables(self): localctx = JSONPathParser.SubscriptablesContext(self, self._ctx, self.state) - self.enterRule(localctx, 4, self.RULE_subscriptables) + self.enterRule(localctx, 8, self.RULE_subscriptables) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 55 + self.state = 67 self.match(JSONPathParser.BRACKET_LEFT) - self.state = 56 + self.state = 68 self.subscriptable() - self.state = 61 + self.state = 73 self._errHandler.sync(self) _la = self._input.LA(1) while _la==JSONPathParser.COMMA: - self.state = 57 + self.state = 69 self.match(JSONPathParser.COMMA) - self.state = 58 + self.state = 70 self.subscriptable() - self.state = 63 + self.state = 75 self._errHandler.sync(self) _la = self._input.LA(1) - self.state = 64 + self.state = 76 self.match(JSONPathParser.BRACKET_RIGHT) except RecognitionException as re: localctx.exception = re @@ -429,11 +554,11 @@ def exitRule(self, listener:ParseTreeListener): def subscriptableBareword(self): localctx = JSONPathParser.SubscriptableBarewordContext(self, self._ctx, self.state) - self.enterRule(localctx, 6, self.RULE_subscriptableBareword) + self.enterRule(localctx, 10, self.RULE_subscriptableBareword) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 66 + self.state = 78 _la = self._input.LA(1) if not(_la==JSONPathParser.WILDCARD_SUBSCRIPT or _la==JSONPathParser.ID): self._errHandler.recoverInline(self) @@ -480,6 +605,10 @@ def expression(self): def PAREN_RIGHT(self): return self.getToken(JSONPathParser.PAREN_RIGHT, 0) + def jsonpath_(self): + return self.getTypedRuleContext(JSONPathParser.Jsonpath_Context,0) + + def getRuleIndex(self): return JSONPathParser.RULE_subscriptable @@ -497,55 +626,60 @@ def exitRule(self, listener:ParseTreeListener): def subscriptable(self): localctx = JSONPathParser.SubscriptableContext(self, self._ctx, self.state) - self.enterRule(localctx, 8, self.RULE_subscriptable) + self.enterRule(localctx, 12, self.RULE_subscriptable) self._la = 0 # Token type try: - self.state = 81 + self.state = 94 self._errHandler.sync(self) token = self._input.LA(1) if token in [JSONPathParser.STRING]: self.enterOuterAlt(localctx, 1) - self.state = 68 + self.state = 80 self.match(JSONPathParser.STRING) pass elif token in [JSONPathParser.NUMBER]: self.enterOuterAlt(localctx, 2) - self.state = 69 + self.state = 81 self.match(JSONPathParser.NUMBER) - self.state = 70 + self.state = 82 if not self.tryCast(int): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.tryCast(int)") - self.state = 72 + self.state = 84 self._errHandler.sync(self) _la = self._input.LA(1) if _la==JSONPathParser.COLON: - self.state = 71 + self.state = 83 self.sliceable() pass elif token in [JSONPathParser.COLON]: self.enterOuterAlt(localctx, 3) - self.state = 74 + self.state = 86 self.sliceable() pass elif token in [JSONPathParser.WILDCARD_SUBSCRIPT]: self.enterOuterAlt(localctx, 4) - self.state = 75 + self.state = 87 self.match(JSONPathParser.WILDCARD_SUBSCRIPT) pass elif token in [JSONPathParser.QUESTION]: self.enterOuterAlt(localctx, 5) - self.state = 76 + self.state = 88 self.match(JSONPathParser.QUESTION) - self.state = 77 + self.state = 89 self.match(JSONPathParser.PAREN_LEFT) - self.state = 78 + self.state = 90 self.expression() - self.state = 79 + self.state = 91 self.match(JSONPathParser.PAREN_RIGHT) pass + elif token in [JSONPathParser.CURRENT_VALUE, JSONPathParser.ROOT_VALUE]: + self.enterOuterAlt(localctx, 6) + self.state = 93 + self.jsonpath_() + pass else: raise NoViableAltException(self) @@ -592,33 +726,33 @@ def exitRule(self, listener:ParseTreeListener): def sliceable(self): localctx = JSONPathParser.SliceableContext(self, self._ctx, self.state) - self.enterRule(localctx, 10, self.RULE_sliceable) + self.enterRule(localctx, 14, self.RULE_sliceable) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 83 + self.state = 96 self.match(JSONPathParser.COLON) - self.state = 86 + self.state = 99 self._errHandler.sync(self) _la = self._input.LA(1) if _la==JSONPathParser.NUMBER: - self.state = 84 + self.state = 97 self.match(JSONPathParser.NUMBER) - self.state = 85 + self.state = 98 if not self.tryCast(int): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.tryCast(int)") - self.state = 91 + self.state = 104 self._errHandler.sync(self) _la = self._input.LA(1) if _la==JSONPathParser.COLON: - self.state = 88 + self.state = 101 self.match(JSONPathParser.COLON) - self.state = 89 + self.state = 102 self.match(JSONPathParser.NUMBER) - self.state = 90 + self.state = 103 if not self.tryCast(int): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.tryCast(int)") @@ -659,10 +793,10 @@ def exitRule(self, listener:ParseTreeListener): def expression(self): localctx = JSONPathParser.ExpressionContext(self, self._ctx, self.state) - self.enterRule(localctx, 12, self.RULE_expression) + self.enterRule(localctx, 16, self.RULE_expression) try: self.enterOuterAlt(localctx, 1) - self.state = 93 + self.state = 106 self.andExpression() except RecognitionException as re: localctx.exception = re @@ -706,19 +840,19 @@ def exitRule(self, listener:ParseTreeListener): def andExpression(self): localctx = JSONPathParser.AndExpressionContext(self, self._ctx, self.state) - self.enterRule(localctx, 14, self.RULE_andExpression) + self.enterRule(localctx, 18, self.RULE_andExpression) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 95 + self.state = 108 self.orExpression() - self.state = 98 + self.state = 111 self._errHandler.sync(self) _la = self._input.LA(1) if _la==JSONPathParser.AND: - self.state = 96 + self.state = 109 self.match(JSONPathParser.AND) - self.state = 97 + self.state = 110 self.andExpression() @@ -764,19 +898,19 @@ def exitRule(self, listener:ParseTreeListener): def orExpression(self): localctx = JSONPathParser.OrExpressionContext(self, self._ctx, self.state) - self.enterRule(localctx, 16, self.RULE_orExpression) + self.enterRule(localctx, 20, self.RULE_orExpression) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 100 + self.state = 113 self.notExpression() - self.state = 103 + self.state = 116 self._errHandler.sync(self) _la = self._input.LA(1) if _la==JSONPathParser.OR: - self.state = 101 + self.state = 114 self.match(JSONPathParser.OR) - self.state = 102 + self.state = 115 self.orExpression() @@ -811,18 +945,11 @@ def expression(self): def PAREN_RIGHT(self): return self.getToken(JSONPathParser.PAREN_RIGHT, 0) - def ROOT_VALUE(self): - return self.getToken(JSONPathParser.ROOT_VALUE, 0) - - def CURRENT_VALUE(self): - return self.getToken(JSONPathParser.CURRENT_VALUE, 0) - - def subscript(self): - return self.getTypedRuleContext(JSONPathParser.SubscriptContext,0) - - - def value(self): - return self.getTypedRuleContext(JSONPathParser.ValueContext,0) + def jsonpath__(self, i:int=None): + if i is None: + return self.getTypedRuleContexts(JSONPathParser.Jsonpath__Context) + else: + return self.getTypedRuleContext(JSONPathParser.Jsonpath__Context,i) def EQ(self): @@ -860,58 +987,45 @@ def exitRule(self, listener:ParseTreeListener): def notExpression(self): localctx = JSONPathParser.NotExpressionContext(self, self._ctx, self.state) - self.enterRule(localctx, 18, self.RULE_notExpression) + self.enterRule(localctx, 22, self.RULE_notExpression) self._la = 0 # Token type try: - self.state = 119 + self.state = 129 self._errHandler.sync(self) token = self._input.LA(1) if token in [JSONPathParser.NOT]: self.enterOuterAlt(localctx, 1) - self.state = 105 + self.state = 118 self.match(JSONPathParser.NOT) - self.state = 106 + self.state = 119 self.notExpression() pass elif token in [JSONPathParser.PAREN_LEFT]: self.enterOuterAlt(localctx, 2) - self.state = 107 + self.state = 120 self.match(JSONPathParser.PAREN_LEFT) - self.state = 108 + self.state = 121 self.expression() - self.state = 109 + self.state = 122 self.match(JSONPathParser.PAREN_RIGHT) pass - elif token in [JSONPathParser.CURRENT_VALUE, JSONPathParser.ROOT_VALUE]: + elif token in [JSONPathParser.CURRENT_VALUE, JSONPathParser.ROOT_VALUE, JSONPathParser.TRUE, JSONPathParser.FALSE, JSONPathParser.NULL, JSONPathParser.BRACE_LEFT, JSONPathParser.BRACKET_LEFT, JSONPathParser.STRING, JSONPathParser.NUMBER]: self.enterOuterAlt(localctx, 3) - self.state = 111 - _la = self._input.LA(1) - if not(_la==JSONPathParser.CURRENT_VALUE or _la==JSONPathParser.ROOT_VALUE): - self._errHandler.recoverInline(self) - else: - self._errHandler.reportMatch(self) - self.consume() - self.state = 113 - self._errHandler.sync(self) - _la = self._input.LA(1) - if (((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << JSONPathParser.RECURSIVE_DESCENT) | (1 << JSONPathParser.SUBSCRIPT) | (1 << JSONPathParser.BRACKET_LEFT))) != 0): - self.state = 112 - self.subscript() - - - self.state = 117 + self.state = 124 + self.jsonpath__() + self.state = 127 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << JSONPathParser.EQ) | (1 << JSONPathParser.GE) | (1 << JSONPathParser.GT) | (1 << JSONPathParser.LE) | (1 << JSONPathParser.LT) | (1 << JSONPathParser.NE))) != 0): - self.state = 115 + self.state = 125 _la = self._input.LA(1) if not((((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << JSONPathParser.EQ) | (1 << JSONPathParser.GE) | (1 << JSONPathParser.GT) | (1 << JSONPathParser.LE) | (1 << JSONPathParser.LT) | (1 << JSONPathParser.NE))) != 0)): self._errHandler.recoverInline(self) else: self._errHandler.reportMatch(self) self.consume() - self.state = 116 - self.value() + self.state = 126 + self.jsonpath__() pass @@ -953,10 +1067,10 @@ def exitRule(self, listener:ParseTreeListener): def json(self): localctx = JSONPathParser.JsonContext(self, self._ctx, self.state) - self.enterRule(localctx, 20, self.RULE_json) + self.enterRule(localctx, 24, self.RULE_json) try: self.enterOuterAlt(localctx, 1) - self.state = 121 + self.state = 131 self.value() except RecognitionException as re: localctx.exception = re @@ -1008,39 +1122,39 @@ def exitRule(self, listener:ParseTreeListener): def obj(self): localctx = JSONPathParser.ObjContext(self, self._ctx, self.state) - self.enterRule(localctx, 22, self.RULE_obj) + self.enterRule(localctx, 26, self.RULE_obj) self._la = 0 # Token type try: - self.state = 136 + self.state = 146 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,17,self._ctx) + la_ = self._interp.adaptivePredict(self._input,18,self._ctx) if la_ == 1: self.enterOuterAlt(localctx, 1) - self.state = 123 + self.state = 133 self.match(JSONPathParser.BRACE_LEFT) - self.state = 124 + self.state = 134 self.pair() - self.state = 129 + self.state = 139 self._errHandler.sync(self) _la = self._input.LA(1) while _la==JSONPathParser.COMMA: - self.state = 125 + self.state = 135 self.match(JSONPathParser.COMMA) - self.state = 126 + self.state = 136 self.pair() - self.state = 131 + self.state = 141 self._errHandler.sync(self) _la = self._input.LA(1) - self.state = 132 + self.state = 142 self.match(JSONPathParser.BRACE_RIGHT) pass elif la_ == 2: self.enterOuterAlt(localctx, 2) - self.state = 134 + self.state = 144 self.match(JSONPathParser.BRACE_LEFT) - self.state = 135 + self.state = 145 self.match(JSONPathParser.BRACE_RIGHT) pass @@ -1086,14 +1200,14 @@ def exitRule(self, listener:ParseTreeListener): def pair(self): localctx = JSONPathParser.PairContext(self, self._ctx, self.state) - self.enterRule(localctx, 24, self.RULE_pair) + self.enterRule(localctx, 28, self.RULE_pair) try: self.enterOuterAlt(localctx, 1) - self.state = 138 + self.state = 148 self.match(JSONPathParser.STRING) - self.state = 139 + self.state = 149 self.match(JSONPathParser.COLON) - self.state = 140 + self.state = 150 self.value() except RecognitionException as re: localctx.exception = re @@ -1145,39 +1259,39 @@ def exitRule(self, listener:ParseTreeListener): def array(self): localctx = JSONPathParser.ArrayContext(self, self._ctx, self.state) - self.enterRule(localctx, 26, self.RULE_array) + self.enterRule(localctx, 30, self.RULE_array) self._la = 0 # Token type try: - self.state = 155 + self.state = 165 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,19,self._ctx) + la_ = self._interp.adaptivePredict(self._input,20,self._ctx) if la_ == 1: self.enterOuterAlt(localctx, 1) - self.state = 142 + self.state = 152 self.match(JSONPathParser.BRACKET_LEFT) - self.state = 143 + self.state = 153 self.value() - self.state = 148 + self.state = 158 self._errHandler.sync(self) _la = self._input.LA(1) while _la==JSONPathParser.COMMA: - self.state = 144 + self.state = 154 self.match(JSONPathParser.COMMA) - self.state = 145 + self.state = 155 self.value() - self.state = 150 + self.state = 160 self._errHandler.sync(self) _la = self._input.LA(1) - self.state = 151 + self.state = 161 self.match(JSONPathParser.BRACKET_RIGHT) pass elif la_ == 2: self.enterOuterAlt(localctx, 2) - self.state = 153 + self.state = 163 self.match(JSONPathParser.BRACKET_LEFT) - self.state = 154 + self.state = 164 self.match(JSONPathParser.BRACKET_RIGHT) pass @@ -1236,44 +1350,44 @@ def exitRule(self, listener:ParseTreeListener): def value(self): localctx = JSONPathParser.ValueContext(self, self._ctx, self.state) - self.enterRule(localctx, 28, self.RULE_value) + self.enterRule(localctx, 32, self.RULE_value) try: - self.state = 164 + self.state = 174 self._errHandler.sync(self) token = self._input.LA(1) if token in [JSONPathParser.STRING]: self.enterOuterAlt(localctx, 1) - self.state = 157 + self.state = 167 self.match(JSONPathParser.STRING) pass elif token in [JSONPathParser.NUMBER]: self.enterOuterAlt(localctx, 2) - self.state = 158 + self.state = 168 self.match(JSONPathParser.NUMBER) pass elif token in [JSONPathParser.BRACE_LEFT]: self.enterOuterAlt(localctx, 3) - self.state = 159 + self.state = 169 self.obj() pass elif token in [JSONPathParser.BRACKET_LEFT]: self.enterOuterAlt(localctx, 4) - self.state = 160 + self.state = 170 self.array() pass elif token in [JSONPathParser.TRUE]: self.enterOuterAlt(localctx, 5) - self.state = 161 + self.state = 171 self.match(JSONPathParser.TRUE) pass elif token in [JSONPathParser.FALSE]: self.enterOuterAlt(localctx, 6) - self.state = 162 + self.state = 172 self.match(JSONPathParser.FALSE) pass elif token in [JSONPathParser.NULL]: self.enterOuterAlt(localctx, 7) - self.state = 163 + self.state = 173 self.match(JSONPathParser.NULL) pass else: @@ -1292,8 +1406,8 @@ def value(self): def sempred(self, localctx:RuleContext, ruleIndex:int, predIndex:int): if self._predicates == None: self._predicates = dict() - self._predicates[4] = self.subscriptable_sempred - self._predicates[5] = self.sliceable_sempred + self._predicates[6] = self.subscriptable_sempred + self._predicates[7] = self.sliceable_sempred pred = self._predicates.get(ruleIndex, None) if pred is None: raise Exception("No predicate with index:" + str(ruleIndex)) diff --git a/jsonpath2/parser/__init__.py b/jsonpath2/parser/__init__.py index 8c592c1..fef53b7 100644 --- a/jsonpath2/parser/__init__.py +++ b/jsonpath2/parser/__init__.py @@ -20,6 +20,7 @@ from jsonpath2.subscripts.arrayindex import ArrayIndexSubscript from jsonpath2.subscripts.arrayslice import ArraySliceSubscript from jsonpath2.subscripts.filter import FilterSubscript +from jsonpath2.subscripts.node import NodeSubscript from jsonpath2.subscripts.objectindex import ObjectIndexSubscript from jsonpath2.subscripts.wildcard import WildcardSubscript @@ -48,6 +49,23 @@ def exitJsonpath(self, ctx: JSONPathParser.JsonpathContext): # NOTE Unreachable when listener is used as tree walker. raise ValueError() # pragma: no cover + def exitJsonpath_(self, ctx: JSONPathParser.JsonpathContext): + if bool(ctx.subscript()): + next_node = self._stack.pop() + else: + next_node = TerminalNode() + + if ctx.getToken(JSONPathParser.ROOT_VALUE, 0) is not None: + self._stack.append(RootNode(next_node)) + elif ctx.getToken(JSONPathParser.CURRENT_VALUE, 0) is not None: + self._stack.append(CurrentNode(next_node)) + else: + # NOTE Unreachable when listener is used as tree walker. + raise ValueError() # pragma: no cover + + def exitJsonpath__(self, ctx: JSONPathParser.JsonpathContext): + pass + # pylint: disable=too-many-branches # It would sure be nice if we had a case statement. def exitSubscript(self, ctx: JSONPathParser.SubscriptContext): @@ -135,6 +153,10 @@ def exitSubscriptable(self, ctx: JSONPathParser.SubscriptableContext): expression = self._stack.pop() self._stack.append(FilterSubscript(expression)) + elif bool(ctx.jsonpath_()): + next_node = self._stack.pop() + + self._stack.append(NodeSubscript(next_node)) else: # NOTE Unreachable when listener is used as tree walker. raise ValueError() # pragma: no cover @@ -209,52 +231,36 @@ def exitNotExpression(self, ctx: JSONPathParser.NotExpressionContext): self._stack.append(expression.expression) else: self._stack.append(NotUnaryOperatorExpression(expression)) - elif (ctx.getToken(JSONPathParser.ROOT_VALUE, 0) is not None) or \ - (ctx.getToken(JSONPathParser.CURRENT_VALUE, 0) is not None): - if bool(ctx.value()): - right_value = self._stack.pop() - - if bool(ctx.subscript()): - next_node = self._stack.pop() - else: - next_node = TerminalNode() - - if ctx.getToken(JSONPathParser.ROOT_VALUE, 0) is not None: - left_node = RootNode(next_node) - elif ctx.getToken(JSONPathParser.CURRENT_VALUE, 0) is not None: - left_node = CurrentNode(next_node) - else: - # NOTE Unreachable when listener is used as tree walker. - raise ValueError() # pragma: no cover - - if ctx.getToken(JSONPathParser.EQ, 0) is not None: - self._stack.append( - EqualBinaryOperatorExpression(left_node, right_value)) - elif ctx.getToken(JSONPathParser.NE, 0) is not None: - self._stack.append( - NotEqualBinaryOperatorExpression(left_node, right_value)) - elif ctx.getToken(JSONPathParser.LT, 0) is not None: - self._stack.append( - LessThanBinaryOperatorExpression(left_node, right_value)) - elif ctx.getToken(JSONPathParser.LE, 0) is not None: - self._stack.append( - LessThanOrEqualToBinaryOperatorExpression(left_node, right_value)) - elif ctx.getToken(JSONPathParser.GT, 0) is not None: - self._stack.append( - GreaterThanBinaryOperatorExpression(left_node, right_value)) - elif ctx.getToken(JSONPathParser.GE, 0) is not None: - self._stack.append( - GreaterThanOrEqualToBinaryOperatorExpression(left_node, right_value)) - else: - # NOTE Unreachable when listener is used as tree walker. - raise ValueError() # pragma: no cover + elif bool(ctx.jsonpath__(1)): + right_node_or_value = self._stack.pop() + + left_node_or_value = self._stack.pop() + + if ctx.getToken(JSONPathParser.EQ, 0) is not None: + self._stack.append( + EqualBinaryOperatorExpression(left_node_or_value, right_node_or_value)) + elif ctx.getToken(JSONPathParser.NE, 0) is not None: + self._stack.append( + NotEqualBinaryOperatorExpression(left_node_or_value, right_node_or_value)) + elif ctx.getToken(JSONPathParser.LT, 0) is not None: + self._stack.append( + LessThanBinaryOperatorExpression(left_node_or_value, right_node_or_value)) + elif ctx.getToken(JSONPathParser.LE, 0) is not None: + self._stack.append( + LessThanOrEqualToBinaryOperatorExpression(left_node_or_value, right_node_or_value)) + elif ctx.getToken(JSONPathParser.GT, 0) is not None: + self._stack.append( + GreaterThanBinaryOperatorExpression(left_node_or_value, right_node_or_value)) + elif ctx.getToken(JSONPathParser.GE, 0) is not None: + self._stack.append( + GreaterThanOrEqualToBinaryOperatorExpression(left_node_or_value, right_node_or_value)) else: - if bool(ctx.subscript()): - next_node = self._stack.pop() - else: - next_node = TerminalNode() + # NOTE Unreachable when listener is used as tree walker. + raise ValueError() # pragma: no cover + elif bool(ctx.jsonpath__(0)): + next_node_or_value = self._stack.pop() - self._stack.append(SomeExpression(CurrentNode(next_node))) + self._stack.append(SomeExpression(next_node_or_value)) else: pass # pylint: enable=too-many-branches diff --git a/jsonpath2/subscripts/node.py b/jsonpath2/subscripts/node.py new file mode 100644 index 0000000..af38c27 --- /dev/null +++ b/jsonpath2/subscripts/node.py @@ -0,0 +1,35 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +"""Node.""" +from typing import Generator +from jsonpath2.node import MatchData, Node +from jsonpath2.subscript import Subscript +from jsonpath2.subscripts.arrayindex import ArrayIndexSubscript +from jsonpath2.subscripts.objectindex import ObjectIndexSubscript + + +class NodeSubscript(Subscript): + """Node subscript in the parse tree.""" + + def __init__(self, next_node: Node): + """Save the node subscript.""" + super(NodeSubscript, self).__init__() + self.next_node = next_node + + def __jsonpath__(self) -> Generator[str, None, None]: + """generate the jsonpath for the path.""" + return self.next_node.__jsonpath__() + + def match(self, root_value: object, current_value: object) -> Generator[MatchData, None, None]: + """Match the node subscript against the current value.""" + for next_node_match_data in self.next_node.match(root_value, current_value): + if isinstance(next_node_match_data.current_value, int): + subscript = ArrayIndexSubscript(next_node_match_data.current_value) + + for subscript_match_data in subscript.match(root_value, current_value): + yield subscript_match_data + elif isinstance(next_node_match_data.current_value, str): + subscript = ObjectIndexSubscript(next_node_match_data.current_value) + + for subscript_match_data in subscript.match(root_value, current_value): + yield subscript_match_data diff --git a/jsonpath2/test/expression_test.py b/jsonpath2/test/expression_test.py index 2406ddc..f624f4a 100644 --- a/jsonpath2/test/expression_test.py +++ b/jsonpath2/test/expression_test.py @@ -82,3 +82,26 @@ def test_binary_operator(self): self.assertEqual('$[?($ = null)]', str(expr)) with self.assertRaises(ValueError): Path.parse_str('$[3.14156:]') + + # pylint: disable=invalid-name + def test_binary_operator_with_path_subscript(self): + """Test a binary operator with a path subscript.""" + data = { + 'foo': 'bar', + 'fum': 'baz', + } + expected_values = { + '$[?(0)]["foo"]': [], + '$[?(0 = 0)]["foo"]': ['bar'], + '$[?(0 = @["fum"])]["foo"]': [], + '$[?(@["foo"] = 0)]["foo"]': [], + '$[?(@["foo"] = @["fum"])]["foo"]': [] + } + # pylint: disable=consider-iterating-dictionary + for string in expected_values.keys(): + # pylint: enable=consider-iterating-dictionary + expr = Path.parse_str(string) + self.assertEqual(string, str(expr)) + values = [match_data.current_value for match_data in expr.match(data)] + self.assertListEqual(expected_values.get(string), values) + # pylint: enable=invalid-name diff --git a/jsonpath2/test/node_subscript_test.py b/jsonpath2/test/node_subscript_test.py new file mode 100644 index 0000000..bf13490 --- /dev/null +++ b/jsonpath2/test/node_subscript_test.py @@ -0,0 +1,92 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +"""Test the jsonpath module.""" +from unittest import TestCase + +from jsonpath2.path import Path + + +class TestNodeSubscript(TestCase): + """Test the node subscript.""" + + # pylint: disable=invalid-name + def test_node_subscript_noop(self): + """Test the node subscript with no indices.""" + string = '$["collection"][$["index"]]' + path = Path.parse_str(string) + self.assertEqual(string, str(path)) + data = { + 'collection': ['foo', 'bar', 'fum', 'baz'], + 'index': [], + } + matches = list(path.match(data)) + self.assertEqual(0, len(matches)) + # pylint: enable=invalid-name + + # pylint: disable=invalid-name + def test_node_subscript_array_index(self): + """Test the node subscript with one array index.""" + string = '$["collection"][$["index"]]' + path = Path.parse_str(string) + self.assertEqual(string, str(path)) + data = { + 'collection': ['foo', 'bar', 'fum', 'baz'], + 'index': 1, + } + matches = list(path.match(data)) + self.assertEqual(1, len(matches)) + self.assertEqual('bar', matches[0].current_value) + # pylint: enable=invalid-name + + # pylint: disable=invalid-name + def test_node_subscript_array_indices(self): + """Test the node subscript with many array indices.""" + string = '$["collection"][$["indices"][*]]' + path = Path.parse_str(string) + self.assertEqual(string, str(path)) + data = { + 'collection': ['foo', 'bar', 'fum', 'baz'], + 'indices': [1, 3], + } + matches = list(path.match(data)) + self.assertEqual(2, len(matches)) + self.assertEqual('bar', matches[0].current_value) + self.assertEqual('baz', matches[1].current_value) + # pylint: enable=invalid-name + + # pylint: disable=invalid-name + def test_node_subscript_object_index(self): + """Test the node subscript with one object index.""" + string = '$["collection"][$["index"]]' + path = Path.parse_str(string) + self.assertEqual(string, str(path)) + data = { + 'collection': { + 'foo': 'bar', + 'fum': 'baz', + }, + 'index': 'foo', + } + matches = list(path.match(data)) + self.assertEqual(1, len(matches)) + self.assertEqual('bar', matches[0].current_value) + # pylint: enable=invalid-name + + # pylint: disable=invalid-name + def test_node_subscript_object_indices(self): + """Test the node subscript with many object indices.""" + string = '$["collection"][$["indices"][*]]' + path = Path.parse_str(string) + self.assertEqual(string, str(path)) + data = { + 'collection': { + 'foo': 'bar', + 'fum': 'baz', + }, + 'indices': ['foo', 'fum'], + } + matches = list(path.match(data)) + self.assertEqual(2, len(matches)) + self.assertEqual('bar', matches[0].current_value) + self.assertEqual('baz', matches[1].current_value) + # pylint: enable=invalid-name