git @ Cat's Eye Technologies SixtyPical / d22edc5
Allow `if` analysis to pass (needs more tests.) Better styling. Chris Pressey 6 years ago
3 changed file(s) with 52 addition(s) and 44 deletion(s). Raw diff Collapse all Expand all
3939 * `while` loops.
4040 * `repeat` loops.
4141 * a little demo that actually compiles and runs on a C64 emulator.
42 * add line number (or at least routine name) to error messages.
4243
4344 For 0.4 and/or beyond:
4445
00 # encoding: UTF-8
11
2 import sys
3
4 from sixtypical.ast import Program, Defn, Routine, Block, Instr
5 from sixtypical.model import ConstantRef, LocationRef
2 from sixtypical.ast import Program, Routine, Block, Instr
3 from sixtypical.model import (
4 ConstantRef, LocationRef, FLAG_Z, FLAG_N, FLAG_V, FLAG_C
5 )
66
77
88 UNINITIALIZED = 'UNINITIALIZED'
5353 c._writeables = set(self._writeables)
5454 return c
5555
56 def assertInitialized(self, *refs, **kwargs):
56 def set_from(self, c):
57 self._store = dict(c._store)
58 self._writeables = set(c._writeables)
59
60 def each_initialized(self):
61 for key, value in self._store.iteritems():
62 if value == INITIALIZED:
63 yield LocationRef(key)
64
65 def assert_initialized(self, *refs, **kwargs):
5766 exception_class = kwargs.get('exception_class', UninitializedAccessError)
5867 for ref in refs:
5968 if isinstance(ref, ConstantRef):
6473 else:
6574 raise ValueError(ref)
6675
67 def assertWriteable(self, *refs):
76 def assert_writeable(self, *refs):
6877 for ref in refs:
6978 if ref.name not in self._writeables:
7079 raise IllegalWriteError(ref.name)
7180
72 def setInitialized(self, *refs):
81 def set_initialized(self, *refs):
7382 for ref in refs:
7483 self.set(ref, INITIALIZED)
7584
76 def setUninitialized(self, *refs):
85 def set_uninitialized(self, *refs):
7786 for ref in refs:
7887 self.set(ref, UNINITIALIZED)
7988
104113 context = Context(routine.inputs, routine.outputs, routine.trashes)
105114 analyze_block(routine.block, context, routines)
106115 for ref in routine.outputs:
107 context.assertInitialized(ref, exception_class=UninitializedOutputError)
116 context.assert_initialized(ref, exception_class=UninitializedOutputError)
108117
109118
110119 def analyze_block(block, context, routines):
120129 src = instr.src
121130
122131 if opcode == 'ld':
123 context.assertInitialized(src)
124 context.assertWriteable(dest, LocationRef('z'), LocationRef('n'))
125 context.setInitialized(dest, LocationRef('z'), LocationRef('n'))
132 context.assert_initialized(src)
133 context.assert_writeable(dest, FLAG_Z, FLAG_N)
134 context.set_initialized(dest, FLAG_Z, FLAG_N)
126135 elif opcode == 'st':
127 context.assertInitialized(src)
128 context.assertWriteable(dest)
129 context.setInitialized(dest)
136 context.assert_initialized(src)
137 context.assert_writeable(dest)
138 context.set_initialized(dest)
130139 elif opcode in ('add', 'sub'):
131 context.assertInitialized(src, dest, LocationRef('c'))
132 context.assertWriteable(dest,
133 LocationRef('z'), LocationRef('n'),
134 LocationRef('c'), LocationRef('v'),
135 )
136 context.setInitialized(dest,
137 LocationRef('z'), LocationRef('n'),
138 LocationRef('c'), LocationRef('v'),
139 )
140 context.assert_initialized(src, dest, FLAG_C)
141 context.assert_writeable(dest, FLAG_Z, FLAG_N, FLAG_C, FLAG_V)
142 context.set_initialized(dest, FLAG_Z, FLAG_N, FLAG_C, FLAG_V)
140143 elif opcode in ('inc', 'dec'):
141 context.assertInitialized(dest)
142 context.assertWriteable(dest, LocationRef('z'), LocationRef('n'))
143 context.setInitialized(dest, LocationRef('z'), LocationRef('n'))
144 context.assert_initialized(dest)
145 context.assert_writeable(dest, FLAG_Z, FLAG_N)
146 context.set_initialized(dest, FLAG_Z, FLAG_N)
144147 elif opcode == 'cmp':
145 context.assertInitialized(src, dest)
146 context.assertWriteable(LocationRef('z'), LocationRef('n'), LocationRef('c'))
147 context.setInitialized(LocationRef('z'), LocationRef('n'), LocationRef('c'))
148 context.assert_initialized(src, dest)
149 context.assert_writeable(FLAG_Z, FLAG_N, FLAG_C)
150 context.set_initialized(FLAG_Z, FLAG_N, FLAG_C)
148151 elif opcode in ('and', 'or', 'xor'):
149 context.assertInitialized(sec, dest)
150 context.assertWriteable(dest, LocationRef('z'), LocationRef('n'))
151 context.setInitialized(dest, LocationRef('z'), LocationRef('n'))
152 context.assert_initialized(src, dest)
153 context.assert_writeable(dest, FLAG_Z, FLAG_N)
154 context.set_initialized(dest, FLAG_Z, FLAG_N)
152155 elif opcode in ('shl', 'shr'):
153 context.assertInitialized(dest)
154 context.assertWriteable(dest, LocationRef('z'), LocationRef('n'), LocationRef('c'))
155 context.setInitialized(dest, LocationRef('z'), LocationRef('n'), LocationRef('c'))
156 context.assert_initialized(dest)
157 context.assert_writeable(dest, FLAG_Z, FLAG_N, FLAG_C)
158 context.set_initialized(dest, FLAG_Z, FLAG_N, FLAG_C)
156159 elif opcode == 'call':
157160 routine = routines[instr.name]
158161 for ref in routine.inputs:
159 context.assertInitialized(ref)
162 context.assert_initialized(ref)
160163 for ref in routine.outputs:
161 context.assertWriteable(ref)
162 context.setInitialized(ref)
164 context.assert_writeable(ref)
165 context.set_initialized(ref)
163166 for ref in routine.trashes:
164 context.assertWriteable(ref)
165 context.setUninitialized(ref)
167 context.assert_writeable(ref)
168 context.set_uninitialized(ref)
166169 elif opcode == 'if':
167170 context1 = context.clone()
168171 context2 = context.clone()
169172 analyze_block(instr.block1, context1, routines)
170173 analyze_block(instr.block2, context2, routines)
171 reconcile_contexts(context1, context2, output=context)
174 for ref in context1.each_initialized():
175 context2.assert_initialized(ref)
176 for ref in context2.each_initialized():
177 context1.assert_initialized(ref)
178 context.set_from(context1)
172179 else:
173 raise NotImplementedError
180 raise NotImplementedError(opcode)
00 # encoding: UTF-8
11
2 from sixtypical.ast import Program, Defn, Routine, Block, Instr
2 from sixtypical.ast import Program, Routine, Block, Instr
33 from sixtypical.model import ConstantRef, LocationRef
44
55