Module 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 evaluator applies to them.
Expand source code Browse git
"""
The value algebra of the XLM macro language: the values the emulator computes, the quoting
the trace spells them with, and the operations the evaluator applies to them.
"""
from __future__ import annotations
import datetime
import math
import operator
from dataclasses import dataclass, replace
from typing import TYPE_CHECKING, Any, NamedTuple
from refinery.lib.excel.common import column_letters
from refinery.lib.excel.formula.model import XlBinaryOperator
from refinery.lib.scripts.xlm.trace import XlmStatus
if TYPE_CHECKING:
from refinery.lib.scripts.xlm.references import XlmArrival, XlmCursor
_DATETIME_FORMATS = (
'%Y-%m-%d %H:%M:%S.%f',
'%H:%M:%S',
)
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}'
def is_number(value: Any) -> bool:
"""
Whether a value reads as a number, as the macro language reads one: a finite number is one,
and so is the text of one. The digit separators, the infinities, and the missing number
that Python reads in a text spell no number of the macro language.
"""
if isinstance(value, str) and '_' in value:
return False
try:
number = float(value)
except (ValueError, TypeError, OverflowError):
return False
return math.isfinite(number)
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 error_outcome(
truth: XlmValue,
spelled: str | None = None,
halts: bool = False,
) -> XlmOutcome:
"""
The outcome a command that reads a condition answers for one that spells no truth value:
the error value the condition is, spelled as the command when it names one, and an error
the macro halts on when its grammar makes the condition fatal.
"""
if spelled is not None:
truth = replace(truth, text=spelled)
return XlmOutcome(value=truth, status=XlmStatus.Error if halts else None)
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 ansi_bytes(text: str) -> bytes:
"""
The bytes a text occupies in the ANSI code page the macro language writes: a character
`CHAR` answers is the byte it was made from, any other character its byte in code page
1252, and a character that code page cannot spell is the question mark Windows writes
instead.
"""
data = bytearray()
for char in text:
code = ord(char)
if code > 0xFF:
try:
code = char.encode('cp1252')[0]
except UnicodeEncodeError:
code = 0x3F
data.append(code)
return bytes(data)
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)}"'
def _default_text(value: Any) -> str:
if value is None:
return ''
if isinstance(value, XlmReference):
return value.a1()
if isinstance(value, bool):
return 'TRUE' if value else 'FALSE'
if isinstance(value, str):
return wrap_literal(value, must_wrap=True)
if isinstance(value, float) and value.is_integer():
return str(int(value))
return str(value)
def error_value(text: str) -> XlmValue:
"""
The error value Excel spells with the given text, such as the `#VALUE!` of a coercion that
failed.
"""
return XlmValue(value=text, text=text, error=True)
@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
_OPERATOR_FUNCTIONS = {
XlBinaryOperator.ADD: operator.add,
XlBinaryOperator.SUB: operator.sub,
XlBinaryOperator.MUL: operator.mul,
XlBinaryOperator.DIV: operator.truediv,
XlBinaryOperator.POW: operator.pow,
XlBinaryOperator.EQ: operator.eq,
XlBinaryOperator.NE: operator.ne,
XlBinaryOperator.LT: operator.lt,
XlBinaryOperator.LE: operator.le,
XlBinaryOperator.GT: operator.gt,
XlBinaryOperator.GE: operator.ge,
}
_OPERATOR_SYMBOLS = {
XlBinaryOperator.ADD: '+',
XlBinaryOperator.SUB: '-',
XlBinaryOperator.MUL: '*',
XlBinaryOperator.DIV: '/',
XlBinaryOperator.POW: '^',
XlBinaryOperator.EQ: '=',
XlBinaryOperator.NE: '<>',
XlBinaryOperator.LT: '<',
XlBinaryOperator.LE: '<=',
XlBinaryOperator.GT: '>',
XlBinaryOperator.GE: '>=',
}
def _operand(value: XlmValue) -> Any:
"""
The operand the operator table applies: a boolean is its number, an empty text is zero,
and the spellings of TRUE and FALSE are theirs.
"""
if isinstance(value.value, bool):
return value.value
if value.unwrap() == '':
return 0
if isinstance(value.value, str):
lowered = value.value.lower()
if lowered == 'true':
return 1
if lowered == 'false':
return 0
return value.value
_ARITHMETIC_OPERATORS = frozenset((
XlBinaryOperator.ADD,
XlBinaryOperator.SUB,
XlBinaryOperator.MUL,
XlBinaryOperator.DIV,
XlBinaryOperator.POW,
))
def _numeric_result(result: Any) -> XlmValue:
if isinstance(result, bool):
return XlmValue(value=result)
if isinstance(result, complex) or (isinstance(result, float) and not math.isfinite(result)):
return error_value('#NUM!')
if isinstance(result, float) and result.is_integer():
return XlmValue(value=int(result))
if isinstance(result, float):
return XlmValue(value=round(result, 10))
return XlmValue(value=result)
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 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!')
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
Functions
def is_number(value)-
Whether a value reads as a number, as the macro language reads one: a finite number is one, and so is the text of one. The digit separators, the infinities, and the missing number that Python reads in a text spell no number of the macro language.
Expand source code Browse git
def is_number(value: Any) -> bool: """ Whether a value reads as a number, as the macro language reads one: a finite number is one, and so is the text of one. The digit separators, the infinities, and the missing number that Python reads in a text spell no number of the macro language. """ if isinstance(value, str) and '_' in value: return False try: number = float(value) except (ValueError, TypeError, OverflowError): return False return math.isfinite(number) 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 error_outcome(truth, spelled=None, halts=False)-
The outcome a command that reads a condition answers for one that spells no truth value: the error value the condition is, spelled as the command when it names one, and an error the macro halts on when its grammar makes the condition fatal.
Expand source code Browse git
def error_outcome( truth: XlmValue, spelled: str | None = None, halts: bool = False, ) -> XlmOutcome: """ The outcome a command that reads a condition answers for one that spells no truth value: the error value the condition is, spelled as the command when it names one, and an error the macro halts on when its grammar makes the condition fatal. """ if spelled is not None: truth = replace(truth, text=spelled) return XlmOutcome(value=truth, status=XlmStatus.Error if halts else None) 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 ansi_bytes(text)-
The bytes a text occupies in the ANSI code page the macro language writes: a character
CHARanswers is the byte it was made from, any other character its byte in code page 1252, and a character that code page cannot spell is the question mark Windows writes instead.Expand source code Browse git
def ansi_bytes(text: str) -> bytes: """ The bytes a text occupies in the ANSI code page the macro language writes: a character `CHAR` answers is the byte it was made from, any other character its byte in code page 1252, and a character that code page cannot spell is the question mark Windows writes instead. """ data = bytearray() for char in text: code = ord(char) if code > 0xFF: try: code = char.encode('cp1252')[0] except UnicodeEncodeError: code = 0x3F data.append(code) return bytes(data) 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)}"' def error_value(text)-
The error value Excel spells with the given text, such as the
#VALUE!of a coercion that failed.Expand source code Browse git
def error_value(text: str) -> XlmValue: """ The error value Excel spells with the given text, such as the `#VALUE!` of a coercion that failed. """ return XlmValue(value=text, text=text, error=True) 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 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!')
Classes
class XlmReference (sheet, row, col)-
The address of one cell: the sheet it sits on —
Nonenames 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 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:
valueis the Python object behind it,textthe spelling the trace prints,partialmarks a value whose computation the program never finished,datemarks a value that came from a date cell,referencenames the cell address the value stands for,cellsholds the elements of an array or the corners of a range, anderrormarks 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 NoneInstance 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
Nonefor 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 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