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

- syntaxtree.py: error propagation added

parent 90bb9074
......@@ -782,10 +782,16 @@ class Grammar:
self.tree__.swallow(result)
return self.tree__
def add_error(self, text, error_msg, code=Error.ERROR):
"""Adds an error at the location of `text` whithin the whole document that is
def location(self, remaining: str) -> int:
"""Returns the location of the `remaining` text within the currently
parsed document.
"""
self.document_length__ - len(remaining)
def add_error(self, location, error_msg, code=Error.ERROR):
"""Adds an error at the location of `text` within the whole document that is
currently being parsed."""
self.tree__.add_errr(self.document_length__ - len(text), error_msg, code)
self.tree__.add_error(location, error_msg, code)
def push_rollback__(self, location, func):
"""
......@@ -863,18 +869,18 @@ class PreprocessorToken(Parser):
if text[0:1] == BEGIN_TOKEN:
end = text.find(END_TOKEN, 1)
if end < 0:
self.grammar.add_error(text,
self.grammar.add_error(self.grammar.location(text),
'END_TOKEN delimiter missing from preprocessor token. '
'(Most likely due to a preprocessor bug!)') # type: Node
return Node(self, ''), text[1:]
elif end == 0:
self.grammar.add_error(text,
self.grammar.add_error(self.grammar.location(text),
'Preprocessor-token cannot have zero length. '
'(Most likely due to a preprocessor bug!)')
return Node(self, ''), text[2:]
elif text.find(BEGIN_TOKEN, 1, end) >= 0:
node = Node(self, text[len(self.name) + 1:end])
self.grammar.add_error(text,
self.grammar.add_error(self.grammar.location(text),
'Preprocessor-tokens must not be nested or contain '
'BEGIN_TOKEN delimiter as part of their argument. '
'(Most likely due to a preprocessor bug!)')
......@@ -1256,7 +1262,8 @@ class ZeroOrMore(Option):
if not node:
break
if len(text) == n:
self.grammar.add_error(text, dsl_error_msg(self, 'Infinite Loop detected.'))
self.grammar.add_error(self.grammar.location(text),
dsl_error_msg(self, 'Infinite Loop detected.'))
results += (node,)
return Node(self, results), text
......@@ -1300,7 +1307,8 @@ class OneOrMore(UnaryOperator):
if not node:
break
if len(text_) == n:
self.grammar.add_error(text, dsl_error_msg(self, 'Infinite Loop detected.'))
self.grammar.add_error(self.grammar.location(text),
dsl_error_msg(self, 'Infinite Loop detected.'))
results += (node,)
if results == ():
return None, text
......@@ -1360,7 +1368,7 @@ class Series(NaryOperator):
i = max(1, text.index(match.regs[1][0])) if match else 1
node = Node(self, text_[:i]).init_pos(self.grammar.document_length__
- len(text_))
self.grammar.add_error(text, '%s expected; "%s" found!'
self.grammar.add_error(self.grammar.location(text), '%s expected; "%s" found!'
% (parser.repr, text_[:10].replace('\n', '\\n ')),
code=Error.MANDATORY_CONTINUATION)
text_ = text_[i:]
......@@ -1629,7 +1637,8 @@ def Required(parser: Parser) -> Parser:
# i = max(1, text.index(m.regs[1][0])) if m else 1
# node = Node(self, text[:i])
# text_ = text[i:]
# self.grammar.add_error(text, '%s expected; "%s" found!' % (str(self.parser),
# self.grammar.add_error(self.grammar.location(text),
# '%s expected; "%s" found!' % (str(self.parser),
# text[:10]), code=Error.MANDATORY_CONTINUATION)
# return node, text_
#
......@@ -1803,7 +1812,7 @@ class Retrieve(Parser):
stack = self.grammar.variables__[self.symbol.name]
value = self.filter(stack)
except (KeyError, IndexError):
self.grammar.add_error(text,
self.grammar.add_error(self.grammar.location(text),
dsl_error_msg(self, "'%s' undefined or exhausted." % self.symbol.name))
return Node(self, ''), text
if text.startswith(value):
......
......@@ -228,22 +228,22 @@ class Node(collections.abc.Sized):
At any rate, it should only be reassigned during the parsing
stage and never during or after the AST-transformation.
errors (list): A list of all errors that occured on this node.
attributes (dict): An optional dictionary of XML-attributes. This
dictionary is created lazily upon first usage. The attributes
will only be shown in the XML-Representation, not in the
S-Expression-output.
_parent (Node): SLOT RESERVED FOR FUTURE USE!
"""
__slots__ = ['_result', 'children', '_len', '_pos', 'parser', '_xml_attr', '_content']
__slots__ = ['_result', 'children', '_len', '_pos', 'parser', 'errors' '_xml_attr', '_content']
def __init__(self, parser, result: ResultType, leafhint: bool = False) -> None:
"""
Initializes the ``Node``-object with the ``Parser``-Instance
that generated the node and the parser's result.
"""
self.errors = [] # type: List[Error]
self._pos = -1 # type: int
# Assignment to self.result initializes the attributes _result, children and _len
# The following if-clause is merely an optimization, i.e. a fast-path for leaf-Nodes
......@@ -259,9 +259,9 @@ class Node(collections.abc.Sized):
def __str__(self):
s = "".join(str(child) for child in self.children) if self.children else self.content
if self._errors:
if self.errors:
return ' <<< Error on "%s" | %s >>> ' % \
(s, '; '.join(e.message for e in self._errors))
(s, '; '.join(e.message for e in self.errors))
return s
......@@ -391,14 +391,10 @@ class Node(collections.abc.Sized):
if isinstance(result, Node):
self.children = (result,)
self._result = self.children
self.error_flag = result.error_flag
else:
if isinstance(result, tuple):
self.children = result
self._result = result or ''
if result:
if self.error_flag == 0:
self.error_flag = max(child.error_flag for child in self.children)
else:
self.children = NoChildren
self._result = result
......@@ -448,7 +444,7 @@ class Node(collections.abc.Sized):
"""
if overwrite or self._pos < 0:
self._pos = pos
for err in self._errors:
for err in self.errors:
err.pos = pos
else:
assert self._pos == pos, str("%i != %i" % (self._pos, pos))
......@@ -466,7 +462,7 @@ class Node(collections.abc.Sized):
# Returns the errors that occurred at this Node,
# not including any errors from child nodes.
# """
# return self._errors.copy()
# return self.errors.copy()
@property
def attributes(self):
......@@ -681,26 +677,50 @@ class Node(collections.abc.Sized):
class RootNode(Node):
"""
errors (list): A list of parser- or compiler-errors:
tuple(position, string) attached to this node
errors (list): A list of all errors that have occured so far during
processing (i.e. parsing, AST-transformation, compiling)
of this tree.
error_flag (int): 0 if no error occurred in either the node
itself or any of its descendants. Otherwise contains the
highest warning or error level or all errors that occurred.
error_flag (int): the highest warning or error level of all errors
that occurred.
"""
def __init__(self):
super().__init__(ZOMBIE_PARSER, '')
self.errors = []
self.error_flag = 0
def _propagate_errors(self):
assert self.children
if not self.errors:
return
self.errors.sort(key=lambda e: e.pos)
errlist = copy.copy(self.errors)
for leaf in self.select(lambda nd: not nd.children, False):
leaf.errors = []
while errlist and leaf.pos <= errlist[0].pos < leaf.pos + leaf.len:
leaf.errors.append(errlist[0])
del errlist[0]
if not errlist:
break
def _propagate_new_error(self, error):
for leaf in self.select(lambda nd: not nd.children, False):
if leaf.pos <= error.pos < leaf.pos + leaf.len:
leaf.errord.append(error)
break
else:
assert False, "Error %s at pos %i out of bounds" % (str(error), error.pos)
def swallow(self, node):
self._result = node._result
self.children = node.children
self._len = node._len
self._pos = node._pos
self.parser = node.parser
self._xml_attr = node._xml_attr
if hasattr(node, '_xml_attr'):
self._xml_attr = node._xml_attr
self._content = node._content
self._propagate_errors()
def add_error(self,
pos: int,
......@@ -713,8 +733,11 @@ class RootNode(Node):
message(str): A string with the error message.abs
code(int): An error code to identify the kind of error
"""
self.errors.append(Error(message, code, pos))
error = Error(message, code, pos)
self.errors.append(error)
self.error_flag = max(self.error_flag, code)
if self.children:
self._propagate_new_error(error)
return self
def collect_errors(self, clear_errors=False) -> List[Error]:
......
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