1515from .token import TOKEN_CONTAINS
1616from .token import TOKEN_DDOT
1717from .token import TOKEN_DOT
18+ from .token import TOKEN_DOT_PROPERTY
1819from .token import TOKEN_DOUBLE_QUOTE_STRING
1920from .token import TOKEN_EQ
2021from .token import TOKEN_ERROR
2122from .token import TOKEN_FALSE
2223from .token import TOKEN_FILTER
2324from .token import TOKEN_FILTER_CONTEXT
2425from .token import TOKEN_FLOAT
26+ from .token import TOKEN_FUNCTION
2527from .token import TOKEN_GE
2628from .token import TOKEN_GT
2729from .token import TOKEN_IN
@@ -82,7 +84,6 @@ class attributes. Then setting `lexer_class` on a `JSONPathEnvironment`.
8284 """
8385
8486 key_pattern = r"[\u0080-\uFFFFa-zA-Z_][\u0080-\uFFFFa-zA-Z0-9_-]*"
85- name_pattern = key_pattern # XXX:
8687
8788 # ! or `not`
8889 logical_not_pattern = r"(?:not\b)|!"
@@ -99,10 +100,15 @@ def __init__(self, *, env: JSONPathEnvironment) -> None:
99100 self .double_quote_pattern = r'"(?P<G_DQUOTE>(?:(?!(?<!\\)").)*)"'
100101 self .single_quote_pattern = r"'(?P<G_SQUOTE>(?:(?!(?<!\\)').)*)'"
101102
102- # TODO: separate re literal tokens
103+ # .thing
104+ self .dot_property_pattern = rf"(?P<G_DOT>\.)(?P<G_PROP>{ self .key_pattern } )"
105+
103106 # /pattern/ or /pattern/flags
104107 self .re_pattern = r"/(?P<G_RE>.+?)/(?P<G_RE_FLAGS>[aims]*)"
105108
109+ # func(
110+ self .function_pattern = r"(?P<G_FUNC>[a-z][a-z_0-9]+)(?P<G_FUNC_PAREN>\()"
111+
106112 self .rules = self .compile_rules ()
107113
108114 def compile_rules (self ) -> Pattern [str ]:
@@ -122,6 +128,7 @@ def compile_rules(self) -> Pattern[str]:
122128 (TOKEN_DOUBLE_QUOTE_STRING , self .double_quote_pattern ),
123129 (TOKEN_SINGLE_QUOTE_STRING , self .single_quote_pattern ),
124130 (TOKEN_RE_PATTERN , self .re_pattern ),
131+ (TOKEN_DOT_PROPERTY , self .dot_property_pattern ),
125132 (TOKEN_FLOAT , r"-?\d+\.\d*(?:[eE][+-]?\d+)?" ),
126133 (TOKEN_INT , r"-?\d+(?P<G_EXP>[eE][+\-]?\d+)?\b" ),
127134 (TOKEN_DDOT , r"\.\." ),
@@ -160,6 +167,7 @@ def compile_rules(self) -> Pattern[str]:
160167 (TOKEN_LT , r"<" ),
161168 (TOKEN_GT , r">" ),
162169 (TOKEN_NOT , self .logical_not_pattern ), # Must go after "!="
170+ (TOKEN_FUNCTION , self .function_pattern ),
163171 (TOKEN_NAME , self .key_pattern ), # Must go after reserved words
164172 (TOKEN_LPAREN , r"\(" ),
165173 (TOKEN_RPAREN , r"\)" ),
@@ -180,7 +188,18 @@ def tokenize(self, path: str) -> Iterator[Token]: # noqa PLR0912
180188 kind = match .lastgroup
181189 assert kind is not None
182190
183- if kind == TOKEN_DOUBLE_QUOTE_STRING :
191+ if kind == TOKEN_DOT_PROPERTY :
192+ yield _token (
193+ kind = TOKEN_DOT ,
194+ value = match .group ("G_DOT" ),
195+ index = match .start ("G_DOT" ),
196+ )
197+ yield _token (
198+ kind = TOKEN_NAME ,
199+ value = match .group ("G_PROP" ),
200+ index = match .start ("G_PROP" ),
201+ )
202+ elif kind == TOKEN_DOUBLE_QUOTE_STRING :
184203 yield _token (
185204 kind = TOKEN_DOUBLE_QUOTE_STRING ,
186205 value = match .group ("G_DQUOTE" ),
@@ -222,6 +241,18 @@ def tokenize(self, path: str) -> Iterator[Token]: # noqa PLR0912
222241 value = match .group (),
223242 index = match .start (),
224243 )
244+ elif kind == TOKEN_FUNCTION :
245+ yield _token (
246+ kind = TOKEN_FUNCTION ,
247+ value = match .group ("G_FUNC" ),
248+ index = match .start ("G_FUNC" ),
249+ )
250+
251+ yield _token (
252+ kind = TOKEN_LPAREN ,
253+ value = match .group ("G_FUNC_PAREN" ),
254+ index = match .start ("G_FUNC_PAREN" ),
255+ )
225256 elif kind == TOKEN_ERROR :
226257 raise JSONPathSyntaxError (
227258 f"unexpected token { match .group ()!r} " ,
0 commit comments