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
use crate::ast::{
    Math, MathExpression,
    MathExpression::{
        Mfrac, Mn, Mo, MoLine, Mover, Mspace, Msqrt, Mstyle, Msub, Msubsup, Msup, Mtext, Munder,
    },
    Mi, Mrow,
};

use petgraph::{graph::NodeIndex, Graph};

/// A graph representation of the MathML abstract syntax tree (AST), for easier inspection,
/// visualization, and debugging.
pub type ASTGraph<'a> = Graph<String, u32>;

fn add_node_and_edge<'a>(
    graph: &mut ASTGraph<'a>,
    parent_index: Option<NodeIndex>,
    x: &'a str,
) -> NodeIndex {
    let node_index = graph.add_node(x.to_string());
    if let Some(p) = parent_index {
        graph.add_edge(p, node_index, 1);
    }
    node_index
}

fn add_to_graph_0<'a>(graph: &mut ASTGraph<'a>, parent_index: Option<NodeIndex>, x: &'a str) {
    add_node_and_edge(graph, parent_index, x);
}

/// Update the parent index
fn update_parent<'a>(
    graph: &mut ASTGraph<'a>,
    mut parent_index: Option<NodeIndex>,
    x: &'a str,
) -> Option<NodeIndex> {
    let node_index = add_node_and_edge(graph, parent_index, x);
    parent_index = Some(node_index);
    parent_index
}

/// Macro to add elements with fixed numbers of child elements.
macro_rules! add_to_graph_n {
    ($graph: ident, $parent_index: ident, $elem_type: literal, $($x:ident),+ ) => {{
            let parent_index = update_parent($graph, $parent_index, $elem_type);
            $( $x.add_to_graph($graph, parent_index); )+
    }}
}

/// Function to add elements with a variable number of child elements.
fn add_to_graph_many0<'a>(
    graph: &mut ASTGraph<'a>,
    parent_index: Option<NodeIndex>,
    elem_type: &'a str,
    elements: &'a Vec<MathExpression>,
) {
    let parent_index = update_parent(graph, parent_index, elem_type);
    for element in elements {
        element.add_to_graph(graph, parent_index);
    }
}

impl MathExpression {
    pub fn add_to_graph<'a>(&'a self, graph: &mut ASTGraph<'a>, parent_index: Option<NodeIndex>) {
        match self {
            MathExpression::Mi(Mi(x)) => add_to_graph_0(graph, parent_index, x),
            Mo(x) => add_to_graph_0(graph, parent_index, &x.to_string()),
            Mn(x) => add_to_graph_0(graph, parent_index, x),
            Msqrt(x) => add_to_graph_n!(graph, parent_index, "msqrt", x),
            Msup(x1, x2) => add_to_graph_n!(graph, parent_index, "msup", x1, x2),
            Msub(x1, x2) => add_to_graph_n!(graph, parent_index, "msub", x1, x2),
            Mfrac(x1, x2) => add_to_graph_n!(graph, parent_index, "mfrac", x1, x2),
            MathExpression::Mrow(Mrow(xs)) => add_to_graph_many0(graph, parent_index, "mrow", xs),
            Munder(x1, x2) => add_to_graph_n!(graph, parent_index, "munder", x1, x2),
            Mover(x1, x2) => add_to_graph_n!(graph, parent_index, "mover", x1, x2),
            Msubsup(x1, x2, x3) => add_to_graph_n!(graph, parent_index, "msubsup", x1, x2, x3),
            Mtext(x) => add_to_graph_0(graph, parent_index, x),
            Mstyle(xs) => add_to_graph_many0(graph, parent_index, "mstyle", xs),
            Mspace(x) => add_to_graph_0(graph, parent_index, x),
            MoLine(x) => add_to_graph_0(graph, parent_index, x),
            _ => {}
        }
    }
}

impl Math {
    /// Create a graph representation of the AST, for easier visualization and debugging.
    pub fn to_graph(&self) -> ASTGraph {
        let mut g = ASTGraph::new();
        let root_index = g.add_node("root".to_string());
        for element in &self.content {
            element.add_to_graph(&mut g, Some(root_index));
        }
        g
    }
}