PythonTransformEngine

TransformEngine

Normalize formulas with a profile, tune the pipeline, and read the transform report.

TransformEngineclass#

Normalize a formula into the canonical form selected by a profile.

The engine runs a multi-phase pipeline (LowerAttributes, a fixed-point Rewrite loop, FinalizeAst, FlattenGroups). A profile picks rule levels; TransformConfig is nested by phase and controls per-run switches without changing the selected rule set. The engine also bundles a parser, so it exposes parse and shares its immutable knowledge_base().

Since 0.4.0 the bundled parser defaults to ParseConfig LENIENT, and TransformConfig is the nested four-phase object (not flat rewrite_enabled / max_iterations keys). Per-call config must be a complete TransformConfig (or ParseConfig on parse); a dict passed as config raises ConfigError. Other keywords are overlays (normalize(src, rewrite={"enabled": False})). Nested overlay values must be dicts, not phase-config class instances. Construct a full object with TransformConfig(...) (all four phases required) or the profile classmethods authoring() / faithful() / corpus() / equiv().

Normalization is gated on a complete tree: an incomplete parse raises ParseError, since normalizing a tree with holes is meaningless. For the conceptual model — profiles, the pipeline, and the eliminated-form contract — see the Transforms guide.

Python
import texform

engine = texform.TransformEngine(profile="corpus")
engine.normalize(r"a \over b")  # '\frac { a } { b }'
engine.normalize(r"a \over b", rewrite={"enabled": False})
engine.normalize(r"a \over b", reject_unknown=True, flatten_groups={"enabled": False})
reported = engine.normalize_with_report(r"a \over b")
reported["normalized"]  # '\frac { a } { b }'
cfg = engine.default_transform_config()
cfg.rewrite.max_iterations = 50
result = engine.parse(r"a \over b")
engine.transform(result["document"], cfg)
engine.transform_with_report(result["document"], texform.TransformConfig.faithful(), rewrite={"enabled": False})

TransformEngine#

TransformEngine(    profile: TransformProfile,    knowledge_base: KnowledgeBase | None = None,    disable_rules: list[str] | None = None,    default_parse_config: ParseConfig | None = None,) -> None

Construct an engine sharing knowledge; invalid profiles or rules raise ConfigError.

ParameterTypeDefault
profileTransformProfile—
knowledge_baseKnowledgeBase | NoneNone
disable_ruleslist[str] | NoneNone
default_parse_configParseConfig | NoneNone

normalize#

normalize(    src: str,    config: TransformConfig | None = None,    **overrides,) -> str

Parse, transform, and serialize a formula in one call.

Normalization is gated on a complete tree. If the input cannot produce a complete tree, normalize raises ParseError carrying the diagnostics and the partial document. Empty input is complete and normalizes normally. This path returns the serialized string and does not build a report.

ParameterTypeDefaultDescription
srcstr—

The LaTeX source string.

configTransformConfig | NoneNone

A complete TransformConfig; it replaces only the transform half of the baseline. **overrides are layered on top. A dict here raises ConfigError.

**overridesUnpack[NormalizeOverrides]—

Keyword overlay from NormalizeOverrides (parse keys plus nested phase dicts). Nested values must be dicts, not config class instances.

Returns
str —

The serialized LaTeX string.

Raises
  • ParseError —

    If the source does not parse into a complete tree.

  • ConfigError —

    If config is not a TransformConfig, a dict is passed as config, or an override key or value is invalid.

Python
engine = texform.TransformEngine(profile="corpus")
engine.normalize(r"\dv{f}{x}")
# '\frac { \mathrm { d } f } { \mathrm { d } x }'

normalize_with_report#

normalize_with_report(    src: str,    config: TransformConfig | None = None,    **overrides,) -> NormalizeReportResult

Parse, transform, and serialize a formula, returning text and a report.

Accepts the same source, config, and keyword overlays as normalize. Output text and errors follow that contract. The report is diagnostic: its fields, phase divisions, and counters are not part of the stable compatibility promise. A failure does not return a partial report.

ParameterTypeDefaultDescription
srcstr—

The LaTeX source string.

configTransformConfig | NoneNone

A complete TransformConfig, with the same meaning as on normalize.

