expression parser logic fix

This commit is contained in:
Christoph J. Scherr 2023-02-13 22:04:46 +01:00
parent 4dbd9e6afb
commit 55c3e142b7
2 changed files with 54 additions and 27 deletions

View File

@ -1,10 +1,6 @@
use std::fmt; use std::fmt;
use std::collections::HashMap;
use regex::Regex; use regex::Regex;
pub const OPERATORS: [char; 5] = ['+', '-', '*', '/', '^'];
fn normalize_string(to_normalize: String) -> String { fn normalize_string(to_normalize: String) -> String {
let mut normalized_text = to_normalize; let mut normalized_text = to_normalize;
normalized_text.retain(|c| !c.is_whitespace()); normalized_text.retain(|c| !c.is_whitespace());
@ -98,11 +94,12 @@ impl Task {
// TaskHandler, if the Task of the Expression is not Task::None // TaskHandler, if the Task of the Expression is not Task::None
pub struct Expression { pub struct Expression {
text: String, text: String,
full_text: String,
task: Task, task: Task,
complex: bool, complex: bool,
inner_value: f64, outer_value: Option<f64>,
outer_value: f64,
children: Vec<Expression>, children: Vec<Expression>,
depth: u8,
} }
// Debug Formatter for Expression // Debug Formatter for Expression
@ -110,11 +107,12 @@ impl fmt::Debug for Expression {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Expression") f.debug_struct("Expression")
.field("text", &self.text) .field("text", &self.text)
.field("full text", &self.full_text)
.field("task", &self.task) .field("task", &self.task)
.field("is complex?", &self.complex) .field("is complex?", &self.complex)
.field("inner value", &self.inner_value)
.field("outer value", &self.outer_value) .field("outer value", &self.outer_value)
.field("children", &self.children) .field("children", &self.children)
.field("depth", &self.depth)
.finish() .finish()
} }
} }
@ -124,11 +122,12 @@ impl Clone for Expression{
fn clone(&self) -> Self { fn clone(&self) -> Self {
Expression { Expression {
text: self.text.clone(), text: self.text.clone(),
full_text: self.full_text.clone(),
task: self.task.clone(), task: self.task.clone(),
complex: self.complex.clone(), // TODO add support for complex numbers complex: self.complex.clone(), // TODO add support for complex numbers
inner_value: self.inner_value.clone(),
outer_value: self.outer_value.clone(), outer_value: self.outer_value.clone(),
children: self.children.clone(), children: self.children.clone(),
depth: self.depth.clone(),
} }
} }
} }
@ -194,16 +193,24 @@ impl Expression {
* example: "12 + log_10(10 + 15) + 3" * example: "12 + log_10(10 + 15) + 3"
* has a sub expression log_10(10 + 5), which has Task::Log with base 10 * has a sub expression log_10(10 + 5), which has Task::Log with base 10
*/ */
pub fn new(expression_text: String, task: Task) -> Expression { pub fn new(expression_text: String, expression_full_text: String, task: Task, depth: u8) -> Expression {
// check if we are too deep
if depth > 254 {
eprintln!("Expression '{}' has a too deep family tree. Maximum generations are 254.", expression_text);
std::process::exit(1);
}
let expression_text = normalize_string(expression_text); let expression_text = normalize_string(expression_text);
let mut task_text_full: String = "".to_string();
let mut children: Vec<Expression> = Vec::new();
let re_contains_sub_expression= Regex::new(r"(\(.*\))|(\[.*\])|(\{.*\})").unwrap(); let re_contains_sub_expression= Regex::new(r"(\(.*\))|(\[.*\])|(\{.*\})").unwrap();
if re_contains_sub_expression.is_match(expression_text.as_str()) { if re_contains_sub_expression.is_match(expression_text.as_str()) {
let brace_groups: Vec<Vec<(usize, usize)>> = find_brace_groups(expression_text.clone()); let brace_groups: Vec<Vec<(usize, usize)>> = find_brace_groups(expression_text.clone());
let mut brace_groups_texts: Vec<String> = Vec::new(); let mut brace_groups_texts: Vec<String> = Vec::new();
let mut children: Vec<Expression> = Vec::new();
// 1 brace group per possible combination, by default, this is only (), so 1 iteration. // 1 brace group per possible combination, by default, this is only (), so 1 iteration.
// This is still O(n¹) // This is still O(n¹)
@ -219,12 +226,12 @@ impl Expression {
let mut stop_at: usize = 0; let mut stop_at: usize = 0;
// TODO check for task parameters // TODO check for task parameters
for (index, char) in possible_task.chars().enumerate() { for (index, char) in possible_task.chars().enumerate() {
stop_at = index; if !(char.is_alphanumeric() | (char == '.') | (char == '_')) | (char == '+') {
if !(char.is_alphanumeric() | (char == '.') | (char == '_')) & (char == '+') {
break; break;
} }
stop_at = index;
} }
let task_text_full = possible_task.clone()[..stop_at + 0].chars().rev().collect::<String>(); task_text_full = possible_task.clone()[..stop_at+ 1].chars().rev().collect::<String>();
let task: Task; let task: Task;
if task_text_full.contains('_') { if task_text_full.contains('_') {
let split: Vec<&str> = task_text_full.split('_').collect(); let split: Vec<&str> = task_text_full.split('_').collect();
@ -233,7 +240,9 @@ impl Expression {
else { else {
task = Task::new(task_text_full.as_str(), ""); task = Task::new(task_text_full.as_str(), "");
} }
let child = Expression::new(text.to_string(), task); let child_full_text = task_text_full + "(" + text + ")";
dbg!(&child_full_text);
let child = Expression::new(text.to_string(), child_full_text, task, depth+1);
children.push(child); children.push(child);
} }
} }
@ -242,11 +251,12 @@ impl Expression {
let expression = Expression { let expression = Expression {
text: expression_text, text: expression_text,
full_text: normalize_string(expression_full_text),
task: task, task: task,
complex: false, complex: false,
inner_value: 0.0, outer_value: Some(0.0),
outer_value: 0.0, children: children,
children: Vec::new(), depth: depth,
}; };
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
dbg!(&expression); dbg!(&expression);
@ -254,15 +264,33 @@ impl Expression {
} }
// calculate value for expression. // calculate value for expression.
pub fn process(&self) { pub fn process(self) -> String {
let normalized_text = self.normalize_text(); let mut normalized_text = self.normalize_text();
// iterate through chars. Find logical groups, and add them into a hashmap. //let re_numeric = Regex::new(r"\d+(\.\d+)?");
let re_numeric = Regex::new(r"\d+(\.\d+)?"); /*
* Algorithm:
*
* First, search child expressions in normalized_text by searching for the text of all
* children in normalized_text. If an expression is found, a value for it should be
* calculated (recursive!) and the text should be substituted with the calculated value.
* If a child expression is not found in the normalized_text, throw an error, as an
* expression has a child but does not contain it's text. (note: a childs child
* expressions are not the child expression of the original expression, so no need to
* worry about the order of substituting texts for values.)
*
* Once there are no more child expressions in the normalized_text, we can use the
* shunting yards algorithm to calculate the result. I'm not yet sure, if I want to use
* another developers shunting yard algorithm or implement it by myself.
*/
// iterate through children, substitute childrens text with childrens results (as string
// slice).
for child in self.children {
dbg!(normalized_text.replace(child.full_text.as_str(), "0"/* debug only */));
normalized_text = normalized_text.replace(child.full_text.as_str(), "0");
}
todo!(); todo!();
// no idea how to do this, seriously, this is so complicated, i wouldn't ever have thought
// this. I probably need to add a enum Operation and an enum Value to create a
// HashMap<String, Value> and match back the Value type to some real usable one? This is so
// complicated.
} }
// wrapper for normalize_string() // wrapper for normalize_string()

View File

@ -46,9 +46,8 @@ fn main() {
expression_texts_concat.push(args.expressions.join(" ").trim().to_string()); expression_texts_concat.push(args.expressions.join(" ").trim().to_string());
for expression_text in expression_texts_concat { for expression_text in expression_texts_concat {
expression_vec.push(Expression::new(expression_text, Task::None)); expression_vec.push(Expression::new(expression_text.clone(), expression_text, Task::None, 0));
} }
for expression in expression_vec { for expression in expression_vec {
expression.process(); expression.process();
} }