[Zope-CVS] CVS: Packages/pypes/pypes - expression.py:1.2

Casey Duncan casey at zope.com
Fri Jan 30 00:42:08 EST 2004


Update of /cvs-repository/Packages/pypes/pypes
In directory cvs.zope.org:/tmp/cvs-serv311

Modified Files:
	expression.py 
Log Message:
After further thought and discussion, switch to a parsed-string based expression scheme rather than a magic expresser object. There were too many vagarities and limitations with the latter to make it practical. String expressions will allow any desired interpretation of operators, without any real loss of transparency or function in the end.


=== Packages/pypes/pypes/expression.py 1.1 => 1.2 ===
--- Packages/pypes/pypes/expression.py:1.1	Thu Jan 29 00:42:08 2004
+++ Packages/pypes/pypes/expression.py	Fri Jan 30 00:41:37 2004
@@ -11,271 +11,53 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""Pypes expression declaration objects
+"""Pypes expression parser
 
-Expressions are declared and stored in pypes using 'Expresser' objects.  When
-expresser objects are manipulated, such as by accessing attributes, items or
-performing arithmetic and comparison operations, the result is a new expresser
-object which records the operation that were performed.
-
-Expresser objects are used to record expressions that are going to be applied
-to objects later. For example, suppose you want to define a filter to that
-will later be applied to a set of objects. Using an expresser object named
-'member', you could write the following::
-    
-  myfilter = member.status == "pending"
-  
-'myfilter' now contains an expresser that can be applied to a collection of
-objects to determine which objects' 'status' attribute have the value "pending".
-Much more complex expressions are, of course, possible.
-
-Expressers are characterized by one or more operations. An operation is a
-single comparison or truth-test of expresser terms. Multiple operations are
-separated by the bitwise operators '&' (and) and '|' (or). The Python boolean
-operators 'and' and 'or' cannot be used because of their shortcut semantics. 
-An expresser term is a subexpression consisting of any Python operators except
-for comparison and bitwise operators. A term ends once a comparison or bitwise
-operation is performed. A new term begins on the right of a bitwise operator.
-Let's extend the example above to explain::
-    
-  myfilter = (member.status == "pending") & (member.children.has_key("photo"))
-
-(Note that the parenthesis are necessary because Python gives comparison
-operators lower precedence then bitwise operators)
-
-In this example 'myfilter' will contain an multi-operation expresser with two
-elements. The first element is an operation representing the comparison of the
-'status' attribute of 'member'. The second element is a truth test of the
-result of calling the 'has_key' method of the children attribute of member
-(note that this 'has_key' "call" is still a declaration resulting in a new
-expresser). Using the '&' bitwise operator means that both operation 
-expressers must evaluate to true for an object to satisfy 'myfilter'.
-
-The expresser terms in the above are 'member.status' and 
-'member.children.has_key("photo")'. These become important in the context of
-pypes indexes and views. There they are used to define the source, filter and 
-act as registration to allow the query service to optimize searching and
-sorting operations.
-
-$Id$"""
-
-import types
-
-_marker = object()
-
-
-class ExpresserBase(object):
-    """Common methods for all expresser types"""
-    
-    # Bitwise operators result in an expresser union or intersection object
-    
-    def __and__(self, other):
-        return ExpresserIntersect(self, other)
-        
-    def __rand__(self, other):
-        return ExpresserIntersect(other, self)
-    
-    def __or__(self, other):
-        return ExpresserUnion(self, other)
-        
-    def __ror__(self, other):
-        return ExpresserUnion(other, self)
+$Id:S"""
 
+import sys
+from compiler import parse, ast
 
-class ExpresserTerm(ExpresserBase):
-    """Declarative expression encapsulation. 
+class Expression:
+    """Encapsulation of an expression and namespace for later evaluation"""
+    
+    def __init__(self, expr, namespace=None):
+        """Create and expression from the string expr, which must be a valid
+        Python expression. The expression is parsed and names used in it are
+        taken from the namespace mapping and stored. Names not in namespace
+        must be provided when calling the expression unless they are built-in
+        names
+        """
+        self._expr = expr
+        self._tree = parse(expr, mode='eval')
+        self._ns = {}
+        if namespace is not None:
+            for name in self.names():
+                try:
+                    self._ns[name] = namespace[name]
+                except KeyError:
+                    pass
+    
+    def names(self, _tree=None, _names=None):
+        """Return a list of the names used in the expression"""
+        if _tree is None:
+            _tree = self._tree
+        if _names is None:
+            _names = []
+        for node in _tree.getChildNodes():
+            if isinstance(node, ast.Name):
+                _names.append(node.name)
+            self.names(node, _names)
+        return _names
     
