Compare commits

...

13 commits

Author SHA1 Message Date
aa2aaaa4bb
Merge pull request #8 from PlexSheep/cscherrNT-patch-2
Delete .swp
2023-08-22 10:59:04 +02:00
Christoph J. Scherr
107dbe59c2
Delete .swp 2023-08-22 10:58:37 +02:00
a3f6ab1407
Update README.md 2023-08-22 10:56:59 +02:00
c3160d612b
Update CITATION.cff 2023-08-22 10:51:16 +02:00
9a355a4ad8
Merge pull request #7 from PlexSheep/cscherrNT-patch-1
Create CITATION.cff
2023-08-22 10:50:30 +02:00
Christoph J. Scherr
b8433fc19b
Create CITATION.cff 2023-08-22 10:49:57 +02:00
23a64d217f
Update README.md 2023-02-14 22:42:40 +01:00
fa6124f4fd
Merge pull request #6 from PlexSheep/devel
debug logs only in debug binary, precedence fix
2023-02-14 22:34:33 +01:00
698eb51be9 debug logs only in debug binary, precedence fix 2023-02-14 22:34:04 +01:00
bd2245b01d
Merge pull request #5 from PlexSheep/devel
Basic calc functionality works
2023-02-14 22:28:14 +01:00
71d7626311 version change 2023-02-14 22:26:57 +01:00
617d7c760a fixed basic calc, testing 2023-02-14 22:25:42 +01:00
37e29f17f0 early calculation 2023-02-14 18:47:06 +01:00
9 changed files with 250 additions and 33 deletions

BIN
.swp

Binary file not shown.

10
CITATION.cff Normal file
View file

@ -0,0 +1,10 @@
cff-version: 1.2.0
message: "If you use this software, please cite it as below."
authors:
- family-names: "Scherr"
given-names: "Christoph Johannes"
orcid: "https://orcid.org/0000-0000-0000-0000"
title: "RustCommandLineCalculator"
version: 0.2.1
date-released: 2023-08-22
url: "https://github.com/PlexSheep/RustCommandLineCalculator"

View file

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

View file

@ -3,22 +3,8 @@ RustCommandLineCalcuator, or simply rclc for short is a fast, scriptable calcula
designed to run right in your shell. No more need to use the python shell, or ugly and bloated
GUIs. Easily calculate complex formulas in your bash scripts.
Currently, rclc's status is `indev`. This means that important major features are still missing
Currently, rclc's status is `alpha`. This means that important major features are still missing
and bugs might not only be possible but common.
# Install
Not yet recommended, but you can always compile rclc by yourself with `cargo build` and copy the
compiled binary executable to `/usr/local/bin`.
# Compatability
| Supported OS | OS |
|--------------|-----------------------|
| Current | Gnu/Linux |
| Planned | Windows, OSX, FreeBSD |
| Not Planned | TempleOS |
| Supported Architectures | Arch |
|-------------------------|--------|
| Current | x86_64 |
| Planned | major arm |
| Not Planned | any legacy |
rclc is still in an early version, but if you wish, you can compile and install it using `cargo install --path .`, this will copy a release version to `$HOME/.cargo/bin`. Otherweise, you can compile rclc manually using `cargo build --release` and copy the binary in `target/release/rclc` to a directory of your choice.

View file

