expression parser logic fix
This commit is contained in:
parent
4dbd9e6afb
commit
55c3e142b7
|
@ -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()
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
Reference in New Issue