1
0
mirror of https://github.com/catseye/SixtyPical.git synced 2024-06-06 15:29:30 +00:00

Implement local locations that aren't statically initialized.

This commit is contained in:
Chris Pressey 2019-05-13 12:32:18 +01:00
parent 81e28fa757
commit dd29b6fd4a
8 changed files with 209 additions and 64 deletions

View File

@ -1,6 +1,14 @@
History of SixtyPical
=====================
0.20
----
* Fixed a bug where two local statics could be declared with
the same name.
* Local locations need no longer be static. If they are not
static, they are considered uninitialized until assigned.
0.19
----

20
TODO.md
View File

@ -53,21 +53,17 @@ at different times.
These can co-exist with general, non-specific-table-linked `pointer` variables.
### Local non-statics
### Space optimization of local non-statics
Somewhat related to the above, it should be possible to declare a local storage
location which is not static.
In this case, it would be considered uninitialized each time the routine was
entered.
So, you do not have a guarantee that it has a valid value. But you are guaranteed
that no other routine can read or modify it.
It also enables a trick: if there are two routines A and B, and A never calls B
(even indirectly), and B never calls A (even indirectly), then their locals can
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.
### Locals with explicit addresses
A local could also be given an explicit address. In this case, two locals in
different routines could be given the same address, and as long as the condition
in the above paragraph holds, that's okay. (If it doesn't, the analyzer should

View File

@ -183,9 +183,14 @@ class AnalysisContext(object):
def assert_meaningful(self, *refs, **kwargs):
exception_class = kwargs.get('exception_class', UnmeaningfulReadError)
for ref in refs:
# statics are always meaningful
if self.symtab.has_static(self.routine.name, ref.name):
continue
if self.symtab.has_local(self.routine.name, ref.name):
if ref not in self._range:
message = ref.name
if kwargs.get('message'):
message += ' (%s)' % kwargs['message']
raise exception_class(self.routine, message)
else:
continue
if self.is_constant(ref):
pass
elif isinstance(ref, LocationRef):
@ -203,8 +208,8 @@ class AnalysisContext(object):
def assert_writeable(self, *refs, **kwargs):
exception_class = kwargs.get('exception_class', ForbiddenWriteError)
for ref in refs:
# statics are always writeable
if self.symtab.has_static(self.routine.name, ref.name):
# locals are always writeable
if self.symtab.has_local(self.routine.name, ref.name):
continue
if ref not in self._writeable:
message = ref.name
@ -384,8 +389,8 @@ class AnalysisContext(object):
def max_range(self, ref):
if isinstance(ref, ConstantRef):
return (ref.value, ref.value)
elif self.symtab.has_static(self.routine.name, ref.name):
return self.symtab.fetch_static_type(self.routine.name, ref.name).max_range
elif self.symtab.has_local(self.routine.name, ref.name):
return self.symtab.fetch_local_type(self.routine.name, ref.name).max_range
else:
return self.symtab.fetch_global_type(ref.name).max_range
@ -401,8 +406,8 @@ class Analyzer(object):
# - - - - helper methods - - - -
def get_type_for_name(self, name):
if self.current_routine and self.symtab.has_static(self.current_routine.name, name):
return self.symtab.fetch_static_type(self.current_routine.name, name)
if self.current_routine and self.symtab.has_local(self.current_routine.name, name):
return self.symtab.fetch_local_type(self.current_routine.name, name)
return self.symtab.fetch_global_type(name)
def get_type(self, ref):
@ -462,6 +467,14 @@ class Analyzer(object):
self.current_routine = routine
type_ = self.get_type_for_name(routine.name)
context = AnalysisContext(self.symtab, routine, type_.inputs, type_.outputs, type_.trashes)
# register any local statics as already-initialized
for local_name, local_symentry in self.symtab.locals.get(routine.name, {}).items():
ref = self.symtab.fetch_local_ref(routine.name, local_name)
if local_symentry.ast_node.initial is not None:
context.set_meaningful(ref)
context.set_range(ref, local_symentry.ast_node.initial, local_symentry.ast_node.initial)
self.exit_contexts = []
self.analyze_block(routine.block, context)
@ -505,7 +518,7 @@ class Analyzer(object):
# if something was touched, then it should have been declared to be writable.
for ref in context.each_touched():
if ref not in type_.outputs and ref not in type_.trashes and not self.symtab.has_static(routine.name, ref.name):
if ref not in type_.outputs and ref not in type_.trashes and not self.symtab.has_local(routine.name, ref.name):
raise ForbiddenWriteError(routine, ref.name)
self.exit_contexts = None

View File

@ -59,7 +59,7 @@ class Defn(AST):
class Routine(AST):
value_attrs = ('name', 'addr', 'initial',)
children_attrs = ('statics',)
children_attrs = ('locals',)
child_attrs = ('block',)

