Commit 43cb9807 authored by Eckhart Arnold's avatar Eckhart Arnold

- error handling simplified

parent cc2ade10
...@@ -511,7 +511,7 @@ class EBNFCompiler(Compiler): ...@@ -511,7 +511,7 @@ class EBNFCompiler(Compiler):
if entry not in symbols and not entry.startswith(":"): if entry not in symbols and not entry.startswith(":"):
messages.append(Error(('Symbol "%s" is not defined in grammar %s but appears in ' messages.append(Error(('Symbol "%s" is not defined in grammar %s but appears in '
'the transformation table!') % (entry, self.grammar_name), 'the transformation table!') % (entry, self.grammar_name),
Error.UNDEFINED_SYMBOL_IN_TRANSFORMATION_TABLE, 0)) 0, Error.UNDEFINED_SYMBOL_IN_TRANSFORMATION_TABLE))
return messages return messages
......
...@@ -41,9 +41,10 @@ import bisect ...@@ -41,9 +41,10 @@ import bisect
from DHParser.preprocess import SourceMapFunc from DHParser.preprocess import SourceMapFunc
from DHParser.stringview import StringView from DHParser.stringview import StringView
from DHParser.toolkit import typing from DHParser.toolkit import typing
from typing import Iterable, Iterator, Union, Tuple, List, Optional from typing import Iterable, Iterator, Union, Tuple, List, NewType
__all__ = ('Error', __all__ = ('ErrorCode',
'Error',
'is_error', 'is_error',
'is_warning', 'is_warning',
'has_errors', 'has_errors',
...@@ -53,44 +54,44 @@ __all__ = ('Error', ...@@ -53,44 +54,44 @@ __all__ = ('Error',
'adjust_error_locations') 'adjust_error_locations')
class ErrorCode(int):
pass
class Error: class Error:
__slots__ = ['message', 'level', 'code', '_pos', 'orig_pos', 'line', 'column', '_node_keep'] __slots__ = ['message', 'level', 'code', '_pos', 'orig_pos', 'line', 'column', '_node_keep']
# error levels # error levels
NO_ERROR = 0 NO_ERROR = ErrorCode(0)
MESSAGE = 1 MESSAGE = ErrorCode(1)
WARNING = 10 WARNING = ErrorCode(10)
ERROR = 1000 ERROR = ErrorCode(1000)
HIGHEST = ERROR HIGHEST = ERROR
# warning codes # warning codes
REDEFINED_DIRECTIVE_WARNING = 101 REDEFINED_DIRECTIVE_WARNING = ErrorCode(101)
REDECLARED_TOKEN_WARNING = 102 REDECLARED_TOKEN_WARNING = ErrorCode(102)
UNDEFINED_SYMBOL_IN_TRANSFORMATION_TABLE = 601 UNDEFINED_SYMBOL_IN_TRANSFORMATION_TABLE = ErrorCode(601)
# error codes # error codes
MANDATORY_CONTINUATION = 1001 MANDATORY_CONTINUATION = ErrorCode(1001)
def __init__(self, message: str, code: int = ERROR, pos: int = -1, def __init__(self, message: str, pos, code: ErrorCode = ERROR,
orig_pos: int = -1, line: int = -1, column: int = -1, orig_pos: int = -1, line: int = -1, column: int = -1) -> None:
node: Optional['Node'] = None) -> None: assert isinstance(code, ErrorCode)
self.message = message assert not isinstance(pos, ErrorCode)
assert pos >= 0
assert code >= 0 assert code >= 0
self.code = code self.message = message
self._pos = pos self._pos = pos
self.code = code
self.orig_pos = orig_pos self.orig_pos = orig_pos
self.line = line self.line = line
self.column = column self.column = column
if node is not None and node._pos >= 0:
assert self._pos < 0 or self._pos == node._pos
self._pos = node._pos
self._node_keep = None # if node is not needed, if pos has been set
else:
self._node_keep = node # redundant: consider removing, see RootNode.collect_errors
def __str__(self): def __str__(self):
prefix = '' prefix = ''
...@@ -104,10 +105,6 @@ class Error: ...@@ -104,10 +105,6 @@ class Error:
@property @property
def pos(self): def pos(self):
if self._pos < 0:
assert self._node_keep and self._node_keep.pos >= 0, "pos value not ready yet"
self._pos = self._node_keep.pos # lazy evaluation of position
self._node_keep = None # forget node to allow GC to free memory
return self._pos return self._pos
@property @property
......
...@@ -1258,9 +1258,9 @@ class ZeroOrMore(Option): ...@@ -1258,9 +1258,9 @@ class ZeroOrMore(Option):
if not node: if not node:
break break
if len(text) == n: if len(text) == n:
node.errors.append(Error("Infinite loop!", Error.MESSAGE, node=node)) node.errors.append(Error("Infinite loop!", node.pos, Error.MESSAGE))
infinite_loop_error = Error(dsl_error_msg(self, 'Infinite Loop encountered.'), infinite_loop_error = Error(dsl_error_msg(self, 'Infinite Loop encountered.'),
node=node) node.pos)
results += (node,) results += (node,)
node = Node(self, results) node = Node(self, results)
if infinite_loop_error: if infinite_loop_error:
...@@ -1308,9 +1308,9 @@ class OneOrMore(UnaryOperator): ...@@ -1308,9 +1308,9 @@ class OneOrMore(UnaryOperator):
if not node: if not node:
break break
if len(text_) == n: if len(text_) == n:
node.errors.append(Error("Infinite loop!", Error.MESSAGE, node=node)) node.errors.append(Error("Infinite loop!", node.pos, Error.MESSAGE))
infinite_loop_error = Error(dsl_error_msg(self, 'Infinite Loop encountered.'), infinite_loop_error = Error(dsl_error_msg(self, 'Infinite Loop encountered.'),
node=node) node.pos)
results += (node,) results += (node,)
if results == (): if results == ():
return None, text return None, text
...@@ -1375,11 +1375,11 @@ class Series(NaryOperator): ...@@ -1375,11 +1375,11 @@ class Series(NaryOperator):
location = self.grammar.document_length__ - len(text_) location = self.grammar.document_length__ - len(text_)
node = Node(self, text_[:i]).init_pos(location) node = Node(self, text_[:i]).init_pos(location)
node.errors.append(Error("§ %s violation" % parser.repr, node.errors.append(Error("§ %s violation" % parser.repr,
Error.MESSAGE, location)) location, Error.MESSAGE))
if not mandatory_violation: if not mandatory_violation:
msg = '%s expected, "%s" found!' \ msg = '%s expected, "%s" found!' \
% (parser.repr, text_[:10].replace('\n', '\\n ')) % (parser.repr, text_[:10].replace('\n', '\\n '))
mandatory_violation = Error(msg, Error.MANDATORY_CONTINUATION, location) mandatory_violation = Error(msg, location, Error.MANDATORY_CONTINUATION)
text_ = text_[i:] text_ = text_[i:]
results += (node,) results += (node,)
# if node.error_flag: # break on first error # if node.error_flag: # break on first error
......
...@@ -274,7 +274,9 @@ class Node(collections.abc.Sized): ...@@ -274,7 +274,9 @@ class Node(collections.abc.Sized):
def __str__(self): def __str__(self):
s = "".join(str(child) for child in self.children) if self.children else self.content # s = self._content if self._content is not None else \
# "".join(str(child) for child in self.children) if self.children else self.content
s = self.content
if self.errors: if self.errors:
return ' <<< Error on "%s" | %s >>> ' % \ return ' <<< Error on "%s" | %s >>> ' % \
(s, '; '.join(e.message for e in self.errors)) (s, '; '.join(e.message for e in self.errors))
...@@ -548,8 +550,6 @@ class Node(collections.abc.Sized): ...@@ -548,8 +550,6 @@ class Node(collections.abc.Sized):
txt.extend(' `(%s "%s")' % (k, v) for k, v in node.attributes.items()) txt.extend(' `(%s "%s")' % (k, v) for k, v in node.attributes.items())
if src: if src:
txt.append(" `(pos %i %i %i)" % (node.pos, *line_col(lbreaks, node.pos))) txt.append(" `(pos %i %i %i)" % (node.pos, *line_col(lbreaks, node.pos)))
# if node.error_flag: # just for debugging error collecting
# txt += " HAS ERRORS"
if showerrors and node.errors: if showerrors and node.errors:
txt.append(" `(err `%s)" % ' '.join(str(err) for err in node.errors)) txt.append(" `(err `%s)" % ' '.join(str(err) for err in node.errors))
return "".join(txt) + '\n' return "".join(txt) + '\n'
...@@ -568,7 +568,7 @@ class Node(collections.abc.Sized): ...@@ -568,7 +568,7 @@ class Node(collections.abc.Sized):
def as_xml(self, src: str = None, showerrors: bool = True, indentation: int = 2, def as_xml(self, src: str = None, showerrors: bool = True, indentation: int = 2,
inline_tags: Set[str]=set(), omit_tags: Set[str]=set(), ) -> str: inline_tags: Set[str]=set(), omit_tags: Set[str]=set()) -> str:
""" """
Returns content as XML-tree. Returns content as XML-tree.
...@@ -703,7 +703,7 @@ class Node(collections.abc.Sized): ...@@ -703,7 +703,7 @@ class Node(collections.abc.Sized):
class RootNode(Node): class RootNode(Node):
"""TODO: Add Documentation!!! """TODO: Add Documentation!!!
errors (list): A list of all errors that have occured so far during all_errors (list): A list of all errors that have occured so far during
processing (i.e. parsing, AST-transformation, compiling) processing (i.e. parsing, AST-transformation, compiling)
of this tree. of this tree.
...@@ -713,7 +713,6 @@ class RootNode(Node): ...@@ -713,7 +713,6 @@ class RootNode(Node):
def __init__(self, node: Optional[Node] = None) -> 'RootNode': def __init__(self, node: Optional[Node] = None) -> 'RootNode':
super().__init__(ZOMBIE_PARSER, '') super().__init__(ZOMBIE_PARSER, '')
self.all_errors = [] self.all_errors = []
self.err_nodes_keep = []
self.error_flag = 0 self.error_flag = 0
if node is not None: if node is not None:
self.swallow(node) self.swallow(node)
...@@ -744,7 +743,6 @@ class RootNode(Node): ...@@ -744,7 +743,6 @@ class RootNode(Node):
self.all_errors.append(error) self.all_errors.append(error)
self.error_flag = max(self.error_flag, error.code) self.error_flag = max(self.error_flag, error.code)
node.errors.append(error) node.errors.append(error)
self.err_nodes_keep.append(node)
return self return self
def new_error(self, def new_error(self,
...@@ -758,27 +756,15 @@ class RootNode(Node): ...@@ -758,27 +756,15 @@ class RootNode(Node):
message(str): A string with the error message.abs message(str): A string with the error message.abs
code(int): An error code to identify the kind of error code(int): An error code to identify the kind of error
""" """
error = Error(message, code, node=node) error = Error(message, node.pos, code)
self.add_error(node, error) self.add_error(node, error)
return self return self
def collect_errors(self) -> List[Error]: def collect_errors(self) -> List[Error]:
"""Returns the list of errors, ordered bv their position. """Returns the list of errors, ordered bv their position.
""" """
# for node in self.err_nodes: # lazy evaluation of positions
# for err in node.errors: # moved to error.Error.pos
# err.pos = node.pos
self.all_errors.sort(key=lambda e: e.pos) self.all_errors.sort(key=lambda e: e.pos)
for node in self.err_nodes_keep: # redundant: consider removing Error.Error._node_keep return self.all_errors
for error in node.errors:
assert error._pos < 0 or node.pos <= error._pos <= node.pos + len(node)
if error._pos < 0:
error._pos = node.pos
self.err_nodes_keep = []
errors = self.all_errors
# for error in self.all_errors:
# _ = error.pos
return errors
ZOMBIE_NODE = Node(ZOMBIE_PARSER, '') ZOMBIE_NODE = Node(ZOMBIE_PARSER, '')
......
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