mathml_rs/
lib.rs

1use mathml_macros::*;
2use quick_xml::events::Event;
3use quick_xml::Reader;
4use std::fs::File;
5use std::io::BufReader;
6pub mod structs;
7pub use structs::apply::*;
8pub use structs::bindings::*;
9pub use structs::ci::*;
10pub use structs::cn::*;
11pub use structs::constants::*;
12pub use structs::lambda::*;
13pub use structs::math_node::*;
14pub use structs::numbers::*;
15pub use structs::op::*;
16pub use structs::piecewise::*;
17pub use structs::root::*;
18
19pub mod methods;
20pub use methods::evaluate::*;
21
22pub fn parse_fragment(
23    mut reader: Reader<BufReader<File>>,
24) -> (Vec<MathNode>, Reader<BufReader<File>>) {
25    reader.trim_text(true);
26    reader.expand_empty_elements(true);
27    let mut buf = Vec::new();
28    let mut stack: Vec<NodeIndex> = Vec::new();
29
30    let mut container = Vec::new();
31    let mut container_len = 0;
32
33    container.push(MathNode::default());
34    container_len += 1;
35    let mut current = 0;
36    stack.push(current);
37
38    loop {
39        match reader.read_event(&mut buf) {
40            // for each starting tag
41            Ok(Event::Start(ref e)) => {
42                let new_tag;
43                match e.name() {
44                    b"apply" => attach![Apply to Root | Apply | Lambda | Piece | Otherwise],
45                    b"times" => attach![Op::Times to Apply],
46                    b"divide" => attach![Op::Divide to Apply],
47                    b"minus" => attach![Op::Minus to Apply],
48                    b"plus" => attach![Op::Plus to Apply],
49                    b"power" => attach![Op::Power to Apply],
50                    b"factorial" => attach![Op::Factorial to Apply],
51                    b"eq" => attach![Op::Eq to Apply],
52                    b"neq" => attach![Op::Neq to Apply],
53                    b"gt" => attach![Op::Gt to Apply],
54                    b"lt" => attach![Op::Lt to Apply],
55                    b"geq" => attach![Op::Geq to Apply],
56                    b"leq" => attach![Op::Leq to Apply],
57                    b"and" => attach![Op::And to Apply],
58                    b"or" => attach![Op::Or to Apply],
59                    b"xor" => attach![Op::Xor to Apply],
60                    b"ceiling" => attach![Op::Ceiling to Apply],
61                    b"floor" => attach![Op::Floor to Apply],
62                    b"true" => attach![Constant::True to Apply | Piece ],
63                    b"false" => attach![Constant::False to Apply | Piece ],
64                    b"ci" => attach![Ci to Root | Apply | BVar | Piece | Otherwise | Lambda ],
65                    b"cn" => attach![Cn with
66                                        r#type as NumType,
67                                    to Root | Apply | BVar | Piece | Otherwise | Lambda ],
68                    b"lambda" => attach![Lambda to Root],
69                    b"bvar" => attach![BVar to Lambda],
70                    b"piecewise" => attach![Piecewise to Root | Apply | Lambda],
71                    b"piece" => attach![Piece to Piecewise],
72                    b"otherwise" => attach![Otherwise to Piecewise],
73                    b"sep" => new_tag = None,
74                    _ => {
75                        panic!("Tag not parsed: {}", std::str::from_utf8(e.name()).unwrap());
76                    }
77                }
78                if let Some(t) = new_tag {
79                    container.push(t);
80                    container_len += 1;
81                }
82            }
83            Ok(Event::End(ref e)) => match e.name() {
84                b"apply" => close![Apply],
85                b"times" => close![Op],
86                b"divide" => close![Op],
87                b"minus" => close![Op],
88                b"plus" => close![Op],
89                b"power" => close![Op],
90                b"factorial" => close![Op],
91                b"eq" => close![Op],
92                b"neq" => close![Op],
93                b"geq" => close![Op],
94                b"leq" => close![Op],
95                b"gt" => close![Op],
96                b"lt" => close![Op],
97                b"and" => close![Op],
98                b"or" => close![Op],
99                b"xor" => close![Op],
100                b"ceiling" => close![Op],
101                b"floor" => close![Op],
102                b"piecewise" => close![Piecewise],
103                b"piece" => close![Piece],
104                b"otherwise" => close![Otherwise],
105                b"true" => close![Constant],
106                b"false" => close![Constant],
107                b"ci" => close![Ci],
108                b"cn" => close![Cn],
109                b"lambda" => close![Lambda],
110                b"bvar" => close![BVar],
111                b"math" => break,
112                _ => {}
113            },
114            // unescape and decode the text event using the reader encoding
115            Ok(Event::Text(e)) => {
116                let s = e.unescape_and_decode(&reader).unwrap();
117                match container[current] {
118                    MathNode::Ci(..) => {
119                        container[current] = MathNode::Ci(Ci::with_name(s));
120                    }
121                    MathNode::Cn(ref mut cn) => match cn.r#type {
122                        Some(NumType::Real) | None => {
123                            let value = s.parse::<f64>().expect("Incorrect type");
124                            cn.value = Some(Number::Real(value));
125                        }
126                        Some(NumType::Integer) => {
127                            let value = s.parse::<i32>().expect("Incorrect type");
128                            cn.value = Some(Number::Integer(value));
129                        }
130                        Some(NumType::Rational) => {
131                            let value = s.parse::<i32>().expect("Incorrect type");
132                            if cn.value.is_none() {
133                                cn.value = Some(Number::Rational(value.into(), 1));
134                            } else if let Some(Number::Rational(x, y)) = cn.value {
135                                if y != 1 {
136                                    panic!("Error occurred while storing rational number.");
137                                }
138                                cn.value = Some(Number::Rational(x, value.into()));
139                            }
140                        }
141
142                        Some(NumType::ENotation) => {
143                            let value = s.parse::<i32>().expect("Incorrect type");
144                            if cn.value.is_none() {
145                                cn.value = Some(Number::ENotation(value.into(), 1));
146                            } else if let Some(Number::ENotation(x, y)) = cn.value {
147                                if y != 1 {
148                                    panic!("Error occurred while storing rational number.");
149                                }
150                                cn.value = Some(Number::ENotation(x, value.into()));
151                            }
152                        }
153                        _ => {
154                            panic!("Math type did not match for cn: {:?}", cn);
155                        }
156                    },
157                    _ => {
158                        panic!("Text not parsed in {:?}: {}", container[current], s);
159                    }
160                }
161            }
162            Ok(Event::Eof) => break, // exits the loop when reaching end of file
163            Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e),
164            _ => (), // There are several other `Event`s we do not consider here
165        }
166    }
167
168    //println!("");
169    //let mut count = 0;
170    //for item in &container {
171    //println!("{:0>2}: {}", count, item);
172    //count += 1;
173    //}
174    //println!("{:?}", txt);
175    //println!("{:?}", stack);
176    //println!("{:?}", current);
177
178    (container, reader)
179}
180
181#[cfg(test)]
182mod tests {
183    use super::*;
184    #[test]
185    fn it_works() {
186        let filename = "../../testsuites/core-semantic/00004/00004-sbml-l3v2.xml";
187        let reader = Reader::from_file(filename).expect("File error.");
188
189        parse_fragment(reader);
190    }
191}