Skip to content

Commit

Permalink
Add more accurate error reporting
Browse files Browse the repository at this point in the history
  • Loading branch information
dwayne committed Sep 2, 2016
1 parent 3fea257 commit 830e9ed
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 20 deletions.
17 changes: 17 additions & 0 deletions tests/test_parser.py
Expand Up @@ -276,6 +276,16 @@ def test_it_must_be_lf_terminated(self):
with self.assertRaisesRegex(ParseError, 'expected the number to be LF terminated'):
parser.parse(src)

def test_it_pinpoints_the_error(self):
with self.assertRaisesRegex(ParseError, 'line 0, column 1 to the end'):
parser.parse(' ')

with self.assertRaisesRegex(ParseError, 'line 0, column 3 to the end'):
parser.parse(' \n')

with self.assertRaisesRegex(ParseError, 'line 0, column 3 to the end'):
parser.parse(' \t')


class TestLabelParsing(unittest.TestCase):
def test_it_parses_lf_terminated_spaces_tabs(self):
Expand All @@ -291,3 +301,10 @@ def test_it_must_be_non_empty(self):
def test_it_must_be_lf_terminated(self):
with self.assertRaisesRegex(ParseError, 'expected the label to be LF terminated'):
parser.parse('\n ')

def test_it_pinpoints_the_error(self):
with self.assertRaisesRegex(ParseError, 'line 1, column 2 to the end'):
parser.parse('\n \n')

with self.assertRaisesRegex(ParseError, 'line 1, column 2 to the end'):
parser.parse('\n ')
59 changes: 39 additions & 20 deletions whitespace/parser.py
@@ -1,9 +1,3 @@
# TODO:
#
# 1. Consider adding a _capture_substart so that we can more accurately
# determine the location of the parse error. The error message line info would
# be closer to the source of the problem.

from collections import namedtuple

from .error import ParseError
Expand Down Expand Up @@ -39,6 +33,10 @@ def parse(self, src):
self._start_line = 0
self._start_column = -1

self._substart_index = -1
self._substart_line = 0
self._substart_column = -1

self._next_line = False

return self._parse_start()
Expand Down Expand Up @@ -191,6 +189,8 @@ def _parse_number(self):
def _parse_sign(self):
t = self._next_token()

self._capture_substart()

if t == space:
return 1
elif t == tab:
Expand All @@ -199,7 +199,11 @@ def _parse_sign(self):
raise self._parse_error('expected a sign')

def _parse_positive_number(self):
return self._parse_positive_number_rec(0, 0, self._next_token())
t = self._next_token()

self._capture_substart()

return self._parse_positive_number_rec(0, 0, t)

def _parse_positive_number_rec(self, n, l, t):
if t == space:
Expand All @@ -215,7 +219,11 @@ def _parse_positive_number_rec(self, n, l, t):
raise self._parse_error('expected the number to be LF terminated')

def _parse_label(self):
return self._parse_label_rec('', 0, self._next_token())
t = self._next_token()

self._capture_substart()

return self._parse_label_rec('', 0, t)

def _parse_label_rec(self, name, l, t):
if t == space or t == tab:
Expand All @@ -229,6 +237,9 @@ def _parse_label_rec(self, name, l, t):
raise self._parse_error('expected the label to be LF terminated')

def _next_token(self):
if self._current_index + 1 == self._src_length:
return None

if self._next_line:
self._next_line = False
self._current_line += 1
Expand All @@ -239,6 +250,9 @@ def _next_token(self):
c = self._c()

while c and c not in tokens:
if self._current_index + 1 == self._src_length:
return None

self._current_index += 1
self._current_column += 1
c = self._c()
Expand All @@ -249,16 +263,20 @@ def _next_token(self):
return c

def _c(self):
if self._current_index < self._src_length:
return self._src[self._current_index]
else:
return None
return self._src[self._current_index]

def _capture_start(self):
self._start_index = self._current_index
self._start_line = self._current_line
self._start_column = self._current_column

self._capture_substart()

def _capture_substart(self):
self._substart_index = self._current_index
self._substart_line = self._current_line
self._substart_column = self._current_column

def _capture_instruction_and_continue(self, instruction):
instruction.source_location = SourceLocation(
self._start_index, self._start_line, self._start_column,
Expand All @@ -270,14 +288,15 @@ def _capture_instruction_and_continue(self, instruction):
return self._parse_start()

def _parse_error(self, message):
if self._current_index < self._src_length:
line_info = 'line {}, column {} to line {}, column {}'.format(
self._start_line, self._start_column,
self._current_line, self._current_column
)
line_from_info = 'see from line {}, column {}'.format(
self._substart_line, self._substart_column
)

if self._current_index + 1 == self._src_length:
line_to_info = 'to the end'
else:
line_info = 'line {}, column {} to the end'.format(
self._start_line, self._start_column
line_to_info = 'to line {}, column {}'.format(
self._current_line, self._current_column
)

return ParseError('{}, see from {}'.format(message, line_info))
return ParseError('{}, {} {}'.format(message, line_from_info, line_to_info))

0 comments on commit 830e9ed

Please sign in to comment.