Suport history movment in the repl 🤓
This commit is contained in:
parent
3aaf2e9e6d
commit
472496800b
6 changed files with 303 additions and 90 deletions
|
@ -39,6 +39,7 @@ log = "0.4.17"
|
||||||
pretty_env_logger = "0.4.0"
|
pretty_env_logger = "0.4.0"
|
||||||
colored = "2.0.0"
|
colored = "2.0.0"
|
||||||
# no-panic = "0.1.16"
|
# no-panic = "0.1.16"
|
||||||
|
console = "0.15.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "1.3.0"
|
pretty_assertions = "1.3.0"
|
135
src/bf_interpreter/cell.rs
Normal file
135
src/bf_interpreter/cell.rs
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
use crate::arguments::Feature;
|
||||||
|
use crate::bf_interpreter::error::{InterpreterError, InterpreterErrorKind};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub enum Cell {
|
||||||
|
Byte(u8),
|
||||||
|
Utf8(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cell {
|
||||||
|
pub fn set_value_utf8(&mut self, ch: char) {
|
||||||
|
match self {
|
||||||
|
Cell::Byte(_) => {}
|
||||||
|
Cell::Utf8(p) => {
|
||||||
|
*p = ch as u32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_value(&mut self, ch: char) {
|
||||||
|
match self {
|
||||||
|
Cell::Byte(p) => {
|
||||||
|
*p = ch as u8;
|
||||||
|
}
|
||||||
|
Cell::Utf8(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cell {
|
||||||
|
pub fn default_cell(future: &Vec<Feature>) -> Self {
|
||||||
|
if future.contains(&Feature::AllowUtf8) {
|
||||||
|
Cell::Utf8(0)
|
||||||
|
} else {
|
||||||
|
Cell::Byte(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(value: u32, future: &Vec<Feature>) -> Self {
|
||||||
|
if future.contains(&Feature::AllowUtf8) {
|
||||||
|
Cell::Utf8(value)
|
||||||
|
} else {
|
||||||
|
Cell::Byte(value as u8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_value(&self) -> u8 {
|
||||||
|
match self {
|
||||||
|
Self::Byte(value) => *value,
|
||||||
|
Self::Utf8(value) => *value as u8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_value_utf8(&self) -> u32 {
|
||||||
|
match self {
|
||||||
|
Self::Byte(value) => *value as u32,
|
||||||
|
Self::Utf8(value) => *value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn increment(&mut self, no_reverse_value: bool) -> Result<(), InterpreterError> {
|
||||||
|
if self.get_value_utf8() == self.max_value() && no_reverse_value {
|
||||||
|
return Err(InterpreterErrorKind::ValueOutOfBounds.to_error())
|
||||||
|
}
|
||||||
|
match self {
|
||||||
|
Self::Byte(value) => {
|
||||||
|
if *value == 255 {
|
||||||
|
*value = 0;
|
||||||
|
} else {
|
||||||
|
*value += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::Utf8(value) => {
|
||||||
|
if *value == 1114111 {
|
||||||
|
*value = 0;
|
||||||
|
} else {
|
||||||
|
*value += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decrement(&mut self, no_reverse_value: bool) -> Result<(), InterpreterError> {
|
||||||
|
if self.get_value_utf8() == 0 && no_reverse_value {
|
||||||
|
return Err(InterpreterErrorKind::ValueOutOfBounds.to_error())
|
||||||
|
}
|
||||||
|
match self {
|
||||||
|
Self::Byte(value) => {
|
||||||
|
if *value == 0 {
|
||||||
|
*value = 255;
|
||||||
|
} else {
|
||||||
|
*value -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::Utf8(value) => {
|
||||||
|
if *value == 0 {
|
||||||
|
*value = 1114111;
|
||||||
|
} else {
|
||||||
|
*value -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn max_value(&self) -> u32 {
|
||||||
|
match self {
|
||||||
|
Self::Byte(_) => u8::MAX as u32,
|
||||||
|
Self::Utf8(_) => u32::MAX,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_char(&self) -> Result<char, InterpreterError> {
|
||||||
|
let c = match self {
|
||||||
|
Self::Byte(value) => Some(*value as char),
|
||||||
|
Self::Utf8(value) => char::from_u32(*value)
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(c) = c {
|
||||||
|
Ok(c)
|
||||||
|
} else {
|
||||||
|
Err(InterpreterErrorKind::InvalidUtf8.to_error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Cell {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Byte(value) => write!(f, "{}", value),
|
||||||
|
Self::Utf8(value) => write!(f, "{}", value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,24 +1,31 @@
|
||||||
use crate::arguments;
|
use crate::arguments;
|
||||||
use crate::bf_interpreter::error::{InterpreterError, InterpreterErrorKind};
|
use crate::bf_interpreter::error::{InterpreterError, InterpreterErrorKind};
|
||||||
use std::io::{Read, Write};
|
use std::io::{Write};
|
||||||
use std::{char, usize, vec};
|
use std::{char, usize, vec};
|
||||||
|
use crate::bf_interpreter::cell::Cell;
|
||||||
|
|
||||||
pub struct Interpreter {
|
pub struct Interpreter {
|
||||||
pub cells: Vec<u8>,
|
pub cells: Vec<Cell>,
|
||||||
pub pointer: usize,
|
pub pointer: usize,
|
||||||
pub bf_commands: Vec<BfCommand>,
|
pub bf_commands: Vec<BfCommand>,
|
||||||
brackets: Vec<BfCommand>,
|
brackets: Vec<BfCommand>,
|
||||||
pub features: Vec<arguments::Feature>,
|
pub features: Vec<arguments::Feature>,
|
||||||
|
term: console::Term,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interpreter {
|
impl Interpreter {
|
||||||
pub fn new(array_size: usize, features: Vec<arguments::Feature>) -> Self {
|
pub fn new(
|
||||||
|
array_size: usize,
|
||||||
|
features: Vec<arguments::Feature>,
|
||||||
|
term: &console::Term,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
cells: vec![0; array_size],
|
cells: vec![Cell::default_cell(&features); array_size],
|
||||||
pointer: 0,
|
pointer: 0,
|
||||||
bf_commands: vec![],
|
bf_commands: vec![],
|
||||||
brackets: Vec::new(),
|
brackets: Vec::new(),
|
||||||
features,
|
features,
|
||||||
|
term: term.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +41,7 @@ impl Interpreter {
|
||||||
// +[>++<-]
|
// +[>++<-]
|
||||||
fn iterate(&mut self, code: &Vec<BfCommand>) -> Result<(), InterpreterError> {
|
fn iterate(&mut self, code: &Vec<BfCommand>) -> Result<(), InterpreterError> {
|
||||||
trace!("Iterate: {:?}", code);
|
trace!("Iterate: {:?}", code);
|
||||||
while self.cells[self.pointer] != 0 {
|
while self.cells[self.pointer].get_value_utf8() != 0 {
|
||||||
self.run_brainfuck_code(code)?;
|
self.run_brainfuck_code(code)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -85,29 +92,15 @@ impl Interpreter {
|
||||||
|
|
||||||
fn increment_value(&mut self) -> Result<(), InterpreterError> {
|
fn increment_value(&mut self) -> Result<(), InterpreterError> {
|
||||||
trace!("Increment value");
|
trace!("Increment value");
|
||||||
if self.cells[self.pointer] == 255 {
|
self.cells[self.pointer].increment(
|
||||||
if !self.features.contains(&arguments::Feature::NoReverseValue) {
|
!self.features.contains(&arguments::Feature::NoReverseValue))?;
|
||||||
self.cells[self.pointer] = 0;
|
|
||||||
} else {
|
|
||||||
return Err(InterpreterErrorKind::ValueOutOfBounds.to_error());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.cells[self.pointer] += 1;
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decrement_value(&mut self) -> Result<(), InterpreterError> {
|
fn decrement_value(&mut self) -> Result<(), InterpreterError> {
|
||||||
trace!("Decrement value");
|
trace!("Decrement value");
|
||||||
if self.cells[self.pointer] == 0 {
|
self.cells[self.pointer].decrement(
|
||||||
if !self.features.contains(&arguments::Feature::NoReverseValue) {
|
!self.features.contains(&arguments::Feature::NoReverseValue))?;
|
||||||
self.cells[self.pointer] = 255;
|
|
||||||
} else {
|
|
||||||
return Err(InterpreterErrorKind::ValueOutOfBounds.to_error());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.cells[self.pointer] -= 1;
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,13 +108,13 @@ impl Interpreter {
|
||||||
trace!("Output value");
|
trace!("Output value");
|
||||||
|
|
||||||
if self.features.contains(&arguments::Feature::AllowUtf8) {
|
if self.features.contains(&arguments::Feature::AllowUtf8) {
|
||||||
let c = char::from_u32(self.cells[self.pointer] as u32);
|
let c = char::from_u32(self.cells[self.pointer].get_value_utf8());
|
||||||
match c {
|
match c {
|
||||||
Some(c) => print!("{}", c),
|
Some(c) => print!("{}", c),
|
||||||
None => return Err(InterpreterErrorKind::InvalidUtf8.to_error()),
|
None => return Err(InterpreterErrorKind::InvalidUtf8.to_error()),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
print!("{}", self.cells[self.pointer] as char);
|
print!("{}", self.cells[self.pointer].get_value() as char);
|
||||||
}
|
}
|
||||||
match std::io::stdout().flush() {
|
match std::io::stdout().flush() {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
|
@ -131,18 +124,25 @@ impl Interpreter {
|
||||||
|
|
||||||
fn input_value(&mut self) -> Result<(), InterpreterError> {
|
fn input_value(&mut self) -> Result<(), InterpreterError> {
|
||||||
trace!("Input value");
|
trace!("Input value");
|
||||||
let mut input = [0; 1];
|
match self.term.read_char() {
|
||||||
match std::io::stdin().read_exact(&mut input) {
|
Ok(ch) => {
|
||||||
Ok(_) => {
|
if self.features.contains(&arguments::Feature::AllowUtf8) {
|
||||||
self.cells[self.pointer] = input[0];
|
self.cells[self.pointer].set_value_utf8(ch);
|
||||||
Ok(())
|
} else {
|
||||||
|
self.cells[self.pointer].set_value(ch);
|
||||||
|
}
|
||||||
|
print!("{}", ch);
|
||||||
|
match std::io::stdout().flush() {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e) => Err(InterpreterErrorKind::FlushError(e).to_error()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(e) => Err(InterpreterErrorKind::IoError(e).to_error()),
|
Err(e) => Err(InterpreterErrorKind::IoError(e).to_error()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset(&mut self) {
|
pub fn reset(&mut self) {
|
||||||
self.cells = vec![0; self.cells.len()];
|
self.cells = vec![Cell::default_cell(&self.features); self.cells.len()];
|
||||||
self.pointer = 0;
|
self.pointer = 0;
|
||||||
self.brackets = Vec::new();
|
self.brackets = Vec::new();
|
||||||
self.bf_commands = Vec::new();
|
self.bf_commands = Vec::new();
|
||||||
|
@ -217,7 +217,8 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn print_h_combine_repl() {
|
fn print_h_combine_repl() {
|
||||||
let mut interpreter = Interpreter::new(30000, vec![]);
|
let mut interpreter = Interpreter::new(30000, vec![],
|
||||||
|
&console::Term::stdout());
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
interpreter.run(String::from(">+++++++++[<++++ ++++>-]<.")),
|
interpreter.run(String::from(">+++++++++[<++++ ++++>-]<.")),
|
||||||
|
@ -229,7 +230,8 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn print_h_repl() {
|
fn print_h_repl() {
|
||||||
let mut interpreter = Interpreter::new(30000, vec![]);
|
let mut interpreter = Interpreter::new(30000, vec![],
|
||||||
|
&console::Term::stdout());
|
||||||
|
|
||||||
assert_eq!(interpreter.run(String::from(">+++++++++")), Ok(0));
|
assert_eq!(interpreter.run(String::from(">+++++++++")), Ok(0));
|
||||||
assert_eq!(interpreter.run(String::from("[<++++ ++++>-]<.")), Ok(0));
|
assert_eq!(interpreter.run(String::from("[<++++ ++++>-]<.")), Ok(0));
|
||||||
|
@ -239,17 +241,19 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn nested_loop_level_1_combine() {
|
fn nested_loop_level_1_combine() {
|
||||||
let mut interpreter = Interpreter::new(5, vec![]);
|
let mut interpreter = Interpreter::new(5, vec![],
|
||||||
|
&console::Term::stdout());
|
||||||
|
|
||||||
assert_eq!(interpreter.run(String::from("++[>++[>+<-]<-]")), Ok(0));
|
assert_eq!(interpreter.run(String::from("++[>++[>+<-]<-]")), Ok(0));
|
||||||
assert_eq!(interpreter.cells[2], 4);
|
assert_eq!(interpreter.cells[2], Cell::new(4, &vec![]));
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn execute_hello_world_from_file() {
|
fn execute_hello_world_from_file() {
|
||||||
let mut interpreter = Interpreter::new(30000, vec![]);
|
let mut interpreter = Interpreter::new(30000, vec![],
|
||||||
|
&console::Term::stdout());
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
|
@ -263,7 +267,8 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn execute_print_hi_from_file() {
|
fn execute_print_hi_from_file() {
|
||||||
let mut interpreter = Interpreter::new(30000, vec![]);
|
let mut interpreter = Interpreter::new(30000, vec![],
|
||||||
|
&console::Term::stdout());
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
|
@ -277,7 +282,8 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn execute_print_hi_yooo_from_file() {
|
fn execute_print_hi_yooo_from_file() {
|
||||||
let mut interpreter = Interpreter::new(30000, vec![]);
|
let mut interpreter = Interpreter::new(30000, vec![],
|
||||||
|
&console::Term::stdout());
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
|
@ -291,7 +297,8 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn execute_print_my_first_name_from_formatted_file() {
|
fn execute_print_my_first_name_from_formatted_file() {
|
||||||
let mut interpreter = Interpreter::new(30000, vec![]);
|
let mut interpreter = Interpreter::new(30000, vec![],
|
||||||
|
&console::Term::stdout());
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
|
@ -305,7 +312,8 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn execute_print_my_first_name_from_file() {
|
fn execute_print_my_first_name_from_file() {
|
||||||
let mut interpreter = Interpreter::new(30000, vec![]);
|
let mut interpreter = Interpreter::new(30000, vec![],
|
||||||
|
&console::Term::stdout());
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
|
@ -319,7 +327,8 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn execute_print_my_first_name_and_last_name_from_formatted_file() {
|
fn execute_print_my_first_name_and_last_name_from_formatted_file() {
|
||||||
let mut interpreter = Interpreter::new(30000, vec![]);
|
let mut interpreter = Interpreter::new(30000, vec![],
|
||||||
|
&console::Term::stdout());
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
|
@ -333,7 +342,8 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn execute_print_my_first_name_and_last_name_from_file() {
|
fn execute_print_my_first_name_and_last_name_from_file() {
|
||||||
let mut interpreter = Interpreter::new(30000, vec![]);
|
let mut interpreter = Interpreter::new(30000, vec![],
|
||||||
|
&console::Term::stdout());
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
|
@ -347,21 +357,22 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn reset() {
|
fn reset() {
|
||||||
let mut interpreter = Interpreter::new(30000, vec![]);
|
let mut interpreter = Interpreter::new(30000, vec![],
|
||||||
|
&console::Term::stdout());
|
||||||
|
|
||||||
assert_eq!(interpreter.run(String::from(">++++")), Ok(0));
|
assert_eq!(interpreter.run(String::from(">++++")), Ok(0));
|
||||||
|
|
||||||
assert_eq!(interpreter.pointer, 1);
|
assert_eq!(interpreter.pointer, 1);
|
||||||
assert_eq!(interpreter.cells[0], 0);
|
assert_eq!(interpreter.cells[0], Cell::new(0, &vec![]));
|
||||||
assert_eq!(interpreter.cells[1], 4);
|
assert_eq!(interpreter.cells[1], Cell::new(4, &vec![]));
|
||||||
// assert_eq!(interpreter.commands, vec!['>', '+', '+', '+', '+']);
|
// assert_eq!(interpreter.commands, vec!['>', '+', '+', '+', '+']);
|
||||||
|
|
||||||
// reset
|
// reset
|
||||||
interpreter.reset();
|
interpreter.reset();
|
||||||
|
|
||||||
assert_eq!(interpreter.pointer, 0);
|
assert_eq!(interpreter.pointer, 0);
|
||||||
assert_eq!(interpreter.cells[0], 0);
|
assert_eq!(interpreter.cells[0], Cell::new(0, &vec![]));
|
||||||
assert_eq!(interpreter.cells[1], 0);
|
assert_eq!(interpreter.cells[1], Cell::new(0, &vec![]));
|
||||||
assert_eq!(interpreter.bf_commands, Vec::<BfCommand>::new());
|
assert_eq!(interpreter.bf_commands, Vec::<BfCommand>::new());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod interpreter;
|
pub mod interpreter;
|
||||||
|
pub mod cell;
|
||||||
|
|
|
@ -19,9 +19,13 @@ fn main() {
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
info!("Parsed command line arguments: {:?}", args);
|
info!("Parsed command line arguments: {:?}", args);
|
||||||
|
|
||||||
|
let term = console::Term::stdout();
|
||||||
|
|
||||||
info!("Initializing interpreter");
|
info!("Initializing interpreter");
|
||||||
let mut interpreter =
|
let mut interpreter =
|
||||||
Interpreter::new(args.array_size, args.features.unwrap_or_else(|| vec![]));
|
Interpreter::new(args.array_size,
|
||||||
|
args.features.unwrap_or_else(|| vec![]),
|
||||||
|
&term);
|
||||||
|
|
||||||
match args.source {
|
match args.source {
|
||||||
Some(source) => {
|
Some(source) => {
|
||||||
|
@ -56,6 +60,6 @@ fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => repl::start(interpreter),
|
None => repl::start(interpreter, term),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
143
src/repl.rs
143
src/repl.rs
|
@ -1,9 +1,11 @@
|
||||||
use crate::bf_interpreter::interpreter::Interpreter;
|
use crate::bf_interpreter::interpreter::Interpreter;
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
use console::{Term, Key};
|
||||||
|
|
||||||
struct Repl {
|
struct Repl {
|
||||||
pub interpreter: Interpreter,
|
pub interpreter: Interpreter,
|
||||||
|
term: console::Term,
|
||||||
history: Vec<String>,
|
history: Vec<String>,
|
||||||
loop_body: String,
|
loop_body: String,
|
||||||
loop_depth: usize,
|
loop_depth: usize,
|
||||||
|
@ -14,9 +16,10 @@ const HISTORY_FILE: &str = "bf-interpreter-history.bfr";
|
||||||
const COMMAND_PREFIX: &str = "!";
|
const COMMAND_PREFIX: &str = "!";
|
||||||
|
|
||||||
impl Repl {
|
impl Repl {
|
||||||
pub fn new(interpreter: Interpreter) -> Repl {
|
pub fn new(interpreter: Interpreter, term: Term) -> Repl {
|
||||||
Repl {
|
Repl {
|
||||||
interpreter,
|
interpreter,
|
||||||
|
term,
|
||||||
history: Vec::new(),
|
history: Vec::new(),
|
||||||
loop_body: String::new(),
|
loop_body: String::new(),
|
||||||
loop_depth: 0,
|
loop_depth: 0,
|
||||||
|
@ -26,33 +29,85 @@ impl Repl {
|
||||||
// #[no_panic]
|
// #[no_panic]
|
||||||
pub fn run(mut self) -> Result<(), std::io::Error> {
|
pub fn run(mut self) -> Result<(), std::io::Error> {
|
||||||
loop {
|
loop {
|
||||||
print!(
|
self.print_prompt();
|
||||||
"{}",
|
|
||||||
if self.loop_depth != 0 {
|
|
||||||
"........ ".yellow()
|
|
||||||
} else {
|
|
||||||
PROMPT.to_string().truecolor(54, 76, 76)
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
std::io::stdout().flush()?;
|
std::io::stdout().flush()?;
|
||||||
|
|
||||||
let mut user_input = String::new();
|
match self.read_input() {
|
||||||
|
Ok(input) => {
|
||||||
|
let user_input = input.trim().to_string(); // Remove trailing newline
|
||||||
|
|
||||||
match std::io::stdin().read_line(&mut user_input) {
|
if !user_input.is_empty() && user_input.len() > 0 {
|
||||||
Ok(_) => {}
|
self.history.push(user_input.clone()); // Save input to history
|
||||||
|
self.process(user_input); // Process the input
|
||||||
|
}
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Failed to read input: {}", e);
|
eprintln!("Error: {}", e);
|
||||||
std::process::exit(1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
user_input = user_input.trim().to_string(); // Remove trailing newline
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !user_input.is_empty() && user_input.len() > 0 {
|
fn print_prompt(&self) {
|
||||||
self.history.push(user_input.clone()); // Save input to history
|
print!(
|
||||||
self.process(user_input); // Process the input
|
"{}",
|
||||||
|
if self.loop_depth != 0 {
|
||||||
|
"........ ".yellow()
|
||||||
|
} else {
|
||||||
|
PROMPT.to_string().truecolor(54, 76, 76)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_input(&mut self) -> Result<String, std::io::Error> {
|
||||||
|
let mut input = String::new();
|
||||||
|
let mut rev_index = 0;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let key = self.term.read_key()?; // Read key from terminal
|
||||||
|
|
||||||
|
match key {
|
||||||
|
Key::ArrowUp => {
|
||||||
|
if !self.history.is_empty() && rev_index < self.history.len() {
|
||||||
|
let last = self.history.get(self.history.len() - 1 - rev_index)
|
||||||
|
.unwrap();
|
||||||
|
rev_index += 1;
|
||||||
|
self.term.clear_line()?;
|
||||||
|
self.print_prompt();
|
||||||
|
self.term.write_str(last)?;
|
||||||
|
input = last.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Key::ArrowDown => {
|
||||||
|
if !self.history.is_empty() && rev_index > 0 {
|
||||||
|
let first = self.history.get(self.history.len() - rev_index)
|
||||||
|
.unwrap();
|
||||||
|
rev_index -= 1;
|
||||||
|
self.term.clear_line()?;
|
||||||
|
self.print_prompt();
|
||||||
|
self.term.write_str(first)?;
|
||||||
|
input = first.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Key::Char(c) => {
|
||||||
|
self.term.write_str(&c.to_string())?;
|
||||||
|
input.push(c);
|
||||||
|
}
|
||||||
|
Key::Backspace => {
|
||||||
|
self.term.clear_line()?;
|
||||||
|
self.term.write_str(&input[0..input.len() - 1])?;
|
||||||
|
self.term.move_cursor_left(1)?;
|
||||||
|
input.pop();
|
||||||
|
}
|
||||||
|
Key::Enter => {
|
||||||
|
self.term.write_str("\n")?;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process(&mut self, mut user_input: String) {
|
pub fn process(&mut self, mut user_input: String) {
|
||||||
|
@ -140,7 +195,7 @@ impl Repl {
|
||||||
println!(
|
println!(
|
||||||
"Current pointer value: {} = \'{}\' (char)",
|
"Current pointer value: {} = \'{}\' (char)",
|
||||||
self.interpreter.cells[self.interpreter.pointer],
|
self.interpreter.cells[self.interpreter.pointer],
|
||||||
self.interpreter.cells[self.interpreter.pointer] as char
|
self.interpreter.cells[self.interpreter.pointer].to_char().unwrap_or_else(|_| '?')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
"history" | "h" => {
|
"history" | "h" => {
|
||||||
|
@ -233,7 +288,7 @@ impl Repl {
|
||||||
user_input,
|
user_input,
|
||||||
(COMMAND_PREFIX.to_string() + "help").green()
|
(COMMAND_PREFIX.to_string() + "help").green()
|
||||||
)
|
)
|
||||||
.red()
|
.red()
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -245,7 +300,7 @@ impl Repl {
|
||||||
/// Run the REPL
|
/// Run the REPL
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `interpreter` - The interpreter to use
|
/// * `interpreter` - The interpreter to use
|
||||||
pub fn start(interpreter: Interpreter) {
|
pub fn start(interpreter: Interpreter, term: Term) {
|
||||||
info!("Entering REPL mode");
|
info!("Entering REPL mode");
|
||||||
println!(
|
println!(
|
||||||
"{}\n\
|
"{}\n\
|
||||||
|
@ -263,7 +318,7 @@ pub fn start(interpreter: Interpreter) {
|
||||||
(COMMAND_PREFIX.to_string() + "help").bold().green(),
|
(COMMAND_PREFIX.to_string() + "help").bold().green(),
|
||||||
);
|
);
|
||||||
|
|
||||||
match Repl::new(interpreter).run() {
|
match Repl::new(interpreter, term).run() {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
info!("Successfully ran REPL");
|
info!("Successfully ran REPL");
|
||||||
}
|
}
|
||||||
|
@ -278,12 +333,14 @@ pub fn start(interpreter: Interpreter) {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
use crate::bf_interpreter::cell::Cell;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn nested_loop_level_1() {
|
fn nested_loop_level_1() {
|
||||||
let interpreter = Interpreter::new(4, vec![]);
|
let mut term = Term::stdout();
|
||||||
|
let interpreter = Interpreter::new(4, vec![], &mut term);
|
||||||
|
|
||||||
let mut repl = Repl::new(interpreter);
|
let mut repl = Repl::new(interpreter, term);
|
||||||
|
|
||||||
repl.process("++".to_string());
|
repl.process("++".to_string());
|
||||||
repl.process("[>++".to_string());
|
repl.process("[>++".to_string());
|
||||||
|
@ -292,16 +349,17 @@ mod tests {
|
||||||
|
|
||||||
let cells = &repl.interpreter.cells;
|
let cells = &repl.interpreter.cells;
|
||||||
|
|
||||||
assert_eq!(cells[0], 0);
|
assert_eq!(cells[0], Cell::default_cell(&vec![]));
|
||||||
assert_eq!(cells[1], 0);
|
assert_eq!(cells[1], Cell::default_cell(&vec![]));
|
||||||
assert_eq!(cells[2], 4);
|
assert_eq!(cells[2], Cell::new(4, &vec![]));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn nested_loop_level_2() {
|
fn nested_loop_level_2() {
|
||||||
let interpreter = Interpreter::new(4, vec![]);
|
let mut term = console::Term::stdout();
|
||||||
|
let interpreter = Interpreter::new(4, vec![], &mut term);
|
||||||
|
|
||||||
let mut repl = Repl::new(interpreter);
|
let mut repl = Repl::new(interpreter, term);
|
||||||
|
|
||||||
repl.process("++".to_string());
|
repl.process("++".to_string());
|
||||||
repl.process("[>++".to_string());
|
repl.process("[>++".to_string());
|
||||||
|
@ -313,16 +371,17 @@ mod tests {
|
||||||
|
|
||||||
let cells = &repl.interpreter.cells;
|
let cells = &repl.interpreter.cells;
|
||||||
|
|
||||||
assert_eq!(cells[0], 0);
|
assert_eq!(cells[0], Cell::default_cell(&vec![]));
|
||||||
assert_eq!(cells[1], 0);
|
assert_eq!(cells[1], Cell::default_cell(&vec![]));
|
||||||
assert_eq!(cells[2], 4);
|
assert_eq!(cells[2], Cell::new(4, &vec![]));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn print_my_first_name() {
|
fn print_my_first_name() {
|
||||||
let interpreter = Interpreter::new(10, vec![]);
|
let mut term = console::Term::stdout();
|
||||||
|
let interpreter = Interpreter::new(10, vec![], &mut term);
|
||||||
|
|
||||||
let mut repl = Repl::new(interpreter);
|
let mut repl = Repl::new(interpreter, term);
|
||||||
|
|
||||||
let code = "++++ ++++ 8
|
let code = "++++ ++++ 8
|
||||||
[
|
[
|
||||||
|
@ -384,9 +443,10 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn print_my_first_name_in_one_command() {
|
fn print_my_first_name_in_one_command() {
|
||||||
let interpreter = Interpreter::new(10, vec![]);
|
let mut term = console::Term::stdout();
|
||||||
|
let interpreter = Interpreter::new(10, vec![], &mut term);
|
||||||
|
|
||||||
let mut repl = Repl::new(interpreter);
|
let mut repl = Repl::new(interpreter, term);
|
||||||
|
|
||||||
let code = "++++++++[>++++[>++<-]>>>>>>++[<<<->>>-]<<<<<<<-]>>+.<<++++[>+++
|
let code = "++++++++[>++++[>++<-]>>>>>>++[<<<->>>-]<<<<<<<-]>>+.<<++++[>+++
|
||||||
[>+++<-]>++<<-]>>+.<<+++[>+++[>-<-]>-<<-]>>-.<<++++++[>>+++<<-]>>."
|
[>+++<-]>++<<-]>>+.<<+++[>+++[>-<-]>-<<-]>>-.<<++++++[>>+++<<-]>>."
|
||||||
|
@ -397,9 +457,10 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn print_hello_world() {
|
fn print_hello_world() {
|
||||||
let interpreter = Interpreter::new(10, vec![]);
|
let mut term = console::Term::stdout();
|
||||||
|
let interpreter = Interpreter::new(10, vec![], &mut term);
|
||||||
|
|
||||||
let mut repl = Repl::new(interpreter);
|
let mut repl = Repl::new(interpreter, term);
|
||||||
|
|
||||||
let _ = "[ This program prints \"Hello World!\" and a newline to the screen, its
|
let _ = "[ This program prints \"Hello World!\" and a newline to the screen, its
|
||||||
length is 106 active command characters. [It is not the shortest.]
|
length is 106 active command characters. [It is not the shortest.]
|
||||||
|
@ -438,8 +499,8 @@ mod tests {
|
||||||
>>+. Add 1 to Cell #5 gives us an exclamation point
|
>>+. Add 1 to Cell #5 gives us an exclamation point
|
||||||
>++. And finally a newline from Cell #6
|
>++. And finally a newline from Cell #6
|
||||||
"
|
"
|
||||||
.to_string()
|
.to_string()
|
||||||
.split("\n")
|
.split("\n")
|
||||||
.for_each(|s| repl.process(s.to_string()));
|
.for_each(|s| repl.process(s.to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue