improve ffigen 1

This commit is contained in:
blueloveTH 2025-12-16 23:29:05 +08:00
parent 05c47f1023
commit 671ea14c4f
14 changed files with 107 additions and 83 deletions

View File

@ -155,9 +155,15 @@ def is_vmath_type(T: str) -> bool:
return False return False
return isinstance(cvt, BuiltinVMathConverter) return isinstance(cvt, BuiltinVMathConverter)
_has_vmath_converter = False
def set_vmath_converter(T: str, py_T: str): def set_vmath_converter(T: str, py_T: str):
global _has_vmath_converter
assert py_T in VMATH_TYPES assert py_T in VMATH_TYPES
_CONVERTERS[T] = BuiltinVMathConverter(T, py_T) _CONVERTERS[T] = BuiltinVMathConverter(T, py_T)
_has_vmath_converter = True
def has_vmath_converter() -> bool:
return _has_vmath_converter
def set_enum_converters(enums: list[str]): def set_enum_converters(enums: list[str]):
for T in enums: for T in enums:

View File

@ -40,12 +40,12 @@ def gen_function(w: Writer, pyi_w: Writer, function: Function):
# pyi # pyi
py_args = [] py_args = []
# arg_names = [f'_{i}' for i in range(len(args_cvt))]
arg_names = [sanitize_name(arg.name) for arg in function.params] arg_names = [sanitize_name(arg.name) for arg in function.params]
for i in range(len(args_cvt)): for i in range(len(args_cvt)):
py_args.append(f'{arg_names[i]}: {args_cvt[i].py_T}') py_args.append(f'{arg_names[i]}: {args_cvt[i].py_T}')
py_args.append('/') if len(py_args) > 0:
py_args.append('/')
pyi_w.write(f'def {name}({", ".join(py_args)}) -> {ret_cvt.py_T}:') pyi_w.write(f'def {name}({", ".join(py_args)}) -> {ret_cvt.py_T}:')
if function.desc: if function.desc:
pyi_w.write(f' """Wraps `{function.signature()}`\n\n {function.desc}"""') pyi_w.write(f' """Wraps `{function.signature()}`\n\n {function.desc}"""')

View File

