Module refinery.lib.scripts.xlm

The XLM macro-language layer over the formula substrate of refinery.lib.excel: the macrosheet model, the name table, the workbook view the interpreter reads, the command registry with its severity classes, and the environment of answers the macro commands read.

Expand source code Browse git
"""
The XLM macro-language layer over the formula substrate of `refinery.lib.excel`: the
macrosheet model, the name table, the workbook view the interpreter reads, the command
registry with its severity classes, and the environment of answers the macro commands read.
"""
from __future__ import annotations

from refinery.lib.scripts.xlm.commands import XLM_COMMANDS, XlmSeverity, severity
from refinery.lib.scripts.xlm.deobfuscation import deobfuscate
from refinery.lib.scripts.xlm.engine import XlmEngine
from refinery.lib.scripts.xlm.environment import XlmEnvironment
from refinery.lib.scripts.xlm.evaluate import evaluate_expression
from refinery.lib.scripts.xlm.model import XlmCell, XlmMacrosheet, build_xlm_model
from refinery.lib.scripts.xlm.names import XlmNameEntry, XlmNameTable
from refinery.lib.scripts.xlm.references import (
    XlmArrival,
    XlmCursor,
    expand_range,
    resolve_reference,
)
from refinery.lib.scripts.xlm.trace import XlmStatus, XlmStep, visible_steps
from refinery.lib.scripts.xlm.values import (
    XlmOutcome,
    XlmReference,
    XlmValue,
    apply_binary,
    concat,
    condition,
    unwrap_literal,
    wrap_literal,
)
from refinery.lib.scripts.xlm.view import XlmView

__all__ = [
    'XLM_COMMANDS',
    'XlmArrival',
    'XlmCell',
    'XlmCursor',
    'XlmEngine',
    'XlmEnvironment',
    'XlmMacrosheet',
    'XlmNameEntry',
    'XlmNameTable',
    'XlmOutcome',
    'XlmReference',
    'XlmSeverity',
    'XlmStatus',
    'XlmStep',
    'XlmValue',
    'XlmView',
    'apply_binary',
    'build_xlm_model',
    'concat',
    'condition',
    'deobfuscate',
    'evaluate_expression',
    'expand_range',
    'resolve_reference',
    'severity',
    'unwrap_literal',
    'visible_steps',
    'wrap_literal',
]

Sub-modules

refinery.lib.scripts.xlm.blocks

The block structure of a column of the macro language: which ELSE, ELSE.IF, and END.IF markers belong to which one-argument block IF, computed …

refinery.lib.scripts.xlm.commands

The command registry of the XLM macro language: the names the language knows, each with the severity that decides at which output level a trace shows it.

refinery.lib.scripts.xlm.deobfuscation

The static deobfuscation of an XLM program: the passes that clean the macrosheet model a listing starts from. The constant folding replaces what the …

refinery.lib.scripts.xlm.engine

The engine of the emulator: it owns the mutable state of a run — the coordinate indexes the cell reads and the row fall-through consult, the values …

refinery.lib.scripts.xlm.environment

The environment of answers the macro commands of an XLM workbook read: fixed workspace and window tables, and the document answers computed from the …

refinery.lib.scripts.xlm.evaluate

The expression evaluator of the emulator: it walks one formula Expression of the model to the XlmValue the macro language computes from it, …

refinery.lib.scripts.xlm.guess

The guess of the day of the month a program runs under: the one answer a DAY command cannot read from its workbook, searched by running the whole …

refinery.lib.scripts.xlm.handlers

The handler table of the emulator: one handler per macro command the interpreter answers, drawn from the per-category registries, with the guarantee …

refinery.lib.scripts.xlm.memory

The emulated state the system commands of the macro language touch: the files FOPEN opens and FWRITE appends to, and the memory regions the …

refinery.lib.scripts.xlm.model

The cell model of the XLM macro language: one XlmMacrosheet script per macrosheet of a workbook and one XlmCell statement per cell, built from the …

refinery.lib.scripts.xlm.names

The name table of an XLM workbook: the defined names the format stores, keyed for the case-insensitive lookup the macro language performs and resolved …

refinery.lib.scripts.xlm.references

The resolution of the reference nodes of the formula model against the cell a formula sits in: the cursor of the running engine turns the offsets of …

refinery.lib.scripts.xlm.trace

The trace the emulator produces: one step per executed cell, with the status of the cell and the output level that decides which steps a trace shows.

refinery.lib.scripts.xlm.values

The value algebra of the XLM macro language: the values the emulator computes, the quoting the trace spells them with, and the operations the …

refinery.lib.scripts.xlm.view

The workbook view of an XLM macro language program: the one object the interpreter is handed, combining the macrosheet model with the worksheet data …

Functions

def apply_binary(operator, left, right)

The value of a binary operation. A partial operand leaves the operation unevaluated, spelled as its two operands around the operator, and an operand that is an error value is the value of the whole operation. Complete operands apply the operator table to numbers, compare the moments an ISO-spelled text names, and compare anything else as text. A division by zero degrades to #DIV/0!, a result no finite number holds to #NUM!, and an arithmetic operand or a coercion that fails to #VALUE!, the errors Excel displays for them.

Expand source code Browse git
def apply_binary(operator: XlBinaryOperator, left: XlmValue, right: XlmValue) -> XlmValue:
    """
    The value of a binary operation. A partial operand leaves the operation unevaluated,
    spelled as its two operands around the operator, and an operand that is an error value is
    the value of the whole operation. Complete operands apply the operator table to numbers,
    compare the moments an ISO-spelled text names, and compare anything else as text. A
    division by zero degrades to `#DIV/0!`, a result no finite number holds to `#NUM!`, and an
    arithmetic operand or a coercion that fails to `#VALUE!`, the errors Excel displays for
    them.
    """
    if operator is XlBinaryOperator.CONCAT:
        return concat(left, right)
    if operator is XlBinaryOperator.RANGE:
        return XlmValue(
            value=F'{left.unwrap()}:{right.unwrap()}',
            cells=(left, right),
            partial=left.partial or right.partial,
        )
    if operator is XlBinaryOperator.ISECT or operator is XlBinaryOperator.UNION:
        separator = ' ' if operator is XlBinaryOperator.ISECT else ','
        return XlmValue(
            value=F'{left.unwrap()}{separator}{right.unwrap()}',
            partial=True,
        )
    if left.partial or right.partial:
        fragment = F'{left.unwrap()}{_OPERATOR_SYMBOLS[operator]}{right.unwrap()}'
        return XlmValue(value=fragment, partial=True)
    if left.error:
        return left
    if right.error:
        return right
    operand_left = _operand(left)
    operand_right = _operand(right)
    if is_number(operand_left) and is_number(operand_right):
        function = _OPERATOR_FUNCTIONS[operator]
        try:
            return _numeric_result(function(float(operand_left), float(operand_right)))
        except ZeroDivisionError:
            return error_value('#DIV/0!')
        except OverflowError:
            return error_value('#NUM!')
    moment_left = left.unwrap_date()
    moment_right = right.unwrap_date()
    if moment_left is not None and moment_right is not None:
        function = _OPERATOR_FUNCTIONS[operator]
        try:
            return _numeric_result(function(moment_left, moment_right))
        except TypeError:
            return error_value('#VALUE!')
    if operator in _ARITHMETIC_OPERATORS:
        return error_value('#VALUE!')
    function = _OPERATOR_FUNCTIONS[operator]
    try:
        return _numeric_result(function(left.unwrap(), right.unwrap()))
    except TypeError:
        return error_value('#VALUE!')
def build_xlm_model(workbook)

One XlmMacrosheet for every macrosheet of the workbook, holding the cells sheet_cells reads from it.

Expand source code Browse git
def build_xlm_model(workbook: ExcelWorkbook) -> list[XlmMacrosheet]:
    """
    One `XlmMacrosheet` for every macrosheet of the workbook, holding the cells `sheet_cells`
    reads from it.
    """
    result: list[XlmMacrosheet] = []
    for sheet in workbook.sheets():
        if sheet.kind is not SheetKind.MACROSHEET:
            continue
        body: list[Statement] = list(sheet_cells(workbook, sheet).values())
        result.append(XlmMacrosheet(name=sheet.name, kind=sheet.kind, body=body))
    return result
def concat(left, right)

The & of two values: their joined texts when both are complete, the two texts joined by the operator itself when either side is partial, and the error of the first side that is an error value.

Expand source code Browse git
def concat(left: XlmValue, right: XlmValue) -> XlmValue:
    """
    The `&` of two values: their joined texts when both are complete, the two texts joined by
    the operator itself when either side is partial, and the error of the first side that is
    an error value.
    """
    if left.partial or right.partial:
        fragment = F'{left.unwrap()}&{right.unwrap()}'
        return XlmValue(value=fragment, partial=True)
    if left.error:
        return left
    if right.error:
        return right
    joined = left.unwrap() + right.unwrap()
    return XlmValue(value=joined)
def condition(value)

The truth of a value, as every command that reads a condition reads it: the spellings of the two truth values are theirs, a number holds when it is not zero — by its type, so the text of a number holds nothing — a value that holds nothing answers FALSE, and a value that is an error value is that error. Any other text is the #VALUE! error Excel answers for a condition that spells no truth value.

Expand source code Browse git
def condition(value: XlmValue) -> bool | XlmValue:
    """
    The truth of a value, as every command that reads a condition reads it: the spellings of the
    two truth values are theirs, a number holds when it is not zero — by its type, so the text
    of a number holds nothing — a value that holds nothing answers `FALSE`, and a value that is
    an error value is that error. Any other text is the `#VALUE!` error Excel answers for a
    condition that spells no truth value.
    """
    if value.error:
        return value
    data = value.value
    if data is None or data == '':
        return False
    if isinstance(data, bool):
        return data
    if isinstance(data, (int, float)):
        return data != 0
    if isinstance(data, str):
        lowered = data.lower()
        if lowered == 'true':
            return True
        if lowered == 'false':
            return False
    return error_value('#VALUE!')
def deobfuscate(view, start_point='')

Clean the macrosheets of the view in place for a listing: fold every statically computable subtree into the value it computes, then remove the cells no formula leads to, keeping the entry points the run starts from.

Expand source code Browse git
def deobfuscate(view: XlmView, start_point: str = '') -> None:
    """
    Clean the macrosheets of the view in place for a listing: fold every statically computable
    subtree into the value it computes, then remove the cells no formula leads to, keeping the
    entry points the run starts from.
    """
    fold(view)
    sweep(view, start_point)
def evaluate_expression(engine, node, cursor)

The value one formula node computes. A function call contributes only its value — the jump a control command asks for is a question of the step the engine is running, not of the expression it reads — and it is nested, because the expression reads it rather than a step running it. A node the evaluator does not know computes nothing.

Expand source code Browse git
def evaluate_expression(
    engine: XlmEngine,
    node,
    cursor: XlmCursor,
) -> XlmValue:
    """
    The value one formula node computes. A function call contributes only its value — the jump
    a control command asks for is a question of the step the engine is running, not of the
    expression it reads — and it is nested, because the expression reads it rather than a step
    running it. A node the evaluator does not know computes nothing.
    """
    if node is None:
        return XlmValue()
    if isinstance(node, XlNumber):
        return XlmValue(value=node.value)
    if isinstance(node, XlString):
        return XlmValue(value=node.value)
    if isinstance(node, XlBoolean):
        return XlmValue(value=node.value)
    if isinstance(node, XlError):
        return error_value(node.value)
    if isinstance(node, XlParenExpression):
        return evaluate_expression(engine, node.operand, cursor)
    if isinstance(node, XlUnaryExpression):
        return _evaluate_unary(engine, node, cursor)
    if isinstance(node, XlBinaryExpression):
        if node.operator is XlBinaryOperator.RANGE:
            return _evaluate_range(engine, node, cursor)
        left = _scalar(engine, evaluate_expression(engine, node.left, cursor), cursor)
        right = _scalar(engine, evaluate_expression(engine, node.right, cursor), cursor)
        return apply_binary(node.operator, left, right)
    if isinstance(node, (XlA1Reference, XlR1C1Reference)):
        return engine.read_reference(resolve_reference(node, cursor), cursor)
    if isinstance(node, XlDefinedName):
        return engine.resolve_name(node, cursor)
    if isinstance(node, XlFunctionCall):
        return engine.call(node, cursor, nested=True).value
    if isinstance(node, XlArrayConstant):
        cells = tuple(
            evaluate_expression(engine, literal, cursor)
            for row in node.rows
            for literal in row
        )
        return XlmValue(cells=cells)
    if isinstance(node, XlMissingArgument):
        return XlmValue()
    if isinstance(node, XlUnparsedFormula):
        return XlmValue(value=node.text, partial=True)
    return XlmValue()
def expand_range(top_left, bottom_right, guard=None)

Every cell address of the rectangle two corners span, in row-major order. The sheet of each address is the sheet of the top left corner. The guard a caller passes runs every so many addresses, so that a walk over a rectangle the workbook barely fills still answers to the deadline of the run.

Expand source code Browse git
def expand_range(
    top_left: XlmReference,
    bottom_right: XlmReference,
    guard: Callable[[], None] | None = None,
) -> Iterator[XlmReference]:
    """
    Every cell address of the rectangle two corners span, in row-major order. The sheet of
    each address is the sheet of the top left corner. The guard a caller passes runs every so
    many addresses, so that a walk over a rectangle the workbook barely fills still answers to
    the deadline of the run.
    """
    row_min = min(top_left.row, bottom_right.row)
    row_max = max(top_left.row, bottom_right.row)
    col_min = min(top_left.col, bottom_right.col)
    col_max = max(top_left.col, bottom_right.col)
    walked = 0
    for row in range(row_min, row_max + 1):
        for col in range(col_min, col_max + 1):
            walked += 1
            if guard is not None and not walked % _GUARD_YIELDS:
                guard()
            yield XlmReference(top_left.sheet, row, col)
def resolve_reference(node, cursor)

The address a reference node names, resolved against the cursor. The numbers of an A1 reference are absolute positions already; the number of a relative axis of an R1C1 reference is an offset from the cursor on that axis. A reference qualified by a 3-D span of two sheets names the first sheet of the span.

Expand source code Browse git
def resolve_reference(
    node: XlA1Reference | XlR1C1Reference,
    cursor: XlmCursor,
) -> XlmReference:
    """
    The address a reference node names, resolved against the cursor. The numbers of an A1
    reference are absolute positions already; the number of a relative axis of an R1C1
    reference is an offset from the cursor on that axis. A reference qualified by a 3-D span
    of two sheets names the first sheet of the span.
    """
    if isinstance(node, XlR1C1Reference):
        row = cursor.row + node.row if node.relative_row else node.row
        col = cursor.col + node.col if node.relative_col else node.col
    else:
        row = node.row
        col = node.col
    sheet = node.sheets[0] if node.sheets else None
    return XlmReference(sheet, row, col)
