git @ Cat's Eye Technologies SixtyPical / f7eb0d4
Spec, parse, evaluate, and analyze `repeat` blocks. Chris Pressey 6 years ago
9 changed file(s) with 183 addition(s) and 3 deletion(s). Raw diff Collapse all Expand all
0 History of SixtyPical
1 =====================
2
3 0.1
4 ---
5
6 Initial inspired-but-messy version implemented in Haskell.
7
8 0.2
9 ---
10
11 A complete reboot of SixtyPical 0.1. The reference implementation was
12 rewritten in Python. The language was much simplified. The aim was to get the
13 analysis completely right before adding more sophisticated and useful features
14 in future versions.
15
16 0.3
17 ---
18
19 Added ability to compile to 6502 machine code and output a `PRG` file.
2020
2121 * Design Goals — coming soon.
2222 * [SixtyPical specification](doc/SixtyPical.md)
23 * [SixtyPical history](HISTORY.md)
2324 * [Literate test suite for SixtyPical syntax](tests/SixtyPical Syntax.md)
2425 * [Literate test suite for SixtyPical execution](tests/SixtyPical Execution.md)
2526 * [Literate test suite for SixtyPical analysis](tests/SixtyPical Analysis.md)
3132
3233 For 0.4:
3334
35 * `if not`.
3436 * `while` loops.
3537 * `repeat` loops.
3638 * explicitly-addressed memory locations
3941
4042 * add line number (or at least routine name) to error messages.
4143 * hexadecimal literals.
42 * `if not`.
4344 * 6502-mnemonic aliases (`sec`, `clc`)
4445 * other handy aliases (`eq` for `z`, etc.)
4546 * source code comments.
291291 * It is illegal if src is not initialized.
292292 * It is illegal if any location initialized at the end of the true-branch
293293 is not initialized at the end of the false-branch, and vice versa.
294
295 ### repeat ###
296
297 repeat {
298 <block>
299 } until <src-memory-location>
300
301 Executes the block repeatedly until the src (observed at the end of the
302 execution of the block) is non-zero. The block is always executed as least
303 once.
304
305 * It is illegal if any memory location is uninitialized at the exit of
306 the loop when that memory location is initialized at the start of
307 the loop.
308
309 To simulate a "while" loop, use an `if` internal to the block, like
310
311 repeat {
312 cmp y, 25
313 if z {
314 }
315 } until z
294316
295317 Grammar
296318 -------
320342 | "inc" LocExpr
321343 | "dec" LocExpr
322344 | "call" RoutineIdent
323 | "if" LocExpr Block ["else" Block].
345 | "if" ["not"] LocExpr Block ["else" Block]
346 | "repeat" Block "until" ["not"] LocExpr
347 .
184184 for ref in context2.each_initialized():
185185 context1.assert_initialized(ref, exception_class=InconsistentInitializationError)
186186 context.set_from(context1)
187 elif opcode == 'repeat':
188 # it will always be executed at least once, so analyze it having
189 # been executed the first time.
190 analyze_block(instr.block, context, routines)
191
192 # now analyze it having been executed a second time, with the context
193 # of it having already been executed.
194 analyze_block(instr.block, context, routines)
195
196 # NB I *think* that's enough... but it might not be?
187197 else:
188198 raise NotImplementedError(opcode)
148148 eval_block(instr.block1, context, routines)
149149 elif instr.block2:
150150 eval_block(instr.block2, context, routines)
151 elif opcode == 'repeat':
152 eval_block(instr.block, context, routines)
153 while context.get(src) == 0:
154 eval_block(instr.block, context, routines)
151155 else:
152156 raise NotImplementedError
166166
167167 def instr(self):
168168 if self.scanner.consume('if'):
169 inverted = False
170 if self.scanner.consume('not'):
171 inverted = True
169172 src = self.locexpr()
170173 block1 = self.block()
171174 block2 = None
172175 if self.scanner.consume('else'):
173176 block2 = self.block()
174 return Instr(opcode='if', dest=None, src=src, block1=block1, block2=block2)
177 return Instr(opcode='if', dest=None, src=src,
178 block1=block1, block2=block2, inverted=inverted)
179 elif self.scanner.consume('repeat'):
180 inverted = False
181 src = None
182 block = self.block()
183 if self.scanner.consume('until'):
184 if self.scanner.consume('not'):
185 inverted = True
186 src = self.locexpr()
187 return Instr(opcode='repeat', dest=None, src=src,
188 block=block, inverted=inverted)
175189 elif self.scanner.token in ("ld", "add", "sub", "cmp", "and", "or", "xor"):
176190 opcode = self.scanner.token
177191 self.scanner.scan()
778778 | }
779779 | }
780780 = ok
781
782 ### repeat ###
783
784 Repeat loop.
785
786 | routine main
787 | outputs x, y, n, z, c
788 | {
789 | ld x, 0
790 | ld y, 15
791 | repeat {
792 | inc x
793 | inc y
794 | cmp x, 10
795 | } until z
796 | }
797 = ok
798
799 You can initialize something inside the loop that was uninitialized outside.
800
801 | routine main
802 | outputs x, y, n, z, c
803 | {
804 | ld x, 0
805 | repeat {
806 | ld y, 15
807 | inc x
808 | cmp x, 10
809 | } until z
810 | }
811 = ok
812
813 But you can't UNinitialize something at the end of the loop that you need
814 initialized at the start.
815
816 | routine foo
817 | trashes y
818 | {
819 | }
820 |
821 | routine main
822 | outputs x, y, n, z, c
823 | {
824 | ld x, 0
825 | ld y, 15
826 | repeat {
827 | inc x
828 | inc y
829 | call foo
830 | cmp x, 10
831 | } until z
832 | }
833 ? UninitializedAccessError: y
368368 = x: 2
369369 = y: 0
370370 = z: 0
371
372 Repeat loop.
373
374 | routine main {
375 | ld x, 0
376 | ld y, 15
377 | repeat {
378 | inc x
379 | inc y
380 | cmp x, 10
381 | } until z
382 | }
383 = a: 0
384 = c: 0
385 = n: 0
386 = v: 0
387 = x: 10
388 = y: 25
389 = z: 1
3838 | trashes x
3939 | @ 65487
4040 = ok
41
42 If with not
43
44 | routine foo {
45 | ld y, 0
46 | cmp y, 10
47 | if not z {
48 | inc y
49 | cmp y, 10
50 | }
51 | }
52 = ok
53
54 Repeat loop
55
56 | routine foo {
57 | ld y, 0
58 | repeat {
59 | inc y
60 | cmp y, 10
61 | } until z
62 | }
63 = ok
64
65 "While" loop
66
67 | routine foo inputs y {
68 | repeat {
69 | cmp y, 10
70 | if not z {
71 | inc y
72 | }
73 | }
74 | }
75 = ok