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

Commit 447927f6 authored by di68kap's avatar di68kap

- errors in user configured error messages are now caught and properly reported

parent 333f4a9a
......@@ -83,6 +83,7 @@ class Error:
PARSER_LOOKAHEAD_MATCH_ONLY = ErrorCode(1003)
PARSER_STOPPED_BEFORE_END = ErrorCode(1004)
CAPTURE_STACK_NOT_EMPTY = ErrorCode(1005)
MALFORMED_ERROR_STRING = ErrorCode(1006)
def __init__(self, message: str, pos, code: ErrorCode = ERROR,
orig_pos: int = -1, line: int = -1, column: int = -1) -> None:
......
......@@ -1424,8 +1424,14 @@ class Series(NaryOperator):
for search, message in self.err_msgs:
rxs = not isinstance(search, str)
if rxs and text_.match(search) or not rxs and text_.startswith(search):
msg = message.format(parser.repr, found)
break
try:
msg = message.format(parser.repr, found)
break
except (ValueError, KeyError, IndexError) as e:
error = Error("Malformed error format string '{}' lead to '{}'"
.format(message, str(e)),
location, Error.MALFORMED_ERROR_STRING)
self.grammar.tree__.add_error(node, error)
else:
msg = '%s expected, "%s" found!' % (parser.repr, found)
mandatory_violation = Error(msg, location, Error.MANDATORY_CONTINUATION)
......
......@@ -168,6 +168,7 @@ class StringView: # collections.abc.Sized
"""Returns the underlying string."""
return self._text
@cython.locals(_start=cython.int, _end=cython.int)
def count(self, sub: str, start: Optional[int] = None, end: Optional[int] = None) -> int:
"""Returns the number of non-overlapping occurrences of substring
`sub` in StringView S[start:end]. Optional arguments start and end
......@@ -181,8 +182,8 @@ class StringView: # collections.abc.Sized
elif start is None and end is None:
return self._text.count(sub, self._begin, self._end)
else:
start, end = real_indices(start, end, self._len)
return self._text.count(sub, self._begin + start, self._begin + end)
_start, _end = real_indices(start, end, self._len)
return self._text.count(sub, self._begin + _start, self._begin + _end)
@cython.locals(_start=cython.int, _end=cython.int)
def find(self, sub: str, start: Optional[int] = None, end: Optional[int] = None) -> int:
......@@ -202,6 +203,7 @@ class StringView: # collections.abc.Sized
_start, _end = real_indices(start, end, self._len)
return self._text.find(sub, self._begin + _start, self._begin + _end) - self._begin
@cython.locals(_start=cython.int, _end=cython.int)
def rfind(self, sub: str, start: Optional[int] = None, end: Optional[int] = None) -> int:
"""Returns the highest index in S where substring `sub` is found,
such that `sub` is contained within S[start:end]. Optional
......@@ -216,8 +218,8 @@ class StringView: # collections.abc.Sized
if start is None and end is None:
return self._text.rfind(sub, self._begin, self._end) - self._begin
else:
start, end = real_indices(start, end, self._len)
return self._text.rfind(sub, self._begin + start, self._begin + end) - self._begin
_start, _end = real_indices(start, end, self._len)
return self._text.rfind(sub, self._begin + _start, self._begin + _end) - self._begin
def startswith(self,
prefix: str,
......@@ -231,6 +233,7 @@ class StringView: # collections.abc.Sized
end = self._end if end is None else self._begin + end
return self._text.startswith(prefix, start, end)
def endswith(self,
suffix: str,
start: int = 0,
......@@ -286,6 +289,7 @@ class StringView: # collections.abc.Sized
"""
return regex.finditer(self._text, pos=self._begin, endpos=self._end)
@cython.locals(begin=cython.int, end=cython.int)
def strip(self):
"""Returns a copy of the StringView `self` with leading and trailing
whitespace removed.
......@@ -294,16 +298,19 @@ class StringView: # collections.abc.Sized
end = last_char(self._text, self._begin, self._end) - self._begin
return self if begin == 0 and end == self._len else self[begin:end]
@cython.locals(begin=cython.int)
def lstrip(self):
"""Returns a copy of `self` with leading whitespace removed."""
begin = first_char(self._text, self._begin, self._end) - self._begin
return self if begin == 0 else self[begin:]
@cython.locals(end=cython.int)
def rstrip(self):
"""Returns a copy of `self` with trailing whitespace removed."""
end = last_char(self._text, self._begin, self._end) - self._begin
return self if end == self._len else self[:end]
@cython.locals(length=cython.int, i=cython.int, k=cython.int)
def split(self, sep=None):
"""Returns a list of the words in `self`, using `sep` as the
delimiter string. If `sep` is not specified or is None, any
......
......@@ -29,9 +29,7 @@ try:
except ImportError:
import re
from DHParser.error import linebreaks, line_col, Error
from DHParser.dsl import grammar_provider, CompilationError
from DHParser.error import linebreaks, line_col
class TestErrorSupport:
def mini_suite(self, s, lbreaks, offset):
......
......@@ -728,6 +728,21 @@ class TestReentryAfterError:
assert len(cst.collect_errors()) == 2
class TestConfiguredErrorMessages:
def test_(self):
lang = """
document = series | /.*/
@series_error = "a badly configured error message {5}"
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.MALFORMED_ERROR_STRING
assert st.collect_errors()[1].code == Error.MANDATORY_CONTINUATION
# print(st.collect_errors())
class TestUnknownParserError:
def test_unknown_parser_error(self):
gr = Grammar()
......@@ -738,6 +753,7 @@ class TestUnknownParserError:
pass
if __name__ == "__main__":
from DHParser.testing import runner
with logging(False):
......
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