Currently job artifacts in CI/CD pipelines on LRZ GitLab never expire. Starting from Wed 26.1.2022 the default expiration time will be 30 days (GitLab default). Currently existing 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: https://gitlab.lrz.de/help/user/admin_area/settings/continuous_integration.html#default-artifacts-expiration

Commit 5b6deeea authored by eckhart's avatar eckhart
Browse files

- 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