add scripts/c_bind

This commit is contained in:
blueloveTH 2024-11-04 13:28:59 +08:00
parent 0c1abd3b5c
commit 7ffd50bcf1
20 changed files with 906 additions and 0 deletions

View File

@ -0,0 +1,7 @@
from .function import gen_function
from .converters import get_converter, set_linalg_converter, set_enum_converters
from .writer import Writer
from .struct import gen_struct
from .enum import gen_enum
from .library import Library

View File

@ -0,0 +1,169 @@
from .writer import Writer
from .types import C_INT_TYPES, C_FLOAT_TYPES, C_BOOL_TYPES, C_STRING_TYPES, LINALG_TYPES
class Converter:
def __init__(self, T: str):
self.T = T
def c2py(self, w: Writer, out: str, expr: str):
raise NotImplementedError
def py2c(self, w: Writer, out: str, expr: str):
raise NotImplementedError
@property
def py_T(self) -> str:
raise NotImplementedError
def is_const(self):
return self.T.startswith('const ') or '[' in self.T
class _SimpleConverter(Converter):
def c2py(self, w: Writer, out: str, expr: str):
w.write(f'py_new{self.py_T}({out}, {expr});')
def py2c(self, w: Writer, out: str, expr: str):
w.write(f'if(!py_check{self.py_T}({expr})) return false;')
w.write(f'{out} = py_to{self.py_T}({expr});')
class IntConverter(_SimpleConverter):
@property
def py_T(self) -> str:
return 'int'
class BoolConverter(_SimpleConverter):
@property
def py_T(self) -> str:
return 'bool'
class StringConverter(_SimpleConverter):
@property
def py_T(self) -> str:
return 'str'
class FloatConverter(Converter):
def c2py(self, w: Writer, out: str, expr: str):
w.write(f'py_newfloat({out}, {expr});')
def py2c(self, w: Writer, out: str, expr: str):
if self.T == 'float':
w.write(f'if(!py_castfloat32({expr}, &{out})) return false;')
else:
w.write(f'if(!py_castfloat({expr}, &{out})) return false;')
@property
def py_T(self) -> str:
return 'float'
class PointerConverter(Converter):
def c2py(self, w: Writer, out: str, expr: str):
w.write(f'py_newint({out}, (py_i64){expr});')
def py2c(self, w: Writer, out: str, expr: str):
w.write(f'if(!py_checkint({expr})) return false;')
w.write(f'{out} = ({self.T})py_toint({expr});')
@property
def py_T(self) -> str:
return 'intptr'
class StructConverter(Converter):
def __init__(self, T: str, type_index: str | None):
super().__init__(T)
if type_index is None:
type_index = f'tp_user_{T}'
self.type_index = type_index
def c2py(self, w: Writer, out: str, expr: str):
w.write('do {')
w.indent()
w.write(f'{self.T}* ud = py_newobject({out}, {self.type_index}, 0, sizeof({self.T}));')
w.write(f'*ud = {expr};')
w.dedent()
w.write('} while(0);')
def py2c(self, w: Writer, out: str, expr: str):
w.write('do {')
w.indent()
w.write(f'if(!py_checktype({expr}, {self.type_index})) return false;')
w.write(f'{out} = *({self.T}*)py_touserdata({expr});')
w.dedent()
w.write('} while(0);')
@property
def py_T(self) -> str:
return self.T
class EnumConverter(Converter):
def __init__(self, T: str):
super().__init__(T)
def c2py(self, w: Writer, out: str, expr: str):
w.write(f'py_newint({out}, (py_i64){expr});')
def py2c(self, w: Writer, out: str, expr: str):
w.write(f'if(!py_checkint({expr})) return false;')
w.write(f'{out} = ({self.T})py_toint({expr});')
@property
def py_T(self) -> str:
return 'int'
class VoidConverter(Converter):
def c2py(self, w: Writer, out: str, expr: str):
w.write(f'py_newnone({out});')
def py2c(self, w: Writer, out: str, expr: str):
# raise NotImplementedError
w.write(f'? // VoidConverter.py2c is not implemented')
@property
def py_T(self) -> str:
return 'None'
class BuiltinVectorConverter(Converter):
def __init__(self, T: str, py_builtin_T: str):
super().__init__(T)
self.py_builtin_T = py_builtin_T
def c2py(self, w: Writer, out: str, expr: str):
w.write(f'py_new{self.py_builtin_T}({out}, *(c11_{self.py_T}*)(&{expr}));')
def py2c(self, w: Writer, out: str, expr: str):
w.write('do {')
w.indent()
w.write(f'if(!py_checktype({expr}, tp_{self.py_T})) return false;')
w.write(f'c11_{self.py_T} tmp = py_to{self.py_builtin_T}({expr});')
# w.write(f'memcpy(&{out}, &tmp, sizeof(c11_{self.py_T}));')
w.write(f'{out} = *({self.T}*)(&tmp);')
w.dedent()
w.write('} while(0);')
@property
def py_T(self) -> str:
return self.py_builtin_T
_CONVERTERS: dict[str, Converter] = {}
for t in C_INT_TYPES:
_CONVERTERS[t] = IntConverter(t)
for t in C_FLOAT_TYPES:
_CONVERTERS[t] = FloatConverter(t)
for t in C_BOOL_TYPES:
_CONVERTERS[t] = BoolConverter(t)
for t in C_STRING_TYPES:
_CONVERTERS[t] = StringConverter(t)
for t in LINALG_TYPES:
_CONVERTERS[t] = BuiltinVectorConverter(f'c11_{t}', t)
_CONVERTERS['void'] = VoidConverter('void')
_CONVERTERS['c11_array2d'] = StructConverter('c11_array2d', 'tp_array2d')
def set_linalg_converter(T: str, py_T: str):
assert py_T in LINALG_TYPES
_CONVERTERS[T] = BuiltinVectorConverter(T, py_T)
def set_enum_converters(enums: list[str]):
for T in enums:
_CONVERTERS[T] = EnumConverter(T)
def get_converter(T: str) -> Converter:
if T in _CONVERTERS:
return _CONVERTERS[T]
if T.endswith('*'):
return PointerConverter(T)
if '[' in T:
return PointerConverter(T)
cvt = _CONVERTERS.get(T)
if cvt is None:
return StructConverter(T, None)

