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!(""), '[' => { 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, /// File to read program from #[clap(short, long)] file: Option, /// 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); }