**overridesUnpack[NormalizeOverrides]—

Keyword overlay from NormalizeOverrides.

Returns
NormalizeReportResult —

A NormalizeReportResult dict with normalized and report.

Raises
  • ParseError —

    If the source does not parse into a complete tree.

  • ConfigError —

    If config or an override is invalid.

transform#

transform(    document: Document,    config: TransformConfig | None = None,    **overrides,) -> None

Transform a live Document in place.

transform accepts documents sharing this engine's knowledge-base instance, including constructed, copied, and imported snapshots. The document must also be complete; a document that has_errors() is rejected with TransformError. This path returns None and does not build a report.

ParameterTypeDefaultDescription
documentDocument—

The live Document to update in place.

configTransformConfig | NoneNone

A complete TransformConfig; **overrides are layered on top. It does not accept parse options. A dict here raises ConfigError.

**overridesUnpack[TransformOverrides]—

Keyword overlay from TransformOverrides. Nested values must be dicts, not config class instances.

Returns
None —

None.

Raises
  • TransformError —

    If the document has a different knowledge base, has parse errors, or on a contract violation.

  • ConfigError —

    If config is not a TransformConfig, a dict is passed as config, or an override key or value is invalid.

Python
engine = texform.TransformEngine(profile="corpus")
result = engine.parse(r"a \over b")
document = result["document"]
assert document is not None
engine.transform(document)
document.to_latex()  # '\frac { a } { b }'

transform_with_report#

transform_with_report(    document: Document,    config: TransformConfig | None = None,    **overrides,) -> TransformReport

Transform a live Document in place and return its diagnostic report.

Accepts the same document, config, and keyword overlays as transform. Document ownership, completeness, and errors follow that contract. The report is diagnostic and is not part of the stable compatibility promise. A failure does not return a partial report.

ParameterTypeDefaultDescription
documentDocument—

The live Document to update in place.

configTransformConfig | NoneNone

A complete TransformConfig, with the same meaning as on transform.

**overridesUnpack[TransformOverrides]—

Keyword overlay from TransformOverrides.

Returns
TransformReport —

The diagnostic transform report dict.

Raises
  • TransformError —

    If the document has a different knowledge base, has parse errors, or on a contract violation.

  • ConfigError —

    If config or an override is invalid.

parse#

parse(    src: str,    config: ParseConfig | None = None,    **overrides,) -> ParseResult

Parse a LaTeX string using the engine's bundled parser.

Uses this engine's parser and default parse config. Engines default to lenient parsing; pass config to override it per call. A document produced here is owned by this engine and is the only kind transform accepts.

ParameterTypeDefaultDescription
srcstr—

The LaTeX source string.

configParseConfig | NoneNone

A complete ParseConfig; **overrides are layered on top. None uses the engine default. A dict here raises ConfigError.

**overridesUnpack[ParseOverrides]—

Keyword overlay from ParseOverrides.

Returns
ParseResult —

A ParseResult dict with document and diagnostics.

Raises
  • ConfigError —

    If config is not a ParseConfig, a dict is passed as config, or an override key or value is invalid.

default_parse_config#

default_parse_config() -> ParseConfig

Return a new ParseConfig holding this engine's parse defaults.

default_transform_config#

default_transform_config() -> TransformConfig

Return a new TransformConfig holding this engine's transform defaults.

Knowledge-base lookups#

Query the knowledge the parser loaded. Predicate methods take a single name; lookup methods take name and a mode of "math" or "text" and return a knowledge-entry dict or None.

knowledge_base#

knowledge_base() -> KnowledgeBase

Return the immutable knowledge base shared with parsed documents.

TransformConfigclass#

Complete transform configuration, nested by pipeline phase.

Since 0.4.0 this is the only accepted config= type for transform / normalize. A dict passed as config raises ConfigError; use keyword overlays (normalize(src, rewrite={"enabled": False})) or construct this class. The constructor requires all four phase configs; the classmethods authoring() / faithful() / corpus() / equiv() return the defaults a profile actually uses.

The four attributes are shared references: cfg.rewrite.enabled = False is visible through cfg.rewrite.enabled and takes effect when the object is passed as config. Nested overlay values must be dicts, not these phase-config instances.

