annotate markup/eval.py @ 203:6addf7af09f6

Fix for handling function calls with star/dstar arguments in expressions. Closes #42. Many thanks to David Fraser for reporting the problem and providing a patch!
author cmlenz
date Fri, 25 Aug 2006 13:12:39 +0000
parents d7c0a7d65783
children 0edf663b97d6
rev   line source
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
1 # -*- coding: utf-8 -*-
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
2 #
66
822089ae65ce Switch copyright to Edgewall and URLs to markup.edgewall.org.
cmlenz
parents: 42
diff changeset
3 # Copyright (C) 2006 Edgewall Software
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
4 # All rights reserved.
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
5 #
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
6 # This software is licensed as described in the file COPYING, which
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
7 # you should have received as part of this distribution. The terms
66
822089ae65ce Switch copyright to Edgewall and URLs to markup.edgewall.org.
cmlenz
parents: 42
diff changeset
8 # are also available at http://markup.edgewall.org/wiki/License.
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
9 #
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
10 # This software consists of voluntary contributions made by many
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
11 # individuals. For the exact contribution history, see the revision
66
822089ae65ce Switch copyright to Edgewall and URLs to markup.edgewall.org.
cmlenz
parents: 42
diff changeset
12 # history and logs, available at http://markup.edgewall.org/log/.
27
b8456279c444 * Fix the boilerplate in the Python source files.
cmlenz
parents: 16
diff changeset
13
b8456279c444 * Fix the boilerplate in the Python source files.
cmlenz
parents: 16
diff changeset
14 """Support for "safe" evaluation of Python expressions."""
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
15
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
16 import __builtin__
87
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
17 from compiler import ast, parse
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
18 from compiler.pycodegen import ExpressionCodeGenerator
131
203f459e7e26 * Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents: 120
diff changeset
19 import new
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
20
192
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
21 __all__ = ['Expression', 'Undefined']
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
22
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
23
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
24 class Expression(object):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
25 """Evaluates Python expressions used in templates.
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
26
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
27 >>> data = dict(test='Foo', items=[1, 2, 3], dict={'some': 'thing'})
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
28 >>> Expression('test').evaluate(data)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
29 'Foo'
81
cc034182061e Template expressions are now compiled to Python bytecode.
cmlenz
parents: 69
diff changeset
30
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
31 >>> Expression('items[0]').evaluate(data)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
32 1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
33 >>> Expression('items[-1]').evaluate(data)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
34 3
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
35 >>> Expression('dict["some"]').evaluate(data)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
36 'thing'
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
37
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
38 Similar to e.g. Javascript, expressions in templates can use the dot
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
39 notation for attribute access to access items in mappings:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
40
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
41 >>> Expression('dict.some').evaluate(data)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
42 'thing'
86
5d98c4259d68 Accidentially left some doctests disabled.
cmlenz
parents: 82
diff changeset
43
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
44 This also works the other way around: item access can be used to access
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
45 any object attribute (meaning there's no use for `getattr()` in templates):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
46
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
47 >>> class MyClass(object):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
48 ... myattr = 'Bar'
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
49 >>> data = dict(mine=MyClass(), key='myattr')
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
50 >>> Expression('mine.myattr').evaluate(data)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
51 'Bar'
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
52 >>> Expression('mine["myattr"]').evaluate(data)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
53 'Bar'
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
54 >>> Expression('mine[key]').evaluate(data)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
55 'Bar'
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
56
31
9a958398bed9 * More test cases for expression evaluation.
cmlenz
parents: 30
diff changeset
57 All of the standard Python operators are available to template expressions.
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
58 Built-in functions such as `len()` are also available in template
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
59 expressions:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
60
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
61 >>> data = dict(items=[1, 2, 3])
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
62 >>> Expression('len(items)').evaluate(data)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
63 3
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
64 """
81
cc034182061e Template expressions are now compiled to Python bytecode.
cmlenz
parents: 69
diff changeset
65 __slots__ = ['source', 'code']
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
66
81
cc034182061e Template expressions are now compiled to Python bytecode.
cmlenz
parents: 69
diff changeset
67 def __init__(self, source, filename=None, lineno=-1):
165
4ed68a904235 Fix handling of keyword arguments in `py:def` directive. Thanks to Christian Boos for reporting the problem and providing the basic patch for this change.
cmlenz
parents: 145
diff changeset
68 if isinstance(source, basestring):
4ed68a904235 Fix handling of keyword arguments in `py:def` directive. Thanks to Christian Boos for reporting the problem and providing the basic patch for this change.
cmlenz
parents: 145
diff changeset
69 self.source = source
167
7888f4104cc0 Handle non-ASCII characters in expressions. Closes #29. Thanks to Arnar Birgisson for reporting the problem and comping up with a patch!
cmlenz
parents: 165
diff changeset
70 if isinstance(source, unicode):
7888f4104cc0 Handle non-ASCII characters in expressions. Closes #29. Thanks to Arnar Birgisson for reporting the problem and comping up with a patch!
cmlenz
parents: 165
diff changeset
71 source = '\xef\xbb\xbf' + source.encode('utf-8')
7888f4104cc0 Handle non-ASCII characters in expressions. Closes #29. Thanks to Arnar Birgisson for reporting the problem and comping up with a patch!
cmlenz
parents: 165
diff changeset
72 self.code = _compile(parse(source, 'eval'), self.source,
165
4ed68a904235 Fix handling of keyword arguments in `py:def` directive. Thanks to Christian Boos for reporting the problem and providing the basic patch for this change.
cmlenz
parents: 145
diff changeset
73 filename=filename, lineno=lineno)
4ed68a904235 Fix handling of keyword arguments in `py:def` directive. Thanks to Christian Boos for reporting the problem and providing the basic patch for this change.
cmlenz
parents: 145
diff changeset
74 else:
4ed68a904235 Fix handling of keyword arguments in `py:def` directive. Thanks to Christian Boos for reporting the problem and providing the basic patch for this change.
cmlenz
parents: 145
diff changeset
75 assert isinstance(source, ast.Node)
4ed68a904235 Fix handling of keyword arguments in `py:def` directive. Thanks to Christian Boos for reporting the problem and providing the basic patch for this change.
cmlenz
parents: 145
diff changeset
76 self.source = '?'
4ed68a904235 Fix handling of keyword arguments in `py:def` directive. Thanks to Christian Boos for reporting the problem and providing the basic patch for this change.
cmlenz
parents: 145
diff changeset
77 self.code = _compile(ast.Expression(source), filename=filename,
4ed68a904235 Fix handling of keyword arguments in `py:def` directive. Thanks to Christian Boos for reporting the problem and providing the basic patch for this change.
cmlenz
parents: 145
diff changeset
78 lineno=lineno)
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
79
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
80 def __repr__(self):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
81 return '<Expression "%s">' % self.source
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
82
120
eb6cead67474 * Allow `py:with` directives to define `lambda`s
cmlenz
parents: 118
diff changeset
83 def evaluate(self, data, nocall=False):
81
cc034182061e Template expressions are now compiled to Python bytecode.
cmlenz
parents: 69
diff changeset
84 """Evaluate the expression against the given data dictionary.
cc034182061e Template expressions are now compiled to Python bytecode.
cmlenz
parents: 69
diff changeset
85
cc034182061e Template expressions are now compiled to Python bytecode.
cmlenz
parents: 69
diff changeset
86 @param data: a mapping containing the data to evaluate against
120
eb6cead67474 * Allow `py:with` directives to define `lambda`s
cmlenz
parents: 118
diff changeset
87 @param nocall: if true, the result of the evaluation is not called if
eb6cead67474 * Allow `py:with` directives to define `lambda`s
cmlenz
parents: 118
diff changeset
88 if it is a callable
81
cc034182061e Template expressions are now compiled to Python bytecode.
cmlenz
parents: 69
diff changeset
89 @return: the result of the evaluation
cc034182061e Template expressions are now compiled to Python bytecode.
cmlenz
parents: 69
diff changeset
90 """
118
226613431921 Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents: 116
diff changeset
91 retval = eval(self.code, {'data': data,
226613431921 Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents: 116
diff changeset
92 '_lookup_name': _lookup_name,
226613431921 Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents: 116
diff changeset
93 '_lookup_attr': _lookup_attr,
226613431921 Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents: 116
diff changeset
94 '_lookup_item': _lookup_item})
194
eb5c8200f49b Fix regression introduced in [242]: any reference to an undefined name would result in a `NameError`.
cmlenz
parents: 192
diff changeset
95 if not nocall and type(retval) is not Undefined and callable(retval):
90
242610137d1f When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents: 88
diff changeset
96 retval = retval()
242610137d1f When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents: 88
diff changeset
97 return retval
30
2ee9f28e16e5 Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents: 27
diff changeset
98
87
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
99
192
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
100 class Undefined(object):
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
101 """Represents a reference to an undefined variable.
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
102
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
103 Unlike the Python runtime, template expressions can refer to an undefined
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
104 variable without causing a `NameError` to be raised. The result will be an
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
105 instance of the `Undefined´ class, which is treated the same as `False` in
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
106 conditions, and acts as an empty collection in iterations:
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
107
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
108 >>> foo = Undefined('foo')
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
109 >>> bool(foo)
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
110 False
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
111 >>> list(foo)
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
112 []
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
113 >>> print foo
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
114 undefined
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
115
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
116 However, calling an undefined variable, or trying to access an attribute
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
117 of that variable, will raise an exception that includes the name used to
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
118 reference that undefined variable.
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
119
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
120 >>> foo('bar')
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
121 Traceback (most recent call last):
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
122 ...
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
123 NameError: Variable "foo" is not defined
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
124
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
125 >>> foo.bar
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
126 Traceback (most recent call last):
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
127 ...
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
128 NameError: Variable "foo" is not defined
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
129 """
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
130 __slots__ = ['name']
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
131
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
132 def __init__(self, name):
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
133 self.name = name
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
134
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
135 def __call__(self, *args, **kwargs):
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
136 self.throw()
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
137
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
138 def __getattr__(self, name):
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
139 self.throw()
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
140
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
141 def __iter__(self):
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
142 return iter([])
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
143
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
144 def __nonzero__(self):
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
145 return False
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
146
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
147 def __repr__(self):
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
148 return 'undefined'
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
149
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
150 def throw(self):
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
151 raise NameError('Variable "%s" is not defined' % self.name)
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
152
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
153
165
4ed68a904235 Fix handling of keyword arguments in `py:def` directive. Thanks to Christian Boos for reporting the problem and providing the basic patch for this change.
cmlenz
parents: 145
diff changeset
154 def _compile(node, source=None, filename=None, lineno=-1):
4ed68a904235 Fix handling of keyword arguments in `py:def` directive. Thanks to Christian Boos for reporting the problem and providing the basic patch for this change.
cmlenz
parents: 145
diff changeset
155 tree = ExpressionASTTransformer().visit(node)
116
88ac4c680120 Merged [135:138/branches/experimental/cspeedups].
cmlenz
parents: 112
diff changeset
156 if isinstance(filename, unicode):
131
203f459e7e26 * Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents: 120
diff changeset
157 # unicode file names not allowed for code objects
116
88ac4c680120 Merged [135:138/branches/experimental/cspeedups].
cmlenz
parents: 112
diff changeset
158 filename = filename.encode('utf-8', 'replace')
131
203f459e7e26 * Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents: 120
diff changeset
159 elif not filename:
203f459e7e26 * Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents: 120
diff changeset
160 filename = '<string>'
132
0288dedce3d2 Name lookup in expressions: try locals first, then the context.
cmlenz
parents: 131
diff changeset
161 tree.filename = filename
131
203f459e7e26 * Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents: 120
diff changeset
162 if lineno <= 0:
203f459e7e26 * Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents: 120
diff changeset
163 lineno = 1
87
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
164
116
88ac4c680120 Merged [135:138/branches/experimental/cspeedups].
cmlenz
parents: 112
diff changeset
165 gen = ExpressionCodeGenerator(tree)
131
203f459e7e26 * Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents: 120
diff changeset
166 gen.optimized = True
203f459e7e26 * Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents: 120
diff changeset
167 code = gen.getCode()
87
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
168
131
203f459e7e26 * Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents: 120
diff changeset
169 # We'd like to just set co_firstlineno, but it's readonly. So we need to
203f459e7e26 * Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents: 120
diff changeset
170 # clone the code object while adjusting the line number
203f459e7e26 * Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents: 120
diff changeset
171 return new.code(0, code.co_nlocals, code.co_stacksize,
203f459e7e26 * Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents: 120
diff changeset
172 code.co_flags | 0x0040, code.co_code, code.co_consts,
165
4ed68a904235 Fix handling of keyword arguments in `py:def` directive. Thanks to Christian Boos for reporting the problem and providing the basic patch for this change.
cmlenz
parents: 145
diff changeset
173 code.co_names, code.co_varnames, filename,
167
7888f4104cc0 Handle non-ASCII characters in expressions. Closes #29. Thanks to Arnar Birgisson for reporting the problem and comping up with a patch!
cmlenz
parents: 165
diff changeset
174 '<Expression %s>' % (repr(source).replace("'", '"') or '?'),
7888f4104cc0 Handle non-ASCII characters in expressions. Closes #29. Thanks to Arnar Birgisson for reporting the problem and comping up with a patch!
cmlenz
parents: 165
diff changeset
175 lineno, code.co_lnotab, (), ())
116
88ac4c680120 Merged [135:138/branches/experimental/cspeedups].
cmlenz
parents: 112
diff changeset
176
192
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
177 BUILTINS = __builtin__.__dict__.copy()
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
178 BUILTINS['Undefined'] = Undefined
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
179
132
0288dedce3d2 Name lookup in expressions: try locals first, then the context.
cmlenz
parents: 131
diff changeset
180 def _lookup_name(data, name, locals_=None):
192
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
181 val = Undefined
132
0288dedce3d2 Name lookup in expressions: try locals first, then the context.
cmlenz
parents: 131
diff changeset
182 if locals_:
192
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
183 val = locals_.get(name, val)
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
184 if val is Undefined:
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
185 val = data.get(name, val)
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
186 if val is Undefined:
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
187 val = BUILTINS.get(name, val)
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
188 if val is not Undefined or name == 'Undefined':
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
189 return val
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
190 else:
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
191 return val
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
192 else:
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
193 return val
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
194 return val(name)
116
88ac4c680120 Merged [135:138/branches/experimental/cspeedups].
cmlenz
parents: 112
diff changeset
195
118
226613431921 Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents: 116
diff changeset
196 def _lookup_attr(data, obj, key):
192
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
197 if type(obj) is Undefined:
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
198 obj.throw()
116
88ac4c680120 Merged [135:138/branches/experimental/cspeedups].
cmlenz
parents: 112
diff changeset
199 if hasattr(obj, key):
88ac4c680120 Merged [135:138/branches/experimental/cspeedups].
cmlenz
parents: 112
diff changeset
200 return getattr(obj, key)
88ac4c680120 Merged [135:138/branches/experimental/cspeedups].
cmlenz
parents: 112
diff changeset
201 try:
88ac4c680120 Merged [135:138/branches/experimental/cspeedups].
cmlenz
parents: 112
diff changeset
202 return obj[key]
88ac4c680120 Merged [135:138/branches/experimental/cspeedups].
cmlenz
parents: 112
diff changeset
203 except (KeyError, TypeError):
88ac4c680120 Merged [135:138/branches/experimental/cspeedups].
cmlenz
parents: 112
diff changeset
204 return None
88ac4c680120 Merged [135:138/branches/experimental/cspeedups].
cmlenz
parents: 112
diff changeset
205
88ac4c680120 Merged [135:138/branches/experimental/cspeedups].
cmlenz
parents: 112
diff changeset
206 def _lookup_item(data, obj, key):
192
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
207 if type(obj) is Undefined:
cda3bdfc19ed Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents: 180
diff changeset
208 obj.throw()
116
88ac4c680120 Merged [135:138/branches/experimental/cspeedups].
cmlenz
parents: 112
diff changeset
209 if len(key) == 1:
88ac4c680120 Merged [135:138/branches/experimental/cspeedups].
cmlenz
parents: 112
diff changeset
210 key = key[0]
88ac4c680120 Merged [135:138/branches/experimental/cspeedups].
cmlenz
parents: 112
diff changeset
211 try:
88ac4c680120 Merged [135:138/branches/experimental/cspeedups].
cmlenz
parents: 112
diff changeset
212 return obj[key]
88ac4c680120 Merged [135:138/branches/experimental/cspeedups].
cmlenz
parents: 112
diff changeset
213 except (KeyError, IndexError, TypeError), e:
88ac4c680120 Merged [135:138/branches/experimental/cspeedups].
cmlenz
parents: 112
diff changeset
214 if isinstance(key, basestring):
88ac4c680120 Merged [135:138/branches/experimental/cspeedups].
cmlenz
parents: 112
diff changeset
215 try:
88ac4c680120 Merged [135:138/branches/experimental/cspeedups].
cmlenz
parents: 112
diff changeset
216 return getattr(obj, key)
88ac4c680120 Merged [135:138/branches/experimental/cspeedups].
cmlenz
parents: 112
diff changeset
217 except (AttributeError, TypeError), e:
88ac4c680120 Merged [135:138/branches/experimental/cspeedups].
cmlenz
parents: 112
diff changeset
218 pass
87
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
219
138
d91e1e822969 Add some more assertions to the XPath tests.
cmlenz
parents: 134
diff changeset
220
87
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
221 class ASTTransformer(object):
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
222 """General purpose base class for AST transformations.
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
223
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
224 Every visitor method can be overridden to return an AST node that has been
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
225 altered or replaced in some way.
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
226 """
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
227 _visitors = {}
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
228
88
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
229 def visit(self, node, *args, **kwargs):
87
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
230 v = self._visitors.get(node.__class__)
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
231 if not v:
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
232 v = getattr(self, 'visit%s' % node.__class__.__name__)
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
233 self._visitors[node.__class__] = v
88
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
234 return v(node, *args, **kwargs)
87
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
235
88
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
236 def visitExpression(self, node, *args, **kwargs):
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
237 node.node = self.visit(node.node, *args, **kwargs)
87
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
238 return node
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
239
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
240 # Functions & Accessors
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
241
88
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
242 def visitCallFunc(self, node, *args, **kwargs):
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
243 node.node = self.visit(node.node, *args, **kwargs)
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
244 node.args = map(lambda x: self.visit(x, *args, **kwargs), node.args)
87
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
245 if node.star_args:
203
6addf7af09f6 Fix for handling function calls with star/dstar arguments in expressions. Closes #42. Many thanks to David Fraser for reporting the problem and providing a patch!
cmlenz
parents: 198
diff changeset
246 node.star_args = self.visit(node.star_args, *args, **kwargs)
87
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
247 if node.dstar_args:
203
6addf7af09f6 Fix for handling function calls with star/dstar arguments in expressions. Closes #42. Many thanks to David Fraser for reporting the problem and providing a patch!
cmlenz
parents: 198
diff changeset
248 node.dstar_args = self.visit(node.dstar_args, *args, **kwargs)
87
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
249 return node
30
2ee9f28e16e5 Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents: 27
diff changeset
250
118
226613431921 Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents: 116
diff changeset
251 def visitLambda(self, node, *args, **kwargs):
226613431921 Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents: 116
diff changeset
252 node.code = self.visit(node.code, *args, **kwargs)
226613431921 Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents: 116
diff changeset
253 node.filename = '<string>' # workaround for bug in pycodegen
226613431921 Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents: 116
diff changeset
254 return node
226613431921 Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents: 116
diff changeset
255
88
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
256 def visitGetattr(self, node, *args, **kwargs):
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
257 node.expr = self.visit(node.expr, *args, **kwargs)
87
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
258 return node
30
2ee9f28e16e5 Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents: 27
diff changeset
259
88
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
260 def visitSubscript(self, node, *args, **kwargs):
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
261 node.expr = self.visit(node.expr, *args, **kwargs)
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
262 node.subs = map(lambda x: self.visit(x, *args, **kwargs), node.subs)
87
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
263 return node
30
2ee9f28e16e5 Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents: 27
diff changeset
264
87
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
265 # Operators
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
266
88
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
267 def _visitBoolOp(self, node, *args, **kwargs):
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
268 node.nodes = map(lambda x: self.visit(x, *args, **kwargs), node.nodes)
87
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
269 return node
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
270 visitAnd = visitOr = visitBitand = visitBitor = _visitBoolOp
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
271
88
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
272 def _visitBinOp(self, node, *args, **kwargs):
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
273 node.left = self.visit(node.left, *args, **kwargs)
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
274 node.right = self.visit(node.right, *args, **kwargs)
87
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
275 return node
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
276 visitAdd = visitSub = _visitBinOp
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
277 visitDiv = visitFloorDiv = visitMod = visitMul = visitPower = _visitBinOp
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
278 visitLeftShift = visitRightShift = _visitBinOp
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
279
88
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
280 def visitCompare(self, node, *args, **kwargs):
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
281 node.expr = self.visit(node.expr, *args, **kwargs)
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
282 node.ops = map(lambda (op, n): (op, self.visit(n, *args, **kwargs)),
87
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
283 node.ops)
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
284 return node
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
285
88
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
286 def _visitUnaryOp(self, node, *args, **kwargs):
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
287 node.expr = self.visit(node.expr, *args, **kwargs)
87
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
288 return node
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
289 visitUnaryAdd = visitUnarySub = visitNot = visitInvert = _visitUnaryOp
98
bc73d3ab823f Bugfix in `builder` module: attribute values need to be converted to strings when generating streams.
cmlenz
parents: 90
diff changeset
290 visitBackquote = _visitUnaryOp
87
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
291
88
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
292 # Identifiers, Literals and Comprehensions
87
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
293
88
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
294 def _visitDefault(self, node, *args, **kwargs):
87
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
295 return node
88
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
296 visitAssName = visitAssTuple = _visitDefault
102
12efdfe8af47 Ported [118] to trunk
jonas
parents: 101
diff changeset
297 visitConst = visitName = _visitDefault
12efdfe8af47 Ported [118] to trunk
jonas
parents: 101
diff changeset
298
12efdfe8af47 Ported [118] to trunk
jonas
parents: 101
diff changeset
299 def visitKeyword(self, node, *args, **kwargs):
12efdfe8af47 Ported [118] to trunk
jonas
parents: 101
diff changeset
300 node.expr = self.visit(node.expr, *args, **kwargs)
12efdfe8af47 Ported [118] to trunk
jonas
parents: 101
diff changeset
301 return node
87
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
302
88
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
303 def visitDict(self, node, *args, **kwargs):
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
304 node.items = map(lambda (k, v): (self.visit(k, *args, **kwargs),
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
305 self.visit(v, *args, **kwargs)),
87
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
306 node.items)
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
307 return node
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
308
88
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
309 def visitTuple(self, node, *args, **kwargs):
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
310 node.nodes = map(lambda n: self.visit(n, *args, **kwargs), node.nodes)
87
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
311 return node
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
312
88
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
313 def visitList(self, node, *args, **kwargs):
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
314 node.nodes = map(lambda n: self.visit(n, *args, **kwargs), node.nodes)
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
315 return node
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
316
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
317 def visitListComp(self, node, *args, **kwargs):
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
318 node.expr = self.visit(node.expr, *args, **kwargs)
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
319 node.quals = map(lambda x: self.visit(x, *args, **kwargs), node.quals)
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
320 return node
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
321
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
322 def visitListCompFor(self, node, *args, **kwargs):
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
323 node.assign = self.visit(node.assign, *args, **kwargs)
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
324 node.list = self.visit(node.list, *args, **kwargs)
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
325 node.ifs = map(lambda x: self.visit(x, *args, **kwargs), node.ifs)
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
326 return node
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
327
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
328 def visitListCompIf(self, node, *args, **kwargs):
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
329 node.test = self.visit(node.test, *args, **kwargs)
87
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
330 return node
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
331
198
d7c0a7d65783 Implemented support for generator expressions (fixes #16)
mgood
parents: 194
diff changeset
332 def visitGenExpr(self, node, *args, **kwargs):
d7c0a7d65783 Implemented support for generator expressions (fixes #16)
mgood
parents: 194
diff changeset
333 node.code = self.visit(node.code, *args, **kwargs)
d7c0a7d65783 Implemented support for generator expressions (fixes #16)
mgood
parents: 194
diff changeset
334 node.filename = '<string>' # workaround for bug in pycodegen
d7c0a7d65783 Implemented support for generator expressions (fixes #16)
mgood
parents: 194
diff changeset
335 return node
d7c0a7d65783 Implemented support for generator expressions (fixes #16)
mgood
parents: 194
diff changeset
336
d7c0a7d65783 Implemented support for generator expressions (fixes #16)
mgood
parents: 194
diff changeset
337 def visitGenExprFor(self, node, *args, **kwargs):
d7c0a7d65783 Implemented support for generator expressions (fixes #16)
mgood
parents: 194
diff changeset
338 node.assign = self.visit(node.assign, *args, **kwargs)
d7c0a7d65783 Implemented support for generator expressions (fixes #16)
mgood
parents: 194
diff changeset
339 node.iter = self.visit(node.iter, *args, **kwargs)
d7c0a7d65783 Implemented support for generator expressions (fixes #16)
mgood
parents: 194
diff changeset
340 node.ifs = map(lambda x: self.visit(x, *args, **kwargs), node.ifs)
d7c0a7d65783 Implemented support for generator expressions (fixes #16)
mgood
parents: 194
diff changeset
341 return node
d7c0a7d65783 Implemented support for generator expressions (fixes #16)
mgood
parents: 194
diff changeset
342
d7c0a7d65783 Implemented support for generator expressions (fixes #16)
mgood
parents: 194
diff changeset
343 def visitGenExprIf(self, node, *args, **kwargs):
d7c0a7d65783 Implemented support for generator expressions (fixes #16)
mgood
parents: 194
diff changeset
344 node.test = self.visit(node.test, locals_=True, *args, **kwargs)
d7c0a7d65783 Implemented support for generator expressions (fixes #16)
mgood
parents: 194
diff changeset
345 return node
d7c0a7d65783 Implemented support for generator expressions (fixes #16)
mgood
parents: 194
diff changeset
346
d7c0a7d65783 Implemented support for generator expressions (fixes #16)
mgood
parents: 194
diff changeset
347 def visitGenExprInner(self, node, *args, **kwargs):
d7c0a7d65783 Implemented support for generator expressions (fixes #16)
mgood
parents: 194
diff changeset
348 node.expr = self.visit(node.expr, locals_=True, *args, **kwargs)
d7c0a7d65783 Implemented support for generator expressions (fixes #16)
mgood
parents: 194
diff changeset
349 node.quals = map(lambda x: self.visit(x, *args, **kwargs), node.quals)
d7c0a7d65783 Implemented support for generator expressions (fixes #16)
mgood
parents: 194
diff changeset
350 return node
d7c0a7d65783 Implemented support for generator expressions (fixes #16)
mgood
parents: 194
diff changeset
351
87
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
352
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
353 class ExpressionASTTransformer(ASTTransformer):
112
a834a6669681 Docstring typo fix.
cmlenz
parents: 102
diff changeset
354 """Concrete AST transformer that implements the AST transformations needed
a834a6669681 Docstring typo fix.
cmlenz
parents: 102
diff changeset
355 for template expressions.
87
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
356 """
c6f07b7cd3ea Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents: 86
diff changeset
357
180
194025141c89 String literals in XPath expressions are assumed to be UTF-8 encoded.
cmlenz
parents: 167
diff changeset
358 def visitConst(self, node, locals_=False):
194025141c89 String literals in XPath expressions are assumed to be UTF-8 encoded.
cmlenz
parents: 167
diff changeset
359 if isinstance(node.value, str):
194025141c89 String literals in XPath expressions are assumed to be UTF-8 encoded.
cmlenz
parents: 167
diff changeset
360 return ast.Const(node.value.decode('utf-8'))
194025141c89 String literals in XPath expressions are assumed to be UTF-8 encoded.
cmlenz
parents: 167
diff changeset
361 return node
194025141c89 String literals in XPath expressions are assumed to be UTF-8 encoded.
cmlenz
parents: 167
diff changeset
362
131
203f459e7e26 * Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents: 120
diff changeset
363 def visitGetattr(self, node, locals_=False):
203f459e7e26 * Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents: 120
diff changeset
364 return ast.CallFunc(ast.Name('_lookup_attr'), [
203f459e7e26 * Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents: 120
diff changeset
365 ast.Name('data'), self.visit(node.expr, locals_=locals_),
203f459e7e26 * Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents: 120
diff changeset
366 ast.Const(node.attrname)
203f459e7e26 * Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents: 120
diff changeset
367 ])
30
2ee9f28e16e5 Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents: 27
diff changeset
368
131
203f459e7e26 * Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents: 120
diff changeset
369 def visitLambda(self, node, locals_=False):
203f459e7e26 * Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents: 120
diff changeset
370 node.code = self.visit(node.code, locals_=True)
118
226613431921 Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents: 116
diff changeset
371 node.filename = '<string>' # workaround for bug in pycodegen
226613431921 Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents: 116
diff changeset
372 return node
226613431921 Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents: 116
diff changeset
373
131
203f459e7e26 * Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents: 120
diff changeset
374 def visitListComp(self, node, locals_=False):
203f459e7e26 * Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents: 120
diff changeset
375 node.expr = self.visit(node.expr, locals_=True)
203f459e7e26 * Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents: 120
diff changeset
376 node.quals = map(lambda x: self.visit(x, locals_=True), node.quals)
88
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
377 return node
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
378
131
203f459e7e26 * Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents: 120
diff changeset
379 def visitName(self, node, locals_=False):
88
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
380 func_args = [ast.Name('data'), ast.Const(node.name)]
131
203f459e7e26 * Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents: 120
diff changeset
381 if locals_:
88
9ecae580dd93 Add support for list comprehension in expressions (see #12).
cmlenz
parents: 87
diff changeset
382 func_args.append(ast.CallFunc(ast.Name('locals'), []))
116
88ac4c680120 Merged [135:138/branches/experimental/cspeedups].
cmlenz
parents: 112
diff changeset
383 return ast.CallFunc(ast.Name('_lookup_name'), func_args)
81
cc034182061e Template expressions are now compiled to Python bytecode.
cmlenz
parents: 69
diff changeset
384
131
203f459e7e26 * Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents: 120
diff changeset
385 def visitSubscript(self, node, locals_=False):
203f459e7e26 * Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents: 120
diff changeset
386 return ast.CallFunc(ast.Name('_lookup_item'), [
203f459e7e26 * Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents: 120
diff changeset
387 ast.Name('data'), self.visit(node.expr, locals_=locals_),
203f459e7e26 * Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents: 120
diff changeset
388 ast.Tuple(map(lambda x: self.visit(x, locals_=locals_), node.subs))
203f459e7e26 * Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents: 120
diff changeset
389 ])
Copyright (C) 2012-2017 Edgewall Software