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)
def compileEBNF(ebnf_src, ebnf_grammar_obj=None, source_only=False):
"""Compiles an EBNF source file into a Grammar class.
Please note: This functions returns a class which must be
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.
"""Compiles an EBNF source file. Either returns a factory function
for the grammar-paraser or the source code of a compiler suite with
skeletons for scanner, transformer and compiler.
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):
DHParser.EBNFcompiler.EBNFGrammar object. This can speed
up compilation, because no new EBNFGrammar object needs to
be instantiated.
source_only (bool): If True, the source code of the Grammar
class is returned instead of the class itself.
source_only (str or bool): Branding name for the compiler
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.
A Grammar class that can be instantiated for parsing a text
which conforms to the language defined by ``ebnf_src``.
A factory function for a grammar-parser for texts in the
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()
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)
if source_only:
SECTION_MARKER.format(marker=SCANNER_SECTION), compiler.gen_scanner_skeleton(),
SECTION_MARKER.format(marker=PARSER_SECTION), grammar_src,
SECTION_MARKER.format(marker=AST_SECTION), compiler.gen_transformer_skeleton(),
SECTION_MARKER.format(marker=COMPILER_SECTION), compiler.gen_compiler_skeleton()]
src = [DHPARSER_IMPORTS, compiler.gen_scanner_skeleton(), grammar_src,
compiler.gen_transformer_skeleton(), compiler.gen_compiler_skeleton(),
return '\n'.join(src)
return compile_python_object(DHPARSER_IMPORTS + grammar_src, 'get_\w*_grammar$')
......@@ -39,7 +39,19 @@ ARITHMETIC_EBNF = """
class TestCompileFunctions:
def test_compileEBNF(self):
parser_src = compileEBNF(ARITHMETIC_EBNF, source_only=True)
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:
trivial_lang = """
......@@ -96,4 +108,4 @@ class TestCompilerGeneration:
if __name__ == "__main__":
from run import runner
runner("TestCompileFunctions", globals())
\ No newline at end of file
runner("", globals())
\ No newline at end of file
......@@ -24,7 +24,8 @@ import sys
sys.path.extend(['../', './'])
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.ebnf import get_ebnf_grammar, get_ebnf_transformer, get_ebnf_compiler
from DHParser.dsl import compileEBNF, DHPARSER_IMPORTS
......@@ -39,18 +40,34 @@ ARITHMETIC_EBNF = """
# example: "5 + 3 * 4"
ARITHMETIC_EBNF_transformation_table = {
# AST Transformations for the DSL-grammar
"formula": no_operation,
"expr": no_operation,
"term": no_operation,
"factor": no_operation,
"": no_operation
"formula": [remove_expendables],
"term, expr": [replace_by_single_child, flatten],
"factor": [remove_expendables, reduce_single_child],
"": [remove_expendables, replace_by_single_child]
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:
def test_direct_left_recursion(self):
......@@ -76,21 +93,6 @@ class TestInfiLoopsAndRecursion:
# print(syntax_tree.collect_errors())
class TestTestGrammar:
cases = {
"factor": {
"match": {
1: "0",
2: "314",
"fail": {
1: "21F",
2: "G123"
class TestRegex:
def test_multilineRegex(self):
mlregex = r"""
......@@ -132,4 +134,4 @@ class TestRegex:
if __name__ == "__main__":
from run import runner
runner("", globals())
runner("TestGrammarTest", globals())
Supports Markdown
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