This commit is contained in:
nichkara
2025-09-20 13:38:35 +02:00
commit 9983748ee6
3 changed files with 442 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/target

6
Cargo.toml Normal file
View File

@@ -0,0 +1,6 @@
[package]
name = "tm_interpreter"
version = "0.1.0"
edition = "2024"
[dependencies]

435
src/lib.rs Normal file
View File

@@ -0,0 +1,435 @@
mod tm_interpreter {
use std::collections::HashMap;
use std::hash::Hash;
pub enum Shift {
Left,
Right,
NoShift,
}
// Some kind of Turing machine
pub struct TM<G, S> {
alphabet: Vec<G>,
b: G,
sigma: Vec<G>,
q: Vec<S>,
q0: S,
f: Vec<S>,
delta: Vec<((S, G), (S, G, Shift))>,
}
impl<Gx, Sx> TM<Gx, Sx> {
pub fn new<G: std::cmp::PartialEq, S: std::cmp::PartialEq>(
alphabet: Vec<G>,
b: G,
sigma: Vec<G>,
q: Vec<S>,
q0: S,
f: Vec<S>,
delta: Vec<((S, G), (S, G, Shift))>,
) -> Result<TM<G, S>, String>
where
S: Eq,
S: Hash,
S: Clone,
{
if !alphabet.contains(&b) {
return Err(String::from("Blank symbol is undefined in alphabet."));
} else if sigma.contains(&b) {
return Err(String::from("Blank symbol is not allowed as input."));
} else if !sigma.iter().all(|x| alphabet.contains(x)) {
return Err(String::from("An input symbol is undefined in alphabet."));
} else if !q.contains(&q0) {
return Err(String::from("The initial state is undefined."));
} else if !f.iter().all(|x| q.contains(x)) {
return Err(String::from("A final state is undefined."));
} else {
let turing_machine: TM<G, S> = TM {
alphabet: alphabet,
b: b,
sigma: sigma,
q: q,
q0: q0,
f: f,
delta: delta,
};
// Checking transition function
// Check parameter integrity
for delta in turing_machine.delta.iter() {
if !turing_machine.q.contains(&delta.0 .0) {
return Err(String::from("Invalid input state."));
}
if !turing_machine.alphabet.contains(&delta.0 .1) {
return Err(String::from("Invalid input symbol."));
}
if !turing_machine.q.contains(&delta.1 .0) {
return Err(String::from("Invalid output state."));
}
if !turing_machine.alphabet.contains(&delta.1 .1) {
return Err(String::from("Invalid output symbol."));
}
}
// Check amount of input combinations
if turing_machine.delta.len()
< (turing_machine.alphabet.len()
* (turing_machine.q.len() - turing_machine.f.len()))
{
return Err(String::from("Undefined behavior."));
} else if turing_machine.delta.len()
> (turing_machine.alphabet.len()
* (turing_machine.q.len() - turing_machine.f.len()))
{
return Err(String::from("Ambiguous behavior."));
}
// Check termination and state usage by calculating all possible transitions
// Check unused states (for now only detecting direct dead states)
for rule in turing_machine.delta.iter() {
let is_unused: bool = turing_machine.delta.iter().all(|x| x.1 .0 != rule.0 .0);
if is_unused {
return Err(String::from("Unused state."));
}
}
// Check termination
// Create map
let mut state_map: HashMap<S, Vec<S>> = HashMap::new();
for state in turing_machine.q.iter() {
state_map.insert(state.clone(), vec![]);
}
// Check all rules
for rule in turing_machine.delta.iter() {
// Get input and output state
let input_state: S = rule.0 .0.clone();
let output_state: S = rule.1 .0.clone();
// Check if input is already known to output
// Append, if not
if !state_map.get(&output_state).unwrap().contains(&input_state) {
state_map
.get_mut(&output_state)
.unwrap()
.push(input_state.clone());
}
}
// Resolve terminating states
let mut terminating_states: Vec<S> = turing_machine.f.clone();
let mut update: bool = true;
while update {
update = false;
for state_index in 0..terminating_states.len() {
let dependends: Vec<S> = state_map
.get(&terminating_states[state_index])
.unwrap()
.to_vec();
for dependend in dependends {
if !terminating_states.contains(&dependend) {
terminating_states.push(dependend);
update = true;
}
}
}
}
if terminating_states.len() != turing_machine.q.len() {
return Err(String::from("Unterminated state."));
}
return Ok(turing_machine);
}
}
}
}
#[cfg(test)]
mod tests {
use crate::tm_interpreter::*;
#[test]
fn new_positive() {
let alphabet: Vec<u8> = vec![0, 1];
let b: u8 = 0;
let sigma: Vec<u8> = vec![1];
let q: Vec<char> = vec!['A', 'B', 'C', 'H'];
let q0: char = 'A';
let f: Vec<char> = vec!['H'];
let delta: Vec<((char, u8), (char, u8, Shift))> = vec![
(('A', 0), ('B', 1, Shift::Right)),
(('A', 1), ('C', 1, Shift::Left)),
(('B', 0), ('A', 1, Shift::Left)),
(('B', 1), ('B', 1, Shift::Right)),
(('C', 0), ('B', 1, Shift::Left)),
(('C', 1), ('H', 1, Shift::Right)),
];
let working_machine = TM::<char, u8>::new(alphabet, b, sigma, q, q0, f, delta);
match working_machine {
Ok(_) => assert_eq!(true, true),
Err(e) => {
println!("{}", e);
panic!("")
}
}
}
#[test]
fn new_undefined_blank() {
let alphabet: Vec<u8> = vec![0, 1];
let b: u8 = 2;
let sigma: Vec<u8> = vec![1];
let q: Vec<char> = vec!['A', 'B', 'C', 'H'];
let q0: char = 'A';
let f: Vec<char> = vec!['H'];
let delta: Vec<((char, u8), (char, u8, Shift))> = vec![
(('A', 0), ('B', 1, Shift::Right)),
(('A', 1), ('C', 1, Shift::Left)),
(('B', 0), ('A', 1, Shift::Left)),
(('B', 1), ('B', 1, Shift::Right)),
(('C', 0), ('B', 1, Shift::Left)),
(('C', 1), ('H', 1, Shift::Right)),
];
let working_machine = TM::<char, u8>::new(alphabet, b, sigma, q, q0, f, delta);
match working_machine {
Ok(_) => panic!(""),
Err(e) => {
assert_eq!(e.as_str(), "Blank symbol is undefined in alphabet.")
}
}
}
#[test]
fn new_blank_in_sigma() {
let alphabet: Vec<u8> = vec![0, 1];
let b: u8 = 1;
let sigma: Vec<u8> = vec![1];
let q: Vec<char> = vec!['A', 'B', 'C', 'H'];
let q0: char = 'A';
let f: Vec<char> = vec!['H'];
let delta: Vec<((char, u8), (char, u8, Shift))> = vec![
(('A', 0), ('B', 1, Shift::Right)),
(('A', 1), ('C', 1, Shift::Left)),
(('B', 0), ('A', 1, Shift::Left)),
(('B', 1), ('B', 1, Shift::Right)),
(('C', 0), ('B', 1, Shift::Left)),
(('C', 1), ('H', 1, Shift::Right)),
];
let working_machine = TM::<char, u8>::new(alphabet, b, sigma, q, q0, f, delta);
match working_machine {
Ok(_) => panic!(""),
Err(e) => {
assert_eq!(e.as_str(), "Blank symbol is not allowed as input.")
}
}
}
#[test]
fn new_undefined_sigma_symbol() {
let alphabet: Vec<u8> = vec![0, 1];
let b: u8 = 0;
let sigma: Vec<u8> = vec![1, 2];
let q: Vec<char> = vec!['A', 'B', 'C', 'H'];
let q0: char = 'A';
let f: Vec<char> = vec!['H'];
let delta: Vec<((char, u8), (char, u8, Shift))> = vec![
(('A', 0), ('B', 1, Shift::Right)),
(('A', 1), ('C', 1, Shift::Left)),
(('B', 0), ('A', 1, Shift::Left)),
(('B', 1), ('B', 1, Shift::Right)),
(('C', 0), ('B', 1, Shift::Left)),
(('C', 1), ('H', 1, Shift::Right)),
];
let working_machine = TM::<char, u8>::new(alphabet, b, sigma, q, q0, f, delta);
match working_machine {
Ok(_) => panic!(""),
Err(e) => {
assert_eq!(e.as_str(), "An input symbol is undefined in alphabet.")
}
}
}
#[test]
fn new_undefined_initial_state() {
let alphabet: Vec<u8> = vec![0, 1];
let b: u8 = 0;
let sigma: Vec<u8> = vec![1];
let q: Vec<char> = vec!['A', 'B', 'C', 'H'];
let q0: char = 'D';
let f: Vec<char> = vec!['H'];
let delta: Vec<((char, u8), (char, u8, Shift))> = vec![
(('A', 0), ('B', 1, Shift::Right)),
(('A', 1), ('C', 1, Shift::Left)),
(('B', 0), ('A', 1, Shift::Left)),
(('B', 1), ('B', 1, Shift::Right)),
(('C', 0), ('B', 1, Shift::Left)),
(('C', 1), ('H', 1, Shift::Right)),
];
let working_machine = TM::<char, u8>::new(alphabet, b, sigma, q, q0, f, delta);
match working_machine {
Ok(_) => panic!(""),
Err(e) => {
assert_eq!(e.as_str(), "The initial state is undefined.")
}
}
}
#[test]
fn new_undefined_final_state() {
let alphabet: Vec<u8> = vec![0, 1];
let b: u8 = 0;
let sigma: Vec<u8> = vec![1];
let q: Vec<char> = vec!['A', 'B', 'C', 'H'];
let q0: char = 'A';
let f: Vec<char> = vec!['Q'];
let delta: Vec<((char, u8), (char, u8, Shift))> = vec![
(('A', 0), ('B', 1, Shift::Right)),
(('A', 1), ('C', 1, Shift::Left)),
(('B', 0), ('A', 1, Shift::Left)),
(('B', 1), ('B', 1, Shift::Right)),
(('C', 0), ('B', 1, Shift::Left)),
(('C', 1), ('H', 1, Shift::Right)),
];
let working_machine = TM::<char, u8>::new(alphabet, b, sigma, q, q0, f, delta);
match working_machine {
Ok(_) => panic!(""),
Err(e) => {
assert_eq!(e.as_str(), "A final state is undefined.")
}
}
}
#[test]
fn new_invalid_inputt_state() {
let alphabet: Vec<u8> = vec![0, 1];
let b: u8 = 0;
let sigma: Vec<u8> = vec![1];
let q: Vec<char> = vec!['A', 'B', 'C', 'H'];
let q0: char = 'A';
let f: Vec<char> = vec!['H'];
let delta: Vec<((char, u8), (char, u8, Shift))> = vec![
(('D', 0), ('B', 1, Shift::Right)),
(('A', 1), ('C', 1, Shift::Left)),
(('B', 0), ('A', 1, Shift::Left)),
(('B', 1), ('B', 1, Shift::Right)),
(('C', 0), ('B', 1, Shift::Left)),
(('C', 1), ('H', 1, Shift::Right)),
];
let working_machine = TM::<char, u8>::new(alphabet, b, sigma, q, q0, f, delta);
match working_machine {
Ok(_) => panic!(""),
Err(e) => {
assert_eq!(e.as_str(), "Invalid input state.")
}
}
}
#[test]
fn new_invalid_input_symbol() {
let alphabet: Vec<u8> = vec![0, 1];
let b: u8 = 0;
let sigma: Vec<u8> = vec![1];
let q: Vec<char> = vec!['A', 'B', 'C', 'H'];
let q0: char = 'A';
let f: Vec<char> = vec!['H'];
let delta: Vec<((char, u8), (char, u8, Shift))> = vec![
(('A', 4), ('B', 1, Shift::Right)),
(('A', 1), ('C', 1, Shift::Left)),
(('B', 0), ('A', 1, Shift::Left)),
(('B', 1), ('B', 1, Shift::Right)),
(('C', 0), ('B', 1, Shift::Left)),
(('C', 1), ('H', 1, Shift::Right)),
];
let working_machine = TM::<char, u8>::new(alphabet, b, sigma, q, q0, f, delta);
match working_machine {
Ok(_) => panic!(""),
Err(e) => {
assert_eq!(e.as_str(), "Invalid input symbol.")
}
}
}
#[test]
fn new_undefined_behavior() {
let alphabet: Vec<u8> = vec![0, 1];
let b: u8 = 0;
let sigma: Vec<u8> = vec![1];
let q: Vec<char> = vec!['A', 'B', 'C', 'H', 'I'];
let q0: char = 'A';
let f: Vec<char> = vec!['H'];
let delta: Vec<((char, u8), (char, u8, Shift))> = vec![
(('A', 0), ('B', 1, Shift::Right)),
(('A', 1), ('C', 1, Shift::Left)),
(('B', 0), ('A', 1, Shift::Left)),
(('B', 1), ('B', 1, Shift::Right)),
(('C', 0), ('B', 1, Shift::Left)),
(('C', 1), ('H', 1, Shift::Right)),
];
let working_machine = TM::<char, u8>::new(alphabet, b, sigma, q, q0, f, delta);
match working_machine {
Ok(_) => panic!(""),
Err(e) => {
assert_eq!(e.as_str(), "Undefined behavior.")
}
}
}
#[test]
fn new_ambigous_behavior() {
let alphabet: Vec<u8> = vec![0, 1];
let b: u8 = 0;
let sigma: Vec<u8> = vec![1];
let q: Vec<char> = vec!['A', 'B', 'C', 'H'];
let q0: char = 'A';
let f: Vec<char> = vec!['H'];
let delta: Vec<((char, u8), (char, u8, Shift))> = vec![
(('A', 0), ('B', 1, Shift::Right)),
(('A', 1), ('C', 1, Shift::Left)),
(('B', 0), ('A', 1, Shift::Left)),
(('B', 0), ('A', 1, Shift::Right)),
(('B', 1), ('B', 1, Shift::Right)),
(('C', 0), ('B', 1, Shift::Left)),
(('C', 1), ('H', 1, Shift::Right)),
];
let working_machine = TM::<char, u8>::new(alphabet, b, sigma, q, q0, f, delta);
match working_machine {
Ok(_) => panic!(""),
Err(e) => {
assert_eq!(e.as_str(), "Ambiguous behavior.")
}
}
}
#[test]
fn new_unterminated_state() {
let alphabet: Vec<u8> = vec![0, 1];
let b: u8 = 0;
let sigma: Vec<u8> = vec![1];
let q: Vec<char> = vec!['A', 'B', 'C', 'D', 'H'];
let q0: char = 'A';
let f: Vec<char> = vec!['H'];
let delta: Vec<((char, u8), (char, u8, Shift))> = vec![
(('A', 0), ('B', 1, Shift::Right)),
(('A', 1), ('C', 1, Shift::Left)),
(('B', 0), ('A', 1, Shift::Left)),
(('B', 1), ('B', 1, Shift::Right)),
(('C', 0), ('B', 1, Shift::Left)),
(('C', 1), ('H', 1, Shift::Right)),
(('D', 0), ('D', 1, Shift::Left)),
(('D', 1), ('D', 1, Shift::Right)),
];
let working_machine = TM::<char, u8>::new(alphabet, b, sigma, q, q0, f, delta);
match working_machine {
Ok(_) => panic!(""),
Err(e) => {
assert_eq!(e.as_str(), "Unterminated state.")
}
}
}
}