mirror of
https://github.com/catseye/SixtyPical.git
synced 2025-02-20 13:29:09 +00:00
Merge branch 'develop-0.17' into remove-legacy-syntax
This commit is contained in:
commit
0c63de9351
@ -5,6 +5,11 @@ History of SixtyPical
|
|||||||
----
|
----
|
||||||
|
|
||||||
* `save X, Y, Z { }` now allowed as a shortcut for nested `save`s.
|
* `save X, Y, Z { }` now allowed as a shortcut for nested `save`s.
|
||||||
|
* If the name in a location expression isn't found in the symbol
|
||||||
|
table, a forward reference will _always_ be generated; and forward
|
||||||
|
references in _all_ operations will be resolved after parsing.
|
||||||
|
* As a consequence, trying to call or goto a non-routine-typed symbol
|
||||||
|
is now an analysis error, not a syntax error.
|
||||||
* Split TODO off into own file.
|
* Split TODO off into own file.
|
||||||
* `sixtypical` no longer writes the compiled binary to standard
|
* `sixtypical` no longer writes the compiled binary to standard
|
||||||
output. The `--output` command-line argument should be given
|
output. The `--output` command-line argument should be given
|
||||||
|
34
TODO.md
34
TODO.md
@ -1,20 +1,16 @@
|
|||||||
TODO for SixtyPical
|
TODO for SixtyPical
|
||||||
===================
|
===================
|
||||||
|
|
||||||
### `low` and `high` address operators
|
### 16-bit `cmp`
|
||||||
|
|
||||||
To turn `word` type into `byte`.
|
This is because we don't actually want `low` and `high` address operators
|
||||||
|
that turn `word` type into `byte`.
|
||||||
|
|
||||||
Trying to remember if we have a compelling case for this or now. The best I can think
|
This is because this immediately makes things harder (that is, effectively
|
||||||
of is for implementing 16-bit `cmp` in an efficient way. Maybe we should see if we
|
impossible) to analyze.
|
||||||
can get by with 16-bit `cmp` instead though.
|
|
||||||
|
|
||||||
The problem is that once a byte is extracted, putting it back into a word is awkward.
|
16-bit `cmp` also benefits from some special differences between `cmp`
|
||||||
The address operators have to modify a destination in a special way. That is, when
|
and `sub` on 6502, so it would be nice to capture them.
|
||||||
you say `st a, >word`, you are updating `word` to be `word & $ff | a << 8`, somelike.
|
|
||||||
Is that consistent with `st`? Well, probably it is, but we have to explain it.
|
|
||||||
It might make more sense, then, for it to be "part of the operation" instead of "part of
|
|
||||||
the reference"; something like `st.hi x, word`; `st.lo y, word`. Dunno.
|
|
||||||
|
|
||||||
### Save values to other-than-the-stack
|
### Save values to other-than-the-stack
|
||||||
|
|
||||||
@ -27,17 +23,12 @@ Allow
|
|||||||
Which uses some other storage location instead of the stack. A local static
|
Which uses some other storage location instead of the stack. A local static
|
||||||
would be a good candidate for such.
|
would be a good candidate for such.
|
||||||
|
|
||||||
### Make all symbols forward-referencable
|
|
||||||
|
|
||||||
Basically, don't do symbol-table lookups when parsing, but do have a more formal
|
|
||||||
"symbol resolution" (linking) phase right after parsing.
|
|
||||||
|
|
||||||
### Associate each pointer with the buffer it points into
|
### Associate each pointer with the buffer it points into
|
||||||
|
|
||||||
Check that the buffer being read or written to through pointer, appears in appropriate
|
Check that the buffer being read or written to through pointer, appears in appropriate
|
||||||
inputs or outputs set.
|
inputs or outputs set.
|
||||||
|
|
||||||
In the analysis, when we obtain a pointer, we need to record, in contect, what buffer
|
In the analysis, when we obtain a pointer, we need to record, in context, what buffer
|
||||||
that pointer came from.
|
that pointer came from.
|
||||||
|
|
||||||
When we write through that pointer, we need to set that buffer as written.
|
When we write through that pointer, we need to set that buffer as written.
|
||||||
@ -78,8 +69,8 @@ error.
|
|||||||
More generally, define a block as having zero or one `goto`s at the end. (and `goto`s cannot
|
More generally, define a block as having zero or one `goto`s at the end. (and `goto`s cannot
|
||||||
appear elsewhere.)
|
appear elsewhere.)
|
||||||
|
|
||||||
If a block ends in a `call` can that be converted to end in a `goto`? Why not? I think it can.
|
If a block ends in a `call` can that be converted to end in a `goto`? Why not? I think it can,
|
||||||
The constraints should iron out the same both ways.
|
if the block is in tail position. The constraints should iron out the same both ways.
|
||||||
|
|
||||||
And - once we have this - why do we need `goto` to be in tail position, strictly?
|
And - once we have this - why do we need `goto` to be in tail position, strictly?
|
||||||
As long as the routine has consistent type context every place it exits, that should be fine.
|
As long as the routine has consistent type context every place it exits, that should be fine.
|
||||||
@ -91,3 +82,8 @@ Search a searchlist of include paths. And use them to make libraries of routine
|
|||||||
One such library routine might be an `interrupt routine` type for various architectures.
|
One such library routine might be an `interrupt routine` type for various architectures.
|
||||||
Since "the supervisor" has stored values on the stack, we should be able to trash them
|
Since "the supervisor" has stored values on the stack, we should be able to trash them
|
||||||
with impunity, in such a routine.
|
with impunity, in such a routine.
|
||||||
|
|
||||||
|
### Line numbers in analysis error messages
|
||||||
|
|
||||||
|
For analysis errors, there is a line number, but it's the line of the routine
|
||||||
|
after the routine in which the analysis error occurred. Fix this.
|
||||||
|
@ -33,9 +33,11 @@
|
|||||||
typedef routine
|
typedef routine
|
||||||
inputs joy2, press_fire_msg, dispatch_game_state,
|
inputs joy2, press_fire_msg, dispatch_game_state,
|
||||||
actor_pos, actor_delta, actor_logic,
|
actor_pos, actor_delta, actor_logic,
|
||||||
|
player_died,
|
||||||
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
|
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
|
||||||
outputs dispatch_game_state,
|
outputs dispatch_game_state,
|
||||||
actor_pos, actor_delta, actor_logic,
|
actor_pos, actor_delta, actor_logic,
|
||||||
|
player_died,
|
||||||
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
|
screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
|
||||||
trashes a, x, y, c, z, n, v, pos, new_pos, delta, ptr, dispatch_logic
|
trashes a, x, y, c, z, n, v, pos, new_pos, delta, ptr, dispatch_logic
|
||||||
game_state_routine
|
game_state_routine
|
||||||
@ -45,13 +47,13 @@ typedef routine
|
|||||||
//
|
//
|
||||||
// Routines that conform to this type also follow this convention:
|
// Routines that conform to this type also follow this convention:
|
||||||
//
|
//
|
||||||
// Set carry if the player perished. Carry clear otherwise.
|
// Set player_died to 1 if the player perished. Unchanged otherwise.
|
||||||
//
|
//
|
||||||
|
|
||||||
typedef routine
|
typedef routine
|
||||||
inputs pos, delta, joy2, screen
|
inputs pos, delta, joy2, screen, player_died
|
||||||
outputs pos, delta, new_pos, screen, c
|
outputs pos, delta, new_pos, screen, player_died
|
||||||
trashes a, x, y, z, n, v, ptr
|
trashes a, x, y, z, n, v, c, ptr
|
||||||
logic_routine
|
logic_routine
|
||||||
|
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
@ -87,6 +89,8 @@ word new_pos
|
|||||||
word table[256] actor_delta
|
word table[256] actor_delta
|
||||||
word delta
|
word delta
|
||||||
|
|
||||||
|
byte player_died
|
||||||
|
|
||||||
vector logic_routine table[256] actor_logic
|
vector logic_routine table[256] actor_logic
|
||||||
vector logic_routine dispatch_logic
|
vector logic_routine dispatch_logic
|
||||||
|
|
||||||
@ -241,7 +245,7 @@ define check_new_position_in_bounds routine
|
|||||||
|
|
||||||
routine init_game
|
routine init_game
|
||||||
inputs actor_pos, actor_delta, actor_logic
|
inputs actor_pos, actor_delta, actor_logic
|
||||||
outputs actor_pos, actor_delta, actor_logic
|
outputs actor_pos, actor_delta, actor_logic, player_died
|
||||||
trashes pos, a, y, z, n, c, v
|
trashes pos, a, y, z, n, c, v
|
||||||
{
|
{
|
||||||
ld y, 0
|
ld y, 0
|
||||||
@ -259,9 +263,11 @@ routine init_game
|
|||||||
} until z
|
} until z
|
||||||
|
|
||||||
ld y, 0
|
ld y, 0
|
||||||
copy word 0, actor_pos + y
|
copy word 40, actor_pos + y
|
||||||
copy word 0, actor_delta + y
|
copy word 0, actor_delta + y
|
||||||
copy player_logic, actor_logic + y
|
copy player_logic, actor_logic + y
|
||||||
|
|
||||||
|
st y, player_died
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
@ -301,10 +307,9 @@ define player_logic logic_routine
|
|||||||
st off, c
|
st off, c
|
||||||
add ptr, pos
|
add ptr, pos
|
||||||
copy 81, [ptr] + y
|
copy 81, [ptr] + y
|
||||||
|
|
||||||
st off, c
|
|
||||||
} else {
|
} else {
|
||||||
st on, c
|
ld a, 1
|
||||||
|
st a, player_died
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME these trashes, strictly speaking, probably shouldn't be needed,
|
// FIXME these trashes, strictly speaking, probably shouldn't be needed,
|
||||||
@ -314,8 +319,6 @@ define player_logic logic_routine
|
|||||||
trash ptr
|
trash ptr
|
||||||
trash y
|
trash y
|
||||||
trash v
|
trash v
|
||||||
} else {
|
|
||||||
st off, c
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -351,10 +354,6 @@ define enemy_logic logic_routine
|
|||||||
st off, c
|
st off, c
|
||||||
add ptr, pos
|
add ptr, pos
|
||||||
copy 82, [ptr] + y
|
copy 82, [ptr] + y
|
||||||
|
|
||||||
st off, c
|
|
||||||
} else {
|
|
||||||
st on, c
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME these trashes, strictly speaking, probably shouldn't be needed,
|
// FIXME these trashes, strictly speaking, probably shouldn't be needed,
|
||||||
@ -373,8 +372,6 @@ define enemy_logic logic_routine
|
|||||||
copy $ffd8, delta
|
copy $ffd8, delta
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
st off, c
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
@ -408,6 +405,7 @@ define game_state_title_screen game_state_routine
|
|||||||
define game_state_play game_state_routine
|
define game_state_play game_state_routine
|
||||||
{
|
{
|
||||||
ld x, 0
|
ld x, 0
|
||||||
|
st x, player_died
|
||||||
for x up to 15 {
|
for x up to 15 {
|
||||||
copy actor_pos + x, pos
|
copy actor_pos + x, pos
|
||||||
copy actor_delta + x, delta
|
copy actor_delta + x, delta
|
||||||
@ -420,18 +418,19 @@ define game_state_play game_state_routine
|
|||||||
save x {
|
save x {
|
||||||
copy actor_logic + x, dispatch_logic
|
copy actor_logic + x, dispatch_logic
|
||||||
call dispatch_logic
|
call dispatch_logic
|
||||||
|
|
||||||
if c {
|
|
||||||
// Player died! Want no dead!
|
|
||||||
call clear_screen
|
|
||||||
copy game_state_game_over, dispatch_game_state
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
copy pos, actor_pos + x
|
copy pos, actor_pos + x
|
||||||
copy delta, actor_delta + x
|
copy delta, actor_delta + x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ld a, player_died
|
||||||
|
if not z {
|
||||||
|
// Player died! Want no dead!
|
||||||
|
call clear_screen
|
||||||
|
copy game_state_game_over, dispatch_game_state
|
||||||
|
}
|
||||||
|
|
||||||
goto save_cinv
|
goto save_cinv
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -550,6 +550,8 @@ class Analyzer(object):
|
|||||||
context.invalidate_range(dest)
|
context.invalidate_range(dest)
|
||||||
elif opcode == 'call':
|
elif opcode == 'call':
|
||||||
type = instr.location.type
|
type = instr.location.type
|
||||||
|
if not isinstance(type, (RoutineType, VectorType)):
|
||||||
|
raise TypeMismatchError(instr, instr.location)
|
||||||
if isinstance(type, VectorType):
|
if isinstance(type, VectorType):
|
||||||
type = type.of_type
|
type = type.of_type
|
||||||
for ref in type.inputs:
|
for ref in type.inputs:
|
||||||
|
@ -37,14 +37,16 @@ class AST(object):
|
|||||||
def all_children(self):
|
def all_children(self):
|
||||||
for attr in self.children_attrs:
|
for attr in self.children_attrs:
|
||||||
for child in self.attrs[attr]:
|
for child in self.attrs[attr]:
|
||||||
|
if child is not None:
|
||||||
|
yield child
|
||||||
|
for subchild in child.all_children():
|
||||||
|
yield subchild
|
||||||
|
for attr in self.child_attrs:
|
||||||
|
child = self.attrs[attr]
|
||||||
|
if child is not None:
|
||||||
yield child
|
yield child
|
||||||
for subchild in child.all_children():
|
for subchild in child.all_children():
|
||||||
yield subchild
|
yield subchild
|
||||||
for attr in self.child_attrs:
|
|
||||||
child = self.attrs[attr]
|
|
||||||
yield child
|
|
||||||
for subchild in child.all_children():
|
|
||||||
yield subchild
|
|
||||||
|
|
||||||
|
|
||||||
class Program(AST):
|
class Program(AST):
|
||||||
|
@ -18,20 +18,6 @@ class Type(object):
|
|||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return hash(self.name)
|
return hash(self.name)
|
||||||
|
|
||||||
def backpatch_constraint_labels(self, resolver):
|
|
||||||
def resolve(w):
|
|
||||||
if not isinstance(w, str):
|
|
||||||
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', max_range=(0, 1))
|
TYPE_BIT = Type('bit', max_range=(0, 1))
|
||||||
TYPE_BYTE = Type('byte', max_range=(0, 255))
|
TYPE_BYTE = Type('byte', max_range=(0, 255))
|
||||||
@ -41,11 +27,11 @@ TYPE_WORD = Type('word', max_range=(0, 65535))
|
|||||||
|
|
||||||
class RoutineType(Type):
|
class RoutineType(Type):
|
||||||
"""This memory location contains the code for a routine."""
|
"""This memory location contains the code for a routine."""
|
||||||
def __init__(self, inputs=None, outputs=None, trashes=None):
|
def __init__(self, inputs, outputs, trashes):
|
||||||
self.name = 'routine'
|
self.name = 'routine'
|
||||||
self.inputs = inputs or set()
|
self.inputs = inputs
|
||||||
self.outputs = outputs or set()
|
self.outputs = outputs
|
||||||
self.trashes = trashes or set()
|
self.trashes = trashes
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '%s(%r, inputs=%r, outputs=%r, trashes=%r)' % (
|
return '%s(%r, inputs=%r, outputs=%r, trashes=%r)' % (
|
||||||
|
@ -18,6 +18,14 @@ class SymEntry(object):
|
|||||||
return "%s(%r, %r)" % (self.__class__.__name__, self.ast_node, self.model)
|
return "%s(%r, %r)" % (self.__class__.__name__, self.ast_node, self.model)
|
||||||
|
|
||||||
|
|
||||||
|
class ForwardReference(object):
|
||||||
|
def __init__(self, name):
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "%s(%r)" % (self.__class__.__name__, self.name)
|
||||||
|
|
||||||
|
|
||||||
class ParsingContext(object):
|
class ParsingContext(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.symbols = {} # token -> SymEntry
|
self.symbols = {} # token -> SymEntry
|
||||||
@ -45,7 +53,6 @@ class Parser(object):
|
|||||||
def __init__(self, context, text, filename):
|
def __init__(self, context, text, filename):
|
||||||
self.context = context
|
self.context = context
|
||||||
self.scanner = Scanner(text, filename)
|
self.scanner = Scanner(text, filename)
|
||||||
self.backpatch_instrs = []
|
|
||||||
|
|
||||||
def syntax_error(self, msg):
|
def syntax_error(self, msg):
|
||||||
self.scanner.syntax_error(msg)
|
self.scanner.syntax_error(msg)
|
||||||
@ -67,6 +74,44 @@ class Parser(object):
|
|||||||
def clear_statics(self):
|
def clear_statics(self):
|
||||||
self.context.statics = {}
|
self.context.statics = {}
|
||||||
|
|
||||||
|
# ---- symbol resolution
|
||||||
|
|
||||||
|
def resolve_symbols(self, program):
|
||||||
|
# This could stand to be better unified.
|
||||||
|
|
||||||
|
def backpatch_constraint_labels(type_):
|
||||||
|
def resolve(w):
|
||||||
|
if not isinstance(w, ForwardReference):
|
||||||
|
return w
|
||||||
|
return self.lookup(w.name)
|
||||||
|
if isinstance(type_, TableType):
|
||||||
|
backpatch_constraint_labels(type_.of_type)
|
||||||
|
elif isinstance(type_, VectorType):
|
||||||
|
backpatch_constraint_labels(type_.of_type)
|
||||||
|
elif isinstance(type_, RoutineType):
|
||||||
|
type_.inputs = set([resolve(w) for w in type_.inputs])
|
||||||
|
type_.outputs = set([resolve(w) for w in type_.outputs])
|
||||||
|
type_.trashes = set([resolve(w) for w in type_.trashes])
|
||||||
|
|
||||||
|
for defn in program.defns:
|
||||||
|
backpatch_constraint_labels(defn.location.type)
|
||||||
|
for routine in program.routines:
|
||||||
|
backpatch_constraint_labels(routine.location.type)
|
||||||
|
|
||||||
|
def resolve_fwd_reference(obj, field):
|
||||||
|
field_value = getattr(obj, field, None)
|
||||||
|
if isinstance(field_value, ForwardReference):
|
||||||
|
setattr(obj, field, self.lookup(field_value.name))
|
||||||
|
elif isinstance(field_value, IndexedRef):
|
||||||
|
if isinstance(field_value.ref, ForwardReference):
|
||||||
|
field_value.ref = self.lookup(field_value.ref.name)
|
||||||
|
|
||||||
|
for node in program.all_children():
|
||||||
|
if isinstance(node, SingleOp):
|
||||||
|
resolve_fwd_reference(node, 'location')
|
||||||
|
resolve_fwd_reference(node, 'src')
|
||||||
|
resolve_fwd_reference(node, 'dest')
|
||||||
|
|
||||||
# --- grammar productions
|
# --- grammar productions
|
||||||
|
|
||||||
def program(self):
|
def program(self):
|
||||||
@ -91,28 +136,9 @@ class Parser(object):
|
|||||||
routines.append(routine)
|
routines.append(routine)
|
||||||
self.scanner.check_type('EOF')
|
self.scanner.check_type('EOF')
|
||||||
|
|
||||||
# now backpatch the executable types.
|
program = Program(self.scanner.line_number, defns=defns, routines=routines)
|
||||||
#for type_name, type_ in self.context.typedefs.items():
|
self.resolve_symbols(program)
|
||||||
# type_.backpatch_constraint_labels(lambda w: self.lookup(w))
|
return program
|
||||||
for defn in defns:
|
|
||||||
defn.location.type.backpatch_constraint_labels(lambda w: self.lookup(w))
|
|
||||||
for routine in routines:
|
|
||||||
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
|
|
||||||
model = self.lookup(name)
|
|
||||||
if not isinstance(model.type, (RoutineType, VectorType)):
|
|
||||||
self.syntax_error('Illegal call of non-executable "%s"' % name)
|
|
||||||
instr.location = model
|
|
||||||
if instr.opcode in ('copy',) and isinstance(instr.src, str):
|
|
||||||
name = instr.src
|
|
||||||
model = self.lookup(name)
|
|
||||||
if not isinstance(model.type, (RoutineType, VectorType)):
|
|
||||||
self.syntax_error('Illegal copy of non-executable "%s"' % name)
|
|
||||||
instr.src = model
|
|
||||||
|
|
||||||
return Program(self.scanner.line_number, defns=defns, routines=routines)
|
|
||||||
|
|
||||||
def typedef(self):
|
def typedef(self):
|
||||||
self.scanner.expect('typedef')
|
self.scanner.expect('typedef')
|
||||||
@ -252,7 +278,11 @@ class Parser(object):
|
|||||||
outputs = set(self.labels())
|
outputs = set(self.labels())
|
||||||
if self.scanner.consume('trashes'):
|
if self.scanner.consume('trashes'):
|
||||||
trashes = set(self.labels())
|
trashes = set(self.labels())
|
||||||
return (inputs, outputs, trashes)
|
return (
|
||||||
|
set([ForwardReference(n) for n in inputs]),
|
||||||
|
set([ForwardReference(n) for n in outputs]),
|
||||||
|
set([ForwardReference(n) for n in trashes])
|
||||||
|
)
|
||||||
|
|
||||||
def routine(self, name):
|
def routine(self, name):
|
||||||
type_ = self.defn_type()
|
type_ = self.defn_type()
|
||||||
@ -302,23 +332,19 @@ class Parser(object):
|
|||||||
accum.append(self.locexpr())
|
accum.append(self.locexpr())
|
||||||
return accum
|
return accum
|
||||||
|
|
||||||
def locexpr(self, forward=False):
|
def locexpr(self):
|
||||||
if self.scanner.token in ('on', 'off', 'word') or self.scanner.token in self.context.consts or self.scanner.on_type('integer literal'):
|
if self.scanner.token in ('on', 'off', 'word') or self.scanner.token in self.context.consts or self.scanner.on_type('integer literal'):
|
||||||
return self.const()
|
return self.const()
|
||||||
elif forward:
|
else:
|
||||||
name = self.scanner.token
|
name = self.scanner.token
|
||||||
self.scanner.scan()
|
self.scanner.scan()
|
||||||
loc = self.context.fetch(name)
|
loc = self.context.fetch(name)
|
||||||
if loc is not None:
|
if loc:
|
||||||
return loc
|
return loc
|
||||||
else:
|
else:
|
||||||
return name
|
return ForwardReference(name)
|
||||||
else:
|
|
||||||
loc = self.lookup(self.scanner.token)
|
|
||||||
self.scanner.scan()
|
|
||||||
return loc
|
|
||||||
|
|
||||||
def indlocexpr(self, forward=False):
|
def indlocexpr(self):
|
||||||
if self.scanner.consume('['):
|
if self.scanner.consume('['):
|
||||||
loc = self.locexpr()
|
loc = self.locexpr()
|
||||||
self.scanner.expect(']')
|
self.scanner.expect(']')
|
||||||
@ -329,10 +355,10 @@ class Parser(object):
|
|||||||
loc = self.locexpr()
|
loc = self.locexpr()
|
||||||
return AddressRef(loc)
|
return AddressRef(loc)
|
||||||
else:
|
else:
|
||||||
return self.indexed_locexpr(forward=forward)
|
return self.indexed_locexpr()
|
||||||
|
|
||||||
def indexed_locexpr(self, forward=False):
|
def indexed_locexpr(self):
|
||||||
loc = self.locexpr(forward=forward)
|
loc = self.locexpr()
|
||||||
if not isinstance(loc, str):
|
if not isinstance(loc, str):
|
||||||
index = None
|
index = None
|
||||||
if self.scanner.consume('+'):
|
if self.scanner.consume('+'):
|
||||||
@ -427,17 +453,15 @@ class Parser(object):
|
|||||||
self.scanner.scan()
|
self.scanner.scan()
|
||||||
name = self.scanner.token
|
name = self.scanner.token
|
||||||
self.scanner.scan()
|
self.scanner.scan()
|
||||||
instr = SingleOp(self.scanner.line_number, opcode=opcode, location=name, dest=None, src=None)
|
instr = SingleOp(self.scanner.line_number, opcode=opcode, location=ForwardReference(name), dest=None, src=None)
|
||||||
self.backpatch_instrs.append(instr)
|
|
||||||
return instr
|
return instr
|
||||||
elif self.scanner.token in ("copy",):
|
elif self.scanner.token in ("copy",):
|
||||||
opcode = self.scanner.token
|
opcode = self.scanner.token
|
||||||
self.scanner.scan()
|
self.scanner.scan()
|
||||||
src = self.indlocexpr(forward=True)
|
src = self.indlocexpr()
|
||||||
self.scanner.expect(',')
|
self.scanner.expect(',')
|
||||||
dest = self.indlocexpr()
|
dest = self.indlocexpr()
|
||||||
instr = SingleOp(self.scanner.line_number, opcode=opcode, dest=dest, src=src)
|
instr = SingleOp(self.scanner.line_number, opcode=opcode, dest=dest, src=src)
|
||||||
self.backpatch_instrs.append(instr)
|
|
||||||
return instr
|
return instr
|
||||||
elif self.scanner.consume("with"):
|
elif self.scanner.consume("with"):
|
||||||
self.scanner.expect("interrupts")
|
self.scanner.expect("interrupts")
|
||||||
|
@ -196,6 +196,35 @@ If a routine reads or writes a user-define memory location, it needs to declare
|
|||||||
| }
|
| }
|
||||||
= ok
|
= ok
|
||||||
|
|
||||||
|
### call ###
|
||||||
|
|
||||||
|
You can't call a non-routine.
|
||||||
|
|
||||||
|
| byte up
|
||||||
|
|
|
||||||
|
| routine main outputs x, y trashes z, n {
|
||||||
|
| ld x, 0
|
||||||
|
| ld y, 1
|
||||||
|
| call up
|
||||||
|
| }
|
||||||
|
? TypeMismatchError: up
|
||||||
|
|
||||||
|
| routine main outputs x, y trashes z, n {
|
||||||
|
| ld x, 0
|
||||||
|
| ld y, 1
|
||||||
|
| call x
|
||||||
|
| }
|
||||||
|
? TypeMismatchError: x
|
||||||
|
|
||||||
|
Nor can you goto a non-routine.
|
||||||
|
|
||||||
|
| byte foo
|
||||||
|
|
|
||||||
|
| routine main {
|
||||||
|
| goto foo
|
||||||
|
| }
|
||||||
|
? TypeMismatchError: foo
|
||||||
|
|
||||||
### ld ###
|
### ld ###
|
||||||
|
|
||||||
Can't `ld` from a memory location that isn't initialized.
|
Can't `ld` from a memory location that isn't initialized.
|
||||||
|
@ -404,24 +404,6 @@ Can't call routine that hasn't been defined.
|
|||||||
| }
|
| }
|
||||||
? SyntaxError
|
? SyntaxError
|
||||||
|
|
||||||
And you can't call a non-routine.
|
|
||||||
|
|
||||||
| byte up
|
|
||||||
|
|
|
||||||
| define main routine {
|
|
||||||
| ld x, 0
|
|
||||||
| ld y, 1
|
|
||||||
| call up
|
|
||||||
| }
|
|
||||||
? SyntaxError
|
|
||||||
|
|
||||||
| define main routine {
|
|
||||||
| ld x, 0
|
|
||||||
| ld y, 1
|
|
||||||
| call x
|
|
||||||
| }
|
|
||||||
? SyntaxError
|
|
||||||
|
|
||||||
But you can call a routine that is yet to be defined, further on.
|
But you can call a routine that is yet to be defined, further on.
|
||||||
|
|
||||||
| define main routine {
|
| define main routine {
|
||||||
@ -589,13 +571,6 @@ goto.
|
|||||||
| }
|
| }
|
||||||
? SyntaxError
|
? SyntaxError
|
||||||
|
|
||||||
| byte foo
|
|
||||||
|
|
|
||||||
| define main routine {
|
|
||||||
| goto foo
|
|
||||||
| }
|
|
||||||
? SyntaxError
|
|
||||||
|
|
||||||
Buffers and pointers.
|
Buffers and pointers.
|
||||||
|
|
||||||
| buffer[2048] buf
|
| buffer[2048] buf
|
||||||
|
Loading…
x
Reference in New Issue
Block a user