diff options
author | Kenn Knowles <kenn.knowles@gmail.com> | 2013-03-27 15:39:18 -0400 |
---|---|---|
committer | Kenn Knowles <kenn.knowles@gmail.com> | 2013-03-27 15:39:18 -0400 |
commit | d9d2a097975e003fb4ac8517393b9bf0811d3d26 (patch) | |
tree | ecabafff690126ba2adf13bc161ab7f40b5e0c08 | |
parent | 5219ef90ea7540604ce475a18e4a6e140bd8b710 (diff) | |
download | jsonpath-rw-d9d2a097975e003fb4ac8517393b9bf0811d3d26.tar.gz |
Switch @ for `this` since it is so common in field names (for example when JSON is generated from XML)
-rw-r--r-- | jsonpath_rw/jsonpath.py | 2 | ||||
-rw-r--r-- | jsonpath_rw/lexer.py | 28 | ||||
-rw-r--r-- | jsonpath_rw/parser.py | 9 | ||||
-rw-r--r-- | tests/test_jsonpath.py | 21 | ||||
-rw-r--r-- | tests/test_lexer.py | 2 |
5 files changed, 44 insertions, 18 deletions
diff --git a/jsonpath_rw/jsonpath.py b/jsonpath_rw/jsonpath.py index 563e147..8af0e87 100644 --- a/jsonpath_rw/jsonpath.py +++ b/jsonpath_rw/jsonpath.py @@ -198,7 +198,7 @@ class This(JSONPath): return val def __str__(self): - return '@' + return '`this`' def __repr__(self): return 'This()' diff --git a/jsonpath_rw/lexer.py b/jsonpath_rw/lexer.py index c7e42fd..35a4305 100644 --- a/jsonpath_rw/lexer.py +++ b/jsonpath_rw/lexer.py @@ -37,21 +37,22 @@ class JsonPathLexer(object): # # Anyhow, it is pythonic to give some rope to hang oneself with :-) - literals = ['*', '.', '[', ']', '(', ')', '$', ',', ':', '|', '&', '@'] + literals = ['*', '.', '[', ']', '(', ')', '$', ',', ':', '|', '&'] reserved_words = { 'where': 'WHERE' } - tokens = ['DOUBLEDOT', 'NUMBER', 'ID'] + list(reserved_words.values()) + tokens = ['DOUBLEDOT', 'NUMBER', 'ID', 'NAMED_OPERATOR'] + list(reserved_words.values()) states = [ ('singlequote', 'exclusive'), - ('doublequote', 'exclusive') ] + ('doublequote', 'exclusive'), + ('backquote', 'exclusive') ] # Normal lexing, rather easy t_DOUBLEDOT = r'\.\.' t_ignore = ' \t' def t_ID(self, t): - r'[a-zA-Z_][a-zA-Z0-9_]*' + r'[a-zA-Z_@][a-zA-Z0-9_@]*' t.type = self.reserved_words.get(t.value, 'ID') return t @@ -95,6 +96,25 @@ class JsonPathLexer(object): def t_doublequote_error(self, t): raise Exception('Error on line %s, col %s while lexing doublequoted field: Unexpected character: %s ' % (t.lexer.lineno, t.lexpos - t.latest_newline, t.value[0])) + + # Back-quoted "magic" operators + t_backquote_ignore = '' + def t_BACKQUOTE(self, t): + r'`' + t.lexer.string_start = t.lexer.lexpos + t.lexer.push_state('backquote') + + def t_backquote_BACKQUOTE(self, t): + r'([^`]|\\`)*`' + t.value = t.value[:-1] + t.type = 'NAMED_OPERATOR' + t.lexer.pop_state() + return t + + def t_backquote_error(self, t): + raise Exception('Error on line %s, col %s while lexing backquoted operator: Unexpected character: %s ' % (t.lexer.lineno, t.lexpos - t.latest_newline, t.value[0])) + + # Counting lines, handling errors def t_newline(self, t): r'\n' diff --git a/jsonpath_rw/parser.py b/jsonpath_rw/parser.py index b8c1231..85d05d9 100644 --- a/jsonpath_rw/parser.py +++ b/jsonpath_rw/parser.py @@ -84,9 +84,12 @@ class JsonPathParser(object): "jsonpath : fields_or_any" p[0] = Fields(*p[1]) - def p_jsonpath_this(self, p): - "jsonpath : '@'" - p[0] = This() + def p_jsonpath_named_operator(self, p): + "jsonpath : NAMED_OPERATOR" + if p[1] == 'this': + p[0] = This() + else: + raise Exception('Unknown named operator `%s` at %s:%s' % (t.value, t.lineno, t.col)) def p_jsonpath_root(self, p): "jsonpath : '$'" diff --git a/tests/test_jsonpath.py b/tests/test_jsonpath.py index fd7cf32..c97c096 100644 --- a/tests/test_jsonpath.py +++ b/tests/test_jsonpath.py @@ -103,10 +103,11 @@ class TestJsonPath(unittest.TestCase): jsonpath.auto_id_field = None self.check_cases([ ('foo', {'foo': 'baz'}, ['baz']), ('foo,baz', {'foo': 1, 'baz': 2}, [1, 2]), + ('@foo', {'@foo': 1}, [1]), ('*', {'foo': 1, 'baz': 2}, set([1, 2])) ]) jsonpath.auto_id_field = 'id' - self.check_cases([ ('*', {'foo': 1, 'baz': 2}, set([1, 2, '@'])) ]) + self.check_cases([ ('*', {'foo': 1, 'baz': 2}, set([1, 2, '`this`'])) ]) def test_root_value(self): jsonpath.auto_id_field = None @@ -119,9 +120,9 @@ class TestJsonPath(unittest.TestCase): def test_this_value(self): jsonpath.auto_id_field = None self.check_cases([ - ('@', {'foo': 'baz'}, [{'foo':'baz'}]), - ('foo.@', {'foo': 'baz'}, ['baz']), - ('foo.@.baz', {'foo': {'baz': 3}}, [3]), + ('`this`', {'foo': 'baz'}, [{'foo':'baz'}]), + ('foo.`this`', {'foo': 'baz'}, ['baz']), + ('foo.`this`.baz', {'foo': {'baz': 3}}, [3]), ]) def test_index_value(self): @@ -194,9 +195,9 @@ class TestJsonPath(unittest.TestCase): def test_this_paths(self): jsonpath.auto_id_field = None self.check_paths([ - ('@', {'foo': 'baz'}, ['@']), - ('foo.@', {'foo': 'baz'}, ['foo']), - ('foo.@.baz', {'foo': {'baz': 3}}, ['foo.baz']), + ('`this`', {'foo': 'baz'}, ['`this`']), + ('foo.`this`', {'foo': 'baz'}, ['foo']), + ('foo.`this`.baz', {'foo': {'baz': 3}}, ['foo.baz']), ]) def test_index_paths(self): @@ -240,9 +241,9 @@ class TestJsonPath(unittest.TestCase): def test_this_auto_id(self): jsonpath.auto_id_field = 'id' self.check_cases([ - ('id', {'foo': 'baz'}, ['@']), # This is, again, a wonky case that is not that interesting - ('foo.@.id', {'foo': 'baz'}, ['foo']), - ('foo.@.baz.id', {'foo': {'baz': 3}}, ['foo.baz']), + ('id', {'foo': 'baz'}, ['`this`']), # This is, again, a wonky case that is not that interesting + ('foo.`this`.id', {'foo': 'baz'}, ['foo']), + ('foo.`this`.baz.id', {'foo': {'baz': 3}}, ['foo.baz']), ]) def test_index_auto_id(self): diff --git a/tests/test_lexer.py b/tests/test_lexer.py index 53ad7ef..c1ddc0f 100644 --- a/tests/test_lexer.py +++ b/tests/test_lexer.py @@ -43,5 +43,7 @@ class TestLexer(unittest.TestCase): self.assert_lex_equiv('fuzz.*', [self.token('fuzz', 'ID'), self.token('.', '.'), self.token('*', '*')]) self.assert_lex_equiv('fuzz..bang', [self.token('fuzz', 'ID'), self.token('..', 'DOUBLEDOT'), self.token('bang', 'ID')]) self.assert_lex_equiv('&', [self.token('&', '&')]) + self.assert_lex_equiv('@', [self.token('@', 'ID')]) + self.assert_lex_equiv('`this`', [self.token('this', 'NAMED_OPERATOR')]) self.assert_lex_equiv('|', [self.token('|', '|')]) self.assert_lex_equiv('where', [self.token('where', 'WHERE')]) |