def severity(command_name)

The severity of a macro command; a name outside the registry is ordinary.

Expand source code Browse git
def severity(command_name: str) -> XlmSeverity:
    """
    The severity of a macro command; a name outside the registry is ordinary.
    """
    return XLM_COMMANDS.get(command_name, XlmSeverity.NORMAL)
def unwrap_literal(text)

The text of a quoted string literal without its quotes; a text that carries no quotes keeps its spelling, and a doubled quote inside the literal spells one quote.

Expand source code Browse git
def unwrap_literal(text: str) -> str:
    """
    The text of a quoted string literal without its quotes; a text that carries no quotes
    keeps its spelling, and a doubled quote inside the literal spells one quote.
    """
    if len(text) > 1 and text.startswith('"') and text.endswith('"'):
        return text[1:-1].replace('""', '"')
    return text
def visible_steps(steps, level)

The steps a trace at the given output level shows: a step the interpreter ignored never shows, at the string-extraction level only the triage commands show and only the quoted string literals their text carries, and otherwise a step shows when its own level reaches the requested one.

Expand source code Browse git
def visible_steps(steps, level: int):
    """
    The steps a trace at the given output level shows: a step the interpreter ignored never
    shows, at the string-extraction level only the triage commands show and only the quoted
    string literals their text carries, and otherwise a step shows when its own level
    reaches the requested one.
    """
    for step in steps:
        if step.status is XlmStatus.IGNORED:
            continue
        if level >= 3 and step.severity is XlmSeverity.IMPORTANT:
            strings = [match.group(0) for match in _QUOTED_TEXT.finditer(step.text)]
            if strings:
                yield step._replace(text='\n'.join(strings))
        elif step.severity.value >= level:
            yield step
def wrap_literal(data, must_wrap=False)

The spelling the trace gives a value: a number, a boolean, and a text that already carries quotes keep their own spelling — the quoted text only while the caller does not insist on quotes — and every other text is quoted with its quotes doubled.

Expand source code Browse git
def wrap_literal(data: Any, must_wrap: bool = False) -> str:
    """
    The spelling the trace gives a value: a number, a boolean, and a text that already carries
    quotes keep their own spelling — the quoted text only while the caller does not insist on
    quotes — and every other text is quoted with its quotes doubled.
    """
    if isinstance(data, bool):
        return 'TRUE' if data else 'FALSE'
    if is_number(data) or (
        isinstance(data, str)
        and len(data) > 1
        and data.startswith('"')
        and data.endswith('"')
        and not must_wrap
    ):
        return str(data)
    if isinstance(data, float) and data.is_integer():
        return str(int(data))
    if isinstance(data, int):
        return str(data)
    return F'"{str(data).replace(chr(34), chr(34) * 2)}"'

Classes

class XlmArrival (*args, **kwds)

How the engine reached the cell of the step it runs: by the jump a command asked for, by the row fall-through or the continuation below the cell a return hands its value to, by resuming a branch of a partial IF or the anchor a run or a call starts at, or by the replay of a false branch onto the marker of its block. A marker that carries an arm — an ELSE, or an ELSE.IF that names a condition — runs it for a jump and for a replay, and skips to the END.IF of the block for a fall — an arm that ran to its end, or the cell below one a macro call returned to — and for a resume, which an empty true arm and an anchor a run or a call starts at arrive by.

Expand source code Browse git
class XlmArrival(enum.Enum):
    """
    How the engine reached the cell of the step it runs: by the jump a command asked for, by
    the row fall-through or the continuation below the cell a return hands its value to, by
    resuming a branch of a partial `IF` or the anchor a run or a call starts at, or by the
    replay of a false branch onto the marker of its block. A marker that carries an arm — an
    `ELSE`, or an `ELSE.IF` that names a condition — runs it for a jump and for a replay, and
    skips to the `END.IF` of the block for a fall — an arm that ran to its end, or the cell
    below one a macro call returned to — and for a resume, which an empty true arm and an
    anchor a run or a call starts at arrive by.
    """

    JUMP = 0
    FALL = 1
    RESUME = 2
    REPLAY = 3

Ancestors

  • enum.Enum

Class variables

var JUMP

The type of the None singleton.

var FALL

The type of the None singleton.

var RESUME

The type of the None singleton.

var REPLAY

The type of the None singleton.

class XlmCell (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, row=0, col=0, kind=CellKind.BLANK, assignment=False, formula=None, value=None)

A single cell at a one-based row and col. The formula is the decoded expression tree, the unparsed carrier for a formula the reader could not decode, or None for a cell that holds only a value; value is the cached value as the reader spelled it, of the kind the reader gave it; assignment marks a formula the container says assigns to a name.

Expand source code Browse git
@dataclass(repr=False, eq=False)
class XlmCell(Statement):
    """
    A single cell at a one-based `row` and `col`. The `formula` is the decoded expression tree,
    the unparsed carrier for a formula the reader could not decode, or `None` for a cell that holds
    only a value; `value` is the cached value as the reader spelled it, of the `kind` the reader
    gave it; `assignment` marks a formula the container says assigns to a name.
    """

    row: int = 0
    col: int = 0
    kind: CellKind = CellKind.BLANK
    assignment: bool = False
    formula: Expression | None = None
    value: str | int | float | bool | None = None

Ancestors

Instance variables

var row

The type of the None singleton.

var col

The type of the None singleton.

var kind

The type of the None singleton.

var assignment

The type of the None singleton.

var formula

The type of the None singleton.

var value

The type of the None singleton.

Inherited members

class XlmCursor (sheet, row, col)

The cell the engine is currently evaluating: the sheet it sits on with its one-based row and column. The relative references of the formula in this cell resolve against it.

Expand source code Browse git
class XlmCursor(NamedTuple):
    """
    The cell the engine is currently evaluating: the sheet it sits on with its one-based row
    and column. The relative references of the formula in this cell resolve against it.
    """

    sheet: str
    row: int
    col: int

    def reference(self) -> XlmReference:
        """
        The address of the cursor's own cell.
        """
        return XlmReference(None, self.row, self.col)

Ancestors

  • builtins.tuple

Instance variables

var sheet

Alias for field number 0

Expand source code Browse git
class XlmCursor(NamedTuple):
    """
    The cell the engine is currently evaluating: the sheet it sits on with its one-based row
    and column. The relative references of the formula in this cell resolve against it.
    """

    sheet: str
    row: int
    col: int

    def reference(self) -> XlmReference:
        """
        The address of the cursor's own cell.
        """
        return XlmReference(None, self.row, self.col)
var row

Alias for field number 1

Expand source code Browse git
class XlmCursor(NamedTuple):
    """
    The cell the engine is currently evaluating: the sheet it sits on with its one-based row
    and column. The relative references of the formula in this cell resolve against it.
    """

    sheet: str
    row: int
    col: int

    def reference(self) -> XlmReference:
        """
        The address of the cursor's own cell.
        """
        return XlmReference(None, self.row, self.col)
var col

Alias for field number 2

Expand source code Browse git
class XlmCursor(NamedTuple):
    """
    The cell the engine is currently evaluating: the sheet it sits on with its one-based row
    and column. The relative references of the formula in this cell resolve against it.
    """

    sheet: str
    row: int
    col: int

    def reference(self) -> XlmReference:
        """
        The address of the cursor's own cell.
        """
        return XlmReference(None, self.row, self.col)

Methods

def reference(self)

The address of the cursor's own cell.

Expand source code Browse git
def reference(self) -> XlmReference:
    """
    The address of the cursor's own cell.
    """
    return XlmReference(None, self.row, self.col)
class XlmEngine (view, output_level=0, day=-1, timeout=0, max_steps=1000000, *, cells=None)

The interpreter of a workbook view. The macrosheet model is read through coordinate indexes the engine maintains itself, so that cells the program writes at run time stay visible to every reference that reads them afterwards; every write lands in an undo journal that a false branch of a partial IF rolls back from. The start point and the deadline of the latest run are kept on the engine, because the trial runs of the day guess share them.