-    Operations performed on expresser terms such as attribute or item access or
-    comparison result in a new expresser object that captures the operation for
-    later execution.
-    """
-    
-    def __init__(
-        self, expression='member', extends=None, prepends=None, parens=True):
-        """Create a new expresser from the expression fragment passed.
-        If extends is specified, then the expresser extends an existing
-        one. If prepends is specified then the expression prepends the
-        expresser"""
-        assert extends is None or prepends is None, (
-            'Cannot specify both extends and prepends')
-        if parens:
-            template = '(%s%s)'
-        else:
-            template = '%s%s'
-        if extends is None and prepends is None:
-            self.__expr = expression
-            self.__locals = {}
-        elif extends is not None:
-            self.__expr = template % (extends._ExpresserTerm__expr, expression)
-            self.__locals = extends._ExpresserTerm__locals.copy()
-        elif prepends is not None:
-            self.__expr = template % (expression, prepends._ExpresserTerm__expr)
-            self.__locals = prepends._ExpresserTerm__locals.copy()
-        
     def __str__(self):
-        expression = self.__expr
-        for name, value in self.__locals.items():
-            expression = expression.replace(name, repr(value))
-        return expression
-
-    __repr__ = __str__        
-            
-    def __addLocal(self, value):
-        """Add local variable of value and return the name"""
-        localname = '__%d__' % len(self.__locals)
-        self.__locals[localname] = value
-        return localname
-        
-    def __extend(self, expr_template, value=_marker, parens=True):
-        """Extend an expresser"""
-        if value is not _marker:
-            return self.__class__(
-                expression=expr_template % self.__addLocal(value),
-                extends=self, parens=parens)
-        else:
-            return self.__class__(
-                expression=expr_template, extends=self, parens=parens)
-        
-    def __prepend(self, expr_template, value=_marker, parens=True):
-        """Prepends an expresser"""
-        if value is not _marker:
-            return self.__class__(
-                expression=expr_template % self.__addLocal(value),
-                prepends=self, parens=parens)
-        else:
-            return self.__class__(
-                expression=expr_template, prepends=self, parens=parens)
-    
-    def __getattr__(self, name):
-        print name
-        return self.__class__(
-            expression='.%s' % name, extends=self, parens=False)
-    
-    def __getitem__(self, key):
-        return self.__extend('[%s]', key, parens=False)
-        
-    # Binary math operators
-    
-    def __add__(self, other):
-        return self.__extend(' + %s', other)
-    
-    def __radd__(self, other):
-        return self.__prepend('%s + ', other)
-    
-    def __sub__(self, other):
-        return self.__extend(' - %s', other)
-    
-    def __rsub__(self, other):
-        return self.__prepend('%s - ', other)
-        
-    def __mul__(self, other):
-        return self.__extend(' * %s', other)
+        return "Expression('%s')" % self._expr
     
-    def __rmul__(self, other):
-        return self.__prepend('%s * ', other)
-        
-    def __floordiv__(self, other):
-        return self.__extend(' // %s', other)
-    
-    def __rfloordiv__(self, other):
-        return self.__prepend('%s // ', other)
-        
-    def __div__(self, other):
-        return self.__extend(' / %s', other)
-    
-    def __rdiv__(self, other):
-        return self.__prepend('%s / ', other)
-        
-    def __truediv__(self, other):
-        return self.__extend(' / %s', other)
-    
-    def __rtruediv__(self, other):
-        return self.__prepend('%s / ', other)
-    
-    def __mod__(self, other):
-        return self.__extend(' %% %s', other)
-    
-    def __rmod__(self, other):
-        return self.__prepend('%s %% ', other)
-    
-    def __pow__(self, other):
-        return self.__extend(' ** %s', other)
-    
-    def __rpow__(self, other):
-        return self.__prepend('%s ** ', other)
-    
-    def __lshift__(self, other):
-        return self.__extend(' << %s', other)
-    
-    def __rlshift__(self, other):
-        return self.__prepend('%s << ', other)
-    
-    def __rshift__(self, other):
-        return self.__extend(' >> %s', other)
-    
-    def __rrshift__(self, other):
-        return self.__prepend('%s >> ', other)
-    
-    # Unary math operators
-    
-    def __neg__(self):
-        return self.__prepend('-', parens=False)
-    
-    def __pos__(self):
-        return self.__prepend('+', parens=False)
-    
-    def __invert__(self):
-        return self.__prepend('~', parens=False)
-        
-    # Comparison operators result in an expresser operation object
-    
-    def __eq__(self, other):
-        return ExpresserComparison(self, '==', other)
-    
-    def __ne__(self, other):
-        return ExpresserComparison(self, '!=', other)
-        
-    def __lt__(self, other):
-        return ExpresserComparison(self, '<', other)
-        
-    def __gt__(self, other):
-        return ExpresserComparison(self, '>', other)
-        
-    def __le__(self, other):
-        return ExpresserComparison(self, '<=', other)
-        
-    def __ge__(self, other):
-        return ExpresserComparison(self, '>=', other)
-
+    __repr__ = __str__
 
-class ExpresserComparison(ExpresserBase):
-    """Encapsulation of expresser comparision operations"""
-    
-    def __init__(self, left_operand, operator, right_operand):
-        self.left_operand = left_operand
-        self.operator = operator
-        self.right_operand = right_operand
-    
-    def __str__(self):
-        return '%s %s %s' % (
-            str(self.left_operand), self.operator, str(self.right_operand))
-
-    # ExpresserComparisons are not themselves comparible
-        
-    def __eq__(self, other):
-        raise RuntimeError, 'Cannot compare expresser operation objects'
-    
-    def __ne__(self, other):
-        raise RuntimeError, 'Cannot compare expresser operation objects'
-        
-    def __lt__(self, other):
-        raise RuntimeError, 'Cannot compare expresser operation objects'
-        
-    def __gt__(self, other):
-        raise RuntimeError, 'Cannot compare expresser operation objects'
-        
-    def __le__(self, other):
-        raise RuntimeError, 'Cannot compare expresser operation objects'
-        
-    def __ge__(self, other):
-        raise RuntimeError, 'Cannot compare expresser operation objects'
+def getCallerNamespace(): 
+    """Look up the stack fram and return a dict of the namespace of the code 
+    that envoked the caller of getCallerNamespace.
+    """
+    frame = sys._getframe(2)
+    return frame.f_globals.copy(), frame.f_locals.copy()




More information about the Zope-CVS mailing list