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

Distinct AST nodes for call and goto instructions.

This commit is contained in:
Chris Pressey 2019-04-08 16:26:51 +01:00
parent bd462d6d8b
commit 4615d8d054
4 changed files with 126 additions and 101 deletions

View File

@ -1,7 +1,7 @@
# encoding: UTF-8
from sixtypical.ast import (
Program, Routine, Block, SingleOp, If, Repeat, For, WithInterruptsOff, Save, PointInto
Program, Routine, Block, SingleOp, Call, GoTo, If, Repeat, For, WithInterruptsOff, Save, PointInto
)
from sixtypical.model import (
TYPE_BYTE, TYPE_WORD,
@ -493,6 +493,10 @@ class Analyzer(object):
def analyze_instr(self, instr, context):
if isinstance(instr, SingleOp):
self.analyze_single_op(instr, context)
elif isinstance(instr, Call):
self.analyze_call(instr, context)
elif isinstance(instr, GoTo):
self.analyze_goto(instr, context)
elif isinstance(instr, If):
self.analyze_if(instr, context)
elif isinstance(instr, Repeat):
@ -667,20 +671,6 @@ class Analyzer(object):
self.assert_type(TYPE_BYTE, dest)
context.set_written(dest, FLAG_Z, FLAG_N, FLAG_C)
context.invalidate_range(dest)
elif opcode == 'call':
type = instr.location.type
if not isinstance(type, (RoutineType, VectorType)):
raise TypeMismatchError(instr, instr.location)
if isinstance(type, VectorType):
type = type.of_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 == 'copy':
if dest == REG_A:
raise ForbiddenWriteError(instr, "{} cannot be used as destination for copy".format(dest))
@ -789,59 +779,6 @@ class Analyzer(object):
context.set_touched(REG_A, FLAG_Z, FLAG_N)
context.set_unmeaningful(REG_A, FLAG_Z, FLAG_N)
elif opcode == 'goto':
location = instr.location
type_ = location.type
if not isinstance(type_, (RoutineType, VectorType)):
raise TypeMismatchError(instr, 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)
# and that this routine's trashes and output constraints are a
# superset of the called routine's
current_type = self.current_routine.location.type
self.assert_affected_within('outputs', type_, current_type)
self.assert_affected_within('trashes', type_, current_type)
context.encounter_gotos(set([instr.location]))
# Now that we have encountered a goto, we update the
# context here to match what someone calling the goto'ed
# function directly, would expect. (which makes sense
# when you think about it; if this goto's F, then calling
# this is like calling F, from the perspective of what is
# returned.)
#
# However, this isn't the current context anymore. This
# is an exit context of this routine.
exit_context = context.clone()
for ref in type_.outputs:
exit_context.set_touched(ref) # ?
exit_context.set_written(ref)
for ref in type_.trashes:
exit_context.assert_writeable(ref)
exit_context.set_touched(ref)
exit_context.set_unmeaningful(ref)
self.exit_contexts.append(exit_context)
# When we get to the end, we'll check that all the
# exit contexts are consistent with each other.
# We set the current context as having terminated.
# If we are in a branch, the merge will deal with
# having terminated. If we are at the end of the
# routine, the routine end will deal with that.
context.set_terminated()
elif opcode == 'trash':
context.set_touched(instr.dest)
@ -851,6 +788,75 @@ class Analyzer(object):
else:
raise NotImplementedError(opcode)
def analyze_call(self, instr, context):
type = instr.location.type
if not isinstance(type, (RoutineType, VectorType)):
raise TypeMismatchError(instr, instr.location)
if isinstance(type, VectorType):
type = type.of_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)
def analyze_goto(self, instr, context):
location = instr.location
type_ = location.type
if not isinstance(type_, (RoutineType, VectorType)):
raise TypeMismatchError(instr, 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)
# and that this routine's trashes and output constraints are a
# superset of the called routine's
current_type = self.current_routine.location.type
self.assert_affected_within('outputs', type_, current_type)
self.assert_affected_within('trashes', type_, current_type)
context.encounter_gotos(set([instr.location]))
# Now that we have encountered a goto, we update the
# context here to match what someone calling the goto'ed
# function directly, would expect. (which makes sense
# when you think about it; if this goto's F, then calling
# this is like calling F, from the perspective of what is
# returned.)
#
# However, this isn't the current context anymore. This
# is an exit context of this routine.
exit_context = context.clone()
for ref in type_.outputs:
exit_context.set_touched(ref) # ?
exit_context.set_written(ref)
for ref in type_.trashes:
exit_context.assert_writeable(ref)
exit_context.set_touched(ref)
exit_context.set_unmeaningful(ref)
self.exit_contexts.append(exit_context)
# When we get to the end, we'll check that all the
# exit contexts are consistent with each other.
# We set the current context as having terminated.
# If we are in a branch, the merge will deal with
# having terminated. If we are at the end of the
# routine, the routine end will deal with that.
context.set_terminated()
def analyze_if(self, instr, context):
incoming_meaningful = set(context.each_meaningful())

