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;
|
||||
|
||||
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()
|
||||
|
|
|
@ -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;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Reference in New Issue