审计并修复 23 个文件:类型注解中的 T1 | T2 一律 fail-fast;typedef/define/export/extern/static/const/volatile/attribute 优先读取 Markers/Attributes/ByteOrder/BitWidth;指针统一 CPtr/rawptr/CRef/CTempRef/CArrayPtr/CAutoPtr Subscript;stub 输出统一新语法。
788 lines
33 KiB
Python
788 lines
33 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
C/H 文件到 Python 存根文件生成器
|
||
|
||
将 C 头文件 (.h) 或 C 源文件 (.c) 转换为 Python 存根文件 (.vpi)
|
||
生成的 Python 代码使用 TransPyC 语法,包含类型注解
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import os
|
||
import re
|
||
import sys
|
||
from typing import Any
|
||
from lib.core.VLogger import get_logger as _vlog
|
||
|
||
|
||
# PROF 热点:C 头文件解析阶段对每一行 / 每个成员反复调用未预编译的 re.match /
|
||
# re.finditer(模块级入口),每次都要「模式串哈希 → re 缓存查找 → 未命中就重新编译」。
|
||
# 这些模式全是字面量 → 提升为模块级预编译常量(模式串与调用点字面量逐字一致)。
|
||
# _ParseDefines 用:#define 指令(含可选值),模式带 re.MULTILINE
|
||
_RE_DEFINE: re.Pattern[str] = re.compile(r'#define\s+(\w+)(?:\s+(.+?))?\s*$', re.MULTILINE)
|
||
# _ParseTypedefStructs / _ParseTypedefEnums / _ParseTypedefUnions 用:typedef 结尾的 `别名;`
|
||
_RE_ALIAS: re.Pattern[str] = re.compile(r'(\w+)\s*;')
|
||
# _ParseFunctions 用:返回类型是否以数字开头
|
||
_RE_DIGIT_START: re.Pattern[str] = re.compile(r'^\d')
|
||
# _ParseFunctions / _ParseVariables 用:标识符是否以字母或下划线开头
|
||
_RE_IDENT_START: re.Pattern[str] = re.compile(r'^[A-Za-z_]')
|
||
# _ParseVariables 用:排除 C 关键字作为变量名
|
||
_RE_C_KEYWORD: re.Pattern[str] = re.compile(r'^(if|else|while|for|return|switch|case|break|continue|do|goto)$')
|
||
# _ParseStructMembers 用:结构体数组成员
|
||
_RE_STRUCT_ARRAY_MEMBER: re.Pattern[str] = re.compile(r'([\w\s\*]+?)\s+(\w+)\s*\[(\d*)\]')
|
||
# _ParseStructMembers 用:结构体指针成员
|
||
_RE_STRUCT_PTR_MEMBER: re.Pattern[str] = re.compile(r'([\w\s\*]+?)\s*\*\s*(\w+)')
|
||
# _ParseStructMembers 用:结构体普通成员
|
||
_RE_STRUCT_SIMPLE_MEMBER: re.Pattern[str] = re.compile(r'([\w\s]+?)\s+(\w+)')
|
||
# _ParseEnumValues 用:枚举项名是否合法
|
||
_RE_ENUM_NAME: re.Pattern[str] = re.compile(r'^[A-Za-z_]\w*$')
|
||
# _ParseFuncParams 用:函数指针参数
|
||
_RE_FUNC_PTR_PARAM: re.Pattern[str] = re.compile(r'([\w\s\*]+?)\s*\(\s*\*\s*(\w*)\s*\)\s*\([^)]*\)')
|
||
# _ParseFuncParams 用:数组参数
|
||
_RE_ARRAY_PARAM: re.Pattern[str] = re.compile(r'([\w\s\*]+?)\s+(\w+)\s*\[\d*\]')
|
||
|
||
|
||
class CTypeMapper:
|
||
|
||
# C 类型名 → 新语法内建类型名
|
||
# (int/uint/int8..uint64/sizet/short/long/uchar/float8..float128/char8..char32/rawptr)
|
||
_CNAME_TO_PY: dict[str, str] = {
|
||
# 基本类型
|
||
'char': 'char', 'int': 'int', 'short': 'short',
|
||
'long': 'long', 'long long': 'longlong',
|
||
'float': 'float', 'double': 'double', 'void': 'void',
|
||
# unsigned / signed 变体
|
||
'unsigned': 'uint', 'unsigned char': 'uchar',
|
||
'unsigned int': 'uint', 'unsigned short': 'ushort',
|
||
'unsigned long': 'ulong',
|
||
'unsigned long long': 'ulonglong',
|
||
'signed char': 'char',
|
||
# bool / size_t
|
||
'bool': 'bool', '_Bool': 'bool', 'size_t': 'sizet',
|
||
'ssize_t': 'ptrdifft',
|
||
# stdint 固定宽度类型
|
||
'int8_t': 'int8', 'int16_t': 'int16',
|
||
'int32_t': 'int32', 'int64_t': 'int64',
|
||
'uint8_t': 'uint8', 'uint16_t': 'uint16',
|
||
'uint32_t': 'uint32', 'uint64_t': 'uint64',
|
||
'intptr_t': 'intptr', 'uintptr_t': 'uintptr',
|
||
'ptrdiff_t': 'ptrdifft',
|
||
# 宽字符 / 字符类型
|
||
'wchar_t': 'wchar', 'char8_t': 'char8',
|
||
'char16_t': 'char16', 'char32_t': 'char32',
|
||
# 浮点特化类型
|
||
'float8_t': 'float8', 'float16_t': 'float16',
|
||
'float32_t': 'float32', 'float64_t': 'float64',
|
||
'float128_t': 'float128',
|
||
# signed 速记
|
||
'signed': 'int', 'signed int': 'int',
|
||
'signed short': 'short', 'signed long': 'long',
|
||
}
|
||
|
||
@classmethod
|
||
def _ResolveCNameToPyType(cls, c_type: str) -> str:
|
||
"""将 C 类型名解析为新语法内建类型名(如 'int' → 'int')
|
||
|
||
使用硬编码映射表,不再依赖 CTypeRegistry._cname_to_class。
|
||
未识别的类型原样返回(用于 struct/union/enum/typedef 用户自定义名)。
|
||
"""
|
||
py: str | None = cls._CNAME_TO_PY.get(c_type)
|
||
if py is not None:
|
||
return py
|
||
return c_type
|
||
|
||
@classmethod
|
||
def MapType(cls, c_type: str, IsPtr: bool = False, IsArray: bool = False) -> str:
|
||
"""C 类型 → 新语法类型(指针写成 `T*`,不再产出 `T | CPtr`)。"""
|
||
c_type = c_type.strip()
|
||
|
||
IsConst: bool = 'const ' in c_type
|
||
c_type = c_type.replace('const ', '').replace('const', '').strip()
|
||
IsVolatile: bool = 'volatile ' in c_type
|
||
c_type = c_type.replace('volatile ', '').replace('volatile', '').strip()
|
||
# static / extern / register / inline 是声明级修饰,不是类型的一部分。
|
||
c_type = c_type.replace('static ', '').replace('static', '').strip()
|
||
c_type = c_type.replace('extern ', '').replace('extern', '').strip()
|
||
c_type = c_type.replace('register ', '').replace('register', '').strip()
|
||
c_type = c_type.replace('inline ', '').replace('inline', '').strip()
|
||
|
||
# 指针层数:类型文本里的 '*' 已表达层数;IsPtr 只在文本无 '*' 时补一层,
|
||
# 避免 `IsPtr=True` + 文本 '*' 被重复计数(Node**)。
|
||
StarCount: int = len(c_type) - len(c_type.rstrip('*'))
|
||
PtrCount: int = StarCount if StarCount > 0 else (1 if IsPtr else 0)
|
||
c_type = c_type.rstrip('*').strip()
|
||
|
||
if c_type.startswith('struct '):
|
||
c_type = c_type[7:].strip()
|
||
elif c_type.startswith('union '):
|
||
c_type = c_type[6:].strip()
|
||
elif c_type.startswith('enum '):
|
||
c_type = c_type[5:].strip()
|
||
|
||
PyType: str = cls._ResolveCNameToPyType(c_type)
|
||
|
||
Prefix: str = ''
|
||
if IsConst:
|
||
Prefix += 'const '
|
||
if IsVolatile:
|
||
Prefix += 'volatile '
|
||
|
||
return Prefix + PyType + ('*' * PtrCount)
|
||
|
||
|
||
def _find_matching_brace(text: str, start: int) -> int:
|
||
depth: int = 0
|
||
i: int = start
|
||
while i < len(text):
|
||
if text[i] == '{':
|
||
depth += 1
|
||
elif text[i] == '}':
|
||
depth -= 1
|
||
if depth == 0:
|
||
return i
|
||
elif text[i] == '"' or text[i] == "'":
|
||
quote: str = text[i]
|
||
i += 1
|
||
while i < len(text) and text[i] != quote:
|
||
if text[i] == '\\':
|
||
i += 1
|
||
i += 1
|
||
elif text[i] == '/' and i + 1 < len(text) and text[i + 1] == '/':
|
||
while i < len(text) and text[i] != '\n':
|
||
i += 1
|
||
continue
|
||
elif text[i] == '/' and i + 1 < len(text) and text[i + 1] == '*':
|
||
i += 2
|
||
while i + 1 < len(text) and not (text[i] == '*' and text[i + 1] == '/'):
|
||
i += 1
|
||
i += 1
|
||
i += 1
|
||
return -1
|
||
|
||
|
||
def _strip_comments(content: str) -> str:
|
||
result: list[str] = []
|
||
i: int = 0
|
||
while i < len(content):
|
||
if content[i] == '/' and i + 1 < len(content) and content[i + 1] == '/':
|
||
while i < len(content) and content[i] != '\n':
|
||
i += 1
|
||
elif content[i] == '/' and i + 1 < len(content) and content[i + 1] == '*':
|
||
i += 2
|
||
while i + 1 < len(content) and not (content[i] == '*' and content[i + 1] == '/'):
|
||
i += 1
|
||
i += 2
|
||
elif content[i] == '"' or content[i] == "'":
|
||
quote: str = content[i]
|
||
result.append(content[i])
|
||
i += 1
|
||
while i < len(content) and content[i] != quote:
|
||
if content[i] == '\\' and i + 1 < len(content):
|
||
result.append(content[i])
|
||
i += 1
|
||
result.append(content[i])
|
||
i += 1
|
||
if i < len(content):
|
||
result.append(content[i])
|
||
i += 1
|
||
else:
|
||
result.append(content[i])
|
||
i += 1
|
||
return ''.join(result)
|
||
|
||
|
||
def _strip_preprocessor_guards(content: str) -> str:
|
||
lines: list[str] = content.split('\n')
|
||
filtered: list[str] = []
|
||
for line in lines:
|
||
stripped: str = line.strip()
|
||
if stripped.startswith('#ifndef') and '_H' in stripped.upper():
|
||
continue
|
||
if stripped.startswith('#define') and '_H' in stripped.upper():
|
||
continue
|
||
if stripped == '#endif':
|
||
continue
|
||
filtered.append(line)
|
||
return '\n'.join(filtered)
|
||
|
||
|
||
class CHeaderParser:
|
||
"""C 头文件解析器"""
|
||
|
||
def __init__(self) -> None:
|
||
self.defines: list[dict[str, str | None]] = []
|
||
self.structs: dict[str, dict[str, Any]] = {}
|
||
self.enums: dict[str, dict[str, Any]] = {}
|
||
self.typedefs: list[dict[str, str]] = []
|
||
self.functions: dict[str, dict[str, Any]] = {}
|
||
self.variables: dict[str, dict[str, Any]] = {}
|
||
self.unions: dict[str, dict[str, Any]] = {}
|
||
self.func_ptr_typedefs: dict[str, dict[str, Any]] = {}
|
||
self._typedef_struct_map: dict[str, str] = {}
|
||
self._typedef_enum_map: dict[str, str] = {}
|
||
self._typedef_union_map: dict[str, str] = {}
|
||
|
||
def ParseFile(self, FilePath: str) -> None:
|
||
with open(FilePath, 'r', encoding='utf-8') as f:
|
||
content: str = f.read()
|
||
self.ParseContent(content)
|
||
|
||
def ParseContent(self, content: str) -> None:
|
||
content = _strip_comments(content)
|
||
content = _strip_preprocessor_guards(content)
|
||
self._ParseDefines(content)
|
||
self._ParseTypedefStructs(content)
|
||
self._ParseTypedefEnums(content)
|
||
self._ParseTypedefUnions(content)
|
||
self._ParseFuncPtrTypedefs(content)
|
||
self._ParseSimpleTypedefs(content)
|
||
self._ParseStandaloneStructs(content)
|
||
self._ParseStandaloneEnums(content)
|
||
self._ParseStandaloneUnions(content)
|
||
stripped: str = self._StripBodies(content)
|
||
self._ParseFunctions(stripped)
|
||
self._ParseVariables(stripped)
|
||
|
||
def _StripBodies(self, content: str) -> str:
|
||
result: str = content
|
||
while True:
|
||
pos: int = result.find('{')
|
||
if pos < 0:
|
||
break
|
||
end: int = _find_matching_brace(result, pos)
|
||
if end < 0:
|
||
break
|
||
result = result[:pos] + result[end + 1:]
|
||
return result
|
||
|
||
def _ParseDefines(self, content: str) -> None:
|
||
for match in _RE_DEFINE.finditer(content):
|
||
Name: str = match.group(1)
|
||
Value: str | None = match.group(2)
|
||
if Name.startswith('_') and Name.endswith('_'):
|
||
continue
|
||
if Name.startswith('__'):
|
||
continue
|
||
self.defines.append({
|
||
'name': Name,
|
||
'value': Value.strip() if Value else None,
|
||
})
|
||
|
||
def _ParseTypedefStructs(self, content: str) -> None:
|
||
pattern: str = r'typedef\s+struct\s*(\w*)\s*\{'
|
||
for match in re.finditer(pattern, content):
|
||
brace_start: int = content.index('{', match.start())
|
||
brace_end: int = _find_matching_brace(content, brace_start)
|
||
if brace_end < 0:
|
||
continue
|
||
body: str = content[brace_start + 1:brace_end]
|
||
rest: str = content[brace_end + 1:].lstrip()
|
||
AliasMatch: re.Match[str] | None = _RE_ALIAS.match(rest)
|
||
if not AliasMatch:
|
||
continue
|
||
AliasName: str = AliasMatch.group(1)
|
||
TagName: str | None = match.group(1) if match.group(1) else None
|
||
members: list[dict[str, Any]] = self._ParseStructMembers(body)
|
||
self.structs[AliasName] = {
|
||
'name': AliasName,
|
||
'tag': TagName,
|
||
'members': members,
|
||
}
|
||
if TagName:
|
||
self._typedef_struct_map[TagName] = AliasName
|
||
|
||
def _ParseTypedefEnums(self, content: str) -> None:
|
||
pattern: str = r'typedef\s+enum\s*(\w*)\s*\{'
|
||
for match in re.finditer(pattern, content):
|
||
brace_start: int = content.index('{', match.start())
|
||
brace_end: int = _find_matching_brace(content, brace_start)
|
||
if brace_end < 0:
|
||
continue
|
||
body: str = content[brace_start + 1:brace_end]
|
||
rest: str = content[brace_end + 1:].lstrip()
|
||
AliasMatch: re.Match[str] | None = _RE_ALIAS.match(rest)
|
||
if not AliasMatch:
|
||
continue
|
||
AliasName: str = AliasMatch.group(1)
|
||
TagName: str | None = match.group(1) if match.group(1) else None
|
||
values: list[dict[str, str | None]] = self._ParseEnumValues(body)
|
||
self.enums[AliasName] = {
|
||
'name': AliasName,
|
||
'tag': TagName,
|
||
'values': values,
|
||
}
|
||
if TagName:
|
||
self._typedef_enum_map[TagName] = AliasName
|
||
|
||
def _ParseTypedefUnions(self, content: str) -> None:
|
||
pattern: str = r'typedef\s+union\s*(\w*)\s*\{'
|
||
for match in re.finditer(pattern, content):
|
||
brace_start: int = content.index('{', match.start())
|
||
brace_end: int = _find_matching_brace(content, brace_start)
|
||
if brace_end < 0:
|
||
continue
|
||
body: str = content[brace_start + 1:brace_end]
|
||
rest: str = content[brace_end + 1:].lstrip()
|
||
AliasMatch: re.Match[str] | None = _RE_ALIAS.match(rest)
|
||
if not AliasMatch:
|
||
continue
|
||
AliasName: str = AliasMatch.group(1)
|
||
TagName: str | None = match.group(1) if match.group(1) else None
|
||
members: list[dict[str, Any]] = self._ParseStructMembers(body)
|
||
self.unions[AliasName] = {
|
||
'name': AliasName,
|
||
'tag': TagName,
|
||
'members': members,
|
||
}
|
||
if TagName:
|
||
self._typedef_union_map[TagName] = AliasName
|
||
|
||
def _ParseFuncPtrTypedefs(self, content: str) -> None:
|
||
pattern: str = r'typedef\s+([\w\s\*]+?)\s*\(\s*\*\s*(\w+)\s*\)\s*\(([^)]*)\)\s*;'
|
||
for match in re.finditer(pattern, content):
|
||
ReturnType: str = match.group(1).strip()
|
||
Name: str = match.group(2)
|
||
ParamsStr: str = match.group(3).strip()
|
||
params: list[dict[str, Any]] = self._ParseFuncParams(ParamsStr)
|
||
self.func_ptr_typedefs[Name] = {
|
||
'name': Name,
|
||
'return_type': ReturnType,
|
||
'params': params,
|
||
}
|
||
|
||
def _ParseSimpleTypedefs(self, content: str) -> None:
|
||
stripped: str = self._StripBodies(content)
|
||
pattern: str = r'typedef[ \t]+([\w \t\*]+?)[ \t]+(\w+)[ \t]*;'
|
||
for match in re.finditer(pattern, stripped):
|
||
BaseType: str = match.group(1).strip()
|
||
AliasName: str = match.group(2)
|
||
if AliasName in self.structs:
|
||
continue
|
||
if AliasName in self.enums:
|
||
continue
|
||
if AliasName in self.unions:
|
||
continue
|
||
if AliasName in self.func_ptr_typedefs:
|
||
continue
|
||
if BaseType.startswith('typedef'):
|
||
continue
|
||
self.typedefs.append({'name': AliasName, 'base': BaseType})
|
||
|
||
def _ParseStandaloneStructs(self, content: str) -> None:
|
||
pattern: str = r'(?<!typedef\s)struct\s+(\w+)\s*\{'
|
||
for match in re.finditer(pattern, content):
|
||
TagName: str = match.group(1)
|
||
if TagName in self._typedef_struct_map:
|
||
continue
|
||
if TagName in self.structs:
|
||
continue
|
||
brace_start: int = content.index('{', match.start())
|
||
brace_end: int = _find_matching_brace(content, brace_start)
|
||
if brace_end < 0:
|
||
continue
|
||
body: str = content[brace_start + 1:brace_end]
|
||
members: list[dict[str, Any]] = self._ParseStructMembers(body)
|
||
self.structs[TagName] = {
|
||
'name': TagName,
|
||
'tag': None,
|
||
'members': members,
|
||
}
|
||
|
||
def _ParseStandaloneEnums(self, content: str) -> None:
|
||
pattern: str = r'(?<!typedef\s)enum\s+(\w+)\s*\{'
|
||
for match in re.finditer(pattern, content):
|
||
TagName: str = match.group(1)
|
||
if TagName in self._typedef_enum_map:
|
||
continue
|
||
if TagName in self.enums:
|
||
continue
|
||
brace_start: int = content.index('{', match.start())
|
||
brace_end: int = _find_matching_brace(content, brace_start)
|
||
if brace_end < 0:
|
||
continue
|
||
body: str = content[brace_start + 1:brace_end]
|
||
values: list[dict[str, str | None]] = self._ParseEnumValues(body)
|
||
self.enums[TagName] = {
|
||
'name': TagName,
|
||
'tag': None,
|
||
'values': values,
|
||
}
|
||
|
||
def _ParseStandaloneUnions(self, content: str) -> None:
|
||
pattern: str = r'(?<!typedef\s)union\s+(\w+)\s*\{'
|
||
for match in re.finditer(pattern, content):
|
||
TagName: str = match.group(1)
|
||
if TagName in self._typedef_union_map:
|
||
continue
|
||
if TagName in self.unions:
|
||
continue
|
||
brace_start: int = content.index('{', match.start())
|
||
brace_end: int = _find_matching_brace(content, brace_start)
|
||
if brace_end < 0:
|
||
continue
|
||
body: str = content[brace_start + 1:brace_end]
|
||
members: list[dict[str, Any]] = self._ParseStructMembers(body)
|
||
self.unions[TagName] = {
|
||
'name': TagName,
|
||
'tag': None,
|
||
'members': members,
|
||
}
|
||
|
||
def _ParseFunctions(self, content: str) -> None:
|
||
pattern: str = r'(?:(?:static|extern|inline)[ \t]+)*([\w][\w \t\*]*?)\b(\w+)[ \t]*\(([^)]*)\)[ \t]*;'
|
||
for match in re.finditer(pattern, content):
|
||
ReturnType: str = match.group(1).strip()
|
||
FuncName: str = match.group(2)
|
||
ParamsStr: str = match.group(3).strip()
|
||
if FuncName in self.structs or FuncName in self.enums or FuncName in self.unions:
|
||
continue
|
||
if FuncName in self.func_ptr_typedefs:
|
||
continue
|
||
if FuncName in self.defines:
|
||
continue
|
||
if ReturnType in ('struct', 'union', 'enum', 'typedef'):
|
||
continue
|
||
if _RE_DIGIT_START.match(ReturnType):
|
||
continue
|
||
if not _RE_IDENT_START.match(FuncName):
|
||
continue
|
||
params: list[dict[str, Any]] = self._ParseFuncParams(ParamsStr)
|
||
self.functions[FuncName] = {
|
||
'name': FuncName,
|
||
'return_type': ReturnType,
|
||
'params': params,
|
||
}
|
||
|
||
def _ParseVariables(self, content: str) -> None:
|
||
pattern: str = r'(?:(?:static|extern|volatile|const)[ \t]+)*([\w][\w \t\*]*?)[ \t]+(\w+)[ \t]*(?:\[(\d*)\])?[ \t]*;'
|
||
for match in re.finditer(pattern, content):
|
||
TypeStr: str = match.group(1).strip()
|
||
VarName: str = match.group(2)
|
||
ArraySize: str | None = match.group(3)
|
||
if VarName in self.functions or VarName in self.structs or VarName in self.enums:
|
||
continue
|
||
if VarName in self.unions or VarName in self.func_ptr_typedefs:
|
||
continue
|
||
if VarName in self.defines:
|
||
continue
|
||
if TypeStr.startswith('typedef'):
|
||
continue
|
||
if TypeStr in ('struct', 'union', 'enum', 'typedef'):
|
||
continue
|
||
if _RE_C_KEYWORD.match(VarName):
|
||
continue
|
||
if not _RE_IDENT_START.match(VarName):
|
||
continue
|
||
IsPtr: bool = '*' in TypeStr
|
||
IsArray: bool = ArraySize is not None
|
||
self.variables[VarName] = {
|
||
'name': VarName,
|
||
'type': TypeStr,
|
||
'IsPtr': IsPtr,
|
||
'is_array': IsArray,
|
||
'array_size': int(ArraySize) if ArraySize and ArraySize.isdigit() else None,
|
||
}
|
||
|
||
def _ParseStructMembers(self, body: str) -> list[dict[str, Any]]:
|
||
members: list[dict[str, Any]] = []
|
||
for line in body.split(';'):
|
||
line = line.strip()
|
||
if not line:
|
||
continue
|
||
arr_match: re.Match[str] | None = _RE_STRUCT_ARRAY_MEMBER.match(line)
|
||
if arr_match:
|
||
MemberType: str = arr_match.group(1).strip()
|
||
MemberName: str = arr_match.group(2)
|
||
ArraySize: str = arr_match.group(3)
|
||
members.append({
|
||
'name': MemberName,
|
||
'type': MemberType,
|
||
'is_array': True,
|
||
'array_size': int(ArraySize) if ArraySize and ArraySize.isdigit() else None,
|
||
})
|
||
continue
|
||
ptr_match: re.Match[str] | None = _RE_STRUCT_PTR_MEMBER.match(line)
|
||
if ptr_match:
|
||
MemberType: str = ptr_match.group(1).strip() + ' *'
|
||
MemberName: str = ptr_match.group(2)
|
||
members.append({
|
||
'name': MemberName,
|
||
'type': MemberType,
|
||
'IsPtr': True,
|
||
})
|
||
continue
|
||
simple_match: re.Match[str] | None = _RE_STRUCT_SIMPLE_MEMBER.match(line)
|
||
if simple_match:
|
||
MemberType: str = simple_match.group(1).strip()
|
||
MemberName: str = simple_match.group(2)
|
||
members.append({
|
||
'name': MemberName,
|
||
'type': MemberType,
|
||
})
|
||
return members
|
||
|
||
def _ParseEnumValues(self, body: str) -> list[dict[str, str | None]]:
|
||
values: list[dict[str, str | None]] = []
|
||
for item in body.split(','):
|
||
item = item.strip()
|
||
if not item:
|
||
continue
|
||
eq_pos: int = item.find('=')
|
||
if eq_pos >= 0:
|
||
Name: str = item[:eq_pos].strip()
|
||
Value: str | None = item[eq_pos + 1:].strip()
|
||
else:
|
||
Name = item.strip()
|
||
Value = None
|
||
if Name and _RE_ENUM_NAME.match(Name):
|
||
values.append({'name': Name, 'value': Value})
|
||
return values
|
||
|
||
def _ParseFuncParams(self, params_str: str) -> list[dict[str, Any]]:
|
||
params: list[dict[str, Any]] = []
|
||
if not params_str or params_str.strip() == 'void' or params_str.strip() == '':
|
||
return params
|
||
for param in params_str.split(','):
|
||
param = param.strip()
|
||
if not param:
|
||
continue
|
||
func_ptr_match: re.Match[str] | None = _RE_FUNC_PTR_PARAM.match(param)
|
||
if func_ptr_match:
|
||
RetType: str = func_ptr_match.group(1).strip()
|
||
ParamName: str = func_ptr_match.group(2) or 'callback'
|
||
params.append({
|
||
'name': ParamName,
|
||
'type': RetType + ' (*)',
|
||
'is_func_ptr': True,
|
||
})
|
||
continue
|
||
arr_match: re.Match[str] | None = _RE_ARRAY_PARAM.match(param)
|
||
if arr_match:
|
||
ParamType: str = arr_match.group(1).strip()
|
||
ParamName: str = arr_match.group(2)
|
||
params.append({
|
||
'name': ParamName,
|
||
'type': ParamType,
|
||
'is_array': True,
|
||
})
|
||
continue
|
||
tokens: list[str] = param.split()
|
||
if len(tokens) >= 2:
|
||
LastToken: str = tokens[-1]
|
||
PtrCount: int = 0
|
||
while LastToken.startswith('*'):
|
||
PtrCount += 1
|
||
LastToken = LastToken[1:]
|
||
TypeTokens: list[str] = tokens[:-1]
|
||
for _ in range(PtrCount):
|
||
TypeTokens.append('*')
|
||
ParamName: str = LastToken
|
||
ParamType: str = ' '.join(TypeTokens)
|
||
if not ParamName:
|
||
ParamName = 'arg'
|
||
ParamType = param
|
||
params.append({
|
||
'name': ParamName,
|
||
'type': ParamType,
|
||
})
|
||
elif len(tokens) == 1:
|
||
params.append({
|
||
'name': 'arg',
|
||
'type': tokens[0],
|
||
})
|
||
return params
|
||
|
||
|
||
class PythonStubGenerator:
|
||
"""Python 存根文件生成器"""
|
||
|
||
def __init__(self, parser: CHeaderParser) -> None:
|
||
self.parser: CHeaderParser = parser
|
||
self.OutputLines: list[str] = []
|
||
|
||
def generate(self, ModuleName: str | None = None) -> str:
|
||
self.OutputLines = []
|
||
self._AddHeader(ModuleName)
|
||
# stub 头部只放模块文档;内建标记按裸名使用,无需 import。
|
||
self._GenerateDefines()
|
||
self._GenerateFuncPtrTypedefs()
|
||
self._GenerateSimpleTypedefs()
|
||
self._GenerateStructs()
|
||
self._GenerateUnions()
|
||
self._GenerateEnums()
|
||
self._GenerateVariables()
|
||
self._GenerateFunctions()
|
||
return '\n'.join(self.OutputLines)
|
||
|
||
def _AddHeader(self, ModuleName: str | None = None) -> None:
|
||
self.OutputLines.append('"""')
|
||
self.OutputLines.append('Auto-generated Python stub file from C header')
|
||
if ModuleName:
|
||
self.OutputLines.append(f'Module: {ModuleName}')
|
||
self.OutputLines.append('"""')
|
||
self.OutputLines.append('')
|
||
|
||
def _GenerateDefines(self) -> None:
|
||
if not self.parser.defines:
|
||
return
|
||
for define in self.parser.defines:
|
||
Name: str = define['name']
|
||
Value: str | None = define['value']
|
||
if Value is not None:
|
||
self.OutputLines.append(f'define {Name} = {Value}')
|
||
else:
|
||
# 新语法要求 define 带初值;无值宏(#define FOO)以 0 占位
|
||
self.OutputLines.append(f'define {Name} = 0')
|
||
self.OutputLines.append('')
|
||
|
||
def _GenerateFuncPtrTypedefs(self) -> None:
|
||
if not self.parser.func_ptr_typedefs:
|
||
return
|
||
for Name, Info in self.parser.func_ptr_typedefs.items():
|
||
RetType: str = CTypeMapper.MapType(Info['return_type'])
|
||
ParamTypes: list[str] = []
|
||
for p in Info['params']:
|
||
ParamTypes.append(CTypeMapper.MapType(p['type']))
|
||
ParamStr: str = ', '.join(ParamTypes) if ParamTypes else ''
|
||
self.OutputLines.append(f'typedef {Name} = callable[[{ParamStr}], {RetType}]')
|
||
self.OutputLines.append('')
|
||
|
||
def _GenerateSimpleTypedefs(self) -> None:
|
||
if not self.parser.typedefs:
|
||
return
|
||
for td in self.parser.typedefs:
|
||
AliasName: str = td['name']
|
||
BaseType: str = td['base']
|
||
PyType: str = CTypeMapper.MapType(BaseType)
|
||
self.OutputLines.append(f'typedef {AliasName} = {PyType}')
|
||
self.OutputLines.append('')
|
||
|
||
def _GenerateStructs(self) -> None:
|
||
if not self.parser.structs:
|
||
return
|
||
for StructName, StructInfo in self.parser.structs.items():
|
||
self.OutputLines.append(f'@Attribute(attr.packed)')
|
||
self.OutputLines.append(f'class {StructName}(CStruct):')
|
||
if StructInfo['members']:
|
||
for member in StructInfo['members']:
|
||
MemberName: str = member['name']
|
||
IsPtr: bool = member.get('IsPtr', False)
|
||
IsArray: bool = member.get('is_array', False)
|
||
ArraySize: int | None = member.get('array_size')
|
||
PyType: str = CTypeMapper.MapType(member['type'], IsPtr=IsPtr)
|
||
if IsArray and ArraySize is not None:
|
||
self.OutputLines.append(f' {MemberName}: CArray[{PyType}, {ArraySize}]')
|
||
elif IsArray:
|
||
self.OutputLines.append(f' {MemberName}: CArray[{PyType}, None]')
|
||
else:
|
||
self.OutputLines.append(f' {MemberName}: {PyType}')
|
||
else:
|
||
self.OutputLines.append(' pass')
|
||
self.OutputLines.append('')
|
||
|
||
def _GenerateUnions(self) -> None:
|
||
if not self.parser.unions:
|
||
return
|
||
for UnionName, UnionInfo in self.parser.unions.items():
|
||
self.OutputLines.append(f'@Attribute(attr.packed)')
|
||
self.OutputLines.append(f'class {UnionName}(CUnion):')
|
||
if UnionInfo['members']:
|
||
for member in UnionInfo['members']:
|
||
PyType: str = CTypeMapper.MapType(member['type'], IsPtr=member.get('IsPtr', False))
|
||
self.OutputLines.append(f' {member["name"]}: {PyType}')
|
||
else:
|
||
self.OutputLines.append(' pass')
|
||
self.OutputLines.append('')
|
||
|
||
def _GenerateEnums(self) -> None:
|
||
if not self.parser.enums:
|
||
return
|
||
for EnumName, EnumInfo in self.parser.enums.items():
|
||
self.OutputLines.append(f'class __{EnumName.lower()}(CEnum):')
|
||
if EnumInfo['values']:
|
||
for value in EnumInfo['values']:
|
||
if value['value'] is not None:
|
||
self.OutputLines.append(f' {value["name"]}: int = {value["value"]}')
|
||
else:
|
||
self.OutputLines.append(f' {value["name"]}: int')
|
||
else:
|
||
self.OutputLines.append(' pass')
|
||
self.OutputLines.append(f'typedef {EnumName} = __{EnumName.lower()}')
|
||
self.OutputLines.append('')
|
||
|
||
def _GenerateVariables(self) -> None:
|
||
if not self.parser.variables:
|
||
return
|
||
for VarName, VarInfo in self.parser.variables.items():
|
||
PyType: str = CTypeMapper.MapType(VarInfo['type'], IsPtr=VarInfo['IsPtr'])
|
||
if VarInfo['is_array']:
|
||
ArraySize: int | None = VarInfo.get('array_size')
|
||
SizeStr: str = str(ArraySize) if ArraySize is not None else 'None'
|
||
PyType = f'CArray[{PyType}, {SizeStr}]'
|
||
self.OutputLines.append(f'{VarName}: {PyType}')
|
||
self.OutputLines.append('')
|
||
|
||
def _GenerateFunctions(self) -> None:
|
||
if not self.parser.functions:
|
||
return
|
||
for FuncName, FuncInfo in self.parser.functions.items():
|
||
params: list[str] = []
|
||
for param in FuncInfo['params']:
|
||
if param.get('is_func_ptr'):
|
||
PyType: str = CTypeMapper.MapType(param['type'])
|
||
elif param.get('is_array'):
|
||
PyType: str = CTypeMapper.MapType(param['type'], IsPtr=True)
|
||
else:
|
||
PyType: str = CTypeMapper.MapType(param['type'])
|
||
params.append(f'{param["name"]}: {PyType}')
|
||
ParamStr: str = ', '.join(params) if params else ''
|
||
ReturnType: str = CTypeMapper.MapType(FuncInfo['return_type'])
|
||
self.OutputLines.append(f'def {FuncName}({ParamStr}) -> {ReturnType}: pass')
|
||
self.OutputLines.append('')
|
||
|
||
|
||
def GenerateStubFromC(InputFile: str, OutputFile: str | None = None) -> str:
|
||
parser: CHeaderParser = CHeaderParser()
|
||
parser.ParseFile(InputFile)
|
||
generator: PythonStubGenerator = PythonStubGenerator(parser)
|
||
ModuleName: str = os.path.splitext(os.path.basename(InputFile))[0]
|
||
content: str = generator.generate(ModuleName)
|
||
if OutputFile:
|
||
with open(OutputFile, 'w', encoding='utf-8') as f:
|
||
f.write(content)
|
||
_vlog().success(f"生成: {OutputFile}")
|
||
return content
|
||
|
||
|
||
if __name__ == '__main__':
|
||
if len(sys.argv) >= 3:
|
||
GenerateStubFromC(sys.argv[1], sys.argv[2])
|
||
elif len(sys.argv) == 2:
|
||
_vlog().info(GenerateStubFromC(sys.argv[1]))
|
||
else:
|
||
test_code: str = '''
|
||
struct memory_block {
|
||
uint64_t size;
|
||
uint8_t state;
|
||
uint8_t order;
|
||
struct memory_block *next;
|
||
struct memory_block *prev;
|
||
};
|
||
|
||
typedef struct memory_block memory_block_t;
|
||
|
||
enum color_format {
|
||
COLOR_FORMAT_BGRA,
|
||
COLOR_FORMAT_RGBA,
|
||
COLOR_FORMAT_BGR,
|
||
COLOR_FORMAT_RGB
|
||
};
|
||
|
||
void *malloc(size_t size);
|
||
void free(void *ptr);
|
||
void *memcpy(void *dest, const void *src, size_t n);
|
||
'''
|
||
parser: CHeaderParser = CHeaderParser()
|
||
parser.ParseContent(test_code)
|
||
generator: PythonStubGenerator = PythonStubGenerator(parser)
|
||
_vlog().info(generator.generate("test_header"))
|