Module refinery.lib.scripts.ps1.model

PowerShell AST node types.

Expand source code Browse git
"""
PowerShell AST node types.
"""
from __future__ import annotations

import enum

from dataclasses import dataclass, field

from refinery.lib.scripts import Block, Expression, Node, Statement


class Ps1ScopeModifier(enum.Enum):
    NONE     = ''          # noqa
    GLOBAL   = 'global'    # noqa
    LOCAL    = 'local'     # noqa
    SCRIPT   = 'script'    # noqa
    PRIVATE  = 'private'   # noqa
    USING    = 'using'     # noqa
    ENV      = 'env'       # noqa
    VARIABLE = 'variable'  # noqa
    FUNCTION = 'function'  # noqa
    ALIAS    = 'alias'     # noqa
    DRIVE    = 'drive'     # noqa


class Ps1CommandArgumentKind(enum.Enum):
    POSITIONAL = 'positional'
    NAMED = 'named'
    SWITCH = 'switch'


class Ps1AccessKind(enum.Enum):
    INSTANCE = '.'
    STATIC = '::'


#: What a numeric literal's multiplier suffix multiplies by, and the whole set of them. The suffix
#: is part of the numeral rather than an operator on it, so it is read where the digits are.
MULTIPLIERS = {
    'kb': 1 << 10,
    'mb': 1 << 20,
    'gb': 1 << 30,
    'tb': 1 << 40,
    'pb': 1 << 50,
}


def _as_float(text: str) -> float:
    """
    The number `text` spells, read first as an integer so that a hexadecimal or binary form is
    accepted, and `0.0` for text that spells no number at all.
    """
    for read in (lambda t: float(int(t, 0)), float):
        try:
            return read(text)
        except (ValueError, OverflowError):
            continue
    return 0.0


@dataclass(repr=False, eq=False)
class Ps1Variable(Expression, spelling='braced'):
    name: str = ''
    scope: Ps1ScopeModifier = Ps1ScopeModifier.NONE
    braced: bool = False
    splatted: bool = False
    drive: str = ''


@dataclass(repr=False, eq=False)
class Ps1IntegerLiteral(Expression):
    """
    A numeral, held as the text it was written as and nothing else.

    How a number is spelled is *what it is* in PowerShell rather than how it looks: `1.5` is a
    Double and `1.5d` a Decimal, `0xFF` an Int32 and `0xFFL` an Int64, and the same digits are an
    Int32, an Int64, a Decimal or a Double depending on how many of them there are. So `raw` is a
    value field and not a spelling one, and `canonical` compares it — two numerals are the same
    program when they are written the same way.

    `value` is the magnitude the digits spell, which is weaker than what the numeral denotes: it
    carries no type, and for a hexadecimal pattern that fills its width it is the unsigned reading
    rather than the negative .NET value. It is derived rather than stored so that it cannot drift
    from `raw`, and it exists only until its last caller asks
    `refinery.lib.scripts.ps1.analysis.values.read` instead, which answers the whole question.
    """
    raw: str = '0'

    @property
    def value(self) -> int:
        text = self.raw.replace('_', '').rstrip('lL')
        try:
            if text.lstrip('+-')[:2].lower() in ('0x', '0b'):
                return int(text, 0)
            return int(text, 10)
        except ValueError:
            return 0


@dataclass(repr=False, eq=False)
class Ps1RealLiteral(Expression):
    """
    A numeral written in a form only a non-integer type can hold — a decimal point, an exponent, a
    `d` suffix or a multiplier. See `Ps1IntegerLiteral` for why `raw` is the value field and what
    `value` is weaker than.
    """
    raw: str = '0.0'

    @property
    def value(self) -> float:
        text = self.raw.replace('_', '')
        for suffix, multiplier in MULTIPLIERS.items():
            if text.lower().endswith(suffix):
                return _as_float(text[:-len(suffix)].rstrip('lL')) * multiplier
        return _as_float(text[:-1] if text[-1:] in ('d', 'D') else text)


@dataclass(repr=False, eq=False)
class Ps1StringLiteral(Expression, spelling='raw'):
    value: str = ''
    raw: str = "''"

    @property
    def is_bare_word(self) -> bool:
        """
        Whether the recorded spelling carries no quotes. Such a spelling is only valid in the slot
        it was read from: PowerShell reads a bare word as a value where a command's name and
        arguments are read, and as the start of a command everywhere else. A node moved out of such
        a slot therefore has to be re-spelled, which is why `raw` alone cannot be replayed.
        """
        return not self.raw.startswith(("'", '"'))


@dataclass(repr=False, eq=False)
class _Ps1Expandable(Expression, spelling='raw'):
    parts: list[Expression] = field(default_factory=list)
    raw: str = ''

    def canonical_form(self):
        """
        An expandable string that interpolates nothing is the literal string it spells. The parser
        collapses the single-part case on its own, but a transform that folds the last expression
        part of `"$a$b"` to text leaves a multi-part all-literal string behind, and that spells the
        same program as the literal it prints as.
        """
        values = []
        for part in self.parts:
            if not isinstance(part, Ps1StringLiteral):
                return None
            values.append(part.value)
        return Ps1StringLiteral(value=''.join(values))


@dataclass(repr=False, eq=False)
class Ps1ExpandableString(_Ps1Expandable):
    """
    An expandable (double-quoted) string with interleaved text and expression parts. Text segments
    are `Ps1StringLiteral`, expression segments are any `refinery.lib.scripts.Expression` node.
    """
    raw: str = '""'


@dataclass(repr=False, eq=False)
class Ps1HereString(Expression, spelling='raw', identity=Ps1StringLiteral):
    value: str = ''
    raw: str = ''


@dataclass(repr=False, eq=False)
class Ps1ExpandableHereString(_Ps1Expandable, identity=Ps1ExpandableString):
    pass


@dataclass(repr=False, eq=False)
class Ps1BinaryExpression(Expression):
    left: Expression | None = None
    operator: str = ''
    right: Expression | None = None


@dataclass(repr=False, eq=False)
class Ps1UnaryExpression(Expression):
    operator: str = ''
    operand: Expression | None = None
    prefix: bool = True


@dataclass(repr=False, eq=False)
class Ps1TypeExpression(Expression):
    name: str = ''


@dataclass(repr=False, eq=False)
class Ps1CastExpression(Expression):
    type_name: str = ''
    operand: Expression | None = None


@dataclass(repr=False, eq=False)
class Ps1MemberAccess(Expression):
    object: Expression | None = None
    member: str | Expression = ''
    access: Ps1AccessKind = Ps1AccessKind.INSTANCE


@dataclass(repr=False, eq=False)
class Ps1IndexExpression(Expression):
    object: Expression | None = None
    index: Expression | None = None


@dataclass(repr=False, eq=False)
class Ps1InvokeMember(Expression):
    object: Expression | None = None
    member: str | Expression = ''
    arguments: list[Expression] = field(default_factory=list)
    access: Ps1AccessKind = Ps1AccessKind.INSTANCE


@dataclass(repr=False, eq=False)
class Ps1CommandArgument(Node):
    kind: Ps1CommandArgumentKind = Ps1CommandArgumentKind.POSITIONAL
    name: str = ''
    value: Expression | None = None


@dataclass(repr=False, eq=False)
class Ps1CommandInvocation(Expression):
    name: Expression | None = None
    arguments: list[Ps1CommandArgument | Expression] = field(default_factory=list)
    invocation_operator: str = ''
    redirections: list[Ps1Redirection] = field(default_factory=list)


@dataclass(repr=False, eq=False)
class Ps1AssignmentExpression(Expression):
    target: Expression | None = None
    operator: str = '='
    value: Node | None = None


@dataclass(repr=False, eq=False)
class Ps1ArrayLiteral(Expression):
    elements: list[Expression] = field(default_factory=list)

    def has_spelling(self) -> bool:
        """
        The comma operator needs something to build an array out of. A bare `,` is not a PowerShell
        expression, so an empty array literal spells nothing at all — `@()` is the empty array that
        does. The parser reaches this shape only through error recovery.
        """
        return bool(self.elements)


