Source code for paxter.core.enclosing

"""
Utility class for textual enclosing (left and right) patterns
which surrounds a particular scope of string data.
It is used in the following scenarios:

- Brace pattern for FragmentList nodes
- Quoted pattern for Text nodes
- Bar pattern for the starter part of Command nodes
"""
import re
from dataclasses import dataclass, field
from typing import Pattern

from paxter.core.lexers import LEXER

__all__ = ['EnclosingPattern', 'GlobalEnclosingPattern']

ALLOWED_LEFT_PATTERN_RE = re.compile(r'(?:#*[|{"])?')
LEFT_TO_RIGHT_TRANS = str.maketrans(r'#|{"', r'#|}"')


[docs]@dataclass class EnclosingPattern: """ Information regarding the enclosing (left and right) patterns for a particular scope of string data. """ #: The left (i.e. opening) pattern enclosing the scope left: str #: The right (i.e. closing) pattern enclosing the scope right: str = None def __post_init__(self): if self.right is None: self.right = self.flip_pattern(self.left) def __bool__(self): return bool(self.left) @staticmethod def flip_pattern(left: str) -> str: """ Flips the given left pattern into the corresponding right pattern. For example, the opened pattern `"<##<{"` should be flipped into the closed pattern `"}>##>". """ if not ALLOWED_LEFT_PATTERN_RE.fullmatch(left): raise RuntimeError("something went horribly wrong") # pragma: no cover return left.translate(LEFT_TO_RIGHT_TRANS)[::-1] @property def non_rec_break_re(self) -> Pattern[str]: """ Compiles a regular expression lexer to non-greedily match some text which is then followed by the given enclosing right pattern. """ return LEXER.non_rec_break_re(self.right) @property def rec_break_re(self) -> Pattern[str]: """ Compiles a regular expression lexer to non-greedily match some text which is then followed by either the @-command switch symbol or the given enclosing right pattern. """ return LEXER.rec_break_re(self.right)
[docs]@dataclass class GlobalEnclosingPattern(EnclosingPattern): """ Specialized scope pattern just for global-level fragment list. """ left: str = field(default=None, init=False, repr=False) right: str = field(default=None, init=False, repr=False) def __post_init__(self): pass @property def non_rec_break_re(self) -> Pattern[str]: raise AttributeError @property def rec_break_re(self) -> Pattern[str]: return LEXER.global_break_re