1
0
mirror of https://github.com/catseye/SixtyPical.git synced 2025-02-16 15:30:26 +00:00

Merge pull request #5 from catseye/refactor-types-for-typedefs

Refactor types for typedefs
This commit is contained in:
Chris Pressey 2018-02-06 11:55:07 +00:00 committed by GitHub
commit bae152f94d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 412 additions and 223 deletions

View File

@ -501,11 +501,22 @@ The sense of the test can be inverted with `not`.
Grammar
-------
Program ::= {Defn} {Routine}.
Program ::= {TypeDefn} {Defn} {Routine}.
TypeDefn::= "typedef" Type Ident<new>.
Defn ::= Type Ident<new> [Constraints] (":" Literal | "@" LitWord).
Type ::= "byte" ["table"] | "vector"
Type ::= "(" Type ")" | TypeExpr ["table" TypeSize].
TypeExpr::= "byte"
| "word"
| "buffer" TypeSize
| "pointer"
| "vector" Type
| "routine" Constraints
.
TypeSize::= "[" LitWord "]".
Constrnt::= ["inputs" LocExprs] ["outputs" LocExprs] ["trashes" LocExprs].
Routine ::= "routine" Ident<new> Constraints (Block | "@" LitWord).
Routine ::= "define" Ident<new> Type (Block | "@" LitWord).
| "routine" Ident<new> Constraints (Block | "@" LitWord)
.
LocExprs::= LocExpr {"," LocExpr}.
LocExpr ::= Register | Flag | Literal | Ident.
Register::= "a" | "x" | "y".

View File

