<commit_before>r"""Defines the parse function.

__EBNF for a google-style docstring__:

  <docstring> ::= <empty-short-description>
              | <full-short-description><long-description><docterm>
              | <full-short-description>
                  [<long-description>]
                  <section>+
                <docterm>
  <long-description> ::= <line>+<newline>
  <short-description> ::= <empty-short-description>|<full-short-description>
  <full-short-description> ::= <doc-term><line><newline>
  <empty-short-description> ::= <doc-term><nnline><doc-term>
  <section> ::= <single-section>|<multi-section>
  <multi-section> ::= <empty-headline>
                        (<full-headline>(<indent><line>)*)+
                        <newline>
  <single-section> ::= <emtpty-headline>(<indent><line>)+<newline>
     | <full-headline>(<indent><line>)*<newline>
  <headline> ::= <empty-headline>|<full-headline>
  <empty-headline> ::= <keyword><colon><newline>
  <full-headline> ::= <keyword><colon><space><line>
  <line> ::= <nnline><newline>
  <nnline> ::= (<word><space>+)*<word>
  <keyword> ::= "Args"
            | "Arguments"
            | "Returns
            | "Yields"
            | "Raises"
  <word> ::= <content>*
  <colon> ::= ":"
  <indent> ::= <space>{4}
  <space> ::= " "
  <newline> ::= "\n"
  <content> ::= A letter, special character, or number.
  <doc-term> ::= \"\"\"

"""

from itertools import chain
from typing import (
    Callable,
    Dict,
    Iterable,
    Tuple,
)

from .token import Token, TokenType
from .peaker import Peaker


class ParserException(BaseException):
    """The exception raised when there is a parsing problem."""

    pass


def _expect_type(peaker: Peaker[Token], token_type: TokenType):
    """Raise an exception if peaker's next value isn't the given type.

    Args:
        peaker: The peaker to check.  Should have the given type next.
        token_type: The type we expect to see next.

    Raises:
        ParserException: If the next token in the Peaker is not of the
            expected type.

    """
    if peaker.peak().token_type != token_type:
        raise ParserException('Expected type {}, but was {}.'.format(
            token_type,
            peaker.peak().token_type,
        ))


def _is_type(peaker: Peaker[Token], token_type: TokenType) -> bool:
    """Tell if the next token in the Peaker is of the given type.

    Args:
        peaker: A peaker holding tokens.
        token_type: A TokenType we are looking for.

    Returns:
        True if the next token in the peaker is of the given type.

    """
    return peaker.peak().token_type == token_type


def _not(*fns) -> Callable:
    """Negates a function which returns a boolean.
<commit_msg>Add word to error when expecting TokenType.<commit_after>r"""Defines the parse function.

__EBNF for a google-style docstring__:

  <docstring> ::= <empty-short-description>
              | <full-short-description><long-description><docterm>
              | <full-short-description>
                  [<long-description>]
                  <section>+
                <docterm>
  <long-description> ::= <line>+<newline>
  <short-description> ::= <empty-short-description>|<full-short-description>
  <full-short-description> ::= <doc-term><line><newline>
  <empty-short-description> ::= <doc-term><nnline><doc-term>
  <section> ::= <single-section>|<multi-section>
  <multi-section> ::= <empty-headline>
                        (<full-headline>(<indent><line>)*)+
                        <newline>
  <single-section> ::= <emtpty-headline>(<indent><line>)+<newline>
     | <full-headline>(<indent><line>)*<newline>
  <headline> ::= <empty-headline>|<full-headline>
  <empty-headline> ::= <keyword><colon><newline>
  <full-headline> ::= <keyword><colon><space><line>
  <line> ::= <nnline><newline>
  <nnline> ::= (<word><space>+)*<word>
  <keyword> ::= "Args"
            | "Arguments"
            | "Returns
            | "Yields"
            | "Raises"
  <word> ::= <content>*
  <colon> ::= ":"
  <indent> ::= <space>{4}
  <space> ::= " "
  <newline> ::= "\n"
  <content> ::= A letter, special character, or number.
  <doc-term> ::= \"\"\"

"""

from itertools import chain
from typing import (
    Callable,
    Dict,
    Iterable,
    Tuple,
)

from .token import Token, TokenType
from .peaker import Peaker


class ParserException(BaseException):
    """The exception raised when there is a parsing problem."""

    pass


def _expect_type(peaker: Peaker[Token], expected_type: TokenType):
    """Raise an exception if peaker's next value isn't the given type.

    Args:
        peaker: The peaker to check.  Should have the given type next.
        expected_type: The type we expect to see next.

    Raises:
        ParserException: If the next token in the Peaker is not of the
            expected type.

    """
    actual_type = peaker.peak().token_type
    if actual_type != expected_type:
        msg = ''
        if actual_type == TokenType.WORD:
            msg = 'Exected type {}, but was {}: "{}"'.format(
                expected_type,
                actual_type,
                peaker.peak().value,
            )
        else:
            msg = 'Expected type {}, but was {}.'.format(
                expected_type,
                actual_type,
            )
        raise ParserException(msg)


def _is_type(peaker: Peaker[Token], token_type: TokenType) -> bool:
    """Tell if the next token in the Peaker is of the given type.

    Args:
        peaker: A peaker holding tokens.
        token_type: A TokenType we are looking for.

    Returns:
        True if the next token in the peaker is of the given type.

    """
    return peaker.peak().token_type == token_type


def _not(*fns) -> Callable:
    """Negates a function which returns a boolean.

{'fraction_non_alphanumeric': 0.14947477182710522, 'fraction_numerical': 0.00034441191665231615, 'mean_word_length': 2.5522935779816516, 'pattern_counts': {'":': 2, '<': 143, '<?xml version=': 0, '>': 147, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3051057', 'n_tokens_mistral': 1877, 'n_tokens_neox': 1808, 'n_words': 460}