1
0
mirror of https://github.com/catseye/SixtyPical.git synced 2024-06-26 16:29:28 +00:00

Try to improve error messages, thus breaking many unit tests.

This commit is contained in:
Chris Pressey 2015-10-21 19:43:44 +01:00
parent 646ec38aa8
commit cc433e9c64
2 changed files with 74 additions and 68 deletions

View File

@ -13,11 +13,11 @@ class StaticAnalysisError(ValueError):
pass pass
class UninitializedAccessError(StaticAnalysisError): class UnmeaningfulReadError(StaticAnalysisError):
pass pass
class UninitializedOutputError(StaticAnalysisError): class UnmeaningfulOutputError(StaticAnalysisError):
pass pass
@ -25,11 +25,11 @@ class InconsistentInitializationError(StaticAnalysisError):
pass pass
class IllegalWriteError(StaticAnalysisError): class ForbiddenWriteError(StaticAnalysisError):
pass pass
class UsageClashError(StaticAnalysisError): class InconsistentConstraintsError(StaticAnalysisError):
pass pass
@ -45,7 +45,7 @@ class IllegalJumpError(StaticAnalysisError):
pass pass
class Context(): class Context(object):
""" """
A location is touched if it was changed (or even potentially A location is touched if it was changed (or even potentially
changed) during this routine, or some routine called by this routine. changed) during this routine, or some routine called by this routine.
@ -57,7 +57,8 @@ class Context():
A location is writeable if it was listed in the outputs and trashes A location is writeable if it was listed in the outputs and trashes
lists of this routine. lists of this routine.
""" """
def __init__(self, inputs, outputs, trashes): def __init__(self, routine, inputs, outputs, trashes):
self.routine = routine
self._touched = set() self._touched = set()
self._meaningful = set() self._meaningful = set()
self._writeable = set() self._writeable = set()
@ -74,13 +75,14 @@ class Context():
self._writeable.add(ref) self._writeable.add(ref)
def clone(self): def clone(self):
c = Context([], [], []) c = Context(self.routine, [], [], [])
c._touched = set(self._touched) c._touched = set(self._touched)
c._meaningful = set(self._meaningful) c._meaningful = set(self._meaningful)
c._writeable = set(self._writeable) c._writeable = set(self._writeable)
return c return c
def set_from(self, c): def set_from(self, c):
assert c.routine == self.routine
self._touched = set(c._touched) self._touched = set(c._touched)
self._meaningful = set(c._meaningful) self._meaningful = set(c._meaningful)
self._writeable = set(c._writeable) self._writeable = set(c._writeable)
@ -94,20 +96,23 @@ class Context():
yield ref yield ref
def assert_meaningful(self, *refs, **kwargs): def assert_meaningful(self, *refs, **kwargs):
exception_class = kwargs.get('exception_class', UninitializedAccessError) exception_class = kwargs.get('exception_class', UnmeaningfulReadError)
for ref in refs: for ref in refs:
if isinstance(ref, ConstantRef): if isinstance(ref, ConstantRef):
pass pass
elif isinstance(ref, LocationRef): elif isinstance(ref, LocationRef):
if ref not in self._meaningful: if ref not in self._meaningful:
raise exception_class(ref.name) message = '%s in %s' % (ref.name, self.routine.name)
raise exception_class(message)
else: else:
raise ValueError(ref) raise NotImplementedError(ref)
def assert_writeable(self, *refs): def assert_writeable(self, *refs, **kwargs):
exception_class = kwargs.get('exception_class', ForbiddenWriteError)
for ref in refs: for ref in refs:
if ref not in self._writeable: if ref not in self._writeable:
raise IllegalWriteError(ref.name) message = '%s in %s' % (ref.name, self.routine.name)
raise exception_class(message)
def set_touched(self, *refs): def set_touched(self, *refs):
for ref in refs: for ref in refs:
@ -151,14 +156,15 @@ class Analyzer(object):
# it's an extern, that's fine # it's an extern, that's fine
return return
type = routine.location.type type = routine.location.type
context = Context(type.inputs, type.outputs, type.trashes) context = Context(routine, type.inputs, type.outputs, type.trashes)
self.analyze_block(routine.block, context, routines) self.analyze_block(routine.block, context, routines)
if not self.has_encountered_goto: if not self.has_encountered_goto:
for ref in type.outputs: for ref in type.outputs:
context.assert_meaningful(ref, exception_class=UninitializedOutputError) context.assert_meaningful(ref, exception_class=UnmeaningfulOutputError)
for ref in context.each_touched(): for ref in context.each_touched():
if ref not in type.outputs and ref not in type.trashes: if ref not in type.outputs and ref not in type.trashes:
raise IllegalWriteError(ref.name) message = '%s in %s' % (ref.name, routine.name)
raise ForbiddenWriteError(message)
self.current_routine = None self.current_routine = None
def analyze_block(self, block, context, routines): def analyze_block(self, block, context, routines):

