From 82e33ab476f5c7c8be81f7c2e744bdd407ec247a Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Tue, 12 Dec 2017 09:42:16 +0000 Subject: [PATCH] Improve error messaging when constraints are exceeded. --- HISTORY.markdown | 1 + src/sixtypical/analyzer.py | 40 ++++++++++++++++++++++---------------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/HISTORY.markdown b/HISTORY.markdown index 81dd7c0..c4cb657 100644 --- a/HISTORY.markdown +++ b/HISTORY.markdown @@ -8,6 +8,7 @@ History of SixtyPical * Add word to pointer (unchecked for now). * Added `word table` type. * Can `copy` from word storage location to word table and back. +* A `vector` can name itself in its `inputs` and `outputs` or `trashes` sets. * Implementation: `--debug` shows some extra info during analysis. * Fixed bug where `copy`ing literal word into word storage used wrong endianness. * Fixed bug where every memory location was allocated 2 bytes of storage, regardless of type. diff --git a/src/sixtypical/analyzer.py b/src/sixtypical/analyzer.py index 8ef8ed7..c5e4506 100644 --- a/src/sixtypical/analyzer.py +++ b/src/sixtypical/analyzer.py @@ -184,6 +184,19 @@ class Analyzer(object): (location.name, self.current_routine.name) ) + def assert_affected_within(self, name, affected, limited_to): + + def format(loc): + assert isinstance(loc, LocationRef) + return loc.name + + if affected <= limited_to: + return + overage = affected - limited_to + overage_s = ', '.join(sorted([format(loc) for loc in overage])) + message = 'affected beyond %s: {%s} in %s' % (name, overage_s, self.current_routine.name) + raise IncompatibleConstraintsError(message) + def analyze_program(self, program): assert isinstance(program, Program) self.routines = {r.location: r for r in program.routines} @@ -364,16 +377,10 @@ class Analyzer(object): elif isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, LocationRef): if src.type == dest.type: pass - elif isinstance(src.type, ExecutableType) and \ - isinstance(dest.type, VectorType): - # if dealing with routines and vectors, - # check that they're not incompatible - if not (src.type.inputs <= dest.type.inputs): - raise IncompatibleConstraintsError(src.type.inputs - dest.type.inputs) - if not (src.type.outputs <= dest.type.outputs): - raise IncompatibleConstraintsError(src.type.outputs - dest.type.outputs) - if not (src.type.trashes <= dest.type.trashes): - raise IncompatibleConstraintsError(src.type.trashes - dest.type.trashes) + elif isinstance(src.type, ExecutableType) and isinstance(dest.type, VectorType): + self.assert_affected_within('inputs', src.type.inputs, dest.type.inputs) + self.assert_affected_within('outputs', src.type.outputs, dest.type.outputs) + self.assert_affected_within('trashes', src.type.trashes, dest.type.trashes) else: raise TypeMismatchError((src, dest)) else: @@ -409,22 +416,21 @@ class Analyzer(object): self.analyze_block(instr.block, context) elif opcode == 'goto': location = instr.location - type = location.type + type_ = location.type - if not isinstance(type, ExecutableType): + if not isinstance(type_, ExecutableType): raise TypeMismatchError(location) # assert that the dest routine's inputs are all initialized - for ref in type.inputs: + for ref in type_.inputs: context.assert_meaningful(ref) # and that this routine's trashes and output constraints are a # superset of the called routine's current_type = self.current_routine.location.type - if not (type.outputs <= current_type.outputs): - raise IncompatibleConstraintsError(type.outputs - current_type.outputs) - if not (type.trashes <= current_type.trashes): - raise IncompatibleConstraintsError(type.trashes - current_type.trashes) + self.assert_affected_within('outputs', type_.outputs, current_type.outputs) + self.assert_affected_within('trashes', type_.trashes, current_type.trashes) + self.has_encountered_goto = True else: raise NotImplementedError(opcode)