#![forbid(unsafe_code)] use std::{io::{stdout, Write, Read, stdin}, fmt::format}; /** * Check code for syntax errors */ pub fn lint_code(code: &&str) -> Result<(), Vec> { let mut loop_stack: Vec = vec![]; let mut pos = 0 as usize; let mut pointer = 0; let chars = code.chars().collect::>(); let size = code.len(); let mut errors = vec![]; loop { if pos == size { break } let char = chars[pos]; pos += 1; if char == '<' { if pointer == 0 { errors.push(format!("ERR: Reducing pointer to less than 0 is not allowed ({})", pos)); continue } pointer -= 1; } if char == '>' { if pointer == 29999 { errors.push(format!("ERR: Increasing pointer to more than 29999 is not allowed ({})", pos)); continue } pointer += 1; } if char == '[' { loop_stack.push(pos); continue } if char == ']' { if loop_stack.len() == 0 { errors.push(format!("ERR: Unmatched ']' ({})", pos)); continue } loop_stack.pop(); } } if loop_stack.len() != 0 { for lup in loop_stack.iter() { errors.push(format!("Unmatched '[' ({})", *lup)); } } if errors.len() != 0 { return Err(errors) } Ok(()) } pub fn eval_mem(bf_str: &&str, mem: &mut [u8], pointer: &mut usize) -> Result<(), String> { let mut pos = 0 as usize; let progsize = bf_str.len(); let chars = bf_str.chars().collect::>(); let mut loop_stack: Vec = vec![]; loop { if pos == progsize { break } let char = chars[pos]; pos += 1; if char == '>' { if (*pointer) + 1 == 30000 { return Err(format!("Attempt to increase pointer exceeding memory size ({})", pos)); } *pointer += 1; continue } if char == '<' { if *pointer == 0 { return Err(format!("Attempt to reduce pointer to less than 1 ({})", pos)); } *pointer -= 1; continue } if char == '+' { if mem[*pointer] == 255 { mem[*pointer] = 0; continue } mem[*pointer] += 1; continue } if char == '-' { if mem[*pointer] == 0 { mem[*pointer] = 255; continue } mem[*pointer] -= 1; continue } if char == '.' { print!("{}", mem[*pointer] as char); stdout().flush().unwrap(); continue } if char == ',' { let byte = stdin().bytes().next(); if byte.is_none() { continue } let byte = byte.unwrap(); if byte.is_err() { return Err(byte.unwrap_err().to_string()); } mem[*pointer] = byte.unwrap(); continue } if char == '[' { loop_stack.push(pos); continue } if char == ']' { if loop_stack.len() == 0 { return Err(format!("Unmatched ']' ({})", pos)); } if mem[*pointer] == 0 { loop_stack.pop(); continue } else { pos = loop_stack.last().unwrap().clone(); continue } } } if loop_stack.len() != 0 { return Err(format!("Unmatched '[' ({})", loop_stack.last().unwrap())); } Ok(()) } pub fn eval(bf_str: &&str) -> Result<[u8; 30000], String> { // first check the code let lint = lint_code(bf_str); if lint.is_err() { return Err( format!( "Code linting failed: \n\n{}", lint.unwrap_err().join("\n") ) ) } // then actually run it let mut memory: [u8; 30000] = core::array::from_fn(|_| 0); let mut pointer = 0 as usize; let e = eval_mem(bf_str, &mut memory, &mut pointer); if e.is_err() { return Err(e.unwrap_err()); } Ok(memory) }