1
0
mirror of https://github.com/catseye/SixtyPical.git synced 2024-11-29 18:49:22 +00:00

Check that the constraints on a routine match those of vector.

This commit is contained in:
Chris Pressey 2015-10-19 19:17:27 +01:00
parent 3010435add
commit 7d56705530
7 changed files with 83 additions and 18 deletions

View File

@ -5,6 +5,8 @@ History of SixtyPical
------- -------
* Added `routine` and `vector` types, and `copy` instruction. * Added `routine` and `vector` types, and `copy` instruction.
* Both routines and vectors can declare `inputs`, `outputs`, and `trashes`,
and these must be compatible to assign a routine or vector to a vector.
0.5 0.5
--- ---

View File

@ -33,10 +33,9 @@ TODO
For 0.6: For 0.6:
* declared `inputs` `outputs` `trashes` on the `vector` type... * `call` vector (generates an indirect JMP.)
* we need to get these 3 things onto the type, and also onto routine types
* `goto` (tail call) a routine or a vector. * `goto` (tail call) a routine or a vector.
* A more involved demo for the C64 — one that sets up an interrupt. * A more involved demo for the C64 — one that sets up an interrupt?
For 0.7: For 0.7:

20
eg/bad-vector.60p Normal file
View File

@ -0,0 +1,20 @@
vector vec
inputs y
outputs y
trashes z, n
routine foo
inputs x
outputs x
trashes z, n
{
inc x
}
routine main
inputs foo
outputs vec
trashes a, z, n
{
copy foo, vec
}

View File

@ -3,7 +3,7 @@
from sixtypical.ast import Program, Routine, Block, Instr from sixtypical.ast import Program, Routine, Block, Instr
from sixtypical.model import ( from sixtypical.model import (
TYPE_BYTE, TYPE_BYTE_TABLE, TYPE_BYTE, TYPE_BYTE_TABLE,
RoutineType, VectorType, RoutineType, VectorType, ExecutableType,
ConstantRef, LocationRef, ConstantRef, LocationRef,
REG_A, FLAG_Z, FLAG_N, FLAG_V, FLAG_C REG_A, FLAG_Z, FLAG_N, FLAG_V, FLAG_C
) )
@ -37,6 +37,10 @@ class TypeMismatchError(StaticAnalysisError):
pass pass
class IncompatibleConstraintsError(StaticAnalysisError):
pass
class Context(): class Context():
""" """
A location is touched if it was changed (or even potentially A location is touched if it was changed (or even potentially
@ -209,6 +213,8 @@ def analyze_instr(instr, context, routines):
if instr.block2 is not None: if instr.block2 is not None:
analyze_block(instr.block2, context2, routines) analyze_block(instr.block2, context2, routines)
# TODO may we need to deal with touched separately here too? # TODO may we need to deal with touched separately here too?
# probably not; if it wasn't meaningful in the first place, it
# doesn't really matter if you modified it or not, coming out.
for ref in context1.each_meaningful(): for ref in context1.each_meaningful():
context2.assert_meaningful(ref, exception_class=InconsistentInitializationError) context2.assert_meaningful(ref, exception_class=InconsistentInitializationError)
for ref in context2.each_meaningful(): for ref in context2.each_meaningful():
@ -225,12 +231,26 @@ def analyze_instr(instr, context, routines):
# NB I *think* that's enough... but it might not be? # NB I *think* that's enough... but it might not be?
elif opcode == 'copy': elif opcode == 'copy':
# check that their types are basically compatible
if src.type == dest.type: if src.type == dest.type:
pass pass
elif isinstance(src.type, RoutineType) and isinstance(dest.type, VectorType): elif isinstance(src.type, ExecutableType) and \
isinstance(dest.type, VectorType):
pass pass
else: else:
raise TypeMismatchError((src, dest)) raise TypeMismatchError((src, dest))
# if dealing with routines and vectors,
# check that they're not incompatible
if isinstance(src.type, ExecutableType) and \
isinstance(dest.type, VectorType):
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)
context.assert_meaningful(src) context.assert_meaningful(src)
context.set_written(dest) context.set_written(dest)
context.set_touched(REG_A, FLAG_Z, FLAG_N) context.set_touched(REG_A, FLAG_Z, FLAG_N)

View File

@ -19,13 +19,13 @@ TYPE_BYTE = Type('byte')
TYPE_BYTE_TABLE = Type('byte table') TYPE_BYTE_TABLE = Type('byte table')
class InteractionConstrainedType(Type): class ExecutableType(Type):
"""Used for routines and vectors.""" """Used for routines and vectors."""
def __init__(self, name, inputs=None, outputs=None, trashes=None): def __init__(self, name, inputs=None, outputs=None, trashes=None):
self.name = name self.name = name
self.inputs = inputs or [] self.inputs = inputs or set()
self.outputs = outputs or [] self.outputs = outputs or set()
self.trashes = trashes or [] self.trashes = trashes or set()
def __repr__(self): def __repr__(self):
return 'RoutineType(%r, inputs=%r, outputs=%r, trashes=%r)' % ( return 'RoutineType(%r, inputs=%r, outputs=%r, trashes=%r)' % (
@ -44,13 +44,13 @@ class InteractionConstrainedType(Type):
return hash(self.name) ^ hash(self.inputs) ^ hash(self.outputs) ^ hash(self.trashes) return hash(self.name) ^ hash(self.inputs) ^ hash(self.outputs) ^ hash(self.trashes)
class RoutineType(InteractionConstrainedType): class RoutineType(ExecutableType):
"""This memory location contains the code for a routine.""" """This memory location contains the code for a routine."""
def __init__(self, **kwargs): def __init__(self, **kwargs):
super(RoutineType, self).__init__('routine', **kwargs) super(RoutineType, self).__init__('routine', **kwargs)
class VectorType(InteractionConstrainedType): class VectorType(ExecutableType):
"""This memory location contains the address of a routine.""" """This memory location contains the address of a routine."""
def __init__(self, **kwargs): def __init__(self, **kwargs):
super(VectorType, self).__init__('vector', **kwargs) super(VectorType, self).__init__('vector', **kwargs)

View File

@ -149,15 +149,15 @@ class Parser(object):
return Defn(name=name, addr=addr, location=location) return Defn(name=name, addr=addr, location=location)
def constraints(self): def constraints(self):
inputs = [] inputs = set()
outputs = [] outputs = set()
trashes = [] trashes = set()
if self.scanner.consume('inputs'): if self.scanner.consume('inputs'):
inputs = self.locexprs() inputs = set(self.locexprs())
if self.scanner.consume('outputs'): if self.scanner.consume('outputs'):
outputs = self.locexprs() outputs = set(self.locexprs())
if self.scanner.consume('trashes'): if self.scanner.consume('trashes'):
trashes = self.locexprs() trashes = set(self.locexprs())
return (inputs, outputs, trashes) return (inputs, outputs, trashes)
def routine(self): def routine(self):

View File

@ -1094,4 +1094,28 @@ But not if the vector is declared inappropriately.
| { | {
| copy foo, vec | copy foo, vec
| } | }
? IllegalWriteError ? IncompatibleConstraintsError
Routines are read-only.
| vector vec
| inputs x
| outputs x
| trashes z, n
|
| routine foo
| inputs x
| outputs x
| trashes z, n
| {
| inc x
| }
|
| routine main
| inputs foo
| outputs vec
| trashes a, z, n
| {
| copy vec, foo
| }
? TypeMismatchError