Module refinery.lib.scripts.ps1.token

Expand source code Browse git
from __future__ import annotations

import enum
import unicodedata

from dataclasses import dataclass

BACKTICK_ESCAPE = {
    '0' : '\0',
    'a' : '\a',
    'b' : '\b',
    'e' : '\x1b',
    'f' : '\f',
    'n' : '\n',
    'r' : '\r',
    't' : '\t',
    'v' : '\v',
}

#: The inverse of `BACKTICK_ESCAPE`: how a character that cannot stand for itself inside a
#: double-quoted string is written. It belongs beside the table it inverts, and beside the lexer
#: that reads what it writes, so that reading and writing an escape stay one fact.
BACKTICK_ENCODE = {v: F'`{k}' for k, v in BACKTICK_ESCAPE.items()}

SINGLE_QUOTES = frozenset("'\u2018\u2019\u201A\u201B")
DOUBLE_QUOTES = frozenset('"\u201C\u201D\u201E')
DASHES = frozenset('-\u2013\u2014\u2015')

#: The Unicode general categories a space, a line separator and a paragraph separator carry. Above
#: Latin-1 these are the whole of PowerShell's whitespace rule, which asks `char.IsSeparator` and
#: nothing of its own.
_SEPARATOR_CATEGORIES = frozenset(('Zl', 'Zp', 'Zs'))

#: The ASCII characters PowerShell passes over between two tokens. A newline is deliberately not one
#: of them: it ends a statement, so it is a token rather than the space around one.
_ASCII_WHITESPACE = frozenset(' \t\v\f')

#: The two characters above ASCII and below `_SEPARATOR_CATEGORIES`' reach that PowerShell names one
#: at a time. Neither is settled by category: a no-break space is a separator and would be found
#: anyway, and a next-line control is not one and would not.
_LATIN1_WHITESPACE = frozenset('\u00A0\u0085')

#: The ASCII characters that end the token being read, whatever that token began with. This is what
#: makes `a#b` one word where `a{` is two.
_ASCII_FORCE_NEW_TOKEN = frozenset(' \t\n\v\f\r&(),;{|}')

#: The ASCII characters that end a token only where it began with a digit, which is the whole of the
#: difference between `7z` and `7+`. A `?` and a `:` are not among them: they end a numeral from 7.0
#: onwards, where a ternary operator may follow one, and on 5.1 `1?` is a single word.
_ASCII_FORCE_NEW_TOKEN_AFTER_NUMBER = frozenset('!#%*+-./<=>]')


def is_whitespace(c: str) -> bool:
    """
    Whether `c` stands between two tokens without being one. Above Latin-1 the question is Unicode's
    rather than PowerShell's \u2014 every separator counts, so an em space and a line separator part two
    words exactly as a space does \u2014 and below it the answer is a table, which is why a vertical tab
    and a form feed are whitespace while a carriage return is not.
    """
    if c < '\x80':
        return c in _ASCII_WHITESPACE
    if c <= '\u0100':
        return c in _LATIN1_WHITESPACE
    return unicodedata.category(c) in _SEPARATOR_CATEGORIES


def forces_new_token(c: str) -> bool:
    """
    Whether `c` ends the token being read, whatever that token began with. Above ASCII that is
    exactly whitespace; below it there are a handful more.

    The empty string stands for the end of the source, and it ends every token. PowerShell says the
    same thing by reading a NUL past the last character and listing the NUL in the table, which is
    a spelling this cannot borrow: the reader here asks about its own first character, where
    PowerShell's has already taken one, so a NUL in the set would part a word beginning with one
    that PowerShell keeps whole.
    """
    if not c:
        return True
    if c < '\x80':
        return c in _ASCII_FORCE_NEW_TOKEN
    return is_whitespace(c)


def forces_new_token_after_number(c: str) -> bool:
    """
    Whether `c` ends a token that began with a digit. A numeral ends on more characters than a word
    does, which is the whole of why `7z` is one token and `7+` is two. Above ASCII a dash is the
    only one, in each of the spellings `DASHES` accepts.

    The end of the source is not among them and is not meant to be: it is `forces_new_token` that
    answers for it, and a reader has to ask that one first anyway, because what ends every token
    ends a numeral too.
    """
    if c < '\x80':
        return c in _ASCII_FORCE_NEW_TOKEN_AFTER_NUMBER
    return c in DASHES


NORMALIZE_QUOTES = str.maketrans({
    '\u2018': "'",
    '\u2019': "'",
    '\u201A': "'",
    '\u201B': "'",
    '\u201C': '"',
    '\u201D': '"',
    '\u201E': '"',
})