@dataclass(repr=False, eq=False)
class Ps1ArrayExpression(Expression):
    body: list[Statement] = field(default_factory=list)


@dataclass(repr=False, eq=False)
class Ps1HashLiteral(Expression):
    """
    The value of an entry is a whole statement, exactly as it is on the right of an assignment, so
    `@{ a = if ($c) { 1 } }` keeps the branch it spells rather than losing it.
    """
    pairs: list[tuple[Expression, Node]] = field(default_factory=list)


@dataclass(repr=False, eq=False)
class Ps1SubExpression(Expression):
    body: list[Statement] = field(default_factory=list)


@dataclass(repr=False, eq=False)
class Ps1ParenExpression(Expression):
    expression: Expression | None = None

    def canonical_form(self):
        """
        A parenthesis groups; it computes nothing of its own. Whether one is written is a question
        about the neighbours an expression is printed among, not about the program, so a bracket
        the synthesizer adds to keep `(1 + 2) * 3` from re-reading as `1 + 2 * 3` must not make the
        result a different tree than the one it was printed from.

        Note that `$(...)` and `@(...)` are not this: they are `Ps1SubExpression` and
        `Ps1ArrayExpression`, and both mean something a bare expression does not.

        An empty parenthesis holds nothing and so is its own canonical form. Whether 5.1 accepts
        `()` at all is a question about the parser rather than about this identification, and it is
        left to be settled against the reference rather than guessed at here.
        """
        return self.expression


@dataclass(repr=False, eq=False)
class Ps1Code(Node):
    param_block: Ps1ParamBlock | None = None
    begin_block: Block | None = None
    process_block: Block | None = None
    end_block: Block | None = None
    dynamicparam_block: Block | None = None
    body: list[Statement] = field(default_factory=list)


@dataclass(repr=False, eq=False)
class Ps1ScriptBlock(Ps1Code, Expression):
    pass


@dataclass(repr=False, eq=False)
class Ps1RangeExpression(Expression):
    start: Expression | None = None
    end: Expression | None = None


@dataclass(repr=False, eq=False)
class Ps1Attribute(Node):
    name: str = ''
    positional_args: list[Expression] = field(default_factory=list)
    named_args: list[tuple[str, Expression]] = field(default_factory=list)


@dataclass(repr=False, eq=False)
class Ps1ParameterDeclaration(Node):
    attributes: list[Ps1Attribute | Ps1TypeExpression] = field(default_factory=list)
    variable: Ps1Variable | None = None
    default_value: Expression | None = None


@dataclass(repr=False, eq=False)
class Ps1ParamBlock(Node):
    attributes: list[Ps1Attribute] = field(default_factory=list)
    parameters: list[Ps1ParameterDeclaration] = field(default_factory=list)


class Ps1RedirectionStream(enum.IntEnum):
    ALL         = 0  # noqa
    OUTPUT      = 1  # noqa
    ERROR       = 2  # noqa
    WARNING     = 3  # noqa
    VERBOSE     = 4  # noqa
    DEBUG       = 5  # noqa
    INFORMATION = 6  # noqa


@dataclass(repr=False, eq=False)
class Ps1FileRedirection(Node):
    stream: Ps1RedirectionStream = Ps1RedirectionStream.OUTPUT
    target: Expression | None = None
    append: bool = False


@dataclass(repr=False, eq=False)
class Ps1MergingRedirection(Node):
    from_stream: Ps1RedirectionStream = Ps1RedirectionStream.ERROR
    to_stream: Ps1RedirectionStream = Ps1RedirectionStream.OUTPUT


@dataclass(repr=False, eq=False)
class Ps1InputRedirection(Node):
    """
    A `<` redirection, which PowerShell reserves and never performs. It names no stream because it
    moves nothing: it exists so that the command it belongs to keeps the shape it spells rather than
    losing its name to an operator that reads nothing. The source is kept even though 5.1 discards
    it, because what is dropped here is what an analyst reads back.
    """
    source: Expression | None = None


Ps1Redirection = Ps1FileRedirection | Ps1MergingRedirection | Ps1InputRedirection


@dataclass(repr=False, eq=False)
class Ps1PipelineElement(Node):
    expression: Expression | None = None
    redirections: list[Ps1Redirection] = field(default_factory=list)

    def canonical_form(self):
        """
        An element that redirects nothing is the expression it holds; the wrapper exists to carry
        the redirections that a stage may have, and spells nothing when it carries none.
        """
        if self.redirections:
            return None
        return self.expression


@dataclass(repr=False, eq=False)
class Ps1Pipeline(Expression, Statement):
    elements: list[Ps1PipelineElement] = field(default_factory=list)

    def canonical_form(self):
        """
        A pipeline of one stage is that stage: nothing is piped anywhere. The parser never builds
        one — it returns the bare expression — so a single-element pipeline is always something a
        transform assembled, and it spells exactly what its element spells.
        """
        if len(self.elements) != 1:
            return None
        return self.elements[0]

    def has_spelling(self) -> bool:
        return bool(self.elements)


@dataclass(repr=False, eq=False)
class Ps1ExpressionStatement(Statement):
    expression: Expression | None = None


@dataclass(repr=False, eq=False)
class Ps1IfStatement(Statement):
    clauses: list[tuple[Expression, Block]] = field(default_factory=list)
    else_block: Block | None = None

    def has_spelling(self) -> bool:
        """
        There is no `else` without an `if`. A statement whose clauses have all been removed spells
        nothing, and its `else` branch — which runs precisely when no clause matched, so with no
        clauses it always runs — is not the same program as the block written alone.
        """
        return bool(self.clauses)


@dataclass(repr=False, eq=False)
class _Ps1Loop(Statement):
    label: str | None = None


@dataclass(repr=False, eq=False)
class Ps1WhileLoop(_Ps1Loop):
    condition: Expression | None = None
    body: Block | None = None


@dataclass(repr=False, eq=False)
class Ps1DoLoop(_Ps1Loop):
    body: Block | None = None
    condition: Expression | None = None
    is_until: bool = False

    def has_spelling(self) -> bool:
        """
        Both halves of a `do` loop are mandatory. `do { } while ()` is a syntax error, so a loop
        missing either spells nothing; the parser reaches this shape only through error recovery.
        """
        return self.condition is not None and self.body is not None


@dataclass(repr=False, eq=False)
class Ps1ForLoop(_Ps1Loop):
    initializer: Expression | None = None
    condition: Expression | None = None
    iterator: Expression | None = None
    body: Block | None = None


@dataclass(repr=False, eq=False)
class Ps1ForEachLoop(_Ps1Loop):
    variable: Expression | None = None
    iterable: Expression | None = None
    body: Block | None = None
    parallel: bool = False


@dataclass(repr=False, eq=False)
class Ps1SwitchStatement(Statement):
    value: Expression | None = None
    clauses: list[tuple[Expression | None, Block]] = field(default_factory=list)
    regex: bool = False
    wildcard: bool = False
    exact: bool = False
    case_sensitive: bool = False
    file: bool = False
    label: str | None = None


@dataclass(repr=False, eq=False)
class Ps1CatchClause(Node):
    types: list[str] = field(default_factory=list)
    body: Block | None = None


@dataclass(repr=False, eq=False)
class Ps1TryCatchFinally(Statement):
    try_block: Block | None = None
    catch_clauses: list[Ps1CatchClause] = field(default_factory=list)
    finally_block: Block | None = None

    def has_spelling(self) -> bool:
        """
        A `try` needs somewhere to go: 5.1 rejects one carrying neither a `catch` nor a `finally`.
        Printing the bare `try` would turn a guarded block into an unguarded one, which is the
        difference between a script that survives an error and one that stops on it.
        """
        return bool(self.catch_clauses) or self.finally_block is not None


@dataclass(repr=False, eq=False)
class Ps1TrapStatement(Statement):
    type_name: str = ''
    body: Block | None = None


