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}