LaTeX::Grammar
Raku package with a parser and interpreters of LaTeX code.
The implemented interpretation formats are:
The MathJSON interpreter was the first to be implemented, and it is the most important one -- the rest are derived from it.
The primary motivation to make this package is:
- Having a readily available LaTeX parser and interpreters (in Raku) gives the ability to work easier with systems for mathematical (symbolic) computations in Raku sessions.
For example, the LaTeX parser can be used to recognize (detect or extract) LaTeX expressions in texts and make certain computations with them.
As for the interpreters:
Installation
From Zef ecosystem:
zef install LaTeX::Grammar
Usage examples
Interpretation to MathJSON (default interpreter):
use LaTeX::Grammar;
latex-interpret('\frac{1}{x}=\sqrt{y}')
# [Equal [Divide 1 x] [Root y 2]]
A table that show interpretations to different formats:
use Data::Translators;
use Data::Reshapers;
my @formulas = (
'\frac{-1214}{117}',
'\\sqrt{4 * x^2 + 12 * x + 9}',
'\\int_{0}^{1} x^{2} d x',
'\\sum_{n=1}^{10} n^2',
'\\lim_{x\\to0} \\frac{\\sin(x)}{x}',
'\\log_{5} x',
'\log\left( \frac{x+1}{x-1} \right)'
);
my @targets = <AsciiMath WL MathJSON>;
my @res = do for @formulas -> $fm {
[
LaTeX => $fm,
MathML => "latex«$fm»",
RakuAST => latex-interpret($fm, actions => 'RakuAST').DEPARSE,
|@targets.map({ $_ => latex-interpret($fm, actions => $_).raku })
].Hash
}
@res
==> { .&to-long-format(id-columns => 'LaTeX', variables-to => 'Format', values-to => 'Translation') }()
==> { group-by($_, 'LaTeX').map({ $_.value.sort(*<LaTeX Format>).kv.map(-> $i, %r { %r<LaTeX> ='' if $i; %r }) }) }()
==> { $_.flat(1) }()
==> to-html(field-names => <LaTeX Format Translation>, align => 'left')
==> { .subst(/ 'latex«' (.*?) '»' /, { latex-interpret($0.Str, actions => 'MathML')}, :g) }()
==> { .subst(/ '"' | '"' /, :g).subst('\{', '{', :g) }()
| LaTeX | Format | Translation |
|---|
| \log_{5} x | AsciiMath | log_5(x) |
| MathJSON | $[Log, x, 5] |
| MathML | log5(x) |
| RakuAST | log(x) |
| WL | Log[5,x] |
| \sqrt{4 * x^2 + 12 * x + 9} | AsciiMath | sqrt((4*x^2+12*x)+9) |
| MathJSON | $[Root, [Add, [Add, [Multiply, 4, [Power, x, 2]], [Multiply, 12, x]], 9], 2] |
| MathML | 4×x2+12×x+9 |
| RakuAST | sqrt((((4 * (x ** 2)) + (12 * x)) + 9)) |
| WL | Sqrt[Plus[Plus[Times[4,Power[x,2]],Times[12,x]],9]] |
| \log\left( \frac{x+1}{x-1} \right) | AsciiMath | log((x+1)/(x-1)) |
| MathJSON | $[Log, [Divide, [Add, x, 1], [Subtract, x, 1]]] |
| MathML | log(x+1x-1) |
| RakuAST | log(((x + 1) / (x - 1))) |
| WL | Log[Rational[Plus[x,1],Plus[x,Times[-1,1]]]] |
| \frac{-1214}{117} | AsciiMath | -1214/117 |
| MathJSON | $[Rational, -1214, 117] |
| MathML | -1214117 |
| RakuAST | (-1214 / 117) |
| WL | Rational[-1214,117] |
| \lim_{x\to0} \frac{\sin(x)}{x} | AsciiMath | lim_(x->0) sin(x)/x |
| MathJSON | $[Limit, [Function, [Block, [Divide, [Sin, x], x]], x], 0] |
| MathML | limx→0sin(x)x |
| RakuAST | limit((sin(x) / x), x, 0, TwoSided) |
| WL | Limit[Times[ Sin[x] , Power[x, -1]],x->0] |
| \int_{0}^{1} x^{2} d x | AsciiMath | int_(0)^(1) x^2 dx |
| MathJSON | $[Integrate, [Function, [Block, [Power, x, 2]], x], [Limits, x, 0, 1]] |
| MathML | ∫01x2dx |
| RakuAST | integral((x ** 2), x, 0, 1) |
| WL | Integrate[Power[x,2],{x,0,1}] |
| \sum_{n=1}^{10} n^2 | AsciiMath | sum_(n=1)^(10) n^2 |
| MathJSON | $[Sum, [Power, n, 2], [Limits, n, 1, 10]] |
| MathML | ∑n=110n2 |
| RakuAST | [+] (1 .. 10).map(-> $n! { ($n ** 2) }) |
| WL | Sum[Power[n,2],{n,1,10}] |
See also the Jupyter notebook "Basic-usage.ipynb".
Remark: In the table above "RakuAST" fields show the "deparsed" RakuAST constructs, i.e., Raku code.
Remark: The MathML code might be rendered incorrectly in GitHub's README.
In Jupyter notebooks and specialized Markdown viewers it can be seen that the generated MathML code
corresponds to the LaTeX code.
Here is a LaTeX translation to RakuAST structure:
latex-interpret('\sin(x + 2)', actions => 'RakuAST')
# RakuAST::Call::Name.new(
# name => RakuAST::Name.from-identifier("sin"),
# args => RakuAST::ArgList.new(
# RakuAST::Circumfix::Parentheses.new(
# RakuAST::SemiList.new(
# RakuAST::Statement::Expression.new(
# expression => RakuAST::ApplyInfix.new(
# left => RakuAST::Term::Name.new(
# RakuAST::Name.new(
# RakuAST::Name::Part::Expression.new(
# RakuAST::QuotedString.new(
# segments => (
# RakuAST::StrLiteral.new("x"),
# )
# )
# )
# )
# ),
# infix => RakuAST::Infix.new("+"),
# right => RakuAST::IntLiteral.new(2)
# )
# )
# )
# )
# )
# )
CLI
The package provides Command Line Interface (CLI) script. Here is usage message:
from-latex --help
# Usage:
# from-latex <text> [-t|--actions|--to=<Str>] [-f|--format=<Str>] [-o|--output=<Str>] -- Converts LaTeX code or files into AsciiMath, MathJSON, MathML, Mathematica, or Raku files.
#
# <text> Input file or LaTeX spec.
# -t|--actions|--to=<Str> Language to translate to. (One of 'asciimath', 'mathjson', 'mathml', 'mathematica', 'raku', 'rakuast', 'wolfram', or 'Whatever'.) [default: 'Whatever']
# -f|--format=<Str> Format of the result. (One of 'ast', 'json', 'raku', or 'Whatever'.) [default: 'Whatever']
# -o|--output=<Str> Output file; if an empty string then the result is printed to stdout. [default: '']
TODO
- TODO Development
- DONE Core grammar
- DONE MathJSON actions
- DONE First, reasonably well working version.
- DONE Make both of these cases work:
\int _{0} ^{1} x^{2} d x\int_{0}^{1} x^{2} d x
- The generated MathJSON is CortexJS-style, but a simpler version might be better.
- For example,
- LaTeX:
\int_{0}^{1}x^{2}dx - CortexJS:
["Integrate",["Function",["Block",["Power","x",2]],"x"],["Limits","x",0,1]] - Simpler:
["Integrate",["Power","x",2],["Limits","x",0,1]]
- I contacted ArnoG (creator of CortexJS and MathJSON):
- He agreed the simpler version has its place as a primary version.
- DONE Implement
:$function-wrap option.
- DONE MathML actions
- DONE AsciiMath actions
- DONE Wolfram Language (WL) actions
- DONE Raku actions (using RakuAST)
- The MathJSON interpreter does give Raku expressions (arrays.)
- But the idea is to make Raku expressions from LaTeX using RakuAST.
- Or generate code and produce AST objects from it.
- TODO Refactor the MathML, AsciiMath, and WL action classes into a separate MathJSON converter package
- TODO Documentation
- DONE Fuller, more comprehensive README
- TODO Blog post
- TODO Basic usage examples notebook
References
Packages
[AAp1] Anton Antonov, CortexJS, Raku package, (2026), GitHub/antononcube.
[AAp2] Anton Antonov, Proc::ZMQed, Raku package, (2022), GitHub/antononcube.
Videos
[AAv1] Anton Antonov, ["Using Wolfram Engine in Raku sessions"], video presentation, (2022), YouTube/@AAA4prediction.