@dataclass(repr=False, eq=False)
class Ps1FunctionDefinition(Statement):
    name: str = ''
    is_filter: bool = False
    body: Ps1ScriptBlock | None = None


class Ps1MemberModifier(enum.Flag):
    NONE   = 0       # noqa
    STATIC = enum.auto()  # noqa
    HIDDEN = enum.auto()  # noqa


@dataclass(repr=False, eq=False)
class Ps1PropertyMember(Node):
    attributes: list[Ps1Attribute] = field(default_factory=list)
    modifiers: Ps1MemberModifier = Ps1MemberModifier.NONE
    type_constraint: Ps1TypeExpression | None = None
    variable: Ps1Variable | None = None
    initial_value: Expression | None = None


@dataclass(repr=False, eq=False)
class Ps1MethodMember(Node):
    attributes: list[Ps1Attribute] = field(default_factory=list)
    modifiers: Ps1MemberModifier = Ps1MemberModifier.NONE
    return_type: Ps1TypeExpression | None = None
    definition: Ps1FunctionDefinition | None = None


@dataclass(repr=False, eq=False)
class Ps1ClassDefinition(Statement):
    name: str = ''
    base_types: list[str] = field(default_factory=list)
    members: list[Ps1PropertyMember | Ps1MethodMember] = field(default_factory=list)


@dataclass(repr=False, eq=False)
class Ps1EnumMember(Node):
    name: str = ''
    value: Expression | None = None


@dataclass(repr=False, eq=False)
class Ps1EnumDefinition(Statement):
    name: str = ''
    base_type: str = ''
    members: list[Ps1EnumMember] = field(default_factory=list)


@dataclass(repr=False, eq=False)
class Ps1Exit(Statement):
    pipeline: Expression | None = None


@dataclass(repr=False, eq=False)
class Ps1ReturnStatement(Ps1Exit):
    pass


@dataclass(repr=False, eq=False)
class Ps1ThrowStatement(Ps1Exit):
    pass


@dataclass(repr=False, eq=False)
class Ps1Jump(Statement):
    label: Expression | None = None


@dataclass(repr=False, eq=False)
class Ps1BreakStatement(Ps1Jump):
    pass


@dataclass(repr=False, eq=False)
class Ps1ContinueStatement(Ps1Jump):
    pass


@dataclass(repr=False, eq=False)
class Ps1ExitStatement(Ps1Exit):
    pass


@dataclass(repr=False, eq=False)
class Ps1DataSection(Statement):
    name: str = ''
    commands: list[Expression] = field(default_factory=list)
    body: Block | None = None


@dataclass(repr=False, eq=False)
class Ps1ErrorNode(Expression, unparsed=True):
    """
    A span of source the parser could not read, kept verbatim so that what an analyst gets back
    still contains what was written. It prints as `text` and so reads back as whatever that text
    parses to, which is why a tree holding one states nothing about synthesizer fidelity.

    This node always has a spelling — the text it captured, which is empty where the parser found
    nothing at all. It is the one node the parser may build in place of a shape that has none, and
    it can only serve as that escape hatch if printing it is always defined.
    """
    text: str = ''
    message: str = ''


@dataclass(repr=False, eq=False)
class Ps1Script(Ps1Code, Statement):
    pass

Global variables

var MULTIPLIERS

What a numeric literal's multiplier suffix multiplies by, and the whole set of them. The suffix is part of the numeral rather than an operator on it, so it is read where the digits are.

Classes

