16.12.2021, 9:00 - 11:00: Due to updates GitLab may be unavailable for some minutes between 09:00 and 11:00.

Commit 6d4c1c4a authored by Eckhart Arnold's avatar Eckhart Arnold
Browse files

compileEBNF optionally returns source code instead of grammar factory

parent c97e4d48
...@@ -185,13 +185,9 @@ def compileDSL(text_or_file, scanner, dsl_grammar, ast_transformation, compiler) ...@@ -185,13 +185,9 @@ def compileDSL(text_or_file, scanner, dsl_grammar, ast_transformation, compiler)
def compileEBNF(ebnf_src, ebnf_grammar_obj=None, source_only=False): def compileEBNF(ebnf_src, ebnf_grammar_obj=None, source_only=False):
"""Compiles an EBNF source file into a Grammar class. """Compiles an EBNF source file. Either returns a factory function
for the grammar-paraser or the source code of a compiler suite with
Please note: This functions returns a class which must be skeletons for scanner, transformer and compiler.
instantiated before calling its parse()-method! Calling the method
directly from the class (which is technically possible in python
yields an error message complaining about a missing parameter,
the cause of which may not be obvious at first sight.
Args: Args:
ebnf_src(str): Either the file name of an EBNF grammar or ebnf_src(str): Either the file name of an EBNF grammar or
...@@ -200,21 +196,24 @@ def compileEBNF(ebnf_src, ebnf_grammar_obj=None, source_only=False): ...@@ -200,21 +196,24 @@ def compileEBNF(ebnf_src, ebnf_grammar_obj=None, source_only=False):
DHParser.EBNFcompiler.EBNFGrammar object. This can speed DHParser.EBNFcompiler.EBNFGrammar object. This can speed
up compilation, because no new EBNFGrammar object needs to up compilation, because no new EBNFGrammar object needs to
be instantiated. be instantiated.
source_only (bool): If True, the source code of the Grammar source_only (str or bool): Branding name for the compiler
class is returned instead of the class itself. suite source code. If ``True`` the default branding "DSL"
will be used. If False (default), no source but a factory
function for grammar-parser callables will be returned.
Returns: Returns:
A Grammar class that can be instantiated for parsing a text A factory function for a grammar-parser for texts in the
which conforms to the language defined by ``ebnf_src``. language defined by ``ebnf_src``. With the ``source_only``
a complete compiler suite skeleton will be returned instead.
""" """
grammar = ebnf_grammar_obj or get_ebnf_grammar() grammar = ebnf_grammar_obj or get_ebnf_grammar()
compiler = get_ebnf_compiler("DSL") if source_only == True: source_only = "DSL"
compiler = get_ebnf_compiler(source_only or "DSL")
grammar_src = compileDSL(ebnf_src, nil_scanner, grammar, EBNFTransformer, compiler) grammar_src = compileDSL(ebnf_src, nil_scanner, grammar, EBNFTransformer, compiler)
if source_only: if source_only:
src = [SECTION_MARKER.format(marker=SYMBOLS_SECTION), DHPARSER_IMPORTS, src = [DHPARSER_IMPORTS, compiler.gen_scanner_skeleton(), grammar_src,
SECTION_MARKER.format(marker=SCANNER_SECTION), compiler.gen_scanner_skeleton(), compiler.gen_transformer_skeleton(), compiler.gen_compiler_skeleton(),
SECTION_MARKER.format(marker=PARSER_SECTION), grammar_src, DHPARSER_MAIN.format(NAME=source_only)]
SECTION_MARKER.format(marker=AST_SECTION), compiler.gen_transformer_skeleton(),
SECTION_MARKER.format(marker=COMPILER_SECTION), compiler.gen_compiler_skeleton()]
return '\n'.join(src) return '\n'.join(src)
else: else:
return compile_python_object(DHPARSER_IMPORTS + grammar_src, 'get_\w*_grammar$') return compile_python_object(DHPARSER_IMPORTS + grammar_src, 'get_\w*_grammar$')
......
...@@ -39,7 +39,19 @@ ARITHMETIC_EBNF = """ ...@@ -39,7 +39,19 @@ ARITHMETIC_EBNF = """
class TestCompileFunctions: class TestCompileFunctions:
def test_compileEBNF(self): def test_compileEBNF(self):
parser_src = compileEBNF(ARITHMETIC_EBNF, source_only=True) parser_src = compileEBNF(ARITHMETIC_EBNF, source_only=True)
print(parser_src) assert isinstance(parser_src, str), str(type(parser_src))
assert parser_src.find('get_DSL') >= 0
parser_src = compileEBNF(ARITHMETIC_EBNF, source_only="CustomDSL")
assert isinstance(parser_src, str), str(type(parser_src))
assert parser_src.find('get_CustomDSL') >= 0
parser_factory = compileEBNF(ARITHMETIC_EBNF, source_only=False)
assert callable(parser_factory)
parser = parser_factory()
result = parser("5 + 3 * 4")
assert not result.error_flag
result = parser("5A + 4B ** 4C")
assert result.error_flag
class TestCompilerGeneration: class TestCompilerGeneration:
trivial_lang = """ trivial_lang = """
...@@ -96,4 +108,4 @@ class TestCompilerGeneration: ...@@ -96,4 +108,4 @@ class TestCompilerGeneration:
if __name__ == "__main__": if __name__ == "__main__":
from run import runner from run import runner
runner("TestCompileFunctions", globals()) runner("", globals())
\ No newline at end of file \ No newline at end of file
...@@ -24,7 +24,8 @@ import sys ...@@ -24,7 +24,8 @@ import sys
sys.path.extend(['../', './']) sys.path.extend(['../', './'])
from DHParser.toolkit import is_logging, compile_python_object from DHParser.toolkit import is_logging, compile_python_object
from DHParser.syntaxtree import no_operation, traverse from DHParser.syntaxtree import no_operation, traverse, remove_expendables, \
replace_by_single_child, reduce_single_child, flatten
from DHParser.parsers import compile_source from DHParser.parsers import compile_source
from DHParser.ebnf import get_ebnf_grammar, get_ebnf_transformer, get_ebnf_compiler from DHParser.ebnf import get_ebnf_grammar, get_ebnf_transformer, get_ebnf_compiler
from DHParser.dsl import compileEBNF, DHPARSER_IMPORTS from DHParser.dsl import compileEBNF, DHPARSER_IMPORTS
...@@ -39,18 +40,34 @@ ARITHMETIC_EBNF = """ ...@@ -39,18 +40,34 @@ ARITHMETIC_EBNF = """
# example: "5 + 3 * 4" # example: "5 + 3 * 4"
""" """
ARITHMETIC_EBNF_transformation_table = { ARITHMETIC_EBNF_transformation_table = {
# AST Transformations for the DSL-grammar # AST Transformations for the DSL-grammar
"formula": no_operation, "formula": [remove_expendables],
"expr": no_operation, "term, expr": [replace_by_single_child, flatten],
"term": no_operation, "factor": [remove_expendables, reduce_single_child],
"factor": no_operation, "": [remove_expendables, replace_by_single_child]
"": no_operation
} }
ARITHMETIC_EBNFTransform = partial(traverse, processing_table=ARITHMETIC_EBNF_transformation_table) ARITHMETIC_EBNFTransform = partial(traverse, processing_table=ARITHMETIC_EBNF_transformation_table)
class TestGrammarTest:
cases = {
"factor": {
"match": {
1: "0",
2: "314",
},
"fail": {
1: "21F",
2: "G123"
}
}
}
class TestInfiLoopsAndRecursion: class TestInfiLoopsAndRecursion:
def test_direct_left_recursion(self): def test_direct_left_recursion(self):
minilang = ARITHMETIC_EBNF minilang = ARITHMETIC_EBNF
...@@ -76,21 +93,6 @@ class TestInfiLoopsAndRecursion: ...@@ -76,21 +93,6 @@ class TestInfiLoopsAndRecursion:
# print(syntax_tree.collect_errors()) # print(syntax_tree.collect_errors())
class TestTestGrammar:
cases = {
"factor": {
"match": {
1: "0",
2: "314",
},
"fail": {
1: "21F",
2: "G123"
}
}
}
class TestRegex: class TestRegex:
def test_multilineRegex(self): def test_multilineRegex(self):
mlregex = r""" mlregex = r"""
...@@ -132,4 +134,4 @@ class TestRegex: ...@@ -132,4 +134,4 @@ class TestRegex:
if __name__ == "__main__": if __name__ == "__main__":
from run import runner from run import runner
runner("", globals()) runner("TestGrammarTest", globals())
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment