use std::cell::RefCell;
use std::fs::File;
use std::io::{BufRead, BufReader, Write};
use std::rc::Rc;
use fileinput::FileInput;
use itertools::Itertools;
use crate::action::{Action, Actions};
use crate::config::Config;
use crate::context::{Context, Format};
use crate::error::{EngineError, MyError, MyResult};
use crate::helper::CommandEditor;
use crate::interface::{Directive, Interface, Operation};
use crate::undo::Undo;
use crate::util;
use crate::value::Value;
pub struct Engine<W: Write> {
config: Config,
editor: CommandEditor,
interface: Rc<RefCell<Interface<W>>>,
context: Rc<RefCell<Context>>,
stack: Vec<Value>,
undos: Vec<Undo>,
redos: Vec<Undo>,
clip: Option<Value>,
dirty: bool,
script: bool,
}
impl<W: Write> Engine<W> {
pub fn new(config: Config) -> MyResult<Engine<W>> {
let interface = Rc::new(RefCell::new(Interface::build()));
let editor = interface.borrow().create_editor()?;
let context = Rc::new(RefCell::new(Context::new()));
let stack = Vec::new();
let undos = Vec::new();
let redos = Vec::new();
let engine = Engine {
config,
editor,
interface,
context,
stack,
undos,
redos,
clip: None,
dirty: false,
script: false,
};
return Ok(engine);
}
pub fn parse_input(&mut self, writer: &mut W) -> MyResult<()> {
if let Some(import) = &self.config.import {
let import = vec![String::from(import)];
self.import_file(writer, import)?;
}
if !self.config.values.is_empty() {
self.script = true;
self.parse_values(writer)?;
self.prepare_result()?;
self.show_simple(writer)?;
} else if !self.config.paths.is_empty() || atty::isnt(atty::Stream::Stdin) || atty::isnt(atty::Stream::Stdout) {
let reader = FileInput::new(&self.config.paths);
self.script = true;
self.parse_files(writer, reader)?;
self.prepare_result()?;
self.show_simple(writer)?;
} else {
self.parse_loop(writer)?;
}
return Ok(());
}
fn parse_values(&mut self, writer: &mut W) -> MyResult<()> {
let values = self.config.values.drain(..).collect::<Vec<String>>();
for value in values {
self.parse_line(writer, &value)?;
}
return Ok(());
}
fn parse_files(&mut self, writer: &mut W, reader: FileInput) -> MyResult<()> {
let reader = BufReader::new(reader);
for line in reader.lines() {
let line = line?;
self.parse_line(writer, &line)?;
}
return Ok(());
}
fn parse_loop(&mut self, writer: &mut W) -> MyResult<()> {
loop {
if self.dirty {
self.show_stack(writer)?;
self.dirty = false;
}
let line = self.editor.readline("rpn> ")?;
self.editor.add_history_entry(&line)?;
if let Err(error) = self.parse_line(writer, &line) {
if let MyError::Engine(error) = error {
writeln!(writer, " {}", error)?;
self.dirty = false;
} else {
return Err(error);
}
}
}
}
fn parse_line(&mut self, writer: &mut W, line: &str) -> MyResult<()> {
let mut empty = true;
let mut undo = Undo::default();
let mut redo = Undo::default();
let (line, comment) = Self::split_comment(line, self.script);
let actions = Actions::new(&self.interface, line.split_ascii_whitespace());
for action in actions {
let action = match action {
Ok((action, token)) => self.apply_action(
writer,
&mut undo,
&mut redo,
action,
Some(token),
),
Err(error) => Err(error),
};
if let Err(error) = action {
undo.apply_to(&mut self.stack);
return Err(error);
}
empty = false;
}
if let Some(comment) = comment {
if let Err(error) = self.apply_comment(&mut undo, comment) {
undo.apply_to(&mut self.stack);
return Err(error);
}
empty = false;
}
self.commit_undo(undo, redo);
self.dirty |= empty;
return Ok(());
}
fn split_comment(line: &str, script: bool) -> (&str, Option<&str>) {
if let Some(stop) = line.find('#') {
let start = stop + 1;
let values = &line[..stop].trim();
let comment = &line[start..].trim();
if comment.is_empty() || script {
return (values, None);
} else {
return (values, Some(comment));
}
} else {
let values = line.trim();
return (values, None);
}
}
fn apply_action(
&mut self,
writer: &mut W,
undo: &mut Undo,
redo: &mut Undo,
action: Action<W>,
token: Option<&str>,
) -> MyResult<()> {
match action {
Action::Operation(operation) => {
match operation.as_ref() {
Operation::ValueNone(function) => {
let result = function()?;
self.push_single(undo, result, token);
self.dirty = true;
}
Operation::ValueOne(function) => {
let value = self.pop_single(undo, None)?;
let result = function(value)?;
self.push_single(undo, result, token);
self.dirty = true;
}
Operation::ValueTwo(function) => {
let (value1, value2) = self.pop_double(undo, None)?;
let result = function(value1, value2)?;
self.push_single(undo, result, token);
self.dirty = true;
}
Operation::ValueAll(function) => {
let values = self.stack.drain(..).collect::<Vec<Value>>();
let result = function(values.clone())?;
self.stack.push(result);
self.dirty = true;
undo.merge(1, values, token);
}
Operation::ContextNone(function) => {
function(&mut self.context.borrow_mut());
self.dirty = true;
}
Operation::ContextOne(function) => {
let value = self.pop_single(undo, token)?;
function(&mut self.context.borrow_mut(), value);
self.dirty = true;
}
Operation::EngineNone(function, _, _) => {
self.dirty |= function(self, writer)?;
}
Operation::EngineUndo(function, _, _) => {
self.dirty |= function(self, undo, redo, token)?;
}
}
}
Action::Directive(directive, tokens) => {
match directive.as_ref() {
Directive::EngineAll(function, _, _) => {
self.dirty |= function(self, writer, tokens)?;
}
}
}
Action::Definition(actions) => {
for action in actions {
self.apply_action(writer, undo, redo, action, None)?;
}
undo.merge(0, vec![], token);
}
Action::Value(value) => {
self.push_single(undo, value, token);
}
}
return Ok(());
}
fn apply_comment(&mut self, undo: &mut Undo, comment: &str) -> MyResult<()> {
let value = self.pop_single(undo, Some("#"))?;
let value = value.with_comment(comment);
self.push_single(undo, value, Some(comment));
self.dirty = true;
return Ok(());
}
fn commit_undo(&mut self, undo: Undo, redo: Undo) {
if !undo.is_empty() {
self.undos.push(undo);
self.redos.clear();
} else if !redo.is_empty() {
self.redos.push(redo);
}
}
fn pop_single(&mut self, undo: &mut Undo, token: Option<&str>) -> MyResult<Value> {
if let Some(value) = self.stack.pop() {
undo.merge(0, vec![value.clone()], token);
return Ok(value);
} else {
return Err(MyError::from(EngineError::MissingValue));
}
}
fn pop_double(&mut self, undo: &mut Undo, token: Option<&str>) -> MyResult<(Value, Value)> {
if self.stack.len() >= 2 {
let value2 = self.stack.pop().unwrap_or_default();
let value1 = self.stack.pop().unwrap_or_default();
undo.merge(0, vec![value1.clone(), value2.clone()], token);
return Ok((value1, value2));
} else {
return Err(MyError::from(EngineError::MissingValue));
}
}
fn push_single(&mut self, undo: &mut Undo, value: Value, token: Option<&str>) {
self.stack.push(value);
undo.merge(1, vec![], token);
}
pub fn clear_values(
&mut self,
undo: &mut Undo,
redo: &mut Undo,
token: Option<&str>,
) -> MyResult<bool> {
let values = self.stack.drain(..).collect();
undo.merge(0, values, token);
redo.clear();
return Ok(true);
}
pub fn pop_value(
&mut self,
undo: &mut Undo,
redo: &mut Undo,
token: Option<&str>,
) -> MyResult<bool> {
self.pop_single(undo, token)?;
redo.clear();
return Ok(true);
}
pub fn dup_value(
&mut self,
undo: &mut Undo,
redo: &mut Undo,
token: Option<&str>,
) -> MyResult<bool> {
if let Some(value) = self.stack.last() {
self.push_single(undo, value.clone(), token);
redo.clear();
return Ok(true);
} else {
return Err(MyError::from(EngineError::MissingValue));
}
}
pub fn swap_values(
&mut self,
undo: &mut Undo,
redo: &mut Undo,
token: Option<&str>,
) -> MyResult<bool> {
let (value1, value2) = self.pop_double(undo, None)?;
self.push_single(undo, value2, None);
self.push_single(undo, value1, token);
redo.clear();
return Ok(true);
}
pub fn cut_value(
&mut self,
undo: &mut Undo,
redo: &mut Undo,
token: Option<&str>,
) -> MyResult<bool> {
let value = self.pop_single(undo, token)?;
self.clip = Some(value);
redo.clear();
return Ok(true);
}
pub fn copy_value(
&mut self,
_undo: &mut Undo,
_redo: &mut Undo,
_token: Option<&str>,
) -> MyResult<bool> {
if let Some(value) = self.stack.last() {
self.clip = Some(value.clone());
return Ok(false);
} else {
return Err(MyError::from(EngineError::MissingValue));
}
}
pub fn paste_value(
&mut self,
undo: &mut Undo,
redo: &mut Undo,
token: Option<&str>,
) -> MyResult<bool> {
if let Some(clip) = &self.clip {
self.push_single(undo, clip.clone(), token);
redo.clear();
return Ok(true);
} else {
return Err(MyError::from(EngineError::MissingClip));
}
}
pub fn import_file(
&mut self,
writer: &mut W,
tokens: Vec<String>,
) -> MyResult<bool> {
let reader = FileInput::new(&tokens);
self.parse_files(writer, reader)?;
return Ok(true);
}
pub fn export_file(
&mut self,
_writer: &mut W,
tokens: Vec<String>,
) -> MyResult<bool> {
if let Some(path) = tokens.first() {
let mut writer = File::create(path)?;
self.show_simple(&mut writer)?;
}
return Ok(false);
}
pub fn define_function(
&mut self,
_writer: &mut W,
tokens: Vec<String>,
) -> MyResult<bool> {
let mut tokens = tokens.iter();
if let Some(keyword) = tokens.next() {
let description = tokens.clone().join(" ");
let tokens = tokens.map(String::as_ref);
let actions = Actions::new(&self.interface, tokens);
let actions = actions
.map(|result| result.map(|(x, _)| x))
.collect::<MyResult<Vec<Action<W>>>>()?;
self.interface.borrow_mut().add_definition(keyword, actions, description);
self.interface.borrow().adjust_editor(&mut self.editor);
}
return Ok(false);
}
pub fn undo_stack(
&mut self,
undo: &mut Undo,
redo: &mut Undo,
_token: Option<&str>,
) -> MyResult<bool> {
if !undo.is_empty() {
self.redos.push(undo.apply_to(&mut self.stack));
redo.clear();
return Ok(true);
} else if let Some(mut undo) = self.undos.pop() {
self.redos.push(undo.apply_to(&mut self.stack));
redo.clear();
return Ok(true);
} else {
return Err(MyError::from(EngineError::MissingUndo));
}
}
pub fn redo_stack(
&mut self,
undo: &mut Undo,
redo: &mut Undo,
_token: Option<&str>,
) -> MyResult<bool> {
if undo.is_empty() {
if !redo.is_empty() {
self.undos.push(redo.apply_to(&mut self.stack));
return Ok(true);
} else if let Some(mut redo) = self.redos.pop() {
self.undos.push(redo.apply_to(&mut self.stack));
return Ok(true);
}
}
return Err(MyError::from(EngineError::MissingRedo));
}
pub fn show_history(&mut self, writer: &mut W) -> MyResult<bool> {
if !self.script {
self.limit_history(writer, 10)?;
}
return Ok(false);
}
fn limit_history(&mut self, writer: &mut W, limit: usize) -> MyResult<()> {
for undo in self.undos.iter().rev().take(limit).rev() {
writeln!(writer, " {}", undo)?;
}
writeln!(writer, " <==")?;
for redo in self.redos.iter().rev().take(limit) {
writeln!(writer, " {}", redo)?;
}
return Ok(());
}
fn prepare_result(&mut self) -> MyResult<()> {
if self.config.sum {
let values = self.stack.drain(..).collect::<Vec<Value>>();
let result = Value::calc_sum(values)?;
self.stack.push(result);
}
if self.config.hex {
self.context.borrow_mut().set_hexadecimal();
}
return Ok(());
}
fn show_simple<V: Write>(&mut self, writer: &mut V) -> MyResult<()> {
self.measure_hexadecimal();
let context = self.context.borrow().clone().with_separator(false);
for value in &self.stack {
let (integer, fraction, _) = value.to_strings(&context);
writeln!(writer, "{}{}", integer, fraction)?;
}
return Ok(());
}
pub fn show_stack(&mut self, writer: &mut W) -> MyResult<bool> {
if !self.script {
self.measure_hexadecimal();
let context = self.context.borrow();
let table = self.stack.iter()
.map(|value| value.to_strings(&context))
.collect::<Vec<(String, String, Option<String>)>>();
let integer_width = table.iter()
.map(|(x, _, _)| x.len())
.max()
.unwrap_or_default();
let fraction_width = table.iter()
.map(|(_, x, _)| x.len())
.max()
.unwrap_or_default();
for (integer, fraction, comment) in table {
let padding = util::create_padding(' ', integer_width + 2, integer.len(), 0);
write!(writer, "{}{}{}", padding, integer, fraction)?;
if let Some(comment) = comment {
let padding = util::create_padding(' ', fraction_width + 1, fraction.len(), 0);
write!(writer, "{}# {}", padding, comment)?;
}
writeln!(writer)?;
}
}
return Ok(false);
}
fn measure_hexadecimal(&mut self) {
let mut context = self.context.borrow_mut();
if let Format::Base16(_) = context.format {
let chunks = self.stack.iter()
.map(|x| x.measure_hexadecimal())
.max()
.unwrap_or_default();
context.format = Format::Base16(chunks);
}
}
pub fn show_help(&mut self, writer: &mut W) -> MyResult<bool> {
self.interface.borrow().show_help(writer, self.script)?;
return Ok(false);
}
}
#[cfg(test)]
pub mod tests {
use std::io::Write;
use pretty_assertions::assert_eq;
use crate::config::Config;
use crate::context::Format;
use crate::engine::Engine;
use crate::error::MyResult;
use crate::undo::tests::create_undo;
use crate::util::tests::BufferWriter;
use crate::value::Value;
#[test]
fn test_split_comment_finds_first_hash_if_interactive() {
assert_eq!(Engine::<BufferWriter>::split_comment("\tone two three\t", false), ("one two three", None));
assert_eq!(Engine::<BufferWriter>::split_comment("\tone two three #\t", false), ("one two three", None));
assert_eq!(Engine::<BufferWriter>::split_comment("\tone # two # three\t", false), ("one", Some("two # three")));
}
#[test]
fn test_split_comment_finds_first_hash_if_scripted() {
assert_eq!(Engine::<BufferWriter>::split_comment("\tone two three\t", true), ("one two three", None));
assert_eq!(Engine::<BufferWriter>::split_comment("\tone two three #\t", true), ("one two three", None));
assert_eq!(Engine::<BufferWriter>::split_comment("\tone # two # three\t", true), ("one", None));
}
#[test]
fn test_create_values_modifies_stack() {
let expected_stack = create_values(vec!["1", "2", "3", "4", "5", "6"]);
let expected_undos = vec![
create_undo(3, vec![], vec!["1", "2", "3"]),
create_undo(3, vec![], vec!["4", "5", "6"]),
];
let expected_redos = vec![];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "1 2 3").is_ok());
assert!(parse_line(&mut engine, &mut writer, "4 5 6").is_ok());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "");
}
#[test]
fn test_comment_modifies_current_line() {
let expected_stack = vec![
create_value("1"),
create_value("2"),
create_value("3"),
create_value("42").with_comment("the answer"),
];
let expected_undos = vec![
create_undo(2, vec![], vec!["1", "2"]),
create_undo(2, vec![], vec!["3", "42", "#", "the answer"]),
];
let expected_redos = vec![];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "1 2").is_ok());
assert!(parse_line(&mut engine, &mut writer, "3 42 # the answer").is_ok());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "");
}
#[test]
fn test_comment_modifies_previous_line() {
let expected_stack = vec![
create_value("1"),
create_value("2"),
create_value("3"),
create_value("42").with_comment("the answer"),
];
let expected_undos = vec![
create_undo(4, vec![], vec!["1", "2", "3", "42"]),
create_undo(1, vec!["42"], vec!["#", "the answer"]),
];
let expected_redos = vec![];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "1 2 3 42").is_ok());
assert!(parse_line(&mut engine, &mut writer, "# the answer").is_ok());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "");
}
#[test]
fn test_comment_fails_cleanly() {
let expected_stack = vec![];
let expected_undos = vec![];
let expected_redos = vec![];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "# the answer").is_err());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "Not enough values on stack\n");
}
#[test]
fn test_unknown_operation_fails_cleanly() {
let expected_stack = create_values(vec!["1", "2", "3"]);
let expected_undos = vec![
create_undo(3, vec![], vec!["1", "2", "3"]),
];
let expected_redos = vec![];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "1 2 3").is_ok());
assert!(parse_line(&mut engine, &mut writer, "4 5 xyz 999").is_err());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "Invalid number or keyword: xyz\n");
}
#[test]
fn test_nullary_operation_modifies_stack() {
let expected_undos = vec![
create_undo(1, vec![], vec!["now"]),
];
let expected_redos = vec![];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "now").is_ok());
assert_eq!(engine.stack.len(), 1);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "");
}
#[test]
fn test_unary_operation_modifies_stack() {
let expected_stack = create_values(vec!["1", "-2"]);
let expected_undos = vec![
create_undo(3, vec![], vec!["1", "2", "3"]),
create_undo(1, vec!["2", "3"], vec!["pop", "neg"]),
];
let expected_redos = vec![];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "1 2 3").is_ok());
assert!(parse_line(&mut engine, &mut writer, "pop neg").is_ok());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "");
}
#[test]
fn test_unary_operation_fails_cleanly() {
let expected_stack = create_values(vec!["1", "2", "3"]);
let expected_undos = vec![
create_undo(3, vec![], vec!["1", "2", "3"]),
];
let expected_redos = vec![];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "1 2 3").is_ok());
assert!(parse_line(&mut engine, &mut writer, "pop pop pop neg 999").is_err());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "Not enough values on stack\n");
}
#[test]
fn test_binary_operation_modifies_stack() {
let expected_stack = create_values(vec!["1", "5"]);
let expected_undos = vec![
create_undo(3, vec![], vec!["1", "2", "3"]),
create_undo(1, vec!["2", "3"], vec!["add"]),
];
let expected_redos = vec![];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "1 2 3").is_ok());
assert!(parse_line(&mut engine, &mut writer, "add").is_ok());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "");
}
#[test]
fn test_binary_operation_fails_cleanly() {
let expected_stack = create_values(vec!["1", "2", "3"]);
let expected_undos = vec![
create_undo(3, vec![], vec!["1", "2", "3"]),
];
let expected_redos = vec![];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "1 2 3").is_ok());
assert!(parse_line(&mut engine, &mut writer, "pop pop add 999").is_err());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "Not enough values on stack\n");
}
#[test]
fn test_series_operation_modifies_stack() {
let expected_stack = create_values(vec!["6"]);
let expected_undos = vec![
create_undo(3, vec![], vec!["1", "2", "3"]),
create_undo(1, vec!["1", "2", "3"], vec!["sum"]),
];
let expected_redos = vec![];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "1 2 3").is_ok());
assert!(parse_line(&mut engine, &mut writer, "sum").is_ok());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "");
}
#[test]
fn test_clear_operation_modifies_stack() {
let expected_stack = vec![];
let expected_undos = vec![
create_undo(3, vec![], vec!["1", "2", "3"]),
create_undo(0, vec!["1", "2", "3"], vec!["clear"]),
];
let expected_redos = vec![];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "1 2 3").is_ok());
assert!(parse_line(&mut engine, &mut writer, "clear").is_ok());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "");
}
#[test]
fn test_pop_operation_modifies_stack() {
let expected_stack = create_values(vec!["1", "2"]);
let expected_undos = vec![
create_undo(3, vec![], vec!["1", "2", "3"]),
create_undo(0, vec!["3"], vec!["pop"]),
];
let expected_redos = vec![];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "1 2 3").is_ok());
assert!(parse_line(&mut engine, &mut writer, "pop").is_ok());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "");
}
#[test]
fn test_pop_operation_fails_cleanly() {
let expected_stack = create_values(vec!["1", "2", "3"]);
let expected_undos = vec![
create_undo(3, vec![], vec!["1", "2", "3"]),
];
let expected_redos = vec![];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "1 2 3").is_ok());
assert!(parse_line(&mut engine, &mut writer, "pop pop pop pop 999").is_err());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "Not enough values on stack\n");
}
#[test]
fn test_dup_operation_modifies_stack() {
let expected_stack = create_values(vec!["1", "2", "3", "3"]);
let expected_undos = vec![
create_undo(3, vec![], vec!["1", "2", "3"]),
create_undo(1, vec![], vec!["dup"]),
];
let expected_redos = vec![];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "1 2 3").is_ok());
assert!(parse_line(&mut engine, &mut writer, "dup").is_ok());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "");
}
#[test]
fn test_dup_operation_fails_cleanly() {
let expected_stack = create_values(vec!["1", "2", "3"]);
let expected_undos = vec![
create_undo(3, vec![], vec!["1", "2", "3"]),
];
let expected_redos = vec![];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "1 2 3").is_ok());
assert!(parse_line(&mut engine, &mut writer, "pop pop pop dup 999").is_err());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "Not enough values on stack\n");
}
#[test]
fn test_swap_operation_modifies_stack() {
let expected_stack = create_values(vec!["1", "3", "2"]);
let expected_undos = vec![
create_undo(3, vec![], vec!["1", "2", "3"]),
create_undo(2, vec!["2", "3"], vec!["swap"]),
];
let expected_redos = vec![];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "1 2 3").is_ok());
assert!(parse_line(&mut engine, &mut writer, "swap").is_ok());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "");
}
#[test]
fn test_swap_operation_fails_cleanly() {
let expected_stack = create_values(vec!["1", "2", "3"]);
let expected_undos = vec![
create_undo(3, vec![], vec!["1", "2", "3"]),
];
let expected_redos = vec![];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "1 2 3").is_ok());
assert!(parse_line(&mut engine, &mut writer, "pop pop swap 999").is_err());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "Not enough values on stack\n");
}
#[test]
fn test_cut_operation_copies_value() {
let expected_stack = vec![];
let expected_undos = vec![
create_undo(1, vec![], vec!["42"]),
create_undo(0, vec!["42"], vec!["cut"]),
];
let expected_redos = vec![];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "42").is_ok());
assert!(parse_line(&mut engine, &mut writer, "cut").is_ok());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(engine.clip, Some(create_value("42")));
assert_eq!(writer.buffer, "");
}
#[test]
fn test_cut_operation_fails_cleanly() {
let expected_stack = vec![];
let expected_undos = vec![];
let expected_redos = vec![];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "cut").is_err());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(engine.clip, None);
assert_eq!(writer.buffer, "Not enough values on stack\n");
}
#[test]
fn test_copy_operation_copies_value() {
let expected_stack = create_values(vec!["42"]);
let expected_undos = vec![
create_undo(1, vec![], vec!["42"]),
];
let expected_redos = vec![];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "42").is_ok());
assert!(parse_line(&mut engine, &mut writer, "copy").is_ok());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(engine.clip, Some(create_value("42")));
assert_eq!(writer.buffer, "");
}
#[test]
fn test_copy_operation_fails_cleanly() {
let expected_stack = vec![];
let expected_undos = vec![];
let expected_redos = vec![];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "copy").is_err());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(engine.clip, None);
assert_eq!(writer.buffer, "Not enough values on stack\n");
}
#[test]
fn test_paste_operation_copies_value() {
let expected_stack = create_values(vec!["42", "42"]);
let expected_undos = vec![
create_undo(1, vec![], vec!["42"]),
create_undo(1, vec![], vec!["paste"]),
];
let expected_redos = vec![];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "42").is_ok());
assert!(parse_line(&mut engine, &mut writer, "copy").is_ok());
assert!(parse_line(&mut engine, &mut writer, "paste").is_ok());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(engine.clip, Some(create_value("42")));
assert_eq!(writer.buffer, "");
}
#[test]
fn test_paste_operation_fails_cleanly() {
let expected_stack = create_values(vec!["42"]);
let expected_undos = vec![
create_undo(1, vec![], vec!["42"]),
];
let expected_redos = vec![];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "42").is_ok());
assert!(parse_line(&mut engine, &mut writer, "paste").is_err());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(engine.clip, None);
assert_eq!(writer.buffer, "No value on clipboard\n");
}
#[test]
fn test_single_undo_resets_previous_line() {
let expected_stack = create_values(vec!["4", "5", "6"]);
let expected_undos = vec![
create_undo(3, vec![], vec!["4", "5", "6"]),
];
let expected_redos = vec![
create_undo(2, vec!["11"], vec!["add"]),
];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "4 5 6").is_ok());
assert!(parse_line(&mut engine, &mut writer, "add").is_ok());
assert!(parse_line(&mut engine, &mut writer, "undo").is_ok());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "");
}
#[test]
fn test_double_undo_resets_previous_lines() {
let expected_stack = vec![];
let expected_undos = vec![];
let expected_redos = vec![
create_undo(2, vec!["11"], vec!["add"]),
create_undo(0, vec!["4", "5", "6"], vec!["4", "5", "6"]),
];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "4 5 6").is_ok());
assert!(parse_line(&mut engine, &mut writer, "add").is_ok());
assert!(parse_line(&mut engine, &mut writer, "undo undo").is_ok());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "");
}
#[test]
fn test_single_undo_with_suffix_resets_previous_line() {
let expected_stack = create_values(vec!["4", "30"]);
let expected_undos = vec![
create_undo(3, vec![], vec!["4", "5", "6"]),
create_undo(1, vec!["5", "6"], vec!["mul"]),
];
let expected_redos = vec![];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "4 5 6").is_ok());
assert!(parse_line(&mut engine, &mut writer, "add").is_ok());
assert!(parse_line(&mut engine, &mut writer, "undo mul").is_ok());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "");
}
#[test]
fn test_double_undo_with_suffix_resets_previous_lines() {
let expected_stack = create_values(vec!["1", "6"]);
let expected_undos = vec![
create_undo(3, vec![], vec!["1", "2", "3"]),
create_undo(1, vec!["2", "3"], vec!["mul"]),
];
let expected_redos = vec![];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "1 2 3").is_ok());
assert!(parse_line(&mut engine, &mut writer, "4 5 6").is_ok());
assert!(parse_line(&mut engine, &mut writer, "add").is_ok());
assert!(parse_line(&mut engine, &mut writer, "undo undo mul").is_ok());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "");
}
#[test]
fn test_single_undo_with_prefix_resets_current_line() {
let expected_stack = create_values(vec!["4", "11"]);
let expected_undos = vec![
create_undo(3, vec![], vec!["4", "5", "6"]),
create_undo(1, vec!["5", "6"], vec!["add"]),
];
let expected_redos = vec![
create_undo(2, vec!["15"], vec!["add"]),
];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "4 5 6").is_ok());
assert!(parse_line(&mut engine, &mut writer, "add").is_ok());
assert!(parse_line(&mut engine, &mut writer, "add undo").is_ok());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "");
}
#[test]
fn test_double_undo_with_prefix_resets_current_and_previous_lines() {
let expected_stack = create_values(vec!["4", "5", "6"]);
let expected_undos = vec![
create_undo(3, vec![], vec!["4", "5", "6"]),
];
let expected_redos = vec![
create_undo(2, vec!["15"], vec!["add"]),
create_undo(2, vec!["11"], vec!["add"]),
];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "4 5 6").is_ok());
assert!(parse_line(&mut engine, &mut writer, "add").is_ok());
assert!(parse_line(&mut engine, &mut writer, "add undo undo").is_ok());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "");
}
#[test]
fn test_single_undo_with_prefix_and_suffix_resets_current_line() {
let expected_stack = create_values(vec!["44"]);
let expected_undos = vec![
create_undo(3, vec![], vec!["4", "5", "6"]),
create_undo(1, vec!["5", "6"], vec!["add"]),
create_undo(1, vec!["4", "11"], vec!["mul"]),
];
let expected_redos = vec![];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "4 5 6").is_ok());
assert!(parse_line(&mut engine, &mut writer, "add").is_ok());
assert!(parse_line(&mut engine, &mut writer, "add undo mul").is_ok());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "");
}
#[test]
fn test_double_undo_with_prefix_and_suffix_resets_current_and_previous_lines() {
let expected_stack = create_values(vec!["4", "30"]);
let expected_undos = vec![
create_undo(3, vec![], vec!["4", "5", "6"]),
create_undo(1, vec!["5", "6"], vec!["mul"]),
];
let expected_redos = vec![];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "4 5 6").is_ok());
assert!(parse_line(&mut engine, &mut writer, "add").is_ok());
assert!(parse_line(&mut engine, &mut writer, "add undo undo mul").is_ok());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "");
}
#[test]
fn test_triple_undo_operation_fails_cleanly() {
let expected_stack = vec![];
let expected_undos = vec![];
let expected_redos = vec![
create_undo(2, vec!["11"], vec!["add"]),
create_undo(0, vec!["4", "5", "6"], vec!["4", "5", "6"]),
];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "4 5 6").is_ok());
assert!(parse_line(&mut engine, &mut writer, "add").is_ok());
assert!(parse_line(&mut engine, &mut writer, "undo undo undo").is_err());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "Start of undo history\n");
}
#[test]
fn test_single_redo_operation_modifies_stack() {
let expected_stack = create_values(vec!["4", "11"]);
let expected_undos = vec![
create_undo(3, vec![], vec!["4", "5", "6"]),
create_undo(1, vec!["5", "6"], vec!["add"]),
];
let expected_redos = vec![
create_undo(2, vec!["15"], vec!["add"]),
];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "4 5 6").is_ok());
assert!(parse_line(&mut engine, &mut writer, "add").is_ok());
assert!(parse_line(&mut engine, &mut writer, "add").is_ok());
assert!(parse_line(&mut engine, &mut writer, "undo undo").is_ok());
assert!(parse_line(&mut engine, &mut writer, "redo").is_ok());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "");
}
#[test]
fn test_double_redo_operation_modifies_stack() {
let expected_stack = create_values(vec!["15"]);
let expected_undos = vec![
create_undo(3, vec![], vec!["4", "5", "6"]),
create_undo(1, vec!["5", "6"], vec!["add"]),
create_undo(1, vec!["4", "11"], vec!["add"]),
];
let expected_redos = vec![];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "4 5 6").is_ok());
assert!(parse_line(&mut engine, &mut writer, "add").is_ok());
assert!(parse_line(&mut engine, &mut writer, "add").is_ok());
assert!(parse_line(&mut engine, &mut writer, "undo undo").is_ok());
assert!(parse_line(&mut engine, &mut writer, "redo redo").is_ok());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "");
}
#[test]
fn test_single_redo_operation_with_prefix_fails_cleanly() {
let expected_stack = create_values(vec!["4", "5", "6"]);
let expected_undos = vec![
create_undo(3, vec![], vec!["4", "5", "6"]),
];
let expected_redos = vec![
create_undo(2, vec!["15"], vec!["add"]),
create_undo(2, vec!["11"], vec!["add"]),
];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "4 5 6").is_ok());
assert!(parse_line(&mut engine, &mut writer, "add").is_ok());
assert!(parse_line(&mut engine, &mut writer, "add").is_ok());
assert!(parse_line(&mut engine, &mut writer, "undo undo").is_ok());
assert!(parse_line(&mut engine, &mut writer, "mul redo 999").is_err());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "End of undo history\n");
}
#[test]
fn test_triple_redo_operation_fails_cleanly() {
let expected_stack = create_values(vec!["15"]);
let expected_undos = vec![
create_undo(3, vec![], vec!["4", "5", "6"]),
create_undo(1, vec!["5", "6"], vec!["add"]),
create_undo(1, vec!["4", "11"], vec!["add"]),
];
let expected_redos = vec![];
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "4 5 6").is_ok());
assert!(parse_line(&mut engine, &mut writer, "add").is_ok());
assert!(parse_line(&mut engine, &mut writer, "add").is_ok());
assert!(parse_line(&mut engine, &mut writer, "undo undo").is_ok());
assert!(parse_line(&mut engine, &mut writer, "redo redo redo 999").is_err());
assert_eq!(engine.stack, expected_stack);
assert_eq!(engine.undos, expected_undos);
assert_eq!(engine.redos, expected_redos);
assert_eq!(writer.buffer, "End of undo history\n");
}
#[test]
fn test_prints_error_on_invalid_decimal() {
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(parse_line(&mut engine, &mut writer, "xyz").is_err());
assert_eq!(writer.buffer, "Invalid number or keyword: xyz\n");
}
#[test]
fn test_prints_error_on_invalid_hexadecimal() {
let mut engine = create_engine();
let mut writer = BufferWriter::new();
engine.context.borrow_mut().set_hexadecimal();
assert!(parse_line(&mut engine, &mut writer, "xyz").is_err());
assert_eq!(writer.buffer, "Invalid number or keyword: xyz\n");
}
#[test]
fn test_shows_history_with_lower_limit() {
let expected = "\
34
56 78 90
<==
98 76
54
";
let mut engine = create_engine();
let mut writer = BufferWriter::new();
engine.undos.push(create_undo(0, vec![], vec!["12"]));
engine.undos.push(create_undo(0, vec![], vec!["34"]));
engine.undos.push(create_undo(0, vec![], vec!["56", "78", "90"]));
engine.redos.push(create_undo(0, vec![], vec!["32", "10"]));
engine.redos.push(create_undo(0, vec![], vec!["54"]));
engine.redos.push(create_undo(0, vec![], vec!["98", "76"]));
assert!(engine.limit_history(&mut writer, 2).is_ok());
assert_eq!(writer.buffer, indent_multiline(expected));
}
#[test]
fn test_shows_history_with_higher_limit() {
let expected = "\
12
34
56 78 90
<==
98 76
54
32 10
";
let mut engine = create_engine();
let mut writer = BufferWriter::new();
engine.undos.push(create_undo(0, vec![], vec!["12"]));
engine.undos.push(create_undo(0, vec![], vec!["34"]));
engine.undos.push(create_undo(0, vec![], vec!["56", "78", "90"]));
engine.redos.push(create_undo(0, vec![], vec!["32", "10"]));
engine.redos.push(create_undo(0, vec![], vec!["54"]));
engine.redos.push(create_undo(0, vec![], vec!["98", "76"]));
assert!(engine.limit_history(&mut writer, 4).is_ok());
assert_eq!(writer.buffer, indent_multiline(expected));
}
#[test]
fn test_shows_stack_with_no_values() {
let mut engine = create_engine();
let mut writer = BufferWriter::new();
assert!(engine.show_stack(&mut writer).is_ok());
assert_eq!(writer.buffer, "");
}
#[test]
fn test_shows_stack_as_decimal_with_ratios() {
let expected = "\
99999 # first
123.45 # second
3.142 # third
3.142
";
let mut engine = create_engine();
let mut writer = BufferWriter::new();
engine.stack.push(create_value("99999").with_comment("first"));
engine.stack.push(create_value("123.45").with_comment("second"));
engine.stack.push(create_value("3.142").with_comment("third"));
engine.stack.push(create_value("3.142"));
assert!(engine.show_stack(&mut writer).is_ok());
assert_eq!(writer.buffer, indent_multiline(expected));
}
#[test]
fn test_shows_stack_as_hexadecimal_single() {
let expected = "\
0x7fffffff # first
0x00000001 # second
0x00000000 # third
0x00000000
";
let mut engine = create_engine();
let mut writer = BufferWriter::new();
engine.context.borrow_mut().separator = false;
engine.context.borrow_mut().format = Format::Base16(0);
engine.stack.push(create_value("2147483647").with_comment("first"));
engine.stack.push(create_value("1").with_comment("second"));
engine.stack.push(create_value("0").with_comment("third"));
engine.stack.push(create_value("0"));
assert!(engine.show_stack(&mut writer).is_ok());
assert_eq!(writer.buffer, indent_multiline(expected));
}
#[test]
fn test_shows_stack_as_hexadecimal_double() {
let expected = "\
0x_00000001_00000000 # first
0x_00000000_00000001 # second
0x_00000000_00000000 # third
0x_00000000_00000000
";
let mut engine = create_engine();
let mut writer = BufferWriter::new();
engine.context.borrow_mut().separator = true;
engine.context.borrow_mut().format = Format::Base16(0);
engine.stack.push(create_value("4294967296").with_comment("first"));
engine.stack.push(create_value("1").with_comment("second"));
engine.stack.push(create_value("0").with_comment("third"));
engine.stack.push(create_value("0"));
assert!(engine.show_stack(&mut writer).is_ok());
assert_eq!(writer.buffer, indent_multiline(expected));
}
fn indent_multiline(lines: &str) -> String {
lines.lines()
.map(|x| format!(" {}\n", x))
.collect::<Vec<String>>()
.join("")
}
pub fn create_value(number: &str) -> Value {
Value::from_string(number).unwrap()
}
pub fn create_values(values: Vec<&str>) -> Vec<Value> {
values.iter().map(|x| create_value(x)).collect()
}
fn create_engine() -> Engine<BufferWriter> {
let config = Config::default();
return Engine::new(config).unwrap();
}
fn parse_line(
engine: &mut Engine<BufferWriter>,
writer: &mut BufferWriter,
line: &str,
) -> MyResult<()> {
let result = engine.parse_line(writer, line);
if let Err(error) = &result {
writeln!(writer, "{}", error)?;
}
return result;
}
}