_VARIABLE_PATTERN_CORE = (
    r'(?:[a-zA-Z0-9_]+:(?!:))?'
    r'(?:\{[^}]+\}|[a-zA-Z0-9_][a-zA-Z0-9_?]*)'
    r'|[$?^]'
)


def _strip_backtick_noop(name: str) -> str:
    result: list[str] = []
    i = 0
    while i < len(name):
        if name[i] == '`' and i + 1 < len(name):
            result.append(name[i + 1])
            i += 2
            continue
        result.append(name[i])
        i += 1
    return ''.join(result)


class Ps1TokenKind(enum.Enum):
    INTEGER          = 'integer'         # noqa
    REAL             = 'real'            # noqa
    STRING_VERBATIM  = 'sq-string'       # noqa
    STRING_EXPAND    = 'dq-string'       # noqa
    HSTRING_VERBATIM = 'sq-hstring'      # noqa
    HSTRING_EXPAND   = 'dq-hstring'      # noqa

    VARIABLE         = 'variable'        # noqa
    SPLAT_VARIABLE   = 'splat-variable'  # noqa
    LABEL            = 'label'           # noqa

    PLUS             = '+'               # noqa
    DASH             = '-'               # noqa
    STAR             = '*'               # noqa
    SLASH            = '/'               # noqa
    PERCENT          = '%'               # noqa
    DOT              = '.'               # noqa
    DOTDOT           = '..'              # noqa
    COMMA            = ','               # noqa
    SEMICOLON        = ';'               # noqa
    INCREMENT        = '++'              # noqa
    DECREMENT        = '--'              # noqa
    EXCLAIM          = '!'               # noqa
    DOUBLE_COLON     = '::'              # noqa
    DOUBLE_AMPERSAND = '&&'              # noqa
    DOUBLE_PIPE      = '||'              # noqa

    EQUALS           = '='               # noqa
    PLUS_ASSIGN      = '+='              # noqa
    DASH_ASSIGN      = '-='              # noqa
    STAR_ASSIGN      = '*='              # noqa
    SLASH_ASSIGN     = '/='              # noqa
    PERCENT_ASSIGN   = '%='              # noqa

    OPERATOR         = 'operator'        # noqa
    PARAMETER        = 'parameter'       # noqa
    GENERIC_TOKEN    = 'generic-token'   # noqa
    GENERIC_EXPAND   = 'generic-expand'  # noqa

    LPAREN           = '('               # noqa
    RPAREN           = ')'               # noqa
    LBRACE           = '{'               # noqa
    RBRACE           = '}'               # noqa
    LBRACKET         = '['               # noqa
    RBRACKET         = ']'               # noqa
    AT_LPAREN        = '@('              # noqa
    AT_LBRACE        = '@{'              # noqa
    DOLLAR_LPAREN    = '$('              # noqa

    PIPE             = '|'               # noqa
    AMPERSAND        = '&'               # noqa
    REDIRECTION      = 'redirection'     # noqa
    REDIRECT_IN      = '<'               # noqa

    IF               = 'if'              # noqa
    ELSEIF           = 'elseif'          # noqa
    ELSE             = 'else'            # noqa
    SWITCH           = 'switch'          # noqa
    WHILE            = 'while'           # noqa
    FOR              = 'for'             # noqa
    FOREACH          = 'foreach'         # noqa
    DO               = 'do'              # noqa
    UNTIL            = 'until'           # noqa
    FUNCTION         = 'function'        # noqa
    FILTER           = 'filter'          # noqa
    RETURN           = 'return'          # noqa
    BREAK            = 'break'           # noqa
    CONTINUE         = 'continue'        # noqa
    THROW            = 'throw'           # noqa
    EXIT             = 'exit'            # noqa
    TRY              = 'try'             # noqa
    CATCH            = 'catch'           # noqa
    FINALLY          = 'finally'         # noqa
    TRAP             = 'trap'            # noqa
    DATA             = 'data'            # noqa
    BEGIN            = 'begin'           # noqa
    PROCESS          = 'process'         # noqa
    END              = 'end'             # noqa
    PARAM            = 'param'           # noqa
    IN               = 'in'              # noqa
    CLASS            = 'class'           # noqa
    USING            = 'using'           # noqa
    ENUM             = 'enum'            # noqa
    DYNAMICPARAM     = 'dynamicparam'    # noqa

    NEWLINE          = 'newline'         # noqa
    EOF              = 'eof'             # noqa

    @property
    def is_keyword(self):
        return self in _KEYWORDS_SET

    @property
    def is_assignment(self):
        return self in _ASSIGNMENT_SET

    @property
    def mode_invariant(self):
        """
        Whether the text of a token of this kind reads the same in every
        `refinery.lib.scripts.ps1.lexer.Ps1LexerMode`. A parser holding one as lookahead may keep it
        across a mode change, because scanning it again could not produce anything else. A kind
        earns membership only by being witnessed in the material the invariance test drives: an
        entry that test cannot reach is a claim nothing checks, and skipping the re-read is worth
        nothing next to that.
        """
        return self in _MODE_INVARIANT_SET


