MAJOR refactoring
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
use crate::parsing::is_number;
|
||||
use crate::parsing::{is_letter, is_number, is_variable};
|
||||
|
||||
pub const HINT_EMPTY: Hint = Hint::Single("x^2");
|
||||
const HINT_CLOSED_PARENS: Hint = Hint::Single(")");
|
||||
@@ -13,7 +13,17 @@ macro_rules! test_print {
|
||||
}
|
||||
|
||||
pub fn split_function(input: &str) -> Vec<String> {
|
||||
split_function_chars(&input.chars().collect::<Vec<char>>())
|
||||
split_function_chars(
|
||||
&input
|
||||
.replace("pi", "π")
|
||||
.replace("**", "^")
|
||||
.replace("exp", "\u{1fc93}")
|
||||
.chars()
|
||||
.collect::<Vec<char>>(),
|
||||
)
|
||||
.iter()
|
||||
.map(|x| x.replace("\u{1fc93}", "exp"))
|
||||
.collect::<Vec<String>>()
|
||||
}
|
||||
|
||||
fn split_function_chars(chars: &[char]) -> Vec<String> {
|
||||
@@ -22,19 +32,120 @@ fn split_function_chars(chars: &[char]) -> Vec<String> {
|
||||
let mut split: Vec<String> = Vec::new();
|
||||
|
||||
let mut buffer: Vec<char> = Vec::new();
|
||||
|
||||
#[derive(Default)]
|
||||
struct BoolSlice {
|
||||
closing_parens: bool,
|
||||
number: bool,
|
||||
letter: bool,
|
||||
variable: bool,
|
||||
masked_num: bool,
|
||||
masked_var: bool,
|
||||
exists: bool,
|
||||
}
|
||||
|
||||
impl BoolSlice {
|
||||
#[inline]
|
||||
fn is_variable(&self) -> bool { self.variable && !self.masked_var }
|
||||
|
||||
#[inline]
|
||||
fn is_number(&self) -> bool { self.number && !self.masked_num }
|
||||
}
|
||||
let mut prev_char: BoolSlice = BoolSlice::default();
|
||||
|
||||
for c in chars {
|
||||
buffer.push(*c);
|
||||
if *c == ')' {
|
||||
split.push(buffer.iter().collect::<String>());
|
||||
buffer.clear();
|
||||
continue;
|
||||
}
|
||||
let mut curr_c = BoolSlice {
|
||||
closing_parens: c == &')',
|
||||
number: is_number(c),
|
||||
letter: is_letter(c),
|
||||
variable: is_variable(c),
|
||||
masked_num: if is_number(c) {
|
||||
prev_char.masked_num
|
||||
} else {
|
||||
false
|
||||
},
|
||||
masked_var: if is_variable(c) {
|
||||
prev_char.masked_var
|
||||
} else {
|
||||
false
|
||||
},
|
||||
exists: true,
|
||||
};
|
||||
|
||||
let buffer_string = buffer.iter().collect::<String>();
|
||||
|
||||
if ((&buffer_string == "log") | (&buffer_string == "log1")) && is_number(&c) {
|
||||
continue;
|
||||
// Check if prev_char is valid
|
||||
if prev_char.exists {
|
||||
// if previous char was a masked number, and current char is a number, mask current char's variable status
|
||||
if prev_char.masked_num && curr_c.number {
|
||||
curr_c.masked_num = true;
|
||||
}
|
||||
|
||||
// if previous char was a masked variable, and current char is a variable, mask current char's variable status
|
||||
if prev_char.masked_var && curr_c.variable {
|
||||
curr_c.masked_var = true;
|
||||
}
|
||||
|
||||
// if letter and not a variable (or a masked variable)
|
||||
if prev_char.letter && !(prev_char.variable && !prev_char.masked_var) {
|
||||
// mask number status if current char is number
|
||||
if curr_c.number {
|
||||
curr_c.masked_num = true;
|
||||
}
|
||||
|
||||
// mask variable status if current char is a variable
|
||||
if curr_c.variable {
|
||||
curr_c.masked_var = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut do_split = false;
|
||||
|
||||
if prev_char.closing_parens {
|
||||
// cases like `)x`, `)2`, and `)(`
|
||||
if (c == &'(')
|
||||
| (curr_c.letter && !curr_c.is_variable())
|
||||
| curr_c.is_variable()
|
||||
| curr_c.is_number()
|
||||
{
|
||||
do_split = true;
|
||||
}
|
||||
} else if c == &'(' {
|
||||
// cases like `x(` and `2(`
|
||||
if (prev_char.is_variable() | prev_char.is_number()) && !prev_char.letter {
|
||||
do_split = true;
|
||||
}
|
||||
} else if prev_char.is_number() {
|
||||
// cases like `2x` and `2sin(x)`
|
||||
if curr_c.is_variable() | curr_c.letter {
|
||||
do_split = true;
|
||||
}
|
||||
} else if curr_c.is_variable() | curr_c.letter {
|
||||
// cases like `e2` and `xx`
|
||||
if prev_char.is_number()
|
||||
| (prev_char.is_variable() && curr_c.is_variable())
|
||||
| prev_char.is_variable()
|
||||
{
|
||||
do_split = true;
|
||||
}
|
||||
} else if (curr_c.is_number() | curr_c.letter | curr_c.is_variable())
|
||||
&& (prev_char.is_number() | prev_char.letter)
|
||||
{
|
||||
// cases like `x2` and `xx`
|
||||
do_split = true;
|
||||
} else if curr_c.is_number() && prev_char.is_variable() {
|
||||
do_split = true;
|
||||
}
|
||||
|
||||
// split and append buffer
|
||||
if do_split {
|
||||
split.push(buffer_string);
|
||||
buffer.clear();
|
||||
}
|
||||
|
||||
buffer.push(*c);
|
||||
prev_char = curr_c;
|
||||
}
|
||||
|
||||
if !buffer.is_empty() {
|
||||
@@ -75,10 +186,6 @@ pub enum Hint<'a> {
|
||||
None,
|
||||
}
|
||||
|
||||
impl<'a> std::fmt::Debug for Hint<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self) }
|
||||
}
|
||||
|
||||
impl<'a> std::fmt::Display for Hint<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
@@ -95,6 +202,12 @@ impl<'a> std::fmt::Display for Hint<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> std::fmt::Debug for Hint<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Hint<'a> {
|
||||
pub fn is_none(&self) -> bool { matches!(self, Hint::None) }
|
||||
|
||||
@@ -163,11 +276,17 @@ mod tests {
|
||||
.flatten()
|
||||
.filter(|func| !SUPPORTED_FUNCTIONS.contains(&func.as_str()))
|
||||
.for_each(|key| {
|
||||
println!("{}", key);
|
||||
if super::generate_hint(&key).is_none() {
|
||||
let split = super::split_function(&key);
|
||||
|
||||
if split.len() != 1 {
|
||||
panic!("failed: {} (len: {}, split: {:?})", key, split.len(), split);
|
||||
}
|
||||
|
||||
let generated_hint = super::generate_hint(&key);
|
||||
if generated_hint.is_none() {
|
||||
println!("success: {}", key);
|
||||
} else {
|
||||
panic!("failed: {}", key);
|
||||
panic!("failed: {} (Hint: '{}')", key, generated_hint.to_string());
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -179,6 +298,7 @@ mod tests {
|
||||
("cos(", vec!["cos("]),
|
||||
("cos(x)sin(x)", vec!["cos(x)", "sin(x)"]),
|
||||
("aaaaaaaaaaa", vec!["aaaaaaaaaaa"]),
|
||||
("emax(x)", vec!["e", "max(x)"]),
|
||||
]);
|
||||
|
||||
for (key, value) in values {
|
||||
|
||||
Reference in New Issue
Block a user