예제 #1
0
    def visit_FuncCall(self, node):
        '''
        FuncCall
        Attrs: name, args
        '''

        # Get (and check) name
        name = self.visit_expr(node.name)
        if not isinstance(name, Var):
            raise NotSupported("Non-var function name: '%s'" % (name,),
                               line=name.line)

        # Parse args
        args = self.visit(node.args) or []

        # Special cases (scanf & printf)
        if name.name == 'scanf':
            return self.visit_scanf(node, args)

        elif name.name == 'printf':
            return self.visit_printf(node, args)

        # Program functions
        elif name.name in self.fncs:
            return Op('FuncCall', name, *args, line=node.coord.line)

        # Library functions
        elif name.name in self.LIB_FNCS:
            return Op(name.name, *args, line=node.coord.line)

        else:
            raise NotSupported(
                "Unsupported function call: '%s'" % (name.name,),
                line=node.coord.line)
예제 #2
0
    def visit_AugAssign(self, node):

        # Aug assign to a name
        if isinstance(node.target, ast.Name):
            target = Var(node.target.id)
            self.addtype(node.target.id, '*')
            value = self.visit_expr(node.value)
            rhs = Op(node.op.__class__.__name__,
                     target,
                     value,
                     line=node.lineno)
            self.addexpr(target.name, rhs)

        # Aug assign to a index
        elif isinstance(node.target, ast.Subscript):
            if isinstance(node.target.slice, ast.Index):
                var = Var(node.target.value.id)
                right = self.visit_expr(node.value)
                index = self.visit_expr(node.target.slice.value)
                rhs = Op(node.op.__class__.__name__,
                         self.visit(node.target),
                         right,
                         line=node.lineno)
                self.addexpr(
                    var.name,
                    Op('AssignElement', var, index, rhs, line=node.lineno))

            else:
                raise NotSupported(
                    'Subscript assignments only allowed to Indices',
                    line=node.lineno)
        else:
            raise NotSupported('Assignments to {} not supported'.format(
                node.target.__class__.__name__),
                               line=node.lineno)
예제 #3
0
    def visit_UnaryOp(self, node):
        '''
        UnaryOp - unary operation
        Attrs: op, expr
        '''

        expr = self.visit_expr(node.expr)

        # Special cases
        # ++/--
        if node.op in ['++', '--']:
            if not isinstance(expr, Var):
                raise NotSupported('++/-- supported only for Vars',
                                   line=node.coord.line)
            self.addexpr(expr.name,
                         Op(node.op[1], expr.copy(), Const('1'),
                            line=node.coord.line))
            return expr
            
        elif node.op in ['p++', 'p--']:
            if not isinstance(expr, Var):
                raise NotSupported('p++/p-- supported only for Vars',
                                   line=node.coord.line)
            self.addexpr(expr.name,
                         Op(node.op[1], expr.copy(), Const('1'),
                            line=node.coord.line))
            self.postincdec += 1
            return expr

        return Op(node.op, expr, line=node.coord.line)
예제 #4
0
    def visit_Expr(self, node):
        '''
        Expressions need to be handled depending on their type.
        Method calls like str.lower() can be ignored
        (Strings are immutable), while
        calls like list.append() must be handled.
        '''
        if isinstance(node.value, ast.Call):
            if isinstance(node.value.func, ast.Name):
                self.warns.append('Ignored call to {} at line {}'.format(
                    node.value.func.id, node.lineno))

            elif isinstance(node.value.func, ast.Attribute):
                if node.value.func.attr in self.ATTR_FNCS:
                    call = self.visit_expr(node.value)
                    if isinstance(node.value.func.value, ast.Subscript):
                        var = self.visit_expr(node.value.func.value.value)
                        index = self.visit_expr(node.value.func.value.slice)
                        expr = Op('AssignElement',
                                  var,
                                  index,
                                  call,
                                  line=node.lineno)
                        if isinstance(var, Var):
                            self.addexpr(var.name, expr)
                        else:
                            raise NotSupported("Non-name element assignment",
                                               line=node.lineno)

                    elif isinstance(node.value.func.value, ast.Name):
                        var = self.visit(node.value.func.value)
                        if isinstance(var, Var):
                            # Skip assignment of 'pop' function
                            if node.value.func.attr != 'pop':
                                self.addexpr(var.name, call)
                        else:
                            raise NotSupported("Non-name call",
                                               line=node.lineno)

                    else:
                        raise NotSupported('Call to {}'.format(
                            node.value.func.__class__.__name__),
                                           line=node.lineno)
                else:
                    self.warns.append('Ignored call to {} at line {}'.format(
                        node.value.func.attr, node.lineno))
            else:
                raise NotSupported('Call to {}'.format(
                    node.value.func.__class__.__name__),
                                   line=node.lineno)

        elif isinstance(node.value,
                        (ast.Num, ast.Str, ast.List, ast.Tuple, ast.Dict)):
            # Happily ignore these side-effect free statements
            pass
        else:
            self.warns.append('Ignored Expr of type {} at line {}'.format(
                node.value.__class__.__name__, node.lineno))