View File

@ -34,7 +34,7 @@ class Compiler(object):
self.symtab = symtab
self.emitter = emitter
self.routines = {} # routine.name -> Routine
self.routine_statics = {} # routine.name -> { static.name -> Label }
self.routine_locals = {} # routine.name -> { local.name -> Label }
self.labels = {} # global.name -> Label ("global" includes routines)
self.trampolines = {} # Location -> Label
self.current_routine = None
@ -42,8 +42,8 @@ class Compiler(object):
# - - - - helper methods - - - -
def get_type_for_name(self, name):
if self.current_routine and self.symtab.has_static(self.current_routine.name, name):
return self.symtab.fetch_static_type(self.current_routine.name, name)
if self.current_routine and self.symtab.has_local(self.current_routine.name, name):
return self.symtab.fetch_local_type(self.current_routine.name, name)
return self.symtab.fetch_global_type(name)
def get_type(self, ref):
@ -76,9 +76,9 @@ class Compiler(object):
def get_label(self, name):
if self.current_routine:
static_label = self.routine_statics.get(self.current_routine.name, {}).get(name)
if static_label:
return static_label
local_label = self.routine_locals.get(self.current_routine.name, {}).get(name)
if local_label:
return local_label
return self.labels[name]
def absolute_or_zero_page(self, label):
@ -107,16 +107,15 @@ class Compiler(object):
label.set_addr(routine.addr)
self.labels[routine.name] = label
if hasattr(routine, 'statics'):
self.current_routine = routine
static_labels = {}
for defn in routine.statics:
length = self.compute_length_of_defn(defn)
label = Label(defn.name, addr=defn.addr, length=length)
static_labels[defn.name] = label
declarations.append((defn, self.symtab.fetch_static_type(routine.name, defn.name), label))
self.routine_statics[routine.name] = static_labels
self.current_routine = None
self.current_routine = routine
local_labels = {}
for defn in routine.locals:
length = self.compute_length_of_defn(defn)
label = Label(defn.name, addr=defn.addr, length=length)
local_labels[defn.name] = label
declarations.append((defn, self.symtab.fetch_local_type(routine.name, defn.name), label))
self.routine_locals[routine.name] = local_labels
self.current_routine = None
if compilation_roster is None:
compilation_roster = [['main']] + [[routine.name] for routine in program.routines if routine.name != 'main']

View File

@ -31,7 +31,7 @@ class ForwardReference(object):
class SymbolTable(object):
def __init__(self):
self.symbols = {} # symbol name -> SymEntry
self.statics = {} # routine name -> (symbol name -> SymEntry)
self.locals = {} # routine name -> (symbol name -> SymEntry)
self.typedefs = {} # type name -> Type AST
self.consts = {} # const name -> ConstantRef
@ -41,25 +41,25 @@ class SymbolTable(object):
self.symbols[name] = SymEntry(None, TYPE_BIT)
def __str__(self):
return "Symbols: {}\nStatics: {}\nTypedefs: {}\nConsts: {}".format(self.symbols, self.statics, self.typedefs, self.consts)
return "Symbols: {}\nLocals: {}\nTypedefs: {}\nConsts: {}".format(self.symbols, self.locals, self.typedefs, self.consts)
def has_static(self, routine_name, name):
return name in self.statics.get(routine_name, {})
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_static_type(self, routine_name, name):
return self.statics[routine_name][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_static_ref(self, routine_name, name):
routine_statics = self.statics.get(routine_name, {})
if name in routine_statics:
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
@ -76,7 +76,7 @@ class Parser(object):
def lookup(self, name, allow_forward=False, routine_name=None):
model = self.symtab.fetch_global_ref(name)
if model is None and routine_name:
model = self.symtab.fetch_static_ref(routine_name, name)
model = self.symtab.fetch_local_ref(routine_name, name)
if model is None and allow_forward:
return ForwardReference(name)
if model is None:
@ -88,10 +88,12 @@ class Parser(object):
self.syntax_error('Symbol "%s" already declared' % name)
self.symtab.symbols[name] = SymEntry(ast_node, type_)
def declare_static(self, routine_name, name, ast_node, type_):
def declare_local(self, routine_name, name, ast_node, type_):
if self.symtab.fetch_local_ref(routine_name, name):
self.syntax_error('Symbol "%s" already declared locally' % name)
if self.symtab.fetch_global_ref(name):
self.syntax_error('Symbol "%s" already declared' % name)
self.symtab.statics.setdefault(routine_name, {})[name] = SymEntry(ast_node, type_)
self.syntax_error('Symbol "%s" already declared globally' % name)
self.symtab.locals.setdefault(routine_name, {})[name] = SymEntry(ast_node, type_)
# ---- symbol resolution
@ -306,17 +308,17 @@ class Parser(object):
type_ = self.defn_type()
if not isinstance(type_, RoutineType):
self.syntax_error("Can only define a routine, not {}".format(repr(type_)))
statics = []
locals_ = []
if self.scanner.consume('@'):
self.scanner.check_type('integer literal')
block = None
addr = int(self.scanner.token)
self.scanner.scan()
else:
statics = self.statics()
locals_ = self.locals()
block = self.block()
addr = None
return type_, Routine(self.scanner.line_number, name=name, block=block, addr=addr, statics=statics)
return type_, Routine(self.scanner.line_number, name=name, block=block, addr=addr, locals=locals_)
def labels(self):
accum = []
@ -370,13 +372,19 @@ class Parser(object):
loc = IndexedRef(loc, offset, index)
return loc
def statics(self):
def locals(self):
defns = []
while self.scanner.consume('static'):
type_, defn = self.defn()
if defn.initial is None:
self.syntax_error("Static definition {} must have initial value".format(defn))
self.declare_static(self.current_routine_name, defn.name, defn, type_)
self.declare_local(self.current_routine_name, defn.name, defn, type_)
defns.append(defn)
while self.scanner.consume('local'):
type_, defn = self.defn()
if defn.initial is not None:
self.syntax_error("Local definition {} may not have initial value".format(defn))
self.declare_local(self.current_routine_name, defn.name, defn, type_)
defns.append(defn)
return defns

View File

@ -4251,7 +4251,7 @@ The new style routine definitions support typedefs.
| }
= ok
### static ###
### locals ###
When memory locations are defined static to a routine, they cannot be
directly input, nor directly output; and since they are always initialized,
@ -4276,3 +4276,30 @@ they cannot be trashed. Thus, they really don't participate in the analysis.
| call foo
| }
= ok
When memory locations are defined local to a routine, but not static,
they cannot be directly input, nor directly output; but they are considered
uninitialized from the time the routine is called to until a value is stored
in them.
| define main routine
| inputs x
| outputs x
| trashes z, n
| local byte t
| {
| st x, t
| inc t
| ld x, t
| }
= ok
| define main routine
| outputs x
| trashes z, n
| local byte t
| {
| inc t
| ld x, t
| }
? UnmeaningfulReadError: t

