forked from bonsai/harakit
rpn(1): better error handling
This commit is contained in:
parent
885f167afc
commit
4cb5e8e2b0
83
src/rpn.rs
83
src/rpn.rs
@ -45,7 +45,7 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::VecDeque,
|
collections::VecDeque,
|
||||||
env::args,
|
env::args,
|
||||||
fmt,
|
fmt::{ self, Display, Formatter },
|
||||||
io::stdin,
|
io::stdin,
|
||||||
process::ExitCode,
|
process::ExitCode,
|
||||||
};
|
};
|
||||||
@ -70,19 +70,6 @@ enum CalcType {
|
|||||||
Empty,
|
Empty,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
struct EvaluationError { pub message: String }
|
|
||||||
|
|
||||||
impl fmt::Display for EvaluationError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(f, "{}", self.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
impl From<&str> for CalcType {
|
impl From<&str> for CalcType {
|
||||||
fn from(value: &str) -> Self {
|
fn from(value: &str) -> Self {
|
||||||
match value {
|
match value {
|
||||||
@ -103,15 +90,48 @@ impl From<&str> for CalcType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for CalcType {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
let y: String;
|
||||||
|
write!(f, "{}", match self {
|
||||||
|
Add => "addition",
|
||||||
|
Subtract => "subtraction",
|
||||||
|
Multiply => "multiplication",
|
||||||
|
Divide => "division",
|
||||||
|
Power => "exponentiation",
|
||||||
|
Floor => "floor divion",
|
||||||
|
Modulo => "modulus",
|
||||||
|
Val(x) => {
|
||||||
|
y = x.to_string(); &y
|
||||||
|
},
|
||||||
|
Empty => "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct EvaluationError { pub message: String }
|
||||||
|
|
||||||
|
impl Display for EvaluationError {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 eval(
|
fn eval(
|
||||||
input: &str,
|
input: &str,
|
||||||
initial_stack: VecDeque<f64>,
|
initial_stack: VecDeque<f64>,
|
||||||
) -> Result<VecDeque<f64>, EvaluationError> {
|
) -> Result<(VecDeque<f64>, bool), EvaluationError> {
|
||||||
let mut stack = initial_stack;
|
let mut stack = initial_stack;
|
||||||
|
let mut oper = false;
|
||||||
|
|
||||||
if input.is_empty() {
|
if input.is_empty() {
|
||||||
stack.clear();
|
stack.clear();
|
||||||
return Ok(stack);
|
return Ok((stack, oper));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Split the input into tokens.
|
// Split the input into tokens.
|
||||||
@ -122,7 +142,7 @@ fn eval(
|
|||||||
let x: CalcType = match CalcType::from(tok) {
|
let x: CalcType = match CalcType::from(tok) {
|
||||||
Empty => {
|
Empty => {
|
||||||
return Err(EvaluationError {
|
return Err(EvaluationError {
|
||||||
message: format!("Invalid token: {}", tok),
|
message: format!("{}: Invalid token", tok),
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
x => x,
|
x => x,
|
||||||
@ -136,13 +156,28 @@ fn eval(
|
|||||||
|
|
||||||
for op in &ops {
|
for op in &ops {
|
||||||
match op {
|
match op {
|
||||||
Val(v) => {
|
Val(_) => {
|
||||||
return Err(EvaluationError { message: format!(
|
return Err(EvaluationError { message: format!(
|
||||||
"{}: Unexpected value in the operator stack.",
|
"{}: Unexpected value in the operator stack.",
|
||||||
v
|
op,
|
||||||
)});
|
)});
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
|
|
||||||
|
if stack.len() < 2 {
|
||||||
|
return Err(EvaluationError {
|
||||||
|
message: format!("{}: Unexpected operation.", op)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if stack.len() > ops.len() + 1 {
|
||||||
|
return Err(
|
||||||
|
EvaluationError { message: format!("Syntax error.")}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
oper = true;
|
||||||
|
|
||||||
let (x, y) = (
|
let (x, y) = (
|
||||||
stack.pop_back().unwrap(),
|
stack.pop_back().unwrap(),
|
||||||
stack.pop_back().unwrap(),
|
stack.pop_back().unwrap(),
|
||||||
@ -162,7 +197,7 @@ fn eval(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(stack)
|
Ok((stack, oper))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Round a float to the given precision level
|
// Round a float to the given precision level
|
||||||
@ -183,15 +218,17 @@ fn main() -> ExitCode {
|
|||||||
while let Ok(_) = stdin().read_line(&mut buf) {
|
while let Ok(_) = stdin().read_line(&mut buf) {
|
||||||
match eval(&buf.trim(), stack) {
|
match eval(&buf.trim(), stack) {
|
||||||
Ok(s) => {
|
Ok(s) => {
|
||||||
stack = s.clone();
|
buf.clear();
|
||||||
|
stack = s.0.clone();
|
||||||
|
|
||||||
let val = match stack.iter().last() {
|
let val = match stack.iter().last() {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
None => break,
|
None => break,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if s.1 == false { continue; }
|
||||||
|
|
||||||
println!("{}", round_precise(val, precision).to_string());
|
println!("{}", round_precise(val, precision).to_string());
|
||||||
buf.clear();
|
|
||||||
},
|
},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
eprintln!("{}: {}", argv[0], err.message);
|
eprintln!("{}: {}", argv[0], err.message);
|
||||||
@ -209,7 +246,7 @@ fn main() -> ExitCode {
|
|||||||
|
|
||||||
match eval(&input, stack) {
|
match eval(&input, stack) {
|
||||||
Ok(s) => {
|
Ok(s) => {
|
||||||
stack = s.clone();
|
stack = s.0.clone();
|
||||||
|
|
||||||
let val = match stack.iter().last() {
|
let val = match stack.iter().last() {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
|
Loading…
Reference in New Issue
Block a user