Skip to content

Commit

Permalink
Added regex match operator '=~'
Browse files Browse the repository at this point in the history
  • Loading branch information
tmshn committed Oct 11, 2017
1 parent 47a4316 commit ccb2158
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 2 deletions.
7 changes: 6 additions & 1 deletion jmespath/lexer.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,15 @@ def tokenize(self, expression):
elif self._current == '!':
yield self._match_or_else('=', 'ne', 'not')
elif self._current == '=':
if self._next() == '=':
next_char = self._next()
if next_char == '=':
yield {'type': 'eq', 'value': '==',
'start': self._position - 1, 'end': self._position}
self._next()
elif next_char == '~':
yield {'type': 'regex_match', 'value': '=~',
'start': self._position - 1, 'end': self._position}
self._next()
else:
if self._current is None:
# If we're at the EOF, we never advanced
Expand Down
4 changes: 4 additions & 0 deletions jmespath/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class Parser(object):
'gte': 5,
'lte': 5,
'ne': 5,
'regex_match': 5,
'flatten': 9,
# Everything above stops a projection.
'star': 20,
Expand Down Expand Up @@ -306,6 +307,9 @@ def _token_led_eq(self, left):
def _token_led_ne(self, left):
return self._parse_comparator(left, 'ne')

def _token_led_regex_match(self, left):
return self._parse_comparator(left, 'regex_match')

def _token_led_gt(self, left):
return self._parse_comparator(left, 'gt')

Expand Down
15 changes: 14 additions & 1 deletion jmespath/visitor.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import operator
import re

from jmespath import functions
from jmespath.compat import string_type
Expand All @@ -12,6 +13,17 @@ def _equals(x, y):
return x == y


def _regex_match(lhs, rhs):
try:
if hasattr(rhs, 'search'):
return rhs.search(lhs) is not None
if hasattr(lhs, 'search'):
return lhs.search(rhs) is not None
return re.search(rhs, lhs) is not None
except TypeError:
return None


def _is_special_integer_case(x, y):
# We need to special case comparing 0 or 1 to
# True/False. While normally comparing any
Expand Down Expand Up @@ -101,12 +113,13 @@ class TreeInterpreter(Visitor):
COMPARATOR_FUNC = {
'eq': _equals,
'ne': lambda x, y: not _equals(x, y),
'regex_match': _regex_match,
'lt': operator.lt,
'gt': operator.gt,
'lte': operator.le,
'gte': operator.ge
}
_EQUALITY_OPS = ['eq', 'ne']
_EQUALITY_OPS = ['eq', 'ne', 'regex_match']
MAP_TYPE = dict

def __init__(self, options=None):
Expand Down
49 changes: 49 additions & 0 deletions tests/compliance/filters.json
Original file line number Diff line number Diff line change
Expand Up @@ -464,5 +464,54 @@
"result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
}
]
},
{
"given": {
"foo": [
{"name": "ax"},
{"name": "Ax"},
{"name": "bx"},
{"name": "Bx"}
]
},
"cases": [
{
"comment": "Using regex in a filter expression",
"expression": "foo[? name =~ '^a']",
"result": [
{"name": "ax"}
]
},
{
"comment": "Using regex in a filter expression (pre-compiled)",
"expression": "foo[? name =~ /^a/]",
"result": [
{"name": "ax"}
]
},
{
"comment": "Using regex in a filter expression (pre-compiled with flag)",
"expression": "foo[? name =~ /^a/i]",
"result": [
{"name": "ax"},
{"name": "Ax"}
]
},
{
"comment": "Using regex as a lhs in a filter expression (pre-compiled)",
"expression": "foo[? /^a/ =~ name]",
"result": [
{"name": "ax"}
]
},
{
"comment": "Using regex as a lhs in a filter expression (pre-compiled with flag)",
"expression": "foo[? /^a/i =~ name]",
"result": [
{"name": "ax"},
{"name": "Ax"}
]
}
]
}
]

0 comments on commit ccb2158

Please sign in to comment.