A tool that rewrites formulas — expanding project shorthands, swapping deprecated commands, normalizing markup — needs to walk a parsed Document, find the nodes it cares about, and replace them with new structure. TeXForm gives you that working tree directly: you read through node handles, stage replacement subtrees with create_* constructors, attach them, and serialize the result back to LaTeX. Edits are validated as you make them and return a structured error on misuse rather than corrupting the tree, and a tree that came back with parse errors is read-only — the model carried over from Parsing.
The task below expands a project shorthand: every \R in the source becomes \mathbb{R}. The parser does not know \R, so under the default lenient configuration it survives as a plain command node — exactly the kind of node a rewriting tool wants to find and replace.
import texform
doc = texform.Parser().parse(r"x \in \R, y \in \R")["document"]
print("before:", doc.to_latex())
# Find the shorthand command, then replace each occurrence with a freshly
# built \mathbb{R}. A slot takes a detached node or source text.
for node in doc.find_commands("R"):
letter = doc.create_char("R") # stage a detached child
replacement = doc.create_command("mathbb", [letter])
doc.replace_with(node, replacement)
print("after :", doc.to_latex())before: x \in \R , y \in \R
after : x \in \mathbb { R } , y \in \mathbb { R }import { Parser } from 'texform';
const doc = new Parser().parse('x \\in \\R, y \\in \\R').document;
console.log('before:', doc.toLatex());
// Find the shorthand command, then replace each occurrence with a freshly
// built \mathbb{R}. A slot takes a detached node or source text.
for (const node of doc.findCommands('R')) {
const letter = doc.createChar('R'); // stage a detached child
const replacement = doc.createCommand('mathbb', [letter]);
doc.replaceWith(node, replacement);
}
console.log('after :', doc.toLatex());before: x \in \R , y \in \R
after : x \in \mathbb { R } , y \in \mathbb { R }The workflow is the same for any rewrite: traverse with finders like find_commands (or the navigation accessors children, descendants, parent), build detached nodes with the create_* constructors, attach them with replace_with, append_child, insert_before, and friends, then read the result with to_latex(). Staged nodes must be detached when you attach them — reusing an argument that is still bound to another node fails, so build fresh content rather than re-parenting live nodes.
Because edits are fallible, misuse surfaces as a structured error instead of a silent corruption or a crash. The clearest case is a tree that parsed with errors: it is read-only, and every editing method rejects the attempt.
import texform
doc = texform.Parser().parse(r"x^")["document"] # incomplete superscript
print(doc.has_errors(), doc.is_read_only())
try:
doc.append_child(doc.root(), doc.create_char("y"))
except texform.EditError as exc:
print("EditError:", exc)True True EditError: document is read-only
import { Parser } from 'texform';
const doc = new Parser().parse('x^').document; // incomplete superscript
console.log(doc.hasErrors(), doc.isReadOnly());
try {
doc.appendChild(doc.root(), doc.createChar('y'));
} catch (exc) {
console.log('error:', exc.constructor.name, '-', exc.message);
}true true error: TexformEditError - document is read-only
The other guard worth knowing is node ownership: a node belongs to the document that created it, and an edit that mixes two documents is rejected before it can corrupt either tree. Python raises EditError with the message node belongs to a different document, JavaScript throws TexformEditError for the same misuse, and in both the rejection happens before either tree is touched. Building replacements with the create_* constructors on the document you are editing, as above, keeps you clear of it.
For the full set of editing methods, the create_* constructors, and every EditError variant, see the Document API; for the read model these edits build on, see Parsing.
Before
to_latex() x \in \R
After
to_latex() x \in \mathbb { R }
Next steps#
Every finder, create_* constructor, and attachment method on the tree.
ParsingThe read model, the three parse states, and why error trees are read-only.
Configure output styleChoose spaced or compact LaTeX output.
TroubleshootingOwnership, read-only trees, and the other edit failures, with real messages.