The parser knows the argument structure of every command in its packages, but real corpora are full of project macros the knowledge base has never seen. Left unknown, a macro like \mycmd[opt]{body} parses as a bare command and its bracketed argument leaks out as loose tokens. You fix that by registering the command yourself: describe its signature as an argspec string, add it to a knowledge base as a context item, and a parser using that knowledge base reads its arguments exactly as if the command were built in.
The workflow has three steps: write the argspec, check it with validate_argspec, then build a context item and pass it to the knowledge base the parser uses. The example registers \mycmd with the signature o m — one optional bracketed argument followed by one mandatory group.
import texform
# 1. Write the signature and confirm it parses to the slots you expect.
check = texform.validate_argspec("o m")
print("valid:", check["valid"], "arg_count:", check["arg_count"])
# 2. Build a context item for the command. Python keys are snake_case.
item = {
"target": "command",
"name": "mycmd",
"kind": "prefix",
"allowed_mode": "math",
"argspec": "o m",
}
# 3. Add it to a knowledge base, build a parser on it, and parse a formula.
kb = texform.KnowledgeBase(items=[item])
parser = texform.Parser(knowledge_base=kb)
doc = parser.parse(r"\mycmd[opt]{body} + \mycmd{bare}")["document"]
print("latex:", doc.to_latex())
for node in doc.find_commands("mycmd"):
slots = [None if s is None else s["kind"] for s in node.arg_slots()]
print("slots:", slots)valid: True arg_count: 2
latex: \mycmd [ o p t ] { b o d y } + \mycmd { b a r e }
slots: ['Math', 'Math']
slots: [None, 'Math']import { KnowledgeBase, Parser, validateArgspec } from 'texform';
// 1. Write the signature and confirm it parses to the slots you expect.
const check = validateArgspec('o m');
console.log('valid:', check.valid, 'argCount:', check.argCount);
// 2. Build a context item for the command. JavaScript keys are camelCase.
const item = {
target: 'command',
name: 'mycmd',
kind: 'prefix',
allowedMode: 'math',
argspec: 'o m',
};
// 3. Add it to a knowledge base, build a parser on it, and parse a formula.
const knowledgeBase = new KnowledgeBase({ items: [item] });
const parser = new Parser({ knowledgeBase });
const doc = parser.parse('\\mycmd[opt]{body} + \\mycmd{bare}').document;
console.log('latex:', doc.toLatex());
for (const node of doc.findCommands('mycmd')) {
const slots = node.argSlots().map((s) => (s === null ? null : s.kind));
console.log('slots:', JSON.stringify(slots));
}valid: true argCount: 2
latex: \mycmd [ o p t ] { b o d y } + \mycmd { b a r e }
slots: ["Math","Math"]
slots: [null,"Math"]The signature is honored: \mycmd[opt]{body} fills both slots, while \mycmd{bare} leaves the optional slot empty (None in Python, null in JavaScript) and fills only the mandatory one. The parser bound [opt] as the optional argument instead of letting it fall through as stray characters, which is the whole point of registering the command.
Commands are not the only thing you can inject. A context item is a tagged record, and the target field selects one of three kinds: "command" as above, "environment" for a custom \begin{...} block (which also takes a body_mode, since an environment body can be math or text), and "delimiter" for a delimiter-control name usable after \left and \right. They all go into the same items list of the KnowledgeBase constructor.
import texform
item = {
"target": "environment",
"name": "myenv",
"allowed_mode": "math",
"body_mode": "math",
"argspec": "m",
}
kb = texform.KnowledgeBase(items=[item])
doc = texform.Parser(knowledge_base=kb).parse(r"\begin{myenv}{c} x \end{myenv}")["document"]
print(doc.to_latex())\begin {myenv} { c } x \end {myenv}What you cannot inject is a rewrite rule. Context items extend the parser's knowledge of what a command's arguments look like; they do not tell the transform engine what to turn that command into. The rule set is compiled in, and the only user-facing control over it is switching rules off — see Disable a rewrite rule.
For the full argspec notation — every slot kind, delimited forms, and the extensions to xparse — see the argspec reference; for the context-item shape and construction options, see the KnowledgeBase API.
Next steps#
Every slot kind, delimited form, and TeXForm extension to xparse notation.
KnowledgeBase APIContext-item shapes and the rest of the knowledge-base construction options.
Disable a rewrite ruleThe one piece of the transform engine you can adjust from the outside.
Clean OCR/MER outputFind the unknown commands worth registering in the first place.