Files
swift-mirror/utils/gyb_syntax_support/Node.py
Alex Hoppen c2695f0ffc [libSyntax] Allow adding garbage nodes in between any two children of a syntax node
When the source code is invalid, this allows us to represent tokens that could not be used to form a valid syntax tree with more fidelity.

This commit does not start using GarbageNodes yet, it just sets everything up for them.
2022-08-04 09:20:31 +02:00

122 lines
4.3 KiB
Python

from .Child import Child # noqa: I201
from .Utils import error
from .kinds import SYNTAX_BASE_KINDS, kind_to_type, lowercase_first_word
class Node(object):
"""
A Syntax node, possibly with children.
If the kind is "SyntaxCollection", then this node is considered a Syntax
Collection that will expose itself as a typedef rather than a concrete
subclass.
"""
def __init__(self, name, description=None, kind=None, traits=None,
children=[], element=None, element_name=None,
element_choices=None, omit_when_empty=False,
elements_separated_by_newline=False):
self.syntax_kind = name
self.element_name = element_name
self.swift_syntax_kind = lowercase_first_word(name)
self.name = kind_to_type(self.syntax_kind)
self.kind = kind
self.description = description
self.traits = traits or []
self.children = []
# Add implicitly generated GarbageNodes children in between any two
# defined children
if kind != 'SyntaxCollection':
for i in range(2 * len(children)):
if i % 2 == 0:
if i == 0:
name = 'GarbageBefore' + children[0].name
else:
name = 'GarbageBetween%sAnd%s' % \
(children[int(i / 2) - 1].name, children[int(i / 2)].name)
self.children.append(Child(
name,
kind='GarbageNodes',
collection_element_name=name,
is_optional=True
))
else:
self.children.append(children[int((i - 1) / 2)])
self.non_garbage_children = \
[child for child in children if not child.is_garbage_nodes()]
self.base_kind = kind
if self.base_kind == 'SyntaxCollection':
self.base_type = 'Syntax'
else:
self.base_type = kind_to_type(self.base_kind)
if self.base_kind not in SYNTAX_BASE_KINDS:
error("unknown base kind '%s' for node '%s'" %
(self.base_kind, self.syntax_kind))
self.omit_when_empty = omit_when_empty
self.collection_element = element or ""
# For SyntaxCollections make sure that the element_name is set.
assert(not self.is_syntax_collection() or element_name or element)
# If there's a preferred name for the collection element that differs
# from its supertype, use that.
self.collection_element_name = element_name or self.collection_element
self.collection_element_type = kind_to_type(self.collection_element)
self.collection_element_choices = element_choices or []
self.elements_separated_by_newline = elements_separated_by_newline
def is_base(self):
"""
Returns `True` if this node declares one of the base syntax kinds.
"""
return self.syntax_kind in SYNTAX_BASE_KINDS
def is_syntax_collection(self):
"""
Returns `True` if this node is a subclass of SyntaxCollection.
"""
return self.base_kind == "SyntaxCollection"
def requires_validation(self):
"""
Returns `True` if this node should have a `validate` method associated.
"""
return self.is_buildable()
def is_unknown(self):
"""
Returns `True` if this node is an `Unknown` syntax subclass.
"""
return "Unknown" in self.syntax_kind
def is_missing(self):
"""
Returns `True` if this node is a `Missing` syntax subclass.
"""
return "Missing" in self.syntax_kind
def is_buildable(self):
"""
Returns `True` if this node should have a builder associated.
"""
return not self.is_base() and \
not self.is_unknown() and \
not self.is_missing() and \
not self.is_syntax_collection()
def shall_be_omitted_when_empty(self):
"""
Returns 'True' if this node shall not be created while parsing if it
has no children.
"""
return self.omit_when_empty
def is_token(self):
"""
Returns true if this child has a token kind.
"""
return 'Token' in self.syntax_kind or \
'Token' in self.collection_element