Implement method 2
This commit is contained in:
parent
0e69681ab8
commit
3084f6c745
142
src/calc.rs
142
src/calc.rs
@ -11,6 +11,7 @@ use errors::{CalculatorError, CalculatorResult};
|
||||
use operations::CalculatorOperation;
|
||||
use std::collections::{HashSet, VecDeque};
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum OpArgs {
|
||||
Unary(f64),
|
||||
Binary([f64; 2]),
|
||||
@ -23,6 +24,12 @@ struct CalculatorStateChange {
|
||||
within_macro: bool,
|
||||
}
|
||||
|
||||
impl CalculatorStateChange {
|
||||
pub fn is_noop(&self) -> bool {
|
||||
self.push == OpArgs::None && self.pop == OpArgs::None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Calculator<'a> {
|
||||
l: String,
|
||||
stack: VecDeque<f64>,
|
||||
@ -46,6 +53,34 @@ impl<'a> Default for Calculator<'a> {
|
||||
active_macros: HashSet::new(),
|
||||
registers: CalculatorRegisters::new(),
|
||||
macros: [
|
||||
(
|
||||
'e',
|
||||
CalculatorMacro {
|
||||
help: "Empty",
|
||||
value: "",
|
||||
},
|
||||
),
|
||||
(
|
||||
'1',
|
||||
CalculatorMacro {
|
||||
help: "Push 1",
|
||||
value: "1 ",
|
||||
},
|
||||
),
|
||||
(
|
||||
'0',
|
||||
CalculatorMacro {
|
||||
help: "Push 0",
|
||||
value: "0 ",
|
||||
},
|
||||
),
|
||||
(
|
||||
't',
|
||||
CalculatorMacro {
|
||||
help: "Test",
|
||||
value: "m1m0\\m1m1\\\\",
|
||||
},
|
||||
),
|
||||
(
|
||||
'm',
|
||||
CalculatorMacro {
|
||||
@ -159,11 +194,8 @@ impl<'a> Calculator<'a> {
|
||||
self.push(f)
|
||||
}
|
||||
CalculatorState::WaitingForMacro => {
|
||||
let value = self
|
||||
.macros
|
||||
.get(&c)
|
||||
.ok_or(CalculatorError::NoSuchMacro(c))?
|
||||
.value;
|
||||
let mac = *self.macros.get(&c).ok_or(CalculatorError::NoSuchMacro(c))?;
|
||||
let value = mac.value;
|
||||
|
||||
if self.active_macros.contains(&c) {
|
||||
return Err(CalculatorError::RecursiveMacro(c));
|
||||
@ -178,6 +210,7 @@ impl<'a> Calculator<'a> {
|
||||
// push: OpArgs::None,
|
||||
// within_macro: self.within_macro(),
|
||||
// })?;
|
||||
self.op(CalculatorOperation::Macro(mac))?;
|
||||
// Record that we are running macro c
|
||||
self.active_macros.insert(c);
|
||||
for c in value.chars() {
|
||||
@ -348,55 +381,84 @@ impl<'a> Calculator<'a> {
|
||||
CalculatorOperation::Pow => self.binary_op(|[a, b]| OpArgs::Unary(b.powf(a))),
|
||||
CalculatorOperation::E => self.binary_op(|[a, b]| OpArgs::Unary(b * 10.0f64.powf(a))),
|
||||
CalculatorOperation::Undo => {
|
||||
let mut undone_already = false;
|
||||
let mut has_run = false;
|
||||
loop {
|
||||
if undone_already {
|
||||
let s = self
|
||||
.undo_buf
|
||||
.last()
|
||||
.ok_or_else(|| CalculatorError::EmptyHistory(String::from("undo")))?;
|
||||
if !s.within_macro {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let s = self
|
||||
.undo_buf
|
||||
.pop()
|
||||
.ok_or_else(|| CalculatorError::EmptyHistory(String::from("undo")))?;
|
||||
let stop = !s.within_macro;
|
||||
let quit = !s.within_macro;
|
||||
let is_noop = s.is_noop();
|
||||
self.apply_state_change(s, false)?;
|
||||
if stop {
|
||||
if is_noop && !has_run {
|
||||
continue;
|
||||
}
|
||||
if quit {
|
||||
return Ok(());
|
||||
}
|
||||
undone_already = true;
|
||||
has_run = true;
|
||||
}
|
||||
}
|
||||
CalculatorOperation::Redo => {
|
||||
let mut redone_already = false;
|
||||
let mut has_run = false;
|
||||
loop {
|
||||
let s = match self.redo_buf.pop() {
|
||||
None => {
|
||||
if redone_already {
|
||||
return Ok(());
|
||||
}
|
||||
return Err(CalculatorError::EmptyHistory(String::from("redo")));
|
||||
}
|
||||
Some(s) => {
|
||||
let stop = !s.within_macro;
|
||||
self.apply_state_change(s, true)?;
|
||||
if stop {
|
||||
return Ok(());
|
||||
}
|
||||
redone_already = true;
|
||||
}
|
||||
};
|
||||
// If we have already redone, but the buffer is empty, last cmd was a macro; don't error
|
||||
if has_run && self.redo_buf.get(0).is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
let s = self
|
||||
.redo_buf
|
||||
.pop()
|
||||
.ok_or_else(|| CalculatorError::EmptyHistory(String::from("redo")))?;
|
||||
|
||||
// We should quit if we will redo something out of a macro
|
||||
let quit = !s.within_macro;
|
||||
let is_noop = s.is_noop();
|
||||
self.apply_state_change(s, true)?;
|
||||
// If this is a noop and it is the first we have encountered, try again. We have hit the beginning of a macro and need to jump straight to the macro contents
|
||||
// We don't want to mark as has_run
|
||||
if is_noop && !has_run {
|
||||
continue;
|
||||
}
|
||||
if quit {
|
||||
return Ok(());
|
||||
}
|
||||
has_run = true;
|
||||
}
|
||||
}
|
||||
// Macros are a no-op as an operator
|
||||
CalculatorOperation::Macro(_) => {
|
||||
return Ok(());
|
||||
}
|
||||
// CalculatorOperation::Redo => {
|
||||
// let mut has_run = false;
|
||||
// loop {
|
||||
// // If we have already redone, but the buffer is empty, last cmd was a macro; don't error
|
||||
// if has_run && self.redo_buf.get(0).is_none() {
|
||||
// return Ok(());
|
||||
// }
|
||||
// let s = self
|
||||
// .redo_buf
|
||||
// .pop()
|
||||
// .ok_or_else(|| CalculatorError::EmptyHistory(String::from("redo")))?;
|
||||
|
||||
// // We should quit if we will redo something out of a macro
|
||||
// let quit = !s.within_macro;
|
||||
// let is_noop = s.is_noop();
|
||||
// self.apply_state_change(s, true)?;
|
||||
// // If this is a noop and it is the first we have encountered, try again. We have hit the beginning of a macro and need to jump straight to the macro contents
|
||||
// // We don't want to mark as has_run
|
||||
// if is_noop && !has_run {
|
||||
// continue;
|
||||
// }
|
||||
// if quit {
|
||||
// return Ok(());
|
||||
// }
|
||||
// has_run = true;
|
||||
// }
|
||||
// }
|
||||
// Macros are a no-op operator; need to insert for undo/redo
|
||||
CalculatorOperation::Macro(_) => Ok(CalculatorStateChange {
|
||||
pop: OpArgs::None,
|
||||
push: OpArgs::None,
|
||||
within_macro: self.within_macro(),
|
||||
}),
|
||||
};
|
||||
|
||||
self.direct_state_change(state_change?)
|
||||
|
Loading…
Reference in New Issue
Block a user