1
0
mirror of https://github.com/catseye/SixtyPical.git synced 2025-02-17 22:30:27 +00:00

Refs have types.

This commit is contained in:
Chris Pressey 2015-10-18 17:23:01 +01:00
parent 4990493b11
commit e1cf162a5b
5 changed files with 88 additions and 40 deletions

View File

@ -36,20 +36,20 @@ class UsageClashError(StaticAnalysisError):
class Context(): class Context():
def __init__(self, inputs, outputs, trashes): def __init__(self, inputs, outputs, trashes):
self._store = {} self._store = {} # Ref -> INITALIZED/UNINITIALIZED
self._writeables = set() self._writeables = set()
for ref in inputs: for ref in inputs:
self._store.setdefault(ref.name, INITIALIZED) self._store.setdefault(ref, INITIALIZED)
output_names = set() output_names = set()
for ref in outputs: for ref in outputs:
output_names.add(ref.name) output_names.add(ref.name)
self._store.setdefault(ref.name, UNINITIALIZED) self._store.setdefault(ref, UNINITIALIZED)
self._writeables.add(ref.name) self._writeables.add(ref.name)
for ref in trashes: for ref in trashes:
if ref.name in output_names: if ref.name in output_names:
raise UsageClashError(ref.name) raise UsageClashError(ref.name)
self._store.setdefault(ref.name, UNINITIALIZED) self._store.setdefault(ref, UNINITIALIZED)
self._writeables.add(ref.name) self._writeables.add(ref.name)
def clone(self): def clone(self):
@ -65,7 +65,7 @@ class Context():
def each_initialized(self): def each_initialized(self):
for key, value in self._store.iteritems(): for key, value in self._store.iteritems():
if value == INITIALIZED: if value == INITIALIZED:
yield LocationRef(key) yield key
def assert_initialized(self, *refs, **kwargs): def assert_initialized(self, *refs, **kwargs):
exception_class = kwargs.get('exception_class', UninitializedAccessError) exception_class = kwargs.get('exception_class', UninitializedAccessError)
@ -95,15 +95,15 @@ class Context():
if isinstance(ref, ConstantRef): if isinstance(ref, ConstantRef):
return INITIALIZED return INITIALIZED
elif isinstance(ref, LocationRef): elif isinstance(ref, LocationRef):
if ref.name not in self._store: if ref not in self._store:
return UNINITIALIZED return UNINITIALIZED
return self._store[ref.name] return self._store[ref]
else: else:
raise ValueError(ref) raise ValueError(ref)
def set(self, ref, value): def set(self, ref, value):
assert isinstance(ref, LocationRef) assert isinstance(ref, LocationRef)
self._store[ref.name] = value self._store[ref] = value
def analyze_program(program): def analyze_program(program):

View File

@ -3,6 +3,7 @@
from sixtypical.ast import Program, Routine, Block, Instr from sixtypical.ast import Program, Routine, Block, Instr
from sixtypical.model import ( from sixtypical.model import (
ConstantRef, LocationRef, ConstantRef, LocationRef,
TYPE_BIT,
REG_A, REG_X, REG_Y, FLAG_Z, FLAG_N, FLAG_V, FLAG_C REG_A, REG_X, REG_Y, FLAG_Z, FLAG_N, FLAG_V, FLAG_C
) )
from sixtypical.emitter import Label, Byte from sixtypical.emitter import Label, Byte
@ -99,9 +100,9 @@ class Compiler(object):
else: else:
raise UnsupportedOpcodeError(instr) raise UnsupportedOpcodeError(instr)
elif opcode == 'st': elif opcode == 'st':
if dest == FLAG_C and src == ConstantRef(0): if dest == FLAG_C and src == ConstantRef(TYPE_BIT, 0):
self.emitter.emit(CLC()) self.emitter.emit(CLC())
elif dest == FLAG_C and src == ConstantRef(1): elif dest == FLAG_C and src == ConstantRef(TYPE_BIT, 1):
self.emitter.emit(SEC()) self.emitter.emit(SEC())
elif src == REG_A: elif src == REG_A:
self.emitter.emit(STA(Absolute(self.labels[dest.name]))) self.emitter.emit(STA(Absolute(self.labels[dest.name])))

View File

