Improve error message and span calculation.

Change-Id: I2c319e97329bd31eae875ab48eae0476bbf979e2
This commit is contained in:
Joan Varvenne 2016-09-21 15:15:20 +01:00
parent d543008431
commit 8313692991
5 changed files with 92 additions and 48 deletions

View File

@ -74,17 +74,21 @@ class JsonEmitter(Emitter):
}
def emit_error(self, span, message):
self.result["errors"].append({
"line": span.get_lineno(),
"col": 0,
"byteRange": [span.lo, span.hi],
"message": message
})
error = JsonEmitter._gen_message_structure(span, message)
self.result["errors"].append(error)
def emit_warning(self, span, message):
self.result["warnings"].append({
"line": span.get_lineno(),
"col": 0,
warning = JsonEmitter._gen_message_structure(span, message)
self.result["warnings"].append(warning)
@staticmethod
def _gen_message_structure(span, message):
spanrange = span.get_range()
return {
"startLineNumber": spanrange[0][0],
"startColumn": spanrange[0][1],
"endLineNumber": spanrange[1][0],
"endColumn": spanrange[1][1],
"byteRange": [span.lo, span.hi],
"message": message
})
}

View File

@ -128,10 +128,25 @@ class BananaFile(object):
def make_span(s, l, t):
def compute_hi(init_loc, tokens):
hi = init_loc
for tok in tokens:
if isinstance(tok, ASTNode):
hi = max(hi, tok.span.hi)
elif isinstance(tok, basestring):
hi += len(tok)
elif isinstance(tok, p.ParseResults):
hi = max(hi, compute_hi(init_loc, tok))
else:
raise exception.BananaGrammarBug(
"Couldn't create span for: {}".format(tok)
)
return hi
if len(t) > 0:
if isinstance(t[0], ASTNode):
return Span(s, l, t[0].span.hi)
return Span(s, l, len(t[0]) + l)
span_hi = compute_hi(l, t)
return Span(s, l, span_hi)
else:
return Span(s, l, 2)
@ -238,7 +253,7 @@ class DotPath(ASTNode):
:return: Returns the next dot path.
"""
return DotPath(
self.span.new_with_offset(len(self.varname.val)),
self.span.new_with_lo(self.properties[0].span.lo),
self.properties[0],
self.properties[1:]
)
@ -415,6 +430,8 @@ class Connection(ASTNode):
:type emitter: emit.Emitter
:param emitter: Emitter.
"""
self.span.hi = max(other_con.span.hi, self.span.hi)
self.span.lo = min(other_con.span.lo, self.span.lo)
old_outputs = self.outputs
self.outputs = other_con.outputs

View File

@ -70,17 +70,17 @@ class Span(object):
else:
return '?SPAN?'
def new_with_offset(self, offset):
def new_with_lo(self, lo_val):
"""
Construct a new Span with an offset applied
to lo.
Construct a new Span with an new value for
lo.
:type offset: int
:param offset: Offset to apply to lo.
:type lo_val: int
:param lo_val: New value for lo.
:rtype: Span
:return: Returns a new span
"""
return Span(self._text, self.lo + offset, self.hi)
return Span(self._text, lo_val, self.hi)
def str_from_to(self, to_span):
"""
@ -107,6 +107,30 @@ class Span(object):
else:
return '?LINE?'
def get_range(self):
"""
Returns the start and end (line number, column number) of this span.
"""
if self._text is not None:
splitted = self._text.splitlines()
current_pos = 0
startlineno = 0
startcolno = 0
endlineno = 0
endcolno = 0
for lineno in xrange(0, len(splitted)):
line = splitted[lineno]
if current_pos <= self.lo <= len(line) + current_pos:
startlineno = lineno + 1
startcolno = self.lo - current_pos + 1
if current_pos <= self.hi <= len(line) + current_pos:
endlineno = lineno + 1
endcolno = self.hi - current_pos + 1
current_pos += len(line) + 1
return (startlineno, startcolno), (endlineno, endcolno)
else:
return (0, 0), (0, 0)
def get_lineno(self):
"""
Returns the line number of this span.

View File

@ -55,5 +55,6 @@ def typeck_connections(connection, type_table):
possible_types = map(lambda x: x.__name__,
valid_connections_types[type(type_from)])
raise exception.BananaConnectionError(
connection.span,
ident_from, ident_to, type_from, possible_types
)

View File

