1#     Copyright 2021, Kay Hayen, mailto:kay.hayen@gmail.com
2#
3#     Part of "Nuitka", an optimizing Python compiler that is compatible and
4#     integrates with CPython, but also works on its own.
5#
6#     Licensed under the Apache License, Version 2.0 (the "License");
7#     you may not use this file except in compliance with the License.
8#     You may obtain a copy of the License at
9#
10#        http://www.apache.org/licenses/LICENSE-2.0
11#
12#     Unless required by applicable law or agreed to in writing, software
13#     distributed under the License is distributed on an "AS IS" BASIS,
14#     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15#     See the License for the specific language governing permissions and
16#     limitations under the License.
17#
18""" Reformulation of sequence creations.
19
20Sequences might be directly translated to constants, or they might become
21nodes that build tuples, lists, or sets.
22
23For Python3.5, unpacking can happen while creating sequences, these are
24being re-formulated to an internal function.
25
26Consult the developer manual for information. TODO: Add ability to sync
27source code comments with developer manual sections.
28
29"""
30
31from nuitka.nodes.AssignNodes import (
32    StatementAssignmentVariable,
33    StatementReleaseVariable,
34)
35from nuitka.nodes.BuiltinIteratorNodes import ExpressionBuiltinIter1
36from nuitka.nodes.BuiltinNextNodes import ExpressionBuiltinNext1
37from nuitka.nodes.BuiltinTypeNodes import ExpressionBuiltinTuple
38from nuitka.nodes.ConstantRefNodes import makeConstantRefNode
39from nuitka.nodes.ContainerMakingNodes import (
40    makeExpressionMakeListOrConstant,
41    makeExpressionMakeSetLiteralOrConstant,
42    makeExpressionMakeTuple,
43    makeExpressionMakeTupleOrConstant,
44)
45from nuitka.nodes.ContainerOperationNodes import (
46    ExpressionListOperationExtend,
47    ExpressionListOperationExtendForUnpack,
48    ExpressionSetOperationUpdate,
49)
50from nuitka.nodes.FunctionNodes import (
51    ExpressionFunctionCall,
52    ExpressionFunctionCreation,
53    ExpressionFunctionRef,
54)
55from nuitka.nodes.LoopNodes import StatementLoop, StatementLoopBreak
56from nuitka.nodes.ReturnNodes import StatementReturn
57from nuitka.nodes.StatementNodes import StatementExpressionOnly
58from nuitka.nodes.VariableRefNodes import (
59    ExpressionTempVariableRef,
60    ExpressionVariableRef,
61)
62from nuitka.PythonVersions import python_version
63from nuitka.specs.ParameterSpecs import ParameterSpec
64
65from . import SyntaxErrors
66from .InternalModule import (
67    internal_source_ref,
68    makeInternalHelperFunctionBody,
69    once_decorator,
70)
71from .ReformulationTryExceptStatements import makeTryExceptSingleHandlerNode
72from .ReformulationTryFinallyStatements import makeTryFinallyStatement
73from .TreeHelpers import (
74    buildNode,
75    buildNodeList,
76    getKind,
77    makeStatementsSequenceFromStatement,
78    makeStatementsSequenceFromStatements,
79)
80
81
82def _raiseStarredSyntaxError(element, source_ref):
83    SyntaxErrors.raiseSyntaxError(
84        "can use starred expression only as assignment target",
85        source_ref.atColumnNumber(element.col_offset),
86    )
87
88
89def buildTupleCreationNode(provider, node, source_ref):
90    if python_version >= 0x300:
91        for element in node.elts:
92            if getKind(element) == "Starred":
93                if python_version < 0x350:
94                    _raiseStarredSyntaxError(element, source_ref)
95                else:
96                    return buildTupleUnpacking(
97                        provider=provider, elements=node.elts, source_ref=source_ref
98                    )
99
100    return makeExpressionMakeTupleOrConstant(
101        elements=buildNodeList(provider, node.elts, source_ref),
102        user_provided=True,
103        source_ref=source_ref,
104    )
105
106
107def buildListCreationNode(provider, node, source_ref):
108    if python_version >= 0x300:
109        for element in node.elts:
110            if getKind(element) == "Starred":
111                if python_version < 0x350:
112                    _raiseStarredSyntaxError(element, source_ref)
113                else:
114                    return buildListUnpacking(
115                        provider=provider, elements=node.elts, source_ref=source_ref
116                    )
117
118    return makeExpressionMakeListOrConstant(
119        elements=buildNodeList(provider, node.elts, source_ref),
120        user_provided=True,
121        source_ref=source_ref,
122    )
123
124
125def buildSetCreationNode(provider, node, source_ref):
126    if python_version >= 0x300:
127        for element in node.elts:
128            if getKind(element) == "Starred":
129                if python_version < 0x350:
130                    _raiseStarredSyntaxError(element, source_ref)
131                else:
132                    return _buildSetUnpacking(
133                        provider=provider, elements=node.elts, source_ref=source_ref
134                    )
135
136    return makeExpressionMakeSetLiteralOrConstant(
137        elements=buildNodeList(provider, node.elts, source_ref),
138        user_provided=True,
139        source_ref=source_ref,
140    )
141
142
143@once_decorator
144def getListUnpackingHelper():
145    helper_name = "_unpack_list"
146
147    result = makeInternalHelperFunctionBody(
148        name=helper_name,
149        parameters=ParameterSpec(
150            ps_name=helper_name,
151            ps_normal_args=(),
152            ps_list_star_arg="args",
153            ps_dict_star_arg=None,
154            ps_default_count=0,
155            ps_kw_only_args=(),
156            ps_pos_only_args=(),
157        ),
158    )
159
160    temp_scope = None
161
162    tmp_result_variable = result.allocateTempVariable(temp_scope, "list")
163    tmp_iter_variable = result.allocateTempVariable(temp_scope, "iter")
164    tmp_item_variable = result.allocateTempVariable(temp_scope, "keys")
165
166    if python_version < 0x390:
167        list_operation_extend = ExpressionListOperationExtend
168    else:
169        list_operation_extend = ExpressionListOperationExtendForUnpack
170
171    loop_body = makeStatementsSequenceFromStatements(
172        makeTryExceptSingleHandlerNode(
173            tried=StatementAssignmentVariable(
174                variable=tmp_item_variable,
175                source=ExpressionBuiltinNext1(
176                    value=ExpressionTempVariableRef(
177                        variable=tmp_iter_variable, source_ref=internal_source_ref
178                    ),
179                    source_ref=internal_source_ref,
180                ),
181                source_ref=internal_source_ref,
182            ),
183            exception_name="StopIteration",
184            handler_body=StatementLoopBreak(source_ref=internal_source_ref),
185            source_ref=internal_source_ref,
186        ),
187        StatementExpressionOnly(
188            expression=list_operation_extend(
189                list_arg=ExpressionTempVariableRef(
190                    variable=tmp_result_variable, source_ref=internal_source_ref
191                ),
192                value=ExpressionTempVariableRef(
193                    variable=tmp_item_variable, source_ref=internal_source_ref
194                ),
195                source_ref=internal_source_ref,
196            ),
197            source_ref=internal_source_ref,
198        ),
199    )
200
201    args_variable = result.getVariableForAssignment(variable_name="args")
202
203    final = (
204        StatementReleaseVariable(
205            variable=tmp_result_variable, source_ref=internal_source_ref
206        ),
207        StatementReleaseVariable(
208            variable=tmp_iter_variable, source_ref=internal_source_ref
209        ),
210        StatementReleaseVariable(
211            variable=tmp_item_variable, source_ref=internal_source_ref
212        ),
213    )
214
215    tried = makeStatementsSequenceFromStatements(
216        StatementAssignmentVariable(
217            variable=tmp_iter_variable,
218            source=ExpressionBuiltinIter1(
219                value=ExpressionVariableRef(
220                    variable=args_variable, source_ref=internal_source_ref
221                ),
222                source_ref=internal_source_ref,
223            ),
224            source_ref=internal_source_ref,
225        ),
226        StatementAssignmentVariable(
227            variable=tmp_result_variable,
228            source=makeConstantRefNode(constant=[], source_ref=internal_source_ref),
229            source_ref=internal_source_ref,
230        ),
231        StatementLoop(loop_body=loop_body, source_ref=internal_source_ref),
232        StatementReturn(
233            expression=ExpressionTempVariableRef(
234                variable=tmp_result_variable, source_ref=internal_source_ref
235            ),
236            source_ref=internal_source_ref,
237        ),
238    )
239
240    result.setChild(
241        "body",
242        makeStatementsSequenceFromStatement(
243            makeTryFinallyStatement(
244                provider=result,
245                tried=tried,
246                final=final,
247                source_ref=internal_source_ref,
248            )
249        ),
250    )
251
252    return result
253
254
255@once_decorator
256def getSetUnpackingHelper():
257    helper_name = "_unpack_set"
258
259    result = makeInternalHelperFunctionBody(
260        name=helper_name,
261        parameters=ParameterSpec(
262            ps_name=helper_name,
263            ps_normal_args=(),
264            ps_list_star_arg="args",
265            ps_dict_star_arg=None,
266            ps_default_count=0,
267            ps_kw_only_args=(),
268            ps_pos_only_args=(),
269        ),
270    )
271
272    temp_scope = None
273
274    tmp_result_variable = result.allocateTempVariable(temp_scope, "set")
275    tmp_iter_variable = result.allocateTempVariable(temp_scope, "iter")
276    tmp_item_variable = result.allocateTempVariable(temp_scope, "keys")
277
278    loop_body = makeStatementsSequenceFromStatements(
279        makeTryExceptSingleHandlerNode(
280            tried=StatementAssignmentVariable(
281                variable=tmp_item_variable,
282                source=ExpressionBuiltinNext1(
283                    value=ExpressionTempVariableRef(
284                        variable=tmp_iter_variable, source_ref=internal_source_ref
285                    ),
286                    source_ref=internal_source_ref,
287                ),
288                source_ref=internal_source_ref,
289            ),
290            exception_name="StopIteration",
291            handler_body=StatementLoopBreak(source_ref=internal_source_ref),
292            source_ref=internal_source_ref,
293        ),
294        StatementExpressionOnly(
295            expression=ExpressionSetOperationUpdate(
296                set_arg=ExpressionTempVariableRef(
297                    variable=tmp_result_variable, source_ref=internal_source_ref
298                ),
299                value=ExpressionTempVariableRef(
300                    variable=tmp_item_variable, source_ref=internal_source_ref
301                ),
302                source_ref=internal_source_ref,
303            ),
304            source_ref=internal_source_ref,
305        ),
306    )
307
308    args_variable = result.getVariableForAssignment(variable_name="args")
309
310    final = (
311        StatementReleaseVariable(
312            variable=tmp_result_variable, source_ref=internal_source_ref
313        ),
314        StatementReleaseVariable(
315            variable=tmp_iter_variable, source_ref=internal_source_ref
316        ),
317        StatementReleaseVariable(
318            variable=tmp_item_variable, source_ref=internal_source_ref
319        ),
320    )
321
322    tried = makeStatementsSequenceFromStatements(
323        StatementAssignmentVariable(
324            variable=tmp_iter_variable,
325            source=ExpressionBuiltinIter1(
326                value=ExpressionVariableRef(
327                    variable=args_variable, source_ref=internal_source_ref
328                ),
329                source_ref=internal_source_ref,
330            ),
331            source_ref=internal_source_ref,
332        ),
333        StatementAssignmentVariable(
334            variable=tmp_result_variable,
335            source=makeConstantRefNode(constant=set(), source_ref=internal_source_ref),
336            source_ref=internal_source_ref,
337        ),
338        StatementLoop(loop_body=loop_body, source_ref=internal_source_ref),
339        StatementReturn(
340            expression=ExpressionTempVariableRef(
341                variable=tmp_result_variable, source_ref=internal_source_ref
342            ),
343            source_ref=internal_source_ref,
344        ),
345    )
346
347    result.setChild(
348        "body",
349        makeStatementsSequenceFromStatement(
350            makeTryFinallyStatement(
351                provider=result,
352                tried=tried,
353                final=final,
354                source_ref=internal_source_ref,
355            )
356        ),
357    )
358
359    return result
360
361
362def buildListUnpacking(provider, elements, source_ref):
363    helper_args = []
364
365    for element in elements:
366
367        # We could be a lot cleverer about the tuples for non-starred
368        # arguments, but lets get this to work first. And then rely on
369        # future optimization to inline the list unpacking helper in a
370        # way that has the same effect.
371        if getKind(element) == "Starred":
372            helper_args.append(buildNode(provider, element.value, source_ref))
373        else:
374            helper_args.append(
375                makeExpressionMakeTupleOrConstant(
376                    elements=(buildNode(provider, element, source_ref),),
377                    user_provided=True,
378                    source_ref=source_ref,
379                )
380            )
381
382    result = ExpressionFunctionCall(
383        function=ExpressionFunctionCreation(
384            function_ref=ExpressionFunctionRef(
385                function_body=getListUnpackingHelper(), source_ref=source_ref
386            ),
387            defaults=(),
388            kw_defaults=None,
389            annotations=None,
390            source_ref=source_ref,
391        ),
392        values=(makeExpressionMakeTuple(helper_args, source_ref),),
393        source_ref=source_ref,
394    )
395
396    result.setCompatibleSourceReference(helper_args[-1].getCompatibleSourceReference())
397
398    return result
399
400
401def buildTupleUnpacking(provider, elements, source_ref):
402    return ExpressionBuiltinTuple(
403        value=buildListUnpacking(provider, elements, source_ref), source_ref=source_ref
404    )
405
406
407def _buildSetUnpacking(provider, elements, source_ref):
408    helper_args = []
409
410    for element in elements:
411
412        # We could be a lot cleverer about the tuples for non-starred
413        # arguments, but lets get this to work first. And then rely on
414        # future optimization to inline the list unpacking helper in a
415        # way that has the same effect.
416        if getKind(element) == "Starred":
417            helper_args.append(buildNode(provider, element.value, source_ref))
418        else:
419            helper_args.append(
420                makeExpressionMakeTupleOrConstant(
421                    elements=(buildNode(provider, element, source_ref),),
422                    user_provided=True,
423                    source_ref=source_ref,
424                )
425            )
426
427    result = ExpressionFunctionCall(
428        function=ExpressionFunctionCreation(
429            function_ref=ExpressionFunctionRef(
430                function_body=getSetUnpackingHelper(), source_ref=source_ref
431            ),
432            defaults=(),
433            kw_defaults=None,
434            annotations=None,
435            source_ref=source_ref,
436        ),
437        values=(makeExpressionMakeTuple(helper_args, source_ref),),
438        source_ref=source_ref,
439    )
440
441    result.setCompatibleSourceReference(helper_args[-1].getCompatibleSourceReference())
442
443    return result
444