mirror of
https://github.com/michaelcmartin/Ophis.git
synced 2025-01-02 14:29:35 +00:00
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
This commit is contained in:
parent
10c3b46996
commit
7f650e787d
@ -12,14 +12,13 @@ import Ophis.Frontend as FE
|
|||||||
import Ophis.Errors as Err
|
import Ophis.Errors as Err
|
||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
loadedfiles = {}
|
|
||||||
basecharmap = "".join([chr(x) for x in range(256)])
|
basecharmap = "".join([chr(x) for x in range(256)])
|
||||||
currentcharmap = basecharmap
|
currentcharmap = basecharmap
|
||||||
|
|
||||||
|
|
||||||
def reset():
|
def reset():
|
||||||
global loadedfiles, currentcharmap, basecharmap
|
global currentcharmap, basecharmap
|
||||||
loadedfiles = {}
|
FE.loadedfiles = {}
|
||||||
currentcharmap = basecharmap
|
currentcharmap = basecharmap
|
||||||
|
|
||||||
|
|
||||||
@ -36,8 +35,6 @@ def pragmaInclude(ppt, line, result):
|
|||||||
filename = line.expect("STRING").value
|
filename = line.expect("STRING").value
|
||||||
line.expect("EOL")
|
line.expect("EOL")
|
||||||
if type(filename) == str:
|
if type(filename) == str:
|
||||||
global loadedfiles
|
|
||||||
loadedfiles[filename] = True
|
|
||||||
result.append(FE.parse_file(ppt, filename))
|
result.append(FE.parse_file(ppt, filename))
|
||||||
|
|
||||||
|
|
||||||
@ -46,10 +43,7 @@ def pragmaRequire(ppt, line, result):
|
|||||||
filename = line.expect("STRING").value
|
filename = line.expect("STRING").value
|
||||||
line.expect("EOL")
|
line.expect("EOL")
|
||||||
if type(filename) == str:
|
if type(filename) == str:
|
||||||
global loadedfiles
|
result.append(FE.parse_file(ppt, filename, True))
|
||||||
if filename not in loadedfiles:
|
|
||||||
loadedfiles[filename] = True
|
|
||||||
result.append(FE.parse_file(ppt, filename))
|
|
||||||
|
|
||||||
|
|
||||||
def pragmaIncbin(ppt, line, result):
|
def pragmaIncbin(ppt, line, result):
|
||||||
@ -69,6 +63,25 @@ def pragmaIncbin(ppt, line, result):
|
|||||||
f = file(os.path.join(FE.context_directory, filename), "rb")
|
f = file(os.path.join(FE.context_directory, filename), "rb")
|
||||||
if offset.hardcoded and (size is None or size.hardcoded):
|
if offset.hardcoded and (size is None or size.hardcoded):
|
||||||
# We know how big it will be, we can just use the values.
|
# 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:
|
if size is None:
|
||||||
size = IR.ConstantExpr(-1)
|
size = IR.ConstantExpr(-1)
|
||||||
f.seek(offset.value())
|
f.seek(offset.value())
|
||||||
@ -97,10 +110,10 @@ def pragmaIncbin(ppt, line, result):
|
|||||||
def pragmaCharmap(ppt, line, result):
|
def pragmaCharmap(ppt, line, result):
|
||||||
"Modify the character map."
|
"Modify the character map."
|
||||||
global currentcharmap, basecharmap
|
global currentcharmap, basecharmap
|
||||||
bytes = readData(line)
|
if str(line.lookahead(0)) == "EOL":
|
||||||
if len(bytes) == 0:
|
|
||||||
currentcharmap = basecharmap
|
currentcharmap = basecharmap
|
||||||
else:
|
else:
|
||||||
|
bytes = readData(line)
|
||||||
try:
|
try:
|
||||||
base = bytes[0].data
|
base = bytes[0].data
|
||||||
newsubstr = "".join([chr(x.data) for x in bytes[1:]])
|
newsubstr = "".join([chr(x.data) for x in bytes[1:]])
|
||||||
|
@ -15,6 +15,9 @@ import os.path
|
|||||||
# license: See README for details.
|
# license: See README for details.
|
||||||
|
|
||||||
|
|
||||||
|
loadedfiles = {}
|
||||||
|
|
||||||
|
|
||||||
class Lexeme(object):
|
class Lexeme(object):
|
||||||
"Class for lexer tokens. Used by lexer and parser."
|
"Class for lexer tokens. Used by lexer and parser."
|
||||||
def __init__(self, type="UNKNOWN", value=None):
|
def __init__(self, type="UNKNOWN", value=None):
|
||||||
@ -180,7 +183,16 @@ class ParseLine(object):
|
|||||||
"""Reads a token from the ParseLine line and returns it if it's of a
|
"""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."""
|
type in the sequence tokens. Otherwise, it logs an error."""
|
||||||
token = self.pop()
|
token = self.pop()
|
||||||
if token.type not in 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) + '"')
|
Err.log('Expected: "' + '", "'.join(tokens) + '"')
|
||||||
return token
|
return token
|
||||||
|
|
||||||
@ -196,7 +208,7 @@ def parse_expr(line):
|
|||||||
next = line.lookahead(0).type
|
next = line.lookahead(0).type
|
||||||
if next == "NUM":
|
if next == "NUM":
|
||||||
return IR.ConstantExpr(line.expect("NUM").value)
|
return IR.ConstantExpr(line.expect("NUM").value)
|
||||||
elif next == "LABEL":
|
elif next in ["LABEL", "X", "Y", "OPCODE"]:
|
||||||
return IR.LabelExpr(line.expect("LABEL").value)
|
return IR.LabelExpr(line.expect("LABEL").value)
|
||||||
elif next == "^":
|
elif next == "^":
|
||||||
line.expect("^")
|
line.expect("^")
|
||||||
@ -352,11 +364,20 @@ def parse_line(ppt, lexemelist):
|
|||||||
context_directory = None
|
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."
|
"Loads an Ophis source file, and returns an IR list."
|
||||||
global context_directory
|
global context_directory, loadedfiles
|
||||||
Err.currentpoint = ppt
|
Err.currentpoint = ppt
|
||||||
old_context = context_directory
|
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 Cmd.print_loaded_files:
|
||||||
if filename != '-':
|
if filename != '-':
|
||||||
print>>sys.stderr, "Loading " + filename
|
print>>sys.stderr, "Loading " + filename
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
# license: See README for details.
|
# license: See README for details.
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
import os
|
||||||
import Ophis.Frontend
|
import Ophis.Frontend
|
||||||
import Ophis.IR
|
import Ophis.IR
|
||||||
import Ophis.CorePragmas
|
import Ophis.CorePragmas
|
||||||
@ -71,7 +72,7 @@ def run_all():
|
|||||||
# We can't dump our binary in test mode; that would be
|
# We can't dump our binary in test mode; that would be
|
||||||
# disastrous. So, we'll do some platform-specific
|
# disastrous. So, we'll do some platform-specific
|
||||||
# things here to force our stdout to binary mode.
|
# things here to force our stdout to binary mode.
|
||||||
import os, msvcrt
|
import msvcrt
|
||||||
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
|
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
|
||||||
elif outfile is None:
|
elif outfile is None:
|
||||||
output = file('ophis.bin', 'wb')
|
output = file('ophis.bin', 'wb')
|
||||||
|
@ -174,10 +174,12 @@ class InitLabels(Pass):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
Pass.__init__(self)
|
Pass.__init__(self)
|
||||||
self.labelmap = {}
|
self.labelmap = {}
|
||||||
|
self.runcount = 0
|
||||||
|
|
||||||
def prePass(self):
|
def prePass(self):
|
||||||
self.changed = False
|
self.changed = False
|
||||||
self.PCvalid = True
|
self.PCvalid = True
|
||||||
|
self.runcount += 1
|
||||||
|
|
||||||
def visitAdvance(self, node, env):
|
def visitAdvance(self, node, env):
|
||||||
self.PCvalid = node.data[0].valid(env, self.PCvalid)
|
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:
|
if val.valid(env, self.PCvalid) and label not in env:
|
||||||
env[label] = 0
|
env[label] = 0
|
||||||
self.changed = True
|
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):
|
def visitUnknown(self, node, env):
|
||||||
pass
|
pass
|
||||||
@ -787,6 +795,10 @@ class Assembler(Pass):
|
|||||||
length = node.data[1].value(env)
|
length = node.data[1].value(env)
|
||||||
if offset < 2:
|
if offset < 2:
|
||||||
Err.log("Negative offset in .incbin")
|
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):
|
elif offset + length > len(node.data):
|
||||||
Err.log("File too small for .incbin subrange")
|
Err.log("File too small for .incbin subrange")
|
||||||
else:
|
else:
|
||||||
|
Loading…
Reference in New Issue
Block a user