mirror of
https://github.com/catseye/SixtyPical.git
synced 2025-02-08 10:30:55 +00:00
Spec, parse, evaluate, and analyze repeat
blocks.
This commit is contained in:
parent
24273f1344
commit
f7eb0d48a8
20
HISTORY.markdown
Normal file
20
HISTORY.markdown
Normal file
@ -0,0 +1,20 @@
|
||||
History of SixtyPical
|
||||
=====================
|
||||
|
||||
0.1
|
||||
---
|
||||
|
||||
Initial inspired-but-messy version implemented in Haskell.
|
||||
|
||||
0.2
|
||||
---
|
||||
|
||||
A complete reboot of SixtyPical 0.1. The reference implementation was
|
||||
rewritten in Python. The language was much simplified. The aim was to get the
|
||||
analysis completely right before adding more sophisticated and useful features
|
||||
in future versions.
|
||||
|
||||
0.3
|
||||
---
|
||||
|
||||
Added ability to compile to 6502 machine code and output a `PRG` file.
|
@ -21,6 +21,7 @@ Documentation
|
||||
|
||||
* Design Goals — coming soon.
|
||||
* [SixtyPical specification](doc/SixtyPical.md)
|
||||
* [SixtyPical history](HISTORY.md)
|
||||
* [Literate test suite for SixtyPical syntax](tests/SixtyPical Syntax.md)
|
||||
* [Literate test suite for SixtyPical execution](tests/SixtyPical Execution.md)
|
||||
* [Literate test suite for SixtyPical analysis](tests/SixtyPical Analysis.md)
|
||||
@ -32,6 +33,7 @@ TODO
|
||||
|
||||
For 0.4:
|
||||
|
||||
* `if not`.
|
||||
* `while` loops.
|
||||
* `repeat` loops.
|
||||
* explicitly-addressed memory locations
|
||||
@ -40,7 +42,6 @@ For 0.5:
|
||||
|
||||
* add line number (or at least routine name) to error messages.
|
||||
* hexadecimal literals.
|
||||
* `if not`.
|
||||
* 6502-mnemonic aliases (`sec`, `clc`)
|
||||
* other handy aliases (`eq` for `z`, etc.)
|
||||
* source code comments.
|
||||
|
@ -293,6 +293,28 @@ it is treated like an empty block.
|
||||
* It is illegal if any location initialized at the end of the true-branch
|
||||
is not initialized at the end of the false-branch, and vice versa.
|
||||
|
||||
### repeat ###
|
||||
|
||||
repeat {
|
||||
<block>
|
||||
} until <src-memory-location>
|
||||
|
||||
Executes the block repeatedly until the src (observed at the end of the
|
||||
execution of the block) is non-zero. The block is always executed as least
|
||||
once.
|
||||
|
||||
* It is illegal if any memory location is uninitialized at the exit of
|
||||
the loop when that memory location is initialized at the start of
|
||||
the loop.
|
||||
|
||||
To simulate a "while" loop, use an `if` internal to the block, like
|
||||
|
||||
repeat {
|
||||
cmp y, 25
|
||||
if z {
|
||||
}
|
||||
} until z
|
||||
|
||||
Grammar
|
||||
-------
|
||||
|
||||
@ -321,4 +343,6 @@ Grammar
|
||||
| "inc" LocExpr
|
||||
| "dec" LocExpr
|
||||
| "call" RoutineIdent
|
||||
| "if" LocExpr Block ["else" Block].
|
||||
| "if" ["not"] LocExpr Block ["else" Block]
|
||||
| "repeat" Block "until" ["not"] LocExpr
|
||||
.
|
||||
|
@ -185,5 +185,15 @@ def analyze_instr(instr, context, routines):
|
||||
for ref in context2.each_initialized():
|
||||
context1.assert_initialized(ref, exception_class=InconsistentInitializationError)
|
||||
context.set_from(context1)
|
||||
elif opcode == 'repeat':
|
||||
# it will always be executed at least once, so analyze it having
|
||||
# been executed the first time.
|
||||
analyze_block(instr.block, context, routines)
|
||||
|
||||
# now analyze it having been executed a second time, with the context
|
||||
# of it having already been executed.
|
||||
analyze_block(instr.block, context, routines)
|
||||
|
||||
# NB I *think* that's enough... but it might not be?
|
||||
else:
|
||||
raise NotImplementedError(opcode)
|
||||
|
@ -149,5 +149,9 @@ def eval_instr(instr, context, routines):
|
||||
eval_block(instr.block1, context, routines)
|
||||
elif instr.block2:
|
||||
eval_block(instr.block2, context, routines)
|
||||
elif opcode == 'repeat':
|
||||
eval_block(instr.block, context, routines)
|
||||
while context.get(src) == 0:
|
||||
eval_block(instr.block, context, routines)
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
@ -167,12 +167,26 @@ class Parser(object):
|
||||
|
||||
def instr(self):
|
||||
if self.scanner.consume('if'):
|
||||
inverted = False
|
||||
if self.scanner.consume('not'):
|
||||
inverted = True
|
||||
src = self.locexpr()
|
||||
block1 = self.block()
|
||||
block2 = None
|
||||
if self.scanner.consume('else'):
|
||||
block2 = self.block()
|
||||
return Instr(opcode='if', dest=None, src=src, block1=block1, block2=block2)
|
||||
return Instr(opcode='if', dest=None, src=src,
|
||||
block1=block1, block2=block2, inverted=inverted)
|
||||
elif self.scanner.consume('repeat'):
|
||||
inverted = False
|
||||
src = None
|
||||
block = self.block()
|
||||
if self.scanner.consume('until'):
|
||||
if self.scanner.consume('not'):
|
||||
inverted = True
|
||||
src = self.locexpr()
|
||||
return Instr(opcode='repeat', dest=None, src=src,
|
||||
block=block, inverted=inverted)
|
||||
elif self.scanner.token in ("ld", "add", "sub", "cmp", "and", "or", "xor"):
|
||||
opcode = self.scanner.token
|
||||
self.scanner.scan()
|
||||
|
@ -779,3 +779,56 @@ An `if` with a single block is analyzed as if it had an empty `else` block.
|
||||
| }
|
||||
| }
|
||||
= ok
|
||||
|
||||
### repeat ###
|
||||
|
||||
Repeat loop.
|
||||
|
||||
| routine main
|
||||
| outputs x, y, n, z, c
|
||||
| {
|
||||
| ld x, 0
|
||||
| ld y, 15
|
||||
| repeat {
|
||||
| inc x
|
||||
| inc y
|
||||
| cmp x, 10
|
||||
| } until z
|
||||
| }
|
||||
= ok
|
||||
|
||||
You can initialize something inside the loop that was uninitialized outside.
|
||||
|
||||
| routine main
|
||||
| outputs x, y, n, z, c
|
||||
| {
|
||||
| ld x, 0
|
||||
| repeat {
|
||||
| ld y, 15
|
||||
| inc x
|
||||
| cmp x, 10
|
||||
| } until z
|
||||
| }
|
||||
= ok
|
||||
|
||||
But you can't UNinitialize something at the end of the loop that you need
|
||||
initialized at the start.
|
||||
|
||||
| routine foo
|
||||
| trashes y
|
||||
| {
|
||||
| }
|
||||
|
|
||||
| routine main
|
||||
| outputs x, y, n, z, c
|
||||
| {
|
||||
| ld x, 0
|
||||
| ld y, 15
|
||||
| repeat {
|
||||
| inc x
|
||||
| inc y
|
||||
| call foo
|
||||
| cmp x, 10
|
||||
| } until z
|
||||
| }
|
||||
? UninitializedAccessError: y
|
||||
|
@ -369,3 +369,22 @@ If.
|
||||
= x: 2
|
||||
= y: 0
|
||||
= z: 0
|
||||
|
||||
Repeat loop.
|
||||
|
||||
| routine main {
|
||||
| ld x, 0
|
||||
| ld y, 15
|
||||
| repeat {
|
||||
| inc x
|
||||
| inc y
|
||||
| cmp x, 10
|
||||
| } until z
|
||||
| }
|
||||
= a: 0
|
||||
= c: 0
|
||||
= n: 0
|
||||
= v: 0
|
||||
= x: 10
|
||||
= y: 25
|
||||
= z: 1
|
||||
|
@ -39,3 +39,38 @@ Extern routines
|
||||
| trashes x
|
||||
| @ 65487
|
||||
= ok
|
||||
|
||||
If with not
|
||||
|
||||
| routine foo {
|
||||
| ld y, 0
|
||||
| cmp y, 10
|
||||
| if not z {
|
||||
| inc y
|
||||
| cmp y, 10
|
||||
| }
|
||||
| }
|
||||
= ok
|
||||
|
||||
Repeat loop
|
||||
|
||||
| routine foo {
|
||||
| ld y, 0
|
||||
| repeat {
|
||||
| inc y
|
||||
| cmp y, 10
|
||||
| } until z
|
||||
| }
|
||||
= ok
|
||||
|
||||
"While" loop
|
||||
|
||||
| routine foo inputs y {
|
||||
| repeat {
|
||||
| cmp y, 10
|
||||
| if not z {
|
||||
| inc y
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
= ok
|
||||
|
Loading…
x
Reference in New Issue
Block a user