1
0
mirror of https://github.com/catseye/SixtyPical.git synced 2025-01-10 02:29:23 +00:00

Beginnings of modularity.

This commit is contained in:
Chris Pressey 2018-03-27 16:23:22 +01:00
parent d9e625db30
commit 6744ad29a9
5 changed files with 99 additions and 38 deletions

View File

@ -84,7 +84,7 @@ is probably NP-complete. But doing it adequately is probably not that hard.
* Tests, and implementation, ensuring a routine can be assigned to a vector of "wider" type * Tests, and implementation, ensuring a routine can be assigned to a vector of "wider" type
* Related: can we simply view a (small) part of a buffer as a byte table? If not, why not? * Related: can we simply view a (small) part of a buffer as a byte table? If not, why not?
* Related: add constant to buffer to get new buffer. (Or to table, but... well, maybe.) * Related: add constant to buffer to get new buffer. (Or to table, but... well, maybe.)
* Check that the buffer being read or written to through pointer, appears in approporiate inputs or outputs set. * Check that the buffer being read or written to through pointer, appears in appropriate inputs or outputs set.
(Associate each pointer with the buffer it points into.) (Associate each pointer with the buffer it points into.)
* `static` pointers -- currently not possible because pointers must be zero-page, thus `@`, thus uninitialized. * `static` pointers -- currently not possible because pointers must be zero-page, thus `@`, thus uninitialized.
* Question the value of the "consistent initialization" principle for `if` statement analysis. * Question the value of the "consistent initialization" principle for `if` statement analysis.
@ -94,6 +94,5 @@ is probably NP-complete. But doing it adequately is probably not that hard.
* Possibly `ld x, [ptr] + y`, possibly `st x, [ptr] + y`. * Possibly `ld x, [ptr] + y`, possibly `st x, [ptr] + y`.
* Maybe even `copy [ptra] + y, [ptrb] + y`, which can be compiled to indirect LDA then indirect STA! * Maybe even `copy [ptra] + y, [ptrb] + y`, which can be compiled to indirect LDA then indirect STA!
* Optimize `ld a, z` and `st a, z` to zero-page operations if address of z < 256. * Optimize `ld a, z` and `st a, z` to zero-page operations if address of z < 256.
* Include files?
[VICE]: http://vice-emu.sourceforge.net/ [VICE]: http://vice-emu.sourceforge.net/

View File

@ -18,26 +18,42 @@ from pprint import pprint
import sys import sys
import traceback import traceback
from sixtypical.parser import Parser from sixtypical.parser import Parser, ParsingContext
from sixtypical.analyzer import Analyzer from sixtypical.analyzer import Analyzer
from sixtypical.emitter import Emitter, Byte, Word from sixtypical.emitter import Emitter, Byte, Word
from sixtypical.compiler import Compiler from sixtypical.compiler import Compiler
def merge_programs(programs):
"""Assumes that the programs do not have any conflicts."""
from sixtypical.ast import Program
full = Program(1, defns=[], routines=[])
for p in programs:
full.defns.extend(p.defns)
full.routines.extend(p.routines)
return full
def process_input_files(filenames, options): def process_input_files(filenames, options):
context = ParsingContext()
programs = [] programs = []
for filename in options.filenames: for filename in options.filenames:
text = open(filename).read() text = open(filename).read()
parser = Parser(text, filename) parser = Parser(context, text, filename)
if options.debug:
print(context)
program = parser.program() program = parser.program()
programs.append(program) programs.append(program)
if options.parse_only: if options.parse_only:
return return
#program = merge_programs(programs) program = merge_programs(programs)
program = programs[0]
analyzer = Analyzer(debug=options.debug) analyzer = Analyzer(debug=options.debug)
analyzer.analyze_program(program) analyzer.analyze_program(program)

View File

@ -0,0 +1,18 @@
routine chrout
inputs a
trashes a
@ 65490
routine printa
trashes a, z, n
{
ld a, 65
call chrout
}
routine printb
trashes a, z, n
{
ld a, 66
call chrout
}

View File

@ -0,0 +1,19 @@
vector routine
trashes a, z, n
print
// routine printb
// trashes a, z, n
// {
// ld a, 66
// call chrout
// }
routine main
trashes print, a, z, n
{
copy printa, print
call print
copy printb, print
call print
}

View File