예제 #5
0
    def visit_Assign(self, node):
        if len(node.targets) != 1:
            raise NotSupported('Only single assignments allowed')

        target = node.targets[0]

        # Assignment to a variable
        if isinstance(target, ast.Name):
            right = self.visit_expr(node.value)
            self.addtype(target.id, '*')
            self.addexpr(target.id, right)

        # Assignment to indexed element
        elif isinstance(target, ast.Subscript):
            if isinstance(target.slice, ast.Index):
                var = Var(target.value.id)
                self.addtype(target.value.id, '*')
                right = self.visit_expr(node.value)
                index = self.visit_expr(target.slice.value)

                self.addexpr(
                    target.value.id,
                    Op('AssignElement', var, index, right, line=right.line))

            else:
                raise NotSupported(
                    'Subscript assignments only allowed to Indices',
                    line=node.lineno)

        # Assignment to a tuple
        elif isinstance(target, ast.Tuple):
            targets = map(self.visit_expr, target.elts)
            right = self.visit_expr(node.value)
            for i, target in enumerate(targets):
                expr = Op('GetElement',
                          right.copy(),
                          Const(str(i)),
                          line=right.line)
                if isinstance(target, Var):
                    self.addexpr(target.name, expr)
                else:
                    raise NotSupported("Tuple non-var assignment",
                                       line=target.line)

        else:
            raise NotSupported('Assignments to {} not supported'.format(
                target.__class__.__name__),
                               line=node.lineno)
예제 #6
0
    def visit_Assignment(self, node):
        '''
        Assignment
        Attrs: op, lvalue, rvalue
        '''

        lvalue = self.visit_expr(node.lvalue)
        postincdec = self.postincdec
        self.postincdec = 0
        rvalue = self.visit(node.rvalue)
        postincdec, self.postincdec = self.postincdec, postincdec

        if not rvalue:
            rvalue = Const('?', line=node.coord.line)
        
        # Cases of assignment operator
        if node.op == '=':
            pass
        elif len(node.op) == 2 and node.op[1] == '=':
            rvalue = Op(node.op[0], lvalue.copy(), rvalue, line=rvalue.line)
        else:
            raise NotSupported("Assignment operator: '%s'" % (node.op,),
                               line=node.coord.line)

        # Distinguish lvalue (ID and Array)
        if isinstance(lvalue, Var):
            lval = lvalue
        
        elif (isinstance(lvalue, Op) and lvalue.name == '[]'and
              isinstance(lvalue.args[0], Var)):
            rvalue = Op('ArrayAssign', lvalue.args[0].copy(),
                        lvalue.args[1].copy(), rvalue, line=node.coord.line)
            lval = lvalue.args[0]

        else:
            raise NotSupported("Assignment lvalue '%s'" % (lvalue,),
                               line=node.coord.line)

        # List of expression
        if isinstance(rvalue, list):
            rvalue = rvalue[-1]
        
        # Special case when previous assignment was p++/p--
        # push this assignment before the previous one
        self.addexpr(lval.name, rvalue.copy(),
                     idx=-postincdec if postincdec else None)

        return lvalue
예제 #7
0
 def visit_Num(self, node):
     if type(node.n) in (int, float, long, complex):
         return Const(str(node.n), line=node.lineno)
     else:
         raise NotSupported('Type {} not supported'.format(
             type(node.n).__name__),
                            line=node.lineno)
