1
0
mirror of https://github.com/catseye/SixtyPical.git synced 2025-01-10 02:29:23 +00:00

Show line numbers in static analysis errors (clumsily.)

This commit is contained in:
Chris Pressey 2018-03-06 13:13:18 +00:00
parent e44c802314
commit 42cbd73a20

View File

@ -10,7 +10,11 @@ from sixtypical.model import (
class StaticAnalysisError(ValueError):
pass
def __init__(self, line_number, message):
super(StaticAnalysisError, self).__init__(line_number, message)
def __str__(self):
return "{} (Line {})".format(self.args[1], self.args[0])
class UnmeaningfulReadError(StaticAnalysisError):
@ -94,19 +98,19 @@ class Context(object):
for ref in inputs:
if ref.is_constant():
raise ConstantConstraintError('%s in %s' % (ref.name, routine.name))
raise ConstantConstraintError(self.routine.line_number, '%s in %s' % (ref.name, routine.name))
self._range[ref] = ref.max_range()
output_names = set()
for ref in outputs:
if ref.is_constant():
raise ConstantConstraintError('%s in %s' % (ref.name, routine.name))
raise ConstantConstraintError(self.routine.line_number, '%s in %s' % (ref.name, routine.name))
output_names.add(ref.name)
self._writeable.add(ref)
for ref in trashes:
if ref.is_constant():
raise ConstantConstraintError('%s in %s' % (ref.name, routine.name))
raise ConstantConstraintError(self.routine.line_number, '%s in %s' % (ref.name, routine.name))
if ref.name in output_names:
raise InconsistentConstraintsError('%s in %s' % (ref.name, routine.name))
raise InconsistentConstraintsError(self.routine.line_number, '%s in %s' % (ref.name, routine.name))
self._writeable.add(ref)
def __str__(self):
@ -142,7 +146,7 @@ class Context(object):
message = '%s in %s' % (ref.name, self.routine.name)
if kwargs.get('message'):
message += ' (%s)' % kwargs['message']
raise exception_class(message)
raise exception_class(self.routine.line_number, message)
elif isinstance(ref, IndexedRef):
self.assert_meaningful(ref.ref, **kwargs)
self.assert_meaningful(ref.index, **kwargs)
@ -159,7 +163,7 @@ class Context(object):
message = '%s in %s' % (ref.name, self.routine.name)
if kwargs.get('message'):
message += ' (%s)' % kwargs['message']
raise exception_class(message)
raise exception_class(self.routine.line_number, message)
def assert_in_range(self, inside, outside):
# FIXME there's a bit of I'm-not-sure-the-best-way-to-do-this-ness, here...
@ -176,7 +180,7 @@ class Context(object):
outside_range = (0, outside.type.size-1)
if inside_range[0] < outside_range[0] or inside_range[1] > outside_range[1]:
raise RangeExceededError(
raise RangeExceededError(self.routine.line_number,
"Possible range of {} {} exceeds acceptable range of {} {}".format(
inside, inside_range, outside, outside_range
)
@ -236,7 +240,7 @@ class Analyzer(object):
def assert_type(self, type, *locations):
for location in locations:
if location.type != type:
raise TypeMismatchError('%s in %s' %
raise TypeMismatchError(self.current_routine.line_number, '%s in %s' %
(location.name, self.current_routine.name)
)
@ -293,7 +297,7 @@ class Analyzer(object):
# even if we goto another routine, we can't trash an output.
for ref in trashed:
if ref in type_.outputs:
raise UnmeaningfulOutputError('%s in %s' % (ref.name, routine.name))
raise UnmeaningfulOutputError(routine.line_number, '%s in %s' % (ref.name, routine.name))
if not self.has_encountered_goto:
for ref in type_.outputs:
@ -301,7 +305,7 @@ class Analyzer(object):
for ref in context.each_touched():
if ref not in type_.outputs and ref not in type_.trashes and not routine_has_static(routine, ref):
message = '%s in %s' % (ref.name, routine.name)
raise ForbiddenWriteError(message)
raise ForbiddenWriteError(routine.line_number, message)
self.current_routine = None
def analyze_block(self, block, context):
@ -334,7 +338,7 @@ class Analyzer(object):
if TableType.is_a_table_type(src.ref.type, TYPE_BYTE) and dest.type == TYPE_BYTE:
pass
else:
raise TypeMismatchError('%s and %s in %s' %
raise TypeMismatchError(instr.line_number, '%s and %s in %s' %
(src.ref.name, dest.name, self.current_routine.name)
)
context.assert_meaningful(src, src.index)
@ -344,10 +348,10 @@ class Analyzer(object):
if isinstance(src.ref.type, PointerType) and dest.type == TYPE_BYTE:
pass
else:
raise TypeMismatchError((src, dest))
raise TypeMismatchError(instr.line_number, (src, dest))
context.assert_meaningful(src.ref, REG_Y)
elif src.type != dest.type:
raise TypeMismatchError('%s and %s in %s' %
raise TypeMismatchError(instr.line_number, '%s and %s in %s' %
(src.name, dest.name, self.current_routine.name)
)
else:
@ -359,7 +363,7 @@ class Analyzer(object):
if src.type == TYPE_BYTE and TableType.is_a_table_type(dest.ref.type, TYPE_BYTE):
pass
else:
raise TypeMismatchError((src, dest))
raise TypeMismatchError(instr.line_number, (src, dest))
context.assert_meaningful(dest.index)
context.assert_in_range(dest.index, dest.ref)
context.set_written(dest.ref)
@ -368,11 +372,11 @@ class Analyzer(object):
if isinstance(dest.ref.type, PointerType) and src.type == TYPE_BYTE:
pass
else:
raise TypeMismatchError((src, dest))
raise TypeMismatchError(instr.line_number, (src, dest))
context.assert_meaningful(dest.ref, REG_Y)
context.set_written(dest.ref)
elif src.type != dest.type:
raise TypeMismatchError('%r and %r in %s' %
raise TypeMismatchError(instr.line_number, '%r and %r in %s' %
(src, dest, self.current_routine.name)
)
else:
@ -443,7 +447,7 @@ class Analyzer(object):
context.set_unmeaningful(ref)
elif opcode == 'copy':
if dest == REG_A:
raise ForbiddenWriteError("{} cannot be used as destination for copy".format(dest))
raise ForbiddenWriteError(instr.line_number, "{} cannot be used as destination for copy".format(dest))
# 1. check that their types are compatible
@ -451,17 +455,17 @@ class Analyzer(object):
if isinstance(src.ref.type, BufferType) and isinstance(dest.type, PointerType):
pass
else:
raise TypeMismatchError((src, dest))
raise TypeMismatchError(instr.line_number, (src, dest))
elif isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, IndirectRef):
if src.type == TYPE_BYTE and isinstance(dest.ref.type, PointerType):
pass
else:
raise TypeMismatchError((src, dest))
raise TypeMismatchError(instr.line_number, (src, dest))
elif isinstance(src, IndirectRef) and isinstance(dest, LocationRef):
if isinstance(src.ref.type, PointerType) and dest.type == TYPE_BYTE:
pass
else:
raise TypeMismatchError((src, dest))
raise TypeMismatchError(instr.line_number, (src, dest))
elif isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, IndexedRef):
if src.type == TYPE_WORD and TableType.is_a_table_type(dest.ref.type, TYPE_WORD):
@ -473,7 +477,7 @@ class Analyzer(object):
RoutineType.executable_types_compatible(src.type, dest.ref.type.of_type)):
pass
else:
raise TypeMismatchError((src, dest))
raise TypeMismatchError(instr.line_number, (src, dest))
context.assert_in_range(dest.index, dest.ref)
elif isinstance(src, IndexedRef) and isinstance(dest, LocationRef):
@ -483,7 +487,7 @@ class Analyzer(object):
RoutineType.executable_types_compatible(src.ref.type.of_type, dest.type.of_type)):
pass
else:
raise TypeMismatchError((src, dest))
raise TypeMismatchError(instr.line_number, (src, dest))
context.assert_in_range(src.index, src.ref)
elif isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, LocationRef):
@ -494,9 +498,9 @@ class Analyzer(object):
self.assert_affected_within('outputs', src.type, dest.type.of_type)
self.assert_affected_within('trashes', src.type, dest.type.of_type)
else:
raise TypeMismatchError((src, dest))
raise TypeMismatchError(instr.line_number, (src, dest))
else:
raise TypeMismatchError((src, dest))
raise TypeMismatchError(instr.line_number, (src, dest))
# 2. check that the context is meaningful
@ -528,7 +532,7 @@ class Analyzer(object):
type_ = location.type
if not isinstance(type_, (RoutineType, VectorType)):
raise TypeMismatchError(location)
raise TypeMismatchError(instr.line_number, location)
# assert that the dest routine's inputs are all initialized
if isinstance(type_, VectorType):