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_Slice(self, node): args = [] if node.lower is None: args.append(Const('None')) else: args.append(self.visit_expr(node.lower)) if node.upper is None: args.append(Const('None')) else: args.append(self.visit_expr(node.upper)) if node.step is None: args.append(Const('None')) else: args.append(self.visit_expr(node.step)) return Op('Slice', *args)
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_expr(self, node, allowlist=False, allownone=False): res = self.visit(node) if isinstance(res, list) and allowlist: ok = True for r in res: if not isinstance(r, Expr): ok = False break if ok: return res if res and not isinstance(res, Expr): raise ParseError("Expected expression, got '%s'" % (res, ), line=node.coord.line) if (not res) and (not allownone): if node: self.addwarn("Expression expected at line %s" % (node.coord.line, )) else: self.addwarn("Expression expected") res = Const('?') return res
def visit_Return(self, node): if node.value is None: ret = Const('None', line=node.lineno) else: ret = self.visit_expr(node.value) self.addexpr(VAR_RET, ret)
def visit_Constant(self, node): ''' Constant Attrs: type, value ''' return Const(node.value, line=node.coord.line)
def visit_Cast(self, node): ''' Expression case Attrs: to_type, expr ''' tt = self.visit(node.to_type) expr = self.visit_expr(node.expr) return Op('cast', Const(tt), expr, line=node.coord.line)
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_ID(self, node): ''' ID Attrs: name ''' if node.name in self.CONSTS: return Const(node.name, line=node.coord.line) return Var(node.name, line=node.coord.line)
def visit_Return(self, node): ''' Return node Attrs: expr ''' expr = self.visit_expr(node.expr) if not expr: expr = Const('top', line=node.coord.line) self.addexpr(VAR_RET, expr)
def visit_printf(self, node, args): ''' printf function call ''' # Extract format and args if len(args) == 0: self.addwarn("'printf' with zero args at line %s" % ( node.coord.line,)) fmt = Const('?', line=node.coord.line) else: if isinstance(args[0], Const): fmt = args[0] args = args[1:] else: self.addwarn("First argument of 'printf' at lines %s should \ be a format" % (node.coord.line,)) fmt = Const('?', line=node.coord.line) fmt.value = fmt.value.replace('%lf', '%f') fmt.value = fmt.value.replace('%ld', '%d') fmt.value = fmt.value.replace('%lld', '%d') expr = Op('StrAppend', Var(VAR_OUT), Op('StrFormat', fmt, *args, line=node.coord.line), line=node.coord.line) self.addexpr(VAR_OUT, expr)
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_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_loop(self, node, init, cond, next, body, do, name, prebody=None): # Visit init stmts if init: self.visit(init) # Add condition (with new location) preloc = self.loc if isinstance(cond, Expr): condexpr = cond else: condexpr = self.visit_expr(cond, allowlist=True) if isinstance(condexpr, list): condexpr = self.expr_list_and(condexpr) if not condexpr: condexpr = Const('1') condloc = self.addloc("the condition of the '%s' loop at line %s" % (name, condexpr.line or self.getline(node))) self.addexpr(VAR_COND, condexpr) # Add exit loc exitloc = self.addloc("*after* the '%s' loop starting at line %d" % (name, self.getline(node))) # Add body with (new location) bodyloc = self.addloc( "inside the body of the '%s' loop beginning at line %d" % (name, self.getline(body) or self.getline(node))) self.addloop((condloc, exitloc)) if prebody: map(lambda x: self.addexpr(*x), prebody) self.visit(body) if next: self.visit(next) self.poploop() afterloc = self.loc # Connect transitions self.addtrans(preloc, True, bodyloc if do else condloc) self.addtrans(condloc, True, bodyloc) self.addtrans(condloc, False, exitloc) self.addtrans(afterloc, True, condloc) self.loc = exitloc
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_Attribute(self, node): value = self.visit(node.value) return Op('GetAttr', value, Const(node.attr), line=node.lineno)
def visit_Str(self, node): return Const('"{}"'.format(node.s), line=node.lineno)
def visit_Name(self, node): if node.id in self.CONSTS: return Const(node.id) self.addtype(node.id, '*') return Var(node.id)