class Ps1ScopeModifier (*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 Ps1ScopeModifier(enum.Enum):
    NONE     = ''          # noqa
    GLOBAL   = 'global'    # noqa
    LOCAL    = 'local'     # noqa
    SCRIPT   = 'script'    # noqa
    PRIVATE  = 'private'   # noqa
    USING    = 'using'     # noqa
    ENV      = 'env'       # noqa
    VARIABLE = 'variable'  # noqa
    FUNCTION = 'function'  # noqa
    ALIAS    = 'alias'     # noqa
    DRIVE    = 'drive'     # noqa

Ancestors

  • enum.Enum

Class variables

var NONE

The type of the None singleton.

var GLOBAL

The type of the None singleton.

var LOCAL

The type of the None singleton.

var SCRIPT

The type of the None singleton.

var PRIVATE

The type of the None singleton.

var USING

The type of the None singleton.

var ENV

The type of the None singleton.

var VARIABLE

The type of the None singleton.

var FUNCTION

The type of the None singleton.

var ALIAS

The type of the None singleton.

var DRIVE

The type of the None singleton.

class Ps1CommandArgumentKind (*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 Ps1CommandArgumentKind(enum.Enum):
    POSITIONAL = 'positional'
    NAMED = 'named'
    SWITCH = 'switch'

Ancestors

  • enum.Enum

Class variables

var POSITIONAL

The type of the None singleton.

var NAMED

The type of the None singleton.

var SWITCH

The type of the None singleton.

class Ps1AccessKind (*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 Ps1AccessKind(enum.Enum):
    INSTANCE = '.'
    STATIC = '::'

Ancestors

  • enum.Enum

Class variables

var INSTANCE

The type of the None singleton.

var STATIC

The type of the None singleton.

class Ps1Variable (offset=-1, parent=None, leading_comments=<factory>, name='', scope=Ps1ScopeModifier.NONE, braced=False, splatted=False, drive='')

Ps1Variable(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , name: 'str' = '', scope: 'Ps1ScopeModifier' = , braced: 'bool' = False, splatted: 'bool' = False, drive: 'str' = '')

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1Variable(Expression, spelling='braced'):
    name: str = ''
    scope: Ps1ScopeModifier = Ps1ScopeModifier.NONE
    braced: bool = False
    splatted: bool = False
    drive: str = ''

Ancestors

Instance variables

var name

The type of the None singleton.

var scope

The type of the None singleton.

var braced

The type of the None singleton.

var splatted

The type of the None singleton.

var drive

The type of the None singleton.

Inherited members

class Ps1IntegerLiteral (offset=-1, parent=None, leading_comments=<factory>, raw='0')

A numeral, held as the text it was written as and nothing else.

How a number is spelled is what it is in PowerShell rather than how it looks: 1.5 is a Double and 1.5d a Decimal, 0xFF an Int32 and 0xFFL an Int64, and the same digits are an Int32, an Int64, a Decimal or a Double depending on how many of them there are. So raw is a value field and not a spelling one, and canonical compares it — two numerals are the same program when they are written the same way.

value is the magnitude the digits spell, which is weaker than what the numeral denotes: it carries no type, and for a hexadecimal pattern that fills its width it is the unsigned reading rather than the negative .NET value. It is derived rather than stored so that it cannot drift from raw, and it exists only until its last caller asks read() instead, which answers the whole question.

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1IntegerLiteral(Expression):
    """
    A numeral, held as the text it was written as and nothing else.

    How a number is spelled is *what it is* in PowerShell rather than how it looks: `1.5` is a
    Double and `1.5d` a Decimal, `0xFF` an Int32 and `0xFFL` an Int64, and the same digits are an
    Int32, an Int64, a Decimal or a Double depending on how many of them there are. So `raw` is a
    value field and not a spelling one, and `canonical` compares it — two numerals are the same
    program when they are written the same way.

    `value` is the magnitude the digits spell, which is weaker than what the numeral denotes: it
    carries no type, and for a hexadecimal pattern that fills its width it is the unsigned reading
    rather than the negative .NET value. It is derived rather than stored so that it cannot drift
    from `raw`, and it exists only until its last caller asks
    `refinery.lib.scripts.ps1.analysis.values.read` instead, which answers the whole question.
    """
    raw: str = '0'

    @property
    def value(self) -> int:
        text = self.raw.replace('_', '').rstrip('lL')
        try:
            if text.lstrip('+-')[:2].lower() in ('0x', '0b'):
                return int(text, 0)
            return int(text, 10)
        except ValueError:
            return 0

Ancestors

Instance variables

var raw

The type of the None singleton.

var value
Expand source code Browse git
@property
def value(self) -> int:
    text = self.raw.replace('_', '').rstrip('lL')
    try:
        if text.lstrip('+-')[:2].lower() in ('0x', '0b'):
            return int(text, 0)
        return int(text, 10)
    except ValueError:
        return 0

Inherited members

class Ps1RealLiteral (offset=-1, parent=None, leading_comments=<factory>, raw='0.0')

A numeral written in a form only a non-integer type can hold — a decimal point, an exponent, a d suffix or a multiplier. See Ps1IntegerLiteral for why raw is the value field and what value is weaker than.

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1RealLiteral(Expression):
    """
    A numeral written in a form only a non-integer type can hold — a decimal point, an exponent, a
    `d` suffix or a multiplier. See `Ps1IntegerLiteral` for why `raw` is the value field and what
    `value` is weaker than.
    """
    raw: str = '0.0'

    @property
    def value(self) -> float:
        text = self.raw.replace('_', '')
        for suffix, multiplier in MULTIPLIERS.items():
            if text.lower().endswith(suffix):
                return _as_float(text[:-len(suffix)].rstrip('lL')) * multiplier
        return _as_float(text[:-1] if text[-1:] in ('d', 'D') else text)

Ancestors

Instance variables

var raw

The type of the None singleton.

var value
Expand source code Browse git
@property
def value(self) -> float:
    text = self.raw.replace('_', '')
    for suffix, multiplier in MULTIPLIERS.items():
        if text.lower().endswith(suffix):
            return _as_float(text[:-len(suffix)].rstrip('lL')) * multiplier
    return _as_float(text[:-1] if text[-1:] in ('d', 'D') else text)

Inherited members

class Ps1StringLiteral (offset=-1, parent=None, leading_comments=<factory>, value='', raw="''")

Ps1StringLiteral(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , value: 'str' = '', raw: 'str' = "''")

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1StringLiteral(Expression, spelling='raw'):
    value: str = ''
    raw: str = "''"

    @property
    def is_bare_word(self) -> bool:
        """
        Whether the recorded spelling carries no quotes. Such a spelling is only valid in the slot
        it was read from: PowerShell reads a bare word as a value where a command's name and
        arguments are read, and as the start of a command everywhere else. A node moved out of such
        a slot therefore has to be re-spelled, which is why `raw` alone cannot be replayed.
        """
        return not self.raw.startswith(("'", '"'))

Ancestors

Instance variables

var value

The type of the None singleton.

var raw

The type of the None singleton.

var is_bare_word

Whether the recorded spelling carries no quotes. Such a spelling is only valid in the slot it was read from: PowerShell reads a bare word as a value where a command's name and arguments are read, and as the start of a command everywhere else. A node moved out of such a slot therefore has to be re-spelled, which is why raw alone cannot be replayed.

Expand source code Browse git
@property
def is_bare_word(self) -> bool:
    """
    Whether the recorded spelling carries no quotes. Such a spelling is only valid in the slot
    it was read from: PowerShell reads a bare word as a value where a command's name and
    arguments are read, and as the start of a command everywhere else. A node moved out of such
    a slot therefore has to be re-spelled, which is why `raw` alone cannot be replayed.
    """
    return not self.raw.startswith(("'", '"'))

Inherited members

class Ps1ExpandableString (offset=-1, parent=None, leading_comments=<factory>, parts=<factory>, raw='""')

An expandable (double-quoted) string with interleaved text and expression parts. Text segments are Ps1StringLiteral, expression segments are any Expression node.

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1ExpandableString(_Ps1Expandable):
    """
    An expandable (double-quoted) string with interleaved text and expression parts. Text segments
    are `Ps1StringLiteral`, expression segments are any `refinery.lib.scripts.Expression` node.
    """
    raw: str = '""'

Ancestors

Instance variables

var raw

The type of the None singleton.

Inherited members

class Ps1HereString (offset=-1, parent=None, leading_comments=<factory>, value='', raw='')

Ps1HereString(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , value: 'str' = '', raw: 'str' = '')

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1HereString(Expression, spelling='raw', identity=Ps1StringLiteral):
    value: str = ''
    raw: str = ''

Ancestors

Instance variables

var value

The type of the None singleton.

var raw

The type of the None singleton.

Inherited members

class Ps1ExpandableHereString (offset=-1, parent=None, leading_comments=<factory>, parts=<factory>, raw='')

Ps1ExpandableHereString(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , parts: 'list[Expression]' = , raw: 'str' = '')

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1ExpandableHereString(_Ps1Expandable, identity=Ps1ExpandableString):
    pass

Ancestors

Inherited members

class Ps1BinaryExpression (offset=-1, parent=None, leading_comments=<factory>, left=None, operator='', right=None)

Ps1BinaryExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , left: 'Expression | None' = None, operator: 'str' = '', right: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1BinaryExpression(Expression):
    left: Expression | None = None
    operator: str = ''
    right: Expression | None = None

Ancestors

Instance variables

var left

The type of the None singleton.

var operator

The type of the None singleton.

var right

The type of the None singleton.

Inherited members

class Ps1UnaryExpression (offset=-1, parent=None, leading_comments=<factory>, operator='', operand=None, prefix=True)

Ps1UnaryExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , operator: 'str' = '', operand: 'Expression | None' = None, prefix: 'bool' = True)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1UnaryExpression(Expression):
    operator: str = ''
    operand: Expression | None = None
    prefix: bool = True

Ancestors

Instance variables

var operator

The type of the None singleton.

var operand

The type of the None singleton.

var prefix

The type of the None singleton.

Inherited members

class Ps1TypeExpression (offset=-1, parent=None, leading_comments=<factory>, name='')

Ps1TypeExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , name: 'str' = '')

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1TypeExpression(Expression):
    name: str = ''

Ancestors

Instance variables

var name

The type of the None singleton.

Inherited members

class Ps1CastExpression (offset=-1, parent=None, leading_comments=<factory>, type_name='', operand=None)

Ps1CastExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , type_name: 'str' = '', operand: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1CastExpression(Expression):
    type_name: str = ''
    operand: Expression | None = None

Ancestors

Instance variables

var type_name

The type of the None singleton.

var operand

The type of the None singleton.

Inherited members

class Ps1MemberAccess (offset=-1, parent=None, leading_comments=<factory>, object=None, member='', access=Ps1AccessKind.INSTANCE)

Ps1MemberAccess(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , object: 'Expression | None' = None, member: 'str | Expression' = '', access: 'Ps1AccessKind' = )

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1MemberAccess(Expression):
    object: Expression | None = None
    member: str | Expression = ''
    access: Ps1AccessKind = Ps1AccessKind.INSTANCE

Ancestors

Instance variables

var object

The type of the None singleton.

var member

The type of the None singleton.

var access

The type of the None singleton.

Inherited members

class Ps1IndexExpression (offset=-1, parent=None, leading_comments=<factory>, object=None, index=None)

Ps1IndexExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , object: 'Expression | None' = None, index: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1IndexExpression(Expression):
    object: Expression | None = None
    index: Expression | None = None

Ancestors

Instance variables

var object

The type of the None singleton.

var index

The type of the None singleton.

Inherited members

class Ps1InvokeMember (offset=-1, parent=None, leading_comments=<factory>, object=None, member='', arguments=<factory>, access=Ps1AccessKind.INSTANCE)

Ps1InvokeMember(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , object: 'Expression | None' = None, member: 'str | Expression' = '', arguments: 'list[Expression]' = , access: 'Ps1AccessKind' = )

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1InvokeMember(Expression):
    object: Expression | None = None
    member: str | Expression = ''
    arguments: list[Expression] = field(default_factory=list)
    access: Ps1AccessKind = Ps1AccessKind.INSTANCE

Ancestors

Instance variables

var arguments

The type of the None singleton.

var object

The type of the None singleton.

var member

The type of the None singleton.

var access

The type of the None singleton.

Inherited members

class Ps1CommandArgument (offset=-1, parent=None, leading_comments=<factory>, kind=Ps1CommandArgumentKind.POSITIONAL, name='', value=None)

Ps1CommandArgument(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , kind: 'Ps1CommandArgumentKind' = , name: 'str' = '', value: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1CommandArgument(Node):
    kind: Ps1CommandArgumentKind = Ps1CommandArgumentKind.POSITIONAL
    name: str = ''
    value: Expression | None = None

Ancestors

Instance variables

var kind

The type of the None singleton.

var name

The type of the None singleton.

var value

The type of the None singleton.

Inherited members

class Ps1CommandInvocation (offset=-1, parent=None, leading_comments=<factory>, name=None, arguments=<factory>, invocation_operator='', redirections=<factory>)

Ps1CommandInvocation(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , name: 'Expression | None' = None, arguments: 'list[Ps1CommandArgument | Expression]' = , invocation_operator: 'str' = '', redirections: 'list[Ps1Redirection]' = )

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1CommandInvocation(Expression):
    name: Expression | None = None
    arguments: list[Ps1CommandArgument | Expression] = field(default_factory=list)
    invocation_operator: str = ''
    redirections: list[Ps1Redirection] = field(default_factory=list)

Ancestors

Instance variables

var arguments

The type of the None singleton.

var redirections

The type of the None singleton.

var name

The type of the None singleton.

var invocation_operator

The type of the None singleton.

Inherited members

class Ps1AssignmentExpression (offset=-1, parent=None, leading_comments=<factory>, target=None, operator='=', value=None)

Ps1AssignmentExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , target: 'Expression | None' = None, operator: 'str' = '=', value: 'Node | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1AssignmentExpression(Expression):
    target: Expression | None = None
    operator: str = '='
    value: Node | None = None

Ancestors

Instance variables

var target

The type of the None singleton.

var operator

The type of the None singleton.

var value

The type of the None singleton.

Inherited members

class Ps1ArrayLiteral (offset=-1, parent=None, leading_comments=<factory>, elements=<factory>)

Ps1ArrayLiteral(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , elements: 'list[Expression]' = )

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1ArrayLiteral(Expression):
    elements: list[Expression] = field(default_factory=list)

    def has_spelling(self) -> bool:
        """
        The comma operator needs something to build an array out of. A bare `,` is not a PowerShell
        expression, so an empty array literal spells nothing at all — `@()` is the empty array that
        does. The parser reaches this shape only through error recovery.
        """
        return bool(self.elements)

Ancestors

Instance variables

var elements

The type of the None singleton.

Methods

def has_spelling(self)

The comma operator needs something to build an array out of. A bare , is not a PowerShell expression, so an empty array literal spells nothing at all — @() is the empty array that does. The parser reaches this shape only through error recovery.

Expand source code Browse git
def has_spelling(self) -> bool:
    """
    The comma operator needs something to build an array out of. A bare `,` is not a PowerShell
    expression, so an empty array literal spells nothing at all — `@()` is the empty array that
    does. The parser reaches this shape only through error recovery.
    """
    return bool(self.elements)

Inherited members

class Ps1ArrayExpression (offset=-1, parent=None, leading_comments=<factory>, body=<factory>)

Ps1ArrayExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , body: 'list[Statement]' = )

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1ArrayExpression(Expression):
    body: list[Statement] = field(default_factory=list)

Ancestors

Instance variables

var body

The type of the None singleton.

Inherited members

class Ps1HashLiteral (offset=-1, parent=None, leading_comments=<factory>, pairs=<factory>)

The value of an entry is a whole statement, exactly as it is on the right of an assignment, so @{ a = if ($c) { 1 } } keeps the branch it spells rather than losing it.

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1HashLiteral(Expression):
    """
    The value of an entry is a whole statement, exactly as it is on the right of an assignment, so
    `@{ a = if ($c) { 1 } }` keeps the branch it spells rather than losing it.
    """
    pairs: list[tuple[Expression, Node]] = field(default_factory=list)

Ancestors

Instance variables

var pairs

The type of the None singleton.

Inherited members

class Ps1SubExpression (offset=-1, parent=None, leading_comments=<factory>, body=<factory>)

Ps1SubExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , body: 'list[Statement]' = )

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1SubExpression(Expression):
    body: list[Statement] = field(default_factory=list)

