From 7f650e787d997a3230138b5a43f72d78654f637b Mon Sep 17 00:00:00 2001 From: Michael Martin Date: Tue, 12 Jun 2012 06:27:11 -0700 Subject: [PATCH] Fix the bugs the test suite found - .require now tracks absolute paths of loaded files - stricter checking of .incbin arguments - fix charmap reset directive - Allow register names (a, x, y) as labels, with warning - Allow opcode names as labels, with warning --- src/Ophis/CorePragmas.py | 35 ++++++++++++++++++++++++----------- src/Ophis/Frontend.py | 31 ++++++++++++++++++++++++++----- src/Ophis/Main.py | 3 ++- src/Ophis/Passes.py | 12 ++++++++++++ 4 files changed, 64 insertions(+), 17 deletions(-) diff --git a/src/Ophis/CorePragmas.py b/src/Ophis/CorePragmas.py index b234b87..3f1506d 100644 --- a/src/Ophis/CorePragmas.py +++ b/src/Ophis/CorePragmas.py @@ -12,14 +12,13 @@ import Ophis.Frontend as FE import Ophis.Errors as Err import os.path -loadedfiles = {} basecharmap = "".join([chr(x) for x in range(256)]) currentcharmap = basecharmap def reset(): - global loadedfiles, currentcharmap, basecharmap - loadedfiles = {} + global currentcharmap, basecharmap + FE.loadedfiles = {} currentcharmap = basecharmap @@ -36,8 +35,6 @@ def pragmaInclude(ppt, line, result): filename = line.expect("STRING").value line.expect("EOL") if type(filename) == str: - global loadedfiles - loadedfiles[filename] = True result.append(FE.parse_file(ppt, filename)) @@ -46,10 +43,7 @@ def pragmaRequire(ppt, line, result): filename = line.expect("STRING").value line.expect("EOL") if type(filename) == str: - global loadedfiles - if filename not in loadedfiles: - loadedfiles[filename] = True - result.append(FE.parse_file(ppt, filename)) + result.append(FE.parse_file(ppt, filename, True)) def pragmaIncbin(ppt, line, result): @@ -69,6 +63,25 @@ def pragmaIncbin(ppt, line, result): f = file(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 + if offset.value() < 0: + Err.log("Offset may not be negative") + f.close() + return + f.seek(0, 2) # Seek to end of file + if offset.value() > f.tell(): + Err.log("Offset runs past end of file") + f.close() + return + if size is not None: + if size.value() < 0: + Err.log("Length may not be negative") + f.close() + return + if offset.value() + size.value() > f.tell(): + Err.log(".incbin length too long") + f.close() + return if size is None: size = IR.ConstantExpr(-1) f.seek(offset.value()) @@ -97,10 +110,10 @@ def pragmaIncbin(ppt, line, result): def pragmaCharmap(ppt, line, result): "Modify the character map." global currentcharmap, basecharmap - bytes = readData(line) - if len(bytes) == 0: + if str(line.lookahead(0)) == "EOL": currentcharmap = basecharmap else: + bytes = readData(line) try: base = bytes[0].data newsubstr = "".join([chr(x.data) for x in bytes[1:]]) diff --git a/src/Ophis/Frontend.py b/src/Ophis/Frontend.py index cb6c34f..0960842 100644 --- a/src/Ophis/Frontend.py +++ b/src/Ophis/Frontend.py @@ -15,6 +15,9 @@ import os.path # license: See README for details. +loadedfiles = {} + + class Lexeme(object): "Class for lexer tokens. Used by lexer and parser." def __init__(self, type="UNKNOWN", value=None): @@ -180,8 +183,17 @@ class ParseLine(object): """Reads a token from the ParseLine line and returns it if it's of a type in the sequence tokens. Otherwise, it logs an error.""" token = self.pop() - if token.type not in tokens: - Err.log('Expected: "' + '", "'.join(tokens) + '"') + if token.type in tokens: + return token + if 'LABEL' in tokens: + if token.type in ['X', 'Y']: + token.value = token.type.lower() + token.type = 'LABEL' + return token + elif token.type == 'OPCODE': + token.type = 'LABEL' + return token + Err.log('Expected: "' + '", "'.join(tokens) + '"') return token @@ -196,7 +208,7 @@ def parse_expr(line): next = line.lookahead(0).type if next == "NUM": return IR.ConstantExpr(line.expect("NUM").value) - elif next == "LABEL": + elif next in ["LABEL", "X", "Y", "OPCODE"]: return IR.LabelExpr(line.expect("LABEL").value) elif next == "^": line.expect("^") @@ -352,11 +364,20 @@ def parse_line(ppt, lexemelist): context_directory = None -def parse_file(ppt, filename): +def parse_file(ppt, filename, load_once=False): "Loads an Ophis source file, and returns an IR list." - global context_directory + global context_directory, loadedfiles Err.currentpoint = ppt old_context = context_directory + if filename != '-': + if context_directory is not None: + filename = os.path.abspath(os.path.join(context_directory, + filename)) + if load_once and filename in loadedfiles: + if Cmd.print_loaded_files: + print>>sys.stderr, "Skipping " + filename + return IR.NullNode + loadedfiles[filename] = True if Cmd.print_loaded_files: if filename != '-': print>>sys.stderr, "Loading " + filename diff --git a/src/Ophis/Main.py b/src/Ophis/Main.py index e5b3f78..5f93bf4 100644 --- a/src/Ophis/Main.py +++ b/src/Ophis/Main.py @@ -8,6 +8,7 @@ # license: See README for details. import sys +import os import Ophis.Frontend import Ophis.IR import Ophis.CorePragmas @@ -71,7 +72,7 @@ def run_all(): # We can't dump our binary in test mode; that would be # disastrous. So, we'll do some platform-specific # things here to force our stdout to binary mode. - import os, msvcrt + import msvcrt msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) elif outfile is None: output = file('ophis.bin', 'wb') diff --git a/src/Ophis/Passes.py b/src/Ophis/Passes.py index 7bf706e..c4d295a 100644 --- a/src/Ophis/Passes.py +++ b/src/Ophis/Passes.py @@ -174,10 +174,12 @@ class InitLabels(Pass): def __init__(self): Pass.__init__(self) self.labelmap = {} + self.runcount = 0 def prePass(self): self.changed = False self.PCvalid = True + self.runcount += 1 def visitAdvance(self, node, env): self.PCvalid = node.data[0].valid(env, self.PCvalid) @@ -195,6 +197,12 @@ class InitLabels(Pass): if val.valid(env, self.PCvalid) and label not in env: 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" + if label in Ops.opcodes and self.runcount == 1: + print>>sys.stderr, str(node.ppt) + ": WARNING: " \ + "using opcode name as label" def visitUnknown(self, node, env): pass @@ -787,6 +795,10 @@ class Assembler(Pass): length = node.data[1].value(env) if offset < 2: Err.log("Negative offset in .incbin") + elif offset > len(node.data): + Err.log("Offset extends past end of file") + elif length < 0: + Err.log("Negative length") elif offset + length > len(node.data): Err.log("File too small for .incbin subrange") else: