From 671ea14c4f11c983f0ce0a97783a164df86fbd1d Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Tue, 16 Dec 2025 23:29:05 +0800 Subject: [PATCH] improve ffigen 1 --- ffigen/ffigen/converters.py | 6 +++++ ffigen/ffigen/function.py | 4 +-- ffigen/ffigen/library.py | 49 +++++++++++++++++++++-------------- ffigen/ffigen/meta/parser.py | 44 +++++++++++++++++-------------- ffigen/ffigen/meta/schema.py | 2 +- ffigen/ffigen/schema.py | 19 +++++--------- ffigen/ffigen/struct.py | 14 +++++----- ffigen/gen_periphery.py | 2 -- ffigen/libc_include/stdarg.h | 2 +- ffigen/libc_include/stdbool.h | 2 +- ffigen/libc_include/stddef.h | 4 ++- ffigen/libc_include/stdint.h | 18 ++++++------- include/typings/stdc.pyi | 17 ++++++------ src/modules/stdc.c | 7 +++++ 14 files changed, 107 insertions(+), 83 deletions(-) diff --git a/ffigen/ffigen/converters.py b/ffigen/ffigen/converters.py index 6c84d1c9..d935fd8e 100644 --- a/ffigen/ffigen/converters.py +++ b/ffigen/ffigen/converters.py @@ -155,9 +155,15 @@ def is_vmath_type(T: str) -> bool: return False return isinstance(cvt, BuiltinVMathConverter) +_has_vmath_converter = False def set_vmath_converter(T: str, py_T: str): + global _has_vmath_converter assert py_T in VMATH_TYPES _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]): for T in enums: diff --git a/ffigen/ffigen/function.py b/ffigen/ffigen/function.py index fe84ad58..478c10c5 100644 --- a/ffigen/ffigen/function.py +++ b/ffigen/ffigen/function.py @@ -40,12 +40,12 @@ def gen_function(w: Writer, pyi_w: Writer, function: Function): # pyi py_args = [] - # arg_names = [f'_{i}' for i in range(len(args_cvt))] arg_names = [sanitize_name(arg.name) for arg in function.params] for i in range(len(args_cvt)): 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}:') if function.desc: pyi_w.write(f' """Wraps `{function.signature()}`\n\n {function.desc}"""') diff --git a/ffigen/ffigen/library.py b/ffigen/ffigen/library.py index 5631e592..92633e8d 100644 --- a/ffigen/ffigen/library.py +++ b/ffigen/ffigen/library.py @@ -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 +from .converters import is_vmath_type, has_vmath_converter from typing import TYPE_CHECKING if TYPE_CHECKING: @@ -14,23 +14,34 @@ class Library: self.name = name # ['defines', 'structs', 'aliases', 'enums', 'callbacks', 'functions'] 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.functions = [] # type: list[Function] 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): self.remove_unsupported() 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('intptr = int') + pyi_w.write('from stdc import intptr') pyi_w.write('') w.write('#include "pocketpy.h"') - w.write(f'#include "string.h"') + w.write(f'#include ') + w.write(f'#include ') if includes: for include in includes: @@ -49,8 +60,10 @@ class Library: w.write('}') w.write('') - for alias in self.aliases: - w.write(f'#define tp_user_{alias.name} tp_user_{alias.type}') + for k in self.aliases.keys(): + node = self.unalias(k) + if isinstance(node, str): + w.write(f'#define tp_user_{k} tp_user_{node}') w.write('') reg_exprs = [ @@ -73,9 +86,14 @@ class Library: w.write('/* aliases */') pyi_w.write('# aliases') - for alias in self.aliases: - w.write(f'py_setdict(mod, py_name("{alias.name}"), py_getdict(mod, py_name("{alias.type}")));') - pyi_w.write(f'{alias.name} = {alias.type}') + for k in self.aliases.keys(): + node = self.unalias(k) + 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 */') for function in self.functions: @@ -125,11 +143,7 @@ class Library: ) for field in struct['fields']] )) for alias in data['aliases']: - self.aliases.append(Alias( - type=alias['type'], - name=alias['name'], - desc=alias['description'] - )) + self.aliases[alias['name']] = str(alias['type']) for enum in data['enums']: self.enums.append(Enum( name=enum['name'], @@ -182,10 +196,7 @@ class Library: ) for value in type.values] )) for k, v in header.type_aliases.items(): - self.aliases.append(Alias( - name=k, - type=v - )) + self.aliases[k] = v for function in header.functions: self.functions.append(Function( diff --git a/ffigen/ffigen/meta/parser.py b/ffigen/ffigen/meta/parser.py index 9839ae55..f7ecf970 100644 --- a/ffigen/ffigen/meta/parser.py +++ b/ffigen/ffigen/meta/parser.py @@ -1,3 +1,4 @@ +from typing import Literal from .schema import * class UnsupportedNode(Exception): @@ -11,7 +12,12 @@ class UnsupportedNode(Exception): class Header: def __init__(self): 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] def remove_types(self, names: set): @@ -20,17 +26,17 @@ class Header: def remove_functions(self, names: set): 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): enum = Enum(node.name) for item in node.values.enumerators: enum.values.append(item.name) 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): if isinstance(node, c_ast.Struct): cls = Struct @@ -51,14 +57,19 @@ class Header: else: 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): self.build_enum(node) + if alias_name: + self.add_type_alias(alias_name, node) elif isinstance(node, (c_ast.Struct, c_ast.Union)): self.build_struct(node) + if alias_name: + self.add_type_alias(alias_name, node) elif isinstance(node, c_ast.IdentifierType): - assert name - self.type_aliases[name] = node.names[0] + assert alias_name + assert node.names[0] + self.add_type_alias(alias_name, node.names[0]) else: raise UnsupportedNode(node) @@ -137,23 +148,18 @@ class Header: if isinstance(node, c_ast.Typedef): name, node = node.name, node.type if isinstance(node, c_ast.TypeDecl): - self.build_type(name, node.type) + self.build_type(node.type, name) elif isinstance(node, c_ast.PtrDecl): - type_name = self.get_type_name(node) - self.type_aliases[name] = type_name + self.add_type_alias(name, node) else: - # raise UnsupportedNode(node.type) - # print(f"Unsupported typedef: {type(node)}") - continue + raise UnsupportedNode(node.type) elif isinstance(node, c_ast.Decl): if isinstance(node.type, c_ast.FuncDecl): self.build_function(node.type) else: try: - self.build_type(None, node.type) + self.build_type(node.type, None) except UnsupportedNode: pass else: - # raise UnsupportedNode(node.type) - # print(f"Unsupported typedef: {type(node)}") - continue \ No newline at end of file + raise UnsupportedNode(node.type) \ No newline at end of file diff --git a/ffigen/ffigen/meta/schema.py b/ffigen/ffigen/meta/schema.py index 52ba4cc6..3e04714d 100644 --- a/ffigen/ffigen/meta/schema.py +++ b/ffigen/ffigen/meta/schema.py @@ -2,7 +2,7 @@ from pycparser import c_ast class Pointer: def __init__(self, base: str, level: int): - super().__init__(f'{base}' + '*' * level) + # super().__init__(f'{base}' + '*' * level) self.base = base self.level = level diff --git a/ffigen/ffigen/schema.py b/ffigen/ffigen/schema.py index aadfe427..9c555df4 100644 --- a/ffigen/ffigen/schema.py +++ b/ffigen/ffigen/schema.py @@ -1,34 +1,29 @@ from dataclasses import dataclass +from pycparser import c_ast @dataclass class StructField: type: str name: str - desc: str = None + desc: str | None = None @dataclass class EnumValue: name: str value: int | None - desc: str = None + desc: str | None = None @dataclass class Struct: name: str - desc: str = None - fields: list[StructField] = None - -@dataclass -class Alias: - type: str - name: str - desc: str = None + desc: str | None = None + fields: list[StructField] | None = None @dataclass class Enum: name: str values: list[EnumValue] - desc: str = None + desc: str | None = None @dataclass class FunctionParam: @@ -40,7 +35,7 @@ class Function: name: str params: list[FunctionParam] ret_type: str - desc: str = None + desc: str | None = None def signature(self) -> str: return f'{self.ret_type} {self.name}({", ".join([f"{param.type} {param.name}" for param in self.params])})' diff --git a/ffigen/ffigen/struct.py b/ffigen/ffigen/struct.py index 9bb4f21c..c89a3661 100644 --- a/ffigen/ffigen/struct.py +++ b/ffigen/ffigen/struct.py @@ -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): 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__ w.write(f'static bool {name}__new__(int argc, py_Ref argv) {{') w.indent() @@ -73,8 +73,8 @@ def gen_struct(w: Writer, pyi_w: Writer, struct: Struct): w.dedent() w.write('}') - for field in struct.fields: - cvt = get_converter(field.type) + for i, field in enumerate(struct.fields): + cvt = converters[i] gen_getter(w, name, cvt, field) if not cvt.is_const(): 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, "copy", {name}__copy__);') - for field in struct.fields: - cvt = get_converter(field.type) + for i, field in enumerate(struct.fields): + cvt = converters[i] if cvt.is_const(): setter = 'NULL' else: @@ -105,8 +105,8 @@ def gen_struct(w: Writer, pyi_w: Writer, struct: Struct): pyi_w.indent() py_args = [] - for field in struct.fields: - cvt = get_converter(field.type) + for i, field in enumerate(struct.fields): + cvt = converters[i] desc = (field.desc or '') + f' ({field.type})' py_args.append(f"{field.name}: {cvt.py_T}") pyi_w.write(f"{py_args[-1]} # {desc}") diff --git a/ffigen/gen_periphery.py b/ffigen/gen_periphery.py index 255bb60f..06d7759c 100644 --- a/ffigen/gen_periphery.py +++ b/ffigen/gen_periphery.py @@ -18,8 +18,6 @@ header.build(ast) lib = Library.from_header('periphery', header) -set_enum_converters([enum.name for enum in lib.enums]) - lib.build( includes=['c-periphery/src/gpio.h'], glue_dir='3rd/periphery/src', diff --git a/ffigen/libc_include/stdarg.h b/ffigen/libc_include/stdarg.h index b7ae7efe..97a4a30b 100644 --- a/ffigen/libc_include/stdarg.h +++ b/ffigen/libc_include/stdarg.h @@ -1 +1 @@ -#define va_list int \ No newline at end of file +typedef int va_list; \ No newline at end of file diff --git a/ffigen/libc_include/stdbool.h b/ffigen/libc_include/stdbool.h index 9a5a8fc5..3bd41ef2 100644 --- a/ffigen/libc_include/stdbool.h +++ b/ffigen/libc_include/stdbool.h @@ -1 +1 @@ -#define bool _Bool \ No newline at end of file +typedef _Bool bool; \ No newline at end of file diff --git a/ffigen/libc_include/stddef.h b/ffigen/libc_include/stddef.h index 3c0fa2ed..d34eba89 100644 --- a/ffigen/libc_include/stddef.h +++ b/ffigen/libc_include/stddef.h @@ -1 +1,3 @@ -#define NULL ((void*)0) \ No newline at end of file +#define NULL ((void*)0) + +typedef unsigned size_t; diff --git a/ffigen/libc_include/stdint.h b/ffigen/libc_include/stdint.h index 1f048310..1fe3c9b4 100644 --- a/ffigen/libc_include/stdint.h +++ b/ffigen/libc_include/stdint.h @@ -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 -#define int16_t int -#define int32_t int -#define int64_t int - -#define uint8_t unsigned -#define uint16_t unsigned -#define uint32_t unsigned -#define uint64_t unsigned \ No newline at end of file +typedef unsigned uint8_t; +typedef unsigned uint16_t; +typedef unsigned uint32_t; +typedef unsigned uint64_t; \ No newline at end of file diff --git a/include/typings/stdc.pyi b/include/typings/stdc.pyi index efb0e786..f3515aaf 100644 --- a/include/typings/stdc.pyi +++ b/include/typings/stdc.pyi @@ -46,14 +46,15 @@ class Double(_BuiltinMemory[float]): ... class Pointer(_BuiltinMemory[intptr]): ... class Bool(_BuiltinMemory[bool]): ... -INT8: _BuiltinMemory[int] = ... -UINT8: _BuiltinMemory[int] = ... -INT16: _BuiltinMemory[int] = ... -UINT16: _BuiltinMemory[int] = ... -INT32: _BuiltinMemory[int] = ... -UINT32: _BuiltinMemory[int] = ... -INT64: _BuiltinMemory[int] = ... -UINT64: _BuiltinMemory[int] = ... +Int8: _BuiltinMemory[int] +UInt8: _BuiltinMemory[int] +Int16: _BuiltinMemory[int] +UInt16: _BuiltinMemory[int] +Int32: _BuiltinMemory[int] +UInt32: _BuiltinMemory[int] +Int64: _BuiltinMemory[int] +UInt64: _BuiltinMemory[int] +SizeT: _BuiltinMemory[int] def addressof(obj: Memory) -> intptr: ... def sizeof(obj: type[Memory]) -> int: ... diff --git a/src/modules/stdc.c b/src/modules/stdc.c index 35f20ef7..8d7bb3bd 100644 --- a/src/modules/stdc.c +++ b/src/modules/stdc.c @@ -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_Double(mod);