def generate(self, module: T.Module, depth=0): new_module: T.Module = ast.Module() new_module.body = [] self.preprocessor(module) for expr in module.body: self.dispatch(expr, depth + 1) # insert our new bindings at the end for k, v in self.new_code: if isinstance(v, T.ClassDef): if self.wrapper_method_count.get(v.name, 0) > 0: self.post_process_class_defintion(v) module.body.append(v) elif v.name in self.wrappers_2_ctypes: c_struct = self.wrappers_2_ctypes.get(v.name) # make it an alias for the ctype module.body.append( T.Expr(T.Assign([T.Name(v.name)], T.Name(c_struct)))) else: module.body.append(T.Expr(v)) return module
def add_docstring(self, new_fun: T.FunctionDef, binding: BindCall, indent_level: int): docstring = binding.docstring if docstring: docstring.value = docstring.value.replace( '\n ', '\n' + ' ' * indent_level) new_fun.body.insert(0, T.Expr(docstring))
def generate(self, tu, guard=None): module: T.Module = Module() module.body = [] children, builtin = sorted_children(tu.cursor) assert len(builtin) > 0 # Process every builtin macros for b in builtin: if b.kind == CursorKind.MACRO_DEFINITION: self.process_builtin_macros(b) # for k, v in self.definitions.items(): # print(k, v) # __BYTE_ORDER__ is defined by clang, __BYTE_ORDER is defined by other includes self.definitions['__BYTE_ORDER'] = self.definitions['__BYTE_ORDER__'] log.debug(f'Processing {len(children)} children') elem: Cursor for elem in children: loc: SourceLocation = elem.location if loc.file is not None and guard is not None and not str( loc.file.name).startswith(guard): continue try: expr = self.dispatch(elem) if expr is not None and not isinstance(expr, str): if isinstance(expr, list): for e in expr: module.body.append(T.Expr(e)) else: module.body.append(T.Expr(expr)) except Unsupported: log.debug(elem) pass return module
def parse_expression(self, depth=0): tok = self.peek() log.debug(f'{d(depth)} parse_expression {tok.kind} {tok.spelling}') if tok.spelling == '\\\n{': body = [] self.next() while tok.spelling != '\\\n}': p = T.Expr(self.parse_expression(depth + 1)) body.append(p) tok = self.peek() if tok.spelling == ';': self.next() tok = self.peek() self.next() return T.If(T.Constant(True), body) if is_operator(tok.spelling) and is_unary_operator(tok.spelling): p = self.parse_unary(depth + 1) else: p = self.parse_primary(depth + 1) tok = self.peek() if tok is None: return p # we are doing a cast or call (type(expr)) if tok.spelling == '(': self.next() expr = self.parse_call(p, depth + 1) if self.peek().spelling == ')': self.next() return expr # argument list do not try to parse operators if tok.spelling == ',' and self.is_call[-1]: return p if tok.spelling == ')': return p # cast (expr) <expr> if not is_operator(tok.spelling): return self.parse_cast(p, depth + 1) # if tok.spelling == ')': # return p return self.parse_expression_1(p, 0, depth + 1)
def generate(self, tu): from tide.generators.binding_generator import sorted_children elem: Cursor for elem in sorted_children(tu.cursor): loc: SourceLocation = elem.location self.module = self.modules.get(loc.file.name, T.Module(body=[])) self.modules[loc.file.name] = self.module #if not str(loc.file.name).startswith('/usr/include/SDL2'): # continue expr = None if elem.kind == CursorKind.FUNCTION_DECL: expr = self.generate_function(elem) elif elem.kind in (CursorKind.STRUCT_DECL, CursorKind.UNION_DECL): expr = self.generate_struct_union(elem) elif elem.kind == CursorKind.INCLUSION_DIRECTIVE: self.generate_include(elem) elif elem.kind == CursorKind.TYPEDEF_DECL: t1 = elem.type t2 = elem.underlying_typedef_type classdef = self.type_registry.get(t2.spelling) if classdef is not None: log.debug(f'{t1.spelling} = {classdef}') self.type_registry[t1.spelling] = classdef else: log.debug(f'typdef {t1.spelling} = {t2.spelling}') if expr is not None: self.module.body.append(T.Expr(expr)) return self.modules
def generate_struct_union(self, elem: Cursor, depth=1, nested=False, rename=None, **kwargs): """Generate a struct or union alias Examples -------- >>> from tide.generators.clang_utils import parse_clang >>> tu, index = parse_clang('struct Point { float x, y;};') >>> module = BindingGenerator().generate(tu) >>> print(compact(unparse(module))) <BLANKLINE> class Point(Structure): pass <BLANKLINE> Point._fields_ = [('x', c_float), ('y', c_float)] <BLANKLINE> >>> from tide.generators.clang_utils import parse_clang >>> tu, index = parse_clang('union Point { float x; int y;};') >>> module = BindingGenerator().generate(tu) >>> print(compact(unparse(module))) <BLANKLINE> class Point(Union): pass <BLANKLINE> Point._fields_ = [('x', c_float), ('y', c_int)] <BLANKLINE> """ log.debug(f'{d(depth)}Generate struct `{elem.spelling}`') pyname = self.get_name(elem, rename=rename, depth=depth + 1) base = 'Structure' if elem.kind == CursorKind.UNION_DECL: base = 'Union' # For recursive data structure # log.debug(pyname) self.type_registry[f'struct {pyname}'] = T.Name(pyname) self.type_registry[f'const struct {pyname}'] = T.Name(pyname) parent = pyname anonymous_renamed = self.find_anonymous_fields(elem, parent, depth=depth + 1) # Docstring is the first element of the body # T.Constant(get_comment(elem)) body = [] docstring = get_comment(elem) if docstring: body.append(T.Expr(T.Constant(docstring, docstring=True))) attrs = T.List() need_pass = len(body) attr: Cursor for attr in elem.get_children(): self.generate_field(body, attrs, attr, anonymous_renamed, depth) # insert pass even if we have a docstring because # we will remove the docstring later if need_pass == len(body): body.append(ast.Pass()) if not attrs.elts: return T.ClassDef(name=pyname, bases=[T.Name(base)], body=body) fields = T.Assign([T.Attribute(T.Name(pyname), '_fields_')], attrs) return [ T.ClassDef(name=pyname, bases=[T.Name(base)], body=body), fields ]
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
def expression(self, expr: T.Expr, depth): values = self.dispatch(expr.value, depth) if isinstance(values, (tuple, list)): return [T.Expr(v) for v in values] return T.Expr(values)
def pre_expression(self, expr: T.Expr, depth): values = self.preprocess(expr.value, depth) if isinstance(values, (tuple, list)): return [T.Expr(v) for v in values] return T.Expr(values)
def parse_expression_1(self, lhs, min_precedence, depth): lookahead = self.peek() precedence = fetch_precedence(lookahead.spelling) log.debug( f'{d(depth)} parse_expression_1 {lhs} {lookahead.kind} {lookahead.spelling}' ) while lookahead and precedence is not None and precedence >= min_precedence: op = lookahead self.next() rhs = self.parse_primary(depth + 1) lookahead = self.peek() if lookahead is None: break is_binary = is_binary_operator(lookahead.spelling) lookahead_pred = fetch_precedence(lookahead.spelling) is_right_asso = is_right_associative(lookahead.spelling) while lookahead and (is_binary and lookahead_pred > precedence ) or (is_right_asso and lookahead_pred == precedence): rhs = self.parse_expression_1(rhs, lookahead_pred, depth + 1) lookahead = self.peek() is_binary = is_binary_operator(lookahead.spelling) lookahead_pred = fetch_precedence(lookahead.spelling) is_right_asso = is_right_associative(lookahead.spelling) if lookahead.spelling == ')': break # the result of applying op with operands lhs and rhs pyop = fetch_python_op(op.spelling) if pyop is not None and not isinstance(pyop, str): if is_comparison(op.spelling): lhs = T.Compare(left=lhs, ops=[pyop()], comparators=[rhs]) elif is_bool(op.spelling): lhs = T.BoolOp(op=pyop(), values=[lhs, rhs]) else: lhs = T.BinOp(left=lhs, op=pyop(), right=rhs) elif pyop == 'if': raise UnsupportedExpression() elif pyop == 'assign': lhs = T.Assign(targets=[lhs], value=rhs) elif op.spelling == '[': lhs = T.Subscript(value=lhs, slice=T.Index(value=rhs), ctx=T.Load()) tok = self.peek() assert tok.spelling == ']' self.next() elif op.spelling == '->': if isinstance(rhs, T.Name): rhs = rhs.id lhs = T.Attribute(lhs, rhs, ctx=T.Load()) elif op.spelling == ',': lhs = T.If(T.Constant(True), [T.Expr(lhs), T.Return(rhs)]) else: show_elem(op) assert False precedence = fetch_precedence(lookahead.spelling) return lhs