diff --git a/src/sixtypical/analyzer.py b/src/sixtypical/analyzer.py index 5c5c1fa..dc2333d 100644 --- a/src/sixtypical/analyzer.py +++ b/src/sixtypical/analyzer.py @@ -1,6 +1,6 @@ # encoding: UTF-8 -from sixtypical.ast import Program, Routine, Block, Instr, SingleOp, If, Repeat, For, WithInterruptsOff +from sixtypical.ast import Program, Routine, Block, Instr, SingleOp, If, Repeat, For, WithInterruptsOff, Save from sixtypical.model import ( TYPE_BYTE, TYPE_WORD, TableType, BufferType, PointerType, VectorType, RoutineType, @@ -269,7 +269,7 @@ class Context(object): self._writeable.remove(ref) def set_writeable(self, *refs): - """Intended to be used for implementing analyzing `for`.""" + """Intended to be used for implementing analyzing `for`, but also used in `save`.""" for ref in refs: self._writeable.add(ref) @@ -292,6 +292,41 @@ class Context(object): self.assert_in_range(dest.index, dest.ref) self.set_written(dest.ref) + def extract(self, location): + """Used in `save`.""" + baton = ( + location, + location in self._touched, + self._range.get(location, None), + location in self._writeable, + ) + + if location in self._touched: + self._touched.remove(location) + self.set_unmeaningful(location) + self.set_writeable(location) + + return baton + + def re_introduce(self, baton): + """Used in `save`.""" + location, was_touched, was_range, was_writeable = baton + + if was_touched: + self._touched.add(location) + elif location in self._touched: + self._touched.remove(location) + + if was_range is not None: + self._range[location] = was_range + elif location in self._range: + del self._range[location] + + if was_writeable: + self._writeable.add(location) + elif location in self._writeable: + self._writeable.remove(location) + class Analyzer(object): @@ -385,6 +420,8 @@ class Analyzer(object): self.analyze_for(instr, context) elif isinstance(instr, WithInterruptsOff): self.analyze_block(instr.block, context) + elif isinstance(instr, Save): + self.analyze_save(instr, context) else: raise NotImplementedError @@ -730,3 +767,13 @@ class Analyzer(object): # after it is executed, we know the range of the loop variable. context.set_range(instr.dest, instr.final, instr.final) context.set_writeable(instr.dest) + + def analyze_save(self, instr, context): + if len(instr.locations) != 1: + raise NotImplementedError("Only 1 location in save is supported right now") + location = instr.locations[0] + + baton = context.extract(location) + self.analyze_block(instr.block, context) + # TODO assert no goto was encountered + context.re_introduce(baton) diff --git a/src/sixtypical/ast.py b/src/sixtypical/ast.py index afd2944..718636f 100644 --- a/src/sixtypical/ast.py +++ b/src/sixtypical/ast.py @@ -90,3 +90,8 @@ class For(Instr): class WithInterruptsOff(Instr): child_attrs = ('block',) + + +class Save(Instr): + value_attrs = ('locations',) + child_attrs = ('block',) diff --git a/src/sixtypical/parser.py b/src/sixtypical/parser.py index 7ca2f69..e16cf66 100644 --- a/src/sixtypical/parser.py +++ b/src/sixtypical/parser.py @@ -1,6 +1,6 @@ # encoding: UTF-8 -from sixtypical.ast import Program, Defn, Routine, Block, SingleOp, If, Repeat, For, WithInterruptsOff +from sixtypical.ast import Program, Defn, Routine, Block, SingleOp, If, Repeat, For, WithInterruptsOff, Save from sixtypical.model import ( TYPE_BIT, TYPE_BYTE, TYPE_WORD, RoutineType, VectorType, TableType, BufferType, PointerType, @@ -470,6 +470,10 @@ class Parser(object): self.scanner.expect("off") block = self.block() return WithInterruptsOff(self.scanner.line_number, block=block) + elif self.scanner.consume("save"): + locations = self.locexprs() + block = self.block() + return Save(self.scanner.line_number, locations=locations, block=block) elif self.scanner.consume("trash"): dest = self.locexpr() return SingleOp(self.scanner.line_number, opcode='trash', src=None, dest=dest) diff --git a/tests/SixtyPical Analysis.md b/tests/SixtyPical Analysis.md index 4670794..f35d667 100644 --- a/tests/SixtyPical Analysis.md +++ b/tests/SixtyPical Analysis.md @@ -1938,6 +1938,21 @@ initialized at the start of that loop. | } ? UnmeaningfulReadError: y +### save ### + +Basic neutral test. + + | routine main + | inputs a, x + | outputs a, x + | trashes z, n + | { + | save x { + | ld a, 0 + | } + | } + = ok + ### copy ### Can't `copy` from a memory location that isn't initialized. diff --git a/tests/SixtyPical Syntax.md b/tests/SixtyPical Syntax.md index 88bdc67..e2ef953 100644 --- a/tests/SixtyPical Syntax.md +++ b/tests/SixtyPical Syntax.md @@ -161,6 +161,20 @@ Basic "open-faced for" loops, up and down. | } = ok +Other blocks. + + | routine main trashes a, x, c, z, v { + | with interrupts off { + | save a, x, c { + | ld a, 0 + | } + | } + | save a, x, c { + | ld a, 0 + | } + | } + = ok + User-defined memory addresses of different types. | byte byt