use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use thiserror::Error;
use crate::Rule;
pub const DEFAULT_BLANK_SYMBOL: char = ' ';
pub const INPUT_BLANK_SYMBOL: char = '_';
pub const MAX_PROGRAM_SIZE: usize = 65536;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Program {
pub name: String,
pub initial_state: String,
pub tapes: Vec<String>,
pub heads: Vec<usize>,
pub blank: char,
pub rules: HashMap<String, Vec<Transition>>,
}
impl Program {
pub fn initial_tape(&self) -> String {
self.tapes.first().cloned().unwrap_or_default()
}
pub fn head_position(&self) -> usize {
self.heads.first().cloned().unwrap_or(0)
}
pub fn is_single_tape(&self) -> bool {
self.tapes.len() == 1
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Transition {
pub read: Vec<char>,
pub write: Vec<char>,
pub directions: Vec<Direction>,
pub next_state: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum Direction {
Left,
Right,
Stay,
}
#[derive(Debug, Clone)]
pub struct ExecutionStep {
pub state: String,
pub tapes: Vec<Vec<char>>,
pub head_positions: Vec<usize>,
pub symbols_read: Vec<char>,
pub transition: Option<Transition>,
}
impl ExecutionStep {
pub fn tape(&self) -> Vec<char> {
self.tapes.first().cloned().unwrap_or_default()
}
pub fn head_position(&self) -> usize {
self.head_positions.first().cloned().unwrap_or(0)
}
pub fn symbol_read(&self) -> char {
self.symbols_read
.first()
.cloned()
.unwrap_or(DEFAULT_BLANK_SYMBOL)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ExecutionResult {
Continue,
Halt,
Error(TuringMachineError),
}
#[derive(Debug, Clone, PartialEq, Error)]
pub enum TuringMachineError {
#[error("Invalid state: {0}")]
InvalidState(String),
#[error("No transition defined for state {state} and symbol '{symbol}'")]
NoTransition { state: String, symbol: char },
#[error("No transition defined for state {state} and symbols {symbols:?}")]
NoMultiTapeTransition { state: String, symbols: Vec<char> },
#[error("Tape boundary exceeded")]
TapeBoundary,
#[error("Program parsing error: {0}")]
ParseError(#[from] Box<pest::error::Error<Rule>>),
#[error("Program validation error: {0}")]
ValidationError(String),
#[error("File error: {0}")]
FileError(String),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_direction_serialization() {
let left = Direction::Left;
let right = Direction::Right;
let left_json = serde_json::to_string(&left).unwrap();
let right_json = serde_json::to_string(&right).unwrap();
assert_eq!(left_json, "\"Left\"");
assert_eq!(right_json, "\"Right\"");
let left_deserialized: Direction = serde_json::from_str(&left_json).unwrap();
let right_deserialized: Direction = serde_json::from_str(&right_json).unwrap();
assert_eq!(left, left_deserialized);
assert_eq!(right, right_deserialized);
}
#[test]
fn test_transition_creation() {
let transition = Transition {
read: vec!['A'],
write: vec!['X'],
directions: vec![Direction::Right],
next_state: "q1".to_string(),
};
assert_eq!(transition.write, vec!['X']);
assert_eq!(transition.directions, vec![Direction::Right]);
assert_eq!(transition.next_state, "q1");
}
#[test]
fn test_error_display() {
let error = TuringMachineError::NoTransition {
state: "q0".to_string(),
symbol: 'a',
};
let error_msg = format!("{}", error);
assert!(error_msg.contains("No transition defined"));
assert!(error_msg.contains("q0"));
assert!(error_msg.contains("'a'"));
}
}