Mercurial > jpath
changeset 1:1e04d0ab763b
implemented ellipsis ..
| author | sheep@wintermute |
|---|---|
| date | Wed, 14 Apr 2010 11:03:13 +0200 |
| parents | f39bd05f3be9 |
| children | 5b63c545fa4a |
| files | jpath.py |
| diffstat | 1 files changed, 78 insertions(+), 22 deletions(-) [+] |
line wrap: on
line diff
--- a/jpath.py Wed Apr 14 10:04:35 2010 +0200 +++ b/jpath.py Wed Apr 14 11:03:13 2010 +0200 @@ -12,6 +12,7 @@ import re import itertools import operator +import logging class Token: @@ -25,6 +26,7 @@ """Marks a token class to be used in tokenizer.""" cls.tokens[token_class.name] = token_class + return cls @classmethod def tokenize(cls, jpath): @@ -61,21 +63,21 @@ under token's value in data. """ - if not tokens: - yield data[self.value] - else: + try: token, *tokens = tokens - for value in token.parse(tokens, data[self.value]): - yield value + except ValueError: + token = NoneToken() + for value in token.parse(tokens, data[self.value]): + yield value @Token.use class FragmentToken(Token): """foo.bar.baz""" - regexp = r'(^|\.)(?P<fragment_name>[a-zA-Z0-9_]+)' + regexp = r'(^|(?<=\.\.)|\.)(?P<fragment_name>[a-zA-Z0-9_]+)' name = 'fragment' - priority = 10 + priority = 50 def __init__(self, fragment, fragment_name): self.value = fragment_name @@ -87,7 +89,7 @@ regexp = r'\["(?P<element_name>([^"]|\\")*)"\]' name = 'element' - priority = 10 + priority = 50 def __init__(self, element, element_name): """ @@ -107,7 +109,7 @@ regexp = r'\[(?P<index_value>-?[0-9]+)\]' name = 'index' - priority = 10 + priority = 50 def __init__(self, index, index_value): self.value = int(index_value) @@ -117,7 +119,7 @@ class WildcardToken(Token): """*[*].*, may return multiple results""" - regexp = r'(^|\.)\*|\[\*\]' + regexp = r'(^|(?<=\.\.)|\.)\*|\[\*\]' name = 'wildcard' priority = 10 @@ -126,23 +128,64 @@ def parse(self, tokens, data): try: + token, *tokens = tokens + except ValueError: + token = NoneToken() + try: data = data.values() except AttributeError: pass - if not tokens: - for item in data: - yield item - else: - token, *tokens = tokens - for item in data: + for item in data: + try: + for value in token.parse(tokens, item): + yield value + except (TypeError, IndexError, KeyError): + # Ignore errors inside branches, because some + # of the branches may have different structure + # than our jpath wants. + pass + + +@Token.use +class EllipsisToken(Token): + """.., may return multiple results""" + + regexp = r'\.\.' + name = 'ellipsis' + priority = 60 + + def __init__(self, ellipsis): + self.value = None + + def parse(self, tokens, data): + def dive(data): + yield data + stack = [data] + while stack: + data = stack.pop() try: - for value in token.parse(tokens, item): - yield value - except (TypeError, IndexError, KeyError): - # Ignore errors inside branches, because some - # of the branches may have different structure - # than our jpath wants. + data = data.values() + except AttributeError: pass + for item in data: + if not isinstance(item, (str, int)): + # Strings are nasty, because they can be iterated + # recursively idefinitely. + stack.append(item) + yield item + try: + token, *tokens = tokens + except ValueError: + token = NoneToken() + for item in dive(data): + try: + for value in token.parse(tokens, item): + yield value + except (TypeError, IndexError, KeyError): + # Ignore errors inside branches, because some + # of the branches may have different structure + # than our jpath wants. + pass @Token.use @@ -161,6 +204,13 @@ yield None +class NoneToken(Token): + """Special token for marking lack of tokens.""" + + def parse(self, tokens, data): + yield data + + def get_all(jpath, data): """ Gets list of all the values matching given jpath in the data datastructure. @@ -179,6 +229,12 @@ [2] >>> get_all('*.ziew', [1, {'ziew':2}, [3], []]) [2] + >>> get_all('..ziew', [1, 'ziew', {'ziew': 3, 'yawn': [{'ziew': 4}]}]) + [3, 4] + >>> get_all('a.b', {'a': {'b': 3, 'c': {'b': 4}}}) + [3] + >>> get_all('a..b', {'a': {'b': 3, 'c': {'b': 4}}}) + [3, 4] """ tokens = list(Token.tokenize(jpath))
