[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