@ -18,27 +18,36 @@ class SymEntry(object):
return "%s(%r, %r)" % (self.__class__.__name__, self.ast_node, self.model) return "%s(%r, %r)" % (self.__class__.__name__, self.ast_node, self.model)
class Parser(object): class ParsingContext(object):
def __init__(self, text, filename): def __init__(self):
self.scanner = Scanner(text, filename)
self.symbols = {} # token -> SymEntry self.symbols = {} # token -> SymEntry
self.current_statics = {} # token -> SymEntry self.statics = {} # token -> SymEntry
self.typedefs = {} # token -> Type AST self.typedefs = {} # token -> Type AST
self.consts = {} # token -> Loc self.consts = {} # token -> Loc
for token in ('a', 'x', 'y'): for token in ('a', 'x', 'y'):
self.symbols[token] = SymEntry(None, LocationRef(TYPE_BYTE, token)) self.symbols[token] = SymEntry(None, LocationRef(TYPE_BYTE, token))
for token in ('c', 'z', 'n', 'v'): for token in ('c', 'z', 'n', 'v'):
self.symbols[token] = SymEntry(None, LocationRef(TYPE_BIT, token)) self.symbols[token] = SymEntry(None, LocationRef(TYPE_BIT, token))
def __str__(self):
return "Symbols: {}\nStatics: {}\nTypedefs: {}\nConsts: {}".format(self.symbols, self.statics, self.typedefs, self.consts)
class Parser(object):
def __init__(self, context, text, filename):
self.context = context
self.scanner = Scanner(text, filename)
self.backpatch_instrs = [] self.backpatch_instrs = []
def syntax_error(self, msg): def syntax_error(self, msg):
self.scanner.syntax_error(msg) self.scanner.syntax_error(msg)
def soft_lookup(self, name): def soft_lookup(self, name):
if name in self.current_statics: if name in self.context.statics:
return self.current_statics[name].model return self.context.statics[name].model
if name in self.symbols: if name in self.context.symbols:
return self.symbols[name].model return self.context.symbols[name].model
return None return None
def lookup(self, name): def lookup(self, name):
@ -58,13 +67,13 @@ class Parser(object):
if self.scanner.on('const'): if self.scanner.on('const'):
self.defn_const() self.defn_const()
typenames = ['byte', 'word', 'table', 'vector', 'buffer', 'pointer'] # 'routine', typenames = ['byte', 'word', 'table', 'vector', 'buffer', 'pointer'] # 'routine',
typenames.extend(self.typedefs.keys()) typenames.extend(self.context.typedefs.keys())
while self.scanner.on(*typenames): while self.scanner.on(*typenames):
defn = self.defn() defn = self.defn()
name = defn.name name = defn.name
if name in self.symbols: if name in self.context.symbols:
self.syntax_error('Symbol "%s" already declared' % name) self.syntax_error('Symbol "%s" already declared' % name)
self.symbols[name] = SymEntry(defn, defn.location) self.context.symbols[name] = SymEntry(defn, defn.location)
defns.append(defn) defns.append(defn)
while self.scanner.on('define', 'routine'): while self.scanner.on('define', 'routine'):
if self.scanner.consume('define'): if self.scanner.consume('define'):
@ -74,14 +83,14 @@ class Parser(object):
else: else:
routine = self.legacy_routine() routine = self.legacy_routine()
name = routine.name name = routine.name
if name in self.symbols: if name in self.context.symbols:
self.syntax_error('Symbol "%s" already declared' % name) self.syntax_error('Symbol "%s" already declared' % name)
self.symbols[name] = SymEntry(routine, routine.location) self.context.symbols[name] = SymEntry(routine, routine.location)
routines.append(routine) routines.append(routine)
self.scanner.check_type('EOF') self.scanner.check_type('EOF')
# now backpatch the executable types. # now backpatch the executable types.
#for type_name, type_ in self.typedefs.iteritems(): #for type_name, type_ in self.context.typedefs.iteritems():
# type_.backpatch_constraint_labels(lambda w: self.lookup(w)) # type_.backpatch_constraint_labels(lambda w: self.lookup(w))
for defn in defns: for defn in defns:
defn.location.type.backpatch_constraint_labels(lambda w: self.lookup(w)) defn.location.type.backpatch_constraint_labels(lambda w: self.lookup(w))
@ -90,18 +99,18 @@ class Parser(object):
for instr in self.backpatch_instrs: for instr in self.backpatch_instrs:
if instr.opcode in ('call', 'goto'): if instr.opcode in ('call', 'goto'):
name = instr.location name = instr.location
if name not in self.symbols: if name not in self.context.symbols:
self.syntax_error('Undefined routine "%s"' % name) self.syntax_error('Undefined routine "%s"' % name)
if not isinstance(self.symbols[name].model.type, (RoutineType, VectorType)): if not isinstance(self.context.symbols[name].model.type, (RoutineType, VectorType)):
self.syntax_error('Illegal call of non-executable "%s"' % name) self.syntax_error('Illegal call of non-executable "%s"' % name)
instr.location = self.symbols[name].model instr.location = self.context.symbols[name].model
if instr.opcode in ('copy',) and isinstance(instr.src, basestring): if instr.opcode in ('copy',) and isinstance(instr.src, basestring):
name = instr.src name = instr.src
if name not in self.symbols: if name not in self.context.symbols:
self.syntax_error('Undefined routine "%s"' % name) self.syntax_error('Undefined routine "%s"' % name)
if not isinstance(self.symbols[name].model.type, (RoutineType, VectorType)): if not isinstance(self.context.symbols[name].model.type, (RoutineType, VectorType)):
self.syntax_error('Illegal copy of non-executable "%s"' % name) self.syntax_error('Illegal copy of non-executable "%s"' % name)
instr.src = self.symbols[name].model instr.src = self.context.symbols[name].model
return Program(self.scanner.line_number, defns=defns, routines=routines) return Program(self.scanner.line_number, defns=defns, routines=routines)
@ -109,18 +118,18 @@ class Parser(object):
self.scanner.expect('typedef') self.scanner.expect('typedef')
type_ = self.defn_type() type_ = self.defn_type()
name = self.defn_name() name = self.defn_name()
if name in self.typedefs: if name in self.context.typedefs:
self.syntax_error('Type "%s" already declared' % name) self.syntax_error('Type "%s" already declared' % name)
self.typedefs[name] = type_ self.context.typedefs[name] = type_
return type_ return type_
def defn_const(self): def defn_const(self):
self.scanner.expect('const') self.scanner.expect('const')
name = self.defn_name() name = self.defn_name()
if name in self.consts: if name in self.context.consts:
self.syntax_error('Const "%s" already declared' % name) self.syntax_error('Const "%s" already declared' % name)
loc = self.const() loc = self.const()
self.consts[name] = loc self.context.consts[name] = loc
return loc return loc
def defn(self): def defn(self):
@ -163,8 +172,8 @@ class Parser(object):
loc = ConstantRef(TYPE_WORD, int(self.scanner.token)) loc = ConstantRef(TYPE_WORD, int(self.scanner.token))
self.scanner.scan() self.scanner.scan()
return loc return loc
elif self.scanner.token in self.consts: elif self.scanner.token in self.context.consts:
loc = self.consts[self.scanner.token] loc = self.context.consts[self.scanner.token]
self.scanner.scan() self.scanner.scan()
return loc return loc
else: else:
@ -215,9 +224,9 @@ class Parser(object):
else: else:
type_name = self.scanner.token type_name = self.scanner.token
self.scanner.scan() self.scanner.scan()
if type_name not in self.typedefs: if type_name not in self.context.typedefs:
self.syntax_error("Undefined type '%s'" % type_name) self.syntax_error("Undefined type '%s'" % type_name)
type_ = self.typedefs[type_name] type_ = self.context.typedefs[type_name]
return type_ return type_
@ -273,9 +282,9 @@ class Parser(object):
else: else:
statics = self.statics() statics = self.statics()
self.current_statics = self.compose_statics_dict(statics) self.context.statics = self.compose_statics_dict(statics)
block = self.block() block = self.block()
self.current_statics = {} self.context.statics = {}
addr = None addr = None
location = LocationRef(type_, name) location = LocationRef(type_, name)
@ -289,7 +298,7 @@ class Parser(object):
c = {} c = {}
for defn in statics: for defn in statics:
name = defn.name name = defn.name
if name in self.symbols or name in self.current_statics: if name in self.context.symbols or name in self.context.statics:
self.syntax_error('Symbol "%s" already declared' % name) self.syntax_error('Symbol "%s" already declared' % name)
c[name] = SymEntry(defn, defn.location) c[name] = SymEntry(defn, defn.location)
return c return c
@ -316,7 +325,7 @@ class Parser(object):
return accum return accum
def locexpr(self, forward=False): def locexpr(self, forward=False):
if self.scanner.token in ('on', 'off', 'word') or self.scanner.token in self.consts or self.scanner.on_type('integer literal'): if self.scanner.token in ('on', 'off', 'word') or self.scanner.token in self.context.consts or self.scanner.on_type('integer literal'):
return self.const() return self.const()
elif forward: elif forward:
name = self.scanner.token name = self.scanner.token