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))