@ -2,6 +2,58 @@
// * Demo Game for SixtyPical *
// ****************************
// ----------------------------------------------------------------
// Type Definitions
// ----------------------------------------------------------------
//
// Type of routines (and vectors to those routines) which are called on each frame
// to implement a certain state of the game (title screen, in play, game over, etc.)
//
// This type is also used as the type for the interrupt vector, even though
// the interrupt routine saves and restores everything before being called and
// thus clearly does not actually trash all the registers. It is declared this
// way so that the game state routines, which do trash these registers, can be
// assigned to it.
//
// This type is also used as the type for the location the old interrupt vector
// is backed up to, because all the game state routines `goto` the old handler
// and the end of their own routines, so the type needs to be compatible.
// (In a good sense, it is a continuation.)
//
// Further,
//
// It's very arguable that screen1/2/3/4 and colormap1/2/3/4 are not REALLY inputs.
// They're only there to support the fact that game states sometimes clear the
// screen, and sometimes don't. When they don't, they preserve the screen, and
// currently the way to say "we preserve the screen" is to have it as both input
// and output. There is probably a better way to do this, but it needs thought.
//
typedef routine
inputs joy2, button_down, press_fire_msg, dispatch_game_state, save_x,
actor_pos, pos, new_pos, actor_delta, delta,
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
outputs button_down, dispatch_game_state,
actor_pos, pos, new_pos, actor_delta, delta,
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
trashes a, x, y, c, z, n, v, ptr, save_x, compare_target
game_state_routine
//
// Routines that are called to get the new state of each actor (player, enemy, etc.)
//
// Routines that conform to this type also follow this convention:
//
// Set carry if the player perished. Carry clear otherwise.
//
typedef routine
inputs pos, delta, joy2, screen
outputs pos, delta, new_pos, screen, c
trashes a, x, y, z, n, v, ptr, compare_target
logic_routine
// ----------------------------------------------------------------
// System Locations
// ----------------------------------------------------------------
@ -36,7 +88,7 @@ word table[256] actor_delta
word delta
byte button_down : 0 // effectively static-local to check_button
byte table[18] press_fire_msg: "PRESS`FIRE`TO`PLAY"
byte table[32] press_fire_msg: "PRESS`FIRE`TO`PLAY"
byte save_x
word compare_target
@ -44,52 +96,23 @@ word compare_target
//
// Points to the routine that implements the current game state.
//
// It's very arguable that screen1/2/3/4 and colormap1/2/3/4 are not REALLY inputs.
// They're only there to support the fact that game states sometimes clear the
// screen, and sometimes don't. When they don't, they preserve the screen, and
// currently the way to say "we preserve the screen" is to have it as both input
// and output. There is probably a better way to do this, but it needs thought.
//
vector dispatch_game_state
inputs joy2, button_down, press_fire_msg, dispatch_game_state, save_x,
actor_pos, pos, new_pos, actor_delta, delta,
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
outputs button_down, dispatch_game_state,
actor_pos, pos, new_pos, actor_delta, delta,
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
trashes a, x, y, c, z, n, v, ptr, save_x, compare_target
vector game_state_routine
dispatch_game_state
//
// The constraints on these 2 vectors are kind-of sort-of big fibs.
// They're only written this way so they can be compatible with our
// routine. In fact, CINV is an interrupt routine where it doesn't
// really matter what you trash anyway, because all registers were
/// saved by the caller (the KERNAL) and will be restored by the end
// of the code of the saved origin cinv routine that we goto.
//
// I wonder if this could be arranged somehow to be less fibby, in
// a future version of SixtyPical.
// Interrupt vector. Has same type as game states (see above.)
//
vector cinv
inputs joy2, button_down, press_fire_msg, dispatch_game_state, save_x,
actor_pos, pos, new_pos, actor_delta, delta,
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
outputs button_down, dispatch_game_state,
actor_pos, pos, new_pos, actor_delta, delta,
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
trashes a, x, y, c, z, n, v, ptr, save_x, compare_target
@ 788
vector game_state_routine
cinv @ 788
vector save_cinv
inputs joy2, button_down, press_fire_msg, dispatch_game_state, save_x,
actor_pos, pos, new_pos, actor_delta, delta,
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
outputs button_down, dispatch_game_state,
actor_pos, pos, new_pos, actor_delta, delta,
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
trashes a, x, y, c, z, n, v, ptr, save_x, compare_target
//
// Location to which the old interrupt vector is saved before replacement.
//
vector game_state_routine
save_cinv
// ----------------------------------------------------------------
// Utility Routines
@ -242,14 +265,7 @@ routine init_game
// Actor Logics
// ----------------------------------------------------------------
//
// Sets carry if the player perished. Carry clear otherwise.
//
routine player_logic
inputs pos, delta, joy2, screen
outputs pos, delta, new_pos, screen, c
trashes a, x, y, z, n, v, ptr, compare_target
define player_logic logic_routine
{
call read_stick
@ -304,14 +320,7 @@ routine player_logic
}
}
//
// Sets carry if the player perished. Carry clear otherwise.
//
routine enemy_logic
inputs pos, delta, screen
outputs pos, delta, new_pos, screen, c
trashes a, x, y, z, n, v, ptr, compare_target
define enemy_logic logic_routine
{
call calculate_new_position
call check_new_position_in_bounds
@ -377,18 +386,7 @@ routine enemy_logic
// Game States
// ----------------------------------------------------------------
//
// Because these all `goto save_cinv` at the end, they must have the same signature as that routine.
//
routine game_state_title_screen
inputs joy2, button_down, press_fire_msg, dispatch_game_state, save_x,
actor_pos, pos, new_pos, actor_delta, delta,
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
outputs button_down, dispatch_game_state,
actor_pos, pos, new_pos, actor_delta, delta,
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
trashes a, x, y, c, z, n, v, ptr, save_x, compare_target
define game_state_title_screen game_state_routine
{
ld y, 0
repeat {
@ -426,14 +424,7 @@ routine game_state_title_screen
goto save_cinv
}
routine game_state_play
inputs joy2, button_down, press_fire_msg, dispatch_game_state, save_x,
actor_pos, pos, new_pos, actor_delta, delta,
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
outputs button_down, dispatch_game_state,
actor_pos, pos, new_pos, actor_delta, delta,
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
trashes a, x, y, c, z, n, v, ptr, save_x, compare_target
define game_state_play game_state_routine
{
ld x, 0
repeat {
@ -479,14 +470,7 @@ routine game_state_play
goto save_cinv
}
routine game_state_game_over
inputs joy2, button_down, press_fire_msg, dispatch_game_state, save_x,
actor_pos, pos, new_pos, actor_delta, delta,
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
outputs button_down, dispatch_game_state,
actor_pos, pos, new_pos, actor_delta, delta,
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
trashes a, x, y, c, z, n, v, ptr, save_x, compare_target
define game_state_game_over game_state_routine
{
st off, c
call check_button
@ -516,14 +500,7 @@ routine game_state_game_over
// * Main Game Loop Driver *
// *************************
routine our_cinv
inputs joy2, button_down, press_fire_msg, dispatch_game_state, save_x,
actor_pos, pos, new_pos, actor_delta, delta,
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
outputs button_down, dispatch_game_state,
actor_pos, pos, new_pos, actor_delta, delta,
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
trashes a, x, y, c, z, n, v, ptr, save_x, compare_target
define our_cinv game_state_routine
{
goto dispatch_game_state
}

View File

@ -3,7 +3,7 @@
from sixtypical.ast import Program, Routine, Block, Instr
from sixtypical.model import (
TYPE_BYTE, TYPE_WORD,
TableType, BufferType, PointerType, VectorType, ExecutableType, RoutineType,
TableType, BufferType, PointerType, VectorType, RoutineType,
ConstantRef, LocationRef, IndirectRef, IndexedRef, AddressRef,
REG_A, REG_Y, FLAG_Z, FLAG_N, FLAG_V, FLAG_C
)
@ -176,13 +176,18 @@ class Analyzer(object):
(location.name, self.current_routine.name)
)
def assert_affected_within(self, name, affected, limited_to):
def assert_affected_within(self, name, affecting_type, limiting_type):
assert name in ('inputs', 'outputs', 'trashes')
affected = getattr(affecting_type, name)
limited_to = getattr(limiting_type, name)
overage = affected - limited_to
if not overage:
return
message = 'in %s: %s are %s but affects %s which exceeds it by: %s ' % (
message = 'in %s: %s for %s are %s\n\nbut %s affects %s\n\nwhich exceeds it by: %s ' % (
self.current_routine.name, name,
LocationRef.format_set(limited_to), LocationRef.format_set(affected), LocationRef.format_set(overage)
limiting_type, LocationRef.format_set(limited_to),
affecting_type, LocationRef.format_set(affected),
LocationRef.format_set(overage)
)
raise IncompatibleConstraintsError(message)
@ -303,6 +308,8 @@ class Analyzer(object):
context.set_written(dest, FLAG_Z, FLAG_N, FLAG_C)
elif opcode == 'call':
type = instr.location.type
if isinstance(type, VectorType):
type = type.of_type
for ref in type.inputs:
context.assert_meaningful(ref)
for ref in type.outputs:
@ -366,8 +373,11 @@ class Analyzer(object):
elif isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, IndexedRef):
if src.type == TYPE_WORD and TableType.is_a_table_type(dest.ref.type, TYPE_WORD):
pass
elif (isinstance(src.type, ExecutableType) and isinstance(dest.ref.type, TableType) and
ExecutableType.executable_types_compatible(src.type, dest.ref.type.of_type)):
elif (isinstance(src.type, VectorType) and isinstance(dest.ref.type, TableType) and
RoutineType.executable_types_compatible(src.type.of_type, dest.ref.type.of_type)):
pass
elif (isinstance(src.type, RoutineType) and isinstance(dest.ref.type, TableType) and
RoutineType.executable_types_compatible(src.type, dest.ref.type.of_type)):
pass
else:
raise TypeMismatchError((src, dest))
@ -376,7 +386,7 @@ class Analyzer(object):
if TableType.is_a_table_type(src.ref.type, TYPE_WORD) and dest.type == TYPE_WORD:
pass
elif (isinstance(src.ref.type, TableType) and isinstance(dest.type, VectorType) and
ExecutableType.executable_types_compatible(src.ref.type.of_type, dest.type)):
RoutineType.executable_types_compatible(src.ref.type.of_type, dest.type.of_type)):
pass
else:
raise TypeMismatchError((src, dest))
@ -384,10 +394,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):
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)
elif isinstance(src.type, RoutineType) and isinstance(dest.type, VectorType):
self.assert_affected_within('inputs', src.type, dest.type.of_type)
self.assert_affected_within('outputs', src.type, dest.type.of_type)
self.assert_affected_within('trashes', src.type, dest.type.of_type)
else:
raise TypeMismatchError((src, dest))
else:
@ -434,18 +444,20 @@ class Analyzer(object):
location = instr.location
type_ = location.type
if not isinstance(type_, ExecutableType):
if not isinstance(type_, (RoutineType, VectorType)):
raise TypeMismatchError(location)
# assert that the dest routine's inputs are all initialized
if isinstance(type_, VectorType):
type_ = type_.of_type
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
self.assert_affected_within('outputs', type_.outputs, current_type.outputs)
self.assert_affected_within('trashes', type_.trashes, current_type.trashes)
self.assert_affected_within('outputs', type_, current_type)
self.assert_affected_within('trashes', type_, current_type)
self.has_encountered_goto = True
elif opcode == 'trash':

