def analyze_tree(tree, category): if isinstance(tree, list): subtrees = [analyze_tree(subtree, category) for subtree in tree] return If(eparse('True'), subtrees, []) elif isinstance(tree, dict): if len(tree) != 1: raise ValueError('len(dict) != 1') rule, subtree = tree.items()[0] r = analyze_rule(rule) if isinstance(r, AST): test = r return If(test, [analyze_tree(subtree, category)], []) else: dueling_category_check(category, r) category = r return analyze_tree(subtree, category) else: rule = tree r = analyze_rule(rule) if isinstance(r, AST): test = r if category is None: raise ValueError('bottomed out without category') return If(test, [Return(Str(category))], []) else: dueling_category_check(category, r) category = r return Return(Str(category))
def body_helper(self, body): from ast import expr, Return # place return in the last expression of body if isinstance(body, expr): body = [Return(body)] elif isinstance(body, list) and not isinstance(body[-1], Return): body[-1] = Return(body[-1]) return body
def _if_pattern(self, pattern): self.imported.add('re') # fix known ietf regex use pattern = pattern.replace('\\p{N}\\p{L}', '\\w') return [ If( test=UnaryOp( op=Not(), operand=Call( func=Attribute( value=Name(id='re', ctx=Load()), attr='match', ctx=Load(), ), args=[ Constant(value=pattern, kind=None), Name(id='value', ctx=Load()), Attribute( value=Name(id='re', ctx=Load()), attr='UNICODE', ctx=Load(), ), ], keywords=[], ), ), body=[ Return(value=Constant(value=False, kind=None), ), ], orelse=[], ), ]
def visit_let(self, letexp: Expr): # To properly account for scoping and ensure that the entire node produces an expression, # we translate the let binding as a function that we call with the value we intend to bind. # Yes, this is somewhat ugly. """ let var = value in body ======================= def let_thunk(var): return body let_thunk(value) """ bind_body, bind_defs = self.visit(letexp.body) func_name = self.generate_function_name("_let_func") binding_func = self.create_def(func_name, [self.get_var_name(letexp.var)], bind_defs + [Return(bind_body)]) # we call the binding func with the intended value for the bound variable # special case: if the value is a function literal, we must ensure it can be # recursive by naming it after the var if isinstance(letexp.value, Function): value_def, value_name = self.convert_func_node( letexp.value, letexp.var) return ( self.create_call(func_name, [Name(value_name, Load())]), [value_def, binding_func], ) value_body, value_defs = self.visit(letexp.value) value_defs.append(binding_func) binding_call = self.create_call(func_name, [value_body]) return (binding_call, value_defs)
def _if_gt(value): if value >= 0: comparators = [Constant(value=value, kind=None)] else: comparators = [ UnaryOp( op=USub(), operand=Constant(value=abs(value), kind=None), ), ] return [ If( test=Compare( left=Call( func=Name(id='int', ctx=Load()), args=[Name(id='value', ctx=Load())], keywords=[], ), ops=[Gt()], comparators=comparators, ), body=[ Return(value=Constant(value=False, kind=None), ), ], orelse=[], ) ]
def visit_match(self, match: Expr): """For matches, we wrap the entire expression in a thunk because it is easiest to implement them using if statements. For each clause, we generate a function that checks if the pattern matches. If yes, we call a function that assigns the variables appropriately and invokes the clause body.""" data, defs = self.visit(match.data) data_var = self.generate_var_name("_match_data") # must ensure the data clause is executed exactly once thunk_body = [Assign([Name(data_var, Store())], data)] for clause in match.clauses: check_expr = self.create_match_check(clause.lhs, Name(data_var, Load())) body_def, body_name = self.create_match_clause_body( clause.lhs, clause.rhs) defs.append(body_def) # equiv: if check(data): return body(data) thunk_body.append( ast.If(check_expr, [ Return( self.create_call(body_name, [Name(data_var, Load())])) ], [])) # finally if nothing matches we have a failed assert (should never happen) thunk_body.append( ast.Assert(NameConstant(False), Str("Match was not exhaustive"))) thunk_name = self.generate_function_name("_match_thunk") thunk_def = self.create_def(thunk_name, [], defs + thunk_body) return (self.create_call(thunk_name, []), [thunk_def])
def getter(item): """Construct the getter function. partof: #SPC-asts.getter """ func_name = f"{item.var}_getter" self_arg = arg(arg="self", annotation=None) func_args = arguments( args=[self_arg], kwonlyargs=[], vararg=None, kwarg=None, defaults=[], kw_defaults=[], ) inst_var = Attribute(value=Name(id="self", ctx=ast.Load()), attr=f"_{item.var}", ctx=ast.Load()) ret_stmt = Return(value=inst_var) func_node = FunctionDef(name=func_name, args=func_args, body=[ret_stmt], decorator_list=[], returns=None) mod_node = Module(body=[func_node]) return ast_to_func(mod_node, func_name)
def getLambdaMethod(methodname, body, args_node, file, id): # methodname = _lambda_0 global lambdaID fullbody = [] fullbody.extend(getTracers(file, args_node, id)) fullbody.append(getScopeTracer(file, methodname, id)) fullbody.append( Return( value=Call( func=Name(id='eval', ctx=Load()), args=[ Constant(value=to_source(body), kind=None), Call( func=Name(id='locals', ctx=Load()), args=[], keywords=[]), Name(id=methodname + '_locals', ctx=Load())], keywords=[] ) ) ) return FunctionDef( name=methodname + '_return', args=args_node, body=fullbody, decorator_list=[], returns=None, type_comment=None)
def make_function(name, args): my_args = arguments( args=[arg(arg="self", annotation=None)] + [ # XXX for arrays the name is '' (empty string) # which would end up being nothing in the generated # source file. arg(arg=my_arg or "arg", annotation=None) for my_arg in args ], defaults=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, ) my_body = [ Return(value=Call( func=Attribute( value=Attribute( value=Attribute(value=Name(id="self"), attr="_contract"), attr="functions", ), attr=name, ), args=[Name(id=my_arg or "arg") for my_arg in args], keywords=[], )) ] return FunctionDef(name=name, args=my_args, body=my_body, decorator_list=[])
def visit_FunctionDef(self, node: FunctionDef) -> FunctionDef: self.generic_visit(node) if len(node.body) and isinstance(node.body[-1], Expr): node.body[-1] = Return(value=node.body[-1].value) fix_missing_locations(node.body[-1]) return node
def add_implicit_bare_return(tree, **kw): if type(tree) in (FunctionDef, AsyncFunctionDef): if type(tree.body[-1]) is not Return: tree.body.append( Return( value=None, # bare "return" lineno=tree.lineno, col_offset=tree.col_offset)) return tree
def handle_when(self, when, *others): test, body = when.get_source_expressions() if others: if others[0].__class__.__name__ == "When": orelse = [self.handle_when(*others)] else: # Can we ever have other expressions after the default? orelse = [ Return(value=self.build_expression(others[0]), **self.file) ] else: orelse = [] return If( test=self.build_expression(test), body=[Return(value=self.build_expression(body), **self.file)], orelse=orelse, **self.file, )
def _make_call_meth(body, return_type, param_names): """ Construct a `__call__` method from the provided `body` :param body: The body, probably from a FunctionDef.body :type body: ```List[AST]``` :param return_type: The return type of the parent symbol (probably class). Used to fill in `__call__` return. :type return_type: ```Optional[str]``` :param param_names: Container of AST `id`s to match for rename :type param_names: ```Optional[Iterator[str]]``` :return: Internal function for `__call__` :rtype: ```FunctionDef``` """ body_len = len(body) return_ = (ast.fix_missing_locations( RewriteName(param_names).visit( Return(get_value(ast.parse(return_type[3:-3]).body[0]), expr=None))) if return_type is not None and len(return_type) > 6 and return_type.startswith("```") and return_type.endswith("```") else None) if body_len: if isinstance(body[0], Expr): doc_str = get_value(body[0].value) if isinstance(doc_str, str) and body_len > 0: body = (body[1:] if body_len > 1 else ([ set_value(doc_str.replace(":cvar", ":param")) if return_ is None else return_ ] if body_len == 1 else body)) # elif not isinstance(body[0], Return) and return_ is not None: # body.append(return_) # elif return_ is not None: # body = [return_] return FunctionDef(args=arguments( args=[set_arg("self")], defaults=[], kw_defaults=[], kwarg=None, kwonlyargs=[], posonlyargs=[], vararg=None, arg=None, ), body=body, decorator_list=[], name="__call__", returns=None, arguments_args=None, identifier_name=None, stmt=None, lineno=None, **maybe_type_comment)
def insert_returns(body): if isinstance(body[-1], Expr): body[-1] = Return(body[-1].value) fix_missing_locations(body[-1]) if isinstance(body[-1], If): insert_returns(body[-1].body) insert_returns(body[-1].orelse) if isinstance(body[-1], With): insert_returns(body[-1].body)
def visit_Return(self, node): existing_node = self.generic_visit(node) value = existing_node.value if value is None: return existing_node return [Assign(targets=[Name(id=RESULT_NAME, ctx=Store())], value=value), self._create_context_call('return_value', [Name(id=RESULT_NAME, ctx=Load()), Num(n=existing_node.lineno)]), Return(value=Name(id=RESULT_NAME, ctx=Load()))]
def visit_Return(self, node: ast.Return): with fast.location_of(node): node.value = fast.Call( func=self._emitter_ast(), args=[TraceEvent.after_return.to_ast(), self._get_copy_id_ast(node.value)], keywords=fast.kwargs( ret=self._make_tuple_event_for( self.visit(node.value), TraceEvent.before_return, orig_node_id=id(node.value), ), ), ) return node
def __init__(self, function): expression = function(None) if getattr(expression, "copy", None): expression = expression.copy() func_code = getattr(function, "__code__", None) or function.func_code self.file = { "lineno": func_code.co_firstlineno, "filename": func_code.co_filename, "col_offset": 0, "ctx": Load(), } self.file_store = dict(self.file, ctx=Store()) self.expression = expression body = self.build_expression(expression) if isinstance(body, list): pass elif not isinstance(body, stmt): body = [Return(value=body, **self.file)] else: body = [body] self.ast = Module( body=[ FunctionDef( name=func_code.co_name, args=arguments( args=[arg(arg="self", annotation=None) ], # function.func_code.co_varnames defaults=[], vararg=None, kwarg=None, kwonlyargs=[], kw_defaults=[], posonlyargs=[], ), kwarg=[], kw_defaults=[], vararg=[], kwonlyargs=[], body=body, decorator_list=[], ), ], type_ignores=[], **self.file, ) fix_missing_locations(self.ast) self.code = compile(self.ast, mode="exec", filename=self.file["filename"])
def convert_func_node(self, func: Function, name_var=None): """Converts the given Relay function into a Python function, with special for named functions (locally or globally)""" if name_var is None: func_name = self.generate_function_name("_anon_func") if isinstance(name_var, GlobalVar): func_name = str(name_var.name_hint) if isinstance(name_var, Var): func_name = self.get_var_name(name_var) var_names = [self.get_var_name(var) for var in func.params] body, defs = self.visit(func.body) ret = self.create_def(func_name, var_names, defs + [Return(body)]) return (ret, func_name)
def visit_ref_write(self, write: Expr): """For writing refs, we wrap the update in a thunk (returning an empty tuple to match Relay's semantics) that we execute at the right time. This ensures such assignments can be properly nested, since assignments are statements in Python but expressions in Relay""" ref, ref_defs = self.visit(write.ref) val, val_defs = self.visit(write.value) thunk_name = self.generate_function_name('_ref_write_thunk') thunk = self.create_def( thunk_name, [], ref_defs + val_defs + [ Assign([ast.Attribute(ref, 'value', Store())], val), Return(self.create_call('_container.tuple_object', [])) ]) return (self.create_call(thunk_name, []), [thunk])
def visit_Return(self,node:ast.Return): if self.hot == None or (not self.hotHasReturnCheck and self.funcNames[self.hot] not in self.exitpatterns): return node # print("Assign: ",self.funcNames[self.hot]) sin = [ Assign(targets=[Name(id='_dbg_ret_var', ctx=Store())], value=node.value), Return(value=Name(id='_dbg_ret_var', ctx=Load())) ] if self.hotHasReturnCheck: expattern = self.funcparams[self.hot] sin.insert(1,Expr(value=Call(func=Name(id='_dbgExit', ctx=Load()), args=[Name(id=pn+'_dbg_str_var_'+str(self.hot),ctx=Load()) for pn in expattern]+[Name(id='_dbg_ret_var', ctx=Load())], keywords=[]))) if self.funcNames[self.hot] in self.exitpatterns: expattern = self.exitpatterns[self.funcNames[self.hot]] sin.insert(1,Expr(value=Call(func=Name(id='_dbgExit_'+self.funcNames[self.hot],ctx=Load()), args=[Name(id=pn+'_dbg_str_var_'+str(self.hot),ctx=Load()) for pn in expattern]+[Name(id='_dbg_ret_var', ctx=Load())], keywords=[]))) for s in sin: ast.copy_location(s, node) ast.fix_missing_locations(s) return sin
def test(): """ Test Function """ stmt = Program([ Function("f", [], [ For("i", 1, 10, 1, [ Assign("x", IntValue(3)), Assign("x", IntValue(4)), IfThenElse(BoolValue(True), []), While(BoolValue(True), [Return(IntValue(3))]), Assign("x", IntValue(5)) ]) ]), Function("g", [], [Assign("x", IntValue(3))]), Assign("x", IntValue(1)) ]) nodes, edges = gen_cfg_main(stmt) gen_dot(nodes, edges)
def _tco_transform_return(tree, *, known_ecs, transform_retexpr, **kw): if type(tree) is Return: non = q[None] non = copy_location(non, tree) value = tree.value or non # return --> return None (bare return has value=None in the AST) if not isec(value, known_ecs): return Return(value=transform_retexpr(value, known_ecs)) else: # An ec call already escapes, so the return is redundant. # # If someone writes "return ec(...)" in a "with continuations" block, # this cleans up the code, since eliminating the "return" allows us # to omit a redundant "let". return Expr(value=value) # return ec(...) --> ec(...) elif isec(tree, known_ecs): # TCO the arg of an ec(...) call if len(tree.args) > 1: assert False, "expected exactly one argument for escape continuation" # pragma: no cover tree.args[0] = transform_retexpr(tree.args[0], known_ecs) return tree
def create_match_clause_body(self, pattern: Pattern, body: Expr): """Given a match clause pattern and a clause body, generates a Python function that when called with an ADT that matches the pattern, returns the result of evaluating the clause body. This function returns a function definition and the name of the generated function.""" def collect_var_assignments(pat, val): """This helper function ensures that the pattern is used to properly assign all subfields of the given AST for use in the clause body E.g., for PatternConstructor(A, PatternVar(v), PatternWildcard(), PatternConstructor(B, PatternVar(w))) we would want to have v = a.fields[0] w = a.fields[2].fields[0] """ if isinstance(pat, relay.PatternWildcard): return [] if isinstance(pat, relay.PatternVar): return [Assign([self.include_var(pat.var, assign=True)], val)] # constructor pattern: assign each field of the value # based on subpatterns assignments = [] for i in range(len(pat.patterns)): # we want the assignments for val.fields[i] field = ast.Subscript( ast.Attribute(val, "fields", Load()), ast.Index(Num(i)), Load() ) assignments += collect_var_assignments(pat.patterns[i], field) return assignments func_name = self.generate_function_name("_match_clause_body") arg_name = self.generate_var_name("_match_clause_body") clause_body, defs = self.visit(body) assignments = collect_var_assignments(pattern, Name(arg_name, Load())) func_def = self.create_def( func_name, [arg_name], defs + assignments + [Return(clause_body)] ) return (func_def, func_name)
def _union(self, node): values = [] generated = [] for union in node: for what, sub in union.items(): if ':' in what: if what in generated: # only generate any imported function once continue generated.append(what) name = what yield self._type(what, name, sub) else: # this is a build_in type (and my have been refined) # therefore generate one function per type name = self._unique(what) yield self._function(name, self._type(what, what, sub)) values += [ UnaryOp( op=Not(), operand=Call( func=Name(id=self._python_name(name), ctx=Load()), args=[Name(id='value', ctx=Load())], keywords=[], ), ), ] yield [ If( test=BoolOp( op=And(), values=values, ), body=[ Return(value=Constant(value=False, kind=None), ), ], orelse=[], ), ]
def transform_tailstmt(tree): # TODO: For/AsyncFor/While? if type(tree) is If: tree.body[-1] = transform_tailstmt(tree.body[-1]) if tree.orelse: tree.orelse[-1] = transform_tailstmt(tree.orelse[-1]) elif type(tree) in (With, AsyncWith): tree.body[-1] = transform_tailstmt(tree.body[-1]) elif type(tree) is Try: # We don't care about finalbody; typically used for unwinding only. if tree.orelse: # tail position is in else clause if present tree.orelse[-1] = transform_tailstmt(tree.orelse[-1]) else: # tail position is in the body of the "try" tree.body[-1] = transform_tailstmt(tree.body[-1]) # additionally, tail position is in each "except" handler for handler in tree.handlers: handler.body[-1] = transform_tailstmt(handler.body[-1]) elif type(tree) is Expr: tree = Return(value=tree.value) return tree
def _if_digit(): return [ If( test=UnaryOp( op=Not(), operand=Call( func=Attribute( value=Name(id='value', ctx=Load()), attr='isdigit', ctx=Load(), ), args=[], keywords=[], ), ), body=[ Return(value=Constant(value=False, kind=None), ), ], orelse=[], ) ]
def define_replace_method(methodname, args, body): """ This generates an ast for a generic function that takes a list parameters and returns a generic body used to construct something like this def <methodname>(<arges>): return Module(body=<body>) The args parameters are like this : [ Name(id='<argname>', ctx=Param()), .... ] """ return Module( # top module body=[ FunctionDef( # new function name=methodname, args=arguments(args=args, vararg=None, kwarg=None, defaults=[]), body=[Return(value=body)], decorator_list=[]) # module2 ]) # module
def getLocalsFunction(var_name): # _lambda_0 return FunctionDef( name=var_name, args=arguments( posonlyargs=[], args=[arg(arg='locls', annotation=None, type_comment=None)], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[ Global(names=[var_name + '_locals']), Assign( targets=[Name(id=var_name + '_locals', ctx=Store())], value=Name(id='locls', ctx=Load()), type_comment=None), Return(value=Name(id=var_name + '_return', ctx=Load())) ], decorator_list=[], returns=None, type_comment=None)
def _if_length(self, minimum, maximum): return [ If( test=Compare( left=Constant(value=int(minimum), kind=None), ops=[ Gt(), Gt(), ], comparators=[ Call( func=Name(id='len', ctx=Load()), args=[Name(id='value', ctx=Load())], keywords=[], ), Constant(value=int(maximum), kind=None), ], ), body=[ Return(value=Constant(value=False, kind=None), ), ], orelse=[], ), ]
def visit_Return(self, n: Return): """ * Decrement the depth before exiting. We have to save the returned value into a temporary, and only then decrement; otherwise, if the return value itself is a Call, we lose the depth. """ var = Assign([Name(self._RETURN_VAR, Store())], n.value) n.value = Name(self._RETURN_VAR, Load()) return self._fix_location_all( [ var, self._make_print( [ self._parse_fstring( colored(f"return {{{self._RETURN_VAR}}}", self.RETURN_COLOR)) ], "< ", ), self._decrement_depth(), n, ], n, )