MAJOR refactoring

This commit is contained in:
Simon Gardling
2022-05-03 11:50:13 -04:00
parent 8952552eef
commit 13a47ec30b
12 changed files with 243 additions and 218 deletions

View File

@@ -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 {