1""" This module contains all classes used to model intrinsics behavior.  """
2
3from pythran.conversion import to_ast
4from pythran.interval import UNKNOWN_RANGE, bool_values
5from pythran.types.signature import extract_combiner
6from pythran.typing import Any, Union, Fun, Generator
7
8import gast as ast
9
10
11class UnboundValueType(object):
12    '''
13    Represents a new location, bound to no identifier
14    '''
15
16
17UnboundValue = UnboundValueType()
18
19# FIXME: we should find a better way to implement default behavior
20DefaultArgNum = 20
21
22
23class UpdateEffect(object):
24    pass
25
26
27class ReadEffect(object):
28    pass
29
30
31class ReadOnceEffect(ReadEffect):
32    pass
33
34
35class Intrinsic(object):
36
37    """
38    Model any Method/Function.
39
40    Its member variables are:
41
42    - argument_effects that describes the effect of the function on its
43      argument (either UpdateEffect, ReadEffect or ReadOnceEffect)
44    - global_effects that describes whether the function has side effects
45    - return_alias that describes the aliasing between the return value
46      and the parameters. The lambda returns an ast expression, generally
47      depending on the node arguments (see dict.setdefault)
48    - args that describes the name and default value of each arg, using the
49      same representation as ast.FunctionDef, i.e. ast.arguments
50    """
51
52    def __init__(self, **kwargs):
53        self.argument_effects = kwargs.get('argument_effects',
54                                           (UpdateEffect(),) * DefaultArgNum)
55        self.global_effects = kwargs.get('global_effects', False)
56        self.return_alias = kwargs.get('return_alias',
57                                       lambda x: {UnboundValue})
58        self.args = ast.arguments(
59            [ast.Name(n, ast.Param(), None, None)
60             for n in kwargs.get('args', [])],
61            [], None,
62            [ast.Name(n, ast.Param(), None, None)
63             for n in kwargs.get('kwonlyargs', [])],
64            [], None,
65            [to_ast(d) for d in kwargs.get('defaults', [])])
66        self.return_range = kwargs.get("return_range",
67                                       lambda call: UNKNOWN_RANGE)
68        self.return_range_content = kwargs.get("return_range_content",
69                                               lambda c: UNKNOWN_RANGE)
70
71    def isliteral(self):
72        return False
73
74    def isfunction(self):
75        return False
76
77    def isstaticfunction(self):
78        return False
79
80    def ismethod(self):
81        return False
82
83    def isattribute(self):
84        return False
85
86    def isconst(self):
87        return not any(
88            isinstance(x, UpdateEffect) for x in self.argument_effects
89            ) and not self.global_effects
90
91    def isreadonce(self, n):
92        return isinstance(self.argument_effects[n], ReadOnceEffect)
93
94    def combiner(self, s, node):
95        pass
96
97
98class FunctionIntr(Intrinsic):
99    def __init__(self, **kwargs):
100        kwargs.setdefault('combiners', ())
101        super(FunctionIntr, self).__init__(**kwargs)
102        self.combiners = kwargs['combiners']
103        if 'signature' in kwargs:
104            self.signature = kwargs['signature']
105            deduced_combiner = extract_combiner(self.signature)
106            if deduced_combiner is not None:
107                self.combiners += deduced_combiner,
108            if 'return_range' not in kwargs:
109                if isinstance(self.signature, Union):
110                    if all(r.__args__[-1] is bool
111                           for r in self.signature.__args__):
112                        self.return_range = bool_values
113                elif isinstance(self.signature, Generator):
114                    if self.signature.__args__[0] is bool:
115                        self.return_range = bool_values
116                elif isinstance(self.signature, Fun):
117                    if self.signature.__args__[-1] is bool:
118                        self.return_range = bool_values
119        else:
120            self.signature = Any
121        if 'immediate_arguments' in kwargs:
122            self.immediate_arguments = kwargs['immediate_arguments']
123        else:
124            self.immediate_arguments = []
125
126    def isfunction(self):
127        return True
128
129    def isstaticfunction(self):
130        return True
131
132    def add_combiner(self, _combiner):
133        self.combiners += (_combiner,)
134
135    def combiner(self, s, node):
136        for comb in self.combiners:
137            comb(s, node)
138
139
140class UserFunction(FunctionIntr):
141    def __init__(self, *combiners, **kwargs):
142        kwargs['combiners'] = combiners
143        super(UserFunction, self).__init__(**kwargs)
144
145
146class ConstFunctionIntr(FunctionIntr):
147    def __init__(self, **kwargs):
148        kwargs.setdefault('argument_effects',
149                          (ReadEffect(),) * DefaultArgNum)
150        super(ConstFunctionIntr, self).__init__(**kwargs)
151
152
153class ConstExceptionIntr(ConstFunctionIntr):
154    def __init__(self, **kwargs):
155        kwargs.setdefault('argument_effects',
156                          (ReadEffect(),) * DefaultArgNum)
157        super(ConstExceptionIntr, self).__init__(**kwargs)
158
159
160class ReadOnceFunctionIntr(ConstFunctionIntr):
161    def __init__(self, **kwargs):
162        super(ReadOnceFunctionIntr, self).__init__(
163            argument_effects=(ReadOnceEffect(),) * DefaultArgNum, **kwargs)
164
165
166class MethodIntr(FunctionIntr):
167    def __init__(self, *combiners, **kwargs):
168        kwargs.setdefault('argument_effects',
169                          (UpdateEffect(),) + (ReadEffect(),) * DefaultArgNum)
170        kwargs['combiners'] = combiners
171        super(MethodIntr, self).__init__(**kwargs)
172
173    def ismethod(self):
174        return True
175
176    def isstaticfunction(self):
177        return False
178
179
180class ConstMethodIntr(MethodIntr):
181    def __init__(self, *combiners, **kwargs):
182        kwargs.setdefault('argument_effects', (ReadEffect(),) * DefaultArgNum)
183        super(ConstMethodIntr, self).__init__(*combiners, **kwargs)
184
185
186class ReadOnceMethodIntr(ConstMethodIntr):
187    def __init__(self, **kwargs):
188        super(ReadOnceMethodIntr, self).__init__(
189            argument_effects=(ReadOnceEffect(),) * DefaultArgNum, **kwargs)
190
191
192class AttributeIntr(Intrinsic):
193
194    """
195    Internal representation for any attributes.
196
197    Examples
198    --------
199    >> a.real
200    """
201
202    def __init__(self, **kwargs):
203        """ Forward arguments. """
204        super(AttributeIntr, self).__init__(**kwargs)
205        if 'signature' in kwargs:
206            self.signature = kwargs['signature']
207        else:
208            self.signature = Any
209
210    def isattribute(self):
211        """ Mark this intrinsic as an attribute. """
212        return True
213
214
215class ConstantIntr(Intrinsic):
216
217    """
218    Internal representation for any constant.
219
220    Examples
221    --------
222    >> math.pi
223    """
224
225    def __init__(self, **kwargs):
226        """ Forward arguments and remove arguments effects. """
227        kwargs["argument_effects"] = ()
228        super(ConstantIntr, self).__init__(**kwargs)
229
230    def isliteral(self):
231        """ Mark this intrinsic as a literal. """
232        return True
233
234
235class Class(Intrinsic):
236    def __init__(self, d, *args, **kwargs):
237        super(Class, self).__init__(*args, **kwargs)
238        self.fields = d
239
240    def __getitem__(self, key):
241        return self.fields[key]
242
243    def __iter__(self):
244        return self.fields.__iter__()
245
246    def __contains__(self, key):
247        """ Forward key content to aliased module. """
248        return key in self.fields
249
250
251class ClassWithReadOnceConstructor(Class, ReadOnceFunctionIntr):
252    def __init__(self, d, *args, **kwargs):
253        super(ClassWithReadOnceConstructor, self).__init__(d, *args, **kwargs)
254
255
256class ClassWithConstConstructor(Class, ConstFunctionIntr):
257    def __init__(self, d, *args, **kwargs):
258        super(ClassWithConstConstructor, self).__init__(d, *args, **kwargs)
259
260
261class ExceptionClass(Class, ConstExceptionIntr):
262    def __init__(self, d, *args, **kwargs):
263        super(ExceptionClass, self).__init__(d, *args, **kwargs)
264
265
266class UFunc(Class, ConstFunctionIntr):
267    """ Representation of ufunc from numpy. """
268