def generate_enum(self, elem: Cursor): name = self.get_name(elem) values = [] for value in elem.get_children(): if value.kind == CursorKind.ENUM_CONSTANT_DECL: values.append(f'{self.get_name(value)} = {value.enum_value}') else: print('ERROR') show_elem(value) values = '\n'.join(values) return f"\nclass {name}(enum):\n pass\n\n{values}\n"
def get_underlying_type_uid(self, type: Type): if type.kind == TypeKind.POINTER: return self.get_underlying_type_uid(type.get_pointee()) if type.kind == TypeKind.TYPEDEF: return type.get_declaration().get_usr() if type.kind == TypeKind.ELABORATED: show_elem(type) print(type.get_declaration().get_usr()) return type.get_declaration().get_usr() return type.get_declaration().get_usr()
def parse_primary(self, depth): tok = self.peek() log.debug(f'{d(depth)} parse_primary {tok.kind} {tok.spelling}') # this can be a cast or a call # cast means that lhs is a type if tok.spelling == '(': self.next() expr = self.parse_expression(depth + 1) tok = self.peek() # lhs is a type if isinstance(expr, T.Name) and expr.id in self.registry: if tok.spelling == ')': self.next() expr = self.parse_cast(expr, depth + 1) elif tok and is_operator(tok.spelling): return self.parse_expression_1(expr, 0, depth=depth + 1) elif tok and tok.spelling != ')': expr = self.parse_cast(expr, depth + 1) tok = self.peek() # FIXME: why is the ) already eaten if tok: assert tok.spelling == ')' self.next() return expr # STRINGIFY arg operation # this is cannot be supported if tok.spelling == '#': self.next() next_tok = self.peek() if next_tok.kind == TokenKind.IDENTIFIER: raise UnsupportedExpression() handler = self.primary_dispatch.get(tok.kind, None) if handler is None: print('----') show_elem(tok) assert False self.next() return handler(tok, depth + 1)
def _generate_type(self, type: Type, depth=0): if type.kind == TypeKind.VOID: return 'None' if type.kind == TypeKind.POINTER and type.get_pointee().kind == TypeKind.VOID: return 'any' if type.kind == TypeKind.ELABORATED or type.kind == TypeKind.RECORD: return type.spelling.replace('struct', '').strip() if type.kind == TypeKind.POINTER: pointee: Type = type.get_pointee() if pointee.kind is TypeKind.TYPEDEF: return Ref(pointee.spelling) # in python every object is a pointer if pointee.kind in (TypeKind.RECORD, TypeKind.FUNCTIONPROTO, TypeKind.UNEXPOSED): return self._generate_type(pointee, depth + 1) if not pointee.is_pod(): show_elem(pointee) # for native types we need to keep ref because python will copy them return Ref(self._generate_type(pointee, depth + 1)) if type.kind == TypeKind.CHAR_S: return 'str' if type.is_const_qualified(): typename = type.spelling.replace('const ', '') return Const(typename) if type.kind == TypeKind.TYPEDEF: return type.spelling if type.kind == TypeKind.FUNCTIONPROTO or (type.kind == TypeKind.UNEXPOSED and type.get_canonical().kind == TypeKind.FUNCTIONPROTO): canon = type.get_canonical() rtype = canon.get_result() args = [] for arg in canon.argument_types(): args.append(self._generate_type(arg, depth + 1)) return Callable(args, self._generate_type(rtype, depth + 1)) # show_elem(type) return type.spelling
def dispatch(self, elem, depth=0, **kwargs): # log.debug(f'{d(depth)} {elem.kind}') fun = self.dispatcher.get(elem.kind, None) if fun is None: return show_elem(elem, print_fun=log.debug) return fun(elem, depth=depth + 1, **kwargs)
def generate_c_cast(self, elem, **kwargs): show_elem(elem) children = list(elem.get_children()) if len(children) == 2: type, expr = children traverse(type, print_fun=log.debug) traverse(expr, print_fun=log.debug) typecast = self.dispatch(type, **kwargs) if isinstance(typecast, str): typecast = T.Name(typecast) return T.Call(typecast, [self.dispatch(expr, **kwargs)]) if len(children) == 1: return self.dispatch(children[0], **kwargs) return None
def generate_field(self, body, attrs, attr, anonymous_renamed, depth, **kwargs): if attr.kind == CursorKind.FIELD_DECL: # Rename anonymous types uid = self.get_underlying_type_uid(attr.type) log.debug( f'{d(depth)}uid: {uid} {attr.type.spelling} {anonymous_renamed}' ) if uid in anonymous_renamed: parent, name = anonymous_renamed[uid] if parent: typename = T.Attribute(T.Name(parent), name) else: typename = T.Name(name) if attr.type.kind == TypeKind.POINTER: typename = T.Call(T.Name('POINTER'), [typename]) else: typename = self.generate_type(attr.type, depth + 1) pair = T.Tuple() pair.elts = [T.Constant(attr.spelling), typename] attrs.elts.append(pair) elif attr.kind in (CursorKind.UNION_DECL, CursorKind.STRUCT_DECL): nested_struct = self.generate_struct_union( attr, depth + 1, nested=True, rename=anonymous_renamed) body.append(nested_struct) elif attr.kind == CursorKind.PACKED_ATTR: for attr2 in attr.get_children(): self.generate_field(body, attrs, attr2, anonymous_renamed, depth + 1) # attrs.append(field) else: show_elem(attr) print('NESTED ', attr.kind) raise RuntimeError('')
def generate_macro_definition(self, elem: Cursor, **kwargs): """Transform a macro into a function if possible Examples -------- >>> from tide.generators.clang_utils import parse_clang >>> tu, index = parse_clang('#define PI 3.14') >>> module = BindingGenerator().generate(tu) >>> print(compact(unparse(module))) <BLANKLINE> PI = 3.14 <BLANKLINE> >>> tu, index = parse_clang('' ... '#define SDL_AUDIO_ALLOW_FREQUENCY_CHANGE 0x00000001\\n' ... '#define SDL_AUDIO_ALLOW_FORMAT_CHANGE 0x00000002\\n' ... '#define SDL_AUDIO_ALLOW_CHANNELS_CHANGE 0x00000004\\n' ... '#define SDL_AUDIO_ALLOW_ANY_CHANGE (SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_FORMAT_CHANGE|SDL_AUDIO_ALLOW_CHANNELS_CHANGE)\\n' ... ) >>> module = BindingGenerator().generate(tu) >>> print(compact(unparse(module))) <BLANKLINE> SDL_AUDIO_ALLOW_FREQUENCY_CHANGE = 1 <BLANKLINE> SDL_AUDIO_ALLOW_FORMAT_CHANGE = 2 <BLANKLINE> SDL_AUDIO_ALLOW_CHANNELS_CHANGE = 4 <BLANKLINE> SDL_AUDIO_ALLOW_ANY_CHANGE = (SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | (SDL_AUDIO_ALLOW_FORMAT_CHANGE | SDL_AUDIO_ALLOW_CHANNELS_CHANGE)) <BLANKLINE> """ # builtin macros if elem.location.file is None: return log.debug(f'Macro definition {elem.spelling}') args = list(elem.get_arguments()) children = list(elem.get_children()) tokens = list(elem.get_tokens()) if len(args) != 0: for arg in args: show_elem(arg, print_fun=log.debug) assert False if len(children) != 0: for child in children: show_elem(child, print_fun=log.debug) assert False if len(tokens) == 1: return name, tok_args, tok_body = parse_macro(tokens) if len(tok_body) == 0: return if name.spelling == 'NULL': return T.Assign([T.Name('NULL')], T.Name('None')) try: bods = {t.spelling for t in tok_body} if not bods.isdisjoint(self.unsupported_macros): raise UnsupportedExpression() py_body = parse_macro2(name, tok_args, tok_body, self.definitions, self.type_registry, self.renaming) except UnsupportedExpression: self.unsupported_macros.add(name.spelling) body = [b.spelling for b in tok_body] log.warning( f'Unsupported expression, cannot transform macro {name.spelling} {"".join(body)}' ) return name = name.spelling # if name == 'SDL_TOUCH_MOUSEID': # print(py_body) # assert False if len(tok_args) == 0 and not isinstance(py_body, T.If): return T.Assign([T.Name(name)], py_body) func = T.FunctionDef( name, T.Arguments(args=[T.Arg(arg=a.spelling) for a in tok_args])) if isinstance(py_body, T.Expr): py_body = py_body.value # we use ifs as a makeshift Body expression if isinstance(py_body, T.If) and isinstance(py_body.test, T.Constant) \ and py_body.test.value is True: func.body = py_body.body else: func.body = [T.Return(py_body)] return func
def _generate_type(self, type: Type, depth=0): # print(type.kind, type.spelling, self.type_registry.get(type.spelling, 'NOT FOUND')) if type.kind == TypeKind.VOID: return T.Name('None') if type.kind == TypeKind.INT: return T.Name('c_int') if type.kind == TypeKind.POINTER and type.get_pointee( ).kind == TypeKind.VOID: return T.Name('c_void_p') if type.kind == TypeKind.POINTER and type.get_pointee( ).kind == TypeKind.CHAR_S: return T.Name('c_char_p') if type.kind == TypeKind.POINTER: pointee: Type = type.get_pointee() # Typedef use the name that it is aliased to if pointee.kind is TypeKind.TYPEDEF: pointee = get_typename(pointee) elif pointee.kind != TypeKind.VOID: pointee = self.generate_type(pointee, depth + 1) else: pointee = T.Name('c_void_p') # Function pointer do not need to be decorated by POINTER call if isinstance(pointee, T.Call) and isinstance( pointee.func, T.Name) and pointee.func.id == 'CFUNCTYPE': return pointee # for native types we need to keep ref because python will copy them return T.Call(T.Name('POINTER'), [pointee]) if type.kind == TypeKind.CHAR_S: return T.Name('c_char_p') # if type.is_const_qualified(): # return T.Name(get_typename(type)) if type.kind == TypeKind.TYPEDEF: return get_typename(type) if type.kind == TypeKind.FUNCTIONPROTO or ( type.kind == TypeKind.UNEXPOSED and type.get_canonical().kind == TypeKind.FUNCTIONPROTO): # SDL_HitTest = CFUNCTYPE(SDL_HitTestResult, POINTER(SDL_Window), POINTER(SDL_Point), c_void_p) canon = type if type.kind == TypeKind.UNEXPOSED: canon = type.get_canonical() rtype = canon.get_result() args = [] for arg in canon.argument_types(): args.append(self.generate_type(arg, depth + 1)) returntype = self.generate_type(rtype, depth + 1) cargs = [returntype] cargs.extend(args) return T.Call(T.Name('CFUNCTYPE'), args=cargs) if type.kind == TypeKind.CONSTANTARRAY: t = self.generate_type(type.element_type, depth + 1) return T.BinOp(t, ast.Mult(), T.Constant(type.element_count)) # Represents a C array with an unspecified size. if type.kind == TypeKind.INCOMPLETEARRAY: element_type = self.generate_type(type.element_type, depth + 1) # char *[] -> char ** return T.Call(T.Name('POINTER'), [element_type]) # struct <TYPENAME> if type.kind == TypeKind.ELABORATED: return T.Name(get_typename(type).id.replace('struct', '').strip()) if type.kind == TypeKind.ENUM: return get_typename(type) # print('gentype') show_elem(type, print_fun=log.debug) return get_typename(type)
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