@ -1,4 +1,4 @@
use std::{fmt, error::Error, num::IntErrorKind};
use std::fmt;
use regex::Regex;
pub mod shunting_yard;
@ -221,7 +221,8 @@ impl Expression {
}
stop_at = index;
}
dbg!(&stop_at);
#[cfg(debug_assertions)]
{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>();
@ -284,23 +285,29 @@ impl Expression {
eprintln!(
"Could not calculate result of child expression '{}': {}",
child.text,
"error placeholder TODO"
err
);
std::process::exit(2);
}
};
#[cfg(debug_assertions)]{
dbg!(&child.full_text);
dbg!(&child_full_text);
}
normalized_text = normalized_text.replace(child.full_text.as_str(), child_full_text.as_str());
}
#[cfg(debug_assertions)]{
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
let rpn = shunting_yard::form_reverse_polish_notation(&normalized_text);
match rpn {
Ok(valid_rpn) => {
#[cfg(debug_assertions)]{
dbg!(&valid_rpn);
}
return shunting_yard::calc_reverse_polish_notation(valid_rpn);
},
Err(err) => {

View file

@ -1,3 +1,4 @@
use std::fmt;
/*
* Custom made implementation of the shunting yard algorithm.
@ -17,6 +18,15 @@ enum Associativity {
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)]
pub struct Operator {
character: char,
@ -24,6 +34,16 @@ pub struct Operator {
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 {
pub fn is_operator(c: char) -> bool {
for op in OPERATORS {
@ -58,19 +78,19 @@ const SUBTRACTION: Operator = Operator {
const MULTIPLICATION: Operator = Operator {
character: '*',
precedence: 2,
precedence: 3,
associativity: Associativity::Left
};
const DIVISION: Operator = Operator {
character: '/',
precedence: 2,
precedence: 3,
associativity: Associativity::Left
};
const EXPONENTIATION: Operator = Operator {
character: '*',
precedence: 2,
character: '^',
precedence: 4,
associativity: Associativity::Right
};
@ -88,8 +108,9 @@ pub fn form_reverse_polish_notation(regular_math: &str) -> Result<Vec<String>, S
while !(input_queue.is_empty()) {
// read a token
let token: char = input_queue.pop().unwrap();
#[cfg(debug_assertions)]
dbg!(&token);
// if the token is:
// a number:
if token.is_numeric() | (token == '.') {
@ -122,25 +143,26 @@ pub fn form_reverse_polish_notation(regular_math: &str) -> Result<Vec<String>, S
Some(valid_op) => valid_op,
None => {panic!("Operator '{}' not found.", token);},
};
// while there is an operator o2 at the top of the stack
if !operator_stack.is_empty() {
#[cfg(debug_assertions)]
dbg!(&operator_stack);
let o2 = match Operator::get_operator(*(operator_stack.clone().last().clone().unwrap())) {
Some(valid_op) => valid_op,
None => {panic!("Operator '{}' not found.", token);},
None => {panic!("Operator '{}' not found.", token);},
};
// and
// (o2 has greater precedence than o1 or (o1 and o2 have the same precedence and o1
// 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.
// after this debug statement, the operator_stack is empty for no reason!!!!
// FIXME
let my_c = match operator_stack.pop() {
Some(c) => c,
None => {panic!("weirdly gone!")},
};
};
output_queue.push(vec![my_c]);
}
}
@ -149,10 +171,10 @@ pub fn form_reverse_polish_notation(regular_math: &str) -> Result<Vec<String>, S
/*
// Unnessecary, will be processed by the expression parser
else if '(' == token {
println!("(");
println!("(");
}
else if ')' == token {
println!(")");
println!(")");
}
*/
else {
@ -163,6 +185,7 @@ pub fn form_reverse_polish_notation(regular_math: &str) -> Result<Vec<String>, S
if currently_processing_numeric_group {
output_queue.push(current_numeric_group);
}
#[cfg(debug_assertions)]
dbg!(&output_queue);
// afterwards, process any operators still on the operator_stack
@ -170,6 +193,7 @@ pub fn form_reverse_polish_notation(regular_math: &str) -> Result<Vec<String>, S
output_queue.push(vec![operator_stack.pop().unwrap()]);
}
#[cfg(debug_assertions)]
dbg!(&output_queue);
let mut rpn: Vec<String> = Vec::new();
for group in output_queue {
@ -180,5 +204,95 @@ 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.
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 {
#[cfg(debug_assertions)]
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);
},
}
#[cfg(debug_assertions)]
dbg!(&stack);
}
else {
let op: Operator = Operator::get_operator(group.chars().last().unwrap()).unwrap();
#[cfg(debug_assertions)]
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 {
#[cfg(debug_assertions)]
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);
}