1
0
forked from bonsai/harakit

rpn(1): refactor to make code more efficient, readable, and maintainable

This commit is contained in:
Emma Tebibyte 2024-08-24 17:22:02 -06:00
parent 150fa22f35
commit a0138be79e
Signed by untrusted user: emma
GPG Key ID: 06FA419A1698C270

View File

@ -54,7 +54,7 @@ use CalcType::*;
extern crate sysexits; extern crate sysexits;
use sysexits::EX_DATAERR; use sysexits::{ EX_DATAERR, EX_IOERR };
#[cfg(target_os="openbsd")] use sysexits::EX_OSERR; #[cfg(target_os="openbsd")] use sysexits::EX_OSERR;
#[cfg(target_os="openbsd")] extern crate strerror; #[cfg(target_os="openbsd")] extern crate strerror;
@ -120,13 +120,18 @@ impl Display for CalcType {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct EvaluationError { struct EvaluationError {
message: String, message: String,
code: i32, code: ExitCode,
} }
/* Im no math nerd but I want the highest possible approximation of 0.9 /* Im no math nerd but I want the highest possible approximation of 0.9
* repeating and it seems this can give it to me */ * repeating and it seems this can give it to me */
const PRECISION_MOD: f64 = 0.9 + f64::EPSILON * 100.0; const PRECISION_MOD: f64 = 0.9 + f64::EPSILON * 100.0;
fn err(argv0: &String, e: std::io::Error, code: Option<u8>) -> ExitCode {
eprintln!("{}: {}", argv0, e.strerror());
ExitCode::from(code.unwrap_or(1 /* unknown error */))
}
fn eval( fn eval(
input: &str, input: &str,
initial_stack: VecDeque<f64>, initial_stack: VecDeque<f64>,
@ -153,12 +158,12 @@ fn eval(
Invalid(i) => { Invalid(i) => {
return Err(EvaluationError { return Err(EvaluationError {
message: format!("{}: Invalid token", i), message: format!("{}: Invalid token", i),
code: EX_DATAERR, code: ExitCode::from(EX_DATAERR),
}) })
}, },
op => { op => {
ops.push_back(op.clone()); ops.push_back(op.clone());
oper = true; oper = true; /* this is an operation */
let vals = ( let vals = (
stack.pop_back(), stack.pop_back(),
@ -179,7 +184,7 @@ fn eval(
} else { } else {
return Err(EvaluationError { return Err(EvaluationError {
message: format!("{}: Unexpected operation", op), message: format!("{}: Unexpected operation", op),
code: EX_DATAERR, code: ExitCode::from(EX_DATAERR),
}) })
} }
}, },
@ -190,51 +195,41 @@ fn eval(
} }
/* Round a float to the given precision level */ /* Round a float to the given precision level */
fn round_precise(value: &f64, precision: usize) -> f64 { fn round_precise(value: &f64) -> f64 {
/* Set floating-point precision for correcting rounding errors based on
* machine epsilon */
let precision = (-f64::EPSILON.log10() * PRECISION_MOD).ceil() as i32;
let multiplier = 10_f64.powi(precision as i32); let multiplier = 10_f64.powi(precision as i32);
(value * multiplier).round() / multiplier (value * multiplier).round() / multiplier
} }
fn unstack(stack: VecDeque<f64>, op: bool) -> bool {
if let Some(val) = stack.iter().last() {
if !op { return true; }
println!("{}", round_precise(val).to_string());
} else {
return false;
}
return true;
}
fn main() -> ExitCode { fn main() -> ExitCode {
let argv = args().collect::<Vec<String>>(); let argv = args().collect::<Vec<String>>();
#[cfg(target_os="openbsd")] { #[cfg(target_os="openbsd")] {
let promises = Promises::new("stdio"); let promises = Promises::new("stdio");
if let Err(e) = pledge(Some(promises), None) { if let Err(e) = pledge(Some(promises), None) {
eprintln!("{}: {}", argv[0], e.strerror()); return err(&argv[0], e, Some(EX_OSERR));
return ExitCode::from(EX_OSERR as u8);
} }
} }
let mut stack = VecDeque::new(); let mut stack = VecDeque::new();
let mut buf = String::new(); let mut buf = String::new();
/* Set floating-point precision for correcting rounding errors based on
* machine epsilon */
let precision = (-f64::EPSILON.log10() * PRECISION_MOD).ceil() as usize;
if argv.get(1).is_none() { /* read from stdin */ if argv.get(1).is_some() { /* read expressions from argv */
while let Ok(_) = stdin().read_line(&mut buf) {
match eval(&buf.trim(), stack) {
Ok(s) => {
buf.clear();
stack = s.0.clone();
let val = match stack.iter().last() {
Some(v) => v,
None => break,
};
if s.1 == false { continue; }
println!("{}", round_precise(val, precision).to_string());
},
Err(err) => {
eprintln!("{}: {}", argv[0], err.message);
return ExitCode::from(err.code as u8);
},
};
}
} else { /* read from argv */
/* join argv into an owned String joined by spaces minus argv[0] */ /* join argv into an owned String joined by spaces minus argv[0] */
let input = argv let input = argv
.iter() .iter()
@ -245,20 +240,36 @@ fn main() -> ExitCode {
match eval(&input, stack) { match eval(&input, stack) {
Ok(s) => { Ok(s) => {
stack = s.0.clone(); let _ = unstack(s.0.clone(), s.1.clone());
return ExitCode::SUCCESS;
let val = match stack.iter().last() { }
Some(v) => v, Err(e) => {
None => return ExitCode::SUCCESS, eprintln!("{}: {}", argv[0], e.message);
}; return e.code;
println!("{}", round_precise(val, precision).to_string())
},
Err(err) => {
eprintln!("{}: {}", argv[0], err.message);
return ExitCode::from(err.code as u8);
}, },
}; };
} }
/* else, read from stdin */
loop { /* take input until EOF */
if let Err(e) = stdin().read_line(&mut buf) {
return err(&argv[0], e, Some(EX_IOERR));
}
match eval(&buf.trim(), stack) {
Ok(s) => {
buf.clear();
stack = s.0.clone();
if unstack(s.0, s.1) { continue; }
else { break; }
},
Err(e) => {
eprintln!("{}: {}", argv[0], e.message);
return e.code;
},
};
}
ExitCode::SUCCESS ExitCode::SUCCESS
} }