1use crate::ast_types::Node;
2use crate::token_types::Tokens;
3
4pub fn parse<'a, T: Iterator<Item = Result<Tokens, String>>>(
6 stream: T,
7) -> Result<Box<Node>, String> {
8 let mut stream = stream.peekable();
9 let mut paren_level = 0;
10 let mut current_stack = vec![Box::new(Node::ForumulaUnit(1, vec![]))];
11 loop {
12 match stream.next() {
13 Some(Err(val)) => return Err(val),
14 Some(Ok(Tokens::Number { data, meta })) => {
15 let fu = current_stack.pop().unwrap();
16 if let box Node::ForumulaUnit(_, vec) = fu {
17 current_stack.push(Box::new(Node::ForumulaUnit(data, vec)));
18 } else {
19 return Err("Invalid parent".to_owned());
20 }
21 }
22 Some(Ok(Tokens::Element { data, meta })) => {
23 let mut fu = *current_stack.pop().unwrap();
24
25 if let Node::ForumulaUnit(_, ref mut vec) = fu {
26 if let Some(Ok(Tokens::Number { data: count, meta })) = stream.peek() {
27 vec.push(Node::Element(*count, data));
28 stream.next();
29 } else {
30 vec.push(Node::Element(1, data));
31 }
32 current_stack.push(Box::new(fu));
33 } else {
34 return Err("Invalid Parent".to_owned());
35 }
36 }
37 Some(Ok(Tokens::Plus { meta })) => {
38 let fu = *current_stack.pop().unwrap();
39 let mut maybe_reactants = current_stack.pop().map(|val| *val);
40 match maybe_reactants {
41 Some(Node::Reactants(ref mut vec)) => {
42 vec.push(fu);
43 current_stack.push(Box::new(maybe_reactants.unwrap()));
44 current_stack.push(Box::new(Node::ForumulaUnit(1, vec![])));
45 }
46 Some(Node::Products(ref mut vec)) => {
47 vec.push(fu);
48 current_stack.push(Box::new(maybe_reactants.unwrap()));
49 current_stack.push(Box::new(Node::ForumulaUnit(1, vec![])));
50 }
51 Some(_) => return Err("Invalid plus".to_owned()),
52 None => {
53 if let Node::ForumulaUnit(_, _) = fu {
54 current_stack.push(Box::new(Node::Reactants(vec![fu])));
55 current_stack.push(Box::new(Node::ForumulaUnit(1, vec![])));
56 }
57 }
58 }
59 }
60 Some(Ok(Tokens::Yields { meta })) => {
61 let fu = *current_stack.pop().unwrap();
62 let mut maybe_reactants = current_stack.pop();
63
64 match maybe_reactants {
65 Some(box Node::Reactants(ref mut vec)) => {
66 vec.push(fu);
67 current_stack.push(maybe_reactants.unwrap());
68 current_stack.push(Box::new(Node::Products(vec![])));
69 current_stack.push(Box::new(Node::ForumulaUnit(1, vec![])));
70 }
71 Some(_) => return Err("Invalid yields location".to_owned()),
72 None => {
73 current_stack.push(Box::new(Node::Reactants(vec![fu])));
74 current_stack.push(Box::new(Node::ForumulaUnit(1, vec![])));
75 }
76 }
77 }
78 Some(Ok(Tokens::Paren {
79 data: super::token_types::ParenType::OPEN,
80 meta,
81 })) => {
82 paren_level += 1;
83 current_stack.push(Box::new(Node::Group(1, vec![])));
84 }
85 Some(Ok(Tokens::Paren {
86 data: super::token_types::ParenType::CLOSE,
87 meta,
88 })) => {
89 if paren_level == 0 {
90 return Err("Invalid closing paren".to_owned());
91 };
92 paren_level -= 1;
93 let group = *current_stack.pop().unwrap();
94 let mut maybe_fu_or_group = current_stack.pop();
95
96 match maybe_fu_or_group {
97 Some(box Node::Group(_, ref mut vec)) => {
98 if let Some(Ok(Tokens::Number { data, meta: _ })) = stream.peek() {
99 if let Node::Group(_, inner_vec) = group {
100 vec.push(Node::Group(*data, inner_vec));
101 }
102 }
103 current_stack.push(maybe_fu_or_group.unwrap());
104 }
105 Some(box Node::ForumulaUnit(_, ref mut vec)) => {
106 vec.push(group);
107 current_stack.push(maybe_fu_or_group.unwrap());
108 }
109
110 Some(_) => return Err("Invalid Parent".to_owned()),
111 None => return Err("Stack Underflow in group".to_owned()),
112 }
113 }
114 None => {
115 let fu = *current_stack.pop().unwrap();
116 let mut products_or_none = current_stack.pop();
117 match products_or_none {
118 Some(box Node::Products(ref mut vec)) => {
119 let reactants = current_stack.pop().unwrap();
120 vec.push(fu);
121 current_stack.push(Box::new(Node::Equation(
122 reactants,
123 products_or_none.unwrap(),
124 )));
125 }
126 Some(_) => return Err("Stack had unexpected value".to_owned()),
127 None => {
128 current_stack.push(Box::new(fu));
129 }
130 }
131 break;
132 }
133 }
134 }
135 Ok(current_stack.pop().unwrap())
136}
137
138#[cfg(test)]
139mod tests {
140 use crate::token_types::TokenMetadata;
141
142 use super::*;
143 #[test]
144 fn can_parse_equation() {
145 let stream = vec![
146 Tokens::Number {
147 data: 2,
148 meta: TokenMetadata::new("2", 0),
149 },
150 Tokens::Element {
151 data: "Fe".to_owned(),
152 meta: TokenMetadata::new("Fe", 1),
153 },
154 Tokens::Plus {
155 meta: TokenMetadata::new("+", 3),
156 },
157 Tokens::Element {
158 data: "Na".to_owned(),
159 meta: TokenMetadata::new("Na", 4),
160 },
161 Tokens::Number {
162 data: 2,
163 meta: TokenMetadata::new("2", 6),
164 },
165 Tokens::Element {
166 data: "F".to_owned(),
167 meta: TokenMetadata::new("F", 7),
168 },
169 Tokens::Number {
170 data: 3,
171 meta: TokenMetadata::new("3", 8),
172 },
173 Tokens::Yields {
174 meta: TokenMetadata::new("->", 9),
175 },
176 Tokens::Number {
177 data: 2,
178 meta: TokenMetadata::new("2", 11),
179 },
180 Tokens::Element {
181 data: "Fe".to_owned(),
182 meta: TokenMetadata::new("Fe", 12),
183 },
184 Tokens::Element {
185 data: "Na".to_owned(),
186 meta: TokenMetadata::new("Na", 14),
187 },
188 Tokens::Plus {
189 meta: TokenMetadata::new("+", 16),
190 },
191 Tokens::Element {
192 data: "F".to_owned(),
193 meta: TokenMetadata::new("F", 17),
194 },
195 Tokens::Number {
196 data: 3,
197 meta: TokenMetadata::new("3", 18),
198 },
199 ];
200
201 let exp = Node::Equation(
202 Box::new(Node::Reactants(vec![
203 Node::ForumulaUnit(2, vec![Node::Element(1, "Fe".to_owned())]),
204 Node::ForumulaUnit(
205 1,
206 vec![
207 Node::Element(2, "Na".to_owned()),
208 Node::Element(3, "F".to_owned()),
209 ],
210 ),
211 ])),
212 Box::new(Node::Products(vec![
213 Node::ForumulaUnit(
214 2,
215 vec![
216 Node::Element(1, "Fe".to_owned()),
217 Node::Element(1, "Na".to_owned()),
218 ],
219 ),
220 Node::ForumulaUnit(1, vec![Node::Element(3, "F".to_owned())]),
221 ])),
222 );
223
224 let res = parse(stream.into_iter().map(|box_tokens| Ok(box_tokens)));
225
226 assert!(res.is_ok());
227
228 assert_eq!(exp, *res.unwrap());
229 }
230 #[test]
231 fn can_parse_formula_unit() {
232 let stream = vec![
233 Tokens::Number {
234 data: 2,
235 meta: TokenMetadata::new("2", 0),
236 },
237 Tokens::Element {
238 data: "Fe".to_owned(),
239 meta: TokenMetadata::new("Fe", 1),
240 },
241 Tokens::Element {
242 data: "C".to_owned(),
243 meta: TokenMetadata::new("C", 3),
244 },
245 Tokens::Element {
246 data: "O".to_owned(),
247 meta: TokenMetadata::new("O", 4),
248 },
249 Tokens::Number {
250 data: 3,
251 meta: TokenMetadata::new("3", 5),
252 },
253 ];
254
255 let exp = Node::ForumulaUnit(
256 2,
257 vec![
258 Node::Element(1, "Fe".to_owned()),
259 Node::Element(1, "C".to_owned()),
260 Node::Element(3, "O".to_owned()),
261 ],
262 );
263
264 let res = parse(stream.into_iter().map(|box_tokens| Ok(box_tokens)));
265
266 assert!(res.is_ok());
267
268 assert_eq!(exp, *res.unwrap());
269 }
270}