absolute hack of progress with shunting yard

This commit is contained in:
Christoph J. Scherr 2023-02-14 14:52:28 +01:00
parent 09c2f91ce3
commit a518550cf1
5 changed files with 162 additions and 19 deletions

View File

@ -1,6 +1,8 @@
use std::fmt; use std::{fmt, error::Error, num::IntErrorKind};
use regex::Regex; use regex::Regex;
pub mod shunting_yard;
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());
@ -80,7 +82,9 @@ impl Task {
} }
}, },
// what to do if a bad task was given: // what to do if a bad task was given:
&_ => {eprintln!("Bad Task: {}", task_text); std::process::exit(1); }, // this would be throwing an error and aborting
//&_ => {eprintln!("Bad Task: {}", task_text); std::process::exit(1); },
_ => Task::None,
} }
} }
} }
@ -93,11 +97,11 @@ impl Task {
// once the Value of the Expression got calculated, the calculated value should be sent to the // once the Value of the Expression got calculated, the calculated value should be sent to the
// 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, pub text: String,
full_text: String, full_text: String,
task: Task, task: Task,
complex: bool, complex: bool,
outer_value: Option<f64>, outer_value: Result<f64, String>,
children: Vec<Expression>, children: Vec<Expression>,
depth: u8, depth: u8,
} }
@ -190,7 +194,7 @@ impl Expression {
} }
let expression_text = normalize_string(expression_text); let expression_text = normalize_string(expression_text);
let mut task_text_full: String = "".to_string(); let mut task_text_full: String;
let mut children: Vec<Expression> = Vec::new(); 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();
@ -206,20 +210,21 @@ impl Expression {
for pair in brace_group { for pair in brace_group {
let text = &expression_text[pair.0..pair.1 + 1]; let text = &expression_text[pair.0..pair.1 + 1];
let text = &text[1..text.len() - 1]; let text = &text[1..text.len() - 1];
#[cfg(debug_assertions)]
brace_groups_texts.push(text.to_string()); brace_groups_texts.push(text.to_string());
// we have the expression_text, now we just need to get the task until we can // we have the expression_text, now we just need to get the task until we can
// pass these parameters into Expression::new(). This is the recursive part. // pass these parameters into Expression::new(). This is the recursive part.
let possible_task = &expression_text[..pair.0].chars().rev().collect::<String>(); let possible_task = &expression_text[..pair.0].chars().rev().collect::<String>();
let mut stop_at: usize = 0; let mut stop_at: usize = 0;
// TODO check for task parameters
for (index, char) in possible_task.chars().enumerate() { for (index, char) in possible_task.chars().enumerate() {
if !(char.is_alphanumeric() | (char == '.') | (char == '_')) | (char == '+') { if !(char.is_alphanumeric()) {
break; break;
} }
stop_at = index; stop_at = index;
} }
task_text_full = possible_task.clone()[..stop_at+ 1].chars().rev().collect::<String>(); dbg!(&stop_at);
// needed for none task: '1 + (1 + 1)'
let fixup = if stop_at == 0 { 0 } else { 1 };
task_text_full = possible_task.clone()[..stop_at+ fixup].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();
@ -235,21 +240,20 @@ impl Expression {
} }
} }
let expression = Expression { let expression = Expression {
text: expression_text, text: expression_text,
full_text: normalize_string(expression_full_text), full_text: normalize_string(expression_full_text),
task: task, task,
complex: false, complex: false,
outer_value: None, outer_value: Err("Value not yet calculated.".to_string()),
children: children, children,
depth: depth, depth,
}; };
expression expression
} }
// calculate value for expression. // calculate value for expression.
pub fn process(self) -> String { pub fn process(self) -> Result<f64, String> {
let mut normalized_text = self.normalize_text(); let mut normalized_text = self.normalize_text();
//let re_numeric = Regex::new(r"\d+(\.\d+)?"); //let re_numeric = Regex::new(r"\d+(\.\d+)?");
/* /*
@ -273,12 +277,37 @@ impl Expression {
// iterate through children, substitute childrens text with childrens results (as string // iterate through children, substitute childrens text with childrens results (as string
// slice). // slice).
for child in self.children { for child in self.children {
normalized_text = normalized_text.replace(child.full_text.clone().as_str(), child.process().as_str()); //normalized_text = normalized_text.replace(child.full_text.clone().as_str(), child.process().expect(self.text).as_str());
let child_full_text = match child.clone().process() {
Ok(result) => result.to_string(),
Err(err) => {
eprintln!(
"Could not calculate result of child expression '{}': {}",
child.text,
"error placeholder TODO"
);
std::process::exit(2);
} }
};
dbg!(&child.full_text);
dbg!(&child_full_text);
normalized_text = normalized_text.replace(child.full_text.as_str(), child_full_text.as_str());
}
dbg!(&normalized_text);
// TODO Shunting yards algorithm, as we now have only calculatable values left. // TODO Shunting yards algorithm, as we now have only calculatable values left.
// Implement this as public module in shunting_yard.rs // Implement this as public module in shunting_yard.rs
// self.result = MYRESULT // self.result = MYRESULT
return "RESULT_STILL_NOT_IMPLEMENTED".to_string(); let rpn = shunting_yard::form_reverse_polish_notation(&normalized_text);
match rpn {
Ok(valid_rpn) => {
dbg!(&valid_rpn);
return shunting_yard::calc_reverse_polish_notation(&valid_rpn);
},
Err(err) => {
eprintln!("Could not calculate a result for expression '{}': {err}", self.text);
std::process::exit(2);
},
}
} }
// wrapper for normalize_string() // wrapper for normalize_string()

View File

@ -0,0 +1,108 @@
/*
* Custom made implementation of the shunting yard algorithm.
* Makes a regular mathmatical expression into reverse polish notation,
* a + b -> a b +
* a * b + c -> a b * c +
* and so on.
* these can be easily interpreted by an algorithm to calculate the value of any given term.
*
* note: this version of shunting yard does not implement functions. They are handled by the
* expression parser.
*/
enum Associativity {
Right,
Left
}
pub struct Operator {
character: char,
precedence: u8,
associativity: Associativity
}
impl Operator {
pub fn is_operator(c: char) -> bool {
for op in OPERATORS {
if c == op.character { return true; }
}
return false;
}
}
const ADDITION: Operator = Operator {
character: '+',
precedence: 2,
associativity: Associativity::Left
};
const SUBTRACTION: Operator = Operator {
character: '-',
precedence: 2,
associativity: Associativity::Left
};
const MULTIPLICATION: Operator = Operator {
character: '*',
precedence: 2,
associativity: Associativity::Left
};
const DIVISION: Operator = Operator {
character: '/',
precedence: 2,
associativity: Associativity::Left
};
const EXPONENTIATION: Operator = Operator {
character: '*',
precedence: 2,
associativity: Associativity::Left
};
const OPERATORS: [Operator; 5] = [ADDITION, SUBTRACTION, MULTIPLICATION, DIVISION, EXPONENTIATION];
pub fn form_reverse_polish_notation(regular_math: &str) -> Result<String, String> {
let mut output_queue: Vec<char> = Vec::new();
let mut input_queue: Vec<char> = regular_math.chars().rev().collect();
let mut operator_stack: Vec<char> = Vec::new();
// process all tokens first.
while !(input_queue.is_empty()) {
let token: char = *(input_queue.last().unwrap());
input_queue.pop();
dbg!(&token);
if token.is_numeric() {
println!("number");
}
else if Operator::is_operator(token) {
println!("operator");
}
// Unnessecary, will be processed by the expression parser
//else if '(' == token {
// println!("(");
//}
//else if ')' == token {
// println!(")");
//}
else {
eprintln!("Unrecognized token: '{token}'");
std::process::exit(1);
}
}
// afterwards, process any operators still on the operator_stack
while !(operator_stack.is_empty()) {
todo!();
}
Ok("TODO".to_string())
}
// after we have the rpn, we may want to calculate the values with it.
pub fn calc_reverse_polish_notation(rpn: &str) -> Result<f64, String> {
Ok(0.0)
}

View File

@ -1,7 +1,6 @@
use clap::Parser; use clap::Parser;
mod expression_parser; mod expression_parser;
mod linear_algebra;
use expression_parser::Expression; use expression_parser::Expression;
use expression_parser::Task; use expression_parser::Task;
@ -53,7 +52,14 @@ fn main() {
dbg!(&expression_vec); dbg!(&expression_vec);
} }
for expression in expression_vec { for expression in expression_vec {
println!("{}", expression.process()); match expression.clone().process() {
Ok(result) => {
println!("{result}");
},
Err(err) => {
eprintln!("Could not calculate expression '{}': {}", &expression.text, err);
}
}
} }
} }

View File