Expand source code Browse git
class XlmEngine:
    """
    The interpreter of a workbook view. The macrosheet model is read through coordinate indexes
    the engine maintains itself, so that cells the program writes at run time stay visible to
    every reference that reads them afterwards; every write lands in an undo journal that a
    false branch of a partial `IF` rolls back from. The start point and the deadline of the
    latest run are kept on the engine, because the trial runs of the day guess share them.
    """

    def __init__(
        self,
        view,
        output_level: int = 0,
        day: int = -1,
        timeout: int = 0,
        max_steps: int = 1_000_000,
        *,
        cells: _CellIndex | None = None,
    ):
        self.view = view
        self.output_level = output_level
        self.day = day
        self.timeout = timeout
        self.max_steps = max_steps
        self.start_point = ''
        self.deadline: float | None = None
        self.aliases: dict[str, str] = {}
        self.indent_level = 0
        self.indent_current_line = False
        self.arrival = XlmArrival.JUMP
        self.now_count = 0
        self.char_errors = 0
        self.iserror_at: XlmCursor | None = None
        self.iserror_flag = False
        self.iserror_repeats = 0
        self.files = XlmFiles()
        self.memory = XlmMemory()
        self.environment = XlmEnvironment()
        self.active_cell: XlmReference | None = None
        self.failed_writes: set[XlmReference] = set()
        self.call_stack: list[XlmCursor | XlmReturnSlot] = []
        self.branch_stack: list[XlmFrame] = []
        self.while_stack: list[XlmLoop] = []
        self._index = _CellIndex(view) if cells is None else cells
        self._journal: list[_Undo] = []
        self._evaluating: set[int] = set()
        self._resolving: set[tuple[str, int | None]] = set()
        self._buffers: list[list[XlmStep]] = []
        self._depth = 0
        self._steps = 0
        self._reads = 0
        self._halted = False

    @property
    def ignore_processing(self) -> bool:
        """
        Whether the engine skips the body of a loop: true while any open loop does not hold.
        """
        return any(not loop.holds for loop in self.while_stack)

    def run(self, start_point: str = '') -> Iterator[XlmStep]:
        """
        The trace of running the program: one step per executed cell. The entry points are the
        defined names that fuzzy-spell `auto_open` or `auto_close`; without one, the start point
        a caller named is the only entry. The run of an entry that exhausts its step budget ends
        with a step that says so, and a run that exhausts its deadline ends the same way and
        runs no further entry. An engine without a timeout of its own keeps the deadline it was
        given.
        """
        self.start_point = start_point
        if self.timeout > 0:
            self.deadline = time.monotonic() + self.timeout
        return self._run()

    def trial(self, day: int) -> XlmEngine:
        """
        A fresh engine for one trial run of the day guess: it shares this engine's view and the
        index over its cells — the cells, values, and names the runs so far have written — and
        the deadline of this engine's run, and answers the given day, but starts with no files,
        no memory, and no registered names of its own.
        """
        trial = XlmEngine(
            self.view,
            self.output_level,
            day,
            0,
            self.max_steps,
            cells=self._index,
        )
        trial.deadline = self.deadline
        return trial

    def read_reference(self, reference: XlmReference, cursor: XlmCursor) -> XlmValue:
        """
        The value a cell address holds. The sheet the reference fails to name is the sheet the
        cursor sits on. A cell the run left a value in — by a step that executed it, a value
        command, or a return — holds that value. Any other formula cell is evaluated with itself
        as the cursor its relative references resolve against, unless its evaluation is already
        under way, the reads of the running evaluation exhausted their budget, or it nests too
        deeply; such a cell, and a cell that holds only a value, reads as the value it was
        stored with, with the date flag a date cell carries. An address the workbook does not
        hold is blank.
        """
        sheet = reference.sheet or cursor.sheet
        reference = XlmReference(sheet, reference.row, reference.col)
        cell = self._find_cell(sheet, reference.row, reference.col)
        if cell is None:
            if reference in self.failed_writes:
                address = F'{column_letters(reference.col)}{reference.row}'
                return XlmValue(value=address, partial=True)
            return _blank(reference)
        held = self._index.held.get(id(cell))
        if held is not None:
            return dataclasses.replace(held, reference=reference)
        if not self._evaluating:
            self._reads = 0
        if (
            cell.formula is not None
            and id(cell) not in self._evaluating
            and self._reads < _READ_BUDGET
        ):
            self.check_deadline()
            self._reads += 1
            self._evaluating.add(id(cell))
            try:
                value = evaluate_expression(
                    self,
                    cell.formula,
                    XlmCursor(sheet, cell.row, cell.col),
                )
            except RecursionError:
                return self._stored(cell, reference)
            finally:
                self._evaluating.discard(id(cell))
            value.reference = reference
            return value
        return self._stored(cell, reference)

    def resolve_name(self, node: XlDefinedName, cursor: XlmCursor) -> XlmValue:
        """
        The value a defined name stands for: the formula the entry for it holds, evaluated
        through the evaluator with the scope of the reading cell. A name the workbook does not
        define, or one whose formula resolves no further, answers its own spelling; a formula
        that reads the name it defines, or nests too deeply, answers the spelling of the formula.
        """
        entry = self._resolve_entry(node.name, node.sheet, cursor)
        if entry is None or entry.formula is None:
            return XlmValue(value=node.name, text=node.name)
        key = (entry.name.lower(), entry.sheet)
        if key in self._resolving:
            return XlmValue(value=synthesize_formula(entry.formula))
        self._resolving.add(key)
        try:
            return evaluate_expression(self, entry.formula, cursor)
        except RecursionError:
            return XlmValue(value=synthesize_formula(entry.formula))
        finally:
            self._resolving.discard(key)

    def call(self, call: XlFunctionCall, cursor: XlmCursor, nested: bool = False) -> XlmOutcome:
        """
        The outcome of one function call. While the engine skips the body of a loop, every call
        but the heads and ends of loops is ignored. A call whose callee is itself a call answers
        what the inner call computed, spelled as the outer one; a registered alias dispatches
        under the name it was registered for; a callee that names a cell — directly or through
        a defined name — calls the macro there. A call is nested when an expression reads it
        instead of a step running it as the whole formula of its cell: a nested macro call runs
        its subroutine to the value it returns, and a nested command answers through the
        handler an expression reads it by, where it has one. Once the deadline of the run
        passed, no call answers.
        """
        self.check_deadline()
        name = callee_name(call)
        if self.ignore_processing and name not in _LOOP_COMMANDS:
            return XlmOutcome(
                value=XlmValue(value=0, text='', partial=True),
                status=XlmStatus.IGNORED,
            )
        if isinstance(call.callee, XlFunctionCall):
            inner = self.call(call.callee, cursor, nested)
            spelled = synthesize_formula(call)
            if inner.value.partial:
                return XlmOutcome(value=XlmValue(value=spelled, partial=True))
            inner.value.text = spelled
            return inner
        target = self.aliases.get(name)
        if target is not None:
            return self.dispatch(target, call, cursor, nested)
        if isinstance(call.callee, (XlA1Reference, XlR1C1Reference)):
            reference = resolve_reference(call.callee, cursor)
            return self._call_cell(reference, cursor, call, nested)
        if isinstance(call.callee, XlDefinedName):
            reference = self._name_reference(call.callee, cursor)
            if reference is not None:
                return self._call_cell(reference, cursor, call, nested)
        else:
            entry = self._resolve_entry(name, None, cursor)
            reference = self._reference_of(entry.formula if entry else None, cursor)
            if reference is not None:
                return self._call_cell(reference, cursor, call, nested)
        return self.dispatch(name, call, cursor, nested)

    def dispatch(
        self,
        name: str,
        call: XlFunctionCall,
        cursor: XlmCursor,
        nested: bool = False,
    ) -> XlmOutcome:
        """
        The outcome of a call under a name no alias rewrote. A nested call answers through the
        handler an expression reads its command by, where the command has one; every other
        command the handler table holds answers through its handler, and a command without one
        answers the spelled fallback.
        """
        handler = EXPRESSION_HANDLERS.get(name) if nested else None
        if handler is None:
            handler = HANDLERS.get(name)
        if handler is not None:
            return handler(self, call, cursor)
        return self._unknown_command(name, call, cursor)

    def return_value(self, value: XlmValue) -> tuple[XlmCursor | None, bool]:
        """
        Hand the value a `RETURN` computes to the macro call it returns from: a call that is the
        whole formula of its cell leaves the value in that cell and continues below it, and a
        call inside an expression takes the value as its own, which ends the run of the
        subroutine. A return without a call to return from continues below itself. The answer
        is the cursor to continue at and whether the run of a subroutine ended.
        """
        if not self.call_stack:
            return None, False
        caller = self.call_stack.pop()
        if isinstance(caller, XlmReturnSlot):
            caller.value = value
            return None, True
        cell = self._find_cell(caller.sheet, caller.row, caller.col)
        if cell is not None:
            self.write_value(cell, value)
        return self.next_formula_cell(caller), False

    def anchor(self, reference: XlmReference) -> XlmCursor | None:
        """
        The cursor the program continues at from an address: the first formula cell at or below
        it on the same column of the same macrosheet, or `None` when the sheet is no macrosheet,
        is not named at all, or holds no formula cell within the row bound.
        """
        sheet = reference.sheet
        if sheet is None or self.view.macrosheet(sheet) is None:
            return None
        row = self._index.fall_through(sheet, reference.col, reference.row)
        if row is None:
            return None
        return XlmCursor(sheet, row, reference.col)

    def next_formula_cell(self, cursor: XlmCursor) -> XlmCursor | None:
        """
        The cursor the row fall-through moves to: the first formula cell below the given one on
        the same column, bounded by the row bound.
        """
        row = self._index.fall_through(cursor.sheet, cursor.col, cursor.row + 1)
        if row is None:
            return None
        return XlmCursor(cursor.sheet, row, cursor.col)

    def column_blocks(self, cursor: XlmCursor) -> dict[int, XlmBlock]:
        """
        The block structure of the column of the cursor: every marker row mapped to the block
        `IF` it belongs to, paired over the live formula rows of the column, so that cells the
        program wrote at run time take part in the pairing.
        """
        sheet = cursor.sheet.lower()
        markers: list[tuple[int, str]] = []
        for row in self._index.formula_rows.get(sheet, {}).get(cursor.col, ()):
            cell = self._index.find(sheet, row, cursor.col)
            if cell is None or cell.formula is None:
                continue
            marker = marker_of(cell.formula)
            if marker is not None:
                markers.append((row, marker))
        return pair_blocks(markers)

    def argument_reference(
        self,
        call: XlFunctionCall,
        index: int,
        cursor: XlmCursor,
    ) -> XlmReference | None:
        """
        The cell address one argument of a call names, as `XlmEngine.node_reference` reads it.
        """
        if index >= len(call.arguments):
            return None
        return self.node_reference(call.arguments[index], cursor)

    def node_reference(self, node, cursor: XlmCursor) -> XlmReference | None:
        """
        The cell address a node names, with the sheet of a local reference filled in from the
        cursor: a reference resolves against it, a defined name through the reference its entry
        holds, and a string literal parses as a formula. Any other expression evaluates to the
        reference its value carries, or to the address its text spells.
        """
        if isinstance(node, (XlA1Reference, XlR1C1Reference)):
            return self._local(resolve_reference(node, cursor), cursor)
        if isinstance(node, XlDefinedName):
            reference = self._name_reference(node, cursor)
            if reference is not None:
                return self._local(reference, cursor)
        if isinstance(node, XlString):
            return self._spelled_reference(node.value, cursor)
        value = evaluate_expression(self, node, cursor)
        if value.reference is not None:
            return self._local(value.reference, cursor)
        if value.partial or not isinstance(value.value, str):
            return None
        return self._spelled_reference(value.value, cursor)

    def range_corners(self, node, cursor: XlmCursor) -> tuple[XlmReference, XlmReference] | None:
        """
        The corners a range argument names, or `None` when the argument is no range.
        """
        if not isinstance(node, XlBinaryExpression) or node.operator is not XlBinaryOperator.RANGE:
            return None
        corners = []
        for side in (node.left, node.right):
            reference = self.node_reference(side, cursor)
            if reference is None:
                return None
            corners.append(reference)
        return corners[0], corners[1]

    def range_cells(self, corners: tuple[XlmReference, XlmReference]) -> Iterator[XlmReference]:
        """
        The addresses of the cells a range spans that the workbook holds, in row-major order:
        the cells the index holds on the sheet of the range, the cells of that sheet where it
        is a worksheet, and the failed writes inside it, which read as the values the program
        left unfinished. The walk counts the cells the workbook holds, not the coordinates the
        rectangle spans.
        """
        first, last = corners
        sheet = first.sheet
        if sheet is None:
            return
        key = sheet.lower()
        row_min = min(first.row, last.row)
        row_max = max(first.row, last.row)
        col_min = min(first.col, last.col)
        col_max = max(first.col, last.col)
        held = self._index.cells.get(key, {})
        worksheet = self.view.worksheet(sheet)
        coordinates = set(held) | set(worksheet or ())
        coordinates.update(
            (reference.row, reference.col)
            for reference in self.failed_writes
            if (reference.sheet or '').lower() == key
        )
        inside = (
            coordinate
            for coordinate in coordinates
            if row_min <= coordinate[0] <= row_max
            and col_min <= coordinate[1] <= col_max
        )
        for row, col in sorted(inside):
            yield XlmReference(sheet, row, col)

    def snapshot(self) -> XlmSnapshot:
        """
        The state a false branch of a partial `IF` rolls back to: the position of the journal,
        and the state of the run the journal does not hold.
        """
        return XlmSnapshot(
            len(self._journal),
            tuple(self.call_stack),
            tuple(loop.copy() for loop in self.while_stack),
            self.active_cell,
        )

    def state_digest(self) -> bytes:
        """
        The digest of the whole state a run can observe: the cells its index holds, with the
        value each holds and the formula it spells, the entries of the name table, the
        registered aliases, the failed-write marks, the content of every open file, every
        region of memory, and the cell the program selected. A run that returns to the digest
        it anchored changes nothing the program can see.
        """
        parts: list[str] = []
        for sheet in sorted(self._index.cells):
            cells = self._index.cells[sheet]
            for row, col in sorted(cells):
                cell = cells[(row, col)]
                held = self._index.held.get(id(cell))
                value = held.text if held is not None else repr(cell.value)
                formula = cell.formula
                spelling = synthesize_formula(formula) if formula is not None else ''
                parts.append(F'{sheet}!{row}!{col}={value}|{spelling}')
        for entry in self.view.names.entries():
            formula = entry.formula
            spelling = synthesize_formula(formula) if formula is not None else ''
            parts.append(F'name:{entry.name.lower()}@{entry.sheet}={spelling}')
        for name in sorted(self.aliases):
            parts.append(F'alias:{name}={self.aliases[name]}')
        for reference in sorted(self.failed_writes):
            parts.append(F'failed:{reference.a1()}')
        for name, content in self.files.snapshot():
            parts.append(F'file:{name}={content}')
        for base, data in self.memory.snapshot():
            parts.append(F'memory:{base}={len(data)}:{data.hex()}')
        parts.append(F'active:{self.active_cell}')
        return hashlib.sha256('\n'.join(parts).encode()).digest()

    def rollback(self, snapshot: XlmSnapshot) -> None:
        """
        Return to a snapshot: every change the journal holds past its position is undone —
        cells, names, aliases, files, memory regions, and failed-write marks return to what they
        carried, and cells the writes created leave their sheet again — and the call stack, the
        open loops, and the selected cell are what they were.
        """
        while len(self._journal) > snapshot.journal:
            self._journal.pop().undo(self)
        self.call_stack = list(snapshot.call_stack)
        self.while_stack = [loop.copy() for loop in snapshot.loops]
        self.active_cell = snapshot.active_cell

    def write_value(self, cell: XlmCell, value: XlmValue) -> None:
        """
        Leave the value a step computed — or a macro call returned — in the cell it belongs to,
        undoable by a branch that rolls back: a later read of the cell answers that value
        instead of evaluating the formula of the cell again.
        """
        self._journal.append(_Write(
            self._index.sheet_of.get(id(cell), ''),
            cell,
            cell.value,
            cell.formula,
            self._index.held.get(id(cell)),
            False,
        ))
        held = _held(value)
        set_value(cell, 'value', held.value)
        self._index.hold(cell, held)

    def write_cell(
        self,
        reference: XlmReference,
        value: XlmValue,
        cursor: XlmCursor,
        value_only: bool = False,
    ) -> None:
        """
        Write a value into the macrosheet cell an address names, as the mutation commands do,
        undoable by a branch that rolls back; a cell that does not exist is created. A
        value-only write changes the value the cell holds and leaves its formula in place. Any
        other write enters the value the way a typed entry does: a text that starts with `=`
        installs a formula, which a later read of the cell evaluates, and anything else replaces
        the formula of the cell by a constant. An address off every macrosheet is not written.
        """
        sheet = reference.sheet or cursor.sheet
        macrosheet = self.view.macrosheet(sheet)
        if macrosheet is None:
            return
        key = macrosheet.name.lower()
        cell = self._index.find(key, reference.row, reference.col)
        created = cell is None
        if cell is None:
            cell = XlmCell(row=reference.row, col=reference.col)
            self._index.insert(key, cell)
            if macrosheet.body:
                edit = BodyEdit(macrosheet)
                edit.splice(macrosheet.body[-1], [macrosheet.body[-1], cell])
                edit.apply()
            else:
                set_body(macrosheet, [cell])
        self._journal.append(_Write(
            key,
            cell,
            cell.value,
            cell.formula,
            self._index.held.get(id(cell)),
            created,
        ))
        held: XlmValue | None = _held(value)
        formula = cell.formula
        if not value_only:
            formula = None
            if isinstance(value.value, str) and value.value.startswith('='):
                formula = parse_formula(value.value)
                held = None
        self._index.update_formula_rows(key, cell, cell.formula is not None, formula is not None)
        set_value(cell, 'value', value.value if held is None else held.value)
        set_child(cell, 'formula', formula)
        self._index.hold(cell, held)

    def define_name(self, entry: XlmNameEntry) -> None:
        """
        Define the name an entry carries, as the name commands do, undoable by a branch that
        rolls back.
        """
        self._journal.append(_NameDefine(self.view.names.entries()))
        self.view.names.define(entry)

    def assign_name(self, name: str, formula, cursor: XlmCursor) -> None:
        """
        Define a name as `SET.NAME` and the `FOR.CELL` loop do, undoable by a branch that rolls
        back: the definition replaces the entry a read on the sheet of the cursor resolves the
        name to, so that the sheet reads back what it assigned, and a name the sheet resolves to
        no entry is defined for the whole workbook. A name assigned no formula reads as one the
        workbook does not define.
        """
        entry = self.view.names.resolve(name, self.view.sheet_index(cursor.sheet))
        sheet = None if entry is None else entry.sheet
        self.define_name(XlmNameEntry(name=name, sheet=sheet, formula=formula))

    def register_alias(self, name: str, target: str) -> None:
        """
        Register the target a command name dispatches to, as `REGISTER` does, undoable by a
        branch that rolls back.
        """
        self._journal.append(_AliasRegistration(name, self.aliases.get(name)))
        self.aliases[name] = target

    def open_file(self, name: str, access: str = '1') -> None:
        """
        Open the name as a file, as `FOPEN` does, undoable by a branch that rolls back; a name
        that already answers a file changes nothing.
        """
        if self.files.opened(name):
            return
        self._journal.append(_FileOpen(name))
        self.files.open(name, access)

    def write_file(self, name: str, text: str) -> bool:
        """
        Append to a file the program opened, as `FWRITE` does, undoable by a branch that rolls
        back, reporting whether the file took the write.
        """
        length = self.files.size(name)
        if length is None:
            return False
        self._journal.append(_FileWrite(name, length))
        self.files.write(name, text)
        return True

    def allocate_memory(self, base: int, size: int) -> int:
        """
        Reserve a region of memory, as `Kernel32.VirtualAlloc` does, undoable by a branch that
        rolls back.
        """
        self._journal.append(_RegionAlloc())
        return self.memory.allocate(base, size)

    def write_memory(self, base: int, data: bytes, size: int) -> bool:
        """
        Write bytes into a region of memory, as the Kernel32 write commands do, undoable by a
        branch that rolls back, reporting whether a region took the whole write.
        """
        before = self.memory.peek(base, size)
        if before is None:
            return False
        self.memory.write(base, data, size)
        self._journal.append(_MemoryWrite(base, before))
        return True

    def mark_failed(self, reference: XlmReference) -> None:
        """
        Mark an address as the destination of a write that never finished, undoable by a
        branch that rolls back.
        """
        if reference in self.failed_writes:
            return
        self._journal.append(_FailedMark(reference, True))
        self.failed_writes.add(reference)

    def unmark_failed(self, reference: XlmReference) -> None:
        """
        Drop the failed-write mark of an address, undoable by a branch that rolls back.
        """
        if reference not in self.failed_writes:
            return
        self._journal.append(_FailedMark(reference, False))
        self.failed_writes.discard(reference)

    def _run(self) -> Iterator[XlmStep]:
        for reference in self.view.entry_points(self.start_point):
            if self._expired():
                return
            anchor = self.anchor(reference)
            if anchor is None:
                continue
            self._steps = 0
            self._halted = False
            self.while_stack.clear()
            self.call_stack.clear()
            yield from self._run_entry(anchor)

    def _run_entry(
        self,
        anchor: XlmCursor,
        caller: XlmReturnSlot | None = None,
    ) -> Iterator[XlmStep]:
        """
        The steps of running the program from one anchor until every branch it opened ran out.
        The run of a subroutine names the call it returns to: its branches are its own, it ends
        with the first value it returns, and a halt ends it together with the branch of every
        run that called it, each after its calling step; a budget or deadline it exhausts ends
        the run that called it at once. The steps of the subroutines a step called precede the
        step.
        """
        observed: list[tuple[str, int, int]] = []
        anchors: dict[tuple[tuple[str, int, int], ...], bytes] = {}
        digest_step = 0
        buffer: list[XlmStep] = []
        branches = self.branch_stack
        indent = 0 if caller is None else self.indent_level
        self.branch_stack = [XlmFrame(anchor, None, None, indent, '')]
        self._buffers.append(buffer)
        cursor = anchor
        try:
            while self.branch_stack:
                frame = self.branch_stack.pop()
                if frame.snapshot is not None:
                    self.rollback(frame.snapshot)
                    anchors.clear()
                cursor = frame.cursor
                node = frame.branch
                self.indent_level = frame.indent
                self.arrival = XlmArrival.REPLAY if frame.replays else XlmArrival.RESUME
                stack_record = True
                while cursor is not None:
                    self._steps += 1
                    if self._steps > self.max_steps:
                        raise _Exhausted(F'step budget of {self.max_steps} exhausted')
                    cell = self._find_cell(cursor.sheet, cursor.row, cursor.col)
                    if cell is None:
                        break
                    if node is None:
                        node = cell.formula
                        if node is None:
                            break
                    if stack_record:
                        previous_indent = self.indent_level - 1 if self.indent_level > 0 else 0
                    else:
                        previous_indent = self.indent_level
                    if caller is None:
                        self._reads = 0
                    self._evaluating.add(id(cell))
                    try:
                        with RecursionDepth(TREE_RECURSION_DEPTH):
                            outcome = self._execute(node, cursor)
                    finally:
                        self._evaluating.discard(id(cell))
                    status = outcome.status
                    if status is None:
                        partial = outcome.value.partial
                        status = (
                            XlmStatus.PartialEvaluation if partial else XlmStatus.FullEvaluation
                        )
                    text = outcome.value.text or ''
                    if not self.while_stack and text != 'NEXT':
                        observed.append((cursor.sheet.lower(), cursor.row, cursor.col))
                        if len(observed) >= _WINDOW_WARMUP:
                            window = tuple(observed[-10:])
                            if window in anchors:
                                if self._steps - digest_step >= _DIGEST_STEPS:
                                    digest_step = self._steps
                                    if self.state_digest() == anchors[window]:
                                        break
                            else:
                                anchors[window] = self.state_digest()
                                digest_step = self._steps
                    if status is not XlmStatus.IGNORED:
                        self.write_value(cell, outcome.value)
                    if stack_record:
                        text = (frame.desc + ' ' + text).strip()
                    if self.indent_current_line:
                        previous_indent = self.indent_level
                        self.indent_current_line = False
                    yield from buffer
                    buffer.clear()
                    if status is not XlmStatus.IGNORED:
                        yield XlmStep(
                            cursor.sheet,
                            cursor.row,
                            cursor.col,
                            status,
                            text,
                            previous_indent,
                            self._step_severity(node),
                        )
                    if caller is not None:
                        if outcome.halts:
                            self._halted = True
                        if caller.value is not None or self._halted:
                            return
                    elif self._halted:
                        self._halted = False
                        break
                    self.check_deadline()
                    if outcome.jump is not None:
                        cursor = outcome.jump
                        self.arrival = (
                            XlmArrival.JUMP if outcome.arrival is None else outcome.arrival
                        )
                    elif status in _FALL_THROUGH:
                        cursor = self.next_formula_cell(cursor)
                        self.arrival = XlmArrival.FALL
                    else:
                        break
                    node = None
                    stack_record = False
        except _Exhausted as exhausted:
            yield from buffer
            if caller is not None:
                raise
            yield self._error_step(cursor or anchor, str(exhausted))
        except Exception as error:
            yield from buffer
            yield self._error_step(cursor or anchor, F'{type(error).__name__}: {error}')
        finally:
            self._buffers.pop()
            self.branch_stack = branches

    def _subroutine(self, reference: XlmReference, spelled: str) -> XlmValue:
        """
        The value a macro call inside an expression computes: the subroutine runs from where
        the call names it until it returns, its steps join the trace ahead of the step that
        called it, and the value it returns is the value of the call. Calls and loops the
        subroutine leaves open end with it. A subroutine that never returns — it halts, runs out
        of formulas, or nests too deeply — leaves the call unfinished.
        """
        anchor = self.anchor(reference)
        if anchor is None or self._depth >= _SUBROUTINE_DEPTH_LIMIT:
            return XlmValue(value=spelled, partial=True)
        caller = XlmReturnSlot()
        indent = self.indent_level
        calls = len(self.call_stack)
        loops = len(self.while_stack)
        steps: list[XlmStep] = []
        self.call_stack.append(caller)
        self._depth += 1
        try:
            steps.extend(self._run_entry(anchor, caller))
        finally:
            self._depth -= 1
            self.indent_level = indent
            del self.call_stack[calls:]
            del self.while_stack[loops:]
            if self._buffers:
                self._buffers[-1].extend(steps)
        if caller.value is None:
            return XlmValue(value=spelled, partial=True)
        return _held(caller.value)

    def _expired(self) -> bool:
        return self.deadline is not None and time.monotonic() > self.deadline

    def check_deadline(self) -> None:
        """
        Raise the exhaustion of the run's deadline, for the walks a command takes inside one
        step, so that a walk over a rectangle the workbook barely fills still answers to the
        deadline of the run.
        """
        if self._expired():
            raise _Exhausted('the run exceeded its timeout')

    def _execute(self, node, cursor: XlmCursor) -> XlmOutcome:
        """
        The outcome of the step that runs a node as the whole formula of its cell. A call runs
        as a command, and any other expression is evaluated. A step that starts inside the body
        of a loop the engine skips is ignored like every command there, even when the expression
        holds the loop end that stops the skipping.
        """
        if isinstance(node, XlFunctionCall):
            return self.call(node, cursor)
        skipping = self.ignore_processing
        value = evaluate_expression(self, node, cursor)
        if skipping:
            return XlmOutcome(value=value, status=XlmStatus.IGNORED)
        return XlmOutcome(value=value)

    def _stored(self, cell: XlmCell, reference: XlmReference) -> XlmValue:
        if cell.value is None:
            return _blank(reference)
        return XlmValue(
            value=cell.value,
            date=cell.kind is CellKind.DATE,
            reference=reference,
        )

    def _step_severity(self, node) -> XlmSeverity:
        if isinstance(node, XlFunctionCall) and isinstance(node.callee, str):
            return severity(node.callee.strip('"'))
        return XlmSeverity.JUMP

    def _error_step(self, cursor: XlmCursor, message: str) -> XlmStep:
        return XlmStep(
            cursor.sheet,
            cursor.row,
            cursor.col,
            XlmStatus.Error,
            message,
            self.indent_level,
            XlmSeverity.JUMP,
        )

    def _unknown_command(self, name: str, call: XlFunctionCall, cursor: XlmCursor) -> XlmOutcome:
        """
        The fallback for a command no handler answers: the call spelled unevaluated, with
        every argument evaluated for the side effects it may have had and a missing argument
        spelling the empty slot the call wrote.
        """
        arguments = [
            evaluate_expression(self, argument, cursor).text or ''
            for argument in call.arguments
        ]
        text = F'={name}({",".join(arguments)})'
        return XlmOutcome(value=XlmValue(value=text, partial=True))

    def _call_cell(
        self,
        reference: XlmReference,
        cursor: XlmCursor,
        call: XlFunctionCall,
        nested: bool,
    ) -> XlmOutcome:
        spelled = synthesize_formula(call)
        if reference.sheet is None:
            reference = XlmReference(cursor.sheet, reference.row, reference.col)
        if self.view.macrosheet(reference.sheet) is None:
            return XlmOutcome(value=XlmValue(value=0, text=spelled), status=XlmStatus.Error)
        if nested:
            return XlmOutcome(value=self._subroutine(reference, spelled))
        self.call_stack.append(cursor)
        return XlmOutcome(
            value=XlmValue(value=0, text=spelled),
            jump=self.anchor(reference),
            arrival=XlmArrival.RESUME,
        )

    def _resolve_entry(self, name: str, sheet: str | None, cursor: XlmCursor):
        if sheet is not None:
            return self.view.names.resolve(name, self.view.sheet_index(sheet))
        return self.view.names.resolve(name, self.view.sheet_index(cursor.sheet))

    def _name_reference(self, node: XlDefinedName, cursor: XlmCursor) -> XlmReference | None:
        entry = self._resolve_entry(node.name, node.sheet, cursor)
        if entry is None:
            return None
        return self._reference_of(entry.formula, cursor)

    def _reference_of(self, formula, cursor: XlmCursor) -> XlmReference | None:
        if isinstance(formula, (XlA1Reference, XlR1C1Reference)):
            return resolve_reference(formula, cursor)
        return None

    def _spelled_reference(self, text: str, cursor: XlmCursor) -> XlmReference | None:
        return self._local(self._reference_of(parse_formula(text), cursor), cursor)

    def _local(self, reference: XlmReference | None, cursor: XlmCursor) -> XlmReference | None:
        if reference is None:
            return None
        if reference.sheet is not None:
            return reference
        return XlmReference(cursor.sheet, reference.row, reference.col)

    def _find_cell(self, sheet: str, row: int, col: int) -> XlmCell | None:
        cell = self._index.find(sheet, row, col)
        if cell is not None:
            return cell
        worksheet = self.view.worksheet(sheet)
        if worksheet is not None:
            return worksheet.get((row, col))
        return None

