Refactor read_bss_sections into a class.

- Pass a constants dict into eval instead of using globals
- Read same-line labels and tokens
This commit is contained in:
yenatch
2014-03-07 00:23:05 -05:00
parent f300444ca1
commit da0ccc6e1d

View File

@@ -5,10 +5,6 @@ RGBDS BSS section and constant parsing.
import os
# TODO: parse these constants from constants.asm
NUM_OBJECTS = 0x10
OBJECT_LENGTH = 0x10
def separate_comment(line):
if ';' in line:
@@ -33,118 +29,149 @@ def make_wram_labels(wram_sections):
def bracket_value(string, i=0):
return string.split('[')[1 + i*2].split(']')[0]
def read_bss_sections(bss, sections=[], section=None, address=None, macros={}):
class BSSReader:
"""Really?"""
sections = []
section = None
address = None
macros = {}
constants = {
# TODO: parse these constants from constants.asm
'NUM_OBJECTS': 0x10,
'OBJECT_LENGTH': 0x10,
}
if section is None:
section = {
"labels": [],
}
section_types = {
'VRAM': 0x8000,
'SRAM': 0xa000,
'WRAM0': 0xc000,
'WRAMX': 0xd000,
'HRAM': 0xff80,
}
if type(bss) is str:
bss = bss.split('\n')
def __init__(self, *args, **kwargs):
self.__dict__.update(kwargs)
macro = False
macro_name = None
for line in bss:
line = line.lstrip()
line, comment = separate_comment(line)
line = line.strip()
def read_bss_line(self, l):
parts = l.strip().split(' ')
token = parts[0].strip()
params = ' '.join(parts[1:]).split(',')
if line[-4:].upper() == 'ENDM':
macro = False
macro_name = None
elif macro:
macros[macro_name] += [line]
elif line[-5:].upper() == 'MACRO':
macro_name = line.split(':')[0]
macro = True
macros[macro_name] = []
elif 'SECTION' == line[:7]:
if section: # previous
sections += [section]
section_def = line.split(',')
name = section_def[0].split('"')[1]
type_ = section_def[1].strip()
if len(section_def) > 2:
bank = bracket_value(section_def[2])
if token in ['ds', 'db', 'dw']:
if any(params):
length = eval(rgbasm_to_py(params[0]), self.constants)
else:
bank = None
length = {'ds': 1, 'db': 1, 'dw': 2}[token]
self.address += length
# assume adjacent labels to use the same space
for label in self.section['labels'][::-1]:
if label['length'] == 0:
label['length'] = length
else:
break
if '[' in type_:
address = int(rgbasm_to_py(bracket_value(type_)), 16)
else:
types = {
'VRAM': 0x8000,
'SRAM': 0xa000,
'WRAM0': 0xc000,
'WRAMX': 0xd000,
'HRAM': 0xff80,
}
if address == None or bank != section['bank'] or section['type'] != type_:
if type_ in types.keys():
address = types[type_]
# else: keep going from this address
elif token in self.macros.keys():
macro_text = '\n'.join(self.macros[token]) + '\n'
for i, p in enumerate(params):
macro_text = macro_text.replace('\\'+str(i+1),p)
macro_text = macro_text.split('\n')
macro_reader = BSSReader(
sections = list(self.sections),
section = dict(self.section),
address = self.address,
constants = self.constants,
)
macro_sections = macro_reader.read_bss_sections(macro_text)
self.section = macro_sections[-1]
self.address = self.section['labels'][-1]['address'] + self.section['labels'][-1]['length']
section = {
'name': name,
'type': type_,
'bank': bank,
'start': address,
'labels': [],
def read_bss_sections(self, bss):
if self.section is None:
self.section = {
"labels": [],
}
elif ':' in line:
# rgbasm allows labels without :, but prefer convention
label = line[:line.find(':')]
if '\\' in label:
raise Exception, line + ' ' + label
if ';' not in label:
section['labels'] += [{
'label': label,
'address': address,
'length': 0,
}]
if type(bss) is str:
bss = bss.split('\n')
elif 'EQU' in line.split():
# some space is defined using constants
name, value = line.split('EQU')
name, value = name.strip(), value.strip().replace('$','0x').replace('%','0b')
globals()[name] = eval(value)
macro = False
macro_name = None
for line in bss:
line = line.lstrip()
line, comment = separate_comment(line)
line = line.strip()
elif line.strip():
parts = line.strip().split(' ')
token = parts[0].strip()
params = ' '.join(parts[1:]).split(',')
if line[-4:].upper() == 'ENDM':
macro = False
macro_name = None
if token in ['ds', 'db', 'dw']:
if len(params):
length = eval(rgbasm_to_py(params[0]))
elif macro:
self.macros[macro_name] += [line]
elif line[-5:].upper() == 'MACRO':
macro_name = line.split(':')[0]
macro = True
self.macros[macro_name] = []
elif 'SECTION' == line[:7]:
if self.section: # previous
self.sections += [self.section]
section_def = line.split(',')
name = section_def[0].split('"')[1]
type_ = section_def[1].strip()
if len(section_def) > 2:
bank = bracket_value(section_def[2])
else:
length = {'ds': 1, 'db': 1, 'dw': 2}[token]
address += length
# assume adjacent labels to use the same space
for label in section['labels'][::-1]:
if label['length'] == 0:
label['length'] = length
else:
break
bank = None
elif token in macros.keys():
macro_text = '\n'.join(macros[token]) + '\n'
for i, p in enumerate(params):
macro_text = macro_text.replace('\\'+str(i+1),p)
macro_text = macro_text.split('\n')
macro_sections = read_bss_sections(macro_text, sections=sections, section=section, address=address)
section = macro_sections[-1]
address = section['labels'][-1]['address'] + section['labels'][-1]['length']
del macro_sections[-1]
if '[' in type_:
self.address = int(rgbasm_to_py(bracket_value(type_)), 16)
else:
if self.address == None or bank != self.section['bank'] or self.section['type'] != type_:
self.address = self.section_types.get(type_, self.address)
# else: keep going from this address
self.section = {
'name': name,
'type': type_,
'bank': bank,
'start': self.address,
'labels': [],
}
elif ':' in line:
# rgbasm allows labels without :, but prefer convention
label = line[:line.find(':')]
if '\\' in label:
raise Exception, line + ' ' + label
if ';' not in label:
section_label = {
'label': label,
'address': self.address,
'length': 0,
}
self.section['labels'] += [section_label]
self.read_bss_line(line.split(':')[-1])
elif 'EQU' in line.split():
# some space is defined using constants
name, value = line.split('EQU')
name, value = name.strip(), value.strip().replace('$','0x').replace('%','0b')
self.constants[name] = eval(value, self.constants)
elif line:
self.read_bss_line(line)
self.sections += [self.section]
return self.sections
def read_bss_sections(bss):
reader = BSSReader()
return reader.read_bss_sections(bss)
sections.append(section)
return sections
def constants_to_dict(constants):
return dict((eval(rgbasm_to_py(constant[constant.find('EQU')+3:constant.find(';')])), constant[:constant.find('EQU')].strip()) for constant in constants)