Skip to main content

Parsing and Serializing LaTeX

The Compute Engine manipulates MathJSON expressions. It can also convert LaTeX strings to MathJSON expressions (parsing) and output MathJSON expressions as LaTeX string (serializing)

Note

In this documentation, functions such as ce.box() and ce.parse() require a ComputeEngine instance which is denoted by a ce. prefix.
Functions that apply to a boxed expression, such as expr.simplify() are denoted with a expr. prefix.

To create a new instance of the Compute Engine, use the new ComputeEngine() constructor.

const ce = new ComputeEngine();

To input math using an interactive mathfield, use the Mathfield library.

A <math-field> DOM element works like a <textarea> in HTML, but for math. It provides its content as a LaTeX string, ready to be used with the Compute Engine.

All the mathfields on the page share a Compute Engine instance, which is available as MathfieldElement.computeEngine.

const ce = MathfieldElement.computeEngine;

You can associate a customized compute engine with the mathfields in the document:

const ce = new ComputeEngine();
MathfieldElement.computeEngine = ce;
console.log(mfe.expression.json);

To parse a LaTeX string as a MathJSON expression, call the ce.parse() function.

console.log(ce.parse("5x + 1").json);
// ➔ ["Add", ["Multiply", 5, "x"], 1]

By default, ce.parse() return a canonical expression. To get a non-canonical expression instead, use the {canonical: false} option: The non-canonical form is closer to the literal LaTeX input.

ce.parse("\\frac{7}{-4}").json;
// ➔ ["Rational", -7, 4]

ce.parse("\\frac{7}{-4}", { canonical: false }).json;
// ➔ ["Divide", 7, -4]

The Compute Engine Natural Parser

Unlike a programming language, mathematical notation is surprisingly ambiguous and full of idiosyncrasies. Mathematicians frequently invent new notations, or have their own preferences to represent even common concepts.

The Compute Engine Natural Parser interprets expressions using the notation you are already familiar with. Write as you would on a blackboard, and get back a semantic representation as an expression ready to be processed.

LaTeXMathJSON
\sin 3t + \cos 2t
\sin 3t + \cos 2t
["Add", ["Sin", ["Multiply", 3, "t"]], ["Cos", ["Multiply", 2, "t"]]]
\int \frac{dx}{x}
\int \frac{dx}{x}
["Integrate", ["Divide", 1, "x"], "x"]
123.4(567)
123.4(567)
123.4(567)
123.4\overline{567}
123.4\overline{567}
123.4(567)
\vert a+\vert b\vert+c\vert
|a+|b|+c|
["Abs", ["Add", "a", ["Abs", "b"], "c"]]
\vert\vert a\vert\vert+\vert b\vert
||a||+|b|
["Add", ["Norm", "a"], ["Abs", "b"]]

The Compute Engine Natural Parser will apply maximum effort to parse the input string as LaTeX, even if it includes errors. If errors are encountered, the resulting expression will have its expr.isValid property set to false. An ["Error"] expression will be produced where a problem was encountered. To get the list of all the errors in an expression, use expr.errors which will return an array of ["Error"] expressions.

Serializing to LaTeX

To serialize an expression to a LaTeX string, read the expr.latex property.

console.log(ce.box(["Add", ["Power", "x", 3], 2]).latex);
// ➔ "x^3 + 2"

Customizing Parsing

The LaTeX parsing can be customized by providing a ParseLatexOptions object as the second argument to the ce.parse() function.

Customizing Parsing of Numbers

See the Number Formatting section for details on how to customize the parsing of numbers. Most of the same options are available for parsing as for serialization.

Other Parsing Options

KeyDescription
skipSpaceIf true, ignore space characters in a math zone. Default is true.
parseNumbersWhen parsing a decimal number, e.g. 3.1415:
- "auto" or "decimal": if a decimal number, parse it as an approximate decimal number with a whole part and a fractional part
- "rational": if a decimal number, parse it as an exact rational number with a numerator and a denominator. If not a decimal number, parse it as a regular number.
- "never": do not parse numbers, instead return each token making up the number (minus sign, digits, decimal marker, etc...).

