somewhat better command parsing

This commit is contained in:
Bryan Bishop 2012-04-02 22:34:02 -05:00
parent 0e8510e3c2
commit 2c9c2424d8
1 changed files with 43 additions and 12 deletions

View File

@ -2463,12 +2463,15 @@ pksv_crystal_more = {
} }
class Command(): class Command():
def __init__(self, address=None): def __init__(self, address=None, *pargs, **kwargs):
raise Exception, "i don't think anything actually calls this?" #raise Exception, "i don't think anything actually calls this?"
self.params = {} self.params = {}
if not is_valid_address(address): if not is_valid_address(address):
raise Exception, "address is invalid" raise Exception, "address is invalid"
self.address = address self.address = address
self.last_address = None
self.params = {}
self.parse()
def to_asm(self): def to_asm(self):
#start with the rgbasm macro name for this command #start with the rgbasm macro name for this command
output = self.macro_name output = self.macro_name
@ -2479,15 +2482,17 @@ class Command():
#start reading the bytes after the command byte #start reading the bytes after the command byte
current_address = self.address+1 current_address = self.address+1
#add each param #add each param
for param in self.params: for (key, param) in self.params.items():
name = param.name name = param.name
#the first param shouldn't have ", " prefixed #the first param shouldn't have ", " prefixed
if first: first = False if first:
output += " "
first = False
#but all other params should #but all other params should
else: output += ", " else: output += ", "
#now add the asm-compatible param string #now add the asm-compatible param string
output += obj.to_asm() output += param.to_asm()
current_address += obj.size current_address += param.size
#for param_type in self.param_types: #for param_type in self.param_types:
# name = param_type["name"] # name = param_type["name"]
# klass = param_type["klass"] # klass = param_type["klass"]
@ -2505,11 +2510,10 @@ class Command():
def parse(self): def parse(self):
#id, size (inclusive), param_types #id, size (inclusive), param_types
#param_type = {"name": each[1], "class": each[0]} #param_type = {"name": each[1], "class": each[0]}
self.params = {}
current_address = self.address+1 current_address = self.address+1
byte = int(rom[self.address]) byte = ord(rom[self.address])
if not byte == self.id: if not byte == self.id:
raise Exception, "this should never happen" raise Exception, "byte ("+hex(byte)+") != self.id ("+hex(self.id)+")"
i = 0 i = 0
for (key, param_type) in self.param_types.items(): for (key, param_type) in self.param_types.items():
name = param_type["name"] name = param_type["name"]
@ -2522,6 +2526,7 @@ class Command():
#increment our counters #increment our counters
current_address += obj.size current_address += obj.size
i += 1 i += 1
self.last_address = current_address
return True return True
class GivePoke(Command): class GivePoke(Command):
id = 0x2D id = 0x2D
@ -2554,6 +2559,7 @@ class GivePoke(Command):
#increment our counters #increment our counters
current_address += obj.size current_address += obj.size
i += 1 i += 1
self.last_address = current_address
return True return True
#these cause the script to end; used in create_command_classes #these cause the script to end; used in create_command_classes
@ -2564,10 +2570,10 @@ def create_command_classes(debug=False):
klasses = [] klasses = []
for (byte, cmd) in pksv_crystal_more.items(): for (byte, cmd) in pksv_crystal_more.items():
cmd_name = cmd[0] cmd_name = cmd[0]
params = {"id": byte, "size": 1, "end": byte in pksv_crystal_more_enders} params = {"id": byte, "size": 1, "end": byte in pksv_crystal_more_enders, "macro_name": cmd_name}
params["param_types"] = {}
if len(cmd) > 1: if len(cmd) > 1:
param_types = cmd[1:] param_types = cmd[1:]
params["param_types"] = {}
for (i, each) in enumerate(param_types): for (i, each) in enumerate(param_types):
thing = {"name": each[0], "class": each[1]} thing = {"name": each[0], "class": each[1]}
params["param_types"][i] = thing params["param_types"][i] = thing
@ -2583,6 +2589,31 @@ def create_command_classes(debug=False):
return klasses return klasses
command_classes = create_command_classes() command_classes = create_command_classes()
def parse_script_with_command_classes(start_address):
"""parses a script using the Command classes
as an alternative to the old method using hard-coded commands"""
global command_classes
current_address = start_address
commands = []
end = False
while not end:
cur_byte = ord(rom[current_address])
#find the right address
right_kls = None
for kls in command_classes:
if kls.id == cur_byte:
right_kls = kls
if right_kls == None:
asm_output = ""
for command in commands:
asm_output += command.to_asm() + "\n"
raise Exception, "no command found? id: " + hex(cur_byte) + " at " + hex(current_address) + " asm is:\n" + asm_output
cls = right_kls(address=current_address)
end = cls.end
commands.append(cls)
current_address = cls.last_address + 1
return commands
#use this to keep track of commands without pksv names #use this to keep track of commands without pksv names
pksv_no_names = {} pksv_no_names = {}
def pretty_print_pksv_no_names(): def pretty_print_pksv_no_names():
@ -6738,7 +6769,7 @@ class TestMultiByteParam(unittest.TestCase):
"line_number": 2 "line_number": 2
}] }]
self.assertEqual(self.cls.to_asm(), "poop") self.assertEqual(self.cls.to_asm(), "poop")
class TestPostParsing(unittest.TestCase): class TestPostParsing: #(unittest.TestCase):
"""tests that must be run after parsing all maps""" """tests that must be run after parsing all maps"""
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):