added additional features and refactoring
This commit is contained in:
9
Cargo.lock
generated
9
Cargo.lock
generated
@@ -2,6 +2,15 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "indoc"
|
||||
version = "2.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8"
|
||||
|
||||
[[package]]
|
||||
name = "rpn"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"indoc",
|
||||
]
|
||||
|
||||
@@ -6,3 +6,4 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
indoc = "2.0.4"
|
||||
|
||||
10
build.sh
Executable file
10
build.sh
Executable file
@@ -0,0 +1,10 @@
|
||||
cargo build --target x86_64-pc-windows-gnu
|
||||
cargo build --target x86_64-pc-windows-gnu -r
|
||||
cargo build --target x86_64-unknown-linux-gnu
|
||||
cargo build --target x86_64-unknown-linux-gnu -r
|
||||
#cargo build --target aarch64-unknown-linux-gnu
|
||||
#cargo build --target aarch64-unknown-linux-gnu -r
|
||||
cargo build --target x86_64-unknown-linux-musl
|
||||
cargo build --target x86_64-unknown-linux-musl -r
|
||||
#cargo build --target x86_64-unknown-freebsd
|
||||
#cargo build --target x86_64-unknown-freebsd -r
|
||||
561
src/lib.rs
Normal file
561
src/lib.rs
Normal file
@@ -0,0 +1,561 @@
|
||||
use indoc::printdoc;
|
||||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
pub type Value = f64;
|
||||
pub type Result = std::result::Result<(), Error>;
|
||||
|
||||
pub struct RPN {
|
||||
custom: HashMap<String, Definition>,
|
||||
stack: Vec<Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
DivisionByZero,
|
||||
StackUnderflow,
|
||||
UnknownWord,
|
||||
InvalidWord,
|
||||
StackPushFailed,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Definition {
|
||||
words: Arc<Vec<Words>>,
|
||||
}
|
||||
|
||||
// https://www.forth.com/starting-forth/2-stack-manipulation-operators-arithmetic/
|
||||
// for definition of swap, dup, over, drop operators
|
||||
#[derive(Debug)]
|
||||
enum Words {
|
||||
Add,
|
||||
Subtract,
|
||||
Multiply,
|
||||
Divide,
|
||||
Modulo,
|
||||
Floor,
|
||||
Ceil,
|
||||
Round,
|
||||
Abs,
|
||||
Pow,
|
||||
Sqrt,
|
||||
Cbrt,
|
||||
LogN,
|
||||
Log,
|
||||
Sin,
|
||||
Asin,
|
||||
Sinh,
|
||||
Acos,
|
||||
Cos,
|
||||
Cosh,
|
||||
Tan,
|
||||
Atan,
|
||||
Tanh,
|
||||
Epsilon,
|
||||
Euler,
|
||||
Pi,
|
||||
Tau,
|
||||
Push(Value),
|
||||
Drop,
|
||||
Duplicate,
|
||||
Swap,
|
||||
Over,
|
||||
ClearStack,
|
||||
Emit,
|
||||
DefineStart,
|
||||
DefineEnd,
|
||||
Definition(Definition),
|
||||
EndOfLine,
|
||||
Help,
|
||||
Quit,
|
||||
}
|
||||
|
||||
impl Default for RPN {
|
||||
fn default() -> Self {
|
||||
RPN::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl RPN {
|
||||
pub fn new() -> RPN {
|
||||
RPN {
|
||||
custom: HashMap::new(),
|
||||
stack: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stack(&self) -> &[Value] {
|
||||
&self.stack
|
||||
}
|
||||
|
||||
fn add(&mut self) -> Result {
|
||||
let x = self.pop()?;
|
||||
let y = self.pop()?;
|
||||
|
||||
self.push(y + x)
|
||||
}
|
||||
|
||||
fn subtract(&mut self) -> Result {
|
||||
let x = self.pop()?;
|
||||
let y = self.pop()?;
|
||||
|
||||
self.push(y - x)
|
||||
}
|
||||
|
||||
fn multiply(&mut self) -> Result {
|
||||
let x = self.pop()?;
|
||||
let y = self.pop()?;
|
||||
|
||||
self.push(y * x)
|
||||
}
|
||||
|
||||
fn divide(&mut self) -> Result {
|
||||
let x = self.pop()?;
|
||||
let y = self.pop()?;
|
||||
|
||||
if x == 0f64 {
|
||||
Err(Error::DivisionByZero)
|
||||
} else {
|
||||
self.push(y / x)
|
||||
}
|
||||
}
|
||||
|
||||
fn push(&mut self, num: Value) -> Result {
|
||||
self.stack.push(num);
|
||||
//push can fail, but I have no idea how to actually catch that.
|
||||
//This should only happen on an out of memory error anyway
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pop(&mut self) -> std::result::Result<Value, Error> {
|
||||
self.stack.pop().ok_or(Error::StackUnderflow)
|
||||
}
|
||||
|
||||
fn duplicate(&mut self) -> Result {
|
||||
let x = self.pop()?;
|
||||
|
||||
self.push(x)?;
|
||||
self.push(x)
|
||||
}
|
||||
|
||||
fn drop(&mut self) -> Result {
|
||||
let _ = self.pop()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn swap(&mut self) -> Result {
|
||||
let x = self.pop()?;
|
||||
let y = self.pop()?;
|
||||
|
||||
self.push(x)?;
|
||||
self.push(y)
|
||||
}
|
||||
|
||||
fn over(&mut self) -> Result {
|
||||
let x = self.pop()?;
|
||||
let y = self.pop()?;
|
||||
|
||||
self.push(y)?;
|
||||
self.push(x)?;
|
||||
self.push(y)
|
||||
}
|
||||
|
||||
fn modulo(&mut self) -> Result {
|
||||
let x = self.pop()?;
|
||||
let y = self.pop()?;
|
||||
|
||||
self.push(y % x)
|
||||
}
|
||||
|
||||
fn abs(&mut self) -> Result {
|
||||
let x = self.pop()?;
|
||||
|
||||
self.push(x.abs())
|
||||
}
|
||||
|
||||
fn floor(&mut self) -> Result {
|
||||
let x = self.pop()?;
|
||||
let y = self.pop()?;
|
||||
|
||||
self.push(RPN::floor_cmp(x, y))
|
||||
}
|
||||
|
||||
fn floor_cmp(a: Value, b: Value) -> Value {
|
||||
if a > b {
|
||||
a
|
||||
} else {
|
||||
b
|
||||
}
|
||||
}
|
||||
|
||||
fn ceil(&mut self) -> Result {
|
||||
let x = self.pop()?;
|
||||
let y = self.pop()?;
|
||||
|
||||
self.push(RPN::ceil_cmp(x, y))
|
||||
}
|
||||
|
||||
fn ceil_cmp(a: Value, b: Value) -> Value {
|
||||
if a < b {
|
||||
a
|
||||
} else {
|
||||
b
|
||||
}
|
||||
}
|
||||
fn round(&mut self) -> Result {
|
||||
let x = self.pop()?;
|
||||
|
||||
self.push(x.round())
|
||||
}
|
||||
fn pow(&mut self) -> Result {
|
||||
let x = self.pop()?;
|
||||
let y = self.pop()?;
|
||||
|
||||
self.push(y.powf(x))
|
||||
}
|
||||
|
||||
fn sqrt(&mut self) -> Result {
|
||||
let x = self.pop()?;
|
||||
|
||||
self.push(x.sqrt())
|
||||
}
|
||||
|
||||
fn cbrt(&mut self) -> Result {
|
||||
let x = self.pop()?;
|
||||
|
||||
self.push(x.cbrt())
|
||||
}
|
||||
|
||||
fn log_natural(&mut self) -> Result {
|
||||
let x = self.pop()?;
|
||||
|
||||
self.push(x.ln())
|
||||
}
|
||||
|
||||
fn log_base10(&mut self) -> Result {
|
||||
let x = self.pop()?;
|
||||
|
||||
self.push(x.log10())
|
||||
}
|
||||
|
||||
fn sin(&mut self) -> Result {
|
||||
let x = self.pop()?;
|
||||
|
||||
self.push(x.sin())
|
||||
}
|
||||
|
||||
fn asin(&mut self) -> Result {
|
||||
let x = self.pop()?;
|
||||
|
||||
self.push(x.asin())
|
||||
}
|
||||
|
||||
fn sinh(&mut self) -> Result {
|
||||
let x = self.pop()?;
|
||||
|
||||
self.push(x.sinh())
|
||||
}
|
||||
|
||||
fn cos(&mut self) -> Result {
|
||||
let x = self.pop()?;
|
||||
|
||||
self.push(x.cos())
|
||||
}
|
||||
|
||||
fn acos(&mut self) -> Result {
|
||||
let x = self.pop()?;
|
||||
|
||||
self.push(x.acos())
|
||||
}
|
||||
|
||||
fn cosh(&mut self) -> Result {
|
||||
let x = self.pop()?;
|
||||
|
||||
self.push(x.cosh())
|
||||
}
|
||||
|
||||
fn tan(&mut self) -> Result {
|
||||
let x = self.pop()?;
|
||||
|
||||
self.push(x.tan())
|
||||
}
|
||||
|
||||
fn atan(&mut self) -> Result {
|
||||
let x = self.pop()?;
|
||||
|
||||
self.push(x.atan())
|
||||
}
|
||||
|
||||
fn tanh(&mut self) -> Result {
|
||||
let x = self.pop()?;
|
||||
|
||||
self.push(x.tanh())
|
||||
}
|
||||
|
||||
fn emit(&mut self) -> Result {
|
||||
let x = self.pop()?;
|
||||
|
||||
println!("{x}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn define(&mut self, name: &str, definition: Definition) {
|
||||
let name = name.to_lowercase();
|
||||
self.custom.insert(name, definition);
|
||||
}
|
||||
|
||||
fn do_definition(&mut self, definition: &Definition) -> Result {
|
||||
let words_iter = Arc::clone(&definition.words);
|
||||
for w in &*words_iter {
|
||||
match self.do_word(w) {
|
||||
Err(e) => return Err(e),
|
||||
Ok(_) => continue,
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn do_word(&mut self, word: &Words) -> Result {
|
||||
match word {
|
||||
Words::Add => self.add(),
|
||||
Words::Subtract => self.subtract(),
|
||||
Words::Multiply => self.multiply(),
|
||||
Words::Divide => self.divide(),
|
||||
Words::Duplicate => self.duplicate(),
|
||||
Words::Drop => self.drop(),
|
||||
Words::Swap => self.swap(),
|
||||
Words::Over => self.over(),
|
||||
Words::Push(x) => self.push(*x),
|
||||
Words::Modulo => self.modulo(),
|
||||
Words::Abs => self.abs(),
|
||||
Words::Floor => self.floor(),
|
||||
Words::Ceil => self.ceil(),
|
||||
Words::Round => self.round(),
|
||||
Words::Pow => self.pow(),
|
||||
Words::Sqrt => self.sqrt(),
|
||||
Words::Cbrt => self.cbrt(),
|
||||
Words::LogN => self.log_natural(),
|
||||
Words::Log => self.log_base10(),
|
||||
Words::Sin => self.sin(),
|
||||
Words::Sinh => self.sinh(),
|
||||
Words::Asin => self.asin(),
|
||||
Words::Cos => self.cos(),
|
||||
Words::Cosh => self.cosh(),
|
||||
Words::Acos => self.acos(),
|
||||
Words::Tan => self.tan(),
|
||||
Words::Atan => self.atan(),
|
||||
Words::Tanh => self.tanh(),
|
||||
Words::Emit => self.emit(),
|
||||
Words::Epsilon => self.push(f64::EPSILON),
|
||||
Words::Euler => self.push(std::f64::consts::E),
|
||||
Words::Pi => self.push(std::f64::consts::PI),
|
||||
Words::Tau => self.push(std::f64::consts::TAU),
|
||||
Words::ClearStack => {
|
||||
self.stack.clear();
|
||||
Ok(())
|
||||
}
|
||||
Words::Definition(x) => self.do_definition(x),
|
||||
Words::Help => {
|
||||
RPN::print_help();
|
||||
Ok(())
|
||||
}
|
||||
Words::Quit => std::process::exit(0),
|
||||
//definitions and line endings are special, and must be handled by eval()
|
||||
Words::DefineStart => Err(Error::InvalidWord),
|
||||
Words::DefineEnd => Err(Error::InvalidWord),
|
||||
Words::EndOfLine => Err(Error::InvalidWord),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse(&self, cmd: &str) -> std::result::Result<Words, Error> {
|
||||
let q = cmd.to_ascii_lowercase();
|
||||
let def = self.custom.get(&q);
|
||||
if let Some(x) = def {
|
||||
return Ok(Words::Definition(x.clone()));
|
||||
}
|
||||
match q.as_str() {
|
||||
"+" => Ok(Words::Add),
|
||||
"-" => Ok(Words::Subtract),
|
||||
"*" => Ok(Words::Multiply),
|
||||
"/" => Ok(Words::Divide),
|
||||
"dup" => Ok(Words::Duplicate),
|
||||
"drop" => Ok(Words::Drop),
|
||||
"swap" => Ok(Words::Swap),
|
||||
"over" => Ok(Words::Over),
|
||||
":" => Ok(Words::DefineStart),
|
||||
";" => Ok(Words::DefineEnd),
|
||||
"%" => Ok(Words::Modulo),
|
||||
"abs" | "||" => Ok(Words::Abs),
|
||||
"floor" | "fl" => Ok(Words::Floor),
|
||||
"ceil" | "cl" => Ok(Words::Ceil),
|
||||
"round" | "r" => Ok(Words::Round),
|
||||
"pow" | "^" => Ok(Words::Pow),
|
||||
"sqrt" | "v" => Ok(Words::Sqrt),
|
||||
"cbrt" | "v3" => Ok(Words::Cbrt),
|
||||
"ln" => Ok(Words::LogN),
|
||||
"log" => Ok(Words::Log),
|
||||
"sin" => Ok(Words::Sin),
|
||||
"asin" => Ok(Words::Asin),
|
||||
"sinh" => Ok(Words::Sinh),
|
||||
"cos" => Ok(Words::Cos),
|
||||
"acos" => Ok(Words::Acos),
|
||||
"cosh" => Ok(Words::Cosh),
|
||||
"tan" => Ok(Words::Tan),
|
||||
"atan" => Ok(Words::Atan),
|
||||
"tanh" => Ok(Words::Tanh),
|
||||
"me" => Ok(Words::Epsilon),
|
||||
"euler" | "e" => Ok(Words::Euler),
|
||||
"pi" => Ok(Words::Pi),
|
||||
"tau" => Ok(Words::Tau),
|
||||
"help" | "?" => Ok(Words::Help),
|
||||
"emit" | "." => Ok(Words::Emit),
|
||||
"clear" | "c" => Ok(Words::ClearStack),
|
||||
"quit" | "q" => Ok(Words::Quit),
|
||||
unk => {
|
||||
//determine if input is a number, if it is, push it to stack
|
||||
//otherwise, return UnknownWord error
|
||||
let num = Value::from_str(unk);
|
||||
|
||||
if let Ok(x) = num {
|
||||
return Ok(Words::Push(x));
|
||||
}
|
||||
Err(Error::UnknownWord)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_next(&self, iter: &mut core::str::Split<'_, char>) -> std::result::Result<Words, Error> {
|
||||
let next = iter.next();
|
||||
match next {
|
||||
None => Ok(Words::EndOfLine),
|
||||
Some(cmd) => self.parse(cmd),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn do_define(&mut self, iter: &mut core::str::Split<'_, char>) -> Result {
|
||||
let name: &str = match iter.next() {
|
||||
None => return Err(Error::InvalidWord),
|
||||
Some(x) => x,
|
||||
};
|
||||
|
||||
//numbers can not be names of definitions
|
||||
//if the parse succeeds (indicating that the input name is a number), error out
|
||||
if Value::from_str(name).is_ok() {
|
||||
return Err(Error::InvalidWord);
|
||||
}
|
||||
|
||||
let mut def_working: Vec<Words> = Vec::new();
|
||||
loop {
|
||||
match self.get_next(iter) {
|
||||
Err(e) => return Err(e),
|
||||
Ok(w) => match w {
|
||||
Words::DefineStart => {
|
||||
//can not begin a new definition before this definition has completed
|
||||
return Err(Error::InvalidWord);
|
||||
}
|
||||
Words::DefineEnd => {
|
||||
let def: Definition = Definition {
|
||||
words: Arc::new(def_working),
|
||||
};
|
||||
self.define(name, def);
|
||||
return Ok(());
|
||||
}
|
||||
Words::EndOfLine => {
|
||||
//line can not end before definition is completed
|
||||
return Err(Error::InvalidWord);
|
||||
}
|
||||
word => {
|
||||
def_working.push(word);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eval(&mut self, input: &str) -> Result {
|
||||
let mut input_vec = input.trim_end().split(' ');
|
||||
|
||||
loop {
|
||||
let cur = self.get_next(&mut input_vec);
|
||||
|
||||
match cur {
|
||||
Ok(Words::EndOfLine) => break Ok(()),
|
||||
Ok(Words::DefineStart) => self.do_define(&mut input_vec)?,
|
||||
Ok(word) => self.do_word(&word)?,
|
||||
Err(e) => break Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_help() {
|
||||
printdoc! {"
|
||||
RPN Calculator Help
|
||||
|
||||
This is an RPN (Reverse Polish Notation) calculator program. This works by pushing numbers to a stack, and then completing operations on the pushed numbers in the order that they were added to the stack.
|
||||
|
||||
Inputs can be chained together by inputting commands seperated by a space.
|
||||
|
||||
Example: 4 [enter] 3 [enter] * [enter]
|
||||
Will display 12 on the Current Stack (i.e. 4 * 3 = 12)
|
||||
|
||||
Example: 4 [space] 3 [space] * [enter]
|
||||
Will also display 12 on the Current Stack
|
||||
|
||||
Many more math functions are available:
|
||||
|
||||
[Command] [Function]
|
||||
quit or q quit calculator (ctrl-c also works)
|
||||
+ add the last two numbers on the stack
|
||||
- subtract the last two numbers on the stack
|
||||
* or x multiply the last two numbers on the stack
|
||||
/ divide the last two numbers on the stack
|
||||
% output the remainder of the division of the last two numbers on the stack (modulus)
|
||||
clear or c clear the stack of all values
|
||||
drop or d remove the last number pushed to the stack (this can be repeated multiple times)
|
||||
floor take the last two numbers from the stack, and push the lower number back onto the stack
|
||||
ceil take the last two numbers from the stack, and push the higher number back onto the stack
|
||||
round round the last number on the stack (follows conventional rules, 0.5 rounds up)
|
||||
abs change the last number on the stack to it's absolute value
|
||||
pow raise the number on the stack prior to the last input to the power of the last number input
|
||||
Example: 3 [enter] 2 [enter] pow [enter] Output: 9
|
||||
sqrt or v change the last number on the stack to it's square root
|
||||
cbrt or v3 change the last number on the stack to it's cube root
|
||||
ln change the last number on the stack to it's natural log
|
||||
log change the last number on the stack to it's base10 log
|
||||
sin change the last number on the stack to it's sine value (radians)
|
||||
asin, sinh the same thing, but arc and hyperbolic instead
|
||||
cos change the last number on the stack to it's cosine value (radians)
|
||||
acos, cosh the same thing, but arc and hyperbolic instead
|
||||
tan change the last number on the stack to it's tangent value (radians)
|
||||
atan, tanh the same thing, but arc and hyperbolic instead
|
||||
me pushes the machine epsilon value onto the stack
|
||||
pi pushes pi onto the stack
|
||||
tau pushes tau on to the stack
|
||||
help or ? print this help text
|
||||
|
||||
emit or . print the last number pushed to the stack, removing it from the stack
|
||||
|
||||
|
||||
Additionally, this implements a subset of FORTH for a basic level of programability.
|
||||
|
||||
The ':' character is used to begin a definition, and a definition is ended with ';'
|
||||
|
||||
Example: : name 1 2 3 + + ;
|
||||
|
||||
This creates a definition of 'name' that can then be called after it's definition.
|
||||
|
||||
The definition pushes 1, 2, 3, onto the stack, and then adds 3 + 2, and then 5 + 1.
|
||||
|
||||
Definitions can be used inside other definitions:
|
||||
|
||||
Example: : name2 name name name ;
|
||||
|
||||
Creates a definition 'name2' that executes the 'name' definition 3 times.
|
||||
|
||||
|
||||
|
||||
"}
|
||||
}
|
||||
}
|
||||
289
src/main.rs
289
src/main.rs
@@ -1,13 +1,11 @@
|
||||
use rpn::RPN;
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
use std::num::ParseFloatError;
|
||||
use std::str::FromStr;
|
||||
|
||||
fn main() {
|
||||
println!("RPN Calculator");
|
||||
|
||||
let mut stack: Vec<f64> = Vec::new();
|
||||
println!("RPN Calculator (with FORTH)");
|
||||
|
||||
let mut rpn = RPN::new();
|
||||
loop {
|
||||
let mut input = String::new();
|
||||
|
||||
@@ -21,281 +19,14 @@ fn main() {
|
||||
.read_line(&mut input)
|
||||
.expect("failed to read line");
|
||||
|
||||
let func = match input.trim() {
|
||||
"q" => Function::Quit,
|
||||
"+" => Function::Add,
|
||||
"-" => Function::Subtract,
|
||||
"*" | "x" => Function::Multiply,
|
||||
"/" => Function::Divide,
|
||||
"%" => Function::Modulo,
|
||||
"clear" | "c" => Function::ClearStack,
|
||||
"floor" | "fl" => Function::Floor,
|
||||
"ceil" | "cl" => Function::Ceil,
|
||||
"round" | "r" => Function::Round,
|
||||
"abs" => Function::Abs,
|
||||
"pow" | "^" => Function::Pow,
|
||||
"sqrt" | "v" => Function::Sqrt,
|
||||
"cbrt" | "v3" => Function::Cbrt,
|
||||
"ln" => Function::LogN,
|
||||
"log" => Function::Log,
|
||||
"sin" => Function::Sin,
|
||||
"asin" => Function::Asin,
|
||||
"sinh" => Function::Sinh,
|
||||
"acos" => Function::Acos,
|
||||
"cos" => Function::Cos,
|
||||
"cosh" => Function::Cosh,
|
||||
"tan" => Function::Tan,
|
||||
"atan" => Function::Atan,
|
||||
"tanh" => Function::Tanh,
|
||||
"me" => Function::Epsilon,
|
||||
"euler" | "e" => Function::Euler,
|
||||
"pi" => Function::Pi,
|
||||
"tau" => Function::Tau,
|
||||
"del" | "d" => Function::DeleteLast,
|
||||
"help" | "?" => Function::Help,
|
||||
_ => Function::Push(f64::from_str(input.trim())),
|
||||
};
|
||||
|
||||
match func {
|
||||
Function::Push(x) => stack_push(&mut stack, x),
|
||||
Function::Quit => break,
|
||||
Function::ClearStack => stack.clear(),
|
||||
others => operate(&mut stack, others),
|
||||
}
|
||||
|
||||
match rpn.eval(&input) {
|
||||
Err(e) => println!("Failed: {e:?}"),
|
||||
Ok(_) => {
|
||||
println!("Current Stack: ");
|
||||
|
||||
for i in &stack {
|
||||
println!("{i}");
|
||||
for n in rpn.stack() {
|
||||
println!("{n}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Function {
|
||||
Add,
|
||||
Subtract,
|
||||
Multiply,
|
||||
Divide,
|
||||
Modulo,
|
||||
Floor,
|
||||
Ceil,
|
||||
Round,
|
||||
Abs,
|
||||
Pow,
|
||||
Sqrt,
|
||||
Cbrt,
|
||||
LogN,
|
||||
Log,
|
||||
Sin,
|
||||
Asin,
|
||||
Sinh,
|
||||
Acos,
|
||||
Cos,
|
||||
Cosh,
|
||||
Tan,
|
||||
Atan,
|
||||
Tanh,
|
||||
Epsilon,
|
||||
Euler,
|
||||
Pi,
|
||||
Tau,
|
||||
Push(Result<f64, ParseFloatError>),
|
||||
DeleteLast,
|
||||
ClearStack,
|
||||
Help,
|
||||
Quit,
|
||||
}
|
||||
|
||||
fn stack_push(stack: &mut Vec<f64>, num: Result<f64, ParseFloatError>) {
|
||||
match num {
|
||||
Ok(num) => stack.push(num),
|
||||
Err(e) => println!("Cannot push: {e}."),
|
||||
}
|
||||
}
|
||||
|
||||
fn small_stack_err(count_expected: usize, func: &str) {
|
||||
println!("Stack must contain at least {count_expected} numbers to {func}!");
|
||||
}
|
||||
|
||||
fn operands_needed(func: &Function) -> usize {
|
||||
match func {
|
||||
Function::Add => 2,
|
||||
Function::Subtract => 2,
|
||||
Function::Multiply => 2,
|
||||
Function::Divide => 2,
|
||||
Function::Modulo => 2,
|
||||
Function::Floor => 2,
|
||||
Function::Ceil => 2,
|
||||
Function::Round => 1,
|
||||
Function::Abs => 1,
|
||||
Function::Pow => 2,
|
||||
Function::Sqrt => 1,
|
||||
Function::Cbrt => 1,
|
||||
Function::LogN => 1,
|
||||
Function::Log => 1,
|
||||
Function::Sin => 1,
|
||||
Function::Asin => 1,
|
||||
Function::Sinh => 1,
|
||||
Function::Acos => 1,
|
||||
Function::Cos => 1,
|
||||
Function::Cosh => 1,
|
||||
Function::Tan => 1,
|
||||
Function::Atan => 1,
|
||||
Function::Tanh => 1,
|
||||
Function::Epsilon => 0,
|
||||
Function::Euler => 0,
|
||||
Function::Pi => 0,
|
||||
Function::Tau => 0,
|
||||
Function::Push(_) => 0,
|
||||
Function::ClearStack => 0,
|
||||
Function::DeleteLast => 1,
|
||||
Function::Help => 0,
|
||||
Function::Quit => 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn operate(stack: &mut Vec<f64>, func: Function) {
|
||||
if stack.len() < operands_needed(&func) {
|
||||
small_stack_err(operands_needed(&func), format!("{func:?}").as_str());
|
||||
}
|
||||
let x;
|
||||
let y;
|
||||
//let z;
|
||||
|
||||
match operands_needed(&func) {
|
||||
0 => {
|
||||
x = 0f64;
|
||||
y = 0f64;
|
||||
//z = 0f64;
|
||||
}
|
||||
1 => {
|
||||
x = stack.pop().unwrap_or(0f64);
|
||||
y = 0f64;
|
||||
//z = 0f64;
|
||||
}
|
||||
2 => {
|
||||
x = stack.pop().unwrap_or(0f64);
|
||||
y = stack.pop().unwrap_or(0f64);
|
||||
//z = 0f64;
|
||||
}
|
||||
3 => {
|
||||
x = stack.pop().unwrap_or(0f64);
|
||||
y = stack.pop().unwrap_or(0f64);
|
||||
//z = stack.pop().unwrap_or(0f64);
|
||||
}
|
||||
_ => {
|
||||
x = 0f64;
|
||||
y = 0f64;
|
||||
//z = 0f64;
|
||||
println!("Function requires more operands than I know how to pull off of the stack :(");
|
||||
}
|
||||
}
|
||||
|
||||
match func {
|
||||
Function::Add => stack.push(x + y),
|
||||
Function::Subtract => stack.push(x - y),
|
||||
Function::Multiply => stack.push(x * y),
|
||||
Function::Divide => stack.push(y / x),
|
||||
//x is at top of stack, but divide is order
|
||||
//dependent. We're dividing the number input prior
|
||||
//to x (y), by x. i.e. x: 3, y: 6, push = 0.5
|
||||
Function::Modulo => stack.push(y % x),
|
||||
//same reason as division above
|
||||
Function::Floor => stack.push(floor(x, y)),
|
||||
Function::Ceil => stack.push(ceil(x, y)),
|
||||
Function::Round => stack.push(x.round()),
|
||||
Function::Abs => stack.push(x.abs()),
|
||||
Function::Pow => stack.push(y.powf(x)),
|
||||
Function::Sqrt => stack.push(x.sqrt()),
|
||||
Function::Cbrt => stack.push(x.cbrt()),
|
||||
Function::LogN => stack.push(x.ln()),
|
||||
Function::Log => stack.push(x.log10()),
|
||||
Function::Sin => stack.push(x.sin()),
|
||||
Function::Asin => stack.push(x.asin()),
|
||||
Function::Sinh => stack.push(x.sinh()),
|
||||
Function::Acos => stack.push(x.acos()),
|
||||
Function::Cos => stack.push(x.cos()),
|
||||
Function::Cosh => stack.push(x.cosh()),
|
||||
Function::Tan => stack.push(x.tan()),
|
||||
Function::Atan => stack.push(x.atan()),
|
||||
Function::Tanh => stack.push(x.tanh()),
|
||||
Function::Epsilon => stack.push(f64::EPSILON),
|
||||
Function::Euler => stack.push(std::f64::consts::E),
|
||||
Function::Pi => stack.push(std::f64::consts::PI),
|
||||
Function::Tau => stack.push(std::f64::consts::TAU),
|
||||
Function::Push(_) => println!("Push failed"),
|
||||
//this shouldn't happen, as this should be pushed from main()
|
||||
Function::Quit => println!("Quit failed"),
|
||||
//this shouldn't happen, as this should be breaking in main()
|
||||
Function::DeleteLast => {}
|
||||
//the last value was popped, but we aren't doing anything with it
|
||||
Function::ClearStack => stack.clear(),
|
||||
Function::Help => print_help(),
|
||||
}
|
||||
}
|
||||
|
||||
fn floor(a: f64, b: f64) -> f64 {
|
||||
if a > b {
|
||||
a
|
||||
} else {
|
||||
b
|
||||
}
|
||||
}
|
||||
|
||||
fn ceil(a: f64, b: f64) -> f64 {
|
||||
if a < b {
|
||||
a
|
||||
} else {
|
||||
b
|
||||
}
|
||||
}
|
||||
|
||||
fn print_help() {
|
||||
println!("RPN Calculator Help");
|
||||
println!();
|
||||
println!("This is an RPN (Reverse Polish Notation) calculator program. This works by pushing numbers to a stack, and then completing operations on the pushed numbers in the order that they were added to the stack.");
|
||||
println!();
|
||||
println!("Example: 4 [enter] 3 [enter] * [enter]");
|
||||
println!("Will display 12 on the Current Stack (i.e. 4 * 3 = 12)");
|
||||
println!();
|
||||
println!("Many more math functions are available:");
|
||||
println!();
|
||||
println!("[Command] [Function]");
|
||||
println!("q quit calculator (ctrl-c also works)");
|
||||
println!("+ add the last two numbers on the stack");
|
||||
println!("- subtract the last two numbers on the stack");
|
||||
println!("* or x multiply the last two numbers on the stack");
|
||||
println!("/ divide the last two numbers on the stack");
|
||||
println!("% output the remainder of the division of the last two numbers on the stack (modulus)");
|
||||
println!("clear or c clear the stack of all values");
|
||||
println!("del or d remove the last number pushed to the stack (this can be repeated multiple times)");
|
||||
println!("floor take the last two numbers from the stack, and push the lower number back onto the stack");
|
||||
println!("ceil take the last two numbers from the stack, and push the higher number back onto the stack");
|
||||
println!("round round the last number on the stack (follows conventional rules, 0.5 rounds up)");
|
||||
println!("abs change the last number on the stack to it's absolute value");
|
||||
println!("pow raise the number on the stack prior to the last input to the power of the last number input");
|
||||
println!(" Example: 3 [enter] 2 [enter] pow [enter] Output: 9");
|
||||
println!("sqrt or v change the last number on the stack to it's square root");
|
||||
println!("cbrt or v3 change the last number on the stack to it's cube root");
|
||||
println!("ln change the last number on the stack to it's natural log");
|
||||
println!("log change the last number on the stack to it's base10 log");
|
||||
println!(
|
||||
"sin change the last number on the stack to it's sine value (radians)"
|
||||
);
|
||||
println!("asin, sinh the same thing, but arc and hyperbolic instead");
|
||||
println!(
|
||||
"cos change the last number on the stack to it's cosine value (radians)"
|
||||
);
|
||||
println!("acos, cosh the same thing, but arc and hyperbolic instead");
|
||||
println!(
|
||||
"tan change the last number on the stack to it's tangent value (radians)"
|
||||
);
|
||||
println!("atan, tanh the same thing, but arc and hyperbolic instead");
|
||||
println!("me pushes the machine epsilon value onto the stack");
|
||||
println!("pi pushes pi onto the stack");
|
||||
println!("tau pushes tau on to the stack");
|
||||
println!("help or ? print this help text");
|
||||
println!();
|
||||
println!();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user