Python
config = texform.TransformConfig(
    lower_attributes=texform.LowerAttributesConfig(),
    rewrite=texform.RewriteConfig(max_iterations=50),
    finalize_ast=texform.FinalizeAstConfig(),
    flatten_groups=texform.FlattenGroupsConfig(enabled=False),
)
config.rewrite.max_iterations  # 50
texform.TransformConfig.corpus().flatten_groups.preserve_rendered_spacing  # False

lower_attributesattribute#

LowerAttributesConfig

The LowerAttributes phase config (shared reference).

rewriteattribute#

RewriteConfig

The Rewrite phase config (shared reference).

finalize_astattribute#

FinalizeAstConfig

The FinalizeAst phase config (shared reference).

flatten_groupsattribute#

FlattenGroupsConfig

The FlattenGroups phase config (shared reference).

TransformConfig#

TransformConfig(    lower_attributes: LowerAttributesConfig,    rewrite: RewriteConfig,    finalize_ast: FinalizeAstConfig,    flatten_groups: FlattenGroupsConfig,) -> None

Construct a transform configuration. All four phases are required.

ParameterTypeDescription
lower_attributesLowerAttributesConfig

The LowerAttributes phase config.

rewriteRewriteConfig

The Rewrite phase config.

finalize_astFinalizeAstConfig

The FinalizeAst phase config.

flatten_groupsFlattenGroupsConfig

The FlattenGroups phase config.

Raises
  • TypeError —

    If a required phase argument is missing. Start with a profile classmethod such as TransformConfig.corpus() to change only selected fields.

authoringclass method#

authoring() -> TransformConfig

Return the transform config the authoring profile uses by default.

faithfulclass method#

faithful() -> TransformConfig

Return the transform config the faithful profile uses by default.

corpusclass method#

corpus() -> TransformConfig

Return the transform config the corpus profile uses by default.

equivclass method#

equiv() -> TransformConfig

Return the transform config the equiv profile uses by default.

RewriteConfigclass#

Complete Rewrite phase configuration.

Nested under TransformConfig.rewrite. Disabling this phase leaves legacy syntax such as \over untouched; it does not change which rule levels the profile selected. Overlay with rewrite={"enabled": False}; do not pass this class as a nested overlay value.

Python
cfg = texform.RewriteConfig(max_iterations=50)
cfg.enabled  # True
cfg.max_iterations  # 50

enabledattribute#

bool

Whether the phase runs. Defaults to True.

max_iterationsattribute#

int

The cap on fixed-point passes. Defaults to 100.

RewriteConfig#

RewriteConfig(enabled: bool = True, max_iterations: int = 100) -> None

Construct a Rewrite phase configuration.

ParameterTypeDefaultDescription
enabledboolTrue

Whether the phase runs.

max_iterationsint100

The cap on fixed-point passes.

LowerAttributesConfigclass#

Complete LowerAttributes phase configuration.

This is a nested field of TransformConfig, not a dict overlay. Pass the parent TransformConfig as config; to overlay only this phase use lower_attributes={"enabled": False}. A dict passed as config on transform / normalize raises ConfigError.

Python
texform.LowerAttributesConfig().enabled  # True
texform.LowerAttributesConfig(enabled=False).enabled  # False

enabledattribute#

bool

Whether the phase runs. Defaults to True.

LowerAttributesConfig#

LowerAttributesConfig(enabled: bool = True) -> None

Construct a LowerAttributes phase configuration.

ParameterTypeDefaultDescription
enabledboolTrue

Whether the phase runs.

FinalizeAstConfigclass#

Complete FinalizeAst phase configuration.

Nested under TransformConfig.finalize_ast. The phase performs local AST cleanup such as merging adjacent Prime nodes and normalizing text sequences. Overlay with finalize_ast={"enabled": False}.

Python
texform.FinalizeAstConfig().enabled  # True

enabledattribute#

bool

Whether the phase runs. Defaults to True.

FinalizeAstConfig#

FinalizeAstConfig(enabled: bool = True) -> None

Construct a FinalizeAst phase configuration.

ParameterTypeDefaultDescription
enabledboolTrue

Whether the phase runs.

FlattenGroupsConfigclass#

Complete FlattenGroups phase configuration.

