Пример #1
0
    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
Пример #2
0
    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))
Пример #3
0
    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
Пример #4
0
    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)
Пример #5
0
    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
Пример #6
0
    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
        ]
Пример #7
0
    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
Пример #8
0
    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)
Пример #9
0
    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)
Пример #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