View File

@ -746,13 +746,14 @@ Only routines can be defined in the new style.
| }
? SyntaxError
Memory locations can be defined static to a routine.
Memory locations can be defined local to a routine.
| define foo routine
| inputs x
| outputs x
| trashes z, n
| static byte t : 0
| local word w
| {
| st x, t
| inc t
@ -762,13 +763,14 @@ Memory locations can be defined static to a routine.
| define main routine
| trashes a, x, z, n
| static byte t : 0
| local word w
| {
| ld x, t
| call foo
| }
= ok
Static memory locations must always be given an initial value.
Local static memory locations must always be given an initial value.
| define main routine
| inputs x
@ -782,7 +784,21 @@ Static memory locations must always be given an initial value.
| }
? SyntaxError
Name of a static cannot shadow an existing global or static.
Local dynamic memory locations may not be given an initial value.
| define main routine
| inputs x
| outputs x
| trashes z, n
| local byte t : 10
| {
| st x, t
| inc t
| ld x, t
| }
? SyntaxError
Name of a local cannot shadow an existing global or local.
| byte t
|
@ -790,7 +806,7 @@ Name of a static cannot shadow an existing global or static.
| inputs x
| outputs x
| trashes z, n
| static byte t
| static byte t : 10
| {
| st x, t
| inc t
@ -802,11 +818,89 @@ Name of a static cannot shadow an existing global or static.
| inputs x
| outputs x
| trashes z, n
| static byte t
| static byte t
| static byte t : 10
| static byte t : 20
| {
| st x, t
| inc t
| ld x, t
| }
? SyntaxError
| byte t
|
| define main routine
| inputs x
| outputs x
| trashes z, n
| local byte t
| {
| st x, t
| inc t
| ld x, t
| }
? SyntaxError
| define main routine
| inputs x
| outputs x
| trashes z, n
| local word w
| local word w
| {
| st x, t
| inc t
| ld x, t
| }
? SyntaxError
Since the names of locals are lexically local to a routine, they cannot
appear in the inputs, outputs, trashes list of the routine.
| define main routine
| inputs t
| static byte t : 0
| {
| inc t
| }
? SyntaxError
| define main routine
| outputs t
| static byte t : 0
| {
| inc t
| }
? SyntaxError
| define main routine
| trashes t
| static byte t : 0
| {
| inc t
| }
? SyntaxError
| define main routine
| inputs t
| local byte t
| {
| inc t
| }
? SyntaxError
| define main routine
| outputs t
| local byte t
| {
| inc t
| }
? SyntaxError
| define main routine
| trashes t
| local byte t
| {
| inc t
| }
? SyntaxError