1
0
mirror of https://github.com/catseye/SixtyPical.git synced 2024-08-01 02:29:03 +00:00
SixtyPical/bin/sixtypical

159 lines
4.4 KiB
Plaintext
Raw Normal View History

#!/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
2018-03-27 15:23:22 +00:00
from sixtypical.parser import Parser, ParsingContext
from sixtypical.analyzer import Analyzer
from sixtypical.emitter import Emitter, Byte, Word
from sixtypical.compiler import Compiler
2018-03-27 15:23:22 +00:00
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):
2018-03-27 15:23:22 +00:00
context = ParsingContext()
programs = []
for filename in options.filenames:
text = open(filename).read()
2018-03-27 15:23:22 +00:00
parser = Parser(context, text, filename)
if options.debug:
print(context)
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(debug=options.debug)
analyzer.analyze_program(program)
if options.analyze_only:
return
fh = sys.stdout
if options.origin.startswith('0x'):
start_addr = int(options.origin, 16)
else:
start_addr = int(options.origin, 10)
output_format = options.output_format
prelude = []
if options.prelude == 'c64':
output_format = 'prg'
start_addr = 0x0801
prelude = [0x10, 0x08, 0xc9, 0x07, 0x9e, 0x32,
0x30, 0x36, 0x31, 0x00, 0x00, 0x00]
elif options.prelude == 'vic20':
output_format = 'prg'
start_addr = 0x1001
prelude = [0x0b, 0x10, 0xc9, 0x07, 0x9e, 0x34,
0x31, 0x30, 0x39, 0x00, 0x00, 0x00]
elif options.prelude:
raise NotImplementedError("Unknown prelude: {}".format(options.prelude))
# If we are outputting a .PRG, we output the load address first.
# We don't use the Emitter for this b/c not part of addr space.
if output_format == 'prg':
fh.write(Word(start_addr).serialize(0))
emitter = Emitter(start_addr)
for byte in prelude:
emitter.emit(Byte(byte))
compiler = Compiler(emitter)
compiler.compile_program(program)
if options.debug:
pprint(emitter.accum)
else:
emitter.serialize(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(
"--analyze-only",
action="store_true",
help="Only parse and analyze the program; do not compile it."
)
argparser.add_argument(
"--origin", type=str, default='0xc000',
help="Location in memory where the `main` routine will be "
"located. Default: 0xc000."
)
argparser.add_argument(
"--output-format", type=str, default='prg',
help="Executable format to produce. Options are: prg (.PRG file "
"for Commodore 8-bit). Default: prg."
)
argparser.add_argument(
"--prelude", type=str,
help="Insert a snippet before the compiled program "
"so that it can be LOADed and RUN on a certain platforms. "
"Also sets the origin and format. "
"Options are: c64 or vic20."
)
argparser.add_argument(
"--debug",
action="store_true",
help="Display debugging information when analyzing and compiling."
)
argparser.add_argument(
"--parse-only",
action="store_true",
help="Only parse the program; do not analyze or compile it."
)
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
2018-02-06 16:14:44 +00:00
else:
traceback.print_exception(e.__class__, e, None)
sys.exit(1)