Subclasses

  • refinery.lib.scripts.xlm.deobfuscation.fold._StaticEngine

Instance variables

var ignore_processing

Whether the engine skips the body of a loop: true while any open loop does not hold.

Expand source code Browse git
@property
def ignore_processing(self) -> bool:
    """
    Whether the engine skips the body of a loop: true while any open loop does not hold.
    """
    return any(not loop.holds for loop in self.while_stack)

Methods

def run(self, start_point='')

The trace of running the program: one step per executed cell. The entry points are the defined names that fuzzy-spell auto_open or auto_close; without one, the start point a caller named is the only entry. The run of an entry that exhausts its step budget ends with a step that says so, and a run that exhausts its deadline ends the same way and runs no further entry. An engine without a timeout of its own keeps the deadline it was given.

Expand source code Browse git
def run(self, start_point: str = '') -> Iterator[XlmStep]:
    """
    The trace of running the program: one step per executed cell. The entry points are the
    defined names that fuzzy-spell `auto_open` or `auto_close`; without one, the start point
    a caller named is the only entry. The run of an entry that exhausts its step budget ends
    with a step that says so, and a run that exhausts its deadline ends the same way and
    runs no further entry. An engine without a timeout of its own keeps the deadline it was
    given.
    """
    self.start_point = start_point
    if self.timeout > 0:
        self.deadline = time.monotonic() + self.timeout
    return self._run()
def trial(self, day)

A fresh engine for one trial run of the day guess: it shares this engine's view and the index over its cells — the cells, values, and names the runs so far have written — and the deadline of this engine's run, and answers the given day, but starts with no files, no memory, and no registered names of its own.

Expand source code Browse git
def trial(self, day: int) -> XlmEngine:
    """
    A fresh engine for one trial run of the day guess: it shares this engine's view and the
    index over its cells — the cells, values, and names the runs so far have written — and
    the deadline of this engine's run, and answers the given day, but starts with no files,
    no memory, and no registered names of its own.
    """
    trial = XlmEngine(
        self.view,
        self.output_level,
        day,
        0,
        self.max_steps,
        cells=self._index,
    )
    trial.deadline = self.deadline
    return trial
def read_reference(self, reference, cursor)

The value a cell address holds. The sheet the reference fails to name is the sheet the cursor sits on. A cell the run left a value in — by a step that executed it, a value command, or a return — holds that value. Any other formula cell is evaluated with itself as the cursor its relative references resolve against, unless its evaluation is already under way, the reads of the running evaluation exhausted their budget, or it nests too deeply; such a cell, and a cell that holds only a value, reads as the value it was stored with, with the date flag a date cell carries. An address the workbook does not hold is blank.

Expand source code Browse git
def read_reference(self, reference: XlmReference, cursor: XlmCursor) -> XlmValue:
    """
    The value a cell address holds. The sheet the reference fails to name is the sheet the
    cursor sits on. A cell the run left a value in — by a step that executed it, a value
    command, or a return — holds that value. Any other formula cell is evaluated with itself
    as the cursor its relative references resolve against, unless its evaluation is already
    under way, the reads of the running evaluation exhausted their budget, or it nests too
    deeply; such a cell, and a cell that holds only a value, reads as the value it was
    stored with, with the date flag a date cell carries. An address the workbook does not
    hold is blank.
    """
    sheet = reference.sheet or cursor.sheet
    reference = XlmReference(sheet, reference.row, reference.col)
    cell = self._find_cell(sheet, reference.row, reference.col)
    if cell is None:
        if reference in self.failed_writes:
            address = F'{column_letters(reference.col)}{reference.row}'
            return XlmValue(value=address, partial=True)
        return _blank(reference)
    held = self._index.held.get(id(cell))
    if held is not None:
        return dataclasses.replace(held, reference=reference)
    if not self._evaluating:
        self._reads = 0
    if (
        cell.formula is not None
        and id(cell) not in self._evaluating
        and self._reads < _READ_BUDGET
    ):
        self.check_deadline()
        self._reads += 1
        self._evaluating.add(id(cell))
        try:
            value = evaluate_expression(
                self,
                cell.formula,
                XlmCursor(sheet, cell.row, cell.col),
            )
        except RecursionError:
            return self._stored(cell, reference)
        finally:
            self._evaluating.discard(id(cell))
        value.reference = reference
        return value
    return self._stored(cell, reference)
def resolve_name(self, node, cursor)

The value a defined name stands for: the formula the entry for it holds, evaluated through the evaluator with the scope of the reading cell. A name the workbook does not define, or one whose formula resolves no further, answers its own spelling; a formula that reads the name it defines, or nests too deeply, answers the spelling of the formula.

