Skip to content

Commit 8aea378

Browse files
committed
[parser] Supports arithmetic expressions
1 parent 6debb36 commit 8aea378

File tree

3 files changed

+99
-0
lines changed

3 files changed

+99
-0
lines changed

jmespath/ast.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22
# {"type": <node type>", children: [], "value": ""}
33

44

5+
def arithmetic_unary(operator, expression):
6+
return {'type': 'arithmetic_unary', 'children': [expression], 'value': operator}
7+
8+
9+
def arithmetic(operator, left, right):
10+
return {'type': 'arithmetic', 'children': [left, right], 'value': operator}
11+
12+
513
def comparator(name, first, second):
614
return {'type': 'comparator', 'children': [first, second], 'value': name}
715

jmespath/parser.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ class Parser(object):
5757
'gte': 5,
5858
'lte': 5,
5959
'ne': 5,
60+
'minus': 6,
61+
'plus': 6,
62+
'div': 7,
63+
'divide': 7,
64+
'modulo': 7,
65+
'multiply': 7,
6066
'flatten': 9,
6167
# Everything above stops a projection.
6268
'star': 20,
@@ -170,6 +176,12 @@ def _token_nud_lparen(self, token):
170176
self._match('rparen')
171177
return expression
172178

179+
def _token_nud_minus(self, token):
180+
return self._parse_arithmetic_unary(token)
181+
182+
def _token_nud_plus(self, token):
183+
return self._parse_arithmetic_unary(token)
184+
173185
def _token_nud_flatten(self, token):
174186
left = ast.flatten(ast.identity())
175187
right = self._parse_projection_rhs(
@@ -318,6 +330,27 @@ def _token_led_lt(self, left):
318330
def _token_led_lte(self, left):
319331
return self._parse_comparator(left, 'lte')
320332

333+
def _token_led_div(self, left):
334+
return self._parse_arithmetic(left, 'div')
335+
336+
def _token_led_divide(self, left):
337+
return self._parse_arithmetic(left, 'divide')
338+
339+
def _token_led_minus(self, left):
340+
return self._parse_arithmetic(left, 'minus')
341+
342+
def _token_led_modulo(self, left):
343+
return self._parse_arithmetic(left, 'modulo')
344+
345+
def _token_led_multiply(self, left):
346+
return self._parse_arithmetic(left, 'multiply')
347+
348+
def _token_led_plus(self, left):
349+
return self._parse_arithmetic(left, 'plus')
350+
351+
def _token_led_star(self, left):
352+
return self._parse_arithmetic(left, 'multiply')
353+
321354
def _token_led_flatten(self, left):
322355
left = ast.flatten(left)
323356
right = self._parse_projection_rhs(
@@ -356,6 +389,14 @@ def _parse_comparator(self, left, comparator):
356389
right = self._expression(self.BINDING_POWER[comparator])
357390
return ast.comparator(comparator, left, right)
358391

392+
def _parse_arithmetic_unary(self, token):
393+
expression = self._expression(self.BINDING_POWER[token['type']])
394+
return ast.arithmetic_unary(token['type'], expression)
395+
396+
def _parse_arithmetic(self, left, operator):
397+
right = self._expression(self.BINDING_POWER[operator])
398+
return ast.arithmetic(operator, left, right)
399+
359400
def _parse_multi_select_list(self):
360401
expressions = []
361402
while True:

tests/test_parser.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import random
44
import string
55
import threading
6+
from jmespath.ast import arithmetic
67
from tests import unittest, OrderedDict
78

89
from jmespath import parser
@@ -77,6 +78,55 @@ def test_or_repr(self):
7778
self.assert_parsed_ast('foo || bar', ast.or_expression(ast.field('foo'),
7879
ast.field('bar')))
7980

81+
def test_arithmetic_expressions(self):
82+
operations = {
83+
'+': 'plus',
84+
'-': 'minus',
85+
'//': 'div',
86+
'/': 'divide',
87+
'%': 'modulo',
88+
u'\u2212': 'minus',
89+
u'\u00d7': 'multiply',
90+
u'\u00f7': 'divide',
91+
}
92+
for sign in operations:
93+
operation = operations[sign]
94+
expression = 'foo {} bar'.format(sign)
95+
print(expression)
96+
self.assert_parsed_ast(
97+
expression,
98+
ast.arithmetic(
99+
operation,
100+
ast.field('foo'),
101+
ast.field('bar')
102+
))
103+
104+
def test_arithmetic_unary(self):
105+
operations = {
106+
'+': 'plus',
107+
'-': 'minus',
108+
u'\u2212': 'minus',
109+
}
110+
for sign in operations:
111+
operation = operations[sign]
112+
expression = '{} foo'.format(sign)
113+
print(expression)
114+
self.assert_parsed_ast(
115+
expression,
116+
ast.arithmetic_unary(
117+
operation,
118+
ast.field('foo'),
119+
))
120+
121+
def test_arithmetic_multiplication(self):
122+
self.assert_parsed_ast(
123+
'foo * bar',
124+
ast.arithmetic(
125+
'multiply',
126+
ast.field('foo'),
127+
ast.field('bar')
128+
))
129+
80130
def test_unicode_literals_escaped(self):
81131
self.assert_parsed_ast(r'`"\u2713"`', ast.literal(u'\u2713'))
82132

0 commit comments

Comments
 (0)