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

Casey Duncan casey at zope.com
Tue Feb 3 01:46:58 EST 2004


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

Modified Files:
	expression.py 
Log Message:
Add expression optimize method with tests


=== Packages/pypes/pypes/expression.py 1.4 => 1.5 ===
--- Packages/pypes/pypes/expression.py:1.4	Sat Jan 31 01:09:26 2004
+++ Packages/pypes/pypes/expression.py	Tue Feb  3 01:46:27 2004
@@ -19,6 +19,7 @@
 import __builtin__
 from sets import Set
 from compiler import parse, ast
+from compiler.pycodegen import ExpressionCodeGenerator
 
 class Expression:
     """Encapsulation of an expression and namespace for later evaluation"""
@@ -82,6 +83,86 @@
             elif Set(self.names(node)) - all_names:
                 _nodes.append(node)
         return _nodes
+    
+    def optimize(self, free_names=[]):
+        """Optimize the expression.
+        
+        The expression is optimized by reducing sub-expressions that can be
+        satisfied from the bound namespace or simply contain constants. Also
+        reduces shortcut boolean operators where their value can be
+        predetermined. The latter can eliminate free sub-expressions which
+        cannot influence the expression result.
+        """
+        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))
+    
+    def _optimizeNode(self, names, node):
+        if isinstance(node, ast.Const):
+            # Cannot optimize Const nodes any further
+            return node
+        node_names = self.names(node)
+        if not node_names - names:
+            # No free names in this node, we can execute it now and optimize
+            # it out to a constant value
+            global_ns = {}
+            # Gather names not in bound namespace from builtins
+            for nm in node_names:
+                if nm not in names:
+                    global_ns[nm] = getattr(__builtin__, nm)
+            # Compile the expression to code and execute
+            node = ast.Expression(node)
+            node.filename = '<string>' # ECG requires filename attr
+            code = ExpressionCodeGenerator(node).getCode()
+            value = eval(code, global_ns, self._bindings)
+            # XXX AFAICT I can stuff any arbitrary object into a Const node and
+            #     things stay happy. In the spirit of the simplest thing that
+            #     could possibly work, that's what I'm gonna do.
+            return ast.Const(value)
+        if isinstance(node, ast.Or):
+            # Or nodes can be optimized out if any child node reduces to True
+            opt_nodes = []
+            for subnode in node.nodes:
+                opt = self._optimizeNode(names, subnode)
+                if isinstance(opt, ast.Const) and opt.value:
+                    return opt
+                opt_nodes.append(opt)
+            node.nodes = opt_nodes
+            return node
+        if isinstance(node, ast.And):
+            # And nodes can be optimized out if any child node reduces to False
+            opt_nodes = []
+            for subnode in node.nodes:
+                opt = self._optimizeNode(names, subnode)
+                if isinstance(opt, ast.Const) and not opt.value:
+                    return opt
+                opt_nodes.append(opt)
+            node.nodes = opt_nodes
+            return node
+        if isinstance(node, ast.Compare):
+            # XXX not sure if we should compare nodes here. If nodes are
+            #     equivilant they would normally compare equal, even if
+            #     they contain free variables. This relies on "sane" comparison
+            #     and I'm not sure it would be that valuable in practice...
+            node.expr = self._optimizeNode(names, node.expr)
+            for i, (op, expr) in enumerate(node.ops):
+                node.ops[i] = (op, self._optimizeNode(names, expr))
+            return node
+        if hasattr(node, 'nodes'):
+            for i, subnode in enumerate(node.nodes):
+                if isinstance(subnode, ast.Node):
+                    node.nodes[i] = self._optimizeNode(names, subnode)
+            return node
+        if hasattr(node, 'left') and hasattr(node, 'right'):
+            node.left = self._optimizeNode(names, node.left)
+            node.right = self._optimizeNode(names, node.right)
+            return node
+        if hasattr(node, 'expr'):
+            node.expr = self._optimizeNode(names, node.expr)
+            return node
+        # Cannot optimize
+        return node
     
     def __call__(self, namespace={}):
         """Evaluate the expression with the namespace provided and return the




More information about the Zope-CVS mailing list