r500/regs/parse_bits.py

164 lines
5.1 KiB
Python

import sys
from typing import Union
import re
from dataclasses import dataclass
from pprint import pprint
from collections import OrderedDict
def split_line_fields(line, fields):
a = line[fields[0]:fields[1]]
b = line[fields[1]:fields[2]]
c = line[fields[2]:fields[3]]
d = line[fields[3]:]
assert a.strip() == '' or a[0] != ' ', a
assert b.strip() == '' or b[0] != ' ', b
assert c.strip() == '' or c[0] != ' ', c
assert d.strip() == '' or d[0] != ' ', d
assert a[-1] == ' '
assert b[-1] == ' '
assert c[-1] == ' ' or len(line) < fields[3]
return a, b, c, d
def find_line_fields(line):
field_name_ix = line.index('Field Name')
bits_ix = line.index('Bits')
default_ix = line.index('Default')
description_ix = line.index('Description')
assert field_name_ix == 0
assert bits_ix > field_name_ix
assert default_ix > bits_ix
assert description_ix > default_ix
return field_name_ix, bits_ix, default_ix, description_ix
def parse_file_fields(filename):
with open(filename) as f:
lines = f.read().split('\n')
first, *rest = lines
fields = find_line_fields(first)
a, b, c, d = split_line_fields(first, fields)
assert a.rstrip() == 'Field Name', a
assert b.rstrip() == 'Bits', b
assert c.rstrip() == 'Default', c
assert d.rstrip() == 'Description', d
for line in rest:
if not line.strip():
continue
a, b, c, d = split_line_fields(line, fields)
yield a.strip(), b.strip(), c.strip(), d.strip()
def parse_bits(s):
if ':' in s:
a, b = s.split(':')
h = int(a, 10)
l = int(b, 10)
assert h > l
return h, l
else:
b = int(s, 10)
return b
@dataclass
class Descriptor:
field_name: str
bits: Union[int, tuple[int, int]]
default: int
description: str
possible_values: list[tuple[int, str]]
def aggregate(fields):
ix = 0
descriptor = None
def next_nonempty():
nonlocal ix
while ix < len(fields) and not ''.join(fields[ix]):
ix += 1
def parse_possible_values():
nonlocal ix
if ix + 1 >= len(fields):
return
if not fields[ix+1][3] == 'POSSIBLE VALUES:':
return
ix += 1
while ix + 1 < len(fields) and fields[ix+1][0] == '' and fields[ix+1][3] != '':
field_name, bits, default, description = fields[ix+1]
assert not field_name, field_name
assert not bits, bits
assert not default, default
m = re.match('^([0-9]{2}) - (.*)$', description)
assert m, repr(description)
value, desc = m.groups()
if ": " in desc and ' ' not in desc.split(": ")[0].strip():
name, desc = desc.split(": ")
name = name
elif len(desc.strip().split()) == 1:
name = desc
desc = None
else:
name = None
if name is not None:
name = name.strip().upper().replace('.', '_').replace('-', '_').replace(',', '_').replace('*', 'x').replace('+', 'p')
if name != 'RESERVED':
yield int(value, 10), (name, desc)
ix += 1
def parse_description_lines():
nonlocal ix
while ix + 1 < len(fields) and fields[ix+1][0] == '':
field_name, bits, default, description = fields[ix+1]
assert not field_name, field_name
assert not bits, bits
assert not default, default
assert not re.match('^[0-9]{2} - ', description), description
if description == 'POSSIBLE VALUES:':
break
else:
yield description
ix += 1
previous_fields = dict()
while ix < len(fields):
field_name, bits, default, description = fields[ix]
if description == 'POSSIBLE VALUES:':
description_lines = []
ix -= 1
else:
description_lines = [description]
description_lines.extend(parse_description_lines())
possible_values = OrderedDict(parse_possible_values())
description = ' '.join(description_lines)
if len(description.split(' ')) == 2 and description.lower().startswith("see"):
_, see_field_name = description.split(' ')
assert see_field_name in previous_fields, see_field_name
description = previous_fields[see_field_name].description
possible_values = previous_fields[see_field_name].possible_values
assert default.startswith('0x') or default == 'none', default
descriptor = Descriptor(
field_name = field_name,
bits = parse_bits(bits),
default = 0 if default == 'none' else int(default, 16),
description = description,
possible_values = possible_values
)
previous_fields[descriptor.field_name] = descriptor
yield descriptor
ix += 1
next_nonempty()
if __name__ == "__main__":
l = list(parse_file_fields(sys.argv[1]))
for descriptor in aggregate(l):
pprint(descriptor, width=200)