You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

165 lines
4.5 KiB

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);
}