src/blib2to3/pgen2/driver.py
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | # Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved.
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | # Licensed to PSF under a Contributor Agreement.
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | # Modifications:
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | # Copyright 2006 Google, Inc. All Rights Reserved.
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | # Licensed to PSF under a Contributor Agreement.
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | """Parser driver.
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | This provides a high-level interface to parse a file into a syntax tree.
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | """
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | __author__ = "Guido van Rossum <guido@python.org>"
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | __all__ = ["Driver", "load_grammar"]
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | # Python imports
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | import io
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | import logging
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | import os
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | import pkgutil
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | import sys
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | from contextlib import contextmanager
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | from dataclasses import dataclass, field
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | from logging import Logger
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | from typing import IO, Any, Iterable, Iterator, List, Optional, Tuple, Union, cast
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | from blib2to3.pgen2.grammar import Grammar
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | from blib2to3.pgen2.tokenize import GoodTokenInfo
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | from blib2to3.pytree import NL
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | # Pgen imports
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | from . import grammar, parse, pgen, token, tokenize
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | Path = Union[str, "os.PathLike[str]"]
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | @dataclass
---- ---- ---- ---- ---- ---- ---- 02 -- --- --- --- --- --- --- ------- ------- ------- | class ReleaseRange:
---- ---- ---- ---- ---- ---- ---- 02 -- --- --- --- --- --- --- ------- ------- ------- | start: int
---- ---- ---- ---- ---- ---- ---- 02 -- --- --- --- --- --- --- ------- ------- ------- | end: Optional[int] = None
---- ---- ---- ---- ---- ---- ---- 02 -- --- --- --- --- --- --- ------- ------- ------- | tokens: List[Any] = field(default_factory=list)
---- ---- ---- ---- ---- ---- ---- 02 -- --- --- --- --- --- --- ------- ------- ------- |
0003 0003 0003 0000 0000 0000 0000 02 01 001 002 001 002 003 003 0004.75 0002.38 0000.50 | def lock(self) -> None:
0003 0003 0003 0000 0000 0000 0000 02 01 001 002 001 002 003 003 0004.75 0002.38 0000.50 | total_eaten = len(self.tokens)
0003 0003 0003 0000 0000 0000 0000 02 01 001 002 001 002 003 003 0004.75 0002.38 0000.50 | self.end = self.start + total_eaten
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- 03 -- --- --- --- --- --- --- ------- ------- ------- | class TokenProxy:
0004 0005 0004 0000 0000 0000 0000 03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | def __init__(self, generator: Any) -> None:
0004 0005 0004 0000 0000 0000 0000 03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | self._tokens = generator
0004 0005 0004 0000 0000 0000 0000 03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | self._counter = 0
0004 0005 0004 0000 0000 0000 0000 03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | self._release_ranges: List[ReleaseRange] = []
---- ---- ---- ---- ---- ---- ---- 03 -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- 03 -- --- --- --- --- --- --- ------- ------- ------- | @contextmanager
0010 0008 0008 0002 0000 0000 0002 03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | def release(self) -> Iterator["TokenProxy"]:
0010 0008 0008 0002 0000 0000 0002 03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | release_range = ReleaseRange(self._counter)
0010 0008 0008 0002 0000 0000 0002 03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | self._release_ranges.append(release_range)
0010 0008 0008 0002 0000 0000 0002 03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | try:
0010 0008 0008 0002 0000 0000 0002 03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | yield self
0010 0008 0008 0002 0000 0000 0002 03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | finally:
0010 0008 0008 0002 0000 0000 0002 03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | # Lock the last release range to the final position that
0010 0008 0008 0002 0000 0000 0002 03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | # has been eaten.
0010 0008 0008 0002 0000 0000 0002 03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | release_range.lock()
0010 0008 0008 0002 0000 0000 0002 03 -- --- --- --- --- --- --- ------- ------- ------- |
0009 0009 0009 0000 0000 0000 0000 03 03 003 004 003 005 007 008 0022.46 0042.11 0001.88 | def eat(self, point: int) -> Any:
0009 0009 0009 0000 0000 0000 0000 03 03 003 004 003 005 007 008 0022.46 0042.11 0001.88 | eaten_tokens = self._release_ranges[-1].tokens
0009 0009 0009 0000 0000 0000 0000 03 03 003 004 003 005 007 008 0022.46 0042.11 0001.88 | if point < len(eaten_tokens):
0009 0009 0009 0000 0000 0000 0000 03 03 003 004 003 005 007 008 0022.46 0042.11 0001.88 | return eaten_tokens[point]
0009 0009 0009 0000 0000 0000 0000 03 03 003 004 003 005 007 008 0022.46 0042.11 0001.88 | else:
0009 0009 0009 0000 0000 0000 0000 03 03 003 004 003 005 007 008 0022.46 0042.11 0001.88 | while point >= len(eaten_tokens):
0009 0009 0009 0000 0000 0000 0000 03 03 003 004 003 005 007 008 0022.46 0042.11 0001.88 | token = next(self._tokens)
0009 0009 0009 0000 0000 0000 0000 03 03 003 004 003 005 007 008 0022.46 0042.11 0001.88 | eaten_tokens.append(token)
0009 0009 0009 0000 0000 0000 0000 03 03 003 004 003 005 007 008 0022.46 0042.11 0001.88 | return token
---- ---- ---- ---- ---- ---- ---- 03 -- --- --- --- --- --- --- ------- ------- ------- |
0002 0002 0002 0000 0000 0000 0000 03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | def __iter__(self) -> "TokenProxy":
0002 0002 0002 0000 0000 0000 0000 03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | return self
---- ---- ---- ---- ---- ---- ---- 03 -- --- --- --- --- --- --- ------- ------- ------- |
0015 0011 0011 0003 0000 0001 0003 03 04 005 005 005 009 010 014 0046.51 0209.28 0004.50 | def __next__(self) -> Any:
0015 0011 0011 0003 0000 0001 0003 03 04 005 005 005 009 010 014 0046.51 0209.28 0004.50 | # If the current position is already compromised (looked up)
0015 0011 0011 0003 0000 0001 0003 03 04 005 005 005 009 010 014 0046.51 0209.28 0004.50 | # return the eaten token, if not just go further on the given
0015 0011 0011 0003 0000 0001 0003 03 04 005 005 005 009 010 014 0046.51 0209.28 0004.50 | # token producer.
0015 0011 0011 0003 0000 0001 0003 03 04 005 005 005 009 010 014 0046.51 0209.28 0004.50 | for release_range in self._release_ranges:
0015 0011 0011 0003 0000 0001 0003 03 04 005 005 005 009 010 014 0046.51 0209.28 0004.50 | assert release_range.end is not None
0015 0011 0011 0003 0000 0001 0003 03 04 005 005 005 009 010 014 0046.51 0209.28 0004.50 |
0015 0011 0011 0003 0000 0001 0003 03 04 005 005 005 009 010 014 0046.51 0209.28 0004.50 | start, end = release_range.start, release_range.end
0015 0011 0011 0003 0000 0001 0003 03 04 005 005 005 009 010 014 0046.51 0209.28 0004.50 | if start <= self._counter < end:
0015 0011 0011 0003 0000 0001 0003 03 04 005 005 005 009 010 014 0046.51 0209.28 0004.50 | token = release_range.tokens[self._counter - start]
0015 0011 0011 0003 0000 0001 0003 03 04 005 005 005 009 010 014 0046.51 0209.28 0004.50 | break
0015 0011 0011 0003 0000 0001 0003 03 04 005 005 005 009 010 014 0046.51 0209.28 0004.50 | else:
0015 0011 0011 0003 0000 0001 0003 03 04 005 005 005 009 010 014 0046.51 0209.28 0004.50 | token = next(self._tokens)
0015 0011 0011 0003 0000 0001 0003 03 04 005 005 005 009 010 014 0046.51 0209.28 0004.50 | self._counter += 1
0015 0011 0011 0003 0000 0001 0003 03 04 005 005 005 009 010 014 0046.51 0209.28 0004.50 | return token
---- ---- ---- ---- ---- ---- ---- 03 -- --- --- --- --- --- --- ------- ------- ------- |
0009 0007 0007 0002 0000 0000 0002 03 03 000 000 000 000 000 000 0000.00 0000.00 0000.00 | def can_advance(self, to: int) -> bool:
0009 0007 0007 0002 0000 0000 0002 03 03 000 000 000 000 000 000 0000.00 0000.00 0000.00 | # Try to eat, fail if it can't. The eat operation is cached
0009 0007 0007 0002 0000 0000 0002 03 03 000 000 000 000 000 000 0000.00 0000.00 0000.00 | # so there won't be any additional cost of eating here
0009 0007 0007 0002 0000 0000 0002 03 03 000 000 000 000 000 000 0000.00 0000.00 0000.00 | try:
0009 0007 0007 0002 0000 0000 0002 03 03 000 000 000 000 000 000 0000.00 0000.00 0000.00 | self.eat(to)
0009 0007 0007 0002 0000 0000 0002 03 03 000 000 000 000 000 000 0000.00 0000.00 0000.00 | except StopIteration:
0009 0007 0007 0002 0000 0000 0002 03 03 000 000 000 000 000 000 0000.00 0000.00 0000.00 | return False
0009 0007 0007 0002 0000 0000 0002 03 03 000 000 000 000 000 000 0000.00 0000.00 0000.00 | else:
0009 0007 0007 0002 0000 0000 0002 03 03 000 000 000 000 000 000 0000.00 0000.00 0000.00 | return True
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- 05 -- --- --- --- --- --- --- ------- ------- ------- | class Driver:
0005 0005 0005 0000 0000 0000 0000 05 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 | def __init__(self, grammar: Grammar, logger: Optional[Logger] = None) -> None:
0005 0005 0005 0000 0000 0000 0000 05 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 | self.grammar = grammar
0005 0005 0005 0000 0000 0000 0000 05 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 | if logger is None:
0005 0005 0005 0000 0000 0000 0000 05 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 | logger = logging.getLogger(__name__)
0005 0005 0005 0000 0000 0000 0000 05 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 | self.logger = logger
---- ---- ---- ---- ---- ---- ---- 05 -- --- --- --- --- --- --- ------- ------- ------- |
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | def parse_tokens(self, tokens: Iterable[GoodTokenInfo], debug: bool = False) -> NL:
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | """Parse a series of tokens and return the syntax tree."""
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | # XXX Move the prefix computation into a wrapper around tokenize.
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | proxy = TokenProxy(tokens)
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 |
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | p = parse.Parser(self.grammar)
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | p.setup(proxy=proxy)
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 |
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | lineno = 1
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | column = 0
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | indent_columns: List[int] = []
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | type = value = start = end = line_text = None
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | prefix = ""
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 |
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | for quintuple in proxy:
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | type, value, start, end, line_text = quintuple
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | if start != (lineno, column):
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | assert (lineno, column) <= start, ((lineno, column), start)
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | s_lineno, s_column = start
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | if lineno < s_lineno:
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | prefix += "\n" * (s_lineno - lineno)
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | lineno = s_lineno
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | column = 0
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | if column < s_column:
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | prefix += line_text[column:s_column]
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | column = s_column
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | if type in (tokenize.COMMENT, tokenize.NL):
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | prefix += value
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | lineno, column = end
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | if value.endswith("\n"):
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | lineno += 1
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | column = 0
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | continue
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | if type == token.OP:
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | type = grammar.opmap[value]
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | if debug:
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | assert type is not None
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | self.logger.debug(
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | "%s %r (prefix=%r)", token.tok_name[type], value, prefix
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | )
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | if type == token.INDENT:
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | indent_columns.append(len(value))
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | _prefix = prefix + value
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | prefix = ""
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | value = ""
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | elif type == token.DEDENT:
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | _indent_col = indent_columns.pop()
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | prefix, _prefix = self._partially_consume_prefix(prefix, _indent_col)
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | if p.addtoken(cast(int, type), value, (prefix, start)):
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | if debug:
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | self.logger.debug("Stop.")
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | break
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | prefix = ""
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | if type in {token.INDENT, token.DEDENT}:
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | prefix = _prefix
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | lineno, column = end
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | if value.endswith("\n"):
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | lineno += 1
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | column = 0
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | else:
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | # We never broke out -- EOF is too soon (how can this happen???)
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | assert start is not None
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | raise parse.ParseError("incomplete input", type, value, (prefix, start))
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | assert p.rootnode is not None
0065 0060 0059 0002 0000 0003 0003 05 16 009 022 020 040 031 060 0297.25 2432.06 0008.18 | return p.rootnode
---- ---- ---- ---- ---- ---- ---- 05 -- --- --- --- --- --- --- ------- ------- ------- |
0004 0004 0003 0000 0000 0000 0001 05 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | def parse_stream_raw(self, stream: IO[str], debug: bool = False) -> NL:
0004 0004 0003 0000 0000 0000 0001 05 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | """Parse a stream and return the syntax tree."""
0004 0004 0003 0000 0000 0000 0001 05 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | tokens = tokenize.generate_tokens(stream.readline, grammar=self.grammar)
0004 0004 0003 0000 0000 0000 0001 05 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | return self.parse_tokens(tokens, debug)
---- ---- ---- ---- ---- ---- ---- 05 -- --- --- --- --- --- --- ------- ------- ------- |
0003 0003 0002 0000 0000 0000 0001 05 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | def parse_stream(self, stream: IO[str], debug: bool = False) -> NL:
0003 0003 0002 0000 0000 0000 0001 05 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | """Parse a stream and return the syntax tree."""
0003 0003 0002 0000 0000 0000 0001 05 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | return self.parse_stream_raw(stream, debug)
---- ---- ---- ---- ---- ---- ---- 05 -- --- --- --- --- --- --- ------- ------- ------- |
0006 0004 0005 0000 0000 0000 0001 05 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | def parse_file(
0006 0004 0005 0000 0000 0000 0001 05 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | self, filename: Path, encoding: Optional[str] = None, debug: bool = False
0006 0004 0005 0000 0000 0000 0001 05 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | ) -> NL:
0006 0004 0005 0000 0000 0000 0001 05 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | """Parse a file and return the syntax tree."""
0006 0004 0005 0000 0000 0000 0001 05 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | with open(filename, encoding=encoding) as stream:
0006 0004 0005 0000 0000 0000 0001 05 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | return self.parse_stream(stream, debug)
---- ---- ---- ---- ---- ---- ---- 05 -- --- --- --- --- --- --- ------- ------- ------- |
0006 0004 0005 0000 0000 0000 0001 05 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | def parse_string(self, text: str, debug: bool = False) -> NL:
0006 0004 0005 0000 0000 0000 0001 05 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | """Parse a string and return the syntax tree."""
0006 0004 0005 0000 0000 0000 0001 05 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | tokens = tokenize.generate_tokens(
0006 0004 0005 0000 0000 0000 0001 05 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | io.StringIO(text).readline, grammar=self.grammar
0006 0004 0005 0000 0000 0000 0001 05 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | )
0006 0004 0005 0000 0000 0000 0001 05 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | return self.parse_tokens(tokens, debug)
---- ---- ---- ---- ---- ---- ---- 05 -- --- --- --- --- --- --- ------- ------- ------- |
0028 0027 0025 0002 0000 0001 0002 05 09 005 010 008 016 015 024 0093.77 0375.06 0004.00 | def _partially_consume_prefix(self, prefix: str, column: int) -> Tuple[str, str]:
0028 0027 0025 0002 0000 0001 0002 05 09 005 010 008 016 015 024 0093.77 0375.06 0004.00 | lines: List[str] = []
0028 0027 0025 0002 0000 0001 0002 05 09 005 010 008 016 015 024 0093.77 0375.06 0004.00 | current_line = ""
0028 0027 0025 0002 0000 0001 0002 05 09 005 010 008 016 015 024 0093.77 0375.06 0004.00 | current_column = 0
0028 0027 0025 0002 0000 0001 0002 05 09 005 010 008 016 015 024 0093.77 0375.06 0004.00 | wait_for_nl = False
0028 0027 0025 0002 0000 0001 0002 05 09 005 010 008 016 015 024 0093.77 0375.06 0004.00 | for char in prefix:
0028 0027 0025 0002 0000 0001 0002 05 09 005 010 008 016 015 024 0093.77 0375.06 0004.00 | current_line += char
0028 0027 0025 0002 0000 0001 0002 05 09 005 010 008 016 015 024 0093.77 0375.06 0004.00 | if wait_for_nl:
0028 0027 0025 0002 0000 0001 0002 05 09 005 010 008 016 015 024 0093.77 0375.06 0004.00 | if char == "\n":
0028 0027 0025 0002 0000 0001 0002 05 09 005 010 008 016 015 024 0093.77 0375.06 0004.00 | if current_line.strip() and current_column < column:
0028 0027 0025 0002 0000 0001 0002 05 09 005 010 008 016 015 024 0093.77 0375.06 0004.00 | res = "".join(lines)
0028 0027 0025 0002 0000 0001 0002 05 09 005 010 008 016 015 024 0093.77 0375.06 0004.00 | return res, prefix[len(res) :]
0028 0027 0025 0002 0000 0001 0002 05 09 005 010 008 016 015 024 0093.77 0375.06 0004.00 |
0028 0027 0025 0002 0000 0001 0002 05 09 005 010 008 016 015 024 0093.77 0375.06 0004.00 | lines.append(current_line)
0028 0027 0025 0002 0000 0001 0002 05 09 005 010 008 016 015 024 0093.77 0375.06 0004.00 | current_line = ""
0028 0027 0025 0002 0000 0001 0002 05 09 005 010 008 016 015 024 0093.77 0375.06 0004.00 | current_column = 0
0028 0027 0025 0002 0000 0001 0002 05 09 005 010 008 016 015 024 0093.77 0375.06 0004.00 | wait_for_nl = False
0028 0027 0025 0002 0000 0001 0002 05 09 005 010 008 016 015 024 0093.77 0375.06 0004.00 | elif char in " \t":
0028 0027 0025 0002 0000 0001 0002 05 09 005 010 008 016 015 024 0093.77 0375.06 0004.00 | current_column += 1
0028 0027 0025 0002 0000 0001 0002 05 09 005 010 008 016 015 024 0093.77 0375.06 0004.00 | elif char == "\n":
0028 0027 0025 0002 0000 0001 0002 05 09 005 010 008 016 015 024 0093.77 0375.06 0004.00 | # unexpected empty line
0028 0027 0025 0002 0000 0001 0002 05 09 005 010 008 016 015 024 0093.77 0375.06 0004.00 | current_column = 0
0028 0027 0025 0002 0000 0001 0002 05 09 005 010 008 016 015 024 0093.77 0375.06 0004.00 | elif char == "\f":
0028 0027 0025 0002 0000 0001 0002 05 09 005 010 008 016 015 024 0093.77 0375.06 0004.00 | current_column = 0
0028 0027 0025 0002 0000 0001 0002 05 09 005 010 008 016 015 024 0093.77 0375.06 0004.00 | else:
0028 0027 0025 0002 0000 0001 0002 05 09 005 010 008 016 015 024 0093.77 0375.06 0004.00 | # indent is finished
0028 0027 0025 0002 0000 0001 0002 05 09 005 010 008 016 015 024 0093.77 0375.06 0004.00 | wait_for_nl = True
0028 0027 0025 0002 0000 0001 0002 05 09 005 010 008 016 015 024 0093.77 0375.06 0004.00 | return "".join(lines), current_line
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
0009 0009 0009 0000 0000 0000 0000 -- 03 002 007 004 008 009 012 0038.04 0043.47 0001.14 | def _generate_pickle_name(gt: Path, cache_dir: Optional[Path] = None) -> str:
0009 0009 0009 0000 0000 0000 0000 -- 03 002 007 004 008 009 012 0038.04 0043.47 0001.14 | head, tail = os.path.splitext(gt)
0009 0009 0009 0000 0000 0000 0000 -- 03 002 007 004 008 009 012 0038.04 0043.47 0001.14 | if tail == ".txt":
0009 0009 0009 0000 0000 0000 0000 -- 03 002 007 004 008 009 012 0038.04 0043.47 0001.14 | tail = ""
0009 0009 0009 0000 0000 0000 0000 -- 03 002 007 004 008 009 012 0038.04 0043.47 0001.14 | name = head + tail + ".".join(map(str, sys.version_info)) + ".pickle"
0009 0009 0009 0000 0000 0000 0000 -- 03 002 007 004 008 009 012 0038.04 0043.47 0001.14 | if cache_dir:
0009 0009 0009 0000 0000 0000 0000 -- 03 002 007 004 008 009 012 0038.04 0043.47 0001.14 | return os.path.join(cache_dir, os.path.basename(name))
0009 0009 0009 0000 0000 0000 0000 -- 03 002 007 004 008 009 012 0038.04 0043.47 0001.14 | else:
0009 0009 0009 0000 0000 0000 0000 -- 03 002 007 004 008 009 012 0038.04 0043.47 0001.14 | return name
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
0023 0017 0021 0001 0000 0000 0002 -- 07 003 006 004 007 009 011 0034.87 0061.02 0001.75 | def load_grammar(
0023 0017 0021 0001 0000 0000 0002 -- 07 003 006 004 007 009 011 0034.87 0061.02 0001.75 | gt: str = "Grammar.txt",
0023 0017 0021 0001 0000 0000 0002 -- 07 003 006 004 007 009 011 0034.87 0061.02 0001.75 | gp: Optional[str] = None,
0023 0017 0021 0001 0000 0000 0002 -- 07 003 006 004 007 009 011 0034.87 0061.02 0001.75 | save: bool = True,
0023 0017 0021 0001 0000 0000 0002 -- 07 003 006 004 007 009 011 0034.87 0061.02 0001.75 | force: bool = False,
0023 0017 0021 0001 0000 0000 0002 -- 07 003 006 004 007 009 011 0034.87 0061.02 0001.75 | logger: Optional[Logger] = None,
0023 0017 0021 0001 0000 0000 0002 -- 07 003 006 004 007 009 011 0034.87 0061.02 0001.75 | ) -> Grammar:
0023 0017 0021 0001 0000 0000 0002 -- 07 003 006 004 007 009 011 0034.87 0061.02 0001.75 | """Load the grammar (maybe from a pickle)."""
0023 0017 0021 0001 0000 0000 0002 -- 07 003 006 004 007 009 011 0034.87 0061.02 0001.75 | if logger is None:
0023 0017 0021 0001 0000 0000 0002 -- 07 003 006 004 007 009 011 0034.87 0061.02 0001.75 | logger = logging.getLogger(__name__)
0023 0017 0021 0001 0000 0000 0002 -- 07 003 006 004 007 009 011 0034.87 0061.02 0001.75 | gp = _generate_pickle_name(gt) if gp is None else gp
0023 0017 0021 0001 0000 0000 0002 -- 07 003 006 004 007 009 011 0034.87 0061.02 0001.75 | if force or not _newer(gp, gt):
0023 0017 0021 0001 0000 0000 0002 -- 07 003 006 004 007 009 011 0034.87 0061.02 0001.75 | g: grammar.Grammar = pgen.generate_grammar(gt)
0023 0017 0021 0001 0000 0000 0002 -- 07 003 006 004 007 009 011 0034.87 0061.02 0001.75 | if save:
0023 0017 0021 0001 0000 0000 0002 -- 07 003 006 004 007 009 011 0034.87 0061.02 0001.75 | try:
0023 0017 0021 0001 0000 0000 0002 -- 07 003 006 004 007 009 011 0034.87 0061.02 0001.75 | g.dump(gp)
0023 0017 0021 0001 0000 0000 0002 -- 07 003 006 004 007 009 011 0034.87 0061.02 0001.75 | except OSError:
0023 0017 0021 0001 0000 0000 0002 -- 07 003 006 004 007 009 011 0034.87 0061.02 0001.75 | # Ignore error, caching is not vital.
0023 0017 0021 0001 0000 0000 0002 -- 07 003 006 004 007 009 011 0034.87 0061.02 0001.75 | pass
0023 0017 0021 0001 0000 0000 0002 -- 07 003 006 004 007 009 011 0034.87 0061.02 0001.75 | else:
0023 0017 0021 0001 0000 0000 0002 -- 07 003 006 004 007 009 011 0034.87 0061.02 0001.75 | g = grammar.Grammar()
0023 0017 0021 0001 0000 0000 0002 -- 07 003 006 004 007 009 011 0034.87 0061.02 0001.75 | g.load(gp)
0023 0017 0021 0001 0000 0000 0002 -- 07 003 006 004 007 009 011 0034.87 0061.02 0001.75 | return g
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
0007 0007 0006 0000 0000 0000 0001 -- 03 002 004 003 004 006 007 0018.09 0018.09 0001.00 | def _newer(a: str, b: str) -> bool:
0007 0007 0006 0000 0000 0000 0001 -- 03 002 004 003 004 006 007 0018.09 0018.09 0001.00 | """Inquire whether file a was written since file b."""
0007 0007 0006 0000 0000 0000 0001 -- 03 002 004 003 004 006 007 0018.09 0018.09 0001.00 | if not os.path.exists(a):
0007 0007 0006 0000 0000 0000 0001 -- 03 002 004 003 004 006 007 0018.09 0018.09 0001.00 | return False
0007 0007 0006 0000 0000 0000 0001 -- 03 002 004 003 004 006 007 0018.09 0018.09 0001.00 | if not os.path.exists(b):
0007 0007 0006 0000 0000 0000 0001 -- 03 002 004 003 004 006 007 0018.09 0018.09 0001.00 | return True
0007 0007 0006 0000 0000 0000 0001 -- 03 002 004 003 004 006 007 0018.09 0018.09 0001.00 | return os.path.getmtime(a) >= os.path.getmtime(b)
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
0022 0011 0012 0000 0008 0002 0000 -- 03 001 002 001 002 003 003 0004.75 0002.38 0000.50 | def load_packaged_grammar(
0022 0011 0012 0000 0008 0002 0000 -- 03 001 002 001 002 003 003 0004.75 0002.38 0000.50 | package: str, grammar_source: str, cache_dir: Optional[Path] = None
0022 0011 0012 0000 0008 0002 0000 -- 03 001 002 001 002 003 003 0004.75 0002.38 0000.50 | ) -> grammar.Grammar:
0022 0011 0012 0000 0008 0002 0000 -- 03 001 002 001 002 003 003 0004.75 0002.38 0000.50 | """Normally, loads a pickled grammar by doing
0022 0011 0012 0000 0008 0002 0000 -- 03 001 002 001 002 003 003 0004.75 0002.38 0000.50 | pkgutil.get_data(package, pickled_grammar)
0022 0011 0012 0000 0008 0002 0000 -- 03 001 002 001 002 003 003 0004.75 0002.38 0000.50 | where *pickled_grammar* is computed from *grammar_source* by adding the
0022 0011 0012 0000 0008 0002 0000 -- 03 001 002 001 002 003 003 0004.75 0002.38 0000.50 | Python version and using a ``.pickle`` extension.
0022 0011 0012 0000 0008 0002 0000 -- 03 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
0022 0011 0012 0000 0008 0002 0000 -- 03 001 002 001 002 003 003 0004.75 0002.38 0000.50 | However, if *grammar_source* is an extant file, load_grammar(grammar_source)
0022 0011 0012 0000 0008 0002 0000 -- 03 001 002 001 002 003 003 0004.75 0002.38 0000.50 | is called instead. This facilitates using a packaged grammar file when needed
0022 0011 0012 0000 0008 0002 0000 -- 03 001 002 001 002 003 003 0004.75 0002.38 0000.50 | but preserves load_grammar's automatic regeneration behavior when possible.
0022 0011 0012 0000 0008 0002 0000 -- 03 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
0022 0011 0012 0000 0008 0002 0000 -- 03 001 002 001 002 003 003 0004.75 0002.38 0000.50 | """
0022 0011 0012 0000 0008 0002 0000 -- 03 001 002 001 002 003 003 0004.75 0002.38 0000.50 | if os.path.isfile(grammar_source):
0022 0011 0012 0000 0008 0002 0000 -- 03 001 002 001 002 003 003 0004.75 0002.38 0000.50 | gp = _generate_pickle_name(grammar_source, cache_dir) if cache_dir else None
0022 0011 0012 0000 0008 0002 0000 -- 03 001 002 001 002 003 003 0004.75 0002.38 0000.50 | return load_grammar(grammar_source, gp=gp)
0022 0011 0012 0000 0008 0002 0000 -- 03 001 002 001 002 003 003 0004.75 0002.38 0000.50 | pickled_name = _generate_pickle_name(os.path.basename(grammar_source), cache_dir)
0022 0011 0012 0000 0008 0002 0000 -- 03 001 002 001 002 003 003 0004.75 0002.38 0000.50 | data = pkgutil.get_data(package, pickled_name)
0022 0011 0012 0000 0008 0002 0000 -- 03 001 002 001 002 003 003 0004.75 0002.38 0000.50 | assert data is not None
0022 0011 0012 0000 0008 0002 0000 -- 03 001 002 001 002 003 003 0004.75 0002.38 0000.50 | g = grammar.Grammar()
0022 0011 0012 0000 0008 0002 0000 -- 03 001 002 001 002 003 003 0004.75 0002.38 0000.50 | g.loads(data)
0022 0011 0012 0000 0008 0002 0000 -- 03 001 002 001 002 003 003 0004.75 0002.38 0000.50 | return g
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
0011 0009 0007 0000 0003 0001 0000 -- 03 001 001 001 001 002 002 0002.00 0001.00 0000.50 | def main(*args: str) -> bool:
0011 0009 0007 0000 0003 0001 0000 -- 03 001 001 001 001 002 002 0002.00 0001.00 0000.50 | """Main program, when run as a script: produce grammar pickle files.
0011 0009 0007 0000 0003 0001 0000 -- 03 001 001 001 001 002 002 0002.00 0001.00 0000.50 |
0011 0009 0007 0000 0003 0001 0000 -- 03 001 001 001 001 002 002 0002.00 0001.00 0000.50 | Calls load_grammar for each argument, a path to a grammar text file.
0011 0009 0007 0000 0003 0001 0000 -- 03 001 001 001 001 002 002 0002.00 0001.00 0000.50 | """
0011 0009 0007 0000 0003 0001 0000 -- 03 001 001 001 001 002 002 0002.00 0001.00 0000.50 | if not args:
0011 0009 0007 0000 0003 0001 0000 -- 03 001 001 001 001 002 002 0002.00 0001.00 0000.50 | args = tuple(sys.argv[1:])
0011 0009 0007 0000 0003 0001 0000 -- 03 001 001 001 001 002 002 0002.00 0001.00 0000.50 | logging.basicConfig(level=logging.INFO, stream=sys.stdout, format="%(message)s")
0011 0009 0007 0000 0003 0001 0000 -- 03 001 001 001 001 002 002 0002.00 0001.00 0000.50 | for gt in args:
0011 0009 0007 0000 0003 0001 0000 -- 03 001 001 001 001 002 002 0002.00 0001.00 0000.50 | load_grammar(gt, save=True, force=True)
0011 0009 0007 0000 0003 0001 0000 -- 03 001 001 001 001 002 002 0002.00 0001.00 0000.50 | return True
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | if __name__ == "__main__":
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | sys.exit(int(not main()))