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