5050from .selectors import KeysFilter
5151from .selectors import KeysSelector
5252from .selectors import PropertySelector
53+ from .selectors import SingularQuerySelector
5354from .selectors import SliceSelector
5455from .selectors import WildSelector
5556from .token import TOKEN_AND
@@ -239,7 +240,7 @@ def __init__(self, *, env: JSONPathEnvironment) -> None:
239240
240241 self .token_map : Dict [str , Callable [[TokenStream ], FilterExpression ]] = {
241242 TOKEN_DOUBLE_QUOTE_STRING : self .parse_string_literal ,
242- TOKEN_PSEUDO_ROOT : self .parse_root_path ,
243+ TOKEN_PSEUDO_ROOT : self .parse_absolute_query ,
243244 TOKEN_FALSE : self .parse_boolean ,
244245 TOKEN_FILTER_CONTEXT : self .parse_filter_context_path ,
245246 TOKEN_FLOAT : self .parse_float_literal ,
@@ -254,8 +255,8 @@ def __init__(self, *, env: JSONPathEnvironment) -> None:
254255 TOKEN_NOT : self .parse_prefix_expression ,
255256 TOKEN_NULL : self .parse_nil ,
256257 TOKEN_RE_PATTERN : self .parse_regex ,
257- TOKEN_ROOT : self .parse_root_path ,
258- TOKEN_SELF : self .parse_self_path ,
258+ TOKEN_ROOT : self .parse_absolute_query ,
259+ TOKEN_SELF : self .parse_relative_query ,
259260 TOKEN_SINGLE_QUOTE_STRING : self .parse_string_literal ,
260261 TOKEN_TRUE : self .parse_boolean ,
261262 TOKEN_UNDEFINED : self .parse_undefined ,
@@ -277,7 +278,7 @@ def __init__(self, *, env: JSONPathEnvironment) -> None:
277278 str , Callable [[TokenStream ], FilterExpression ]
278279 ] = {
279280 TOKEN_DOUBLE_QUOTE_STRING : self .parse_string_literal ,
280- TOKEN_PSEUDO_ROOT : self .parse_root_path ,
281+ TOKEN_PSEUDO_ROOT : self .parse_absolute_query ,
281282 TOKEN_FALSE : self .parse_boolean ,
282283 TOKEN_FILTER_CONTEXT : self .parse_filter_context_path ,
283284 TOKEN_FLOAT : self .parse_float_literal ,
@@ -287,8 +288,8 @@ def __init__(self, *, env: JSONPathEnvironment) -> None:
287288 TOKEN_NIL : self .parse_nil ,
288289 TOKEN_NONE : self .parse_nil ,
289290 TOKEN_NULL : self .parse_nil ,
290- TOKEN_ROOT : self .parse_root_path ,
291- TOKEN_SELF : self .parse_self_path ,
291+ TOKEN_ROOT : self .parse_absolute_query ,
292+ TOKEN_SELF : self .parse_relative_query ,
292293 TOKEN_SINGLE_QUOTE_STRING : self .parse_string_literal ,
293294 TOKEN_TRUE : self .parse_boolean ,
294295 }
@@ -299,15 +300,15 @@ def parse(self, stream: TokenStream) -> Iterator[JSONPathSegment]:
299300 if stream .current ().kind in {TOKEN_ROOT , TOKEN_PSEUDO_ROOT }:
300301 stream .next ()
301302
302- yield from self .parse_path (stream )
303+ yield from self .parse_query (stream )
303304
304305 if stream .current ().kind not in (TOKEN_EOF , TOKEN_INTERSECTION , TOKEN_UNION ):
305306 raise JSONPathSyntaxError (
306307 f"unexpected token { stream .current ().value !r} " ,
307308 token = stream .current (),
308309 )
309310
310- def parse_path (self , stream : TokenStream ) -> Iterable [JSONPathSegment ]:
311+ def parse_query (self , stream : TokenStream ) -> Iterable [JSONPathSegment ]:
311312 """Parse a JSONPath query string.
312313
313314 This method assumes the root, current or pseudo root identifier has
@@ -405,7 +406,7 @@ def parse_selector(self, stream: TokenStream) -> tuple[JSONPathSelector, ...]:
405406 stream .pos -= 1
406407 return ()
407408
408- def parse_bracketed_selection (self , stream : TokenStream ) -> List [JSONPathSelector ]: # noqa: PLR0912
409+ def parse_bracketed_selection (self , stream : TokenStream ) -> List [JSONPathSelector ]: # noqa: PLR0912, PLR0915
409410 """Parse a comma separated list of JSONPath selectors."""
410411 segment_token = stream .eat (TOKEN_LBRACKET )
411412 selectors : List [JSONPathSelector ] = []
@@ -470,6 +471,8 @@ def parse_bracketed_selection(self, stream: TokenStream) -> List[JSONPathSelecto
470471 selectors .append (self .parse_filter_selector (stream ))
471472 elif token .kind == TOKEN_KEYS_FILTER :
472473 selectors .append (self .parse_filter_selector (stream , keys = True ))
474+ elif token .kind in (TOKEN_ROOT , TOKEN_NAME ):
475+ selectors .append (self .parse_singular_query_selector (stream ))
473476 elif token .kind == TOKEN_EOF :
474477 raise JSONPathSyntaxError ("unexpected end of query" , token = token )
475478 else :
@@ -664,20 +667,41 @@ def parse_grouped_expression(self, stream: TokenStream) -> FilterExpression:
664667 stream .eat (TOKEN_RPAREN )
665668 return expr
666669
667- def parse_root_path (self , stream : TokenStream ) -> FilterExpression :
670+ def parse_absolute_query (self , stream : TokenStream ) -> FilterExpression :
668671 root = stream .next ()
669672 return RootFilterQuery (
670673 JSONPath (
671674 env = self .env ,
672- segments = self .parse_path (stream ),
675+ segments = self .parse_query (stream ),
673676 pseudo_root = root .kind == TOKEN_PSEUDO_ROOT ,
674677 )
675678 )
676679
677- def parse_self_path (self , stream : TokenStream ) -> FilterExpression :
678- stream .next ( )
680+ def parse_relative_query (self , stream : TokenStream ) -> FilterExpression :
681+ stream .eat ( TOKEN_SELF )
679682 return RelativeFilterQuery (
680- JSONPath (env = self .env , segments = self .parse_path (stream ))
683+ JSONPath (env = self .env , segments = self .parse_query (stream ))
684+ )
685+
686+ def parse_singular_query_selector (
687+ self , stream : TokenStream
688+ ) -> SingularQuerySelector :
689+ # TODO: optionally require root identifier
690+ token = (
691+ stream .next () if stream .current ().kind == TOKEN_ROOT else stream .current ()
692+ )
693+
694+ query = JSONPath (env = self .env , segments = self .parse_query (stream ))
695+
696+ if not query .singular_query ():
697+ raise JSONPathSyntaxError (
698+ "embedded query selectors must be singular queries" , token = token
699+ )
700+
701+ return SingularQuerySelector (
702+ env = self .env ,
703+ token = token ,
704+ query = query ,
681705 )
682706
683707 def parse_current_key (self , stream : TokenStream ) -> FilterExpression :
@@ -687,7 +711,7 @@ def parse_current_key(self, stream: TokenStream) -> FilterExpression:
687711 def parse_filter_context_path (self , stream : TokenStream ) -> FilterExpression :
688712 stream .next ()
689713 return FilterContextPath (
690- JSONPath (env = self .env , segments = self .parse_path (stream ))
714+ JSONPath (env = self .env , segments = self .parse_query (stream ))
691715 )
692716
693717 def parse_regex (self , stream : TokenStream ) -> FilterExpression :
0 commit comments