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

Commit 5b6deeea authored by eckhart's avatar eckhart

- ebnf.py: Ambigous error messages now cause a compiler error

parent 220512ba
......@@ -432,8 +432,19 @@ class EBNFCompiler(Compiler):
root_symbol: The name of the root symbol.
directives: A dictionary of all directives and their default
values.
directives: A record of all directives and their default values.
defined_directives: A set of all directives that have already been
defined. With the exception of those directives contained
in EBNFCompiler.REPEATABLE_DIRECTIVES, directives must only
be defined once.
consumed_custom_errors: A set of symbols for which a custom error
has been defined and(!) consumed during compilation. This
allows to add a compiler error in those cases where (i) an
error message has been defined but will never used or (ii)
an error message is accidently used twice. For examples, see
`test_ebnf.TestErrorCustomization`
re_flags: A set of regular expression flags to be added to all
regular expressions found in the current parsing process
......@@ -481,8 +492,9 @@ class EBNFCompiler(Compiler):
self.definitions = {} # type: Dict[str, str]
self.deferred_tasks = [] # type: List[Callable]
self.root_symbol = "" # type: str
self.directives = EBNFDirectives() # type: EBNFDirectives
self.defined_directives = set() # type: Set[str]
self.directives = EBNFDirectives() # type: EBNFDirectives
self.defined_directives = set() # type: Set[str]
self.consumed_custom_errors = set() # type: Set[str]
self.grammar_id += 1
......@@ -989,8 +1001,15 @@ class EBNFCompiler(Compiler):
if custom_args:
current_symbol = next(reversed(self.rules.keys()))
if current_symbol in self.directives.error:
# use class field instead or direct representation of error messages!
custom_args.append('err_msgs=' + current_symbol + self.ERR_MSG_SUFFIX)
if current_symbol in self.consumed_custom_errors:
self.tree.new_error(
node, "Cannot apply customized error messages unambigiously, because "
"symbol {} contains more than one series with a mandatory marker '§' "
"in its definiens.".format(current_symbol), Error.AMBIGUOUS_ERROR_MSG)
else:
# use class field instead or direct representation of error messages!
custom_args.append('err_msgs=' + current_symbol + self.ERR_MSG_SUFFIX)
self.consumed_custom_errors.add(current_symbol)
compiled = self.non_terminal(node, 'Series', custom_args)
# TODO: Maybe add a warning about ambiguous error messages in case there are several
# Series with mandatory items within the definiens of the same symbol?
......
......@@ -85,6 +85,8 @@ class Error:
PARSER_STOPPED_BEFORE_END = ErrorCode(1040)
CAPTURE_STACK_NOT_EMPTY = ErrorCode(1050)
MALFORMED_ERROR_STRING = ErrorCode(1060)
AMBIGUOUS_ERROR_MSG = ErrorCode(1070)
UNUSED_ERROR_MSG = ErrorCode(1080)
def __init__(self, message: str, pos, code: ErrorCode = ERROR,
orig_pos: int = -1, line: int = -1, column: int = -1) -> None:
......
......@@ -448,12 +448,12 @@ class TestAllSome:
assert grammar('B').content == 'B'
class TestCuratedErrors:
class TestErrorCustomization:
"""
Cureted Errors replace existing errors with alternative
Customized Errors replace existing errors with alternative
error codes and messages that are more helptful to the user.
"""
def test_curated_mandatory_continuation(self):
def test_customized_mandatory_continuation(self):
lang = """
document = series | /.*/
@series_error = "a user defined error message"
......@@ -472,7 +472,7 @@ class TestCuratedErrors:
assert st.collect_errors()[0].code == Error.MANDATORY_CONTINUATION
assert st.collect_errors()[0].message == "a user defined error message"
def test_curated_error_case_sensitive(self):
def test_customized_error_case_sensitive(self):
lang = """
document = Series | /.*/
@Series_error = "a user defined error message"
......@@ -484,14 +484,27 @@ class TestCuratedErrors:
assert st.collect_errors()[0].code == Error.MANDATORY_CONTINUATION
assert st.collect_errors()[0].message == "a user defined error message"
def test_error_cusomization_mistakes(self):
def test_ambiguous_error_customization(self):
lang = """
document = series
@series_error = "ambiguous error message: does it apply to 'one' or 'two'?"
series = "A" § "B" "C" | "X" § "Y" "Z"
"""
try:
parser = grammar_provider(lang)()
assert False, "CompilationError because of ambiguous error message exptected!"
except CompilationError as compilation_error:
err = next(compilation_error.errors)
assert err.code == Error.AMBIGUOUS_ERROR_MSG, str(compilation_error)
def test_unsed_error_customization(self):
lang = """
document = series | other
@other_error = "a user defined error message"
series = "A" § "B" "C"
other = "X" | "Y" | "Z"
"""
parser = grammar_provider(lang)()
parser = grammar_provider(lang)() # TODO: Here an error should occur!
st = parser("ABC")
assert not st.error_flag
st = parser("Y")
......
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