修复了大量存在的问题,增加了假鸭子类型等等机制
This commit is contained in:
@@ -5,54 +5,83 @@ C/H 文件到 Python 存根文件生成器
|
||||
将 C 头文件 (.h) 或 C 源文件 (.c) 转换为 Python 存根文件 (.pyi)
|
||||
生成的 Python 代码使用 TransPyC 语法,包含类型注解
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import re
|
||||
from typing import List, Dict, Optional
|
||||
import sys
|
||||
from typing import Any
|
||||
from lib.core.VLogger import get_logger as _vlog
|
||||
|
||||
|
||||
class CTypeMapper:
|
||||
|
||||
# 硬编码 C 类型名 → Python 类型映射表
|
||||
# 这是 stub 生成器(C 头文件 → Python stub)专用的固定映射,
|
||||
# 不再依赖运行时 CTypeRegistry._cname_to_class(该映射已删除)。
|
||||
_CNAME_TO_PY: dict[str, str] = {
|
||||
# 基本类型
|
||||
'char': 't.CChar', 'int': 't.CInt', 'short': 't.CShort',
|
||||
'long': 't.CLong', 'long long': 't.CLongLong',
|
||||
'float': 't.CFloat', 'double': 't.CDouble', 'void': 't.CVoid',
|
||||
# unsigned / signed 变体
|
||||
'unsigned': 't.CUnsigned', 'unsigned char': 't.CUnsignedChar',
|
||||
'unsigned int': 't.CUnsignedInt', 'unsigned short': 't.CUnsignedShort',
|
||||
'unsigned long': 't.CUnsignedLong',
|
||||
'unsigned long long': 't.CUnsignedLongLong',
|
||||
'signed char': 't.CSignedChar',
|
||||
# bool / size_t
|
||||
'bool': 't.CBool', '_Bool': 't.CBool', 'size_t': 't.CSizeT',
|
||||
'ssize_t': 't.CLong',
|
||||
# stdint 固定宽度类型
|
||||
'int8_t': 't.CInt8T', 'int16_t': 't.CInt16T',
|
||||
'int32_t': 't.CInt32T', 'int64_t': 't.CInt64T',
|
||||
'uint8_t': 't.CUInt8T', 'uint16_t': 't.CUInt16T',
|
||||
'uint32_t': 't.CUInt32T', 'uint64_t': 't.CUInt64T',
|
||||
'intptr_t': 't.CIntPtrT', 'uintptr_t': 't.CUIntPtrT',
|
||||
'ptrdiff_t': 't.CPtrDiffT',
|
||||
# 宽字符 / 字符类型
|
||||
'wchar_t': 't.CWCharT', 'char8_t': 't.CChar8T',
|
||||
'char16_t': 't.CChar16T', 'char32_t': 't.CChar32T',
|
||||
# 浮点特化类型
|
||||
'float8_t': 't.CFloat8T', 'float16_t': 't.CFloat16T',
|
||||
'float32_t': 't.CFloat32T', 'float64_t': 't.CFloat64T',
|
||||
'float128_t': 't.CFloat128T',
|
||||
# signed 速记
|
||||
'signed': 't.CInt', 'signed int': 't.CInt',
|
||||
'signed short': 't.CShort', 'signed long': 't.CLong',
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def _ResolveCNameToPyType(cls, c_type: str) -> str:
|
||||
from lib.includes.t import CTypeRegistry
|
||||
ctype_cls = CTypeRegistry.CNameToClass(c_type)
|
||||
if ctype_cls is not None:
|
||||
name = ctype_cls.__name__
|
||||
return f't.{name}'
|
||||
resolved = CTypeRegistry.ResolveName(c_type)
|
||||
if resolved is not None:
|
||||
ctype_cls, _ = resolved
|
||||
name = ctype_cls.__name__
|
||||
return f't.{name}'
|
||||
_SPECIAL = {
|
||||
'signed': 't.CInt', 'unsigned': 't.CUnsigned',
|
||||
'signed char': 't.CChar', 'signed short': 't.CShort',
|
||||
'signed int': 't.CInt', 'signed long': 't.CLong',
|
||||
'_Bool': 't.CBool', 'ssize_t': 't.CLong',
|
||||
}
|
||||
if c_type in _SPECIAL:
|
||||
return _SPECIAL[c_type]
|
||||
"""将 C 类型名解析为 Python 类型字符串(如 'int' → 't.CInt')
|
||||
|
||||
使用硬编码映射表,不再依赖 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_type = c_type.strip()
|
||||
|
||||
IsConst = 'const ' in c_type
|
||||
IsConst: bool = 'const ' in c_type
|
||||
c_type = c_type.replace('const ', '').replace('const', '').strip()
|
||||
IsVolatile = 'volatile ' in c_type
|
||||
IsVolatile: bool = 'volatile ' in c_type
|
||||
c_type = c_type.replace('volatile ', '').replace('volatile', '').strip()
|
||||
IsStatic = 'static ' in c_type
|
||||
IsStatic: bool = 'static ' in c_type
|
||||
c_type = c_type.replace('static ', '').replace('static', '').strip()
|
||||
IsExtern = 'extern ' in c_type
|
||||
IsExtern: bool = 'extern ' in c_type
|
||||
c_type = c_type.replace('extern ', '').replace('extern', '').strip()
|
||||
IsRegister = 'register ' in c_type
|
||||
IsRegister: bool = 'register ' in c_type
|
||||
c_type = c_type.replace('register ', '').replace('register', '').strip()
|
||||
IsInline = 'inline ' in c_type
|
||||
IsInline: bool = 'inline ' in c_type
|
||||
c_type = c_type.replace('inline ', '').replace('inline', '').strip()
|
||||
|
||||
PtrSuffix = ''
|
||||
PtrSuffix: str = ''
|
||||
if IsPtr or c_type.endswith('*'):
|
||||
PtrSuffix = ' | t.CPtr'
|
||||
c_type = c_type.rstrip('*').strip()
|
||||
@@ -64,9 +93,9 @@ class CTypeMapper:
|
||||
elif c_type.startswith('enum '):
|
||||
c_type = c_type[5:].strip()
|
||||
|
||||
PyType = cls._ResolveCNameToPyType(c_type)
|
||||
PyType: str = cls._ResolveCNameToPyType(c_type)
|
||||
|
||||
result = PyType + PtrSuffix
|
||||
result: str = PyType + PtrSuffix
|
||||
|
||||
if IsArray:
|
||||
result += ' | t.CArrayPtr'
|
||||
@@ -75,8 +104,8 @@ class CTypeMapper:
|
||||
|
||||
|
||||
def _find_matching_brace(text: str, start: int) -> int:
|
||||
depth = 0
|
||||
i = start
|
||||
depth: int = 0
|
||||
i: int = start
|
||||
while i < len(text):
|
||||
if text[i] == '{':
|
||||
depth += 1
|
||||
@@ -85,7 +114,7 @@ def _find_matching_brace(text: str, start: int) -> int:
|
||||
if depth == 0:
|
||||
return i
|
||||
elif text[i] == '"' or text[i] == "'":
|
||||
quote = text[i]
|
||||
quote: str = text[i]
|
||||
i += 1
|
||||
while i < len(text) and text[i] != quote:
|
||||
if text[i] == '\\':
|
||||
@@ -105,8 +134,8 @@ def _find_matching_brace(text: str, start: int) -> int:
|
||||
|
||||
|
||||
def _strip_comments(content: str) -> str:
|
||||
result = []
|
||||
i = 0
|
||||
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':
|
||||
@@ -117,7 +146,7 @@ def _strip_comments(content: str) -> str:
|
||||
i += 1
|
||||
i += 2
|
||||
elif content[i] == '"' or content[i] == "'":
|
||||
quote = content[i]
|
||||
quote: str = content[i]
|
||||
result.append(content[i])
|
||||
i += 1
|
||||
while i < len(content) and content[i] != quote:
|
||||
@@ -136,10 +165,10 @@ def _strip_comments(content: str) -> str:
|
||||
|
||||
|
||||
def _strip_preprocessor_guards(content: str) -> str:
|
||||
lines = content.split('\n')
|
||||
filtered = []
|
||||
lines: list[str] = content.split('\n')
|
||||
filtered: list[str] = []
|
||||
for line in lines:
|
||||
stripped = line.strip()
|
||||
stripped: str = line.strip()
|
||||
if stripped.startswith('#ifndef') and '_H' in stripped.upper():
|
||||
continue
|
||||
if stripped.startswith('#define') and '_H' in stripped.upper():
|
||||
@@ -153,22 +182,22 @@ def _strip_preprocessor_guards(content: str) -> str:
|
||||
class CHeaderParser:
|
||||
"""C 头文件解析器"""
|
||||
|
||||
def __init__(self):
|
||||
self.defines: List[Dict] = []
|
||||
self.structs: Dict[str, Dict] = {}
|
||||
self.enums: Dict[str, Dict] = {}
|
||||
self.typedefs: Dict[str, str] = []
|
||||
self.functions: Dict[str, Dict] = {}
|
||||
self.variables: Dict[str, Dict] = {}
|
||||
self.unions: Dict[str, Dict] = {}
|
||||
self.func_ptr_typedefs: Dict[str, Dict] = {}
|
||||
self._typedef_struct_map: Dict[str, str] = {}
|
||||
self._typedef_enum_map: Dict[str, str] = {}
|
||||
self._typedef_union_map: Dict[str, str] = {}
|
||||
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 = f.read()
|
||||
content: str = f.read()
|
||||
self.ParseContent(content)
|
||||
|
||||
def ParseContent(self, content: str) -> None:
|
||||
@@ -183,17 +212,17 @@ class CHeaderParser:
|
||||
self._ParseStandaloneStructs(content)
|
||||
self._ParseStandaloneEnums(content)
|
||||
self._ParseStandaloneUnions(content)
|
||||
stripped = self._StripBodies(content)
|
||||
stripped: str = self._StripBodies(content)
|
||||
self._ParseFunctions(stripped)
|
||||
self._ParseVariables(stripped)
|
||||
|
||||
def _StripBodies(self, content: str) -> str:
|
||||
result = content
|
||||
result: str = content
|
||||
while True:
|
||||
pos = result.find('{')
|
||||
pos: int = result.find('{')
|
||||
if pos < 0:
|
||||
break
|
||||
end = _find_matching_brace(result, pos)
|
||||
end: int = _find_matching_brace(result, pos)
|
||||
if end < 0:
|
||||
break
|
||||
result = result[:pos] + result[end + 1:]
|
||||
@@ -201,8 +230,8 @@ class CHeaderParser:
|
||||
|
||||
def _ParseDefines(self, content: str) -> None:
|
||||
for match in re.finditer(r'#define\s+(\w+)(?:\s+(.+?))?\s*$', content, re.MULTILINE):
|
||||
Name = match.group(1)
|
||||
Value = match.group(2)
|
||||
Name: str = match.group(1)
|
||||
Value: str | None = match.group(2)
|
||||
if Name.startswith('_') and Name.endswith('_'):
|
||||
continue
|
||||
if Name.startswith('__'):
|
||||
@@ -213,20 +242,20 @@ class CHeaderParser:
|
||||
})
|
||||
|
||||
def _ParseTypedefStructs(self, content: str) -> None:
|
||||
pattern = r'typedef\s+struct\s*(\w*)\s*\{'
|
||||
pattern: str = r'typedef\s+struct\s*(\w*)\s*\{'
|
||||
for match in re.finditer(pattern, content):
|
||||
brace_start = content.index('{', match.start())
|
||||
brace_end = _find_matching_brace(content, brace_start)
|
||||
brace_start: int = content.index('{', match.start())
|
||||
brace_end: int = _find_matching_brace(content, brace_start)
|
||||
if brace_end < 0:
|
||||
continue
|
||||
body = content[brace_start + 1:brace_end]
|
||||
rest = content[brace_end + 1:].lstrip()
|
||||
AliasMatch = re.match(r'(\w+)\s*;', rest)
|
||||
body: str = content[brace_start + 1:brace_end]
|
||||
rest: str = content[brace_end + 1:].lstrip()
|
||||
AliasMatch: re.Match[str] | None = re.match(r'(\w+)\s*;', rest)
|
||||
if not AliasMatch:
|
||||
continue
|
||||
AliasName = AliasMatch.group(1)
|
||||
TagName = match.group(1) if match.group(1) else None
|
||||
members = self._ParseStructMembers(body)
|
||||
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,
|
||||
@@ -236,20 +265,20 @@ class CHeaderParser:
|
||||
self._typedef_struct_map[TagName] = AliasName
|
||||
|
||||
def _ParseTypedefEnums(self, content: str) -> None:
|
||||
pattern = r'typedef\s+enum\s*(\w*)\s*\{'
|
||||
pattern: str = r'typedef\s+enum\s*(\w*)\s*\{'
|
||||
for match in re.finditer(pattern, content):
|
||||
brace_start = content.index('{', match.start())
|
||||
brace_end = _find_matching_brace(content, brace_start)
|
||||
brace_start: int = content.index('{', match.start())
|
||||
brace_end: int = _find_matching_brace(content, brace_start)
|
||||
if brace_end < 0:
|
||||
continue
|
||||
body = content[brace_start + 1:brace_end]
|
||||
rest = content[brace_end + 1:].lstrip()
|
||||
AliasMatch = re.match(r'(\w+)\s*;', rest)
|
||||
body: str = content[brace_start + 1:brace_end]
|
||||
rest: str = content[brace_end + 1:].lstrip()
|
||||
AliasMatch: re.Match[str] | None = re.match(r'(\w+)\s*;', rest)
|
||||
if not AliasMatch:
|
||||
continue
|
||||
AliasName = AliasMatch.group(1)
|
||||
TagName = match.group(1) if match.group(1) else None
|
||||
values = self._ParseEnumValues(body)
|
||||
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,
|
||||
@@ -259,20 +288,20 @@ class CHeaderParser:
|
||||
self._typedef_enum_map[TagName] = AliasName
|
||||
|
||||
def _ParseTypedefUnions(self, content: str) -> None:
|
||||
pattern = r'typedef\s+union\s*(\w*)\s*\{'
|
||||
pattern: str = r'typedef\s+union\s*(\w*)\s*\{'
|
||||
for match in re.finditer(pattern, content):
|
||||
brace_start = content.index('{', match.start())
|
||||
brace_end = _find_matching_brace(content, brace_start)
|
||||
brace_start: int = content.index('{', match.start())
|
||||
brace_end: int = _find_matching_brace(content, brace_start)
|
||||
if brace_end < 0:
|
||||
continue
|
||||
body = content[brace_start + 1:brace_end]
|
||||
rest = content[brace_end + 1:].lstrip()
|
||||
AliasMatch = re.match(r'(\w+)\s*;', rest)
|
||||
body: str = content[brace_start + 1:brace_end]
|
||||
rest: str = content[brace_end + 1:].lstrip()
|
||||
AliasMatch: re.Match[str] | None = re.match(r'(\w+)\s*;', rest)
|
||||
if not AliasMatch:
|
||||
continue
|
||||
AliasName = AliasMatch.group(1)
|
||||
TagName = match.group(1) if match.group(1) else None
|
||||
members = self._ParseStructMembers(body)
|
||||
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,
|
||||
@@ -282,12 +311,12 @@ class CHeaderParser:
|
||||
self._typedef_union_map[TagName] = AliasName
|
||||
|
||||
def _ParseFuncPtrTypedefs(self, content: str) -> None:
|
||||
pattern = r'typedef\s+([\w\s\*]+?)\s*\(\s*\*\s*(\w+)\s*\)\s*\(([^)]*)\)\s*;'
|
||||
pattern: str = r'typedef\s+([\w\s\*]+?)\s*\(\s*\*\s*(\w+)\s*\)\s*\(([^)]*)\)\s*;'
|
||||
for match in re.finditer(pattern, content):
|
||||
ReturnType = match.group(1).strip()
|
||||
Name = match.group(2)
|
||||
ParamsStr = match.group(3).strip()
|
||||
params = self._ParseFuncParams(ParamsStr)
|
||||
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,
|
||||
@@ -295,11 +324,11 @@ class CHeaderParser:
|
||||
}
|
||||
|
||||
def _ParseSimpleTypedefs(self, content: str) -> None:
|
||||
stripped = self._StripBodies(content)
|
||||
pattern = r'typedef\s+([\w\s\*]+?)\s+(\w+)\s*;'
|
||||
stripped: str = self._StripBodies(content)
|
||||
pattern: str = r'typedef\s+([\w\s\*]+?)\s+(\w+)\s*;'
|
||||
for match in re.finditer(pattern, stripped):
|
||||
BaseType = match.group(1).strip()
|
||||
AliasName = match.group(2)
|
||||
BaseType: str = match.group(1).strip()
|
||||
AliasName: str = match.group(2)
|
||||
if AliasName in self.structs:
|
||||
continue
|
||||
if AliasName in self.enums:
|
||||
@@ -313,19 +342,19 @@ class CHeaderParser:
|
||||
self.typedefs.append({'name': AliasName, 'base': BaseType})
|
||||
|
||||
def _ParseStandaloneStructs(self, content: str) -> None:
|
||||
pattern = r'(?<!typedef\s)struct\s+(\w+)\s*\{'
|
||||
pattern: str = r'(?<!typedef\s)struct\s+(\w+)\s*\{'
|
||||
for match in re.finditer(pattern, content):
|
||||
TagName = match.group(1)
|
||||
TagName: str = match.group(1)
|
||||
if TagName in self._typedef_struct_map:
|
||||
continue
|
||||
if TagName in self.structs:
|
||||
continue
|
||||
brace_start = content.index('{', match.start())
|
||||
brace_end = _find_matching_brace(content, brace_start)
|
||||
brace_start: int = content.index('{', match.start())
|
||||
brace_end: int = _find_matching_brace(content, brace_start)
|
||||
if brace_end < 0:
|
||||
continue
|
||||
body = content[brace_start + 1:brace_end]
|
||||
members = self._ParseStructMembers(body)
|
||||
body: str = content[brace_start + 1:brace_end]
|
||||
members: list[dict[str, Any]] = self._ParseStructMembers(body)
|
||||
self.structs[TagName] = {
|
||||
'name': TagName,
|
||||
'tag': None,
|
||||
@@ -333,19 +362,19 @@ class CHeaderParser:
|
||||
}
|
||||
|
||||
def _ParseStandaloneEnums(self, content: str) -> None:
|
||||
pattern = r'(?<!typedef\s)enum\s+(\w+)\s*\{'
|
||||
pattern: str = r'(?<!typedef\s)enum\s+(\w+)\s*\{'
|
||||
for match in re.finditer(pattern, content):
|
||||
TagName = match.group(1)
|
||||
TagName: str = match.group(1)
|
||||
if TagName in self._typedef_enum_map:
|
||||
continue
|
||||
if TagName in self.enums:
|
||||
continue
|
||||
brace_start = content.index('{', match.start())
|
||||
brace_end = _find_matching_brace(content, brace_start)
|
||||
brace_start: int = content.index('{', match.start())
|
||||
brace_end: int = _find_matching_brace(content, brace_start)
|
||||
if brace_end < 0:
|
||||
continue
|
||||
body = content[brace_start + 1:brace_end]
|
||||
values = self._ParseEnumValues(body)
|
||||
body: str = content[brace_start + 1:brace_end]
|
||||
values: list[dict[str, str | None]] = self._ParseEnumValues(body)
|
||||
self.enums[TagName] = {
|
||||
'name': TagName,
|
||||
'tag': None,
|
||||
@@ -353,19 +382,19 @@ class CHeaderParser:
|
||||
}
|
||||
|
||||
def _ParseStandaloneUnions(self, content: str) -> None:
|
||||
pattern = r'(?<!typedef\s)union\s+(\w+)\s*\{'
|
||||
pattern: str = r'(?<!typedef\s)union\s+(\w+)\s*\{'
|
||||
for match in re.finditer(pattern, content):
|
||||
TagName = match.group(1)
|
||||
TagName: str = match.group(1)
|
||||
if TagName in self._typedef_union_map:
|
||||
continue
|
||||
if TagName in self.unions:
|
||||
continue
|
||||
brace_start = content.index('{', match.start())
|
||||
brace_end = _find_matching_brace(content, brace_start)
|
||||
brace_start: int = content.index('{', match.start())
|
||||
brace_end: int = _find_matching_brace(content, brace_start)
|
||||
if brace_end < 0:
|
||||
continue
|
||||
body = content[brace_start + 1:brace_end]
|
||||
members = self._ParseStructMembers(body)
|
||||
body: str = content[brace_start + 1:brace_end]
|
||||
members: list[dict[str, Any]] = self._ParseStructMembers(body)
|
||||
self.unions[TagName] = {
|
||||
'name': TagName,
|
||||
'tag': None,
|
||||
@@ -373,11 +402,11 @@ class CHeaderParser:
|
||||
}
|
||||
|
||||
def _ParseFunctions(self, content: str) -> None:
|
||||
pattern = r'(?:(?:static|extern|inline)\s+)*([\w][\w\s\*]*?)\b(\w+)\s*\(([^)]*)\)\s*;'
|
||||
pattern: str = r'(?:(?:static|extern|inline)\s+)*([\w][\w\s\*]*?)\b(\w+)\s*\(([^)]*)\)\s*;'
|
||||
for match in re.finditer(pattern, content):
|
||||
ReturnType = match.group(1).strip()
|
||||
FuncName = match.group(2)
|
||||
ParamsStr = match.group(3).strip()
|
||||
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:
|
||||
@@ -390,7 +419,7 @@ class CHeaderParser:
|
||||
continue
|
||||
if not re.match(r'^[A-Za-z_]', FuncName):
|
||||
continue
|
||||
params = self._ParseFuncParams(ParamsStr)
|
||||
params: list[dict[str, Any]] = self._ParseFuncParams(ParamsStr)
|
||||
self.functions[FuncName] = {
|
||||
'name': FuncName,
|
||||
'return_type': ReturnType,
|
||||
@@ -398,11 +427,11 @@ class CHeaderParser:
|
||||
}
|
||||
|
||||
def _ParseVariables(self, content: str) -> None:
|
||||
pattern = r'(?:(?:static|extern|volatile|const)\s+)*([\w][\w\s\*]*?)\s+(\w+)\s*(?:\[(\d*)\])?\s*;'
|
||||
pattern: str = r'(?:(?:static|extern|volatile|const)\s+)*([\w][\w\s\*]*?)\s+(\w+)\s*(?:\[(\d*)\])?\s*;'
|
||||
for match in re.finditer(pattern, content):
|
||||
TypeStr = match.group(1).strip()
|
||||
VarName = match.group(2)
|
||||
ArraySize = match.group(3)
|
||||
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:
|
||||
@@ -417,8 +446,8 @@ class CHeaderParser:
|
||||
continue
|
||||
if not re.match(r'^[A-Za-z_]', VarName):
|
||||
continue
|
||||
IsPtr = '*' in TypeStr
|
||||
IsArray = ArraySize is not None
|
||||
IsPtr: bool = '*' in TypeStr
|
||||
IsArray: bool = ArraySize is not None
|
||||
self.variables[VarName] = {
|
||||
'name': VarName,
|
||||
'type': TypeStr,
|
||||
@@ -427,17 +456,17 @@ class CHeaderParser:
|
||||
'array_size': int(ArraySize) if ArraySize and ArraySize.isdigit() else None,
|
||||
}
|
||||
|
||||
def _ParseStructMembers(self, body: str) -> List[Dict]:
|
||||
members = []
|
||||
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(r'([\w\s\*]+?)\s+(\w+)\s*\[(\d*)\]', line)
|
||||
arr_match: re.Match[str] | None = re.match(r'([\w\s\*]+?)\s+(\w+)\s*\[(\d*)\]', line)
|
||||
if arr_match:
|
||||
MemberType = arr_match.group(1).strip()
|
||||
MemberName = arr_match.group(2)
|
||||
ArraySize = arr_match.group(3)
|
||||
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,
|
||||
@@ -445,36 +474,36 @@ class CHeaderParser:
|
||||
'array_size': int(ArraySize) if ArraySize and ArraySize.isdigit() else None,
|
||||
})
|
||||
continue
|
||||
ptr_match = re.match(r'([\w\s\*]+?)\s*\*\s*(\w+)', line)
|
||||
ptr_match: re.Match[str] | None = re.match(r'([\w\s\*]+?)\s*\*\s*(\w+)', line)
|
||||
if ptr_match:
|
||||
MemberType = ptr_match.group(1).strip() + ' *'
|
||||
MemberName = ptr_match.group(2)
|
||||
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(r'([\w\s]+?)\s+(\w+)', line)
|
||||
simple_match: re.Match[str] | None = re.match(r'([\w\s]+?)\s+(\w+)', line)
|
||||
if simple_match:
|
||||
MemberType = simple_match.group(1).strip()
|
||||
MemberName = simple_match.group(2)
|
||||
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]:
|
||||
values = []
|
||||
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 = item.find('=')
|
||||
eq_pos: int = item.find('=')
|
||||
if eq_pos >= 0:
|
||||
Name = item[:eq_pos].strip()
|
||||
Value = item[eq_pos + 1:].strip()
|
||||
Name: str = item[:eq_pos].strip()
|
||||
Value: str | None = item[eq_pos + 1:].strip()
|
||||
else:
|
||||
Name = item.strip()
|
||||
Value = None
|
||||
@@ -482,46 +511,46 @@ class CHeaderParser:
|
||||
values.append({'name': Name, 'value': Value})
|
||||
return values
|
||||
|
||||
def _ParseFuncParams(self, params_str: str) -> List[Dict]:
|
||||
params = []
|
||||
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(r'([\w\s\*]+?)\s*\(\s*\*\s*(\w*)\s*\)\s*\([^)]*\)', param)
|
||||
func_ptr_match: re.Match[str] | None = re.match(r'([\w\s\*]+?)\s*\(\s*\*\s*(\w*)\s*\)\s*\([^)]*\)', param)
|
||||
if func_ptr_match:
|
||||
RetType = func_ptr_match.group(1).strip()
|
||||
ParamName = func_ptr_match.group(2) or 'callback'
|
||||
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(r'([\w\s\*]+?)\s+(\w+)\s*\[\d*\]', param)
|
||||
arr_match: re.Match[str] | None = re.match(r'([\w\s\*]+?)\s+(\w+)\s*\[\d*\]', param)
|
||||
if arr_match:
|
||||
ParamType = arr_match.group(1).strip()
|
||||
ParamName = arr_match.group(2)
|
||||
ParamType: str = arr_match.group(1).strip()
|
||||
ParamName: str = arr_match.group(2)
|
||||
params.append({
|
||||
'name': ParamName,
|
||||
'type': ParamType,
|
||||
'is_array': True,
|
||||
})
|
||||
continue
|
||||
tokens = param.split()
|
||||
tokens: list[str] = param.split()
|
||||
if len(tokens) >= 2:
|
||||
LastToken = tokens[-1]
|
||||
PtrCount = 0
|
||||
LastToken: str = tokens[-1]
|
||||
PtrCount: int = 0
|
||||
while LastToken.startswith('*'):
|
||||
PtrCount += 1
|
||||
LastToken = LastToken[1:]
|
||||
TypeTokens = tokens[:-1]
|
||||
TypeTokens: list[str] = tokens[:-1]
|
||||
for _ in range(PtrCount):
|
||||
TypeTokens.append('*')
|
||||
ParamName = LastToken
|
||||
ParamType = ' '.join(TypeTokens)
|
||||
ParamName: str = LastToken
|
||||
ParamType: str = ' '.join(TypeTokens)
|
||||
if not ParamName:
|
||||
ParamName = 'arg'
|
||||
ParamType = param
|
||||
@@ -540,11 +569,11 @@ class CHeaderParser:
|
||||
class PythonStubGenerator:
|
||||
"""Python 存根文件生成器"""
|
||||
|
||||
def __init__(self, parser: CHeaderParser):
|
||||
self.parser = parser
|
||||
self.OutputLines: List[str] = []
|
||||
def __init__(self, parser: CHeaderParser) -> None:
|
||||
self.parser: CHeaderParser = parser
|
||||
self.OutputLines: list[str] = []
|
||||
|
||||
def generate(self, ModuleName: Optional[str] = None) -> str:
|
||||
def generate(self, ModuleName: str | None = None) -> str:
|
||||
self.OutputLines = []
|
||||
self._AddHeader(ModuleName)
|
||||
self._GenerateImports()
|
||||
@@ -558,7 +587,7 @@ class PythonStubGenerator:
|
||||
self._GenerateFunctions()
|
||||
return '\n'.join(self.OutputLines)
|
||||
|
||||
def _AddHeader(self, ModuleName: Optional[str] = None) -> None:
|
||||
def _AddHeader(self, ModuleName: str | None = None) -> None:
|
||||
self.OutputLines.append('"""')
|
||||
self.OutputLines.append('Auto-generated Python stub file from C header')
|
||||
if ModuleName:
|
||||
@@ -575,8 +604,8 @@ class PythonStubGenerator:
|
||||
if not self.parser.defines:
|
||||
return
|
||||
for define in self.parser.defines:
|
||||
Name = define['name']
|
||||
Value = define['value']
|
||||
Name: str = define['name']
|
||||
Value: str | None = define['value']
|
||||
if Value is not None:
|
||||
self.OutputLines.append(f'{Name}: t.CDefine = {Value}')
|
||||
else:
|
||||
@@ -587,11 +616,11 @@ class PythonStubGenerator:
|
||||
if not self.parser.func_ptr_typedefs:
|
||||
return
|
||||
for Name, Info in self.parser.func_ptr_typedefs.items():
|
||||
RetType = CTypeMapper.MapType(Info['return_type'])
|
||||
ParamTypes = []
|
||||
RetType: str = CTypeMapper.MapType(Info['return_type'])
|
||||
ParamTypes: list[str] = []
|
||||
for p in Info['params']:
|
||||
ParamTypes.append(CTypeMapper.MapType(p['type']))
|
||||
ParamStr = ', '.join(ParamTypes) if ParamTypes else ''
|
||||
ParamStr: str = ', '.join(ParamTypes) if ParamTypes else ''
|
||||
self.OutputLines.append(f'{Name}: t.CTypedef = t.Callable[[{ParamStr}], {RetType}]')
|
||||
self.OutputLines.append('')
|
||||
|
||||
@@ -599,9 +628,9 @@ class PythonStubGenerator:
|
||||
if not self.parser.typedefs:
|
||||
return
|
||||
for td in self.parser.typedefs:
|
||||
AliasName = td['name']
|
||||
BaseType = td['base']
|
||||
PyType = CTypeMapper.MapType(BaseType)
|
||||
AliasName: str = td['name']
|
||||
BaseType: str = td['base']
|
||||
PyType: str = CTypeMapper.MapType(BaseType)
|
||||
self.OutputLines.append(f'{AliasName}: t.CTypedef | {PyType}')
|
||||
self.OutputLines.append('')
|
||||
|
||||
@@ -613,11 +642,11 @@ class PythonStubGenerator:
|
||||
self.OutputLines.append(f'class {StructName}(t.CStruct):')
|
||||
if StructInfo['members']:
|
||||
for member in StructInfo['members']:
|
||||
MemberName = member['name']
|
||||
IsPtr = member.get('IsPtr', False)
|
||||
IsArray = member.get('is_array', False)
|
||||
ArraySize = member.get('array_size')
|
||||
PyType = CTypeMapper.MapType(member['type'], IsPtr=IsPtr)
|
||||
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}: list[{PyType}, {ArraySize}]')
|
||||
elif IsArray:
|
||||
@@ -636,7 +665,7 @@ class PythonStubGenerator:
|
||||
self.OutputLines.append(f'class {UnionName}(t.CUnion):')
|
||||
if UnionInfo['members']:
|
||||
for member in UnionInfo['members']:
|
||||
PyType = CTypeMapper.MapType(member['type'], IsPtr=member.get('IsPtr', False))
|
||||
PyType: str = CTypeMapper.MapType(member['type'], IsPtr=member.get('IsPtr', False))
|
||||
self.OutputLines.append(f' {member["name"]}: {PyType}')
|
||||
else:
|
||||
self.OutputLines.append(' pass')
|
||||
@@ -662,7 +691,7 @@ class PythonStubGenerator:
|
||||
if not self.parser.variables:
|
||||
return
|
||||
for VarName, VarInfo in self.parser.variables.items():
|
||||
PyType = CTypeMapper.MapType(VarInfo['type'], IsPtr=VarInfo['IsPtr'], IsArray=VarInfo['is_array'])
|
||||
PyType: str = CTypeMapper.MapType(VarInfo['type'], IsPtr=VarInfo['IsPtr'], IsArray=VarInfo['is_array'])
|
||||
self.OutputLines.append(f'{VarName}: {PyType}')
|
||||
self.OutputLines.append('')
|
||||
|
||||
@@ -670,42 +699,41 @@ class PythonStubGenerator:
|
||||
if not self.parser.functions:
|
||||
return
|
||||
for FuncName, FuncInfo in self.parser.functions.items():
|
||||
params = []
|
||||
params: list[str] = []
|
||||
for param in FuncInfo['params']:
|
||||
if param.get('is_func_ptr'):
|
||||
PyType = CTypeMapper.MapType(param['type'])
|
||||
PyType: str = CTypeMapper.MapType(param['type'])
|
||||
elif param.get('is_array'):
|
||||
PyType = CTypeMapper.MapType(param['type'], IsPtr=True)
|
||||
PyType: str = CTypeMapper.MapType(param['type'], IsPtr=True)
|
||||
else:
|
||||
PyType = CTypeMapper.MapType(param['type'])
|
||||
PyType: str = CTypeMapper.MapType(param['type'])
|
||||
params.append(f'{param["name"]}: {PyType}')
|
||||
ParamStr = ', '.join(params) if params else ''
|
||||
ReturnType = CTypeMapper.MapType(FuncInfo['return_type'])
|
||||
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: Optional[str] = None) -> str:
|
||||
parser = CHeaderParser()
|
||||
def GenerateStubFromC(InputFile: str, OutputFile: str | None = None) -> str:
|
||||
parser: CHeaderParser = CHeaderParser()
|
||||
parser.ParseFile(InputFile)
|
||||
generator = PythonStubGenerator(parser)
|
||||
ModuleName = os.path.splitext(os.path.basename(InputFile))[0]
|
||||
content = generator.generate(ModuleName)
|
||||
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)
|
||||
print(f"Generated: {OutputFile}")
|
||||
_vlog().success(f"生成: {OutputFile}")
|
||||
return content
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
if len(sys.argv) >= 3:
|
||||
GenerateStubFromC(sys.argv[1], sys.argv[2])
|
||||
elif len(sys.argv) == 2:
|
||||
print(GenerateStubFromC(sys.argv[1]))
|
||||
_vlog().info(GenerateStubFromC(sys.argv[1]))
|
||||
else:
|
||||
test_code = '''
|
||||
test_code: str = '''
|
||||
struct memory_block {
|
||||
uint64_t size;
|
||||
uint8_t state;
|
||||
@@ -727,7 +755,7 @@ void *malloc(size_t size);
|
||||
void free(void *ptr);
|
||||
void *memcpy(void *dest, const void *src, size_t n);
|
||||
'''
|
||||
parser = CHeaderParser()
|
||||
parser: CHeaderParser = CHeaderParser()
|
||||
parser.ParseContent(test_code)
|
||||
generator = PythonStubGenerator(parser)
|
||||
print(generator.generate("test_header"))
|
||||
generator: PythonStubGenerator = PythonStubGenerator(parser)
|
||||
_vlog().info(generator.generate("test_header"))
|
||||
|
||||
Reference in New Issue
Block a user