| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- # -*- coding: utf-8 -*-
- from unittest import SkipTest, TestCase
- from parsimonious import Grammar, NodeVisitor, VisitationError, rule
- from parsimonious.expressions import Literal
- from parsimonious.nodes import Node
- class HtmlFormatter(NodeVisitor):
- """Visitor that turns a parse tree into HTML fragments"""
- grammar = Grammar("""bold_open = '(('""") # just partial
- def visit_bold_open(self, node, visited_children):
- return '<b>'
- def visit_bold_close(self, node, visited_children):
- return '</b>'
- def visit_text(self, node, visited_children):
- """Return the text verbatim."""
- return node.text
- def visit_bold_text(self, node, visited_children):
- return ''.join(visited_children)
- class ExplosiveFormatter(NodeVisitor):
- """Visitor which raises exceptions"""
- def visit_boom(self, node, visited_children):
- raise ValueError
- class SimpleTests(TestCase):
- def test_visitor(self):
- """Assert a tree gets visited correctly."""
- grammar = Grammar(r'''
- bold_text = bold_open text bold_close
- text = ~'[a-zA-Z 0-9]*'
- bold_open = '(('
- bold_close = '))'
- ''')
- text = '((o hai))'
- tree = Node(grammar['bold_text'], text, 0, 9,
- [Node(grammar['bold_open'], text, 0, 2),
- Node(grammar['text'], text, 2, 7),
- Node(grammar['bold_close'], text, 7, 9)])
- self.assertEqual(grammar.parse(text), tree)
- result = HtmlFormatter().visit(tree)
- self.assertEqual(result, '<b>o hai</b>')
- def test_visitation_exception(self):
- self.assertRaises(VisitationError,
- ExplosiveFormatter().visit,
- Node(Literal(''), '', 0, 0))
- def test_str(self):
- """Test str and unicode of ``Node``."""
- n = Node(Literal('something', name='text'), 'o hai', 0, 5)
- good = '<Node called "text" matching "o hai">'
- self.assertEqual(str(n), good)
- def test_repr(self):
- """Test repr of ``Node``."""
- s = 'hai ö'
- boogie = 'böogie'
- n = Node(Literal(boogie), s, 0, 3, children=[
- Node(Literal(' '), s, 3, 4), Node(Literal('ö'), s, 4, 5)])
- self.assertEqual(repr(n),
- str("""s = {hai_o}\nNode({boogie}, s, 0, 3, children=[Node({space}, s, 3, 4), Node({o}, s, 4, 5)])""").format(
- hai_o=repr(s),
- boogie=repr(Literal(boogie)),
- space=repr(Literal(" ")),
- o=repr(Literal("ö")),
- )
- )
- def test_parse_shortcut(self):
- """Exercise the simple case in which the visitor takes care of parsing."""
- self.assertEqual(HtmlFormatter().parse('(('), '<b>')
- def test_match_shortcut(self):
- """Exercise the simple case in which the visitor takes care of matching."""
- self.assertEqual(HtmlFormatter().match('((other things'), '<b>')
- class CoupledFormatter(NodeVisitor):
- @rule('bold_open text bold_close')
- def visit_bold_text(self, node, visited_children):
- return ''.join(visited_children)
- @rule('"(("')
- def visit_bold_open(self, node, visited_children):
- return '<b>'
- @rule('"))"')
- def visit_bold_close(self, node, visited_children):
- return '</b>'
- @rule('~"[a-zA-Z 0-9]*"')
- def visit_text(self, node, visited_children):
- """Return the text verbatim."""
- return node.text
- class DecoratorTests(TestCase):
- def test_rule_decorator(self):
- """Make sure the @rule decorator works."""
- self.assertEqual(CoupledFormatter().parse('((hi))'), '<b>hi</b>')
- def test_rule_decorator_subclassing(self):
- """Make sure we can subclass and override visitor methods without blowing
- away the rules attached to them."""
- class OverridingFormatter(CoupledFormatter):
- def visit_text(self, node, visited_children):
- """Return the text capitalized."""
- return node.text.upper()
- @rule('"not used"')
- def visit_useless(self, node, visited_children):
- """Get in the way. Tempt the metaclass to pave over the
- superclass's grammar with a new one."""
- raise SkipTest("I haven't got around to making this work yet.")
- self.assertEqual(OverridingFormatter().parse('((hi))'), '<b>HI</b>')
- class PrimalScream(Exception):
- pass
- class SpecialCasesTests(TestCase):
- def test_unwrapped_exceptions(self):
- class Screamer(NodeVisitor):
- grammar = Grammar("""greeting = 'howdy'""")
- unwrapped_exceptions = (PrimalScream,)
- def visit_greeting(self, thing, visited_children):
- raise PrimalScream('This should percolate up!')
- self.assertRaises(PrimalScream, Screamer().parse, 'howdy')
- def test_node_inequality(self):
- node = Node(Literal('12345'), 'o hai', 0, 5)
- self.assertTrue(node != 5)
- self.assertTrue(node != None)
- self.assertTrue(node != Node(Literal('23456'), 'o hai', 0, 5))
- self.assertTrue(not (node != Node(Literal('12345'), 'o hai', 0, 5)))
- def test_generic_visit_NotImplementedError_unnamed_node(self):
- """
- Test that generic_visit provides informative error messages
- when visitors are not defined.
- Regression test for https://github.com/erikrose/parsimonious/issues/110
- """
- class MyVisitor(NodeVisitor):
- grammar = Grammar(r'''
- bar = "b" "a" "r"
- ''')
- unwrapped_exceptions = (NotImplementedError, )
- with self.assertRaises(NotImplementedError) as e:
- MyVisitor().parse('bar')
- self.assertIn("No visitor method was defined for this expression: 'b'", str(e.exception))
- def test_generic_visit_NotImplementedError_named_node(self):
- """
- Test that generic_visit provides informative error messages
- when visitors are not defined.
- """
- class MyVisitor(NodeVisitor):
- grammar = Grammar(r'''
- bar = myrule myrule myrule
- myrule = ~"[bar]"
- ''')
- unwrapped_exceptions = (NotImplementedError, )
- with self.assertRaises(NotImplementedError) as e:
- MyVisitor().parse('bar')
- self.assertIn("No visitor method was defined for this expression: myrule = ~'[bar]'", str(e.exception))
|