#!/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 from sixtypical.analyzer import Analyzer from sixtypical.emitter import Emitter, Byte, Word from sixtypical.compiler import Compiler 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) for filename in options.filenames: text = open(filename).read() try: parser = Parser(text) program = parser.program() except Exception as e: if options.traceback: raise else: traceback.print_exception(e.__class__, e, None) sys.exit(1) if options.parse_only: sys.exit(0) try: analyzer = Analyzer(debug=options.debug) analyzer.analyze_program(program) except Exception as e: if options.traceback: raise else: traceback.print_exception(e.__class__, e, None) sys.exit(1) if options.analyze_only: sys.exit(0) 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)