OLD | NEW |
| (Empty) |
1 #!/usr/bin/python | |
2 # Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | |
3 # for details. All rights reserved. Use of this source code is governed by a | |
4 # BSD-style license that can be found in the LICENSE file. | |
5 | |
6 import logging.config | |
7 import pprint | |
8 import re | |
9 import sys | |
10 import unittest | |
11 from pegparser import * | |
12 | |
13 | |
14 class PegParserTestCase(unittest.TestCase): | |
15 | |
16 def _run_test(self, grammar, text, expected, | |
17 strings_are_tokens=False, whitespace_rule=None): | |
18 """Utility for running a parser test and comparing results. | |
19 | |
20 Program exits (sys.exit) if expected does not match actual. | |
21 | |
22 Args: | |
23 grammar -- the root rule to be used by the parser. | |
24 text -- the text to parse. | |
25 expected -- the expected abstract syntax tree. None means | |
26 failure is expected. | |
27 strings_are_tokens -- whether strings are treated as tokens. | |
28 whitespace_rule -- the rule used for matching whitespace. | |
29 Default is None, which means that no whitespace is tolerated. | |
30 """ | |
31 parser = PegParser(grammar, whitespace_rule, | |
32 strings_are_tokens=strings_are_tokens) | |
33 actual = None | |
34 error = None | |
35 try: | |
36 actual = parser.parse(text) | |
37 except SyntaxError, e: | |
38 error = e | |
39 pass | |
40 | |
41 if actual != expected: | |
42 msg = ''' | |
43 CONTENT: | |
44 %s | |
45 EXPECTED: | |
46 %s | |
47 ACTUAL: | |
48 %s | |
49 ERROR: %s''' % (text, pprint.pformat(expected), pprint.pformat(actual), error) | |
50 self.fail(msg) | |
51 | |
52 def test_sequence(self): | |
53 sequence = SEQUENCE('A', 'BB', 'C') | |
54 self._run_test(grammar=sequence, text='ABBC', expected=['A', 'BB', 'C']) | |
55 self._run_test(grammar=sequence, text='BBAC', expected=None) | |
56 # Syntax Sugar | |
57 sequence = ['A', 'BB', 'C'] | |
58 self._run_test(grammar=sequence, text='ABBC', expected=['A', 'BB', 'C']) | |
59 self._run_test(grammar=sequence, text='BBAC', expected=None) | |
60 | |
61 def test_regex(self): | |
62 regex = re.compile(r'[A-Za-z]*') | |
63 self._run_test(grammar=regex, text='AaBb', expected='AaBb') | |
64 self._run_test(grammar=regex, text='0AaBb', expected=None) | |
65 self._run_test(grammar=regex, text='Aa0Bb', expected=None) | |
66 | |
67 def test_function(self): | |
68 def Func(): | |
69 return 'ABC' | |
70 self._run_test(grammar=Func, text='ABC', expected=('Func', 'ABC')) | |
71 self._run_test(grammar=Func, text='XYZ', expected=None) | |
72 | |
73 def test_function_label(self): | |
74 def func(): | |
75 return 'ABC' | |
76 | |
77 def _func(): | |
78 return 'ABC' | |
79 | |
80 self._run_test(grammar=func, text='ABC', expected=('func', 'ABC')) | |
81 self._run_test(grammar=_func, text='ABC', expected='ABC') | |
82 | |
83 def test_label(self): | |
84 sequence = [TOKEN('def'), LABEL('funcName', re.compile(r'[a-z0-9]*')), | |
85 TOKEN('():')] | |
86 self._run_test(grammar=sequence, text='def f1():', | |
87 whitespace_rule=' ', expected=[('funcName', 'f1')]) | |
88 self._run_test(grammar=sequence, text='def f2():', | |
89 whitespace_rule=' ', expected=[('funcName', 'f2')]) | |
90 | |
91 def test_or(self): | |
92 grammer = OR('A', 'B') | |
93 self._run_test(grammar=grammer, text='A', expected='A') | |
94 self._run_test(grammar=grammer, text='B', expected='B') | |
95 self._run_test(grammar=grammer, text='C', expected=None) | |
96 | |
97 def test_maybe(self): | |
98 seq = ['A', MAYBE('B'), 'C'] | |
99 self._run_test(grammar=seq, text='ABC', expected=['A', 'B', 'C']) | |
100 self._run_test(grammar=seq, text='ADC', expected=None) | |
101 self._run_test(grammar=seq, text='AC', expected=['A', 'C']) | |
102 self._run_test(grammar=seq, text='AB', expected=None) | |
103 | |
104 def test_many(self): | |
105 seq = ['A', MANY('B'), 'C'] | |
106 self._run_test(grammar=seq, text='ABC', expected=['A', 'B', 'C']) | |
107 self._run_test(grammar=seq, text='ABBBBC', | |
108 expected=['A', 'B', 'B', 'B', 'B', 'C']) | |
109 self._run_test(grammar=seq, text='AC', expected=None) | |
110 | |
111 def test_many_with_separator(self): | |
112 letter = OR('A', 'B', 'C') | |
113 | |
114 def _gram(): | |
115 return [letter, MAYBE([TOKEN(','), _gram])] | |
116 | |
117 self._run_test(grammar=_gram, text='A,B,C,B', | |
118 expected=['A', 'B', 'C', 'B']) | |
119 self._run_test(grammar=_gram, text='A B C', expected=None) | |
120 shortergrammar = MANY(letter, TOKEN(',')) | |
121 self._run_test(grammar=shortergrammar, text='A,B,C,B', | |
122 expected=['A', 'B', 'C', 'B']) | |
123 self._run_test(grammar=shortergrammar, text='A B C', expected=None) | |
124 | |
125 def test_raise(self): | |
126 self._run_test(grammar=['A', 'B'], text='AB', | |
127 expected=['A', 'B']) | |
128 try: | |
129 self._run_test(grammar=['A', 'B', RAISE('test')], text='AB', | |
130 expected=None) | |
131 print 'Expected RuntimeError' | |
132 sys.exit(-1) | |
133 except RuntimeError, e: | |
134 return | |
135 | |
136 def test_whitespace(self): | |
137 gram = MANY('A') | |
138 self._run_test(grammar=gram, text='A A A', expected=None) | |
139 self._run_test(grammar=gram, whitespace_rule=' ', text='A A A', | |
140 expected=['A', 'A', 'A']) | |
141 | |
142 def test_math_expression_syntax(self): | |
143 operator = LABEL('op', OR('+', '-', '/', '*')) | |
144 literal = LABEL('num', re.compile(r'[0-9]+')) | |
145 | |
146 def _exp(): | |
147 return MANY(OR(literal, [TOKEN('('), _exp, TOKEN(')')]), | |
148 separator=operator) | |
149 | |
150 self._run_test(grammar=_exp, | |
151 text='(1-2)+3*((4*5)*6)+(7+8/9)-10', | |
152 expected=[[('num', '1'), ('op', '-'), ('num', '2')], | |
153 ('op', '+'), | |
154 ('num', '3'), | |
155 ('op', '*'), | |
156 [[('num', '4'), ('op', '*'), ('num', '5')], | |
157 ('op', '*'), ('num', '6')], | |
158 ('op', '+'), | |
159 [('num', '7'), ('op', '+'), ('num', '8'), | |
160 ('op', '/'), ('num', '9')], | |
161 ('op', '-'), | |
162 ('num', '10')]) | |
163 | |
164 def test_mini_language(self): | |
165 def name(): | |
166 return re.compile(r'[a-z]+') | |
167 | |
168 def var_decl(): | |
169 return ['var', name, ';'] | |
170 | |
171 def func_invoke(): | |
172 return [name, '(', ')', ';'] | |
173 | |
174 def func_body(): | |
175 return MANY(OR(var_decl, func_invoke)) | |
176 | |
177 def func_decl(): | |
178 return ['function', name, '(', ')', '{', func_body, '}'] | |
179 | |
180 def args(): | |
181 return MANY(name, ',') | |
182 | |
183 def program(): | |
184 return MANY(OR(var_decl, func_decl)) | |
185 | |
186 self._run_test(grammar=program, | |
187 whitespace_rule=OR('\n', ' '), | |
188 strings_are_tokens=True, | |
189 text='var x;\nfunction f(){\n var y;\n g();\n}\n', | |
190 expected=('program',[ | |
191 ('var_decl', [('name', 'x')]), | |
192 ('func_decl', [('name', 'f'), ('func_body', [ | |
193 ('var_decl', [('name', 'y')]), | |
194 ('func_invoke', [('name', 'g')])])])])) | |
195 | |
196 | |
197 if __name__ == "__main__": | |
198 logging.config.fileConfig("logging.conf") | |
199 if __name__ == '__main__': | |
200 unittest.main() | |
OLD | NEW |