Note: if the number includes repeating digits (e.g. 1.33(333)), it will be parsed as a decimal number even if this setting is "rational". Default: "auto"
preserveLatexIf true, the expression will be decorated with the LaTeX fragments corresponding to each element of the expression. The top-level expression, that is the one returned by parse(), will include the verbatim LaTeX input that was parsed. The sub-expressions may contain a slightly different LaTeX, for example with consecutive spaces replaced by one, with comments removed, and with some low-level LaTeX commands replaced, for example \egroup and \bgroup. Default: false

getIdentifierType

This handler is invoked when the parser encounters an identifier that has not yet been declared.

The identifier argument is a valid identifier.

The handler can return:

  • "variable": the identifier is a variable
  • "function": the identifier is a function name. If an apply function operator (typically, parentheses) follow, they will be parsed as arguments to the function.
  • "unknown": the identifier is not recognized.
console.info(ce.parse("f(x)", { getIdentifierType: (identifier) => { if (identifier === "f") { return "function"; } return "unknown"; }, }).json);

parseUnexpectedToken

This handler is invoked when the parser encounters a token that it does not know how to handle.

The lhs argument is the previous token, if any.

The handler can access the unexpected token with parser.peek. If it is a token that should be recognized, the handler can consume it by calling parser.nextToken().

The handler should return an expression or null if the token is not recognized.

console.info(ce.parse("3\\frac{1}{\\foo}", { parseUnexpectedToken: (lhs, parser) => { if (parser.peek === '\\foo') { parser.nextToken(); return "foo"; } return null; }, }).json);

Customizing Serialization

While expr.latex provides a simple, default serialization to LaTeX, it may not always be the most suitable for your needs.

To customize the serialization to LaTeX, use the expr.toLatex() method.

The argument of the expr.toLatex() method is a SerializeLatexOptions object that can be used to customize the serialization. The keys are explained in the sections below.

Number Formatting

KeyDescription
fractionalDigitsThe number of decimal places to use when formatting numbers. Use "max" to include all available digits and "auto" to use the same precision as for evaluation. Default is "auto".
notationThe notation to use for numbers. Use "auto", "scientific", or "engineering". Default is "auto".
avoidExponentsInRangeA tuple of two values representing a range of exponents. If the exponent for the number is within this range, a decimal notation is used. Otherwise, the number is displayed with an exponent. Default is [-6, 20].
digitGroupSeparatorThe LaTeX string used to separate group of digits, for example thousands. Default is "\,". To turn off group separators, set to "". If a string tuple is provide, the first string is used to group digits in the whole part and the second string to group digits in the fractional part.
digitGroupSizeThe number of digits in a group. If set to "lakh" the digits are in groups of 2, except for the last group which has 3 digits. If a tupe is provided, the first element is used for the whole part and the second element for the fractional part. Default is 3.
exponentProductA LaTeX string inserted before an exponent, if necessary. Default is "\cdot".
beginExponentMarkerA LaTeX string used as template to format an exponent. Default value is "10^{".
endExponentMarkerA LaTeX string used as template to format an exponent. Default value is "}".
truncationMarkerA LaTeX string used to indicate that a number has more precision than what is displayed. Default is "\ldots".
repeatingDecimalThe decoration around repeating digits. Valid values are "auto", "vinculum", "dots", "parentheses", "arc" and "none". Default is "auto".
console.log(ce.parse("\\pi").N().toLatex({ fractionalDigits: 6, }));
console.log(ce.box(700).toLatex({ notation: "scientific", avoidExponentsInRange: null, exponentProduct: "\\times" })); // ➔ "7\times10^{2}" console.log(ce.box(123456.789).toLatex({ notation: "scientific", avoidExponentsInRange: null, exponentProduct: "\\times", })); // ➔ "1.234\,567\,89\times10^{5}"

Customizing the Decimal Separator

The world is about evenly split between using a dot or a comma as a decimal marker.

By default, the ComputeEngine is configured to use a dot, i.e. 3.1415.

To use a comma as a decimal marker, set the decimalSeparator option:

