diff options
Diffstat (limited to 'sphinx/pycode/nodes.py')
| -rw-r--r-- | sphinx/pycode/nodes.py | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/sphinx/pycode/nodes.py b/sphinx/pycode/nodes.py new file mode 100644 index 00000000..d0fb522b --- /dev/null +++ b/sphinx/pycode/nodes.py @@ -0,0 +1,202 @@ +# -*- coding: utf-8 -*- +""" + sphinx.pycode.nodes + ~~~~~~~~~~~~~~~~~~~ + + Parse tree node implementations. + + :copyright: 2009 by Georg Brandl. + :license: BSD, see LICENSE for details. +""" + + +class BaseNode(object): + """ + Node superclass for both terminal and nonterminal nodes. + """ + + def _eq(self, other): + raise NotImplementedError + + def __eq__(self, other): + if self.__class__ is not other.__class__: + return NotImplemented + return self._eq(other) + + def __ne__(self, other): + if self.__class__ is not other.__class__: + return NotImplemented + return not self._eq(other) + + def get_prev_sibling(self): + """Return previous child in parent's children, or None.""" + if self.parent is None: + return None + for i, child in enumerate(self.parent.children): + if child is self: + if i == 0: + return None + return self.parent.children[i-1] + + def get_next_sibling(self): + """Return next child in parent's children, or None.""" + if self.parent is None: + return None + for i, child in enumerate(self.parent.children): + if child is self: + try: + return self.parent.children[i+1] + except IndexError: + return None + + def get_prev_leaf(self): + """Return the leaf node that precedes this node in the parse tree.""" + def last_child(node): + if isinstance(node, Leaf): + return node + elif not node.children: + return None + else: + return last_child(node.children[-1]) + if self.parent is None: + return None + prev = self.get_prev_sibling() + if isinstance(prev, Leaf): + return prev + elif prev is not None: + return last_child(prev) + return self.parent.get_prev_leaf() + + def get_next_leaf(self): + """Return self if leaf, otherwise the leaf node that succeeds this + node in the parse tree. + """ + node = self + while not isinstance(node, Leaf): + assert node.children + node = node.children[0] + return node + + def get_lineno(self): + """Return the line number which generated the invocant node.""" + return self.get_next_leaf().lineno + + def get_prefix(self): + """Return the prefix of the next leaf node.""" + # only leaves carry a prefix + return self.get_next_leaf().prefix + + +class Node(BaseNode): + """ + Node implementation for nonterminals. + """ + + def __init__(self, type, children, context=None): + # type of nonterminals is >= 256 + # assert type >= 256, type + self.type = type + self.children = list(children) + for ch in self.children: + # assert ch.parent is None, repr(ch) + ch.parent = self + + def __repr__(self): + return '%s(%s, %r)' % (self.__class__.__name__, self.type, self.children) + + def __str__(self): + """This reproduces the input source exactly.""" + return ''.join(map(str, self.children)) + + def _eq(self, other): + return (self.type, self.children) == (other.type, other.children) + + # support indexing the node directly instead of .children + + def __getitem__(self, index): + return self.children[index] + + def __iter__(self): + return iter(self.children) + + def __len__(self): + return len(self.children) + + +class Leaf(BaseNode): + """ + Node implementation for leaf nodes (terminals). + """ + prefix = '' # Whitespace and comments preceding this token in the input + lineno = 0 # Line where this token starts in the input + column = 0 # Column where this token tarts in the input + + def __init__(self, type, value, context=None): + # type of terminals is below 256 + # assert 0 <= type < 256, type + self.type = type + self.value = value + if context is not None: + self.prefix, (self.lineno, self.column) = context + + def __repr__(self): + return '%s(%r, %r, %r)' % (self.__class__.__name__, + self.type, self.value, self.prefix) + + def __str__(self): + """This reproduces the input source exactly.""" + return self.prefix + str(self.value) + + def _eq(self, other): + """Compares two nodes for equality.""" + return (self.type, self.value) == (other.type, other.value) + + +def convert(grammar, raw_node): + """Convert raw node to a Node or Leaf instance.""" + type, value, context, children = raw_node + if children or type in grammar.number2symbol: + # If there's exactly one child, return that child instead of + # creating a new node. + if len(children) == 1: + return children[0] + return Node(type, children, context=context) + else: + return Leaf(type, value, context=context) + + +def nice_repr(node, number2name, prefix=False): + def _repr(node): + if isinstance(node, Leaf): + return "%s(%r)" % (number2name[node.type], node.value) + else: + return "%s(%s)" % (number2name[node.type], + ', '.join(map(_repr, node.children))) + def _prepr(node): + if isinstance(node, Leaf): + return "%s(%r, %r)" % (number2name[node.type], node.prefix, node.value) + else: + return "%s(%s)" % (number2name[node.type], + ', '.join(map(_prepr, node.children))) + return (prefix and _prepr or _repr)(node) + + +class NodeVisitor(object): + def __init__(self, number2name, *args): + self.number2name = number2name + self.init(*args) + + def init(self, *args): + pass + + def visit(self, node): + """Visit a node.""" + method = 'visit_' + self.number2name[node.type] + visitor = getattr(self, method, self.generic_visit) + return visitor(node) + + def generic_visit(self, node): + """Called if no explicit visitor function exists for a node.""" + if isinstance(node, Node): + for child in node: + self.visit(child) |
