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:
parent
4990493b11
commit
e1cf162a5b
@ -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):
|
||||||
|
@ -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])))
|
||||||
|
@ -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')
|
|
||||||
|
@ -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:
|
||||||
|
@ -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"
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user