|
# generate a token grammar |
|
good_grammar = Grammar({}) |
|
|
|
|
|
# define an exception to raise when encountering some other grammar |
|
class BadGrammarException(Exception): |
|
pass |
|
|
|
|
|
# define a combinatorial parser that validates the grammar parameter |
|
class GoodGrammarParser(Parser): |
|
|
|
def __init__(self, child): |
|
self.child = child |
|
|
|
def __repr__(self): |
|
return "GoodGrammarParser({!r})".format(self.child) |
|
|
|
def __str__(self): |
|
return str(self.child) |
|
|
|
def parse(self, document, grammar): |
|
if good_grammar != grammar: |
|
raise BadGrammarException |
|
return self.child.parse(document, grammar) |
|
|
|
def generate(self, entropy, grammar): |
|
if good_grammar != grammar: |
|
raise BadGrammarException |
|
return self.child.generate(entropy, grammar) |
|
|
|
|
|
# define a composable strategy for the validation parser |
|
def good_grammar_parsers(children): |
|
return builds(GoodGrammarParser, children) |
|
|
|
|
|
# compose with combinatorial and primitive parsers |
|
# compare with the definition of `parsers` strategy |
|
def parsers_and_good_grammar_parsers(): |
|
self = deferred(lambda: parsers_and_good_grammar_parsers()) |
|
return one_of( |
|
primitive_parsers(), |
|
combinatorial_parsers(self), |
|
good_grammar_parsers(self), |
|
) |
|
|
|
|
|
# attempted filter for identifying parsers that have good grammar parsers |
|
def has_good_grammar_parser(entropy, parser): |
|
try: |
|
parser.generate(entropy, None) |
|
return False |
|
except BadGrammarException: |
|
return True |
|
|
|
|
|
class TestParsers(TestCase): |
|
|
|
@given( |
|
entropys(), |
|
parsers_and_good_grammar_parsers(), |
|
) |
|
def test_parsers_retain_grammar(self, entropy, parser): |
|
assume(has_good_grammar_parser(entropy, parser)) |
|
assume(parsers_are_good(entropy, [parser])) |
|
for _ in range(iterations): |
|
try: |
|
parser.parse(parser.generate(entropy, good_grammar), good_grammar) |
|
except GenerationException: |
|
pass |