Skip to content

math_spec.expansion

Named sub-expressions and macros, expanded into the core AST before anything reads the expression.

expand(node, ns, context, *, shadow=frozenset()) #

expand(
    node: ArithmeticNode,
    ns: Namespace,
    context: str,
    *,
    shadow: frozenset[str] = ...,
) -> ArithmeticNode
expand(
    node: ComparisonNode,
    ns: Namespace,
    context: str,
    *,
    shadow: frozenset[str] = ...,
) -> ComparisonNode

Expand all named sub-expressions and macro calls under node.

A comparison stays a comparison and an arithmetic node stays arithmetic. A named expression arrives as the node :meth:Namespace.named resolved it to, once for every use.

PARAMETER DESCRIPTION
node

The parsed expression.

TYPE: ParsedNode

ns

Where names and macros are declared.

TYPE: Namespace

context

What an error names.

TYPE: str

shadow

Names left as written even where a named expression has that name — a template's formals, checked without a call to bind them.

TYPE: frozenset[str] DEFAULT: frozenset()

Source code in src/math_spec/expansion.py
def expand(node: ParsedNode, ns: Namespace, context: str, *, shadow: frozenset[str] = frozenset()) -> ParsedNode:
    """Expand all named sub-expressions and macro calls under *node*.

    A comparison stays a comparison and an arithmetic node stays arithmetic. A
    named expression arrives as the node :meth:`Namespace.named` resolved it
    to, once for every use.

    Args:
        node: The parsed expression.
        ns: Where names and macros are declared.
        context: What an error names.
        shadow: Names left as written even where a named expression has that
            name — a template's formals, checked without a call to bind them.
    """
    if isinstance(node, ComparisonNode):
        return ComparisonNode(
            node.op,
            _expand(node.left, ns, context, (), shadow),
            _expand(node.right, ns, context, (), shadow),
        )
    return _expand(node, ns, context, (), shadow)

macro_signature(name, macro) #

Human-readable call signature, for error messages.

Source code in src/math_spec/expansion.py
def macro_signature(name: str, macro: MacroBlock) -> str:
    """Human-readable call signature, for error messages."""
    parts = [*macro.args, *(f'{k}=...' for k in macro.kwargs)]
    return f'{name}({", ".join(parts)})'

parse_and_expand(text, ns, context) #

Parse text and expand named sub-expressions and macros to core AST.

PARAMETER DESCRIPTION
text

The expression as the file wrote it.

TYPE: str

ns

Where names and macros are declared, and where a named expression is resolved.

TYPE: Namespace

context

What an error names.

TYPE: str

Source code in src/math_spec/expansion.py
def parse_and_expand(text: str, ns: Namespace, context: str) -> ParsedNode:
    """Parse *text* and expand named sub-expressions and macros to core AST.

    Args:
        text: The expression as the file wrote it.
        ns: Where names and macros are declared, and where a named expression is resolved.
        context: What an error names.
    """
    return expand(parse_expression(text), ns, context)

parse_template(name, macro, context) #

Parse a macro template, rejecting comparisons.

Source code in src/math_spec/expansion.py
def parse_template(name: str, macro: MacroBlock, context: str) -> ArithmeticNode:
    """Parse a macro template, rejecting comparisons."""
    body = parse_expression(macro.template)
    if isinstance(body, ComparisonNode):
        msg = f"{context}: macro '{name}' template must not contain a comparison operator. Got: {macro.template!r}"
        raise SchemaError(msg)
    return body