View File

@ -73,7 +73,7 @@ If a routine modifies a location, it needs to either output it or trash it.
| { | {
| ld x, 0 | ld x, 0
| } | }
? IllegalWriteError: x ? ForbiddenWriteError: x in main
| routine main | routine main
| outputs x, z, n | outputs x, z, n
@ -324,7 +324,7 @@ Can't `add` from or to a memory location that isn't initialized.
| st off, c | st off, c
| add a, lives | add a, lives
| } | }
? UninitializedAccessError: lives ? UnmeaningfulReadError: lives in main
| byte lives | byte lives
| routine main | routine main
@ -335,7 +335,7 @@ Can't `add` from or to a memory location that isn't initialized.
| st off, c | st off, c
| add a, lives | add a, lives
| } | }
? UninitializedAccessError: a ? UnmeaningfulReadError: a in main
Can't `add` to a memory location that isn't writeable. Can't `add` to a memory location that isn't writeable.
@ -346,7 +346,7 @@ Can't `add` to a memory location that isn't writeable.
| st off, c | st off, c
| add a, 0 | add a, 0
| } | }
? IllegalWriteError: a ? ForbiddenWriteError: a in main
### sub ### ### sub ###
@ -371,7 +371,7 @@ Can't `sub` from or to a memory location that isn't initialized.
| st off, c | st off, c
| sub a, lives | sub a, lives
| } | }
? UninitializedAccessError: lives ? UnmeaningfulReadError: lives in main
| byte lives | byte lives
| routine main | routine main
@ -382,7 +382,7 @@ Can't `sub` from or to a memory location that isn't initialized.
| st off, c | st off, c
| sub a, lives | sub a, lives
| } | }
? UninitializedAccessError: a ? UnmeaningfulReadError: a in main
Can't `sub` to a memory location that isn't writeable. Can't `sub` to a memory location that isn't writeable.
@ -393,7 +393,7 @@ Can't `sub` to a memory location that isn't writeable.
| st off, c | st off, c
| sub a, 0 | sub a, 0
| } | }
? IllegalWriteError: a ? ForbiddenWriteError: a in main
### inc ### ### inc ###
@ -405,7 +405,7 @@ Location must be initialized and writeable.
| { | {
| inc x | inc x
| } | }
? UninitializedAccessError: x ? UnmeaningfulReadError: x in main
| routine main | routine main
| inputs x | inputs x
@ -413,7 +413,7 @@ Location must be initialized and writeable.
| { | {
| inc x | inc x
| } | }
? IllegalWriteError: x ? ForbiddenWriteError: x in main
| routine main | routine main
| inputs x | inputs x
@ -434,7 +434,7 @@ Location must be initialized and writeable.
| { | {
| dec x | dec x
| } | }
? UninitializedAccessError: x ? UnmeaningfulReadError: x in main
| routine main | routine main
| inputs x | inputs x
@ -442,7 +442,7 @@ Location must be initialized and writeable.
| { | {
| dec x | dec x
| } | }
? IllegalWriteError: x ? ForbiddenWriteError: x in main
| routine main | routine main
| inputs x | inputs x
@ -471,14 +471,14 @@ Some rudimentary tests for cmp.
| { | {
| cmp a, 4 | cmp a, 4
| } | }
? IllegalWriteError: c ? ForbiddenWriteError: c in main
| routine main | routine main
| trashes z, c, n | trashes z, c, n
| { | {
| cmp a, 4 | cmp a, 4
| } | }
? UninitializedAccessError: a ? UnmeaningfulReadError: a in main
### and ### ### and ###
@ -498,14 +498,14 @@ Some rudimentary tests for and.
| { | {
| and a, 4 | and a, 4
| } | }
? IllegalWriteError: a ? ForbiddenWriteError: a in main
| routine main | routine main
| trashes z, n | trashes z, n
| { | {
| and a, 4 | and a, 4
| } | }
? UninitializedAccessError: a ? UnmeaningfulReadError: a in main
### or ### ### or ###
@ -525,14 +525,14 @@ Writing unit tests on a train. Wow.
| { | {
| or a, 4 | or a, 4
| } | }
? IllegalWriteError: a ? ForbiddenWriteError: a in main
| routine main | routine main
| trashes z, n | trashes z, n
| { | {
| or a, 4 | or a, 4
| } | }
? UninitializedAccessError: a ? UnmeaningfulReadError: a in main
### xor ### ### xor ###
@ -552,14 +552,14 @@ Writing unit tests on a train. Wow.
| { | {
| xor a, 4 | xor a, 4
| } | }
? IllegalWriteError: a ? ForbiddenWriteError: a in main
| routine main | routine main
| trashes z, n | trashes z, n
| { | {
| xor a, 4 | xor a, 4
| } | }
? UninitializedAccessError: a ? UnmeaningfulReadError: a in main
### shl ### ### shl ###
@ -579,7 +579,7 @@ Some rudimentary tests for shl.
| { | {
| shl a | shl a
| } | }
? IllegalWriteError: a ? ForbiddenWriteError: a in main
| routine main | routine main
| inputs a | inputs a
@ -587,7 +587,7 @@ Some rudimentary tests for shl.
| { | {
| shl a | shl a
| } | }
? UninitializedAccessError: c ? UnmeaningfulReadError: c in main
### shr ### ### shr ###
@ -607,7 +607,7 @@ Some rudimentary tests for shr.
| { | {
| shr a | shr a
| } | }
? IllegalWriteError: a ? ForbiddenWriteError: a in main
| routine main | routine main
| inputs a | inputs a
@ -615,7 +615,7 @@ Some rudimentary tests for shr.
| { | {
| shr a | shr a
| } | }
? UninitializedAccessError: c ? UnmeaningfulReadError: c in main
### call ### ### call ###
@ -635,7 +635,7 @@ initialized.
| { | {
| call foo | call foo
| } | }
? UninitializedAccessError: x ? UnmeaningfulReadError: x in main
Note that if you call a routine that trashes a location, you also trash it. Note that if you call a routine that trashes a location, you also trash it.
@ -654,7 +654,7 @@ Note that if you call a routine that trashes a location, you also trash it.
| ld x, 0 | ld x, 0
| call foo | call foo
| } | }
? IllegalWriteError: lives ? ForbiddenWriteError: lives in main
| byte lives | byte lives
| |
@ -691,7 +691,7 @@ You can't output a value that the thing you called trashed.
| ld x, 0 | ld x, 0
| call foo | call foo
| } | }
? UninitializedOutputError: lives ? UnmeaningfulOutputError: lives in main
...unless you write to it yourself afterwards. ...unless you write to it yourself afterwards.
@ -742,7 +742,7 @@ calling it.
| call foo | call foo
| ld a, x | ld a, x
| } | }
? UninitializedAccessError: x ? UnmeaningfulReadError: x in main
If a routine trashes locations, they are uninitialized in the caller after If a routine trashes locations, they are uninitialized in the caller after
calling it. calling it.
@ -767,7 +767,7 @@ calling it.
| call foo | call foo
| ld a, x | ld a, x
| } | }
? UninitializedAccessError: x ? UnmeaningfulReadError: x in main
Calling an extern is just the same as calling a defined routine with the Calling an extern is just the same as calling a defined routine with the
same constraints. same constraints.
@ -795,7 +795,7 @@ same constraints.
| { | {
| call chrout | call chrout
| } | }
? UninitializedAccessError: a ? UnmeaningfulReadError: a in main
| routine chrout | routine chrout
| inputs a | inputs a
@ -809,7 +809,7 @@ same constraints.
| call chrout | call chrout
| ld x, a | ld x, a
| } | }
? UninitializedAccessError: a ? UnmeaningfulReadError: a in main
### if ### ### if ###
@ -964,7 +964,7 @@ initialized at the start.
| cmp x, 10 | cmp x, 10
| } until z | } until z
| } | }
? UninitializedAccessError: y ? UnmeaningfulReadError: y in main
### copy ### ### copy ###
@ -987,7 +987,7 @@ Can't `copy` from a memory location that isn't initialized.
| { | {
| copy x, lives | copy x, lives
| } | }
? UninitializedAccessError: x ? UnmeaningfulReadError: x in main
Can't `copy` to a memory location that doesn't appear in (outputs trashes). Can't `copy` to a memory location that doesn't appear in (outputs trashes).
@ -1015,7 +1015,7 @@ Can't `copy` to a memory location that doesn't appear in (outputs trashes).
| { | {
| copy 0, lives | copy 0, lives
| } | }
? IllegalWriteError: lives ? ForbiddenWriteError: lives in main
a, z, and n are trashed, and must be declared as such a, z, and n are trashed, and must be declared as such
@ -1025,7 +1025,7 @@ a, z, and n are trashed, and must be declared as such
| { | {
| copy 0, lives | copy 0, lives
| } | }
? IllegalWriteError: a ? ForbiddenWriteError: a in main
a, z, and n are trashed, and must not be declared as outputs. a, z, and n are trashed, and must not be declared as outputs.
@ -1035,7 +1035,7 @@ a, z, and n are trashed, and must not be declared as outputs.
| { | {
| copy 0, lives | copy 0, lives
| } | }
? UninitializedOutputError: a ? UnmeaningfulOutputError: a in main
Unless of course you subsequently initialize them. Unless of course you subsequently initialize them.
@ -1147,7 +1147,7 @@ Calling the vector does indeed trash the things the vector says it does.
| copy bar, foo | copy bar, foo
| call foo | call foo
| } | }
? UninitializedOutputError: x ? UnmeaningfulOutputError: x in main
`goto`, if present, must be in tail position (the final instruction in a routine.) `goto`, if present, must be in tail position (the final instruction in a routine.)
@ -1250,22 +1250,22 @@ Indirect goto.
Jumping through the vector does indeed output the things the vector says it does. Jumping through the vector does indeed output the things the vector says it does.
| vector foo trashes a, x, z, n > | vector foo trashes a, x, z, n
| > |
| routine bar trashes a, x, z, n { > | routine bar trashes a, x, z, n {
| ld x, 200 > | ld x, 200
| } > | }
| > |
| routine sub inputs bar trashes foo, a, x, z, n { > | routine sub inputs bar trashes foo, a, x, z, n {
| ld x, 0 > | ld x, 0
| copy bar, foo > | copy bar, foo
| goto foo > | goto foo
| } > | }
| > |
| routine main inputs bar outputs a trashes z, n { > | routine main inputs bar outputs a trashes z, n {
| call sub > | call sub
| ld a, x > | ld a, x
| } > | }
? UninitializedOutputError: x > ? UnmeaningfulReadError: x in main
Ack, I have become a bit confused... Ack, I have become a bit confused...