Expand source code Browse git
def resolve_name(self, node: XlDefinedName, cursor: XlmCursor) -> XlmValue:
    """
    The value a defined name stands for: the formula the entry for it holds, evaluated
    through the evaluator with the scope of the reading cell. A name the workbook does not
    define, or one whose formula resolves no further, answers its own spelling; a formula
    that reads the name it defines, or nests too deeply, answers the spelling of the formula.
    """
    entry = self._resolve_entry(node.name, node.sheet, cursor)
    if entry is None or entry.formula is None:
        return XlmValue(value=node.name, text=node.name)
    key = (entry.name.lower(), entry.sheet)
    if key in self._resolving:
        return XlmValue(value=synthesize_formula(entry.formula))
    self._resolving.add(key)
    try:
        return evaluate_expression(self, entry.formula, cursor)
    except RecursionError:
        return XlmValue(value=synthesize_formula(entry.formula))
    finally:
        self._resolving.discard(key)
def call(self, call, cursor, nested=False)

The outcome of one function call. While the engine skips the body of a loop, every call but the heads and ends of loops is ignored. A call whose callee is itself a call answers what the inner call computed, spelled as the outer one; a registered alias dispatches under the name it was registered for; a callee that names a cell — directly or through a defined name — calls the macro there. A call is nested when an expression reads it instead of a step running it as the whole formula of its cell: a nested macro call runs its subroutine to the value it returns, and a nested command answers through the handler an expression reads it by, where it has one. Once the deadline of the run passed, no call answers.

Expand source code Browse git
def call(self, call: XlFunctionCall, cursor: XlmCursor, nested: bool = False) -> XlmOutcome:
    """
    The outcome of one function call. While the engine skips the body of a loop, every call
    but the heads and ends of loops is ignored. A call whose callee is itself a call answers
    what the inner call computed, spelled as the outer one; a registered alias dispatches
    under the name it was registered for; a callee that names a cell — directly or through
    a defined name — calls the macro there. A call is nested when an expression reads it
    instead of a step running it as the whole formula of its cell: a nested macro call runs
    its subroutine to the value it returns, and a nested command answers through the
    handler an expression reads it by, where it has one. Once the deadline of the run
    passed, no call answers.
    """
    self.check_deadline()
    name = callee_name(call)
    if self.ignore_processing and name not in _LOOP_COMMANDS:
        return XlmOutcome(
            value=XlmValue(value=0, text='', partial=True),
            status=XlmStatus.IGNORED,
        )
    if isinstance(call.callee, XlFunctionCall):
        inner = self.call(call.callee, cursor, nested)
        spelled = synthesize_formula(call)
        if inner.value.partial:
            return XlmOutcome(value=XlmValue(value=spelled, partial=True))
        inner.value.text = spelled
        return inner
    target = self.aliases.get(name)
    if target is not None:
        return self.dispatch(target, call, cursor, nested)
    if isinstance(call.callee, (XlA1Reference, XlR1C1Reference)):
        reference = resolve_reference(call.callee, cursor)
        return self._call_cell(reference, cursor, call, nested)
    if isinstance(call.callee, XlDefinedName):
        reference = self._name_reference(call.callee, cursor)
        if reference is not None:
            return self._call_cell(reference, cursor, call, nested)
    else:
        entry = self._resolve_entry(name, None, cursor)
        reference = self._reference_of(entry.formula if entry else None, cursor)
        if reference is not None:
            return self._call_cell(reference, cursor, call, nested)
    return self.dispatch(name, call, cursor, nested)
def dispatch(self, name, call, cursor, nested=False)

The outcome of a call under a name no alias rewrote. A nested call answers through the handler an expression reads its command by, where the command has one; every other command the handler table holds answers through its handler, and a command without one answers the spelled fallback.

Expand source code Browse git
def dispatch(
    self,
    name: str,
    call: XlFunctionCall,
    cursor: XlmCursor,
    nested: bool = False,
) -> XlmOutcome:
    """
    The outcome of a call under a name no alias rewrote. A nested call answers through the
    handler an expression reads its command by, where the command has one; every other
    command the handler table holds answers through its handler, and a command without one
    answers the spelled fallback.
    """
    handler = EXPRESSION_HANDLERS.get(name) if nested else None
    if handler is None:
        handler = HANDLERS.get(name)
    if handler is not None:
        return handler(self, call, cursor)
    return self._unknown_command(name, call, cursor)
def return_value(self, value)

Hand the value a RETURN computes to the macro call it returns from: a call that is the whole formula of its cell leaves the value in that cell and continues below it, and a call inside an expression takes the value as its own, which ends the run of the subroutine. A return without a call to return from continues below itself. The answer is the cursor to continue at and whether the run of a subroutine ended.

Expand source code Browse git
def return_value(self, value: XlmValue) -> tuple[XlmCursor | None, bool]:
    """
    Hand the value a `RETURN` computes to the macro call it returns from: a call that is the
    whole formula of its cell leaves the value in that cell and continues below it, and a
    call inside an expression takes the value as its own, which ends the run of the
    subroutine. A return without a call to return from continues below itself. The answer
    is the cursor to continue at and whether the run of a subroutine ended.
    """
    if not self.call_stack:
        return None, False
    caller = self.call_stack.pop()
    if isinstance(caller, XlmReturnSlot):
        caller.value = value
        return None, True
    cell = self._find_cell(caller.sheet, caller.row, caller.col)
    if cell is not None:
        self.write_value(cell, value)
    return self.next_formula_cell(caller), False
def anchor(self, reference)

The cursor the program continues at from an address: the first formula cell at or below it on the same column of the same macrosheet, or None when the sheet is no macrosheet, is not named at all, or holds no formula cell within the row bound.

Expand source code Browse git
def anchor(self, reference: XlmReference) -> XlmCursor | None:
    """
    The cursor the program continues at from an address: the first formula cell at or below
    it on the same column of the same macrosheet, or `None` when the sheet is no macrosheet,
    is not named at all, or holds no formula cell within the row bound.
    """
    sheet = reference.sheet
    if sheet is None or self.view.macrosheet(sheet) is None:
        return None
    row = self._index.fall_through(sheet, reference.col, reference.row)
    if row is None:
        return None
    return XlmCursor(sheet, row, reference.col)
def next_formula_cell(self, cursor)

The cursor the row fall-through moves to: the first formula cell below the given one on the same column, bounded by the row bound.

Expand source code Browse git
def next_formula_cell(self, cursor: XlmCursor) -> XlmCursor | None:
    """
    The cursor the row fall-through moves to: the first formula cell below the given one on
    the same column, bounded by the row bound.
    """
    row = self._index.fall_through(cursor.sheet, cursor.col, cursor.row + 1)
    if row is None:
        return None
    return XlmCursor(cursor.sheet, row, cursor.col)
def column_blocks(self, cursor)

The block structure of the column of the cursor: every marker row mapped to the block IF it belongs to, paired over the live formula rows of the column, so that cells the program wrote at run time take part in the pairing.

Expand source code Browse git
def column_blocks(self, cursor: XlmCursor) -> dict[int, XlmBlock]:
    """
    The block structure of the column of the cursor: every marker row mapped to the block
    `IF` it belongs to, paired over the live formula rows of the column, so that cells the
    program wrote at run time take part in the pairing.
    """
    sheet = cursor.sheet.lower()
    markers: list[tuple[int, str]] = []
    for row in self._index.formula_rows.get(sheet, {}).get(cursor.col, ()):
        cell = self._index.find(sheet, row, cursor.col)
        if cell is None or cell.formula is None:
            continue
        marker = marker_of(cell.formula)
        if marker is not None:
            markers.append((row, marker))
    return pair_blocks(markers)
def argument_reference(self, call, index, cursor)

The cell address one argument of a call names, as XlmEngine.node_reference() reads it.

Expand source code Browse git
def argument_reference(
    self,
    call: XlFunctionCall,
    index: int,
    cursor: XlmCursor,
) -> XlmReference | None:
    """
    The cell address one argument of a call names, as `XlmEngine.node_reference` reads it.
    """
    if index >= len(call.arguments):
        return None
    return self.node_reference(call.arguments[index], cursor)
def node_reference(self, node, cursor)

The cell address a node names, with the sheet of a local reference filled in from the cursor: a reference resolves against it, a defined name through the reference its entry holds, and a string literal parses as a formula. Any other expression evaluates to the reference its value carries, or to the address its text spells.

Expand source code Browse git
def node_reference(self, node, cursor: XlmCursor) -> XlmReference | None:
    """
    The cell address a node names, with the sheet of a local reference filled in from the
    cursor: a reference resolves against it, a defined name through the reference its entry
    holds, and a string literal parses as a formula. Any other expression evaluates to the
    reference its value carries, or to the address its text spells.
    """
    if isinstance(node, (XlA1Reference, XlR1C1Reference)):
        return self._local(resolve_reference(node, cursor), cursor)
    if isinstance(node, XlDefinedName):
        reference = self._name_reference(node, cursor)
        if reference is not None:
            return self._local(reference, cursor)
    if isinstance(node, XlString):
        return self._spelled_reference(node.value, cursor)
    value = evaluate_expression(self, node, cursor)
    if value.reference is not None:
        return self._local(value.reference, cursor)
    if value.partial or not isinstance(value.value, str):
        return None
    return self._spelled_reference(value.value, cursor)
def range_corners(self, node, cursor)

The corners a range argument names, or None when the argument is no range.

Expand source code Browse git
def range_corners(self, node, cursor: XlmCursor) -> tuple[XlmReference, XlmReference] | None:
    """
    The corners a range argument names, or `None` when the argument is no range.
    """
    if not isinstance(node, XlBinaryExpression) or node.operator is not XlBinaryOperator.RANGE:
        return None
    corners = []
    for side in (node.left, node.right):
        reference = self.node_reference(side, cursor)
        if reference is None:
            return None
        corners.append(reference)
    return corners[0], corners[1]
def range_cells(self, corners)

The addresses of the cells a range spans that the workbook holds, in row-major order: the cells the index holds on the sheet of the range, the cells of that sheet where it is a worksheet, and the failed writes inside it, which read as the values the program left unfinished. The walk counts the cells the workbook holds, not the coordinates the rectangle spans.

Expand source code Browse git
def range_cells(self, corners: tuple[XlmReference, XlmReference]) -> Iterator[XlmReference]:
    """
    The addresses of the cells a range spans that the workbook holds, in row-major order:
    the cells the index holds on the sheet of the range, the cells of that sheet where it
    is a worksheet, and the failed writes inside it, which read as the values the program
    left unfinished. The walk counts the cells the workbook holds, not the coordinates the
    rectangle spans.
    """
    first, last = corners
    sheet = first.sheet
    if sheet is None:
        return
    key = sheet.lower()
    row_min = min(first.row, last.row)
    row_max = max(first.row, last.row)
    col_min = min(first.col, last.col)
    col_max = max(first.col, last.col)
    held = self._index.cells.get(key, {})
    worksheet = self.view.worksheet(sheet)
    coordinates = set(held) | set(worksheet or ())
    coordinates.update(
        (reference.row, reference.col)
        for reference in self.failed_writes
        if (reference.sheet or '').lower() == key
    )
    inside = (
        coordinate
        for coordinate in coordinates
        if row_min <= coordinate[0] <= row_max
        and col_min <= coordinate[1] <= col_max
    )
    for row, col in sorted(inside):
        yield XlmReference(sheet, row, col)
def snapshot(self)

The state a false branch of a partial IF rolls back to: the position of the journal, and the state of the run the journal does not hold.

Expand source code Browse git
def snapshot(self) -> XlmSnapshot:
    """
    The state a false branch of a partial `IF` rolls back to: the position of the journal,
    and the state of the run the journal does not hold.
    """
    return XlmSnapshot(
        len(self._journal),
        tuple(self.call_stack),
        tuple(loop.copy() for loop in self.while_stack),
        self.active_cell,
    )
def state_digest(self)

The digest of the whole state a run can observe: the cells its index holds, with the value each holds and the formula it spells, the entries of the name table, the registered aliases, the failed-write marks, the content of every open file, every region of memory, and the cell the program selected. A run that returns to the digest it anchored changes nothing the program can see.

Expand source code Browse git
def state_digest(self) -> bytes:
    """
    The digest of the whole state a run can observe: the cells its index holds, with the
    value each holds and the formula it spells, the entries of the name table, the
    registered aliases, the failed-write marks, the content of every open file, every
    region of memory, and the cell the program selected. A run that returns to the digest
    it anchored changes nothing the program can see.
    """
    parts: list[str] = []
    for sheet in sorted(self._index.cells):
        cells = self._index.cells[sheet]
        for row, col in sorted(cells):
            cell = cells[(row, col)]
            held = self._index.held.get(id(cell))
            value = held.text if held is not None else repr(cell.value)
            formula = cell.formula
            spelling = synthesize_formula(formula) if formula is not None else ''
            parts.append(F'{sheet}!{row}!{col}={value}|{spelling}')
    for entry in self.view.names.entries():
        formula = entry.formula
        spelling = synthesize_formula(formula) if formula is not None else ''
        parts.append(F'name:{entry.name.lower()}@{entry.sheet}={spelling}')
    for name in sorted(self.aliases):
        parts.append(F'alias:{name}={self.aliases[name]}')
    for reference in sorted(self.failed_writes):
        parts.append(F'failed:{reference.a1()}')
    for name, content in self.files.snapshot():
        parts.append(F'file:{name}={content}')
    for base, data in self.memory.snapshot():
        parts.append(F'memory:{base}={len(data)}:{data.hex()}')
    parts.append(F'active:{self.active_cell}')
    return hashlib.sha256('\n'.join(parts).encode()).digest()
def rollback(self, snapshot)

Return to a snapshot: every change the journal holds past its position is undone — cells, names, aliases, files, memory regions, and failed-write marks return to what they carried, and cells the writes created leave their sheet again — and the call stack, the open loops, and the selected cell are what they were.

Expand source code Browse git
def rollback(self, snapshot: XlmSnapshot) -> None:
    """
    Return to a snapshot: every change the journal holds past its position is undone —
    cells, names, aliases, files, memory regions, and failed-write marks return to what they
    carried, and cells the writes created leave their sheet again — and the call stack, the
    open loops, and the selected cell are what they were.
    """
    while len(self._journal) > snapshot.journal:
        self._journal.pop().undo(self)
    self.call_stack = list(snapshot.call_stack)
    self.while_stack = [loop.copy() for loop in snapshot.loops]
    self.active_cell = snapshot.active_cell
def write_value(self, cell, value)

Leave the value a step computed — or a macro call returned — in the cell it belongs to, undoable by a branch that rolls back: a later read of the cell answers that value instead of evaluating the formula of the cell again.

