def test_conflict_first_follow(): """ Valid input example but produces a first/follow conflict. """ TestParser._run(**{ 'name': 'First/Follow Conflict', 'productions': { '<S>': [['<A>', 'a', 'b']], '<A>': [['a'], []] }, 'start': '<S>', 'terminals': set(['a', 'b']), 'nonterminals': set(['<S>', '<A>']), 'first': { 'a': set(['a']), 'b': set(['b']), '<S>': set(['a']), '<A>': set(['a', ContextFreeGrammar.epsilon()]) }, 'follow': { '<S>': set([ContextFreeGrammar.end_of_input()]), '<A>': set(['a']) }, 'rules': [ ('<S>', ['<A>', 'a', 'b']), ('<A>', ['a']), ('<A>', []) ], 'table': [ [' ', 'a', ContextFreeGrammar.end_of_input(), 'b'], ['<S>', set([0]), set([]), set([])], ['<A>', set([1, 2]), set([]), set([])] ] })
def _run(**kwargs): """ The 'main' for testing which creates the required object and compares the results are what was expected, failing appropriately if they are not. """ context_free_grammar = ContextFreeGrammar(kwargs['name'], kwargs['productions'], kwargs['start']) assert context_free_grammar.name == kwargs['name'], \ 'Invalid name produced' assert context_free_grammar.start == kwargs['start'], \ 'Invalid start production produced' assert context_free_grammar.terminals == kwargs['terminals'], \ 'Invalid terminal set produced' assert context_free_grammar.nonterminals == kwargs['nonterminals'], \ 'Invalid nonterminal set produced' TestParser._compare_first_sets(kwargs['first'], context_free_grammar.first) TestParser._compare_follow_sets(kwargs['follow'], context_free_grammar.follow) mapping = TestParser._compare_rules(kwargs['rules'], context_free_grammar.rules) TestParser._compare_tables(kwargs['table'], context_free_grammar.table, mapping)
def test_translate_not_implemented(): """ Ensure a NotImplementedError is raised if generation to source language is attempted with the base Generator. """ generator = Generator() generator.scanner = RegularGrammar('test', {'foo': ['b', 'a', 'r']}) generator.parser = ContextFreeGrammar('test', {'S': [['a']]}, 'S') generator.generate()
def test_parser_valid(): """ Ensure overwriting the paser property works as expected when given proper input as a ContextFreeGrammar. """ parser = ContextFreeGrammar('test', {'S': [['a']]}, 'S') generator = Generator() generator.parser = parser assert generator.parser is parser, 'Invalid parser set/retrieved'
def test_overrides_output_python(): """ Make sure the 'Python' generator properly overrides the output method. """ module = __import__('spag.generators.python', fromlist=['Python']) generator = getattr(module, 'Python')() assert generator, 'constructor failed' generator.scanner = RegularGrammar('test', {'foo': 'bar'}) generator.parser = ContextFreeGrammar('test', {'S': [['a']]}, 'S') assert generator.generate(), 'no result returned'
def test_overrides_output(language): """ Make sure the generators properly override the output method. """ module = __import__('spag.generators.' + language.lower(), fromlist=[language.capitalize()]) generator = getattr(module, language.capitalize())() assert generator, 'constructor failed' generator.scanner = RegularGrammar('test', {'foo': ['b', 'a', 'r']}) generator.parser = ContextFreeGrammar('test', {'S': [['a']]}, 'S') assert generator.generate(), 'no result returned'
def test_translate_return_empty(): """ Ensure a ValueError is raised if a child Generator returns empty data. """ class _EmptyFiles(Generator): def _translate(self): return dict() generator = _EmptyFiles() generator.scanner = RegularGrammar('test', {'foo': ['b', 'a', 'r']}) generator.parser = ContextFreeGrammar('test', {'S': [['a']]}, 'S') generator.generate()
def test_translate_return_invalid(): """ Ensure a TypeError is raised if a child Generator returns invalid data. """ class _InvalidFiles(Generator): def _translate(self): return list() generator = _InvalidFiles() generator.scanner = RegularGrammar('test', {'foo': ['b', 'a', 'r']}) generator.parser = ContextFreeGrammar('test', {'S': [['a']]}, 'S') generator.generate()
def test_translate_requirements(): """ Ensure correctly overwriting the abstract method in the child Generator works as expected. """ class _OutputRequirements(Generator): def _translate(self): return {self.filename + '.txt': 'hukarz'} generator = _OutputRequirements() generator.scanner = RegularGrammar('test', {'foo': ['b', 'a', 'r']}) generator.parser = ContextFreeGrammar('test', {'S': [['a']]}, 'S') generator.generate()
def test_translate_content_empty(): """ Ensure a ValueError is raised if a child Generator returns empty file contents. """ class _EmptyContent(Generator): def _translate(self): return {self.filename + '.txt': ''} generator = _EmptyContent() generator.scanner = RegularGrammar('test', {'foo': ['b', 'a', 'r']}) generator.parser = ContextFreeGrammar('test', {'S': [['a']]}, 'S') generator.generate()
def test_translate_content_invalid(): """ Ensure a TypeError is raised if a child Generator returns invalid file contents. """ class _InvalidContent(Generator): def _translate(self): return {self.filename + '.txt': None} generator = _InvalidContent() generator.scanner = RegularGrammar('test', {'foo': ['b', 'a', 'r']}) generator.parser = ContextFreeGrammar('test', {'S': [['a']]}, 'S') generator.generate()
def test_translate_filename_empty(): """ Ensure a ValueError is raised if a child Generator returns an empty filename. """ class _EmptyFilename(Generator): def _translate(self): return {'': 'invalid'} generator = _EmptyFilename() generator.scanner = RegularGrammar('test', {'foo': ['b', 'a', 'r']}) generator.parser = ContextFreeGrammar('test', {'S': [['a']]}, 'S') generator.generate()
def test_translate_filename_invalid(): """ Ensure a TypeError is raised if a child Generator returns an invalid filename. """ class _InvalidFilename(Generator): def _translate(self): return {None: 'invalid'} generator = _InvalidFilename() generator.scanner = RegularGrammar('test', {'foo': ['b', 'a', 'r']}) generator.parser = ContextFreeGrammar('test', {'S': [['a']]}, 'S') generator.generate()
def test_epsilon_absent(): """ Ensure the creation of a simple grammar goes as expected. """ TestParser._run(**{ 'name': 'No Epsilon', 'productions': { '<S>': [['<A>', 'a', '<A>', 'b'], ['<B>', 'b', '<B>', 'a']], '<A>': [[]], '<B>': [[]] }, 'start': '<S>', 'terminals': set(['a', 'b']), 'nonterminals': set(['<S>', '<A>', '<B>']), 'first': { 'a': set(['a']), 'b': set(['b']), '<S>': set(['a', 'b']), '<A>': set([ContextFreeGrammar.epsilon()]), '<B>': set([ContextFreeGrammar.epsilon()]) }, 'follow': { '<S>': set([ContextFreeGrammar.end_of_input()]), '<A>': set(['b', 'a']), '<B>': set(['a', 'b']) }, 'rules': [ ('<S>', ['<A>', 'a', '<A>', 'b']), ('<S>', ['<B>', 'b', '<B>', 'a']), ('<A>', []), ('<B>', []) ], 'table': [ [' ', ContextFreeGrammar.end_of_input(), 'a', 'b'], ['<S>', set([]), set([0]), set([1])], ['<A>', set([]), set([2]), set([2])], ['<B>', set([]), set([3]), set([3])] ] })
def test_translate_not_overridden(): """ Ensure a NotImplementedError is raised if a child Generator does not override the private _translate(self, filename) method. """ # pylint: disable=abstract-method class _GenerateNothing(Generator): pass # pylint: enable=abstract-method generator = _GenerateNothing() generator.scanner = RegularGrammar('test', {'foo': 'bar'}) generator.parser = ContextFreeGrammar('test', {'S': [['a']]}, 'S') generator.generate()
class TestGenerator: """ A test suite for testing the base Generator object. """ @staticmethod def test_constructor(): """ Ensure successful creation of a Generator object. """ generator = Generator() assert generator is not None, 'No Generator produced' @staticmethod @pytest.mark.parametrize('scanner', [ None, RegularGrammar('test', {'foo': ['b', 'a', 'r']}), pytest.param('invalid_scanner', marks=pytest.mark.xfail( reason='Scanner not of type RegularGrammar or None.', raises=TypeError, )), ]) def test_scanner(scanner): """ Ensure the Generator object's scanner property behaves as expected. """ generator = Generator() generator.scanner = scanner assert generator.scanner is scanner, 'Invalid scanner set/retrieved' @staticmethod def test_scanner_default(): """ Ensure default scanner retrieval works as expected upon successful creation of a Generator object. """ generator = Generator() assert generator.scanner is None, 'Invalid scanner default retrieved' @staticmethod @pytest.mark.parametrize('parser', [ None, ContextFreeGrammar('test', {'S': [['a']]}, 'S'), pytest.param( 'invalid_parser', marks=pytest.mark.xfail( reason='Parser not of type ContextFreeGrammar or None.', raises=TypeError, )), ]) def test_parser(parser): """ Ensure the Generator object's parser property behaves as expected. """ generator = Generator() generator.parser = parser assert generator.parser is parser, 'Invalid parser set/retrieved' @staticmethod def test_parser_default(): """ Ensure default parser retrieval works as expected upon successful creation of a Generator object. """ generator = Generator() assert generator.parser is None, 'Invalid parser default retrieved' @staticmethod @pytest.mark.parametrize('filename', [ 'foobar', pytest.param(None, marks=pytest.mark.xfail( reason='Filename not of type string.', raises=TypeError, )), pytest.param('', marks=pytest.mark.xfail( reason='Filename must be a non empty string.', raises=ValueError, )), ]) def test_filename(filename): """ Ensure the Generator object's filename property behaves as expected. """ generator = Generator() generator.filename = filename assert generator.filename == filename, 'Invalid filename set/retrieved' @staticmethod def test_filename_default(): """ Ensure default filename retrieval works as expected upon successful creation of a Generator object. """ generator = Generator() assert generator.filename == 'out', 'Invalid default filename retrieved' @staticmethod @pytest.mark.parametrize('encoding', [ 'table', 'direct', pytest.param(None, marks=pytest.mark.xfail( reason='Encoding not of type string.', raises=TypeError, )), pytest.param('', marks=pytest.mark.xfail( reason='Encoding must be a non empty string.', raises=ValueError, )), pytest.param('foo', marks=pytest.mark.xfail( reason='Encoding value not recognized.', raises=ValueError, )), ]) def test_encoding(encoding): """ Ensure the Generator object's encoding property behaves as expected. """ generator = Generator() generator.encoding = encoding assert generator.encoding == encoding, 'Invalid encoding set/retrieved' @staticmethod def test_encoding_default(): """ Ensure default encoding retrieval works as expected upon successful creation of a Generator object. """ generator = Generator() assert generator.encoding == 'direct', 'Invalid default encoding retrieved' @staticmethod @pytest.mark.parametrize('match', [ 'longest', 'shortest', pytest.param(None, marks=pytest.mark.xfail( reason='Match not of type string.', raises=TypeError, )), pytest.param('', marks=pytest.mark.xfail( reason='Match must be a non empty string.', raises=ValueError, )), pytest.param('foo', marks=pytest.mark.xfail( reason='Match value not recognized.', raises=ValueError, )), ]) def test_match(match): """ Ensure the Generator object's match property behaves as expected. """ generator = Generator() generator.match = match assert generator.match == match, 'Invalid matching-strategy set/retrieval' @staticmethod def test_matching_default(): """ Ensure default matching retrieval works as expected upon successful creation of a Generator object. """ generator = Generator() assert generator.match == 'longest', 'Invalid default match retrieved' @staticmethod @pytest.mark.xfail( reason='Scanner or parser required for generation.', raises=ValueError, ) def test_output_no_scanner_parser(): """ Ensure a ValueError is raised if generation to source language is attempted without a set scanner and parser. """ generator = Generator() generator.generate() @staticmethod @pytest.mark.xfail( reason='Output not handled by base Generator.', raises=NotImplementedError, ) def test_translate_not_implemented(): """ Ensure a NotImplementedError is raised if generation to source language is attempted with the base Generator. """ generator = Generator() generator.scanner = RegularGrammar('test', {'foo': ['b', 'a', 'r']}) generator.parser = ContextFreeGrammar('test', {'S': [['a']]}, 'S') generator.generate() @staticmethod @pytest.mark.xfail( reason='Output not overridden by child Generator.', raises=NotImplementedError, ) def test_translate_not_overridden(): """ Ensure a NotImplementedError is raised if a child Generator does not override the private _translate(self, filename) method. """ # pylint: disable=abstract-method class _GenerateNothing(Generator): pass # pylint: enable=abstract-method generator = _GenerateNothing() generator.scanner = RegularGrammar('test', {'foo': ['b', 'a', 'r']}) generator.parser = ContextFreeGrammar('test', {'S': [['a']]}, 'S') generator.generate() @staticmethod @pytest.mark.xfail( reason='Invalid output type of child Generator.', raises=TypeError, ) def test_translate_return_invalid(): """ Ensure a TypeError is raised if a child Generator returns invalid data. """ class _InvalidFiles(Generator): def _translate(self): return list() generator = _InvalidFiles() generator.scanner = RegularGrammar('test', {'foo': ['b', 'a', 'r']}) generator.parser = ContextFreeGrammar('test', {'S': [['a']]}, 'S') generator.generate() @staticmethod @pytest.mark.xfail( reason='Empty output of child Generator.', raises=ValueError, ) def test_translate_return_empty(): """ Ensure a ValueError is raised if a child Generator returns empty data. """ class _EmptyFiles(Generator): def _translate(self): return dict() generator = _EmptyFiles() generator.scanner = RegularGrammar('test', {'foo': ['b', 'a', 'r']}) generator.parser = ContextFreeGrammar('test', {'S': [['a']]}, 'S') generator.generate() @staticmethod @pytest.mark.xfail( reason='Invalid filename output of child Generator.', raises=TypeError, ) def test_translate_filename_invalid(): """ Ensure a TypeError is raised if a child Generator returns an invalid filename. """ class _InvalidFilename(Generator): def _translate(self): return {None: 'invalid'} generator = _InvalidFilename() generator.scanner = RegularGrammar('test', {'foo': ['b', 'a', 'r']}) generator.parser = ContextFreeGrammar('test', {'S': [['a']]}, 'S') generator.generate() @staticmethod @pytest.mark.xfail( reason='Empty filename output of child Generator.', raises=ValueError, ) def test_translate_filename_empty(): """ Ensure a ValueError is raised if a child Generator returns an empty filename. """ class _EmptyFilename(Generator): def _translate(self): return {'': 'invalid'} generator = _EmptyFilename() generator.scanner = RegularGrammar('test', {'foo': ['b', 'a', 'r']}) generator.parser = ContextFreeGrammar('test', {'S': [['a']]}, 'S') generator.generate() @staticmethod @pytest.mark.xfail( reason='Invalid file contents output of child Generator.', raises=TypeError, ) def test_translate_content_invalid(): """ Ensure a TypeError is raised if a child Generator returns invalid file contents. """ class _InvalidContent(Generator): def _translate(self): return {self.filename + '.txt': None} generator = _InvalidContent() generator.scanner = RegularGrammar('test', {'foo': ['b', 'a', 'r']}) generator.parser = ContextFreeGrammar('test', {'S': [['a']]}, 'S') generator.generate() @staticmethod @pytest.mark.xfail( reason='Empty file contents output of child Generator.', raises=ValueError, ) def test_translate_content_empty(): """ Ensure a ValueError is raised if a child Generator returns empty file contents. """ class _EmptyContent(Generator): def _translate(self): return {self.filename + '.txt': ''} generator = _EmptyContent() generator.scanner = RegularGrammar('test', {'foo': ['b', 'a', 'r']}) generator.parser = ContextFreeGrammar('test', {'S': [['a']]}, 'S') generator.generate() @staticmethod def test_translate_requirements(): """ Ensure correctly overwriting the abstract method in the child Generator works as expected. """ class _OutputRequirements(Generator): def _translate(self): return {self.filename + '.txt': 'hukarz'} generator = _OutputRequirements() generator.scanner = RegularGrammar('test', {'foo': ['b', 'a', 'r']}) generator.parser = ContextFreeGrammar('test', {'S': [['a']]}, 'S') generator.generate()
def test_conflict_left_recursion(): """ Valid input example but produces some conflicts due to the use of left recursion. """ TestParser._run(**{ 'name': 'Left Recursion', 'productions': { '<E>': [['<E>', '<A>', '<T>'], ['<T>']], '<A>': [['+'], ['-']], '<T>': [['<T>', '<M>', '<F>'], ['<F>']], '<M>': [['*']], '<F>': [['(', '<E>', ')'], ['id']] }, 'start': '<E>', 'terminals': set(['(', ')', '+', '*', '-', 'id']), 'nonterminals': set(['<E>', '<A>', '<T>', '<M>', '<F>']), 'first': { '(': set(['(']), ')': set([')']), '+': set(['+']), '-': set(['-']), '*': set(['*']), 'id': set(['id']), '<E>': set(['(', 'id']), '<A>': set(['+', '-']), '<T>': set(['(', 'id']), '<M>': set(['*']), '<F>': set(['(', 'id']) }, 'follow': { '<E>': set([ContextFreeGrammar.end_of_input(), '+', '-', ')']), '<A>': set(['(', 'id']), '<T>': set([ContextFreeGrammar.end_of_input(), '+', '-', '*', ')']), '<M>': set(['(', 'id']), '<F>': set([ContextFreeGrammar.end_of_input(), '+', '-', '*', ')']) }, 'rules': [ ('<E>', ['<E>', '<A>', '<T>']), ('<E>', ['<T>']), ('<A>', ['+']), ('<A>', ['-']), ('<T>', ['<T>', '<M>', '<F>']), ('<T>', ['<F>']), ('<M>', ['*']), ('<F>', ['(', '<E>', ')']), ('<F>', ['id']) ], 'table': [ [' ', ContextFreeGrammar.end_of_input(), 'id', ')', '(', '+', '*', '-'], ['<E>', set([]), set([0, 1]), set([]), set([0, 1]), set([]), set([]), set([])], ['<A>', set([]), set([]), set([]), set([]), set([2]), set([]), set([3])], ['<M>', set([]), set([]), set([]), set([]), set([]), set([6]), set([])], ['<T>', set([]), set([4, 5]), set([]), set([4, 5]), set([]), set([]), set([])], ['<F>', set([]), set([8]), set([]), set([7]), set([]), set([]), set([])] ] })
def test_lisp(): """ Ensure the creation of the Lisp grammar goes as expected. """ TestParser._run(**{ 'name': 'Lisp', 'productions': { '<expression>': [['<atom>'], ['<pair>']], '<pair>': [['(', '<expression>', '.', '<expression>', ')']], '<atom>': [ ['symbol'], ['character'], ['string'], ['boolean'], ['int'], ['float'], ['nil'] ] }, 'start': '<expression>', 'terminals': set(['(', '.', ')', 'symbol', 'character', 'string', 'boolean', 'int', 'float', 'nil']), 'nonterminals': set(['<atom>', '<pair>', '<expression>']), 'first': { '(': set(['(']), '.': set(['.']), ')': set([')']), 'symbol': set(['symbol']), 'character': set(['character']), 'string': set(['string']), 'boolean': set(['boolean']), 'int': set(['int']), 'float': set(['float']), 'nil': set(['nil']), '<atom>': set(['boolean', 'character', 'float', 'int', 'nil', 'string', 'symbol']), '<pair>': set(['(']), '<expression>': set(['(', 'boolean', 'character', 'float', 'int', 'nil', 'string', 'symbol']) }, 'follow': { '<atom>': set([ContextFreeGrammar.end_of_input(), ')', '.']), '<pair>': set([ContextFreeGrammar.end_of_input(), ')', '.']), '<expression>': set([ContextFreeGrammar.end_of_input(), ')', '.']) }, 'rules': [ ('<expression>', ['<atom>']), ('<expression>', ['<pair>']), ('<pair>', ['(', '<expression>', '.', '<expression>', ')']), ('<atom>', ['symbol']), ('<atom>', ['character']), ('<atom>', ['string']), ('<atom>', ['boolean']), ('<atom>', ['int']), ('<atom>', ['float']), ('<atom>', ['nil']) ], 'table': [ [' ', ContextFreeGrammar.end_of_input(), 'symbol', 'character', 'string', 'boolean', 'int', 'float', 'nil', '(', '.', ')'], ['<expression>', set([]), set([0]), set([0]), set([0]), set([0]), set([0]), set([0]), set([0]), set([1]), set([]), set([])], ['<atom>', set([]), set([3]), set([4]), set([5]), set([6]), set([7]), set([8]), set([9]), set([]), set([]), set([])], ['<pair>', set([]), set([]), set([]), set([]), set([]), set([]), set([]), set([]), set([2]), set([]), set([])], ] })
def test_ini(): """ Ensure the creation of the INI grammar goes as expected. """ TestParser._run(**{ 'name': 'INI', 'productions': { '<INI>': [['<SECTION>', '<INI>'], []], '<SECTION>': [['<HEADER>', '<SETTINGS>']], '<HEADER>': [['[', 'string', ']']], '<SETTINGS>': [['<KEY>', '<SEP>', '<VALUE>', '<SETTINGS>'], []], '<KEY>': [['string']], '<SEP>': [[':'], ['=']], '<VALUE>': [['string'], ['number'], ['bool']] }, 'start': '<INI>', 'terminals': set(['string', 'number', 'bool', ':', '=', '[', ']']), 'nonterminals': set(['<INI>', '<SECTION>', '<HEADER>', '<SETTINGS>', '<KEY>', '<SEP>', '<VALUE>']), 'first': { 'string': set(['string']), 'number': set(['number']), 'bool': set(['bool']), ':': set([':']), '=': set(['=']), '[': set(['[']), ']': set([']']), '<INI>': set([ContextFreeGrammar.epsilon(), '[']), '<SECTION>': set(['[']), '<HEADER>': set(['[']), '<SETTINGS>': set([ContextFreeGrammar.epsilon(), 'string']), '<KEY>': set(['string']), '<SEP>': set([':', '=']), '<VALUE>': set(['string', 'number', 'bool']) }, 'follow': { '<INI>': set([ContextFreeGrammar.end_of_input()]), '<SECTION>': set([ContextFreeGrammar.end_of_input(), '[']), '<HEADER>': set([ContextFreeGrammar.end_of_input(), '[', 'string']), '<SETTINGS>': set([ContextFreeGrammar.end_of_input(), '[']), '<KEY>': set([':', '=']), '<SEP>': set(['string', 'number', 'bool']), '<VALUE>': set([ContextFreeGrammar.end_of_input(), '[', 'string']) }, 'rules': [ ('<INI>', ['<SECTION>', '<INI>']), ('<INI>', []), ('<SECTION>', ['<HEADER>', '<SETTINGS>']), ('<HEADER>', ['[', 'string', ']']), ('<SETTINGS>', ['<KEY>', '<SEP>', '<VALUE>', '<SETTINGS>']), ('<SETTINGS>', []), ('<KEY>', ['string']), ('<SEP>', [':']), ('<SEP>', ['=']), ('<VALUE>', ['string']), ('<VALUE>', ['number']), ('<VALUE>', ['bool']) ], 'table': [[' ', ContextFreeGrammar.end_of_input(), 'bool', 'string', '=', '[', ':', ']', 'number'], ['<VALUE>', set([]), set([11]), set([9]), set([]), set([]), set([]), set([]), set([10])], ['<KEY>', set([]), set([]), set([6]), set([]), set([]), set([]), set([]), set([])], ['<SETTINGS>', set([5]), set([]), set([4]), set([]), set([5]), set([]), set([]), set([])], ['<SECTION>', set([]), set([]), set([]), set([]), set([2]), set([]), set([]), set([])], ['<HEADER>', set([]), set([]), set([]), set([]), set([3]), set([]), set([]), set([])], ['<SEP>', set([]), set([]), set([]), set([8]), set([]), set([7]), set([]), set([])], ['<INI>', set([1]), set([]), set([]), set([]), set([0]), set([]), set([]), set([])], ] })
def test_epsilon_present(): """ Ensure the creation of a simple grammar containing an epsilon goes as expected. """ TestParser._run(**{ 'name': 'Epsilon', 'productions': { '<E>': [['<T>', '<E\'>']], '<E\'>': [['<A>', '<T>', '<E\'>'], []], '<A>': [['+'], ['-']], '<T>': [['<F>', '<T\'>']], '<T\'>': [['<M>', '<F>', '<T\'>'], []], '<M>': [['*']], '<F>': [['(', '<E>', ')'], ['id']] }, 'start': '<E>', 'terminals': set(['+', '-', '*', '(', ')', 'id']), 'nonterminals': set(['<E>', '<E\'>', '<A>', '<T>', '<T\'>', '<M>', '<F>']), 'first': { '+': set(['+']), '-': set(['-']), '*': set(['*']), '(': set(['(']), ')': set([')']), 'id': set(['id']), '<E>': set(['(', 'id']), '<E\'>': set(['+', '-', ContextFreeGrammar.epsilon()]), '<A>': set(['+', '-']), '<T>': set(['(', 'id']), '<T\'>': set([ContextFreeGrammar.epsilon(), '*']), '<M>': set(['*']), '<F>': set(['(', 'id']) }, 'follow': { '<E>': set([ContextFreeGrammar.end_of_input(), ')']), '<E\'>': set([ContextFreeGrammar.end_of_input(), ')']), '<A>': set(['(', 'id']), '<T>': set([')', '+', '-', ContextFreeGrammar.end_of_input()]), '<T\'>': set([')', '+', '-', ContextFreeGrammar.end_of_input()]), '<M>': set(['(', 'id']), '<F>': set([')', '+', '-', '*', ContextFreeGrammar.end_of_input()]) }, 'rules': [ ('<E>', ['<T>', '<E\'>']), ('<E\'>', ['<A>', '<T>', '<E\'>']), ('<E\'>', []), ('<A>', ['+']), ('<A>', ['-']), ('<T>', ['<F>', '<T\'>']), ('<T\'>', ['<M>', '<F>', '<T\'>']), ('<T\'>', []), ('<M>', ['*']), ('<F>', ['(', '<E>', ')']), ('<F>', ['id']) ], 'table': [ [' ', ContextFreeGrammar.end_of_input(), 'id', ')', '(', '+', '*', '-'], ['<E>', set([]), set([0]), set([]), set([0]), set([]), set([]), set([])], ['<E\'>', set([2]), set([]), set([2]), set([]), set([1]), set([]), set([1])], ['<A>', set([]), set([]), set([]), set([]), set([3]), set([]), set([4])], ['<T>', set([]), set([5]), set([]), set([5]), set([]), set([]), set([])], ['<T\'>', set([7]), set([]), set([7]), set([]), set([7]), set([6]), set([7])], ['<M>', set([]), set([]), set([]), set([]), set([]), set([8]), set([])], ['<F>', set([]), set([10]), set([]), set([9]), set([]), set([]), set([])] ] })
def test_json(): """ Ensure the creation of the JSON grammar goes as expected. """ TestParser._run(**{ 'name': 'JSON', 'productions': { '<VALUE>': [ ['string'], ['number'], ['bool'], ['null'], ['<OBJECT>'], ['<ARRAY>'] ], '<OBJECT>': [['{', '<OBJECT\'>']], '<OBJECT\'>': [['}'], ['<MEMBERS>', '}']], '<MEMBERS>': [['<PAIR>', '<MEMBERS\'>']], '<PAIR>': [['string', ':', '<VALUE>']], '<MEMBERS\'>': [[',', '<MEMBERS>'], []], '<ARRAY>': [['[', '<ARRAY\'>']], '<ARRAY\'>': [[']'], ['<ELEMENTS>', ']']], '<ELEMENTS>': [['<VALUE>', '<ELEMENTS\'>']], '<ELEMENTS\'>': [[',', '<ELEMENTS>'], []] }, 'start': '<VALUE>', 'terminals': set(['{', '}', ',', '[', ']', ':', 'string', 'number', 'bool', 'null']), 'nonterminals': set(['<VALUE>', '<OBJECT>', '<OBJECT\'>', '<MEMBERS>', '<PAIR>', '<MEMBERS\'>', '<ARRAY>', '<ARRAY\'>', '<ELEMENTS>', '<ELEMENTS\'>']), 'first': { '{': set(['{']), '}': set(['}']), ',': set([',']), '[': set(['[']), ']': set([']']), ':': set([':']), 'string': set(['string']), 'number': set(['number']), 'bool': set(['bool']), 'null': set(['null']), '<VALUE>': set(['string', 'number', 'bool', 'null', '{', '[']), '<OBJECT>': set(['{']), '<OBJECT\'>': set(['}', 'string']), '<MEMBERS>': set(['string']), '<PAIR>': set(['string']), '<MEMBERS\'>': set([ContextFreeGrammar.epsilon(), ',']), '<ARRAY>': set(['[']), '<ARRAY\'>': set([']', 'string', 'number', 'bool', 'null', '{', '[']), '<ELEMENTS>': set(['string', 'number', 'bool', 'null', '{', '[']), '<ELEMENTS\'>': set([ContextFreeGrammar.epsilon(), ',']) }, 'follow': { '<VALUE>': set([ContextFreeGrammar.end_of_input(), ']', '}', ',']), '<OBJECT>': set([ContextFreeGrammar.end_of_input(), ']', '}', ',']), '<OBJECT\'>': set([ContextFreeGrammar.end_of_input(), ']', '}', ',']), '<MEMBERS>': set(['}']), '<PAIR>': set(['}', ',']), '<MEMBERS\'>': set(['}']), '<ARRAY>': set([ContextFreeGrammar.end_of_input(), ']', '}', ',']), '<ARRAY\'>': set([ContextFreeGrammar.end_of_input(), ']', '}', ',']), '<ELEMENTS>': set([']']), '<ELEMENTS\'>': set([']']) }, 'rules': [ ('<VALUE>', ['string']), ('<VALUE>', ['number']), ('<VALUE>', ['bool']), ('<VALUE>', ['null']), ('<VALUE>', ['<OBJECT>']), ('<VALUE>', ['<ARRAY>']), ('<OBJECT>', ['{', '<OBJECT\'>']), ('<OBJECT\'>', ['}']), ('<OBJECT\'>', ['<MEMBERS>', '}']), ('<MEMBERS>', ['<PAIR>', '<MEMBERS\'>']), ('<PAIR>', ['string', ':', '<VALUE>']), ('<MEMBERS\'>', [',', '<MEMBERS>']), ('<MEMBERS\'>', []), ('<ARRAY>', ['[', '<ARRAY\'>']), ('<ARRAY\'>', [']']), ('<ARRAY\'>', ['<ELEMENTS>', ']']), ('<ELEMENTS>', ['<VALUE>', '<ELEMENTS\'>']), ('<ELEMENTS\'>', [',', '<ELEMENTS>']), ('<ELEMENTS\'>', []) ], 'table': [[' ', ContextFreeGrammar.end_of_input(), ':', 'string', ']', 'number', ',', 'bool', '{', 'null', '}', '['], ['<PAIR>', set([]), set([]), set([10]), set([]), set([]), set([]), set([]), set([]), set([]), set([]), set([])], ['<VALUE>', set([]), set([]), set([0]), set([]), set([1]), set([]), set([2]), set([4]), set([3]), set([]), set([5])], ['<OBJECT>', set([]), set([]), set([]), set([]), set([]), set([]), set([]), set([6]), set([]), set([]), set([])], ['<ELEMENTS>', set([]), set([]), set([16]), set([]), set([16]), set([]), set([16]), set([16]), set([16]), set([]), set([16])], ['<OBJECT\'>', set([]), set([]), set([8]), set([]), set([]), set([]), set([]), set([]), set([]), set([7]), set([])], ['<MEMBERS\'>', set([]), set([]), set([]), set([]), set([]), set([11]), set([]), set([]), set([]), set([12]), set([])], ['<ARRAY>', set([]), set([]), set([]), set([]), set([]), set([]), set([]), set([]), set([]), set([]), set([13])], ['<MEMBERS>', set([]), set([]), set([9]), set([]), set([]), set([]), set([]), set([]), set([]), set([]), set([])], ['<ELEMENTS\'>', set([]), set([]), set([]), set([18]), set([]), set([17]), set([]), set([]), set([]), set([]), set([])], ["<ARRAY'>", set([]), set([]), set([15]), set([14]), set([15]), set([]), set([15]), set([15]), set([15]), set([]), set([15])] ] })
def test_simple_language(): """ Ensure the creation of a simple langugage grammar goes as expected. """ TestParser._run(**{ 'name': 'Simple language', 'productions': { '<STMT>': [ ['if', '<EXPR>', 'then', '<STMT>'], ['while', '<EXPR>', 'do', '<STMT>'], ['<EXPR>'] ], '<EXPR>': [ ['<TERM>', '->', 'id'], ['zero?', '<TERM>'], ['not', '<EXPR>'], ['++', 'id'], ['--', 'id'] ], '<TERM>': [['id'], ['constant']], '<BLOCK>': [['<STMT>'], ['{', '<STMTS>', '}']], '<STMTS>': [['<STMT>', '<STMTS>'], []] }, 'start': '<STMTS>', 'terminals': set(['if', 'then', 'while', 'do', '->', 'zero?', 'not', '++', '--', 'id', 'constant', '{', '}']), 'nonterminals': set(['<STMT>', '<STMTS>', '<BLOCK>', '<TERM>', '<EXPR>']), 'first': { 'if': set(['if']), 'then': set(['then']), 'while': set(['while']), 'do': set(['do']), '->': set(['->']), 'zero?': set(['zero?']), 'not': set(['not']), '++': set(['++']), '--': set(['--']), 'id': set(['id']), 'constant': set(['constant']), '{': set(['{']), '}': set(['}']), '<STMT>': set(['constant', '++', 'zero?', 'while', 'not', '--', 'id', 'if']), '<STMTS>': set([ContextFreeGrammar.epsilon(), 'constant', '++', 'zero?', 'while', 'not', '--', 'id', 'if']), '<BLOCK>': set(['constant', '++', 'zero?', 'while', 'not', '--', '{', 'id', 'if']), '<TERM>': set(['constant', 'id']), '<EXPR>': set(['++', 'not', 'constant', 'zero?', '--', 'id']) }, 'follow': { '<STMT>': set([ContextFreeGrammar.end_of_input(), 'constant', '++', 'not', 'while', 'zero?', '--', '}', 'id', 'if']), '<STMTS>': set([ContextFreeGrammar.end_of_input(), '}']), '<BLOCK>': set([]), '<TERM>': set([ContextFreeGrammar.end_of_input(), 'then', 'constant', 'do', 'not', 'id', 'if', '++', '--', 'while', 'zero?', '->', '}']), '<EXPR>': set([ContextFreeGrammar.end_of_input(), 'then', 'constant', 'do', '++', '--', 'while', 'not', 'zero?', '}', 'id', 'if']) }, 'rules': [ ('<STMT>', ['if', '<EXPR>', 'then', '<STMT>']), ('<STMT>', ['while', '<EXPR>', 'do', '<STMT>']), ('<STMT>', ['<EXPR>']), ('<EXPR>', ['<TERM>', '->', 'id']), ('<EXPR>', ['zero?', '<TERM>']), ('<EXPR>', ['not', '<EXPR>']), ('<EXPR>', ['++', 'id']), ('<EXPR>', ['--', 'id']), ('<TERM>', ['id']), ('<TERM>', ['constant']), ('<BLOCK>', ['<STMT>']), ('<BLOCK>', ['{', '<STMTS>', '}']), ('<STMTS>', ['<STMT>', '<STMTS>']), ('<STMTS>', []) ], 'table': [ [' ', ContextFreeGrammar.end_of_input(), 'then', 'constant', 'do', '++', 'zero?', 'while', 'not', '--', '{', '->', '}', 'id', 'if'], ['<STMT>', set([]), set([]), set([2]), set([]), set([2]), set([2]), set([1]), set([2]), set([2]), set([]), set([]), set([]), set([2]), set([0])], ['<EXPR>', set([]), set([]), set([3]), set([]), set([6]), set([4]), set([]), set([5]), set([7]), set([]), set([]), set([]), set([3]), set([])], ['<BLOCK>', set([]), set([]), set([10]), set([]), set([10]), set([10]), set([10]), set([10]), set([10]), set([11]), set([]), set([]), set([10]), set([10])], ['<STMTS>', set([13]), set([]), set([12]), set([]), set([12]), set([12]), set([12]), set([12]), set([12]), set([]), set([]), set([13]), set([12]), set([12])], ['<TERM>', set([]), set([]), set([9]), set([]), set([]), set([]), set([]), set([]), set([]), set([]), set([]), set([]), set([8]), set([])] ] })