_KEYWORDS: dict[str, Ps1TokenKind] = {
    'if'           : Ps1TokenKind.IF,
    'elseif'       : Ps1TokenKind.ELSEIF,
    'else'         : Ps1TokenKind.ELSE,
    'switch'       : Ps1TokenKind.SWITCH,
    'while'        : Ps1TokenKind.WHILE,
    'for'          : Ps1TokenKind.FOR,
    'foreach'      : Ps1TokenKind.FOREACH,
    'do'           : Ps1TokenKind.DO,
    'until'        : Ps1TokenKind.UNTIL,
    'function'     : Ps1TokenKind.FUNCTION,
    'filter'       : Ps1TokenKind.FILTER,
    'return'       : Ps1TokenKind.RETURN,
    'break'        : Ps1TokenKind.BREAK,
    'continue'     : Ps1TokenKind.CONTINUE,
    'throw'        : Ps1TokenKind.THROW,
    'exit'         : Ps1TokenKind.EXIT,
    'try'          : Ps1TokenKind.TRY,
    'catch'        : Ps1TokenKind.CATCH,
    'finally'      : Ps1TokenKind.FINALLY,
    'trap'         : Ps1TokenKind.TRAP,
    'data'         : Ps1TokenKind.DATA,
    'begin'        : Ps1TokenKind.BEGIN,
    'process'      : Ps1TokenKind.PROCESS,
    'end'          : Ps1TokenKind.END,
    'param'        : Ps1TokenKind.PARAM,
    'in'           : Ps1TokenKind.IN,
    'class'        : Ps1TokenKind.CLASS,
    'using'        : Ps1TokenKind.USING,
    'enum'         : Ps1TokenKind.ENUM,
    'dynamicparam' : Ps1TokenKind.DYNAMICPARAM,
}

_KEYWORDS_SET = frozenset(_KEYWORDS.values())

_ASSIGNMENT_SET = frozenset((
    Ps1TokenKind.EQUALS,
    Ps1TokenKind.PLUS_ASSIGN,
    Ps1TokenKind.DASH_ASSIGN,
    Ps1TokenKind.STAR_ASSIGN,
    Ps1TokenKind.SLASH_ASSIGN,
    Ps1TokenKind.PERCENT_ASSIGN,
))

_MODE_INVARIANT_SET = frozenset((
    Ps1TokenKind.AMPERSAND,
    Ps1TokenKind.AT_LBRACE,
    Ps1TokenKind.AT_LPAREN,
    Ps1TokenKind.COMMA,
    Ps1TokenKind.DOLLAR_LPAREN,
    Ps1TokenKind.EOF,
    Ps1TokenKind.HSTRING_VERBATIM,
    Ps1TokenKind.LBRACE,
    Ps1TokenKind.LPAREN,
    Ps1TokenKind.NEWLINE,
    Ps1TokenKind.PIPE,
    Ps1TokenKind.RBRACE,
    Ps1TokenKind.RBRACKET,
    Ps1TokenKind.RPAREN,
    Ps1TokenKind.SEMICOLON,
    Ps1TokenKind.STRING_EXPAND,
    Ps1TokenKind.STRING_VERBATIM,
))

KEYWORD_SPELLING: dict[str, str] = {
    'param': 'Param',
}


@dataclass
class Ps1Token:
    kind: Ps1TokenKind
    value: str
    offset: int

    def __repr__(self):
        v = self.value
        if len(v) > 40:
            v = v[:37] + '...'
        return F'Token({self.kind.name}, {v!r}, @{self.offset})'

Global variables

var BACKTICK_ENCODE

The inverse of BACKTICK_ESCAPE: how a character that cannot stand for itself inside a double-quoted string is written. It belongs beside the table it inverts, and beside the lexer that reads what it writes, so that reading and writing an escape stay one fact.