@ -77,11 +77,10 @@ class BananaArgumentTypeError(BananaException):
def __init__(self, where, expected_type, received_type):
if isinstance(where, ast.ASTNode):
self._span = where.span
where = where.span
else:
self._span = where
self._value = "'{}': Wrong type of argument. Expected '{}' got '{}'"\
.format(where.get_line(), expected_type, received_type)
self._value = "Wrong type of argument. Expected '{}' got '{}'."\
.format(expected_type, received_type)
def __str__(self):
return self._value
@ -93,7 +92,7 @@ class BananaArgumentTypeError(BananaException):
class BananaComponentTooManyParams(BananaException):
def __init__(self, span):
self._span = span
self._value = "Too many params provided to '{}' (line {})".format(
self._value = "Too many params provided to '{}'.".format(
span, span.get_lineno()
)
@ -145,7 +144,7 @@ class BananaComponentAlreadyDefined(BananaException):
def __init__(self, first_def, second_def):
self._value = "Component already defined!\n" \
" First definition: '{}'\n" \
" Second definition: '{}'"\
" Second definition: '{}'."\
.format(first_def, second_def)
def __str__(self):
@ -171,7 +170,7 @@ class BananaShadowingComponentError(BananaException):
class BananaAssignmentError(BananaException):
def __init__(self, lhs, rhs):
self._value = "You can't assign '{}' to '{}'".format(lhs, rhs)
self._value = "You can't assign '{}' to '{}'.".format(lhs, rhs)
def __str__(self):
return self._value
@ -196,7 +195,7 @@ class BananaGrammarBug(BananaException, p.ParseFatalException):
class BananaJsonObjShadowingError(BananaException, p.ParseFatalException):
def __init__(self, span, error):
self._span = span
error = "Can't shadow property already defined in {}".format(error)
error = "Can't shadow property already defined in {}.".format(error)
super(BananaJsonObjShadowingError, self).__init__(pstr=error)
def __str__(self):
@ -233,7 +232,7 @@ class BananaEvalBug(BananaException):
class BananaUnknown(BananaException):
def __init__(self, ident):
self._span = ident.span
self._value = "Unknown '{}'".format(
self._value = "Unknown '{}'.".format(
ident.into_unmodified_str()
)
@ -247,7 +246,7 @@ class BananaUnknown(BananaException):
class BananaUnknownOperator(BananaException):
def __init__(self, span, operator, for_type):
self._span = span
self._value = "Unknown operator '{}' for type '{}'".format(
self._value = "Unknown operator '{}' for type '{}'.".format(
operator,
for_type
)
@ -263,17 +262,15 @@ class BananaPropertyDoesNotExists(BananaException):
def __init__(self, dotpath, on_type=None):
self._span = dotpath.span
if on_type is None:
self._value = "Error at '{}': Property '{}' " \
"does not exists"\
self._value = "Property '{}' " \
"does not exists."\
.format(
dotpath.span.get_line(),
dotpath.into_unmodified_str()
)
else:
self._value = "Error at '{}': Property '{}' " \
"does not exists on type '{}'"\
self._value = "Property '{}' " \
"does not exists on type '{}'."\
.format(
dotpath.span.get_line(),
dotpath.into_unmodified_str(),
str(on_type)
)
@ -296,21 +293,21 @@ class BananaTypeError(BananaException):
if found_type is None:
if isinstance(expected_type, list):
self._value = "Type error found. Expected" \
" one among '{}'"\
" one among '{}'."\
.format(', '.join(map(lambda x: str(x), expected_type)))
else:
self._value = "Type error found. Expected '{}'".format(
self._value = "Type error found. Expected '{}'.".format(
str(expected_type)
)
else:
if isinstance(expected_type, list):
self._value = "Type error found. Expected" \
" one among '{}', found '{}'"\
" one among '{}', found '{}'."\
.format(', '.join(map(lambda x: str(x), expected_type)),
str(found_type))
else:
self._value = "Type error found. Expected" \
" '{}', found '{}'"\
" '{}', found '{}'."\
.format(str(expected_type), str(found_type))
def __str__(self):
@ -327,7 +324,7 @@ class BananaAssignCompError(BananaException):
self._span = span
self._value = "Component objects " \
"can't be assigned to " \
"properties of other objects"
"properties of other objects."
def __str__(self):
return self._value
@ -338,15 +335,16 @@ class BananaAssignCompError(BananaException):
class BananaConnectionError(BananaException):
def __init__(self, ident_from, ident_to, type_from, possible_connections):
self._span = ident_to.span
self._value = "Can't connect '{}' (line:{})" \
" to '{}' (line:{})," \
" '{}' can only be connected to {}"\
def __init__(self, span, ident_from, ident_to, type_from,
possible_connections):
self._value = "Can't connect '{}'" \
" to '{}'," \
" '{}' can only be connected to a {}."\
.format(
ident_from.val, ident_from.span.get_lineno(),
ident_to.val, ident_to.span.get_lineno(),
type_from, possible_connections)
ident_from.val,
ident_to.val,
type_from.class_name, ' or a '.join(possible_connections))
self._span = span
def __str__(self):
return self._value