modm_data.header2svd
CMSIS Header to SVD Pipeline
class
Header:
10class Header: 11 CMSIS_PATH = root_path("ext/cmsis/header/CMSIS/Core/Include") 12 _CACHE_HEADER = defaultdict(dict) 13 14 def __init__(self, filename, substitutions=None): 15 self.filename = filename 16 self.substitutions = {r"__(IO|IM|I|O)": ""} 17 if substitutions is not None: 18 self.substitutions.update(substitutions) 19 20 @property 21 def _cache(self): 22 return Header._CACHE_HEADER[self.filename] 23 24 @property 25 def _content(self) -> str: 26 """The header content without comments and with joined line continuations.""" 27 if "content" not in self._cache: 28 content = self.filename.read_text(encoding="utf-8-sig", errors="replace") 29 content = re.sub(r"\\\s*\n", " ", content) 30 content = re.sub(r"/\*.*?\*/", "", content, flags=re.DOTALL) 31 self._cache["content"] = re.sub(r"//[^\n]*", "", content) 32 return self._cache["content"] 33 34 @property 35 def macros(self) -> dict[str, str]: 36 """All object-like and function-like macro definitions of the header by name.""" 37 if "macros" not in self._cache: 38 self._cache["macros"] = { 39 name: " ".join(value.split()) 40 for name, value in re.findall(r"^\s*#define\s+(\w+)(.*)$", self._content, re.M) 41 } 42 return self._cache["macros"] 43 44 @property 45 def typedefs(self) -> dict[str, tuple[str, ...]]: 46 """The member names of all `typedef struct` definitions by type name, without the reserved members.""" 47 if "typedefs" not in self._cache: 48 content, typedefs = self._content, {} 49 for match in re.finditer(r"typedef\s+struct\s*\w*\s*\{", content): 50 end, depth = match.end(), 1 51 while depth and end < len(content): 52 depth += {"{": 1, "}": -1}.get(content[end], 0) 53 end += 1 54 if name := re.match(r"\s*(\w+)\s*;", content[end:]): 55 members = re.findall(r"(\w+)\s*(?:\[[^\]]*\])?\s*;", content[match.end() : end - 1]) 56 typedefs[name.group(1)] = tuple(m for m in members if not m.upper().startswith("RESERVED")) 57 self._cache["typedefs"] = typedefs 58 return self._cache["typedefs"] 59 60 @property 61 def peripherals(self) -> dict[str, str]: 62 """The type names of all peripheral instances, for example, `{"SPI1": "SPI_TypeDef"}`.""" 63 if "peripherals" not in self._cache: 64 self._cache["peripherals"] = dict(re.findall(r"#define\s+(\w+)\s+\(\(\s*(\w+)\s*\*\s*\)", self._content)) 65 return self._cache["peripherals"] 66 67 def is_defined(self, name: str) -> bool: 68 """:return: True if the macro is defined in the header.""" 69 return name in self.macros 70 71 def define_value(self, name: str) -> int | None: 72 """:return: The integer value of a macro that is composed of integers and other macros.""" 73 if (value := self.macros.get(name)) is None: 74 return None 75 return self.evaluate(value) 76 77 def evaluate(self, value: str) -> int | None: 78 """:return: The integer value of an expression that is composed of integers and macros of this header.""" 79 value = re.sub(r"\b(0x[0-9A-Fa-f]+|\d+)[UuLl]*\b", r"\1", value) 80 value = re.sub(r"\((uint32_t|uint16_t|uint8_t)\)", "", value) 81 for identifier in set(re.findall(r"\b[A-Za-z_]\w*\b", value)): 82 if (resolved := self.define_value(identifier)) is None: 83 return None 84 value = re.sub(rf"\b{identifier}\b", str(resolved), value) 85 if not re.fullmatch(r"[\d\sxXa-fA-F()<>|&~+\-*]+", value): 86 return None 87 return int(eval(value)) 88 89 def instances(self, macro: str) -> set[str]: 90 """:return: The instance names checked by an `IS_*_INSTANCE(INSTANCE)` macro.""" 91 return set(re.findall(r"==\s*\(?(\w+)\)?", self.macros.get(macro, ""))) 92 93 @property 94 def header(self): 95 from CppHeaderParser import CppHeader 96 97 if "header" not in self._cache: 98 content = self.filename.read_text(encoding="utf-8-sig", errors="replace") 99 for pattern, subs in self.substitutions.items(): 100 content = re.sub(pattern, subs, content, flags=(re.DOTALL | re.MULTILINE)) 101 self._cache["header"] = CppHeader(content, "string") 102 return self._cache["header"]
macros: dict[str, str]
34 @property 35 def macros(self) -> dict[str, str]: 36 """All object-like and function-like macro definitions of the header by name.""" 37 if "macros" not in self._cache: 38 self._cache["macros"] = { 39 name: " ".join(value.split()) 40 for name, value in re.findall(r"^\s*#define\s+(\w+)(.*)$", self._content, re.M) 41 } 42 return self._cache["macros"]
All object-like and function-like macro definitions of the header by name.
typedefs: dict[str, tuple[str, ...]]
44 @property 45 def typedefs(self) -> dict[str, tuple[str, ...]]: 46 """The member names of all `typedef struct` definitions by type name, without the reserved members.""" 47 if "typedefs" not in self._cache: 48 content, typedefs = self._content, {} 49 for match in re.finditer(r"typedef\s+struct\s*\w*\s*\{", content): 50 end, depth = match.end(), 1 51 while depth and end < len(content): 52 depth += {"{": 1, "}": -1}.get(content[end], 0) 53 end += 1 54 if name := re.match(r"\s*(\w+)\s*;", content[end:]): 55 members = re.findall(r"(\w+)\s*(?:\[[^\]]*\])?\s*;", content[match.end() : end - 1]) 56 typedefs[name.group(1)] = tuple(m for m in members if not m.upper().startswith("RESERVED")) 57 self._cache["typedefs"] = typedefs 58 return self._cache["typedefs"]
The member names of all typedef struct definitions by type name, without the reserved members.
peripherals: dict[str, str]
60 @property 61 def peripherals(self) -> dict[str, str]: 62 """The type names of all peripheral instances, for example, `{"SPI1": "SPI_TypeDef"}`.""" 63 if "peripherals" not in self._cache: 64 self._cache["peripherals"] = dict(re.findall(r"#define\s+(\w+)\s+\(\(\s*(\w+)\s*\*\s*\)", self._content)) 65 return self._cache["peripherals"]
The type names of all peripheral instances, for example, {"SPI1": "SPI_TypeDef"}.
def
is_defined(self, name: str) -> bool:
67 def is_defined(self, name: str) -> bool: 68 """:return: True if the macro is defined in the header.""" 69 return name in self.macros
Returns
True if the macro is defined in the header.
def
define_value(self, name: str) -> int | None:
71 def define_value(self, name: str) -> int | None: 72 """:return: The integer value of a macro that is composed of integers and other macros.""" 73 if (value := self.macros.get(name)) is None: 74 return None 75 return self.evaluate(value)
Returns
The integer value of a macro that is composed of integers and other macros.
def
evaluate(self, value: str) -> int | None:
77 def evaluate(self, value: str) -> int | None: 78 """:return: The integer value of an expression that is composed of integers and macros of this header.""" 79 value = re.sub(r"\b(0x[0-9A-Fa-f]+|\d+)[UuLl]*\b", r"\1", value) 80 value = re.sub(r"\((uint32_t|uint16_t|uint8_t)\)", "", value) 81 for identifier in set(re.findall(r"\b[A-Za-z_]\w*\b", value)): 82 if (resolved := self.define_value(identifier)) is None: 83 return None 84 value = re.sub(rf"\b{identifier}\b", str(resolved), value) 85 if not re.fullmatch(r"[\d\sxXa-fA-F()<>|&~+\-*]+", value): 86 return None 87 return int(eval(value))
Returns
The integer value of an expression that is composed of integers and macros of this header.
def
instances(self, macro: str) -> set[str]:
89 def instances(self, macro: str) -> set[str]: 90 """:return: The instance names checked by an `IS_*_INSTANCE(INSTANCE)` macro.""" 91 return set(re.findall(r"==\s*\(?(\w+)\)?", self.macros.get(macro, "")))
Returns
The instance names checked by an
IS_*_INSTANCE(INSTANCE)macro.
header
93 @property 94 def header(self): 95 from CppHeaderParser import CppHeader 96 97 if "header" not in self._cache: 98 content = self.filename.read_text(encoding="utf-8-sig", errors="replace") 99 for pattern, subs in self.substitutions.items(): 100 content = re.sub(pattern, subs, content, flags=(re.DOTALL | re.MULTILINE)) 101 self._cache["header"] = CppHeader(content, "string") 102 return self._cache["header"]