def test_given_non_lr1_when_collision_then_error(self) -> None: from cmaj.parser.grammar import augment from cmaj.parser.graph import graph_for grammar = Grammar(Rule('X', ['0', 'X', '0']), Rule('X', ['1', 'X', '1']), Rule('X', ['0']), Rule('X', ['1'])) grammar = augment(grammar, 'X') graph = graph_for(grammar) self.assertRaises(ConflictError, table_for, grammar, graph)
def test_given_grammar_with_terminal_and_valid_token_then_ast( self) -> None: grammar = augment(Grammar(Rule('A', ['a'])), 'A') graph = graph_for(grammar) table = table_for(grammar, graph) actual_root = parse(tokens('a'), grammar, table) self._assert_correct_tree(('A', ('a', )), actual_root) self.assertRaises(ParserError, parse, tokens('aa'), grammar, table)
def test_given_count_grammar_when_00001111_then_ast(self) -> None: grammar = augment( Grammar(Rule('X', ['0', 'X', '1']), Rule('X', ['0', '1'])), 'X') graph = graph_for(grammar) table = table_for(grammar, graph) actual_root = parse(tokens('00001111'), grammar, table) expected_tree = ('X', '0', ('X', '0', ('X', '0', ('X', '0', '1'), '1'), '1'), '1') self._assert_correct_tree(expected_tree, actual_root) self.assertRaises(ParserError, parse, tokens('001'), grammar, table)
def test_given_slr_grammar_then_correct_table(self) -> None: from cmaj.parser.grammar import augment from cmaj.parser.graph import graph_for grammar = augment(Grammar(Rule('S', ['X', 'X']), Rule('X', ['a', 'X']), Rule('X', ['b'])), 'S') graph = graph_for(grammar) table = table_for(grammar, graph) s = [row_of(graph, 0, 0, Grammar.AUGMENTED_EOF), row_of(graph, 3, 1, Grammar.AUGMENTED_EOF), row_of(graph, 0, 1, Grammar.AUGMENTED_EOF), row_of(graph, 1, 1, 'a'), row_of(graph, 2, 1, 'a'), row_of(graph, 0, 2, Grammar.AUGMENTED_EOF), row_of(graph, 1, 1, Grammar.AUGMENTED_EOF), row_of(graph, 2, 1, Grammar.AUGMENTED_EOF), row_of(graph, 1, 2, 'a'), row_of(graph, 1, 2, Grammar.AUGMENTED_EOF)] count = sum(1 for row in range(10) for column in ['S', 'X', 'a', 'b', Grammar.AUGMENTED_EOF] if table.action(row, column) is None) self.assertEqual(29, count) self.assertEqual(Action.shift(s[3]), table.action(s[0], 'a')) self.assertEqual(Action.shift(s[4]), table.action(s[0], 'b')) self.assertEqual(Action.goto(s[1]), table.action(s[0], 'S')) self.assertEqual(Action.goto(s[2]), table.action(s[0], 'X')) self.assertEqual(Action.accept(3), table.action(s[1], Grammar.AUGMENTED_EOF)) self.assertEqual(Action.shift(s[6]), table.action(s[2], 'a')) self.assertEqual(Action.shift(s[7]), table.action(s[2], 'b')) self.assertEqual(Action.goto(s[5]), table.action(s[2], 'X')) self.assertEqual(Action.shift(s[3]), table.action(s[3], 'a')) self.assertEqual(Action.shift(s[4]), table.action(s[3], 'b')) self.assertEqual(Action.goto(s[8]), table.action(s[3], 'X')) self.assertEqual(Action.reduce(2), table.action(s[4], 'a')) self.assertEqual(Action.reduce(2), table.action(s[4], 'b')) self.assertEqual(Action.reduce(0), table.action(s[5], Grammar.AUGMENTED_EOF)) self.assertEqual(Action.shift(s[6]), table.action(s[6], 'a')) self.assertEqual(Action.shift(s[7]), table.action(s[6], 'b')) self.assertEqual(Action.goto(s[9]), table.action(s[6], 'X')) self.assertEqual(Action.reduce(2), table.action(s[7], Grammar.AUGMENTED_EOF)) self.assertEqual(Action.reduce(1), table.action(s[8], 'a')) self.assertEqual(Action.reduce(1), table.action(s[8], 'b')) self.assertEqual(Action.reduce(1), table.action(s[9], Grammar.AUGMENTED_EOF))
def meta_grammar() -> Grammar: from cmaj.parser.grammar import Rule, augment grammar = Grammar(Rule('GRAMMAR', ['LINE', 'GRAMMAR']), Rule('GRAMMAR', ['LINE']), Rule('LINE', ['DEFINITION', 'eol']), Rule('LINE', ['comment', 'eol']), Rule('LINE', ['eol']), Rule('DEFINITION', ['identifier', '=', 'OPTION']), Rule('OPTION', ['SEQUENCE', '|', 'OPTION']), Rule('OPTION', ['SEQUENCE']), Rule('SEQUENCE', ['ANCHOR', 'SEQUENCE']), Rule('SEQUENCE', ['ANCHOR']), Rule('ANCHOR', ['string']), Rule('ANCHOR', ['identifier'])) return augment(grammar, 'GRAMMAR')
def test_given_grammar_and_graph_then_table_with_num_closures_rows_and_num_symbols_columns(self) -> None: from cmaj.parser.grammar import augment from cmaj.testing.closure import closure grammar = augment(Grammar(Rule('A', ['a']), Rule('B', ['b'])), 'A') graph = ClosureGraph() c1 = closure((0, 0, '$')) c2 = closure((1, 0, '$')) graph.add_edge(c1, 'a', c2) graph.add_edge(c2, 'b', c1) table = table_for(grammar, graph) self.assertEqual(2, table.num_rows) self.assertEqual(5, table.num_columns)
def test_given_arithmetic_grammar_when_1add1add1mul1add1_then_ast( self) -> None: grammar = augment( Grammar(Rule('ADD', ['ADD', '+', 'MUL']), Rule('ADD', ['MUL']), Rule('MUL', ['MUL', '*', '1']), Rule('MUL', ['1'])), 'ADD') graph = graph_for(grammar) table = table_for(grammar, graph) actual_root = parse(tokens('1+1+1*1+1'), grammar, table) mul_one = ('MUL', '1') add_one = ('ADD', mul_one) expected_tree = ('ADD', ('ADD', ('ADD', add_one, '+', mul_one), '+', ('MUL', mul_one, '*', '1')), '+', mul_one) self._assert_correct_tree(expected_tree, actual_root) self.assertRaises(ParserError, parse, tokens('11+1'), grammar, table)
def _given_grammar_then_correct_graph(self, grammar: Grammar, start: str, expected_closures: List[Closure], expected_edges: List[Tuple[int, str, int]]) -> None: from cmaj.parser.grammar import augment actual_graph = graph_for(augment(grammar, start)) self.assertEqual(len(expected_closures), actual_graph.num_closures) self.assertEqual(len(expected_edges), actual_graph.num_edges) self.assertEqual(0, actual_graph.index(expected_closures[0])) # Initial state contains start symbol. for c in expected_closures: self.assertIn(c, actual_graph.closures) for i, symbol, j in expected_edges: source = actual_graph.index(expected_closures[i]) target = actual_graph.index(expected_closures[j]) self.assertEqual(target, actual_graph.successor(source, symbol))
def test_given_augmented_grammar_then_augmented_rule_is_last(self) -> None: from cmaj.parser.grammar import augment grammar = augment(Grammar(Rule('S', ['s'])), 'S') self.assertEqual(Rule(grammar.AUGMENTED_START, ['S']), grammar.rule_at(-1))