1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
//! Types of the syntax tree
/// The class of a symbol. It decides the space before and after it, and whether it is italic
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Class {
/// A character typeset in italic as a variable
Variable,
/// An ordinary character typeset upright (such as a digit)
Ordinary,
/// Binary operator
Binary,
/// Relation symbol
Relation,
/// Opening delimiter
Open,
/// Closing delimiter
Close,
/// Separator
Punctuation,
/// Function name
Function,
/// Large operator
Large,
/// Space that has only a width. The value is in em
Space(f64),
}
/// Style of a character
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Style {
/// Decided by the class (only variables are italic)
Auto,
/// Upright
Roman,
/// Italic
Italic,
/// Bold
Bold,
/// Bold italic
BoldItalic,
/// Blackboard bold
Blackboard,
/// Script letters
Script,
/// Fraktur
Fraktur,
/// Sans serif
SansSerif,
/// Monospace
Monospace,
}
/// Whether scripts are placed directly above and below the base
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Limits {
/// Above and below only in display. The default for large operators
Auto,
/// Always above and below
Always,
/// Always at the side
Never,
}
/// Level of typesetting
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Level {
Display,
Text,
Script,
ScriptScript,
}
/// How a column of a matrix is aligned
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Align {
Left,
Center,
Right,
}
/// How space is put between the columns and rows of a matrix
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Spacing {
/// matrix, array, and the like. All columns are separated by the same space
Matrix,
/// align, aligned, and gather from amsmath. A right-aligned column and a left-aligned
/// column are packed together as one pair, and space is put between the pairs
Align,
/// alignat from amsmath. The pairs are packed together too, with no space between them
Alignat,
}
/// Syntax tree of a formula
#[derive(Debug, Clone, PartialEq)]
pub enum Node {
/// A single symbol. The text may have more than one character (sin, log, and so on)
Symbol {
text: String,
class: Class,
},
/// The content of \text{...}. It keeps spaces as they are and is typeset upright
Text(String),
/// A sequence of items placed side by side
Row(Vec<Node>),
/// A fraction. If bar is false, no rule is drawn (\binom)
Fraction {
numerator: Box<Node>,
denominator: Box<Node>,
bar: bool,
},
/// A radical. The index is the 3 in \sqrt[3]{x}
Root {
index: Option<Box<Node>>,
body: Box<Node>,
},
/// Superscript and subscript
Scripts {
base: Box<Node>,
superscript: Option<Box<Node>>,
subscript: Option<Box<Node>>,
limits: Limits,
},
/// Stretchy delimiters that fit the height of the content. A side that is not needed is an
/// empty string
Fenced {
left: &'static str,
right: &'static str,
body: Box<Node>,
},
/// A matrix. rows[row][column] is a cell, and lines holds the numbers of the rows that have
/// a horizontal rule drawn just before them
Matrix {
rows: Vec<Vec<Node>>,
left: &'static str,
right: &'static str,
align: Vec<Align>,
lines: Vec<usize>,
spacing: Spacing,
},
/// Content with its style changed
Styled {
style: Style,
body: Box<Node>,
},
/// An operator name (\operatorname). The content is typeset upright, and the space before
/// and after it is the same as for function names such as \sin
Operator {
body: Box<Node>,
},
/// An accent attached to the content. If above is false, it goes below
Accent {
mark: &'static str,
body: Box<Node>,
above: bool,
},
/// Content with its typesetting level switched
Sized {
level: Level,
body: Box<Node>,
},
/// A delimiter with a given size. The height is in em
Delimiter {
text: &'static str,
height: f64,
class: Class,
},
/// A symbol that stretches to the width of what is placed above and below it
Extend {
mark: &'static str,
above: Option<Box<Node>>,
below: Option<Box<Node>>,
},
/// A rule across the whole content. It goes above if above is true, and below if false
Line {
body: Box<Node>,
above: bool,
},
/// Something that takes up space but draws nothing
///
/// The width and height fields tell which dimensions of the content are kept.
/// `\phantom` keeps both, `\hphantom` keeps only the width, and `\vphantom` only the height
Phantom {
body: Box<Node>,
width: bool,
height: bool,
},
}
/// Creates an empty Row
pub fn empty() -> Node {
Node::Row(Vec::new())
}
/// Whether the node is a Row with no items
pub fn is_empty(node: &Node) -> bool {
matches!(node, Node::Row(items) if items.is_empty())
}
/// The class used to decide the space before and after a node. Every group is treated as
/// Ordinary
pub fn class_of(node: &Node) -> Class {
match node {
Node::Symbol {class, ..} => *class,
Node::Scripts {base, ..} => class_of(base),
Node::Styled {body, ..} => class_of(body),
Node::Operator {..} => Class::Function,
Node::Sized {body, ..} => class_of(body),
Node::Delimiter {class, ..} => *class,
Node::Extend {..} => Class::Relation,
_ => Class::Ordinary,
}
}
/// Name of the CSS class
pub fn class_name(class: Class) -> &'static str {
match class {
Class::Variable => "variable",
Class::Ordinary => "ordinary",
Class::Binary => "binary",
Class::Relation => "relation",
Class::Open => "open",
Class::Close => "close",
Class::Punctuation => "punctuation",
Class::Function => "function",
Class::Large => "large",
Class::Space(_) => "space",
}
}