Merge pull request #5 from PlexSheep/devel

Basic calc functionality works
This commit is contained in:
Christoph J. Scherr 2023-02-14 22:28:14 +01:00 committed by GitHub
commit bd2245b01d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 219 additions and 13 deletions

View File

@ -1,6 +1,6 @@
[package] [package]
name = "rust_command_line_calculator" name = "rust_command_line_calculator"
version = "0.2.0" version = "0.2.1"
edition = "2021" edition = "2021"
authors = ["Christoph J. Scherr <software@cscherr.de>"] authors = ["Christoph J. Scherr <software@cscherr.de>"]
license = "GPL3" license = "GPL3"

View File

@ -1,4 +1,4 @@
use std::{fmt, error::Error, num::IntErrorKind}; use std::fmt;
use regex::Regex; use regex::Regex;
pub mod shunting_yard; pub mod shunting_yard;
@ -284,7 +284,7 @@ impl Expression {
eprintln!( eprintln!(
"Could not calculate result of child expression '{}': {}", "Could not calculate result of child expression '{}': {}",
child.text, child.text,
"error placeholder TODO" err
); );
std::process::exit(2); std::process::exit(2);
} }

View File

@ -1,3 +1,4 @@
use std::fmt;
/* /*
* Custom made implementation of the shunting yard algorithm. * Custom made implementation of the shunting yard algorithm.
@ -17,6 +18,15 @@ enum Associativity {
Left Left
} }
impl fmt::Debug for Associativity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Associativity::Right => write!(f, "Right"),
Associativity::Left => write!(f, "Left"),
}
}
}
#[derive(PartialEq)] #[derive(PartialEq)]
pub struct Operator { pub struct Operator {
character: char, character: char,
@ -24,6 +34,16 @@ pub struct Operator {
associativity: Associativity associativity: Associativity
} }
impl fmt::Debug for Operator {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Operator")
.field("character", &self.character)
.field("precedence", &self.precedence)
.field("associativity", &self.associativity)
.finish()
}
}
impl Operator { impl Operator {
pub fn is_operator(c: char) -> bool { pub fn is_operator(c: char) -> bool {
for op in OPERATORS { for op in OPERATORS {
@ -69,7 +89,7 @@ const DIVISION: Operator = Operator {
}; };
const EXPONENTIATION: Operator = Operator { const EXPONENTIATION: Operator = Operator {
character: '*', character: '^',
precedence: 2, precedence: 2,
associativity: Associativity::Right associativity: Associativity::Right
}; };
@ -128,19 +148,19 @@ pub fn form_reverse_polish_notation(regular_math: &str) -> Result<Vec<String>, S
dbg!(&operator_stack); dbg!(&operator_stack);
let o2 = match Operator::get_operator(*(operator_stack.clone().last().clone().unwrap())) { let o2 = match Operator::get_operator(*(operator_stack.clone().last().clone().unwrap())) {
Some(valid_op) => valid_op, Some(valid_op) => valid_op,
None => {panic!("Operator '{}' not found.", token);}, None => {panic!("Operator '{}' not found.", token);},
}; };
// and // and
// (o2 has greater precedence than o1 or (o1 and o2 have the same precedence and o1 // (o2 has greater precedence than o1 or (o1 and o2 have the same precedence and o1
// is left associative)) // is left associative))
while ((operator_stack.last().is_some()) & ((o2.precedence > o1.precedence) | ((o1.precedence == o2.precedence) & (o1.associativity == Associativity::Left)))) { while (operator_stack.last().is_some()) & ((o2.precedence > o1.precedence) | ((o1.precedence == o2.precedence) & (o1.associativity == Associativity::Left))) {
// pop o2 from the operator stack into the output queue. // pop o2 from the operator stack into the output queue.
// after this debug statement, the operator_stack is empty for no reason!!!! // after this debug statement, the operator_stack is empty for no reason!!!!
// FIXME // FIXME
let my_c = match operator_stack.pop() { let my_c = match operator_stack.pop() {
Some(c) => c, Some(c) => c,
None => {panic!("weirdly gone!")}, None => {panic!("weirdly gone!")},
}; };
output_queue.push(vec![my_c]); output_queue.push(vec![my_c]);
} }
} }
@ -149,10 +169,10 @@ pub fn form_reverse_polish_notation(regular_math: &str) -> Result<Vec<String>, S
/* /*
// Unnessecary, will be processed by the expression parser // Unnessecary, will be processed by the expression parser
else if '(' == token { else if '(' == token {
println!("("); println!("(");
} }
else if ')' == token { else if ')' == token {
println!(")"); println!(")");
} }
*/ */
else { else {
@ -180,5 +200,91 @@ pub fn form_reverse_polish_notation(regular_math: &str) -> Result<Vec<String>, S
// after we have the rpn, we may want to calculate the values with it. // after we have the rpn, we may want to calculate the values with it.
pub fn calc_reverse_polish_notation(rpn: Vec<String>) -> Result<f64, String> { pub fn calc_reverse_polish_notation(rpn: Vec<String>) -> Result<f64, String> {
Ok(0.0)
// # function to evaluate reverse polish notation
// def evaluate(expression):
// # splitting expression at whitespaces
// expression = expression.split()
// # stack
// stack = []
// # iterating expression
// for ele in expression:
// # ele is a number
// if ele not in '/*+-':
// stack.append(int(ele))
// # ele is an operator
// else:
// # getting operands
// right = stack.pop()
// left = stack.pop()
// # performing operation according to operator
// if ele == '+':
// stack.append(left + right)
// elif ele == '-':
// stack.append(left - right)
// elif ele == '*':
// stack.append(left * right)
// elif ele == '/':
// stack.append(int(left / right))
// # return final answer.
// return stack.pop()
let mut stack: Vec<f64> = Vec::new();
for group in rpn {
dbg!(&group);
// find out what the group is, an operator, a number, or a variable.
// TODO add variables
if !Operator::is_operator(group.chars().last().unwrap()) {
let possible_num = group.parse::<f64>();
match possible_num {
Ok(valid) => {stack.push(valid);},
Err(_whatever) => {
eprint!("weird error happened, ending process...");
std::process::exit(2);
},
}
dbg!(&stack);
}
else {
let op: Operator = Operator::get_operator(group.chars().last().unwrap()).unwrap();
dbg!(&op);
let right = stack.pop().unwrap();
let left = stack.pop().unwrap();
if op == ADDITION {
stack.push(left + right);
}
else if op == SUBTRACTION {
stack.push(left - right);
}
else if op == MULTIPLICATION {
stack.push(left * right);
}
else if op == DIVISION {
stack.push(left / right);
}
else if op == EXPONENTIATION {
stack.push(left.powf(right));
}
else {
todo!();
}
}
}
if stack.is_empty() {
return Err("result stack empty".to_string());
}
if stack.len() > 1 {
dbg!(stack);
return Err("result stack has too many results.".to_string());
}
return Ok(stack[0]);
} }

7
src/lib.rs Normal file
View File

@ -0,0 +1,7 @@
// Make module public
pub mod expression_parser;
// Make the function available at the root of the crate
pub use expression_parser::*;

View File

@ -1 +0,0 @@
13 + 2525 + sqrt(15 + log_10(100)) + power_10(10)

94
tests/test.rs Normal file
View File

@ -0,0 +1,94 @@
use rust_command_line_calculator as rclc;
#[test]
fn test_tests_are_loaded() {
assert_eq!("AA", "AA");
}
#[test]
fn test_main_sum_simple() {
let my_expression: rclc::Expression =
rclc::Expression::new(
String::from("40 + 33"),
String::from("40 + 33"),
rclc::Task::None,
0);
assert_eq!(my_expression.process().unwrap(), 73.0);
}
#[test]
fn test_main_sum_chain() {
let my_expression: rclc::Expression =
rclc::Expression::new(
String::from("20340 + 32424 + 24 + 23"),
String::from("20340 + 32424 + 24 + 23"),
rclc::Task::None,
0);
assert_eq!(my_expression.process().unwrap(), 20340.0 + 32424.0 + 24.0 + 23.0);
}
#[test]
fn test_main_difference_simple() {
let my_expression: rclc::Expression =
rclc::Expression::new(
String::from("33-13"),
String::from("33-13"),
rclc::Task::None,
0);
assert_eq!(my_expression.process().unwrap(), 33.0 - 13.0);
}
#[test]
fn test_main_difference_chain() {
let my_expression: rclc::Expression =
rclc::Expression::new(
String::from("353535 - 2405 - 33 - 13 - 4"),
String::from("353535 - 2405 - 33 - 13 - 4"),
rclc::Task::None,
0);
assert_eq!(my_expression.process().unwrap(), 353535.0 - 2405.0 - 33.0 - 13.0 - 4.0);
}
#[test]
fn test_main_product_simple() {
let my_expression: rclc::Expression =
rclc::Expression::new(
String::from("353* 13"),
String::from("353* 13"),
rclc::Task::None,
0);
assert_eq!(my_expression.process().unwrap(), 353.0 * 13.0);
}
#[test]
fn test_main_procuct_chain() {
let my_expression: rclc::Expression =
rclc::Expression::new(
String::from("353535 * 2405 * 33 * 13 * 4"),
String::from("353535 * 2405 * 33 * 13 * 4"),
rclc::Task::None,
0);
assert_eq!(my_expression.process().unwrap(), 353535.0 * 2405.0 * 33.0 * 13.0 * 4.0);
}
#[test]
fn test_main_quotient_simple() {
let my_expression: rclc::Expression =
rclc::Expression::new(
String::from("353 / 13"),
String::from("353 / 13"),
rclc::Task::None,
0);
assert_eq!(my_expression.process().unwrap(), 353.0 / 13.0);
}
#[test]
fn test_main_quotient_chain() {
let my_expression: rclc::Expression =
rclc::Expression::new(
String::from("353535 / 2405 / 33 / 13 / 4"),
String::from("353535 / 2405 / 33 / 13 / 4"),
rclc::Task::None,
0);
assert_eq!(my_expression.process().unwrap(), 353535.0 / 2405.0 / 33.0 / 13.0 / 4.0);
}