From 41bf01d035b8c3463e7e8022e9ef8f86b28a5aec Mon Sep 17 00:00:00 2001 From: Michael Martin Date: Wed, 9 Jan 2019 20:45:01 -0800 Subject: [PATCH] Convert Ophis to Python 3. Most of the work is handled by 2to3, but there's a few extra tricks needed to finish the job, mostly about picking the right bits to be Unicode and the right bits to be bytes. --- bin/ophis | 2 +- src/Ophis/CorePragmas.py | 11 +- src/Ophis/Errors.py | 8 +- src/Ophis/Frontend.py | 16 +-- src/Ophis/IR.py | 2 +- src/Ophis/Listing.py | 4 +- src/Ophis/Macro.py | 8 +- src/Ophis/Main.py | 10 +- src/Ophis/Passes.py | 50 +++---- src/tools/charmaps/makea2maps.py | 12 +- src/tools/opcodes/gensets.py | 32 ++--- tests/test_ophis.py | 237 ++++++++++++++++--------------- 12 files changed, 197 insertions(+), 195 deletions(-) diff --git a/bin/ophis b/bin/ophis index f293e4e..8920d4f 100755 --- a/bin/ophis +++ b/bin/ophis @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 from os.path import realpath, dirname, join from sys import argv, exit, path diff --git a/src/Ophis/CorePragmas.py b/src/Ophis/CorePragmas.py index b57dbb3..2fe3079 100644 --- a/src/Ophis/CorePragmas.py +++ b/src/Ophis/CorePragmas.py @@ -10,7 +10,8 @@ import Ophis.CmdLine import Ophis.IR as IR import Ophis.Frontend as FE import Ophis.Errors as Err -import math, os.path +import math +import os.path basecharmap = "".join([chr(x) for x in range(256)]) currentcharmap = basecharmap @@ -68,7 +69,7 @@ def pragmaIncbin(ppt, line, result): line.expect("EOL") if type(filename) == str: try: - f = file(os.path.join(FE.context_directory, filename), "rb") + f = open(os.path.join(FE.context_directory, filename), "rb") if offset.hardcoded and (size is None or size.hardcoded): # We know how big it will be, we can just use the values. # First check to make sure they're sane @@ -94,7 +95,7 @@ def pragmaIncbin(ppt, line, result): size = IR.ConstantExpr(-1) f.seek(offset.value()) bytes = f.read(size.value()) - bytes = [IR.ConstantExpr(ord(x)) for x in bytes] + bytes = [IR.ConstantExpr(x) for x in bytes] result.append(IR.Node(ppt, "Byte", *bytes)) else: # offset or length could change based on label placement. @@ -103,7 +104,7 @@ def pragmaIncbin(ppt, line, result): # alias. Don't use symbolic aliases when extracting tiny # pieces out of humongous files, I guess. bytes = f.read() - bytes = [IR.ConstantExpr(ord(x)) for x in bytes] + bytes = [IR.ConstantExpr(x) for x in bytes] if size is None: size = IR.SequenceExpr([IR.ConstantExpr(len(bytes)), "-", @@ -141,7 +142,7 @@ def pragmaCharmapbin(ppt, line, result): line.expect("EOL") if type(filename) == str: try: - f = file(os.path.join(FE.context_directory, filename), "rb") + f = open(os.path.join(FE.context_directory, filename), "rb") bytes = f.read() f.close() except IOError: diff --git a/src/Ophis/Errors.py b/src/Ophis/Errors.py index d7e5882..2704ec0 100644 --- a/src/Ophis/Errors.py +++ b/src/Ophis/Errors.py @@ -18,14 +18,14 @@ def log(err): the global error count.""" global count count = count + 1 - print>>sys.stderr, currentpoint + ": " + err + print(currentpoint + ": " + err, file=sys.stderr) def report(): "Print out the number of errors." if count == 0: - print>>sys.stderr, "No errors" + print("No errors", file=sys.stderr) elif count == 1: - print>>sys.stderr, "1 error" + print("1 error", file=sys.stderr) else: - print>>sys.stderr, str(count) + " errors" + print(str(count) + " errors", file=sys.stderr) diff --git a/src/Ophis/Frontend.py b/src/Ophis/Frontend.py index cbe6b2d..f2bd503 100644 --- a/src/Ophis/Frontend.py +++ b/src/Ophis/Frontend.py @@ -70,7 +70,7 @@ def lex(point, line): return elif firstchar in bases: try: - result.append(Lexeme("NUM", long(rest, bases[firstchar][1]))) + result.append(Lexeme("NUM", int(rest, bases[firstchar][1]))) return except ValueError: Err.log('Invalid ' + bases[firstchar][0] + ' constant: ' + @@ -79,7 +79,7 @@ def lex(point, line): return elif firstchar.isdigit(): try: - result.append(Lexeme("NUM", long(token))) + result.append(Lexeme("NUM", int(token))) except ValueError: Err.log('Identifiers may not begin with a number') result.append(Lexeme("LABEL", "ERROR")) @@ -403,19 +403,19 @@ def parse_file(ppt, filename, load_once=False): filename)) if load_once and filename in loadedfiles: if Cmd.print_loaded_files: - print>>sys.stderr, "Skipping " + filename + print("Skipping " + filename, file=sys.stderr) return IR.NullNode loadedfiles[filename] = True if Cmd.print_loaded_files: if filename != '-': - print>>sys.stderr, "Loading " + filename + print("Loading " + filename, file=sys.stderr) else: - print>>sys.stderr, "Loading from standard input" + print("Loading from standard input", file=sys.stderr) try: if filename != '-': if context_directory is not None: filename = os.path.join(context_directory, filename) - f = file(filename) + f = open(filename, "rt") linelist = f.readlines() f.close() context_directory = os.path.abspath(os.path.dirname(filename)) @@ -423,8 +423,8 @@ def parse_file(ppt, filename, load_once=False): context_directory = os.getcwd() linelist = sys.stdin.readlines() pptlist = ["%s:%d" % (filename, i + 1) for i in range(len(linelist))] - lexlist = map(lex, pptlist, linelist) - IRlist = map(parse_line, pptlist, lexlist) + lexlist = list(map(lex, pptlist, linelist)) + IRlist = list(map(parse_line, pptlist, lexlist)) IRlist = [node for node in IRlist if node is not IR.NullNode] context_directory = old_context return IR.SequenceNode(ppt, IRlist) diff --git a/src/Ophis/IR.py b/src/Ophis/IR.py index 71a7642..a71fd36 100644 --- a/src/Ophis/IR.py +++ b/src/Ophis/IR.py @@ -179,7 +179,7 @@ class SequenceExpr(Expr): return True def value(self, env=None): - subs = map((lambda x: x.value(env)), self.operands) + subs = list(map((lambda x: x.value(env)), self.operands)) result = subs[0] index = 1 for op in self.operators: diff --git a/src/Ophis/Listing.py b/src/Ophis/Listing.py index f0b4a95..d7b606b 100644 --- a/src/Ophis/Listing.py +++ b/src/Ophis/Listing.py @@ -43,7 +43,7 @@ class Listing(object): out = file(self.filename, "w") for x in self.listing: if type(x) is str: - print>>out, x + print(x, file=out) elif type(x) is tuple: i = 0 pc = x[0] @@ -59,7 +59,7 @@ class Listing(object): charline += "." else: charline += chr(c) - print>>out, "%-54s |%-16s|" % (dataline, charline) + print("%-54s |%-16s|" % (dataline, charline), file=out) i += 16 if self.filename != "-": out.close() diff --git a/src/Ophis/Macro.py b/src/Ophis/Macro.py index cdf1573..aeea97c 100644 --- a/src/Ophis/Macro.py +++ b/src/Ophis/Macro.py @@ -55,7 +55,7 @@ def expandMacro(ppt, name, arglist): Err.log("Undefined macro '%s'" % name) return IR.NullNode argexprs = [IR.Node(ppt, "Label", "_*%d" % i, arg) - for (i, arg) in zip(xrange(1, sys.maxint), arglist)] + for (i, arg) in zip(range(1, sys.maxsize), arglist)] bindexprs = [IR.Node(ppt, "Label", "_%d" % i, IR.LabelExpr("_*%d" % i)) for i in range(1, len(arglist) + 1)] body = [IR.Node("%s->%s" % (ppt, node.ppt), node.nodetype, *node.data) @@ -70,7 +70,7 @@ def dump(): global macros for mac in macros: body = macros[mac] - print>>sys.stderr, "Macro: " + mac + print("Macro: " + mac, file=sys.stderr) for node in body: - print>>sys.stderr, node - print>>sys.stderr, "" + print(node, file=sys.stderr) + print("", file=sys.stderr) diff --git a/src/Ophis/Main.py b/src/Ophis/Main.py index 32b3135..f1f8c2d 100644 --- a/src/Ophis/Main.py +++ b/src/Ophis/Main.py @@ -72,7 +72,7 @@ def run_all(): try: outfile = Ophis.CmdLine.outfile if outfile == '-': - output = sys.stdout + output = sys.stdout.buffer if sys.platform == "win32": # We can't dump our binary in text mode; that would be # disastrous. So, we'll do some platform-specific @@ -80,16 +80,16 @@ def run_all(): import msvcrt msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) elif outfile is None: - output = file('ophis.bin', 'wb') + output = open('ophis.bin', 'wb') else: - output = file(outfile, 'wb') - output.write("".join(map(chr, a.output))) + output = open(outfile, 'wb') + output.write(bytes(a.output)) output.flush() if outfile != '-': output.close() return 0 except IOError: - print>>sys.stderr, "Could not write to " + outfile + print("Could not write to " + outfile, file=sys.stderr) return 1 else: Err.report() diff --git a/src/Ophis/Passes.py b/src/Ophis/Passes.py index e8fd380..8ef5292 100644 --- a/src/Ophis/Passes.py +++ b/src/Ophis/Passes.py @@ -67,18 +67,18 @@ class Pass(object): printing debugging information.""" if Err.count == 0: if Cmd.print_pass: - print>>sys.stderr, "Running: " + self.name + print("Running: " + self.name, file=sys.stderr) env.reset() self.prePass() node.accept(self, env) self.postPass() env.reset() if Cmd.print_labels: - print>>sys.stderr, "Current labels:" - print>>sys.stderr, env + print("Current labels:", file=sys.stderr) + print(env, file=sys.stderr) if Cmd.print_ir: - print>>sys.stderr, "Current IR:" - print>>sys.stderr, node + print("Current IR:", file=sys.stderr) + print(node, file=sys.stderr) class FixPoint(object): @@ -92,7 +92,7 @@ class FixPoint(object): def go(self, node, env): """Runs this FixPoint's passes, in order, until the fixpoint is true. Always runs the passes at least once.""" - for i in xrange(100): + for i in range(100): if Err.count != 0: break for p in self.passes: @@ -102,7 +102,7 @@ class FixPoint(object): if self.fixpoint(): break if Cmd.print_pass: - print>>sys.stderr, "Fixpoint failed, looping back" + print("Fixpoint failed, looping back", file=sys.stderr) else: Err.log("Can't make %s converge! Maybe there's a recursive " "dependency somewhere?" % self.name) @@ -120,7 +120,7 @@ class DefineMacros(Pass): if self.inDef: Err.log("Unmatched .macro") elif Cmd.print_ir: - print>>sys.stderr, "Macro definitions:" + print("Macro definitions:", file=sys.stderr) Macro.dump() def visitMacroBegin(self, node, env): @@ -197,11 +197,11 @@ class InitLabels(Pass): env[label] = 0 self.changed = True if label in ['a', 'x', 'y'] and self.runcount == 1: - print>>sys.stderr, str(node.ppt) + ": WARNING: " \ - "using register name as label" + print(str(node.ppt) + ": WARNING: " \ + "using register name as label", file=sys.stderr) if label in Ops.opcodes and self.runcount == 1: - print>>sys.stderr, str(node.ppt) + ": WARNING: " \ - "using opcode name as label" + print(str(node.ppt) + ": WARNING: " \ + "using opcode name as label", file=sys.stderr) def visitUnknown(self, node, env): pass @@ -636,16 +636,16 @@ class ExtendBranches(PCTracker): if Cmd.enable_4502_exts: node.nodetype = "RelativeLong" if Cmd.warn_on_branch_extend: - print>>sys.stderr, str(node.ppt) + ": WARNING: " \ - "branch out of range, replacing with 16-bit relative branch" + print(str(node.ppt) + ": WARNING: " \ + "branch out of range, replacing with 16-bit relative branch", file=sys.stderr) else: if opcode == 'bra': # If BRA - BRanch Always - is out of range, it's a JMP. node.data = ('jmp', expr, None) node.nodetype = "Absolute" if Cmd.warn_on_branch_extend: - print>>sys.stderr, str(node.ppt) + ": WARNING: " \ - "bra out of range, replacing with jmp" + print(str(node.ppt) + ": WARNING: " \ + "bra out of range, replacing with jmp", file=sys.stderr) else: # Otherwise, we replace it with a 'macro' of sorts by hand: # $branch LOC -> $reversed_branch ^+5; JMP LOC @@ -661,11 +661,11 @@ class ExtendBranches(PCTracker): node.nodetype = 'SEQUENCE' node.data = expansion if Cmd.warn_on_branch_extend: - print>>sys.stderr, str(node.ppt) + ": WARNING: " + \ + print(str(node.ppt) + ": WARNING: " + \ opcode + " out of range, " \ "replacing with " + \ ExtendBranches.reversed[opcode] + \ - "/jmp combo" + "/jmp combo", file=sys.stderr) self.changed = True node.accept(self, env) else: @@ -690,11 +690,11 @@ class ExtendBranches(PCTracker): node.nodetype = 'SEQUENCE' node.data = expansion if Cmd.warn_on_branch_extend: - print>>sys.stderr, str(node.ppt) + ": WARNING: " + \ + print(str(node.ppt) + ": WARNING: " + \ opcode + " out of range, " \ "replacing with " + \ ExtendBranches.reversed[opcode] + \ - "/jmp combo" + "/jmp combo", file=sys.stderr) self.changed = True node.accept(self, env) else: @@ -763,10 +763,10 @@ class Assembler(Pass): self.listing.dump() self.mapper.dump() if Cmd.print_summary and Err.count == 0: - print>>sys.stderr, "Assembly complete: %s bytes output " \ + print("Assembly complete: %s bytes output " \ "(%s code, %s data, %s filler)" \ % (len(self.output), - self.code, self.data, self.filler) + self.code, self.data, self.filler), file=sys.stderr) def outputbyte(self, expr, env, tee=None): 'Outputs a byte, with range checking' @@ -799,7 +799,7 @@ class Assembler(Pass): 'Outputs a little-endian dword, with range checking' if self.writeOK: val = expr.value(env) - if val < 0x00000000 or val > 0xFFFFFFFFL: + if val < 0x00000000 or val > 0xFFFFFFFF: Err.log("DWord constant " + str(expr) + " out of range") val = 0 self.output.append(int(val & 0xFF)) @@ -829,7 +829,7 @@ class Assembler(Pass): 'Outputs a big-endian dword, with range checking' if self.writeOK: val = expr.value(env) - if val < 0x00000000 or val > 0xFFFFFFFFL: + if val < 0x00000000 or val > 0xFFFFFFFF: Err.log("DWord constant " + str(expr) + " out of range") val = 0 self.output.append(int((val >> 24) & 0xFF)) @@ -1083,7 +1083,7 @@ class Assembler(Pass): (pc, target)) else: created = [] - for i in xrange(target - pc): + for i in range(target - pc): self.outputbyte(node.data[1], env, created) self.filler += target - pc self.registerData(created, env.getPC()) diff --git a/src/tools/charmaps/makea2maps.py b/src/tools/charmaps/makea2maps.py index 91b6f01..0ae94c7 100644 --- a/src/tools/charmaps/makea2maps.py +++ b/src/tools/charmaps/makea2maps.py @@ -1,18 +1,18 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import struct -x = ''.join([chr(x) for x in range(256)]) +x = bytes(list(range(256))) bits = struct.unpack("32s32s32s32s32s32s32s32s", x) norm = [4, 5, 6, 7, 0, 1, 2, 3] ivrs = [4, 1, 0, 7, 6, 5, 2, 3] blnk = [4, 3, 2, 7, 0, 1, 6, 5] -normmap = ''.join([bits[x] for x in norm]) -ivrsmap = ''.join([bits[x] for x in ivrs]) -blnkmap = ''.join([bits[x] for x in blnk]) +normmap = b''.join([bits[x] for x in norm]) +ivrsmap = b''.join([bits[x] for x in ivrs]) +blnkmap = b''.join([bits[x] for x in blnk]) def dumpfile(n, m): - f = file(n, 'wb') + f = open(n, 'wb') f.write(m) f.close() diff --git a/src/tools/opcodes/gensets.py b/src/tools/opcodes/gensets.py index 5c1d72a..83b5e4d 100755 --- a/src/tools/opcodes/gensets.py +++ b/src/tools/opcodes/gensets.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import sys verbose = 0 @@ -80,7 +80,7 @@ def decomment(l): def decomment_readlines(fname): - result = [decomment(x) for x in file(fname).readlines()] + result = [decomment(x) for x in open(fname, "rt").readlines()] return [x for x in result if len(x) > 0] @@ -103,22 +103,22 @@ def parse_chipset_file(fname): result[op] = [] result[op].append((mnem, flatmodes.index(mode))) else: - print "Unknown mode '%s'" % s_p[1] + print("Unknown mode '%s'" % s_p[1]) except ValueError: - print "Illegal opcode '%s'" % l[0] + print("Illegal opcode '%s'" % l[0]) return result def collate_chipset_map(cs_list, base): result = {} - for (opcode, insts) in zip(range(256), cs_list): + for (opcode, insts) in zip(list(range(256)), cs_list): if insts is not None: for inst in insts: (mnem, mode) = inst if mnem not in result: result[mnem] = [None] * len(modes) if result[mnem][mode] is not None: - print "Warning: Reassigning %s - %s" % (mnem, modes[mode]) + print("Warning: Reassigning %s - %s" % (mnem, modes[mode])) result[mnem][mode] = opcode if base is not None: todel = [] @@ -127,9 +127,9 @@ def collate_chipset_map(cs_list, base): if result[x] == base[x]: todel.append(x) elif verbose != 0: - print "# Opcode %s changed" % x + print("# Opcode %s changed" % x) elif verbose != 0: - print "# Opcode %s added" % x + print("# Opcode %s added" % x) for x in todel: del result[x] return result @@ -143,14 +143,14 @@ def mapval(x): def dump_map(m, prologue=''): - mnems = m.keys() + mnems = list(m.keys()) mnems.sort() for mnem in mnems: codes = [mapval(x) for x in m[mnem]] - print "%s'%s': [%s,\n%s %s]," % (prologue, mnem, + print("%s'%s': [%s,\n%s %s]," % (prologue, mnem, ', '.join(codes[:8]), prologue + " " * len(mnem), - ', '.join(codes[8:])) + ', '.join(codes[8:]))) if __name__ == '__main__': @@ -165,18 +165,18 @@ if __name__ == '__main__': for y in [z.split(':', 1) for z in decomment_readlines(x)]] for l in ls: if len(l) != 2: - print "Could not parse the chipset line '%s'" % ":".join(l) + print("Could not parse the chipset line '%s'" % ":".join(l)) else: archs.append((l[0], l[1])) except IOError: - print "Could not read file %s" % x - print prologue + print("Could not read file %s" % x) + print(prologue) baseset = None for (field, fname) in archs: chipset_list = parse_chipset_file(fname) instruction_map = collate_chipset_map(chipset_list, baseset) if baseset is None: baseset = instruction_map - print "%s = {" % field + print("%s = {" % field) dump_map(instruction_map, ' ' * (len(field) + 4)) - print "%s}" % (' ' * (len(field) + 3)) + print("%s}" % (' ' * (len(field) + 3))) diff --git a/tests/test_ophis.py b/tests/test_ophis.py index 8f2182c..2b12b15 100755 --- a/tests/test_ophis.py +++ b/tests/test_ophis.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import sys import subprocess @@ -20,14 +20,14 @@ failed = 0 def assembled(raw): - return ' '.join(["%02X" % ord(c) for c in raw]) + return ' '.join(["%02X" % c for c in raw]) def assemble_raw(asm="", options=[]): p = subprocess.Popen([pythonpath, ophispath] + options, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - return p.communicate(asm) + return p.communicate(asm.encode("UTF-8")) def assemble_string(asm, options=[]): @@ -37,15 +37,15 @@ def assemble_string(asm, options=[]): def test_string(test_name, asm, expected, options=[]): (out, err) = assemble_string(asm, options) if out == expected: - print "%s: SUCCESS" % test_name + print("%s: SUCCESS" % test_name) else: global failed failed += 1 - print "%s: FAILED" % test_name - print "Assembled code: ", assembled(out) - print "Expected code: ", assembled(expected) + print("%s: FAILED" % test_name) + print("Assembled code: ", assembled(out)) + print("Expected code: ", assembled(expected)) if err != '': - print "Error output:\n%s" % err + print("Error output:\n%s" % err.decode(sys.stderr.encoding)) def test_file(test_name, fname, ename, options=[]): @@ -57,7 +57,7 @@ def test_file(test_name, fname, ename, options=[]): expected = f.read() f.close() else: # a test where we expect failure - expected = '' + expected = b'' test_string(test_name, asm, expected, options) @@ -66,16 +66,16 @@ def test_file(test_name, fname, ename, options=[]): # on, then we start running through the features. def test_basic(): - print - print "==== BASIC OPERATION ====" + print() + print("==== BASIC OPERATION ====") test_string('Basic Ophis operation', '.byte "Hello, world!"', - 'Hello, world!') + b'Hello, world!') test_string('Newline/EOF passthrough', '.byte 10,26,13,4,0,"Hi",13,10', - '\n\x1a\r\x04\x00Hi\r\n') + b'\n\x1a\r\x04\x00Hi\r\n') # Normally these would go in Expressions but we need them to run the # tests for relative instructions. - test_string('Program counter recognition', '.org $41\nlda #^\n', '\xa9A') - test_string('Program counter math', '.org $41\nlda #^+3\n', '\xa9D') + test_string('Program counter recognition', '.org $41\nlda #^\n', b'\xa9A') + test_string('Program counter math', '.org $41\nlda #^+3\n', b'\xa9D') if failed == 0: test_file('Basic instructions', 'testbase.oph', 'testbase.bin') test_file('Basic data pragmas', 'testdata.oph', 'testdata.bin') @@ -100,13 +100,13 @@ def test_basic(): def test_outfile(): global failed - print "\n==== INPUT AND OUTPUT ====" + print("\n==== INPUT AND OUTPUT ====") if os.path.exists("ophis.bin"): - print "TEST SUITE FAILED: unclean test environment (ophis.bin exists)" + print("TEST SUITE FAILED: unclean test environment (ophis.bin exists)") failed += 1 return elif os.path.exists("custom.bin"): - print "TEST SUITE FAILED: unclean test environment (custom.bin exists)" + print("TEST SUITE FAILED: unclean test environment (custom.bin exists)") failed += 1 return @@ -114,45 +114,46 @@ def test_outfile(): try: assemble_raw('.byte "Hello, world!", 10', ['-']) f = open('ophis.bin', 'rb') - if f.read() != 'Hello, world!\n': - print "Default output filename: FAILED (bad output)" + if f.read() != b'Hello, world!\n': + print("Default output filename: FAILED (bad output)") failed += 1 else: - print "Default output filename: SUCCESS" + print("Default output filename: SUCCESS") f.close() os.unlink('ophis.bin') except: - print "Default output filename: FAILED (exception)" + print("Default output filename: FAILED (exception)") failed += 1 + raise # Test 2: Command line override try: assemble_raw('.byte "Hello, world!", 10', ['-', '-o', 'custom.bin']) f = open('custom.bin', 'rb') - if f.read() != 'Hello, world!\n': - print "Commandline output filename: FAILED (bad output)" + if f.read() != b'Hello, world!\n': + print("Commandline output filename: FAILED (bad output)") failed += 1 else: - print "Commandline output filename: SUCCESS" + print("Commandline output filename: SUCCESS") f.close() os.unlink('custom.bin') except: - print "Commandline output filename: FAILED (exception)" + print("Commandline output filename: FAILED (exception)") failed += 1 # Test 3: Pragma override try: assemble_raw('.outfile "custom.bin"\n.byte "Hello, world!", 10', ['-']) f = open('custom.bin', 'rb') - if f.read() != 'Hello, world!\n': - print "Commandline output filename: FAILED (bad output)" + if f.read() != b'Hello, world!\n': + print("Commandline output filename: FAILED (bad output)") failed += 1 else: - print "Commandline output filename: SUCCESS" + print("Commandline output filename: SUCCESS") f.close() os.unlink('custom.bin') except: - print "Commandline output filename: FAILED (exception)" + print("Commandline output filename: FAILED (exception)") failed += 1 # Test 4: Command line override of .outfile @@ -160,15 +161,15 @@ def test_outfile(): assemble_raw('.outfile "custom2.bin"\n' '.byte "Hello, world!", 10', ['-', '-o', 'custom.bin']) f = open('custom.bin', 'rb') - if f.read() != 'Hello, world!\n': - print "Commandline override of pragma: FAILED (bad output)" + if f.read() != b'Hello, world!\n': + print("Commandline override of pragma: FAILED (bad output)") failed += 1 else: - print "Commandline override of pragma: SUCCESS" + print("Commandline override of pragma: SUCCESS") f.close() os.unlink('custom.bin') except: - print "Commandline override of pragma: FAILED (exception)" + print("Commandline override of pragma: FAILED (exception)") failed += 1 # Test 5: Pragma repetition priority @@ -177,15 +178,15 @@ def test_outfile(): '.outfile "custom2.bin"\n' '.byte "Hello, world!", 10', ['-']) f = open('custom.bin', 'rb') - if f.read() != 'Hello, world!\n': - print "Pragma repetition: FAILED (bad output)" + if f.read() != b'Hello, world!\n': + print("Pragma repetition: FAILED (bad output)") failed += 1 else: - print "Pragma repetition: SUCCESS" + print("Pragma repetition: SUCCESS") f.close() os.unlink('custom.bin') except: - print "Pragma repetition: FAILED (exception)" + print("Pragma repetition: FAILED (exception)") failed += 1 # Test 6: multiple input files @@ -200,73 +201,73 @@ def test_outfile(): s += f.read() f.close() if out != s: - print "Multiple input files: FAILED (bad output)" + print("Multiple input files: FAILED (bad output)") failed += 1 else: - print "Multiple input files: SUCCESS" + print("Multiple input files: SUCCESS") except: - print "Multiple input files: FAILED (exception)" + print("Multiple input files: FAILED (exception)") failed += 1 def test_transforms(): - print "\n==== BINARY TRANSFORM PASSES ====" - print "Simple zero page selection: SUCCESS (covered in basic tests)" + print("\n==== BINARY TRANSFORM PASSES ====") + print("Simple zero page selection: SUCCESS (covered in basic tests)") test_string('Chained collapse', '.org $fa \n lda + \n lda ^ \n * rts \n', - '\xa5\xfe\xa5\xfc\x60') + b'\xa5\xfe\xa5\xfc\x60') test_string('Reversible collapse', '.org $fb \n bne ^+200 \n lda ^ \n', - '\xf0\x03\x4c\xc5\x01\xad\x00\x01') + b'\xf0\x03\x4c\xc5\x01\xad\x00\x01') def test_expressions(): - print "\n==== EXPRESSIONS AND LABELS ====" - test_string('Basic addition', '.byte 3+2', '\x05') - test_string('Basic subtraction', '.byte 3-2', '\x01') - test_string('Basic multiplication', '.byte 3*2', '\x06') - test_string('Basic division', '.byte 6/2', '\x03') - test_string('Basic bit-union', '.byte 5|9', '\x0d') - test_string('Basic bit-intersection', '.byte 5&9', '\x01') - test_string('Basic bit-toggle', '.byte 5^9', '\x0c') - test_string('Division truncation', '.byte 5/2', '\x02') - test_string('Overflow', '.byte $FF*$10', '') - test_string('Multibyte overflow', '.word $FF*$10', '\xf0\x0f') - test_string('Masked overflow', '.byte $FF*$10&$FF', '\xf0') - test_string('Underflow', '.byte 2-3', '') - test_string('Masked underflow', '.byte 2-3&$FF', '\xff') - test_string('Arithmetic precedence', '.byte 2+3*4-6/2', '\x0b') - test_string('Parentheses', '.byte [2+3]*[4-6/2]', '\x05') + print("\n==== EXPRESSIONS AND LABELS ====") + test_string('Basic addition', '.byte 3+2', b'\x05') + test_string('Basic subtraction', '.byte 3-2', b'\x01') + test_string('Basic multiplication', '.byte 3*2', b'\x06') + test_string('Basic division', '.byte 6/2', b'\x03') + test_string('Basic bit-union', '.byte 5|9', b'\x0d') + test_string('Basic bit-intersection', '.byte 5&9', b'\x01') + test_string('Basic bit-toggle', '.byte 5^9', b'\x0c') + test_string('Division truncation', '.byte 5/2', b'\x02') + test_string('Overflow', '.byte $FF*$10', b'') + test_string('Multibyte overflow', '.word $FF*$10', b'\xf0\x0f') + test_string('Masked overflow', '.byte $FF*$10&$FF', b'\xf0') + test_string('Underflow', '.byte 2-3', b'') + test_string('Masked underflow', '.byte 2-3&$FF', b'\xff') + test_string('Arithmetic precedence', '.byte 2+3*4-6/2', b'\x0b') + test_string('Parentheses', '.byte [2+3]*[4-6/2]', b'\x05') test_string('String escapes', '.byte "The man said, \\"The \\\\ is Windowsy.\\""', - 'The man said, "The \\ is Windowsy."') + b'The man said, "The \\ is Windowsy."') test_string('Byte selector precedence', '.byte >$d000+32,>[$d000+32],<[$D000-275]', - '\xf0\xd0\xed') - test_string('Named labels', '.org $6948\nl: .word l', 'Hi') - test_string('.alias directive (basic)', '.alias hi $6948\n.word hi', 'Hi') + b'\xf0\xd0\xed') + test_string('Named labels', '.org $6948\nl: .word l', b'Hi') + test_string('.alias directive (basic)', '.alias hi $6948\n.word hi', b'Hi') test_string('.alias directive (derived)', - '.alias hi $6948\n.alias ho hi+$600\n.word hi,ho', 'HiHo') + '.alias hi $6948\n.alias ho hi+$600\n.word hi,ho', b'HiHo') test_string('.alias directive (circular)', '.alias a c+1\n.alias b a+3\n.alias c b-4\n.word a, b, c', - '') + b'') test_string('.advance directive (basic)', 'lda #$05\n.advance $05\n.byte ^', - '\xa9\x05\x00\x00\x00\x05') + b'\xa9\x05\x00\x00\x00\x05') test_string('.advance directive (filler)', 'lda #$05\nf: .advance $05,f+3\n.byte ^', - '\xa9\x05\x05\x05\x05\x05') + b'\xa9\x05\x05\x05\x05\x05') test_string('.advance no-op', 'lda #$05\n.advance $02\n.byte ^', - '\xa9\x05\x02') - test_string('.advance failure', 'lda #$05\n.advance $01\n.byte ^', '') - test_string('.checkpc, space > 0', 'lda #$05\n.checkpc $10', '\xa9\x05') - test_string('.checkpc, space = 0', 'lda #$05\n.checkpc 2', '\xa9\x05') - test_string('.checkpc, space < 0', 'lda $05\n.checkpc 1', '') + b'\xa9\x05\x02') + test_string('.advance failure', 'lda #$05\n.advance $01\n.byte ^', b'') + test_string('.checkpc, space > 0', 'lda #$05\n.checkpc $10', b'\xa9\x05') + test_string('.checkpc, space = 0', 'lda #$05\n.checkpc 2', b'\xa9\x05') + test_string('.checkpc, space < 0', 'lda $05\n.checkpc 1', b'') test_string('A X Y usable as labels', '.alias a 1\n.alias x 2\n.alias y 3\n' 'lda (a+x+y),y\nlda (x+y,x)', - '\xb1\x06\xa1\x05') + b'\xb1\x06\xa1\x05') test_string('Opcodes usable as labels', 'ldy #$00\n dey: dey\n bne dey', - '\xa0\x00\x88\xd0\xfd') + b'\xa0\x00\x88\xd0\xfd') def test_segments(): @@ -277,18 +278,18 @@ def test_segments(): '.org $61\n' 'd:\n' '.text\n' - 'l: .byte l, d', 'Aa') - test_string('Data cleanliness', '.byte 65\n.data\n.byte 65', '') + 'l: .byte l, d', b'Aa') + test_string('Data cleanliness', '.byte 65\n.data\n.byte 65', b'') test_string('.space directive', '.data\n.org $41\n.space a 2\n.space b 1\n.space c 1\n' - '.text\n.byte a, b, c\n', 'ACD') + '.text\n.byte a, b, c\n', b'ACD') test_string('Multiple named segments', '.data\n.org $41\n.data a\n.org $61\n.data b\n.org $4a\n' '.data\n.space q 1\n.data a\n.space r 1\n.data b\n.space s 1\n' '.text\n.org $10\n.text a\n.org $20\n' '.text\n.byte ^,q,r,s\n' '.text a\n.byte ^,q,r,s\n', - '\x10AaJ\x20AaJ') + b'\x10AaJ\x20AaJ') def test_scopes(): @@ -300,13 +301,13 @@ def test_scopes(): '.scend\n' '.scope\n' '_l: .byte _l\n' - '.scend\n', 'AB') + '.scend\n', b'AB') test_string('Data hiding outside of scope', '.org $41\n' '.scope\n' '_l: .byte _l\n' '.scend\n' - ' .byte _l\n', '') + ' .byte _l\n', b'') test_string('Repeated labels, nested scopes', '.org $41\n' '.scope\n' @@ -315,17 +316,17 @@ def test_scopes(): '_l: .byte _l\n' '.scend\n' ' .byte _l\n' - '.scend\n', 'ABA') + '.scend\n', b'ABA') test_string('Anonymous labels (basic)', '.org $41\n' '* .byte -, +\n' - '* .byte -, --\n', 'ACCA') + '* .byte -, --\n', b'ACCA') test_string('Anonymous labels (across scopes)', '.org $41\n' '* .byte -, +\n' '.scope\n' '* .byte -, --\n' - '.scend\n', 'ACCA') + '.scend\n', b'ACCA') def test_macros(): @@ -334,12 +335,12 @@ def test_macros(): '.macro greet\n' ' .byte "hi"\n' '.macend\n' - '`greet\n.invoke greet', "hihi") + '`greet\n.invoke greet', b"hihi") test_string('Macros with arguments', '.macro greet\n' ' .byte "hi",_1\n' '.macend\n' - "`greet 'A\n.invoke greet 'B", "hiAhiB") + "`greet 'A\n.invoke greet 'B", b"hiAhiB") test_string('Macros invoking macros', '.macro inner\n' ' .byte " there"\n' @@ -348,7 +349,7 @@ def test_macros(): ' .byte "hi"\n' ' `inner\n' '.macend\n' - "`greet", "hi there") + "`greet", b"hi there") test_string('Macros defining macros (illegal)', '.macro greet\n' '.macro inner\n' @@ -357,7 +358,7 @@ def test_macros(): ' .byte "hi"\n' ' `inner\n' '.macend\n' - "`greet", "") + "`greet", b"") test_string('Fail on infinite recursion', '.macro inner\n' ' .byte " there"\n' @@ -367,75 +368,75 @@ def test_macros(): ' .byte "hi"\n' ' `inner\n' '.macend\n' - "`greet", "") + "`greet", b"") def test_subfiles(): print("\n==== COMPILATION UNITS ====") - test_string(".include pragma", '.include "baseinc.oph"', 'BASIC\n') + test_string(".include pragma", '.include "baseinc.oph"', b'BASIC\n') test_string(".include repeated", '.include "baseinc.oph"\n.include "baseinc.oph"', - 'BASIC\nBASIC\n') - test_string(".require pragma", '.require "baseinc.oph"', 'BASIC\n') + b'BASIC\nBASIC\n') + test_string(".require pragma", '.require "baseinc.oph"', b'BASIC\n') test_string(".include before .require", '.include "baseinc.oph"\n.require "baseinc.oph"', - 'BASIC\n') + b'BASIC\n') test_string(".require before .include", '.require "baseinc.oph"\n.include "baseinc.oph"', - 'BASIC\nBASIC\n') + b'BASIC\nBASIC\n') test_string(".require same file twice with different paths", '.include "baseinc.oph"\n.include "sub/baseinc.oph"', - 'BASIC\nSUB 1 START\nSUB 1 END\n') + b'BASIC\nSUB 1 START\nSUB 1 END\n') test_string(".require different files with identical paths", '.include "sub/sub/sub.oph"', - 'SUB 2 START\nSUB 1 START\nBASIC\nSUB 1 END\nSUB 2 END\n') + b'SUB 2 START\nSUB 1 START\nBASIC\nSUB 1 END\nSUB 2 END\n') test_string(".charmap (basic)", '.charmap \'A, "abcdefghijklmnopqrstuvwxyz"\n' '.charmap \'a, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"\n' - '.byte "hELLO, wORLD!"', "Hello, World!") + '.byte "hELLO, wORLD!"', b"Hello, World!") test_string(".charmap (reset)", '.charmap \'A, "abcdefghijklmnopqrstuvwxyz"\n' '.charmap \'a, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"\n' '.byte "hELLO, wORLD!",10\n' '.charmap\n' '.byte "hELLO, wORLD!",10\n', - "Hello, World!\nhELLO, wORLD!\n") + b"Hello, World!\nhELLO, wORLD!\n") test_string(".charmap (out of range)", '.charmap 250, "ABCDEFGHIJKLM"\n.byte 250,251', - '') + b'') test_string(".charmapbin (basic)", '.charmapbin "../examples/petscii.map"\n.byte "hELLO, wORLD!"', - "Hello, World!") + b"Hello, World!") test_string(".charmapbin (illegal)", - '.charmapbin "baseinc.bin"\n.byte "hELLO, wORLD!"', '') - test_string(".incbin (basic)", '.incbin "baseinc.bin"', "BASIC\n") + '.charmapbin "baseinc.bin"\n.byte "hELLO, wORLD!"', b'') + test_string(".incbin (basic)", '.incbin "baseinc.bin"', b"BASIC\n") test_string(".incbin (hardcoded offset)", - '.incbin "baseinc.bin",3', "IC\n") + '.incbin "baseinc.bin",3', b"IC\n") test_string(".incbin (hardcoded offset and length)", - '.incbin "baseinc.bin",3,2', "IC") + '.incbin "baseinc.bin",3,2', b"IC") test_string(".incbin (softcoded offset and length)", '.alias off len+1\n.alias len 2\n' - '.incbin "baseinc.bin",off,len', "IC") + '.incbin "baseinc.bin",off,len', b"IC") test_string(".incbin (length too long)", - '.byte 65\n.incbin "baseinc.bin",3,4', "") + '.byte 65\n.incbin "baseinc.bin",3,4', b"") test_string(".incbin (negative offset)", - '.byte 65\n.incbin "baseinc.bin",1-5,4', "") + '.byte 65\n.incbin "baseinc.bin",1-5,4', b"") test_string(".incbin (offset = size)", - '.byte 65\n.incbin "baseinc.bin",6', "A") + '.byte 65\n.incbin "baseinc.bin",6', b"A") test_string(".incbin (offset > size)", - '.byte 65\n.incbin "baseinc.bin",7', "") + '.byte 65\n.incbin "baseinc.bin",7', b"") test_string(".incbin (softcoded length too long)", '.alias off len\n.alias len 4\n' - '.byte 65\n.incbin "baseinc.bin",off,len', "") + '.byte 65\n.incbin "baseinc.bin",off,len', b"") test_string(".incbin (softcoded negative offset)", '.alias off 1-5\n' - '.byte 65\n.incbin "baseinc.bin",off,4', "") + '.byte 65\n.incbin "baseinc.bin",off,4', b"") test_string(".incbin (softcoded offset = size)", '.alias off 6\n' - '.byte 65\n.incbin "baseinc.bin",off', "A") + '.byte 65\n.incbin "baseinc.bin",off', b"A") test_string(".incbin (softcoded offset > size)", '.alias off 7\n' - '.byte 65\n.incbin "baseinc.bin",off', "") + '.byte 65\n.incbin "baseinc.bin",off', b"") def test_systematic(): @@ -449,18 +450,18 @@ def test_systematic(): if __name__ == '__main__': - print "Using Python interpreter:", pythonpath + print("Using Python interpreter:", pythonpath) test_basic() os.chdir(os.path.dirname(os.path.realpath(sys.argv[0]))) if failed == 0: test_systematic() else: - print "\nBasic test cases failed, aborting test." + print("\nBasic test cases failed, aborting test.") if failed > 0: - print "\nTotal test case failures: %d" % failed + print("\nTotal test case failures: %d" % failed) sys.exit(1) else: - print "\nAll test cases succeeded" + print("\nAll test cases succeeded") sys.exit(0)