| 58 | |
| 59 | |
| 60 | class Rule: |
| 61 | def __init__(self, name: str, type: str | None, rhs: Rhs, flags: frozenset[str] | None = None): |
| 62 | self.name = name |
| 63 | self.type = type |
| 64 | self.rhs = rhs |
| 65 | self.flags = flags or frozenset() |
| 66 | self.left_recursive = False |
| 67 | self.leader = False |
| 68 | |
| 69 | def is_loop(self) -> bool: |
| 70 | return self.name.startswith("_loop") |
| 71 | |
| 72 | def is_gather(self) -> bool: |
| 73 | return self.name.startswith("_gather") |
| 74 | |
| 75 | def __str__(self) -> str: |
| 76 | if SIMPLE_STR or self.type is None: |
| 77 | res = f"{self.name}: {self.rhs}" |
| 78 | else: |
| 79 | res = f"{self.name}[{self.type}]: {self.rhs}" |
| 80 | if len(res) < 88: |
| 81 | return res |
| 82 | lines = [res.split(":")[0] + ":"] |
| 83 | lines += [f" | {alt}" for alt in self.rhs.alts] |
| 84 | return "\n".join(lines) |
| 85 | |
| 86 | def __repr__(self) -> str: |
| 87 | return f"Rule({self.name!r}, {self.type!r}, {self.rhs!r})" |
| 88 | |
| 89 | def __iter__(self) -> Iterator[Rhs]: |
| 90 | yield self.rhs |
| 91 | |
| 92 | def flatten(self) -> Rhs: |
| 93 | # If it's a single parenthesized group, flatten it. |
| 94 | rhs = self.rhs |
| 95 | if ( |
| 96 | not self.is_loop() |
| 97 | and len(rhs.alts) == 1 |
| 98 | and len(rhs.alts[0].items) == 1 |
| 99 | and isinstance(rhs.alts[0].items[0].item, Group) |
| 100 | ): |
| 101 | rhs = rhs.alts[0].items[0].item.rhs |
| 102 | return rhs |
| 103 | |
| 104 | |
| 105 | class Leaf: |
no outgoing calls
no test coverage detected
searching dependent graphs…