1 """Expression parser (for arr1, arr2, cond, and vercond xml attributes of
2 <add> tag)."""
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 import sys
46 """This class represents an expression.
47
48 >>> class A(object):
49 ... x = False
50 ... y = True
51 >>> a = A()
52 >>> e = Expression('x || y')
53 >>> e.eval(a)
54 1
55 >>> Expression('99 & 15').eval(a)
56 3
57 >>> bool(Expression('(99&15)&&y').eval(a))
58 True
59 >>> a.hello_world = False
60 >>> def nameFilter(s):
61 ... return 'hello_' + s.lower()
62 >>> bool(Expression('(99 &15) &&WoRlD', name_filter = nameFilter).eval(a))
63 False
64 >>> Expression('c && d').eval(a)
65 Traceback (most recent call last):
66 ...
67 AttributeError: 'A' object has no attribute 'c'
68 >>> bool(Expression('1 == 1').eval())
69 True
70 >>> bool(Expression('(1 == 1)').eval())
71 True
72 >>> bool(Expression('1 != 1').eval())
73 False
74 >>> bool(Expression('!(1 == 1)').eval())
75 False
76 >>> bool(Expression('!((1 <= 2) && (2 <= 3))').eval())
77 False
78 >>> bool(Expression('(1 <= 2) && (2 <= 3) && (3 <= 4)').eval())
79 True
80 """
81 operators = [ '==', '!=', '>=', '<=', '&&', '||', '&', '|', '-', '!',
82 '<', '>', '/', '*', '+' ]
83 - def __init__(self, expr_str, name_filter = None):
84 try:
85 left, self._op, right = self._partition(expr_str)
86 self._left = self._parse(left, name_filter)
87 self._right = self._parse(right, name_filter)
88 except:
89 print("error while parsing expression '%s'" % expr_str)
90 raise
91
92 - def eval(self, data = None):
93 """Evaluate the expression to an integer."""
94
95 if isinstance(self._left, Expression):
96 left = self._left.eval(data)
97 elif isinstance(self._left, basestring):
98 if self._left == '""':
99 left = ""
100 else:
101 left = data
102 for part in self._left.split("."):
103 left = getattr(left, part)
104 elif self._left is None:
105 pass
106 else:
107 assert(isinstance(self._left, (int, long)))
108 left = self._left
109
110 if not self._op:
111 return left
112
113 if isinstance(self._right, Expression):
114 right = self._right.eval(data)
115 elif isinstance(self._right, basestring):
116 if (not self._right) or self._right == '""':
117 right = ""
118 else:
119 right = getattr(data, self._right)
120 elif self._right is None:
121 pass
122 else:
123 assert(isinstance(self._right, (int, long)))
124 right = self._right
125
126 if self._op == '==':
127 return int(left == right)
128 elif self._op == '!=':
129 return int(left != right)
130 elif self._op == '>=':
131 return int(left >= right)
132 elif self._op == '<=':
133 return int(left <= right)
134 elif self._op == '&&':
135 return int(left and right)
136 elif self._op == '||':
137 return int(left or right)
138 elif self._op == '&':
139 return left & right
140 elif self._op == '|':
141 return left | right
142 elif self._op == '-':
143 return left - right
144 elif self._op == '!':
145 return int(not(right))
146 elif self._op == '>':
147 return int(left > right)
148 elif self._op == '<':
149 return int(left < right)
150 elif self._op == '/':
151 return int(left / right)
152 elif self._op == '*':
153 return int(left * right)
154 elif self._op == '+':
155 return left + right
156 else:
157 raise NotImplementedError("expression syntax error: operator '" + self._op + "' not implemented")
158
160 """Reconstruct the expression to a string."""
161
162 left = str(self._left) if not self._left is None else ""
163 if not self._op: return left
164 right = str(self._right) if not self._right is None else ""
165 return left + ' ' + self._op + ' ' + right
166
167 @classmethod
168 - def _parse(cls, expr_str, name_filter = None):
169 """Returns an Expression, string, or int, depending on the
170 contents of <expr_str>."""
171 if not expr_str:
172
173 return None
174
175 if ("(" in expr_str) or (")" in expr_str):
176 return Expression(expr_str, name_filter)
177 for op in cls.operators:
178 if expr_str.find(op) != -1:
179 return Expression(expr_str, name_filter)
180
181 try:
182 return int(expr_str)
183
184 except ValueError:
185 if name_filter:
186 result = name_filter(expr_str)
187 if isinstance(result, (long, int)):
188
189 return result
190 else:
191
192
193 return '.'.join(name_filter(comp)
194 for comp in expr_str.split("."))
195 else:
196 return expr_str
197
198 @classmethod
200 """Partitions expr_str. See examples below.
201
202 >>> Expression._partition('abc || efg')
203 ('abc', '||', 'efg')
204 >>> Expression._partition('abc||efg')
205 ('abc', '||', 'efg')
206 >>> Expression._partition('abcdefg')
207 ('abcdefg', '', '')
208 >>> Expression._partition(' abcdefg ')
209 ('abcdefg', '', '')
210 >>> Expression._partition(' (a | b) & c ')
211 ('a | b', '&', 'c')
212 >>> Expression._partition('(a | b)!=(b&c)')
213 ('a | b', '!=', 'b&c')
214 >>> Expression._partition('(a== b) &&(( b!=c)||d )')
215 ('a== b', '&&', '( b!=c)||d')
216 >>> Expression._partition('!(1 <= 2)')
217 ('', '!', '(1 <= 2)')
218 >>> Expression._partition('')
219 ('', '', '')
220 >>> Expression._partition('(1 == 1)')
221 ('1 == 1', '', '')
222 """
223
224 expr_str = expr_str.strip()
225
226
227
228 if expr_str.startswith("!"):
229 return "", "!", expr_str[1:].strip()
230
231
232
233
234 left_startpos, left_endpos = cls._scanBrackets(expr_str)
235 if left_startpos >= 0:
236
237
238
239 left_str = expr_str[left_startpos+1:left_endpos].strip()
240
241
242 if left_endpos + 1 == len(expr_str):
243 return left_str, "", ""
244
245
246 op_startpos = left_endpos+1
247 while expr_str[op_startpos] == " ":
248 op_startpos += 1
249
250
251
252 for op_endpos in xrange(op_startpos+1, op_startpos-1, -1):
253 op_str = expr_str[op_startpos:op_endpos+1]
254 if op_str in cls.operators:
255 break
256 else:
257 raise ValueError("expression syntax error: expected operator at '%s'"%expr_str[op_startpos:])
258 else:
259
260 for op_startpos, ch in enumerate(expr_str):
261 if ch == ' ': continue
262 if ch == '(' or ch == ')':
263 raise ValueError("expression syntax error: expected operator before '%s'"%expr_str[op_startpos:])
264
265
266 for op_endpos in xrange(op_startpos+1, op_startpos-1, -1):
267 op_str = expr_str[op_startpos:op_endpos+1]
268 if op_str in cls.operators:
269 break
270 else:
271 continue
272 break
273 else:
274
275 left_str = expr_str.strip()
276 op_str = ''
277 right_str = ''
278 return left_str, op_str, right_str
279
280 left_str = expr_str[:op_startpos].strip()
281
282
283
284 right_startpos, right_endpos = cls._scanBrackets(expr_str, op_endpos+1)
285 if right_startpos >= 0:
286
287
288
289 right_str = expr_str[right_startpos+1:right_endpos].strip()
290
291 if expr_str[right_endpos+1:] and not expr_str[right_endpos+1:] == ' ':
292 for op in cls.operators:
293 if expr_str.find(op) != -1:
294 break
295 else:
296 raise ValueError("expression syntax error: unexpected trailing characters '%s'"%expr_str[right_endpos+1:])
297
298
299
300
301 right_str = expr_str[op_endpos+1:].strip()
302 else:
303
304 right_str = expr_str[op_endpos+1:].strip()
305
306 if ("(" in right_str) or (")" in right_str):
307 raise ValueError("expression syntax error: unexpected brackets in '%s'"%right_str)
308 return left_str, op_str, right_str
309
310 @staticmethod
312 """Looks for matching brackets.
313
314 >>> Expression._scanBrackets('abcde')
315 (-1, -1)
316 >>> Expression._scanBrackets('()')
317 (0, 1)
318 >>> Expression._scanBrackets('(abc(def))g')
319 (0, 9)
320 >>> s = ' (abc(dd efy 442))xxg'
321 >>> startpos, endpos = Expression._scanBrackets(s)
322 >>> print(s[startpos+1:endpos])
323 abc(dd efy 442)
324 """
325 startpos = -1
326 endpos = -1
327 scandepth = 0
328 for scanpos in xrange(fromIndex, len(expr_str)):
329 scanchar = expr_str[scanpos]
330 if scanchar == "(":
331 if startpos == -1:
332 startpos = scanpos
333 scandepth += 1
334 elif scanchar == ")":
335 scandepth -= 1
336 if scandepth == 0:
337 endpos = scanpos
338 break
339 else:
340 if startpos != -1 or endpos != -1:
341 raise ValueError("expression syntax error (non-matching brackets?)")
342 return (startpos, endpos)
343
344 if __name__ == "__main__":
345 import doctest
346 doctest.testmod()
347