1
0
mirror of https://github.com/catseye/SixtyPical.git synced 2024-07-05 15:29:07 +00:00
This commit is contained in:
Chris Pressey 2015-10-21 19:09:23 +01:00
commit ed44a8c1b9
13 changed files with 518 additions and 276 deletions

View File

@ -33,12 +33,17 @@ TODO
For 0.6:
* `call` vector (generates an indirect JMP.)
* `goto` (tail call) a routine or a vector.
* A more involved demo for the C64 — one that sets up an interrupt?
* `call` vector (generates an JSR to a trampoline that does indirect JMP.)
* `goto` (tail call) a a vector.
* add routine name to error messages.
* routines shouldn't need to be listed as inputs.
For 0.7:
* A more involved demo for the C64 — one that sets up an interrupt?
For 0.8:
* `word` type.
* `trash` instruction.
* zero-page memory locations.
@ -47,7 +52,6 @@ For 0.7:
At some point...
* `interrupt` routines.
* add line number (or at least routine name) to error messages.
* 6502-mnemonic aliases (`sec`, `clc`)
* other handy aliases (`eq` for `z`, etc.)
* have `copy` instruction able to copy a constant to a user-def mem loc, etc.

View File

@ -18,8 +18,8 @@ import sys
import traceback
from sixtypical.parser import Parser
from sixtypical.evaluator import eval_program
from sixtypical.analyzer import analyze_program
from sixtypical.evaluator import Evaluator
from sixtypical.analyzer import Analyzer
from sixtypical.emitter import Emitter, Byte, Word
from sixtypical.compiler import Compiler
@ -50,12 +50,13 @@ if __name__ == '__main__':
for filename in args:
text = open(filename).read()
p = Parser(text)
program = p.program()
parser = Parser(text)
program = parser.program()
if options.analyze:
try:
analyze_program(program)
analyzer = Analyzer()
analyzer.analyze_program(program)
except Exception as e:
if options.traceback:
raise
@ -88,5 +89,5 @@ if __name__ == '__main__':
emitter.serialize(fh)
if options.execute:
context = eval_program(program)
context = Evaluator().eval_program(program)
print str(context)

View File

@ -363,16 +363,20 @@ copy more general types of data (for example, vectors,) and it trashes the
After execution, dest is considered initialized, and `z` and `n`, and
`a` are considered uninitialized.
### goto ###
TBW
Grammar
-------
Program ::= {Defn} {Routine}.
Defn ::= Type NewIdent [Constraints] ["@" WordConst].
Defn ::= Type Ident<new> [Constraints] ["@" WordConst].
Type ::= "byte" ["table"] | "vector"
Constrnt::= ["inputs" LocExprs] ["outputs" LocExprs] ["trashes" LocExprs].
Routine ::= "routine" NewIdent Constraints (Block | "@" WordConst).
Routine ::= "routine" Ident<new> Constraints (Block | "@" WordConst).
LocExprs::= LocExpr {"," LocExpr}.
LocExpr ::= Register | Flag | LitByte | DefnIdent.
LocExpr ::= Register | Flag | LitByte | Ident.
Register::= "a" | "x" | "y".
Flag ::= "c" | "z" | "n" | "v".
LitByte ::= "0" ... "255".
@ -390,7 +394,8 @@ Grammar
| "shr" LocExpr
| "inc" LocExpr
| "dec" LocExpr
| "call" RoutineIdent
| "call" Ident<routine>
| "goto" Ident<executable>
| "if" ["not"] LocExpr Block ["else" Block]
| "repeat" Block ("until" ["not"] LocExpr | "forever")
| "copy" LocExpr "," LocExpr ["+" LocExpr]

15
eg/goto.60p Normal file
View File

@ -0,0 +1,15 @@
routine chrout
inputs a
trashes a
@ 65490
routine bar trashes a, z, n {
ld a, 66
call chrout
}
routine main trashes a, z, n {
ld a, 65
call chrout
goto bar
}

10
eg/vector.60p Normal file
View File

@ -0,0 +1,10 @@
vector foo outputs x trashes z, n
routine bar outputs x trashes z, n {
ld x, 200
}
routine main inputs bar outputs x, foo trashes a, z, n {
copy bar, foo
call foo
}

View File