View File

@ -72,7 +72,15 @@ class Instr(AST):
class SingleOp(Instr):
value_attrs = ('opcode', 'dest', 'src', 'location',)
value_attrs = ('opcode', 'dest', 'src',)
class Call(Instr):
value_attrs = ('location',)
class GoTo(Instr):
value_attrs = ('location',)
class If(Instr):

View File

@ -1,7 +1,7 @@
# encoding: UTF-8
from sixtypical.ast import (
Program, Routine, Block, SingleOp, If, Repeat, For, WithInterruptsOff, Save, PointInto
Program, Routine, Block, SingleOp, Call, GoTo, If, Repeat, For, WithInterruptsOff, Save, PointInto
)
from sixtypical.model import (
ConstantRef, LocationRef, IndexedRef, IndirectRef,
@ -162,6 +162,10 @@ class Compiler(object):
def compile_instr(self, instr):
if isinstance(instr, SingleOp):
return self.compile_single_op(instr)
elif isinstance(instr, Call):
return self.compile_call(instr)
elif isinstance(instr, GoTo):
return self.compile_goto(instr)
elif isinstance(instr, If):
return self.compile_if(instr)
elif isinstance(instr, Repeat):
@ -393,31 +397,6 @@ class Compiler(object):
self.emitter.emit(cls(mode(Offset(self.get_label(dest.ref.name), dest.offset.value))))
else:
self.emitter.emit(cls(self.absolute_or_zero_page(self.get_label(dest.name))))
elif opcode == 'call':
location = instr.location
label = self.get_label(instr.location.name)
if isinstance(location.type, RoutineType):
self.emitter.emit(JSR(Absolute(label)))
elif isinstance(location.type, VectorType):
trampoline = self.trampolines.setdefault(
location, Label(location.name + '_trampoline')
)
self.emitter.emit(JSR(Absolute(trampoline)))
else:
raise NotImplementedError
elif opcode == 'goto':
self.final_goto_seen = True
if self.skip_final_goto:
pass
else:
location = instr.location
label = self.get_label(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 == 'copy':
self.compile_copy(instr, instr.src, instr.dest)
elif opcode == 'trash':
@ -427,6 +406,33 @@ class Compiler(object):
else:
raise NotImplementedError(opcode)
def compile_call(self, instr):
location = instr.location
label = self.get_label(instr.location.name)
if isinstance(location.type, RoutineType):
self.emitter.emit(JSR(Absolute(label)))
elif isinstance(location.type, VectorType):
trampoline = self.trampolines.setdefault(
location, Label(location.name + '_trampoline')
)
self.emitter.emit(JSR(Absolute(trampoline)))
else:
raise NotImplementedError
def compile_goto(self, instr):
self.final_goto_seen = True
if self.skip_final_goto:
pass
else:
location = instr.location
label = self.get_label(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
def compile_cmp(self, instr, src, dest):
"""`instr` is only for reporting purposes"""
if isinstance(src, LocationRef) and src.type == TYPE_WORD:

View File

@ -1,7 +1,7 @@
# encoding: UTF-8
from sixtypical.ast import (
Program, Defn, Routine, Block, SingleOp, If, Repeat, For, WithInterruptsOff, Save, PointInto
Program, Defn, Routine, Block, SingleOp, Call, GoTo, If, Repeat, For, WithInterruptsOff, Save, PointInto
)
from sixtypical.model import (
TYPE_BIT, TYPE_BYTE, TYPE_WORD,
@ -113,6 +113,8 @@ class Parser(object):
resolve_fwd_reference(node, 'location')
resolve_fwd_reference(node, 'src')
resolve_fwd_reference(node, 'dest')
if isinstance(node, (Call, GoTo)):
resolve_fwd_reference(node, 'location')
# --- grammar productions
@ -380,7 +382,7 @@ class Parser(object):
self.scanner.expect('{')
while not self.scanner.on('}'):
instrs.append(self.instr())
if isinstance(instrs[-1], SingleOp) and instrs[-1].opcode == 'goto':
if isinstance(instrs[-1], GoTo):
break
self.scanner.expect('}')
return Block(self.scanner.line_number, instrs=instrs)
@ -450,12 +452,15 @@ class Parser(object):
opcode = self.scanner.token
self.scanner.scan()
return SingleOp(self.scanner.line_number, opcode=opcode, dest=None, src=None)
elif self.scanner.token in ("call", "goto"):
opcode = self.scanner.token
self.scanner.scan()
elif self.scanner.consume("call"):
name = self.scanner.token
self.scanner.scan()
instr = SingleOp(self.scanner.line_number, opcode=opcode, location=ForwardReference(name), dest=None, src=None)
instr = Call(self.scanner.line_number, location=ForwardReference(name))
return instr
elif self.scanner.consume("goto"):
name = self.scanner.token
self.scanner.scan()
instr = GoTo(self.scanner.line_number, location=ForwardReference(name))
return instr
elif self.scanner.token in ("copy",):
opcode = self.scanner.token