math_spec.expansion
Named sub-expressions and expression macros — YAML-defined, schema-local.
Both are expanded into core AST before any backend sees the expression, so the eager builder and the relational backend support them identically and the engine contract (core AST is the whole language) is untouched.
Two mechanisms, one substitution engine, zero global state:
-
Named sub-expressions: the YAML
expressions:block maps a name to an expression string. Referencing the name splices in the parsed subtree. Substitution is only half of what the block means, though: a named expression has fixed dims and is readable after a solve (the rules for named expressions), which a macro — parameterised, dimensionless until called — never is. -
Macros: the YAML
macros:block declares parameterised expression templates — language, not code::macros: weighted_sum: args: [array, weights] kwargs: [over] template: sum(array * weights, over=over)
Usage: weighted_sum(p, cost, over=generator). Formal names shadow
model names inside the body; everything else resolves against the model
namespace as usual.
Because macros live in the schema, a YAML file is fully self-contained: its
meaning never depends on Python-side registration state. This also makes
load-time validation complete — every template can be name-checked against
this schema (see validation.py), used or not.
There is no Python operator registry: the built-in set is closed, macros cover
composition, and math the language cannot say goes in a declared escape:
island (#38) — visible in the file and bounded by its where mask, rather
than a registered function that reads like a built-in on the page.
expand(node, schema, context='expression')
#
Expand all named sub-expressions and macro calls under node.
Expansion never changes the shape of the root: a comparison stays a
comparison, an arithmetic node stays arithmetic. The overloads say so, so
callers holding an ArithmeticNode keep it across the call.
Source code in src/math_spec/expansion.py
macro_signature(name, macro)
#
Human-readable call signature, for error messages.
parse_and_expand(text, schema, context='expression')
#
Parse text and expand named sub-expressions and macros to core AST.
parse_template(name, macro, context)
#
Parse a macro template, rejecting comparisons.