예제 #8
0
    def visit_Decl(self, node):
        '''
        Decl - Declaration
        Attrs: name, quals, storage, funcspec, type, init, bitsize
        (using only: name, type & init)
        '''

        (name, type, dim) = self.visit(node.type)
        init = self.visit_expr(node.init, allownone=True)

        if not self.fncdef:
            try:
                self.addtype(name, type)
            except AssertionError:
                self.addwarn("Ignored global definition '%s' on line %s." % (
                    name, node.coord.line,))
                return

        if init and dim:
            raise NotSupported("Array Init & Create together",
                               line=node.coord.line)

        if init:
            self.addexpr(name, init)
            
        if dim:
            self.addexpr(name, Op('ArrayCreate', dim, line=dim.line))

        return (name, type, dim)
예제 #9
0
    def visit_comprehension(self, node):

        if node.ifs is None or len(node.ifs) == 0:
            ifs = Const('True')
        elif len(node.ifs) == 1:
            ifs = self.visit_expr(node.ifs[0])
        else:
            raise NotSupported("Comprehension multiple ifs")

        target = self.visit(node.target)
        if not self.islistofnames(target):
            raise NotSupported("Target is not a list of names")

        iter = self.visit(node.iter)

        return Op('Comp', target, iter, ifs)
예제 #10
0
    def visit_While(self, node):

        if node.orelse:
            raise NotSupported("While-Else not supported",
                               line=self.getline(node.orelse))

        self.visit_loop(node, None, node.test, None, node.body, False, 'while')
예제 #11
0
    def visit_Call(self, node):
        if len(node.keywords) > 0 or node.starargs or node.kwargs:
            raise NotSupported(
                'starargs, kwargs and keyword arguments not supported',
                line=node.lineno)

        if isinstance(node.func, ast.Name):
            if node.func.id in self.BUILTIN_FNCS:
                fncname = node.func.id
                args = list(map(self.visit_expr, node.args))
                return Op(fncname, *args, line=node.lineno)
            else:
                fnc = Var(node.func.id)
                args = list(map(self.visit_expr, node.args))
                return Op('FuncCall', fnc, *args, line=node.lineno)

        elif isinstance(node.func, ast.Num):
            num = self.visit_expr(node.func)
            self.addwarn("Call to a number '%s' on line %s ignored", num,
                         num.line)
            return Const('?')

        elif isinstance(node.func, ast.Attribute):
            attr = node.func.attr
            val = self.visit_expr(node.func.value)
            args = list(map(self.visit_expr, node.args))
            if isinstance(val, Var) and val.name in self.MODULE_NAMES:
                return Op('%s_%s' % (val, attr), *args, line=node.lineno)
            if attr == 'pop':
                if isinstance(val, Var):
                    popvar = self.ssavar('pop#')
                    self.addexpr(popvar, Op(attr, val, *args,
                                            line=node.lineno))
                    self.addexpr(val.name,
                                 Op('GetElement', Var(popvar), Const('0')))
                    return Op('GetElement', Var(popvar), Const('1'))
                else:
                    raise NotSupported('Pop to a non-name list')
            return Op(attr, val, *args, line=node.lineno)

        else:
            raise NotSupported('Call of {} not supported'.format(
                node.func.__class__.__name__),
                               line=node.lineno)
예제 #12
0
    def visit_SetComp(self, node):
        elt = self.visit_expr(node.elt)

        if len(node.generators) != 1:
            raise NotSupported("Only one generator supported",
                               line=node.lineno)

        gen = self.visit_expr(node.generators[0])

        return Op('SetComp', elt, gen, line=node.lineno)
예제 #13
0
    def visit_DictComp(self, node):
        key = self.visit_expr(node.key)
        value = self.visit_expr(node.value)

        if len(node.generators) != 1:
            raise NotSupported("Only one generator supported",
                               line=node.lineno)

        gen = self.visit_expr(node.generators[0])

        return Op('DictComp', key, value, gen, line=node.lineno)
