mirror of
https://github.com/catseye/SixtyPical.git
synced 2024-06-29 05:29:34 +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
|
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):
|
||||||
|
|
|
@ -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...
|
||||||
|
|
Loading…
Reference in New Issue
Block a user