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:
commit
bae152f94d
@ -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".
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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':
|
||||
|
@ -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)])
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user