Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/blib2to3/pytree.py: 28%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Copyright 2006 Google, Inc. All Rights Reserved.
2# Licensed to PSF under a Contributor Agreement.
4"""
5Python parse tree definitions.
7This is a very concrete parse tree; we need to keep every token and
8even the comments and whitespace between tokens.
10There's also a pattern matching implementation here.
11"""
13# mypy: allow-untyped-defs, allow-incomplete-defs
15from collections.abc import Iterable, Iterator
16from typing import Any, Optional, TypeVar, Union
18from blib2to3.pgen2.grammar import Grammar
20__author__ = "Guido van Rossum <guido@python.org>"
22import sys
23from io import StringIO
25HUGE: int = 0x7FFFFFFF # maximum repeat count, default max
27_type_reprs: dict[int, str | int] = {}
30def type_repr(type_num: int) -> str | int:
31 global _type_reprs
32 if not _type_reprs:
33 from . import pygram
35 if not hasattr(pygram, "python_symbols"):
36 pygram.initialize(cache_dir=None)
38 # printing tokens is possible but not as useful
39 # from .pgen2 import token // token.__dict__.items():
40 for name in dir(pygram.python_symbols):
41 val = getattr(pygram.python_symbols, name)
42 if type(val) == int:
43 _type_reprs[val] = name
44 return _type_reprs.setdefault(type_num, type_num)
47_P = TypeVar("_P", bound="Base")
49NL = Union["Node", "Leaf"]
50Context = tuple[str, tuple[int, int]]
51RawNode = tuple[int, Optional[str], Optional[Context], Optional[list[NL]]]
54class Base:
55 """
56 Abstract base class for Node and Leaf.
58 This provides some default functionality and boilerplate using the
59 template pattern.
61 A node may be a subnode of at most one parent.
62 """
64 # Default values for instance variables
65 type: int # int: token number (< 256) or symbol number (>= 256)
66 parent: Optional["Node"] = None # Parent node pointer, or None
67 children: list[NL] # List of subnodes
68 was_changed: bool = False
70 def __new__(cls, *args, **kwds):
71 """Constructor that prevents Base from being instantiated."""
72 assert cls is not Base, "Cannot instantiate Base"
73 return object.__new__(cls)
75 def __eq__(self, other: Any) -> bool:
76 """
77 Compare two nodes for equality.
79 This calls the method _eq().
80 """
81 if self.__class__ is not other.__class__:
82 return NotImplemented
83 return self._eq(other)
85 @property
86 def prefix(self) -> str:
87 raise NotImplementedError
89 def _eq(self: _P, other: _P) -> bool:
90 """
91 Compare two nodes for equality.
93 This is called by __eq__ and __ne__. It is only called if the two nodes
94 have the same type. This must be implemented by the concrete subclass.
95 Nodes should be considered equal if they have the same structure,
96 ignoring the prefix string and other context information.
97 """
98 raise NotImplementedError
100 def __deepcopy__(self: _P, memo: Any) -> _P:
101 return self.clone()
103 def clone(self: _P) -> _P:
104 """
105 Return a cloned (deep) copy of self.
107 This must be implemented by the concrete subclass.
108 """
109 raise NotImplementedError
111 def post_order(self) -> Iterator[NL]:
112 """
113 Return a post-order iterator for the tree.
115 This must be implemented by the concrete subclass.
116 """
117 raise NotImplementedError
119 def pre_order(self) -> Iterator[NL]:
120 """
121 Return a pre-order iterator for the tree.
123 This must be implemented by the concrete subclass.
124 """
125 raise NotImplementedError
127 def replace(self, new: NL | list[NL]) -> None:
128 """Replace this node with a new one in the parent."""
129 assert self.parent is not None, str(self)
130 assert new is not None
131 if not isinstance(new, list):
132 new = [new]
133 parent = self.parent
134 for i, ch in enumerate(parent.children):
135 if ch is self:
136 break
137 else:
138 raise AssertionError((parent.children, self, new))
139 # Splice the new children in place of `self` and keep the cached sibling
140 # maps valid without rebuilding them, which would be O(len(children)) per
141 # call and quadratic when many children are replaced while siblings are
142 # read in between (e.g. merging implicitly concatenated strings).
143 parent._replace_child_in_sibling_maps(self, new)
144 parent.children[i : i + 1] = new
145 parent.changed()
146 for x in new:
147 x.parent = parent
148 self.parent = None
150 def get_lineno(self) -> int | None:
151 """Return the line number which generated the invocant node."""
152 node = self
153 while not isinstance(node, Leaf):
154 if not node.children:
155 return None
156 node = node.children[0]
157 return node.lineno
159 def changed(self) -> None:
160 if self.was_changed:
161 return
162 if self.parent:
163 self.parent.changed()
164 self.was_changed = True
166 def remove(self, search_start: int = 0) -> int | None:
167 """
168 Remove the node from the tree. Returns the position of the node in its
169 parent's children before it was removed.
171 ``search_start`` hints where to begin looking in the parent's children.
172 A caller that removes many siblings of the same parent in left-to-right
173 order can pass the last returned position to avoid rescanning the whole
174 list from index 0 every time, which is quadratic over the parent. The
175 entire list is still searched (starting at the hint and wrapping around),
176 so an inaccurate hint only costs a little extra scanning.
177 """
178 if self.parent:
179 children = self.parent.children
180 count = len(children)
181 for offset in range(count):
182 i = (search_start + offset) % count
183 if children[i] is self:
184 del children[i]
185 self.parent.changed()
186 self.parent._remove_from_sibling_maps(self)
187 self.parent = None
188 return i
189 return None
191 @property
192 def next_sibling(self) -> NL | None:
193 """
194 The node immediately following the invocant in their parent's children
195 list. If the invocant does not have a next sibling, it is None
196 """
197 if self.parent is None:
198 return None
200 if self.parent.next_sibling_map is None:
201 self.parent.update_sibling_maps()
202 assert self.parent.next_sibling_map is not None
203 return self.parent.next_sibling_map[id(self)]
205 @property
206 def prev_sibling(self) -> NL | None:
207 """
208 The node immediately preceding the invocant in their parent's children
209 list. If the invocant does not have a previous sibling, it is None.
210 """
211 if self.parent is None:
212 return None
214 if self.parent.prev_sibling_map is None:
215 self.parent.update_sibling_maps()
216 assert self.parent.prev_sibling_map is not None
217 return self.parent.prev_sibling_map[id(self)]
219 def leaves(self) -> Iterator["Leaf"]:
220 # Walk iteratively rather than recursing with `yield from`, whose per-node
221 # generator delegation is O(depth) and makes a full traversal quadratic on
222 # deeply nested trees (e.g. a long `a ** b ** c ** ...` chain).
223 stack: list[NL] = list(reversed(self.children))
224 while stack:
225 node = stack.pop()
226 if isinstance(node, Leaf):
227 yield node
228 else:
229 stack.extend(reversed(node.children))
231 def depth(self) -> int:
232 if self.parent is None:
233 return 0
234 return 1 + self.parent.depth()
236 def get_suffix(self) -> str:
237 """
238 Return the string immediately following the invocant node. This is
239 effectively equivalent to node.next_sibling.prefix
240 """
241 next_sib = self.next_sibling
242 if next_sib is None:
243 return ""
244 prefix = next_sib.prefix
245 return prefix
248class Node(Base):
249 """Concrete implementation for interior nodes."""
251 fixers_applied: list[Any] | None
252 used_names: set[str] | None
254 def __init__(
255 self,
256 type: int,
257 children: list[NL],
258 context: Any | None = None,
259 prefix: str | None = None,
260 fixers_applied: list[Any] | None = None,
261 ) -> None:
262 """
263 Initializer.
265 Takes a type constant (a symbol number >= 256), a sequence of
266 child nodes, and an optional context keyword argument.
268 As a side effect, the parent pointers of the children are updated.
269 """
270 assert type >= 256, type
271 self.type = type
272 self.children = list(children)
273 for ch in self.children:
274 assert ch.parent is None, repr(ch)
275 ch.parent = self
276 self.invalidate_sibling_maps()
277 if prefix is not None:
278 self.prefix = prefix
279 if fixers_applied:
280 self.fixers_applied = fixers_applied[:]
281 else:
282 self.fixers_applied = None
284 def __repr__(self) -> str:
285 """Return a canonical string representation."""
286 assert self.type is not None
287 return f"{self.__class__.__name__}({type_repr(self.type)}, {self.children!r})"
289 def __str__(self) -> str:
290 """
291 Return a pretty string representation.
293 This reproduces the input source exactly.
294 """
295 return "".join(map(str, self.children))
297 def _eq(self, other: Base) -> bool:
298 """Compare two nodes for equality."""
299 return (self.type, self.children) == (other.type, other.children)
301 def clone(self) -> "Node":
302 assert self.type is not None
303 """Return a cloned (deep) copy of self."""
304 return Node(
305 self.type,
306 [ch.clone() for ch in self.children],
307 fixers_applied=self.fixers_applied,
308 )
310 def post_order(self) -> Iterator[NL]:
311 """Return a post-order iterator for the tree."""
312 # Walk iteratively rather than recursing with `yield from`. The recursive
313 # form resumes one delegating generator per level on every step, so a full
314 # traversal costs O(depth) per node and is quadratic on deeply nested trees.
315 stack: list[tuple[NL, bool]] = [(self, False)]
316 while stack:
317 node, expanded = stack.pop()
318 if expanded:
319 yield node
320 else:
321 stack.append((node, True))
322 stack.extend((child, False) for child in reversed(node.children))
324 def pre_order(self) -> Iterator[NL]:
325 """Return a pre-order iterator for the tree."""
326 # See `post_order` for why this is iterative instead of recursive.
327 stack: list[NL] = [self]
328 while stack:
329 node = stack.pop()
330 yield node
331 stack.extend(reversed(node.children))
333 @property
334 def prefix(self) -> str:
335 """
336 The whitespace and comments preceding this node in the input.
337 """
338 if not self.children:
339 return ""
340 return self.children[0].prefix
342 @prefix.setter
343 def prefix(self, prefix: str) -> None:
344 if self.children:
345 self.children[0].prefix = prefix
347 def set_child(self, i: int, child: NL) -> None:
348 """
349 Equivalent to 'node.children[i] = child'. This method also sets the
350 child's parent attribute appropriately.
351 """
352 child.parent = self
353 old = self.children[i]
354 old.parent = None
355 self.children[i] = child
356 self.changed()
357 self._replace_in_sibling_maps(old, child)
359 def insert_child(self, i: int, child: NL) -> None:
360 """
361 Equivalent to 'node.children.insert(i, child)'. This method also sets
362 the child's parent attribute appropriately.
363 """
364 child.parent = self
365 self.children.insert(i, child)
366 self.changed()
367 if 0 <= i < len(self.children) and self.children[i] is child:
368 self._insert_into_sibling_maps(i, child)
369 else:
370 self.invalidate_sibling_maps()
372 def append_child(self, child: NL) -> None:
373 """
374 Equivalent to 'node.children.append(child)'. This method also sets the
375 child's parent attribute appropriately.
376 """
377 child.parent = self
378 self.children.append(child)
379 self.changed()
380 self._insert_into_sibling_maps(len(self.children) - 1, child)
382 def invalidate_sibling_maps(self) -> None:
383 self.prev_sibling_map: dict[int, NL | None] | None = None
384 self.next_sibling_map: dict[int, NL | None] | None = None
386 def _insert_into_sibling_maps(self, i: int, child: NL) -> None:
387 """Splice a newly inserted child into the cached sibling maps.
389 Keeps the maps valid without rebuilding them from scratch, which would
390 be O(len(children)) per call and quadratic when many children are
391 inserted while siblings are read in between (e.g. ``append_leaves``).
392 """
393 prev_map = self.prev_sibling_map
394 next_map = self.next_sibling_map
395 if prev_map is None or next_map is None:
396 return
397 before = self.children[i - 1] if i > 0 else None
398 after = self.children[i + 1] if i + 1 < len(self.children) else None
399 prev_map[id(child)] = before
400 next_map[id(child)] = after
401 next_map[id(before)] = child
402 if after is not None:
403 prev_map[id(after)] = child
405 def _remove_from_sibling_maps(self, child: NL) -> None:
406 """Splice a removed child out of the cached sibling maps."""
407 prev_map = self.prev_sibling_map
408 next_map = self.next_sibling_map
409 if prev_map is None or next_map is None:
410 return
411 if id(child) not in prev_map:
412 self.invalidate_sibling_maps()
413 return
414 before = prev_map.pop(id(child))
415 after = next_map.pop(id(child))
416 next_map[id(before)] = after
417 if after is not None:
418 prev_map[id(after)] = before
420 def _replace_in_sibling_maps(self, old: NL, child: NL) -> None:
421 """Swap ``old`` for ``child`` at the same position in the sibling maps."""
422 prev_map = self.prev_sibling_map
423 next_map = self.next_sibling_map
424 if prev_map is None or next_map is None:
425 return
426 if id(old) not in prev_map:
427 self.invalidate_sibling_maps()
428 return
429 before = prev_map.pop(id(old))
430 after = next_map.pop(id(old))
431 prev_map[id(child)] = before
432 next_map[id(child)] = after
433 next_map[id(before)] = child
434 if after is not None:
435 prev_map[id(after)] = child
437 def _replace_child_in_sibling_maps(self, old: NL, new_children: list[NL]) -> None:
438 """Swap ``old`` for ``new_children`` at the same position in the maps."""
439 prev_map = self.prev_sibling_map
440 next_map = self.next_sibling_map
441 if prev_map is None or next_map is None:
442 return
443 if id(old) not in prev_map:
444 self.invalidate_sibling_maps()
445 return
446 before = prev_map.pop(id(old))
447 after = next_map.pop(id(old))
448 previous = before
449 for child in new_children:
450 prev_map[id(child)] = previous
451 next_map[id(previous)] = child
452 previous = child
453 next_map[id(previous)] = after
454 if after is not None:
455 prev_map[id(after)] = previous
457 def update_sibling_maps(self) -> None:
458 _prev: dict[int, NL | None] = {}
459 _next: dict[int, NL | None] = {}
460 self.prev_sibling_map = _prev
461 self.next_sibling_map = _next
462 previous: NL | None = None
463 for current in self.children:
464 _prev[id(current)] = previous
465 _next[id(previous)] = current
466 previous = current
467 _next[id(current)] = None
470class Leaf(Base):
471 """Concrete implementation for leaf nodes."""
473 # Default values for instance variables
474 value: str
475 fixers_applied: list[Any]
476 bracket_depth: int
477 # Changed later in brackets.py
478 opening_bracket: Optional["Leaf"] = None
479 used_names: set[str] | None
480 _prefix = "" # Whitespace and comments preceding this token in the input
481 lineno: int = 0 # Line where this token starts in the input
482 column: int = 0 # Column where this token starts in the input
483 # If not None, this Leaf is created by converting a block of fmt off/skip
484 # code, and `fmt_pass_converted_first_leaf` points to the first Leaf in the
485 # converted code.
486 fmt_pass_converted_first_leaf: Optional["Leaf"] = None
488 def __init__(
489 self,
490 type: int,
491 value: str,
492 context: Context | None = None,
493 prefix: str | None = None,
494 fixers_applied: list[Any] = [],
495 opening_bracket: Optional["Leaf"] = None,
496 fmt_pass_converted_first_leaf: Optional["Leaf"] = None,
497 ) -> None:
498 """
499 Initializer.
501 Takes a type constant (a token number < 256), a string value, and an
502 optional context keyword argument.
503 """
505 assert 0 <= type < 256, type
506 if context is not None:
507 self._prefix, (self.lineno, self.column) = context
508 self.type = type
509 self.value = value
510 if prefix is not None:
511 self._prefix = prefix
512 self.fixers_applied: list[Any] | None = fixers_applied[:]
513 self.children = []
514 self.opening_bracket = opening_bracket
515 self.fmt_pass_converted_first_leaf = fmt_pass_converted_first_leaf
517 def __repr__(self) -> str:
518 """Return a canonical string representation."""
519 from .pgen2.token import tok_name
521 assert self.type is not None
522 return (
523 f"{self.__class__.__name__}({tok_name.get(self.type, self.type)},"
524 f" {self.value!r})"
525 )
527 def __str__(self) -> str:
528 """
529 Return a pretty string representation.
531 This reproduces the input source exactly.
532 """
533 return self._prefix + str(self.value)
535 def _eq(self, other: "Leaf") -> bool:
536 """Compare two nodes for equality."""
537 return (self.type, self.value) == (other.type, other.value)
539 def clone(self) -> "Leaf":
540 assert self.type is not None
541 """Return a cloned (deep) copy of self."""
542 return Leaf(
543 self.type,
544 self.value,
545 (self.prefix, (self.lineno, self.column)),
546 fixers_applied=self.fixers_applied,
547 )
549 def leaves(self) -> Iterator["Leaf"]:
550 yield self
552 def post_order(self) -> Iterator["Leaf"]:
553 """Return a post-order iterator for the tree."""
554 yield self
556 def pre_order(self) -> Iterator["Leaf"]:
557 """Return a pre-order iterator for the tree."""
558 yield self
560 @property
561 def prefix(self) -> str:
562 """
563 The whitespace and comments preceding this token in the input.
564 """
565 return self._prefix
567 @prefix.setter
568 def prefix(self, prefix: str) -> None:
569 self.changed()
570 self._prefix = prefix
573def convert(gr: Grammar, raw_node: RawNode) -> NL:
574 """
575 Convert raw node information to a Node or Leaf instance.
577 This is passed to the parser driver which calls it whenever a reduction of a
578 grammar rule produces a new complete node, so that the tree is build
579 strictly bottom-up.
580 """
581 type, value, context, children = raw_node
582 if children or type in gr.number2symbol:
583 # If there's exactly one child, return that child instead of
584 # creating a new node.
585 assert children is not None
586 if len(children) == 1:
587 return children[0]
588 return Node(type, children, context=context)
589 else:
590 return Leaf(type, value or "", context=context)
593_Results = dict[str, NL]
596class BasePattern:
597 """
598 A pattern is a tree matching pattern.
600 It looks for a specific node type (token or symbol), and
601 optionally for a specific content.
603 This is an abstract base class. There are three concrete
604 subclasses:
606 - LeafPattern matches a single leaf node;
607 - NodePattern matches a single node (usually non-leaf);
608 - WildcardPattern matches a sequence of nodes of variable length.
609 """
611 # Defaults for instance variables
612 type: int | None
613 type = None # Node type (token if < 256, symbol if >= 256)
614 content: Any = None # Optional content matching pattern
615 name: str | None = None # Optional name used to store match in results dict
617 def __new__(cls, *args, **kwds):
618 """Constructor that prevents BasePattern from being instantiated."""
619 assert cls is not BasePattern, "Cannot instantiate BasePattern"
620 return object.__new__(cls)
622 def __repr__(self) -> str:
623 assert self.type is not None
624 args = [type_repr(self.type), self.content, self.name]
625 while args and args[-1] is None:
626 del args[-1]
627 return f"{self.__class__.__name__}({', '.join(map(repr, args))})"
629 def _submatch(self, node, results=None) -> bool:
630 raise NotImplementedError
632 def optimize(self) -> "BasePattern":
633 """
634 A subclass can define this as a hook for optimizations.
636 Returns either self or another node with the same effect.
637 """
638 return self
640 def match(self, node: NL, results: _Results | None = None) -> bool:
641 """
642 Does this pattern exactly match a node?
644 Returns True if it matches, False if not.
646 If results is not None, it must be a dict which will be
647 updated with the nodes matching named subpatterns.
649 Default implementation for non-wildcard patterns.
650 """
651 if self.type is not None and node.type != self.type:
652 return False
653 if self.content is not None:
654 r: _Results | None = None
655 if results is not None:
656 r = {}
657 if not self._submatch(node, r):
658 return False
659 if r:
660 assert results is not None
661 results.update(r)
662 if results is not None and self.name:
663 results[self.name] = node
664 return True
666 def match_seq(self, nodes: list[NL], results: _Results | None = None) -> bool:
667 """
668 Does this pattern exactly match a sequence of nodes?
670 Default implementation for non-wildcard patterns.
671 """
672 if len(nodes) != 1:
673 return False
674 return self.match(nodes[0], results)
676 def generate_matches(self, nodes: list[NL]) -> Iterator[tuple[int, _Results]]:
677 """
678 Generator yielding all matches for this pattern.
680 Default implementation for non-wildcard patterns.
681 """
682 r: _Results = {}
683 if nodes and self.match(nodes[0], r):
684 yield 1, r
687class LeafPattern(BasePattern):
688 def __init__(
689 self,
690 type: int | None = None,
691 content: str | None = None,
692 name: str | None = None,
693 ) -> None:
694 """
695 Initializer. Takes optional type, content, and name.
697 The type, if given must be a token type (< 256). If not given,
698 this matches any *leaf* node; the content may still be required.
700 The content, if given, must be a string.
702 If a name is given, the matching node is stored in the results
703 dict under that key.
704 """
705 if type is not None:
706 assert 0 <= type < 256, type
707 if content is not None:
708 assert isinstance(content, str), repr(content)
709 self.type = type
710 self.content = content
711 self.name = name
713 def match(self, node: NL, results=None) -> bool:
714 """Override match() to insist on a leaf node."""
715 if not isinstance(node, Leaf):
716 return False
717 return BasePattern.match(self, node, results)
719 def _submatch(self, node, results=None):
720 """
721 Match the pattern's content to the node's children.
723 This assumes the node type matches and self.content is not None.
725 Returns True if it matches, False if not.
727 If results is not None, it must be a dict which will be
728 updated with the nodes matching named subpatterns.
730 When returning False, the results dict may still be updated.
731 """
732 return self.content == node.value
735class NodePattern(BasePattern):
736 wildcards: bool = False
738 def __init__(
739 self,
740 type: int | None = None,
741 content: Iterable[str] | None = None,
742 name: str | None = None,
743 ) -> None:
744 """
745 Initializer. Takes optional type, content, and name.
747 The type, if given, must be a symbol type (>= 256). If the
748 type is None this matches *any* single node (leaf or not),
749 except if content is not None, in which it only matches
750 non-leaf nodes that also match the content pattern.
752 The content, if not None, must be a sequence of Patterns that
753 must match the node's children exactly. If the content is
754 given, the type must not be None.
756 If a name is given, the matching node is stored in the results
757 dict under that key.
758 """
759 if type is not None:
760 assert type >= 256, type
761 if content is not None:
762 assert not isinstance(content, str), repr(content)
763 newcontent = list(content)
764 for i, item in enumerate(newcontent):
765 assert isinstance(item, BasePattern), (i, item)
766 # I don't even think this code is used anywhere, but it does cause
767 # unreachable errors from mypy. This function's signature does look
768 # odd though *shrug*.
769 if isinstance(item, WildcardPattern): # type: ignore[unreachable]
770 self.wildcards = True # type: ignore[unreachable]
771 self.type = type
772 self.content = newcontent # TODO: this is unbound when content is None
773 self.name = name
775 def _submatch(self, node, results=None) -> bool:
776 """
777 Match the pattern's content to the node's children.
779 This assumes the node type matches and self.content is not None.
781 Returns True if it matches, False if not.
783 If results is not None, it must be a dict which will be
784 updated with the nodes matching named subpatterns.
786 When returning False, the results dict may still be updated.
787 """
788 if self.wildcards:
789 for c, r in generate_matches(self.content, node.children):
790 if c == len(node.children):
791 if results is not None:
792 results.update(r)
793 return True
794 return False
795 if len(self.content) != len(node.children):
796 return False
797 for subpattern, child in zip(self.content, node.children):
798 if not subpattern.match(child, results):
799 return False
800 return True
803class WildcardPattern(BasePattern):
804 """
805 A wildcard pattern can match zero or more nodes.
807 This has all the flexibility needed to implement patterns like:
809 .* .+ .? .{m,n}
810 (a b c | d e | f)
811 (...)* (...)+ (...)? (...){m,n}
813 except it always uses non-greedy matching.
814 """
816 min: int
817 max: int
819 def __init__(
820 self,
821 content: str | None = None,
822 min: int = 0,
823 max: int = HUGE,
824 name: str | None = None,
825 ) -> None:
826 """
827 Initializer.
829 Args:
830 content: optional sequence of subsequences of patterns;
831 if absent, matches one node;
832 if present, each subsequence is an alternative [*]
833 min: optional minimum number of times to match, default 0
834 max: optional maximum number of times to match, default HUGE
835 name: optional name assigned to this match
837 [*] Thus, if content is [[a, b, c], [d, e], [f, g, h]] this is
838 equivalent to (a b c | d e | f g h); if content is None,
839 this is equivalent to '.' in regular expression terms.
840 The min and max parameters work as follows:
841 min=0, max=maxint: .*
842 min=1, max=maxint: .+
843 min=0, max=1: .?
844 min=1, max=1: .
845 If content is not None, replace the dot with the parenthesized
846 list of alternatives, e.g. (a b c | d e | f g h)*
847 """
848 assert 0 <= min <= max <= HUGE, (min, max)
849 if content is not None:
850 f = lambda s: tuple(s)
851 wrapped_content = tuple(map(f, content)) # Protect against alterations
852 # Check sanity of alternatives
853 assert len(wrapped_content), repr(
854 wrapped_content
855 ) # Can't have zero alternatives
856 for alt in wrapped_content:
857 assert len(alt), repr(alt) # Can have empty alternatives
858 self.content = wrapped_content
859 self.min = min
860 self.max = max
861 self.name = name
863 def optimize(self) -> Any:
864 """Optimize certain stacked wildcard patterns."""
865 subpattern = None
866 if (
867 self.content is not None
868 and len(self.content) == 1
869 and len(self.content[0]) == 1
870 ):
871 subpattern = self.content[0][0]
872 if self.min == 1 and self.max == 1:
873 if self.content is None:
874 return NodePattern(name=self.name)
875 if subpattern is not None and self.name == subpattern.name:
876 return subpattern.optimize()
877 if (
878 self.min <= 1
879 and isinstance(subpattern, WildcardPattern)
880 and subpattern.min <= 1
881 and self.name == subpattern.name
882 ):
883 return WildcardPattern(
884 subpattern.content,
885 self.min * subpattern.min,
886 self.max * subpattern.max,
887 subpattern.name,
888 )
889 return self
891 def match(self, node, results=None) -> bool:
892 """Does this pattern exactly match a node?"""
893 return self.match_seq([node], results)
895 def match_seq(self, nodes, results=None) -> bool:
896 """Does this pattern exactly match a sequence of nodes?"""
897 for c, r in self.generate_matches(nodes):
898 if c == len(nodes):
899 if results is not None:
900 results.update(r)
901 if self.name:
902 results[self.name] = list(nodes)
903 return True
904 return False
906 def generate_matches(self, nodes) -> Iterator[tuple[int, _Results]]:
907 """
908 Generator yielding matches for a sequence of nodes.
910 Args:
911 nodes: sequence of nodes
913 Yields:
914 (count, results) tuples where:
915 count: the match comprises nodes[:count];
916 results: dict containing named submatches.
917 """
918 if self.content is None:
919 # Shortcut for special case (see __init__.__doc__)
920 for count in range(self.min, 1 + min(len(nodes), self.max)):
921 r = {}
922 if self.name:
923 r[self.name] = nodes[:count]
924 yield count, r
925 elif self.name == "bare_name":
926 yield self._bare_name_matches(nodes)
927 else:
928 # The reason for this is that hitting the recursion limit usually
929 # results in some ugly messages about how RuntimeErrors are being
930 # ignored. We only have to do this on CPython, though, because other
931 # implementations don't have this nasty bug in the first place.
932 if hasattr(sys, "getrefcount"):
933 save_stderr = sys.stderr
934 sys.stderr = StringIO()
935 try:
936 for count, r in self._recursive_matches(nodes, 0):
937 if self.name:
938 r[self.name] = nodes[:count]
939 yield count, r
940 except RuntimeError:
941 # We fall back to the iterative pattern matching scheme if the recursive
942 # scheme hits the recursion limit.
943 for count, r in self._iterative_matches(nodes):
944 if self.name:
945 r[self.name] = nodes[:count]
946 yield count, r
947 finally:
948 if hasattr(sys, "getrefcount"):
949 sys.stderr = save_stderr
951 def _iterative_matches(self, nodes) -> Iterator[tuple[int, _Results]]:
952 """Helper to iteratively yield the matches."""
953 nodelen = len(nodes)
954 if 0 >= self.min:
955 yield 0, {}
957 results = []
958 # generate matches that use just one alt from self.content
959 for alt in self.content:
960 for c, r in generate_matches(alt, nodes):
961 yield c, r
962 results.append((c, r))
964 # for each match, iterate down the nodes
965 while results:
966 new_results = []
967 for c0, r0 in results:
968 # stop if the entire set of nodes has been matched
969 if c0 < nodelen and c0 <= self.max:
970 for alt in self.content:
971 for c1, r1 in generate_matches(alt, nodes[c0:]):
972 if c1 > 0:
973 r = {}
974 r.update(r0)
975 r.update(r1)
976 yield c0 + c1, r
977 new_results.append((c0 + c1, r))
978 results = new_results
980 def _bare_name_matches(self, nodes) -> tuple[int, _Results]:
981 """Special optimized matcher for bare_name."""
982 count = 0
983 r = {} # type: _Results
984 done = False
985 max = len(nodes)
986 while not done and count < max:
987 done = True
988 for leaf in self.content:
989 if leaf[0].match(nodes[count], r):
990 count += 1
991 done = False
992 break
993 assert self.name is not None
994 r[self.name] = nodes[:count]
995 return count, r
997 def _recursive_matches(self, nodes, count) -> Iterator[tuple[int, _Results]]:
998 """Helper to recursively yield the matches."""
999 assert self.content is not None
1000 if count >= self.min:
1001 yield 0, {}
1002 if count < self.max:
1003 for alt in self.content:
1004 for c0, r0 in generate_matches(alt, nodes):
1005 for c1, r1 in self._recursive_matches(nodes[c0:], count + 1):
1006 r = {}
1007 r.update(r0)
1008 r.update(r1)
1009 yield c0 + c1, r
1012class NegatedPattern(BasePattern):
1013 def __init__(self, content: BasePattern | None = None) -> None:
1014 """
1015 Initializer.
1017 The argument is either a pattern or None. If it is None, this
1018 only matches an empty sequence (effectively '$' in regex
1019 lingo). If it is not None, this matches whenever the argument
1020 pattern doesn't have any matches.
1021 """
1022 if content is not None:
1023 assert isinstance(content, BasePattern), repr(content)
1024 self.content = content
1026 def match(self, node, results=None) -> bool:
1027 # We never match a node in its entirety
1028 return False
1030 def match_seq(self, nodes, results=None) -> bool:
1031 # We only match an empty sequence of nodes in its entirety
1032 return len(nodes) == 0
1034 def generate_matches(self, nodes: list[NL]) -> Iterator[tuple[int, _Results]]:
1035 if self.content is None:
1036 # Return a match if there is an empty sequence
1037 if len(nodes) == 0:
1038 yield 0, {}
1039 else:
1040 # Return a match if the argument pattern has no matches
1041 for c, r in self.content.generate_matches(nodes):
1042 return
1043 yield 0, {}
1046def generate_matches(
1047 patterns: list[BasePattern], nodes: list[NL]
1048) -> Iterator[tuple[int, _Results]]:
1049 """
1050 Generator yielding matches for a sequence of patterns and nodes.
1052 Args:
1053 patterns: a sequence of patterns
1054 nodes: a sequence of nodes
1056 Yields:
1057 (count, results) tuples where:
1058 count: the entire sequence of patterns matches nodes[:count];
1059 results: dict containing named submatches.
1060 """
1061 if not patterns:
1062 yield 0, {}
1063 else:
1064 p, rest = patterns[0], patterns[1:]
1065 for c0, r0 in p.generate_matches(nodes):
1066 if not rest:
1067 yield c0, r0
1068 else:
1069 for c1, r1 in generate_matches(rest, nodes[c0:]):
1070 r = {}
1071 r.update(r0)
1072 r.update(r1)
1073 yield c0 + c1, r