#![warn(
clippy::all,
clippy::restriction,
clippy::pedantic,
clippy::nursery,
clippy::cargo
)]
use crate::TapirError::{BracketError, InputError, MemPtrUnderflowError};
use std::collections::VecDeque;
use std::error::Error;
use std::fmt::{Display, Formatter};
use std::io;
use std::io::Write;
fn match_right(index: usize, program: &[u8], lma: usize, lmv: usize) -> (usize, usize, usize) {
if lma == index {
return (lmv, lma, lmv);
}
let lma = index;
let mut index = index;
let mut depth = 0;
while index < program.len() {
if program[index] as char == '[' {
depth += 1;
} else if program[index] as char == ']' {
depth -= 1;
}
if depth == 0 {
break;
}
index += 1;
}
if depth != 0 {
return (0, lma, lmv);
}
(index, lma, index)
}
fn match_left(
index: usize,
program: &[u8],
lma: usize,
lmv: usize,
) -> (Option<usize>, usize, usize) {
if lma == index {
return (Some(lmv), lma, lmv);
}
let lma = index;
let origin = index;
let mut index = index;
let mut depth = 0;
while index <= origin {
if program[index] as char == '[' {
depth += 1;
} else if program[index] as char == ']' {
depth -= 1;
}
if depth == 0 {
break;
}
index -= 1;
}
if depth != 0 {
return (None, lma, lmv);
}
(Some(index), lma, index)
}
fn parse_to_bytes(s: &str) -> Result<Vec<u8>, String> {
let mut bytes: Vec<u8> = Vec::with_capacity(s.len());
let byte_input = s.as_bytes();
let mut i = 0;
let input_len = byte_input.len();
while i < input_len {
if byte_input[i] as char == '\\' {
if i + 1 < input_len {
i += 1;
bytes.push(match byte_input[i] {
b'n' => b'\n' as u8,
b't' => b'\t' as u8,
b'\\' => b'\\' as u8,
b'#' => b'#' as u8,
_ => {
return Err(format!(
"expected an escape code but found '{}'",
byte_input[i] as char
));
}
});
} else {
return Err("expected an escape code but found nothing".to_string());
}
} else if byte_input[i] as char == '#' {
if i + 3 < input_len {
let mut res: u8 = 0;
for j in 1..4 {
if byte_input[i + j].is_ascii_digit() {
res += (byte_input[i + j] - 48) * 10_u8.pow(3 - j as u32);
} else {
return Err(format!(
"expected a digit but found '{}'",
byte_input[i + j] as char
));
}
}
bytes.push(res);
i += 3;
} else {
return Err("expected a byte literal but didn't find enough digits".to_string());
}
} else {
bytes.push(byte_input[i]);
}
i += 1;
}
assert!(!bytes.is_empty());
Ok(bytes)
}
#[derive(Debug)]
pub enum TapirError {
BracketError,
InputError(String),
MemPtrUnderflowError,
}
impl Display for TapirError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
BracketError => write!(f, "BracketError"),
InputError(e) => write!(f, "InputError ({})", e),
MemPtrUnderflowError => write!(f, "MemPtrUnderflowError"),
}
}
}
impl Into<i32> for TapirError {
fn into(self) -> i32 {
match self {
BracketError => 2,
InputError(_) => 3,
MemPtrUnderflowError => 4,
}
}
}
pub fn interpret<I, E>(
mem: &mut Vec<u8>,
program: &[u8],
mut input: I,
) -> Result<(), (TapirError, usize)>
where
I: Iterator<Item = Result<String, E>>,
E: Error + Sized,
{
let mut mem_ptr: usize = 0;
let mut ins_ptr: usize = 0;
let mut last_match_left_arg = 0;
let mut last_match_left_val = 0;
let mut last_match_right_arg = usize::MAX;
let mut last_match_right_val = 0;
if mem.is_empty() {
mem.push(0);
} let mut incoming: VecDeque<u8> = VecDeque::new();
while ins_ptr < program.len() {
match program[ins_ptr] as char {
'>' => {
mem_ptr += 1;
let mem_len = mem.len();
if mem_ptr == mem_len {
mem.push(0);
continue;
}
assert!(mem_ptr < mem.len()); }
'<' => {
let new_mem_ptr = mem_ptr - 1;
if new_mem_ptr > mem_ptr {
return Err((MemPtrUnderflowError, ins_ptr));
}
mem_ptr = new_mem_ptr;
}
'+' => mem[mem_ptr] += 1,
'-' => mem[mem_ptr] -= 1,
'.' => {
print!("{}", mem[mem_ptr] as char);
}
',' => {
if incoming.is_empty() {
io::stdout().flush().expect("Failed to flush stdout (???)");
let line_potential = &mut input.next();
let incoming_bytes = match line_potential {
Some(Ok(s)) => s,
_ => return Err((InputError("Failed to read input".to_owned()), ins_ptr)),
};
if !incoming_bytes.is_empty() {
match parse_to_bytes(incoming_bytes) {
Ok(bytes) => {
incoming.extend(bytes);
mem[mem_ptr] = unsafe { incoming.pop_front().unwrap_unchecked() };
}
Err(e) => return Err((InputError(e), ins_ptr)),
}
}
} else {
mem[mem_ptr] = unsafe { incoming.pop_front().unwrap_unchecked() };
}
}
'[' => {
if mem[mem_ptr] == 0 {
match match_right(ins_ptr, program, last_match_right_arg, last_match_right_val)
{
(0, _, _) => return Err((BracketError, ins_ptr)),
(n, lma, lmv) => {
ins_ptr = n;
last_match_right_arg = lma;
last_match_right_val = lmv;
}
}
}
}
']' => {
if mem[mem_ptr] != 0 {
match match_left(ins_ptr, program, last_match_left_arg, last_match_left_val) {
(None, _, _) => return Err((BracketError, ins_ptr)),
(Some(n), lma, lmv) => {
ins_ptr = n;
last_match_left_arg = lma;
last_match_left_val = lmv;
}
}
}
}
_ => {}
}
ins_ptr += 1;
}
io::stdout().flush().expect("Failed to flush stdout (???)");
Ok(())
}