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