| 213 | |
| 214 | |
| 215 | class PythonParserGenerator(ParserGenerator, GrammarVisitor): |
| 216 | def __init__( |
| 217 | self, |
| 218 | grammar: grammar.Grammar, |
| 219 | file: IO[str] | None, |
| 220 | tokens: set[str] = set(token.tok_name.values()), |
| 221 | location_formatting: str | None = None, |
| 222 | unreachable_formatting: str | None = None, |
| 223 | ): |
| 224 | tokens.add("SOFT_KEYWORD") |
| 225 | super().__init__(grammar, tokens, file) |
| 226 | self.callmakervisitor: PythonCallMakerVisitor = PythonCallMakerVisitor(self) |
| 227 | self.invalidvisitor: InvalidNodeVisitor = InvalidNodeVisitor() |
| 228 | self.unreachable_formatting = unreachable_formatting or "None # pragma: no cover" |
| 229 | self.location_formatting = ( |
| 230 | location_formatting |
| 231 | or "lineno=start_lineno, col_offset=start_col_offset, " |
| 232 | "end_lineno=end_lineno, end_col_offset=end_col_offset" |
| 233 | ) |
| 234 | |
| 235 | def generate(self, filename: str) -> None: |
| 236 | self.collect_rules() |
| 237 | header = self.grammar.metas.get("header", MODULE_PREFIX) |
| 238 | if header is not None: |
| 239 | basename = os.path.basename(filename) |
| 240 | self.print(header.rstrip("\n").format(filename=basename)) |
| 241 | subheader = self.grammar.metas.get("subheader", "") |
| 242 | if subheader: |
| 243 | self.print(subheader) |
| 244 | cls_name = self.grammar.metas.get("class", "GeneratedParser") |
| 245 | self.print("# Keywords and soft keywords are listed at the end of the parser definition.") |
| 246 | self.print(f"class {cls_name}(Parser):") |
| 247 | for rule in self.all_rules.values(): |
| 248 | self.print() |
| 249 | with self.indent(): |
| 250 | self.visit(rule) |
| 251 | |
| 252 | self.print() |
| 253 | with self.indent(): |
| 254 | self.print(f"KEYWORDS = {tuple(self.keywords)}") |
| 255 | self.print(f"SOFT_KEYWORDS = {tuple(self.soft_keywords)}") |
| 256 | |
| 257 | trailer = self.grammar.metas.get("trailer", MODULE_SUFFIX.format(class_name=cls_name)) |
| 258 | if trailer is not None: |
| 259 | self.print(trailer.rstrip("\n")) |
| 260 | |
| 261 | def alts_uses_locations(self, alts: Sequence[Alt]) -> bool: |
| 262 | for alt in alts: |
| 263 | if alt.action and "LOCATIONS" in alt.action: |
| 264 | return True |
| 265 | for n in alt.items: |
| 266 | if isinstance(n.item, Group) and self.alts_uses_locations(n.item.rhs.alts): |
| 267 | return True |
| 268 | return False |
| 269 | |
| 270 | def visit_Rule(self, node: Rule) -> None: |
| 271 | is_loop = node.is_loop() |
| 272 | is_gather = node.is_gather() |
no outgoing calls
searching dependent graphs…