Source code for almanac.parsing.lexer

from __future__ import annotations

import re

from typing import Iterator, Tuple, Type, TypeVar, TYPE_CHECKING

from pygments.lexer import RegexLexer, bygroups
from pygments.token import (
    Name,
    Keyword,
    Number,
    Operator,
    Text,
    Token,
    String
)

from .parsing import Patterns

if TYPE_CHECKING:
    from ..core import Application, CommandEngine


_T = TypeVar('_T')


class _with_app:
    def __init__(
        self,
        app: Application
    ) -> None:
        self._app = app

    def __call__(
        self,
        cls: Type[_T]
    ) -> Type[_T]:
        setattr(cls, 'app', self._app)
        return cls


def _resolve_command(lexer, match) -> Iterator[Tuple[int, Token, str]]:
    """Pygments lexer callback for determining if a command is valid.

    Yielded values take the form: (index, tokentype, value).

    See:
        https://pygments.org/docs/lexerdevelopment/#callbacks

    """
    command_engine: CommandEngine = lexer.app.command_engine
    command_name = match.group(0)

    if command_name.strip() in command_engine.keys():
        token_type = Name.RealCommand
    else:
        token_type = Name.NonexistentCommand

    yield match.start(), token_type, command_name


[docs]def get_lexer_cls_for_app( app: Application ) -> Type[RegexLexer]: """Get a lexer class for a specific Application instance.""" @_with_app(app) class _Lexer(RegexLexer): """A lexer for almanac command lines. See: https://pygments.org/docs/lexerdevelopment/ """ name = 'almanac lexer' flags = re.IGNORECASE tokens = { 'root': [ (Patterns.WHITESPACE, Text), (Patterns.BOOLEAN, Keyword.Boolean), (Patterns.INTEGER, Number.Integer), (Patterns.FLOAT, Number.Float), (Patterns.STRING_SINGLE_QUOTE, String.SingleQuote), (Patterns.STRING_DOUBLE_QUOTE, String.DoubleQuote), (Patterns.KWARG, bygroups(Name.Kwarg, Operator)), (Patterns.COMMAND, _resolve_command), ] } return _Lexer