Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/blib2to3/pytree.py: 32%
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, Union[str, int]] = {}
30def type_repr(type_num: int) -> Union[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
69 was_checked: bool = False
71 def __new__(cls, *args, **kwds):
72 """Constructor that prevents Base from being instantiated."""
73 assert cls is not Base, "Cannot instantiate Base"
74 return object.__new__(cls)
76 def __eq__(self, other: Any) -> bool:
77 """
78 Compare two nodes for equality.
80 This calls the method _eq().
81 """
82 if self.__class__ is not other.__class__:
83 return NotImplemented
84 return self._eq(other)
86 @property
87 def prefix(self) -> str:
88 raise NotImplementedError
90 def _eq(self: _P, other: _P) -> bool:
91 """
92 Compare two nodes for equality.
94 This is called by __eq__ and __ne__. It is only called if the two nodes
95 have the same type. This must be implemented by the concrete subclass.
96 Nodes should be considered equal if they have the same structure,
97 ignoring the prefix string and other context information.
98 """
99 raise NotImplementedError
101 def __deepcopy__(self: _P, memo: Any) -> _P:
102 return self.clone()
104 def clone(self: _P) -> _P:
105 """
106 Return a cloned (deep) copy of self.
108 This must be implemented by the concrete subclass.
109 """
110 raise NotImplementedError
112 def post_order(self) -> Iterator[NL]:
113 """
114 Return a post-order iterator for the tree.
116 This must be implemented by the concrete subclass.
117 """
118 raise NotImplementedError
120 def pre_order(self) -> Iterator[NL]:
121 """
122 Return a pre-order iterator for the tree.
124 This must be implemented by the concrete subclass.
125 """
126 raise NotImplementedError
128 def replace(self, new: Union[NL, list[NL]]) -> None:
129 """Replace this node with a new one in the parent."""
130 assert self.parent is not None, str(self)
131 assert new is not None
132 if not isinstance(new, list):
133 new = [new]
134 l_children = []
135 found = False
136 for ch in self.parent.children:
137 if ch is self:
138 assert not found, (self.parent.children, self, new)
139 if new is not None:
140 l_children.extend(new)
141 found = True
142 else:
143 l_children.append(ch)
144 assert found, (self.children, self, new)
145 self.parent.children = l_children
146 self.parent.changed()
147 self.parent.invalidate_sibling_maps()
148 for x in new:
149 x.parent = self.parent
150 self.parent = None
152 def get_lineno(self) -> Optional[int]:
153 """Return the line number which generated the invocant node."""
154 node = self
155 while not isinstance(node, Leaf):
156 if not node.children:
157 return None
158 node = node.children[0]
159 return node.lineno
161 def changed(self) -> None:
162 if self.was_changed:
163 return
164 if self.parent:
165 self.parent.changed()
166 self.was_changed = True
168 def remove(self) -> Optional[int]:
169 """
170 Remove the node from the tree. Returns the position of the node in its
171 parent's children before it was removed.
172 """
173 if self.parent:
174 for i, node in enumerate(self.parent.children):
175 if node is self:
176 del self.parent.children[i]
177 self.parent.changed()
178 self.parent.invalidate_sibling_maps()
179 self.parent = None
180 return i
181 return None
183 @property
184 def next_sibling(self) -> Optional[NL]:
185 """
186 The node immediately following the invocant in their parent's children
187 list. If the invocant does not have a next sibling, it is None
188 """
189 if self.parent is None:
190 return None
192 if self.parent.next_sibling_map is None:
193 self.parent.update_sibling_maps()
194 assert self.parent.next_sibling_map is not None
195 return self.parent.next_sibling_map[id(self)]
197 @property
198 def prev_sibling(self) -> Optional[NL]:
199 """
200 The node immediately preceding the invocant in their parent's children
201 list. If the invocant does not have a previous sibling, it is None.
202 """
203 if self.parent is None:
204 return None
206 if self.parent.prev_sibling_map is None:
207 self.parent.update_sibling_maps()
208 assert self.parent.prev_sibling_map is not None
209 return self.parent.prev_sibling_map[id(self)]
211 def leaves(self) -> Iterator["Leaf"]:
212 for child in self.children:
213 yield from child.leaves()
215 def depth(self) -> int:
216 if self.parent is None:
217 return 0
218 return 1 + self.parent.depth()
220 def get_suffix(self) -> str:
221 """
222 Return the string immediately following the invocant node. This is
223 effectively equivalent to node.next_sibling.prefix
224 """
225 next_sib = self.next_sibling
226 if next_sib is None:
227 return ""
228 prefix = next_sib.prefix
229 return prefix
232class Node(Base):
233 """Concrete implementation for interior nodes."""
235 fixers_applied: Optional[list[Any]]
236 used_names: Optional[set[str]]
238 def __init__(
239 self,
240 type: int,
241 children: list[NL],
242 context: Optional[Any] = None,
243 prefix: Optional[str] = None,
244 fixers_applied: Optional[list[Any]] = None,
245 ) -> None:
246 """
247 Initializer.
249 Takes a type constant (a symbol number >= 256), a sequence of
250 child nodes, and an optional context keyword argument.
252 As a side effect, the parent pointers of the children are updated.
253 """
254 assert type >= 256, type
255 self.type = type
256 self.children = list(children)
257 for ch in self.children:
258 assert ch.parent is None, repr(ch)
259 ch.parent = self
260 self.invalidate_sibling_maps()
261 if prefix is not None:
262 self.prefix = prefix
263 if fixers_applied:
264 self.fixers_applied = fixers_applied[:]
265 else:
266 self.fixers_applied = None
268 def __repr__(self) -> str:
269 """Return a canonical string representation."""
270 assert self.type is not None
271 return f"{self.__class__.__name__}({type_repr(self.type)}, {self.children!r})"
273 def __str__(self) -> str:
274 """
275 Return a pretty string representation.
277 This reproduces the input source exactly.
278 """
279 return "".join(map(str, self.children))
281 def _eq(self, other: Base) -> bool:
282 """Compare two nodes for equality."""
283 return (self.type, self.children) == (other.type, other.children)
285 def clone(self) -> "Node":
286 assert self.type is not None
287 """Return a cloned (deep) copy of self."""
288 return Node(
289 self.type,
290 [ch.clone() for ch in self.children],
291 fixers_applied=self.fixers_applied,
292 )
294 def post_order(self) -> Iterator[NL]:
295 """Return a post-order iterator for the tree."""
296 for child in self.children:
297 yield from child.post_order()
298 yield self
300 def pre_order(self) -> Iterator[NL]:
301 """Return a pre-order iterator for the tree."""
302 yield self
303 for child in self.children:
304 yield from child.pre_order()
306 @property
307 def prefix(self) -> str:
308 """
309 The whitespace and comments preceding this node in the input.
310 """
311 if not self.children:
312 return ""
313 return self.children[0].prefix
315 @prefix.setter
316 def prefix(self, prefix: str) -> None:
317 if self.children:
318 self.children[0].prefix = prefix
320 def set_child(self, i: int, child: NL) -> None:
321 """
322 Equivalent to 'node.children[i] = child'. This method also sets the
323 child's parent attribute appropriately.
324 """
325 child.parent = self
326 self.children[i].parent = None
327 self.children[i] = child
328 self.changed()
329 self.invalidate_sibling_maps()
331 def insert_child(self, i: int, child: NL) -> None:
332 """
333 Equivalent to 'node.children.insert(i, child)'. This method also sets
334 the child's parent attribute appropriately.
335 """
336 child.parent = self
337 self.children.insert(i, child)
338 self.changed()
339 self.invalidate_sibling_maps()
341 def append_child(self, child: NL) -> None:
342 """
343 Equivalent to 'node.children.append(child)'. This method also sets the
344 child's parent attribute appropriately.
345 """
346 child.parent = self
347 self.children.append(child)
348 self.changed()
349 self.invalidate_sibling_maps()
351 def invalidate_sibling_maps(self) -> None:
352 self.prev_sibling_map: Optional[dict[int, Optional[NL]]] = None
353 self.next_sibling_map: Optional[dict[int, Optional[NL]]] = None
355 def update_sibling_maps(self) -> None:
356 _prev: dict[int, Optional[NL]] = {}
357 _next: dict[int, Optional[NL]] = {}
358 self.prev_sibling_map = _prev
359 self.next_sibling_map = _next
360 previous: Optional[NL] = None
361 for current in self.children:
362 _prev[id(current)] = previous
363 _next[id(previous)] = current
364 previous = current
365 _next[id(current)] = None
368class Leaf(Base):
369 """Concrete implementation for leaf nodes."""
371 # Default values for instance variables
372 value: str
373 fixers_applied: list[Any]
374 bracket_depth: int
375 # Changed later in brackets.py
376 opening_bracket: Optional["Leaf"] = None
377 used_names: Optional[set[str]]
378 _prefix = "" # Whitespace and comments preceding this token in the input
379 lineno: int = 0 # Line where this token starts in the input
380 column: int = 0 # Column where this token starts in the input
381 # If not None, this Leaf is created by converting a block of fmt off/skip
382 # code, and `fmt_pass_converted_first_leaf` points to the first Leaf in the
383 # converted code.
384 fmt_pass_converted_first_leaf: Optional["Leaf"] = None
386 def __init__(
387 self,
388 type: int,
389 value: str,
390 context: Optional[Context] = None,
391 prefix: Optional[str] = None,
392 fixers_applied: list[Any] = [],
393 opening_bracket: Optional["Leaf"] = None,
394 fmt_pass_converted_first_leaf: Optional["Leaf"] = None,
395 ) -> None:
396 """
397 Initializer.
399 Takes a type constant (a token number < 256), a string value, and an
400 optional context keyword argument.
401 """
403 assert 0 <= type < 256, type
404 if context is not None:
405 self._prefix, (self.lineno, self.column) = context
406 self.type = type
407 self.value = value
408 if prefix is not None:
409 self._prefix = prefix
410 self.fixers_applied: Optional[list[Any]] = fixers_applied[:]
411 self.children = []
412 self.opening_bracket = opening_bracket
413 self.fmt_pass_converted_first_leaf = fmt_pass_converted_first_leaf
415 def __repr__(self) -> str:
416 """Return a canonical string representation."""
417 from .pgen2.token import tok_name
419 assert self.type is not None
420 return (
421 f"{self.__class__.__name__}({tok_name.get(self.type, self.type)},"
422 f" {self.value!r})"
423 )
425 def __str__(self) -> str:
426 """
427 Return a pretty string representation.
429 This reproduces the input source exactly.
430 """
431 return self._prefix + str(self.value)
433 def _eq(self, other: "Leaf") -> bool:
434 """Compare two nodes for equality."""
435 return (self.type, self.value) == (other.type, other.value)
437 def clone(self) -> "Leaf":
438 assert self.type is not None
439 """Return a cloned (deep) copy of self."""
440 return Leaf(
441 self.type,
442 self.value,
443 (self.prefix, (self.lineno, self.column)),
444 fixers_applied=self.fixers_applied,
445 )
447 def leaves(self) -> Iterator["Leaf"]:
448 yield self
450 def post_order(self) -> Iterator["Leaf"]:
451 """Return a post-order iterator for the tree."""
452 yield self
454 def pre_order(self) -> Iterator["Leaf"]:
455 """Return a pre-order iterator for the tree."""
456 yield self
458 @property
459 def prefix(self) -> str:
460 """
461 The whitespace and comments preceding this token in the input.
462 """
463 return self._prefix
465 @prefix.setter
466 def prefix(self, prefix: str) -> None:
467 self.changed()
468 self._prefix = prefix
471def convert(gr: Grammar, raw_node: RawNode) -> NL:
472 """
473 Convert raw node information to a Node or Leaf instance.
475 This is passed to the parser driver which calls it whenever a reduction of a
476 grammar rule produces a new complete node, so that the tree is build
477 strictly bottom-up.
478 """
479 type, value, context, children = raw_node
480 if children or type in gr.number2symbol:
481 # If there's exactly one child, return that child instead of
482 # creating a new node.
483 assert children is not None
484 if len(children) == 1:
485 return children[0]
486 return Node(type, children, context=context)
487 else:
488 return Leaf(type, value or "", context=context)
491_Results = dict[str, NL]
494class BasePattern:
495 """
496 A pattern is a tree matching pattern.
498 It looks for a specific node type (token or symbol), and
499 optionally for a specific content.
501 This is an abstract base class. There are three concrete
502 subclasses:
504 - LeafPattern matches a single leaf node;
505 - NodePattern matches a single node (usually non-leaf);
506 - WildcardPattern matches a sequence of nodes of variable length.
507 """
509 # Defaults for instance variables
510 type: Optional[int]
511 type = None # Node type (token if < 256, symbol if >= 256)
512 content: Any = None # Optional content matching pattern
513 name: Optional[str] = None # Optional name used to store match in results dict
515 def __new__(cls, *args, **kwds):
516 """Constructor that prevents BasePattern from being instantiated."""
517 assert cls is not BasePattern, "Cannot instantiate BasePattern"
518 return object.__new__(cls)
520 def __repr__(self) -> str:
521 assert self.type is not None
522 args = [type_repr(self.type), self.content, self.name]
523 while args and args[-1] is None:
524 del args[-1]
525 return f"{self.__class__.__name__}({', '.join(map(repr, args))})"
527 def _submatch(self, node, results=None) -> bool:
528 raise NotImplementedError
530 def optimize(self) -> "BasePattern":
531 """
532 A subclass can define this as a hook for optimizations.
534 Returns either self or another node with the same effect.
535 """
536 return self
538 def match(self, node: NL, results: Optional[_Results] = None) -> bool:
539 """
540 Does this pattern exactly match a node?
542 Returns True if it matches, False if not.
544 If results is not None, it must be a dict which will be
545 updated with the nodes matching named subpatterns.
547 Default implementation for non-wildcard patterns.
548 """
549 if self.type is not None and node.type != self.type:
550 return False
551 if self.content is not None:
552 r: Optional[_Results] = None
553 if results is not None:
554 r = {}
555 if not self._submatch(node, r):
556 return False
557 if r:
558 assert results is not None
559 results.update(r)
560 if results is not None and self.name:
561 results[self.name] = node
562 return True
564 def match_seq(self, nodes: list[NL], results: Optional[_Results] = None) -> bool:
565 """
566 Does this pattern exactly match a sequence of nodes?
568 Default implementation for non-wildcard patterns.
569 """
570 if len(nodes) != 1:
571 return False
572 return self.match(nodes[0], results)
574 def generate_matches(self, nodes: list[NL]) -> Iterator[tuple[int, _Results]]:
575 """
576 Generator yielding all matches for this pattern.
578 Default implementation for non-wildcard patterns.
579 """
580 r: _Results = {}
581 if nodes and self.match(nodes[0], r):
582 yield 1, r
585class LeafPattern(BasePattern):
586 def __init__(
587 self,
588 type: Optional[int] = None,
589 content: Optional[str] = None,
590 name: Optional[str] = None,
591 ) -> None:
592 """
593 Initializer. Takes optional type, content, and name.
595 The type, if given must be a token type (< 256). If not given,
596 this matches any *leaf* node; the content may still be required.
598 The content, if given, must be a string.
600 If a name is given, the matching node is stored in the results
601 dict under that key.
602 """
603 if type is not None:
604 assert 0 <= type < 256, type
605 if content is not None:
606 assert isinstance(content, str), repr(content)
607 self.type = type
608 self.content = content
609 self.name = name
611 def match(self, node: NL, results=None) -> bool:
612 """Override match() to insist on a leaf node."""
613 if not isinstance(node, Leaf):
614 return False
615 return BasePattern.match(self, node, results)
617 def _submatch(self, node, results=None):
618 """
619 Match the pattern's content to the node's children.
621 This assumes the node type matches and self.content is not None.
623 Returns True if it matches, False if not.
625 If results is not None, it must be a dict which will be
626 updated with the nodes matching named subpatterns.
628 When returning False, the results dict may still be updated.
629 """
630 return self.content == node.value
633class NodePattern(BasePattern):
634 wildcards: bool = False
636 def __init__(
637 self,
638 type: Optional[int] = None,
639 content: Optional[Iterable[str]] = None,
640 name: Optional[str] = None,
641 ) -> None:
642 """
643 Initializer. Takes optional type, content, and name.
645 The type, if given, must be a symbol type (>= 256). If the
646 type is None this matches *any* single node (leaf or not),
647 except if content is not None, in which it only matches
648 non-leaf nodes that also match the content pattern.
650 The content, if not None, must be a sequence of Patterns that
651 must match the node's children exactly. If the content is
652 given, the type must not be None.
654 If a name is given, the matching node is stored in the results
655 dict under that key.
656 """
657 if type is not None:
658 assert type >= 256, type
659 if content is not None:
660 assert not isinstance(content, str), repr(content)
661 newcontent = list(content)
662 for i, item in enumerate(newcontent):
663 assert isinstance(item, BasePattern), (i, item)
664 # I don't even think this code is used anywhere, but it does cause
665 # unreachable errors from mypy. This function's signature does look
666 # odd though *shrug*.
667 if isinstance(item, WildcardPattern): # type: ignore[unreachable]
668 self.wildcards = True # type: ignore[unreachable]
669 self.type = type
670 self.content = newcontent # TODO: this is unbound when content is None
671 self.name = name
673 def _submatch(self, node, results=None) -> bool:
674 """
675 Match the pattern's content to the node's children.
677 This assumes the node type matches and self.content is not None.
679 Returns True if it matches, False if not.
681 If results is not None, it must be a dict which will be
682 updated with the nodes matching named subpatterns.
684 When returning False, the results dict may still be updated.
685 """
686 if self.wildcards:
687 for c, r in generate_matches(self.content, node.children):
688 if c == len(node.children):
689 if results is not None:
690 results.update(r)
691 return True
692 return False
693 if len(self.content) != len(node.children):
694 return False
695 for subpattern, child in zip(self.content, node.children):
696 if not subpattern.match(child, results):
697 return False
698 return True
701class WildcardPattern(BasePattern):
702 """
703 A wildcard pattern can match zero or more nodes.
705 This has all the flexibility needed to implement patterns like:
707 .* .+ .? .{m,n}
708 (a b c | d e | f)
709 (...)* (...)+ (...)? (...){m,n}
711 except it always uses non-greedy matching.
712 """
714 min: int
715 max: int
717 def __init__(
718 self,
719 content: Optional[str] = None,
720 min: int = 0,
721 max: int = HUGE,
722 name: Optional[str] = None,
723 ) -> None:
724 """
725 Initializer.
727 Args:
728 content: optional sequence of subsequences of patterns;
729 if absent, matches one node;
730 if present, each subsequence is an alternative [*]
731 min: optional minimum number of times to match, default 0
732 max: optional maximum number of times to match, default HUGE
733 name: optional name assigned to this match
735 [*] Thus, if content is [[a, b, c], [d, e], [f, g, h]] this is
736 equivalent to (a b c | d e | f g h); if content is None,
737 this is equivalent to '.' in regular expression terms.
738 The min and max parameters work as follows:
739 min=0, max=maxint: .*
740 min=1, max=maxint: .+
741 min=0, max=1: .?
742 min=1, max=1: .
743 If content is not None, replace the dot with the parenthesized
744 list of alternatives, e.g. (a b c | d e | f g h)*
745 """
746 assert 0 <= min <= max <= HUGE, (min, max)
747 if content is not None:
748 f = lambda s: tuple(s)
749 wrapped_content = tuple(map(f, content)) # Protect against alterations
750 # Check sanity of alternatives
751 assert len(wrapped_content), repr(
752 wrapped_content
753 ) # Can't have zero alternatives
754 for alt in wrapped_content:
755 assert len(alt), repr(alt) # Can have empty alternatives
756 self.content = wrapped_content
757 self.min = min
758 self.max = max
759 self.name = name
761 def optimize(self) -> Any:
762 """Optimize certain stacked wildcard patterns."""
763 subpattern = None
764 if (
765 self.content is not None
766 and len(self.content) == 1
767 and len(self.content[0]) == 1
768 ):
769 subpattern = self.content[0][0]
770 if self.min == 1 and self.max == 1:
771 if self.content is None:
772 return NodePattern(name=self.name)
773 if subpattern is not None and self.name == subpattern.name:
774 return subpattern.optimize()
775 if (
776 self.min <= 1
777 and isinstance(subpattern, WildcardPattern)
778 and subpattern.min <= 1
779 and self.name == subpattern.name
780 ):
781 return WildcardPattern(
782 subpattern.content,
783 self.min * subpattern.min,
784 self.max * subpattern.max,
785 subpattern.name,
786 )
787 return self
789 def match(self, node, results=None) -> bool:
790 """Does this pattern exactly match a node?"""
791 return self.match_seq([node], results)
793 def match_seq(self, nodes, results=None) -> bool:
794 """Does this pattern exactly match a sequence of nodes?"""
795 for c, r in self.generate_matches(nodes):
796 if c == len(nodes):
797 if results is not None:
798 results.update(r)
799 if self.name:
800 results[self.name] = list(nodes)
801 return True
802 return False
804 def generate_matches(self, nodes) -> Iterator[tuple[int, _Results]]:
805 """
806 Generator yielding matches for a sequence of nodes.
808 Args:
809 nodes: sequence of nodes
811 Yields:
812 (count, results) tuples where:
813 count: the match comprises nodes[:count];
814 results: dict containing named submatches.
815 """
816 if self.content is None:
817 # Shortcut for special case (see __init__.__doc__)
818 for count in range(self.min, 1 + min(len(nodes), self.max)):
819 r = {}
820 if self.name:
821 r[self.name] = nodes[:count]
822 yield count, r
823 elif self.name == "bare_name":
824 yield self._bare_name_matches(nodes)
825 else:
826 # The reason for this is that hitting the recursion limit usually
827 # results in some ugly messages about how RuntimeErrors are being
828 # ignored. We only have to do this on CPython, though, because other
829 # implementations don't have this nasty bug in the first place.
830 if hasattr(sys, "getrefcount"):
831 save_stderr = sys.stderr
832 sys.stderr = StringIO()
833 try:
834 for count, r in self._recursive_matches(nodes, 0):
835 if self.name:
836 r[self.name] = nodes[:count]
837 yield count, r
838 except RuntimeError:
839 # We fall back to the iterative pattern matching scheme if the recursive
840 # scheme hits the recursion limit.
841 for count, r in self._iterative_matches(nodes):
842 if self.name:
843 r[self.name] = nodes[:count]
844 yield count, r
845 finally:
846 if hasattr(sys, "getrefcount"):
847 sys.stderr = save_stderr
849 def _iterative_matches(self, nodes) -> Iterator[tuple[int, _Results]]:
850 """Helper to iteratively yield the matches."""
851 nodelen = len(nodes)
852 if 0 >= self.min:
853 yield 0, {}
855 results = []
856 # generate matches that use just one alt from self.content
857 for alt in self.content:
858 for c, r in generate_matches(alt, nodes):
859 yield c, r
860 results.append((c, r))
862 # for each match, iterate down the nodes
863 while results:
864 new_results = []
865 for c0, r0 in results:
866 # stop if the entire set of nodes has been matched
867 if c0 < nodelen and c0 <= self.max:
868 for alt in self.content:
869 for c1, r1 in generate_matches(alt, nodes[c0:]):
870 if c1 > 0:
871 r = {}
872 r.update(r0)
873 r.update(r1)
874 yield c0 + c1, r
875 new_results.append((c0 + c1, r))
876 results = new_results
878 def _bare_name_matches(self, nodes) -> tuple[int, _Results]:
879 """Special optimized matcher for bare_name."""
880 count = 0
881 r = {} # type: _Results
882 done = False
883 max = len(nodes)
884 while not done and count < max:
885 done = True
886 for leaf in self.content:
887 if leaf[0].match(nodes[count], r):
888 count += 1
889 done = False
890 break
891 assert self.name is not None
892 r[self.name] = nodes[:count]
893 return count, r
895 def _recursive_matches(self, nodes, count) -> Iterator[tuple[int, _Results]]:
896 """Helper to recursively yield the matches."""
897 assert self.content is not None
898 if count >= self.min:
899 yield 0, {}
900 if count < self.max:
901 for alt in self.content:
902 for c0, r0 in generate_matches(alt, nodes):
903 for c1, r1 in self._recursive_matches(nodes[c0:], count + 1):
904 r = {}
905 r.update(r0)
906 r.update(r1)
907 yield c0 + c1, r
910class NegatedPattern(BasePattern):
911 def __init__(self, content: Optional[BasePattern] = None) -> None:
912 """
913 Initializer.
915 The argument is either a pattern or None. If it is None, this
916 only matches an empty sequence (effectively '$' in regex
917 lingo). If it is not None, this matches whenever the argument
918 pattern doesn't have any matches.
919 """
920 if content is not None:
921 assert isinstance(content, BasePattern), repr(content)
922 self.content = content
924 def match(self, node, results=None) -> bool:
925 # We never match a node in its entirety
926 return False
928 def match_seq(self, nodes, results=None) -> bool:
929 # We only match an empty sequence of nodes in its entirety
930 return len(nodes) == 0
932 def generate_matches(self, nodes: list[NL]) -> Iterator[tuple[int, _Results]]:
933 if self.content is None:
934 # Return a match if there is an empty sequence
935 if len(nodes) == 0:
936 yield 0, {}
937 else:
938 # Return a match if the argument pattern has no matches
939 for c, r in self.content.generate_matches(nodes):
940 return
941 yield 0, {}
944def generate_matches(
945 patterns: list[BasePattern], nodes: list[NL]
946) -> Iterator[tuple[int, _Results]]:
947 """
948 Generator yielding matches for a sequence of patterns and nodes.
950 Args:
951 patterns: a sequence of patterns
952 nodes: a sequence of nodes
954 Yields:
955 (count, results) tuples where:
956 count: the entire sequence of patterns matches nodes[:count];
957 results: dict containing named submatches.
958 """
959 if not patterns:
960 yield 0, {}
961 else:
962 p, rest = patterns[0], patterns[1:]
963 for c0, r0 in p.generate_matches(nodes):
964 if not rest:
965 yield c0, r0
966 else:
967 for c1, r1 in generate_matches(rest, nodes[c0:]):
968 r = {}
969 r.update(r0)
970 r.update(r1)
971 yield c0 + c1, r