comparison expr.py @ 4:a42bb6dc2fa7

change parse mapping to **kwargs
author Jeff Hammel <jhammel@mozilla.com>
date Fri, 03 Jun 2011 08:22:56 -0700
parents 5ac8eed85684
children 325dccc38308
comparison
equal deleted inserted replaced
3:5ac8eed85684 4:a42bb6dc2fa7
21 21
22 # Identifiers take their values from a mapping dictionary passed as the second 22 # Identifiers take their values from a mapping dictionary passed as the second
23 # argument. 23 # argument.
24 24
25 __all__ = ['parse', 'ParseError'] 25 __all__ = ['parse', 'ParseError']
26 import re, unittest 26 import re
27 27
28 # token classes 28 # token classes
29 class ident_token: 29 class ident_token(object):
30 def __init__(self, value): 30 def __init__(self, value):
31 self.value = value 31 self.value = value
32 def nud(self, parser): 32 def nud(self, parser):
33 # identifiers take their value from the value mappings passed 33 # identifiers take their value from the value mappings passed
34 # to the parser 34 # to the parser
35 return parser.value(self.value) 35 return parser.value(self.value)
36 36
37 class literal_token: 37 class literal_token(object):
38 def __init__(self, value): 38 def __init__(self, value):
39 self.value = value 39 self.value = value
40 def nud(self, parser): 40 def nud(self, parser):
41 return self.value 41 return self.value
42 42
43 class eq_op_token: 43 class eq_op_token(object):
44 "==" 44 "=="
45 lbp = 20 45 lbp = 20
46 def led(self, parser, left): 46 def led(self, parser, left):
47 return left == parser.expression(self.lbp) 47 return left == parser.expression(self.lbp)
48 48
49 class neq_op_token: 49 class neq_op_token(object):
50 "!=" 50 "!="
51 lbp = 20 51 lbp = 20
52 def led(self, parser, left): 52 def led(self, parser, left):
53 return left != parser.expression(self.lbp) 53 return left != parser.expression(self.lbp)
54 54
55 class and_op_token: 55 class and_op_token(object):
56 "&&" 56 "&&"
57 lbp = 11 57 lbp = 11
58 def led(self, parser, left): 58 def led(self, parser, left):
59 right = parser.expression(self.lbp) 59 right = parser.expression(self.lbp)
60 return left and right 60 return left and right
61 61
62 class or_op_token: 62 class or_op_token(object):
63 "||" 63 "||"
64 lbp = 10 64 lbp = 10
65 def led(self, parser, left): 65 def led(self, parser, left):
66 right = parser.expression(self.lbp) 66 right = parser.expression(self.lbp)
67 return left or right 67 return left or right
68 68
69 class lparen_token: 69 class lparen_token(object):
70 "(" 70 "("
71 lbp = 50 71 lbp = 50
72 def nud(self, parser): 72 def nud(self, parser):
73 expr = parser.expression() 73 expr = parser.expression()
74 parser.advance(rparen_token) 74 parser.advance(rparen_token)
75 return expr 75 return expr
76 76
77 class rparen_token: 77 class rparen_token(object):
78 ")" 78 ")"
79 lbp = 0 79 lbp = 0
80 80
81 class end_token: 81 class end_token(object):
82 # lowest left binding power, always ends parsing 82 # lowest left binding power, always ends parsing
83 lbp = 0 83 lbp = 0
84 84
85 precedence = [(end_token, rparen_token),
86 (or_op_token,),
87 (and_op_token,),
88 (eq_op_token, neq_op_token),
89 (lparen_token,),
90 ]
91
85 class ParseError(Exception): 92 class ParseError(Exception):
86 pass 93 """errror parsing conditional expression"""
87 94
88 class ExpressionParser(object): 95 class ExpressionParser(object):
89 def __init__(self, text, valuemapping): 96 def __init__(self, text, valuemapping):
90 """ 97 """
91 Initialize the parser with input |text|, and |valuemapping| as 98 Initialize the parser with input |text|, and |valuemapping| as
167 try: 174 try:
168 self.iter = self._tokenize() 175 self.iter = self._tokenize()
169 self.token = self.iter.next() 176 self.token = self.iter.next()
170 return self.expression() 177 return self.expression()
171 except: 178 except:
172 raise ParseError 179 raise ParseError("could not parse: %s" % self.text)
173 180
174 def parse(text, values): 181 __call__ = parse
182
183 def parse(text, **values):
175 """ 184 """
176 Parse and evaluate a boolean expression in |text|. Use |values| to look 185 Parse and evaluate a boolean expression in |text|. Use |values| to look
177 up the value of identifiers referenced in the expression. Returns the final 186 up the value of identifiers referenced in the expression. Returns the final
178 value of the expression. A ParseError will be raised if parsing fails. 187 value of the expression. A ParseError will be raised if parsing fails.
179 """ 188 """
180 return ExpressionParser(text, values).parse() 189 return ExpressionParser(text, values).parse()
181
182 class ExpressionParserUnittest(unittest.TestCase):
183 def test_BasicValues(self):
184 self.assertEqual(1, parse("1", {}))
185 self.assertEqual(100, parse("100", {}))
186 self.assertEqual(True, parse("true", {}))
187 self.assertEqual(False, parse("false", {}))
188 self.assertEqual("", parse('""', {}))
189 self.assertEqual("foo bar", parse('"foo bar"', {}))
190 self.assertEqual(1, parse("foo", {'foo':1}))
191 self.assertEqual(True, parse("bar", {'bar':True}))
192 self.assertEqual("xyz", parse("abc123", {'abc123':"xyz"}))
193
194 def test_Equality(self):
195 self.assertTrue(parse("true == true", {}))
196 self.assertTrue(parse("false == false", {}))
197 self.assertTrue(parse("false == false", {}))
198 self.assertTrue(parse("1 == 1", {}))
199 self.assertTrue(parse("100 == 100", {}))
200 self.assertTrue(parse('"some text" == "some text"', {}))
201 self.assertTrue(parse("true != false", {}))
202 self.assertTrue(parse("1 != 2", {}))
203 self.assertTrue(parse('"text" != "other text"', {}))
204 self.assertTrue(parse("foo == true", {'foo': True}))
205 self.assertTrue(parse("foo == 1", {'foo': 1}))
206 self.assertTrue(parse('foo == "bar"', {'foo': 'bar'}))
207 self.assertTrue(parse("foo == bar", {'foo': True, 'bar': True}))
208 self.assertTrue(parse("true == foo", {'foo': True}))
209 self.assertTrue(parse("foo != true", {'foo': False}))
210 self.assertTrue(parse("foo != 2", {'foo': 1}))
211 self.assertTrue(parse('foo != "bar"', {'foo': 'abc'}))
212 self.assertTrue(parse("foo != bar", {'foo': True, 'bar': False}))
213 self.assertTrue(parse("true != foo", {'foo': False}))
214
215 def test_Conjunctions(self):
216 self.assertTrue(parse("true && true", {}))
217 self.assertTrue(parse("true || false", {}))
218 self.assertFalse(parse("false || false", {}))
219 self.assertFalse(parse("true && false", {}))
220 self.assertTrue(parse("true || false && false", {}))
221
222 def test_Parens(self):
223 self.assertTrue(parse("(true)", {}))
224 self.assertEquals(10, parse("(10)", {}))
225 self.assertEquals('foo', parse('("foo")', {}))
226 self.assertEquals(1, parse("(foo)", {'foo':1}))
227 self.assertTrue(parse("(true == true)", {}))
228 self.assertTrue(parse("(true != false)", {}))
229 self.assertTrue(parse("(true && true)", {}))
230 self.assertTrue(parse("(true || false)", {}))
231 self.assertTrue(parse("(true && true || false)", {}))
232 self.assertFalse(parse("(true || false) && false", {}))
233 self.assertTrue(parse("(true || false) && true", {}))
234 self.assertTrue(parse("true && (true || false)", {}))
235 self.assertTrue(parse("true && (true || false)", {}))
236
237 if __name__ == '__main__':
238 unittest.main()