def exec_ast_nodes(self, ast_nodes): container_code = ast.parse("""def _container1(): def _container2(): print(x) return _container2 x = 1 """) container1_body = container_code.body[0].body if isinstance(ast_nodes[-1], ast.Expr): ast_nodes[-1] = ast.Return(value=ast_nodes[-1].value) container1_body[0].body = ast_nodes container1_body[2].targets = [ ast.Name(id=name, ctx=ast.Store()) for name in self.locals_cells.keys()] # 1st call of python compiler gets the values of local vars compiled_code = compile(ast.fix_missing_locations(container_code), '<string>', 'exec') ld = {} eval(compiled_code, {}, ld) func = ld['_container1']() declared_vars = func.__code__.co_varnames if declared_vars: # There are assignments inside the user_code. Need to create new # cells in locals_cells and make sure the assignments target those cells for name in declared_vars: if name not in self.locals_cells: self.locals_cells[name] = self.create_empty_cell() container1_body[0].body.insert(0, ast.Nonlocal(names=list(declared_vars))) container1_body[2].targets = [ ast.Name(id=name, ctx=ast.Store()) for name in list(func.__code__.co_freevars) + list(declared_vars)] compiled_code = compile(ast.fix_missing_locations(container_code), '<string>', 'exec') ld = {} eval(compiled_code, {}, ld) func = ld['_container1']() assert len(func.__code__.co_varnames) == 0 # Get the cells for the relevant vars closure = tuple(self.locals_cells[name] for name in func.__code__.co_freevars) res = types.FunctionType(func.__code__, self.globals_dict, func.__name__, func.__defaults__, closure) return res()
def test_simple_statements(self): # Simple statements can be put on a single line as long as the scope # has not changed. for body, expect in [ (ast.Expr(ast.Num(42)), '42'), (ast.Import([ast.alias('a', None)]), 'import a'), (ast.ImportFrom('b', [ast.alias('a', None)], 1), 'from .b import a'), (ast.Break(), 'break'), (ast.Continue(), 'continue'), (ast.Pass(), 'pass'), (ast.Assign([ast.Name('X', ast.Store())], ast.Num(42)), 'X=42'), (ast.Delete([ast.Name('X', ast.Del())]), 'del X'), (ast.Raise(None, None), 'raise'), (ast.Return(None), 'return'), (ast.AugAssign(ast.Name('X', ast.Store()), ast.Add(), ast.Num(42)), 'X+=42'), (ast.Assert(ast.Num(42), None), 'assert 42'), (ast.Global(['x']), 'global x'), (ast.Nonlocal(['x']), 'nonlocal x'), ]: if_simple = ast.If(ast.Num(42), [body], None) self.verify(if_simple, 'if 42:{}'.format(expect)) if_multiple_simples = ast.If(ast.Num(42), [ast.Pass(), ast.Pass()], None) self.verify(if_multiple_simples, 'if 42:pass;pass') inner_if = ast.If(ast.Num(6), [ast.Pass()], None) funky_if = ast.If(ast.Num(42), [ ast.Break(), ast.Continue(), inner_if, ast.Break(), ast.Continue() ], None) self.verify(funky_if, 'if 42:\n break;continue\n if 6:pass\n break;continue')
def test_Nonlocal(self): nonlocal_ = ast.Nonlocal(['X']) self.verify(nonlocal_, 'nonlocal X') many_nonlocal = ast.Nonlocal(['X', 'Y']) self.verify(many_nonlocal, 'nonlocal X,Y')
def test_nonlocal(self): self.stmt(ast.Nonlocal([]), "empty names on Nonlocal")
def p_nonlocal_stmt1(self, p): ''' nonlocal_stmt : NONLOCAL NAME''' nonlocal_stmt = ast.Nonlocal() nonlocal_stmt.names = [p[2]] nonlocal_stmt.lineno = p.lineno(1) p[0] = nonlocal_stmt
def parse_block(s, context=globals()): """ s : a (possibly multiline) string containing lambdascript code context : the context in which the functions are to be mirrored internal : lambdascript global variables """ # A lambdascript cell is like a Python dictionary without enclosing braces node = ast.parse('{'+s+'}', mode='eval').body # Extraction of names (some of them are reserved symbols names, reserved = {}, {} nonlambda = [] for k, v in zip([k.id for k in node.keys], node.values): if len(k) >= 2 and k[:2] == "__": if k in reserved: raise DuplicateDeclarationError( # TODO: find a better sentence "Several uses of the special symbol '%s'" + " in the same environment" % k ) reserved[k] = v else: if k in names: raise DuplicateDeclarationError( "Several declarations for the symbol '%s'" + " in the same environment" % k ) names[k] = v if not isinstance(v, ast.Lambda): nonlambda.append(k) else: # TODO pass # parse content of Lambda in order to find the tail-recursion # symbol ...( *args ) with Ellipsis(). See: # ast.dump(ast.parse("...(3)", mode='eval')) # 'Expression(body=Call(func=Ellipsis(), args=[Num(n=3)], keywords=[], starargs=None, kwargs=None))' # On pourra aussi chercher ...[k](3) pour la continuation # Extraction of free variables (but not global ones) freevars = {} body = [ ast.Assign(targets=[ast.Name(id=k, ctx=ast.Store())], value=ast.Lambda(args=ast.arguments( args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=ast.Num(n=0))) for k in names ] c = {} # local context for k in names: # We append a 'Lambda' in front of the expression in case it isn't a Lambda # itself (in order to avoid getting the expression evaluated) body.append(ast.Return( value=ast.Lambda(args=ast.arguments( args=[], varargs=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=names[k]))) M = ast.Module(body=[ast.FunctionDef(name='__lambdascript__', args=ast.arguments(args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=body, decorator_list=[], returns=None)]) M = ast.fix_missing_locations(M) exec(compile(M, '<string>', mode='exec'), context, c) body.pop() freevars[k] = c['__lambdascript__']().__code__.co_freevars # An O(n^2) algorithm for checking that non-lambda expressions are not # involved in circular dependancies (lambda expressions are allowed to be) for k in names: if k in nonlambda: checked = { k:False for k in names } stack = [k] while stack: i = stack.pop() checked[i] = True j = freevars[i] for e in j: if e==k: raise CircularReferenceError( "Symbol '"+k+"' involved in a circular reference relation" ) if not checked[e]: stack.append(e) # Tail-recursion for k in names: if k not in nonlambda and __ast_check_tail_recursive__(names[k], k): for w in ast.walk(names[k]): if isinstance(w, ast.Name) and w.id==k: w.id = k names[k] = ast.Lambda(args = ast.arguments( args=[ast.arg(arg=k, annotation=None)], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body= names[k]) names[k] = ast.Call(func=ast.Name(id='__make_tail_recursive__', ctx = ast.Load()), args=[names[k]], keywords=[], starargs=None, kwargs=None) # Curry for k in names: if k not in nonlambda: names[k] = ast.Call(func=ast.Name(id='__make_curry__', ctx = ast.Load()), args=[names[k]], keywords=[], starargs=None, kwargs=None) # Reference of a lambda in another lambda can now be safely removed # from the dictionary 'freevars' because sorting the declarations not # care about order between two lambda expressions. for k in names: if k not in nonlambda: freevars[k] = tuple( i for i in freevars[k] if i in nonlambda ) # Sort the declarations D = [] tmp = list(names) while tmp: for i in range(len(tmp)): e = tmp[i] ev = freevars[e] if all(i in D for i in ev): D.append(tmp.pop(i)) break # for/else: raise # useless after previous check # Compile all expressions body_outer = list(preamble) body_inner = [] for k in nonlambda: body_inner.append(ast.Nonlocal(names=[k])) for k in D: if k in nonlambda: body_outer.append(ast.Assign(targets=[ast.Name(id=k, ctx=ast.Store())], value=ast.Num(n=0))) body_inner.append(ast.Assign(targets=[ast.Name(id=k, ctx=ast.Store())], value=names[k])) else: body_outer.append(ast.Assign( targets=[ast.Name(id=k, ctx=ast.Store())], value=names[k])) body_inner.append(ast.Assign( targets=[ast.Attribute(value=ast.Name(id=k, ctx=ast.Load()), attr='__code__', ctx=ast.Store())], value=ast.Attribute(value=names[k], attr='__code__', ctx=ast.Load()))) body_inner.append(ast.Return(value=ast.Dict( keys=[ast.Str(s=k) for k in D], values=[ast.Name(id=k, ctx=ast.Load()) for k in D]))) body_outer.append(ast.FunctionDef(name='__inner__', args=ast.arguments( args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=body_inner, decorator_list=[], returns=None)) body_outer.append(ast.Return(value=ast.Call( func=ast.Name(id='__inner__', ctx=ast.Load()), args=[], keywords=[], starargs=None, kwargs=None))) M = ast.Module(body=[ast.FunctionDef(name='__lambdascript__', args=ast.arguments(args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=body_outer, decorator_list=[], returns=None)]) M = ast.fix_missing_locations(M) exec(compile(M, '<string>', mode='exec'), context, c) S = c['__lambdascript__']() # mirror all symbols in context (generally globals()) # don't mirror private symbols for k in D: if k[0] != '_': context[k] = S[k] # Parse special symbols (AFTER) for k in reserved: if k == "__print__": E = ast.Expression(body=reserved[k]) print(eval(compile(E, '<string>', mode='eval'), context, c))
def Nonlocal(draw) -> ast.Nonlocal: return ast.Nonlocal(draw(lists(name(), min_size=1, max_size=3)))
def visitNonlocal(self, n, *args): return ast.Nonlocal(names=n.names)
def visit_Nonlocal(self, node: Nonlocal, *args, **kwargs) -> C.Nonlocal: names = self.visit(node.names, *args, **kwargs) return C.Nonlocal(names=names, )
def generate_nonlocal(max_depth=None): num_names = random.choice([1, 1, 1, 1, 1, 2, 2, 3]) names = [generate_variable_name() for _ in range(num_names)] return ast.Nonlocal(names)