Compare commits

..

No commits in common. "aa2aaaa4bb2c1ff09d972e7bca435745a41d045b" and "801e052ce2bce29a70281b640f1495d913594fa5" have entirely different histories.

9 changed files with 33 additions and 250 deletions

BIN
.swp Normal file

Binary file not shown.

View file

@ -1,10 +0,0 @@
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.1"
version = "0.2.0"
edition = "2021"
authors = ["Christoph J. Scherr <software@cscherr.de>"]
license = "GPL3"

View file

@ -3,8 +3,22 @@ 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 `alpha`. This means that important major features are still missing
Currently, rclc's status is `indev`. This means that important major features are still missing
and bugs might not only be possible but common.
# Install
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.
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 |

View file

@ -1,4 +1,4 @@
use std::fmt;
use std::{fmt, error::Error, num::IntErrorKind};
use regex::Regex;
pub mod shunting_yard;
@ -221,8 +221,7 @@ impl Expression {
}
stop_at = index;
}
#[cfg(debug_assertions)]
{dbg!(&stop_at);}
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>();
@ -285,29 +284,23 @@ impl Expression {
eprintln!(
"Could not calculate result of child expression '{}': {}",
child.text,
err
"error placeholder TODO"
);
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,4 +1,3 @@
use std::fmt;
/*
* Custom made implementation of the shunting yard algorithm.
@ -18,15 +17,6 @@ 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,
@ -34,16 +24,6 @@ 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 {
@ -78,19 +58,19 @@ const SUBTRACTION: Operator = Operator {
const MULTIPLICATION: Operator = Operator {
character: '*',
precedence: 3,
precedence: 2,
associativity: Associativity::Left
};
const DIVISION: Operator = Operator {
character: '/',
precedence: 3,
precedence: 2,
associativity: Associativity::Left
};
const EXPONENTIATION: Operator = Operator {
character: '^',
precedence: 4,
character: '*',
precedence: 2,
associativity: Associativity::Right
};
@ -108,9 +88,8 @@ 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 == '.') {
@ -143,26 +122,25 @@ 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]);
}
}
@ -171,10 +149,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 {
@ -185,7 +163,6 @@ 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
@ -193,7 +170,6 @@ 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 {
@ -204,95 +180,5 @@ 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> {
// # 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]);
Ok(0.0)
}

View file

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

1
test.txt Normal file
View file

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

View file

@ -1,94 +0,0 @@
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);
}