You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

364 lines
11 KiB

4 years ago
  1. from abc import abstractmethod, abstractproperty
  2. from parso._compatibility import utf8_repr, encoding, py_version
  3. def search_ancestor(node, *node_types):
  4. """
  5. Recursively looks at the parents of a node and returns the first found node
  6. that matches node_types. Returns ``None`` if no matching node is found.
  7. :param node: The ancestors of this node will be checked.
  8. :param node_types: type names that are searched for.
  9. :type node_types: tuple of str
  10. """
  11. while True:
  12. node = node.parent
  13. if node is None or node.type in node_types:
  14. return node
  15. class NodeOrLeaf(object):
  16. """
  17. The base class for nodes and leaves.
  18. """
  19. __slots__ = ()
  20. type = None
  21. '''
  22. The type is a string that typically matches the types of the grammar file.
  23. '''
  24. def get_root_node(self):
  25. """
  26. Returns the root node of a parser tree. The returned node doesn't have
  27. a parent node like all the other nodes/leaves.
  28. """
  29. scope = self
  30. while scope.parent is not None:
  31. scope = scope.parent
  32. return scope
  33. def get_next_sibling(self):
  34. """
  35. Returns the node immediately following this node in this parent's
  36. children list. If this node does not have a next sibling, it is None
  37. """
  38. # Can't use index(); we need to test by identity
  39. for i, child in enumerate(self.parent.children):
  40. if child is self:
  41. try:
  42. return self.parent.children[i + 1]
  43. except IndexError:
  44. return None
  45. def get_previous_sibling(self):
  46. """
  47. Returns the node immediately preceding this node in this parent's
  48. children list. If this node does not have a previous sibling, it is
  49. None.
  50. """
  51. # Can't use index(); we need to test by identity
  52. for i, child in enumerate(self.parent.children):
  53. if child is self:
  54. if i == 0:
  55. return None
  56. return self.parent.children[i - 1]
  57. def get_previous_leaf(self):
  58. """
  59. Returns the previous leaf in the parser tree.
  60. Returns `None` if this is the first element in the parser tree.
  61. """
  62. node = self
  63. while True:
  64. c = node.parent.children
  65. i = c.index(node)
  66. if i == 0:
  67. node = node.parent
  68. if node.parent is None:
  69. return None
  70. else:
  71. node = c[i - 1]
  72. break
  73. while True:
  74. try:
  75. node = node.children[-1]
  76. except AttributeError: # A Leaf doesn't have children.
  77. return node
  78. def get_next_leaf(self):
  79. """
  80. Returns the next leaf in the parser tree.
  81. Returns None if this is the last element in the parser tree.
  82. """
  83. node = self
  84. while True:
  85. c = node.parent.children
  86. i = c.index(node)
  87. if i == len(c) - 1:
  88. node = node.parent
  89. if node.parent is None:
  90. return None
  91. else:
  92. node = c[i + 1]
  93. break
  94. while True:
  95. try:
  96. node = node.children[0]
  97. except AttributeError: # A Leaf doesn't have children.
  98. return node
  99. @abstractproperty
  100. def start_pos(self):
  101. """
  102. Returns the starting position of the prefix as a tuple, e.g. `(3, 4)`.
  103. :return tuple of int: (line, column)
  104. """
  105. @abstractproperty
  106. def end_pos(self):
  107. """
  108. Returns the end position of the prefix as a tuple, e.g. `(3, 4)`.
  109. :return tuple of int: (line, column)
  110. """
  111. @abstractmethod
  112. def get_start_pos_of_prefix(self):
  113. """
  114. Returns the start_pos of the prefix. This means basically it returns
  115. the end_pos of the last prefix. The `get_start_pos_of_prefix()` of the
  116. prefix `+` in `2 + 1` would be `(1, 1)`, while the start_pos is
  117. `(1, 2)`.
  118. :return tuple of int: (line, column)
  119. """
  120. @abstractmethod
  121. def get_first_leaf(self):
  122. """
  123. Returns the first leaf of a node or itself if this is a leaf.
  124. """
  125. @abstractmethod
  126. def get_last_leaf(self):
  127. """
  128. Returns the last leaf of a node or itself if this is a leaf.
  129. """
  130. @abstractmethod
  131. def get_code(self, include_prefix=True):
  132. """
  133. Returns the code that was input the input for the parser for this node.
  134. :param include_prefix: Removes the prefix (whitespace and comments) of
  135. e.g. a statement.
  136. """
  137. class Leaf(NodeOrLeaf):
  138. '''
  139. Leafs are basically tokens with a better API. Leafs exactly know where they
  140. were defined and what text preceeds them.
  141. '''
  142. __slots__ = ('value', 'parent', 'line', 'column', 'prefix')
  143. def __init__(self, value, start_pos, prefix=''):
  144. self.value = value
  145. '''
  146. :py:func:`str` The value of the current token.
  147. '''
  148. self.start_pos = start_pos
  149. self.prefix = prefix
  150. '''
  151. :py:func:`str` Typically a mixture of whitespace and comments. Stuff
  152. that is syntactically irrelevant for the syntax tree.
  153. '''
  154. self.parent = None
  155. '''
  156. The parent :class:`BaseNode` of this leaf.
  157. '''
  158. @property
  159. def start_pos(self):
  160. return self.line, self.column
  161. @start_pos.setter
  162. def start_pos(self, value):
  163. self.line = value[0]
  164. self.column = value[1]
  165. def get_start_pos_of_prefix(self):
  166. previous_leaf = self.get_previous_leaf()
  167. if previous_leaf is None:
  168. return self.line - self.prefix.count('\n'), 0 # It's the first leaf.
  169. return previous_leaf.end_pos
  170. def get_first_leaf(self):
  171. return self
  172. def get_last_leaf(self):
  173. return self
  174. def get_code(self, include_prefix=True):
  175. if include_prefix:
  176. return self.prefix + self.value
  177. else:
  178. return self.value
  179. @property
  180. def end_pos(self):
  181. lines = self.value.split('\n')
  182. end_pos_line = self.line + len(lines) - 1
  183. # Check for multiline token
  184. if self.line == end_pos_line:
  185. end_pos_column = self.column + len(lines[-1])
  186. else:
  187. end_pos_column = len(lines[-1])
  188. return end_pos_line, end_pos_column
  189. @utf8_repr
  190. def __repr__(self):
  191. value = self.value
  192. if not value:
  193. value = self.type
  194. return "<%s: %s>" % (type(self).__name__, value)
  195. class TypedLeaf(Leaf):
  196. __slots__ = ('type',)
  197. def __init__(self, type, value, start_pos, prefix=''):
  198. super(TypedLeaf, self).__init__(value, start_pos, prefix)
  199. self.type = type
  200. class BaseNode(NodeOrLeaf):
  201. """
  202. The super class for all nodes.
  203. A node has children, a type and possibly a parent node.
  204. """
  205. __slots__ = ('children', 'parent')
  206. type = None
  207. def __init__(self, children):
  208. for c in children:
  209. c.parent = self
  210. self.children = children
  211. """
  212. A list of :class:`NodeOrLeaf` child nodes.
  213. """
  214. self.parent = None
  215. '''
  216. The parent :class:`BaseNode` of this leaf.
  217. None if this is the root node.
  218. '''
  219. @property
  220. def start_pos(self):
  221. return self.children[0].start_pos
  222. def get_start_pos_of_prefix(self):
  223. return self.children[0].get_start_pos_of_prefix()
  224. @property
  225. def end_pos(self):
  226. return self.children[-1].end_pos
  227. def _get_code_for_children(self, children, include_prefix):
  228. if include_prefix:
  229. return "".join(c.get_code() for c in children)
  230. else:
  231. first = children[0].get_code(include_prefix=False)
  232. return first + "".join(c.get_code() for c in children[1:])
  233. def get_code(self, include_prefix=True):
  234. return self._get_code_for_children(self.children, include_prefix)
  235. def get_leaf_for_position(self, position, include_prefixes=False):
  236. """
  237. Get the :py:class:`parso.tree.Leaf` at ``position``
  238. :param tuple position: A position tuple, row, column. Rows start from 1
  239. :param bool include_prefixes: If ``False``, ``None`` will be returned if ``position`` falls
  240. on whitespace or comments before a leaf
  241. :return: :py:class:`parso.tree.Leaf` at ``position``, or ``None``
  242. """
  243. def binary_search(lower, upper):
  244. if lower == upper:
  245. element = self.children[lower]
  246. if not include_prefixes and position < element.start_pos:
  247. # We're on a prefix.
  248. return None
  249. # In case we have prefixes, a leaf always matches
  250. try:
  251. return element.get_leaf_for_position(position, include_prefixes)
  252. except AttributeError:
  253. return element
  254. index = int((lower + upper) / 2)
  255. element = self.children[index]
  256. if position <= element.end_pos:
  257. return binary_search(lower, index)
  258. else:
  259. return binary_search(index + 1, upper)
  260. if not ((1, 0) <= position <= self.children[-1].end_pos):
  261. raise ValueError('Please provide a position that exists within this node.')
  262. return binary_search(0, len(self.children) - 1)
  263. def get_first_leaf(self):
  264. return self.children[0].get_first_leaf()
  265. def get_last_leaf(self):
  266. return self.children[-1].get_last_leaf()
  267. @utf8_repr
  268. def __repr__(self):
  269. code = self.get_code().replace('\n', ' ').strip()
  270. if not py_version >= 30:
  271. code = code.encode(encoding, 'replace')
  272. return "<%s: %s@%s,%s>" % \
  273. (type(self).__name__, code, self.start_pos[0], self.start_pos[1])
  274. class Node(BaseNode):
  275. """Concrete implementation for interior nodes."""
  276. __slots__ = ('type',)
  277. def __init__(self, type, children):
  278. super(Node, self).__init__(children)
  279. self.type = type
  280. def __repr__(self):
  281. return "%s(%s, %r)" % (self.__class__.__name__, self.type, self.children)
  282. class ErrorNode(BaseNode):
  283. """
  284. A node that contains valid nodes/leaves that we're follow by a token that
  285. was invalid. This basically means that the leaf after this node is where
  286. Python would mark a syntax error.
  287. """
  288. __slots__ = ()
  289. type = 'error_node'
  290. class ErrorLeaf(Leaf):
  291. """
  292. A leaf that is either completely invalid in a language (like `$` in Python)
  293. or is invalid at that position. Like the star in `1 +* 1`.
  294. """
  295. __slots__ = ('token_type',)
  296. type = 'error_leaf'
  297. def __init__(self, token_type, value, start_pos, prefix=''):
  298. super(ErrorLeaf, self).__init__(value, start_pos, prefix)
  299. self.token_type = token_type
  300. def __repr__(self):
  301. return "<%s: %s:%s, %s>" % \
  302. (type(self).__name__, self.token_type, repr(self.value), self.start_pos)