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