Mercurial > hg > expressionparser
comparison expr.py @ 2:94a293b914af
add documentation, clean up interface slightly, tweak tests
| author | Ted Mielczarek <ted.mielczarek@gmail.com> |
|---|---|
| date | Thu, 02 Jun 2011 07:44:25 -0400 |
| parents | c45135ec8c13 |
| children | 5ac8eed85684 |
comparison
equal
deleted
inserted
replaced
| 1:c45135ec8c13 | 2:94a293b914af |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 | 2 |
| 3 # Implements a top-down parser/evaluator for simple boolean expressions. | |
| 4 # ideas taken from http://effbot.org/zone/simple-top-down-parsing.htm | |
| 5 # | |
| 6 # Rough grammar: | |
| 7 # expr := literal | |
| 8 # | '(' expr ')' | |
| 9 # | expr '&&' expr | |
| 10 # | expr '||' expr | |
| 11 # | expr '==' expr | |
| 12 # | expr '!=' expr | |
| 13 # literal := BOOL | |
| 14 # | INT | |
| 15 # | STRING | |
| 16 # | IDENT | |
| 17 # BOOL := true|false | |
| 18 # INT := [0-9]+ | |
| 19 # STRING := "[^"]*" | |
| 20 # IDENT := [A-Za-z_]\w* | |
| 21 | |
| 22 # Identifiers take their values from a mapping dictionary passed as the second | |
| 23 # argument. | |
| 24 | |
| 25 __all__ = ['parse', 'ParseError'] | |
| 3 import re, unittest | 26 import re, unittest |
| 4 | 27 |
| 5 # ideas taken from http://effbot.org/zone/simple-top-down-parsing.htm | |
| 6 # token classes | 28 # token classes |
| 7 class ident_token: | 29 class ident_token: |
| 8 def __init__(self, value): | 30 def __init__(self, value): |
| 9 self.value = value | 31 self.value = value |
| 10 def nud(self, parser): | 32 def nud(self, parser): |
| 69 return self.value | 91 return self.value |
| 70 | 92 |
| 71 class end_token: | 93 class end_token: |
| 72 # lowest left binding power, always ends parsing | 94 # lowest left binding power, always ends parsing |
| 73 lbp = 0 | 95 lbp = 0 |
| 96 | |
| 97 class ParseError(Exception): | |
| 98 pass | |
| 74 | 99 |
| 75 class ExpressionParser(object): | 100 class ExpressionParser(object): |
| 76 def __init__(self, text, valuemapping): | 101 def __init__(self, text, valuemapping): |
| 77 """ | 102 """ |
| 78 Initialize the parser with input |text|, and |valuemapping| as | 103 Initialize the parser with input |text|, and |valuemapping| as |
| 146 return left | 171 return left |
| 147 | 172 |
| 148 def parse(self): | 173 def parse(self): |
| 149 """ | 174 """ |
| 150 Parse and return the value of the expression in the text | 175 Parse and return the value of the expression in the text |
| 151 passed to the constructor. | 176 passed to the constructor. Raises a ParseError if the expression |
| 152 """ | 177 could not be parsed. |
| 153 self.iter = self._tokenize() | 178 """ |
| 154 self.token = self.iter.next() | 179 try: |
| 155 return self.expression() | 180 self.iter = self._tokenize() |
| 181 self.token = self.iter.next() | |
| 182 return self.expression() | |
| 183 except: | |
| 184 raise ParseError | |
| 185 | |
| 186 def parse(text, values): | |
| 187 """ | |
| 188 Parse and evaluate a boolean expression in |text|. Use |values| to look | |
| 189 up the value of identifiers referenced in the expression. Returns the final | |
| 190 value of the expression. A ParseError will be raised if parsing fails. | |
| 191 """ | |
| 192 return ExpressionParser(text, values).parse() | |
| 156 | 193 |
| 157 class ExpressionParserUnittest(unittest.TestCase): | 194 class ExpressionParserUnittest(unittest.TestCase): |
| 158 def parse(self, text, values): | |
| 159 return ExpressionParser(text, values).parse() | |
| 160 | |
| 161 def test_BasicValues(self): | 195 def test_BasicValues(self): |
| 162 self.assertEqual(1, self.parse("1", {})) | 196 self.assertEqual(1, parse("1", {})) |
| 163 self.assertEqual(100, self.parse("100", {})) | 197 self.assertEqual(100, parse("100", {})) |
| 164 self.assertEqual(True, self.parse("true", {})) | 198 self.assertEqual(True, parse("true", {})) |
| 165 self.assertEqual(False, self.parse("false", {})) | 199 self.assertEqual(False, parse("false", {})) |
| 166 self.assertEqual("", self.parse('""', {})) | 200 self.assertEqual("", parse('""', {})) |
| 167 self.assertEqual("foo bar", self.parse('"foo bar"', {})) | 201 self.assertEqual("foo bar", parse('"foo bar"', {})) |
| 168 self.assertEqual(1, self.parse("foo", {'foo':1})) | 202 self.assertEqual(1, parse("foo", {'foo':1})) |
| 169 self.assertEqual(True, self.parse("bar", {'bar':True})) | 203 self.assertEqual(True, parse("bar", {'bar':True})) |
| 170 self.assertEqual("xyz", self.parse("abc123", {'abc123':"xyz"})) | 204 self.assertEqual("xyz", parse("abc123", {'abc123':"xyz"})) |
| 171 | 205 |
| 172 def test_Equality(self): | 206 def test_Equality(self): |
| 173 self.assertTrue(self.parse("true == true", {})) | 207 self.assertTrue(parse("true == true", {})) |
| 174 self.assertTrue(self.parse("false == false", {})) | 208 self.assertTrue(parse("false == false", {})) |
| 175 self.assertTrue(self.parse("false == false", {})) | 209 self.assertTrue(parse("false == false", {})) |
| 176 self.assertTrue(self.parse("1 == 1", {})) | 210 self.assertTrue(parse("1 == 1", {})) |
| 177 self.assertTrue(self.parse("100 == 100", {})) | 211 self.assertTrue(parse("100 == 100", {})) |
| 178 self.assertTrue(self.parse('"some text" == "some text"', {})) | 212 self.assertTrue(parse('"some text" == "some text"', {})) |
| 179 self.assertTrue(self.parse("true != false", {})) | 213 self.assertTrue(parse("true != false", {})) |
| 180 self.assertTrue(self.parse("1 != 2", {})) | 214 self.assertTrue(parse("1 != 2", {})) |
| 181 self.assertTrue(self.parse('"text" != "other text"', {})) | 215 self.assertTrue(parse('"text" != "other text"', {})) |
| 182 self.assertTrue(self.parse("foo == true", {'foo': True})) | 216 self.assertTrue(parse("foo == true", {'foo': True})) |
| 183 self.assertTrue(self.parse("foo == 1", {'foo': 1})) | 217 self.assertTrue(parse("foo == 1", {'foo': 1})) |
| 184 self.assertTrue(self.parse('foo == "bar"', {'foo': 'bar'})) | 218 self.assertTrue(parse('foo == "bar"', {'foo': 'bar'})) |
| 185 self.assertTrue(self.parse("foo == bar", {'foo': True, 'bar': True})) | 219 self.assertTrue(parse("foo == bar", {'foo': True, 'bar': True})) |
| 186 self.assertTrue(self.parse("true == foo", {'foo': True})) | 220 self.assertTrue(parse("true == foo", {'foo': True})) |
| 187 self.assertTrue(self.parse("foo != true", {'foo': False})) | 221 self.assertTrue(parse("foo != true", {'foo': False})) |
| 188 self.assertTrue(self.parse("foo != 2", {'foo': 1})) | 222 self.assertTrue(parse("foo != 2", {'foo': 1})) |
| 189 self.assertTrue(self.parse('foo != "bar"', {'foo': 'abc'})) | 223 self.assertTrue(parse('foo != "bar"', {'foo': 'abc'})) |
| 190 self.assertTrue(self.parse("foo != bar", {'foo': True, 'bar': False})) | 224 self.assertTrue(parse("foo != bar", {'foo': True, 'bar': False})) |
| 191 self.assertTrue(self.parse("true != foo", {'foo': False})) | 225 self.assertTrue(parse("true != foo", {'foo': False})) |
| 192 | 226 |
| 193 def test_Conjunctions(self): | 227 def test_Conjunctions(self): |
| 194 self.assertTrue(self.parse("true && true", {})) | 228 self.assertTrue(parse("true && true", {})) |
| 195 self.assertTrue(self.parse("true || false", {})) | 229 self.assertTrue(parse("true || false", {})) |
| 196 self.assertFalse(self.parse("false || false", {})) | 230 self.assertFalse(parse("false || false", {})) |
| 197 self.assertFalse(self.parse("true && false", {})) | 231 self.assertFalse(parse("true && false", {})) |
| 232 self.assertTrue(parse("true || false && false", {})) | |
| 233 | |
| 234 def test_Parens(self): | |
| 235 self.assertTrue(parse("(true)", {})) | |
| 236 self.assertEquals(10, parse("(10)", {})) | |
| 237 self.assertEquals('foo', parse('("foo")', {})) | |
| 238 self.assertEquals(1, parse("(foo)", {'foo':1})) | |
| 239 self.assertTrue(parse("(true == true)", {})) | |
| 240 self.assertTrue(parse("(true != false)", {})) | |
| 241 self.assertTrue(parse("(true && true)", {})) | |
| 242 self.assertTrue(parse("(true || false)", {})) | |
| 243 self.assertTrue(parse("(true && true || false)", {})) | |
| 244 self.assertFalse(parse("(true || false) && false", {})) | |
| 245 self.assertTrue(parse("(true || false) && true", {})) | |
| 246 self.assertTrue(parse("true && (true || false)", {})) | |
| 247 self.assertTrue(parse("true && (true || false)", {})) | |
| 198 | 248 |
| 199 if __name__ == '__main__': | 249 if __name__ == '__main__': |
| 200 unittest.main() | 250 unittest.main() |
