const MATH_CHARS: [char; 23] = ['+', '-', '−', '*', '×', '·', '/', '∕', '⁄', '÷', '(', ')', '%', '⟌', '!', '~', '^', '&', '|', '≪', '<', '>', '≫'];
#[derive(Debug, PartialEq)]
pub enum MathValue<'a> {
Name(&'a str),
Operator(char),
Operation(char, isize, isize),
UnaryOperation(char, isize),
ParenOpen(isize),
ParenClose(usize),
TrailingError,
}
use MathValue::*;
pub fn math_token<'a>(s: &'a str) -> Vec<MathValue<'a>> {
fn token_base<'a>(s: &'a str) -> Vec<MathValue<'a>> {
let mut ret = Vec::<MathValue>::new();
let mut new_name_index = !0; let mut current_index = 0;
for c in s.chars() {
if is_in(c, &MATH_CHARS) {
if new_name_index != !0 { ret.push(Name(&s[new_name_index..current_index]));
new_name_index = !0;
}
ret.push(Operator(c));
} else if new_name_index == !0 && !c.is_whitespace() {
new_name_index = current_index;
}
current_index += c.len_utf8();
}
if new_name_index != !0 { ret.push(Name(&s[new_name_index..]));
}
ret.push(TrailingError);
ret
}
fn token_complex(line: &mut [MathValue]) {
for i in 1..line.len() {
let previous_op = if let Operator(c) = line[i-1] {
Some(c)
} else {
None
};
let current_op = if let Operator(c) = line[i] {
Some(c)
} else {
None
};
match (previous_op, current_op) {
(Some('/'), Some('/')) => {
line[i-1] = Operator('⟌');
line[i] = ParenOpen(1); },
(Some('<'), Some('<')) => {
line[i-1] = Operator('≪');
line[i] = ParenOpen(1);
},
(Some('>'), Some('>')) => {
line[i-1] = Operator('≫');
line[i] = ParenOpen(1);
},
(_, _) => {},
}
}
}
let mut ret = token_base(s);
token_complex(&mut ret);
ret
}
fn is_in<T: Eq>(a: T, set: &[T]) -> bool {
for elem in set {
if a == *elem {
return true;
}
}
false
}
pub fn contains_math_char(s: &str) -> bool {
s.contains(MATH_CHARS)
}
#[cfg(test)]
use crate::name_p;
#[test]
fn test_math_token() {
let math_line = "+4/88*toto";
assert_eq!(math_token(math_line), vec![Operator('+'), name_p("4"), Operator('/'), name_p("88"), Operator('*'), name_p("toto"), TrailingError]);
}