Project

latexmath

0.0
A long-lived project that still receives updates
LaTeX math parser that runs a compiled Parsanol grammar artifact and returns a typed LaTeX model declared with LutaML Model. Used by Plurimath.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Runtime

~> 0.8, >= 0.8.162
~> 1.3, >= 1.3.131
 Project Readme

Latexmath: LaTeX math parser for Ruby

Gem Version Build Status

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
end

The 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.