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"""
5The code generator generates code based on a graph of code fragments.  Each node
6of the graph is represented with CodeNode and its subclasses.  This module
7provides a collection of the classes that represent code nodes independent from
8specific bindings, such as ECMAScript bindings.
9"""
10
11from .codegen_accumulator import CodeGenAccumulator
12from .codegen_format import format_template
13from .mako_renderer import MakoRenderer
14from .mako_renderer import MakoTemplate
15
16
17def render_code_node(code_node):
18    """
19    Renders |code_node| and turns it into text letting |code_node| apply all
20    necessary changes (side effects).  Returns the resulting text.
21    """
22    assert isinstance(code_node, CodeNode)
23    assert code_node.outer is None
24
25    renderer = code_node.renderer
26    accumulator = code_node.accumulator
27
28    accumulated_size = accumulator.total_size()
29    while True:
30        prev_accumulated_size = accumulated_size
31        renderer.reset()
32        code_node.render(renderer)
33        accumulated_size = accumulator.total_size()
34        if (renderer.is_rendering_complete()
35                and accumulated_size == prev_accumulated_size):
36            break
37
38    return renderer.to_text()
39
40
41class Likeliness(object):
42    """
43    Represents how much likely a code node will be executed.
44
45    Used in SymbolScopeNode in order to determine where SymbolDefinitionNodes
46    should be inserted.  Likeliness level can change only at SymbolScopeNode.
47
48    Relational operators are supported, and it's guaranteed to be:
49      NEVER < UNLIKELY < LIKELY < ALWAYS
50    """
51
52    class Level(int):
53        pass
54
55    NEVER = Level(0)
56    UNLIKELY = Level(1)
57    LIKELY = Level(2)
58    ALWAYS = Level(3)
59
60
61class CodeNode(object):
62    """
63    This is the base class of all code fragment nodes.
64
65    - Graph structure
66    CodeNode can be nested and |outer| points to the nesting CodeNode.  Also
67    CodeNode can make a sequence and |prev| points to the previous CodeNode.
68    See also |ListNode|.
69
70    - Template rendering
71    CodeNode has template text and template variable bindings.  Either of
72    |__str__| or |render| returns a text of generated code.  However, these
73    methods have side effects on rendering states, and repeated calls may return
74    different results.
75    """
76
77    class _RenderState(object):
78        """
79        Represents a set of per-render states.  Every call to CodeNode.render
80        resets all the per-render states.
81        """
82
83        def __init__(self):
84            # List of SymbolNodes that are defined at this point of rendering.
85            # Used to determine whether a certain symbol is already defined by
86            # this point of rendering.
87            self.defined_code_symbols = []
88
89            # List of SymbolNodes that are not yet defined at this point of
90            # rendering.  SymbolNodes are accumulated in order of their first
91            # appearance.  The order affects the insertion order of
92            # SymbolDefinitionNodes.
93            self.undefined_code_symbols = []
94
95            # Dict from a SymbolNode to a set of tuples of SymbolScopeNodes
96            # where the symbol was used.
97            #
98            # For example, given a code symbol |x|, the following code
99            # structure:
100            #   {  // Scope1
101            #     {  // Scope2A
102            #       {  // Scope3
103            #         x;          // [1]
104            #       }
105            #       x;            // [2]
106            #     }
107            #     x;              // [3]
108            #     {  // Scope2B
109            #       x;            // [4]
110            #     }
111            #     x;              // [5]
112            #   }
113            # is translated into an entry of the dict below:
114            #   set([
115            #     (Scope1),                   # [3], [5]
116            #     (Scope1, Scope2A),          # [2]
117            #     (Scope1, Scope2A, Scope3),  # [1]
118            #     (Scope1, Scope2B),          # [4]
119            #   ])
120            self.symbol_to_scope_chains = {}
121
122    _gensym_seq_id = 0
123
124    @classmethod
125    def gensym(cls):
126        """
127        Creates a new template variable that never conflicts with anything.
128
129        The name 'gensym' came from 'gensym' (generated symbol) in Lisp that
130        exists for exactly the same purpose.
131
132        Note that |gensym| is used to produce a new Mako template variable while
133        SymbolNode is used to represent a code symbol (such as a local variable)
134        in generated code.
135
136        Bad example:
137            template_text = "abc ${tmp} xyz"
138            a = CodeNodeA(template_text='123')
139            b = CodeNodeB(template_text=template_text, {'tmp': a})
140        |b| expects "abc 123 xyz" but what if 'tmp' were already bound to
141        something else?
142
143        Good example:
144            sym = CodeNode.gensym()
145            template_text = format_template(
146                "abc ${{{node_a}}} xyz", node_a=sym)
147            a = CodeNodeA(template_text='123')
148            b = CodeNodeB(template_text=template_text, {sym: a})
149        "{{" and "}}" are literal of "{" and "}" themselves, and the innermost
150        "{node_a}" will be replaced with |sym|.  The resulting template text
151        will be "abc ${gensym1} xyz" when |sym| is 'gensym1'.
152        """
153        cls._gensym_seq_id += 1
154        return "gensym{}".format(cls._gensym_seq_id)
155
156    def __init__(self, template_text=None, template_vars=None):
157        assert template_text is None or isinstance(template_text, str)
158        assert template_vars is None or isinstance(template_vars, dict)
159
160        # The outer CodeNode or None iff this is a top-level node
161        self._outer = None
162        # The previous CodeNode if this is a Sequence or None
163        self._prev = None
164
165        # Mako's template text, bindings dict
166        if template_text is None:
167            self._template = None
168        else:
169            self._template = MakoTemplate(template_text)
170
171        # Template variable bindings
172        self._own_template_vars = None
173        self._base_template_vars = None
174        self._cached_template_vars = None
175
176        self._accumulator = None  # CodeGenAccumulator
177        self._accumulate_requests = None
178
179        self._renderer = None  # MakoRenderer
180
181        self._render_state = CodeNode._RenderState()
182        self._is_rendering = False
183
184        if template_vars:
185            self.add_template_vars(template_vars)
186
187    def __str__(self):
188        """
189        Renders this CodeNode object directly into the renderer's text buffer
190        and always returns the empty string.  This is because it's faster to
191        accumulate the rendering result directly in a single text buffer than
192        making a lot of string pieces and concatenating them.
193
194        This function is supposed to be used in a Mako template as ${code_node}.
195        """
196        renderer = self.renderer
197        assert renderer
198
199        self.render(renderer)
200        return ""
201
202    def render(self, renderer):
203        """
204        Renders this CodeNode object as a text string and also propagates
205        updates to related CodeNode objects.  As this method has side-effects
206        not only to this object but also other related objects, the resulting
207        text may change on each invocation.
208        """
209        last_render_state = self._render_state
210        self._render_state = CodeNode._RenderState()
211        self._is_rendering = True
212
213        try:
214            self._render(
215                renderer=renderer, last_render_state=last_render_state)
216        finally:
217            self._is_rendering = False
218
219        if self._accumulate_requests:
220            accumulator = self.accumulator
221            assert accumulator
222            for request in self._accumulate_requests:
223                request(accumulator)
224            self._accumulate_requests = None
225
226    def _render(self, renderer, last_render_state):
227        """
228        Renders this CodeNode object as a text string and also propagates
229        updates to related CodeNode objects.
230
231        Only limited subclasses may override this method.
232        """
233        renderer.render(
234            caller=self,
235            template=self._template,
236            template_vars=self.template_vars)
237
238    @property
239    def outer(self):
240        """Returns the outer CodeNode or None iff this is a top-level node."""
241        return self._outer
242
243    def set_outer(self, outer):
244        assert isinstance(outer, CodeNode)
245        assert self._outer is None
246        self._outer = outer
247
248    def reset_outer(self, outer):
249        assert isinstance(outer, CodeNode) or outer is None
250        self._outer = outer
251
252    @property
253    def prev(self):
254        """Returns the previous CodeNode if this is a Sequence or None."""
255        return self._prev
256
257    def set_prev(self, prev):
258        assert isinstance(prev, CodeNode)
259        assert self._prev is None
260        self._prev = prev
261
262    def reset_prev(self, prev):
263        assert isinstance(prev, CodeNode) or prev is None
264        self._prev = prev
265
266    def outer_scope(self):
267        """Returns the outer scope closest to this scope or None."""
268        node = self.outer
269        while node is not None:
270            if isinstance(node, SymbolScopeNode):
271                return node
272            node = node.outer
273        return None
274
275    def outermost(self):
276        """Returns the outermost node, i.e. the node whose |outer| is None."""
277        node = self
278        while node.outer is not None:
279            node = node.outer
280        return node
281
282    def inclusive_outers(self):
283        """
284        Returns a list of outer nodes including this node in order from this
285        node to the outermost node.
286        """
287        outers = []
288        node = self
289        while node is not None:
290            outers.append(node)
291            node = node.outer
292        return outers
293
294    @property
295    def template_vars(self):
296        """
297        Returns the template variable bindings available at this point, i.e.
298        bound at this node or outer nodes.
299
300        CAUTION: This accessor caches the result.  This accessor must not be
301        called during construction of a code node tree.
302        """
303        if self._cached_template_vars is not None:
304            return self._cached_template_vars
305
306        outers = self.inclusive_outers()
307        bindings = None
308
309        for node in outers:
310            if node.base_template_vars is not None:
311                bindings = dict(node.base_template_vars)
312                break
313        if bindings is None:
314            bindings = {}
315
316        for node in outers:
317            if node.own_template_vars is None:
318                continue
319            for name, value in node.own_template_vars.items():
320                assert name not in bindings, (
321                    "Duplicated template variable binding: {}".format(name))
322                bindings[name] = value
323
324        self._cached_template_vars = bindings
325        return self._cached_template_vars
326
327    @property
328    def own_template_vars(self):
329        """Returns the template variables bound at this code node."""
330        return self._own_template_vars
331
332    def add_template_var(self, name, value):
333        if self._own_template_vars is None:
334            self._own_template_vars = {}
335        assert isinstance(name, str)
336        assert name not in self._own_template_vars, (
337            "Duplicated template variable binding: {}".format(name))
338        if isinstance(value, CodeNode):
339            value.set_outer(self)
340        self._own_template_vars[name] = value
341
342    def add_template_vars(self, template_vars):
343        assert isinstance(template_vars, dict)
344        for name, value in template_vars.items():
345            self.add_template_var(name, value)
346
347    @property
348    def base_template_vars(self):
349        """
350        Returns the base template variables if it's set at this code node.
351
352        The base template variables are a set of template variables that of
353        the innermost code node takes effect.  It means that the base template
354        variables are layered and shadowable.
355        """
356        return self._base_template_vars
357
358    def set_base_template_vars(self, template_vars):
359        assert isinstance(template_vars, dict)
360        for name, value in template_vars.items():
361            assert isinstance(name, str)
362            assert not isinstance(value, CodeNode)
363        assert self._base_template_vars is None
364        self._base_template_vars = template_vars
365
366    @property
367    def accumulator(self):
368        # Always consistently use the accumulator of the root node.
369        if self.outer is None:
370            return self._accumulator
371        return self.outermost().accumulator
372
373    def set_accumulator(self, accumulator):
374        assert isinstance(accumulator, CodeGenAccumulator)
375        assert self._accumulator is None
376        self._accumulator = accumulator
377
378    def accumulate(self, request):
379        """
380        While rendering the code node, |request| will be called with the
381        argument of self.accumulator.
382        """
383        assert callable(request)
384        if self._accumulate_requests is None:
385            self._accumulate_requests = []
386        self._accumulate_requests.append(request)
387
388    @property
389    def renderer(self):
390        # Always consistently use the renderer of the root node.
391        if self.outer is None:
392            return self._renderer
393        return self.outermost().renderer
394
395    def set_renderer(self, renderer):
396        assert isinstance(renderer, MakoRenderer)
397        assert self._renderer is None
398        self._renderer = renderer
399
400    @property
401    def current_render_state(self):
402        assert self._is_rendering
403        return self._render_state
404
405    @property
406    def last_render_state(self):
407        assert not self._is_rendering
408        return self._render_state
409
410    def on_code_symbol_referenced(self, symbol_node, symbol_scope_chain):
411        """Receives a report of use of a symbol node."""
412        assert isinstance(symbol_node, SymbolNode)
413        assert isinstance(symbol_scope_chain, tuple)
414        assert all(
415            isinstance(scope, SymbolScopeNode) for scope in symbol_scope_chain)
416        self.current_render_state.symbol_to_scope_chains.setdefault(
417            symbol_node, set()).add(symbol_scope_chain)
418
419
420class EmptyNode(CodeNode):
421    """Represents the zero-length text and renders nothing."""
422
423    def __init__(self):
424        CodeNode.__init__(self)
425
426    def _render(self, renderer, last_render_state):
427        pass
428
429
430class LiteralNode(CodeNode):
431    """
432    Represents a literal text, which will be rendered as is without any template
433    magic applied.  The given literal text object will be stringified on each
434    rendering.
435    """
436
437    def __init__(self, literal_text):
438        CodeNode.__init__(self)
439
440        self._literal_text = literal_text
441
442    def _render(self, renderer, last_render_state):
443        renderer.push_caller(self)
444        try:
445            renderer.render_text(str(self._literal_text))
446        finally:
447            renderer.pop_caller()
448
449
450def TextNode(template_text):
451    """
452    Represents a template text node.
453
454    TextNode is designed to be a leaf node of a code node tree.  TextNode
455    represents a template text while LiteralNode represents a literal text.
456    All template magics will be applied to |template_text|.
457
458    This function is pretending to be a CodeNode subclass and instantiates one
459    of text-ish code node subclass depending on the content of |template_text|.
460    """
461    assert isinstance(template_text, str)
462
463    if "$" in template_text or "%" in template_text:
464        return _TextNode(template_text)
465    elif template_text:
466        return LiteralNode(template_text)
467    else:
468        return EmptyNode()
469
470
471class _TextNode(CodeNode):
472    """
473    Represents a template text node.
474
475    TextNode is designed to be a leaf node of a code node tree.  TextNode
476    represents a template text while LiteralNode represents a literal text.
477    All template magics will be applied to |template_text|.
478    """
479
480    def __init__(self, template_text):
481        CodeNode.__init__(self, template_text=template_text)
482
483
484class CompositeNode(CodeNode):
485    """
486    Represents a composition of multiple code nodes.  Composition will be done
487    by using |CodeNode.gensym| so that it won't contaminate a namespace of the
488    template variables.
489    """
490
491    def __init__(self, template_format_str, *args, **kwargs):
492        """
493        Args:
494            template_format_str: A format string that is used to produce the
495                template text.
496            args:
497            kwargs: Arguments to be passed to |format_template|.  Not
498                necessarily be CodeNode, but also anything renderable can be
499                passed in.
500        """
501        assert isinstance(template_format_str, str)
502        gensym_args = []
503        gensym_kwargs = {}
504        template_vars = {}
505        for arg in args:
506            assert isinstance(arg, (CodeNode, int, long, str))
507            gensym = CodeNode.gensym()
508            gensym_args.append("${{{}}}".format(gensym))
509            template_vars[gensym] = arg
510        for key, value in kwargs.items():
511            assert isinstance(key, (int, long, str))
512            assert isinstance(value, (CodeNode, int, long, str))
513            gensym = CodeNode.gensym()
514            gensym_kwargs[key] = "${{{}}}".format(gensym)
515            template_vars[gensym] = value
516        template_text = format_template(template_format_str, *gensym_args,
517                                        **gensym_kwargs)
518
519        CodeNode.__init__(
520            self, template_text=template_text, template_vars=template_vars)
521
522
523class ListNode(CodeNode):
524    """
525    Represents a list of nodes.
526
527    append, extend, insert, and remove work just like built-in list's methods
528    except that addition and removal of None have no effect.
529    """
530
531    def __init__(self, code_nodes=None, separator="\n", head="", tail=""):
532        """
533        Args:
534            code_nodes: A list of CodeNode to be rendered.
535            separator: A str inserted between code nodes.
536            head:
537            tail: The head and tail sections that will be rendered iff the
538                content list is not empty.
539        """
540        assert isinstance(separator, str)
541        assert isinstance(head, str)
542        assert isinstance(tail, str)
543
544        CodeNode.__init__(self)
545
546        self._element_nodes = []
547        self._separator = separator
548        self._head = head
549        self._tail = tail
550
551        self._will_skip_separator = False
552
553        if code_nodes is not None:
554            self.extend(code_nodes)
555
556    def __getitem__(self, index):
557        return self._element_nodes[index]
558
559    def __iter__(self):
560        return iter(self._element_nodes)
561
562    def __len__(self):
563        return len(self._element_nodes)
564
565    def _render(self, renderer, last_render_state):
566        renderer.push_caller(self)
567        try:
568            if self._element_nodes:
569                renderer.render_text(self._head)
570            self._will_skip_separator = True
571            for node in self._element_nodes:
572                if self._will_skip_separator:
573                    self._will_skip_separator = False
574                else:
575                    renderer.render_text(self._separator)
576                node.render(renderer)
577            if self._element_nodes:
578                renderer.render_text(self._tail)
579        finally:
580            renderer.pop_caller()
581
582    def skip_separator(self):
583        self._will_skip_separator = True
584
585    def append(self, node):
586        if node is None:
587            return
588        assert isinstance(node, CodeNode)
589        assert node.outer is None and node.prev is None
590
591        if len(self._element_nodes) == 0:
592            self._element_nodes.append(node)
593        else:
594            node.set_prev(self._element_nodes[-1])
595            self._element_nodes.append(node)
596        node.set_outer(self)
597
598    def extend(self, nodes):
599        for node in nodes:
600            self.append(node)
601
602    def insert(self, index, node):
603        if node is None:
604            return
605        assert isinstance(index, (int, long))
606        assert isinstance(node, CodeNode)
607        assert node.outer is None and node.prev is None
608
609        if index < 0:
610            index += len(self._element_nodes)
611        index = max(0, min(index, len(self._element_nodes)))
612
613        if (len(self._element_nodes) == 0
614                or index == len(self._element_nodes)):
615            return self.append(node)
616
617        next_node = self._element_nodes[index]
618        if next_node.prev:
619            node.set_prev(next_node.prev)
620        next_node.reset_prev(node)
621        node.set_outer(self)
622        self._element_nodes.insert(index, node)
623
624    def remove(self, node):
625        if node is None:
626            return
627        assert node in self
628
629        index = self._element_nodes.index(node)
630        if index + 1 < len(self._element_nodes):
631            next_node = self._element_nodes[index + 1]
632            prev_node = self._element_nodes[index - 1] if index != 0 else None
633            next_node.reset_prev(prev_node)
634        del self._element_nodes[index]
635        node.reset_outer(None)
636        node.reset_prev(None)
637
638
639class SequenceNode(ListNode):
640    """
641    Represents a sequence of generated code without introducing any new scope,
642    and provides the points where SymbolDefinitionNodes can be inserted.
643    """
644
645    def __init__(self, code_nodes=None, separator="\n", head="", tail=""):
646        ListNode.__init__(
647            self,
648            code_nodes=code_nodes,
649            separator=separator,
650            head=head,
651            tail=tail)
652
653        self._to_be_removed = []
654
655    def _render(self, renderer, last_render_state):
656        if self._to_be_removed:
657            for node in self._to_be_removed:
658                self.remove(node)
659            self._to_be_removed = []
660
661        super(SequenceNode, self)._render(
662            renderer=renderer, last_render_state=last_render_state)
663
664    def schedule_to_remove(self, node):
665        """Schedules a task to remove the |node| in the next rendering cycle."""
666        assert node in self
667        self._to_be_removed.append(node)
668
669
670class SymbolScopeNode(SequenceNode):
671    """
672    Represents a scope of generated code.
673
674    If SymbolNodes are rendered inside this node, this node will attempt to
675    insert corresponding SymbolDefinitionNodes appropriately.
676    """
677
678    def __init__(self, code_nodes=None, separator="\n", head="", tail=""):
679        SequenceNode.__init__(
680            self,
681            code_nodes=code_nodes,
682            separator=separator,
683            head=head,
684            tail=tail)
685
686        self._likeliness = Likeliness.ALWAYS
687        self._registered_code_symbols = set()
688
689    def _render(self, renderer, last_render_state):
690        for symbol_node in last_render_state.undefined_code_symbols:
691            assert self.is_code_symbol_registered(symbol_node)
692            if not self.is_code_symbol_defined(symbol_node):
693                self._insert_symbol_definition(symbol_node, last_render_state)
694
695        super(SymbolScopeNode, self)._render(
696            renderer=renderer, last_render_state=last_render_state)
697
698        if self.current_render_state.undefined_code_symbols:
699            renderer.invalidate_rendering_result()
700
701    def _insert_symbol_definition(self, symbol_node, last_render_state):
702        DIRECT_USES = "u"
703        DIRECT_CHILD_SCOPES = "s"
704        ANALYSIS_RESULT_KEYS = (
705            # Number of direct uses in this scope
706            DIRECT_USES,
707            # Number of direct child scopes
708            DIRECT_CHILD_SCOPES,
709            # Number of direct child scopes per likeliness
710            Likeliness.ALWAYS,
711            Likeliness.LIKELY,
712            Likeliness.UNLIKELY,
713        )
714
715        def analyze_symbol_usage(render_state):
716            counts = dict.fromkeys(ANALYSIS_RESULT_KEYS, 0)
717
718            scope_chains = render_state.symbol_to_scope_chains.get(symbol_node)
719            if not scope_chains:
720                return counts
721
722            self_index = iter(scope_chains).next().index(self)
723            scope_chains = map(
724                lambda scope_chain: scope_chain[self_index + 1:], scope_chains)
725            scope_to_likeliness = {}
726            for scope_chain in scope_chains:
727                if not scope_chain:
728                    counts[DIRECT_USES] += 1
729                else:
730                    likeliness = min(
731                        map(lambda scope: scope.likeliness, scope_chain))
732                    scope = scope_chain[0]
733                    scope_to_likeliness[scope] = max(
734                        likeliness, scope_to_likeliness.get(scope, likeliness))
735            for likeliness in scope_to_likeliness.values():
736                counts[DIRECT_CHILD_SCOPES] += 1
737                counts[likeliness] += 1
738            return counts
739
740        def likeliness_at(render_state):
741            counts = analyze_symbol_usage(render_state)
742            if counts[DIRECT_USES] >= 1:
743                return Likeliness.ALWAYS
744            for likeliness in (Likeliness.ALWAYS, Likeliness.LIKELY,
745                               Likeliness.UNLIKELY):
746                if counts[likeliness] > 0:
747                    return likeliness
748            return Likeliness.NEVER
749
750        def insert_before_threshold(sequence_node, threshold):
751            for index, node in enumerate(sequence_node):
752                if (isinstance(node, SequenceNode)
753                        and not isinstance(node, SymbolScopeNode)):
754                    did_insert = insert_before_threshold(node, threshold)
755                    if did_insert:
756                        return True
757                elif likeliness_at(node.last_render_state) >= threshold:
758                    sequence_node.insert(index,
759                                         symbol_node.create_definition_node())
760                    return True
761            return False
762
763        counts = analyze_symbol_usage(last_render_state)
764        if counts[DIRECT_USES] >= 1:
765            did_insert = insert_before_threshold(self, Likeliness.UNLIKELY)
766            assert did_insert
767        elif counts[DIRECT_CHILD_SCOPES] == 1:
768            pass  # Let the child SymbolScopeNode do the work.
769        elif counts[Likeliness.ALWAYS] + counts[Likeliness.LIKELY] >= 2:
770            did_insert = insert_before_threshold(self, Likeliness.LIKELY)
771            assert did_insert
772        else:
773            pass  # Let descendant SymbolScopeNodes do the work.
774
775    def is_code_symbol_registered(self, symbol_node):
776        """
777        Returns True if |symbol_node| is registered and available for use within
778        this scope.
779        """
780        assert isinstance(symbol_node, SymbolNode)
781
782        if symbol_node in self._registered_code_symbols:
783            return True
784
785        outer = self.outer_scope()
786        if outer is None:
787            return False
788        return outer.is_code_symbol_registered(symbol_node)
789
790    def register_code_symbol(self, symbol_node):
791        """Registers a SymbolNode and makes it available in this scope."""
792        assert isinstance(symbol_node, SymbolNode)
793        self.add_template_var(symbol_node.name, symbol_node)
794        self._registered_code_symbols.add(symbol_node)
795
796    def register_code_symbols(self, symbol_nodes):
797        for symbol_node in symbol_nodes:
798            self.register_code_symbol(symbol_node)
799
800    @property
801    def likeliness(self):
802        """
803        Returns how much likely that this SymbolScopeNode will be executed in
804        runtime.  The likeliness is relative to the closest outer
805        SymbolScopeNode.
806        """
807        return self._likeliness
808
809    def set_likeliness(self, likeliness):
810        assert isinstance(likeliness, Likeliness.Level)
811        self._likeliness = likeliness
812
813    def is_code_symbol_defined(self, symbol_node):
814        """
815        Returns True if |symbol_node| is defined in this scope by the moment
816        when the method is called.
817        """
818        assert isinstance(symbol_node, SymbolNode)
819
820        if symbol_node in self.current_render_state.defined_code_symbols:
821            return True
822
823        outer = self.outer_scope()
824        if outer is None:
825            return False
826        return outer.is_code_symbol_defined(symbol_node)
827
828    def on_code_symbol_defined(self, symbol_node):
829        """Receives a report that a symbol gets defined."""
830        assert isinstance(symbol_node, SymbolNode)
831        self.current_render_state.defined_code_symbols.append(symbol_node)
832
833    def on_undefined_code_symbol_found(self, symbol_node):
834        """Receives a report of use of an undefined symbol node."""
835        assert isinstance(symbol_node, SymbolNode)
836        state = self.current_render_state
837        if symbol_node not in state.undefined_code_symbols:
838            state.undefined_code_symbols.append(symbol_node)
839
840
841class SymbolNode(CodeNode):
842    """
843    Represents a code symbol such as a local variable of generated code.
844
845    Using a SymbolNode combined with SymbolScopeNode, SymbolDefinitionNode(s)
846    will be automatically inserted iff this symbol is referenced.
847    """
848
849    def __init__(self, name, template_text=None, definition_constructor=None):
850        """
851        Args:
852            name: The name of this code symbol.
853            template_text: Template text to be used to define the code symbol.
854            definition_constructor: A callable that creates and returns a new
855                definition node.  This SymbolNode will be passed as the
856                argument.
857                Either of |template_text| or |definition_constructor| must be
858                given.
859        """
860        assert isinstance(name, str) and name
861
862        CodeNode.__init__(self)
863
864        self._name = name
865
866        if template_text is not None:
867            assert isinstance(template_text, str)
868            assert definition_constructor is None
869
870            def constructor(symbol_node):
871                return SymbolDefinitionNode(
872                    symbol_node=symbol_node,
873                    code_nodes=[TextNode(template_text)])
874
875            self._definition_constructor = constructor
876        else:
877            assert template_text is None
878            assert callable(definition_constructor)
879
880            self._definition_constructor = definition_constructor
881
882    def _render(self, renderer, last_render_state):
883        self._request_symbol_definition(renderer)
884
885        renderer.render_text(self.name)
886
887    def request_symbol_definition(self):
888        self._request_symbol_definition(self.renderer)
889
890    def _request_symbol_definition(self, renderer):
891        symbol_scope_chain = tuple(
892            filter(lambda node: isinstance(node, SymbolScopeNode),
893                   renderer.callers_from_first_to_last))
894
895        for caller in renderer.callers_from_last_to_first:
896            caller.on_code_symbol_referenced(self, symbol_scope_chain)
897            if caller is self.outer:
898                break
899
900        if not symbol_scope_chain[-1].is_code_symbol_defined(self):
901            for scope in reversed(symbol_scope_chain):
902                scope.on_undefined_code_symbol_found(self)
903                if scope is self.outer:
904                    break
905
906    @property
907    def name(self):
908        return self._name
909
910    def create_definition_node(self):
911        """Creates a new definition node."""
912        node = self._definition_constructor(self)
913        assert isinstance(node, SymbolDefinitionNode)
914        assert node.target_symbol is self
915        return node
916
917
918class SymbolDefinitionNode(SequenceNode):
919    """
920    Represents a definition of a code symbol.
921
922    It's allowed to define the same code symbol multiple times, and most
923    upstream definition(s) are effective.
924    """
925
926    def __init__(self, symbol_node, code_nodes=None):
927        assert isinstance(symbol_node, SymbolNode)
928
929        SequenceNode.__init__(self, code_nodes)
930
931        self._symbol_node = symbol_node
932
933    def _render(self, renderer, last_render_state):
934        scope = self.outer_scope()
935        if scope.is_code_symbol_defined(self._symbol_node):
936            assert isinstance(self.outer, SequenceNode)
937            self.outer.schedule_to_remove(self)
938            self.outer.skip_separator()
939            return
940
941        scope.on_code_symbol_defined(self._symbol_node)
942
943        super(SymbolDefinitionNode, self)._render(
944            renderer=renderer, last_render_state=last_render_state)
945
946    @property
947    def target_symbol(self):
948        return self._symbol_node
949