omlish 0.0.0.dev117__py3-none-any.whl → 0.0.0.dev118__py3-none-any.whl
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.
- omlish/__about__.py +2 -2
- omlish/collections/hasheq.py +0 -10
- omlish/fnpairs.py +1 -10
- omlish/formats/json/__init__.py +5 -0
- omlish/formats/json/literals.py +179 -0
- omlish/formats/json/render.py +2 -3
- omlish/formats/json/stream/render.py +1 -1
- omlish/formats/json/types.py +6 -0
- omlish/lang/classes/abstract.py +37 -14
- omlish/lang/imports.py +1 -1
- omlish/lang/maybes.py +10 -12
- omlish/specs/jmespath/ast.py +199 -60
- omlish/specs/jmespath/cli.py +43 -29
- omlish/specs/jmespath/functions.py +397 -274
- omlish/specs/jmespath/lexer.py +2 -2
- omlish/specs/jmespath/parser.py +169 -133
- omlish/specs/jmespath/scope.py +15 -11
- omlish/specs/jmespath/visitor.py +211 -137
- {omlish-0.0.0.dev117.dist-info → omlish-0.0.0.dev118.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev117.dist-info → omlish-0.0.0.dev118.dist-info}/RECORD +24 -22
- {omlish-0.0.0.dev117.dist-info → omlish-0.0.0.dev118.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev117.dist-info → omlish-0.0.0.dev118.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev117.dist-info → omlish-0.0.0.dev118.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev117.dist-info → omlish-0.0.0.dev118.dist-info}/top_level.txt +0 -0
omlish/specs/jmespath/lexer.py
CHANGED
@@ -249,14 +249,14 @@ class Lexer:
|
|
249
249
|
}
|
250
250
|
|
251
251
|
def _peek_may_be_valid_unquoted_identifier(self) -> bool:
|
252
|
-
if
|
252
|
+
if self._position == self._length - 1:
|
253
253
|
return False
|
254
254
|
else:
|
255
255
|
nxt = self._chars[self._position + 1]
|
256
256
|
return nxt in self.START_IDENTIFIER
|
257
257
|
|
258
258
|
def _peek_is_next_digit(self) -> bool:
|
259
|
-
if
|
259
|
+
if self._position == self._length - 1:
|
260
260
|
return False
|
261
261
|
else:
|
262
262
|
nxt = self._chars[self._position + 1]
|
omlish/specs/jmespath/parser.py
CHANGED
@@ -20,14 +20,48 @@ A few notes on the implementation.
|
|
20
20
|
interestingly enough, creating a token list first is actually faster than consuming from the token iterator one token
|
21
21
|
at a time.
|
22
22
|
"""
|
23
|
+
import dataclasses as dc
|
23
24
|
import random
|
24
25
|
import typing as ta
|
25
26
|
|
26
27
|
from ... import check
|
27
|
-
from . import
|
28
|
-
from . import
|
29
|
-
from . import
|
30
|
-
from . import
|
28
|
+
from .ast import AndExpression
|
29
|
+
from .ast import Arithmetic
|
30
|
+
from .ast import ArithmeticUnary
|
31
|
+
from .ast import Assign
|
32
|
+
from .ast import Comparator
|
33
|
+
from .ast import CurrentNode
|
34
|
+
from .ast import Expref
|
35
|
+
from .ast import Field
|
36
|
+
from .ast import FilterProjection
|
37
|
+
from .ast import Flatten
|
38
|
+
from .ast import FunctionExpression
|
39
|
+
from .ast import Identity
|
40
|
+
from .ast import Index
|
41
|
+
from .ast import IndexExpression
|
42
|
+
from .ast import KeyValPair
|
43
|
+
from .ast import LetExpression
|
44
|
+
from .ast import Literal
|
45
|
+
from .ast import MultiSelectDict
|
46
|
+
from .ast import MultiSelectList
|
47
|
+
from .ast import Node
|
48
|
+
from .ast import NotExpression
|
49
|
+
from .ast import OrExpression
|
50
|
+
from .ast import Pipe
|
51
|
+
from .ast import Projection
|
52
|
+
from .ast import RootNode
|
53
|
+
from .ast import Slice
|
54
|
+
from .ast import Subexpression
|
55
|
+
from .ast import ValueProjection
|
56
|
+
from .ast import VariableRef
|
57
|
+
from .exceptions import IncompleteExpressionError
|
58
|
+
from .exceptions import LexerError
|
59
|
+
from .exceptions import ParseError
|
60
|
+
from .lexer import Lexer
|
61
|
+
from .lexer import Token
|
62
|
+
from .visitor import GraphvizVisitor
|
63
|
+
from .visitor import Options
|
64
|
+
from .visitor import TreeInterpreter
|
31
65
|
|
32
66
|
|
33
67
|
class Parser:
|
@@ -83,12 +117,12 @@ class Parser:
|
|
83
117
|
def __init__(self, lookahead: int = 2) -> None:
|
84
118
|
super().__init__()
|
85
119
|
|
86
|
-
self._tokenizer: ta.Iterable[
|
87
|
-
self._tokens: list[
|
120
|
+
self._tokenizer: ta.Iterable[Token] | None = None
|
121
|
+
self._tokens: list[Token | None] = [None] * lookahead
|
88
122
|
self._buffer_size = lookahead
|
89
123
|
self._index = 0
|
90
124
|
|
91
|
-
def parse(self, expression: str, options:
|
125
|
+
def parse(self, expression: str, options: Options | None = None) -> 'ParsedResult':
|
92
126
|
cached = self._CACHE.get(expression)
|
93
127
|
if cached is not None:
|
94
128
|
return cached
|
@@ -101,24 +135,24 @@ class Parser:
|
|
101
135
|
|
102
136
|
return parsed_result
|
103
137
|
|
104
|
-
def _do_parse(self, expression: str, options:
|
138
|
+
def _do_parse(self, expression: str, options: Options | None = None) -> 'ParsedResult':
|
105
139
|
try:
|
106
140
|
return self._parse(expression, options)
|
107
141
|
|
108
|
-
except
|
142
|
+
except LexerError as e:
|
109
143
|
e.expression = expression
|
110
144
|
raise
|
111
145
|
|
112
|
-
except
|
146
|
+
except IncompleteExpressionError as e:
|
113
147
|
e.set_expression(expression)
|
114
148
|
raise
|
115
149
|
|
116
|
-
except
|
150
|
+
except ParseError as e:
|
117
151
|
e.expression = expression
|
118
152
|
raise
|
119
153
|
|
120
|
-
def _parse(self, expression: str, options:
|
121
|
-
self._tokenizer =
|
154
|
+
def _parse(self, expression: str, options: Options | None = None) -> 'ParsedResult':
|
155
|
+
self._tokenizer = Lexer().tokenize(expression, options)
|
122
156
|
self._tokens = list(self._tokenizer)
|
123
157
|
self._index = 0
|
124
158
|
|
@@ -126,7 +160,7 @@ class Parser:
|
|
126
160
|
|
127
161
|
if self._current_token() != 'eof':
|
128
162
|
t = check.not_none(self._lookahead_token(0))
|
129
|
-
raise
|
163
|
+
raise ParseError(
|
130
164
|
t['start'],
|
131
165
|
t['value'],
|
132
166
|
t['type'],
|
@@ -135,7 +169,7 @@ class Parser:
|
|
135
169
|
|
136
170
|
return ParsedResult(expression, parsed)
|
137
171
|
|
138
|
-
def _expression(self, binding_power: int = 0) ->
|
172
|
+
def _expression(self, binding_power: int = 0) -> Node:
|
139
173
|
left_token = check.not_none(self._lookahead_token(0))
|
140
174
|
|
141
175
|
self._advance()
|
@@ -166,19 +200,19 @@ class Parser:
|
|
166
200
|
|
167
201
|
return left
|
168
202
|
|
169
|
-
def _token_nud_literal(self, token:
|
170
|
-
return
|
203
|
+
def _token_nud_literal(self, token: Token) -> Node:
|
204
|
+
return Literal(token['value'])
|
171
205
|
|
172
|
-
def _token_nud_variable(self, token:
|
173
|
-
return
|
206
|
+
def _token_nud_variable(self, token: Token) -> Node:
|
207
|
+
return VariableRef(token['value'][1:])
|
174
208
|
|
175
|
-
def _token_nud_unquoted_identifier(self, token:
|
209
|
+
def _token_nud_unquoted_identifier(self, token: Token) -> Node:
|
176
210
|
if token['value'] == 'let' and self._current_token() == 'variable':
|
177
211
|
return self._parse_let_expression()
|
178
212
|
else:
|
179
|
-
return
|
213
|
+
return Field(token['value'])
|
180
214
|
|
181
|
-
def _parse_let_expression(self) ->
|
215
|
+
def _parse_let_expression(self) -> Node:
|
182
216
|
bindings = []
|
183
217
|
while True:
|
184
218
|
var_token = check.not_none(self._lookahead_token(0))
|
@@ -187,28 +221,28 @@ class Parser:
|
|
187
221
|
self._advance()
|
188
222
|
self._match('assign')
|
189
223
|
assign_expr = self._expression()
|
190
|
-
bindings.append(
|
224
|
+
bindings.append(Assign(varname, assign_expr))
|
191
225
|
if self._is_in_keyword(check.not_none(self._lookahead_token(0))):
|
192
226
|
self._advance()
|
193
227
|
break
|
194
228
|
else:
|
195
229
|
self._match('comma')
|
196
230
|
expr = self._expression()
|
197
|
-
return
|
231
|
+
return LetExpression(bindings, expr)
|
198
232
|
|
199
|
-
def _is_in_keyword(self, token:
|
233
|
+
def _is_in_keyword(self, token: Token) -> bool:
|
200
234
|
return (
|
201
235
|
token['type'] == 'unquoted_identifier' and
|
202
236
|
token['value'] == 'in'
|
203
237
|
)
|
204
238
|
|
205
|
-
def _token_nud_quoted_identifier(self, token:
|
206
|
-
field =
|
239
|
+
def _token_nud_quoted_identifier(self, token: Token) -> Node:
|
240
|
+
field = Field(token['value'])
|
207
241
|
|
208
242
|
# You can't have a quoted identifier as a function name.
|
209
243
|
if self._current_token() == 'lparen':
|
210
244
|
t = check.not_none(self._lookahead_token(0))
|
211
|
-
raise
|
245
|
+
raise ParseError(
|
212
246
|
0,
|
213
247
|
t['value'],
|
214
248
|
t['type'],
|
@@ -217,73 +251,74 @@ class Parser:
|
|
217
251
|
|
218
252
|
return field
|
219
253
|
|
220
|
-
def _token_nud_star(self, token:
|
221
|
-
left =
|
254
|
+
def _token_nud_star(self, token: Token) -> Node:
|
255
|
+
left = Identity()
|
256
|
+
right: Node
|
222
257
|
if self._current_token() == 'rbracket':
|
223
|
-
right =
|
258
|
+
right = Identity()
|
224
259
|
else:
|
225
260
|
right = self._parse_projection_rhs(self.BINDING_POWER['star'])
|
226
|
-
return
|
261
|
+
return ValueProjection(left, right)
|
227
262
|
|
228
|
-
def _token_nud_filter(self, token:
|
229
|
-
return self._token_led_filter(
|
263
|
+
def _token_nud_filter(self, token: Token) -> Node:
|
264
|
+
return self._token_led_filter(Identity())
|
230
265
|
|
231
|
-
def _token_nud_lbrace(self, token:
|
266
|
+
def _token_nud_lbrace(self, token: Token) -> Node:
|
232
267
|
return self._parse_multi_select_hash()
|
233
268
|
|
234
|
-
def _token_nud_lparen(self, token:
|
269
|
+
def _token_nud_lparen(self, token: Token) -> Node:
|
235
270
|
expression = self._expression()
|
236
271
|
self._match('rparen')
|
237
272
|
return expression
|
238
273
|
|
239
|
-
def _token_nud_minus(self, token:
|
274
|
+
def _token_nud_minus(self, token: Token) -> Node:
|
240
275
|
return self._parse_arithmetic_unary(token)
|
241
276
|
|
242
|
-
def _token_nud_plus(self, token:
|
277
|
+
def _token_nud_plus(self, token: Token) -> Node:
|
243
278
|
return self._parse_arithmetic_unary(token)
|
244
279
|
|
245
|
-
def _token_nud_flatten(self, token:
|
246
|
-
left =
|
280
|
+
def _token_nud_flatten(self, token: Token) -> Node:
|
281
|
+
left = Flatten(Identity())
|
247
282
|
right = self._parse_projection_rhs(
|
248
283
|
self.BINDING_POWER['flatten'])
|
249
|
-
return
|
284
|
+
return Projection(left, right)
|
250
285
|
|
251
|
-
def _token_nud_not(self, token:
|
286
|
+
def _token_nud_not(self, token: Token) -> Node:
|
252
287
|
expr = self._expression(self.BINDING_POWER['not'])
|
253
|
-
return
|
288
|
+
return NotExpression(expr)
|
254
289
|
|
255
|
-
def _token_nud_lbracket(self, token:
|
290
|
+
def _token_nud_lbracket(self, token: Token) -> Node:
|
256
291
|
if self._current_token() in ['number', 'colon']:
|
257
292
|
right = self._parse_index_expression()
|
258
293
|
# We could optimize this and remove the identity() node. We don't really need an index_expression node, we
|
259
294
|
# can just use emit an index node here if we're not dealing with a slice.
|
260
|
-
return self._project_if_slice(
|
295
|
+
return self._project_if_slice(Identity(), right)
|
261
296
|
|
262
297
|
elif self._current_token() == 'star' and self._lookahead(1) == 'rbracket':
|
263
298
|
self._advance()
|
264
299
|
self._advance()
|
265
300
|
right = self._parse_projection_rhs(self.BINDING_POWER['star'])
|
266
|
-
return
|
301
|
+
return Projection(Identity(), right)
|
267
302
|
|
268
303
|
else:
|
269
304
|
return self._parse_multi_select_list()
|
270
305
|
|
271
|
-
def _parse_index_expression(self) ->
|
306
|
+
def _parse_index_expression(self) -> Node:
|
272
307
|
# We're here:
|
273
308
|
# [<current>
|
274
309
|
# ^
|
275
310
|
# | current token
|
276
|
-
if
|
311
|
+
if self._lookahead(0) == 'colon' or self._lookahead(1) == 'colon':
|
277
312
|
return self._parse_slice_expression()
|
278
313
|
|
279
314
|
else:
|
280
315
|
# Parse the syntax [number]
|
281
|
-
node =
|
316
|
+
node = Index(check.not_none(self._lookahead_token(0))['value'])
|
282
317
|
self._advance()
|
283
318
|
self._match('rbracket')
|
284
319
|
return node
|
285
320
|
|
286
|
-
def _parse_slice_expression(self) ->
|
321
|
+
def _parse_slice_expression(self) -> Node:
|
287
322
|
# [start:end:step]
|
288
323
|
# Where start, end, and step are optional. The last colon is optional as well.
|
289
324
|
parts = [None, None, None]
|
@@ -306,60 +341,59 @@ class Parser:
|
|
306
341
|
current_token = self._current_token()
|
307
342
|
|
308
343
|
self._match('rbracket')
|
309
|
-
return
|
344
|
+
return Slice(*parts)
|
310
345
|
|
311
|
-
def _token_nud_current(self, token:
|
312
|
-
return
|
346
|
+
def _token_nud_current(self, token: Token) -> Node:
|
347
|
+
return CurrentNode()
|
313
348
|
|
314
|
-
def _token_nud_root(self, token:
|
315
|
-
return
|
349
|
+
def _token_nud_root(self, token: Token) -> Node:
|
350
|
+
return RootNode()
|
316
351
|
|
317
|
-
def _token_nud_expref(self, token:
|
352
|
+
def _token_nud_expref(self, token: Token) -> Node:
|
318
353
|
expression = self._expression(self.BINDING_POWER['expref'])
|
319
|
-
return
|
354
|
+
return Expref(expression)
|
320
355
|
|
321
|
-
def _token_led_dot(self, left:
|
356
|
+
def _token_led_dot(self, left: Node) -> Node:
|
322
357
|
if self._current_token() != 'star':
|
323
358
|
right = self._parse_dot_rhs(self.BINDING_POWER['dot'])
|
324
|
-
if left
|
325
|
-
left[
|
326
|
-
return left
|
359
|
+
if isinstance(left, Subexpression):
|
360
|
+
return dc.replace(left, children_nodes=[*left.children_nodes, right])
|
327
361
|
|
328
362
|
else:
|
329
|
-
return
|
363
|
+
return Subexpression([left, right])
|
330
364
|
|
331
365
|
else:
|
332
366
|
# We're creating a projection.
|
333
367
|
self._advance()
|
334
368
|
right = self._parse_projection_rhs(self.BINDING_POWER['dot'])
|
335
|
-
return
|
369
|
+
return ValueProjection(left, right)
|
336
370
|
|
337
|
-
def _token_led_pipe(self, left:
|
371
|
+
def _token_led_pipe(self, left: Node) -> Node:
|
338
372
|
right = self._expression(self.BINDING_POWER['pipe'])
|
339
|
-
return
|
373
|
+
return Pipe(left, right)
|
340
374
|
|
341
|
-
def _token_led_or(self, left:
|
375
|
+
def _token_led_or(self, left: Node) -> Node:
|
342
376
|
right = self._expression(self.BINDING_POWER['or'])
|
343
|
-
return
|
377
|
+
return OrExpression(left, right)
|
344
378
|
|
345
|
-
def _token_led_and(self, left:
|
379
|
+
def _token_led_and(self, left: Node) -> Node:
|
346
380
|
right = self._expression(self.BINDING_POWER['and'])
|
347
|
-
return
|
381
|
+
return AndExpression(left, right)
|
348
382
|
|
349
|
-
def _token_led_lparen(self, left:
|
350
|
-
if left
|
383
|
+
def _token_led_lparen(self, left: Node) -> Node:
|
384
|
+
if not isinstance(left, Field):
|
351
385
|
# 0 - first func arg or closing paren.
|
352
386
|
# -1 - '(' token
|
353
387
|
# -2 - invalid function "name".
|
354
388
|
prev_t = check.not_none(self._lookahead_token(-2))
|
355
|
-
raise
|
389
|
+
raise ParseError(
|
356
390
|
prev_t['start'],
|
357
391
|
prev_t['value'],
|
358
392
|
prev_t['type'],
|
359
393
|
f"Invalid function name '{prev_t['value']}'",
|
360
394
|
)
|
361
395
|
|
362
|
-
name = left
|
396
|
+
name = left.name
|
363
397
|
args = []
|
364
398
|
while self._current_token() != 'rparen':
|
365
399
|
expression = self._expression()
|
@@ -368,72 +402,72 @@ class Parser:
|
|
368
402
|
args.append(expression)
|
369
403
|
self._match('rparen')
|
370
404
|
|
371
|
-
function_node =
|
405
|
+
function_node = FunctionExpression(name, args)
|
372
406
|
return function_node
|
373
407
|
|
374
|
-
def _token_led_filter(self, left:
|
408
|
+
def _token_led_filter(self, left: Node) -> Node:
|
375
409
|
# Filters are projections.
|
376
410
|
condition = self._expression(0)
|
377
411
|
self._match('rbracket')
|
412
|
+
right: Node
|
378
413
|
if self._current_token() == 'flatten':
|
379
|
-
right =
|
414
|
+
right = Identity()
|
380
415
|
else:
|
381
416
|
right = self._parse_projection_rhs(self.BINDING_POWER['filter'])
|
382
|
-
return
|
417
|
+
return FilterProjection(left, right, condition)
|
383
418
|
|
384
|
-
def _token_led_eq(self, left:
|
419
|
+
def _token_led_eq(self, left: Node) -> Node:
|
385
420
|
return self._parse_comparator(left, 'eq')
|
386
421
|
|
387
|
-
def _token_led_ne(self, left:
|
422
|
+
def _token_led_ne(self, left: Node) -> Node:
|
388
423
|
return self._parse_comparator(left, 'ne')
|
389
424
|
|
390
|
-
def _token_led_gt(self, left:
|
425
|
+
def _token_led_gt(self, left: Node) -> Node:
|
391
426
|
return self._parse_comparator(left, 'gt')
|
392
427
|
|
393
|
-
def _token_led_gte(self, left:
|
428
|
+
def _token_led_gte(self, left: Node) -> Node:
|
394
429
|
return self._parse_comparator(left, 'gte')
|
395
430
|
|
396
|
-
def _token_led_lt(self, left:
|
431
|
+
def _token_led_lt(self, left: Node) -> Node:
|
397
432
|
return self._parse_comparator(left, 'lt')
|
398
433
|
|
399
|
-
def _token_led_lte(self, left:
|
434
|
+
def _token_led_lte(self, left: Node) -> Node:
|
400
435
|
return self._parse_comparator(left, 'lte')
|
401
436
|
|
402
|
-
def _token_led_div(self, left:
|
437
|
+
def _token_led_div(self, left: Node) -> Node:
|
403
438
|
return self._parse_arithmetic(left, 'div')
|
404
439
|
|
405
|
-
def _token_led_divide(self, left:
|
440
|
+
def _token_led_divide(self, left: Node) -> Node:
|
406
441
|
return self._parse_arithmetic(left, 'divide')
|
407
442
|
|
408
|
-
def _token_led_minus(self, left:
|
443
|
+
def _token_led_minus(self, left: Node) -> Node:
|
409
444
|
return self._parse_arithmetic(left, 'minus')
|
410
445
|
|
411
|
-
def _token_led_modulo(self, left:
|
446
|
+
def _token_led_modulo(self, left: Node) -> Node:
|
412
447
|
return self._parse_arithmetic(left, 'modulo')
|
413
448
|
|
414
|
-
def _token_led_multiply(self, left:
|
449
|
+
def _token_led_multiply(self, left: Node) -> Node:
|
415
450
|
return self._parse_arithmetic(left, 'multiply')
|
416
451
|
|
417
|
-
def _token_led_plus(self, left:
|
452
|
+
def _token_led_plus(self, left: Node) -> Node:
|
418
453
|
return self._parse_arithmetic(left, 'plus')
|
419
454
|
|
420
|
-
def _token_led_star(self, left:
|
455
|
+
def _token_led_star(self, left: Node) -> Node:
|
421
456
|
return self._parse_arithmetic(left, 'multiply')
|
422
457
|
|
423
|
-
def _token_led_flatten(self, left:
|
424
|
-
left =
|
458
|
+
def _token_led_flatten(self, left: Node) -> Node:
|
459
|
+
left = Flatten(left)
|
425
460
|
right = self._parse_projection_rhs(self.BINDING_POWER['flatten'])
|
426
|
-
return
|
461
|
+
return Projection(left, right)
|
427
462
|
|
428
|
-
def _token_led_lbracket(self, left:
|
463
|
+
def _token_led_lbracket(self, left: Node) -> Node:
|
429
464
|
token = check.not_none(self._lookahead_token(0))
|
430
465
|
if token['type'] in ['number', 'colon']:
|
431
466
|
right = self._parse_index_expression()
|
432
|
-
if left
|
467
|
+
if isinstance(left, IndexExpression):
|
433
468
|
# Optimization: if the left node is an index expr, we can avoid creating another node and instead just
|
434
469
|
# add the right node as a child of the left.
|
435
|
-
left[
|
436
|
-
return left
|
470
|
+
return dc.replace(left, nodes=[*left.nodes, right])
|
437
471
|
|
438
472
|
else:
|
439
473
|
return self._project_if_slice(left, right)
|
@@ -443,32 +477,32 @@ class Parser:
|
|
443
477
|
self._match('star')
|
444
478
|
self._match('rbracket')
|
445
479
|
right = self._parse_projection_rhs(self.BINDING_POWER['star'])
|
446
|
-
return
|
480
|
+
return Projection(left, right)
|
447
481
|
|
448
|
-
def _project_if_slice(self, left:
|
449
|
-
index_expr =
|
450
|
-
if right
|
451
|
-
return
|
482
|
+
def _project_if_slice(self, left: Node, right: Node) -> Node:
|
483
|
+
index_expr = IndexExpression([left, right])
|
484
|
+
if isinstance(right, Slice):
|
485
|
+
return Projection(
|
452
486
|
index_expr,
|
453
487
|
self._parse_projection_rhs(self.BINDING_POWER['star']),
|
454
488
|
)
|
455
489
|
else:
|
456
490
|
return index_expr
|
457
491
|
|
458
|
-
def _parse_comparator(self, left:
|
492
|
+
def _parse_comparator(self, left: Node, comparator: str) -> Node:
|
459
493
|
right = self._expression(self.BINDING_POWER[comparator])
|
460
|
-
return
|
494
|
+
return Comparator(comparator, left, right)
|
461
495
|
|
462
|
-
def _parse_arithmetic_unary(self, token:
|
496
|
+
def _parse_arithmetic_unary(self, token: Token) -> Node:
|
463
497
|
expression = self._expression(self.BINDING_POWER[token['type']])
|
464
|
-
return
|
498
|
+
return ArithmeticUnary(token['type'], expression)
|
465
499
|
|
466
|
-
def _parse_arithmetic(self, left:
|
500
|
+
def _parse_arithmetic(self, left: Node, operator: str) -> Node:
|
467
501
|
right = self._expression(self.BINDING_POWER[operator])
|
468
|
-
return
|
502
|
+
return Arithmetic(operator, left, right)
|
469
503
|
|
470
|
-
def _parse_multi_select_list(self) ->
|
471
|
-
expressions: list[
|
504
|
+
def _parse_multi_select_list(self) -> Node:
|
505
|
+
expressions: list[Node] = []
|
472
506
|
while True:
|
473
507
|
expression = self._expression()
|
474
508
|
expressions.append(expression)
|
@@ -477,10 +511,10 @@ class Parser:
|
|
477
511
|
else:
|
478
512
|
self._match('comma')
|
479
513
|
self._match('rbracket')
|
480
|
-
return
|
514
|
+
return MultiSelectList(expressions)
|
481
515
|
|
482
|
-
def _parse_multi_select_hash(self) ->
|
483
|
-
pairs = []
|
516
|
+
def _parse_multi_select_hash(self) -> Node:
|
517
|
+
pairs: list[KeyValPair] = []
|
484
518
|
while True:
|
485
519
|
key_token = check.not_none(self._lookahead_token(0))
|
486
520
|
|
@@ -491,7 +525,7 @@ class Parser:
|
|
491
525
|
self._match('colon')
|
492
526
|
value = self._expression(0)
|
493
527
|
|
494
|
-
node =
|
528
|
+
node = KeyValPair(key_name=key_name, node=value)
|
495
529
|
|
496
530
|
pairs.append(node)
|
497
531
|
if self._current_token() == 'comma':
|
@@ -501,13 +535,15 @@ class Parser:
|
|
501
535
|
self._match('rbrace')
|
502
536
|
break
|
503
537
|
|
504
|
-
return
|
538
|
+
return MultiSelectDict(nodes=pairs)
|
539
|
+
|
540
|
+
def _parse_projection_rhs(self, binding_power: int) -> Node:
|
541
|
+
right: Node
|
505
542
|
|
506
|
-
def _parse_projection_rhs(self, binding_power: int) -> ast.Node:
|
507
543
|
# Parse the right hand side of the projection.
|
508
544
|
if self.BINDING_POWER[self._current_token()] < self._PROJECTION_STOP:
|
509
545
|
# BP of 10 are all the tokens that stop a projection.
|
510
|
-
right =
|
546
|
+
right = Identity()
|
511
547
|
|
512
548
|
elif self._current_token() == 'lbracket':
|
513
549
|
right = self._expression(binding_power)
|
@@ -524,7 +560,7 @@ class Parser:
|
|
524
560
|
|
525
561
|
return right
|
526
562
|
|
527
|
-
def _parse_dot_rhs(self, binding_power: int) ->
|
563
|
+
def _parse_dot_rhs(self, binding_power: int) -> Node:
|
528
564
|
# From the grammar:
|
529
565
|
# expression '.' ( identifier /
|
530
566
|
# multi-select-list /
|
@@ -553,9 +589,9 @@ class Parser:
|
|
553
589
|
self._raise_parse_error_for_token(t, msg)
|
554
590
|
raise RuntimeError # noqa
|
555
591
|
|
556
|
-
def _error_nud_token(self, token:
|
592
|
+
def _error_nud_token(self, token: Token) -> ta.NoReturn:
|
557
593
|
if token['type'] == 'eof':
|
558
|
-
raise
|
594
|
+
raise IncompleteExpressionError(
|
559
595
|
token['start'],
|
560
596
|
token['value'],
|
561
597
|
token['type'],
|
@@ -563,7 +599,7 @@ class Parser:
|
|
563
599
|
|
564
600
|
self._raise_parse_error_for_token(token, 'invalid token')
|
565
601
|
|
566
|
-
def _error_led_token(self, token:
|
602
|
+
def _error_led_token(self, token: Token) -> ta.NoReturn:
|
567
603
|
self._raise_parse_error_for_token(token, 'invalid token')
|
568
604
|
|
569
605
|
def _match(self, token_type: str | None = None) -> None:
|
@@ -588,14 +624,14 @@ class Parser:
|
|
588
624
|
def _lookahead(self, number: int) -> str:
|
589
625
|
return check.not_none(self._tokens[self._index + number])['type']
|
590
626
|
|
591
|
-
def _lookahead_token(self, number: int) ->
|
627
|
+
def _lookahead_token(self, number: int) -> Token | None:
|
592
628
|
return self._tokens[self._index + number]
|
593
629
|
|
594
|
-
def _raise_parse_error_for_token(self, token:
|
630
|
+
def _raise_parse_error_for_token(self, token: Token, reason: str) -> ta.NoReturn:
|
595
631
|
lex_position = token['start']
|
596
632
|
actual_value = token['value']
|
597
633
|
actual_type = token['type']
|
598
|
-
raise
|
634
|
+
raise ParseError(
|
599
635
|
lex_position,
|
600
636
|
actual_value,
|
601
637
|
actual_type,
|
@@ -607,14 +643,14 @@ class Parser:
|
|
607
643
|
actual_value = token['value']
|
608
644
|
actual_type = token['type']
|
609
645
|
if actual_type == 'eof':
|
610
|
-
raise
|
646
|
+
raise IncompleteExpressionError(
|
611
647
|
lex_position,
|
612
648
|
actual_value,
|
613
649
|
actual_type,
|
614
650
|
)
|
615
651
|
|
616
652
|
message = f'Expecting: {expected_type}, got: {actual_type}'
|
617
|
-
raise
|
653
|
+
raise ParseError(
|
618
654
|
lex_position,
|
619
655
|
actual_value,
|
620
656
|
actual_type,
|
@@ -627,21 +663,21 @@ class Parser:
|
|
627
663
|
self._CACHE.pop(key, None)
|
628
664
|
|
629
665
|
@classmethod
|
630
|
-
def purge(cls):
|
666
|
+
def purge(cls) -> None:
|
631
667
|
"""Clear the expression compilation cache."""
|
632
668
|
|
633
669
|
cls._CACHE.clear()
|
634
670
|
|
635
671
|
|
636
672
|
class ParsedResult:
|
637
|
-
def __init__(self, expression: str, parsed:
|
673
|
+
def __init__(self, expression: str, parsed: Node) -> None:
|
638
674
|
super().__init__()
|
639
675
|
|
640
676
|
self.expression = expression
|
641
677
|
self.parsed = parsed
|
642
678
|
|
643
|
-
def search(self, value: ta.Any, options:
|
644
|
-
evaluator =
|
679
|
+
def search(self, value: ta.Any, options: Options | None = None) -> ta.Any:
|
680
|
+
evaluator = TreeInterpreter(options)
|
645
681
|
return evaluator.evaluate(self.parsed, value)
|
646
682
|
|
647
683
|
def _render_dot_file(self) -> str:
|
@@ -653,7 +689,7 @@ class ParsedResult:
|
|
653
689
|
the public supported API. Use at your own risk.
|
654
690
|
"""
|
655
691
|
|
656
|
-
renderer =
|
692
|
+
renderer = GraphvizVisitor()
|
657
693
|
contents = renderer.visit(self.parsed)
|
658
694
|
return contents
|
659
695
|
|
@@ -661,9 +697,9 @@ class ParsedResult:
|
|
661
697
|
return repr(self.parsed)
|
662
698
|
|
663
699
|
|
664
|
-
def compile(expression: str, options:
|
700
|
+
def compile(expression: str, options: Options | None = None) -> ParsedResult: # noqa
|
665
701
|
return Parser().parse(expression, options=options)
|
666
702
|
|
667
703
|
|
668
|
-
def search(expression: str, data: ta.Any, options:
|
704
|
+
def search(expression: str, data: ta.Any, options: Options | None = None) -> ta.Any:
|
669
705
|
return compile(expression, options).search(data, options=options)
|