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)
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)
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)
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))
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)
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
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)
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)
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)
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')
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)
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)
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)
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__, ))
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')
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')
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')
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))
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)
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)
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)
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))
def visit_Goto(self, node): ''' Goto Attrs: name ''' raise NotSupported('Not supporting GOTO - it is considered harmful.')