Mercurial > repo
comparison ply-3.8/test/testyacc.py @ 7267:343ff337a19b
<ais523> ` tar -xf ply-3.8.tar.gz
author | HackBot |
---|---|
date | Wed, 23 Mar 2016 02:40:16 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
7266:61a39a120dee | 7267:343ff337a19b |
---|---|
1 # testyacc.py | |
2 | |
3 import unittest | |
4 try: | |
5 import StringIO | |
6 except ImportError: | |
7 import io as StringIO | |
8 | |
9 import sys | |
10 import os | |
11 import warnings | |
12 import re | |
13 import platform | |
14 | |
15 sys.path.insert(0,"..") | |
16 sys.tracebacklimit = 0 | |
17 | |
18 import ply.yacc | |
19 | |
20 def make_pymodule_path(filename): | |
21 path = os.path.dirname(filename) | |
22 file = os.path.basename(filename) | |
23 mod, ext = os.path.splitext(file) | |
24 | |
25 if sys.hexversion >= 0x3040000: | |
26 import importlib.util | |
27 fullpath = importlib.util.cache_from_source(filename, ext=='.pyc') | |
28 elif sys.hexversion >= 0x3020000: | |
29 import imp | |
30 modname = mod+"."+imp.get_tag()+ext | |
31 fullpath = os.path.join(path,'__pycache__',modname) | |
32 else: | |
33 fullpath = filename | |
34 return fullpath | |
35 | |
36 def pymodule_out_exists(filename): | |
37 return os.path.exists(make_pymodule_path(filename)) | |
38 | |
39 def pymodule_out_remove(filename): | |
40 os.remove(make_pymodule_path(filename)) | |
41 | |
42 def implementation(): | |
43 if platform.system().startswith("Java"): | |
44 return "Jython" | |
45 elif hasattr(sys, "pypy_version_info"): | |
46 return "PyPy" | |
47 else: | |
48 return "CPython" | |
49 | |
50 # Check the output to see if it contains all of a set of expected output lines. | |
51 # This alternate implementation looks weird, but is needed to properly handle | |
52 # some variations in error message order that occurs due to dict hash table | |
53 # randomization that was introduced in Python 3.3 | |
54 def check_expected(result, expected): | |
55 # Normalize 'state n' text to account for randomization effects in Python 3.3 | |
56 expected = re.sub(r' state \d+', 'state <n>', expected) | |
57 result = re.sub(r' state \d+', 'state <n>', result) | |
58 | |
59 resultlines = set() | |
60 for line in result.splitlines(): | |
61 if line.startswith("WARNING: "): | |
62 line = line[9:] | |
63 elif line.startswith("ERROR: "): | |
64 line = line[7:] | |
65 resultlines.add(line) | |
66 | |
67 # Selectively remove expected lines from the output | |
68 for eline in expected.splitlines(): | |
69 resultlines = set(line for line in resultlines if not line.endswith(eline)) | |
70 | |
71 # Return True if no result lines remain | |
72 return not bool(resultlines) | |
73 | |
74 def run_import(module): | |
75 code = "import "+module | |
76 exec(code) | |
77 del sys.modules[module] | |
78 | |
79 # Tests related to errors and warnings when building parsers | |
80 class YaccErrorWarningTests(unittest.TestCase): | |
81 def setUp(self): | |
82 sys.stderr = StringIO.StringIO() | |
83 sys.stdout = StringIO.StringIO() | |
84 try: | |
85 os.remove("parsetab.py") | |
86 pymodule_out_remove("parsetab.pyc") | |
87 except OSError: | |
88 pass | |
89 | |
90 if sys.hexversion >= 0x3020000: | |
91 warnings.filterwarnings('ignore', category=ResourceWarning) | |
92 warnings.filterwarnings('ignore', category=DeprecationWarning) | |
93 | |
94 def tearDown(self): | |
95 sys.stderr = sys.__stderr__ | |
96 sys.stdout = sys.__stdout__ | |
97 def test_yacc_badargs(self): | |
98 self.assertRaises(ply.yacc.YaccError,run_import,"yacc_badargs") | |
99 result = sys.stderr.getvalue() | |
100 self.assert_(check_expected(result, | |
101 "yacc_badargs.py:23: Rule 'p_statement_assign' has too many arguments\n" | |
102 "yacc_badargs.py:27: Rule 'p_statement_expr' requires an argument\n" | |
103 )) | |
104 def test_yacc_badid(self): | |
105 self.assertRaises(ply.yacc.YaccError,run_import,"yacc_badid") | |
106 result = sys.stderr.getvalue() | |
107 self.assert_(check_expected(result, | |
108 "yacc_badid.py:32: Illegal name 'bad&rule' in rule 'statement'\n" | |
109 "yacc_badid.py:36: Illegal rule name 'bad&rule'\n" | |
110 )) | |
111 | |
112 def test_yacc_badprec(self): | |
113 try: | |
114 run_import("yacc_badprec") | |
115 except ply.yacc.YaccError: | |
116 result = sys.stderr.getvalue() | |
117 self.assert_(check_expected(result, | |
118 "precedence must be a list or tuple\n" | |
119 )) | |
120 def test_yacc_badprec2(self): | |
121 self.assertRaises(ply.yacc.YaccError,run_import,"yacc_badprec2") | |
122 result = sys.stderr.getvalue() | |
123 self.assert_(check_expected(result, | |
124 "Bad precedence table\n" | |
125 )) | |
126 | |
127 def test_yacc_badprec3(self): | |
128 run_import("yacc_badprec3") | |
129 result = sys.stderr.getvalue() | |
130 self.assert_(check_expected(result, | |
131 "Precedence already specified for terminal 'MINUS'\n" | |
132 "Generating LALR tables\n" | |
133 | |
134 )) | |
135 | |
136 def test_yacc_badrule(self): | |
137 self.assertRaises(ply.yacc.YaccError,run_import,"yacc_badrule") | |
138 result = sys.stderr.getvalue() | |
139 self.assert_(check_expected(result, | |
140 "yacc_badrule.py:24: Syntax error. Expected ':'\n" | |
141 "yacc_badrule.py:28: Syntax error in rule 'statement'\n" | |
142 "yacc_badrule.py:33: Syntax error. Expected ':'\n" | |
143 "yacc_badrule.py:42: Syntax error. Expected ':'\n" | |
144 )) | |
145 | |
146 def test_yacc_badtok(self): | |
147 try: | |
148 run_import("yacc_badtok") | |
149 except ply.yacc.YaccError: | |
150 result = sys.stderr.getvalue() | |
151 self.assert_(check_expected(result, | |
152 "tokens must be a list or tuple\n")) | |
153 | |
154 def test_yacc_dup(self): | |
155 run_import("yacc_dup") | |
156 result = sys.stderr.getvalue() | |
157 self.assert_(check_expected(result, | |
158 "yacc_dup.py:27: Function p_statement redefined. Previously defined on line 23\n" | |
159 "Token 'EQUALS' defined, but not used\n" | |
160 "There is 1 unused token\n" | |
161 "Generating LALR tables\n" | |
162 | |
163 )) | |
164 def test_yacc_error1(self): | |
165 try: | |
166 run_import("yacc_error1") | |
167 except ply.yacc.YaccError: | |
168 result = sys.stderr.getvalue() | |
169 self.assert_(check_expected(result, | |
170 "yacc_error1.py:61: p_error() requires 1 argument\n")) | |
171 | |
172 def test_yacc_error2(self): | |
173 try: | |
174 run_import("yacc_error2") | |
175 except ply.yacc.YaccError: | |
176 result = sys.stderr.getvalue() | |
177 self.assert_(check_expected(result, | |
178 "yacc_error2.py:61: p_error() requires 1 argument\n")) | |
179 | |
180 def test_yacc_error3(self): | |
181 try: | |
182 run_import("yacc_error3") | |
183 except ply.yacc.YaccError: | |
184 e = sys.exc_info()[1] | |
185 result = sys.stderr.getvalue() | |
186 self.assert_(check_expected(result, | |
187 "'p_error' defined, but is not a function or method\n")) | |
188 | |
189 def test_yacc_error4(self): | |
190 self.assertRaises(ply.yacc.YaccError,run_import,"yacc_error4") | |
191 result = sys.stderr.getvalue() | |
192 self.assert_(check_expected(result, | |
193 "yacc_error4.py:62: Illegal rule name 'error'. Already defined as a token\n" | |
194 )) | |
195 | |
196 | |
197 def test_yacc_error5(self): | |
198 run_import("yacc_error5") | |
199 result = sys.stdout.getvalue() | |
200 self.assert_(check_expected(result, | |
201 "Group at 3:10 to 3:12\n" | |
202 "Undefined name 'a'\n" | |
203 "Syntax error at 'b'\n" | |
204 "Syntax error at 4:18 to 4:22\n" | |
205 "Assignment Error at 2:5 to 5:27\n" | |
206 "13\n" | |
207 )) | |
208 | |
209 def test_yacc_error6(self): | |
210 run_import("yacc_error6") | |
211 result = sys.stdout.getvalue() | |
212 self.assert_(check_expected(result, | |
213 "a=7\n" | |
214 "Line 3: Syntax error at '*'\n" | |
215 "c=21\n" | |
216 )) | |
217 | |
218 def test_yacc_error7(self): | |
219 run_import("yacc_error7") | |
220 result = sys.stdout.getvalue() | |
221 self.assert_(check_expected(result, | |
222 "a=7\n" | |
223 "Line 3: Syntax error at '*'\n" | |
224 "c=21\n" | |
225 )) | |
226 | |
227 def test_yacc_inf(self): | |
228 self.assertRaises(ply.yacc.YaccError,run_import,"yacc_inf") | |
229 result = sys.stderr.getvalue() | |
230 self.assert_(check_expected(result, | |
231 "Token 'NUMBER' defined, but not used\n" | |
232 "There is 1 unused token\n" | |
233 "Infinite recursion detected for symbol 'statement'\n" | |
234 "Infinite recursion detected for symbol 'expression'\n" | |
235 )) | |
236 def test_yacc_literal(self): | |
237 self.assertRaises(ply.yacc.YaccError,run_import,"yacc_literal") | |
238 result = sys.stderr.getvalue() | |
239 self.assert_(check_expected(result, | |
240 "yacc_literal.py:36: Literal token '**' in rule 'expression' may only be a single character\n" | |
241 )) | |
242 def test_yacc_misplaced(self): | |
243 self.assertRaises(ply.yacc.YaccError,run_import,"yacc_misplaced") | |
244 result = sys.stderr.getvalue() | |
245 self.assert_(check_expected(result, | |
246 "yacc_misplaced.py:32: Misplaced '|'\n" | |
247 )) | |
248 | |
249 def test_yacc_missing1(self): | |
250 self.assertRaises(ply.yacc.YaccError,run_import,"yacc_missing1") | |
251 result = sys.stderr.getvalue() | |
252 self.assert_(check_expected(result, | |
253 "yacc_missing1.py:24: Symbol 'location' used, but not defined as a token or a rule\n" | |
254 )) | |
255 | |
256 def test_yacc_nested(self): | |
257 run_import("yacc_nested") | |
258 result = sys.stdout.getvalue() | |
259 self.assert_(check_expected(result, | |
260 "A\n" | |
261 "A\n" | |
262 "A\n", | |
263 )) | |
264 | |
265 def test_yacc_nodoc(self): | |
266 run_import("yacc_nodoc") | |
267 result = sys.stderr.getvalue() | |
268 self.assert_(check_expected(result, | |
269 "yacc_nodoc.py:27: No documentation string specified in function 'p_statement_expr' (ignored)\n" | |
270 "Generating LALR tables\n" | |
271 )) | |
272 | |
273 def test_yacc_noerror(self): | |
274 run_import("yacc_noerror") | |
275 result = sys.stderr.getvalue() | |
276 self.assert_(check_expected(result, | |
277 "no p_error() function is defined\n" | |
278 "Generating LALR tables\n" | |
279 )) | |
280 | |
281 def test_yacc_nop(self): | |
282 run_import("yacc_nop") | |
283 result = sys.stderr.getvalue() | |
284 self.assert_(check_expected(result, | |
285 "yacc_nop.py:27: Possible grammar rule 'statement_expr' defined without p_ prefix\n" | |
286 "Generating LALR tables\n" | |
287 )) | |
288 | |
289 def test_yacc_notfunc(self): | |
290 run_import("yacc_notfunc") | |
291 result = sys.stderr.getvalue() | |
292 self.assert_(check_expected(result, | |
293 "'p_statement_assign' not defined as a function\n" | |
294 "Token 'EQUALS' defined, but not used\n" | |
295 "There is 1 unused token\n" | |
296 "Generating LALR tables\n" | |
297 )) | |
298 def test_yacc_notok(self): | |
299 try: | |
300 run_import("yacc_notok") | |
301 except ply.yacc.YaccError: | |
302 result = sys.stderr.getvalue() | |
303 self.assert_(check_expected(result, | |
304 "No token list is defined\n")) | |
305 | |
306 def test_yacc_rr(self): | |
307 run_import("yacc_rr") | |
308 result = sys.stderr.getvalue() | |
309 self.assert_(check_expected(result, | |
310 "Generating LALR tables\n" | |
311 "1 reduce/reduce conflict\n" | |
312 "reduce/reduce conflict in state 15 resolved using rule (statement -> NAME EQUALS NUMBER)\n" | |
313 "rejected rule (expression -> NUMBER) in state 15\n" | |
314 | |
315 )) | |
316 | |
317 def test_yacc_rr_unused(self): | |
318 run_import("yacc_rr_unused") | |
319 result = sys.stderr.getvalue() | |
320 self.assert_(check_expected(result, | |
321 "no p_error() function is defined\n" | |
322 "Generating LALR tables\n" | |
323 "3 reduce/reduce conflicts\n" | |
324 "reduce/reduce conflict in state 1 resolved using rule (rule3 -> A)\n" | |
325 "rejected rule (rule4 -> A) in state 1\n" | |
326 "reduce/reduce conflict in state 1 resolved using rule (rule3 -> A)\n" | |
327 "rejected rule (rule5 -> A) in state 1\n" | |
328 "reduce/reduce conflict in state 1 resolved using rule (rule4 -> A)\n" | |
329 "rejected rule (rule5 -> A) in state 1\n" | |
330 "Rule (rule5 -> A) is never reduced\n" | |
331 )) | |
332 | |
333 def test_yacc_simple(self): | |
334 run_import("yacc_simple") | |
335 result = sys.stderr.getvalue() | |
336 self.assert_(check_expected(result, | |
337 "Generating LALR tables\n" | |
338 )) | |
339 | |
340 def test_yacc_sr(self): | |
341 run_import("yacc_sr") | |
342 result = sys.stderr.getvalue() | |
343 self.assert_(check_expected(result, | |
344 "Generating LALR tables\n" | |
345 "20 shift/reduce conflicts\n" | |
346 )) | |
347 | |
348 def test_yacc_term1(self): | |
349 self.assertRaises(ply.yacc.YaccError,run_import,"yacc_term1") | |
350 result = sys.stderr.getvalue() | |
351 self.assert_(check_expected(result, | |
352 "yacc_term1.py:24: Illegal rule name 'NUMBER'. Already defined as a token\n" | |
353 )) | |
354 | |
355 def test_yacc_unicode_literals(self): | |
356 run_import("yacc_unicode_literals") | |
357 result = sys.stderr.getvalue() | |
358 self.assert_(check_expected(result, | |
359 "Generating LALR tables\n" | |
360 )) | |
361 | |
362 def test_yacc_unused(self): | |
363 self.assertRaises(ply.yacc.YaccError,run_import,"yacc_unused") | |
364 result = sys.stderr.getvalue() | |
365 self.assert_(check_expected(result, | |
366 "yacc_unused.py:62: Symbol 'COMMA' used, but not defined as a token or a rule\n" | |
367 "Symbol 'COMMA' is unreachable\n" | |
368 "Symbol 'exprlist' is unreachable\n" | |
369 )) | |
370 def test_yacc_unused_rule(self): | |
371 run_import("yacc_unused_rule") | |
372 result = sys.stderr.getvalue() | |
373 self.assert_(check_expected(result, | |
374 "yacc_unused_rule.py:62: Rule 'integer' defined, but not used\n" | |
375 "There is 1 unused rule\n" | |
376 "Symbol 'integer' is unreachable\n" | |
377 "Generating LALR tables\n" | |
378 )) | |
379 | |
380 def test_yacc_uprec(self): | |
381 self.assertRaises(ply.yacc.YaccError,run_import,"yacc_uprec") | |
382 result = sys.stderr.getvalue() | |
383 self.assert_(check_expected(result, | |
384 "yacc_uprec.py:37: Nothing known about the precedence of 'UMINUS'\n" | |
385 )) | |
386 | |
387 def test_yacc_uprec2(self): | |
388 self.assertRaises(ply.yacc.YaccError,run_import,"yacc_uprec2") | |
389 result = sys.stderr.getvalue() | |
390 self.assert_(check_expected(result, | |
391 "yacc_uprec2.py:37: Syntax error. Nothing follows %prec\n" | |
392 )) | |
393 | |
394 def test_yacc_prec1(self): | |
395 self.assertRaises(ply.yacc.YaccError,run_import,"yacc_prec1") | |
396 result = sys.stderr.getvalue() | |
397 self.assert_(check_expected(result, | |
398 "Precedence rule 'left' defined for unknown symbol '+'\n" | |
399 "Precedence rule 'left' defined for unknown symbol '*'\n" | |
400 "Precedence rule 'left' defined for unknown symbol '-'\n" | |
401 "Precedence rule 'left' defined for unknown symbol '/'\n" | |
402 )) | |
403 | |
404 def test_pkg_test1(self): | |
405 from pkg_test1 import parser | |
406 self.assertTrue(os.path.exists('pkg_test1/parsing/parsetab.py')) | |
407 self.assertTrue(os.path.exists('pkg_test1/parsing/lextab.py')) | |
408 self.assertTrue(os.path.exists('pkg_test1/parsing/parser.out')) | |
409 r = parser.parse('3+4+5') | |
410 self.assertEqual(r, 12) | |
411 | |
412 def test_pkg_test2(self): | |
413 from pkg_test2 import parser | |
414 self.assertTrue(os.path.exists('pkg_test2/parsing/calcparsetab.py')) | |
415 self.assertTrue(os.path.exists('pkg_test2/parsing/calclextab.py')) | |
416 self.assertTrue(os.path.exists('pkg_test2/parsing/parser.out')) | |
417 r = parser.parse('3+4+5') | |
418 self.assertEqual(r, 12) | |
419 | |
420 def test_pkg_test3(self): | |
421 from pkg_test3 import parser | |
422 self.assertTrue(os.path.exists('pkg_test3/generated/parsetab.py')) | |
423 self.assertTrue(os.path.exists('pkg_test3/generated/lextab.py')) | |
424 self.assertTrue(os.path.exists('pkg_test3/generated/parser.out')) | |
425 r = parser.parse('3+4+5') | |
426 self.assertEqual(r, 12) | |
427 | |
428 def test_pkg_test4(self): | |
429 from pkg_test4 import parser | |
430 self.assertFalse(os.path.exists('pkg_test4/parsing/parsetab.py')) | |
431 self.assertFalse(os.path.exists('pkg_test4/parsing/lextab.py')) | |
432 self.assertFalse(os.path.exists('pkg_test4/parsing/parser.out')) | |
433 r = parser.parse('3+4+5') | |
434 self.assertEqual(r, 12) | |
435 | |
436 def test_pkg_test5(self): | |
437 from pkg_test5 import parser | |
438 self.assertTrue(os.path.exists('pkg_test5/parsing/parsetab.py')) | |
439 self.assertTrue(os.path.exists('pkg_test5/parsing/lextab.py')) | |
440 self.assertTrue(os.path.exists('pkg_test5/parsing/parser.out')) | |
441 r = parser.parse('3+4+5') | |
442 self.assertEqual(r, 12) | |
443 | |
444 def test_pkg_test6(self): | |
445 from pkg_test6 import parser | |
446 self.assertTrue(os.path.exists('pkg_test6/parsing/parsetab.py')) | |
447 self.assertTrue(os.path.exists('pkg_test6/parsing/lextab.py')) | |
448 self.assertTrue(os.path.exists('pkg_test6/parsing/parser.out')) | |
449 r = parser.parse('3+4+5') | |
450 self.assertEqual(r, 12) | |
451 | |
452 unittest.main() |