Skip to content

Commit cdb57fa

Browse files
committed
Implemented '$' root-node token.
1 parent 1d5ade0 commit cdb57fa

File tree

3 files changed

+54
-8
lines changed

3 files changed

+54
-8
lines changed

jmespath/parser.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"""
2828
import random
2929

30+
3031
from jmespath import lexer
3132
from jmespath.compat import with_repr_method
3233
from jmespath import ast
@@ -509,9 +510,8 @@ def __init__(self, expression, parsed):
509510
self.parsed = parsed
510511

511512
def search(self, value, options=None):
512-
interpreter = visitor.ScopedInterpreter(options)
513-
result = interpreter.visit(self.parsed, value)
514-
return result
513+
evaluator = visitor.ScopedInterpreter(options)
514+
return evaluator.evaluate(self.parsed, value)
515515

516516
def _render_dot_file(self):
517517
"""Render the parsed AST as a dot file.
@@ -526,6 +526,3 @@ def _render_dot_file(self):
526526
renderer = visitor.GraphvizVisitor()
527527
contents = renderer.visit(self.parsed)
528528
return contents
529-
530-
def __repr__(self):
531-
return repr(self.parsed)

jmespath/visitor.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from jmespath import functions
44
from jmespath.compat import string_type
5+
from jmespath.compat import with_str_method
56
from numbers import Number
67

78

@@ -172,6 +173,11 @@ def visit_comparator(self, node, value):
172173
def visit_current(self, node, value):
173174
return value
174175

176+
def visit_root(self, *args, **kwargs):
177+
if 'scopes' in kwargs:
178+
return kwargs['scopes'].getValue('$')
179+
return None
180+
175181
def visit_expref(self, node, value):
176182
return _Expression(node['children'][0], self, value)
177183

@@ -340,6 +346,7 @@ def _visit(self, node, current):
340346
self._visit(child, child_name)
341347

342348

349+
@with_str_method
343350
class Scopes:
344351
def __init__(self):
345352
self._scopes = []
@@ -357,15 +364,22 @@ def getValue(self, identifier):
357364
return scope[identifier]
358365
return None
359366

367+
def __str__(self):
368+
return '{}'.format(self._scopes)
369+
360370

361371
class ScopedInterpreter(TreeInterpreter):
362372
def __init__(self, options = None):
363373
super().__init__(options)
364374
self._scopes = Scopes()
365375

376+
def evaluate(self, ast, root_scope):
377+
self._scopes.pushScope({'$': root_scope})
378+
return self.visit(ast, root_scope)
379+
366380
def visit(self, node, *args, **kwargs):
367-
node_type = node['type']
368-
if (node_type in ['field', 'function_expression']):
381+
scoped_types = ['field', 'function_expression', 'root']
382+
if (node['type'] in scoped_types):
369383
kwargs.update({'scopes': self._scopes})
370384
else:
371385
if 'scopes' in kwargs:

tests/compliance/lexical_scoping.json

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,5 +166,40 @@
166166
"result": "fourth"
167167
}
168168
]
169+
},
170+
{
171+
"given": {
172+
"first_choice": "WA",
173+
"states": [
174+
{
175+
"name": "WA",
176+
"cities": [
177+
"Seattle",
178+
"Bellevue",
179+
"Olympia"
180+
]
181+
},
182+
{
183+
"name": "CA",
184+
"cities": [
185+
"Los Angeles",
186+
"San Francisco"
187+
]
188+
},
189+
{
190+
"name": "NY",
191+
"cities": [
192+
"New York City",
193+
"Albany"
194+
]
195+
}
196+
]
197+
},
198+
"cases": [
199+
{
200+
"expression": "states[?name==$.first_choice].cities[]",
201+
"result": ["Seattle", "Bellevue", "Olympia"]
202+
}
203+
]
169204
}
170205
]

0 commit comments

Comments
 (0)