1use std::fmt;
2use super::attribute::{Variant, Accent, LineThickness, ColumnAlign};
3use crate::DisplayStyle;
4
5#[derive(Debug, Clone, PartialEq)]
7pub enum Node {
8 Number(String),
9 Letter(char, Variant),
10 Operator(char),
11 Function(String, Option<Box<Node>>),
12 Space(f32),
13 Subscript(Box<Node>, Box<Node>),
14 Superscript(Box<Node>, Box<Node>),
15 SubSup{ target: Box<Node>, sub: Box<Node>, sup: Box<Node>},
16 OverOp(char, Accent, Box<Node>),
17 UnderOp(char, Accent, Box<Node>),
18 Overset{over: Box<Node>, target: Box<Node>},
19 Underset{under: Box<Node>, target: Box<Node>},
20 Under(Box<Node>, Box<Node>),
21 UnderOver { target: Box<Node>, under: Box<Node>, over: Box<Node>},
22 Sqrt(Option<Box<Node>>, Box<Node>),
23 Frac(Box<Node>, Box<Node>, LineThickness),
24 Row(Vec<Node>),
25 Fenced { open: &'static str, close: &'static str, content: Box<Node> },
26 StrechedOp(bool, String),
27 OtherOperator(&'static str),
28 SizedParen{ size: &'static str, paren: &'static str },
29 Text(String),
30 Matrix(Vec<Node>, ColumnAlign),
31 Ampersand,
32 NewLine,
33 Slashed(Box<Node>),
34 Style(Option<DisplayStyle>, Box<Node>),
35 Undefined(String),
36}
37
38impl fmt::Display for Node {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 match self {
41 Node::Number(number) => write!(f, "<mn>{}</mn>", number),
42 Node::Letter(letter, var) => match var {
43 Variant::Italic => write!(f, "<mi>{}</mi>", letter),
44 var => write!(f, r#"<mi mathvariant="{}">{}</mi>"#, var, letter),
45 },
46 Node::Operator(op) => if op == &'∂' {
47 write!(f, r#"<mo mathvariant="italic">∂</mo>"#)
48 } else { write!(f, r#"<mo>{}</mo>"#, op) },
49 Node::Function(fun, arg) => match arg {
50 Some(arg) => write!(f, "<mi>{}</mi><mo>⁡</mo>{}", fun, arg),
51 None => write!(f, "<mi>{}</mi>", fun),
52 },
53 Node::Space(space) => write!(f, r#"<mspace width="{}em"/>"#, space),
54 Node::Subscript(a, b) => write!(f, "<msub>{}{}</msub>", a, b),
55 Node::Superscript(a, b) => write!(f, "<msup>{}{}</msup>", a, b),
56 Node::SubSup{target, sub, sup} => write!(f, "<msubsup>{}{}{}</msubsup>", target, sub, sup),
57 Node::OverOp(op, acc, target) => write!(f, r#"<mover>{}<mo accent="{}">{}</mo></mover>"#, target, acc, op),
58 Node::UnderOp(op, acc, target) => write!(f, r#"<munder>{}<mo accent="{}">{}</mo></munder>"#, target, acc, op),
59 Node::Overset{over, target} => write!(f, r#"<mover>{}{}</mover>"#, target, over),
60 Node::Underset{under, target} => write!(f, r#"<munder>{}{}</munder>"#, target, under),
61 Node::Under(target, under) => write!(f, r#"<munder>{}{}</munder>"#, target, under),
62 Node::UnderOver{target, under, over} => write!(f, r#"<munderover>{}{}{}</munderover>"#, target, under, over),
63 Node::Sqrt(degree, content) => match degree {
64 Some(deg) => write!(f, "<mroot>{}{}</mroot>", content, deg),
65 None => write!(f, "<msqrt>{}</msqrt>", content),
66 },
67 Node::Frac(num, denom, lt) => write!(f, "<mfrac{}>{}{}</mfrac>", lt, num, denom),
68 Node::Row(vec) => write!(f, "<mrow>{}</mrow>",
69 vec.iter().map(|node| format!("{}", node)).collect::<String>()
70 ),
71 Node::Fenced{open, close, content} => {
72 write!(f, r#"<mrow><mo stretchy="true" form="prefix">{}</mo>{}<mo stretchy="true" form="postfix">{}</mo></mrow>"#, open, content, close)
73 },
74 Node::StrechedOp(stretchy, op) => write!(f, r#"<mo stretchy="{}">{}</mo>"#, stretchy, op),
75 Node::OtherOperator(op) => write!(f, "<mo>{}</mo>", op),
76 Node::SizedParen{size, paren} => write!(f, r#"<mrow><mo maxsize="{0}" minsize="{0}">{1}</mro></mrow>"#, size, paren),
77 Node::Slashed(node) => match &**node {
78 Node::Letter(x, var) => write!(f, "<mi mathvariant=\"{}\">{}̸</mi>", var, x),
79 Node::Operator(x) => write!(f, "<mo>{}̸</mo>", x),
80 n => write!(f, "{}", n),
81 },
82 Node::Matrix(content, columnalign) => {
83 let mut mathml = format!("<mtable{}><mtr><mtd>", columnalign);
84 for (i, node) in content.iter().enumerate() {
85 match node {
86 Node::NewLine => {
87 mathml.push_str("</mtd></mtr>");
88 if i < content.len() {
89 mathml.push_str("<mtr><mtd>")
90 }
91 },
92 Node::Ampersand => {
93 mathml.push_str("</mtd>");
94 if i < content.len() {
95 mathml.push_str("<mtd>")
96 }
97 },
98 node => { mathml = format!("{}{}", mathml, node); },
99 }
100 }
101 mathml.push_str("</mtd></mtr></mtable>");
102
103 write!(f, "{}", mathml)
104 },
105 Node::Text(text) => write!(f, "<mtext>{}</mtext>", text),
106 Node::Style(display, content) => match display {
107 Some(DisplayStyle::Block) => write!(f, r#"<mstyle displaystyle="true">{}</mstyle>"#, content),
108 Some(DisplayStyle::Inline) => write!(f, r#"<mstyle displaystyle="false">{}</mstyle>"#, content),
109 None => write!(f, "<mstyle>{}</mstyle>", content),
110 },
111 node => write!(f, "<mtext>[PARSE ERROR: {:?}]</mtext>", node),
112 }
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use super::super::attribute::Variant;
119 use super::Node;
120
121 #[test]
122 fn node_display() {
123 let problems = vec![
124 (Node::Number("3.14".to_owned()), "<mn>3.14</mn>"),
125 (Node::Letter('x', Variant::Italic), "<mi>x</mi>"),
126 (Node::Letter('α', Variant::Italic), "<mi>α</mi>"),
127 (Node::Letter('あ', Variant::Normal), r#"<mi mathvariant="normal">あ</mi>"#),
128 (
129 Node::Row(vec![ Node::Operator('+'), Node::Operator('-') ]),
130 r"<mrow><mo>+</mo><mo>-</mo></mrow>"
131 ),
132 ];
133 for (problem, answer) in problems.iter() {
134 assert_eq!(&format!("{}", problem), answer);
135 }
136 }
137}