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
use std::fmt;
use super::attribute::{Variant, Accent, LineThickness, ColumnAlign};
use crate::DisplayStyle;

/// AST node
#[derive(Debug, Clone, PartialEq)]
pub enum Node {
    Number(String),
    Letter(char, Variant),
    Operator(char),
    Function(String, Option<Box<Node>>),
    Space(f32),
    Subscript(Box<Node>, Box<Node>),
    Superscript(Box<Node>, Box<Node>),
    SubSup{ target: Box<Node>, sub: Box<Node>, sup: Box<Node>},
    OverOp(char, Accent, Box<Node>),
    UnderOp(char, Accent, Box<Node>),
    Overset{over: Box<Node>, target: Box<Node>},
    Underset{under: Box<Node>, target: Box<Node>},
    Under(Box<Node>, Box<Node>),
    UnderOver { target: Box<Node>, under: Box<Node>, over: Box<Node>},
    Sqrt(Option<Box<Node>>, Box<Node>),
    Frac(Box<Node>, Box<Node>, LineThickness),
    Row(Vec<Node>),
    Fenced { open: &'static str, close: &'static str, content: Box<Node> },
    OtherOperator(&'static str),
    Text(String),
    Matrix(Vec<Node>, ColumnAlign),
    Ampersand,
    NewLine,
    Slashed(Box<Node>),
    Style(Option<DisplayStyle>, Box<Node>),
    Undefined(String),
}

impl fmt::Display for Node {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Node::Number(number)  => write!(f, "<mn>{}</mn>", number),
            Node::Letter(letter, var) => match var {
                Variant::Italic => write!(f, "<mi>{}</mi>", letter),
                var             => write!(f, r#"<mi mathvariant="{}">{}</mi>"#, var, letter),
            },
            Node::Operator(op) => if op == &'∂' {
                write!(f, r#"<mo mathvariant="italic">∂</mo>"#)
            } else { write!(f, r#"<mo>{}</mo>"#, op) },
            Node::Function(fun, arg) => match arg {
                Some(arg) => write!(f, "<mi>{}</mi><mo>&#x2061;</mo>{}", fun, arg),
                None      => write!(f, "<mi>{}</mi>", fun),
            },
            Node::Space(space) => write!(f, r#"<mspace width="{}em"/>"#, space),
            Node::Subscript(a, b) => write!(f, "<msub>{}{}</msub>", a, b),
            Node::Superscript(a, b) => write!(f, "<msup>{}{}</msup>", a, b),
            Node::SubSup{target, sub, sup} => write!(f, "<msubsup>{}{}{}</msubsup>", target, sub, sup),
            Node::OverOp(op, acc, target) => write!(f, r#"<mover>{}<mo accent="{}">{}</mo></mover>"#, target, acc, op),
            Node::UnderOp(op, acc, target) => write!(f, r#"<munder>{}<mo accent="{}">{}</mo></munder>"#, target, acc, op),
            Node::Overset{over, target} => write!(f, r#"<mover>{}{}</mover>"#, target, over),
            Node::Underset{under, target} => write!(f, r#"<munder>{}{}</munder>"#, target, under),
            Node::Under(target, under) => write!(f, r#"<munder>{}{}</munder>"#, target, under),
            Node::UnderOver{target, under, over} => write!(f, r#"<munderover>{}{}{}</munderover>"#, target, under, over),
            Node::Sqrt(degree, content) => match degree {
                Some(deg) => write!(f, "<mroot>{}{}</mroot>", content, deg),
                None      => write!(f, "<msqrt>{}</msqrt>", content),
            },
            Node::Frac(num, denom, lt) => write!(f, "<mfrac{}>{}{}</mfrac>", lt, num, denom),
            Node::Row(vec) => write!(f, "<mrow>{}</mrow>", 
                vec.iter().map(|node| format!("{}", node)).collect::<String>()
            ),
            Node::Fenced{open, close, content} => {
                write!(f, r#"<mrow><mo stretchy="true" form="prefix">{}</mo>{}<mo stretchy="true" form="postfix">{}</mo></mrow>"#, open, content, close)
            },
            Node::OtherOperator(op) => write!(f, "<mo>{}</mo>", op),
            Node::Slashed(node) => match &**node {
                Node::Letter(x, var) => write!(f, "<mi mathvariant=\"{}\">{}&#x0338;</mi>", var, x),
                Node::Operator(x) => write!(f, "<mo>{}&#x0338;</mo>", x),
                n => write!(f, "{}", n),
            },
            Node::Matrix(content, columnalign) => {
                let mut mathml = format!("<mtable{}><mtr><mtd>", columnalign);
                for (i, node) in content.iter().enumerate() {
                    match node {
                        Node::NewLine => {
                            mathml.push_str("</mtd></mtr>");
                            if i < content.len() {
                                mathml.push_str("<mtr><mtd>")
                            }
                        },
                        Node::Ampersand => {
                            mathml.push_str("</mtd>");
                            if i < content.len() {
                                mathml.push_str("<mtd>")
                            }
                        },
                        node => { mathml = format!("{}{}", mathml, node); },
                    }
                }
                mathml.push_str("</mtd></mtr></mtable>");
                
                write!(f, "{}", mathml)
            },
            Node::Text(text) => write!(f, "<mtext>{}</mtext>", text),
            Node::Style(display, content) => match display {
                Some(DisplayStyle::Block)  => write!(f, r#"<mstyle displaystyle="true">{}</mstyle>"#, content),
                Some(DisplayStyle::Inline) => write!(f, r#"<mstyle displaystyle="false">{}</mstyle>"#, content),
                None => write!(f, "<mstyle>{}</mstyle>", content),
            },
            node => write!(f, "<mtext>[PARSE ERROR: {:?}]</mtext>", node),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::super::attribute::Variant;
    use super::Node;

    #[test]
    fn node_display() {
        let problems = vec![
            (Node::Number("3.14".to_owned()), "<mn>3.14</mn>"),
            (Node::Letter('x', Variant::Italic), "<mi>x</mi>"),
            (Node::Letter('α', Variant::Italic), "<mi>α</mi>"),
            (Node::Letter('あ', Variant::Normal), r#"<mi mathvariant="normal">あ</mi>"#),
            (
                Node::Row(vec![ Node::Operator('+'), Node::Operator('-') ]), 
                r"<mrow><mo>+</mo><mo>-</mo></mrow>"
            ),
        ];
        for (problem, answer) in problems.iter() {
            assert_eq!(&format!("{}", problem), answer);
        }
    }
}