console.log(ce.box(3.141).toLatex({ decimalSeparator: "{,}" }));

Note that in LaTeX, in order to get the correct spacing around the comma, it must be surrounded by curly brackets.

Special Numbers and Symbols

KeyDescription
positiveInfinityThe LaTeX string used to represent positive infinity. Default is "\infty" \infty.
negativeInfinityThe LaTeX string used to represent negative infinity. Default is "-\infty" -\infty.
imaginaryUnitThe LaTeX string used to represent the imaginary unit symbol. Default is "\imaginaryI" \imaginaryI
notANumberThe LaTeX string to represent the number NaN. Default value is "\operatorname{NaN}" \operatorname{NaN}.
prettifyIf true, the output will be formatted to be more human-readable. Default is false.
invisibleMultiplyA LaTeX string to use as an invisible multiply operator between expressions. Use "\cdot" to use a \cdot. Default is "".
invisiblePlusA LaTeX string to use as an invisible plus operator between expressions, for example with mixed numbers. Leave it empty to join the main number and the fraction. Use "+" to insert an explicit + operator between them. Default is "".
multiplyA LaTeX string to use as a multiply operator between expressions. Use "\cdot" to use a \cdot. Default is "\times" \times.
missingSymbolA LaTeX string to use when a symbol is missing. Default is "\placeholder{}" \placeholder{}.
console.log(ce.parse("3\\frac{1}{4}").toLatex({ invisiblePlus: "+" }));

Customizing the Serialization Style

In addition, the keys applyFunctionStyle, groupStyle, powerStyle, rootStyle, fractionStyle, logicStyle and numericSetStyle can be used to customize the serialization of specific types of expressions.

For example, a group can be indicated by simple parentheses, or by a \left...\right command. A fraction can be indicated by a \frac{}{} command or by a {}{}^{-1}.

The Compute Engine includes some built-in defaults, but they can be customized as desired. These style options are functions that take an expression fragment and return a string indicating the desired style.

For example to always represent fractions with a solidus (forward slash) use:

console.log(ce.parse("\\frac{3}{5}").toLatex({ fractionStyle: () => "quotient" })); console.log(ce.parse("\\frac{3}{5}").toLatex({ fractionStyle: () => "inline-solidus" }));

The style option handler has two arguments:

  • the expression fragment being styled
  • the depth/level of the expression in the overall expression

For example, to serialize fractions deeper than level 0 as an inline solidus:

console.log(ce.parse("\\frac{a}{b}+\\sqrt{\\frac{c}{d}}").toLatex({ fractionStyle: (expr, level) => level > 0 ? "inline-solidus" : "quotient" }));

Function Application

To customize the serialization of function application, use the applyFunctionStyle style option handler.

console.log(ce.parse("\\sin x").toLatex({ applyFunctionStyle: () => "big" }));
"paren"\sin(x)\sin(x)
"leftright"\sin\left(x\right)\sin\left(x\right)
"big"\sin\bigl(x\bigr)\sin\bigl(x\bigr)
"none"\sin x\sin x

Group

To customize the serialization of groups, use the groupStyle style option handler.

console.log(ce.parse("(a+b)", {canonical: false}).toLatex({ groupStyle: () => "big" }));
"paren"x(a+b)x(a+b)
"leftright"x\left(a+b\right)x\left(a+b\right)
"big"x\bigl(a+b\bigr)x\bigl(a+b\bigr)
"none"x a+b x a+b

Root

To customize how roots are serialized, use the rootStyle style option handler.

console.log(ce.parse("\\sqrt{2}").toLatex({ rootStyle: () => "solidus" }));
"radical"
"quotient"
"solidus"

Fraction

To customize how fractions are serialized, use the fractionStyle style option handler.

console.log(ce.parse("\\frac{3}{5}").toLatex({ fractionStyle: () => "nice-solidus" }));
"quotient"
"inline-solidus"
"nice-solidus"
"reciprocal"
"factor"

Logic

To customize how logic expressions are serialized, use the logicStyle style option handler.

