1# testyacc.py 2 3import unittest 4try: 5 import StringIO 6except ImportError: 7 import io as StringIO 8 9import sys 10import os 11 12sys.path.insert(0,"..") 13sys.tracebacklimit = 0 14 15import ply.yacc 16 17def check_expected(result,expected): 18 resultlines = [] 19 for line in result.splitlines(): 20 if line.startswith("WARNING: "): 21 line = line[9:] 22 elif line.startswith("ERROR: "): 23 line = line[7:] 24 resultlines.append(line) 25 26 expectedlines = expected.splitlines() 27 if len(resultlines) != len(expectedlines): 28 return False 29 for rline,eline in zip(resultlines,expectedlines): 30 if not rline.endswith(eline): 31 return False 32 return True 33 34def run_import(module): 35 code = "import "+module 36 exec(code) 37 del sys.modules[module] 38 39# Tests related to errors and warnings when building parsers 40class YaccErrorWarningTests(unittest.TestCase): 41 def setUp(self): 42 sys.stderr = StringIO.StringIO() 43 sys.stdout = StringIO.StringIO() 44 try: 45 os.remove("parsetab.py") 46 os.remove("parsetab.pyc") 47 except OSError: 48 pass 49 50 def tearDown(self): 51 sys.stderr = sys.__stderr__ 52 sys.stdout = sys.__stdout__ 53 def test_yacc_badargs(self): 54 self.assertRaises(ply.yacc.YaccError,run_import,"yacc_badargs") 55 result = sys.stderr.getvalue() 56 self.assert_(check_expected(result, 57 "yacc_badargs.py:23: Rule 'p_statement_assign' has too many arguments\n" 58 "yacc_badargs.py:27: Rule 'p_statement_expr' requires an argument\n" 59 )) 60 def test_yacc_badid(self): 61 self.assertRaises(ply.yacc.YaccError,run_import,"yacc_badid") 62 result = sys.stderr.getvalue() 63 self.assert_(check_expected(result, 64 "yacc_badid.py:32: Illegal name 'bad&rule' in rule 'statement'\n" 65 "yacc_badid.py:36: Illegal rule name 'bad&rule'\n" 66 )) 67 68 def test_yacc_badprec(self): 69 try: 70 run_import("yacc_badprec") 71 except ply.yacc.YaccError: 72 result = sys.stderr.getvalue() 73 self.assert_(check_expected(result, 74 "precedence must be a list or tuple\n" 75 )) 76 def test_yacc_badprec2(self): 77 self.assertRaises(ply.yacc.YaccError,run_import,"yacc_badprec2") 78 result = sys.stderr.getvalue() 79 self.assert_(check_expected(result, 80 "Bad precedence table\n" 81 )) 82 83 def test_yacc_badprec3(self): 84 run_import("yacc_badprec3") 85 result = sys.stderr.getvalue() 86 self.assert_(check_expected(result, 87 "Precedence already specified for terminal 'MINUS'\n" 88 "Generating LALR tables\n" 89 90 )) 91 92 def test_yacc_badrule(self): 93 self.assertRaises(ply.yacc.YaccError,run_import,"yacc_badrule") 94 result = sys.stderr.getvalue() 95 self.assert_(check_expected(result, 96 "yacc_badrule.py:24: Syntax error. Expected ':'\n" 97 "yacc_badrule.py:28: Syntax error in rule 'statement'\n" 98 "yacc_badrule.py:33: Syntax error. Expected ':'\n" 99 "yacc_badrule.py:42: Syntax error. Expected ':'\n" 100 )) 101 102 def test_yacc_badtok(self): 103 try: 104 run_import("yacc_badtok") 105 except ply.yacc.YaccError: 106 result = sys.stderr.getvalue() 107 self.assert_(check_expected(result, 108 "tokens must be a list or tuple\n")) 109 110 def test_yacc_dup(self): 111 run_import("yacc_dup") 112 result = sys.stderr.getvalue() 113 self.assert_(check_expected(result, 114 "yacc_dup.py:27: Function p_statement redefined. Previously defined on line 23\n" 115 "Token 'EQUALS' defined, but not used\n" 116 "There is 1 unused token\n" 117 "Generating LALR tables\n" 118 119 )) 120 def test_yacc_error1(self): 121 try: 122 run_import("yacc_error1") 123 except ply.yacc.YaccError: 124 result = sys.stderr.getvalue() 125 self.assert_(check_expected(result, 126 "yacc_error1.py:61: p_error() requires 1 argument\n")) 127 128 def test_yacc_error2(self): 129 try: 130 run_import("yacc_error2") 131 except ply.yacc.YaccError: 132 result = sys.stderr.getvalue() 133 self.assert_(check_expected(result, 134 "yacc_error2.py:61: p_error() requires 1 argument\n")) 135 136 def test_yacc_error3(self): 137 try: 138 run_import("yacc_error3") 139 except ply.yacc.YaccError: 140 e = sys.exc_info()[1] 141 result = sys.stderr.getvalue() 142 self.assert_(check_expected(result, 143 "'p_error' defined, but is not a function or method\n")) 144 145 def test_yacc_error4(self): 146 self.assertRaises(ply.yacc.YaccError,run_import,"yacc_error4") 147 result = sys.stderr.getvalue() 148 self.assert_(check_expected(result, 149 "yacc_error4.py:62: Illegal rule name 'error'. Already defined as a token\n" 150 )) 151 152 def test_yacc_inf(self): 153 self.assertRaises(ply.yacc.YaccError,run_import,"yacc_inf") 154 result = sys.stderr.getvalue() 155 self.assert_(check_expected(result, 156 "Token 'NUMBER' defined, but not used\n" 157 "There is 1 unused token\n" 158 "Infinite recursion detected for symbol 'statement'\n" 159 "Infinite recursion detected for symbol 'expression'\n" 160 )) 161 def test_yacc_literal(self): 162 self.assertRaises(ply.yacc.YaccError,run_import,"yacc_literal") 163 result = sys.stderr.getvalue() 164 self.assert_(check_expected(result, 165 "yacc_literal.py:36: Literal token '**' in rule 'expression' may only be a single character\n" 166 )) 167 def test_yacc_misplaced(self): 168 self.assertRaises(ply.yacc.YaccError,run_import,"yacc_misplaced") 169 result = sys.stderr.getvalue() 170 self.assert_(check_expected(result, 171 "yacc_misplaced.py:32: Misplaced '|'\n" 172 )) 173 174 def test_yacc_missing1(self): 175 self.assertRaises(ply.yacc.YaccError,run_import,"yacc_missing1") 176 result = sys.stderr.getvalue() 177 self.assert_(check_expected(result, 178 "yacc_missing1.py:24: Symbol 'location' used, but not defined as a token or a rule\n" 179 )) 180 181 def test_yacc_nested(self): 182 run_import("yacc_nested") 183 result = sys.stdout.getvalue() 184 self.assert_(check_expected(result, 185 "A\n" 186 "A\n" 187 "A\n", 188 )) 189 190 def test_yacc_nodoc(self): 191 run_import("yacc_nodoc") 192 result = sys.stderr.getvalue() 193 self.assert_(check_expected(result, 194 "yacc_nodoc.py:27: No documentation string specified in function 'p_statement_expr' (ignored)\n" 195 "Generating LALR tables\n" 196 )) 197 198 def test_yacc_noerror(self): 199 run_import("yacc_noerror") 200 result = sys.stderr.getvalue() 201 self.assert_(check_expected(result, 202 "no p_error() function is defined\n" 203 "Generating LALR tables\n" 204 )) 205 206 def test_yacc_nop(self): 207 run_import("yacc_nop") 208 result = sys.stderr.getvalue() 209 self.assert_(check_expected(result, 210 "yacc_nop.py:27: Possible grammar rule 'statement_expr' defined without p_ prefix\n" 211 "Generating LALR tables\n" 212 )) 213 214 def test_yacc_notfunc(self): 215 run_import("yacc_notfunc") 216 result = sys.stderr.getvalue() 217 self.assert_(check_expected(result, 218 "'p_statement_assign' not defined as a function\n" 219 "Token 'EQUALS' defined, but not used\n" 220 "There is 1 unused token\n" 221 "Generating LALR tables\n" 222 )) 223 def test_yacc_notok(self): 224 try: 225 run_import("yacc_notok") 226 except ply.yacc.YaccError: 227 result = sys.stderr.getvalue() 228 self.assert_(check_expected(result, 229 "No token list is defined\n")) 230 231 def test_yacc_rr(self): 232 run_import("yacc_rr") 233 result = sys.stderr.getvalue() 234 self.assert_(check_expected(result, 235 "Generating LALR tables\n" 236 "1 reduce/reduce conflict\n" 237 "reduce/reduce conflict in state 15 resolved using rule (statement -> NAME EQUALS NUMBER)\n" 238 "rejected rule (expression -> NUMBER) in state 15\n" 239 240 )) 241 242 def test_yacc_rr_unused(self): 243 run_import("yacc_rr_unused") 244 result = sys.stderr.getvalue() 245 self.assert_(check_expected(result, 246 "no p_error() function is defined\n" 247 "Generating LALR tables\n" 248 "3 reduce/reduce conflicts\n" 249 "reduce/reduce conflict in state 1 resolved using rule (rule3 -> A)\n" 250 "rejected rule (rule4 -> A) in state 1\n" 251 "reduce/reduce conflict in state 1 resolved using rule (rule3 -> A)\n" 252 "rejected rule (rule5 -> A) in state 1\n" 253 "reduce/reduce conflict in state 1 resolved using rule (rule4 -> A)\n" 254 "rejected rule (rule5 -> A) in state 1\n" 255 "Rule (rule5 -> A) is never reduced\n" 256 )) 257 258 def test_yacc_simple(self): 259 run_import("yacc_simple") 260 result = sys.stderr.getvalue() 261 self.assert_(check_expected(result, 262 "Generating LALR tables\n" 263 )) 264 def test_yacc_sr(self): 265 run_import("yacc_sr") 266 result = sys.stderr.getvalue() 267 self.assert_(check_expected(result, 268 "Generating LALR tables\n" 269 "20 shift/reduce conflicts\n" 270 )) 271 272 def test_yacc_term1(self): 273 self.assertRaises(ply.yacc.YaccError,run_import,"yacc_term1") 274 result = sys.stderr.getvalue() 275 self.assert_(check_expected(result, 276 "yacc_term1.py:24: Illegal rule name 'NUMBER'. Already defined as a token\n" 277 )) 278 279 def test_yacc_unused(self): 280 self.assertRaises(ply.yacc.YaccError,run_import,"yacc_unused") 281 result = sys.stderr.getvalue() 282 self.assert_(check_expected(result, 283 "yacc_unused.py:62: Symbol 'COMMA' used, but not defined as a token or a rule\n" 284 "Symbol 'COMMA' is unreachable\n" 285 "Symbol 'exprlist' is unreachable\n" 286 )) 287 def test_yacc_unused_rule(self): 288 run_import("yacc_unused_rule") 289 result = sys.stderr.getvalue() 290 self.assert_(check_expected(result, 291 "yacc_unused_rule.py:62: Rule 'integer' defined, but not used\n" 292 "There is 1 unused rule\n" 293 "Symbol 'integer' is unreachable\n" 294 "Generating LALR tables\n" 295 )) 296 297 def test_yacc_uprec(self): 298 self.assertRaises(ply.yacc.YaccError,run_import,"yacc_uprec") 299 result = sys.stderr.getvalue() 300 print repr(result) 301 self.assert_(check_expected(result, 302 "yacc_uprec.py:37: Nothing known about the precedence of 'UMINUS'\n" 303 )) 304 305 def test_yacc_uprec2(self): 306 self.assertRaises(ply.yacc.YaccError,run_import,"yacc_uprec2") 307 result = sys.stderr.getvalue() 308 self.assert_(check_expected(result, 309 "yacc_uprec2.py:37: Syntax error. Nothing follows %prec\n" 310 )) 311 312 def test_yacc_prec1(self): 313 self.assertRaises(ply.yacc.YaccError,run_import,"yacc_prec1") 314 result = sys.stderr.getvalue() 315 self.assert_(check_expected(result, 316 "Precedence rule 'left' defined for unknown symbol '+'\n" 317 "Precedence rule 'left' defined for unknown symbol '*'\n" 318 "Precedence rule 'left' defined for unknown symbol '-'\n" 319 "Precedence rule 'left' defined for unknown symbol '/'\n" 320 )) 321 322 323 324unittest.main() 325