예제 #14
0
    def visit_Delete(self, node):
        if len(node.targets) > 1:
            raise NotSupported('Multiple delete targets')
        target = self.visit(node.targets[0])

        if isinstance(target, Op):
            if target.name == 'GetElement':
                if isinstance(target.args[0], Var):
                    if len(target.args) != 2:
                        raise NotSupported('Delete target with %d args' %
                                           (len(target.args, )))
                    delexpr = Op('Delete', *target.args, line=node.lineno)
                    self.addexpr(target.args[0].name, delexpr)
                else:
                    raise NotSupported('Delete target not Var, but %s' %
                                       (target.args[0].__class__))
            else:
                raise NotSupported('Delete target op: %s' % (target.name, ))
        else:
            raise NotSupported('Delete target: %s' % (target.__class__, ))
예제 #15
0
    def visit_While(self, node):
        '''
        While
        Attrs: cond, stmt
        '''

        if self.inswitch:
            raise NotSupported("Loop inside switch", line=node.coord.line)

        self.visit_loop(node, None, node.cond, None, node.stmt,
                        False, 'while')
예제 #16
0
    def visit_For(self, node):
        '''
        For
        Attrs: init, cond, next, stmt
        '''

        if self.inswitch:
            raise NotSupported("Loop inside switch", line=node.coord.line)

        self.visit_loop(node, node.init, node.cond, node.next, node.stmt,
                        False, 'for')
예제 #17
0
    def visit_DoWhile(self, node):
        '''
        DoWhile loop
        Attrs: cond, stmt
        '''

        if self.inswitch:
            raise NotSupported("Loop inside switch", line=node.coord.line)

        self.visit_loop(node, None, node.cond, None, node.stmt,
                        True, 'do-while')
예제 #18
0
    def visit_ArrayDecl(self, node):
        '''
        ArrayDecl - Array declaration
        Attrs: type, dim, dim_quals (ignored)
        '''

        (name, type, dim) = self.visit(node.type)

        if dim is not None or type.endswith('[]'):
            raise NotSupported('Double Array', line=node.coord.line)
        
        type += '[]'
        return (name, type, self.visit_expr(node.dim))
예제 #19
0
    def visit_ArrayRef(self, node):
        '''
        Array reference
        Attrs: name, subscript
        '''

        name = self.visit_expr(node.name)
        if not isinstance(name, Var):
            raise NotSupported("ArrayName: '%s'" % (name,))

        sub = self.visit_expr(node.subscript)

        return Op('[]', name, sub, line=node.coord.line)
예제 #20
0
    def visit_Switch(self, node):
        '''
        Switch statement
        Attrs: cond, stmt
        '''

        # Parse condition
        condexpr = self.visit_expr(node.cond)

        # Check that stmt is a compound of "case"/"defaults"
        # and covert to "if-then-else"
        # TODO: Check only one "case"/"default"
        if isinstance(node.stmt, c_ast.Compound):

            n = len(node.stmt.block_items)

            def convert(i):

                if i >= n:
                    return

                item = node.stmt.block_items[i]

                # Item statement
                stmt = (c_ast.Compound(item.stmts, coord=item.coord)
                        if isinstance(item.stmts, list) else item.stmts)

                if i == (n - 1) and isinstance(item, c_ast.Default):
                    return stmt
                    
                if isinstance(item, c_ast.Case):
                    next = convert(i + 1)

                    ifcond = c_ast.BinaryOp('==', node.cond, item.expr,
                                            coord=item.expr.coord)
                    return c_ast.If(ifcond, stmt, next, coord=item.expr.coord)

            stmt = convert(0)
            if stmt:
                insw = self.inswitch
                self.inswitch = True
                
                res = self.visit(stmt)
                
                self.inswitch = insw
                
                return res

        # Otherwise not-supported
        raise NotSupported("Switch statement", line=node.coord.line)
