1# Copyright 2019 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4"""
5This module provides C++ language specific implementations of
6code_node.CodeNode.
7"""
8
9from .code_node import CodeNode
10from .code_node import CompositeNode
11from .code_node import Likeliness
12from .code_node import ListNode
13from .code_node import SymbolScopeNode
14from .code_node import TextNode
15from .codegen_expr import CodeGenExpr
16from .codegen_format import format_template
17
18
19class CxxBlockNode(CompositeNode):
20    def __init__(self, body):
21        template_format = (
22            "{{\n"  #
23            "  {body}\n"
24            "}}")
25
26        CompositeNode.__init__(
27            self,
28            template_format,
29            body=_to_symbol_scope_node(body, Likeliness.ALWAYS))
30
31
32class CxxIfNode(CompositeNode):
33    def __init__(self, cond, body, likeliness):
34        template_format = (
35            "if ({cond}) {{\n"  #
36            "  {body}\n"
37            "}}")
38
39        CompositeNode.__init__(
40            self,
41            template_format,
42            cond=_to_conditional_node(cond),
43            body=_to_symbol_scope_node(body, likeliness))
44
45
46class CxxIfElseNode(CompositeNode):
47    def __init__(self, cond, then, then_likeliness, else_, else_likeliness):
48        template_format = (
49            "if ({cond}) {{\n"  #
50            "  {then}\n"
51            "}} else {{\n"
52            "  {else_}\n"
53            "}}")
54
55        CompositeNode.__init__(
56            self,
57            template_format,
58            cond=_to_conditional_node(cond),
59            then=_to_symbol_scope_node(then, then_likeliness),
60            else_=_to_symbol_scope_node(else_, else_likeliness))
61
62
63class CxxLikelyIfNode(CxxIfNode):
64    def __init__(self, cond, body):
65        CxxIfNode.__init__(self, cond, body, Likeliness.LIKELY)
66
67
68class CxxUnlikelyIfNode(CxxIfNode):
69    def __init__(self, cond, body):
70        CxxIfNode.__init__(self, cond, body, Likeliness.UNLIKELY)
71
72
73class CxxMultiBranchesNode(CodeNode):
74    class _Clause(object):
75        def __init__(self, cond, body):
76            assert isinstance(cond, (CodeNode, bool))
77            assert isinstance(body, SymbolScopeNode)
78            self.cond = cond
79            self.body = body
80
81    def __init__(self):
82        clauses_gensym = CodeNode.gensym()
83        clauses = []
84        template_text = format_template(
85            """\
86% for {clause} in {clauses}:
87% if not loop.first:
88 else \\
89% endif
90% if {clause}.cond is not False:
91% if {clause}.cond is not True:
92if (${{{clause}.cond}}) \\
93% endif
94{{
95  ${{{clause}.body}}
96}}\\
97% if {clause}.cond is True:
98  <% break %>
99% endif
100% endif
101% endfor\
102""",
103            clause=CodeNode.gensym(),
104            clauses=clauses_gensym)
105        template_vars = {clauses_gensym: clauses}
106
107        CodeNode.__init__(
108            self, template_text=template_text, template_vars=template_vars)
109
110        self._clauses = clauses
111
112    def append(self, cond, body, likeliness=Likeliness.LIKELY):
113        if cond is None:
114            cond = False
115        elif isinstance(cond, CodeGenExpr):
116            if cond.is_always_true:
117                cond = True
118            elif cond.is_always_false:
119                cond = False
120
121        if not isinstance(cond, bool):
122            cond = _to_conditional_node(cond)
123        body = _to_symbol_scope_node(body, likeliness)
124
125        if isinstance(cond, CodeNode):
126            cond.set_outer(self)
127        body.set_outer(self)
128
129        self._clauses.append(self._Clause(cond, body))
130
131
132class CxxBreakableBlockNode(CompositeNode):
133    def __init__(self, body, likeliness=Likeliness.LIKELY):
134        template_format = ("do {{  // Dummy loop for use of 'break'.\n"
135                           "  {body}\n"
136                           "}} while (false);")
137
138        CompositeNode.__init__(
139            self,
140            template_format,
141            body=_to_symbol_scope_node(body, likeliness))
142
143
144class CxxFuncDeclNode(CompositeNode):
145    def __init__(self,
146                 name,
147                 arg_decls,
148                 return_type,
149                 template_params=None,
150                 static=False,
151                 explicit=False,
152                 constexpr=False,
153                 const=False,
154                 override=False,
155                 default=False,
156                 delete=False):
157        """
158        Args:
159            name: Function name.
160            arg_decls: List of argument declarations.
161            return_type: Return type.
162            template_params: List of template parameters or None.
163            static: True makes this a static function.
164            explicit: True makes this an explicit constructor.
165            constexpr: True makes this a constexpr function.
166            const: True makes this a const function.
167            override: True makes this an overriding function.
168            default: True makes this have the default implementation.
169            delete: True makes this function be deleted.
170        """
171        assert isinstance(static, bool)
172        assert isinstance(explicit, bool)
173        assert isinstance(constexpr, bool)
174        assert isinstance(const, bool)
175        assert isinstance(override, bool)
176        assert isinstance(default, bool)
177        assert isinstance(delete, bool)
178        assert not (default and delete)
179
180        template_format = ("{template}"
181                           "{static}{explicit}{constexpr}"
182                           "{return_type} "
183                           "{name}({arg_decls})"
184                           "{const}"
185                           "{override}"
186                           "{default_or_delete}"
187                           ";")
188
189        if template_params is None:
190            template = ""
191        else:
192            template = "template <{}>\n".format(", ".join(template_params))
193
194        static = "static " if static else ""
195        explicit = "explicit " if explicit else ""
196        constexpr = "constexpr " if constexpr else ""
197        const = " const" if const else ""
198        override = " override" if override else ""
199
200        if default:
201            default_or_delete = " = default"
202        elif delete:
203            default_or_delete = " = delete"
204        else:
205            default_or_delete = ""
206
207        CompositeNode.__init__(
208            self,
209            template_format,
210            name=_to_maybe_text_node(name),
211            arg_decls=ListNode(
212                map(_to_maybe_text_node, arg_decls), separator=", "),
213            return_type=_to_maybe_text_node(return_type),
214            template=template,
215            static=static,
216            explicit=explicit,
217            constexpr=constexpr,
218            const=const,
219            override=override,
220            default_or_delete=default_or_delete)
221
222
223class CxxFuncDefNode(CompositeNode):
224    def __init__(self,
225                 name,
226                 arg_decls,
227                 return_type,
228                 class_name=None,
229                 template_params=None,
230                 static=False,
231                 inline=False,
232                 explicit=False,
233                 constexpr=False,
234                 const=False,
235                 override=False,
236                 member_initializer_list=None):
237        """
238        Args:
239            name: Function name.
240            arg_decls: List of argument declarations.
241            return_type: Return type.
242            class_name: Class name to be used as nested-name-specifier.
243            template_params: List of template parameters or None.
244            static: True makes this a static function.
245            inline: True makes this an inline function.
246            explicit: True makes this an explicit constructor.
247            constexpr: True makes this a constexpr function.
248            const: True makes this a const function.
249            override: True makes this an overriding function.
250            member_initializer_list: List of member initializers.
251        """
252        assert isinstance(static, bool)
253        assert isinstance(inline, bool)
254        assert isinstance(explicit, bool)
255        assert isinstance(constexpr, bool)
256        assert isinstance(const, bool)
257        assert isinstance(override, bool)
258
259        template_format = ("{template}"
260                           "{static}{inline}{explicit}{constexpr}"
261                           "{return_type} "
262                           "{class_name}{name}({arg_decls})"
263                           "{const}"
264                           "{override}"
265                           "{member_initializer_list} {{\n"
266                           "  {body}\n"
267                           "}}")
268
269        if class_name is None:
270            class_name = ""
271        else:
272            class_name = ListNode([_to_maybe_text_node(class_name)], tail="::")
273
274        if template_params is None:
275            template = ""
276        else:
277            template = "template <{}>\n".format(", ".join(template_params))
278
279        static = "static " if static else ""
280        inline = "inline " if inline else ""
281        explicit = "explicit " if explicit else ""
282        constexpr = "constexpr " if constexpr else ""
283        const = " const" if const else ""
284        override = " override" if override else ""
285
286        if member_initializer_list is None:
287            member_initializer_list = ""
288        else:
289            member_initializer_list = ListNode(
290                map(_to_maybe_text_node, member_initializer_list),
291                separator=", ",
292                head=" : ")
293
294        self._body_node = SymbolScopeNode()
295
296        CompositeNode.__init__(
297            self,
298            template_format,
299            name=_to_maybe_text_node(name),
300            arg_decls=ListNode(
301                map(_to_maybe_text_node, arg_decls), separator=", "),
302            return_type=_to_maybe_text_node(return_type),
303            class_name=class_name,
304            template=template,
305            static=static,
306            inline=inline,
307            explicit=explicit,
308            constexpr=constexpr,
309            const=const,
310            override=override,
311            member_initializer_list=member_initializer_list,
312            body=self._body_node)
313
314    @property
315    def body(self):
316        return self._body_node
317
318
319class CxxClassDefNode(CompositeNode):
320    def __init__(self, name, base_class_names=None, final=False, export=None):
321        """
322        Args:
323            name: The class name to be defined.
324            base_class_names: The list of base class names.
325            final: True makes this a final class.
326            export: Class export annotation.
327        """
328        assert isinstance(final, bool)
329
330        template_format = ("class{export} {name}{final}{base_clause} {{\n"
331                           "  {top_section}\n"
332                           "  {public_section}\n"
333                           "  {protected_section}\n"
334                           "  {private_section}\n"
335                           "  {bottom_section}\n"
336                           "}};")
337
338        if export is None:
339            export = ""
340        else:
341            export = ListNode([_to_maybe_text_node(export)], head=" ")
342
343        final = " final" if final else ""
344
345        if base_class_names is None:
346            base_clause = ""
347        else:
348            base_specifier_list = [
349                CompositeNode(
350                    "public {base_class_name}",
351                    base_class_name=_to_maybe_text_node(base_class_name))
352                for base_class_name in base_class_names
353            ]
354            base_clause = ListNode(
355                base_specifier_list, separator=", ", head=" : ")
356
357        self._top_section = ListNode(tail="\n")
358        self._public_section = ListNode(head="public:\n", tail="\n")
359        self._protected_section = ListNode(head="protected:\n", tail="\n")
360        self._private_section = ListNode(head="private:\n", tail="\n")
361        self._bottom_section = ListNode()
362
363        CompositeNode.__init__(
364            self,
365            template_format,
366            name=_to_maybe_text_node(name),
367            base_clause=base_clause,
368            final=final,
369            export=export,
370            top_section=self._top_section,
371            public_section=self._public_section,
372            protected_section=self._protected_section,
373            private_section=self._private_section,
374            bottom_section=self._bottom_section)
375
376    @property
377    def top_section(self):
378        return self._top_section
379
380    @property
381    def public_section(self):
382        return self._public_section
383
384    @property
385    def protected_section(self):
386        return self._protected_section
387
388    @property
389    def private_section(self):
390        return self._private_section
391
392    @property
393    def bottom_section(self):
394        return self._bottom_section
395
396
397class CxxNamespaceNode(CompositeNode):
398    def __init__(self, name="", body=None):
399        template_format = ("namespace {name} {{\n"
400                           "\n"
401                           "{body}\n"
402                           "\n"
403                           "}}  // namespace {name}")
404
405        if body is None:
406            self._body = ListNode()
407        else:
408            self._body = _to_list_node(body)
409
410        CompositeNode.__init__(
411            self, template_format, name=name, body=self._body)
412
413    @property
414    def body(self):
415        return self._body
416
417
418def _to_conditional_node(cond):
419    if isinstance(cond, CodeNode):
420        return cond
421    if isinstance(cond, CodeGenExpr):
422        return TextNode(cond.to_text())
423    if isinstance(cond, str):
424        return TextNode(cond)
425    assert False
426
427
428def _to_list_node(node):
429    if isinstance(node, ListNode):
430        return node
431    if isinstance(node, CodeNode):
432        return ListNode([node])
433    if isinstance(node, (list, tuple)):
434        return ListNode(node)
435    assert False
436
437
438def _to_maybe_text_node(node):
439    if isinstance(node, CodeNode):
440        return node
441    if isinstance(node, str):
442        return TextNode(node)
443    assert False
444
445
446def _to_symbol_scope_node(node, likeliness):
447    if isinstance(node, SymbolScopeNode):
448        pass
449    elif isinstance(node, CodeNode):
450        node = SymbolScopeNode([node])
451    elif isinstance(node, (list, tuple)):
452        node = SymbolScopeNode(node)
453    else:
454        assert False
455    node.set_likeliness(likeliness)
456    return node
457