Mercurial > hg > expressionparser
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() |