Starting from 2021-07-01, all LRZ GitLab users will be required to explicitly accept the GitLab Terms of Service. Please see the detailed information at https://doku.lrz.de/display/PUBLIC/GitLab and make sure that your projects conform to the requirements.

Commit 5dc386ad authored by Eckhart Arnold's avatar Eckhart Arnold
Browse files

ebnf.EBNFCompiler and parsers.Grammar now raise errors when parsing rules can...

ebnf.EBNFCompiler and parsers.Grammar now raise errors when parsing rules can never be reached from the root parser
parent 739242bd
......@@ -127,7 +127,6 @@ class CompilationError(Exception):
"""Raised when a string or file in a domain specific language (DSL)
contains errors.
"""
def __init__(self, error_messages, dsl_text, dsl_grammar, AST, result):
self.error_messages = error_messages
self.dsl_text = dsl_text
......
......@@ -345,7 +345,14 @@ class Grammar:
self.root__.apply(self._add_parser)
def __getitem__(self, key):
return getattr(self, key)
try:
return self.__dict__[key]
except KeyError:
parser = getattr(self, key, None)
if parser:
raise KeyError(('Parser "%s" inaccesible, because it is not connected '
'to the root parser "%s" !') % (key, self.root__.name))
raise KeyError('Unknown parser "%s" !' % key)
def _reset(self):
# variables stored and recalled by Capture and Retrieve parsers
......
......@@ -87,7 +87,7 @@ def mock_syntax_tree(sexpr):
return Node(MockParser(name, ':' + class_name), result)
def recompile_grammar(ebnf_filename):
def recompile_grammar(ebnf_filename, force=False):
"""Recompiles an ebnf-grammar if necessary, that is if either no
corresponding 'XXXXCompiler.py'-file exists or if that file is
outdated.
......@@ -97,17 +97,19 @@ def recompile_grammar(ebnf_filename):
grammar. In case this is a directory and not a file all
files within this directory ending with .ebnf will be
compiled.
force(bool): If False (default), the grammar will only be
recompiled if it has been changed.
"""
if os.path.isdir(ebnf_filename):
for entry in os.listdir(ebnf_filename):
if entry.lower().endswith('.ebnf') and os.path.isfile(entry):
recompile_grammar(entry)
recompile_grammar(entry, force)
return
base, ext = os.path.splitext(ebnf_filename)
compiler_name = base + 'Compiler.py'
errors = []
if (not os.path.exists(compiler_name) or
if (not os.path.exists(compiler_name) or force or
grammar_changed(compiler_name, ebnf_filename)):
# print("recompiling parser for: " + ebnf_filename)
errors = compile_on_disk(ebnf_filename)
......
......@@ -25,4 +25,4 @@ import sys
sys.path.extend(['../../', '../', './'])
from DHParser.testing import recompile_grammar
recompile_grammar('.')
recompile_grammar('.', True)
......@@ -25,10 +25,10 @@ from multiprocessing import Pool
import sys
sys.path.extend(['../', './'])
from DHParser.toolkit import is_logging
from DHParser.toolkit import is_logging, compile_python_object
from DHParser.parsers import compile_source, Retrieve, WHITESPACE_PTYPE, nil_scanner
from DHParser.ebnf import get_ebnf_grammar, get_ebnf_transformer, EBNFTransformer, get_ebnf_compiler
from DHParser.dsl import CompilationError, compileDSL, parser_factory
from DHParser.dsl import CompilationError, compileDSL, DHPARSER_IMPORTS, parser_factory
class TestDirectives:
......@@ -281,8 +281,6 @@ class TestSelfHosting:
repetition = "{" expression §"}"
option = "[" expression §"]"
link = regexp | symbol | literal # semantic restriction: symbol must evaluate to a regexp or chain
symbol = /(?!\d)\w+/~ # e.g. expression, factor, parameter_list
literal = /"(?:[^"]|\\")*?"/~ # e.g. "(", '+', 'while'
| /'(?:[^']|\\')*?'/~ # whitespace following literals will be ignored tacitly.
......@@ -353,9 +351,22 @@ class TestBoundaryCases:
grammar = parser_factory(ebnf)()
assert False, "EBNF compiler should complain about unconnected rules."
except CompilationError as err:
grammar = err.result
assert grammar.__dict__['root']
assert grammar.__dict__['unconnected']
grammar_src = err.result
grammar = compile_python_object(DHPARSER_IMPORTS + grammar_src,
'get_(?:\w+_)?grammar$')()
assert grammar['root'], "Grammar objects should be subscriptable by parser names!"
try:
unconnected = grammar['unconnected']
assert False, "Grammar objects should raise a KeyError if subscripted by " \
"names of parsers that are unconnected to the root parser!"
except KeyError:
pass
try:
nonexistant = grammar['nonexistant']
assert False, "Grammar object shoul raise a KeyError if subscripted by " \
"a non-existant parser name!"
except KeyError:
pass
class TestSynonymDetection:
......
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