diff --git a/src/rpn.rs b/src/rpn.rs index 7669efb..ff0b1bd 100644 --- a/src/rpn.rs +++ b/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) -> ExitCode { + eprintln!("{}: {}", argv0, e.strerror()); + ExitCode::from(code.unwrap_or(1 /* unknown error */)) +} + fn eval( input: &str, initial_stack: VecDeque, @@ -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, 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::>(); #[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 }