Expand source code Browse git
def write_value(self, cell: XlmCell, value: XlmValue) -> None:
    """
    Leave the value a step computed — or a macro call returned — in the cell it belongs to,
    undoable by a branch that rolls back: a later read of the cell answers that value
    instead of evaluating the formula of the cell again.
    """
    self._journal.append(_Write(
        self._index.sheet_of.get(id(cell), ''),
        cell,
        cell.value,
        cell.formula,
        self._index.held.get(id(cell)),
        False,
    ))
    held = _held(value)
    set_value(cell, 'value', held.value)
    self._index.hold(cell, held)
def write_cell(self, reference, value, cursor, value_only=False)

Write a value into the macrosheet cell an address names, as the mutation commands do, undoable by a branch that rolls back; a cell that does not exist is created. A value-only write changes the value the cell holds and leaves its formula in place. Any other write enters the value the way a typed entry does: a text that starts with = installs a formula, which a later read of the cell evaluates, and anything else replaces the formula of the cell by a constant. An address off every macrosheet is not written.

Expand source code Browse git
def write_cell(
    self,
    reference: XlmReference,
    value: XlmValue,
    cursor: XlmCursor,
    value_only: bool = False,
) -> None:
    """
    Write a value into the macrosheet cell an address names, as the mutation commands do,
    undoable by a branch that rolls back; a cell that does not exist is created. A
    value-only write changes the value the cell holds and leaves its formula in place. Any
    other write enters the value the way a typed entry does: a text that starts with `=`
    installs a formula, which a later read of the cell evaluates, and anything else replaces
    the formula of the cell by a constant. An address off every macrosheet is not written.
    """
    sheet = reference.sheet or cursor.sheet
    macrosheet = self.view.macrosheet(sheet)
    if macrosheet is None:
        return
    key = macrosheet.name.lower()
    cell = self._index.find(key, reference.row, reference.col)
    created = cell is None
    if cell is None:
        cell = XlmCell(row=reference.row, col=reference.col)
        self._index.insert(key, cell)
        if macrosheet.body:
            edit = BodyEdit(macrosheet)
            edit.splice(macrosheet.body[-1], [macrosheet.body[-1], cell])
            edit.apply()
        else:
            set_body(macrosheet, [cell])
    self._journal.append(_Write(
        key,
        cell,
        cell.value,
        cell.formula,
        self._index.held.get(id(cell)),
        created,
    ))
    held: XlmValue | None = _held(value)
    formula = cell.formula
    if not value_only:
        formula = None
        if isinstance(value.value, str) and value.value.startswith('='):
            formula = parse_formula(value.value)
            held = None
    self._index.update_formula_rows(key, cell, cell.formula is not None, formula is not None)
    set_value(cell, 'value', value.value if held is None else held.value)
    set_child(cell, 'formula', formula)
    self._index.hold(cell, held)
def define_name(self, entry)

Define the name an entry carries, as the name commands do, undoable by a branch that rolls back.

Expand source code Browse git
def define_name(self, entry: XlmNameEntry) -> None:
    """
    Define the name an entry carries, as the name commands do, undoable by a branch that
    rolls back.
    """
    self._journal.append(_NameDefine(self.view.names.entries()))
    self.view.names.define(entry)
def assign_name(self, name, formula, cursor)

Define a name as SET.NAME and the FOR.CELL loop do, undoable by a branch that rolls back: the definition replaces the entry a read on the sheet of the cursor resolves the name to, so that the sheet reads back what it assigned, and a name the sheet resolves to no entry is defined for the whole workbook. A name assigned no formula reads as one the workbook does not define.

Expand source code Browse git
def assign_name(self, name: str, formula, cursor: XlmCursor) -> None:
    """
    Define a name as `SET.NAME` and the `FOR.CELL` loop do, undoable by a branch that rolls
    back: the definition replaces the entry a read on the sheet of the cursor resolves the
    name to, so that the sheet reads back what it assigned, and a name the sheet resolves to
    no entry is defined for the whole workbook. A name assigned no formula reads as one the
    workbook does not define.
    """
    entry = self.view.names.resolve(name, self.view.sheet_index(cursor.sheet))
    sheet = None if entry is None else entry.sheet
    self.define_name(XlmNameEntry(name=name, sheet=sheet, formula=formula))
def register_alias(self, name, target)

Register the target a command name dispatches to, as REGISTER does, undoable by a branch that rolls back.

Expand source code Browse git
def register_alias(self, name: str, target: str) -> None:
    """
    Register the target a command name dispatches to, as `REGISTER` does, undoable by a
    branch that rolls back.
    """
    self._journal.append(_AliasRegistration(name, self.aliases.get(name)))
    self.aliases[name] = target
def open_file(self, name, access='1')

Open the name as a file, as FOPEN does, undoable by a branch that rolls back; a name that already answers a file changes nothing.

Expand source code Browse git
def open_file(self, name: str, access: str = '1') -> None:
    """
    Open the name as a file, as `FOPEN` does, undoable by a branch that rolls back; a name
    that already answers a file changes nothing.
    """
    if self.files.opened(name):
        return
    self._journal.append(_FileOpen(name))
    self.files.open(name, access)
def write_file(self, name, text)

Append to a file the program opened, as FWRITE does, undoable by a branch that rolls back, reporting whether the file took the write.

Expand source code Browse git
def write_file(self, name: str, text: str) -> bool:
    """
    Append to a file the program opened, as `FWRITE` does, undoable by a branch that rolls
    back, reporting whether the file took the write.
    """
    length = self.files.size(name)
    if length is None:
        return False
    self._journal.append(_FileWrite(name, length))
    self.files.write(name, text)
    return True
def allocate_memory(self, base, size)

Reserve a region of memory, as Kernel32.VirtualAlloc does, undoable by a branch that rolls back.

Expand source code Browse git
def allocate_memory(self, base: int, size: int) -> int:
    """
    Reserve a region of memory, as `Kernel32.VirtualAlloc` does, undoable by a branch that
    rolls back.
    """
    self._journal.append(_RegionAlloc())
    return self.memory.allocate(base, size)
def write_memory(self, base, data, size)

Write bytes into a region of memory, as the Kernel32 write commands do, undoable by a branch that rolls back, reporting whether a region took the whole write.

Expand source code Browse git
def write_memory(self, base: int, data: bytes, size: int) -> bool:
    """
    Write bytes into a region of memory, as the Kernel32 write commands do, undoable by a
    branch that rolls back, reporting whether a region took the whole write.
    """
    before = self.memory.peek(base, size)
    if before is None:
        return False
    self.memory.write(base, data, size)
    self._journal.append(_MemoryWrite(base, before))
    return True
def mark_failed(self, reference)

Mark an address as the destination of a write that never finished, undoable by a branch that rolls back.

Expand source code Browse git
def mark_failed(self, reference: XlmReference) -> None:
    """
    Mark an address as the destination of a write that never finished, undoable by a
    branch that rolls back.
    """
    if reference in self.failed_writes:
        return
    self._journal.append(_FailedMark(reference, True))
    self.failed_writes.add(reference)
def unmark_failed(self, reference)

Drop the failed-write mark of an address, undoable by a branch that rolls back.

Expand source code Browse git
def unmark_failed(self, reference: XlmReference) -> None:
    """
    Drop the failed-write mark of an address, undoable by a branch that rolls back.
    """
    if reference not in self.failed_writes:
        return
    self._journal.append(_FailedMark(reference, False))
    self.failed_writes.discard(reference)
def check_deadline(self)

Raise the exhaustion of the run's deadline, for the walks a command takes inside one step, so that a walk over a rectangle the workbook barely fills still answers to the deadline of the run.

Expand source code Browse git
def check_deadline(self) -> None:
    """
    Raise the exhaustion of the run's deadline, for the walks a command takes inside one
    step, so that a walk over a rectangle the workbook barely fills still answers to the
    deadline of the run.
    """
    if self._expired():
        raise _Exhausted('the run exceeded its timeout')
class XlmEnvironment

The answers the environment-reading macro commands get from an emulated Excel installation: the workspace and window tables and the two document answers.

Expand source code Browse git
class XlmEnvironment:
    """
    The answers the environment-reading macro commands get from an emulated Excel
    installation: the workspace and window tables and the two document answers.
    """

    def workspace(self, number: int) -> str | None:
        """
        The workspace-table answer, or `None` for a number the table holds none for; a call
        the table does not answer reports as fully evaluated to no value.
        """
        return _WORKSPACE.get(number)

    def window(self, number: int) -> str | int | None:
        """
        The window-table answer, which is also the text a GET.WINDOW trace line shows: for
        numbers 1 and 30 the table holds the placeholder `[Book1]Sheet1` even though the value
        the command returns names the workbook and the sheet it was called from.
        """
        return _WINDOW.get(number)

    def window_value(self, number: int, workbook_name: str, sheet_name: str) -> str | int | None:
        """
        The value the GET.WINDOW command returns: the window-table answer, except that numbers
        1 and 30 name the given workbook and sheet.
        """
        if number in (1, 30):
            return F'[{workbook_name}]{sheet_name}'
        return _WINDOW.get(number)

    def document(self, number: int, workbook_name: str, sheet_name: str) -> str | None:
        """
        The document-table answer, which holds two entries: number 76 names the workbook and
        the given sheet, number 88 names the workbook, and every other number yields a partial
        evaluation, which `None` stands for here.
        """
        if number == 76:
            return F'[{workbook_name}]{sheet_name}'
        if number == 88:
            return workbook_name
        return None

    def cell_info(self, sheet_name: str, row: int, col: int, number: int) -> str | None:
        """
        Not implemented: the style surface a real answer reads — row heights, font sizes and
        colors, alignment — does not exist, so every GET.CELL call yields nothing.
        """
        return None

Methods

def workspace(self, number)

The workspace-table answer, or None for a number the table holds none for; a call the table does not answer reports as fully evaluated to no value.

Expand source code Browse git
def workspace(self, number: int) -> str | None:
    """
    The workspace-table answer, or `None` for a number the table holds none for; a call
    the table does not answer reports as fully evaluated to no value.
    """
    return _WORKSPACE.get(number)
def window(self, number)

The window-table answer, which is also the text a GET.WINDOW trace line shows: for numbers 1 and 30 the table holds the placeholder [Book1]Sheet1 even though the value the command returns names the workbook and the sheet it was called from.

Expand source code Browse git
def window(self, number: int) -> str | int | None:
    """
    The window-table answer, which is also the text a GET.WINDOW trace line shows: for
    numbers 1 and 30 the table holds the placeholder `[Book1]Sheet1` even though the value
    the command returns names the workbook and the sheet it was called from.
    """
    return _WINDOW.get(number)
def window_value(self, number, workbook_name, sheet_name)

The value the GET.WINDOW command returns: the window-table answer, except that numbers 1 and 30 name the given workbook and sheet.

Expand source code Browse git
def window_value(self, number: int, workbook_name: str, sheet_name: str) -> str | int | None:
    """
    The value the GET.WINDOW command returns: the window-table answer, except that numbers
    1 and 30 name the given workbook and sheet.
    """
    if number in (1, 30):
        return F'[{workbook_name}]{sheet_name}'
    return _WINDOW.get(number)
def document(self, number, workbook_name, sheet_name)

The document-table answer, which holds two entries: number 76 names the workbook and the given sheet, number 88 names the workbook, and every other number yields a partial evaluation, which None stands for here.

Expand source code Browse git
def document(self, number: int, workbook_name: str, sheet_name: str) -> str | None:
    """
    The document-table answer, which holds two entries: number 76 names the workbook and
    the given sheet, number 88 names the workbook, and every other number yields a partial
    evaluation, which `None` stands for here.
    """
    if number == 76:
        return F'[{workbook_name}]{sheet_name}'
    if number == 88:
        return workbook_name
    return None
def cell_info(self, sheet_name, row, col, number)

Not implemented: the style surface a real answer reads — row heights, font sizes and colors, alignment — does not exist, so every GET.CELL call yields nothing.

Expand source code Browse git
def cell_info(self, sheet_name: str, row: int, col: int, number: int) -> str | None:
    """
    Not implemented: the style surface a real answer reads — row heights, font sizes and
    colors, alignment — does not exist, so every GET.CELL call yields nothing.
    """
    return None
class XlmMacrosheet (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, body=<factory>, name='', kind=SheetKind.MACROSHEET)

A macrosheet as a script of cells in document order. The body field is inherited from Script and holds XlmCell statements; a body typed as a list of cells would fail the variance check the framework's field classification runs.

Expand source code Browse git
@dataclass(repr=False, eq=False)
class XlmMacrosheet(Script):
    """
    A macrosheet as a script of cells in document order. The `body` field is inherited from
    `refinery.lib.scripts.Script` and holds `XlmCell` statements; a body typed as a list of
    cells would fail the variance check the framework's field classification runs.
    """

    name: str = ''
    kind: SheetKind = SheetKind.MACROSHEET

    def cell(self, row: int, col: int) -> XlmCell | None:
        """
        The cell at the one-based `row` and `col`, or `None` where the sheet holds none.
        """
        for item in self.body:
            if isinstance(item, XlmCell) and item.row == row and item.col == col:
                return item
        return None

    def listing(self, sort_formulas: bool = False) -> list[XlmCell]:
        """
        The cells in the order the extract listing shows them: the cells that hold a formula
        first and the cells that hold only a value after, each group in document order — or,
        when sorted, the formulas by column letters and then by row.
        """
        cells = [cell for cell in self.body if isinstance(cell, XlmCell)]
        formulas = [cell for cell in cells if cell.formula is not None]
        if sort_formulas:
            formulas.sort(key=lambda cell: (column_letters(cell.col), cell.row))
        return formulas + [cell for cell in cells if cell.formula is None]

    def next_formula_cell(self, row: int, col: int) -> XlmCell | None:
        """
        The row fall-through of the macro language: the first cell at or below `row` in the
        same column that holds a formula, bounded by `_ROW_FALL_THROUGH_LIMIT` rows below the
        starting row.
        """
        result: XlmCell | None = None
        for item in self.body:
            if not isinstance(item, XlmCell) or item.col != col or item.formula is None:
                continue
            if not row <= item.row <= row + _ROW_FALL_THROUGH_LIMIT:
                continue
            if result is None or item.row < result.row:
                result = item
        return result

Ancestors

Instance variables

var name

The type of the None singleton.

var kind

The type of the None singleton.

Methods

def cell(self, row, col)

The cell at the one-based row and col, or None where the sheet holds none.

Expand source code Browse git
def cell(self, row: int, col: int) -> XlmCell | None:
    """
    The cell at the one-based `row` and `col`, or `None` where the sheet holds none.
    """
    for item in self.body:
        if isinstance(item, XlmCell) and item.row == row and item.col == col:
            return item
    return None
def listing(self, sort_formulas=False)

