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 operations::CalculatorOperation;
use std::collections::{HashSet, VecDeque}; use std::collections::{HashSet, VecDeque};
#[derive(PartialEq)]
enum OpArgs { enum OpArgs {
Unary(f64), Unary(f64),
Binary([f64; 2]), Binary([f64; 2]),
@ -23,6 +24,12 @@ struct CalculatorStateChange {
within_macro: bool, within_macro: bool,
} }
impl CalculatorStateChange {
pub fn is_noop(&self) -> bool {
self.push == OpArgs::None && self.pop == OpArgs::None
}
}
pub struct Calculator<'a> { pub struct Calculator<'a> {
l: String, l: String,
stack: VecDeque<f64>, stack: VecDeque<f64>,
@ -46,6 +53,34 @@ impl<'a> Default for Calculator<'a> {
active_macros: HashSet::new(), active_macros: HashSet::new(),
registers: CalculatorRegisters::new(), registers: CalculatorRegisters::new(),
macros: [ 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', 'm',
CalculatorMacro { CalculatorMacro {
@ -159,11 +194,8 @@ impl<'a> Calculator<'a> {
self.push(f) self.push(f)
} }
CalculatorState::WaitingForMacro => { CalculatorState::WaitingForMacro => {
let value = self let mac = *self.macros.get(&c).ok_or(CalculatorError::NoSuchMacro(c))?;
.macros let value = mac.value;
.get(&c)
.ok_or(CalculatorError::NoSuchMacro(c))?
.value;
if self.active_macros.contains(&c) { if self.active_macros.contains(&c) {
return Err(CalculatorError::RecursiveMacro(c)); return Err(CalculatorError::RecursiveMacro(c));
@ -178,6 +210,7 @@ impl<'a> Calculator<'a> {
// push: OpArgs::None, // push: OpArgs::None,
// within_macro: self.within_macro(), // within_macro: self.within_macro(),
// })?; // })?;
self.op(CalculatorOperation::Macro(mac))?;
// Record that we are running macro c // Record that we are running macro c
self.active_macros.insert(c); self.active_macros.insert(c);
for c in value.chars() { 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::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::E => self.binary_op(|[a, b]| OpArgs::Unary(b * 10.0f64.powf(a))),
CalculatorOperation::Undo => { CalculatorOperation::Undo => {
let mut undone_already = false; let mut has_run = false;
loop { 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 let s = self
.undo_buf .undo_buf
.pop() .pop()
.ok_or_else(|| CalculatorError::EmptyHistory(String::from("undo")))?; .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)?; self.apply_state_change(s, false)?;
if stop { if is_noop && !has_run {
continue;
}
if quit {
return Ok(()); return Ok(());
} }
undone_already = true; has_run = true;
} }
} }
CalculatorOperation::Redo => { CalculatorOperation::Redo => {
let mut redone_already = false; let mut has_run = false;
loop { loop {
let s = match self.redo_buf.pop() { // If we have already redone, but the buffer is empty, last cmd was a macro; don't error
None => { if has_run && self.redo_buf.get(0).is_none() {
if redone_already {
return Ok(()); return Ok(());
} }
return Err(CalculatorError::EmptyHistory(String::from("redo"))); let s = self
} .redo_buf
Some(s) => { .pop()
let stop = !s.within_macro; .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)?; self.apply_state_change(s, true)?;
if stop { // 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(()); return Ok(());
} }
redone_already = true; has_run = true;
}
};
} }
} }
// Macros are a no-op as an operator // CalculatorOperation::Redo => {
CalculatorOperation::Macro(_) => { // let mut has_run = false;
return Ok(()); // 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?) self.direct_state_change(state_change?)