1
0
mirror of https://github.com/catseye/SixtyPical.git synced 2024-06-06 15:29:30 +00:00

Allow call and goto routines defined further down in the source.

This commit is contained in:
Chris Pressey 2017-12-12 13:17:00 +00:00
parent 45bc4bd0a0
commit 19dd089a03
5 changed files with 53 additions and 13 deletions

View File

@ -1,6 +1,11 @@
History of SixtyPical
=====================
0.10
----
* Can `call` and `goto` routines that are defined further down in the source code.
0.9
---

View File

@ -46,11 +46,6 @@ Finish the little demo "game" where you can move a block around the screen with
the joystick (i.e. bring it up to par with the original demo game that was written
for SixtyPical)
### `call` routines that are defined further down in the source code
We might have a graph of states that refer to each other and that want to `goto`
each other. Thus we need this. We have it for vectors, but we need it for `call`.
### Allow branches to diverge in what they touch
For example, if the routine inputs and outputs `foo`, and one branch of an `if`

View File

@ -108,7 +108,7 @@ class LocationRef(Ref):
def is_constant(self):
return isinstance(self.type, RoutineType)
def backpatch_labels(self, resolver):
def backpatch_vector_labels(self, resolver):
if isinstance(self.type, ExecutableType):
t = self.type
t.inputs = set([resolver(w) for w in t.inputs])

View File

@ -29,6 +29,27 @@ class Parser(object):
raise SyntaxError('Undefined symbol "%s"' % name)
return self.symbols[name].model
def backpatch_call_labels(self, block):
"""Backpatches labels in call and goto instructions."""
if block is None:
return
for instr in block.instrs:
if instr.opcode == 'if':
self.backpatch_call_labels(instr.block1)
self.backpatch_call_labels(instr.block2)
elif instr.opcode == 'repeat':
self.backpatch_call_labels(instr.block)
elif instr.opcode == 'with-sei':
self.backpatch_call_labels(instr.block)
elif instr.opcode in ('call', 'goto'):
if isinstance(instr.location, basestring):
name = instr.location
if name not in self.symbols:
raise SyntaxError('Undefined routine "%s"' % name)
if not isinstance(self.symbols[name].model.type, ExecutableType):
raise SyntaxError('Illegal call of non-executable "%s"' % name)
instr.location = self.symbols[name].model
# --- grammar productions
def program(self):
@ -51,9 +72,10 @@ class Parser(object):
self.scanner.check_type('EOF')
# now backpatch the executable types.
for defn in defns:
defn.location.backpatch_labels(lambda w: self.lookup(w))
defn.location.backpatch_vector_labels(lambda w: self.lookup(w))
for routine in routines:
routine.location.backpatch_labels(lambda w: self.lookup(w))
routine.location.backpatch_vector_labels(lambda w: self.lookup(w))
self.backpatch_call_labels(routine.block)
return Program(defns=defns, routines=routines)
def defn(self):
@ -265,11 +287,8 @@ class Parser(object):
self.scanner.scan()
name = self.scanner.token
self.scanner.scan()
if name not in self.symbols:
raise SyntaxError('Undefined routine "%s"' % name)
if not isinstance(self.symbols[name].model.type, ExecutableType):
raise SyntaxError('Illegal call of non-executable "%s"' % name)
return Instr(opcode=opcode, location=self.symbols[name].model, dest=None, src=None)
# this will be backpatched
return Instr(opcode=opcode, location=name, dest=None, src=None)
elif self.scanner.token in ("copy",):
opcode = self.scanner.token
self.scanner.scan()

View File

@ -239,6 +239,19 @@ And you can't call a non-routine.
| }
? SyntaxError
But you can call a routine that is yet to be defined, further on.
| routine main {
| ld x, 0
| ld y, 1
| call up
| call up
| }
| routine up {
| ld a, 0
| }
= ok
Can't define two routines with the same name.
| routine main {
@ -353,6 +366,14 @@ goto.
| }
= ok
| routine main {
| goto foo
| }
| routine foo {
| ld a, 0
| }
= ok
| vector foo
|
| routine main {