View File

@ -17,16 +17,31 @@ class Type(object):
def __hash__(self):
return hash(self.name)
def backpatch_constraint_labels(self, resolver):
def resolve(w):
if not isinstance(w, basestring):
return w
return resolver(w)
if isinstance(self, TableType):
self.of_type.backpatch_constraint_labels(resolver)
elif isinstance(self, VectorType):
self.of_type.backpatch_constraint_labels(resolver)
elif isinstance(self, RoutineType):
self.inputs = set([resolve(w) for w in self.inputs])
self.outputs = set([resolve(w) for w in self.outputs])
self.trashes = set([resolve(w) for w in self.trashes])
TYPE_BIT = Type('bit')
TYPE_BYTE = Type('byte')
TYPE_WORD = Type('word')
class ExecutableType(Type):
"""Used for routines and vectors."""
def __init__(self, name, inputs=None, outputs=None, trashes=None):
self.name = name
class RoutineType(Type):
"""This memory location contains the code for a routine."""
def __init__(self, inputs=None, outputs=None, trashes=None):
self.name = 'routine'
self.inputs = inputs or set()
self.outputs = outputs or set()
self.trashes = trashes or set()
@ -37,7 +52,7 @@ class ExecutableType(Type):
)
def __eq__(self, other):
return isinstance(other, ExecutableType) and (
return isinstance(other, RoutineType) and (
other.name == self.name and
other.inputs == self.inputs and
other.outputs == self.outputs and
@ -50,7 +65,11 @@ class ExecutableType(Type):
@classmethod
def executable_types_compatible(cls_, src, dest):
"""Returns True iff a value of type `src` can be assigned to a storage location of type `dest`."""
if isinstance(src, ExecutableType) and isinstance(dest, VectorType):
if isinstance(src, VectorType):
src = src.of_type
if isinstance(dest, VectorType):
dest = dest.of_type
if isinstance(src, RoutineType) and isinstance(dest, RoutineType):
# TODO: I'm sure we can replace some of these with subset-containment, but that requires thought
return (
src.inputs == dest.inputs and
@ -61,16 +80,22 @@ class ExecutableType(Type):
return False
class RoutineType(ExecutableType):
"""This memory location contains the code for a routine."""
def __init__(self, **kwargs):
super(RoutineType, self).__init__('routine', **kwargs)
class VectorType(Type):
"""This memory location contains the address of some other type (currently, only RoutineType)."""
def __init__(self, of_type):
self.name = 'vector'
self.of_type = of_type
def __repr__(self):
return '%s(%r)' % (
self.__class__.__name__, self.of_type
)
class VectorType(ExecutableType):
"""This memory location contains the address of a routine."""
def __init__(self, **kwargs):
super(VectorType, self).__init__('vector', **kwargs)
def __eq__(self, other):
return self.name == other.name and self.of_type == other.of_type
def __hash__(self):
return hash(self.name) ^ hash(self.of_type)
class TableType(Type):
@ -135,18 +160,6 @@ class LocationRef(Ref):
def is_constant(self):
return isinstance(self.type, RoutineType)
def backpatch_vector_labels(self, resolver):
if isinstance(self.type, ExecutableType):
t = self.type
t.inputs = set([resolver(w) for w in t.inputs])
t.outputs = set([resolver(w) for w in t.outputs])
t.trashes = set([resolver(w) for w in t.trashes])
if isinstance(self.type, TableType) and isinstance(self.type.of_type, ExecutableType):
t = self.type.of_type
t.inputs = set([resolver(w) for w in t.inputs])
t.outputs = set([resolver(w) for w in t.outputs])
t.trashes = set([resolver(w) for w in t.trashes])
@classmethod
def format_set(cls, location_refs):
return '{%s}' % ', '.join([str(loc) for loc in sorted(location_refs)])

View File

@ -3,7 +3,7 @@
from sixtypical.ast import Program, Defn, Routine, Block, Instr
from sixtypical.model import (
TYPE_BIT, TYPE_BYTE, TYPE_WORD,
RoutineType, VectorType, ExecutableType, TableType, BufferType, PointerType,
RoutineType, VectorType, TableType, BufferType, PointerType,
LocationRef, ConstantRef, IndirectRef, IndexedRef, AddressRef,
)
from sixtypical.scanner import Scanner
@ -19,6 +19,7 @@ class Parser(object):
def __init__(self, text):
self.scanner = Scanner(text)
self.symbols = {} # token -> SymEntry
self.typedefs = {} # token -> Type AST
for token in ('a', 'x', 'y'):
self.symbols[token] = SymEntry(None, LocationRef(TYPE_BYTE, token))
for token in ('c', 'z', 'n', 'v'):
@ -35,16 +36,25 @@ class Parser(object):
def program(self):
defns = []
routines = []
while self.scanner.on('byte', 'word', 'vector', 'buffer', 'pointer'):
while self.scanner.on('typedef'):
typedef = self.typedef()
typenames = ['byte', 'word', 'table', 'vector', 'buffer', 'pointer'] # 'routine',
typenames.extend(self.typedefs.keys())
while self.scanner.on(*typenames):
defn = self.defn()
name = defn.name
if name in self.symbols:
raise SyntaxError('Symbol "%s" already declared' % name)
self.symbols[name] = SymEntry(defn, defn.location)
defns.append(defn)
while self.scanner.on('routine'):
routine = self.routine()
name = routine.name
while self.scanner.on('define', 'routine'):
if self.scanner.consume('define'):
name = self.scanner.token
self.scanner.scan()
routine = self.routine(name)
else:
routine = self.legacy_routine()
name = routine.name
if name in self.symbols:
raise SyntaxError('Symbol "%s" already declared' % name)
self.symbols[name] = SymEntry(routine, routine.location)
@ -52,30 +62,42 @@ class Parser(object):
self.scanner.check_type('EOF')
# now backpatch the executable types.
#for type_name, type_ in self.typedefs.iteritems():
# type_.backpatch_constraint_labels(lambda w: self.lookup(w))
for defn in defns:
defn.location.backpatch_vector_labels(lambda w: self.lookup(w))
defn.location.type.backpatch_constraint_labels(lambda w: self.lookup(w))
for routine in routines:
routine.location.backpatch_vector_labels(lambda w: self.lookup(w))
routine.location.type.backpatch_constraint_labels(lambda w: self.lookup(w))
for instr in self.backpatch_instrs:
if instr.opcode in ('call', 'goto'):
name = instr.location
if name not in self.symbols:
raise SyntaxError('Undefined routine "%s"' % name)
if not isinstance(self.symbols[name].model.type, ExecutableType):
if not isinstance(self.symbols[name].model.type, (RoutineType, VectorType)):
raise SyntaxError('Illegal call of non-executable "%s"' % name)
instr.location = self.symbols[name].model
if instr.opcode in ('copy',) and isinstance(instr.src, basestring):
name = instr.src
if name not in self.symbols:
raise SyntaxError('Undefined routine "%s"' % name)
if not isinstance(self.symbols[name].model.type, ExecutableType):
if not isinstance(self.symbols[name].model.type, (RoutineType, VectorType)):
raise SyntaxError('Illegal copy of non-executable "%s"' % name)
instr.src = self.symbols[name].model
return Program(defns=defns, routines=routines)
def typedef(self):
self.scanner.expect('typedef')
type_ = self.defn_type()
name = self.defn_name()
if name in self.typedefs:
raise SyntaxError('Type "%s" already declared' % name)
self.typedefs[name] = type_
return type_
def defn(self):
type_, name = self.defn_type_and_name()
type_ = self.defn_type()
name = self.defn_name()
initial = None
if self.scanner.consume(':'):
@ -107,39 +129,43 @@ class Parser(object):
self.scanner.expect(']')
return size
def defn_type_and_name(self):
def defn_type(self):
type_ = None
if self.scanner.consume('('):
type_ = self.defn_type()
self.scanner.expect(')')
return type_
if self.scanner.consume('byte'):
type_ = TYPE_BYTE
if self.scanner.consume('table'):
size = self.defn_size()
type_ = TableType(type_, size)
name = self.defn_name()
return type_, name
elif self.scanner.consume('word'):
type_ = TYPE_WORD
if self.scanner.consume('table'):
size = self.defn_size()
type_ = TableType(type_, size)
name = self.defn_name()
return type_, name
elif self.scanner.consume('vector'):
size = None
if self.scanner.consume('table'):
size = self.defn_size()
name = self.defn_name()
type_ = self.defn_type()
if not isinstance(type_, RoutineType):
raise SyntaxError("Vectors can only be of a routine, not %r" % type_)
type_ = VectorType(type_)
elif self.scanner.consume('routine'):
(inputs, outputs, trashes) = self.constraints()
type_ = VectorType(inputs=inputs, outputs=outputs, trashes=trashes)
if size is not None:
type_ = TableType(type_, size)
return type_, name
type_ = RoutineType(inputs=inputs, outputs=outputs, trashes=trashes)
elif self.scanner.consume('buffer'):
size = self.defn_size()
name = self.defn_name()
return BufferType(size), name
type_ = BufferType(size)
elif self.scanner.consume('pointer'):
type_ = PointerType()
else:
self.scanner.expect('pointer')
name = self.defn_name()
return PointerType(), name
type_name = self.scanner.token
self.scanner.scan()
if type_name not in self.typedefs:
raise SyntaxError("Undefined type '%s'" % type_name)
type_ = self.typedefs[type_name]
if self.scanner.consume('table'):
size = self.defn_size()
type_ = TableType(type_, size)
return type_
def defn_name(self):
self.scanner.check_type('identifier')
@ -159,11 +185,12 @@ class Parser(object):
trashes = set(self.labels())
return (inputs, outputs, trashes)
def routine(self):
def legacy_routine(self):
self.scanner.expect('routine')
name = self.scanner.token
self.scanner.scan()
(inputs, outputs, trashes) = self.constraints()
type_ = RoutineType(inputs=inputs, outputs=outputs, trashes=trashes)
if self.scanner.consume('@'):
self.scanner.check_type('integer literal')
block = None
@ -172,10 +199,25 @@ class Parser(object):
else:
block = self.block()
addr = None
location = LocationRef(
RoutineType(inputs=inputs, outputs=outputs, trashes=trashes),
name
location = LocationRef(type_, name)
return Routine(
name=name, block=block, addr=addr,
location=location
)
def routine(self, name):
type_ = self.defn_type()
if not isinstance(type_, RoutineType):
raise SyntaxError("Can only define a routine, not %r" % type_)
if self.scanner.consume('@'):
self.scanner.check_type('integer literal')
block = None
addr = int(self.scanner.token)
self.scanner.scan()
else:
block = self.block()
addr = None
location = LocationRef(type_, name)
return Routine(
name=name, block=block, addr=addr,
location=location

View File

@ -1593,10 +1593,11 @@ Read through a pointer.
Routines are constants. You need not, and in fact cannot, specify a constant
as an input to, an output of, or as a trashed value of a routine.
| vector vec
| vector routine
| inputs x
| outputs x
| trashes z, n
| vec
|
| routine foo
| inputs x
@ -1615,10 +1616,11 @@ as an input to, an output of, or as a trashed value of a routine.
| }
? ConstantConstraintError: foo in main
| vector vec
| vector routine
| inputs x
| outputs x
| trashes z, n
| vec
|
| routine foo
| inputs x
@ -1636,10 +1638,11 @@ as an input to, an output of, or as a trashed value of a routine.
| }
? ConstantConstraintError: foo in main
| vector vec
| vector routine
| inputs x
| outputs x
| trashes z, n
| vec
|
| routine foo
| inputs x
@ -1660,10 +1663,11 @@ as an input to, an output of, or as a trashed value of a routine.
You can copy the address of a routine into a vector, if that vector is
declared appropriately.
| vector vec
| vector routine
| inputs x
| outputs x
| trashes z, n
| vec
|
| routine foo
| inputs x
@ -1683,10 +1687,11 @@ declared appropriately.
But not if the vector is declared inappropriately.
| vector vec
| vector routine
| inputs y
| outputs y
| trashes z, n
| vec
|
| routine foo
| inputs x
@ -1707,10 +1712,11 @@ But not if the vector is declared inappropriately.
"Appropriately" means, if the routine affects no more than what is named
in the input/output sets of the vector.
| vector vec
| vector routine
| inputs a, x
| outputs x
| trashes a, z, n
| vec
|
| routine foo
| inputs x
@ -1730,10 +1736,11 @@ in the input/output sets of the vector.
Routines are read-only.
| vector vec
| vector routine
| inputs x
| outputs x
| trashes z, n
| vec
|
| routine foo
| inputs x
@ -1753,7 +1760,9 @@ Routines are read-only.
Indirect call.
| vector foo outputs x trashes z, n
| vector routine
| outputs x trashes z, n
| foo
|
| routine bar outputs x trashes z, n {
| ld x, 200
@ -1767,7 +1776,7 @@ Indirect call.
Calling the vector does indeed trash the things the vector says it does.
| vector foo trashes x, z, n
| vector routine trashes x, z, n foo
|
| routine bar trashes x, z, n {
| ld x, 200
@ -1867,7 +1876,7 @@ Can `goto` a routine that outputs or trashes less than the current routine.
Indirect goto.
| vector foo outputs x trashes a, z, n
| vector routine outputs x trashes a, z, n foo
|
| routine bar outputs x trashes a, z, n {
| ld x, 200
@ -1882,8 +1891,9 @@ Indirect goto.
Jumping through the vector does indeed trash, or output, the things the
vector says it does.
| vector foo
| vector routine
| trashes a, x, z, n
| foo
|
| routine bar
| trashes a, x, z, n {
@ -1905,9 +1915,9 @@ vector says it does.
| }
? UnmeaningfulReadError: x in main
| vector foo
| vector routine
| outputs x
| trashes a, z, n
| trashes a, z, n foo
|
| routine bar
| outputs x
@ -1931,16 +1941,18 @@ vector says it does.
| }
= ok
### Vector tables ###
### vector tables ###
A vector can be copied into a vector table.
| vector one
| vector routine
| outputs x
| trashes a, z, n
| vector table[256] many
| one
| vector (routine
| outputs x
| trashes a, z, n
| trashes a, z, n)
| table[256] many
|
| routine bar outputs x trashes a, z, n {
| ld x, 200
@ -1959,12 +1971,14 @@ A vector can be copied into a vector table.
A vector can be copied out of a vector table.
| vector one
| vector routine
| outputs x
| trashes a, z, n
| vector table[256] many
| one
| vector (routine
| outputs x
| trashes a, z, n
| trashes a, z, n)
| table[256] many
|
| routine bar outputs x trashes a, z, n {
| ld x, 200
@ -1983,9 +1997,10 @@ A vector can be copied out of a vector table.
A routine can be copied into a vector table.
| vector table[256] many
| outputs x
| trashes a, z, n
| vector (routine
| outputs x
| trashes a, z, n)
| table[256] many
|
| routine bar outputs x trashes a, z, n {
| ld x, 200
@ -2003,9 +2018,10 @@ A routine can be copied into a vector table.
A vector in a vector table cannot be directly called.
| vector table[256] many
| outputs x
| trashes a, z, n
| vector (routine
| outputs x
| trashes a, z, n)
| table[256] many
|
| routine bar outputs x trashes a, z, n {
| ld x, 200
@ -2021,3 +2037,55 @@ A vector in a vector table cannot be directly called.
| call many + x
| }
? ValueError
### typedef ###
A typedef is a more-readable alias for a type. "Alias" means
that types have structural equivalence, not name equivalence.
| typedef routine
| inputs x
| outputs x
| trashes z, n
| routine_type
|
| vector routine_type vec
|
| routine foo
| inputs x
| outputs x
| trashes z, n
| {
| inc x
| }
|
| routine main
| outputs vec
| trashes a, z, n
| {
| copy foo, vec
| }
= ok
The new style routine definitions support typedefs.
| typedef routine
| inputs x
| outputs x
| trashes z, n
| routine_type
|
| vector routine_type vec
|
| define foo routine_type
| {
| inc x
| }
|
| routine main
| outputs vec
| trashes a, z, n
| {
| copy foo, vec
| }
= ok

View File

@ -477,8 +477,8 @@ You can also copy a literal word to a word table.
Copy vector to vector.
| vector bar
| vector baz
| vector routine bar
| vector routine baz
|
| routine main
| inputs baz
@ -495,7 +495,7 @@ Copy vector to vector.
Copy routine to vector, inside an `interrupts off` block.
| vector bar
| vector routine bar
|
| routine foo
| inputs x
@ -568,7 +568,7 @@ Copy word to word table and back, with both `x` and `y` as indexes.
Indirect call.
| vector foo outputs x trashes z, n
| vector routine outputs x trashes z, n foo
|
| routine bar outputs x trashes z, n {
| ld x, 200
@ -608,12 +608,14 @@ goto.
Copying to and from a vector table.
| vector one
| vector routine
| outputs x
| trashes a, z, n
| vector table[256] many
| one
| vector (routine
| outputs x
| trashes a, z, n
| trashes a, z, n)
| table[256] many
|
| routine bar outputs x trashes a, z, n {
| ld x, 200

View File

@ -135,10 +135,7 @@ User-defined memory addresses of different types.
| byte byt
| word wor
| vector vec trashes a
| byte table[256] tab
| word table[256] wtab
| vector table[256] vtab trashes a
| vector routine trashes a vec
| buffer[2048] buf
| pointer ptr
|
@ -146,6 +143,36 @@ User-defined memory addresses of different types.
| }
= ok
Tables of different types.
| byte table[256] tab
| word table[256] wtab
| vector (routine trashes a) table[256] vtab
|
| routine main {
| }
= ok
Typedefs of different types.
| typedef byte octet
| typedef octet table[256] twokay
| typedef routine trashes a game_routine
| vector game_routine start_game
|
| routine main {
| }
= ok
Can't have two typedefs with the same name.
| typedef byte frank
| typedef word frank
|
| routine main {
| }
? SyntaxError
Explicit memory address.
| byte screen @ 1024
@ -189,7 +216,7 @@ User-defined locations of other types.
Initialized byte table.
| byte table[28] message : "WHAT DO YOU WANT TO DO NEXT?"
| byte table[32] message : "WHAT DO YOU WANT TO DO NEXT?"
|
| routine main {
| }
@ -314,11 +341,11 @@ Declaring byte and word table memory location.
Declaring and calling a vector.
| vector cinv
| vector routine
| inputs a
| outputs x
| trashes a, x, z, n
| @ 788
| cinv @ 788
|
| routine foo {
| ld a, 0
@ -345,11 +372,11 @@ Only vectors can be decorated with constraints like that.
Constraints set may only contain labels.
| vector cinv
| vector routine
| inputs a
| outputs 200
| trashes a, x, z, n
| @ 788
| cinv @ 788
|
| routine foo {
| ld a, 0
@ -364,11 +391,11 @@ Constraints set may only contain labels.
A vector can name itself in its inputs, outputs, and trashes.
| vector cinv
| vector routine
| inputs cinv, a
| outputs cinv, x
| trashes a, x, z, n
| @ 788
| cinv @ 788
|
| routine foo {
| ld a, 0
@ -384,11 +411,11 @@ A vector can name itself in its inputs, outputs, and trashes.
A routine can be copied into a vector before the routine appears in the program,
*however*, it must be marked as such with the keyword `forward`.
| vector cinv
| vector routine
| inputs cinv, a
| outputs cinv, x
| trashes a, x, z, n
| @ 788
| cinv @ 788
| routine main {
| with interrupts off {
| copy foo, cinv
@ -400,11 +427,11 @@ A routine can be copied into a vector before the routine appears in the program,
| }
? SyntaxError: Undefined symbol
| vector cinv
| vector routine
| inputs cinv, a
| outputs cinv, x
| trashes a, x, z, n
| @ 788
| cinv @ 788
| routine main {
| with interrupts off {
| copy forward foo, cinv
@ -434,7 +461,7 @@ goto.
| }
= ok
| vector foo
| vector routine foo
|
| routine main {
| goto foo
@ -465,3 +492,40 @@ Buffers and pointers.
| copy [ptr] + y, foo
| }
= ok
Routines can be defined in a new style.
| typedef routine
| inputs x
| outputs x
| trashes z, n
| routine_type
|
| vector routine_type vec
|
| define foo routine
| inputs x
| outputs x
| trashes z, n
| {
| inc x
| }
|
| routine main
| outputs vec
| trashes a, z, n
| {
| copy foo, vec
| }
= ok
Only routines can be defined in the new style.
| define foo byte table[256]
|
| routine main
| trashes a, z, n
| {
| ld a, 0
| }
? SyntaxError