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