183 lines
4.3 KiB
Rust
183 lines
4.3 KiB
Rust
#![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<String>> {
|
|
|
|
let mut loop_stack: Vec<usize> = vec![];
|
|
let mut pos = 0 as usize;
|
|
let mut pointer = 0;
|
|
|
|
let chars = code.chars().collect::<Vec<char>>();
|
|
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::<Vec<char>>();
|
|
|
|
let mut loop_stack: Vec<usize> = 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)
|
|
} |