Mercurial > lcfOS
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/testpyy.py Fri Nov 15 13:43:22 2013 +0100 @@ -0,0 +1,220 @@ +import unittest, pprint +from pyyacc import Grammar, Item, ParserGenerationException, ParserException, EPS, EOF +from ppci import Token + +def genTokens(lst): + for t in lst: + yield Token(t, t) + +class testLR(unittest.TestCase): + """ Test basic LR(1) parser generator constructs """ + def testSimpleGrammar(self): + # 1. define a simple grammar: + g = Grammar(['identifier', '(', ')', '+', '*']) + g.add_production('input', ['expression']) + g.add_production('expression', ['term']) + g.add_production('expression', ['expression', '+', 'term']) + g.add_production('term', ['factor']) + g.add_production('term', ['term', '*', 'factor']) + g.add_production('factor', ['(', 'expression', ')']) + g.add_production('factor', ['identifier']) + g.start_symbol = 'input' + # 2. define input: + tokens = genTokens(['identifier', '+', 'identifier', '+', 'identifier']) + # 3. build parser: + p = g.genParser() + # 4. feed input: + p.parse(tokens) + def testReduceReduceConflict(self): + """ Check if a reduce-reduce conflict is detected """ + # Define a grammar with an obvious reduce-reduce conflict: + g = Grammar(['id']) + g.add_production('goal', ['a']) + g.add_production('a', ['b']) + g.add_production('a', ['c']) + g.add_production('b', ['id']) + g.add_production('c', ['id']) + g.start_symbol = 'goal' + with self.assertRaises(ParserGenerationException): + p = g.genParser() + def testShiftReduceConflict(self): + """ Must be handled automatically by doing shift """ + g = Grammar([EOF, 'if', 'then', 'else', 'ass']) + # Ambiguous grammar: + g.add_production('if_stmt', ['if', 'then', 'stmt']) + g.add_production('if_stmt', ['if', 'then', 'stmt', 'else', 'stmt']) + g.add_production('stmt', ['if_stmt']) + g.add_production('stmt', ['ass']) + g.start_symbol = 'stmt' + p = g.genParser() + # Ambiguous program: + tokens = genTokens(['if', 'then','if', 'then', 'ass', 'else', 'ass']) + p.parse(tokens) + + def testUndefinedTerminal(self): + """ Test correct behavior when a terminal is undefined """ + g = Grammar(['b']) + g.add_production('goal', ['a']) + g.add_production('a', ['b']) + g.add_production('a', ['c']) + g.start_symbol = 'goal' + with self.assertRaises(ParserGenerationException): + g.genParser() + def testRedefineTerminal(self): + """ Test correct behavior when a terminal is redefined """ + g = Grammar([EOF, 'b', 'c']) + g.add_production('goal', ['a']) + with self.assertRaises(ParserGenerationException): + g.add_production('b', ['c']) # Not allowed + g.add_production('a', ['c']) + g.start_symbol = 'goal' + g.genParser() + def testEmpty(self): + """ Test empty token stream """ + g = Grammar([',']) + g.add_production('input', [',']) + g.start_symbol = 'input' + p = g.genParser() + tokens = genTokens([]) + with self.assertRaises(ParserException): + p.parse(tokens) + + def testEps(self): + """ Test epsilon terminal """ + g = Grammar(['a', 'b']) + g.add_production('input', ['optional_a', 'b']) + g.add_production('optional_a', ['a']) + g.add_production('optional_a', []) + g.start_symbol = 'input' + p = g.genParser() + tokens = genTokens(['b']) + p.parse(tokens) + + def testEps2(self): + g = Grammar(['id', ':']) + g.add_production('input', ['opt_lab', 'ins', 'op1']) + g.add_production('input', ['ins', 'op1']) + g.add_production('opt_lab', ['id', ':']) + g.add_production('ins', ['id']) + g.add_production('op1', ['id']) + g.start_symbol = 'input' + p = g.genParser() + tokens = genTokens(['id', ':', 'id', 'id']) # i.e. "lab_0: inc rax" + p.parse(tokens) + tokens = genTokens(['id', 'id']) # i.e. "inc rax" + p.parse(tokens) + + def test_cb(self): + """ Test callback of one rule and order or parameters """ + self.cb_called = False + def cb(a, c, b): + self.cb_called = True + self.assertEqual(a, 'a') + self.assertEqual(b, 'b') + self.assertEqual(c, 'c') + g = Grammar(['a', 'b', 'c']) + g.add_production('goal', ['a', 'c', 'b'], cb) + g.start_symbol = 'goal' + p = g.genParser() + tokens = genTokens(['a', 'c', 'b']) + p.parse(tokens) + self.assertTrue(self.cb_called) + + +class testExpressionGrammar(unittest.TestCase): + def setUp(self): + g = Grammar(['EOF', 'identifier', '(', ')', '+', '*', 'num']) + g.add_production('input', ['expression']) + g.add_production('expression', ['term']) + g.add_production('expression', ['expression', '+', 'term']) + g.add_production('term', ['factor']) + g.add_production('term', ['term', '*', 'factor']) + g.add_production('factor', ['(', 'expression', ')']) + g.add_production('factor', ['identifier']) + g.add_production('factor', ['num']) + g.start_symbol = 'input' + self.g = g + + def testFirstSimpleGrammar(self): + # 1. define a simple grammar: + first = self.g.calcFirstSets() + self.assertEqual(first['input'], {'identifier', '(', 'num'}) + self.assertEqual(first['term'], {'identifier', '(', 'num'}) + + def testCanonical(self): + s0 = self.g.initialItemSet() + s, gt = self.g.genCanonicalSet(s0) + # Must result in 12 sets: + self.assertEqual(len(s), 24) + +class testPG(unittest.TestCase): + """ Tests several parts of the parser generator """ + def setUp(self): + g = Grammar(['(', ')']) + g.add_production('goal', ['list']) + g.add_production('list', ['list', 'pair']) + g.add_production('list', ['pair']) + g.add_production('pair', ['(', 'pair', ')']) + g.add_production('pair', ['(', ')']) + g.start_symbol = 'goal' + self.g = g + + def testFirstSet(self): + for a in ['(', ')', EOF, 'EPS']: + self.assertEqual(self.g.first[a], {a}) + for nt in ['list', 'pair', 'goal']: + self.assertEqual(self.g.first[nt], {'('}) + + def testInitItemSet(self): + p0, p1, p2, p3, p4 = self.g.productions + s0 = self.g.initialItemSet() + self.assertEqual(len(s0), 9) # 9 with the goal rule included! + self.assertIn(Item(p0, 0, EOF), s0) + self.assertIn(Item(p1, 0, EOF), s0) + self.assertIn(Item(p1, 0, '('), s0) + self.assertIn(Item(p2, 0, EOF), s0) + self.assertIn(Item(p2, 0, '('), s0) + self.assertIn(Item(p3, 0, EOF), s0) + self.assertIn(Item(p3, 0, '('), s0) + self.assertIn(Item(p4, 0, EOF), s0) + self.assertIn(Item(p4, 0, '('), s0) + + def testCanonical(self): + s0 = self.g.initialItemSet() + s, gt = self.g.genCanonicalSet(s0) + # Must result in 12 sets: + self.assertEqual(len(s), 12) + + def testClosure(self): + p0, p1, p2, p3, p4 = self.g.productions + s0 = set() + s0.add(Item(p0, 0, EOF)) + self.assertEqual(len(s0), 1) # 1 rule + self.assertIn(Item(p0, 0, EOF), s0) + + # Invoke closure on set: + s0 = self.g.closure(s0) + self.assertIn(Item(p0, 0, EOF), s0) + self.assertIn(Item(p1, 0, EOF), s0) + self.assertIn(Item(p1, 0, '('), s0) + self.assertIn(Item(p2, 0, EOF), s0) + self.assertIn(Item(p2, 0, '('), s0) + self.assertIn(Item(p3, 0, EOF), s0) + self.assertIn(Item(p3, 0, '('), s0) + self.assertIn(Item(p4, 0, EOF), s0) + self.assertIn(Item(p4, 0, '('), s0) + + def testParser(self): + tokens = ['(', '(', ')', ')', '(', ')'] + # 3. build parser: + p = self.g.genParser() + self.assertEqual(len(p.goto_table), 5) + self.assertEqual(len(p.action_table), 19) + + # 4. feed input: + p.parse(genTokens(tokens)) + +if __name__ == '__main__': + unittest.main() + +