1
0
mirror of https://github.com/catseye/SixtyPical.git synced 2024-11-22 17:32:01 +00:00
SixtyPical/bin/sixtypical

195 lines
6.0 KiB
Plaintext
Raw Normal View History

#!/usr/bin/env python
from os.path import realpath, dirname, join
import sys
sys.path.insert(0, join(dirname(realpath(sys.argv[0])), '..', 'src'))
# ----------------------------------------------------------------- #
from argparse import ArgumentParser
import codecs
import json
from pprint import pprint
from subprocess import check_call
import sys
from tempfile import NamedTemporaryFile
import traceback
from sixtypical.parser import Parser, SymbolTable, merge_programs
from sixtypical.analyzer import Analyzer
from sixtypical.outputter import outputter_class_for
from sixtypical.compiler import Compiler
def process_input_files(filenames, options):
symtab = SymbolTable()
2018-03-27 15:23:22 +00:00
programs = []
for filename in options.filenames:
text = open(filename).read()
parser = Parser(symtab, text, filename)
2018-03-27 15:23:22 +00:00
if options.debug:
print(symtab)
program = parser.program()
programs.append(program)
if options.parse_only:
return
2018-03-27 15:23:22 +00:00
program = merge_programs(programs)
analyzer = Analyzer(symtab, debug=options.debug)
try:
analyzer.analyze_program(program)
finally:
if options.dump_exit_contexts:
sys.stdout.write(json.dumps(analyzer.exit_contexts_map, indent=4, sort_keys=True, separators=(',', ':')))
sys.stdout.write("\n")
compilation_roster = None
if options.optimize_fallthru:
from sixtypical.fallthru import FallthruAnalyzer
def dump(data, label=None):
if not options.dump_fallthru_info:
return
2018-04-04 13:13:53 +00:00
if label:
sys.stdout.write("*** {}:\n".format(label))
sys.stdout.write(json.dumps(data, indent=4, sort_keys=True, separators=(',', ':')))
2018-04-04 13:13:53 +00:00
sys.stdout.write("\n")
fa = FallthruAnalyzer(symtab, debug=options.debug)
fa.analyze_program(program)
compilation_roster = fa.serialize()
dump(compilation_roster)
if options.analyze_only or (options.output is None and not options.run):
return
start_addr = None
if options.origin is not None:
if options.origin.startswith('0x'):
start_addr = int(options.origin, 16)
else:
start_addr = int(options.origin, 10)
if options.run:
fh = NamedTemporaryFile(delete=False)
output_filename = fh.name
else:
fh = open(options.output, 'wb')
output_filename = options.output
outputter = outputter_class_for(options.output_format)(fh, start_addr=start_addr)
outputter.write_prelude()
compiler = Compiler(symtab, outputter.emitter)
compiler.compile_program(program, compilation_roster=compilation_roster)
outputter.write_postlude()
if options.debug:
pprint(outputter.emitter)
else:
outputter.emitter.serialize_to(fh)
fh.close()
if options.run:
emu = {
'c64-basic-prg': "x64 -config vicerc",
'vic20-basic-prg': "xvic -config vicerc",
'atari2600-cart': "stella"
}.get(options.output_format)
if not emu:
raise ValueError("No emulator configured for selected --output-format '{}'".format(options.output_format))
command = "{} {}".format(emu, output_filename)
check_call(command, shell=True)
if __name__ == '__main__':
argparser = ArgumentParser()
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(
"--dump-exit-contexts",
action="store_true",
help="Dump a map, in JSON, of the analysis context at each exit of each routine "
"after analyzing the program."
)
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 ordered fallthru map, in JSON, 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(
"--run",
action="store_true",
help="Engage 'load-and-go' operation: write the output to a temporary filename, "
"infer an emulator from the given --output-format, and boot the emulator."
)
argparser.add_argument(
"--traceback",
action="store_true",
help="When an error occurs, display a full Python traceback."
)
argparser.add_argument(
"--version",
action="version",
version="%(prog)s 0.19"
)
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
2018-02-06 16:14:44 +00:00
else:
traceback.print_exception(e.__class__, e, None)
sys.exit(1)