[Zope-CVS] CVS: Packages/pypes/pypes - expression.py:1.9 interfaces.py:1.18

Casey Duncan casey at zope.com
Tue Apr 13 00:15:59 EDT 2004


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

Modified Files:
	expression.py interfaces.py 
Log Message:
change freeOperands() to return Expressions rather than AST nodes
Add alternate constructor fromAstNode() to create expression from AST node rather than a string.
Expressions are now compiled from their AST. Expression.__call__ evals the compiled code instead of the string


=== Packages/pypes/pypes/expression.py 1.8 => 1.9 ===
--- Packages/pypes/pypes/expression.py:1.8	Mon Apr 12 22:44:59 2004
+++ Packages/pypes/pypes/expression.py	Tue Apr 13 00:15:28 2004
@@ -23,7 +23,7 @@
 from zope.interface import implements
 from pypes.interfaces import IExpression
 
-class Expression:
+class Expression(object):
     """Encapsulation of an expression and namespace for later evaluation"""
     
     implements(IExpression)
@@ -36,7 +36,7 @@
         names
         """
         self._expr = expr
-        self._tree = parse(expr, mode='eval')
+        self._setAst(parse(expr, mode='eval'))
         self._bindings = {}
         if namespace is not None:
             for name in self.names():
@@ -45,6 +45,27 @@
                 except KeyError:
                     pass
     
+    def fromAstNode(cls, node, bindings):
+        """Create an expression from an AST node object using the bindings
+        provided. Usually used to create a subexpression from a node in an
+        existing expression.
+        """
+        assert isinstance(node, ast.Node), 'node must be ast.Node object'
+        if not isinstance(node, ast.Expression):
+            node = ast.Expression(node)
+        expression = object.__new__(cls)
+        expression._expr = repr(node) # XXX Improve this later
+        expression._setAst(node)
+        expression._bindings = bindings
+        return expression
+    fromAstNode = classmethod(fromAstNode)
+    
+    def _setAst(self, tree):
+        """Set the AST for the expression and compile it to bytecode"""
+        tree.filename = '<pypes expression>' # ECG requires filename attr
+        self._code = ExpressionCodeGenerator(tree).getCode()
+        self._tree = tree
+    
     def names(self, _tree=None, _names=None):
         """Return a set of the names used in the expression"""
         if _tree is None:
@@ -67,37 +88,45 @@
         all_names = (
             Set(self._bindings.keys() + vars(__builtin__).keys()) 
             - Set(free_names))
-        return self.names() - all_names        
+        return self.names() - all_names
     
-    def freeOperands(self, free_names=[], _tree=None, _nodes=None):
-        """Return a list of ast nodes cooresponding to top-level operands with
-        free variables not in the expression's bound  namespace or builtins,
-        excepting names supplied in free_names. These operands are satisfied by
-        names supplied when the expression is executed, which makes them
-        interesting to machinery intending to apply the expression.
+    def _freeNodes(self, top_node, names, _free=None):
+        """Return a list of AST nodes in top_node which are operands with name
+        references are not in the names set
+        """
+        if _free is None:
+            _free = []
+        for node in top_node.getChildNodes():
+            if isinstance(node, (ast.And, ast.Or, ast.Not)):
+                self._freeNodes(node, names, _free)
+            elif isinstance(node, ast.Compare):
+                for operand in node.getChildNodes():
+                    if Set(self.names(operand)) - names:
+                        _free.append(operand)     
+            elif Set(self.names(node)) - names:
+                _free.append(node)
+        return _free  
+    
+    def freeOperands(self, free_names=[]):
+        """Return a list of expression objects cooresponding to top-level
+        operands with free variables. Free variables are name references in
+        free_names or other names not found in the expression's bound 
+        namespace or python builtin. These operands are satisfied by names
+        supplied when the expression is executed, which makes them interesting
+        to machinery intending to apply the expression.
 
         These nodes are the right and left sides of comparisons, or single
         nodes used in truth tests. Note that only top-level comparison nodes
-        are decended to find operands. Nested comparisons are therefore
-        themselves considered whole operands.  
+        are decended to find operands. Nested comparisons are themselves
+        considered whole operands. Therefore it is usually undesirable to use
+        expressions like '(x > 4) == True' since 'x > 4' will be considered the
+        operand instead of just 'x'. 
         """
-        if _tree is None:
-            _tree = self._tree
-        if _nodes is None:
-            _nodes = []
         all_names = (
             Set(self._bindings.keys() + vars(__builtin__).keys()) 
             - Set(free_names))
-        for node in _tree.getChildNodes():
-            if isinstance(node, (ast.And, ast.Or, ast.Not)):
-                self.freeOperands(free_names, node, _nodes)
-            elif isinstance(node, ast.Compare):
-                for operand in node.getChildNodes():
-                    if Set(self.names(operand)) - all_names:
-                        _nodes.append(operand)     
-            elif Set(self.names(node)) - all_names:
-                _nodes.append(node)
-        return _nodes
+        return [Expression.fromAstNode(node, self._bindings)
+                for node in self._freeNodes(self._tree, all_names)]
     
     def optimize(self, free_names=[]):
         """Optimize the expression.
@@ -111,7 +140,7 @@
         all_names = (
             Set(self._bindings.keys() + dir(__builtin__)) - Set(free_names))
         top_node = self._tree.getChildNodes()[0]
-        self._tree = ast.Expression(self._optimizeNode(all_names, top_node))
+        self._setAst(ast.Expression(self._optimizeNode(all_names, top_node)))
     
     def _optimizeNode(self, names, node):
         if isinstance(node, ast.Const):
@@ -184,7 +213,7 @@
         result. The provided namespace extends and overrides names bound to
         the expression when constructed.
         """
-        return eval(self._expr, self._bindings, namespace)
+        return eval(self._code, self._bindings, namespace)
     
     def __str__(self):
         return "Expression('%s')" % self._expr
@@ -211,7 +240,7 @@
                 return children1 == children2
         else:
             return False
-    return node1 == node2            
+    return node1 == node2      
 
 def getCallerNamespace(): 
     """Look up the stack fram and return a dict of the namespace of the code 


=== Packages/pypes/pypes/interfaces.py 1.17 => 1.18 ===
--- Packages/pypes/pypes/interfaces.py:1.17	Mon Apr 12 22:24:32 2004
+++ Packages/pypes/pypes/interfaces.py	Tue Apr 13 00:15:28 2004
@@ -109,7 +109,8 @@
 
     def idSet():
         """Return an identity set of all registered objects."""
-        
+
+
 class ISet(Interface):
     """Basic immutable set of objects"""
     
@@ -606,7 +607,7 @@
         """
     
     def freeOperands(free_names=[]):
-        """Return a list of Python ast node objects that coorespond to
+        """Return a sequence of IExpression objects that coorespond to
         operand subexpressions with free variables. Operands are considered
         free if they reference a name in free_names, or a name not found
         in bound names or Python builtins




More information about the Zope-CVS mailing list