changeset 516:0e5a25f1b83d

Implemented XPath sub-expressions.
author athomas
date Wed, 06 Jun 2007 13:57:40 +0000
parents 6983367c1c78
children c98e9dcbedbb
files doc/xpath.txt genshi/path.py
diffstat 2 files changed, 47 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- a/doc/xpath.txt
+++ b/doc/xpath.txt
@@ -52,8 +52,8 @@
 * ``sum()``
 
 The mathematical operators (``+``, ``-``, ``*``, ``div``, and ``mod``) are not
-yet supported, whereas the various comparison and logical operators should work
-as expected.
+yet supported, whereas sub-expressions and the various comparison and logical
+operators should work as expected.
 
 You can also use XPath variable references (``$var``) inside predicates.
 
@@ -68,20 +68,28 @@
 .. code-block:: pycon
 
   >>> from genshi.input import XML
-  
+
   >>> doc = XML('''<doc>
-  ...   <items count="2">
-  ...     <item status="new">
-  ...       <summary>Foo</summary>
-  ...     </item>
-  ...     <item status="closed">
-  ...       <summary>Bar</summary>
-  ...     </item>
-  ...    </items>
+  ...  <items count="4">
+  ...       <item status="new">
+  ...         <summary>Foo</summary>
+  ...       </item>
+  ...       <item status="closed">
+  ...         <summary>Bar</summary>
+  ...       </item>
+  ...       <item status="closed" resolution="invalid">
+  ...         <summary>Baz</summary>
+  ...       </item>
+  ...       <item status="closed" resolution="fixed">
+  ...         <summary>Waz</summary>
+  ...       </item>
+  ...   </items>
   ... </doc>''')
-  
-  >>> print doc.select('items/item[@status="closed"]/summary/text()')
-  Bar
+
+  >>> print doc.select('items/item[@status="closed" and '
+  ...     '(@resolution="invalid" or not(@resolution))]/summary/text()')
+  BarBaz
+
 
 
 ---------------------
--- a/genshi/path.py
+++ b/genshi/path.py
@@ -15,17 +15,24 @@
 
 >>> from genshi.input import XML
 >>> doc = XML('''<doc>
-...  <items count="2">
+...  <items count="4">
 ...       <item status="new">
 ...         <summary>Foo</summary>
 ...       </item>
 ...       <item status="closed">
 ...         <summary>Bar</summary>
 ...       </item>
+...       <item status="closed" resolution="invalid">
+...         <summary>Baz</summary>
+...       </item>
+...       <item status="closed" resolution="fixed">
+...         <summary>Waz</summary>
+...       </item>
 ...   </items>
 ... </doc>''')
->>> print doc.select('items/item[@status="closed"]/summary/text()')
-Bar
+>>> print doc.select('items/item[@status="closed" and '
+...     '(@resolution="invalid" or not(@resolution))]/summary/text()')
+BarBaz
 
 Because the XPath engine operates on markup streams (as opposed to tree
 structures), it only implements a subset of the full XPath 1.0 language.
@@ -476,11 +483,24 @@
         return expr
 
     def _relational_expr(self):
-        expr = self._primary_expr()
+        expr = self._sub_expr()
         while self.cur_token in ('>', '>=', '<', '>='):
             op = _operator_map[self.cur_token]
             self.next_token()
-            expr = op(expr, self._primary_expr())
+            expr = op(expr, self._sub_expr())
+        return expr
+
+    def _sub_expr(self):
+        token = self.cur_token
+        if token != '(':
+            return self._primary_expr()
+        self.next_token()
+        expr = self._or_expr()
+        if self.cur_token != ')':
+            raise PathSyntaxError('Expected ")" to close sub-expression, '
+                                  'but found "%s"' % self.cur_token,
+                                  self.filename, self.lineno)
+        self.next_token()
         return expr
 
     def _primary_expr(self):
Copyright (C) 2012-2017 Edgewall Software