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;
pub mod shunting_yard;
fn normalize_string(to_normalize: String) -> String {
let mut normalized_text = to_normalize;
normalized_text.retain(|c| !c.is_whitespace());
@ -80,7 +82,9 @@ impl Task {
}
},
// 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
// TaskHandler, if the Task of the Expression is not Task::None
pub struct Expression {
text: String,
pub text: String,
full_text: String,
task: Task,
complex: bool,
outer_value: Option<f64>,
outer_value: Result<f64, String>,
children: Vec<Expression>,
depth: u8,
}
@ -190,7 +194,7 @@ impl Expression {
}
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 re_contains_sub_expression= Regex::new(r"(\(.*\))|(\[.*\])|(\{.*\})").unwrap();
@ -206,20 +210,21 @@ impl Expression {
for pair in brace_group {
let text = &expression_text[pair.0..pair.1 + 1];
let text = &text[1..text.len() - 1];
#[cfg(debug_assertions)]
brace_groups_texts.push(text.to_string());
// 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.
let possible_task = &expression_text[..pair.0].chars().rev().collect::<String>();
let mut stop_at: usize = 0;
// TODO check for task parameters
for (index, char) in possible_task.chars().enumerate() {
if !(char.is_alphanumeric() | (char == '.') | (char == '_')) | (char == '+') {
if !(char.is_alphanumeric()) {
break;
}
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;
if task_text_full.contains('_') {
let split: Vec<&str> = task_text_full.split('_').collect();
@ -235,21 +240,20 @@ impl Expression {
}
}
let expression = Expression {
text: expression_text,
full_text: normalize_string(expression_full_text),
task: task,
task,
complex: false,
outer_value: None,
children: children,
depth: depth,
outer_value: Err("Value not yet calculated.".to_string()),
children,
depth,
};
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 re_numeric = Regex::new(r"\d+(\.\d+)?");
/*
@ -273,12 +277,37 @@ impl Expression {
// iterate through children, substitute childrens text with childrens results (as string
// slice).
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.
// Implement this as public module in shunting_yard.rs
// 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()

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;
mod expression_parser;
mod linear_algebra;
use expression_parser::Expression;
use expression_parser::Task;
@ -53,7 +52,14 @@ fn main() {
dbg!(&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