Ancestors

Instance variables

var body

The type of the None singleton.

Inherited members

class Ps1ParenExpression (offset=-1, parent=None, leading_comments=<factory>, expression=None)

Ps1ParenExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , expression: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1ParenExpression(Expression):
    expression: Expression | None = None

    def canonical_form(self):
        """
        A parenthesis groups; it computes nothing of its own. Whether one is written is a question
        about the neighbours an expression is printed among, not about the program, so a bracket
        the synthesizer adds to keep `(1 + 2) * 3` from re-reading as `1 + 2 * 3` must not make the
        result a different tree than the one it was printed from.

        Note that `$(...)` and `@(...)` are not this: they are `Ps1SubExpression` and
        `Ps1ArrayExpression`, and both mean something a bare expression does not.

        An empty parenthesis holds nothing and so is its own canonical form. Whether 5.1 accepts
        `()` at all is a question about the parser rather than about this identification, and it is
        left to be settled against the reference rather than guessed at here.
        """
        return self.expression

Ancestors

Instance variables

var expression

The type of the None singleton.

Methods

def canonical_form(self)

A parenthesis groups; it computes nothing of its own. Whether one is written is a question about the neighbours an expression is printed among, not about the program, so a bracket the synthesizer adds to keep (1 + 2) * 3 from re-reading as 1 + 2 * 3 must not make the result a different tree than the one it was printed from.

Note that $(...) and @(...) are not this: they are Ps1SubExpression and Ps1ArrayExpression, and both mean something a bare expression does not.

An empty parenthesis holds nothing and so is its own canonical form. Whether 5.1 accepts () at all is a question about the parser rather than about this identification, and it is left to be settled against the reference rather than guessed at here.

Expand source code Browse git
def canonical_form(self):
    """
    A parenthesis groups; it computes nothing of its own. Whether one is written is a question
    about the neighbours an expression is printed among, not about the program, so a bracket
    the synthesizer adds to keep `(1 + 2) * 3` from re-reading as `1 + 2 * 3` must not make the
    result a different tree than the one it was printed from.

    Note that `$(...)` and `@(...)` are not this: they are `Ps1SubExpression` and
    `Ps1ArrayExpression`, and both mean something a bare expression does not.

    An empty parenthesis holds nothing and so is its own canonical form. Whether 5.1 accepts
    `()` at all is a question about the parser rather than about this identification, and it is
    left to be settled against the reference rather than guessed at here.
    """
    return self.expression

Inherited members

class Ps1Code (offset=-1, parent=None, leading_comments=<factory>, param_block=None, begin_block=None, process_block=None, end_block=None, dynamicparam_block=None, body=<factory>)

Ps1Code(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , param_block: 'Ps1ParamBlock | None' = None, begin_block: 'Block | None' = None, process_block: 'Block | None' = None, end_block: 'Block | None' = None, dynamicparam_block: 'Block | None' = None, body: 'list[Statement]' = )

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1Code(Node):
    param_block: Ps1ParamBlock | None = None
    begin_block: Block | None = None
    process_block: Block | None = None
    end_block: Block | None = None
    dynamicparam_block: Block | None = None
    body: list[Statement] = field(default_factory=list)

Ancestors

Subclasses

Instance variables

var body

The type of the None singleton.

var param_block

The type of the None singleton.

var begin_block

The type of the None singleton.

var process_block

The type of the None singleton.

var end_block

The type of the None singleton.

var dynamicparam_block

The type of the None singleton.

Inherited members

class Ps1ScriptBlock (offset=-1, parent=None, leading_comments=<factory>, param_block=None, begin_block=None, process_block=None, end_block=None, dynamicparam_block=None, body=<factory>)

Ps1ScriptBlock(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , param_block: 'Ps1ParamBlock | None' = None, begin_block: 'Block | None' = None, process_block: 'Block | None' = None, end_block: 'Block | None' = None, dynamicparam_block: 'Block | None' = None, body: 'list[Statement]' = )

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1ScriptBlock(Ps1Code, Expression):
    pass

