git @ Cat's Eye Technologies SixtyPical / 19dd089
Allow `call` and `goto` routines defined further down in the source. Chris Pressey 3 years ago
5 changed file(s) with 53 addition(s) and 13 deletion(s). Raw diff Collapse all Expand all
00 History of SixtyPical
11 =====================
2
3 0.10
4 ----
5
6 * Can `call` and `goto` routines that are defined further down in the source code.
27
38 0.9
49 ---
4545 the joystick (i.e. bring it up to par with the original demo game that was written
4646 for SixtyPical)
4747
48 ### `call` routines that are defined further down in the source code
49
50 We might have a graph of states that refer to each other and that want to `goto`
51 each other. Thus we need this. We have it for vectors, but we need it for `call`.
52
5348 ### Allow branches to diverge in what they touch
5449
5550 For example, if the routine inputs and outputs `foo`, and one branch of an `if`
107107 def is_constant(self):
108108 return isinstance(self.type, RoutineType)
109109
110 def backpatch_labels(self, resolver):
110 def backpatch_vector_labels(self, resolver):
111111 if isinstance(self.type, ExecutableType):
112112 t = self.type
113113 t.inputs = set([resolver(w) for w in t.inputs])
2727 if name not in self.symbols:
2828 raise SyntaxError('Undefined symbol "%s"' % name)
2929 return self.symbols[name].model
30
31 def backpatch_call_labels(self, block):
32 """Backpatches labels in call and goto instructions."""
33 if block is None:
34 return
35 for instr in block.instrs:
36 if instr.opcode == 'if':
37 self.backpatch_call_labels(instr.block1)
38 self.backpatch_call_labels(instr.block2)
39 elif instr.opcode == 'repeat':
40 self.backpatch_call_labels(instr.block)
41 elif instr.opcode == 'with-sei':
42 self.backpatch_call_labels(instr.block)
43 elif instr.opcode in ('call', 'goto'):
44 if isinstance(instr.location, basestring):
45 name = instr.location
46 if name not in self.symbols:
47 raise SyntaxError('Undefined routine "%s"' % name)
48 if not isinstance(self.symbols[name].model.type, ExecutableType):
49 raise SyntaxError('Illegal call of non-executable "%s"' % name)
50 instr.location = self.symbols[name].model
3051
3152 # --- grammar productions
3253
5071 self.scanner.check_type('EOF')
5172 # now backpatch the executable types.
5273 for defn in defns:
53 defn.location.backpatch_labels(lambda w: self.lookup(w))
74 defn.location.backpatch_vector_labels(lambda w: self.lookup(w))
5475 for routine in routines:
55 routine.location.backpatch_labels(lambda w: self.lookup(w))
76 routine.location.backpatch_vector_labels(lambda w: self.lookup(w))
77 self.backpatch_call_labels(routine.block)
5678 return Program(defns=defns, routines=routines)
5779
5880 def defn(self):
264286 self.scanner.scan()
265287 name = self.scanner.token
266288 self.scanner.scan()
267 if name not in self.symbols:
268 raise SyntaxError('Undefined routine "%s"' % name)
269 if not isinstance(self.symbols[name].model.type, ExecutableType):
270 raise SyntaxError('Illegal call of non-executable "%s"' % name)
271 return Instr(opcode=opcode, location=self.symbols[name].model, dest=None, src=None)
289 # this will be backpatched
290 return Instr(opcode=opcode, location=name, dest=None, src=None)
272291 elif self.scanner.token in ("copy",):
273292 opcode = self.scanner.token
274293 self.scanner.scan()
238238 | }
239239 ? SyntaxError
240240
241 But you can call a routine that is yet to be defined, further on.
242
243 | routine main {
244 | ld x, 0
245 | ld y, 1
246 | call up
247 | call up
248 | }
249 | routine up {
250 | ld a, 0
251 | }
252 = ok
253
241254 Can't define two routines with the same name.
242255
243256 | routine main {
352365 | }
353366 = ok
354367
368 | routine main {
369 | goto foo
370 | }
371 | routine foo {
372 | ld a, 0
373 | }
374 = ok
375
355376 | vector foo
356377 |
357378 | routine main {