@ -3,7 +3,7 @@ from .writer import Writer
from .enum import gen_enum from .enum import gen_enum
from .struct import gen_struct from .struct import gen_struct
from .function import gen_function from .function import gen_function
from .converters import is_vmath_type from .converters import is_vmath_type, has_vmath_converter
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
@ -14,23 +14,34 @@ class Library:
self.name = name self.name = name
# ['defines', 'structs', 'aliases', 'enums', 'callbacks', 'functions'] # ['defines', 'structs', 'aliases', 'enums', 'callbacks', 'functions']
self.structs = [] # type: list[Struct] self.structs = [] # type: list[Struct]
self.aliases = [] # type: list[Alias] self.aliases = {} # type: dict[str, str | c_ast.Node]
self.enums = [] # type: list[Enum] self.enums = [] # type: list[Enum]
self.functions = [] # type: list[Function] self.functions = [] # type: list[Function]
self.callbacks = set() # type: set[str] self.callbacks = set() # type: set[str]
def unalias(self, name: str) -> c_ast.Node:
while name in self.aliases:
node = self.aliases[name]
if isinstance(node, str):
name = node
else:
return node
assert False, f'alias {name} not found'
def build(self, *, glue_dir='.', stub_dir='.', includes: list[str] | None = None): def build(self, *, glue_dir='.', stub_dir='.', includes: list[str] | None = None):
self.remove_unsupported() self.remove_unsupported()
w, pyi_w = Writer(), Writer() w, pyi_w = Writer(), Writer()
pyi_w.write('from vmath import vec2, vec3, vec2i, vec3i, mat3x3, color32') if has_vmath_converter():
pyi_w.write('from vmath import vec2, vec3, vec2i, vec3i, mat3x3, color32')
pyi_w.write('from typing import overload') pyi_w.write('from typing import overload')
pyi_w.write('intptr = int') pyi_w.write('from stdc import intptr')
pyi_w.write('') pyi_w.write('')
w.write('#include "pocketpy.h"') w.write('#include "pocketpy.h"')
w.write(f'#include "string.h"') w.write(f'#include <string.h>')
w.write(f'#include <stdint.h>')
if includes: if includes:
for include in includes: for include in includes:
@ -49,8 +60,10 @@ class Library:
w.write('}') w.write('}')
w.write('') w.write('')
for alias in self.aliases: for k in self.aliases.keys():
w.write(f'#define tp_user_{alias.name} tp_user_{alias.type}') node = self.unalias(k)
if isinstance(node, str):
w.write(f'#define tp_user_{k} tp_user_{node}')
w.write('') w.write('')
reg_exprs = [ reg_exprs = [
@ -73,9 +86,14 @@ class Library:
w.write('/* aliases */') w.write('/* aliases */')
pyi_w.write('# aliases') pyi_w.write('# aliases')
for alias in self.aliases: for k in self.aliases.keys():
w.write(f'py_setdict(mod, py_name("{alias.name}"), py_getdict(mod, py_name("{alias.type}")));') node = self.unalias(k)
pyi_w.write(f'{alias.name} = {alias.type}') if isinstance(node, str):
w.write(f'py_setdict(mod, py_name("{k}"), py_getdict(mod, py_name("{node}")));')
pyi_w.write(f'{k} = {node}')
elif isinstance(node, c_ast.Enum):
w.write(f'py_setdict(mod, py_name("{k}"), py_tpobject(tp_int));')
pyi_w.write(f'{k} = int')
w.write('/* functions */') w.write('/* functions */')
for function in self.functions: for function in self.functions:
@ -125,11 +143,7 @@ class Library:
) for field in struct['fields']] ) for field in struct['fields']]
)) ))
for alias in data['aliases']: for alias in data['aliases']:
self.aliases.append(Alias( self.aliases[alias['name']] = str(alias['type'])
type=alias['type'],
name=alias['name'],
desc=alias['description']
))
for enum in data['enums']: for enum in data['enums']:
self.enums.append(Enum( self.enums.append(Enum(
name=enum['name'], name=enum['name'],
@ -182,10 +196,7 @@ class Library:
) for value in type.values] ) for value in type.values]
)) ))
for k, v in header.type_aliases.items(): for k, v in header.type_aliases.items():
self.aliases.append(Alias( self.aliases[k] = v
name=k,
type=v
))
for function in header.functions: for function in header.functions:
self.functions.append(Function( self.functions.append(Function(

View File

@ -1,3 +1,4 @@
from typing import Literal
from .schema import * from .schema import *
class UnsupportedNode(Exception): class UnsupportedNode(Exception):
@ -11,7 +12,12 @@ class UnsupportedNode(Exception):
class Header: class Header:
def __init__(self): def __init__(self):
self.types = [] # type: list self.types = [] # type: list
self.type_aliases = {} # type: dict[str, str] self.type_aliases = {} # type: dict[str, str | c_ast.Node]
self.builtin_aliases = {
'size_t', 'bool',
'int8_t', 'int16_t', 'int32_t', 'int64_t',
'uint8_t', 'uint16_t', 'uint32_t', 'uint64_t',
}
self.functions = [] # type: list[Function] self.functions = [] # type: list[Function]
def remove_types(self, names: set): def remove_types(self, names: set):
@ -20,17 +26,17 @@ class Header:
def remove_functions(self, names: set): def remove_functions(self, names: set):
self.functions = [f for f in self.functions if f.name not in names] self.functions = [f for f in self.functions if f.name not in names]
def add_type_alias(self, k: str, v: str | c_ast.Node):
if k in self.builtin_aliases:
return
self.type_aliases[k] = v
def build_enum(self, node: c_ast.Enum): def build_enum(self, node: c_ast.Enum):
enum = Enum(node.name) enum = Enum(node.name)
for item in node.values.enumerators: for item in node.values.enumerators:
enum.values.append(item.name) enum.values.append(item.name)
self.types.append(enum) self.types.append(enum)
def unalias(self, name: str):
while name in self.type_aliases:
name = self.type_aliases[name]
return name
def build_struct(self, node): def build_struct(self, node):
if isinstance(node, c_ast.Struct): if isinstance(node, c_ast.Struct):
cls = Struct cls = Struct
@ -51,14 +57,19 @@ class Header:
else: else:
self.types.append(cls(node.name, None)) self.types.append(cls(node.name, None))
def build_type(self, name, node): def build_type(self, node, alias_name):
if isinstance(node, c_ast.Enum): if isinstance(node, c_ast.Enum):
self.build_enum(node) self.build_enum(node)
if alias_name:
self.add_type_alias(alias_name, node)
elif isinstance(node, (c_ast.Struct, c_ast.Union)): elif isinstance(node, (c_ast.Struct, c_ast.Union)):
self.build_struct(node) self.build_struct(node)
if alias_name:
self.add_type_alias(alias_name, node)
elif isinstance(node, c_ast.IdentifierType): elif isinstance(node, c_ast.IdentifierType):
assert name assert alias_name
self.type_aliases[name] = node.names[0] assert node.names[0]
self.add_type_alias(alias_name, node.names[0])
else: else:
raise UnsupportedNode(node) raise UnsupportedNode(node)
@ -137,23 +148,18 @@ class Header:
if isinstance(node, c_ast.Typedef): if isinstance(node, c_ast.Typedef):
name, node = node.name, node.type name, node = node.name, node.type
if isinstance(node, c_ast.TypeDecl): if isinstance(node, c_ast.TypeDecl):
self.build_type(name, node.type) self.build_type(node.type, name)
elif isinstance(node, c_ast.PtrDecl): elif isinstance(node, c_ast.PtrDecl):
type_name = self.get_type_name(node) self.add_type_alias(name, node)
self.type_aliases[name] = type_name
else: else:
# raise UnsupportedNode(node.type) raise UnsupportedNode(node.type)
# print(f"Unsupported typedef: {type(node)}")
continue
elif isinstance(node, c_ast.Decl): elif isinstance(node, c_ast.Decl):
if isinstance(node.type, c_ast.FuncDecl): if isinstance(node.type, c_ast.FuncDecl):
self.build_function(node.type) self.build_function(node.type)
else: else:
try: try:
self.build_type(None, node.type) self.build_type(node.type, None)
except UnsupportedNode: except UnsupportedNode:
pass pass
else: else:
# raise UnsupportedNode(node.type) raise UnsupportedNode(node.type)
# print(f"Unsupported typedef: {type(node)}")
continue

View File

@ -2,7 +2,7 @@ from pycparser import c_ast
class Pointer: class Pointer:
def __init__(self, base: str, level: int): def __init__(self, base: str, level: int):
super().__init__(f'{base}' + '*' * level) # super().__init__(f'{base}' + '*' * level)
self.base = base self.base = base
self.level = level self.level = level

View File

@ -1,34 +1,29 @@
from dataclasses import dataclass from dataclasses import dataclass
from pycparser import c_ast
@dataclass @dataclass
class StructField: class StructField:
type: str type: str
name: str name: str
desc: str = None desc: str | None = None
@dataclass @dataclass
class EnumValue: class EnumValue:
name: str name: str
value: int | None value: int | None
desc: str = None desc: str | None = None
@dataclass @dataclass
class Struct: class Struct:
name: str name: str
desc: str = None desc: str | None = None
fields: list[StructField] = None fields: list[StructField] | None = None
@dataclass
class Alias:
type: str
name: str
desc: str = None
@dataclass @dataclass
class Enum: class Enum:
name: str name: str
values: list[EnumValue] values: list[EnumValue]
desc: str = None desc: str | None = None
@dataclass @dataclass
class FunctionParam: class FunctionParam:
@ -40,7 +35,7 @@ class Function:
name: str name: str
params: list[FunctionParam] params: list[FunctionParam]
ret_type: str ret_type: str
desc: str = None desc: str | None = None
def signature(self) -> str: def signature(self) -> str:
return f'{self.ret_type} {self.name}({", ".join([f"{param.type} {param.name}" for param in self.params])})' return f'{self.ret_type} {self.name}({", ".join([f"{param.type} {param.name}" for param in self.params])})'

View File

@ -25,7 +25,7 @@ def gen_setter(w: Writer, name: str, cvt: Converter, field: StructField):
def gen_struct(w: Writer, pyi_w: Writer, struct: Struct): def gen_struct(w: Writer, pyi_w: Writer, struct: Struct):
name = struct.name name = struct.name
converters = [get_converter(field.type) for field in struct.fields] converters = [get_converter(field.type) for field in struct.fields or []]
# default __new__ # default __new__
w.write(f'static bool {name}__new__(int argc, py_Ref argv) {{') w.write(f'static bool {name}__new__(int argc, py_Ref argv) {{')
w.indent() w.indent()
@ -73,8 +73,8 @@ def gen_struct(w: Writer, pyi_w: Writer, struct: Struct):
w.dedent() w.dedent()
w.write('}') w.write('}')
for field in struct.fields: for i, field in enumerate(struct.fields):
cvt = get_converter(field.type) cvt = converters[i]
gen_getter(w, name, cvt, field) gen_getter(w, name, cvt, field)
if not cvt.is_const(): if not cvt.is_const():
gen_setter(w, name, cvt, field) gen_setter(w, name, cvt, field)
@ -88,8 +88,8 @@ def gen_struct(w: Writer, pyi_w: Writer, struct: Struct):
w.write(f'py_bindmethod(type, "__address__", struct__address__);') w.write(f'py_bindmethod(type, "__address__", struct__address__);')
w.write(f'py_bindmethod(type, "copy", {name}__copy__);') w.write(f'py_bindmethod(type, "copy", {name}__copy__);')
for field in struct.fields: for i, field in enumerate(struct.fields):
cvt = get_converter(field.type) cvt = converters[i]
if cvt.is_const(): if cvt.is_const():
setter = 'NULL' setter = 'NULL'
else: else:
@ -105,8 +105,8 @@ def gen_struct(w: Writer, pyi_w: Writer, struct: Struct):
pyi_w.indent() pyi_w.indent()
py_args = [] py_args = []
for field in struct.fields: for i, field in enumerate(struct.fields):
cvt = get_converter(field.type) cvt = converters[i]
desc = (field.desc or '') + f' ({field.type})' desc = (field.desc or '') + f' ({field.type})'
py_args.append(f"{field.name}: {cvt.py_T}") py_args.append(f"{field.name}: {cvt.py_T}")
pyi_w.write(f"{py_args[-1]} # {desc}") pyi_w.write(f"{py_args[-1]} # {desc}")

View File

@ -18,8 +18,6 @@ header.build(ast)
lib = Library.from_header('periphery', header) lib = Library.from_header('periphery', header)
set_enum_converters([enum.name for enum in lib.enums])
lib.build( lib.build(
includes=['c-periphery/src/gpio.h'], includes=['c-periphery/src/gpio.h'],
glue_dir='3rd/periphery/src', glue_dir='3rd/periphery/src',

View File

@ -1 +1 @@
#define va_list int typedef int va_list;

View File

@ -1 +1 @@
#define bool _Bool typedef _Bool bool;

View File

@ -1 +1,3 @@
#define NULL ((void*)0) #define NULL ((void*)0)
typedef unsigned size_t;

View File

@ -1,11 +1,9 @@
#define size_t int typedef int int8_t;
typedef int int16_t;
typedef int int32_t;
typedef int int64_t;
#define int8_t int typedef unsigned uint8_t;
#define int16_t int typedef unsigned uint16_t;
#define int32_t int typedef unsigned uint32_t;
#define int64_t int typedef unsigned uint64_t;
#define uint8_t unsigned
#define uint16_t unsigned
#define uint32_t unsigned
#define uint64_t unsigned

View File

@ -46,14 +46,15 @@ class Double(_BuiltinMemory[float]): ...
class Pointer(_BuiltinMemory[intptr]): ... class Pointer(_BuiltinMemory[intptr]): ...
class Bool(_BuiltinMemory[bool]): ... class Bool(_BuiltinMemory[bool]): ...
INT8: _BuiltinMemory[int] = ... Int8: _BuiltinMemory[int]
UINT8: _BuiltinMemory[int] = ... UInt8: _BuiltinMemory[int]
INT16: _BuiltinMemory[int] = ... Int16: _BuiltinMemory[int]
UINT16: _BuiltinMemory[int] = ... UInt16: _BuiltinMemory[int]
INT32: _BuiltinMemory[int] = ... Int32: _BuiltinMemory[int]
UINT32: _BuiltinMemory[int] = ... UInt32: _BuiltinMemory[int]
INT64: _BuiltinMemory[int] = ... Int64: _BuiltinMemory[int]
UINT64: _BuiltinMemory[int] = ... UInt64: _BuiltinMemory[int]
SizeT: _BuiltinMemory[int]
def addressof(obj: Memory) -> intptr: ... def addressof(obj: Memory) -> intptr: ...
def sizeof(obj: type[Memory]) -> int: ... def sizeof(obj: type[Memory]) -> int: ...

View File

@ -267,6 +267,13 @@ void pk__add_module_stdc() {
} }
} }
} }
for(py_Type t = tp_stdc_Char; t <= tp_stdc_ULongLong; t += 2) {
py_Ref size_var = py_getdict(py_tpobject(t), py_name("size"));
if(py_toint(size_var) == sizeof(size_t)) {
py_setdict(mod, py_name("SizeT"), py_tpobject(t + 1));
break;
}
}
pk__bind_stdc_Float(mod); pk__bind_stdc_Float(mod);
pk__bind_stdc_Double(mod); pk__bind_stdc_Double(mod);