comparison test/testpyy.py @ 284:05184b95fa16

Moved tests to seperate folder
author Windel Bouwman
date Fri, 15 Nov 2013 13:43:22 +0100
parents python/testpyy.py@37ac6c016e0f
children e84047f29c78
comparison
equal deleted inserted replaced
283:c9781c73e7e2 284:05184b95fa16
1 import unittest, pprint
2 from pyyacc import Grammar, Item, ParserGenerationException, ParserException, EPS, EOF
3 from ppci import Token
4
5 def genTokens(lst):
6 for t in lst:
7 yield Token(t, t)
8
9 class testLR(unittest.TestCase):
10 """ Test basic LR(1) parser generator constructs """
11 def testSimpleGrammar(self):
12 # 1. define a simple grammar:
13 g = Grammar(['identifier', '(', ')', '+', '*'])
14 g.add_production('input', ['expression'])
15 g.add_production('expression', ['term'])
16 g.add_production('expression', ['expression', '+', 'term'])
17 g.add_production('term', ['factor'])
18 g.add_production('term', ['term', '*', 'factor'])
19 g.add_production('factor', ['(', 'expression', ')'])
20 g.add_production('factor', ['identifier'])
21 g.start_symbol = 'input'
22 # 2. define input:
23 tokens = genTokens(['identifier', '+', 'identifier', '+', 'identifier'])
24 # 3. build parser:
25 p = g.genParser()
26 # 4. feed input:
27 p.parse(tokens)
28 def testReduceReduceConflict(self):
29 """ Check if a reduce-reduce conflict is detected """
30 # Define a grammar with an obvious reduce-reduce conflict:
31 g = Grammar(['id'])
32 g.add_production('goal', ['a'])
33 g.add_production('a', ['b'])
34 g.add_production('a', ['c'])
35 g.add_production('b', ['id'])
36 g.add_production('c', ['id'])
37 g.start_symbol = 'goal'
38 with self.assertRaises(ParserGenerationException):
39 p = g.genParser()
40 def testShiftReduceConflict(self):
41 """ Must be handled automatically by doing shift """
42 g = Grammar([EOF, 'if', 'then', 'else', 'ass'])
43 # Ambiguous grammar:
44 g.add_production('if_stmt', ['if', 'then', 'stmt'])
45 g.add_production('if_stmt', ['if', 'then', 'stmt', 'else', 'stmt'])
46 g.add_production('stmt', ['if_stmt'])
47 g.add_production('stmt', ['ass'])
48 g.start_symbol = 'stmt'
49 p = g.genParser()
50 # Ambiguous program:
51 tokens = genTokens(['if', 'then','if', 'then', 'ass', 'else', 'ass'])
52 p.parse(tokens)
53
54 def testUndefinedTerminal(self):
55 """ Test correct behavior when a terminal is undefined """
56 g = Grammar(['b'])
57 g.add_production('goal', ['a'])
58 g.add_production('a', ['b'])
59 g.add_production('a', ['c'])
60 g.start_symbol = 'goal'
61 with self.assertRaises(ParserGenerationException):
62 g.genParser()
63 def testRedefineTerminal(self):
64 """ Test correct behavior when a terminal is redefined """
65 g = Grammar([EOF, 'b', 'c'])
66 g.add_production('goal', ['a'])
67 with self.assertRaises(ParserGenerationException):
68 g.add_production('b', ['c']) # Not allowed
69 g.add_production('a', ['c'])
70 g.start_symbol = 'goal'
71 g.genParser()
72 def testEmpty(self):
73 """ Test empty token stream """
74 g = Grammar([','])
75 g.add_production('input', [','])
76 g.start_symbol = 'input'
77 p = g.genParser()
78 tokens = genTokens([])
79 with self.assertRaises(ParserException):
80 p.parse(tokens)
81
82 def testEps(self):
83 """ Test epsilon terminal """
84 g = Grammar(['a', 'b'])
85 g.add_production('input', ['optional_a', 'b'])
86 g.add_production('optional_a', ['a'])
87 g.add_production('optional_a', [])
88 g.start_symbol = 'input'
89 p = g.genParser()
90 tokens = genTokens(['b'])
91 p.parse(tokens)
92
93 def testEps2(self):
94 g = Grammar(['id', ':'])
95 g.add_production('input', ['opt_lab', 'ins', 'op1'])
96 g.add_production('input', ['ins', 'op1'])
97 g.add_production('opt_lab', ['id', ':'])
98 g.add_production('ins', ['id'])
99 g.add_production('op1', ['id'])
100 g.start_symbol = 'input'
101 p = g.genParser()
102 tokens = genTokens(['id', ':', 'id', 'id']) # i.e. "lab_0: inc rax"
103 p.parse(tokens)
104 tokens = genTokens(['id', 'id']) # i.e. "inc rax"
105 p.parse(tokens)
106
107 def test_cb(self):
108 """ Test callback of one rule and order or parameters """
109 self.cb_called = False
110 def cb(a, c, b):
111 self.cb_called = True
112 self.assertEqual(a, 'a')
113 self.assertEqual(b, 'b')
114 self.assertEqual(c, 'c')
115 g = Grammar(['a', 'b', 'c'])
116 g.add_production('goal', ['a', 'c', 'b'], cb)
117 g.start_symbol = 'goal'
118 p = g.genParser()
119 tokens = genTokens(['a', 'c', 'b'])
120 p.parse(tokens)
121 self.assertTrue(self.cb_called)
122
123
124 class testExpressionGrammar(unittest.TestCase):
125 def setUp(self):
126 g = Grammar(['EOF', 'identifier', '(', ')', '+', '*', 'num'])
127 g.add_production('input', ['expression'])
128 g.add_production('expression', ['term'])
129 g.add_production('expression', ['expression', '+', 'term'])
130 g.add_production('term', ['factor'])
131 g.add_production('term', ['term', '*', 'factor'])
132 g.add_production('factor', ['(', 'expression', ')'])
133 g.add_production('factor', ['identifier'])
134 g.add_production('factor', ['num'])
135 g.start_symbol = 'input'
136 self.g = g
137
138 def testFirstSimpleGrammar(self):
139 # 1. define a simple grammar:
140 first = self.g.calcFirstSets()
141 self.assertEqual(first['input'], {'identifier', '(', 'num'})
142 self.assertEqual(first['term'], {'identifier', '(', 'num'})
143
144 def testCanonical(self):
145 s0 = self.g.initialItemSet()
146 s, gt = self.g.genCanonicalSet(s0)
147 # Must result in 12 sets:
148 self.assertEqual(len(s), 24)
149
150 class testPG(unittest.TestCase):
151 """ Tests several parts of the parser generator """
152 def setUp(self):
153 g = Grammar(['(', ')'])
154 g.add_production('goal', ['list'])
155 g.add_production('list', ['list', 'pair'])
156 g.add_production('list', ['pair'])
157 g.add_production('pair', ['(', 'pair', ')'])
158 g.add_production('pair', ['(', ')'])
159 g.start_symbol = 'goal'
160 self.g = g
161
162 def testFirstSet(self):
163 for a in ['(', ')', EOF, 'EPS']:
164 self.assertEqual(self.g.first[a], {a})
165 for nt in ['list', 'pair', 'goal']:
166 self.assertEqual(self.g.first[nt], {'('})
167
168 def testInitItemSet(self):
169 p0, p1, p2, p3, p4 = self.g.productions
170 s0 = self.g.initialItemSet()
171 self.assertEqual(len(s0), 9) # 9 with the goal rule included!
172 self.assertIn(Item(p0, 0, EOF), s0)
173 self.assertIn(Item(p1, 0, EOF), s0)
174 self.assertIn(Item(p1, 0, '('), s0)
175 self.assertIn(Item(p2, 0, EOF), s0)
176 self.assertIn(Item(p2, 0, '('), s0)
177 self.assertIn(Item(p3, 0, EOF), s0)
178 self.assertIn(Item(p3, 0, '('), s0)
179 self.assertIn(Item(p4, 0, EOF), s0)
180 self.assertIn(Item(p4, 0, '('), s0)
181
182 def testCanonical(self):
183 s0 = self.g.initialItemSet()
184 s, gt = self.g.genCanonicalSet(s0)
185 # Must result in 12 sets:
186 self.assertEqual(len(s), 12)
187
188 def testClosure(self):
189 p0, p1, p2, p3, p4 = self.g.productions
190 s0 = set()
191 s0.add(Item(p0, 0, EOF))
192 self.assertEqual(len(s0), 1) # 1 rule
193 self.assertIn(Item(p0, 0, EOF), s0)
194
195 # Invoke closure on set:
196 s0 = self.g.closure(s0)
197 self.assertIn(Item(p0, 0, EOF), s0)
198 self.assertIn(Item(p1, 0, EOF), s0)
199 self.assertIn(Item(p1, 0, '('), s0)
200 self.assertIn(Item(p2, 0, EOF), s0)
201 self.assertIn(Item(p2, 0, '('), s0)
202 self.assertIn(Item(p3, 0, EOF), s0)
203 self.assertIn(Item(p3, 0, '('), s0)
204 self.assertIn(Item(p4, 0, EOF), s0)
205 self.assertIn(Item(p4, 0, '('), s0)
206
207 def testParser(self):
208 tokens = ['(', '(', ')', ')', '(', ')']
209 # 3. build parser:
210 p = self.g.genParser()
211 self.assertEqual(len(p.goto_table), 5)
212 self.assertEqual(len(p.action_table), 19)
213
214 # 4. feed input:
215 p.parse(genTokens(tokens))
216
217 if __name__ == '__main__':
218 unittest.main()
219
220