diff --git a/README.md b/README.md index 8456707..8fe4e74 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ SixtyPical ========== -_Version 0.14. Work-in-progress, everything is subject to change._ +_Version 0.15. Work-in-progress, everything is subject to change._ **SixtyPical** is a 6502-like programming language with advanced static analysis. diff --git a/bin/sixtypical b/bin/sixtypical index 710926a..0f621b6 100755 --- a/bin/sixtypical +++ b/bin/sixtypical @@ -24,6 +24,66 @@ from sixtypical.emitter import Emitter, Byte, Word from sixtypical.compiler import Compiler +def process_input_files(filenames, options): + programs = [] + + for filename in options.filenames: + text = open(filename).read() + parser = Parser(text, filename) + program = parser.program() + programs.append(program) + + if options.parse_only: + return + + #program = merge_programs(programs) + program = programs[0] + + 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()) @@ -72,69 +132,11 @@ if __name__ == '__main__': 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) + try: + process_input_files(options.filenames, options) + except Exception as e: + if options.traceback: + raise 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) + traceback.print_exception(e.__class__, e, None) + sys.exit(1) diff --git a/src/sixtypical/parser.py b/src/sixtypical/parser.py index 22ab9ec..c2bb76e 100644 --- a/src/sixtypical/parser.py +++ b/src/sixtypical/parser.py @@ -6,7 +6,7 @@ from sixtypical.model import ( RoutineType, VectorType, TableType, BufferType, PointerType, LocationRef, ConstantRef, IndirectRef, IndexedRef, AddressRef, ) -from sixtypical.scanner import Scanner, SixtyPicalSyntaxError +from sixtypical.scanner import Scanner class SymEntry(object): @@ -19,8 +19,8 @@ class SymEntry(object): class Parser(object): - def __init__(self, text): - self.scanner = Scanner(text) + def __init__(self, text, filename): + self.scanner = Scanner(text, filename) self.symbols = {} # token -> SymEntry self.current_statics = {} # token -> SymEntry self.typedefs = {} # token -> Type AST @@ -32,7 +32,7 @@ class Parser(object): self.backpatch_instrs = [] def syntax_error(self, msg): - raise SixtyPicalSyntaxError(self.scanner.line_number, msg) + self.scanner.syntax_error(msg) def soft_lookup(self, name): if name in self.current_statics: diff --git a/src/sixtypical/scanner.py b/src/sixtypical/scanner.py index d15ac4f..a85da67 100644 --- a/src/sixtypical/scanner.py +++ b/src/sixtypical/scanner.py @@ -4,16 +4,17 @@ import re class SixtyPicalSyntaxError(ValueError): - def __init__(self, line_number, message): - super(SixtyPicalSyntaxError, self).__init__(line_number, message) + def __init__(self, filename, line_number, message): + super(SixtyPicalSyntaxError, self).__init__(filename, line_number, message) def __str__(self): - return "Line {}: {}".format(self.args[0], self.args[1]) + return "{}, line {}: {}".format(self.args[0], self.args[1], self.args[2]) class Scanner(object): - def __init__(self, text): + def __init__(self, text, filename): self.text = text + self.filename = filename self.token = None self.type = None self.line_number = 1 @@ -62,9 +63,7 @@ class Scanner(object): if self.token == token: self.scan() else: - raise SixtyPicalSyntaxError(self.line_number, "Expected '{}', but found '{}'".format( - token, self.token - )) + self.syntax_error("Expected '{}', but found '{}'".format(token, self.token)) def on(self, *tokens): return self.token in tokens @@ -74,9 +73,7 @@ class Scanner(object): def check_type(self, type): if not self.type == type: - raise SixtyPicalSyntaxError(self.line_number, "Expected {}, but found '{}'".format( - self.type, self.token - )) + self.syntax_error("Expected {}, but found '{}'".format(self.type, self.token)) def consume(self, token): if self.token == token: @@ -84,3 +81,6 @@ class Scanner(object): return True else: return False + + def syntax_error(self, msg): + raise SixtyPicalSyntaxError(self.filename, self.line_number, msg)