Refactor ui code

This commit is contained in:
Austen Adler 2021-04-28 00:48:40 -04:00
parent 5772147ef1
commit 4600f5d31e
5 changed files with 312 additions and 145 deletions

View File

@ -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<f64>,
macros: CalculatorMacros<'a>,
active_macros: HashSet<char>,
constants: CalculatorConstants<'a>,
registers: CalculatorRegisters,
undo_buf: Vec<CalculatorStateChange>,
redo_buf: Vec<CalculatorStateChange>,
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<bool> {
if self.l.is_empty() {
Ok(false)
} else {
let f = self.l.parse::<f64>().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?)

View File

@ -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,

View File

@ -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"),
}
}

View File

@ -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<CalculatorOperation> {
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<CalculatorOperation<'a>> {
// 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,
// }
// }
// }

View File

@ -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<dyn Error>> {
<ret> => 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<bool
Key::Char('q') => {
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<bool
}
Key::Char('\n') | Key::Char(' ') => {
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<bool
calc_operation(app, '>')?;
}
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<bool
_ => {}
},
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::<f64>()
.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::<f64>()
// .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<bool
}
fn calc_operation(app: &mut App, c: char) -> 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> {