Skip to main content

l_group_formulas/l_group_term/
parse_l_group_term.rs

1use super::super::free_group_term::FreeGroupTerm;
2use super::super::l_group_term::LGroupTerm;
3use super::super::Term;
4use std::collections::BTreeSet;
5use super::super::parsing_error::ParsingError;
6
7
8pub (super) fn parse(s: &str) -> Result<LGroupTerm, ParsingError> {
9    let mut string = s.to_string();
10    // remove whitespace and outer brackets
11    string.retain(|c| !c.is_whitespace());
12    string.retain(|c| c.is_alphanumeric() || c == '(' || c == ')' || c == '^' || c == '-');
13    loop {
14        let result = has_outermost_brackets(&string);
15        match result {
16            Err(e) => return Err(e),
17            Ok(can_be_stripped) => {
18                if string.len() >= 2 && can_be_stripped {
19                    string = string[1..string.len() - 1].to_string();
20                }
21                else {
22                    break;
23                }
24            }
25        }
26    }
27
28    if is_atom(&string) { 
29        let parsed_free_group_term = std::panic::catch_unwind(|| FreeGroupTerm::from(string.as_str()));
30        match parsed_free_group_term {
31            Err(_) => return Err(ParsingError::ParsingAtomError(string)),
32            Ok(term) => return Ok(LGroupTerm::Atom(term))
33        };
34    }
35    
36    if is_inverse(&string) {
37        let result = parse(&string[1..string.len()].to_string());
38        match result {
39            Ok(term) => return Ok(term.inverse()),
40            Err(e) => return Err(ParsingError::ParsingInverseError(string, e.to_string()))
41        };
42    }
43
44    if is_meet(&string) {
45        let mut meetands = BTreeSet::new();
46        let result = split_at_outermost_meet(&string);
47        match result {
48            Err(e) => return Err(e),
49            Ok(strings) => {
50                for term_string in strings {
51                    let result = parse(&term_string);
52                    match result {
53                        Ok(term) => meetands.insert(term),
54                        Err(e) => return Err(ParsingError::ParsingMeetError(string, e.to_string()))
55                    };
56                }
57            }
58        };
59        return Ok(LGroupTerm::Meet(meetands));
60    }
61
62    if is_join(&string) {
63        let mut joinands = BTreeSet::new();
64        let result = split_at_outermost_join(&string);
65        match result {
66            Err(e) => return Err(e),
67            Ok(strings) => {
68                for term_string in strings {
69                    let result = parse(&term_string);
70                    match result {
71                        Ok(term) => joinands.insert(term),
72                        Err(e) => return Err(ParsingError::ParsingJoinError(string, e.to_string()))
73                    };
74                }
75            }
76        };
77        return Ok(LGroupTerm::Join(joinands));
78    }
79
80    let mut factors = Vec::new();
81    let mut current_factor = String::new();
82    let mut depth = 0;
83    for c in string.chars() {
84        match &c {
85            '(' => {
86                if depth == 0 && current_factor.len() > 0 {
87                    let result = parse(&current_factor);
88                    match result {
89                        Ok(term) => factors.push(term),
90                        Err(e) => return Err(ParsingError::ParsingProductError(string, current_factor, e.to_string()))
91                    };
92                    current_factor = String::new();
93                }
94                depth += 1;
95                current_factor.push(c);
96            },
97            ')' => {
98                match depth {
99                    0 => return Err(ParsingError::NonMatchingBracketsError(string.to_string())),
100                    _ => depth -= 1
101                };
102                current_factor.push(c);
103                if depth == 0 {
104                    let result = parse(&current_factor);
105                    match result {
106                        Ok(term) => factors.push(term),
107                        Err(e) => return Err(ParsingError::ParsingProductError(string, current_factor, e.to_string()))
108                    };
109                    current_factor = String::new();
110                }
111            },
112            '-' => {
113                if depth == 0 && current_factor.len() > 0 {
114                    let result = parse(&current_factor);
115                    match result {
116                        Ok(term) => factors.push(term),
117                        Err(e) => return Err(ParsingError::ParsingProductError(string, current_factor, e.to_string()))
118                    };
119                    current_factor = String::new();
120                }
121                current_factor.push(c);
122            }
123            _ => current_factor.push(c)
124        };
125    }
126    if current_factor.len() > 0 {
127        let result = parse(&current_factor);
128            match result {
129                Ok(term) => factors.push(term),
130                Err(e) => return Err(ParsingError::ParsingProductError(string, current_factor, e.to_string()))
131            };
132    }
133    return Ok(LGroupTerm::Prod(factors));
134}
135
136fn split_at_outermost_join(s: &String) -> Result<Vec<String>, ParsingError> {
137    let mut depth = 0;
138    let mut strings = Vec::new();
139    let mut current_string = String::new();
140    for c in s.chars() {
141        match c {
142            '(' => {
143                depth += 1;
144                current_string.push(c);
145            },
146            ')' => {
147                match depth {
148                    0 => return Err(ParsingError::NonMatchingBracketsError(s.clone())),
149                    _ => depth -= 1
150                };
151                current_string.push(c);
152            },
153            'v' => {
154                if depth == 0 {
155                    strings.push(current_string.clone());
156                    current_string = String::new();
157                }
158                else {
159                    current_string.push(c);
160                }
161            },
162            _ => current_string.push(c)
163        };
164    }
165    strings.push(current_string);
166    return Ok(strings);
167}
168
169fn split_at_outermost_meet(s: &String) -> Result<Vec<String>, ParsingError> {
170    let mut depth = 0;
171    let mut strings = Vec::new();
172    let mut current_string = String::new();
173    for c in s.chars() {
174        match c {
175            '(' => depth += 1,
176            ')' => {
177                match depth {
178                    0 => return Err(ParsingError::NonMatchingBracketsError(s.clone())),
179                    _ => depth -= 1
180                };
181            },
182            '^' => {
183                if depth == 0 {
184                    strings.push(current_string.clone());
185                    current_string = String::new();
186                }
187                else {
188                    current_string.push(c);
189                }
190            },
191            _ => current_string.push(c)
192        };
193    }
194    strings.push(current_string);
195    return Ok(strings);
196}
197
198/// Returns whether the input contains `^`, `v`, or `-`
199fn is_atom(s: &String) -> bool {
200    for c in s.chars() {
201        if c == '^' || c == 'v' || c == '-' { return false; }
202    }
203    return true;
204}
205
206/// Warning: only save to call if it is known not to be an atom
207fn is_inverse(s: &String) -> bool {
208    let mut chars = s.chars();
209    let c = chars.next();
210    if c != Some('-') { return false };
211    let mut depth = 0;
212    for c in chars {
213        match &c {
214            '(' => depth += 1,
215            ')' => depth -= 1,
216            'v' | '^' | '-'  => { if depth == 0 { return false; }},
217            _ => {}
218        };
219    }
220    return true;
221}
222
223/// Warning: only save to call if it is known not to be an atom or an inverse
224fn is_meet(s: &String) -> bool {
225    let mut depth = 0;
226    for c in s.chars() {
227        match &c {
228            '(' => depth += 1,
229            ')' => depth -= 1,
230            _ => {}
231        };
232        if depth == 0 && c == '^' { return true };
233    }
234    return false;
235}
236
237/// Warning: only save to call if it is known not to be an atom or an inverse
238fn is_join(s: &String) -> bool {
239    let mut depth = 0;
240    for c in s.chars() {
241        match &c {
242            '(' => depth += 1,
243            ')' => depth -= 1,
244            _ => {}
245        };
246        if depth == 0 && c == 'v' { return true };
247    }
248    return false;
249}
250
251
252/// returns true if it its outermost brackets are totally left and right,
253/// and are redundant.
254fn has_outermost_brackets(s: &String) -> Result<bool, ParsingError> {
255    let mut depth = 0;
256    let s = s[0 .. s.len() - 1].to_string();
257    for c in s.chars() {
258        match c {
259            '(' => depth += 1,
260            ')' => { 
261                match depth {
262                    0 => return Err(ParsingError::NonMatchingBracketsError(s)),
263                    _ => depth -= 1
264                }
265            },
266            _ => {}
267        };
268        if depth == 0 { return Ok(false); }
269    }
270    return Ok(true);
271}
272
273#[cfg(test)]
274mod tests {
275    
276    #[test]
277    fn test_does_not_crash() {
278        let string = String::from("(x v (z v (x ^ y)))");
279        assert_eq!(string, super::parse(&string).expect("crashed ...").to_string());
280    }
281}