Skip to main content

rust_latex_parser/
ast.rs

1//! AST types for the equation layout tree.
2//!
3//! The core type is [`EqNode`] — a recursive enum representing every structural
4//! element the parser can produce. You'll pattern-match on it to build your
5//! renderer, converter, or whatever else you need.
6//!
7//! [`EqMetrics`] is a simple struct for stashing width/ascent/descent
8//! measurements if you're building a layout engine. It's not used by the
9//! parser itself — it's here for your convenience.
10
11/// A node in the equation layout tree.
12///
13/// Returned by [`crate::parse_equation`]. Every variant is recursive, so a
14/// fraction's numerator can contain another fraction, a matrix cell can hold
15/// a summation, and so on — there's no depth limit.
16///
17/// # Walking the tree
18///
19/// ```
20/// use rust_latex_parser::{parse_equation, EqNode};
21///
22/// fn count_fractions(node: &EqNode) -> usize {
23///     match node {
24///         EqNode::Frac(n, d) => 1 + count_fractions(n) + count_fractions(d),
25///         EqNode::Seq(nodes) => nodes.iter().map(count_fractions).sum(),
26///         EqNode::Sup(base, sup) => count_fractions(base) + count_fractions(sup),
27///         EqNode::Sub(base, sub) => count_fractions(base) + count_fractions(sub),
28///         EqNode::Sqrt(inner) => count_fractions(inner),
29///         _ => 0,
30///     }
31/// }
32///
33/// let tree = parse_equation("\\frac{\\frac{a}{b}}{c}");
34/// assert_eq!(count_fractions(&tree), 2);
35/// ```
36#[derive(Debug, Clone)]
37pub enum EqNode {
38    /// Plain text — a variable name, number, operator character, or Unicode symbol.
39    ///
40    /// Examples: `"x"`, `"42"`, `"+"`, `"α"`, `"∑"`.
41    Text(String),
42
43    /// Horizontal space, measured in points.
44    ///
45    /// Inserted automatically around binary operators (`+`, `=`, `\leq`, etc.)
46    /// and by explicit spacing commands (`\quad`, `\,`, `\;`).
47    /// Can be negative (e.g. `\!` produces `Space(-3.0)`).
48    Space(f32),
49
50    /// A horizontal sequence of nodes. This is the most common container —
51    /// any expression with more than one piece gets wrapped in a `Seq`.
52    Seq(Vec<EqNode>),
53
54    /// Base with superscript. Produced by `x^2` or `x^{2n}`.
55    ///
56    /// Fields: `(base, superscript)`.
57    Sup(Box<EqNode>, Box<EqNode>),
58
59    /// Base with subscript. Produced by `x_1` or `x_{i+1}`.
60    ///
61    /// Fields: `(base, subscript)`.
62    Sub(Box<EqNode>, Box<EqNode>),
63
64    /// Base with both superscript and subscript, stacked vertically to the
65    /// right of the base. Produced by `x^2_3` or `x_3^2` (order doesn't matter).
66    ///
67    /// Fields: `(base, superscript, subscript)`.
68    SupSub(Box<EqNode>, Box<EqNode>, Box<EqNode>),
69
70    /// Fraction with numerator over denominator.
71    ///
72    /// Produced by `\frac{a}{b}` or the shorthand `a/b`.
73    ///
74    /// Fields: `(numerator, denominator)`.
75    Frac(Box<EqNode>, Box<EqNode>),
76
77    /// Square root wrapping its contents.
78    ///
79    /// Produced by `\sqrt{x}` or the bareword `sqrt(x)`.
80    Sqrt(Box<EqNode>),
81
82    /// Big operator — summation, integral, product, etc. — with optional
83    /// upper and lower limits.
84    ///
85    /// The `symbol` field is a Unicode character (e.g. `"∑"`, `"∫"`, `"∏"`).
86    /// Produced by `\sum_{i=0}^{n}` or the bareword `sum_{i=0}^{n}`.
87    BigOp {
88        symbol: String,
89        lower: Option<Box<EqNode>>,
90        upper: Option<Box<EqNode>>,
91    },
92
93    /// An accent mark above a node.
94    ///
95    /// Produced by `\hat{x}`, `\bar{x}`, `\dot{x}`, `\ddot{x}`, `\tilde{x}`,
96    /// or `\vec{v}`. See [`AccentKind`] for the variants.
97    Accent(Box<EqNode>, AccentKind),
98
99    /// A named limit-style operator (`lim`, `sin`, `log`, etc.) with an
100    /// optional subscript limit.
101    ///
102    /// These render as upright text (not italic), with the limit below when
103    /// present. Produced by `\lim_{x \to 0}` or barewords like `sin`, `cos`.
104    Limit {
105        name: String,
106        lower: Option<Box<EqNode>>,
107    },
108
109    /// Upright text block within an equation.
110    ///
111    /// Produced by `\text{hello world}`. The content is not parsed as math —
112    /// it's passed through as-is.
113    TextBlock(String),
114
115    /// Math font override for a subexpression.
116    ///
117    /// Produced by `\mathbb{R}`, `\mathbf{v}`, `\mathcal{F}`, etc.
118    /// See [`MathFontKind`] for the supported font families.
119    MathFont { kind: MathFontKind, content: Box<EqNode> },
120
121    /// Content wrapped in stretchy delimiters.
122    ///
123    /// Produced by `\left( ... \right)`. The `left` and `right` strings are
124    /// the delimiter characters (e.g. `"("` and `")"`). An invisible delimiter
125    /// (`\left.` or `\right.`) produces an empty string.
126    Delimited { left: String, right: String, content: Box<EqNode> },
127
128    /// Matrix or matrix-like environment.
129    ///
130    /// Produced by `\begin{pmatrix}`, `\begin{bmatrix}`, etc.
131    /// Each inner `Vec<EqNode>` is one row, each `EqNode` in a row is one cell.
132    /// See [`MatrixKind`] for the delimiter styles.
133    Matrix { kind: MatrixKind, rows: Vec<Vec<EqNode>> },
134
135    /// Piecewise / cases environment.
136    ///
137    /// Produced by `\begin{cases} ... \end{cases}`. Each row is a
138    /// `(value, optional_condition)` pair. The condition comes after `&`.
139    Cases { rows: Vec<(EqNode, Option<EqNode>)> },
140
141    /// Binomial coefficient. Rendered as a stacked pair in parentheses.
142    ///
143    /// Produced by `\binom{n}{k}`. Fields: `(top, bottom)`.
144    Binom(Box<EqNode>, Box<EqNode>),
145
146    /// Overbrace or underbrace with an optional label.
147    ///
148    /// Produced by `\overbrace{a+b}^{n}` (over=true) or
149    /// `\underbrace{x+y}_{text}` (over=false).
150    Brace { content: Box<EqNode>, label: Option<Box<EqNode>>, over: bool },
151
152    /// One expression stacked above or below another.
153    ///
154    /// Produced by `\overset{def}{=}` (over=true), `\underset{lim}{=}`
155    /// (over=false), or `\stackrel{above}{base}` (over=true).
156    StackRel { base: Box<EqNode>, annotation: Box<EqNode>, over: bool },
157}
158
159/// The kind of accent mark in an [`EqNode::Accent`] node.
160#[derive(Debug, Clone, Copy)]
161pub enum AccentKind {
162    /// `\hat{x}` — circumflex above.
163    Hat,
164    /// `\bar{x}` or `\overline{x}` — horizontal bar above.
165    Bar,
166    /// `\dot{x}` — single dot above.
167    Dot,
168    /// `\ddot{x}` — double dot (diaeresis) above.
169    DoubleDot,
170    /// `\tilde{x}` — tilde above.
171    Tilde,
172    /// `\vec{v}` — right arrow above.
173    Vec,
174}
175
176/// The font family in a [`EqNode::MathFont`] node.
177#[derive(Debug, Clone, Copy)]
178pub enum MathFontKind {
179    /// `\mathbf{...}` — bold.
180    Bold,
181    /// `\mathbb{...}` — double-struck / blackboard bold.
182    Blackboard,
183    /// `\mathcal{...}` — calligraphic / script.
184    Calligraphic,
185    /// `\mathrm{...}` — upright roman.
186    Roman,
187    /// `\mathfrak{...}` — Fraktur / blackletter.
188    Fraktur,
189    /// `\mathsf{...}` — sans-serif.
190    SansSerif,
191    /// `\mathtt{...}` — monospace / typewriter.
192    Monospace,
193}
194
195/// The delimiter style for a [`EqNode::Matrix`] node.
196#[derive(Debug, Clone, Copy)]
197pub enum MatrixKind {
198    /// `\begin{matrix}` — no delimiters.
199    Plain,
200    /// `\begin{pmatrix}` — parentheses `( )`.
201    Paren,
202    /// `\begin{bmatrix}` — square brackets `[ ]`.
203    Bracket,
204    /// `\begin{vmatrix}` — single vertical bars `| |`.
205    VBar,
206    /// `\begin{Vmatrix}` — double vertical bars `‖ ‖`.
207    DoubleVBar,
208    /// `\begin{Bmatrix}` — curly braces `{ }`.
209    Brace,
210}
211
212/// Measured dimensions of a laid-out equation node.
213///
214/// This struct isn't produced by the parser — it's here for your convenience
215/// if you're building a renderer and need somewhere to store measurements
216/// during the layout pass.
217///
218/// All values are in the same unit as your font size (typically points).
219///
220/// ```text
221///          ┬
222///          │ ascent (above baseline)
223///  ───────────── baseline
224///          │ descent (below baseline)
225///          ┴
226///  ├─────────────┤
227///       width
228/// ```
229#[derive(Debug, Clone, Copy)]
230pub struct EqMetrics {
231    /// Horizontal extent of the node.
232    pub width: f32,
233    /// Distance from the baseline to the top of the node. Always non-negative.
234    pub ascent: f32,
235    /// Distance from the baseline to the bottom of the node. Always non-negative.
236    pub descent: f32,
237}
238
239impl EqMetrics {
240    /// Total height: `ascent + descent`.
241    pub fn height(&self) -> f32 {
242        self.ascent + self.descent
243    }
244}