From e1cf162a5bdf1e01cd3dc9e5067615beb6d8e015 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Sun, 18 Oct 2015 17:23:01 +0100 Subject: [PATCH] Refs have types. --- src/sixtypical/analyzer.py | 16 ++++---- src/sixtypical/compiler.py | 5 ++- src/sixtypical/model.py | 72 +++++++++++++++++++++++++++--------- src/sixtypical/parser.py | 33 +++++++++++------ tests/SixtyPical Analysis.md | 2 +- 5 files changed, 88 insertions(+), 40 deletions(-) diff --git a/src/sixtypical/analyzer.py b/src/sixtypical/analyzer.py index a885d50..b3806f5 100644 --- a/src/sixtypical/analyzer.py +++ b/src/sixtypical/analyzer.py @@ -36,20 +36,20 @@ class UsageClashError(StaticAnalysisError): class Context(): def __init__(self, inputs, outputs, trashes): - self._store = {} + self._store = {} # Ref -> INITALIZED/UNINITIALIZED self._writeables = set() for ref in inputs: - self._store.setdefault(ref.name, INITIALIZED) + self._store.setdefault(ref, INITIALIZED) output_names = set() for ref in outputs: output_names.add(ref.name) - self._store.setdefault(ref.name, UNINITIALIZED) + self._store.setdefault(ref, UNINITIALIZED) self._writeables.add(ref.name) for ref in trashes: if ref.name in output_names: raise UsageClashError(ref.name) - self._store.setdefault(ref.name, UNINITIALIZED) + self._store.setdefault(ref, UNINITIALIZED) self._writeables.add(ref.name) def clone(self): @@ -65,7 +65,7 @@ class Context(): def each_initialized(self): for key, value in self._store.iteritems(): if value == INITIALIZED: - yield LocationRef(key) + yield key def assert_initialized(self, *refs, **kwargs): exception_class = kwargs.get('exception_class', UninitializedAccessError) @@ -95,15 +95,15 @@ class Context(): if isinstance(ref, ConstantRef): return INITIALIZED elif isinstance(ref, LocationRef): - if ref.name not in self._store: + if ref not in self._store: return UNINITIALIZED - return self._store[ref.name] + return self._store[ref] else: raise ValueError(ref) def set(self, ref, value): assert isinstance(ref, LocationRef) - self._store[ref.name] = value + self._store[ref] = value def analyze_program(program): diff --git a/src/sixtypical/compiler.py b/src/sixtypical/compiler.py index e64ec3b..7560af1 100644 --- a/src/sixtypical/compiler.py +++ b/src/sixtypical/compiler.py @@ -3,6 +3,7 @@ from sixtypical.ast import Program, Routine, Block, Instr from sixtypical.model import ( ConstantRef, LocationRef, + TYPE_BIT, REG_A, REG_X, REG_Y, FLAG_Z, FLAG_N, FLAG_V, FLAG_C ) from sixtypical.emitter import Label, Byte @@ -99,9 +100,9 @@ class Compiler(object): else: raise UnsupportedOpcodeError(instr) 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()) - elif dest == FLAG_C and src == ConstantRef(1): + elif dest == FLAG_C and src == ConstantRef(TYPE_BIT, 1): self.emitter.emit(SEC()) elif src == REG_A: self.emitter.emit(STA(Absolute(self.labels[dest.name]))) diff --git a/src/sixtypical/model.py b/src/sixtypical/model.py index b869601..06afbc8 100644 --- a/src/sixtypical/model.py +++ b/src/sixtypical/model.py @@ -1,36 +1,74 @@ """Data/storage model for SixtyPical.""" -class LocationRef(object): +class Type(object): def __init__(self, name): self.name = name def __repr__(self): - return 'LocationRef(%r)' % self.name + return 'Type(%r)' % self.name 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): - def __init__(self, value): +TYPE_BIT = Type('bit') +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 def __repr__(self): return 'ConstantRef(%r)' % self.value 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') -REG_X = LocationRef('x') -REG_Y = LocationRef('y') - -# TODO type=bit - -FLAG_Z = LocationRef('z') -FLAG_C = LocationRef('c') -FLAG_N = LocationRef('n') -FLAG_V = LocationRef('v') +FLAG_Z = LocationRef(TYPE_BIT, 'z') +FLAG_C = LocationRef(TYPE_BIT, 'c') +FLAG_N = LocationRef(TYPE_BIT, 'n') +FLAG_V = LocationRef(TYPE_BIT, 'v') diff --git a/src/sixtypical/parser.py b/src/sixtypical/parser.py index 413b7bd..7865482 100644 --- a/src/sixtypical/parser.py +++ b/src/sixtypical/parser.py @@ -3,7 +3,9 @@ import re 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): @@ -70,16 +72,19 @@ class Scanner(object): return False +class SymEntry(object): + def __init__(self, ast_node, model): + self.ast_node = ast_node + self.model = model + + class Parser(object): def __init__(self, text): self.scanner = Scanner(text) - self.symbols = {} + self.symbols = {} # token -> SymEntry def lookup(self, name): - if name in self.symbols: - return LocationRef(name) - else: - raise KeyError(name) + return self.symbols[name].model def program(self): defns = [] @@ -89,14 +94,14 @@ class Parser(object): name = defn.name if name in self.symbols: raise KeyError(name) - self.symbols[name] = defn + self.symbols[name] = SymEntry(defn, LocationRef(TYPE_BYTE, name)) defns.append(defn) while self.scanner.on('routine'): routine = self.routine() name = routine.name if name in self.symbols: raise KeyError(name) - self.symbols[name] = routine + self.symbols[name] = SymEntry(routine, None) routines.append(routine) self.scanner.check_type('EOF') return Program(defns=defns, routines=routines) @@ -146,16 +151,20 @@ class Parser(object): return accum def locexpr(self): - if self.scanner.token in ('a', 'x', 'y', 'c', 'z', 'n', 'v'): - loc = LocationRef(self.scanner.token) + if self.scanner.token in ('a', 'x', 'y'): + 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() return loc 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() return loc elif self.scanner.on_type('integer literal'): - loc = ConstantRef(int(self.scanner.token)) + loc = ConstantRef(TYPE_BYTE, int(self.scanner.token)) self.scanner.scan() return loc else: diff --git a/tests/SixtyPical Analysis.md b/tests/SixtyPical Analysis.md index 638f073..8d8a00c 100644 --- a/tests/SixtyPical Analysis.md +++ b/tests/SixtyPical Analysis.md @@ -7,7 +7,7 @@ static analysis rules. [Falderal]: http://catseye.tc/node/Falderal -> 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"