Functions

def is_whitespace(c)

Whether c stands between two tokens without being one. Above Latin-1 the question is Unicode's rather than PowerShell's — every separator counts, so an em space and a line separator part two words exactly as a space does — and below it the answer is a table, which is why a vertical tab and a form feed are whitespace while a carriage return is not.

Expand source code Browse git
def is_whitespace(c: str) -> bool:
    """
    Whether `c` stands between two tokens without being one. Above Latin-1 the question is Unicode's
    rather than PowerShell's \u2014 every separator counts, so an em space and a line separator part two
    words exactly as a space does \u2014 and below it the answer is a table, which is why a vertical tab
    and a form feed are whitespace while a carriage return is not.
    """
    if c < '\x80':
        return c in _ASCII_WHITESPACE
    if c <= '\u0100':
        return c in _LATIN1_WHITESPACE
    return unicodedata.category(c) in _SEPARATOR_CATEGORIES
def forces_new_token(c)

Whether c ends the token being read, whatever that token began with. Above ASCII that is exactly whitespace; below it there are a handful more.

The empty string stands for the end of the source, and it ends every token. PowerShell says the same thing by reading a NUL past the last character and listing the NUL in the table, which is a spelling this cannot borrow: the reader here asks about its own first character, where PowerShell's has already taken one, so a NUL in the set would part a word beginning with one that PowerShell keeps whole.

Expand source code Browse git
def forces_new_token(c: str) -> bool:
    """
    Whether `c` ends the token being read, whatever that token began with. Above ASCII that is
    exactly whitespace; below it there are a handful more.

    The empty string stands for the end of the source, and it ends every token. PowerShell says the
    same thing by reading a NUL past the last character and listing the NUL in the table, which is
    a spelling this cannot borrow: the reader here asks about its own first character, where
    PowerShell's has already taken one, so a NUL in the set would part a word beginning with one
    that PowerShell keeps whole.
    """
    if not c:
        return True
    if c < '\x80':
        return c in _ASCII_FORCE_NEW_TOKEN
    return is_whitespace(c)
def forces_new_token_after_number(c)

Whether c ends a token that began with a digit. A numeral ends on more characters than a word does, which is the whole of why 7z is one token and 7+ is two. Above ASCII a dash is the only one, in each of the spellings DASHES accepts.

The end of the source is not among them and is not meant to be: it is forces_new_token() that answers for it, and a reader has to ask that one first anyway, because what ends every token ends a numeral too.

Expand source code Browse git
def forces_new_token_after_number(c: str) -> bool:
    """
    Whether `c` ends a token that began with a digit. A numeral ends on more characters than a word
    does, which is the whole of why `7z` is one token and `7+` is two. Above ASCII a dash is the
    only one, in each of the spellings `DASHES` accepts.

    The end of the source is not among them and is not meant to be: it is `forces_new_token` that
    answers for it, and a reader has to ask that one first anyway, because what ends every token
    ends a numeral too.
    """
    if c < '\x80':
        return c in _ASCII_FORCE_NEW_TOKEN_AFTER_NUMBER
    return c in DASHES

Classes

class Ps1TokenKind (*args, **kwds)

Create a collection of name/value pairs.

Example enumeration:

>>> class Color(Enum):
...     RED = 1
...     BLUE = 2
...     GREEN = 3

Access them by:

  • attribute access:

Color.RED

  • value lookup:

Color(1)

  • name lookup:

Color['RED']

Enumerations can be iterated over, and know how many members they have:

>>> len(Color)
3
>>> list(Color)
[<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]

Methods can be added to enumerations, and members can have their own attributes – see the documentation for details.

