Implement method 2

This commit is contained in:
Austen Adler 2021-04-29 23:09:03 -04:00
parent 0e69681ab8
commit 3084f6c745

View File

@ -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?)