def generate_function(self, elem: Cursor, depth=0, **kwargs): """Generate a type alias Examples -------- >>> from tide.generators.clang_utils import parse_clang >>> tu, index = parse_clang('float add(float a, float b);') >>> module = BindingGenerator().generate(tu) >>> print(compact(unparse(module))) <BLANKLINE> add = _bind('add', [c_float, c_float], c_float, arg_names=['a', 'b']) <BLANKLINE> """ log.debug(f'{d(depth)}Generate function `{elem.spelling}`') definition: Cursor = elem.get_definition() if definition is None: definition = elem rtype = self.generate_type(definition.result_type, depth + 1) args = definition.get_arguments() docstring = get_comment(elem) pyargs = [] arg_names = [] for a in args: atype = self.generate_type(a.type, depth + 1) pyargs.append(atype) if a.spelling != '': arg_names.append(T.Constant(a.spelling)) funnane = definition.spelling if not pyargs: pyargs = T.Name('None') else: pyargs = T.List(elts=pyargs) kwargs = [] if len(docstring) > 0: kwargs.append( T.Keyword('docstring', T.Constant(docstring, docstring=True))) if arg_names: kwargs.append(T.Keyword('arg_names', T.List(arg_names))) binding_call = T.Call(T.Name('_bind'), [T.Constant(funnane), pyargs, rtype], kwargs) return T.Assign([T.Name(funnane)], binding_call)
def get_references(cursor: clang.Cursor) -> typing.Generator[clang.Cursor, None, None]: """ Retrieves the reference dependencies from the AST node pointed to by the given cursor. :param cursor: The cursor pointing to the node. :return: The cursors of the nodes referred to by the node. """ # If the cursor is not a reference, there are no reference dependencies if not cursor.kind.is_reference(): yield from () referenced = cursor.referenced declaration = cursor.get_definition() if referenced is not None: yield referenced if declaration is not None and referenced != declaration: yield declaration
def parse_VAR_DECL(cursor: Cursor) -> ast.Node: assign: Optional[Cursor] = cursor.get_definition() value: Optional[ast.Expression] = None if assign is not None: got_eq: bool = False for t in assign.get_tokens(): if value is not None: logging.debug( 'c_compat: error: unhandled token while getting value: {}'. format(t.spelling)) elif got_eq: value = value_to_ehlit(t.spelling, cursor.type) elif t.spelling == '=': got_eq = True if got_eq is False: logging.debug('c_compat: error: unhandled assignment') typ: ast.Node = type_to_ehlit(cursor.type) assert isinstance(typ, ast.Symbol) return ast.VariableDeclaration( typ, ast.Identifier(0, cursor.spelling), ast.Assignment(value) if value is not None else None)
def generate_function(self, elem: Cursor): """Generate the Python function corresponding to the c function Examples -------- >>> from tide.generators.clang_utils import parse_clang >>> tu, index = parse_clang('double add(double a, double b);') >>> modules = APIGenerator().generate(tu) >>> for k, m in modules.items(): ... print(f'# {k}') ... print(unparse(m)) # temporary_buffer_1234.c <BLANKLINE> <BLANKLINE> <BLANKLINE> def add(a: double, b: double) -> double: return add(a, b) <BLANKLINE> """ definition: Cursor = elem.get_definition() if definition is None: definition = elem log.debug(f'Generate function {definition.spelling}') rtype = self.get_typename(definition.result_type) args = definition.get_arguments() pyargs = [] cargs = [] for a in args: atype = self.get_typename(a.type) aname = a.displayname pyargs.append(T.Arg(arg=aname, annotation=T.Name(atype))) cargs.append(T.Name(aname)) c_function = definition.spelling module, names = self.parse_name(c_function) fundef = T.FunctionDef(self.topyfunname(names), T.Arguments(args=pyargs)) fundef.returns = T.Name(rtype) fundef.body = [ # this is docstring but it is not unparsed correctly # T.Expr(T.Str(get_comment(elem))), T.Return(T.Call(T.Name(c_function), cargs)) ] # This could be a method if len(pyargs) > 0: # log.debug(f'Looking for {pyargs[0].annotation} in {self.type_registry}') selftype = pyargs[0].annotation classdef = None # For class grouping the first arg needs to be a reference to the class type lookuptype = selftype.id if isinstance(lookuptype, Ref): lookuptype = lookuptype.base classdef: T.ClassDef = self.type_registry.get(lookuptype) if isinstance(classdef, T.ClassDef): log.debug(f'found {classdef} for {lookuptype}') else: classdef = None if classdef is not None: # Remove annotation for `self` pyargs[0].arg = 'self' pyargs[0].annotation = None # replace the object by self.handle cargs[0] = T.Attribute(T.Name('self'), 'handle') # Generate Accessor properties if len(pyargs) == 1 and names[0] == 'get': # @property # def x(self): # return SDL_GetX(self.handle) offset = 1 if names[1] == 'set': offset = 2 fundef.name = self.topyfunname(names[offset:]) fundef.decorator_list.append(T.Name('property')) if len(pyargs) == 2 and (names[0] == 'get' or names[1] == 'get') and is_ref(pyargs[1].annotation): rtype = pyargs[1].annotation cargs[1] = T.Name('result') offset = 1 if names[1] == 'get': offset = 2 fundef.name = self.topyfunname(names[offset:]) fundef.decorator_list.append(T.Name('property')) fundef.returns = rtype fundef.args.args = [fundef.args.args[0]] fundef.body = [ # T.Expr(T.Str(get_comment(elem))), T.Assign([T.Name('result')], T.Call(rtype)), T.Expr(T.Call(T.Name(c_function), cargs)), # T.Assign([T.Name('err')], T.Call(T.Name(c_function), cargs)), # T.If(T.Name('err'), T.Return(T.Name('None'))), T.Return(T.Name('result')) ] if len(pyargs) == 2 and (names[0] == 'set' or names[1] == 'set'): # @x.setter # def x(self, x): # return SDL_SetX(self.handle, x) offset = 1 if names[1] == 'set': offset = 2 fundef.name = self.topyfunname(names[offset:]) fundef.decorator_list.append(T.Attribute(T.Name(fundef.name), 'setter')) # Standard method log.debug(f'Adding method to {classdef.name}') classdef.body.append(fundef) return None return fundef