Expand source code Browse git
class Ps1TokenKind(enum.Enum):
    INTEGER          = 'integer'         # noqa
    REAL             = 'real'            # noqa
    STRING_VERBATIM  = 'sq-string'       # noqa
    STRING_EXPAND    = 'dq-string'       # noqa
    HSTRING_VERBATIM = 'sq-hstring'      # noqa
    HSTRING_EXPAND   = 'dq-hstring'      # noqa

    VARIABLE         = 'variable'        # noqa
    SPLAT_VARIABLE   = 'splat-variable'  # noqa
    LABEL            = 'label'           # noqa

    PLUS             = '+'               # noqa
    DASH             = '-'               # noqa
    STAR             = '*'               # noqa
    SLASH            = '/'               # noqa
    PERCENT          = '%'               # noqa
    DOT              = '.'               # noqa
    DOTDOT           = '..'              # noqa
    COMMA            = ','               # noqa
    SEMICOLON        = ';'               # noqa
    INCREMENT        = '++'              # noqa
    DECREMENT        = '--'              # noqa
    EXCLAIM          = '!'               # noqa
    DOUBLE_COLON     = '::'              # noqa
    DOUBLE_AMPERSAND = '&&'              # noqa
    DOUBLE_PIPE      = '||'              # noqa

    EQUALS           = '='               # noqa
    PLUS_ASSIGN      = '+='              # noqa
    DASH_ASSIGN      = '-='              # noqa
    STAR_ASSIGN      = '*='              # noqa
    SLASH_ASSIGN     = '/='              # noqa
    PERCENT_ASSIGN   = '%='              # noqa

    OPERATOR         = 'operator'        # noqa
    PARAMETER        = 'parameter'       # noqa
    GENERIC_TOKEN    = 'generic-token'   # noqa
    GENERIC_EXPAND   = 'generic-expand'  # noqa

    LPAREN           = '('               # noqa
    RPAREN           = ')'               # noqa
    LBRACE           = '{'               # noqa
    RBRACE           = '}'               # noqa
    LBRACKET         = '['               # noqa
    RBRACKET         = ']'               # noqa
    AT_LPAREN        = '@('              # noqa
    AT_LBRACE        = '@{'              # noqa
    DOLLAR_LPAREN    = '$('              # noqa

    PIPE             = '|'               # noqa
    AMPERSAND        = '&'               # noqa
    REDIRECTION      = 'redirection'     # noqa
    REDIRECT_IN      = '<'               # noqa

    IF               = 'if'              # noqa
    ELSEIF           = 'elseif'          # noqa
    ELSE             = 'else'            # noqa
    SWITCH           = 'switch'          # noqa
    WHILE            = 'while'           # noqa
    FOR              = 'for'             # noqa
    FOREACH          = 'foreach'         # noqa
    DO               = 'do'              # noqa
    UNTIL            = 'until'           # noqa
    FUNCTION         = 'function'        # noqa
    FILTER           = 'filter'          # noqa
    RETURN           = 'return'          # noqa
    BREAK            = 'break'           # noqa
    CONTINUE         = 'continue'        # noqa
    THROW            = 'throw'           # noqa
    EXIT             = 'exit'            # noqa
    TRY              = 'try'             # noqa
    CATCH            = 'catch'           # noqa
    FINALLY          = 'finally'         # noqa
    TRAP             = 'trap'            # noqa
    DATA             = 'data'            # noqa
    BEGIN            = 'begin'           # noqa
    PROCESS          = 'process'         # noqa
    END              = 'end'             # noqa
    PARAM            = 'param'           # noqa
    IN               = 'in'              # noqa
    CLASS            = 'class'           # noqa
    USING            = 'using'           # noqa
    ENUM             = 'enum'            # noqa
    DYNAMICPARAM     = 'dynamicparam'    # noqa

    NEWLINE          = 'newline'         # noqa
    EOF              = 'eof'             # noqa

    @property
    def is_keyword(self):
        return self in _KEYWORDS_SET

    @property
    def is_assignment(self):
        return self in _ASSIGNMENT_SET

    @property
    def mode_invariant(self):
        """
        Whether the text of a token of this kind reads the same in every
        `refinery.lib.scripts.ps1.lexer.Ps1LexerMode`. A parser holding one as lookahead may keep it
        across a mode change, because scanning it again could not produce anything else. A kind
        earns membership only by being witnessed in the material the invariance test drives: an
        entry that test cannot reach is a claim nothing checks, and skipping the re-read is worth
        nothing next to that.
        """
        return self in _MODE_INVARIANT_SET

Ancestors

  • enum.Enum

Class variables

var INTEGER

The type of the None singleton.

var REAL

The type of the None singleton.

var STRING_VERBATIM

The type of the None singleton.

var STRING_EXPAND

The type of the None singleton.

var HSTRING_VERBATIM

The type of the None singleton.

var HSTRING_EXPAND

The type of the None singleton.

var VARIABLE

The type of the None singleton.

var SPLAT_VARIABLE

The type of the None singleton.

var LABEL

The type of the None singleton.

var PLUS

The type of the None singleton.

var DASH

The type of the None singleton.

var STAR

The type of the None singleton.

var SLASH

The type of the None singleton.

var PERCENT

The type of the None singleton.

var DOT

The type of the None singleton.

var DOTDOT

The type of the None singleton.

var COMMA

