The expiration time for new job artifacts in CI/CD pipelines is now 30 days (GitLab default). Previously generated artifacts in already completed jobs will not be affected by the change. The latest artifacts for all jobs in the latest successful pipelines will be kept. More information:

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())
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