Files
TransPyC/lib/core/stub_generator.py
T
TermiNexus 0bded58999 refactor(type): 全面清理旧联合与 t/c 标记判断
审计并修复 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 输出统一新语法。
2026-10-18 18:01:20 +08:00

788 lines
33 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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"))