Nested under TransformConfig.flatten_groups. enabled governs whether the phase runs. preserve_rendered_spacing keeps groups whose only public-facing effect is rendered math spacing; it does not control serializer source whitespace (see SerializeOptions). Structural guards stay on even when preserve_rendered_spacing is False. Constructor defaults match the authoring / faithful strict pair (both True); the corpus and equiv profiles leave preserve_rendered_spacing off. Overlay with flatten_groups={"enabled": False}; a dict passed as config still raises ConfigError.

Python
texform.FlattenGroupsConfig().enabled  # True
texform.FlattenGroupsConfig().preserve_rendered_spacing  # True
texform.FlattenGroupsConfig(enabled=False).enabled  # False
texform.TransformConfig.corpus().flatten_groups.preserve_rendered_spacing  # False

enabledattribute#

bool

Whether the phase runs. Defaults to True.

preserve_rendered_spacingattribute#

bool

Keep groups whose only public-facing effect is rendered math spacing. Defaults to True. This is not serializer source spacing.

FlattenGroupsConfig#

FlattenGroupsConfig(    enabled: bool = True,    preserve_rendered_spacing: bool = True,) -> None

Construct a FlattenGroups phase configuration.

ParameterTypeDefaultDescription
enabledboolTrue

Whether the phase runs.

preserve_rendered_spacingboolTrue

Keep groups whose only public-facing effect is rendered math spacing.

NormalizeOverridesclassextends ParseOverrides, TransformOverrides#

Flat parse+transform overlays for normalize.

Combines ParseOverrides keys with nested phase overlays from TransformOverrides. A dict passed as config still raises ConfigError; these keys are keywords only (normalize(src, rewrite={"enabled": False}, reject_unknown=True)).

Python
engine = texform.TransformEngine(profile="corpus")
engine.normalize(r"a \over b", reject_unknown=True)
# '\frac { a } { b }'

TransformOverridesdict type#

Keyword overlays for transform.

Each nested value must be a dict of that phase's overlay keys, not a complete phase-config class instance.

FieldTypeDescription
lower_attributesLowerAttributesOverrides

Overlay dict for the LowerAttributes phase.

rewriteRewriteOverrides

Overlay dict for the Rewrite phase.

finalize_astFinalizeAstOverrides

Overlay dict for the FinalizeAst phase.

flatten_groupsFlattenGroupsOverrides | None

Overlay dict for the FlattenGroups phase.

RewriteOverridesdict type#

Keyword overlays for the Rewrite phase.

Nested under transform / normalize as rewrite={...}. The nested value must be a dict, not a RewriteConfig instance.

FieldTypeDescription
enabledbool

Overlay for RewriteConfig.enabled.

max_iterationsint

Overlay for RewriteConfig.max_iterations.

LowerAttributesOverridesdict type#

Keyword overlays for the LowerAttributes phase.

Nested under transform / normalize as lower_attributes={...}. The nested value must be a dict, not a LowerAttributesConfig instance.

FieldTypeDescription
enabledbool

Overlay for LowerAttributesConfig.enabled.

FinalizeAstOverridesdict type#

Keyword overlays for the FinalizeAst phase.

Nested under transform / normalize as finalize_ast={...}. The nested value must be a dict, not a FinalizeAstConfig instance.

FieldTypeDescription
enabledbool

Overlay for FinalizeAstConfig.enabled.

FlattenGroupsOverridesdict type#

Keyword overlays for the FlattenGroups phase.

Nested under transform / normalize as flatten_groups={...}. The nested value must be a dict, not a FlattenGroupsConfig instance.

FieldTypeDescription
enabledbool | None

Overlay for FlattenGroupsConfig.enabled. None leaves the current value unchanged.

preserve_rendered_spacingbool | None

Overlay for FlattenGroupsConfig.preserve_rendered_spacing. None leaves the current value unchanged.

NormalizeReportResultdict type#

Text plus the diagnostic report from one explicit normalize report call.

Plain normalize returns the string directly and does not build this object.

FieldTypeDescription
normalizedstr

The serialized LaTeX string.

reportTransformReport

The diagnostic transform report for this call.

TransformReportdict type#

Diagnostic report for one explicit report call.

