1
0
mirror of https://github.com/catseye/SixtyPical.git synced 2024-10-21 06:23:57 +00:00
SixtyPical/bin/sixtypical

170 lines
4.9 KiB
Python
Executable File

#!/usr/bin/env python
"""Usage: sixtypical [OPTIONS] FILES
Analyzes and compiles a Sixtypical program.
"""
from os.path import realpath, dirname, join
import sys
sys.path.insert(0, join(dirname(realpath(sys.argv[0])), '..', 'src'))
# ----------------------------------------------------------------- #
import codecs
from argparse import ArgumentParser
from pprint import pprint
import sys
import traceback
from sixtypical.parser import Parser, ParsingContext
from sixtypical.analyzer import Analyzer
from sixtypical.outputter import Outputter
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):
context = ParsingContext()
programs = []
for filename in options.filenames:
text = open(filename).read()
parser = Parser(context, text, filename)
if options.debug:
print(context)
program = parser.program()
programs.append(program)
if options.parse_only:
return
program = merge_programs(programs)
analyzer = Analyzer(debug=options.debug)
analyzer.analyze_program(program)
compilation_roster = None
if options.optimize_fallthru:
from sixtypical.fallthru import FallthruAnalyzer
def dump(data, label=None):
import json
if not options.dump_fallthru_info:
return
if label:
sys.stdout.write("*** {}:\n".format(label))
sys.stdout.write(json.dumps(data, indent=4, sort_keys=True, separators=(',', ':')))
sys.stdout.write("\n")
fa = FallthruAnalyzer(debug=options.debug)
fa.analyze_program(program)
compilation_roster = fa.serialize()
dump(compilation_roster)
if options.analyze_only:
return
outputter = Outputter(options.output_format)
if options.origin is not None:
if options.origin.startswith('0x'):
outputter.set_start_addr(int(options.origin, 16))
else:
outputter.set_start_addr(int(options.origin, 10))
with open(options.output, 'wb') as fh:
# this is *awful* binding
emitter = outputter.write_prelude(fh)
compiler = Compiler(emitter)
compiler.compile_program(program, compilation_roster=compilation_roster)
outputter.write_postlude(emitter)
if options.debug:
pprint(emitter.accum)
else:
emitter.serialize_to(fh)
if __name__ == '__main__':
argparser = ArgumentParser(__doc__.strip())
argparser.add_argument(
'filenames', metavar='FILENAME', type=str, nargs='+',
help="The SixtyPical source files to compile."
)
argparser.add_argument(
"--output", "-o", type=str, metavar='FILENAME',
help="File to which generated 6502 code will be written."
)
argparser.add_argument(
"--origin", type=str, default=None,
help="Location in memory where the `main` routine will be "
"located. Default: depends on output format."
)
argparser.add_argument(
"--output-format", type=str, default='raw',
help="Executable format to produce; also sets a default origin. "
"Options are: raw, prg, c64-basic-prg, vic20-basic-prg, atari2600-cart."
"Default: raw."
)
argparser.add_argument(
"--analyze-only",
action="store_true",
help="Only parse and analyze the program; do not compile it."
)
argparser.add_argument(
"--optimize-fallthru",
action="store_true",
help="Reorder the routines in the program to maximize the number of tail calls "
"that can be removed by having execution 'fall through' to the next routine."
)
argparser.add_argument(
"--dump-fallthru-info",
action="store_true",
help="Dump the fallthru map and ordering to stdout after analyzing the program."
)
argparser.add_argument(
"--parse-only",
action="store_true",
help="Only parse the program; do not analyze or compile it."
)
argparser.add_argument(
"--debug",
action="store_true",
help="Display debugging information when analyzing and compiling."
)
argparser.add_argument(
"--traceback",
action="store_true",
help="When an error occurs, display a full Python traceback."
)
options, unknown = argparser.parse_known_args(sys.argv[1:])
remainder = ' '.join(unknown)
try:
process_input_files(options.filenames, options)
except Exception as e:
if options.traceback:
raise
else:
traceback.print_exception(e.__class__, e, None)
sys.exit(1)