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 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(());
|
}
|
||||||
}
|
let s = self
|
||||||
return Err(CalculatorError::EmptyHistory(String::from("redo")));
|
.redo_buf
|
||||||
}
|
.pop()
|
||||||
Some(s) => {
|
.ok_or_else(|| CalculatorError::EmptyHistory(String::from("redo")))?;
|
||||||
let stop = !s.within_macro;
|
|
||||||
self.apply_state_change(s, true)?;
|
// We should quit if we will redo something out of a macro
|
||||||
if stop {
|
let quit = !s.within_macro;
|
||||||
return Ok(());
|
let is_noop = s.is_noop();
|
||||||
}
|
self.apply_state_change(s, true)?;
|
||||||
redone_already = 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::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?)
|
||||||
|
Loading…
Reference in New Issue
Block a user