mirror of
https://github.com/catseye/SixtyPical.git
synced 2024-06-18 03:29:32 +00:00
Try to improve error messages, thus breaking many unit tests.
This commit is contained in:
parent
646ec38aa8
commit
cc433e9c64
|
@ -13,11 +13,11 @@ class StaticAnalysisError(ValueError):
|
|||
pass
|
||||
|
||||
|
||||
class UninitializedAccessError(StaticAnalysisError):
|
||||
class UnmeaningfulReadError(StaticAnalysisError):
|
||||
pass
|
||||
|
||||
|
||||
class UninitializedOutputError(StaticAnalysisError):
|
||||
class UnmeaningfulOutputError(StaticAnalysisError):
|
||||
pass
|
||||
|
||||
|
||||
|
@ -25,11 +25,11 @@ class InconsistentInitializationError(StaticAnalysisError):
|
|||
pass
|
||||
|
||||
|
||||
class IllegalWriteError(StaticAnalysisError):
|
||||
class ForbiddenWriteError(StaticAnalysisError):
|
||||
pass
|
||||
|
||||
|
||||
class UsageClashError(StaticAnalysisError):
|
||||
class InconsistentConstraintsError(StaticAnalysisError):
|
||||
pass
|
||||
|
||||
|
||||
|
@ -45,7 +45,7 @@ class IllegalJumpError(StaticAnalysisError):
|
|||
pass
|
||||
|
||||
|
||||
class Context():
|
||||
class Context(object):
|
||||
"""
|
||||
A location is touched if it was changed (or even potentially
|
||||
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
|
||||
lists of this routine.
|
||||
"""
|
||||
def __init__(self, inputs, outputs, trashes):
|
||||
def __init__(self, routine, inputs, outputs, trashes):
|
||||
self.routine = routine
|
||||
self._touched = set()
|
||||
self._meaningful = set()
|
||||
self._writeable = set()
|
||||
|
@ -74,13 +75,14 @@ class Context():
|
|||
self._writeable.add(ref)
|
||||
|
||||
def clone(self):
|
||||
c = Context([], [], [])
|
||||
c = Context(self.routine, [], [], [])
|
||||
c._touched = set(self._touched)
|
||||
c._meaningful = set(self._meaningful)
|
||||
c._writeable = set(self._writeable)
|
||||
return c
|
||||
|
||||
def set_from(self, c):
|
||||
assert c.routine == self.routine
|
||||
self._touched = set(c._touched)
|
||||
self._meaningful = set(c._meaningful)
|
||||
self._writeable = set(c._writeable)
|
||||
|
@ -94,20 +96,23 @@ class Context():
|
|||
yield ref
|
||||
|
||||
def assert_meaningful(self, *refs, **kwargs):
|
||||
exception_class = kwargs.get('exception_class', UninitializedAccessError)
|
||||
exception_class = kwargs.get('exception_class', UnmeaningfulReadError)
|
||||
for ref in refs:
|
||||
if isinstance(ref, ConstantRef):
|
||||
pass
|
||||
elif isinstance(ref, LocationRef):
|
||||
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:
|
||||
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:
|
||||
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):
|
||||
for ref in refs:
|
||||
|
@ -151,14 +156,15 @@ class Analyzer(object):
|
|||
# it's an extern, that's fine
|
||||
return
|
||||
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)
|
||||
if not self.has_encountered_goto:
|
||||
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():
|
||||
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
|
||||
|
||||
def analyze_block(self, block, context, routines):
|
||||
|
|
|
@ -73,7 +73,7 @@ If a routine modifies a location, it needs to either output it or trash it.
|
|||
| {
|
||||
| ld x, 0
|
||||
| }
|
||||
? IllegalWriteError: x
|
||||
? ForbiddenWriteError: x in main
|
||||
|
||||
| routine main
|
||||
| outputs x, z, n
|
||||
|
@ -324,7 +324,7 @@ Can't `add` from or to a memory location that isn't initialized.
|
|||
| st off, c
|
||||
| add a, lives
|
||||
| }
|
||||
? UninitializedAccessError: lives
|
||||
? UnmeaningfulReadError: lives in main
|
||||
|
||||
| byte lives
|
||||
| routine main
|
||||
|
@ -335,7 +335,7 @@ Can't `add` from or to a memory location that isn't initialized.
|
|||
| st off, c
|
||||
| add a, lives
|
||||
| }
|
||||
? UninitializedAccessError: a
|
||||
? UnmeaningfulReadError: a in main
|
||||
|
||||
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
|
||||
| add a, 0
|
||||
| }
|
||||
? IllegalWriteError: a
|
||||
? ForbiddenWriteError: a in main
|
||||
|
||||
### sub ###
|
||||
|
||||
|
@ -371,7 +371,7 @@ Can't `sub` from or to a memory location that isn't initialized.
|
|||
| st off, c
|
||||
| sub a, lives
|
||||
| }
|
||||
? UninitializedAccessError: lives
|
||||
? UnmeaningfulReadError: lives in main
|
||||
|
||||
| byte lives
|
||||
| routine main
|
||||
|
@ -382,7 +382,7 @@ Can't `sub` from or to a memory location that isn't initialized.
|
|||
| st off, c
|
||||
| sub a, lives
|
||||
| }
|
||||
? UninitializedAccessError: a
|
||||
? UnmeaningfulReadError: a in main
|
||||
|
||||
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
|
||||
| sub a, 0
|
||||
| }
|
||||
? IllegalWriteError: a
|
||||
? ForbiddenWriteError: a in main
|
||||
|
||||
### inc ###
|
||||
|
||||
|
@ -405,7 +405,7 @@ Location must be initialized and writeable.
|
|||
| {
|
||||
| inc x
|
||||
| }
|
||||
? UninitializedAccessError: x
|
||||
? UnmeaningfulReadError: x in main
|
||||
|
||||
| routine main
|
||||
| inputs x
|
||||
|
@ -413,7 +413,7 @@ Location must be initialized and writeable.
|
|||
| {
|
||||
| inc x
|
||||
| }
|
||||
? IllegalWriteError: x
|
||||
? ForbiddenWriteError: x in main
|
||||
|
||||
| routine main
|
||||
| inputs x
|
||||
|
@ -434,7 +434,7 @@ Location must be initialized and writeable.
|
|||
| {
|
||||
| dec x
|
||||
| }
|
||||
? UninitializedAccessError: x
|
||||
? UnmeaningfulReadError: x in main
|
||||
|
||||
| routine main
|
||||
| inputs x
|
||||
|
@ -442,7 +442,7 @@ Location must be initialized and writeable.
|
|||
| {
|
||||
| dec x
|
||||
| }
|
||||
? IllegalWriteError: x
|
||||
? ForbiddenWriteError: x in main
|
||||
|
||||
| routine main
|
||||
| inputs x
|
||||
|
@ -471,14 +471,14 @@ Some rudimentary tests for cmp.
|
|||
| {
|
||||
| cmp a, 4
|
||||
| }
|
||||
? IllegalWriteError: c
|
||||
? ForbiddenWriteError: c in main
|
||||
|
||||
| routine main
|
||||
| trashes z, c, n
|
||||
| {
|
||||
| cmp a, 4
|
||||
| }
|
||||
? UninitializedAccessError: a
|
||||
? UnmeaningfulReadError: a in main
|
||||
|
||||
### and ###
|
||||
|
||||
|
@ -498,14 +498,14 @@ Some rudimentary tests for and.
|
|||
| {
|
||||
| and a, 4
|
||||
| }
|
||||
? IllegalWriteError: a
|
||||
? ForbiddenWriteError: a in main
|
||||
|
||||
| routine main
|
||||
| trashes z, n
|
||||
| {
|
||||
| and a, 4
|
||||
| }
|
||||
? UninitializedAccessError: a
|
||||
? UnmeaningfulReadError: a in main
|
||||
|
||||
### or ###
|
||||
|
||||
|
@ -525,14 +525,14 @@ Writing unit tests on a train. Wow.
|
|||
| {
|
||||
| or a, 4
|
||||
| }
|
||||
? IllegalWriteError: a
|
||||
? ForbiddenWriteError: a in main
|
||||
|
||||
| routine main
|
||||
| trashes z, n
|
||||
| {
|
||||
| or a, 4
|
||||
| }
|
||||
? UninitializedAccessError: a
|
||||
? UnmeaningfulReadError: a in main
|
||||
|
||||
### xor ###
|
||||
|
||||
|
@ -552,14 +552,14 @@ Writing unit tests on a train. Wow.
|
|||
| {
|
||||
| xor a, 4
|
||||
| }
|
||||
? IllegalWriteError: a
|
||||
? ForbiddenWriteError: a in main
|
||||
|
||||
| routine main
|
||||
| trashes z, n
|
||||
| {
|
||||
| xor a, 4
|
||||
| }
|
||||
? UninitializedAccessError: a
|
||||
? UnmeaningfulReadError: a in main
|
||||
|
||||
### shl ###
|
||||
|
||||
|
@ -579,7 +579,7 @@ Some rudimentary tests for shl.
|
|||
| {
|
||||
| shl a
|
||||
| }
|
||||
? IllegalWriteError: a
|
||||
? ForbiddenWriteError: a in main
|
||||
|
||||
| routine main
|
||||
| inputs a
|
||||
|
@ -587,7 +587,7 @@ Some rudimentary tests for shl.
|
|||
| {
|
||||
| shl a
|
||||
| }
|
||||
? UninitializedAccessError: c
|
||||
? UnmeaningfulReadError: c in main
|
||||
|
||||
### shr ###
|
||||
|
||||
|
@ -607,7 +607,7 @@ Some rudimentary tests for shr.
|
|||
| {
|
||||
| shr a
|
||||
| }
|
||||
? IllegalWriteError: a
|
||||
? ForbiddenWriteError: a in main
|
||||
|
||||
| routine main
|
||||
| inputs a
|
||||
|
@ -615,7 +615,7 @@ Some rudimentary tests for shr.
|
|||
| {
|
||||
| shr a
|
||||
| }
|
||||
? UninitializedAccessError: c
|
||||
? UnmeaningfulReadError: c in main
|
||||
|
||||
### call ###
|
||||
|
||||
|
@ -635,7 +635,7 @@ initialized.
|
|||
| {
|
||||
| call foo
|
||||
| }
|
||||
? UninitializedAccessError: x
|
||||
? UnmeaningfulReadError: x in main
|
||||
|
||||
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
|
||||
| call foo
|
||||
| }
|
||||
? IllegalWriteError: lives
|
||||
? ForbiddenWriteError: lives in main
|
||||
|
||||
| byte lives
|
||||
|
|
||||
|
@ -691,7 +691,7 @@ You can't output a value that the thing you called trashed.
|
|||
| ld x, 0
|
||||
| call foo
|
||||
| }
|
||||
? UninitializedOutputError: lives
|
||||
? UnmeaningfulOutputError: lives in main
|
||||
|
||||
...unless you write to it yourself afterwards.
|
||||
|
||||
|
@ -742,7 +742,7 @@ calling it.
|
|||
| call foo
|
||||
| ld a, x
|
||||
| }
|
||||
? UninitializedAccessError: x
|
||||
? UnmeaningfulReadError: x in main
|
||||
|
||||
If a routine trashes locations, they are uninitialized in the caller after
|
||||
calling it.
|
||||
|
@ -767,7 +767,7 @@ calling it.
|
|||
| call foo
|
||||
| ld a, x
|
||||
| }
|
||||
? UninitializedAccessError: x
|
||||
? UnmeaningfulReadError: x in main
|
||||
|
||||
Calling an extern is just the same as calling a defined routine with the
|
||||
same constraints.
|
||||
|
@ -795,7 +795,7 @@ same constraints.
|
|||
| {
|
||||
| call chrout
|
||||
| }
|
||||
? UninitializedAccessError: a
|
||||
? UnmeaningfulReadError: a in main
|
||||
|
||||
| routine chrout
|
||||
| inputs a
|
||||
|
@ -809,7 +809,7 @@ same constraints.
|
|||
| call chrout
|
||||
| ld x, a
|
||||
| }
|
||||
? UninitializedAccessError: a
|
||||
? UnmeaningfulReadError: a in main
|
||||
|
||||
### if ###
|
||||
|
||||
|
@ -964,7 +964,7 @@ initialized at the start.
|
|||
| cmp x, 10
|
||||
| } until z
|
||||
| }
|
||||
? UninitializedAccessError: y
|
||||
? UnmeaningfulReadError: y in main
|
||||
|
||||
### copy ###
|
||||
|
||||
|
@ -987,7 +987,7 @@ Can't `copy` from a memory location that isn't initialized.
|
|||
| {
|
||||
| copy x, lives
|
||||
| }
|
||||
? UninitializedAccessError: x
|
||||
? UnmeaningfulReadError: x in main
|
||||
|
||||
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
|
||||
| }
|
||||
? IllegalWriteError: lives
|
||||
? ForbiddenWriteError: lives in main
|
||||
|
||||
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
|
||||
| }
|
||||
? IllegalWriteError: a
|
||||
? ForbiddenWriteError: a in main
|
||||
|
||||
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
|
||||
| }
|
||||
? UninitializedOutputError: a
|
||||
? UnmeaningfulOutputError: a in main
|
||||
|
||||
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
|
||||
| call foo
|
||||
| }
|
||||
? UninitializedOutputError: x
|
||||
? UnmeaningfulOutputError: x in main
|
||||
|
||||
`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.
|
||||
|
||||
| vector foo trashes a, x, z, n
|
||||
|
|
||||
| routine bar trashes a, x, z, n {
|
||||
| ld x, 200
|
||||
| }
|
||||
|
|
||||
| routine sub inputs bar trashes foo, a, x, z, n {
|
||||
| ld x, 0
|
||||
| copy bar, foo
|
||||
| goto foo
|
||||
| }
|
||||
|
|
||||
| routine main inputs bar outputs a trashes z, n {
|
||||
| call sub
|
||||
| ld a, x
|
||||
| }
|
||||
? UninitializedOutputError: x
|
||||
> | vector foo trashes a, x, z, n
|
||||
> |
|
||||
> | routine bar trashes a, x, z, n {
|
||||
> | ld x, 200
|
||||
> | }
|
||||
> |
|
||||
> | routine sub inputs bar trashes foo, a, x, z, n {
|
||||
> | ld x, 0
|
||||
> | copy bar, foo
|
||||
> | goto foo
|
||||
> | }
|
||||
> |
|
||||
> | routine main inputs bar outputs a trashes z, n {
|
||||
> | call sub
|
||||
> | ld a, x
|
||||
> | }
|
||||
> ? UnmeaningfulReadError: x in main
|
||||
|
||||
Ack, I have become a bit confused...
|
||||
|
|
Loading…
Reference in New Issue
Block a user