Output text and errors follow the ordinary transform contract. Report fields, phase divisions, and these counters are diagnostic: they are not part of the stable compatibility promise. A disabled phase stays present as zeros or an empty list. The Python report uses snake_case; JavaScript exposes the same data in camelCase.

FieldTypeDescription
warningslist[TransformWarning]

Unknown input names, deduplicated and sorted by kind and name.

lower_attributesLowerAttributesReport

LowerAttributes activity, summed across its two passes.

rewriteRewriteReport

Rewrite iterations and per-rule outcomes.

finalize_astFinalizeAstReport

Prime merges and text normalizations.

flatten_groupsFlattenGroupsReport

Flatten actions and guard hits.

TransformWarningdict type#

An unknown input name whose arguments or scope cannot be guaranteed.

FieldType
kindLiteral['unknown-command', 'unknown-environment']
namestr
messagestr

LowerAttributesReportdict type#

The LowerAttributes phase report.

FieldTypeDescription
attributeslist[AttributeStat]

Per-attribute activity, sorted by attr then value.

eliminated_empty_segmentsint

Empty attribute segments removed by the phase.

AttributeStatdict type#

Activity for one (attr, value) pair in LowerAttributes.

FieldTypeDescription
attrstr

Stable attribute name, such as "math_font" or "text_size".

valuestr

Stable value token for that attribute.

consumedAttributeFormCounts

Input forms consumed by the phase.

redundantAttributeFormCounts

Consumed forms that did not change the emitted attribute state.

emittedAttributeFormCounts

Canonical forms emitted by the phase.

AttributeFormCountsdict type#

Counts of one attribute form, split by carrier.

FieldTypeDescription
declarativesint

Count carried by declarative markup such as {\bf ...}.

prefixesint

Count carried by prefix commands such as \mathbf{...}.

RewriteReportdict type#

The Rewrite phase report.

FieldTypeDescription
iterationsint

Fixed-point passes, including the final unchanged convergence check. Zero when the phase is disabled or no rules are scheduled.

ruleslist[RewriteRuleStat]

Rules that recorded activity, sorted by key.

RewriteRuleStatdict type#

One rewrite rule's outcome for a single call.

FieldTypeDescription
keystr

Stable rule key.

applied_countint

Times the rule applied.

skipped_countint

Times the scheduler matched the rule and apply returned skipped.

FinalizeAstReportdict type#

The FinalizeAst phase report.

FieldTypeDescription
prime_run_mergesint

Contiguous Prime runs merged. Each run counts once.

text_normalizationsint

Text segments or text argument slots whose content changed.

FlattenGroupsReportdict type#

The FlattenGroups phase report.

FieldTypeDescription
actionsFlattenGroupsActionCounts

Flatten actions that changed the tree.

guard_hitsFlattenGroupsGuardCounts

Preserve-guard hits for this call.

FlattenGroupsActionCountsdict type#

Flatten actions that changed the tree.

FieldTypeDescription
removed_emptyint

Empty groups removed.

replaced_single_childint

Groups replaced by their only child.

inlined_multi_childint

Multi-child groups inlined into the parent.

unwrapped_slotint

Slot wrappers removed.

FlattenGroupsGuardCountsdict type#

Preserve-guard hits for one FlattenGroups run.

A hit is the first guard branch reached for a group, plus the scripted-base sub-count. command_contact_via_scripted_base is also included in command_contact. These counters are not a partition of preserved groups.

FieldTypeDescription
unknown_command_argumentsint

Possible arguments following an unknown command.

argument_captureint

Groups whose removal would let a command before or inside them read the next token as an omitted optional or delimited argument.

declarative_scopeint

Declarative-scope protection.

script_baseint

Script-base protection.

env_bodyint

Environment-body protection.

infix_scopeint

Infix-scope protection.

command_contactint

Command-contact protection.

command_contact_via_scripted_baseint

Command contact established through a scripted base.

empty_groupint

Empty-group protection.

lone_atom_spacing_charint

Single spacing-character protection.

leading_atom_spacing_charint

Leading spacing-character protection.

delimited_pairint

Delimited-pair protection.

TransformProfiletype#

Literal['authoring', 'faithful', 'corpus', 'equiv']

Normalization profile name: "authoring", "faithful", "corpus", or "equiv".