1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
"""
ExpandGlobals replaces globals variables by function call.
It also turn globals assignment in function definition.
"""
from pythran.analyses import LocalNameDeclarations
from pythran.passmanager import Transformation
from pythran.syntax import PythranSyntaxError
from pythran.utils import path_to_attr
from pythran import metadata
import gast as ast
class GlobalTransformer(ast.NodeTransformer):
'''
Use assumptions on globals to improve code generation
'''
def visit_Call(self, node):
# because a list can be a call parameter during global init
return node
def visit_List(self, node):
# because global lists in pythran are static lists
return ast.Call(path_to_attr(('builtins', 'pythran', 'static_list')),
[ast.Tuple([self.visit(elt) for elt in node.elts],
ast.Load())],
[])
class ExpandGlobals(Transformation):
"""
Expands all builtins into full paths.
>>> import gast as ast
>>> from pythran import passmanager, backend
>>> node = ast.parse('''
... a = 1
... def foo():
... return a''')
>>> pm = passmanager.PassManager("test")
>>> _, node = pm.apply(ExpandGlobals, node)
>>> print(pm.dump(backend.Python, node))
def a():
return 1
def foo():
return a()
"""
def __init__(self):
""" Initialize local declaration and constant name to expand. """
self.local_decl = set()
self.to_expand = set()
super(ExpandGlobals, self).__init__()
def visit_Module(self, node):
"""Turn globals assignment to functionDef and visit function defs. """
module_body = list()
symbols = set()
# Gather top level assigned variables.
for stmt in node.body:
if isinstance(stmt, (ast.Import, ast.ImportFrom)):
for alias in stmt.names:
name = alias.asname or alias.name
symbols.add(name) # no warning here
elif isinstance(stmt, ast.FunctionDef):
if stmt.name in symbols:
raise PythranSyntaxError(
"Multiple top-level definition of %s." % stmt.name,
stmt)
else:
symbols.add(stmt.name)
if not isinstance(stmt, ast.Assign):
continue
for target in stmt.targets:
if not isinstance(target, ast.Name):
raise PythranSyntaxError(
"Top-level assignment to an expression.",
target)
if target.id in self.to_expand:
raise PythranSyntaxError(
"Multiple top-level definition of %s." % target.id,
target)
if isinstance(stmt.value, ast.Name):
if stmt.value.id in symbols:
continue # create aliasing between top level symbols
self.to_expand.add(target.id)
for stmt in node.body:
if isinstance(stmt, ast.Assign):
# that's not a global var, but a module/function aliasing
if all(isinstance(t, ast.Name) and t.id not in self.to_expand
for t in stmt.targets):
module_body.append(stmt)
continue
self.local_decl = set()
cst_value = GlobalTransformer().visit(self.visit(stmt.value))
for target in stmt.targets:
assert isinstance(target, ast.Name)
module_body.append(
ast.FunctionDef(target.id,
ast.arguments([], [], None, [],
[], None, []),
[ast.Return(value=cst_value)],
[], None, None))
metadata.add(module_body[-1].body[0],
metadata.StaticReturn())
else:
self.local_decl = self.gather(
LocalNameDeclarations, stmt)
module_body.append(self.visit(stmt))
self.update |= bool(self.to_expand)
node.body = module_body
return node
def visit_Name(self, node):
"""
Turn global variable used not shadows to function call.
We check it is a name from an assignment as import or functions use
should not be turn into call.
"""
if (isinstance(node.ctx, ast.Load) and
node.id not in self.local_decl and
node.id in self.to_expand):
self.update = True
return ast.Call(func=node,
args=[], keywords=[])
return node
|