The cells in the order the extract listing shows them: the cells that hold a formula first and the cells that hold only a value after, each group in document order — or, when sorted, the formulas by column letters and then by row.

Expand source code Browse git
def listing(self, sort_formulas: bool = False) -> list[XlmCell]:
    """
    The cells in the order the extract listing shows them: the cells that hold a formula
    first and the cells that hold only a value after, each group in document order — or,
    when sorted, the formulas by column letters and then by row.
    """
    cells = [cell for cell in self.body if isinstance(cell, XlmCell)]
    formulas = [cell for cell in cells if cell.formula is not None]
    if sort_formulas:
        formulas.sort(key=lambda cell: (column_letters(cell.col), cell.row))
    return formulas + [cell for cell in cells if cell.formula is None]
def next_formula_cell(self, row, col)

The row fall-through of the macro language: the first cell at or below row in the same column that holds a formula, bounded by _ROW_FALL_THROUGH_LIMIT rows below the starting row.

Expand source code Browse git
def next_formula_cell(self, row: int, col: int) -> XlmCell | None:
    """
    The row fall-through of the macro language: the first cell at or below `row` in the
    same column that holds a formula, bounded by `_ROW_FALL_THROUGH_LIMIT` rows below the
    starting row.
    """
    result: XlmCell | None = None
    for item in self.body:
        if not isinstance(item, XlmCell) or item.col != col or item.formula is None:
            continue
        if not row <= item.row <= row + _ROW_FALL_THROUGH_LIMIT:
            continue
        if result is None or item.row < result.row:
            result = item
    return result

Inherited members

class XlmNameEntry (name, sheet, formula)

A defined name as the macro language sees it: the name as written, the zero-based index of the sheet the name is scoped to in the full sheet table of the format — chartsheets included — or None when the name is global, and the decoded formula.

Expand source code Browse git
class XlmNameEntry(NamedTuple):
    """
    A defined name as the macro language sees it: the name as written, the zero-based index
    of the sheet the name is scoped to in the full sheet table of the format — chartsheets
    included — or `None` when the name is global, and the decoded formula.
    """

    name: str
    sheet: int | None
    formula: Expression | None

Ancestors

  • builtins.tuple

Instance variables

var name

Alias for field number 0

Expand source code Browse git
class XlmNameEntry(NamedTuple):
    """
    A defined name as the macro language sees it: the name as written, the zero-based index
    of the sheet the name is scoped to in the full sheet table of the format — chartsheets
    included — or `None` when the name is global, and the decoded formula.
    """

    name: str
    sheet: int | None
    formula: Expression | None
var sheet

Alias for field number 1

Expand source code Browse git
class XlmNameEntry(NamedTuple):
    """
    A defined name as the macro language sees it: the name as written, the zero-based index
    of the sheet the name is scoped to in the full sheet table of the format — chartsheets
    included — or `None` when the name is global, and the decoded formula.
    """

    name: str
    sheet: int | None
    formula: Expression | None
var formula

Alias for field number 2

Expand source code Browse git
class XlmNameEntry(NamedTuple):
    """
    A defined name as the macro language sees it: the name as written, the zero-based index
    of the sheet the name is scoped to in the full sheet table of the format — chartsheets
    included — or `None` when the name is global, and the decoded formula.
    """

    name: str
    sheet: int | None
    formula: Expression | None
class XlmNameTable (workbook)

The defined names of a workbook as a list in document order, keeping an entry for every sheet a name is scoped to. The table is mutable because SET.NAME and DEFINE.NAME define names at run time.

Expand source code Browse git
class XlmNameTable:
    """
    The defined names of a workbook as a list in document order, keeping an entry for every
    sheet a name is scoped to. The table is mutable because `SET.NAME` and `DEFINE.NAME`
    define names at run time.
    """

    def __init__(self, workbook: ExcelWorkbook):
        self._entries: list[XlmNameEntry] = [
            XlmNameEntry(
                name=record.name,
                sheet=record.sheet,
                formula=workbook.formula(record.formula),
            )
            for record in workbook.defined_names()
        ]

    def entries(self) -> list[XlmNameEntry]:
        """
        Every entry, in document order.
        """
        return list(self._entries)

    def resolve(self, name: str, sheet: int | None = None) -> XlmNameEntry | None:
        """
        The entry a name spells, matched case-insensitively. An entry scoped to the given
        sheet wins over a global entry, and a name scoped only to another sheet answers
        nothing — the way Excel answers `#NAME?`. `None` when the workbook defines no such
        name for the scope.
        """
        key = name.lower()
        scoped: XlmNameEntry | None = None
        global_entry: XlmNameEntry | None = None
        for entry in self._entries:
            if entry.name.lower() != key:
                continue
            if entry.sheet == sheet and scoped is None:
                scoped = entry
            elif entry.sheet is None and global_entry is None:
                global_entry = entry
        return scoped or global_entry

    def fuzzy(self, pattern: str) -> list[XlmNameEntry]:
        """
        The entries a pattern might mean, in document order: every entry whose name starts
        with the pattern, and when none does, every entry whose name contains the pattern's
        characters in order, each character of the name matching at most one of the pattern.
        """
        pattern = pattern.lower()
        matches = [entry for entry in self._entries if entry.name.lower().startswith(pattern)]
        if matches:
            return matches
        return [entry for entry in self._entries if _is_subsequence(pattern, entry.name.lower())]

    def define(self, entry: XlmNameEntry) -> None:
        """
        Define the name an entry carries, replacing the entry that spells it in the same
        scope and appending a new one when no such entry exists.
        """
        key = entry.name.lower()
        for position, existing in enumerate(self._entries):
            if existing.name.lower() == key and existing.sheet == entry.sheet:
                self._entries[position] = entry
                return
        self._entries.append(entry)

    def restore(self, entries: list[XlmNameEntry]) -> None:
        """
        Hold the entries an earlier state of the table held, as the undo of a definition
        performs.
        """
        self._entries = list(entries)

    def undefine(self, name: str) -> None:
        """
        Remove every entry a name spells, case-insensitively, whatever sheet it is scoped to.
        """
        key = name.lower()
        self._entries = [entry for entry in self._entries if entry.name.lower() != key]

Methods

def entries(self)

Every entry, in document order.

Expand source code Browse git
def entries(self) -> list[XlmNameEntry]:
    """
    Every entry, in document order.
    """
    return list(self._entries)
def resolve(self, name, sheet=None)

The entry a name spells, matched case-insensitively. An entry scoped to the given sheet wins over a global entry, and a name scoped only to another sheet answers nothing — the way Excel answers #NAME?. None when the workbook defines no such name for the scope.

Expand source code Browse git
def resolve(self, name: str, sheet: int | None = None) -> XlmNameEntry | None:
    """
    The entry a name spells, matched case-insensitively. An entry scoped to the given
    sheet wins over a global entry, and a name scoped only to another sheet answers
    nothing — the way Excel answers `#NAME?`. `None` when the workbook defines no such
    name for the scope.
    """
    key = name.lower()
    scoped: XlmNameEntry | None = None
    global_entry: XlmNameEntry | None = None
    for entry in self._entries:
        if entry.name.lower() != key:
            continue
        if entry.sheet == sheet and scoped is None:
            scoped = entry
        elif entry.sheet is None and global_entry is None:
            global_entry = entry
    return scoped or global_entry
def fuzzy(self, pattern)

The entries a pattern might mean, in document order: every entry whose name starts with the pattern, and when none does, every entry whose name contains the pattern's characters in order, each character of the name matching at most one of the pattern.

Expand source code Browse git
def fuzzy(self, pattern: str) -> list[XlmNameEntry]:
    """
    The entries a pattern might mean, in document order: every entry whose name starts
    with the pattern, and when none does, every entry whose name contains the pattern's
    characters in order, each character of the name matching at most one of the pattern.
    """
    pattern = pattern.lower()
    matches = [entry for entry in self._entries if entry.name.lower().startswith(pattern)]
    if matches:
        return matches
    return [entry for entry in self._entries if _is_subsequence(pattern, entry.name.lower())]
def define(self, entry)

Define the name an entry carries, replacing the entry that spells it in the same scope and appending a new one when no such entry exists.

Expand source code Browse git
def define(self, entry: XlmNameEntry) -> None:
    """
    Define the name an entry carries, replacing the entry that spells it in the same
    scope and appending a new one when no such entry exists.
    """
    key = entry.name.lower()
    for position, existing in enumerate(self._entries):
        if existing.name.lower() == key and existing.sheet == entry.sheet:
            self._entries[position] = entry
            return
    self._entries.append(entry)
def restore(self, entries)

Hold the entries an earlier state of the table held, as the undo of a definition performs.

Expand source code Browse git
def restore(self, entries: list[XlmNameEntry]) -> None:
    """
    Hold the entries an earlier state of the table held, as the undo of a definition
    performs.
    """
    self._entries = list(entries)
def undefine(self, name)

Remove every entry a name spells, case-insensitively, whatever sheet it is scoped to.

Expand source code Browse git
def undefine(self, name: str) -> None:
    """
    Remove every entry a name spells, case-insensitively, whatever sheet it is scoped to.
    """
    key = name.lower()
    self._entries = [entry for entry in self._entries if entry.name.lower() != key]
class XlmOutcome (value=None, jump=None, status=None, halts=False, arrival=None)

What one macro command produced: the value it computed, the jump it asks the engine to take, the status that overrides the one its value derives, whether it halts the program, and the arrival the engine lands the jump by, for a jump the command that asks for it does not name — the step a macro call jumps to and the cell a return continues at. A handler that neither jumps nor overrides a status leaves both empty.

Expand source code Browse git
class XlmOutcome:
    """
    What one macro command produced: the value it computed, the jump it asks the engine to
    take, the status that overrides the one its value derives, whether it halts the program,
    and the arrival the engine lands the jump by, for a jump the command that asks for it
    does not name — the step a macro call jumps to and the cell a return continues at. A
    handler that neither jumps nor overrides a status leaves both empty.
    """

    def __init__(
        self,
        value: XlmValue | None = None,
        jump: XlmCursor | None = None,
        status: XlmStatus | None = None,
        halts: bool = False,
        arrival: XlmArrival | None = None,
    ):
        self.value = XlmValue() if value is None else value
        self.jump = jump
        self.status = status
        self.halts = halts
        self.arrival = arrival
class XlmReference (sheet, row, col)

The address of one cell: the sheet it sits on — None names the sheet the reading formula is on — and its one-based row and column.

Expand source code Browse git
class XlmReference(NamedTuple):
    """
    The address of one cell: the sheet it sits on — `None` names the sheet the reading
    formula is on — and its one-based row and column.
    """

    sheet: str | None
    row: int
    col: int

    def a1(self) -> str:
        """
        The A1 spelling of the address, qualified by its sheet when it names one.
        """
        cell = F'{column_letters(self.col)}{self.row}'
        if self.sheet is None:
            return cell
        return F'{self.sheet}!{cell}'

Ancestors

  • builtins.tuple

Instance variables

var sheet

Alias for field number 0

Expand source code Browse git
class XlmReference(NamedTuple):
    """
    The address of one cell: the sheet it sits on — `None` names the sheet the reading
    formula is on — and its one-based row and column.
    """

    sheet: str | None
    row: int
    col: int

    def a1(self) -> str:
        """
        The A1 spelling of the address, qualified by its sheet when it names one.
        """
        cell = F'{column_letters(self.col)}{self.row}'
        if self.sheet is None:
            return cell
        return F'{self.sheet}!{cell}'
var row

Alias for field number 1

Expand source code Browse git
class XlmReference(NamedTuple):
    """
    The address of one cell: the sheet it sits on — `None` names the sheet the reading
    formula is on — and its one-based row and column.
    """

    sheet: str | None
    row: int
    col: int

    def a1(self) -> str:
        """
        The A1 spelling of the address, qualified by its sheet when it names one.
        """
        cell = F'{column_letters(self.col)}{self.row}'
        if self.sheet is None:
            return cell
        return F'{self.sheet}!{cell}'
var col

Alias for field number 2

Expand source code Browse git
class XlmReference(NamedTuple):
    """
    The address of one cell: the sheet it sits on — `None` names the sheet the reading
    formula is on — and its one-based row and column.
    """

    sheet: str | None
    row: int
    col: int

    def a1(self) -> str:
        """
        The A1 spelling of the address, qualified by its sheet when it names one.
        """
        cell = F'{column_letters(self.col)}{self.row}'
        if self.sheet is None:
            return cell
        return F'{self.sheet}!{cell}'

Methods

def a1(self)

The A1 spelling of the address, qualified by its sheet when it names one.

Expand source code Browse git
def a1(self) -> str:
    """
    The A1 spelling of the address, qualified by its sheet when it names one.
    """
    cell = F'{column_letters(self.col)}{self.row}'
    if self.sheet is None:
        return cell
    return F'{self.sheet}!{cell}'
class XlmSeverity (*args, **kwds)

The output level a macro command shows at: the commands that move execution print at level 0, ordinary commands at level 1, and the commands that matter for triage at level 2.

Expand source code Browse git
class XlmSeverity(enum.Enum):
    """
    The output level a macro command shows at: the commands that move execution print at
    level 0, ordinary commands at level 1, and the commands that matter for triage at level 2.
    """

    JUMP = 0
    NORMAL = 1
    IMPORTANT = 2

Ancestors

  • enum.Enum

Class variables

var JUMP

The type of the None singleton.

var NORMAL

The type of the None singleton.

var IMPORTANT

The type of the None singleton.

class XlmStatus (*args, **kwds)

The state one executed cell left the program in.

Expand source code Browse git
class XlmStatus(enum.Enum):
    """
    The state one executed cell left the program in.
    """

    FullEvaluation = enum.auto()
    PartialEvaluation = enum.auto()
    Error = enum.auto()
    NotImplemented = enum.auto()
    End = enum.auto()
    Branching = enum.auto()
    FullBranching = enum.auto()
    IGNORED = enum.auto()

Ancestors

  • enum.Enum

Class variables

var FullEvaluation

The type of the None singleton.

var PartialEvaluation

The type of the None singleton.

var Error

The type of the None singleton.

var NotImplemented

The type of the None singleton.

var End

The type of the None singleton.

var Branching

The type of the None singleton.

var FullBranching

The type of the None singleton.

var IGNORED

The type of the None singleton.

class XlmStep (sheet, row, col, status, text, indent, severity)

One executed cell of a run: the sheet it sits on with its one-based row and column, the status the run left it in, the text the trace prints for it, its indentation inside a branch, and the output level it prints at — the level of its command, or the lowest level when the cell is no command call at all.

