mirror of
https://github.com/Noratrieb/brainfuck.git
synced 2026-01-14 21:35:02 +01:00
interactive brainfuck repl
This commit is contained in:
parent
5cc058267b
commit
c17bea08e0
7 changed files with 168 additions and 43 deletions
|
|
@ -1,13 +1,16 @@
|
||||||
|
use crate::interpreter::optimized::PrintMode;
|
||||||
|
use std::str::Chars;
|
||||||
|
|
||||||
pub mod simple;
|
pub mod simple;
|
||||||
pub mod parsed;
|
pub mod parsed;
|
||||||
pub mod optimized;
|
pub mod optimized;
|
||||||
|
|
||||||
const MEM_SIZE: usize = 0xFFFF;
|
pub const MEM_SIZE: usize = 0xFFFF;
|
||||||
|
|
||||||
type Memory = [u8; MEM_SIZE];
|
pub type Memory = [u8; MEM_SIZE];
|
||||||
|
|
||||||
#[derive(Debug, PartialOrd, PartialEq, Ord, Eq, Clone)]
|
#[derive(Debug, PartialOrd, PartialEq, Ord, Eq, Clone)]
|
||||||
enum Statement {
|
pub enum Statement {
|
||||||
Inc,
|
Inc,
|
||||||
Dec,
|
Dec,
|
||||||
R,
|
R,
|
||||||
|
|
@ -20,11 +23,11 @@ enum Statement {
|
||||||
|
|
||||||
const ALLOWED_CHARS: [char; 8] = ['>', '<', '+', '-', '.', ',', '[', ']'];
|
const ALLOWED_CHARS: [char; 8] = ['>', '<', '+', '-', '.', ',', '[', ']'];
|
||||||
|
|
||||||
fn minify(code: &str) -> String {
|
pub fn minify(code: &str) -> String {
|
||||||
code.chars().filter(|c| ALLOWED_CHARS.contains(c)).collect()
|
code.chars().filter(|c| ALLOWED_CHARS.contains(c)).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse(chars: Vec<char>, direct_print: bool) -> Vec<Statement> {
|
pub fn parse(chars: Chars, print_mode: PrintMode) -> Vec<Statement> {
|
||||||
let mut loop_stack = vec![vec![]];
|
let mut loop_stack = vec![vec![]];
|
||||||
|
|
||||||
for c in chars {
|
for c in chars {
|
||||||
|
|
@ -34,10 +37,9 @@ fn parse(chars: Vec<char>, direct_print: bool) -> Vec<Statement> {
|
||||||
'>' => loop_stack.last_mut().unwrap().push(Statement::R),
|
'>' => loop_stack.last_mut().unwrap().push(Statement::R),
|
||||||
'<' => loop_stack.last_mut().unwrap().push(Statement::L),
|
'<' => loop_stack.last_mut().unwrap().push(Statement::L),
|
||||||
'.' => {
|
'.' => {
|
||||||
if direct_print {
|
match print_mode {
|
||||||
loop_stack.last_mut().unwrap().push(Statement::DOut)
|
PrintMode::ToString => loop_stack.last_mut().unwrap().push(Statement::Out),
|
||||||
} else {
|
PrintMode::DirectPrint => loop_stack.last_mut().unwrap().push(Statement::DOut)
|
||||||
loop_stack.last_mut().unwrap().push(Statement::Out)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
',' => loop_stack.last_mut().unwrap().push(Statement::In),
|
',' => loop_stack.last_mut().unwrap().push(Statement::In),
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ enum ExStatement {
|
||||||
Loop(Vec<ExStatement>),
|
Loop(Vec<ExStatement>),
|
||||||
SetNull,
|
SetNull,
|
||||||
Repeat(Box<ExStatement>, usize),
|
Repeat(Box<ExStatement>, usize),
|
||||||
ForLoop(usize, Box<ExStatement>),
|
_ForLoop(usize, Box<ExStatement>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Statement> for ExStatement {
|
impl From<Statement> for ExStatement {
|
||||||
|
|
@ -46,7 +46,7 @@ impl From<Statement> for ExStatement {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct BfErr {
|
pub struct BfErr {
|
||||||
msg: &'static str
|
msg: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BfErr {
|
impl BfErr {
|
||||||
|
|
@ -64,10 +64,15 @@ impl Display for BfErr {
|
||||||
impl Error for BfErr {}
|
impl Error for BfErr {}
|
||||||
|
|
||||||
|
|
||||||
pub fn run(pgm: &str, direct_print: bool) -> Result<String, BfErr> {
|
pub enum PrintMode {
|
||||||
|
ToString,
|
||||||
|
DirectPrint,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(pgm: &str, print_mode: PrintMode) -> Result<String, BfErr> {
|
||||||
let pgm = minify(pgm);
|
let pgm = minify(pgm);
|
||||||
if pgm.is_empty() { return Err(BfErr::new("no program found")); };
|
if pgm.is_empty() { return Err(BfErr::new("no program found")); };
|
||||||
let pgm = parse(pgm.chars().collect(), direct_print);
|
let pgm = parse(pgm.chars(), print_mode);
|
||||||
let pgm = optimize(&pgm);
|
let pgm = optimize(&pgm);
|
||||||
let out = interpret(&pgm);
|
let out = interpret(&pgm);
|
||||||
Ok(out)
|
Ok(out)
|
||||||
|
|
@ -177,7 +182,7 @@ fn execute(statement: &ExStatement, mem: &mut Memory, pointer: &mut usize, out:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ExStatement::ForLoop(offset, statement) => {
|
ExStatement::_ForLoop(offset, statement) => {
|
||||||
*pointer += offset;
|
*pointer += offset;
|
||||||
while mem[*pointer - offset] != 0 {
|
while mem[*pointer - offset] != 0 {
|
||||||
execute(statement, mem, pointer, out);
|
execute(statement, mem, pointer, out);
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,12 @@ use crate::interpreter::optimized::ExStatement;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Replace this: `[>>x<<-]` or `[->>x<<]` with `WhileAdd(2, x)`
|
/// Replace this: `[>>x<<-]` or `[->>x<<]` with `WhileAdd(2, x)`
|
||||||
fn for_loop(to_test: ExStatement) -> ExStatement {
|
fn _for_loop(to_test: ExStatement) -> ExStatement {
|
||||||
match to_test {
|
match to_test {
|
||||||
ExStatement::Loop(v) => {
|
ExStatement::Loop(v) => {
|
||||||
match v[..] {
|
match v[..] {
|
||||||
[ExStatement::R, ExStatement::Inc, ExStatement::L, ExStatement::Dec] => {
|
[ExStatement::R, ExStatement::Inc, ExStatement::L, ExStatement::Dec] => {
|
||||||
ExStatement::ForLoop(1, Box::from(ExStatement::Inc))
|
ExStatement::_ForLoop(1, Box::from(ExStatement::Inc))
|
||||||
}
|
}
|
||||||
_ => ExStatement::Loop(v)
|
_ => ExStatement::Loop(v)
|
||||||
}
|
}
|
||||||
|
|
@ -26,18 +26,18 @@ fn for_loop(to_test: ExStatement) -> ExStatement {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::interpreter::optimized::ExStatement::{Out, Loop, Inc, R, L, Dec, ForLoop};
|
use crate::interpreter::optimized::ExStatement::{Out, Loop, Inc, R, L, Dec, _ForLoop};
|
||||||
use crate::interpreter::optimized::patterns::for_loop;
|
use crate::interpreter::optimized::patterns::_for_loop;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn for_loop_false() {
|
fn for_loop_false() {
|
||||||
let statement = Loop(vec![Out, Inc]);
|
let statement = Loop(vec![Out, Inc]);
|
||||||
assert_eq!(statement.clone(), for_loop(statement));
|
assert_eq!(statement.clone(), _for_loop(statement));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn for_loop_simplest() {
|
fn for_loop_simplest() {
|
||||||
let statement = Loop(vec![R, Inc, L, Dec]);
|
let statement = Loop(vec![R, Inc, L, Dec]);
|
||||||
assert_eq!(ForLoop(1, Box::from(Inc)), for_loop(statement));
|
assert_eq!(_ForLoop(1, Box::from(Inc)), _for_loop(statement));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8,13 +8,16 @@
|
||||||
use std::io::{Read, stdin, Write};
|
use std::io::{Read, stdin, Write};
|
||||||
|
|
||||||
use crate::interpreter::{MEM_SIZE, Memory, minify, parse, Statement};
|
use crate::interpreter::{MEM_SIZE, Memory, minify, parse, Statement};
|
||||||
|
use crate::interpreter::optimized::PrintMode;
|
||||||
|
use crate::repl::BrainfuckState;
|
||||||
|
|
||||||
pub fn run(pgm: &str) -> String {
|
pub fn run(pgm: &str) -> String {
|
||||||
let pgm = minify(pgm);
|
let pgm = minify(pgm);
|
||||||
let pgm = parse(pgm.chars().collect(), false);
|
let pgm = parse(pgm.chars(), PrintMode::ToString);
|
||||||
interpret(&pgm)
|
interpret(&pgm)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn interpret(pgm: &[Statement]) -> String {
|
fn interpret(pgm: &[Statement]) -> String {
|
||||||
let mut out = String::new();
|
let mut out = String::new();
|
||||||
let mut pointer: usize = 0;
|
let mut pointer: usize = 0;
|
||||||
|
|
@ -27,6 +30,12 @@ fn interpret(pgm: &[Statement]) -> String {
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn interpret_with_state(pgm: &[Statement], state: &mut BrainfuckState) {
|
||||||
|
for s in pgm {
|
||||||
|
execute(s, &mut state.memory, &mut state.pointer, &mut String::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn execute(statement: &Statement, mem: &mut Memory, pointer: &mut usize, out: &mut String) {
|
fn execute(statement: &Statement, mem: &mut Memory, pointer: &mut usize, out: &mut String) {
|
||||||
match statement {
|
match statement {
|
||||||
Statement::R => if *pointer == MEM_SIZE - 1 { *pointer = 0 } else { *pointer += 1 },
|
Statement::R => if *pointer == MEM_SIZE - 1 { *pointer = 0 } else { *pointer += 1 },
|
||||||
|
|
|
||||||
|
|
@ -3,37 +3,35 @@ mod repl;
|
||||||
|
|
||||||
use std::{env, fs};
|
use std::{env, fs};
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
use crate::repl::start_repl;
|
||||||
|
use crate::interpreter::optimized::{PrintMode};
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let path = env::args().nth(1);
|
let path = env::args().nth(1);
|
||||||
let path = match path {
|
|
||||||
Some(p) => p,
|
match path {
|
||||||
None => {
|
Some(p) => {
|
||||||
println!("Please specify a path");
|
if let Err(why) = run_program(p) {
|
||||||
return;
|
eprintln!("An error occurred in the program: {}", why)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
None => start_repl()
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_program(path: String) -> Result<(), Box<dyn Error>> {
|
||||||
let program = match fs::read_to_string(path) {
|
let program = match fs::read_to_string(path) {
|
||||||
Ok(p) => p,
|
Ok(p) => p,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Error reading file: {}", e);
|
println!("Error reading file: {}", e);
|
||||||
return;
|
return Err(Box::from(e));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let start_time = SystemTime::now();
|
||||||
run(program);
|
let out = interpreter::optimized::run(&*program, PrintMode::DirectPrint)?;
|
||||||
}
|
let duration = start_time.elapsed()?;
|
||||||
|
println!("{}\nFinished execution. Took {}ms", out, duration.as_millis());
|
||||||
fn run(program: String) {
|
Ok(())
|
||||||
/*
|
|
||||||
let start1 = SystemTime::now();
|
|
||||||
let out = interpreter::o1::run(&*program);
|
|
||||||
let end1 = start1.elapsed().unwrap();*/
|
|
||||||
let start2 = SystemTime::now();
|
|
||||||
let out2 = interpreter::optimized::run(&*program, false).unwrap();
|
|
||||||
let end2 = start2.elapsed().unwrap();
|
|
||||||
//assert_eq!(out, out2);
|
|
||||||
//println!("{}\nFinished execution. Took o1: 18008ms (for hanoi), o2: {}ms", out2/*, end1.as_millis()*/, end2.as_millis());
|
|
||||||
println!("{}\nFinished execution. Took {}ms", out2/*, end1.as_millis()*/, end2.as_millis());
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
use std::fmt;
|
||||||
|
use std::io::{stdin, stdout, Write};
|
||||||
|
use crate::interpreter::{minify, parse, parsed, Memory, MEM_SIZE};
|
||||||
|
use crate::interpreter::optimized::PrintMode;
|
||||||
|
|
||||||
|
pub struct BrainfuckState {
|
||||||
|
pub memory: Memory,
|
||||||
|
pub pointer: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for BrainfuckState {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", display_state(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_state(state: &BrainfuckState) -> String {
|
||||||
|
let start = if state.pointer < 5 {
|
||||||
|
0
|
||||||
|
} else if state.pointer > MEM_SIZE - 10 {
|
||||||
|
MEM_SIZE - 10
|
||||||
|
} else {
|
||||||
|
state.pointer - 5
|
||||||
|
};
|
||||||
|
|
||||||
|
format!("{}-\n|{}|\n{}-\n{}|\n{}|\n{}|\n{}-\n {}^^^^",
|
||||||
|
"----------".repeat(10),
|
||||||
|
{
|
||||||
|
let mut out = String::new();
|
||||||
|
let end = start + 10;
|
||||||
|
for i in start..end {
|
||||||
|
out.push_str(&*format!(" {: >5} ", i));
|
||||||
|
}
|
||||||
|
out
|
||||||
|
},
|
||||||
|
"----------".repeat(10),
|
||||||
|
"| ".repeat(10),
|
||||||
|
{
|
||||||
|
let mut out = String::new();
|
||||||
|
let end = start + 10;
|
||||||
|
for i in start..end {
|
||||||
|
out.push_str(&*format!("| {: >3} ", state.memory[i]));
|
||||||
|
}
|
||||||
|
out
|
||||||
|
},
|
||||||
|
"| ".repeat(10),
|
||||||
|
"----------".repeat(10),
|
||||||
|
" ".repeat(state.pointer - start))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start_repl() {
|
||||||
|
println!("Brainfuck REPL");
|
||||||
|
|
||||||
|
let mut state = BrainfuckState {
|
||||||
|
memory: [0; MEM_SIZE],
|
||||||
|
pointer: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("Enter Brainfuck programs and they will be executed immediatly.");
|
||||||
|
println!("State is kept.");
|
||||||
|
println!("{}", state);
|
||||||
|
loop {
|
||||||
|
print!(">> ");
|
||||||
|
stdout().flush().unwrap();
|
||||||
|
match read_line() {
|
||||||
|
Ok(s) => {
|
||||||
|
match &*s {
|
||||||
|
":q" => break,
|
||||||
|
":?" | "help" | "?" => print_help(),
|
||||||
|
":r" => {
|
||||||
|
reset(&mut state);
|
||||||
|
println!("{}", state);
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
print!("Output: ");
|
||||||
|
println!();
|
||||||
|
parse_input(s, &mut state);
|
||||||
|
println!("{}", state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(why) => println!("Error reading input: {}\nPlease try again.", why)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(state: &mut BrainfuckState) {
|
||||||
|
state.pointer = 0;
|
||||||
|
state.memory = [0; MEM_SIZE];
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_help() {
|
||||||
|
println!("Brainfuck REPL help
|
||||||
|
:q => quit
|
||||||
|
:? => help
|
||||||
|
:r => reset state");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_input(pgm: String, state: &mut BrainfuckState) {
|
||||||
|
let pgm = minify(&*pgm);
|
||||||
|
let pgm = parse(pgm.chars(), PrintMode::DirectPrint);
|
||||||
|
parsed::interpret_with_state(&*pgm, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_line() -> Result<String, std::io::Error> {
|
||||||
|
let mut buf = String::new();
|
||||||
|
stdin().read_line(&mut buf)?;
|
||||||
|
buf.pop();
|
||||||
|
Ok(buf)
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue