From a518550cf1dedf0c387f6ce3c9fce1bc4754d956 Mon Sep 17 00:00:00 2001 From: PlexSheep Date: Tue, 14 Feb 2023 14:52:28 +0100 Subject: [PATCH] absolute hack of progress with shunting yard --- src/expression_parser.rs | 63 +++++++--- src/{ => expression_parser}/linear_algebra.rs | 0 src/expression_parser/shunting_yard.rs | 108 ++++++++++++++++++ src/main.rs | 10 +- src/shunting_yard.rs | 0 5 files changed, 162 insertions(+), 19 deletions(-) rename src/{ => expression_parser}/linear_algebra.rs (100%) create mode 100644 src/expression_parser/shunting_yard.rs delete mode 100644 src/shunting_yard.rs diff --git a/src/expression_parser.rs b/src/expression_parser.rs index cea657e..1268f24 100644 --- a/src/expression_parser.rs +++ b/src/expression_parser.rs @@ -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, + outer_value: Result, children: Vec, 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 = 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::(); 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::(); + 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::(); 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 { 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() diff --git a/src/linear_algebra.rs b/src/expression_parser/linear_algebra.rs similarity index 100% rename from src/linear_algebra.rs rename to src/expression_parser/linear_algebra.rs diff --git a/src/expression_parser/shunting_yard.rs b/src/expression_parser/shunting_yard.rs new file mode 100644 index 0000000..16bb5b7 --- /dev/null +++ b/src/expression_parser/shunting_yard.rs @@ -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 { + let mut output_queue: Vec = Vec::new(); + let mut input_queue: Vec = regular_math.chars().rev().collect(); + let mut operator_stack: Vec = 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 { + Ok(0.0) +} diff --git a/src/main.rs b/src/main.rs index 2d5173b..c9d9782 100644 --- a/src/main.rs +++ b/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); + } + } } } diff --git a/src/shunting_yard.rs b/src/shunting_yard.rs deleted file mode 100644 index e69de29..0000000