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""" Print nodes.
19
20Right now there is only the print statement, but in principle, there should
21also be the print function here. These perform output, which can be combined
22if possible, and could be detected to fail, which would be perfect.
23
24Predicting the behavior of 'print' is not trivial at all, due to many special
25cases.
26"""
27
28from .NodeBases import StatementChildHavingBase, StatementChildrenHavingBase
29from .NodeMakingHelpers import (
30    makeStatementExpressionOnlyReplacementNode,
31    makeStatementsSequenceReplacementNode,
32    wrapStatementWithSideEffects,
33)
34
35
36class StatementPrintValue(StatementChildrenHavingBase):
37    kind = "STATEMENT_PRINT_VALUE"
38
39    named_children = ("dest", "value")
40
41    def __init__(self, dest, value, source_ref):
42        StatementChildrenHavingBase.__init__(
43            self, values={"value": value, "dest": dest}, source_ref=source_ref
44        )
45
46        assert value is not None
47
48    def computeStatement(self, trace_collection):
49        dest = trace_collection.onExpression(
50            expression=self.subnode_dest, allow_none=True
51        )
52
53        if dest is not None and dest.mayRaiseException(BaseException):
54            trace_collection.onExceptionRaiseExit(BaseException)
55
56        if dest is not None and dest.willRaiseException(BaseException):
57            result = makeStatementExpressionOnlyReplacementNode(
58                expression=dest, node=self
59            )
60
61            return (
62                result,
63                "new_raise",
64                """\
65Exception raise in 'print' statement destination converted to explicit raise.""",
66            )
67
68        value = trace_collection.onExpression(expression=self.subnode_value)
69
70        if value.mayRaiseException(BaseException):
71            trace_collection.onExceptionRaiseExit(BaseException)
72
73        if value.willRaiseException(BaseException):
74            if dest is not None:
75                result = wrapStatementWithSideEffects(
76                    new_node=makeStatementExpressionOnlyReplacementNode(
77                        expression=value, node=self
78                    ),
79                    old_node=dest,
80                )
81            else:
82                result = makeStatementExpressionOnlyReplacementNode(
83                    expression=value, node=self
84                )
85
86            return (
87                result,
88                "new_raise",
89                """\
90Exception raise in 'print' statement arguments converted to explicit raise.""",
91            )
92
93        trace_collection.onExceptionRaiseExit(BaseException)
94
95        if dest is None:
96            if value.isExpressionSideEffects():
97                self.setChild("value", value.subnode_expression)
98
99                statements = [
100                    makeStatementExpressionOnlyReplacementNode(side_effect, self)
101                    for side_effect in value.subnode_side_effects
102                ]
103
104                statements.append(self)
105
106                result = makeStatementsSequenceReplacementNode(
107                    statements=statements, node=self
108                )
109
110                return (
111                    result,
112                    "new_statements",
113                    """\
114Side effects printed item promoted to statements.""",
115                )
116
117        if value.isCompileTimeConstant():
118            # Avoid unicode encoding issues.
119            if not value.isExpressionConstantUnicodeRef():
120                new_value = value.getStrValue()
121                assert new_value is not None, value
122
123                if value is not new_value:
124                    self.setChild("value", new_value)
125
126        return self, None, None
127
128    @staticmethod
129    def mayRaiseException(exception_type):
130        # Output may always fail due to external reasons.
131        return True
132
133
134class StatementPrintNewline(StatementChildHavingBase):
135    kind = "STATEMENT_PRINT_NEWLINE"
136
137    named_child = "dest"
138
139    def __init__(self, dest, source_ref):
140        StatementChildHavingBase.__init__(self, value=dest, source_ref=source_ref)
141
142    def computeStatement(self, trace_collection):
143        dest = trace_collection.onExpression(
144            expression=self.subnode_dest, allow_none=True
145        )
146
147        if dest is not None and dest.mayRaiseException(BaseException):
148            trace_collection.onExceptionRaiseExit(BaseException)
149
150        if dest is not None and dest.willRaiseException(BaseException):
151            result = makeStatementExpressionOnlyReplacementNode(
152                expression=dest, node=self
153            )
154
155            return (
156                result,
157                "new_raise",
158                """\
159Exception raise in 'print' statement destination converted to explicit raise.""",
160            )
161
162        trace_collection.onExceptionRaiseExit(BaseException)
163
164        return self, None, None
165
166    @staticmethod
167    def mayRaiseException(exception_type):
168        # Output may always fail due to external reasons.
169        return True
170