|
|
|
use clap::Parser;
|
|
|
|
|
|
|
|
|
|
|
|
fn check_brainfuck(prog: &str) -> bool {
|
|
|
|
/*
|
|
|
|
let valid_bf_chars = "<>+-.,[]";
|
|
|
|
for (n, c) in prog.char_indices() {
|
|
|
|
if ! valid_bf_chars.contains(c) {
|
|
|
|
println!("Invalid character '{}' on pos {}", c, n);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
let mut brackets = 0;
|
|
|
|
let mut borked = false;
|
|
|
|
for (n, c) in prog.char_indices() {
|
|
|
|
match c {
|
|
|
|
'[' => brackets += 1,
|
|
|
|
']' => {
|
|
|
|
if brackets > 0 {
|
|
|
|
brackets -= 1;
|
|
|
|
} else {
|
|
|
|
println!("Closing bracket without matching opening bracket in pos {}", n);
|
|
|
|
borked = true;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'<' | '>' | '+' | '-' | '.' | ',' => (),
|
|
|
|
_ => {
|
|
|
|
println!("Invalid character '{}' on pos {}", c, n);
|
|
|
|
borked = true;
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if brackets > 0 {
|
|
|
|
println!("Not all brackets have been closed, {} remain open", brackets);
|
|
|
|
borked = true;
|
|
|
|
}
|
|
|
|
return ! borked;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn run_brainfuck(prog: &str, debug: bool) {
|
|
|
|
println!("Running programm {}", prog);
|
|
|
|
|
|
|
|
let mut data = vec![];
|
|
|
|
data.push(0);
|
|
|
|
let mut data_ptr = 0;
|
|
|
|
let mut pc = 0;
|
|
|
|
|
|
|
|
loop {
|
|
|
|
let c = prog.chars().nth(pc).unwrap();
|
|
|
|
if debug {
|
|
|
|
println!("At pos {} char {} with data {} at {} (data {:?})", pc, c, data[data_ptr], data_ptr, data);
|
|
|
|
}
|
|
|
|
let mut inc_pc = true;
|
|
|
|
match c {
|
|
|
|
'+' => data[data_ptr] += 1,
|
|
|
|
'-' => data[data_ptr] -= 1,
|
|
|
|
'>' => {
|
|
|
|
data_ptr += 1;
|
|
|
|
if data_ptr == data.len() {
|
|
|
|
data.push(0)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'<' => {
|
|
|
|
if data_ptr > 0 {
|
|
|
|
data_ptr -= 1;
|
|
|
|
} else {
|
|
|
|
data.insert(0, 0);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'.' => print!("{}", std::char::from_u32(data[data_ptr]).unwrap()),
|
|
|
|
',' => println!("<input not implemented>"),
|
|
|
|
'[' => {
|
|
|
|
if data[data_ptr] == 0 {
|
|
|
|
let mut brackets = 0;
|
|
|
|
loop {
|
|
|
|
match prog.chars().nth(pc).unwrap() {
|
|
|
|
'[' => brackets += 1,
|
|
|
|
']' => {
|
|
|
|
brackets -= 1;
|
|
|
|
if brackets == 0 {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
pc += 1;
|
|
|
|
}
|
|
|
|
inc_pc = false;
|
|
|
|
if debug {
|
|
|
|
println!("Executed conditional [ jump to pos {}", pc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
']' => {
|
|
|
|
if data[data_ptr] != 0 {
|
|
|
|
let mut brackets = 1;
|
|
|
|
loop {
|
|
|
|
pc -= 1;
|
|
|
|
match prog.chars().nth(pc).unwrap() {
|
|
|
|
']' => brackets += 1,
|
|
|
|
'[' => {
|
|
|
|
brackets -= 1;
|
|
|
|
if brackets == 0 {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if debug {
|
|
|
|
println!("Executed conditional ] jump to pos {}", pc + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
|
|
|
|
if inc_pc {
|
|
|
|
pc += 1
|
|
|
|
}
|
|
|
|
if pc == prog.len() {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Brainfuck interpreter in RUST!
|
|
|
|
#[derive(Parser, Debug)]
|
|
|
|
#[clap(about, version, author)]
|
|
|
|
struct Args {
|
|
|
|
/// Program to run (as string)
|
|
|
|
#[clap(short, long)]
|
|
|
|
program: Option<String>,
|
|
|
|
|
|
|
|
/// File to read program from
|
|
|
|
#[clap(short, long)]
|
|
|
|
file: Option<String>,
|
|
|
|
|
|
|
|
/// Debug output
|
|
|
|
#[clap(short, long)]
|
|
|
|
debug: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
println!("Welcome to rust BF!");
|
|
|
|
|
|
|
|
let args = Args::parse();
|
|
|
|
|
|
|
|
let prog;
|
|
|
|
if let Some(arg_prog) = args.program {
|
|
|
|
prog = arg_prog;
|
|
|
|
} else if let Some(file) = args.file {
|
|
|
|
prog = std::fs::read_to_string(file).unwrap().trim().into();
|
|
|
|
} else {
|
|
|
|
println!("Either give me a program or a file to run");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ! check_brainfuck(&prog) {
|
|
|
|
println!("Invalid program, aborting");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
run_brainfuck(&prog, args.debug);
|
|
|
|
}
|