1from __future__ import annotations
2
3from abc import abstractmethod
4from typing import (
5    TYPE_CHECKING,
6    AbstractSet,
7    Any,
8    Dict,
9    Iterable,
10    Iterator,
11    List,
12    Optional,
13    Set,
14    Tuple,
15    Union,
16)
17
18if TYPE_CHECKING:
19    from pegen.parser_generator import ParserGenerator
20
21
22class GrammarError(Exception):
23    pass
24
25
26class GrammarVisitor:
27    def visit(self, node: Any, *args: Any, **kwargs: Any) -> Any:
28        """Visit a node."""
29        method = "visit_" + node.__class__.__name__
30        visitor = getattr(self, method, self.generic_visit)
31        return visitor(node, *args, **kwargs)
32
33    def generic_visit(self, node: Iterable[Any], *args: Any, **kwargs: Any) -> Any:
34        """Called if no explicit visitor function exists for a node."""
35        for value in node:
36            if isinstance(value, list):
37                for item in value:
38                    self.visit(item, *args, **kwargs)
39            else:
40                self.visit(value, *args, **kwargs)
41
42
43class Grammar:
44    def __init__(self, rules: Iterable[Rule], metas: Iterable[Tuple[str, Optional[str]]]):
45        self.rules = {rule.name: rule for rule in rules}
46        self.metas = dict(metas)
47
48    def __str__(self) -> str:
49        return "\n".join(str(rule) for name, rule in self.rules.items())
50
51    def __repr__(self) -> str:
52        lines = ["Grammar("]
53        lines.append("  [")
54        for rule in self.rules.values():
55            lines.append(f"    {repr(rule)},")
56        lines.append("  ],")
57        lines.append("  {repr(list(self.metas.items()))}")
58        lines.append(")")
59        return "\n".join(lines)
60
61    def __iter__(self) -> Iterator[Rule]:
62        yield from self.rules.values()
63
64
65# Global flag whether we want actions in __str__() -- default off.
66SIMPLE_STR = True
67
68
69class Rule:
70    def __init__(self, name: str, type: Optional[str], rhs: Rhs, memo: Optional[object] = None):
71        self.name = name
72        self.type = type
73        self.rhs = rhs
74        self.memo = bool(memo)
75        self.left_recursive = False
76        self.leader = False
77
78    def is_loop(self) -> bool:
79        return self.name.startswith("_loop")
80
81    def is_gather(self) -> bool:
82        return self.name.startswith("_gather")
83
84    def __str__(self) -> str:
85        if SIMPLE_STR or self.type is None:
86            res = f"{self.name}: {self.rhs}"
87        else:
88            res = f"{self.name}[{self.type}]: {self.rhs}"
89        if len(res) < 88:
90            return res
91        lines = [res.split(":")[0] + ":"]
92        lines += [f"    | {alt}" for alt in self.rhs.alts]
93        return "\n".join(lines)
94
95    def __repr__(self) -> str:
96        return f"Rule({self.name!r}, {self.type!r}, {self.rhs!r})"
97
98    def __iter__(self) -> Iterator[Rhs]:
99        yield self.rhs
100
101    def flatten(self) -> Rhs:
102        # If it's a single parenthesized group, flatten it.
103        rhs = self.rhs
104        if (
105            not self.is_loop()
106            and len(rhs.alts) == 1
107            and len(rhs.alts[0].items) == 1
108            and isinstance(rhs.alts[0].items[0].item, Group)
109        ):
110            rhs = rhs.alts[0].items[0].item.rhs
111        return rhs
112
113
114class Leaf:
115    def __init__(self, value: str):
116        self.value = value
117
118    def __str__(self) -> str:
119        return self.value
120
121    def __iter__(self) -> Iterable[str]:
122        if False:
123            yield
124
125
126class NameLeaf(Leaf):
127    """The value is the name."""
128
129    def __str__(self) -> str:
130        if self.value == "ENDMARKER":
131            return "$"
132        return super().__str__()
133
134    def __repr__(self) -> str:
135        return f"NameLeaf({self.value!r})"
136
137
138class StringLeaf(Leaf):
139    """The value is a string literal, including quotes."""
140
141    def __repr__(self) -> str:
142        return f"StringLeaf({self.value!r})"
143
144
145class Rhs:
146    def __init__(self, alts: List[Alt]):
147        self.alts = alts
148        self.memo: Optional[Tuple[Optional[str], str]] = None
149
150    def __str__(self) -> str:
151        return " | ".join(str(alt) for alt in self.alts)
152
153    def __repr__(self) -> str:
154        return f"Rhs({self.alts!r})"
155
156    def __iter__(self) -> Iterator[List[Alt]]:
157        yield self.alts
158
159    @property
160    def can_be_inlined(self) -> bool:
161        if len(self.alts) != 1 or len(self.alts[0].items) != 1:
162            return False
163        # If the alternative has an action we cannot inline
164        if getattr(self.alts[0], "action", None) is not None:
165            return False
166        return True
167
168
169class Alt:
170    def __init__(self, items: List[NamedItem], *, icut: int = -1, action: Optional[str] = None):
171        self.items = items
172        self.icut = icut
173        self.action = action
174
175    def __str__(self) -> str:
176        core = " ".join(str(item) for item in self.items)
177        if not SIMPLE_STR and self.action:
178            return f"{core} {{ {self.action} }}"
179        else:
180            return core
181
182    def __repr__(self) -> str:
183        args = [repr(self.items)]
184        if self.icut >= 0:
185            args.append(f"icut={self.icut}")
186        if self.action:
187            args.append(f"action={self.action!r}")
188        return f"Alt({', '.join(args)})"
189
190    def __iter__(self) -> Iterator[List[NamedItem]]:
191        yield self.items
192
193
194class NamedItem:
195    def __init__(self, name: Optional[str], item: Item, type: Optional[str] = None):
196        self.name = name
197        self.item = item
198        self.type = type
199
200    def __str__(self) -> str:
201        if not SIMPLE_STR and self.name:
202            return f"{self.name}={self.item}"
203        else:
204            return str(self.item)
205
206    def __repr__(self) -> str:
207        return f"NamedItem({self.name!r}, {self.item!r})"
208
209    def __iter__(self) -> Iterator[Item]:
210        yield self.item
211
212
213class Forced:
214    def __init__(self, node: Plain):
215        self.node = node
216
217    def __str__(self) -> str:
218        return f"&&{self.node}"
219
220    def __iter__(self) -> Iterator[Plain]:
221        yield self.node
222
223
224class Lookahead:
225    def __init__(self, node: Plain, sign: str):
226        self.node = node
227        self.sign = sign
228
229    def __str__(self) -> str:
230        return f"{self.sign}{self.node}"
231
232    def __iter__(self) -> Iterator[Plain]:
233        yield self.node
234
235
236class PositiveLookahead(Lookahead):
237    def __init__(self, node: Plain):
238        super().__init__(node, "&")
239
240    def __repr__(self) -> str:
241        return f"PositiveLookahead({self.node!r})"
242
243
244class NegativeLookahead(Lookahead):
245    def __init__(self, node: Plain):
246        super().__init__(node, "!")
247
248    def __repr__(self) -> str:
249        return f"NegativeLookahead({self.node!r})"
250
251
252class Opt:
253    def __init__(self, node: Item):
254        self.node = node
255
256    def __str__(self) -> str:
257        s = str(self.node)
258        # TODO: Decide whether to use [X] or X? based on type of X
259        if " " in s:
260            return f"[{s}]"
261        else:
262            return f"{s}?"
263
264    def __repr__(self) -> str:
265        return f"Opt({self.node!r})"
266
267    def __iter__(self) -> Iterator[Item]:
268        yield self.node
269
270
271class Repeat:
272    """Shared base class for x* and x+."""
273
274    def __init__(self, node: Plain):
275        self.node = node
276        self.memo: Optional[Tuple[Optional[str], str]] = None
277
278    def __iter__(self) -> Iterator[Plain]:
279        yield self.node
280
281
282class Repeat0(Repeat):
283    def __str__(self) -> str:
284        s = str(self.node)
285        # TODO: Decide whether to use (X)* or X* based on type of X
286        if " " in s:
287            return f"({s})*"
288        else:
289            return f"{s}*"
290
291    def __repr__(self) -> str:
292        return f"Repeat0({self.node!r})"
293
294
295class Repeat1(Repeat):
296    def __str__(self) -> str:
297        s = str(self.node)
298        # TODO: Decide whether to use (X)+ or X+ based on type of X
299        if " " in s:
300            return f"({s})+"
301        else:
302            return f"{s}+"
303
304    def __repr__(self) -> str:
305        return f"Repeat1({self.node!r})"
306
307
308class Gather(Repeat):
309    def __init__(self, separator: Plain, node: Plain):
310        self.separator = separator
311        self.node = node
312
313    def __str__(self) -> str:
314        return f"{self.separator!s}.{self.node!s}+"
315
316    def __repr__(self) -> str:
317        return f"Gather({self.separator!r}, {self.node!r})"
318
319
320class Group:
321    def __init__(self, rhs: Rhs):
322        self.rhs = rhs
323
324    def __str__(self) -> str:
325        return f"({self.rhs})"
326
327    def __repr__(self) -> str:
328        return f"Group({self.rhs!r})"
329
330    def __iter__(self) -> Iterator[Rhs]:
331        yield self.rhs
332
333
334class Cut:
335    def __init__(self) -> None:
336        pass
337
338    def __repr__(self) -> str:
339        return f"Cut()"
340
341    def __str__(self) -> str:
342        return f"~"
343
344    def __iter__(self) -> Iterator[Tuple[str, str]]:
345        if False:
346            yield
347
348    def __eq__(self, other: object) -> bool:
349        if not isinstance(other, Cut):
350            return NotImplemented
351        return True
352
353    def initial_names(self) -> AbstractSet[str]:
354        return set()
355
356
357Plain = Union[Leaf, Group]
358Item = Union[Plain, Opt, Repeat, Forced, Lookahead, Rhs, Cut]
359RuleName = Tuple[str, str]
360MetaTuple = Tuple[str, Optional[str]]
361MetaList = List[MetaTuple]
362RuleList = List[Rule]
363NamedItemList = List[NamedItem]
364LookaheadOrCut = Union[Lookahead, Cut]
365