commit
060e00daff
3 changed files with 162 additions and 0 deletions
@ -0,0 +1,10 @@ |
|||||||
|
[package] |
||||||
|
name = "brainfuck" |
||||||
|
version = "0.1.0" |
||||||
|
edition = "2021" |
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
||||||
|
|
||||||
|
[dependencies] |
||||||
|
clap = { version = "3.2.17", features = ["derive"] } |
||||||
|
atty = "0.2.14" |
@ -0,0 +1,149 @@ |
|||||||
|
use clap::CommandFactory; |
||||||
|
use clap::Parser; |
||||||
|
use std::io::Write; |
||||||
|
use std::{path::PathBuf, io::{self, Read, BufReader, BufRead}}; |
||||||
|
|
||||||
|
#[derive(Parser, Debug)] |
||||||
|
#[clap(author, version, about, long_about = None)] |
||||||
|
struct Args { |
||||||
|
#[clap(value_parser, value_name = "FILE")] |
||||||
|
file: Option<PathBuf>, |
||||||
|
|
||||||
|
#[clap(short, long, action)] |
||||||
|
interactive: bool, |
||||||
|
|
||||||
|
#[clap(short, long, action)] |
||||||
|
verbose: bool, |
||||||
|
} |
||||||
|
|
||||||
|
fn main() -> Result<(), io::Error> { |
||||||
|
//clap magic
|
||||||
|
let args = Args::parse(); |
||||||
|
let mut cmd = Args::command(); |
||||||
|
|
||||||
|
let mut reader: Box<dyn BufRead> = match &args.file { |
||||||
|
None => { |
||||||
|
if atty::is(atty::Stream::Stdin) && !args.interactive { |
||||||
|
//special case when there is no piped file
|
||||||
|
//and --interactive is not specified
|
||||||
|
//display the help and exit
|
||||||
|
cmd.print_help()?; |
||||||
|
return Ok(()); |
||||||
|
} |
||||||
|
Box::new(BufReader::new(io::stdin())) |
||||||
|
}, |
||||||
|
Some(filepath) => Box::new(BufReader::new(std::fs::File::open(filepath)?)) |
||||||
|
}; |
||||||
|
|
||||||
|
//init
|
||||||
|
let actions = std::collections::HashSet::from(['>', '<', '+', '-', '.', ',', '[', ']']); |
||||||
|
let mut memory = [0_u8; 30000]; |
||||||
|
let mut pointer = 0; |
||||||
|
let mut instructions_str: String = "".into(); |
||||||
|
let mut i = 0; //instructions index
|
||||||
|
|
||||||
|
if args.interactive && args.file.is_some() { |
||||||
|
reader.read_to_string(&mut instructions_str)?; |
||||||
|
reader = Box::new(BufReader::new(io::stdin())); |
||||||
|
} else if !args.interactive { |
||||||
|
reader.read_to_string(&mut instructions_str)?; |
||||||
|
}
|
||||||
|
|
||||||
|
loop { |
||||||
|
//filtering instructions to only contain brainfuck code (optimization)
|
||||||
|
let instructions = instructions_str.chars() |
||||||
|
.filter(|c| actions.contains(c)) |
||||||
|
.collect::<Vec<char>>(); |
||||||
|
|
||||||
|
'reading_instructions: while i < instructions.len() { |
||||||
|
match instructions[i] { |
||||||
|
'>' => pointer += 1, |
||||||
|
'<' => pointer -= 1, |
||||||
|
'+' => memory[pointer] += 1, |
||||||
|
'-' => memory[pointer] -= 1, |
||||||
|
'.' => print!("{}", memory[pointer] as char), |
||||||
|
',' => { |
||||||
|
let mut input = "".into(); |
||||||
|
print!("Enter input (one byte only): "); |
||||||
|
io::stdout().flush()?; |
||||||
|
let mut handle = io::stdin().lock(); |
||||||
|
handle.read_line(&mut input)?; |
||||||
|
|
||||||
|
if input.len() > 0 { |
||||||
|
memory[pointer] = input.chars().nth(0).unwrap() as u8; |
||||||
|
} |
||||||
|
}, |
||||||
|
'[' =>
|
||||||
|
if memory[pointer] == 0 { |
||||||
|
let mut pile = 1; |
||||||
|
while i < instructions.len() && pile != 0 { |
||||||
|
i += 1; |
||||||
|
|
||||||
|
match instructions[i] { |
||||||
|
'[' => pile += 1, |
||||||
|
']' => pile -= 1, |
||||||
|
_ => () |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
']' =>
|
||||||
|
if memory[pointer] != 0 { |
||||||
|
let mut pile = 1; |
||||||
|
while i > 0 { |
||||||
|
match instructions[i-1] { |
||||||
|
']' => pile += 1, |
||||||
|
'[' => pile -= 1, |
||||||
|
_ => () |
||||||
|
} |
||||||
|
|
||||||
|
if pile == 0 { break; } |
||||||
|
i -= 1; |
||||||
|
} |
||||||
|
continue 'reading_instructions; |
||||||
|
} |
||||||
|
|
||||||
|
_ => () |
||||||
|
} |
||||||
|
|
||||||
|
i += 1; |
||||||
|
} |
||||||
|
|
||||||
|
if args.verbose || args.interactive { |
||||||
|
print_mem(&memory); |
||||||
|
println!("pointer: {} (0x{:02x})", &pointer, &memory[pointer]); |
||||||
|
println!(); |
||||||
|
} |
||||||
|
|
||||||
|
if !args.interactive { break; } |
||||||
|
|
||||||
|
print!("~ "); |
||||||
|
io::stdout().flush()?; |
||||||
|
reader.read_line(&mut instructions_str)?; |
||||||
|
} |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
fn print_mem(memory: &[u8]) { |
||||||
|
let mut i = 0; |
||||||
|
let mut s = String::new(); |
||||||
|
let mut z_counter = 0; |
||||||
|
let z_limit = 3; |
||||||
|
|
||||||
|
while i < memory.len() { |
||||||
|
if memory[i] == 0 { |
||||||
|
z_counter += 1; |
||||||
|
} else { |
||||||
|
z_counter = 0; |
||||||
|
} |
||||||
|
if z_counter == z_limit { |
||||||
|
s += ".. "; |
||||||
|
|
||||||
|
} else if z_counter < z_limit { |
||||||
|
s += &format!("{:02x} ", memory[i]); |
||||||
|
} |
||||||
|
i += 1; |
||||||
|
} |
||||||
|
println!(); |
||||||
|
println!("-- memory (hex) --\n{}", s); |
||||||
|
} |
Loading…
Reference in new issue