@ -1,36 +1,74 @@
"""Data/storage model for SixtyPical.""" """Data/storage model for SixtyPical."""
class LocationRef(object): class Type(object):
def __init__(self, name): def __init__(self, name):
self.name = name self.name = name
def __repr__(self): def __repr__(self):
return 'LocationRef(%r)' % self.name return 'Type(%r)' % self.name
def __eq__(self, other): def __eq__(self, other):
return isinstance(other, LocationRef) and other.name == self.name return isinstance(other, Type) and other.name == self.name
def __hash__(self):
return hash(self.name)
class ConstantRef(object): TYPE_BIT = Type('bit')
def __init__(self, value): TYPE_BYTE = Type('byte')
TYPE_BYTE_TABLE = Type('byte table')
class Ref(object):
pass
class LocationRef(Ref):
def __init__(self, type, name):
self.type = type
self.name = name
def __eq__(self, other):
# Ordinarily there will only be one ref with a given name,
# but because we store the type in here and we want to treat
# these objects as immutable, we compare the types, too.
# Not sure if very wise.
return isinstance(other, LocationRef) and (
other.name == self.name and other.type == self.type
)
def __hash__(self):
return hash(self.name + str(self.type))
def __repr__(self):
return '%s(%r, %r)' % (self.__class__.__name__, self.type, self.name)
class ConstantRef(Ref):
def __init__(self, type, value):
self.type = type
self.value = value self.value = value
def __repr__(self): def __repr__(self):
return 'ConstantRef(%r)' % self.value return 'ConstantRef(%r)' % self.value
def __eq__(self, other): def __eq__(self, other):
return isinstance(other, ConstantRef) and other.value == self.value return isinstance(other, ConstantRef) and (
other.type == self.type and other.value == self.value
)
def __hash__(self):
return hash(str(self.value) + str(self.type))
def __repr__(self):
return '%s(%r, %r)' % (self.__class__.__name__, self.type, self.value)
# TODO type=byte REG_A = LocationRef(TYPE_BYTE, 'a')
REG_X = LocationRef(TYPE_BYTE, 'x')
REG_Y = LocationRef(TYPE_BYTE, 'y')
REG_A = LocationRef('a') FLAG_Z = LocationRef(TYPE_BIT, 'z')
REG_X = LocationRef('x') FLAG_C = LocationRef(TYPE_BIT, 'c')
REG_Y = LocationRef('y') FLAG_N = LocationRef(TYPE_BIT, 'n')
FLAG_V = LocationRef(TYPE_BIT, 'v')
# TODO type=bit
FLAG_Z = LocationRef('z')
FLAG_C = LocationRef('c')
FLAG_N = LocationRef('n')
FLAG_V = LocationRef('v')

View File

@ -3,7 +3,9 @@
import re import re
from sixtypical.ast import Program, Defn, Routine, Block, Instr from sixtypical.ast import Program, Defn, Routine, Block, Instr
from sixtypical.model import LocationRef, ConstantRef from sixtypical.model import (
TYPE_BIT, TYPE_BYTE, LocationRef, ConstantRef
)
class Scanner(object): class Scanner(object):
@ -70,16 +72,19 @@ class Scanner(object):
return False return False
class SymEntry(object):
def __init__(self, ast_node, model):
self.ast_node = ast_node
self.model = model
class Parser(object): class Parser(object):
def __init__(self, text): def __init__(self, text):
self.scanner = Scanner(text) self.scanner = Scanner(text)
self.symbols = {} self.symbols = {} # token -> SymEntry
def lookup(self, name): def lookup(self, name):
if name in self.symbols: return self.symbols[name].model
return LocationRef(name)
else:
raise KeyError(name)
def program(self): def program(self):
defns = [] defns = []
@ -89,14 +94,14 @@ class Parser(object):
name = defn.name name = defn.name
if name in self.symbols: if name in self.symbols:
raise KeyError(name) raise KeyError(name)
self.symbols[name] = defn self.symbols[name] = SymEntry(defn, LocationRef(TYPE_BYTE, name))
defns.append(defn) defns.append(defn)
while self.scanner.on('routine'): while self.scanner.on('routine'):
routine = self.routine() routine = self.routine()
name = routine.name name = routine.name
if name in self.symbols: if name in self.symbols:
raise KeyError(name) raise KeyError(name)
self.symbols[name] = routine self.symbols[name] = SymEntry(routine, None)
routines.append(routine) routines.append(routine)
self.scanner.check_type('EOF') self.scanner.check_type('EOF')
return Program(defns=defns, routines=routines) return Program(defns=defns, routines=routines)
@ -146,16 +151,20 @@ class Parser(object):
return accum return accum
def locexpr(self): def locexpr(self):
if self.scanner.token in ('a', 'x', 'y', 'c', 'z', 'n', 'v'): if self.scanner.token in ('a', 'x', 'y'):
loc = LocationRef(self.scanner.token) loc = LocationRef(TYPE_BYTE, self.scanner.token)
self.scanner.scan()
return loc
elif self.scanner.token in ('c', 'z', 'n', 'v'):
loc = LocationRef(TYPE_BIT, self.scanner.token)
self.scanner.scan() self.scanner.scan()
return loc return loc
elif self.scanner.token in ('on', 'off'): elif self.scanner.token in ('on', 'off'):
loc = ConstantRef(1 if self.scanner.token == 'on' else 0) loc = ConstantRef(TYPE_BIT, 1 if self.scanner.token == 'on' else 0)
self.scanner.scan() self.scanner.scan()
return loc return loc
elif self.scanner.on_type('integer literal'): elif self.scanner.on_type('integer literal'):
loc = ConstantRef(int(self.scanner.token)) loc = ConstantRef(TYPE_BYTE, int(self.scanner.token))
self.scanner.scan() self.scanner.scan()
return loc return loc
else: else:

View File

@ -7,7 +7,7 @@ static analysis rules.
[Falderal]: http://catseye.tc/node/Falderal [Falderal]: http://catseye.tc/node/Falderal
-> Functionality "Analyze SixtyPical program" is implemented by -> Functionality "Analyze SixtyPical program" is implemented by
-> shell command "bin/sixtypical --analyze %(test-body-file) && echo ok" -> shell command "bin/sixtypical --analyze --traceback %(test-body-file) && echo ok"
-> Tests for functionality "Analyze SixtyPical program" -> Tests for functionality "Analyze SixtyPical program"