improve ffigen 2

This commit is contained in:
blueloveTH 2025-12-17 00:03:48 +08:00
parent 671ea14c4f
commit 13594f9bfe
7 changed files with 59 additions and 57 deletions

View File

@ -165,9 +165,8 @@ def set_vmath_converter(T: str, py_T: str):
def has_vmath_converter() -> bool:
return _has_vmath_converter
def set_enum_converters(enums: list[str]):
for T in enums:
_CONVERTERS[T] = EnumConverter(T)
def set_enum_converter(T: str):
_CONVERTERS[T] = EnumConverter(T)
def get_converter(T: str) -> Converter:
if T in _CONVERTERS:

View File

@ -3,7 +3,7 @@ from .writer import Writer
from .enum import gen_enum
from .struct import gen_struct
from .function import gen_function
from .converters import is_vmath_type, has_vmath_converter
from .converters import is_vmath_type, has_vmath_converter, set_enum_converter
from typing import TYPE_CHECKING
if TYPE_CHECKING:
@ -31,6 +31,11 @@ class Library:
def build(self, *, glue_dir='.', stub_dir='.', includes: list[str] | None = None):
self.remove_unsupported()
for k in self.aliases.keys():
node = self.unalias(k)
if isinstance(node, c_ast.Enum):
set_enum_converter(k)
w, pyi_w = Writer(), Writer()
if has_vmath_converter():
@ -50,15 +55,6 @@ class Library:
w.write('')
w.write('#define ADD_ENUM(name) py_newint(py_emplacedict(mod, py_name(#name)), name)')
w.write('')
w.write('static bool struct__address__(int argc, py_Ref argv) {')
w.indent()
w.write('PY_CHECK_ARGC(1);')
w.write('void* ud = py_touserdata(argv);')
w.write('py_newint(py_retval(), (py_i64)ud);')
w.write('return true;')
w.dedent()
w.write('}')
w.write('')
for k in self.aliases.keys():
node = self.unalias(k)
@ -135,6 +131,7 @@ class Library:
continue
self.structs.append(Struct(
name=struct['name'],
typedef_name=struct['name'],
desc=struct['description'],
fields=[StructField(
type=field['type'],
@ -182,6 +179,7 @@ class Library:
assert fields is not None
self.structs.append(Struct(
name=type.name,
typedef_name=type.typedef_name,
fields=[StructField(
type=field_type,
name=field_name

View File

@ -31,13 +31,13 @@ class Header:
return
self.type_aliases[k] = v
def build_enum(self, node: c_ast.Enum):
def build_enum(self, node: c_ast.Enum, alias_name: str | None = None):
enum = Enum(node.name)
for item in node.values.enumerators:
enum.values.append(item.name)
self.types.append(enum)
def build_struct(self, node):
def build_struct(self, node: c_ast.Struct | c_ast.Union, alias_name: str | None = None):
if isinstance(node, c_ast.Struct):
cls = Struct
elif isinstance(node, c_ast.Union):
@ -53,17 +53,17 @@ class Header:
fields[name] = type
except UnsupportedNode:
pass
self.types.append(cls(node.name, fields))
self.types.append(cls(node.name, fields, alias_name))
else:
self.types.append(cls(node.name, None))
self.types.append(cls(node.name, None, alias_name))
def build_type(self, node, alias_name):
if isinstance(node, c_ast.Enum):
self.build_enum(node)
self.build_enum(node, alias_name)
if alias_name:
self.add_type_alias(alias_name, node)
elif isinstance(node, (c_ast.Struct, c_ast.Union)):
self.build_struct(node)
self.build_struct(node, alias_name)
if alias_name:
self.add_type_alias(alias_name, node)
elif isinstance(node, c_ast.IdentifierType):
@ -132,6 +132,9 @@ class Header:
assert isinstance(node, c_ast.TypeDecl), type(node)
name = node.declname
ret = node.type.names[0]
is_const = node.quals and 'const' in node.quals
if is_const:
ret = 'const ' + ret
func = Function(name, ret + '*' * level)
if args is not None:
for param in args.params:

View File

@ -7,9 +7,10 @@ class Pointer:
self.level = level
class NamedFields:
def __init__(self, name: str, fields: dict[str, str] | None):
def __init__(self, name: str, fields: dict[str, str] | None, typedef_name: str | None = None):
self.name = name
self.fields = fields
self.typedef_name = typedef_name
def is_opaque(self):
return self.fields is None

View File

@ -15,10 +15,25 @@ class EnumValue:
@dataclass
class Struct:
name: str
name: str | None = None
typedef_name: str | None = None
desc: str | None = None
fields: list[StructField] | None = None
@property
def code_name(self):
if self.typedef_name:
return self.typedef_name
assert self.name is not None
return f'struct {self.name}'
@property
def identifier(self):
if self.name:
return self.name
assert self.typedef_name is not None
return self.typedef_name
@dataclass
class Enum:
name: str

View File

@ -2,21 +2,21 @@ from .writer import Writer
from .converters import get_converter, Converter
from .schema import Struct, StructField
def gen_getter(w: Writer, name: str, cvt: Converter, field: StructField):
w.write(f'static bool {name}__get_{field.name}(int argc, py_Ref argv) {{')
def gen_getter(w: Writer, struct: Struct, cvt: Converter, field: StructField):
w.write(f'static bool {struct.identifier}__get_{field.name}(int argc, py_Ref argv) {{')
w.indent()
w.write(f'PY_CHECK_ARGC(1);')
w.write(f'{name}* self = py_touserdata(argv);')
w.write(f'{struct.code_name}* self = py_touserdata(argv);')
cvt.c2py(w, 'py_retval()', f'self->{field.name}')
w.write('return true;')
w.dedent()
w.write('}')
def gen_setter(w: Writer, name: str, cvt: Converter, field: StructField):
w.write(f'static bool {name}__set_{field.name}(int argc, py_Ref argv) {{')
def gen_setter(w: Writer, struct: Struct, cvt: Converter, field: StructField):
w.write(f'static bool {struct.identifier}__set_{field.name}(int argc, py_Ref argv) {{')
w.indent()
w.write(f'PY_CHECK_ARGC(2);')
w.write(f'{name}* self = py_touserdata(argv);')
w.write(f'{struct.code_name}* self = py_touserdata(argv);')
cvt.py2c(w, f'self->{field.name}', 'py_arg(1)')
w.write('py_newnone(py_retval());')
w.write('return true;')
@ -24,10 +24,11 @@ def gen_setter(w: Writer, name: str, cvt: Converter, field: StructField):
w.write('}')
def gen_struct(w: Writer, pyi_w: Writer, struct: Struct):
name = struct.name
name = struct.code_name
identifier = struct.identifier
converters = [get_converter(field.type) for field in struct.fields or []]
# default __new__
w.write(f'static bool {name}__new__(int argc, py_Ref argv) {{')
w.write(f'static bool {identifier}__new__(int argc, py_Ref argv) {{')
w.indent()
w.write(f'py_Type cls = py_totype(argv);')
w.write(f'py_newobject(py_retval(), cls, 0, sizeof({name}));')
@ -36,7 +37,7 @@ def gen_struct(w: Writer, pyi_w: Writer, struct: Struct):
w.write('}')
# default __init__
w.write(f'static bool {name}__init__(int argc, py_Ref argv) {{')
w.write(f'static bool {identifier}__init__(int argc, py_Ref argv) {{')
w.indent()
w.write(f'{name}* self = py_touserdata(argv);')
w.write(f'if(argc == 1) {{')
@ -62,46 +63,33 @@ def gen_struct(w: Writer, pyi_w: Writer, struct: Struct):
w.dedent()
w.write('}')
# default __copy__
w.write(f'static bool {name}__copy__(int argc, py_Ref argv) {{')
w.indent()
w.write(f'PY_CHECK_ARGC(1);')
w.write(f'{name}* self = py_touserdata(argv);')
w.write(f'{name}* res = py_newobject(py_retval(), py_typeof(argv), 0, sizeof({name}));')
w.write(f'*res = *self;')
w.write('return true;')
w.dedent()
w.write('}')
for i, field in enumerate(struct.fields):
cvt = converters[i]
gen_getter(w, name, cvt, field)
gen_getter(w, struct, cvt, field)
if not cvt.is_const():
gen_setter(w, name, cvt, field)
gen_setter(w, struct, cvt, field)
w.write(f'static py_Type register__{name}(py_GlobalRef mod) {{')
w.write(f'static py_Type register__{identifier}(py_GlobalRef mod) {{')
w.indent()
w.write(f'py_Type type = py_newtype("{name}", tp_object, mod, NULL);')
w.write(f'py_Type type = py_newtype("{identifier}", tp_object, mod, NULL);')
w.write(f'py_bindmagic(type, __new__, {name}__new__);')
w.write(f'py_bindmagic(type, __init__, {name}__init__);')
w.write(f'py_bindmethod(type, "__address__", struct__address__);')
w.write(f'py_bindmethod(type, "copy", {name}__copy__);')
w.write(f'py_bindmethod(type, "__new__", {identifier}__new__);')
w.write(f'py_bindmethod(type, "__init__", {identifier}__init__);')
for i, field in enumerate(struct.fields):
cvt = converters[i]
if cvt.is_const():
setter = 'NULL'
else:
setter = f'{name}__set_{field.name}'
w.write(f'py_bindproperty(type, "{field.name}", {name}__get_{field.name}, {setter});')
setter = f'{identifier}__set_{field.name}'
w.write(f'py_bindproperty(type, "{field.name}", {identifier}__get_{field.name}, {setter});')
w.write(f'return type;')
w.dedent()
w.write('}')
# pyi
pyi_w.write(f'class {name}:')
pyi_w.write(f'class {identifier}:')
pyi_w.indent()
py_args = []
@ -116,10 +104,8 @@ def gen_struct(w: Writer, pyi_w: Writer, struct: Struct):
pyi_w.write(f'def __init__(self): ...')
pyi_w.write(f'@overload')
pyi_w.write(f'def __init__(self, {", ".join(py_args)}): ...')
pyi_w.write(f'def __address__(self) -> int: ...')
pyi_w.write(f"def copy(self) -> '{name}': ...")
pyi_w.write('')
pyi_w.dedent()
w.write(f'static py_Type tp_user_{name};')
return f'tp_user_{name} = register__{name}(mod);'
w.write(f'static py_Type tp_user_{identifier};')
return f'tp_user_{identifier} = register__{identifier}(mod);'

View File

@ -1,7 +1,7 @@
import pcpp
import pycparser
from ffigen.library import Library
from ffigen.converters import set_vmath_converter, set_enum_converters
from ffigen.converters import set_vmath_converter, set_enum_converter
from ffigen.meta import Header
import os