turing_lib/
instruction.rs

1use std::{fmt::Display, str::FromStr};
2
3use crate::{turing::Rule, CompilerError, ErrorPosition};
4use pest::iterators::Pairs;
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
8/// The possible movements of the tape head
9pub enum Movement {
10    RIGHT,
11    LEFT,
12    HALT,
13}
14
15impl std::str::FromStr for Movement {
16    type Err = String;
17
18    /// Parse a movement from a string
19    fn from_str(input: &str) -> Result<Self, Self::Err> {
20        match input {
21            "R" | "D" => Ok(Self::RIGHT),
22            "L" | "I" => Ok(Self::LEFT),
23            "H" | "N" => Ok(Self::HALT),
24            _ => Err(format!("\"{input}\" is an unknown movement")),
25        }
26    }
27}
28
29impl Display for Movement {
30    /// Display a movement as a string
31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        match self {
33            Movement::RIGHT => write!(f, "R"),
34            Movement::LEFT => write!(f, "L"),
35            Movement::HALT => write!(f, "H"),
36        }
37    }
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
41/// A Turing machine instruction
42pub struct TuringInstruction {
43    pub from_state: String,
44    pub from_value: bool,
45    pub to_value: bool,
46    pub movement: Movement,
47    pub to_state: String,
48}
49
50impl Display for TuringInstruction {
51    /// Display an instruction as a string
52    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53        write!(
54            f,
55            "({}, {}, {}, {}, {})",
56            self.from_state,
57            if self.from_value { "1" } else { "0" },
58            if self.to_value { "1" } else { "0" },
59            self.movement,
60            self.to_state
61        )
62    }
63}
64
65impl TuringInstruction {
66    /// Create an instruction from a `Pairs<Rule>` object
67    pub fn from(mut code: Pairs<Rule>) -> Result<Self, CompilerError> {
68        let from_state = match code.next() {
69            Some(s) => String::from(s.as_span().as_str()),
70            None => panic!("The instruction lacks an initial state"),
71        };
72        let from_value = match code.next() {
73            Some(s) => s.as_span().as_str() == "1",
74            None => panic!("The instruction lacks an initial tape value"),
75        };
76        let to_value = match code.next() {
77            Some(s) => s.as_span().as_str() == "1",
78            None => panic!("The instruction lacks a target tape value"),
79        };
80
81        let movement = match code.next() {
82            Some(s) => match Movement::from_str(s.as_span().as_str()) {
83                Ok(m) => m,
84                Err(message) => {
85                    return Err(CompilerError::SyntaxError {
86                        position: ErrorPosition::from(&s),
87                        message,
88                        code: String::from(s.as_str()),
89                        expected: Rule::movement,
90                        found: None,
91                    })
92                }
93            },
94            None => panic!("The instruction lacks an initial state"),
95        };
96
97        let to_state = match code.next() {
98            Some(s) => String::from(s.as_span().as_str()),
99            None => panic!("The instruction lacks a target state"),
100        };
101
102        Ok(Self {
103            from_state,
104            from_value,
105            to_value,
106            movement,
107            to_state,
108        })
109    }
110
111    /// Create a halt instruction when there is missing information
112    pub fn halt(index: (String, bool)) -> Self {
113        Self {
114            from_state: index.0.clone(),
115            from_value: index.1,
116            to_value: index.1,
117            movement: Movement::HALT,
118            to_state: index.0,
119        }
120    }
121}