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.
This commit is contained in:
Michael Martin 2019-01-09 20:45:01 -08:00
parent 971fafd918
commit 41bf01d035
12 changed files with 197 additions and 195 deletions

View File

@ -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

View File

@ -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:

View File

@ -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)

View File

@ -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)

View File

@ -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:

View File

@ -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()

View File

@ -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)

View File

@ -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()

View File

@ -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())

View File

@ -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()

View File

@ -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)))

View File

@ -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)