Browse Source

first commit

master
Geoff 3 years ago
commit
060e00daff
  1. 3
      .gitignore
  2. 10
      Cargo.toml
  3. 149
      src/main.rs

3
.gitignore vendored

@ -0,0 +1,3 @@
/target
*.bf
Cargo.lock

10
Cargo.toml

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

149
src/main.rs

@ -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…
Cancel
Save