Latexmath: LaTeX math parser for Ruby
Purpose
Latexmath parses LaTeX math, the notation inside $…$ or a \[…\]
display, into a typed model of the formula. It reads the LaTeX that
Plurimath reads, with the same
grammar, and is built for Plurimath to use for LaTeX input
(plurimath/plurimath#531).
The model records the structure of the formula: which symbols, fractions, roots, sub- and superscripts, matrices, environments and brackets it contains, and how they nest. Your code decides what to do with it, such as rendering, converting or analysing it. Latexmath does not write MathML or any other output format; Plurimath does that.
Version 1.0.0 replaces the 0.1.x gem, which converted LaTeX to MathML. See the changelog for what changed and how to move.
Installation
Add it to your Gemfile:
gem "latexmath", "~> 1.0"Latexmath needs Ruby 3.2 or later. Its runtime dependencies are Parsanol, LutaML Model and HTMLEntities. It does not load Plurimath.
Usage
require "latexmath"
doc = Latexmath.parse("x^2")
power = doc.expression.power
power.symbols.spelling # => "x"
power.supscript.number # => "2"Latexmath.parse returns a Latexmath::Model::Document. Its expression
holds the formula and decimal the decimal marker it was parsed with. Input
with no formula, such as an empty string, gives a document whose expression
is nil.
The model classes are plain LutaML Model classes, generated from the grammar:
each named part of the grammar is a class with one attribute per part it can
contain. Symbols such as \alpha are Latexmath::Model::MathSymbol objects.
A symbol keeps the text the grammar matched as spelling and gives its
Unicode text and kind (symbol, open or close):
symbol = Latexmath.parse("\\alpha").expression.symbols
symbol.spelling # => "alpha"
symbol.value # => "α"
symbol.kind # => "symbol"Numbers keep their digits and decimal marker as written. A dot is the decimal marker unless you say otherwise; a comma and the Arabic decimal separator are also supported:
Latexmath.parse("1,5", decimal: ",")
Latexmath.parse("1٫5", decimal: "٫")The rules for each model class are in the model contract, and the accepted syntax and the preprocessing of the input in the parser contract.
For a description of the LaTeX notation the gem reads and the model each construct becomes, see LaTeX notation.
Errors
Input that is not valid LaTeX math raises Latexmath::ParseError, which
tells you where the parser gave up, as a position in the text you passed:
begin
Latexmath.parse("é}")
rescue Latexmath::ParseError => e
e.char_offset # => 1, position in characters
e.byte_offset # => 2, position in UTF-8 bytes
endThe parser first rewrites the input as Plurimath does: it removes spaces and
newlines and writes non-ASCII characters as HTML entities. The place the
grammar stops is therefore a position in text you did not write. The offsets are mapped back to your input; a position
inside rewritten text points at the start of the text it came from. The
position in the rewritten text is also available, as grammar_char_offset
and grammar_byte_offset.
Every error Latexmath raises on purpose is a Latexmath::Error, except
ArgumentError for an invalid argument such as an unknown engine or decimal
marker.
Engines
Latexmath runs on Parsanol’s native engine where its extension is available,
and on its pure Ruby engine elsewhere. Both give the same results. You can
choose one with engine: :native or engine: :ruby. Asking for the native
engine where it cannot run raises Latexmath::EngineUnavailable instead of
quietly using Ruby. Latexmath.last_engine tells you which engine handled
the last call on the current thread.
The native engine has a recursion limit. With the default engine: :auto,
input that nests deeper than that is parsed with the Ruby engine; with
engine: :native it raises Latexmath::NestingTooDeep, a kind of
Latexmath::ParseError that has no offsets, because the input may well be
valid.
Testing against the Plurimath testsuite
The LaTeX cases of
plurimath-testsuite are
kept there as numbered, immutable generations. spec/fixtures/testsuite-pin.yml
names the exact commit, generation and digest this gem is tested against, and
CI replays every raw-tree and typed-model row of that generation on both
engines. CI also
compares the parse tree with the one the pinned Plurimath release produces
for every LaTeX case of the testsuite. See
the testsuite pin.
Relation to Plurimath
Latexmath is one of the language gems split out of Plurimath, with amath for AsciiMath and unicodemath for UnicodeMath. The plan and its progress are tracked in plurimath/plurimath#541. Plurimath is to turn the model into its own formula objects and write the output formats.
License
Copyright Ribose Inc. Licensed under the BSD 2-Clause License; see LICENSE.md.