mirror of
https://github.com/catseye/SixtyPical.git
synced 2025-01-10 02:29:23 +00:00
Tighten structure of AST more.
This commit is contained in:
parent
4b43ba734e
commit
0a83d90515
@ -1,6 +1,6 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
from sixtypical.ast import Program, Routine, Block, Instr
|
||||
from sixtypical.ast import Program, Routine, Block, Instr, SingleOp, BlockOp, IfOp
|
||||
from sixtypical.model import (
|
||||
TYPE_BYTE, TYPE_WORD,
|
||||
TableType, BufferType, PointerType, VectorType, RoutineType,
|
||||
@ -312,7 +312,17 @@ class Analyzer(object):
|
||||
self.analyze_instr(i, context)
|
||||
|
||||
def analyze_instr(self, instr, context):
|
||||
assert isinstance(instr, Instr)
|
||||
if isinstance(instr, SingleOp):
|
||||
return self.analyze_single_op(instr, context)
|
||||
elif isinstance(instr, BlockOp):
|
||||
return self.analyze_block_op(instr, context)
|
||||
elif isinstance(instr, IfOp):
|
||||
return self.analyze_if_op(instr, context)
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
def analyze_single_op(self, instr, context):
|
||||
|
||||
opcode = instr.opcode
|
||||
dest = instr.dest
|
||||
src = instr.src
|
||||
@ -429,58 +439,6 @@ class Analyzer(object):
|
||||
context.assert_writeable(ref)
|
||||
context.set_touched(ref)
|
||||
context.set_unmeaningful(ref)
|
||||
elif opcode == 'if':
|
||||
incoming_meaningful = set(context.each_meaningful())
|
||||
|
||||
context1 = context.clone()
|
||||
context2 = context.clone()
|
||||
self.analyze_block(instr.block1, context1)
|
||||
if instr.block2 is not None:
|
||||
self.analyze_block(instr.block2, context2)
|
||||
|
||||
outgoing_meaningful = set(context1.each_meaningful()) & set(context2.each_meaningful())
|
||||
outgoing_trashes = incoming_meaningful - outgoing_meaningful
|
||||
|
||||
# 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():
|
||||
if ref in outgoing_trashes:
|
||||
continue
|
||||
context2.assert_meaningful(
|
||||
ref, exception_class=InconsistentInitializationError,
|
||||
message='initialized in block 1 but not in block 2 of `if {}`'.format(src)
|
||||
)
|
||||
for ref in context2.each_meaningful():
|
||||
if ref in outgoing_trashes:
|
||||
continue
|
||||
context1.assert_meaningful(
|
||||
ref, exception_class=InconsistentInitializationError,
|
||||
message='initialized in block 2 but not in block 1 of `if {}`'.format(src)
|
||||
)
|
||||
|
||||
# merge the contexts. this used to be a method called `set_from`
|
||||
context._touched = set(context1._touched) | set(context2._touched)
|
||||
context.set_meaningful(*list(outgoing_meaningful))
|
||||
context._writeable = set(context1._writeable) | set(context2._writeable)
|
||||
|
||||
for ref in outgoing_trashes:
|
||||
context.set_touched(ref)
|
||||
context.set_unmeaningful(ref)
|
||||
|
||||
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)
|
||||
if src is not None: # None indicates 'repeat forever'
|
||||
context.assert_meaningful(src)
|
||||
|
||||
# now analyze it having been executed a second time, with the context
|
||||
# of it having already been executed.
|
||||
self.analyze_block(instr.block, context)
|
||||
if src is not None:
|
||||
context.assert_meaningful(src)
|
||||
|
||||
elif opcode == 'copy':
|
||||
if dest == REG_A:
|
||||
raise ForbiddenWriteError("{} cannot be used as destination for copy".format(dest))
|
||||
@ -563,9 +521,6 @@ class Analyzer(object):
|
||||
|
||||
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)
|
||||
elif opcode == 'goto':
|
||||
location = instr.location
|
||||
type_ = location.type
|
||||
@ -591,3 +546,60 @@ class Analyzer(object):
|
||||
context.set_unmeaningful(instr.dest)
|
||||
else:
|
||||
raise NotImplementedError(opcode)
|
||||
|
||||
def analyze_if_op(self, instr, context):
|
||||
incoming_meaningful = set(context.each_meaningful())
|
||||
|
||||
context1 = context.clone()
|
||||
context2 = context.clone()
|
||||
self.analyze_block(instr.block1, context1)
|
||||
if instr.block2 is not None:
|
||||
self.analyze_block(instr.block2, context2)
|
||||
|
||||
outgoing_meaningful = set(context1.each_meaningful()) & set(context2.each_meaningful())
|
||||
outgoing_trashes = incoming_meaningful - outgoing_meaningful
|
||||
|
||||
# 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():
|
||||
if ref in outgoing_trashes:
|
||||
continue
|
||||
context2.assert_meaningful(
|
||||
ref, exception_class=InconsistentInitializationError,
|
||||
message='initialized in block 1 but not in block 2 of `if {}`'.format(instr.src)
|
||||
)
|
||||
for ref in context2.each_meaningful():
|
||||
if ref in outgoing_trashes:
|
||||
continue
|
||||
context1.assert_meaningful(
|
||||
ref, exception_class=InconsistentInitializationError,
|
||||
message='initialized in block 2 but not in block 1 of `if {}`'.format(instr.src)
|
||||
)
|
||||
|
||||
# merge the contexts. this used to be a method called `set_from`
|
||||
context._touched = set(context1._touched) | set(context2._touched)
|
||||
context.set_meaningful(*list(outgoing_meaningful))
|
||||
context._writeable = set(context1._writeable) | set(context2._writeable)
|
||||
|
||||
for ref in outgoing_trashes:
|
||||
context.set_touched(ref)
|
||||
context.set_unmeaningful(ref)
|
||||
|
||||
def analyze_block_op(self, instr, context):
|
||||
if instr.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)
|
||||
if instr.src is not None: # None indicates 'repeat forever'
|
||||
context.assert_meaningful(instr.src)
|
||||
|
||||
# now analyze it having been executed a second time, with the context
|
||||
# of it having already been executed.
|
||||
self.analyze_block(instr.block, context)
|
||||
if instr.src is not None:
|
||||
context.assert_meaningful(instr.src)
|
||||
elif instr.opcode == 'with-sei':
|
||||
self.analyze_block(instr.block, context)
|
||||
else:
|
||||
raise NotImplementedError(opcode)
|
||||
|
@ -73,10 +73,10 @@ class SingleOp(Instr):
|
||||
|
||||
|
||||
class BlockOp(Instr):
|
||||
value_attrs = ('opcode', 'dest', 'src', 'inverted')
|
||||
value_attrs = ('opcode', 'src', 'inverted')
|
||||
child_attrs = ('block',)
|
||||
|
||||
|
||||
class IfOp(Instr):
|
||||
value_attrs = ('opcode', 'dest', 'src', 'inverted')
|
||||
value_attrs = ('src', 'inverted')
|
||||
child_attrs = ('block1', 'block2',)
|
||||
|
@ -1,6 +1,6 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
from sixtypical.ast import Program, Routine, Block, Instr
|
||||
from sixtypical.ast import Program, Routine, Block, Instr, SingleOp, BlockOp, IfOp
|
||||
from sixtypical.model import (
|
||||
ConstantRef, LocationRef, IndexedRef, IndirectRef, AddressRef,
|
||||
TYPE_BIT, TYPE_BYTE, TYPE_WORD,
|
||||
@ -142,7 +142,17 @@ class Compiler(object):
|
||||
self.compile_instr(instr)
|
||||
|
||||
def compile_instr(self, instr):
|
||||
assert isinstance(instr, Instr)
|
||||
if isinstance(instr, SingleOp):
|
||||
return self.compile_single_op(instr)
|
||||
elif isinstance(instr, BlockOp):
|
||||
return self.compile_block_op(instr)
|
||||
elif isinstance(instr, IfOp):
|
||||
return self.compile_if_op(instr)
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
def compile_single_op(self, instr):
|
||||
|
||||
opcode = instr.opcode
|
||||
dest = instr.dest
|
||||
src = instr.src
|
||||
@ -356,53 +366,6 @@ class Compiler(object):
|
||||
self.emitter.emit(JMP(Indirect(label)))
|
||||
else:
|
||||
raise NotImplementedError
|
||||
elif opcode == 'if':
|
||||
cls = {
|
||||
False: {
|
||||
'c': BCC,
|
||||
'z': BNE,
|
||||
},
|
||||
True: {
|
||||
'c': BCS,
|
||||
'z': BEQ,
|
||||
},
|
||||
}[instr.inverted].get(src.name)
|
||||
if cls is None:
|
||||
raise UnsupportedOpcodeError(instr)
|
||||
else_label = Label('else_label')
|
||||
self.emitter.emit(cls(Relative(else_label)))
|
||||
self.compile_block(instr.block1)
|
||||
if instr.block2 is not None:
|
||||
end_label = Label('end_label')
|
||||
self.emitter.emit(JMP(Absolute(end_label)))
|
||||
self.emitter.resolve_label(else_label)
|
||||
self.compile_block(instr.block2)
|
||||
self.emitter.resolve_label(end_label)
|
||||
else:
|
||||
self.emitter.resolve_label(else_label)
|
||||
elif opcode == 'repeat':
|
||||
top_label = self.emitter.make_label()
|
||||
self.compile_block(instr.block)
|
||||
if src is None: # indicates 'repeat forever'
|
||||
self.emitter.emit(JMP(Absolute(top_label)))
|
||||
else:
|
||||
cls = {
|
||||
False: {
|
||||
'c': BCC,
|
||||
'z': BNE,
|
||||
},
|
||||
True: {
|
||||
'c': BCS,
|
||||
'z': BEQ,
|
||||
},
|
||||
}[instr.inverted].get(src.name)
|
||||
if cls is None:
|
||||
raise UnsupportedOpcodeError(instr)
|
||||
self.emitter.emit(cls(Relative(top_label)))
|
||||
elif opcode == 'with-sei':
|
||||
self.emitter.emit(SEI())
|
||||
self.compile_block(instr.block)
|
||||
self.emitter.emit(CLI())
|
||||
elif opcode == 'copy':
|
||||
if isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, IndirectRef):
|
||||
if src.type == TYPE_BYTE and isinstance(dest.ref.type, PointerType):
|
||||
@ -532,3 +495,55 @@ class Compiler(object):
|
||||
pass
|
||||
else:
|
||||
raise NotImplementedError(opcode)
|
||||
|
||||
def compile_if_op(self, instr):
|
||||
cls = {
|
||||
False: {
|
||||
'c': BCC,
|
||||
'z': BNE,
|
||||
},
|
||||
True: {
|
||||
'c': BCS,
|
||||
'z': BEQ,
|
||||
},
|
||||
}[instr.inverted].get(instr.src.name)
|
||||
if cls is None:
|
||||
raise UnsupportedOpcodeError(instr)
|
||||
else_label = Label('else_label')
|
||||
self.emitter.emit(cls(Relative(else_label)))
|
||||
self.compile_block(instr.block1)
|
||||
if instr.block2 is not None:
|
||||
end_label = Label('end_label')
|
||||
self.emitter.emit(JMP(Absolute(end_label)))
|
||||
self.emitter.resolve_label(else_label)
|
||||
self.compile_block(instr.block2)
|
||||
self.emitter.resolve_label(end_label)
|
||||
else:
|
||||
self.emitter.resolve_label(else_label)
|
||||
|
||||
def compile_block_op(self, instr):
|
||||
if instr.opcode == 'repeat':
|
||||
top_label = self.emitter.make_label()
|
||||
self.compile_block(instr.block)
|
||||
if instr.src is None: # indicates 'repeat forever'
|
||||
self.emitter.emit(JMP(Absolute(top_label)))
|
||||
else:
|
||||
cls = {
|
||||
False: {
|
||||
'c': BCC,
|
||||
'z': BNE,
|
||||
},
|
||||
True: {
|
||||
'c': BCS,
|
||||
'z': BEQ,
|
||||
},
|
||||
}[instr.inverted].get(instr.src.name)
|
||||
if cls is None:
|
||||
raise UnsupportedOpcodeError(instr)
|
||||
self.emitter.emit(cls(Relative(top_label)))
|
||||
elif instr.opcode == 'with-sei':
|
||||
self.emitter.emit(SEI())
|
||||
self.compile_block(instr.block)
|
||||
self.emitter.emit(CLI())
|
||||
else:
|
||||
raise NotImplementedError(opcode)
|
||||
|
@ -352,8 +352,7 @@ class Parser(object):
|
||||
block2 = None
|
||||
if self.scanner.consume('else'):
|
||||
block2 = self.block()
|
||||
return IfOp(opcode='if', dest=None, src=src,
|
||||
block1=block1, block2=block2, inverted=inverted)
|
||||
return IfOp(src=src, block1=block1, block2=block2, inverted=inverted)
|
||||
elif self.scanner.consume('repeat'):
|
||||
inverted = False
|
||||
src = None
|
||||
@ -364,8 +363,7 @@ class Parser(object):
|
||||
src = self.locexpr()
|
||||
else:
|
||||
self.scanner.expect('forever')
|
||||
return BlockOp(opcode='repeat', dest=None, src=src,
|
||||
block=block, inverted=inverted)
|
||||
return BlockOp(opcode='repeat', src=src, block=block, inverted=inverted)
|
||||
elif self.scanner.token in ("ld",):
|
||||
# the same as add, sub, cmp etc below, except supports an indlocexpr for the src
|
||||
opcode = self.scanner.token
|
||||
@ -417,7 +415,7 @@ class Parser(object):
|
||||
self.scanner.expect("interrupts")
|
||||
self.scanner.expect("off")
|
||||
block = self.block()
|
||||
return BlockOp(opcode='with-sei', dest=None, src=None, block=block)
|
||||
return BlockOp(opcode='with-sei', src=None, block=block)
|
||||
elif self.scanner.consume("trash"):
|
||||
dest = self.locexpr()
|
||||
return SingleOp(opcode='trash', src=None, dest=dest)
|
||||
|
Loading…
x
Reference in New Issue
Block a user