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 dbd41bc6 authored by di68kap's avatar di68kap
Browse files

- bugfix: multiple error messages now accepted

parent deb408cc
......@@ -906,11 +906,11 @@ class EBNFCompiler(Compiler):
key = node.children[0].content
assert key not in self.directives.tokens
if key not in self.REPEATABLE_DIRECTIVES:
if key not in self.REPEATABLE_DIRECTIVES and not key.endswith('_error'):
if key in self.defined_directives:
self.tree.new_error(node, 'Directive "%s" has already been defined earlier. '
% key + 'Later definition will be ignored!',
code=Error.REDEFINED_DIRECTIVE_WARNING)
code=Error.REDEFINED_DIRECTIVE)
return ""
self.defined_directives.add(key)
......@@ -986,9 +986,6 @@ class EBNFCompiler(Compiler):
elif key.endswith('_skip'):
symbol = key[:-5]
if symbol in self.directives.skip:
self.tree.new_error(node, 'In-series resuming for "%s" has already been defined'
' earlier!' % symbol)
if symbol in self.rules:
self.tree.new_error(node, 'Skip list for resuming in series for symbol "{}"'
'must be defined before the symbol!'.format(symbol))
......@@ -996,10 +993,6 @@ class EBNFCompiler(Compiler):
elif key.endswith('_resume'):
symbol = key[:-7]
if symbol in self.directives.resume:
self.tree.new_error(node, 'Reentry conditions for "%s" have already been defined'
' earlier!' % symbol)
else:
self.directives.resume[symbol] = self._gen_search_list(node.children[1:])
else:
......
......@@ -71,7 +71,6 @@ class Error:
# warning codes
REDEFINED_DIRECTIVE_WARNING = ErrorCode(110)
REDECLARED_TOKEN_WARNING = ErrorCode(120)
UNUSED_ERROR_HANDLING_WARNING = ErrorCode(130)
......@@ -87,6 +86,7 @@ class Error:
CAPTURE_STACK_NOT_EMPTY = ErrorCode(1050)
MALFORMED_ERROR_STRING = ErrorCode(1060)
AMBIGUOUS_ERROR_HANDLING = ErrorCode(1070)
REDEFINED_DIRECTIVE = ErrorCode(1080)
def __init__(self, message: str, pos, code: ErrorCode = ERROR,
orig_pos: int = -1, line: int = -1, column: int = -1) -> None:
......
......@@ -208,7 +208,6 @@ class Parser:
def __init__(self) -> None:
# assert isinstance(name, str), str(name)
self.pname = '' # type: str
self.ptype = ':' + self.__class__.__name__ # type: str
self.tag_name = self.ptype # type: str
if not hasattr(self.__class__, 'alive'):
self._grammar = ZOMBIE_GRAMMAR # type: Grammar
......@@ -223,7 +222,6 @@ class Parser:
"""
duplicate = self.__class__()
duplicate.pname = self.pname
duplicate.ptype = self.ptype
duplicate.tag_name = self.tag_name
return duplicate
......@@ -233,6 +231,12 @@ class Parser:
def __str__(self):
return self.pname + (' = ' if self.pname else '') + repr(self)
@property
def ptype(self) -> str:
"""Returns a type name for the parser. By default this is the name of
the parser class with an added leading colon ':'. """
return ':' + self.__class__.__name__
@property
def repr(self) -> str:
"""Returns the parser's name if it has a name and self.__repr___() otherwise."""
......@@ -459,7 +463,6 @@ class ZombieParser(Parser):
def __init__(self):
super().__init__()
self.pname = ZOMBIE
self.ptype = ':' + ZOMBIE
self.tag_name = ZOMBIE
# no need to call super class constructor
assert not self.__class__.alive, "There can be only one!"
......@@ -476,8 +479,13 @@ class ZombieParser(Parser):
def __call__(self, text):
raise AssertionError("Better call Saul ;-)")
def _grammar_assigned_notifier(self):
raise AssertionError("No zombies allowed in any grammar!")
@property
def grammar(self) -> 'Grammar':
raise AssertionError("Zombie parser doesn't have a grammar!")
@grammar.setter
def grammar(self, grammar: 'Grammar'):
raise AssertionError('Cannot assign a grammar a zombie parser or vice versa!')
def apply(self, func: ApplyFunc):
return "Eaten alive..."
......@@ -1048,8 +1056,8 @@ class PreprocessorToken(Parser):
def __deepcopy__(self, memo):
duplicate = self.__class__(self.pname)
duplicate.pname = self.pname
duplicate.ptype = self.ptype
# duplicate.pname = self.pname # will be written by the constructor, anyway
duplicate.tage_name = self.tag_name
return duplicate
def _parse(self, text: StringView) -> Tuple[Optional[Node], StringView]:
......@@ -1102,7 +1110,7 @@ class Token(Parser):
def __deepcopy__(self, memo):
duplicate = self.__class__(self.text)
duplicate.pname = self.pname
duplicate.ptype = self.ptype
duplicate.tag_name = self.tag_name
return duplicate
def _parse(self, text: StringView) -> Tuple[Optional[Node], StringView]:
......@@ -1146,7 +1154,7 @@ class RegExp(Parser):
regexp = self.regexp.pattern
duplicate = self.__class__(regexp)
duplicate.pname = self.pname
duplicate.ptype = self.ptype
duplicate.tag_name = self.tag_name
return duplicate
def _parse(self, text: StringView) -> Tuple[Optional[Node], StringView]:
......@@ -1240,7 +1248,7 @@ class UnaryOperator(Parser):
parser = copy.deepcopy(self.parser, memo)
duplicate = self.__class__(parser)
duplicate.pname = self.pname
duplicate.ptype = self.ptype
duplicate.tag_name = self.tag_name
return duplicate
def _apply(self, func: ApplyFunc, flip: FlagFunc) -> bool:
......@@ -1271,7 +1279,7 @@ class NaryOperator(Parser):
parsers = copy.deepcopy(self.parsers, memo)
duplicate = self.__class__(*parsers)
duplicate.pname = self.pname
duplicate.ptype = self.ptype
duplicate.tag_name = self.tag_name
return duplicate
def _apply(self, func: ApplyFunc, flip: FlagFunc) -> bool:
......@@ -1314,7 +1322,7 @@ class Option(UnaryOperator):
super().__init__(parser)
# assert isinstance(parser, Parser)
assert not isinstance(parser, Option), \
"Redundant nesting of options: %s%s" % (str(parser.pname), str(self.ptype))
"Redundant nesting of options: %s(%s)" % (self.ptype, parser.pname)
def _parse(self, text: StringView) -> Tuple[Optional[Node], StringView]:
node, text = self.parser(text)
......@@ -1393,7 +1401,7 @@ class OneOrMore(UnaryOperator):
super().__init__(parser)
assert not isinstance(parser, Option), \
"Use ZeroOrMore instead of nesting OneOrMore and Option: " \
"%s(%s)" % (str(self.ptype), str(parser.pname))
"%s(%s)" % (self.ptype, parser.pname)
def _parse(self, text: StringView) -> Tuple[Optional[Node], StringView]:
results = () # type: Tuple[Node, ...]
......@@ -1513,7 +1521,7 @@ class Series(NaryOperator):
duplicate = self.__class__(*parsers, mandatory=self.mandatory,
err_msgs=self.err_msgs, skip=self.skip)
duplicate.pname = self.pname
duplicate.ptype = self.ptype
duplicate.tag_name = self.tag_name
return duplicate
def _parse(self, text: StringView) -> Tuple[Optional[Node], StringView]:
......@@ -1722,7 +1730,7 @@ class AllOf(NaryOperator):
duplicate = self.__class__(*parsers, mandatory=self.mandatory,
err_msgs=self.err_msgs, skip=self.skip)
duplicate.pname = self.pname
duplicate.ptype = self.ptype
duplicate.tag_name = self.tag_name
duplicate.num_parsers = self.num_parsers
return duplicate
......@@ -2014,7 +2022,7 @@ class Retrieve(Parser):
def __deepcopy__(self, memo):
duplicate = self.__class__(self.symbol, self.filter)
duplicate.pname = self.pname
duplicate.ptype = self.ptype
duplicate.tag_name = self.tag_name
return duplicate
def _parse(self, text: StringView) -> Tuple[Optional[Node], StringView]:
......@@ -2132,7 +2140,7 @@ class Forward(Parser):
def __deepcopy__(self, memo):
duplicate = self.__class__()
# duplicate.pname = self.pname # Forward-Parsers should never have a name!
duplicate.ptype = self.ptype
duplicate.tag_name = self.tag_name
memo[id(self)] = duplicate
parser = copy.deepcopy(self.parser, memo)
duplicate.set(parser)
......
......@@ -495,10 +495,37 @@ class TestErrorCustomization:
assert st.collect_errors()[0].code == Error.MANDATORY_CONTINUATION
assert st.collect_errors()[0].message == "a user defined error message"
def test_multiple_error_messages(self):
lang = """
document = series | /.*/
@series_error = '_', "the underscore is wrong in this place"
@series_error = '*', "the asterix is wrong in this place"
@series_error = /\w/, "wrong letter {0} in place of {1}"
@series_error = "fallback error message"
series = "X" | head §"C" "D"
head = "A" "B"
"""
parser = grammar_provider(lang)()
st = parser("AB*D"); assert st.error_flag
assert st.collect_errors()[0].code == Error.MANDATORY_CONTINUATION
assert st.collect_errors()[0].message == "the asterix is wrong in this place"
# transitivity of mandatory-operator
st = parser("ABC_"); assert st.error_flag
assert st.collect_errors()[0].code == Error.MANDATORY_CONTINUATION
assert st.collect_errors()[0].message == "the underscore is wrong in this place"
st = parser("ABiD"); assert st.error_flag
assert st.collect_errors()[0].code == Error.MANDATORY_CONTINUATION
assert st.collect_errors()[0].message.startswith('wrong letter')
st = parser("AB+D"); assert st.error_flag
assert st.collect_errors()[0].code == Error.MANDATORY_CONTINUATION
assert st.collect_errors()[0].message == "fallback error message"
class TestErrorCustomizationErrors:
def test_ambiguous_error_customization(self):
lang = """
document = series
@series_error = "ambiguous error message: does it apply to 'one' or 'two'?"
@series_error = "ambiguous error message: does it apply to first or second '§'?"
series = "A" § "B" "C" | "X" § "Y" "Z"
"""
try:
......@@ -518,6 +545,26 @@ class TestErrorCustomization:
result, messages, ast = compile_ebnf(lang)
assert messages[0].code == Error.UNUSED_ERROR_HANDLING_WARNING
def test_multiple_resume_definitions(self):
lang = """
document = series
@series_resume = /B/, /C/, /D/, /E/, /F/, /G/
@series_resume = /X/, /Y/
series = "A" §"B" "C" "D" "E" "F" "G"
"""
result, messages, ast = compile_ebnf(lang)
assert messages[0].code == Error.REDEFINED_DIRECTIVE
def test_multiple_skip_definitions(self):
lang = """
document = series
@series_skip = /B/, /C/, /D/, /E/, /F/, /G/
@series_skip = /X/, /Y/
series = "A" §"B" "C" "D" "E" "F" "G"
"""
result, messages, ast = compile_ebnf(lang)
assert messages[0].code == Error.REDEFINED_DIRECTIVE
class TestCustomizedResumeParsing:
def setup(self):
......
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