console.log(ce.parse("p\\land q").toLatex({ logicStyle: () => "word" }));
"word"a \text{ and } ba \text{ and } b
"boolean"
"uppercase-word"p \text{ AND } qp \text{ AND } q
"punctuation"

Power

To customize how powers are serialized, use the powerStyle style option handler.

console.log(ce.parse("x^2").toLatex({ powerStyle: () => "solidus" }));
"root"
"solidus"
"quotient"

Numeric Sets

To customize how numeric sets are serialized, use the numericSetStyle style option handler.

console.log(ce.parse("x \\in \\Z").toLatex({ numericSetStyle: () => "interval" }));
"compact"
"regular"
"interval"
"set-builder"

Customizing the LaTeX Dictionary

The MathJSON format is independent of any source or target language (LaTeX, MathASCII, Python, etc...) or of any specific interpretation of the identifiers used in a MathJSON expression ("Pi", "Sin", etc...).

A LaTeX dictionary defines how a MathJSON expression can be expressed as a LaTeX string (serialization) or constructed from a LaTeX string (parsing).

The Compute Engine includes a default LaTeX dictionary to parse and serialize common math expressions.

It includes definitions such as:

  • "The Power function is represented as "x^{n}""
  • "The Divide function is represented as "\frac{x}{y}"".

Note that the dictionary will include LaTeX commands as triggers. LaTeX commands are usually prefixed with a backslash, such as \frac or \pm. It will also reference MathJSON identifiers. MathJSON identifiers are usually capitalized, such as Divide or PlusMinus and are not prefixed with a backslash.

To extend the LaTeX syntax update the latexDictionary property of the Compute Engine

