absolute hack of progress with shunting yard
This commit is contained in:
parent
09c2f91ce3
commit
a518550cf1
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
10
src/main.rs
10
src/main.rs
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Reference in New Issue