12632Sstever@eecs.umich.edu# testyacc.py
22632Sstever@eecs.umich.edu
36498Snate@binkert.orgimport unittest
46498Snate@binkert.orgtry:
56498Snate@binkert.org    import StringIO
66498Snate@binkert.orgexcept ImportError:
76498Snate@binkert.org    import io as StringIO
82632Sstever@eecs.umich.edu
96498Snate@binkert.orgimport sys
106498Snate@binkert.orgimport os
112632Sstever@eecs.umich.edu
126498Snate@binkert.orgsys.path.insert(0,"..")
136498Snate@binkert.orgsys.tracebacklimit = 0
142632Sstever@eecs.umich.edu
156498Snate@binkert.orgimport ply.yacc
162632Sstever@eecs.umich.edu
176498Snate@binkert.orgdef check_expected(result,expected):
186498Snate@binkert.org    resultlines = []
196498Snate@binkert.org    for line in result.splitlines():
206498Snate@binkert.org        if line.startswith("WARNING: "):
216498Snate@binkert.org            line = line[9:]
226498Snate@binkert.org        elif line.startswith("ERROR: "):
236498Snate@binkert.org            line = line[7:]
246498Snate@binkert.org        resultlines.append(line)
252632Sstever@eecs.umich.edu
266498Snate@binkert.org    expectedlines = expected.splitlines()
276498Snate@binkert.org    if len(resultlines) != len(expectedlines):
286498Snate@binkert.org        return False
296498Snate@binkert.org    for rline,eline in zip(resultlines,expectedlines):
306498Snate@binkert.org        if not rline.endswith(eline):
316498Snate@binkert.org            return False
326498Snate@binkert.org    return True
332632Sstever@eecs.umich.edu
346498Snate@binkert.orgdef run_import(module):
356498Snate@binkert.org    code = "import "+module
366498Snate@binkert.org    exec(code)
376498Snate@binkert.org    del sys.modules[module]
386498Snate@binkert.org
396498Snate@binkert.org# Tests related to errors and warnings when building parsers
406498Snate@binkert.orgclass YaccErrorWarningTests(unittest.TestCase):
416498Snate@binkert.org    def setUp(self):
426498Snate@binkert.org        sys.stderr = StringIO.StringIO()
436498Snate@binkert.org        sys.stdout = StringIO.StringIO()
446498Snate@binkert.org        try:
456498Snate@binkert.org            os.remove("parsetab.py")
466498Snate@binkert.org            os.remove("parsetab.pyc")
476498Snate@binkert.org        except OSError:
486498Snate@binkert.org            pass
496498Snate@binkert.org
506498Snate@binkert.org    def tearDown(self):
516498Snate@binkert.org        sys.stderr = sys.__stderr__
526498Snate@binkert.org        sys.stdout = sys.__stdout__
536498Snate@binkert.org    def test_yacc_badargs(self):
546498Snate@binkert.org        self.assertRaises(ply.yacc.YaccError,run_import,"yacc_badargs")
556498Snate@binkert.org        result = sys.stderr.getvalue()
566498Snate@binkert.org        self.assert_(check_expected(result,
576498Snate@binkert.org                                    "yacc_badargs.py:23: Rule 'p_statement_assign' has too many arguments\n"
586498Snate@binkert.org                                    "yacc_badargs.py:27: Rule 'p_statement_expr' requires an argument\n"
596498Snate@binkert.org                                    ))
606498Snate@binkert.org    def test_yacc_badid(self):
616498Snate@binkert.org        self.assertRaises(ply.yacc.YaccError,run_import,"yacc_badid")
626498Snate@binkert.org        result = sys.stderr.getvalue()
636498Snate@binkert.org        self.assert_(check_expected(result,
646498Snate@binkert.org                                    "yacc_badid.py:32: Illegal name 'bad&rule' in rule 'statement'\n"
656498Snate@binkert.org                                    "yacc_badid.py:36: Illegal rule name 'bad&rule'\n"
666498Snate@binkert.org                                    ))
672632Sstever@eecs.umich.edu
686498Snate@binkert.org    def test_yacc_badprec(self):
696498Snate@binkert.org        try:
706498Snate@binkert.org            run_import("yacc_badprec")
716498Snate@binkert.org        except ply.yacc.YaccError:
726498Snate@binkert.org            result = sys.stderr.getvalue()
736498Snate@binkert.org            self.assert_(check_expected(result,
746498Snate@binkert.org                                        "precedence must be a list or tuple\n"
756498Snate@binkert.org                                        ))
766498Snate@binkert.org    def test_yacc_badprec2(self):
776498Snate@binkert.org        self.assertRaises(ply.yacc.YaccError,run_import,"yacc_badprec2")
786498Snate@binkert.org        result = sys.stderr.getvalue()
796498Snate@binkert.org        self.assert_(check_expected(result,
806498Snate@binkert.org                                    "Bad precedence table\n"
816498Snate@binkert.org                                    ))
822632Sstever@eecs.umich.edu
836498Snate@binkert.org    def test_yacc_badprec3(self):
846498Snate@binkert.org        run_import("yacc_badprec3")
856498Snate@binkert.org        result = sys.stderr.getvalue()
866498Snate@binkert.org        self.assert_(check_expected(result,
876498Snate@binkert.org                                    "Precedence already specified for terminal 'MINUS'\n"
886498Snate@binkert.org                                    "Generating LALR tables\n"
892632Sstever@eecs.umich.edu
906498Snate@binkert.org                                    ))
916498Snate@binkert.org
926498Snate@binkert.org    def test_yacc_badrule(self):
936498Snate@binkert.org        self.assertRaises(ply.yacc.YaccError,run_import,"yacc_badrule")
946498Snate@binkert.org        result = sys.stderr.getvalue()
956498Snate@binkert.org        self.assert_(check_expected(result,
966498Snate@binkert.org                                    "yacc_badrule.py:24: Syntax error. Expected ':'\n"
976498Snate@binkert.org                                    "yacc_badrule.py:28: Syntax error in rule 'statement'\n"
986498Snate@binkert.org                                    "yacc_badrule.py:33: Syntax error. Expected ':'\n"
996498Snate@binkert.org                                    "yacc_badrule.py:42: Syntax error. Expected ':'\n"
1006498Snate@binkert.org                                    ))
1012632Sstever@eecs.umich.edu
1026498Snate@binkert.org    def test_yacc_badtok(self):
1036498Snate@binkert.org        try:
1046498Snate@binkert.org            run_import("yacc_badtok")
1056498Snate@binkert.org        except ply.yacc.YaccError:
1066498Snate@binkert.org            result = sys.stderr.getvalue()
1076498Snate@binkert.org            self.assert_(check_expected(result,
1086498Snate@binkert.org                                        "tokens must be a list or tuple\n"))
1092632Sstever@eecs.umich.edu
1106498Snate@binkert.org    def test_yacc_dup(self):
1116498Snate@binkert.org        run_import("yacc_dup")
1126498Snate@binkert.org        result = sys.stderr.getvalue()
1136498Snate@binkert.org        self.assert_(check_expected(result,
1146498Snate@binkert.org                                    "yacc_dup.py:27: Function p_statement redefined. Previously defined on line 23\n"
1156498Snate@binkert.org                                    "Token 'EQUALS' defined, but not used\n"
1166498Snate@binkert.org                                    "There is 1 unused token\n"
1176498Snate@binkert.org                                    "Generating LALR tables\n"
1182632Sstever@eecs.umich.edu
1196498Snate@binkert.org                                    ))
1206498Snate@binkert.org    def test_yacc_error1(self):
1216498Snate@binkert.org        try:
1226498Snate@binkert.org            run_import("yacc_error1")
1236498Snate@binkert.org        except ply.yacc.YaccError:
1246498Snate@binkert.org            result = sys.stderr.getvalue()
1256498Snate@binkert.org            self.assert_(check_expected(result,
1266498Snate@binkert.org                                        "yacc_error1.py:61: p_error() requires 1 argument\n"))
1272632Sstever@eecs.umich.edu
1286498Snate@binkert.org    def test_yacc_error2(self):
1296498Snate@binkert.org        try:
1306498Snate@binkert.org            run_import("yacc_error2")
1316498Snate@binkert.org        except ply.yacc.YaccError:
1326498Snate@binkert.org            result = sys.stderr.getvalue()
1336498Snate@binkert.org            self.assert_(check_expected(result,
1346498Snate@binkert.org                                        "yacc_error2.py:61: p_error() requires 1 argument\n"))
1352632Sstever@eecs.umich.edu
1366498Snate@binkert.org    def test_yacc_error3(self):
1376498Snate@binkert.org        try:
1386498Snate@binkert.org            run_import("yacc_error3")
1396498Snate@binkert.org        except ply.yacc.YaccError:
1406498Snate@binkert.org            e = sys.exc_info()[1]
1416498Snate@binkert.org            result = sys.stderr.getvalue()
1426498Snate@binkert.org            self.assert_(check_expected(result,
1436498Snate@binkert.org                                        "'p_error' defined, but is not a function or method\n"))
1446498Snate@binkert.org
1456498Snate@binkert.org    def test_yacc_error4(self):
1466498Snate@binkert.org        self.assertRaises(ply.yacc.YaccError,run_import,"yacc_error4")
1476498Snate@binkert.org        result = sys.stderr.getvalue()
1486498Snate@binkert.org        self.assert_(check_expected(result,
1496498Snate@binkert.org                                    "yacc_error4.py:62: Illegal rule name 'error'. Already defined as a token\n"
1506498Snate@binkert.org                                    ))
1516498Snate@binkert.org
1526498Snate@binkert.org    def test_yacc_inf(self):
1536498Snate@binkert.org        self.assertRaises(ply.yacc.YaccError,run_import,"yacc_inf")
1546498Snate@binkert.org        result = sys.stderr.getvalue()
1556498Snate@binkert.org        self.assert_(check_expected(result,
1566498Snate@binkert.org                                    "Token 'NUMBER' defined, but not used\n"
1576498Snate@binkert.org                                    "There is 1 unused token\n"
1586498Snate@binkert.org                                    "Infinite recursion detected for symbol 'statement'\n"
1596498Snate@binkert.org                                    "Infinite recursion detected for symbol 'expression'\n"
1606498Snate@binkert.org                                    ))
1616498Snate@binkert.org    def test_yacc_literal(self):
1626498Snate@binkert.org        self.assertRaises(ply.yacc.YaccError,run_import,"yacc_literal")
1636498Snate@binkert.org        result = sys.stderr.getvalue()
1646498Snate@binkert.org        self.assert_(check_expected(result,
1656498Snate@binkert.org                                    "yacc_literal.py:36: Literal token '**' in rule 'expression' may only be a single character\n"
1666498Snate@binkert.org                                    ))
1676498Snate@binkert.org    def test_yacc_misplaced(self):
1686498Snate@binkert.org        self.assertRaises(ply.yacc.YaccError,run_import,"yacc_misplaced")
1696498Snate@binkert.org        result = sys.stderr.getvalue()
1706498Snate@binkert.org        self.assert_(check_expected(result,
1716498Snate@binkert.org                                    "yacc_misplaced.py:32: Misplaced '|'\n"
1726498Snate@binkert.org                                    ))
1732632Sstever@eecs.umich.edu
1746498Snate@binkert.org    def test_yacc_missing1(self):
1756498Snate@binkert.org        self.assertRaises(ply.yacc.YaccError,run_import,"yacc_missing1")
1766498Snate@binkert.org        result = sys.stderr.getvalue()
1776498Snate@binkert.org        self.assert_(check_expected(result,
1786498Snate@binkert.org                                    "yacc_missing1.py:24: Symbol 'location' used, but not defined as a token or a rule\n"
1796498Snate@binkert.org                                    ))
1806498Snate@binkert.org
1816498Snate@binkert.org    def test_yacc_nested(self):
1826498Snate@binkert.org        run_import("yacc_nested")
1836498Snate@binkert.org        result = sys.stdout.getvalue()
1846498Snate@binkert.org        self.assert_(check_expected(result,
1856498Snate@binkert.org                                    "A\n"
1866498Snate@binkert.org                                    "A\n"
1876498Snate@binkert.org                                    "A\n",
1886498Snate@binkert.org                                    ))
1896498Snate@binkert.org
1906498Snate@binkert.org    def test_yacc_nodoc(self):
1916498Snate@binkert.org        run_import("yacc_nodoc")
1926498Snate@binkert.org        result = sys.stderr.getvalue()
1936498Snate@binkert.org        self.assert_(check_expected(result,
1946498Snate@binkert.org                                    "yacc_nodoc.py:27: No documentation string specified in function 'p_statement_expr' (ignored)\n"
1956498Snate@binkert.org                                    "Generating LALR tables\n"
1966498Snate@binkert.org                                    ))
1976498Snate@binkert.org
1986498Snate@binkert.org    def test_yacc_noerror(self):
1996498Snate@binkert.org        run_import("yacc_noerror")
2006498Snate@binkert.org        result = sys.stderr.getvalue()
2016498Snate@binkert.org        self.assert_(check_expected(result,
2026498Snate@binkert.org                                    "no p_error() function is defined\n"
2036498Snate@binkert.org                                    "Generating LALR tables\n"
2046498Snate@binkert.org                                    ))
2056498Snate@binkert.org
2066498Snate@binkert.org    def test_yacc_nop(self):
2076498Snate@binkert.org        run_import("yacc_nop")
2086498Snate@binkert.org        result = sys.stderr.getvalue()
2096498Snate@binkert.org        self.assert_(check_expected(result,
2106498Snate@binkert.org                                    "yacc_nop.py:27: Possible grammar rule 'statement_expr' defined without p_ prefix\n"
2116498Snate@binkert.org                                    "Generating LALR tables\n"
2126498Snate@binkert.org                                    ))
2136498Snate@binkert.org
2146498Snate@binkert.org    def test_yacc_notfunc(self):
2156498Snate@binkert.org        run_import("yacc_notfunc")
2166498Snate@binkert.org        result = sys.stderr.getvalue()
2176498Snate@binkert.org        self.assert_(check_expected(result,
2186498Snate@binkert.org                                    "'p_statement_assign' not defined as a function\n"
2196498Snate@binkert.org                                    "Token 'EQUALS' defined, but not used\n"
2206498Snate@binkert.org                                    "There is 1 unused token\n"
2216498Snate@binkert.org                                    "Generating LALR tables\n"
2226498Snate@binkert.org                                    ))
2236498Snate@binkert.org    def test_yacc_notok(self):
2246498Snate@binkert.org        try:
2256498Snate@binkert.org            run_import("yacc_notok")
2266498Snate@binkert.org        except ply.yacc.YaccError:
2276498Snate@binkert.org            result = sys.stderr.getvalue()
2286498Snate@binkert.org            self.assert_(check_expected(result,
2296498Snate@binkert.org                                        "No token list is defined\n"))
2306498Snate@binkert.org
2316498Snate@binkert.org    def test_yacc_rr(self):
2326498Snate@binkert.org        run_import("yacc_rr")
2336498Snate@binkert.org        result = sys.stderr.getvalue()
2346498Snate@binkert.org        self.assert_(check_expected(result,
2356498Snate@binkert.org                                    "Generating LALR tables\n"
2366498Snate@binkert.org                                    "1 reduce/reduce conflict\n"
2376498Snate@binkert.org                                    "reduce/reduce conflict in state 15 resolved using rule (statement -> NAME EQUALS NUMBER)\n"
2386498Snate@binkert.org                                    "rejected rule (expression -> NUMBER) in state 15\n"
2396498Snate@binkert.org
2406498Snate@binkert.org                                    ))
2416498Snate@binkert.org
2426498Snate@binkert.org    def test_yacc_rr_unused(self):
2436498Snate@binkert.org        run_import("yacc_rr_unused")
2446498Snate@binkert.org        result = sys.stderr.getvalue()
2456498Snate@binkert.org        self.assert_(check_expected(result,
2466498Snate@binkert.org                                    "no p_error() function is defined\n"
2476498Snate@binkert.org                                    "Generating LALR tables\n"
2486498Snate@binkert.org                                    "3 reduce/reduce conflicts\n"
2496498Snate@binkert.org                                    "reduce/reduce conflict in state 1 resolved using rule (rule3 -> A)\n"
2506498Snate@binkert.org                                    "rejected rule (rule4 -> A) in state 1\n"
2516498Snate@binkert.org                                    "reduce/reduce conflict in state 1 resolved using rule (rule3 -> A)\n"
2526498Snate@binkert.org                                    "rejected rule (rule5 -> A) in state 1\n"
2536498Snate@binkert.org                                    "reduce/reduce conflict in state 1 resolved using rule (rule4 -> A)\n"
2546498Snate@binkert.org                                    "rejected rule (rule5 -> A) in state 1\n"
2556498Snate@binkert.org                                    "Rule (rule5 -> A) is never reduced\n"
2566498Snate@binkert.org                                    ))
2576498Snate@binkert.org
2586498Snate@binkert.org    def test_yacc_simple(self):
2596498Snate@binkert.org        run_import("yacc_simple")
2606498Snate@binkert.org        result = sys.stderr.getvalue()
2616498Snate@binkert.org        self.assert_(check_expected(result,
2626498Snate@binkert.org                                    "Generating LALR tables\n"
2636498Snate@binkert.org                                    ))
2646498Snate@binkert.org    def test_yacc_sr(self):
2656498Snate@binkert.org        run_import("yacc_sr")
2666498Snate@binkert.org        result = sys.stderr.getvalue()
2676498Snate@binkert.org        self.assert_(check_expected(result,
2686498Snate@binkert.org                                    "Generating LALR tables\n"
2696498Snate@binkert.org                                    "20 shift/reduce conflicts\n"
2706498Snate@binkert.org                                    ))
2716498Snate@binkert.org
2726498Snate@binkert.org    def test_yacc_term1(self):
2736498Snate@binkert.org        self.assertRaises(ply.yacc.YaccError,run_import,"yacc_term1")
2746498Snate@binkert.org        result = sys.stderr.getvalue()
2756498Snate@binkert.org        self.assert_(check_expected(result,
2766498Snate@binkert.org                                    "yacc_term1.py:24: Illegal rule name 'NUMBER'. Already defined as a token\n"
2776498Snate@binkert.org                                    ))
2786498Snate@binkert.org
2796498Snate@binkert.org    def test_yacc_unused(self):
2806498Snate@binkert.org        self.assertRaises(ply.yacc.YaccError,run_import,"yacc_unused")
2816498Snate@binkert.org        result = sys.stderr.getvalue()
2826498Snate@binkert.org        self.assert_(check_expected(result,
2836498Snate@binkert.org                                    "yacc_unused.py:62: Symbol 'COMMA' used, but not defined as a token or a rule\n"
2846498Snate@binkert.org                                    "Symbol 'COMMA' is unreachable\n"
2856498Snate@binkert.org                                    "Symbol 'exprlist' is unreachable\n"
2866498Snate@binkert.org                                    ))
2876498Snate@binkert.org    def test_yacc_unused_rule(self):
2886498Snate@binkert.org        run_import("yacc_unused_rule")
2896498Snate@binkert.org        result = sys.stderr.getvalue()
2906498Snate@binkert.org        self.assert_(check_expected(result,
2916498Snate@binkert.org                                    "yacc_unused_rule.py:62: Rule 'integer' defined, but not used\n"
2926498Snate@binkert.org                                    "There is 1 unused rule\n"
2936498Snate@binkert.org                                    "Symbol 'integer' is unreachable\n"
2946498Snate@binkert.org                                    "Generating LALR tables\n"
2956498Snate@binkert.org                                    ))
2966498Snate@binkert.org
2976498Snate@binkert.org    def test_yacc_uprec(self):
2986498Snate@binkert.org        self.assertRaises(ply.yacc.YaccError,run_import,"yacc_uprec")
2996498Snate@binkert.org        result = sys.stderr.getvalue()
3006498Snate@binkert.org        print repr(result)
3016498Snate@binkert.org        self.assert_(check_expected(result,
3026498Snate@binkert.org                                    "yacc_uprec.py:37: Nothing known about the precedence of 'UMINUS'\n"
3036498Snate@binkert.org                                    ))
3046498Snate@binkert.org
3056498Snate@binkert.org    def test_yacc_uprec2(self):
3066498Snate@binkert.org        self.assertRaises(ply.yacc.YaccError,run_import,"yacc_uprec2")
3076498Snate@binkert.org        result = sys.stderr.getvalue()
3086498Snate@binkert.org        self.assert_(check_expected(result,
3096498Snate@binkert.org                                    "yacc_uprec2.py:37: Syntax error. Nothing follows %prec\n"
3106498Snate@binkert.org                                    ))
3116498Snate@binkert.org
3126498Snate@binkert.org    def test_yacc_prec1(self):
3136498Snate@binkert.org        self.assertRaises(ply.yacc.YaccError,run_import,"yacc_prec1")
3146498Snate@binkert.org        result = sys.stderr.getvalue()
3156498Snate@binkert.org        self.assert_(check_expected(result,
3166498Snate@binkert.org                                    "Precedence rule 'left' defined for unknown symbol '+'\n"
3176498Snate@binkert.org                                    "Precedence rule 'left' defined for unknown symbol '*'\n"
3186498Snate@binkert.org                                    "Precedence rule 'left' defined for unknown symbol '-'\n"
3196498Snate@binkert.org                                    "Precedence rule 'left' defined for unknown symbol '/'\n"
3206498Snate@binkert.org                                    ))
3216498Snate@binkert.org
3226498Snate@binkert.org
3236498Snate@binkert.org
3246498Snate@binkert.orgunittest.main()
325