diff --git a/src/sixtypical/analyzer.py b/src/sixtypical/analyzer.py index 9f07485..07ec9b4 100644 --- a/src/sixtypical/analyzer.py +++ b/src/sixtypical/analyzer.py @@ -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):