forked from bonsai/harakit
rpn(1): more improvements
This commit is contained in:
parent
cc53dab035
commit
b875aa1058
48
src/rpn.rs
48
src/rpn.rs
@ -120,14 +120,20 @@ impl Display for CalcType {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct EvaluationError {
|
struct EvaluationError {
|
||||||
message: String,
|
message: String,
|
||||||
code: ExitCode,
|
code: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StrError for EvaluationError {
|
||||||
|
fn strerror(&self) -> String {
|
||||||
|
self.message.clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* I’m no math nerd but I want the highest possible approximation of 0.9
|
/* 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 */
|
* repeating and it seems this can give it to me */
|
||||||
const PRECISION_MOD: f64 = 0.9 + f64::EPSILON;
|
const PRECISION_MOD: f64 = 0.9 + f64::EPSILON;
|
||||||
|
|
||||||
fn err(argv0: &String, e: std::io::Error, code: Option<u8>) -> ExitCode {
|
fn err<T: StrError>(argv0: &String, e: &T, code: Option<u8>) -> ExitCode {
|
||||||
eprintln!("{}: {}", argv0, e.strerror());
|
eprintln!("{}: {}", argv0, e.strerror());
|
||||||
ExitCode::from(code.unwrap_or(1 /* unknown error */))
|
ExitCode::from(code.unwrap_or(1 /* unknown error */))
|
||||||
}
|
}
|
||||||
@ -150,6 +156,7 @@ fn eval(
|
|||||||
.rev()
|
.rev()
|
||||||
.map(|t| CalcType::from(t))
|
.map(|t| CalcType::from(t))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut ops: VecDeque<CalcType> = VecDeque::new();
|
let mut ops: VecDeque<CalcType> = VecDeque::new();
|
||||||
|
|
||||||
while let Some(n) = toks.pop_back() {
|
while let Some(n) = toks.pop_back() {
|
||||||
@ -157,18 +164,15 @@ fn eval(
|
|||||||
Val(v) => stack.push_back(v),
|
Val(v) => stack.push_back(v),
|
||||||
Invalid(i) => {
|
Invalid(i) => {
|
||||||
return Err(EvaluationError {
|
return Err(EvaluationError {
|
||||||
message: format!("{}: Invalid token", i),
|
message: format!("{}: invalid token", i),
|
||||||
code: ExitCode::from(EX_DATAERR),
|
code: EX_DATAERR,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
op => {
|
op => {
|
||||||
ops.push_back(op.clone());
|
ops.push_back(op.clone());
|
||||||
oper = true; /* this is an operation */
|
oper = true; /* this is an operation */
|
||||||
|
|
||||||
let vals = (
|
let vals = (stack.pop_back(), stack.pop_back());
|
||||||
stack.pop_back(),
|
|
||||||
stack.pop_back(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if let (Some(x), Some(y)) = vals {
|
if let (Some(x), Some(y)) = vals {
|
||||||
match op {
|
match op {
|
||||||
@ -183,8 +187,8 @@ fn eval(
|
|||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return Err(EvaluationError {
|
return Err(EvaluationError {
|
||||||
message: format!("{}: Unexpected operation", op),
|
message: format!("{}: unexpected operation", op),
|
||||||
code: ExitCode::from(EX_DATAERR),
|
code: EX_DATAERR,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -204,6 +208,7 @@ fn round_precise(value: &f64) -> f64 {
|
|||||||
(value * multiplier).round() / multiplier
|
(value * multiplier).round() / multiplier
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* print the stack and let the caller know if evaluation should continue */
|
||||||
fn unstack(stack: VecDeque<f64>, op: bool) -> Result<bool, Error> {
|
fn unstack(stack: VecDeque<f64>, op: bool) -> Result<bool, Error> {
|
||||||
if let Some(val) = stack.iter().last() {
|
if let Some(val) = stack.iter().last() {
|
||||||
if !op { return Ok(true); }
|
if !op { return Ok(true); }
|
||||||
@ -222,7 +227,7 @@ fn main() -> ExitCode {
|
|||||||
#[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) {
|
||||||
return err(&argv[0], e, Some(EX_OSERR));
|
return err(&argv[0], &e, Some(EX_OSERR));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,14 +245,16 @@ fn main() -> ExitCode {
|
|||||||
|
|
||||||
match eval(&input, stack) {
|
match eval(&input, stack) {
|
||||||
Ok(s) => {
|
Ok(s) => {
|
||||||
|
/* we can ignore the return value of unstack() because we are
|
||||||
|
* not continually evaluating from stdin */
|
||||||
if let Err(e) = unstack(s.0.clone(), s.1.clone()) {
|
if let Err(e) = unstack(s.0.clone(), s.1.clone()) {
|
||||||
return err(&argv[0], e, Some(EX_IOERR));
|
return err(&argv[0], &e, Some(EX_IOERR));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ExitCode::SUCCESS;
|
return ExitCode::SUCCESS;
|
||||||
}
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("{}: {}", argv[0], e.message);
|
return err(&argv[0], &e, Some(e.code));
|
||||||
return e.code;
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -255,23 +262,24 @@ fn main() -> ExitCode {
|
|||||||
/* else, read from stdin */
|
/* else, read from stdin */
|
||||||
loop { /* take input until EOF */
|
loop { /* take input until EOF */
|
||||||
if let Err(e) = stdin().read_line(&mut buf) {
|
if let Err(e) = stdin().read_line(&mut buf) {
|
||||||
return err(&argv[0], e, Some(EX_IOERR));
|
return err(&argv[0], &e, Some(EX_IOERR));
|
||||||
}
|
}
|
||||||
|
|
||||||
match eval(&buf.trim(), stack) {
|
match eval(&buf.trim(), stack) {
|
||||||
Ok(s) => {
|
Ok(s) => {
|
||||||
buf.clear();
|
buf.clear();
|
||||||
stack = s.0.clone();
|
stack = s.0.clone(); /* out with the old, in with the new */
|
||||||
|
|
||||||
match unstack(s.0, s.1) {
|
match unstack(s.0, s.1) {
|
||||||
Ok(b) if b => continue,
|
Ok(b) if b => continue,
|
||||||
Ok(_) => break,
|
Ok(_) => break,
|
||||||
Err(e) => return err(&argv[0], e, Some(EX_IOERR)),
|
Err(e) => {
|
||||||
|
return err(&argv[0], &e, Some(EX_IOERR))
|
||||||
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("{}: {}", argv[0], e.message);
|
return err(&argv[0], &e, Some(e.code));
|
||||||
return e.code;
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user