Mercurial > jpath
changeset 3:d0fecf87e3bc
move tokenizer to separate class
| author | sheep@wintermute |
|---|---|
| date | Wed, 14 Apr 2010 11:34:49 +0200 |
| parents | 5b63c545fa4a |
| children | c0c786f4bbb5 |
| files | jpath.py |
| diffstat | 1 files changed, 41 insertions(+), 29 deletions(-) [+] |
line wrap: on
line diff
--- a/jpath.py Wed Apr 14 11:20:26 2010 +0200 +++ b/jpath.py Wed Apr 14 11:34:49 2010 +0200 @@ -12,50 +12,51 @@ import re import itertools import operator -import logging - -class Token: - """Base class for tokens.""" - - # list of all token classes marked with @Token.use +class Tokenizer: + # list of all token classes marked with @Tokenizer.use tokens = {} + def __init__(self): + self.tokens_re = re.compile(r'|'.join( + r'(?P<{0}>{1})'.format(token.name, token.regexp) + for token in sorted(self.tokens.values(), + key=operator.attrgetter("priority")) + )) + @classmethod - def use(cls, token_class): + def token(cls, token_class): """Marks a token class to be used in tokenizer.""" cls.tokens[token_class.name] = token_class return cls - @classmethod - def tokenize(cls, jpath): + def tokenize(self, jpath): """ Breaks a string into tokens. - >>> [(t.name, t.value) for t in Token.tokenize("ziew")] + >>> [(t.name, t.value) for t in TOKENIZER.tokenize("ziew")] [('fragment', 'ziew')] - >>> [(t.name, t.value) for t in Token.tokenize("ziew.yawn")] + >>> [(t.name, t.value) for t in TOKENIZER.tokenize("ziew.yawn")] [('fragment', 'ziew'), ('fragment', 'yawn')] - >>> [(t.name, t.value) for t in Token.tokenize('ziew["yawn"]')] + >>> [(t.name, t.value) for t in TOKENIZER.tokenize('ziew["yawn"]')] [('fragment', 'ziew'), ('element', 'yawn')] - >>> [(t.name, t.value) for t in Token.tokenize('ziew[0]')] + >>> [(t.name, t.value) for t in TOKENIZER.tokenize('ziew[0]')] [('fragment', 'ziew'), ('index', 0)] - >>> [(t.name, t.value) for t in Token.tokenize("ziew.yawn+")] + >>> [(t.name, t.value) for t in TOKENIZER.tokenize("ziew.yawn+")] [('fragment', 'ziew'), ('fragment', 'yawn'), ('error', '+')] """ - tokens_re = re.compile(r'|'.join( - r'(?P<{0}>{1})'.format(token.name, token.regexp) - for token in sorted(cls.tokens.values(), - key=operator.attrgetter("priority")) - )) - for match in tokens_re.finditer(jpath): + for match in self.tokens_re.finditer(jpath): name = match.lastgroup if match.group(name): params = dict(p for p in match.groupdict().items() if p[1] is not None) - yield cls.tokens[name](**params) + yield self.tokens[name](**params) + + +class Token: + """Base class for tokens.""" @staticmethod def apply(tokens, data): @@ -75,7 +76,7 @@ return Token.apply(tokens, data[self.value]) -@Token.use +@Tokenizer.token class FragmentToken(Token): """foo.bar.baz""" @@ -87,7 +88,7 @@ self.value = fragment_name -@Token.use +@Tokenizer.token class ElementToken(Token): """["foo"]["bar"]["baz"], with quote escaping""" @@ -107,7 +108,7 @@ self.value = element_name.replace(r'\"', '"') -@Token.use +@Tokenizer.token class IndexToken(Token): """[0][1][-1]""" @@ -119,7 +120,7 @@ self.value = int(index_value) -@Token.use +@Tokenizer.token class SliceToken(Token): """[0:-1:2], may return multiple results""" @@ -138,7 +139,7 @@ return Token.apply(tokens, data[self.start:self.end:self.step]) -@Token.use +@Tokenizer.token class WildcardToken(Token): """*[*].*, may return multiple results""" @@ -155,6 +156,7 @@ except ValueError: token = NoneToken() try: + # If it's a dict, iterate over values, not keys. data = data.values() except AttributeError: pass @@ -169,7 +171,7 @@ pass -@Token.use +@Tokenizer.token class EllipsisToken(Token): """.., may return multiple results""" @@ -187,9 +189,15 @@ while stack: data = stack.pop() try: + # If it's a dict, iterate over values, not keys. data = data.values() except AttributeError: pass + try: + # Skip non-iterable datatypes. + data = iter(data) + except TypeError: + continue for item in data: if not isinstance(item, (str, int)): # Strings are nasty, because they can be iterated @@ -211,7 +219,7 @@ pass -@Token.use +@Tokenizer.token class ErrorToken(Token): """Catchall for everything that didn't match other tokens.""" @@ -234,6 +242,10 @@ yield data +# We only ever need one tokenizer +TOKENIZER = Tokenizer() + + def get_all(jpath, data): """ Gets list of all the values matching given jpath in the data datastructure. @@ -264,7 +276,7 @@ [[1, 2]] """ - tokens = list(Token.tokenize(jpath)) + tokens = list(TOKENIZER.tokenize(jpath)) if not tokens: return [data] token, *tokens = tokens
