Beispiel #1
0
    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"
Beispiel #2
0
    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()
Beispiel #3
0
    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)
Beispiel #4
0
    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
Beispiel #5
0
    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)
Beispiel #6
0
    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
Beispiel #7
0
    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('')
Beispiel #8
0
    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
Beispiel #9
0
    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)
Beispiel #10
0
    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