[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