optimizations #161
103
src/rpn.rs
103
src/rpn.rs
@ -54,7 +54,7 @@ use CalcType::*;
|
||||
|
||||
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")] extern crate strerror;
|
||||
@ -120,13 +120,18 @@ impl Display for CalcType {
|
||||
#[derive(Debug, Clone)]
|
||||
struct EvaluationError {
|
||||
message: String,
|
||||
code: i32,
|
||||
code: ExitCode,
|
||||
}
|
||||
|
||||
/* I’m no math nerd but I want the highest possible approximation of 0.9
|
||||
* repeating and it seems this can give it to me */
|
||||
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(
|
||||
input: &str,
|
||||
initial_stack: VecDeque<f64>,
|
||||
@ -153,12 +158,12 @@ fn eval(
|
||||
Invalid(i) => {
|
||||
return Err(EvaluationError {
|
||||
message: format!("{}: Invalid token", i),
|
||||
code: EX_DATAERR,
|
||||
code: ExitCode::from(EX_DATAERR),
|
||||
})
|
||||
},
|
||||
op => {
|
||||
ops.push_back(op.clone());
|
||||
oper = true;
|
||||
oper = true; /* this is an operation */
|
||||
|
||||
let vals = (
|
||||
stack.pop_back(),
|
||||
@ -179,7 +184,7 @@ fn eval(
|
||||
} else {
|
||||
return Err(EvaluationError {
|
||||
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 */
|
||||
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);
|
||||
(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 {
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
|
||||
#[cfg(target_os="openbsd")] {
|
||||
let promises = Promises::new("stdio");
|
||||
if let Err(e) = pledge(Some(promises), None) {
|
||||
eprintln!("{}: {}", argv[0], e.strerror());
|
||||
return ExitCode::from(EX_OSERR as u8);
|
||||
return err(&argv[0], e, Some(EX_OSERR));
|
||||
}
|
||||
}
|
||||
|
||||
let mut stack = VecDeque::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 */
|
||||
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 */
|
||||
if argv.get(1).is_some() { /* read expressions from argv */
|
||||
/* join argv into an owned String joined by spaces minus argv[0] */
|
||||
let input = argv
|
||||
.iter()
|
||||
@ -245,20 +240,36 @@ fn main() -> ExitCode {
|
||||
|
||||
match eval(&input, stack) {
|
||||
Ok(s) => {
|
||||
stack = s.0.clone();
|
||||
|
||||
let val = match stack.iter().last() {
|
||||
Some(v) => v,
|
||||
None => return ExitCode::SUCCESS,
|
||||
};
|
||||
|
||||
println!("{}", round_precise(val, precision).to_string())
|
||||
},
|
||||
Err(err) => {
|
||||
eprintln!("{}: {}", argv[0], err.message);
|
||||
return ExitCode::from(err.code as u8);
|
||||
let _ = unstack(s.0.clone(), s.1.clone());
|
||||
return ExitCode::SUCCESS;
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("{}: {}", argv[0], e.message);
|
||||
return e.code;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/* 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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user