From 4600f5d31ee3b4279b06dbeec4e9112ad3caaee9 Mon Sep 17 00:00:00 2001 From: Austen Adler Date: Wed, 28 Apr 2021 00:48:40 -0400 Subject: [PATCH] Refactor ui code --- src/calc.rs | 206 +++++++++++++++++++++++++++++++++++++---- src/calc/constants.rs | 13 +++ src/calc/errors.rs | 18 ++-- src/calc/operations.rs | 117 +++++++++-------------- src/main.rs | 103 +++++++++++---------- 5 files changed, 312 insertions(+), 145 deletions(-) diff --git a/src/calc.rs b/src/calc.rs index ff71c74..3646283 100644 --- a/src/calc.rs +++ b/src/calc.rs @@ -5,10 +5,11 @@ pub mod operations; use constants::{ CalculatorConstant, CalculatorConstants, CalculatorConstantsIter, CalculatorMacro, CalculatorMacros, CalculatorMacrosIter, CalculatorRegisters, CalculatorRegistersIter, + CalculatorState, RegisterState, }; use errors::{CalculatorError, CalculatorResult}; use operations::CalculatorOperation; -use std::collections::VecDeque; +use std::collections::{HashSet, VecDeque}; enum OpArgs { Unary(f64), @@ -22,20 +23,26 @@ struct CalculatorStateChange { } pub struct Calculator<'a> { + l: String, stack: VecDeque, macros: CalculatorMacros<'a>, + active_macros: HashSet, constants: CalculatorConstants<'a>, registers: CalculatorRegisters, undo_buf: Vec, redo_buf: Vec, + state: CalculatorState, } impl<'a> Default for Calculator<'a> { fn default() -> Calculator<'a> { Calculator { + l: String::new(), stack: vec![1.2, 1.3].into_iter().collect(), + state: CalculatorState::Normal, undo_buf: vec![], redo_buf: vec![], + active_macros: HashSet::new(), registers: CalculatorRegisters::new(), macros: [ ( @@ -94,6 +101,150 @@ impl<'a> Default for Calculator<'a> { } impl<'a> Calculator<'a> { + pub fn take_input(&mut self, c: char) -> CalculatorResult<()> { + //for c in input.chars() { + match &self.state { + CalculatorState::Normal => match c { + c @ '0'..='9' | c @ '.' | c @ 'e' => self.entry(c), + //'!' => self.op(CalculatorOperation::Factorial), + '+' => self.op(CalculatorOperation::Add), + '-' => self.op(CalculatorOperation::Subtract), + '*' => self.op(CalculatorOperation::Multiply), + '/' => self.op(CalculatorOperation::Divide), + 'n' => self.op(CalculatorOperation::Negate), + '|' => self.op(CalculatorOperation::AbsoluteValue), + 'i' => self.op(CalculatorOperation::Inverse), + '%' => self.op(CalculatorOperation::Modulo), + '\\' => self.op(CalculatorOperation::Drop), + '?' => self.op(CalculatorOperation::IntegerDivide), + ' ' => self.op(CalculatorOperation::Dup), + '>' => self.op(CalculatorOperation::Swap), + 's' => self.op(CalculatorOperation::Sin), + 'c' => self.op(CalculatorOperation::Cos), + 't' => self.op(CalculatorOperation::Tan), + 'S' => self.op(CalculatorOperation::ASin), + 'C' => self.op(CalculatorOperation::ACos), + 'T' => self.op(CalculatorOperation::ATan), + 'v' => self.op(CalculatorOperation::Sqrt), + '^' => self.op(CalculatorOperation::Pow), + 'l' => self.op(CalculatorOperation::Log), + 'L' => self.op(CalculatorOperation::Ln), + //'e' => self.op(CalculatorOperation::E), + // Special + 'u' => self.op(CalculatorOperation::Undo), + 'U' => self.op(CalculatorOperation::Redo), + // State modifiers + 'm' => { + self.state = CalculatorState::WaitingForMacro; + Ok(()) + } + 'r' => { + self.state = CalculatorState::WaitingForRegister(RegisterState::Load); + Ok(()) + } + 'R' => { + self.state = CalculatorState::WaitingForRegister(RegisterState::Load); + Ok(()) + } + _ => { + return Err(CalculatorError::NoSuchOperator(c)); + } + }, + CalculatorState::WaitingForConstant => { + let f = self + .constants + .get(&c) + .ok_or(CalculatorError::NoSuchConstant(c))? + .value; + + self.push(f) + } + CalculatorState::WaitingForMacro => { + let value = self + .macros + .get(&c) + .ok_or(CalculatorError::NoSuchMacro(c))? + .value; + + if self.active_macros.contains(&c) { + return Err(CalculatorError::RecursiveMacro(c)); + } + + // The macro needs to run in normal mode + self.state = CalculatorState::Normal; + + // Record that we are running macro c + self.active_macros.insert(c); + for c in value.chars() { + self.take_input(c).or_else(|e| { + self.cancel(); + Err(e) + })?; + } + // Macro c should be over now + self.active_macros.remove(&c); + Ok(()) + + // self.push(value) + } + CalculatorState::WaitingForRegister(register_state) => { + match register_state { + RegisterState::Save => { + let f = self.pop()?; + self.registers.insert(c, f); + } + RegisterState::Load => { + let f = self + .registers + .get(&c) + .ok_or(CalculatorError::NoSuchRegister(c))?; + let f = *f; + self.push(f)?; + } + } + + self.state = CalculatorState::Normal; + Ok(()) + } + } + //} + //Ok(()) + } + + pub fn cancel(&mut self) { + self.state = CalculatorState::Normal; + self.active_macros.clear(); + } + + fn entry(&mut self, c: char) -> CalculatorResult<()> { + match c { + '0'..='9' => { + self.l.push(c); + Ok(()) + } + 'e' => { + if self.l.is_empty() { + let f = self.pop().or(Err(CalculatorError::NotEnoughStackEntries))?; + + self.l = f.to_string(); + } + + if !self.l.contains('e') { + self.l.push('e'); + } + Ok(()) + } + + '.' => { + if !self.l.contains('.') && !self.l.contains('e') { + self.l.push('.'); + } + Ok(()) + } + _ => Err(CalculatorError::ParseError), + } + } + pub fn get_constants_iter(&'a self) -> CalculatorConstantsIter<'a> { self.constants.iter() } @@ -110,31 +261,40 @@ impl<'a> Calculator<'a> { let value = *value; self.push(value) } - None => Err(CalculatorError::NoSuchConstant), + None => Err(CalculatorError::NoSuchConstant(key)), } } - pub fn push_register(&mut self, key: char) -> CalculatorResult<()> { - match self.registers.get(&key) { - Some(f) => { - let f = *f; - self.push(f) - } - None => Err(CalculatorError::NoSuchRegister), - } - } - // TODO: Use hashmap - pub fn save_register(&mut self, key: char) -> CalculatorResult<()> { - let f = self.pop()?; - self.registers.insert(key, f); - Ok(()) - } + // pub fn push_register(&mut self, key: char) -> CalculatorResult<()> { + // match self.registers.get(&key) { + // Some(f) => { + // let f = *f; + // self.push(f) + // } + // None => Err(CalculatorError::NoSuchRegister), + // } + // } + // pub fn save_register(&mut self, key: char) -> CalculatorResult<()> { + // let f = self.pop()?; + // self.registers.insert(key, f); + // Ok(()) + // } pub fn get_macro(&mut self, key: char) -> Result<&CalculatorMacro<'a>, CalculatorError> { match self.macros.get(&key) { Some(m) => Ok(m), - None => Err(CalculatorError::NoSuchMacro), + None => Err(CalculatorError::NoSuchMacro(key)), } } + pub fn flush_l(&mut self) -> CalculatorResult { + if self.l.is_empty() { + Ok(false) + } else { + let f = self.l.parse::().or(Err(CalculatorError::ParseError))?; + self.push(f)?; + self.l.clear(); + Ok(true) + } + } pub fn push(&mut self, f: f64) -> CalculatorResult<()> { self.direct_state_change(CalculatorStateChange { pop: OpArgs::None, @@ -154,6 +314,12 @@ impl<'a> Calculator<'a> { } //TODO: VecDeque could have other types pub fn op(&mut self, op: CalculatorOperation) -> CalculatorResult<()> { + // Dup is special -- don't actually run it if l needs to be flushed + if self.flush_l()? { + if let CalculatorOperation::Dup = op { + return Ok(()); + } + } let state_change = match op { CalculatorOperation::Add => self.binary_op(|[a, b]| OpArgs::Unary(b + a)), CalculatorOperation::Subtract => self.binary_op(|[a, b]| OpArgs::Unary(b - a)), @@ -198,6 +364,10 @@ impl<'a> Calculator<'a> { .ok_or_else(|| CalculatorError::EmptyHistory(String::from("redo")))?; return self.apply_state_change(s, true); } + // TODO: This should not happen -- need to pull all macro/register accesses into their own enum + CalculatorOperation::Macro(_) => { + return Ok(()); + } }; self.direct_state_change(state_change?) diff --git a/src/calc/constants.rs b/src/calc/constants.rs index e8d84ea..01201a3 100644 --- a/src/calc/constants.rs +++ b/src/calc/constants.rs @@ -1,6 +1,19 @@ use std::collections::hash_map::Iter; use std::collections::HashMap; +pub enum RegisterState { + Save, + Load, +} + +pub enum CalculatorState { + Normal, + //Macro, + WaitingForConstant, + WaitingForMacro, + WaitingForRegister(RegisterState), +} + #[derive(Debug, Clone, Copy)] pub struct CalculatorConstant<'a> { pub help: &'a str, diff --git a/src/calc/errors.rs b/src/calc/errors.rs index 934c819..3da452e 100644 --- a/src/calc/errors.rs +++ b/src/calc/errors.rs @@ -7,11 +7,12 @@ pub enum CalculatorError { NotEnoughStackEntries, CorruptStateChange(String), EmptyHistory(String), - NoSuchConstant, - NoSuchRegister, - NoSuchMacro, + NoSuchConstant(char), + NoSuchRegister(char), + NoSuchMacro(char), + NoSuchOperator(char), + RecursiveMacro(char), ParseError, - NoSuchOperator, } impl fmt::Display for CalculatorError { @@ -23,10 +24,11 @@ impl fmt::Display for CalculatorError { write!(f, "Corrupt state change: {}", msg) } CalculatorError::EmptyHistory(msg) => write!(f, "No history to {}", msg), - CalculatorError::NoSuchOperator => write!(f, "No such operator"), - CalculatorError::NoSuchConstant => write!(f, "No such constant"), - CalculatorError::NoSuchRegister => write!(f, "No such register"), - CalculatorError::NoSuchMacro => write!(f, "No such macro"), + CalculatorError::NoSuchOperator(c) => write!(f, "No such operator '{}'", c), + CalculatorError::NoSuchConstant(c) => write!(f, "No such constant '{}'", c), + CalculatorError::NoSuchRegister(c) => write!(f, "No such register '{}'", c), + CalculatorError::NoSuchMacro(c) => write!(f, "No such macro '{}'", c), + CalculatorError::RecursiveMacro(c) => write!(f, "Recursive macro '{}'", c), CalculatorError::ParseError => write!(f, "Parse error"), } } diff --git a/src/calc/operations.rs b/src/calc/operations.rs index 52663da..83f69ff 100644 --- a/src/calc/operations.rs +++ b/src/calc/operations.rs @@ -1,6 +1,7 @@ -use super::errors::{CalculatorError, CalculatorResult}; +use super::constants::CalculatorMacro; +// use super::errors::{CalculatorError, CalculatorResult}; -pub enum CalculatorOperation { +pub enum CalculatorOperation<'a> { Add, Subtract, Multiply, @@ -28,77 +29,51 @@ pub enum CalculatorOperation { Log, Ln, E, + Macro(CalculatorMacro<'a>), } -impl CalculatorOperation { - pub fn from_char(key: char) -> CalculatorResult { - match key { - '+' => Ok(CalculatorOperation::Add), - '-' => Ok(CalculatorOperation::Subtract), - '*' => Ok(CalculatorOperation::Multiply), - '/' => Ok(CalculatorOperation::Divide), - 'n' => Ok(CalculatorOperation::Negate), - '|' => Ok(CalculatorOperation::AbsoluteValue), - 'i' => Ok(CalculatorOperation::Inverse), - '%' => Ok(CalculatorOperation::Modulo), - //'r' => Ok(CalculatorOperation::Remainder), - '\\' => Ok(CalculatorOperation::Drop), - '?' => Ok(CalculatorOperation::IntegerDivide), - '\n' => Ok(CalculatorOperation::Dup), - '>' => Ok(CalculatorOperation::Swap), - 's' => Ok(CalculatorOperation::Sin), - 'c' => Ok(CalculatorOperation::Cos), - 't' => Ok(CalculatorOperation::Tan), - 'S' => Ok(CalculatorOperation::ASin), - 'C' => Ok(CalculatorOperation::ACos), - 'T' => Ok(CalculatorOperation::ATan), - 'v' => Ok(CalculatorOperation::Sqrt), - //TODO: Should not be calculator states probably - 'u' => Ok(CalculatorOperation::Undo), - 'U' => Ok(CalculatorOperation::Redo), - '^' => Ok(CalculatorOperation::Pow), - //'!' => Ok(CalculatorOperation::Factorial), - 'l' => Ok(CalculatorOperation::Log), - 'L' => Ok(CalculatorOperation::Ln), - 'e' => Ok(CalculatorOperation::E), - _ => Err(CalculatorError::NoSuchOperator), - } - } +// impl CalculatorOperation<'_> { +// pub fn from_char<'a>(key: char) -> CalculatorResult> { +// match key { +// _ => Err(CalculatorError::NoSuchOperator), +// } +// } - pub fn num_stack(&self) -> usize { - match self { - CalculatorOperation::Add - | CalculatorOperation::Subtract - | CalculatorOperation::Multiply - | CalculatorOperation::Divide - | CalculatorOperation::Modulo - | CalculatorOperation::IntegerDivide - | CalculatorOperation::Swap - | CalculatorOperation::Pow - | CalculatorOperation::E - //| CalculatorOperation::Remainder - => 2, - CalculatorOperation::Negate - | CalculatorOperation::AbsoluteValue - | CalculatorOperation::Inverse - | CalculatorOperation::Drop +// pub fn num_stack(&self) -> usize { +// match self { +// CalculatorOperation::Add +// | CalculatorOperation::Subtract +// | CalculatorOperation::Multiply +// | CalculatorOperation::Divide +// | CalculatorOperation::Modulo +// | CalculatorOperation::IntegerDivide +// | CalculatorOperation::Swap +// | CalculatorOperation::Pow +// | CalculatorOperation::E +// //| CalculatorOperation::Remainder +// => 2, +// CalculatorOperation::Negate +// | CalculatorOperation::AbsoluteValue +// | CalculatorOperation::Inverse +// | CalculatorOperation::Drop - | CalculatorOperation::Sin - | CalculatorOperation::Cos - | CalculatorOperation::Tan - | CalculatorOperation::ASin - | CalculatorOperation::ACos - | CalculatorOperation::ATan - | CalculatorOperation::Sqrt - | CalculatorOperation::Dup +// | CalculatorOperation::Sin +// | CalculatorOperation::Cos +// | CalculatorOperation::Tan +// | CalculatorOperation::ASin +// | CalculatorOperation::ACos +// | CalculatorOperation::ATan +// | CalculatorOperation::Sqrt +// | CalculatorOperation::Dup - //|CalculatorOperation::Factorial - | CalculatorOperation::Log - | CalculatorOperation::Ln - => 1, - CalculatorOperation::Undo - | CalculatorOperation::Redo - => 0, - } - } -} +// //|CalculatorOperation::Factorial +// | CalculatorOperation::Log +// | CalculatorOperation::Ln +// => 1, +// CalculatorOperation::Macro(_) +// |CalculatorOperation::Undo +// | CalculatorOperation::Redo +// => 0, +// } +// } +// } diff --git a/src/main.rs b/src/main.rs index 2ab1813..eaeb72c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,6 @@ mod calc; mod util; use calc::errors::{CalculatorError, CalculatorResult}; -use calc::operations::CalculatorOperation; use calc::Calculator; use std::cmp; use std::convert::TryFrom; @@ -165,7 +164,8 @@ fn main() -> Result<(), Box> { => Dup l => Log\n\ > => Swap L => Ln\n\ e => E ^c => Constants\n\ - m => Macros rR => Registers\ + m => Macros rR => Registers\n\ + uU => Undo/Redo\ ", }, f, @@ -257,18 +257,18 @@ fn handle_key(app: &mut App, events: &Events, key: Key) -> CalculatorResult { return Ok(true); } - Key::Ctrl('c') => { - app.state = AppState::Constants; - } - Key::Char('r') => { - app.state = AppState::Registers(RegisterState::Load); - } - Key::Char('R') => { - app.state = AppState::Registers(RegisterState::Save); - } - Key::Char('m') => { - app.state = AppState::Macros; - } + // Key::Ctrl('c') => { + // app.state = AppState::Constants; + // } + // Key::Char('r') => { + // app.state = AppState::Registers(RegisterState::Load); + // } + // Key::Char('R') => { + // app.state = AppState::Registers(RegisterState::Save); + // } + // Key::Char('m') => { + // app.state = AppState::Macros; + // } Key::Char('h') => { app.state = AppState::Help; } @@ -295,7 +295,7 @@ fn handle_key(app: &mut App, events: &Events, key: Key) -> CalculatorResult { if app.input.is_empty() { - calc_operation(app, '\n')?; + calc_operation(app, ' ')?; } else { let mut tmp_input = app.input.clone(); if tmp_input.ends_with('e') { @@ -313,6 +313,7 @@ fn handle_key(app: &mut App, events: &Events, key: Key) -> CalculatorResult')?; } Key::Down => { + // TODO: Internal calculator function if let Ok(x) = app.calculator.pop() { app.input = x.to_string(); } @@ -329,63 +330,68 @@ fn handle_key(app: &mut App, events: &Events, key: Key) -> CalculatorResult {} }, AppState::Help => match key { - Key::Esc => { + Key::Esc | Key::Char('q') => { app.state = AppState::Calculator; + app.calculator.cancel(); } _ => {} }, AppState::Constants => match key { Key::Esc => { app.state = AppState::Calculator; + app.calculator.cancel(); } Key::Char(c) => { app.calculator.push_constant(c)?; app.input.clear(); app.state = AppState::Calculator; + app.calculator.cancel(); } _ => {} }, AppState::Registers(task) => match key { Key::Esc => { app.state = AppState::Calculator; + app.calculator.cancel(); } - Key::Char(c) => { - match task { - RegisterState::Save => { - app.calculator.save_register(c)?; - } - RegisterState::Load => { - app.calculator.push_register(c)?; - } - } - app.input.clear(); - app.state = AppState::Calculator; - } + // Key::Char(c) => { + // match task { + // RegisterState::Save => { + // app.calculator.save_register(c)?; + // } + // RegisterState::Load => { + // app.calculator.push_register(c)?; + // } + // } + // app.input.clear(); + // app.state = AppState::Calculator; + // } _ => {} }, AppState::Macros => match key { Key::Esc => { app.state = AppState::Calculator; + app.calculator.cancel(); } - Key::Char(c) => { - if !app.input.is_empty() { - //TODO: A better way to do this - //Can use calc_operation in the future. Already performs this check - let f = app - .input - .parse::() - .or(Err(CalculatorError::ParseError))?; - app.calculator.push(f).and_then(|()| { - app.input.clear(); - Ok(()) - })?; - } + // Key::Char(c) => { + // if !app.input.is_empty() { + // //TODO: A better way to do this + // //Can use calc_operation in the future. Already performs this check + // let f = app + // .input + // .parse::() + // .or(Err(CalculatorError::ParseError))?; + // app.calculator.push(f).and_then(|()| { + // app.input.clear(); + // Ok(()) + // })?; + // } - // TODO: Handle macros internally to the calculator - let mac = app.calculator.get_macro(c)?; - events.fill_event_buf(mac.value); - app.state = AppState::Calculator; - } + // // TODO: Handle macros internally to the calculator + // let mac = app.calculator.get_macro(c)?; + // events.fill_event_buf(mac.value); + // app.state = AppState::Calculator; + // } _ => {} }, } @@ -393,7 +399,6 @@ fn handle_key(app: &mut App, events: &Events, key: Key) -> CalculatorResult CalculatorResult<()> { - let op = CalculatorOperation::from_char(c)?; if !app.input.is_empty() { let f = app .input @@ -403,7 +408,9 @@ fn calc_operation(app: &mut App, c: char) -> CalculatorResult<()> { app.input.clear(); } - app.calculator.op(op) + //let op = CalculatorOperation::from_char(c)?; + app.calculator.take_input(c) + //app.calculator.op(op) } struct ClippyRectangle<'a> {