예제 #21
0
    def visit_For(self, node):

        if node.orelse:
            raise NotSupported("For-Else not supported",
                               line=self.getline(node.orelse))

        # Iterated expression
        it = self.visit_expr(node.iter)

        # Targets of iteration
        if isinstance(node.target, ast.Name):
            targets = [self.visit_expr(node.target)]
        elif isinstance(node.target, ast.Tuple):
            targets = map(self.visit_expr, node.target.elts)
        else:
            raise NotSupported('For loop with {} as target'.format(
                node.target.__class__.__name__),
                               line=node.lineno)

        hiddenvar = self.hiddenvarcnt
        self.hiddenvarcnt += 1

        # Set up the iterated variable
        iter_name = 'iter#{}'.format(hiddenvar)
        it_var = Var(iter_name)
        self.addtype(iter_name, '*')

        # Set up the iteration index
        ind_name = 'ind#{}'.format(hiddenvar)
        ind_var = Var(ind_name)
        self.addtype(ind_name, 'int')

        # Add assignments to iterators
        self.addexpr(it_var.name, it)
        self.addexpr(ind_var.name, Const(str(0), line=node.lineno))

        # Condition is ind_var < len(iter_var)
        cond = Op('Lt',
                  ind_var.copy(),
                  Op('len', it_var.copy()),
                  line=node.iter.lineno)

        # Assignments to iterated variable(s)
        prebody = []
        el = Op('GetElement',
                it_var.copy(),
                ind_var.copy(),
                line=node.target.lineno)
        if len(targets) == 1:
            prebody.append((targets[0].name, el.copy()))
        else:
            for i, t in enumerate(targets):
                eli = Op('GetElement',
                         el.copy(),
                         Const(str(i)),
                         line=node.target.lineno)
                prebody.append((t.name, eli))

        # Add index variable increment
        prebody.append((ind_var.name,
                        Op('Add',
                           ind_var.copy(),
                           Const(str(1)),
                           line=node.iter.lineno)))

        self.visit_loop(node,
                        None,
                        cond,
                        None,
                        node.body,
                        False,
                        'for',
                        prebody=prebody)
예제 #22
0
    def visit_scanf(self, node, args):
        '''
        scanf function call
        '''

        # Check format
        if len(args) == 0:
            self.warn("'scanf' without arguments at line %s (ignored)",
                      node.coord.line)
        else:
            fmt = args[0]

            if isinstance(fmt, Const) and fmt.value[0] == '"' \
               and fmt.value[-1] == '"':
                fmt = fmt.value[1:-1]
                args = args[1:]
            else:
                self.warn("First argument of 'scanf' at line %s should be a \
(string) format (ignored)",
                          node.coord.line)
                fmt = ''
                args = []

        # Extract format arguments
        fs = list(re.findall(
            r'(%((d)|(i)|(li)|(lli)|(ld)|(lld)|(lf)|(f)|(s)|(c)))', fmt))

        # Check argument number
        if len(fs) != len(args):
            self.addwarn("Mismatch between format and number of argument(s)\
of 'scanf' at line %s.",
                         node.coord.line)

            if len(args) > len(fs):
                fs += ['*' for _ in xrange(len(args) - len(fs))]

        # Iterate formats and arguments
        for f, a in zip(fs, args):

            if f:
                f = f[0]

            # Get type from an argument
            if f in ['%d', '%ld', '%i', '%li', '%lli', '%lld']:
                t = 'int'
            elif f in ['%c']:
                t = 'char'
            elif f in ['%s']:
                t = 'string'
            elif f in ['%f', '%lf']:
                t = 'float'
            elif f == '*':
                t = '*'
            else:
                self.addwarn("Invalid 'scanf' format at line %s.",
                             node.coord.line)
                t = '*'

            # Check argument type
            if isinstance(a, Op) and a.name == '&' and len(a.args) == 1:
                a = a.args[0]

            elif isinstance(a, Var) or (isinstance(a, Op) and a.name == '[]'):
                self.addwarn("Forgoten '&' in 'scanf' at line %s?",
                             node.coord.line)

            else:
                raise NotSupported("Argument to scanf: '%s'" % (a,),
                                   line=node.coord.line)

            # Add operations
            rexpr = Op('ListHead', Const(t), Var(VAR_IN), line=node.coord.line)
            if isinstance(a, Var):
                self.addexpr(a.name, rexpr)
            elif isinstance(a, Op) and a.name == '[]' and isinstance(a.args[0],
                                                                     Var):
                self.addexpr(a.args[0].name,
                             Op('ArrayAssign', a.args[0], a.args[1], rexpr,
                                line=node.coord.line))
            else:
                raise NotSupported("Argument to scanf: '%s'" % (a,),
                                   line=node.coord.line)
            self.addexpr(VAR_IN,
                         Op('ListTail', Var(VAR_IN), line=node.coord.line))
예제 #23
0
 def visit_Goto(self, node):
     '''
     Goto
     Attrs: name
     '''
     raise NotSupported('Not supporting GOTO - it is considered harmful.')