Expand source code Browse git
class XlmStep(NamedTuple):
    """
    One executed cell of a run: the sheet it sits on with its one-based row and column, the
    status the run left it in, the text the trace prints for it, its indentation inside a
    branch, and the output level it prints at — the level of its command, or the lowest level
    when the cell is no command call at all.
    """

    sheet: str
    row: int
    col: int
    status: XlmStatus
    text: str
    indent: int
    severity: XlmSeverity

Ancestors

  • builtins.tuple

Instance variables

var sheet

Alias for field number 0

Expand source code Browse git
class XlmStep(NamedTuple):
    """
    One executed cell of a run: the sheet it sits on with its one-based row and column, the
    status the run left it in, the text the trace prints for it, its indentation inside a
    branch, and the output level it prints at — the level of its command, or the lowest level
    when the cell is no command call at all.
    """

    sheet: str
    row: int
    col: int
    status: XlmStatus
    text: str
    indent: int
    severity: XlmSeverity
var row

Alias for field number 1

Expand source code Browse git
class XlmStep(NamedTuple):
    """
    One executed cell of a run: the sheet it sits on with its one-based row and column, the
    status the run left it in, the text the trace prints for it, its indentation inside a
    branch, and the output level it prints at — the level of its command, or the lowest level
    when the cell is no command call at all.
    """

    sheet: str
    row: int
    col: int
    status: XlmStatus
    text: str
    indent: int
    severity: XlmSeverity
var col

Alias for field number 2

Expand source code Browse git
class XlmStep(NamedTuple):
    """
    One executed cell of a run: the sheet it sits on with its one-based row and column, the
    status the run left it in, the text the trace prints for it, its indentation inside a
    branch, and the output level it prints at — the level of its command, or the lowest level
    when the cell is no command call at all.
    """

    sheet: str
    row: int
    col: int
    status: XlmStatus
    text: str
    indent: int
    severity: XlmSeverity
var status

Alias for field number 3

Expand source code Browse git
class XlmStep(NamedTuple):
    """
    One executed cell of a run: the sheet it sits on with its one-based row and column, the
    status the run left it in, the text the trace prints for it, its indentation inside a
    branch, and the output level it prints at — the level of its command, or the lowest level
    when the cell is no command call at all.
    """

    sheet: str
    row: int
    col: int
    status: XlmStatus
    text: str
    indent: int
    severity: XlmSeverity
var text

Alias for field number 4

Expand source code Browse git
class XlmStep(NamedTuple):
    """
    One executed cell of a run: the sheet it sits on with its one-based row and column, the
    status the run left it in, the text the trace prints for it, its indentation inside a
    branch, and the output level it prints at — the level of its command, or the lowest level
    when the cell is no command call at all.
    """

    sheet: str
    row: int
    col: int
    status: XlmStatus
    text: str
    indent: int
    severity: XlmSeverity
var indent

Alias for field number 5

Expand source code Browse git
class XlmStep(NamedTuple):
    """
    One executed cell of a run: the sheet it sits on with its one-based row and column, the
    status the run left it in, the text the trace prints for it, its indentation inside a
    branch, and the output level it prints at — the level of its command, or the lowest level
    when the cell is no command call at all.
    """

    sheet: str
    row: int
    col: int
    status: XlmStatus
    text: str
    indent: int
    severity: XlmSeverity
var severity

Alias for field number 6

Expand source code Browse git
class XlmStep(NamedTuple):
    """
    One executed cell of a run: the sheet it sits on with its one-based row and column, the
    status the run left it in, the text the trace prints for it, its indentation inside a
    branch, and the output level it prints at — the level of its command, or the lowest level
    when the cell is no command call at all.
    """

    sheet: str
    row: int
    col: int
    status: XlmStatus
    text: str
    indent: int
    severity: XlmSeverity
class XlmValue (value=None, text=None, partial=False, date=False, reference=None, cells=None, error=False)

One value of the macro language as the emulator computes it: value is the Python object behind it, text the spelling the trace prints, partial marks a value whose computation the program never finished, date marks a value that came from a date cell, reference names the cell address the value stands for, cells holds the elements of an array or the corners of a range, and error marks an error value such as the #DIV/0! of a division by zero. A given spelling of a number is normalized unless the value is a text, whose spelling is its content.

Expand source code Browse git
@dataclass(repr=False, eq=False)
class XlmValue:
    """
    One value of the macro language as the emulator computes it: `value` is the Python object
    behind it, `text` the spelling the trace prints, `partial` marks a value whose computation
    the program never finished, `date` marks a value that came from a date cell, `reference`
    names the cell address the value stands for, `cells` holds the elements of an array or the
    corners of a range, and `error` marks an error value such as the `#DIV/0!` of a division
    by zero. A given spelling of a number is normalized unless the value is a text, whose
    spelling is its content.
    """

    value: Any = None
    text: str | None = None
    partial: bool = False
    date: bool = False
    reference: XlmReference | None = None
    cells: tuple[XlmValue, ...] | None = None
    error: bool = False

    def __post_init__(self):
        if self.text is None:
            if isinstance(self.value, bool):
                self.text = 'TRUE' if self.value else 'FALSE'
            elif self.partial:
                self.text = str(self.value)
            else:
                self.text = _default_text(self.value)
        elif not isinstance(self.value, str) and is_number(self.text):
            number = float(self.text)
            self.text = str(int(number) if number.is_integer() else number)

    def unwrap(self) -> str:
        """
        The content of the value as a text. A text the program finished computing is its own
        content, and any other finished value is spelled as itself, whatever spelling the trace
        gives the command that computed it; a value the program never finished is the spelling
        of what it left unfinished, without the quotes of a string literal.
        """
        if self.partial:
            return unwrap_literal(self.text or '')
        if isinstance(self.value, str):
            return self.value
        return _default_text(self.value)

    def unwrap_date(self) -> datetime.datetime | None:
        """
        The moment an ISO-spelled text names, in one of the spellings the macro language
        writes into cells, or `None` for a text that names none.
        """
        for spelling in _DATETIME_FORMATS:
            try:
                return datetime.datetime.strptime(self.unwrap(), spelling)
            except ValueError:
                continue
        return None

Instance variables

var value

The type of the None singleton.

var text

The type of the None singleton.

var partial

The type of the None singleton.

var date

The type of the None singleton.

var reference

The type of the None singleton.

var cells

The type of the None singleton.

var error

The type of the None singleton.

Methods

def unwrap(self)

The content of the value as a text. A text the program finished computing is its own content, and any other finished value is spelled as itself, whatever spelling the trace gives the command that computed it; a value the program never finished is the spelling of what it left unfinished, without the quotes of a string literal.

Expand source code Browse git
def unwrap(self) -> str:
    """
    The content of the value as a text. A text the program finished computing is its own
    content, and any other finished value is spelled as itself, whatever spelling the trace
    gives the command that computed it; a value the program never finished is the spelling
    of what it left unfinished, without the quotes of a string literal.
    """
    if self.partial:
        return unwrap_literal(self.text or '')
    if isinstance(self.value, str):
        return self.value
    return _default_text(self.value)
def unwrap_date(self)

The moment an ISO-spelled text names, in one of the spellings the macro language writes into cells, or None for a text that names none.

Expand source code Browse git
def unwrap_date(self) -> datetime.datetime | None:
    """
    The moment an ISO-spelled text names, in one of the spellings the macro language
    writes into cells, or `None` for a text that names none.
    """
    for spelling in _DATETIME_FORMATS:
        try:
            return datetime.datetime.strptime(self.unwrap(), spelling)
        except ValueError:
            continue
    return None
class XlmView (data)

A workbook opened for interpretation. The macrosheets are the model the trace and the deobfuscation passes work on; the worksheets are data, each read into a coordinate dictionary of model cells when it is first asked for, because no reader offers random access to them. Sheet names are matched case-insensitively, as Excel matches them.

Expand source code Browse git
class XlmView:
    """
    A workbook opened for interpretation. The macrosheets are the model the trace and the
    deobfuscation passes work on; the worksheets are data, each read into a coordinate dictionary
    of model cells when it is first asked for, because no reader offers random access to them.
    Sheet names are matched case-insensitively, as Excel matches them.
    """

    def __init__(self, data: bytes | bytearray | memoryview):
        workbook = open_workbook(data)
        self._workbook = workbook
        self._macrosheets: dict[str, XlmMacrosheet] = {}
        for macrosheet in build_xlm_model(workbook):
            self._macrosheets.setdefault(macrosheet.name.lower(), macrosheet)
        self._worksheet_sources: dict[str, ExcelSheet] = {}
        for sheet in workbook.sheets():
            if sheet.kind is SheetKind.WORKSHEET:
                self._worksheet_sources.setdefault(sheet.name.lower(), sheet)
        self._worksheets: dict[str, dict[tuple[int, int], XlmCell]] = {}
        self.names = XlmNameTable(workbook)
        self.workbook_name = _WORKBOOK_NAMES[workbook.format]
        self.international = International()

    def macrosheets(self) -> list[XlmMacrosheet]:
        """
        Every macrosheet of the workbook in document order.
        """
        return list(self._macrosheets.values())

    def entry_points(self, start_point: str = '') -> list[XlmReference]:
        """
        The cells a run of the program starts at: every defined name that fuzzy-spells
        `auto_open` or `auto_close` points at one, and without such a name a start point a
        caller named is the only entry.
        """
        result: list[XlmReference] = []
        for pattern in ('auto_open', 'auto_close'):
            for entry in self.names.fuzzy(pattern):
                formula = entry.formula
                if not isinstance(formula, (XlA1Reference, XlR1C1Reference)):
                    continue
                reference = resolve_reference(formula, XlmCursor('', 0, 0))
                if reference.sheet is not None:
                    result.append(reference)
        if not result and start_point:
            parsed = parse_formula(start_point)
            if isinstance(parsed, (XlA1Reference, XlR1C1Reference)):
                reference = resolve_reference(parsed, XlmCursor('', 0, 0))
                if reference.sheet is not None:
                    result.append(reference)
        return result

    def macrosheet(self, name: str) -> XlmMacrosheet | None:
        """
        The macrosheet a name spells, or `None` when the workbook has none.
        """
        return self._macrosheets.get(name.lower())

    def worksheet(self, name: str) -> dict[tuple[int, int], XlmCell] | None:
        """
        The cells of the worksheet a name spells, keyed by one-based row and column as
        `refinery.lib.scripts.xlm.model.sheet_cells` reads them, or `None` when the workbook has
        no such worksheet.
        """
        key = name.lower()
        cells = self._worksheets.get(key)
        if cells is None:
            sheet = self._worksheet_sources.get(key)
            if sheet is None:
                return None
            cells = self._worksheets[key] = sheet_cells(self._workbook, sheet)
        return cells

    def sheet_index(self, name: str) -> int | None:
        """
        The position of the sheet a name spells in the full sheet table of the workbook — the
        sequence the scopes of its defined names count, chartsheets included — or `None` when
        the workbook has no such sheet.
        """
        return self._workbook.sheet_index(name)

    def cell(self, sheet_name: str, row: int, col: int) -> XlmCell | None:
        """
        The cell of the sheet a name spells at the one-based `row` and `col`, or `None` when the
        sheet or the cell does not exist.
        """
        macrosheet = self.macrosheet(sheet_name)
        if macrosheet is not None:
            return macrosheet.cell(row, col)
        worksheet = self.worksheet(sheet_name)
        if worksheet is not None:
            return worksheet.get((row, col))
        return None

Methods

def macrosheets(self)

Every macrosheet of the workbook in document order.

Expand source code Browse git
def macrosheets(self) -> list[XlmMacrosheet]:
    """
    Every macrosheet of the workbook in document order.
    """
    return list(self._macrosheets.values())
def entry_points(self, start_point='')

The cells a run of the program starts at: every defined name that fuzzy-spells auto_open or auto_close points at one, and without such a name a start point a caller named is the only entry.

Expand source code Browse git
def entry_points(self, start_point: str = '') -> list[XlmReference]:
    """
    The cells a run of the program starts at: every defined name that fuzzy-spells
    `auto_open` or `auto_close` points at one, and without such a name a start point a
    caller named is the only entry.
    """
    result: list[XlmReference] = []
    for pattern in ('auto_open', 'auto_close'):
        for entry in self.names.fuzzy(pattern):
            formula = entry.formula
            if not isinstance(formula, (XlA1Reference, XlR1C1Reference)):
                continue
            reference = resolve_reference(formula, XlmCursor('', 0, 0))
            if reference.sheet is not None:
                result.append(reference)
    if not result and start_point:
        parsed = parse_formula(start_point)
        if isinstance(parsed, (XlA1Reference, XlR1C1Reference)):
            reference = resolve_reference(parsed, XlmCursor('', 0, 0))
            if reference.sheet is not None:
                result.append(reference)
    return result
def macrosheet(self, name)

The macrosheet a name spells, or None when the workbook has none.

Expand source code Browse git
def macrosheet(self, name: str) -> XlmMacrosheet | None:
    """
    The macrosheet a name spells, or `None` when the workbook has none.
    """
    return self._macrosheets.get(name.lower())
def worksheet(self, name)

The cells of the worksheet a name spells, keyed by one-based row and column as sheet_cells() reads them, or None when the workbook has no such worksheet.

Expand source code Browse git
def worksheet(self, name: str) -> dict[tuple[int, int], XlmCell] | None:
    """
    The cells of the worksheet a name spells, keyed by one-based row and column as
    `refinery.lib.scripts.xlm.model.sheet_cells` reads them, or `None` when the workbook has
    no such worksheet.
    """
    key = name.lower()
    cells = self._worksheets.get(key)
    if cells is None:
        sheet = self._worksheet_sources.get(key)
        if sheet is None:
            return None
        cells = self._worksheets[key] = sheet_cells(self._workbook, sheet)
    return cells
def sheet_index(self, name)

The position of the sheet a name spells in the full sheet table of the workbook — the sequence the scopes of its defined names count, chartsheets included — or None when the workbook has no such sheet.

Expand source code Browse git
def sheet_index(self, name: str) -> int | None:
    """
    The position of the sheet a name spells in the full sheet table of the workbook — the
    sequence the scopes of its defined names count, chartsheets included — or `None` when
    the workbook has no such sheet.
    """
    return self._workbook.sheet_index(name)
def cell(self, sheet_name, row, col)

The cell of the sheet a name spells at the one-based row and col, or None when the sheet or the cell does not exist.

Expand source code Browse git
def cell(self, sheet_name: str, row: int, col: int) -> XlmCell | None:
    """
    The cell of the sheet a name spells at the one-based `row` and `col`, or `None` when the
    sheet or the cell does not exist.
    """
    macrosheet = self.macrosheet(sheet_name)
    if macrosheet is not None:
        return macrosheet.cell(row, col)
    worksheet = self.worksheet(sheet_name)
    if worksheet is not None:
        return worksheet.get((row, col))
    return None