1
0
mirror of https://github.com/catseye/SixtyPical.git synced 2024-11-25 23:49:17 +00:00

Kind-of not-pretty syntax refactor to support adding typedefs.

This commit is contained in:
Chris Pressey 2018-02-05 17:01:25 +00:00
parent 15072eff52
commit 0be721667a
6 changed files with 193 additions and 141 deletions

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
)
@ -303,6 +303,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 +368,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 +381,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 +389,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.inputs, dest.type.of_type.inputs)
self.assert_affected_within('outputs', src.type.outputs, dest.type.of_type.outputs)
self.assert_affected_within('trashes', src.type.trashes, dest.type.of_type.trashes)
else:
raise TypeMismatchError((src, dest))
else:
@ -434,10 +439,12 @@ 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)

View File

@ -17,16 +17,27 @@ class Type(object):
def __hash__(self):
return hash(self.name)
def backpatch_constraint_labels(self, resolver):
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([resolver(w) for w in self.inputs])
self.outputs = set([resolver(w) for w in self.outputs])
self.trashes = set([resolver(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 +48,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 +61,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 +76,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 +156,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
@ -35,16 +35,20 @@ class Parser(object):
def program(self):
defns = []
routines = []
while self.scanner.on('byte', 'word', 'vector', 'buffer', 'pointer'):
while self.scanner.on('byte', 'word', 'table', 'vector', 'buffer', 'pointer'): # 'routine',
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()
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)
@ -53,29 +57,30 @@ class Parser(object):
# now backpatch the executable types.
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 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 +112,28 @@ class Parser(object):
self.scanner.expect(']')
return size
def defn_type_and_name(self):
def defn_type(self):
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
return TYPE_BYTE
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
return TYPE_WORD
elif self.scanner.consume('table'):
size = self.defn_size()
type_ = self.defn_type()
return TableType(type_, size)
elif self.scanner.consume('vector'):
size = None
if self.scanner.consume('table'):
size = self.defn_size()
name = self.defn_name()
type_ = self.defn_type()
# TODO: assert that it's a routine type
return 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
return RoutineType(inputs=inputs, outputs=outputs, trashes=trashes)
elif self.scanner.consume('buffer'):
size = self.defn_size()
name = self.defn_name()
return BufferType(size), name
return BufferType(size)
else:
self.scanner.expect('pointer')
name = self.defn_name()
return PointerType(), name
return PointerType()
def defn_name(self):
self.scanner.check_type('identifier')
@ -159,11 +153,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 +167,26 @@ 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 = self.scanner.token
self.scanner.scan()
type_ = self.defn_type()
# TODO assert that it's a routine
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

@ -243,7 +243,7 @@ Can't `st` a `word` type.
Storing to a table, you must use an index.
| byte one
| byte table[256] many
| table[256] byte many
|
| routine main
| outputs one
@ -256,7 +256,7 @@ Storing to a table, you must use an index.
= ok
| byte one
| byte table[256] many
| table[256] byte many
|
| routine main
| outputs many
@ -269,7 +269,7 @@ Storing to a table, you must use an index.
? TypeMismatchError
| byte one
| byte table[256] many
| table[256] byte many
|
| routine main
| outputs one
@ -282,7 +282,7 @@ Storing to a table, you must use an index.
? TypeMismatchError
| byte one
| byte table[256] many
| table[256] byte many
|
| routine main
| outputs many
@ -297,7 +297,7 @@ Storing to a table, you must use an index.
The index must be initialized.
| byte one
| byte table[256] many
| table[256] byte many
|
| routine main
| outputs many
@ -334,7 +334,7 @@ Reading from a table, you must use an index.
| }
? TypeMismatchError
| byte table[256] many
| table[256] byte many
|
| routine main
| outputs many
@ -347,7 +347,7 @@ Reading from a table, you must use an index.
| }
? TypeMismatchError
| byte table[256] many
| table[256] byte many
|
| routine main
| outputs many
@ -360,7 +360,7 @@ Reading from a table, you must use an index.
| }
= ok
| byte table[256] many
| table[256] byte many
|
| routine main
| inputs many
@ -374,7 +374,7 @@ Reading from a table, you must use an index.
The index must be initialized.
| byte table[256] many
| table[256] byte many
|
| routine main
| inputs many
@ -388,7 +388,7 @@ The index must be initialized.
Copying to and from a word table.
| word one
| word table[256] many
| table[256] word many
|
| routine main
| inputs one, many
@ -402,7 +402,7 @@ Copying to and from a word table.
= ok
| word one
| word table[256] many
| table[256] word many
|
| routine main
| inputs one, many
@ -415,7 +415,7 @@ Copying to and from a word table.
? TypeMismatchError
| word one
| word table[256] many
| table[256] word many
|
| routine main
| inputs one, many
@ -429,7 +429,7 @@ Copying to and from a word table.
You can also copy a literal word to a word table.
| word table[256] many
| table[256] word many
|
| routine main
| inputs many
@ -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
@ -1935,12 +1945,14 @@ vector says it does.
A vector can be copied into a vector table.
| vector one
| vector routine
| outputs x
| trashes a, z, n
| vector table[256] many
| one
| table[256] vector routine
| outputs x
| trashes a, z, n
| 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
| table[256] vector routine
| outputs x
| trashes a, z, n
| 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
| table[256] vector routine
| outputs x
| trashes a, z, n
| 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
| table[256] vector routine
| outputs x
| trashes a, z, n
| many
|
| routine bar outputs x trashes a, z, n {
| ld x, 200

View File

@ -139,7 +139,7 @@ Word memory locations with explicit address, initial value.
Initialized byte table. Bytes allocated, but beyond the string, are 0's.
| byte table[8] message : "WHAT?"
| table[8] byte message : "WHAT?"
|
| routine main
| inputs message
@ -352,7 +352,7 @@ The body of `repeat forever` can be empty.
Indexed access.
| byte one
| byte table[256] many
| table[256] byte many
|
| routine main
| outputs many
@ -371,8 +371,8 @@ Indexed access.
Byte tables take up 256 bytes in memory.
| byte table[256] tab1
| byte table[256] tab2
| table[256] byte tab1
| table[256] byte tab2
|
| routine main
| inputs tab1
@ -458,7 +458,7 @@ Copy literal word to word.
You can also copy a literal word to a word table.
| word table[256] many
| table[256] word many
|
| routine main
| inputs many
@ -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
@ -527,7 +527,7 @@ Copy routine to vector, inside an `interrupts off` block.
Copy word to word table and back, with both `x` and `y` as indexes.
| word one
| word table[256] many
| table[256] word many
|
| routine main
| inputs one, many
@ -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
| table[256] vector routine
| outputs x
| trashes a, z, n
| 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,16 @@ User-defined memory addresses of different types.
| }
= ok
Tables of different types.
| table[256] byte tab
| table[256] word wtab
| table[256] vector routine trashes a vtab
|
| routine main {
| }
= ok
Explicit memory address.
| byte screen @ 1024
@ -178,7 +185,7 @@ Cannot have both initial value and explicit address.
User-defined locations of other types.
| byte table[256] screen @ 1024
| table[256] byte screen @ 1024
| word r1
| word r2 @ 60000
| word r3 : 2000
@ -189,7 +196,7 @@ User-defined locations of other types.
Initialized byte table.
| byte table[28] message : "WHAT DO YOU WANT TO DO NEXT?"
| table[28] byte message : "WHAT DO YOU WANT TO DO NEXT?"
|
| routine main {
| }
@ -291,7 +298,7 @@ Can't define two routines with the same name.
Declaring byte and word table memory location.
| byte table[256] tab
| table[256] byte tab
|
| routine main {
| ld x, 0
@ -302,7 +309,7 @@ Declaring byte and word table memory location.
= ok
| word one
| word table[256] many
| table[256] word many
|
| routine main {
| ld x, 0
@ -314,11 +321,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 +352,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 +371,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 +391,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 +407,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 +441,7 @@ goto.
| }
= ok
| vector foo
| vector routine foo
|
| routine main {
| goto foo