@ -41,6 +41,10 @@ class IncompatibleConstraintsError(StaticAnalysisError):
pass
class IllegalJumpError(StaticAnalysisError):
pass
class Context():
"""
A location is touched if it was changed (or even potentially
@ -126,135 +130,164 @@ class Context():
self.set_touched(*refs)
self.set_meaningful(*refs)
def analyze_program(program):
assert isinstance(program, Program)
routines = {r.name: r for r in program.routines}
for routine in program.routines:
analyze_routine(routine, routines)
class Analyzer(object):
def analyze_routine(routine, routines):
assert isinstance(routine, Routine)
if routine.block is None:
# it's an extern, that's fine
return
type = routine.location.type
context = Context(type.inputs, type.outputs, type.trashes)
analyze_block(routine.block, context, routines)
for ref in type.outputs:
context.assert_meaningful(ref, exception_class=UninitializedOutputError)
for ref in context.each_touched():
if ref not in type.outputs and ref not in type.trashes:
raise IllegalWriteError(ref.name)
def __init__(self):
self.current_routine = None
self.has_encountered_goto = False
def analyze_program(self, program):
assert isinstance(program, Program)
routines = {r.name: r for r in program.routines}
for routine in program.routines:
self.analyze_routine(routine, routines)
def analyze_block(block, context, routines):
assert isinstance(block, Block)
for i in block.instrs:
analyze_instr(i, context, routines)
def analyze_routine(self, routine, routines):
assert isinstance(routine, Routine)
self.current_routine = routine
self.has_encountered_goto = False
if routine.block is None:
# it's an extern, that's fine
return
type = routine.location.type
context = Context(type.inputs, type.outputs, type.trashes)
self.analyze_block(routine.block, context, routines)
if not self.has_encountered_goto:
for ref in type.outputs:
context.assert_meaningful(ref, exception_class=UninitializedOutputError)
for ref in context.each_touched():
if ref not in type.outputs and ref not in type.trashes:
raise IllegalWriteError(ref.name)
self.current_routine = None
def analyze_block(self, block, context, routines):
assert isinstance(block, Block)
for i in block.instrs:
if self.has_encountered_goto:
raise IllegalJumpError(i)
self.analyze_instr(i, context, routines)
def analyze_instr(instr, context, routines):
assert isinstance(instr, Instr)
opcode = instr.opcode
dest = instr.dest
src = instr.src
if opcode == 'ld':
if instr.index:
if src.type == TYPE_BYTE_TABLE and dest.type == TYPE_BYTE:
def analyze_instr(self, instr, context, routines):
assert isinstance(instr, Instr)
opcode = instr.opcode
dest = instr.dest
src = instr.src
if opcode == 'ld':
if instr.index:
if src.type == TYPE_BYTE_TABLE and dest.type == TYPE_BYTE:
pass
else:
raise TypeMismatchError((src, dest))
elif src.type != dest.type:
raise TypeMismatchError((src, dest))
context.assert_meaningful(src)
context.set_written(dest, FLAG_Z, FLAG_N)
elif opcode == 'st':
if instr.index:
if src.type == TYPE_BYTE and dest.type == TYPE_BYTE_TABLE:
pass
else:
raise TypeMismatchError((src, dest))
elif src.type != dest.type:
raise TypeMismatchError((src, dest))
context.assert_meaningful(src)
context.set_written(dest)
elif opcode in ('add', 'sub'):
context.assert_meaningful(src, dest, FLAG_C)
context.set_written(dest, FLAG_Z, FLAG_N, FLAG_C, FLAG_V)
elif opcode in ('inc', 'dec'):
context.assert_meaningful(dest)
context.set_written(dest, FLAG_Z, FLAG_N)
elif opcode == 'cmp':
context.assert_meaningful(src, dest)
context.set_written(FLAG_Z, FLAG_N, FLAG_C)
elif opcode in ('and', 'or', 'xor'):
context.assert_meaningful(src, dest)
context.set_written(dest, FLAG_Z, FLAG_N)
elif opcode in ('shl', 'shr'):
context.assert_meaningful(dest, FLAG_C)
context.set_written(dest, FLAG_Z, FLAG_N, FLAG_C)
elif opcode == 'call':
type = instr.location.type
for ref in type.inputs:
context.assert_meaningful(ref)
for ref in type.outputs:
context.set_written(ref)
for ref in type.trashes:
context.assert_writeable(ref)
context.set_touched(ref)
context.set_unmeaningful(ref)
elif opcode == 'if':
context1 = context.clone()
context2 = context.clone()
self.analyze_block(instr.block1, context1, routines)
if instr.block2 is not None:
self.analyze_block(instr.block2, context2, routines)
# TODO may we need to deal with touched separately here too?
# probably not; if it wasn't meaningful in the first place, it
# doesn't really matter if you modified it or not, coming out.
for ref in context1.each_meaningful():
context2.assert_meaningful(ref, exception_class=InconsistentInitializationError)
for ref in context2.each_meaningful():
context1.assert_meaningful(ref, exception_class=InconsistentInitializationError)
context.set_from(context1)
elif opcode == 'repeat':
# it will always be executed at least once, so analyze it having
# been executed the first time.
self.analyze_block(instr.block, context, routines)
# now analyze it having been executed a second time, with the context
# of it having already been executed.
self.analyze_block(instr.block, context, routines)
# NB I *think* that's enough... but it might not be?
elif opcode == 'copy':
# check that their types are basically compatible
if src.type == dest.type:
pass
elif isinstance(src.type, ExecutableType) and \
isinstance(dest.type, VectorType):
pass
else:
raise TypeMismatchError((src, dest))
elif src.type != dest.type:
raise TypeMismatchError((src, dest))
context.assert_meaningful(src)
context.set_written(dest, FLAG_Z, FLAG_N)
elif opcode == 'st':
if instr.index:
if src.type == TYPE_BYTE and dest.type == TYPE_BYTE_TABLE:
pass
else:
raise TypeMismatchError((src, dest))
elif src.type != dest.type:
raise TypeMismatchError((src, dest))
context.assert_meaningful(src)
context.set_written(dest)
elif opcode in ('add', 'sub'):
context.assert_meaningful(src, dest, FLAG_C)
context.set_written(dest, FLAG_Z, FLAG_N, FLAG_C, FLAG_V)
elif opcode in ('inc', 'dec'):
context.assert_meaningful(dest)
context.set_written(dest, FLAG_Z, FLAG_N)
elif opcode == 'cmp':
context.assert_meaningful(src, dest)
context.set_written(FLAG_Z, FLAG_N, FLAG_C)
elif opcode in ('and', 'or', 'xor'):
context.assert_meaningful(src, dest)
context.set_written(dest, FLAG_Z, FLAG_N)
elif opcode in ('shl', 'shr'):
context.assert_meaningful(dest, FLAG_C)
context.set_written(dest, FLAG_Z, FLAG_N, FLAG_C)
elif opcode == 'call':
type = instr.location.type
for ref in type.inputs:
context.assert_meaningful(ref)
for ref in type.outputs:
context.set_written(ref)
for ref in type.trashes:
context.assert_writeable(ref)
context.set_touched(ref)
context.set_unmeaningful(ref)
elif opcode == 'if':
context1 = context.clone()
context2 = context.clone()
analyze_block(instr.block1, context1, routines)
if instr.block2 is not None:
analyze_block(instr.block2, context2, routines)
# TODO may we need to deal with touched separately here too?
# probably not; if it wasn't meaningful in the first place, it
# doesn't really matter if you modified it or not, coming out.
for ref in context1.each_meaningful():
context2.assert_meaningful(ref, exception_class=InconsistentInitializationError)
for ref in context2.each_meaningful():
context1.assert_meaningful(ref, exception_class=InconsistentInitializationError)
context.set_from(context1)
elif opcode == 'repeat':
# it will always be executed at least once, so analyze it having
# been executed the first time.
analyze_block(instr.block, context, routines)
# now analyze it having been executed a second time, with the context
# of it having already been executed.
analyze_block(instr.block, context, routines)
# NB I *think* that's enough... but it might not be?
elif opcode == 'copy':
# check that their types are basically compatible
if src.type == dest.type:
pass
elif isinstance(src.type, ExecutableType) and \
isinstance(dest.type, VectorType):
pass
# if dealing with routines and vectors,
# check that they're not incompatible
if isinstance(src.type, ExecutableType) and \
isinstance(dest.type, VectorType):
if not (src.type.inputs <= dest.type.inputs):
raise IncompatibleConstraintsError(src.type.inputs - dest.type.inputs)
if not (src.type.outputs <= dest.type.outputs):
raise IncompatibleConstraintsError(src.type.outputs - dest.type.outputs)
if not (src.type.trashes <= dest.type.trashes):
raise IncompatibleConstraintsError(src.type.trashes - dest.type.trashes)
context.assert_meaningful(src)
context.set_written(dest)
context.set_touched(REG_A, FLAG_Z, FLAG_N)
context.set_unmeaningful(REG_A, FLAG_Z, FLAG_N)
elif opcode == 'with-sei':
self.analyze_block(instr.block, context, routines)
elif opcode == 'goto':
location = instr.location
type = location.type
if not isinstance(type, ExecutableType):
raise TypeMismatchError(location)
# assert that the dest routine's inputs are all initialized
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
if not (type.outputs <= current_type.outputs):
raise IncompatibleConstraintsError(type.outputs - current_type.outputs)
if not (type.trashes <= current_type.trashes):
raise IncompatibleConstraintsError(type.trashes - current_type.trashes)
self.has_encountered_goto = True
else:
raise TypeMismatchError((src, dest))
# if dealing with routines and vectors,
# check that they're not incompatible
if isinstance(src.type, ExecutableType) and \
isinstance(dest.type, VectorType):
if not (src.type.inputs <= dest.type.inputs):
raise IncompatibleConstraintsError(src.type.inputs - dest.type.inputs)
if not (src.type.outputs <= dest.type.outputs):
raise IncompatibleConstraintsError(src.type.outputs - dest.type.outputs)
if not (src.type.trashes <= dest.type.trashes):
raise IncompatibleConstraintsError(src.type.trashes - dest.type.trashes)
context.assert_meaningful(src)
context.set_written(dest)
context.set_touched(REG_A, FLAG_Z, FLAG_N)
context.set_unmeaningful(REG_A, FLAG_Z, FLAG_N)
elif opcode == 'with-sei':
analyze_block(instr.block, context, routines)
else:
raise NotImplementedError(opcode)
raise NotImplementedError(opcode)

View File

@ -203,6 +203,15 @@ class Compiler(object):
self.emitter.emit(JMP(Indirect(label)))
else:
raise NotImplementedError
elif opcode == 'goto':
location = instr.location
label = self.labels[instr.location.name]
if isinstance(location.type, RoutineType):
self.emitter.emit(JMP(Absolute(label)))
elif isinstance(location.type, VectorType):
self.emitter.emit(JMP(Indirect(label)))
else:
raise NotImplementedError
elif opcode == 'if':
cls = {
False: {

View File

@ -29,144 +29,151 @@ class Context(object):
self._store[ref.name] = value
def eval_program(program):
assert isinstance(program, Program)
context = Context()
for ref in (REG_A, REG_X, REG_Y, FLAG_Z, FLAG_N, FLAG_V, FLAG_C):
context.set(ref, 0)
main = None
for routine in program.routines:
context.set(routine.location, routine)
if routine.name == 'main':
main = routine
eval_routine(main, context)
return context
class Evaluator(object):
def eval_program(self, program):
assert isinstance(program, Program)
context = Context()
for ref in (REG_A, REG_X, REG_Y, FLAG_Z, FLAG_N, FLAG_V, FLAG_C):
context.set(ref, 0)
main = None
for routine in program.routines:
context.set(routine.location, routine)
if routine.name == 'main':
main = routine
self.eval_routine(main, context)
return context
def eval_routine(routine, context):
assert isinstance(routine, Routine)
eval_block(routine.block, context)
def eval_routine(self, routine, context):
assert isinstance(routine, Routine)
self.next_routine = routine
while self.next_routine:
routine = self.next_routine
self.next_routine = None
self.eval_block(routine.block, context)
def eval_block(self, block, context):
assert isinstance(block, Block)
for i in block.instrs:
self.eval_instr(i, context)
if self.next_routine:
break
def eval_block(block, context):
assert isinstance(block, Block)
for i in block.instrs:
eval_instr(i, context)
def eval_instr(self, instr, context):
assert isinstance(instr, Instr)
opcode = instr.opcode
dest = instr.dest
src = instr.src
def eval_instr(instr, context):
assert isinstance(instr, Instr)
opcode = instr.opcode
dest = instr.dest
src = instr.src
if opcode == 'ld':
result = context.get(src)
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
context.set(dest, result)
elif opcode == 'st':
context.set(dest, context.get(src))
elif opcode == 'add':
carry = context.get(FLAG_C)
val = context.get(src)
now = context.get(dest)
result = now + val + carry
if result > 255:
result &= 255
context.set(FLAG_C, 1)
if opcode == 'ld':
result = context.get(src)
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
context.set(dest, result)
elif opcode == 'st':
context.set(dest, context.get(src))
elif opcode == 'add':
carry = context.get(FLAG_C)
val = context.get(src)
now = context.get(dest)
result = now + val + carry
if result > 255:
result &= 255
context.set(FLAG_C, 1)
else:
context.set(FLAG_C, 0)
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
context.set(dest, result)
elif opcode == 'sub':
carry = context.get(FLAG_C)
val = context.get(src)
now = context.get(dest)
result = now - val - carry
if result < 0:
result &= 255
context.set(FLAG_C, 1)
else:
context.set(FLAG_C, 0)
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
context.set(dest, result)
elif opcode == 'inc':
val = context.get(dest)
result = (val + 1) & 255
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
context.set(dest, result)
elif opcode == 'dec':
val = context.get(dest)
result = (val - 1) & 255
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
context.set(dest, result)
elif opcode == 'cmp':
val = context.get(src)
now = context.get(dest)
result = now - val
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
if result < 0:
result &= 255
context.set(FLAG_C, 1)
else:
context.set(FLAG_C, 0)
elif opcode == 'and':
result = context.get(dest) & context.get(src)
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
context.set(dest, result)
elif opcode == 'or':
result = context.get(dest) | context.get(src)
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
context.set(dest, result)
elif opcode == 'xor':
result = context.get(dest) ^ context.get(src)
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
context.set(dest, result)
elif opcode == 'shl':
val = context.get(dest)
carry = context.get(FLAG_C)
context.set(FLAG_C, 1 if val & 128 else 0)
result = ((val << 1) + carry) & 255
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
context.set(dest, result)
elif opcode == 'shr':
val = context.get(dest)
carry = context.get(FLAG_C)
context.set(FLAG_C, 1 if val & 1 else 0)
result = (val >> 1) + (carry * 128)
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
context.set(dest, result)
elif opcode == 'call':
self.eval_routine(context.get(instr.location), context)
elif opcode == 'goto':
self.next_routine = context.get(instr.location)
elif opcode == 'if':
val = context.get(src)
test = (val != 0) if not instr.inverted else (val == 0)
if test:
self.eval_block(instr.block1, context)
elif instr.block2:
self.eval_block(instr.block2, context)
elif opcode == 'repeat':
self.eval_block(instr.block, context)
while context.get(src) == 0:
self.eval_block(instr.block, context)
elif opcode == 'copy':
context.set(dest, context.get(src))
# these are trashed; so could be anything really
context.set(REG_A, 0)
context.set(FLAG_Z, 0)
context.set(FLAG_N, 0)
elif opcode == 'with-sei':
self.eval_block(instr.block)
else:
context.set(FLAG_C, 0)
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
context.set(dest, result)
elif opcode == 'sub':
carry = context.get(FLAG_C)
val = context.get(src)
now = context.get(dest)
result = now - val - carry
if result < 0:
result &= 255
context.set(FLAG_C, 1)
else:
context.set(FLAG_C, 0)
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
context.set(dest, result)
elif opcode == 'inc':
val = context.get(dest)
result = (val + 1) & 255
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
context.set(dest, result)
elif opcode == 'dec':
val = context.get(dest)
result = (val - 1) & 255
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
context.set(dest, result)
elif opcode == 'cmp':
val = context.get(src)
now = context.get(dest)
result = now - val
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
if result < 0:
result &= 255
context.set(FLAG_C, 1)
else:
context.set(FLAG_C, 0)
elif opcode == 'and':
result = context.get(dest) & context.get(src)
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
context.set(dest, result)
elif opcode == 'or':
result = context.get(dest) | context.get(src)
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
context.set(dest, result)
elif opcode == 'xor':
result = context.get(dest) ^ context.get(src)
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
context.set(dest, result)
elif opcode == 'shl':
val = context.get(dest)
carry = context.get(FLAG_C)
context.set(FLAG_C, 1 if val & 128 else 0)
result = ((val << 1) + carry) & 255
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
context.set(dest, result)
elif opcode == 'shr':
val = context.get(dest)
carry = context.get(FLAG_C)
context.set(FLAG_C, 1 if val & 1 else 0)
result = (val >> 1) + (carry * 128)
context.set(FLAG_Z, 1 if result == 0 else 0)
context.set(FLAG_N, 1 if result & 128 else 0)
context.set(dest, result)
elif opcode == 'call':
eval_routine(context.get(instr.location), context)
elif opcode == 'if':
val = context.get(src)
test = (val != 0) if not instr.inverted else (val == 0)
if test:
eval_block(instr.block1, context)
elif instr.block2:
eval_block(instr.block2, context)
elif opcode == 'repeat':
eval_block(instr.block, context)
while context.get(src) == 0:
eval_block(instr.block, context)
elif opcode == 'copy':
context.set(dest, context.get(src))
# these are trashed; so could be anything really
context.set(REG_A, 0)
context.set(FLAG_Z, 0)
context.set(FLAG_N, 0)
elif opcode == 'with-sei':
eval_block(instr.block)
else:
raise NotImplementedError
raise NotImplementedError

View File

@ -260,7 +260,7 @@ class Parser(object):
self.scanner.scan()
dest = self.locexpr()
return Instr(opcode=opcode, dest=dest, src=None)
elif self.scanner.token in ("call",):
elif self.scanner.token in ("call", "goto"):
opcode = self.scanner.token
self.scanner.scan()
name = self.scanner.token

View File

@ -1134,7 +1134,7 @@ Indirect call.
| }
= ok
Calling the vector has indeed trashed stuff etc,
Calling the vector does indeed trash the things the vector says it does.
| vector foo trashes x, z, n
|
@ -1149,7 +1149,7 @@ Calling the vector has indeed trashed stuff etc,
| }
? UninitializedOutputError: x
A goto, if present, must appear at the end of the routine.
`goto`, if present, must be in tail position (the final instruction in a routine.)
| routine bar trashes x, z, n {
| ld x, 200
@ -1169,4 +1169,103 @@ A goto, if present, must appear at the end of the routine.
| goto bar
| ld x, 0
| }
? IllegalGotoError
? IllegalJumpError
| routine bar trashes x, z, n {
| ld x, 200
| }
|
| routine main trashes x, z, n {
| ld x, 0
| if z {
| ld x, 1
| goto bar
| }
| }
= ok
| routine bar trashes x, z, n {
| ld x, 200
| }
|
| routine main trashes x, z, n {
| ld x, 0
| if z {
| ld x, 1
| goto bar
| }
| ld x, 0
| }
? IllegalJumpError
Can't `goto` a routine that outputs or trashes more than the current routine.
| routine bar trashes x, y, z, n {
| ld x, 200
| ld y, 200
| }
|
| routine main trashes x, z, n {
| ld x, 0
| goto bar
| }
? IncompatibleConstraintsError
| routine bar outputs y trashes z, n {
| ld y, 200
| }
|
| routine main trashes x, z, n {
| ld x, 0
| goto bar
| }
? IncompatibleConstraintsError
Can `goto` a routine that outputs or trashes less than the current routine.
| routine bar trashes x, z, n {
| ld x, 1
| }
|
| routine main trashes a, x, z, n {
| ld a, 0
| ld x, 0
| goto bar
| }
= ok
Indirect goto.
| vector foo outputs x trashes a, z, n
|
| routine bar outputs x trashes a, z, n {
| ld x, 200
| }
|
| routine main inputs bar outputs x trashes foo, a, z, n {
| copy bar, foo
| goto foo
| }
= ok
Jumping through the vector does indeed output the things the vector says it does.
| vector foo trashes a, x, z, n
|
| routine bar trashes a, x, z, n {
| ld x, 200
| }
|
| routine sub inputs bar trashes foo, a, x, z, n {
| ld x, 0
| copy bar, foo
| goto foo
| }
|
| routine main inputs bar outputs a trashes z, n {
| call sub
| ld a, x
| }
? UninitializedOutputError: x
Ack, I have become a bit confused...

View File

@ -274,4 +274,16 @@ Indirect call.
| copy bar, foo
| call foo
| }
= 00c0
= 00c0wewillfixthislater
goto.
| routine bar outputs x trashes z, n {
| ld x, 200
| }
|
| routine main outputs x trashes a, z, n {
| ld y, 200
| goto bar
| }
= 00c0a0c84c06c060a2c860

View File

@ -420,3 +420,21 @@ Indirect call.
= x: 200
= y: 0
= z: 0
goto.
| routine bar outputs x trashes z, n {
| ld x, 200
| }
|
| routine main outputs x trashes a, z, n {
| ld y, 200
| goto bar
| }
= a: 0
= c: 0
= n: 1
= v: 0
= x: 200
= y: 200
= z: 0

View File

@ -248,3 +248,32 @@ Only vectors can be decorated with constraints like that.
| routine main {
| }
? SyntaxError
goto.
| routine foo {
| ld a, 0
| }
| routine main {
| goto foo
| }
= ok
| vector foo
|
| routine main {
| goto foo
| }
= ok
| routine main {
| goto foo
| }
? SyntaxError
| byte foo
|
| routine main {
| goto foo
| }
? SyntaxError