From 3084f6c7459734d3f4d9ca1d160f8470d9b659df Mon Sep 17 00:00:00 2001 From: Austen Adler Date: Thu, 29 Apr 2021 23:09:03 -0400 Subject: [PATCH] Implement method 2 --- src/calc.rs | 142 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 102 insertions(+), 40 deletions(-) diff --git a/src/calc.rs b/src/calc.rs index cf49dba..198ec30 100644 --- a/src/calc.rs +++ b/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, @@ -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?)