Ancestors

Inherited members

class Ps1RangeExpression (offset=-1, parent=None, leading_comments=<factory>, start=None, end=None)

Ps1RangeExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , start: 'Expression | None' = None, end: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1RangeExpression(Expression):
    start: Expression | None = None
    end: Expression | None = None

Ancestors

Instance variables

var start

The type of the None singleton.

var end

The type of the None singleton.

Inherited members

class Ps1Attribute (offset=-1, parent=None, leading_comments=<factory>, name='', positional_args=<factory>, named_args=<factory>)

Ps1Attribute(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , name: 'str' = '', positional_args: 'list[Expression]' = , named_args: 'list[tuple[str, Expression]]' = )

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1Attribute(Node):
    name: str = ''
    positional_args: list[Expression] = field(default_factory=list)
    named_args: list[tuple[str, Expression]] = field(default_factory=list)

Ancestors

Instance variables

var positional_args

The type of the None singleton.

var named_args

The type of the None singleton.

var name

The type of the None singleton.

Inherited members

class Ps1ParameterDeclaration (offset=-1, parent=None, leading_comments=<factory>, attributes=<factory>, variable=None, default_value=None)

Ps1ParameterDeclaration(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , attributes: 'list[Ps1Attribute | Ps1TypeExpression]' = , variable: 'Ps1Variable | None' = None, default_value: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1ParameterDeclaration(Node):
    attributes: list[Ps1Attribute | Ps1TypeExpression] = field(default_factory=list)
    variable: Ps1Variable | None = None
    default_value: Expression | None = None

Ancestors

Instance variables

var attributes

The type of the None singleton.

var variable

The type of the None singleton.

var default_value

The type of the None singleton.

Inherited members

class Ps1ParamBlock (offset=-1, parent=None, leading_comments=<factory>, attributes=<factory>, parameters=<factory>)

Ps1ParamBlock(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , attributes: 'list[Ps1Attribute]' = , parameters: 'list[Ps1ParameterDeclaration]' = )

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1ParamBlock(Node):
    attributes: list[Ps1Attribute] = field(default_factory=list)
    parameters: list[Ps1ParameterDeclaration] = field(default_factory=list)

Ancestors

Instance variables

var attributes

The type of the None singleton.

var parameters

The type of the None singleton.

Inherited members

class Ps1RedirectionStream (*args, **kwds)

Enum where members are also (and must be) ints

Expand source code Browse git
class Ps1RedirectionStream(enum.IntEnum):
    ALL         = 0  # noqa
    OUTPUT      = 1  # noqa
    ERROR       = 2  # noqa
    WARNING     = 3  # noqa
    VERBOSE     = 4  # noqa
    DEBUG       = 5  # noqa
    INFORMATION = 6  # noqa

Ancestors

  • enum.IntEnum
  • builtins.int
  • enum.ReprEnum
  • enum.Enum

Class variables

var ALL

The type of the None singleton.

var OUTPUT

The type of the None singleton.

var ERROR

The type of the None singleton.

var WARNING

The type of the None singleton.

var VERBOSE

The type of the None singleton.

var DEBUG

The type of the None singleton.

var INFORMATION

The type of the None singleton.

class Ps1FileRedirection (offset=-1, parent=None, leading_comments=<factory>, stream=1, target=None, append=False)

Ps1FileRedirection(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , stream: 'Ps1RedirectionStream' = , target: 'Expression | None' = None, append: 'bool' = False)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1FileRedirection(Node):
    stream: Ps1RedirectionStream = Ps1RedirectionStream.OUTPUT
    target: Expression | None = None
    append: bool = False

Ancestors

Instance variables

var stream

The type of the None singleton.

var target

The type of the None singleton.

var append

The type of the None singleton.

Inherited members

class Ps1MergingRedirection (offset=-1, parent=None, leading_comments=<factory>, from_stream=2, to_stream=1)

Ps1MergingRedirection(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , from_stream: 'Ps1RedirectionStream' = , to_stream: 'Ps1RedirectionStream' = )

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1MergingRedirection(Node):
    from_stream: Ps1RedirectionStream = Ps1RedirectionStream.ERROR
    to_stream: Ps1RedirectionStream = Ps1RedirectionStream.OUTPUT

Ancestors

Instance variables

var from_stream

The type of the None singleton.

var to_stream

The type of the None singleton.

Inherited members

class Ps1InputRedirection (offset=-1, parent=None, leading_comments=<factory>, source=None)

A < redirection, which PowerShell reserves and never performs. It names no stream because it moves nothing: it exists so that the command it belongs to keeps the shape it spells rather than losing its name to an operator that reads nothing. The source is kept even though 5.1 discards it, because what is dropped here is what an analyst reads back.

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1InputRedirection(Node):
    """
    A `<` redirection, which PowerShell reserves and never performs. It names no stream because it
    moves nothing: it exists so that the command it belongs to keeps the shape it spells rather than
    losing its name to an operator that reads nothing. The source is kept even though 5.1 discards
    it, because what is dropped here is what an analyst reads back.
    """
    source: Expression | None = None

Ancestors

Instance variables

var source

The type of the None singleton.

Inherited members

class Ps1PipelineElement (offset=-1, parent=None, leading_comments=<factory>, expression=None, redirections=<factory>)

Ps1PipelineElement(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , expression: 'Expression | None' = None, redirections: 'list[Ps1Redirection]' = )

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1PipelineElement(Node):
    expression: Expression | None = None
    redirections: list[Ps1Redirection] = field(default_factory=list)

    def canonical_form(self):
        """
        An element that redirects nothing is the expression it holds; the wrapper exists to carry
        the redirections that a stage may have, and spells nothing when it carries none.
        """
        if self.redirections:
            return None
        return self.expression

Ancestors

Instance variables

var redirections

The type of the None singleton.

var expression

The type of the None singleton.

Methods

def canonical_form(self)

An element that redirects nothing is the expression it holds; the wrapper exists to carry the redirections that a stage may have, and spells nothing when it carries none.

Expand source code Browse git
def canonical_form(self):
    """
    An element that redirects nothing is the expression it holds; the wrapper exists to carry
    the redirections that a stage may have, and spells nothing when it carries none.
    """
    if self.redirections:
        return None
    return self.expression

Inherited members

class Ps1Pipeline (offset=-1, parent=None, leading_comments=<factory>, elements=<factory>)

Ps1Pipeline(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , elements: 'list[Ps1PipelineElement]' = )

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1Pipeline(Expression, Statement):
    elements: list[Ps1PipelineElement] = field(default_factory=list)

    def canonical_form(self):
        """
        A pipeline of one stage is that stage: nothing is piped anywhere. The parser never builds
        one — it returns the bare expression — so a single-element pipeline is always something a
        transform assembled, and it spells exactly what its element spells.
        """
        if len(self.elements) != 1:
            return None
        return self.elements[0]

    def has_spelling(self) -> bool:
        return bool(self.elements)

Ancestors

Instance variables

var elements

The type of the None singleton.

Methods

def canonical_form(self)

A pipeline of one stage is that stage: nothing is piped anywhere. The parser never builds one — it returns the bare expression — so a single-element pipeline is always something a transform assembled, and it spells exactly what its element spells.

Expand source code Browse git
def canonical_form(self):
    """
    A pipeline of one stage is that stage: nothing is piped anywhere. The parser never builds
    one — it returns the bare expression — so a single-element pipeline is always something a
    transform assembled, and it spells exactly what its element spells.
    """
    if len(self.elements) != 1:
        return None
    return self.elements[0]

Inherited members

class Ps1ExpressionStatement (offset=-1, parent=None, leading_comments=<factory>, expression=None)

Ps1ExpressionStatement(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , expression: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1ExpressionStatement(Statement):
    expression: Expression | None = None

Ancestors

Instance variables

var expression

The type of the None singleton.

Inherited members

class Ps1IfStatement (offset=-1, parent=None, leading_comments=<factory>, clauses=<factory>, else_block=None)

Ps1IfStatement(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , clauses: 'list[tuple[Expression, Block]]' = , else_block: 'Block | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1IfStatement(Statement):
    clauses: list[tuple[Expression, Block]] = field(default_factory=list)
    else_block: Block | None = None

    def has_spelling(self) -> bool:
        """
        There is no `else` without an `if`. A statement whose clauses have all been removed spells
        nothing, and its `else` branch — which runs precisely when no clause matched, so with no
        clauses it always runs — is not the same program as the block written alone.
        """
        return bool(self.clauses)

Ancestors

Instance variables

var clauses

The type of the None singleton.

var else_block

The type of the None singleton.

Methods

def has_spelling(self)

There is no else without an if. A statement whose clauses have all been removed spells nothing, and its else branch — which runs precisely when no clause matched, so with no clauses it always runs — is not the same program as the block written alone.

Expand source code Browse git
def has_spelling(self) -> bool:
    """
    There is no `else` without an `if`. A statement whose clauses have all been removed spells
    nothing, and its `else` branch — which runs precisely when no clause matched, so with no
    clauses it always runs — is not the same program as the block written alone.
    """
    return bool(self.clauses)

Inherited members

class Ps1WhileLoop (offset=-1, parent=None, leading_comments=<factory>, label=None, condition=None, body=None)

Ps1WhileLoop(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , label: 'str | None' = None, condition: 'Expression | None' = None, body: 'Block | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1WhileLoop(_Ps1Loop):
    condition: Expression | None = None
    body: Block | None = None

Ancestors

Instance variables

var condition

The type of the None singleton.

var body

The type of the None singleton.

Inherited members

class Ps1DoLoop (offset=-1, parent=None, leading_comments=<factory>, label=None, body=None, condition=None, is_until=False)

Ps1DoLoop(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , label: 'str | None' = None, body: 'Block | None' = None, condition: 'Expression | None' = None, is_until: 'bool' = False)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1DoLoop(_Ps1Loop):
    body: Block | None = None
    condition: Expression | None = None
    is_until: bool = False

    def has_spelling(self) -> bool:
        """
        Both halves of a `do` loop are mandatory. `do { } while ()` is a syntax error, so a loop
        missing either spells nothing; the parser reaches this shape only through error recovery.
        """
        return self.condition is not None and self.body is not None

Ancestors

Instance variables

var body

The type of the None singleton.

var condition

The type of the None singleton.

var is_until

The type of the None singleton.

Methods

def has_spelling(self)

Both halves of a do loop are mandatory. do { } while () is a syntax error, so a loop missing either spells nothing; the parser reaches this shape only through error recovery.

Expand source code Browse git
def has_spelling(self) -> bool:
    """
    Both halves of a `do` loop are mandatory. `do { } while ()` is a syntax error, so a loop
    missing either spells nothing; the parser reaches this shape only through error recovery.
    """
    return self.condition is not None and self.body is not None

Inherited members

class Ps1ForLoop (offset=-1, parent=None, leading_comments=<factory>, label=None, initializer=None, condition=None, iterator=None, body=None)

Ps1ForLoop(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , label: 'str | None' = None, initializer: 'Expression | None' = None, condition: 'Expression | None' = None, iterator: 'Expression | None' = None, body: 'Block | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1ForLoop(_Ps1Loop):
    initializer: Expression | None = None
    condition: Expression | None = None
    iterator: Expression | None = None
    body: Block | None = None

Ancestors

Instance variables

var initializer

The type of the None singleton.

var condition

The type of the None singleton.

var iterator

The type of the None singleton.

var body

The type of the None singleton.

Inherited members

class Ps1ForEachLoop (offset=-1, parent=None, leading_comments=<factory>, label=None, variable=None, iterable=None, body=None, parallel=False)

Ps1ForEachLoop(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , label: 'str | None' = None, variable: 'Expression | None' = None, iterable: 'Expression | None' = None, body: 'Block | None' = None, parallel: 'bool' = False)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1ForEachLoop(_Ps1Loop):
    variable: Expression | None = None
    iterable: Expression | None = None
    body: Block | None = None
    parallel: bool = False

Ancestors

Instance variables

var variable

The type of the None singleton.

var iterable

The type of the None singleton.

var body

The type of the None singleton.

var parallel

The type of the None singleton.

Inherited members

class Ps1SwitchStatement (offset=-1, parent=None, leading_comments=<factory>, value=None, clauses=<factory>, regex=False, wildcard=False, exact=False, case_sensitive=False, file=False, label=None)

Ps1SwitchStatement(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , value: 'Expression | None' = None, clauses: 'list[tuple[Expression | None, Block]]' = , regex: 'bool' = False, wildcard: 'bool' = False, exact: 'bool' = False, case_sensitive: 'bool' = False, file: 'bool' = False, label: 'str | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1SwitchStatement(Statement):
    value: Expression | None = None
    clauses: list[tuple[Expression | None, Block]] = field(default_factory=list)
    regex: bool = False
    wildcard: bool = False
    exact: bool = False
    case_sensitive: bool = False
    file: bool = False
    label: str | None = None

Ancestors

Instance variables

var clauses

The type of the None singleton.

var value

The type of the None singleton.

var regex

The type of the None singleton.

var wildcard

The type of the None singleton.

var exact

The type of the None singleton.

var case_sensitive

The type of the None singleton.

var file

The type of the None singleton.

var label

The type of the None singleton.

Inherited members

class Ps1CatchClause (offset=-1, parent=None, leading_comments=<factory>, types=<factory>, body=None)

Ps1CatchClause(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , types: 'list[str]' = , body: 'Block | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1CatchClause(Node):
    types: list[str] = field(default_factory=list)
    body: Block | None = None

Ancestors

Instance variables

var types

The type of the None singleton.

var body

The type of the None singleton.

Inherited members

class Ps1TryCatchFinally (offset=-1, parent=None, leading_comments=<factory>, try_block=None, catch_clauses=<factory>, finally_block=None)

Ps1TryCatchFinally(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , try_block: 'Block | None' = None, catch_clauses: 'list[Ps1CatchClause]' = , finally_block: 'Block | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1TryCatchFinally(Statement):
    try_block: Block | None = None
    catch_clauses: list[Ps1CatchClause] = field(default_factory=list)
    finally_block: Block | None = None

    def has_spelling(self) -> bool:
        """
        A `try` needs somewhere to go: 5.1 rejects one carrying neither a `catch` nor a `finally`.
        Printing the bare `try` would turn a guarded block into an unguarded one, which is the
        difference between a script that survives an error and one that stops on it.
        """
        return bool(self.catch_clauses) or self.finally_block is not None

Ancestors

Instance variables

var catch_clauses

The type of the None singleton.

var try_block

The type of the None singleton.

var finally_block

The type of the None singleton.

Methods

def has_spelling(self)

A try needs somewhere to go: 5.1 rejects one carrying neither a catch nor a finally. Printing the bare try would turn a guarded block into an unguarded one, which is the difference between a script that survives an error and one that stops on it.

Expand source code Browse git
def has_spelling(self) -> bool:
    """
    A `try` needs somewhere to go: 5.1 rejects one carrying neither a `catch` nor a `finally`.
    Printing the bare `try` would turn a guarded block into an unguarded one, which is the
    difference between a script that survives an error and one that stops on it.
    """
    return bool(self.catch_clauses) or self.finally_block is not None

Inherited members

class Ps1TrapStatement (offset=-1, parent=None, leading_comments=<factory>, type_name='', body=None)

Ps1TrapStatement(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , type_name: 'str' = '', body: 'Block | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1TrapStatement(Statement):
    type_name: str = ''
    body: Block | None = None

Ancestors

Instance variables

var type_name

The type of the None singleton.

var body

The type of the None singleton.

Inherited members

class Ps1FunctionDefinition (offset=-1, parent=None, leading_comments=<factory>, name='', is_filter=False, body=None)

Ps1FunctionDefinition(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , name: 'str' = '', is_filter: 'bool' = False, body: 'Ps1ScriptBlock | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1FunctionDefinition(Statement):
    name: str = ''
    is_filter: bool = False
    body: Ps1ScriptBlock | None = None

Ancestors

Instance variables

var name

The type of the None singleton.

var is_filter

The type of the None singleton.

var body

The type of the None singleton.

Inherited members

class Ps1MemberModifier (*args, **kwds)

Support for flags

Expand source code Browse git
class Ps1MemberModifier(enum.Flag):
    NONE   = 0       # noqa
    STATIC = enum.auto()  # noqa
    HIDDEN = enum.auto()  # noqa

Ancestors

  • enum.Flag
  • enum.Enum

Class variables

var NONE

The type of the None singleton.

var STATIC

The type of the None singleton.

var HIDDEN

The type of the None singleton.

class Ps1PropertyMember (offset=-1, parent=None, leading_comments=<factory>, attributes=<factory>, modifiers=Ps1MemberModifier.NONE, type_constraint=None, variable=None, initial_value=None)

Ps1PropertyMember(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , attributes: 'list[Ps1Attribute]' = , modifiers: 'Ps1MemberModifier' = , type_constraint: 'Ps1TypeExpression | None' = None, variable: 'Ps1Variable | None' = None, initial_value: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1PropertyMember(Node):
    attributes: list[Ps1Attribute] = field(default_factory=list)
    modifiers: Ps1MemberModifier = Ps1MemberModifier.NONE
    type_constraint: Ps1TypeExpression | None = None
    variable: Ps1Variable | None = None
    initial_value: Expression | None = None

Ancestors

Instance variables

var attributes

The type of the None singleton.

var modifiers

The type of the None singleton.

var type_constraint

The type of the None singleton.

var variable

The type of the None singleton.

var initial_value

The type of the None singleton.

Inherited members

class Ps1MethodMember (offset=-1, parent=None, leading_comments=<factory>, attributes=<factory>, modifiers=Ps1MemberModifier.NONE, return_type=None, definition=None)

Ps1MethodMember(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , attributes: 'list[Ps1Attribute]' = , modifiers: 'Ps1MemberModifier' = , return_type: 'Ps1TypeExpression | None' = None, definition: 'Ps1FunctionDefinition | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1MethodMember(Node):
    attributes: list[Ps1Attribute] = field(default_factory=list)
    modifiers: Ps1MemberModifier = Ps1MemberModifier.NONE
    return_type: Ps1TypeExpression | None = None
    definition: Ps1FunctionDefinition | None = None

Ancestors

Instance variables

var attributes

The type of the None singleton.

var modifiers

The type of the None singleton.

var return_type

The type of the None singleton.

var definition

The type of the None singleton.

Inherited members

class Ps1ClassDefinition (offset=-1, parent=None, leading_comments=<factory>, name='', base_types=<factory>, members=<factory>)

Ps1ClassDefinition(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , name: 'str' = '', base_types: 'list[str]' = , members: 'list[Ps1PropertyMember | Ps1MethodMember]' = )

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1ClassDefinition(Statement):
    name: str = ''
    base_types: list[str] = field(default_factory=list)
    members: list[Ps1PropertyMember | Ps1MethodMember] = field(default_factory=list)

Ancestors

Instance variables

var base_types

The type of the None singleton.

var members

The type of the None singleton.

var name

The type of the None singleton.

Inherited members

class Ps1EnumMember (offset=-1, parent=None, leading_comments=<factory>, name='', value=None)

Ps1EnumMember(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , name: 'str' = '', value: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1EnumMember(Node):
    name: str = ''
    value: Expression | None = None

Ancestors

Instance variables

var name

The type of the None singleton.

var value

The type of the None singleton.

Inherited members

class Ps1EnumDefinition (offset=-1, parent=None, leading_comments=<factory>, name='', base_type='', members=<factory>)

Ps1EnumDefinition(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , name: 'str' = '', base_type: 'str' = '', members: 'list[Ps1EnumMember]' = )

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1EnumDefinition(Statement):
    name: str = ''
    base_type: str = ''
    members: list[Ps1EnumMember] = field(default_factory=list)

Ancestors

Instance variables

var members

The type of the None singleton.

var name

The type of the None singleton.

var base_type

The type of the None singleton.

Inherited members

class Ps1Exit (offset=-1, parent=None, leading_comments=<factory>, pipeline=None)

Ps1Exit(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , pipeline: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1Exit(Statement):
    pipeline: Expression | None = None

Ancestors

Subclasses

Instance variables

var pipeline

The type of the None singleton.

Inherited members

class Ps1ReturnStatement (offset=-1, parent=None, leading_comments=<factory>, pipeline=None)

Ps1ReturnStatement(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , pipeline: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1ReturnStatement(Ps1Exit):
    pass

Ancestors

Inherited members

class Ps1ThrowStatement (offset=-1, parent=None, leading_comments=<factory>, pipeline=None)

Ps1ThrowStatement(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , pipeline: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1ThrowStatement(Ps1Exit):
    pass

Ancestors

Inherited members

class Ps1Jump (offset=-1, parent=None, leading_comments=<factory>, label=None)

Ps1Jump(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , label: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1Jump(Statement):
    label: Expression | None = None

Ancestors

Subclasses

Instance variables

var label

The type of the None singleton.

Inherited members

class Ps1BreakStatement (offset=-1, parent=None, leading_comments=<factory>, label=None)

Ps1BreakStatement(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , label: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1BreakStatement(Ps1Jump):
    pass

Ancestors

Inherited members

class Ps1ContinueStatement (offset=-1, parent=None, leading_comments=<factory>, label=None)

Ps1ContinueStatement(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , label: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1ContinueStatement(Ps1Jump):
    pass

Ancestors

Inherited members

class Ps1ExitStatement (offset=-1, parent=None, leading_comments=<factory>, pipeline=None)

Ps1ExitStatement(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , pipeline: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1ExitStatement(Ps1Exit):
    pass

Ancestors

Inherited members

class Ps1DataSection (offset=-1, parent=None, leading_comments=<factory>, name='', commands=<factory>, body=None)

Ps1DataSection(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , name: 'str' = '', commands: 'list[Expression]' = , body: 'Block | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1DataSection(Statement):
    name: str = ''
    commands: list[Expression] = field(default_factory=list)
    body: Block | None = None

Ancestors

Instance variables

var commands

The type of the None singleton.

var name

The type of the None singleton.

var body

The type of the None singleton.

Inherited members

class Ps1ErrorNode (offset=-1, parent=None, leading_comments=<factory>, text='', message='')

A span of source the parser could not read, kept verbatim so that what an analyst gets back still contains what was written. It prints as text and so reads back as whatever that text parses to, which is why a tree holding one states nothing about synthesizer fidelity.

This node always has a spelling — the text it captured, which is empty where the parser found nothing at all. It is the one node the parser may build in place of a shape that has none, and it can only serve as that escape hatch if printing it is always defined.

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1ErrorNode(Expression, unparsed=True):
    """
    A span of source the parser could not read, kept verbatim so that what an analyst gets back
    still contains what was written. It prints as `text` and so reads back as whatever that text
    parses to, which is why a tree holding one states nothing about synthesizer fidelity.

    This node always has a spelling — the text it captured, which is empty where the parser found
    nothing at all. It is the one node the parser may build in place of a shape that has none, and
    it can only serve as that escape hatch if printing it is always defined.
    """
    text: str = ''
    message: str = ''

Ancestors

Instance variables

var text

The type of the None singleton.

var message

The type of the None singleton.

Inherited members

class Ps1Script (offset=-1, parent=None, leading_comments=<factory>, param_block=None, begin_block=None, process_block=None, end_block=None, dynamicparam_block=None, body=<factory>)

Ps1Script(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , param_block: 'Ps1ParamBlock | None' = None, begin_block: 'Block | None' = None, process_block: 'Block | None' = None, end_block: 'Block | None' = None, dynamicparam_block: 'Block | None' = None, body: 'list[Statement]' = )

Expand source code Browse git
@dataclass(repr=False, eq=False)
class Ps1Script(Ps1Code, Statement):
    pass

Ancestors

Inherited members