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() |
