mirror of
https://github.com/catseye/SixtyPical.git
synced 2025-04-07 23:37:23 +00:00
Forbid nested with interrupts
blocks, and more refactoring.
This commit is contained in:
parent
3f666f4385
commit
2e2e80664e
12
HISTORY.md
12
HISTORY.md
@ -12,10 +12,18 @@ History of SixtyPical
|
||||
* Local locations need no longer be static. If they are not
|
||||
static, they are considered uninitialized until assigned,
|
||||
and they can be declared with an explicit fixed address.
|
||||
* Along with `goto`, `call` and `with interrupts off` are
|
||||
now forbidden inside a `with interrupts off` block.
|
||||
* More tests to assure that using `call` inside a `point into`
|
||||
block or inside a `for` block does not cause trouble,
|
||||
particularly when the routine being called also uses the
|
||||
variable named in that block.
|
||||
* Fixed a bug where two local statics could be declared with
|
||||
the same name.
|
||||
* Split context off from analyzer and put it in its own module.
|
||||
* Split the SixtyPical Analysis tests across three files.
|
||||
* Split analysis context support off from analyzer, and
|
||||
symbol table support from parse, and it their own modules.
|
||||
* Split the SixtyPical Analysis tests across three files,
|
||||
and placed test appliances for `sixtypical` in own file.
|
||||
|
||||
0.19
|
||||
----
|
||||
|
4
LICENSE
4
LICENSE
@ -7,7 +7,7 @@ covered by the following BSD-compatible license, modelled after the
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
Copyright (c)2014-2018 Chris Pressey, Cat's Eye Technologies.
|
||||
Copyright (c)2014-2019 Chris Pressey, Cat's Eye Technologies.
|
||||
|
||||
The authors intend this Report to belong to the entire SixtyPical
|
||||
community, and so we grant permission to copy and distribute it for
|
||||
@ -24,7 +24,7 @@ The source code for the reference implementation and supporting tools (in the
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
Copyright (c)2014-2018, Chris Pressey, Cat's Eye Technologies.
|
||||
Copyright (c)2014-2019, Chris Pressey, Cat's Eye Technologies.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -1,7 +1,7 @@
|
||||
SixtyPical
|
||||
==========
|
||||
|
||||
_Version 0.19. Work-in-progress, everything is subject to change._
|
||||
_Version 0.20. Work-in-progress, everything is subject to change._
|
||||
|
||||
**SixtyPical** is a [low-level](#low-level) programming language
|
||||
supporting a sophisticated [static analysis](#static-analysis).
|
||||
@ -109,7 +109,9 @@ In order to run the tests for compilation, [dcc6502][] needs to be installed.
|
||||
|
||||
* [SixtyPical specification](doc/SixtyPical.md)
|
||||
* [Literate test suite for SixtyPical syntax](tests/SixtyPical%20Syntax.md)
|
||||
* [Literate test suite for SixtyPical analysis](tests/SixtyPical%20Analysis.md)
|
||||
* [Literate test suite for SixtyPical analysis (operations)](tests/SixtyPical%20Analysis.md)
|
||||
* [Literate test suite for SixtyPical analysis (storage)](tests/SixtyPical%20Storage.md)
|
||||
* [Literate test suite for SixtyPical analysis (control flow)](tests/SixtyPical%20Control%20Flow.md)
|
||||
* [Literate test suite for SixtyPical compilation](tests/SixtyPical%20Compilation.md)
|
||||
* [Literate test suite for SixtyPical fallthru optimization](tests/SixtyPical%20Fallthru.md)
|
||||
|
||||
|
162
TODO.md
162
TODO.md
@ -1,6 +1,9 @@
|
||||
TODO for SixtyPical
|
||||
===================
|
||||
|
||||
Language
|
||||
--------
|
||||
|
||||
### Save values to other-than-the-stack
|
||||
|
||||
Allow
|
||||
@ -9,25 +12,109 @@ Allow
|
||||
...
|
||||
}
|
||||
|
||||
Which uses some other storage location instead of the stack. A local static
|
||||
would be a good candidate for such.
|
||||
Which uses some other storage location instead of the stack. A local non-static
|
||||
would be a good candidate for such. At any rate, the location must not
|
||||
be writeable by anything that is called from within the block. So, probably
|
||||
just restrict this to local non-statics.
|
||||
|
||||
### Analyze `call` within blocks?
|
||||
### Copy byte to/from table
|
||||
|
||||
What happens if you call another routine from inside a `with interrupts off` block?
|
||||
Do we want a `copy bytevar, table + x` instruction? We don't currently have one.
|
||||
You have to `ld a`, `st a`. I think maybe we should have one.
|
||||
|
||||
What happens if you call another routine from inside a `save` block?
|
||||
### Character literals
|
||||
|
||||
What happens if you call another routine from inside a `point into` block?
|
||||
For goodness sake, let the programmer say `'A'` instead of `65`.
|
||||
|
||||
What happens if you call another routine from inside a `for` block?
|
||||
### Character set mapping
|
||||
|
||||
Remember that any of these may have a `goto` ... and they may have a second
|
||||
instance of the same block (e.g. `with interrupts off` nested within
|
||||
`with interrupts off` shouldn't be allowed to turn them back on after the
|
||||
inner block has finished -- even if there is no `call`.)
|
||||
Not all computers think `'A'` should be `65`. Allow the character set to be
|
||||
mapped. Probably copy what Ophis does.
|
||||
|
||||
These holes need to be plugged.
|
||||
### "Include" directives
|
||||
|
||||
Search a searchlist of include paths. And use them to make libraries of routines.
|
||||
|
||||
One such library routine might be an `interrupt routine` type for various architectures.
|
||||
Since "the supervisor" has stored values on the stack, we should be able to trash them
|
||||
with impunity, in such a routine.
|
||||
|
||||
### Pointers into non-byte tables
|
||||
|
||||
Right now you cannot get a pointer into a non-byte (for instance, word or vector) table.
|
||||
|
||||
Word and vector tables are stored as two byte tables in memory. This is useful for
|
||||
indexed access, but makes pointer access more difficult.
|
||||
|
||||
Laying them out for pointer access would make indexed access more difficult.
|
||||
|
||||
### Saving non-byte values
|
||||
|
||||
Right now you cannot save a word value.
|
||||
|
||||
There doesn't seem to be a hugely pressing reason why not.
|
||||
|
||||
Analysis
|
||||
--------
|
||||
|
||||
### Forbid recursion
|
||||
|
||||
What happens if a routine calls itself, directly or indirectly? Many
|
||||
constraints might be violated in this case. We should probably disallow
|
||||
recursion by default. (Which means assembling the callgraph in all cases.)
|
||||
|
||||
### Analyze memory usage
|
||||
|
||||
If you define two variables that occupy the same address, an analysis error ought
|
||||
to be raised. (But there should also be a way to annotate this as intentional.
|
||||
Intentionally making two tables overlap could be valuable. However, the analysis
|
||||
will probably completely miss this fact.)
|
||||
|
||||
Optimization
|
||||
------------
|
||||
|
||||
### Space optimization of local non-statics
|
||||
|
||||
If there are two routines A and B, and A never calls B (even indirectly), and
|
||||
B never calls A (even indirectly), then their non-static locals can
|
||||
be allocated at the same space.
|
||||
|
||||
This is not just an impressive trick -- in the presence of local pointers, which
|
||||
use up a word in zero-page, which we consider a precious resource, it allow those
|
||||
zero-page locations to be re-used.
|
||||
|
||||
### Tail-call optimization
|
||||
|
||||
If a block ends in a `call` can that be converted to end in a `goto`? Why not? I think it can,
|
||||
if the block is in tail position. The constraints should iron out the same both ways.
|
||||
|
||||
As long as the routine has consistent type context every place it exits, that should be fine.
|
||||
|
||||
### Branch optimization in `if`
|
||||
|
||||
Currently the `if` generator is not smart enough to avoid generating silly
|
||||
jump instructions. (See the Fallthru tests.) Improve it.
|
||||
|
||||
### Dead code removal
|
||||
|
||||
Once we have a call graph we can omit routines that we're sure aren't called.
|
||||
|
||||
This would let us use include-files and standard-libraries nicely: any
|
||||
routines they define, but that you don't use, don't get included.
|
||||
|
||||
Analyzing the set of possible routines that a vector can take on would help
|
||||
this immensely.
|
||||
|
||||
Implementation
|
||||
--------------
|
||||
|
||||
### Line numbers in analysis error messages
|
||||
|
||||
For analysis errors, there is a line number, but it's the line of the routine
|
||||
after the routine in which the analysis error occurred. Fix this.
|
||||
|
||||
Blue-skying
|
||||
-----------
|
||||
|
||||
### Pointers associated globally with a table(?)
|
||||
|
||||
@ -45,52 +132,5 @@ at different times.
|
||||
|
||||
These can co-exist with general, non-specific-table-linked `pointer` variables.
|
||||
|
||||
### Space optimization of local non-statics
|
||||
|
||||
If there are two routines A and B, and A never calls B (even indirectly), and
|
||||
B never calls A (even indirectly), then their non-static locals can
|
||||
be allocated at the same space.
|
||||
|
||||
This is more an impressive trick than a really useful feature, but still.
|
||||
Impressive tricks are impressive.
|
||||
|
||||
### Copy byte to/from table
|
||||
|
||||
Do we want a `copy bytevar, table + x` instruction? We don't currently have one.
|
||||
You have to `ld a`, `st a`. I think maybe we should have one.
|
||||
|
||||
### Analyze memory usage
|
||||
|
||||
If you define two variables that occupy the same address, an analysis error ought
|
||||
to be raised. (But there should also be a way to annotate this as intentional.
|
||||
Intentionally making two tables overlap could be valuable. However, the analysis
|
||||
will probably completely miss this fact.)
|
||||
|
||||
### Character literals
|
||||
|
||||
For goodness sake, let the programmer say `'A'` instead of `65`.
|
||||
|
||||
### Character set mapping
|
||||
|
||||
Not all computers think `'A'` should be `65`. Allow the character set to be
|
||||
mapped. Probably copy what Ophis does.
|
||||
|
||||
### Tail-call optimization
|
||||
|
||||
If a block ends in a `call` can that be converted to end in a `goto`? Why not? I think it can,
|
||||
if the block is in tail position. The constraints should iron out the same both ways.
|
||||
|
||||
As long as the routine has consistent type context every place it exits, that should be fine.
|
||||
|
||||
### "Include" directives
|
||||
|
||||
Search a searchlist of include paths. And use them to make libraries of routines.
|
||||
|
||||
One such library routine might be an `interrupt routine` type for various architectures.
|
||||
Since "the supervisor" has stored values on the stack, we should be able to trash them
|
||||
with impunity, in such a routine.
|
||||
|
||||
### Line numbers in analysis error messages
|
||||
|
||||
For analysis errors, there is a line number, but it's the line of the routine
|
||||
after the routine in which the analysis error occurred. Fix this.
|
||||
If we have local pointers and space optimization for local non-statics, though,
|
||||
these don't add as much.
|
||||
|
@ -16,7 +16,8 @@ import sys
|
||||
from tempfile import NamedTemporaryFile
|
||||
import traceback
|
||||
|
||||
from sixtypical.parser import Parser, SymbolTable, merge_programs
|
||||
from sixtypical.symtab import SymbolTable
|
||||
from sixtypical.parser import Parser, merge_programs
|
||||
from sixtypical.analyzer import Analyzer
|
||||
from sixtypical.outputter import outputter_class_for
|
||||
from sixtypical.compiler import Compiler
|
||||
@ -184,7 +185,7 @@ if __name__ == '__main__':
|
||||
argparser.add_argument(
|
||||
"--version",
|
||||
action="version",
|
||||
version="%(prog)s 0.19"
|
||||
version="%(prog)s 0.20"
|
||||
)
|
||||
|
||||
options, unknown = argparser.parse_known_args(sys.argv[1:])
|
||||
|
@ -1,7 +1,7 @@
|
||||
SixtyPical
|
||||
==========
|
||||
|
||||
This document describes the SixtyPical programming language version 0.19,
|
||||
This document describes the SixtyPical programming language version 0.20,
|
||||
both its static semantics (the capabilities and limits of the static
|
||||
analyses it defines) and its runtime semantics (with reference to the
|
||||
semantics of 6502 machine code.)
|
||||
@ -196,7 +196,8 @@ table pointed to is implemented with "indirect indexed" addressing, as in
|
||||
There are extended instruction modes for using these types of memory location.
|
||||
See `copy` below, but here is some illustrative example code:
|
||||
|
||||
point ptr into buf { // this is the only way to initialize a pointer
|
||||
point ptr into buf { // this associates this pointer with this table
|
||||
reset ptr 0 // this is the only way to initialize a pointer
|
||||
add ptr, 4 // note, this is unchecked against table's size!
|
||||
ld y, 0 // you must set this to something yourself
|
||||
copy [ptr] + y, byt // read memory through pointer, into byte
|
||||
@ -658,4 +659,6 @@ Grammar
|
||||
| "repeat" Block ("until" ["not"] LocExpr | "forever")
|
||||
| "for" LocExpr ("up"|"down") "to" Const Block
|
||||
| "with" "interrupts" LitBit Block
|
||||
| "point" LocExpr "into" LocExpr Block
|
||||
| "reset" LocExpr Const
|
||||
.
|
||||
|
@ -269,36 +269,33 @@ define player_logic logic_routine
|
||||
st off, c
|
||||
add ptr, new_pos
|
||||
ld y, 0
|
||||
|
||||
// check collision.
|
||||
ld a, [ptr] + y
|
||||
}
|
||||
|
||||
// if "collision" is with your own self, treat it as if it's blank space!
|
||||
cmp a, 81
|
||||
if z {
|
||||
ld a, 32
|
||||
}
|
||||
cmp a, 32
|
||||
if z {
|
||||
point ptr into screen {
|
||||
// if "collision" is with your own self, treat it as if it's blank space!
|
||||
cmp a, 81
|
||||
if z {
|
||||
ld a, 32
|
||||
}
|
||||
cmp a, 32
|
||||
if z {
|
||||
reset ptr 0
|
||||
st off, c
|
||||
add ptr, pos
|
||||
copy 32, [ptr] + y
|
||||
}
|
||||
|
||||
copy new_pos, pos
|
||||
copy new_pos, pos
|
||||
|
||||
point ptr into screen {
|
||||
reset ptr 0
|
||||
st off, c
|
||||
add ptr, pos
|
||||
copy 81, [ptr] + y
|
||||
}
|
||||
} else {
|
||||
ld a, 1
|
||||
st a, player_died
|
||||
}
|
||||
} else {
|
||||
ld a, 1
|
||||
st a, player_died
|
||||
}
|
||||
}
|
||||
|
||||
@ -314,26 +311,24 @@ define enemy_logic logic_routine
|
||||
st off, c
|
||||
add ptr, new_pos
|
||||
ld y, 0
|
||||
|
||||
// check collision.
|
||||
ld a, [ptr] + y
|
||||
}
|
||||
// if "collision" is with your own self, treat it as if it's blank space!
|
||||
cmp a, 82
|
||||
if z {
|
||||
ld a, 32
|
||||
}
|
||||
cmp a, 32
|
||||
if z {
|
||||
point ptr into screen {
|
||||
|
||||
// if "collision" is with your own self, treat it as if it's blank space!
|
||||
cmp a, 82
|
||||
if z {
|
||||
ld a, 32
|
||||
}
|
||||
cmp a, 32
|
||||
if z {
|
||||
reset ptr 0
|
||||
st off, c
|
||||
add ptr, pos
|
||||
copy 32, [ptr] + y
|
||||
}
|
||||
|
||||
copy new_pos, pos
|
||||
copy new_pos, pos
|
||||
|
||||
point ptr into screen {
|
||||
reset ptr 0
|
||||
st off, c
|
||||
add ptr, pos
|
||||
|
33
eg/rudiments/nested-for.60p
Normal file
33
eg/rudiments/nested-for.60p
Normal file
@ -0,0 +1,33 @@
|
||||
// Include `support/${PLATFORM}.60p` before this source
|
||||
// Should print H (being ASCII 72 = 8 * 9)
|
||||
|
||||
// Increase y by 7, circuitously
|
||||
//
|
||||
define foo routine
|
||||
inputs y
|
||||
outputs y, n, z
|
||||
trashes a, c
|
||||
{
|
||||
save x {
|
||||
ld x, 0
|
||||
for x up to 6 {
|
||||
inc y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Each iteration increases y by 8; there are 9 iterations
|
||||
//
|
||||
define main routine
|
||||
outputs x, y, n, z
|
||||
trashes a, c
|
||||
{
|
||||
ld x, 0
|
||||
ld y, 0
|
||||
for x up to 8 {
|
||||
inc y
|
||||
call foo
|
||||
}
|
||||
ld a, y
|
||||
call chrout
|
||||
}
|
@ -230,9 +230,7 @@ class Analyzer(object):
|
||||
elif isinstance(instr, For):
|
||||
self.analyze_for(instr, context)
|
||||
elif isinstance(instr, WithInterruptsOff):
|
||||
self.analyze_block(instr.block, context)
|
||||
if context.encountered_gotos():
|
||||
raise IllegalJumpError(instr, instr)
|
||||
self.analyze_with_interrupts_off(instr, context)
|
||||
elif isinstance(instr, Save):
|
||||
self.analyze_save(instr, context)
|
||||
elif isinstance(instr, PointInto):
|
||||
@ -290,7 +288,6 @@ class Analyzer(object):
|
||||
target = context.get_assoc(dest.ref)
|
||||
if not target:
|
||||
raise ForbiddenWriteError(instr, dest.ref)
|
||||
context.set_touched(target)
|
||||
context.set_written(target)
|
||||
|
||||
elif self.get_type(src) != self.get_type(dest):
|
||||
@ -465,8 +462,6 @@ class Analyzer(object):
|
||||
target = context.get_assoc(dest.ref)
|
||||
if not target:
|
||||
raise ForbiddenWriteError(instr, dest.ref)
|
||||
context.assert_writeable(target)
|
||||
context.set_touched(target)
|
||||
context.set_written(target)
|
||||
|
||||
elif isinstance(src, IndirectRef) and isinstance(dest, LocationRef):
|
||||
@ -477,9 +472,8 @@ class Analyzer(object):
|
||||
raise UnmeaningfulReadError(instr, src.ref)
|
||||
context.assert_meaningful(origin)
|
||||
|
||||
context.assert_writeable(dest)
|
||||
context.set_touched(dest)
|
||||
context.set_written(dest)
|
||||
|
||||
elif isinstance(src, IndirectRef) and isinstance(dest, IndirectRef):
|
||||
context.assert_meaningful(src.ref, dest.ref, REG_Y)
|
||||
|
||||
@ -491,8 +485,6 @@ class Analyzer(object):
|
||||
target = context.get_assoc(dest.ref)
|
||||
if not target:
|
||||
raise ForbiddenWriteError(instr, dest.ref)
|
||||
context.assert_writeable(target)
|
||||
context.set_touched(target)
|
||||
context.set_written(target)
|
||||
|
||||
elif isinstance(src, LocationRef) and isinstance(dest, IndexedRef):
|
||||
@ -503,7 +495,6 @@ class Analyzer(object):
|
||||
context.set_written(dest.ref)
|
||||
elif isinstance(src, IndexedRef) and isinstance(dest, LocationRef):
|
||||
context.assert_meaningful(src.ref, src.index)
|
||||
context.set_touched(dest)
|
||||
context.set_written(dest)
|
||||
else:
|
||||
context.assert_meaningful(src)
|
||||
@ -569,7 +560,6 @@ class Analyzer(object):
|
||||
exit_context = context.clone()
|
||||
|
||||
for ref in type_.outputs:
|
||||
exit_context.set_touched(ref) # ?
|
||||
exit_context.set_written(ref)
|
||||
|
||||
for ref in type_.trashes:
|
||||
@ -677,6 +667,13 @@ class Analyzer(object):
|
||||
context.set_range(instr.dest, instr.final, instr.final)
|
||||
context.set_writeable(instr.dest)
|
||||
|
||||
def analyze_with_interrupts_off(self, instr, context):
|
||||
block = instr.block
|
||||
for instr in block.instrs:
|
||||
if isinstance(instr, (Call, GoTo, WithInterruptsOff)):
|
||||
raise IllegalJumpError(instr, instr)
|
||||
self.analyze_instr(instr, context)
|
||||
|
||||
def analyze_save(self, instr, context):
|
||||
batons = []
|
||||
for location in instr.locations:
|
||||
|
@ -6,18 +6,10 @@ from sixtypical.ast import (
|
||||
from sixtypical.model import (
|
||||
TYPE_BIT, TYPE_BYTE, TYPE_WORD,
|
||||
RoutineType, VectorType, TableType, PointerType,
|
||||
LocationRef, ConstantRef, IndirectRef, IndexedRef,
|
||||
ConstantRef, IndirectRef, IndexedRef,
|
||||
)
|
||||
from sixtypical.scanner import Scanner
|
||||
|
||||
|
||||
class SymEntry(object):
|
||||
def __init__(self, ast_node, type_):
|
||||
self.ast_node = ast_node
|
||||
self.type_ = type_
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r, %r)" % (self.__class__.__name__, self.ast_node, self.type_)
|
||||
from sixtypical.symtab import SymEntry
|
||||
|
||||
|
||||
class ForwardReference(object):
|
||||
@ -28,42 +20,6 @@ class ForwardReference(object):
|
||||
return "%s(%r)" % (self.__class__.__name__, self.name)
|
||||
|
||||
|
||||
class SymbolTable(object):
|
||||
def __init__(self):
|
||||
self.symbols = {} # symbol name -> SymEntry
|
||||
self.locals = {} # routine name -> (symbol name -> SymEntry)
|
||||
self.typedefs = {} # type name -> Type AST
|
||||
self.consts = {} # const name -> ConstantRef
|
||||
|
||||
for name in ('a', 'x', 'y'):
|
||||
self.symbols[name] = SymEntry(None, TYPE_BYTE)
|
||||
for name in ('c', 'z', 'n', 'v'):
|
||||
self.symbols[name] = SymEntry(None, TYPE_BIT)
|
||||
|
||||
def __str__(self):
|
||||
return "Symbols: {}\nLocals: {}\nTypedefs: {}\nConsts: {}".format(self.symbols, self.locals, self.typedefs, self.consts)
|
||||
|
||||
def has_local(self, routine_name, name):
|
||||
return name in self.locals.get(routine_name, {})
|
||||
|
||||
def fetch_global_type(self, name):
|
||||
return self.symbols[name].type_
|
||||
|
||||
def fetch_local_type(self, routine_name, name):
|
||||
return self.locals[routine_name][name].type_
|
||||
|
||||
def fetch_global_ref(self, name):
|
||||
if name in self.symbols:
|
||||
return LocationRef(name)
|
||||
return None
|
||||
|
||||
def fetch_local_ref(self, routine_name, name):
|
||||
routine_locals = self.locals.get(routine_name, {})
|
||||
if name in routine_locals:
|
||||
return LocationRef(name)
|
||||
return None
|
||||
|
||||
|
||||
class Parser(object):
|
||||
def __init__(self, symtab, text, filename):
|
||||
self.symtab = symtab
|
||||
|
50
src/sixtypical/symtab.py
Normal file
50
src/sixtypical/symtab.py
Normal file
@ -0,0 +1,50 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
from sixtypical.model import (
|
||||
TYPE_BIT, TYPE_BYTE, LocationRef,
|
||||
)
|
||||
|
||||
|
||||
class SymEntry(object):
|
||||
def __init__(self, ast_node, type_):
|
||||
self.ast_node = ast_node
|
||||
self.type_ = type_
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r, %r)" % (self.__class__.__name__, self.ast_node, self.type_)
|
||||
|
||||
|
||||
class SymbolTable(object):
|
||||
def __init__(self):
|
||||
self.symbols = {} # symbol name -> SymEntry
|
||||
self.locals = {} # routine name -> (symbol name -> SymEntry)
|
||||
self.typedefs = {} # type name -> Type AST
|
||||
self.consts = {} # const name -> ConstantRef
|
||||
|
||||
for name in ('a', 'x', 'y'):
|
||||
self.symbols[name] = SymEntry(None, TYPE_BYTE)
|
||||
for name in ('c', 'z', 'n', 'v'):
|
||||
self.symbols[name] = SymEntry(None, TYPE_BIT)
|
||||
|
||||
def __str__(self):
|
||||
return "Symbols: {}\nLocals: {}\nTypedefs: {}\nConsts: {}".format(self.symbols, self.locals, self.typedefs, self.consts)
|
||||
|
||||
def has_local(self, routine_name, name):
|
||||
return name in self.locals.get(routine_name, {})
|
||||
|
||||
def fetch_global_type(self, name):
|
||||
return self.symbols[name].type_
|
||||
|
||||
def fetch_local_type(self, routine_name, name):
|
||||
return self.locals[routine_name][name].type_
|
||||
|
||||
def fetch_global_ref(self, name):
|
||||
if name in self.symbols:
|
||||
return LocationRef(name)
|
||||
return None
|
||||
|
||||
def fetch_local_ref(self, routine_name, name):
|
||||
routine_locals = self.locals.get(routine_name, {})
|
||||
if name in routine_locals:
|
||||
return LocationRef(name)
|
||||
return None
|
4
test.sh
4
test.sh
@ -1,6 +1,10 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This currently represents a lot of tests! If you only want to run a subset,
|
||||
# it's probably best to run `falderal` manually on the file(s) you want to test.
|
||||
|
||||
falderal --substring-error \
|
||||
"tests/appliances/sixtypical.md" \
|
||||
"tests/SixtyPical Syntax.md" \
|
||||
"tests/SixtyPical Analysis.md" \
|
||||
"tests/SixtyPical Storage.md" \
|
||||
|
@ -10,9 +10,6 @@ For control flow, see [SixtyPical Control Flow](SixtyPical%20Control%20Flow.md).
|
||||
|
||||
[Falderal]: http://catseye.tc/node/Falderal
|
||||
|
||||
-> Functionality "Analyze SixtyPical program" is implemented by
|
||||
-> shell command "bin/sixtypical --analyze-only --traceback %(test-body-file) && echo ok"
|
||||
|
||||
-> Tests for functionality "Analyze SixtyPical program"
|
||||
|
||||
### add ###
|
||||
@ -754,6 +751,66 @@ A `goto` cannot appear within a `with interrupts` block.
|
||||
| }
|
||||
? IllegalJumpError
|
||||
|
||||
A `call` cannot appear within a `with interrupts` block.
|
||||
|
||||
| vector routine
|
||||
| inputs x
|
||||
| outputs x
|
||||
| trashes z, n
|
||||
| bar
|
||||
|
|
||||
| define foo routine
|
||||
| inputs x
|
||||
| outputs x
|
||||
| trashes z, n
|
||||
| {
|
||||
| inc x
|
||||
| }
|
||||
|
|
||||
| define other routine
|
||||
| trashes bar, a, n, z
|
||||
| {
|
||||
| ld a, 0
|
||||
| }
|
||||
|
|
||||
| define main routine
|
||||
| trashes bar, a, n, z
|
||||
| {
|
||||
| with interrupts off {
|
||||
| copy foo, bar
|
||||
| call other
|
||||
| }
|
||||
| }
|
||||
? IllegalJumpError
|
||||
|
||||
A `with interrupts` block cannot appear within a `with interrupts` block.
|
||||
|
||||
| vector routine
|
||||
| inputs x
|
||||
| outputs x
|
||||
| trashes z, n
|
||||
| bar
|
||||
|
|
||||
| define foo routine
|
||||
| inputs x
|
||||
| outputs x
|
||||
| trashes z, n
|
||||
| {
|
||||
| inc x
|
||||
| }
|
||||
|
|
||||
| define main routine
|
||||
| trashes bar, a, n, z
|
||||
| {
|
||||
| with interrupts off {
|
||||
| copy foo, bar
|
||||
| with interrupts off {
|
||||
| copy foo, bar
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
? IllegalJumpError
|
||||
|
||||
### typedef ###
|
||||
|
||||
A typedef is a more-readable alias for a type. "Alias" means
|
||||
|
@ -6,9 +6,6 @@ SixtyPical to 6502 machine code.
|
||||
|
||||
[Falderal]: http://catseye.tc/node/Falderal
|
||||
|
||||
-> Functionality "Compile SixtyPical program" is implemented by
|
||||
-> shell command "bin/sixtypical --output-format=c64-basic-prg --traceback %(test-body-file) --output /tmp/foo && tests/appliances/bin/dcc6502-adapter </tmp/foo"
|
||||
|
||||
-> Tests for functionality "Compile SixtyPical program"
|
||||
|
||||
Null program.
|
||||
|
@ -704,6 +704,82 @@ initialized at the start of that loop.
|
||||
| }
|
||||
? UnmeaningfulReadError: y
|
||||
|
||||
And in particular, you can't uninitialize the loop variable, in the loop.
|
||||
|
||||
| define foo routine
|
||||
| trashes x
|
||||
| {
|
||||
| }
|
||||
|
|
||||
| define main routine
|
||||
| outputs x, y, n, z
|
||||
| trashes c
|
||||
| {
|
||||
| ld x, 0
|
||||
| ld y, 15
|
||||
| for x up to 15 {
|
||||
| inc y
|
||||
| call foo
|
||||
| }
|
||||
| }
|
||||
? ForbiddenWriteError: x
|
||||
|
||||
So, if you call a routine from inside the loop, it better not also
|
||||
loop on the same variable.
|
||||
|
||||
| define foo routine
|
||||
| inputs y
|
||||
| outputs x, y, n, z
|
||||
| trashes c
|
||||
| {
|
||||
| ld x, 0
|
||||
| for x up to 15 {
|
||||
| inc y
|
||||
| }
|
||||
| }
|
||||
|
|
||||
| define main routine
|
||||
| outputs x, y, n, z
|
||||
| trashes c
|
||||
| {
|
||||
| ld x, 0
|
||||
| ld y, 15
|
||||
| for x up to 15 {
|
||||
| inc y
|
||||
| call foo
|
||||
| }
|
||||
| }
|
||||
? ForbiddenWriteError: x
|
||||
|
||||
But if you take care to save and restore the loop variable in the
|
||||
called routine, it will be okay.
|
||||
|
||||
| define foo routine
|
||||
| inputs y
|
||||
| outputs y, n, z
|
||||
| trashes a, c
|
||||
| {
|
||||
| save x {
|
||||
| ld x, 0
|
||||
| for x up to 15 {
|
||||
| inc y
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
|
|
||||
| define main routine
|
||||
| outputs x, y, n, z
|
||||
| trashes a, c
|
||||
| {
|
||||
| ld x, 0
|
||||
| ld y, 15
|
||||
| for x up to 15 {
|
||||
| inc y
|
||||
| call foo
|
||||
| }
|
||||
| }
|
||||
= ok
|
||||
|
||||
The "for" loop does not preserve the `z` or `n` registers.
|
||||
|
||||
| define foo routine trashes x {
|
||||
|
@ -61,12 +61,6 @@ to pass these tests to be considered an implementation of SixtyPical.
|
||||
|
||||
[Falderal]: http://catseye.tc/node/Falderal
|
||||
|
||||
-> Functionality "Dump fallthru info for SixtyPical program" is implemented by
|
||||
-> shell command "bin/sixtypical --optimize-fallthru --dump-fallthru-info --analyze-only --traceback %(test-body-file)"
|
||||
|
||||
-> Functionality "Compile SixtyPical program with fallthru optimization" is implemented by
|
||||
-> shell command "bin/sixtypical --output-format=c64-basic-prg --optimize-fallthru --traceback %(test-body-file) --output /tmp/foo && tests/appliances/bin/dcc6502-adapter </tmp/foo"
|
||||
|
||||
-> Tests for functionality "Dump fallthru info for SixtyPical program"
|
||||
|
||||
A single routine, obviously, falls through to nothing and has nothing fall
|
||||
|
@ -1589,6 +1589,68 @@ The offset in `reset` may not exceed the table's size.
|
||||
| }
|
||||
? RangeExceededError
|
||||
|
||||
### dynamic recurrence of `point ... into` blocks
|
||||
|
||||
You cannot call a routine which trashes the pointer from inside a
|
||||
`point ... into` block. Remember that `point ... into` by
|
||||
itself doesn't change anything, so doesn't trash anything.
|
||||
|
||||
| byte table[256] tab
|
||||
| byte table[256] other
|
||||
| pointer ptr
|
||||
|
|
||||
| define sub routine
|
||||
| inputs other
|
||||
| outputs other
|
||||
| {
|
||||
| point ptr into other {
|
||||
| }
|
||||
| }
|
||||
|
|
||||
| define main routine
|
||||
| inputs tab, other
|
||||
| outputs tab, other
|
||||
| trashes a, y, c, z, n, v, ptr
|
||||
| {
|
||||
| ld y, 0
|
||||
| point ptr into tab {
|
||||
| reset ptr 0
|
||||
| copy 123, [ptr] + y
|
||||
| call sub
|
||||
| copy 123, [ptr] + y
|
||||
| }
|
||||
| }
|
||||
= ok
|
||||
|
||||
| byte table[256] tab
|
||||
| byte table[256] other
|
||||
| pointer ptr
|
||||
|
|
||||
| define sub routine
|
||||
| inputs other
|
||||
| outputs other
|
||||
| trashes ptr
|
||||
| {
|
||||
| point ptr into other {
|
||||
| reset ptr 0
|
||||
| }
|
||||
| }
|
||||
|
|
||||
| define main routine
|
||||
| inputs tab, other
|
||||
| outputs tab, other
|
||||
| trashes a, y, c, z, n, v, ptr
|
||||
| {
|
||||
| ld y, 0
|
||||
| point ptr into tab {
|
||||
| reset ptr 0
|
||||
| copy 123, [ptr] + y
|
||||
| call sub
|
||||
| copy 123, [ptr] + y
|
||||
| }
|
||||
| }
|
||||
? UnmeaningfulReadError
|
||||
|
||||
### locals ###
|
||||
|
||||
When memory locations are defined static to a routine, they cannot be
|
||||
|
@ -9,9 +9,6 @@ but not necessarily sensible programs.
|
||||
|
||||
[Falderal]: http://catseye.tc/node/Falderal
|
||||
|
||||
-> Functionality "Check syntax of SixtyPical program" is implemented by
|
||||
-> shell command "bin/sixtypical --parse-only --traceback %(test-body-file) && echo ok"
|
||||
|
||||
-> Tests for functionality "Check syntax of SixtyPical program"
|
||||
|
||||
Rudimentary program.
|
||||
|
20
tests/appliances/sixtypical.md
Normal file
20
tests/appliances/sixtypical.md
Normal file
@ -0,0 +1,20 @@
|
||||
This file contains only the [Falderal][] directives that define the different
|
||||
functionalities tested by the test suite, assuming that it's the reference
|
||||
implementation, `sixtypical`, that is going to implement these functionalities.
|
||||
|
||||
[Falderal]: http://catseye.tc/node/Falderal
|
||||
|
||||
-> Functionality "Check syntax of SixtyPical program" is implemented by
|
||||
-> shell command "bin/sixtypical --parse-only --traceback %(test-body-file) && echo ok"
|
||||
|
||||
-> Functionality "Analyze SixtyPical program" is implemented by
|
||||
-> shell command "bin/sixtypical --analyze-only --traceback %(test-body-file) && echo ok"
|
||||
|
||||
-> Functionality "Compile SixtyPical program" is implemented by
|
||||
-> shell command "bin/sixtypical --output-format=c64-basic-prg --traceback %(test-body-file) --output /tmp/foo && tests/appliances/bin/dcc6502-adapter </tmp/foo"
|
||||
|
||||
-> Functionality "Dump fallthru info for SixtyPical program" is implemented by
|
||||
-> shell command "bin/sixtypical --optimize-fallthru --dump-fallthru-info --analyze-only --traceback %(test-body-file)"
|
||||
|
||||
-> Functionality "Compile SixtyPical program with fallthru optimization" is implemented by
|
||||
-> shell command "bin/sixtypical --output-format=c64-basic-prg --optimize-fallthru --traceback %(test-body-file) --output /tmp/foo && tests/appliances/bin/dcc6502-adapter </tmp/foo"
|
Loading…
x
Reference in New Issue
Block a user