The type of the None singleton.

var SEMICOLON

The type of the None singleton.

var INCREMENT

The type of the None singleton.

var DECREMENT

The type of the None singleton.

var EXCLAIM

The type of the None singleton.

var DOUBLE_COLON

The type of the None singleton.

var DOUBLE_AMPERSAND

The type of the None singleton.

var DOUBLE_PIPE

The type of the None singleton.

var EQUALS

The type of the None singleton.

var PLUS_ASSIGN

The type of the None singleton.

var DASH_ASSIGN

The type of the None singleton.

var STAR_ASSIGN

The type of the None singleton.

var SLASH_ASSIGN

The type of the None singleton.

var PERCENT_ASSIGN

The type of the None singleton.

var OPERATOR

The type of the None singleton.

var PARAMETER

The type of the None singleton.

var GENERIC_TOKEN

The type of the None singleton.

var GENERIC_EXPAND

The type of the None singleton.

var LPAREN

The type of the None singleton.

var RPAREN

The type of the None singleton.

var LBRACE

The type of the None singleton.

var RBRACE

The type of the None singleton.

var LBRACKET

The type of the None singleton.

var RBRACKET

The type of the None singleton.

var AT_LPAREN

The type of the None singleton.

var AT_LBRACE

The type of the None singleton.

var DOLLAR_LPAREN

The type of the None singleton.

var PIPE

The type of the None singleton.

var AMPERSAND

The type of the None singleton.

var REDIRECTION

The type of the None singleton.

var REDIRECT_IN

The type of the None singleton.

var IF

The type of the None singleton.

var ELSEIF

The type of the None singleton.

var ELSE

The type of the None singleton.

var SWITCH

The type of the None singleton.

var WHILE

The type of the None singleton.

var FOR

The type of the None singleton.

var FOREACH

The type of the None singleton.

var DO

The type of the None singleton.

var UNTIL

The type of the None singleton.

var FUNCTION

The type of the None singleton.

var FILTER

The type of the None singleton.

var RETURN

The type of the None singleton.

var BREAK

The type of the None singleton.

var CONTINUE

The type of the None singleton.

var THROW

The type of the None singleton.

var EXIT

The type of the None singleton.

var TRY

The type of the None singleton.

var CATCH

The type of the None singleton.

var FINALLY

The type of the None singleton.

var TRAP

The type of the None singleton.

var DATA

The type of the None singleton.

var BEGIN

The type of the None singleton.

var PROCESS

The type of the None singleton.

var END

The type of the None singleton.

var PARAM

The type of the None singleton.

var IN

The type of the None singleton.

var CLASS

The type of the None singleton.

var USING

The type of the None singleton.

var ENUM

The type of the None singleton.

var DYNAMICPARAM

The type of the None singleton.

var NEWLINE

The type of the None singleton.

var EOF

The type of the None singleton.

Instance variables

var is_keyword
Expand source code Browse git
@property
def is_keyword(self):
    return self in _KEYWORDS_SET
var is_assignment
Expand source code Browse git
@property
def is_assignment(self):
    return self in _ASSIGNMENT_SET
var mode_invariant

Whether the text of a token of this kind reads the same in every Ps1LexerMode. A parser holding one as lookahead may keep it across a mode change, because scanning it again could not produce anything else. A kind earns membership only by being witnessed in the material the invariance test drives: an entry that test cannot reach is a claim nothing checks, and skipping the re-read is worth nothing next to that.

Expand source code Browse git
@property
def mode_invariant(self):
    """
    Whether the text of a token of this kind reads the same in every
    `refinery.lib.scripts.ps1.lexer.Ps1LexerMode`. A parser holding one as lookahead may keep it
    across a mode change, because scanning it again could not produce anything else. A kind
    earns membership only by being witnessed in the material the invariance test drives: an
    entry that test cannot reach is a claim nothing checks, and skipping the re-read is worth
    nothing next to that.
    """
    return self in _MODE_INVARIANT_SET
class Ps1Token (kind, value, offset)

Ps1Token(kind: 'Ps1TokenKind', value: 'str', offset: 'int')

Expand source code Browse git
@dataclass
class Ps1Token:
    kind: Ps1TokenKind
    value: str
    offset: int

    def __repr__(self):
        v = self.value
        if len(v) > 40:
            v = v[:37] + '...'
        return F'Token({self.kind.name}, {v!r}, @{self.offset})'

Instance variables

var kind

The type of the None singleton.

var value

The type of the None singleton.

var offset

The type of the None singleton.