View File

@ -0,0 +1,12 @@
from .writer import Writer
from .schema import Enum
def gen_enum(w: Writer, pyi_w: Writer, enum: Enum):
for value in enum.values:
w.write(f'ADD_ENUM({value.name});')
if value.value is not None:
pyi_w.write(f'{value.name}: int = {value.value}')
else:
pyi_w.write(f'{value.name}: int')

View File

@ -0,0 +1,47 @@
from .writer import Writer
from .converters import get_converter
from .schema import Function
def gen_function(w: Writer, pyi_w: Writer, function: Function):
name = function.name
w.write(f'static bool cfunc__{name}(int argc, py_Ref argv) {{')
w.indent()
w.write(f'PY_CHECK_ARGC({len(function.params)});')
args_cvt = [get_converter(arg.type) for arg in function.params]
ret_cvt = get_converter(function.ret_type)
for i in range(len(args_cvt)):
w.write(f'{args_cvt[i].T} _{i};')
args_cvt[i].py2c(w, f'_{i}', f'py_arg({i})')
call_args = ', '.join([f'_{i}' for i in range(len(args_cvt))])
# gen retval
if function.ret_type == 'void':
w.write(f'{name}({call_args});')
w.write(f'py_newnone(py_retval());')
else:
w.write(f'{ret_cvt.T} res = {name}({call_args});')
ret_cvt.c2py(w, 'py_retval()', 'res')
w.write('return true;')
w.dedent()
w.write('}')
# pyi
py_args = []
# arg_names = [f'_{i}' for i in range(len(args_cvt))]
arg_names = [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}')
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}"""')
else:
pyi_w.write(f' """Wraps `{function.signature()}`"""')
pyi_w.write('')

View File

@ -0,0 +1,195 @@
from .schema import *
from .writer import Writer
from .enum import gen_enum
from .struct import gen_struct
from .function import gen_function
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from .meta import Header
class Library:
def __init__(self, name: str) -> None:
self.name = name
# ['defines', 'structs', 'aliases', 'enums', 'callbacks', 'functions']
self.structs = [] # type: list[Struct]
self.aliases = [] # type: list[Alias]
self.enums = [] # type: list[Enum]
self.functions = [] # type: list[Function]
self.callbacks = set() # type: set[str]
def set_includes(self, includes: list[str]):
self.user_includes.extend(includes)
def build(self, *, glue_dir='.', stub_dir='.', includes: list[str] = None):
self.remove_unsupported()
w, pyi_w = Writer(), Writer()
pyi_w.write('from linalg import vec2, vec3, vec2i, vec3i, mat3x3')
pyi_w.write('from typing import overload')
pyi_w.write('intptr = int')
pyi_w.write('')
w.write('#include "pocketpy.h"')
w.write(f'#include "string.h"')
if includes:
for include in includes:
w.write(f'#include "{include}"')
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 alias in self.aliases:
w.write(f'#define tp_user_{alias.name} tp_user_{alias.type}')
w.write('')
reg_exprs = [
gen_struct(w, pyi_w, struct)
for struct in self.structs
]
w.write('/* functions */')
for function in self.functions:
gen_function(w, pyi_w, function)
w.write(f'void py__add_module_{self.name}() {{')
w.indent()
w.write(f'py_GlobalRef mod = py_newmodule("{self.name}");')
w.write('/* structs */')
for reg_expr in reg_exprs:
w.write(reg_expr)
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}')
w.write('/* functions */')
for function in self.functions:
w.write(f'py_bindfunc(mod, "{function.name}", &cfunc__{function.name});')
w.write('/* enums */')
pyi_w.write('# enums')
for enum in self.enums:
gen_enum(w, pyi_w, enum)
w.dedent()
w.write('}')
with open(f'{glue_dir}/{self.name}.c', 'w') as f:
f.write(str(w))
with open(f'{stub_dir}/{self.name}.pyi', 'w') as f:
f.write(str(pyi_w))
def remove_unsupported(self):
functions = []
for f in self.functions:
if f.params and f.params[-1].type == '...':
print('[WARN]', f.signature(), 'is variadic')
continue
for p in f.params:
if p.type in self.callbacks:
print('[WARN]', f.signature(), 'has callback param')
break
else:
functions.append(f)
self.functions.clear()
self.functions.extend(functions)
@staticmethod
def from_raylib(data: dict):
self = Library('raylib')
for struct in data['structs']:
self.structs.append(Struct(
name=struct['name'],
desc=struct['description'],
fields=[StructField(
type=field['type'],
name=field['name'],
desc=field['description']
) for field in struct['fields']]
))
for alias in data['aliases']:
self.aliases.append(Alias(
type=alias['type'],
name=alias['name'],
desc=alias['description']
))
for enum in data['enums']:
self.enums.append(Enum(
name=enum['name'],
desc=enum['description'],
values=[EnumValue(
name=value['name'],
value=value['value'],
desc=value['description']
) for value in enum['values']]
))
for function in data['functions']:
self.functions.append(Function(
name=function['name'],
desc=function['description'],
params=[FunctionParam(
type=param['type'],
name=param['name']
) for param in function['params']
] if 'params' in function else [],
ret_type=function['returnType']
))
for callback in data['callbacks']:
self.callbacks.add(callback['name'])
return self
@staticmethod
def from_header(name: str, header: 'Header'):
from c_bind.meta import schema
self = Library(name)
for type in header.types:
if isinstance(type, schema.NamedFields):
if type.is_opaque():
continue
else:
self.structs.append(Struct(
name=type.name,
fields=[StructField(
type=field_type,
name=field_name
) for field_name, field_type in type.fields.items()]
))
elif isinstance(type, schema.Enum):
self.enums.append(Enum(
name=type.name,
values=[EnumValue(
name=value,
value=None
) for value in type.values]
))
for k, v in header.type_aliases.items():
self.aliases.append(Alias(
name=k,
type=v
))
for function in header.functions:
self.functions.append(Function(
name=function.name,
params=[FunctionParam(
type=param,
name=f'_{i}'
) for i, param in enumerate(function.args)],
ret_type=function.ret
))
return self

View File

@ -0,0 +1 @@
from .parser import Header

View File

@ -0,0 +1,159 @@
from .schema import *
class UnsupportedNode(Exception):
def __init__(self, node: c_ast.Node):
self.node = node
def __str__(self):
return f"'{type(self.node)}' is not supported\n{self.node!r}"
class Header:
def __init__(self):
self.types = [] # type: list
self.type_aliases = {} # type: dict[str, str]
self.functions = [] # type: list[Function]
def remove_types(self, names: set):
self.types = [t for t in self.types if getattr(t, 'name', None) not in names]
def remove_functions(self, names: set):
self.functions = [f for f in self.functions if f.name not in names]
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
elif isinstance(node, c_ast.Union):
cls = Union
else:
raise UnsupportedNode(node)
if node.decls is not None:
fields = {}
for decl in node.decls:
try:
type, name = self.build_param(decl)
assert name
fields[name] = type
except UnsupportedNode:
pass
self.types.append(cls(node.name, fields))
else:
self.types.append(cls(node.name, None))
def build_type(self, name, node):
if isinstance(node, c_ast.Enum):
self.build_enum(node)
elif isinstance(node, (c_ast.Struct, c_ast.Union)):
self.build_struct(node)
elif isinstance(node, c_ast.IdentifierType):
assert name
self.type_aliases[name] = node.names[0]
else:
raise UnsupportedNode(node)
def get_type_name(self, node):
# convert array to const T*
if isinstance(node, c_ast.ArrayDecl):
dims = []
while isinstance(node, c_ast.ArrayDecl):
dims.append(node.dim.value)
node = node.type
base_name = self.get_type_name(node)
# return base_name + ''.join(f'[{dim}]' for dim in dims)
return f'const {base_name}*'
node, level = self.eat_pointers(node)
if isinstance(node, c_ast.FuncDecl):
assert level == 1
return 'void (*)()'
if not isinstance(node, (c_ast.TypeDecl, c_ast.Typename)):
raise UnsupportedNode(node)
is_const = node.quals and 'const' in node.quals
node = node.type
base_name: str
if isinstance(node, c_ast.IdentifierType):
base_name = node.names[0]
elif isinstance(node, c_ast.Enum):
base_name = 'enum ' + node.name
elif isinstance(node, c_ast.Struct):
base_name = 'struct ' + node.name
elif isinstance(node, c_ast.Union):
base_name = 'union ' + node.name
else:
base_name = self.get_type_name(node)
if is_const:
base_name = 'const ' + base_name
return base_name + '*' * level
def build_param(self, node):
name = None
if isinstance(node, c_ast.Decl):
name = node.name
node = node.type
return self.get_type_name(node), name
def eat_pointers(self, node):
level = 0
while isinstance(node, c_ast.PtrDecl):
node = node.type
level += 1
return node, level
def build_function(self, node: c_ast.FuncDecl):
args = node.args
node = node.type
node, level = self.eat_pointers(node)
assert isinstance(node, c_ast.TypeDecl), type(node)
name = node.declname
ret = node.type.names[0]
func = Function(name, ret + '*' * level)
if args is not None:
for param in args.params:
if isinstance(param, c_ast.EllipsisParam):
func.args.append('...')
else:
T, name = self.build_param(param)
if T != 'void':
func.args.append(T)
self.functions.append(func)
def build(self, ast: c_ast.FileAST):
for _, node in ast.children():
if isinstance(node, c_ast.Typedef):
name, node = node.name, node.type
if isinstance(node, c_ast.TypeDecl):
self.build_type(name, node.type)
elif isinstance(node, c_ast.PtrDecl):
type_name = self.get_type_name(node)
self.type_aliases[name] = type_name
else:
# raise UnsupportedNode(node.type)
# print(f"Unsupported typedef: {type(node)}")
continue
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)
except UnsupportedNode:
pass
else:
# raise UnsupportedNode(node.type)
# print(f"Unsupported typedef: {type(node)}")
continue

View File

@ -0,0 +1,39 @@
from pycparser import c_ast
class Pointer:
def __init__(self, base: str, level: int):
super().__init__(f'{base}' + '*' * level)
self.base = base
self.level = level
class NamedFields:
def __init__(self, name: str, fields: dict[str, str] | None):
self.name = name
self.fields = fields
def is_opaque(self):
return self.fields is None
def __repr__(self):
cls = type(self).__name__
return f"{cls}('{self.name}', {self.fields!r})"
class Struct(NamedFields): pass
class Union(NamedFields): pass
class Enum:
def __init__(self, name: str):
self.name = name
self.values = [] # type: list[str]
def __repr__(self):
return f"Enum('{self.name}', values={self.values!r})"
class Function:
def __init__(self, name: str, ret: str):
self.name = name
self.args = [] # type: list[str]
self.ret = ret
def __repr__(self):
return f"Function('{self.name}', args={self.args!r}, ret={self.ret!r})"

View File

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

View File

@ -0,0 +1,125 @@
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) {{')
w.indent()
w.write(f'PY_CHECK_ARGC(1);')
w.write(f'{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) {{')
w.indent()
w.write(f'PY_CHECK_ARGC(2);')
w.write(f'{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;')
w.dedent()
w.write('}')
def gen_struct(w: Writer, pyi_w: Writer, struct: Struct):
name = struct.name
converters = [get_converter(field.type) for field in struct.fields]
# default __new__
w.write(f'static bool {name}__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}));')
w.write('return true;')
w.dedent()
w.write('}')
# default __init__
w.write(f'static bool {name}__init__(int argc, py_Ref argv) {{')
w.indent()
w.write(f'{name}* self = py_touserdata(argv);')
w.write(f'if(argc == 1) {{')
w.indent()
w.write(f'memset(self, 0, sizeof({name}));')
w.dedent()
w.write(f'}} else if(argc == 1 + {len(struct.fields)}) {{')
w.indent()
for i, field in enumerate(struct.fields):
cvt = converters[i]
if not cvt.is_const():
cvt.py2c(w, f'self->{field.name}', f'py_arg({i+1})')
else:
w.write(f'// _{i} {field.name} is read-only')
w.dedent()
w.write('} else {')
w.indent()
w.write(f'return TypeError("expected 1 or {len(struct.fields)+1} arguments");')
w.dedent()
w.write('}')
w.write('py_newnone(py_retval());')
w.write('return true;')
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 field in struct.fields:
cvt = get_converter(field.type)
gen_getter(w, name, cvt, field)
if not cvt.is_const():
gen_setter(w, name, cvt, field)
w.write(f'static py_Type register__{name}(py_GlobalRef mod) {{')
w.indent()
w.write(f'py_Type type = py_newtype("{name}", 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__);')
for field in struct.fields:
cvt = get_converter(field.type)
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});')
w.write(f'return type;')
w.dedent()
w.write('}')
# pyi
pyi_w.write(f'class {name}:')
pyi_w.indent()
py_args = []
for field in struct.fields:
cvt = get_converter(field.type)
desc = (field.desc or '') + f' ({field.type})'
py_args.append(f"{field.name}: {cvt.py_T}")
pyi_w.write(f"{py_args[-1]} # {desc}")
pyi_w.write('')
pyi_w.write(f'@overload')
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);'

View File

@ -0,0 +1,30 @@
C_INT_TYPES = [
'char', 'short', 'int', 'long', 'long long',
'signed char', 'signed short', 'signed int', 'signed long', 'signed long long',
'unsigned char', 'unsigned short', 'unsigned int', 'unsigned long', 'unsigned long long',
'int8_t', 'int16_t', 'int32_t', 'int64_t',
'uint8_t', 'uint16_t', 'uint32_t', 'uint64_t',
'intptr_t', 'uintptr_t',
'ptrdiff_t', 'size_t',
'unsigned', 'signed',
'py_i64',
]
C_FLOAT_TYPES = [
'float', 'double',
'py_f64',
]
C_BOOL_TYPES = [
'bool', '_Bool',
]
C_STRING_TYPES = [
'const char*',
'const char *',
]
LINALG_TYPES = [
'vec2', 'vec3', 'vec2i', 'vec3i',
'mat3x3'
]

View File

@ -0,0 +1,16 @@
class Writer:
def __init__(self) -> None:
self.buffer = []
self.indent_level = 0
def indent(self):
self.indent_level += 1
def dedent(self):
self.indent_level -= 1
def write(self, line: str):
self.buffer.append(' ' * self.indent_level + line)
def __str__(self) -> str:
return '\n'.join(self.buffer)

View File

@ -0,0 +1,30 @@
import pcpp
import pycparser
from c_bind import Library, set_linalg_converter, set_enum_converters
from c_bind.meta import Header
import os
path = '../3rd/box2d/include/box2d/box2d.h'
code = pcpp.CmdPreprocessor([None, path, '-o', 'tmp.h', '--line-directive', '-I', 'libc_include', '-I', '../3rd/box2d/include'])
ast = pycparser.parse_file('tmp.h')
os.remove('tmp.h')
header = Header()
header.build(ast)
header.remove_types({'b2Timer', 'b2DebugDraw'})
header.remove_functions({'b2CreateTimer', 'b2Hash', 'b2DefaultDebugDraw'})
lib = Library.from_header('box2d', header)
set_linalg_converter('b2Vec2', 'vec2')
set_linalg_converter('b2Vec3', 'vec3')
set_enum_converters([enum.name for enum in lib.enums])
lib.build(
includes=['box2d/box2d.h'],
glue_dir='../src',
stub_dir='../include/typings'
)

View File

@ -0,0 +1,15 @@
import json
from c_bind import Library, set_linalg_converter
with open('../3rd/raylib/parser/output/raylib_api.json') as f:
data = json.load(f)
lib = Library.from_raylib(data)
set_linalg_converter('Vector2', 'vec2')
set_linalg_converter('Vector3', 'vec3')
lib.build(
includes=['raylib.h'],
glue_dir='../src',
stub_dir='../include/typings'
)

View File

View File

View File

@ -0,0 +1 @@
#define va_list int

View File

@ -0,0 +1 @@
#define bool _Bool

View File

@ -0,0 +1 @@
#define NULL ((void*)0)

View File

@ -0,0 +1,11 @@
#define size_t int
#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