stm32-data/stm32data/header.py
2022-02-04 01:47:29 +02:00

210 lines
6.5 KiB
Python

import re
import os
import json
from glob import glob
from stm32data import yaml
from stm32data.util import removeprefix, removesuffix
headers_parsed = {}
header_map = {}
with open('header_map.yaml', 'r') as f:
y = yaml.load(f)
for header, chips in y.items():
for chip in chips.split(','):
header_map[chip.strip().lower()] = header.lower()
def get_for_chip(model):
if header := get_header_name_for_chip(model):
return headers_parsed[header]
return None
def get_header_name_for_chip(model):
# for a, b in header_map:
# model = re.sub(a, b, model, flags=re.IGNORECASE)
model = model.lower()
# if it's in the map, just go
if r := header_map.get(model):
return r
# if not, find it by regex, taking `x` meaning `anything`
res = []
for h in headers_parsed.keys():
if re.match('^' + h.replace('x', '.') + '$', model):
res.append(h)
if len(res) == 0:
return None
assert len(res) == 1
return res[0]
def paren_ok(val):
n = 0
for c in val:
if c == '(':
n += 1
if c == ')':
n -= 1
if n < 0:
return False
return n == 0
# warning: horrible abomination ahead
def parse_value(val, defines):
val = val.strip()
if val == '':
return 0
if m := re.match('(0([1-9][0-9]*)(U))', val):
return int(m.group(2), 10)
if m := re.match('((0x[0-9a-fA-F]+|\\d+))(|u|ul|U|UL)$', val):
return int(m.group(1), 0)
if m := re.match('([0-9A-Za-z_]+)$', val):
return defines.get(m.group(1), 0)
if m := re.match('\\((.*)\\)$', val):
if paren_ok(m.group(1)):
return parse_value(m.group(1), defines)
if m := re.match('\\*?\\([0-9A-Za-z_]+ *\\*?\\)(.*)$', val):
return parse_value(m.group(1), defines)
# if m := re.match('\\*?\\(u?int(8|16|32|64)_t\\ *)(.*)$', val):
# return parse_value(m.group(1), defines)
if m := re.match('(.*)/(.*)$', val):
return parse_value(m.group(1), defines) / parse_value(m.group(2), defines)
if m := re.match('(.*)<<(.*)$', val):
return (parse_value(m.group(1), defines) << parse_value(m.group(2), defines)) & 0xFFFFFFFF
if m := re.match('(.*)>>(.*)$', val):
return parse_value(m.group(1), defines) >> parse_value(m.group(2), defines)
if m := re.match('(.*)\\|(.*)$', val):
return parse_value(m.group(1), defines) | parse_value(m.group(2), defines)
if m := re.match('(.*)&(.*)$', val):
return parse_value(m.group(1), defines) | parse_value(m.group(2), defines)
if m := re.match('~(.*)$', val):
return (~parse_value(m.group(1), defines)) & 0xFFFFFFFF
if m := re.match('(.*)\\+(.*)$', val):
return parse_value(m.group(1), defines) + parse_value(m.group(2), defines)
if m := re.match('(.*)-(.*)$', val):
return parse_value(m.group(1), defines) - parse_value(m.group(2), defines)
raise Exception("can't parse: " + val)
def parse_header(f):
irqs = {}
defines = {}
cores = []
cur_core = 'all'
accum = ''
for l in open(f, 'r', encoding='utf-8', errors='ignore'):
l = l.strip()
l = accum + l
if l.endswith('\\'):
accum = l[:-1]
continue
accum = ''
# Scoped by a single core
if m := re.match('.*if defined.*CORE_CM(\\d+)(PLUS)?.*', l):
cur_core = "cm" + str(m.group(1))
if m.group(2) != None:
cur_core += "p"
# print("Cur core is ", cur_core, "matched", l)
found = False
for core in cores:
if core == cur_core:
found = True
if not found:
cores.append(cur_core)
# print("Switching to core", cur_core, "for", f)
elif m := re.match('.*else.*', l):
cur_core = "all"
if m := re.match('.*else.*CORE_CM(\\d+)(PLUS)?.*', l):
cur_core = "cm" + str(m.group(1))
if m.group(2) != None:
cur_core += "p"
# print("Cur core is ", cur_core, "matched", l)
elif len(cores) > 1:
# Pick the second core assuming we've already parsed one
cur_core = cores[1]
found = False
for core in cores:
if core == cur_core:
found = True
if not found:
cores.append(cur_core)
# print("Switching to core", cur_core, "for", f)
elif m := re.match('.*endif.*', l):
# print("Switching to common core for", f)
cur_core = "all"
if cur_core not in irqs:
# print("Registering new core", cur_core)
irqs[cur_core] = {}
if cur_core not in defines:
defines[cur_core] = {}
if m := re.match('([a-zA-Z0-9_]+)_IRQn += (\\d+),? +/\\*!< (.*) \\*/', l):
# print("Found irq for", cur_core)
irqs[cur_core][m.group(1)] = int(m.group(2))
if m := re.match('#define +([0-9A-Za-z_]+)\\(', l):
defines[cur_core][m.group(1)] = -1
if m := re.match('#define +([0-9A-Za-z_]+) +(.*)', l):
name = m.group(1)
val = m.group(2)
name = name.strip()
if name == 'FLASH_SIZE':
continue
val = val.split('/*')[0].strip()
val = parse_value(val, defines[cur_core])
# print("Found define for", cur_core)
defines[cur_core][name] = val
# print("Found", len(cores), "cores for", f)
# print("Found", len(irqs['all']), "shared interrupts for", f)
if len(cores) == 0:
cores.append("all")
for core in cores:
if core != "all":
irqs[core].update(irqs['all'])
defines[core].update(defines['all'])
return {
'cores': cores,
'interrupts': irqs,
'defines': defines,
}
def parse_headers():
os.makedirs('sources/headers_parsed', exist_ok=True)
print('loading headers...')
for f in glob('sources/headers/*.h'):
f = f.replace(os.path.sep, '/')
# if 'stm32f4' not in f: continue
ff = removeprefix(f, 'sources/headers/')
ff = removesuffix(ff, '.h')
try:
with open('sources/headers_parsed/{}.json'.format(ff), 'r') as j:
res = json.load(j)
except:
print(f)
res = parse_header(f)
with open('sources/headers_parsed/{}.json'.format(ff), 'w') as j:
json.dump(res, j)
headers_parsed[ff] = res
parse_headers()