@@ -338,100 +338,91 @@ def parse_query(self, stream: TokenStream) -> Iterable[JSONPathSegment]:
338338 This method assumes the root, current or pseudo root identifier has
339339 already been consumed.
340340 """
341+ if not self .env .strict and stream .current ().kind in {
342+ TOKEN_NAME ,
343+ TOKEN_WILD ,
344+ TOKEN_KEYS ,
345+ TOKEN_KEY_NAME ,
346+ }:
347+ # A non-standard "bare" path. One that starts with a shorthand selector
348+ # without a leading identifier (`$`, `@`, `^` or `_`).
349+ #
350+ # When no identifier is given, a root query (`$`) is assumed.
351+ token = stream .current ()
352+ selector = self .parse_shorthand_selector (stream )
353+ yield JSONPathChildSegment (env = self .env , token = token , selectors = (selector ,))
354+
341355 while True :
342356 stream .skip_whitespace ()
343- _token = stream .current ()
344- if _token .kind == TOKEN_DOT :
345- stream .eat (TOKEN_DOT )
346- # Assert that dot is followed by shorthand selector without whitespace.
347- stream .expect (TOKEN_NAME , TOKEN_WILD , TOKEN_KEYS , TOKEN_KEY_NAME )
348- token = stream .current ()
349- selectors = self .parse_selector (stream )
357+ token = stream .next ()
358+
359+ if token .kind == TOKEN_DOT :
360+ selector = self .parse_shorthand_selector (stream )
350361 yield JSONPathChildSegment (
351- env = self .env , token = token , selectors = selectors
362+ env = self .env , token = token , selectors = ( selector ,)
352363 )
353- elif _token .kind == TOKEN_DDOT :
354- token = stream .eat (TOKEN_DDOT )
355- selectors = self .parse_selector (stream )
356- if not selectors :
357- raise JSONPathSyntaxError (
358- "missing selector for recursive descent segment" ,
359- token = stream .current (),
360- )
364+ elif token .kind == TOKEN_DDOT :
365+ if stream .current ().kind == TOKEN_LBRACKET :
366+ selectors = tuple (self .parse_bracketed_selection (stream ))
367+ else :
368+ selectors = (self .parse_shorthand_selector (stream ),)
369+
361370 yield JSONPathRecursiveDescentSegment (
362371 env = self .env , token = token , selectors = selectors
363372 )
364- elif _token .kind == TOKEN_LBRACKET :
365- selectors = self .parse_selector (stream )
366- yield JSONPathChildSegment (
367- env = self .env , token = _token , selectors = selectors
368- )
369- elif _token .kind in {TOKEN_NAME , TOKEN_WILD , TOKEN_KEYS , TOKEN_KEY_NAME }:
370- # A non-standard "bare" path. One without a leading identifier (`$`,
371- # `@`, `^` or `_`).
372- token = stream .current ()
373- selectors = self .parse_selector (stream )
373+ elif token .kind == TOKEN_LBRACKET :
374+ stream .pos -= 1
374375 yield JSONPathChildSegment (
375- env = self .env , token = token , selectors = selectors
376+ env = self .env ,
377+ token = token ,
378+ selectors = tuple (self .parse_bracketed_selection (stream )),
376379 )
380+ elif token .kind == TOKEN_EOF :
381+ break
377382 else :
383+ # An embedded query. Put the token back on the stream.
384+ stream .pos -= 1
378385 break
379386
380- def parse_selector (self , stream : TokenStream ) -> tuple [ JSONPathSelector , ...]: # noqa: PLR0911
387+ def parse_shorthand_selector (self , stream : TokenStream ) -> JSONPathSelector :
381388 token = stream .next ()
382389
383390 if token .kind == TOKEN_NAME :
384- return (
385- NameSelector (
386- env = self .env ,
387- token = token ,
388- name = token .value ,
389- ),
391+ return NameSelector (
392+ env = self .env ,
393+ token = token ,
394+ name = token .value ,
390395 )
391396
392397 if token .kind == TOKEN_KEY_NAME :
393- return (
394- KeySelector (
395- env = self .env ,
396- token = token ,
397- key = token .value ,
398- ),
398+ return KeySelector (
399+ env = self .env ,
400+ token = token ,
401+ key = token .value ,
399402 )
400403
401404 if token .kind == TOKEN_WILD :
402- return (
403- WildcardSelector (
404- env = self .env ,
405- token = token ,
406- ),
405+ return WildcardSelector (
406+ env = self .env ,
407+ token = token ,
407408 )
408409
409410 if token .kind == TOKEN_KEYS :
410411 if stream .current ().kind == TOKEN_NAME :
411- return (
412- KeySelector (
413- env = self .env ,
414- token = token ,
415- key = self ._decode_string_literal (stream .next ()),
416- ),
417- )
418-
419- return (
420- KeysSelector (
412+ return KeySelector (
421413 env = self .env ,
422414 token = token ,
423- ),
424- )
415+ key = self . _decode_string_literal ( stream . next () ),
416+ )
425417
426- if token .kind == TOKEN_LBRACKET :
427- stream .pos -= 1
428- return tuple (self .parse_bracketed_selection (stream ))
418+ return KeysSelector (
419+ env = self .env ,
420+ token = token ,
421+ )
429422
430- stream .pos -= 1
431- return ()
423+ raise JSONPathSyntaxError ("expected a shorthand selector" , token = token )
432424
433425 def parse_bracketed_selection (self , stream : TokenStream ) -> List [JSONPathSelector ]: # noqa: PLR0912, PLR0915
434- """Parse a comma separated list of JSONPath selectors."""
435426 segment_token = stream .eat (TOKEN_LBRACKET )
436427 selectors : List [JSONPathSelector ] = []
437428
@@ -704,7 +695,7 @@ def parse_grouped_expression(self, stream: TokenStream) -> BaseExpression:
704695 return expr
705696
706697 def parse_absolute_query (self , stream : TokenStream ) -> BaseExpression :
707- root = stream .next ()
698+ root = stream .next () # Could be TOKEN_ROOT or TOKEN_PSEUDO_ROOT
708699 return RootFilterQuery (
709700 JSONPath (
710701 env = self .env ,
0 commit comments