ce.latexDictionary = [ // Include all the entries from the default dictionary... ...ce.latexDictionary, // ...and add the `\smoll{}{}` command { // The parse handler below will be invoked when this LaTeX command // is encountered latexTrigger: '\\smoll', parse: (parser) => { // We're expecting two arguments, so we're calling // `parseGroup()` twice. If `parseGroup()` returns `null`, // we assume that the argument is missing. return [ "Divide", parser.parseGroup() ?? ["Error", "'missing'"], parser.parseGroup() ?? ["Error", "'missing'"], ]; }, }, ]; console.log(ce.parse('\\smoll{1}{5}').json); // The "Divide" get represented as a "Rational" by default when // both arguments are integers. // ➔ ["Rational", 1, 5]
caution

Do not modify the ce.latexDictionary array directly. Instead, create a new array that includes the entries from the default dictionary, and add your own entries. Later entries will override earlier ones, so you can replace or modify existing entries by providing a new definition for them.

LaTeX Dictionary Entries

Each entry in the LaTeX dictionary is an object with the following properties:

  • kind

    The kind of expression associated with this entry.

    Valid values are prefix, postfix, infix, expression, function, symbol, environment and matchfix.

    If not provided, the default is expression.

    The meaning of the values and how to use them is explained below.

    Note that it is possible to provide multiple entries with the same latexTrigger or identifierTrigger but with different kind properties. For example, the + operator is both an infix (binary) and a prefix (unary) operator.

  • latexTrigger

    A LaTeX fragment that will trigger the entry. For example, ^{+} or \mathbb{D}.

  • identifierTrigger

    A string, usually wrapped in a LaTeX command, that will trigger the entry.

    For example, if identifierTrigger is floor, the LaTeX command \mathrm{floor} or \operatorname{floor} will trigger the entry.

    Only one of latexTrigger or identifierTrigger should be provided.

    If kind is "environment", only identifierTrigger is valid, and it represents the name of the environment.

    If kind is matchfix, both openTrigger and closeTrigger must be provided instead.

  • parse

    A handler that will be invoked when the trigger is encountered in the LaTeX input.

    It will be passed a parser object that can be used to parse the input.

    The parse handler is invoked when the preconditions for the entry are met. For example, an infix entry will only be invoked if the trigger is encountered in the LaTeX input and there is a left-hand side to the operator.

    The signature of the parse handler will vary depending on the kind. For example, for an entry of kind infix the left-hand side argument will be passed to the parse handler. See below for more info about parsing for each kind.

    The parse handler should return a MathJSON expression or null if the expression is not recognized. When null is returned, the Compute Engine Natural Parser will backtrack and attempt to find another handler that matches the current token. If there can be no ambiguity and the expression is not recognized, the parse handler should return an ["Error"] expression. In general, it is better to return null and let the Compute Engine Natural Parser attempt to find another handler that matches the current token. If none is found, an ["Error"] expression will be returned.

  • serialize

    A handler that will be invoked when the expr.latex property is read. It will be passed a Serializer object that can be used to serialize the expression. The serialize handler should return a LaTeX string. See below for more info about serialization.

    If a serialize handler is provided, the name property must be provided as well.

  • name

    The name of the MathJSON identifier associated with this entry.

    If provided, a default parse handler will be used that is equivalent to: parse: name.

    It is possible to have multiple definitions with the same triggers, but the name property must be unique. The record with the name property will be used to serialize the expression. A serialize handler is invalid if the name property is not provided.

    The name property must be unique. However, multiple entries can have different triggers that produce the same expression. This is useful for synonyms, such as \operatorname{floor} and \lfloor...\rfloor.

Expressions

The most general type of entry is one of kind expression. If no kind property is provided, the kind is assumed to be expression.

For entries of kind expression the parse handler is invoked when the trigger is encountered in the LaTeX input. The parse handler is passed a parser object that can be used to parse the input.

The kind expression is suitable for a simple symbol, for example a mathematical constant. It can also be used for more complex constructs, such as to parse a series of tokens representing an integral expression. In this case, the parse handler would be responsible for parsing the entire expression and would use the parser object to parse the tokens.

If the tokens are not recognized, the parse handler should return null and the parser will continue to look for another handler that matches the current token.

Functions

The function kind is a special case of expression where the expression is a function, possibly using multi-character identifiers, as in \operatorname{concat}.

Unlike an expression entry, after the parse handler is invoked, the parser will look for a pair of parentheses to parse the arguments of the function and apply them to the function.

The parse handler should return the identifier corresponding to the function, such as Concatenate. As a shortcut, the parse handler can be provided as an Expression. For example:

{
kind: "function",
identifierTrigger: "concat",
parse: "Concatenate"
}

Operators: prefix, infix, postfix

The prefix, infix and postfix kinds are used for operators.

Entries for prefix, infix and postfix operators must include a precedence property. The precedence property is a number that indicates the precedence of the operator. The higher the number, the higher the precedence, that is the more "binding" the operator is.

For example, the precedence of the Add operator is 275 (ADDITION_PRECEDENCE), while the precedence of the Multiply operator is 390 (MULTIPLICATION_PRECEDENCE).

In 1 + 2 * 3, the Multiply operator has a higher precedence than the Add operator, so it is applied first.

The precedence range is an integer from 0 to 1000.

Here are some rough ranges for the precedence:

  • 800: prefix and postfix operators: \lnot etc...
    • POSTFIX_PRECEDENCE = 810: !, '
  • 700: some arithmetic operators
    • EXPONENTIATION_PRECEDENCE = 700: ^
  • 600: some binary operators
    • DIVISION_PRECEDENCE = 600: \div
  • 300: some logic and arithmetic operators: \land, \lor etc...
    • MULTIPLICATION_PRECEDENCE = 390: \times
  • 200: arithmetic operators, inequalities:
    • ADDITION_PRECEDENCE = 275: + -
    • ARROW_PRECEDENCE = 270: \to \rightarrow
    • ASSIGNMENT_PRECEDENCE = 260: :=
    • COMPARISON_PRECEDENCE = 245: \lt \gt
    • 241: \leq
  • 0: ,, ;, etc...

The infix kind is used for binary operators (operators with a left-hand-side and right-hand-side).

The parse handler will be passed a parser object and the left-hand side of the operator, for postfix and infix operators.

The parser object can be used to parse the right-hand side of the expression.

{
kind: "infix",
latexTrigger: '\\oplus',
precedence: ADDITION_PRECEDENCE,
parse: (parser, lhs) => {
return ["Concatenate", lhs, parser.parseExpression()];
},
}

The prefix kind is used for unary operators.

The parse handler will be passed a parser object.

{
kind: "prefix",
latexTrigger: '\\neg',
precedence: ADDITION_PRECEDENCE,
parse: (parser, lhs) => {
return ["Negate", lhs];
},
}

The postfix kind is used for postfix operators. The parse handler will be passed a parser object and the left-hand side of the operator.

{
kind: "postfix",
latexTrigger: '\\!',
parse: (parser, lhs) => {
return ["Factorial", lhs];
},
}

Environment

The environment kind is used for LaTeX environments.

The `identifierTrigger property in that case is the name of the environment.

The parse handler wil be passed a parser object. The parseTabular() method can be used to parse the rows and columns of the environment. It returns a two dimensional array of expressions.

The parse handler should return a MathJSON expression.

{
kind: "environment",
identifierTrigger: "matrix",
parse: (parser) => {
const content = parser.parseTabular();
return ["Matrix", ["List", content.map(row => ["List", row.map(cell => cell)])]];
},
}

Matchfix

The matchfix kind is used for LaTeX commands that are used to enclose an expression.

The openTrigger and closeTrigger indicate the LaTeX commands that enclose the expression. The parse handler is passed a parser object and the "body" (the expression between the open and close delimiters). The parse handler should return a MathJSON expression.

{
kind: "matchfix",
openTrigger: '\\lvert',
closeTrigger: '\\rvert',
parse: (parser, body) => {
return ["Abs", body];
},
}

Parsing

When parsing a LaTeX string, the first step is to tokenize the string according to the LaTeX syntax. For example, the input string \frac{ab}{10} will result in the tokens ["\\frac", "{", "a", "b", "}", "{", "1", "0", "}"].

Note that each LaTeX command is a single token, but that digits and ordinary letters are each separate tokens.

The parse handler is invoked when the trigger is encountered in the LaTeX token strings.

A common case is to return from the parse handler a MathJSON identifier for a symbol or function.

For example, let's say you wanted to map the LaTeX command \div to the MathJSON Divide function. You would write:

{
latexTrigger: '\\div',
parse: (parser) => {
return "Divide";
},
}

As a shortcut, you can also write:

{
latexTrigger: '\\div',
parse: () => "Divide"
}

Or even more succintly:

{
latexTrigger: '\\div',
parse: "Divide"
}

The LaTeX \div(1, 2) would then produce the MathJSON expression ["Divide", 1, 2]. Note that the arguments are provided as comma-separated, parenthesized expressions, not as LaTeX arguments in curly brackets.

If you need to parse some more complex LaTeX syntax, you can use the parser argument of the parse handler. The parser object has numerous methods to help you parse the LaTeX string:

  • parser.peek is the current token.
  • parser.index is the index of the current token. If backtracking is necessary, it is possible to set the index to a previous value.
  • parser.nextToken() returns the next token and advances the index.
  • parser.skipSpace() in LaTeX math mode, skip over "space" which includes space tokens, and empty groups {}. Whether space tokens are skipped or not depends on the skipSpace option.
  • parser.skipVisualSpace() skip over "visual space" which includes space tokens, empty groups {}, and commands such as \, and \!.
  • parser.match(token: LatexToken) return true if the next token matches the argument, or null otherwise.
  • parser.matchAll(tokens) return true if the next tokens match the argument, an array of tokens, or null otherwise.
  • parser.matchAny(tokens: LatexToken[]) return the next token if it matches any of the token in the argument or null otherwise.
  • parser.matchChar() return the next token if it is a plain character (e.g. "a", '+'...), or the character corresponding to a hex literal (^^ and ^^^^) or the \char and \unicode commands
  • parser.parseGroup() return an expression if the next token is a group begin token { followed by a sequence of LaTeX tokens until a group end token } is encountered, or null otherwise.
  • parser.parseToken() return an expression if the next token can be parsed as a MathJSON expression, or null otherwise. This is useful when the argument of a LaTeX command can be a single token, for example for \sqrt5. Some, but not all, LaTeX commands accept a single token as an argument.
  • parser.parseOptionalGroup() return an expression if the next token is an optional group begin token [ followed by a sequence of LaTeX tokens until an optional group end token ] is encountered, or null otherwise.
  • parser.parseExpression() return an expression if the next tokens can be parsed as a MathJSON expression, or null otherwise. After this call, there may be some tokens left to parse.
  • parser.parseArguments() return an array of expressions if the next tokens can be parsed as a sequence of MathJSON expressions separated by a comma, or null otherwise. This is useful to parse the argument of a function. For example with f(x, y, z), the arguments would be [x, y, z].

If the parse() handler returns null, the parser will continue to look for another handler that matches the current token.

Note there is a pattern in the names of the methods of the parser. The match prefix means that the method will return the next token if it matches the argument, or null otherwise. These methods are more primitive. The parse prefix indicates that the method will return a MathJSON expression or null.

The most common usage is to call parser.parseGroup() to parse a group of tokens as an argument to a LaTeX command.

For example:

{
latexTrigger: '\\div',
parse: (parser) => {
return ["Divide", parser.parseGroup(), parser.parseGroup()];
},
}

In this case, the LaTeX input \div{1}{2} would produce the MathJSON expression ["Divide", 1, 2] (note the use of the curly brackets, rather than the parentheses in the LaTeX input).

If we wanted instead to treat the \div command as a binary operator, we could write:

{
latexTrigger: '\\div',
kind: "infix",
parse: (parser, lhs) => {
return ["Divide", lhs, parser.parseExpression()];
},
}

By using the kind: "infix" option, the parser will automatically insert the left-hand side of the operator as the first argument to the parse handler.

Serializing

When serializing a MathJSON expression to a LaTeX string, the serialize() handler is invoked. You must specify a name property to associate the serialization handler with a MathJSON identifier.

{
name: "Concatenate",
latexTrigger: "\\oplus",
serialize: (serializer, expr) =>
"\\oplus" + serializer.wrapArguments(expr),
evaluate: (ce, args) => {
let result = '';
for (const arg of args) {
val = arg.numericValue;
if (val === null || ce.isComplex(val) || Array.isArray(val)) return null;
if (ce.isBignum(val)) {
if (!val.isInteger() || val.isNegative()) return null;
result += val.toString();
} else if (typeof val === "number") {
if (!Number.isInteger(val) || val < 0) return null;
result += val.toString();
}
}
return ce.parse(result);
},
}

In the example above, the LaTeX command \oplus is associated with the Concatenate function. The serialize() handler will be invoked when the expr.latex property is read.

Note that we did not provide a parse() handler: if a name property is provided, a default parse handler will be used that is equivalent to: parse: name.

It is possible to have multiple definitions with the same triggers, but the name property must be unique. The record with the name property will be used to serialize the expression. A serialize handler is invalid if the name property is not provided.

Using a New Function with a Mathfield

You may also want to use your new function with a mathfield.

First you need to define a LaTeX macro so that the mathfield knows how to render this command. Let's define the \smallfrac macro.

const mfe = document.querySelector("math-field");

mfe.macros = {
...mfe.macros,
smallfrac: {
args: 2,
def: "{}^{#1}\\!\\!/\\!{}_{#2}",
},
};

The content of the def property is a LaTeX fragment that will be used to render the \\smallfrac command.

The #1 token in def is a reference to the first argument and #2 to the second one.

You may also want to define an inline shortcut to make it easier to input the command.

With the code below, we define a shortcut "smallfrac".

When typed, the shortcut is replaced with the associated LaTeX.

The #@ token represents the argument to the left of the shortcut, and the #? token represents a placeholder to be filled by the user.

mfe.inlineShortcuts = {
...mfe.inlineShortcuts,
smallfrac: "\\smallfrac{#@}{#?}",
};

You can now parse the input from a mathfield using:

console.log(ce.parse(mfe.value).json);

Alternatively, the customized compute engine can be associated with the mathfields in the document:

MathfieldElement.computeEngine = ce;
console.log(mfe.getValue("math-json"));