rstm_core/actors/
actor.rs

1/*
2    Appellation: actor <module>
3    Contrib: FL03 <jo3mccain@icloud.com>
4*/
5use super::Executor;
6use crate::rules::RuleSet;
7use crate::{Direction, Error, Head, State};
8
9/// An [Actor] is an implementation of a Turing machine with a moving head (TMH).
10///
11/// The model contains the following components:
12///
13/// - `alpha`: the input alphabet
14/// - `head`: the head of the tape
15#[derive(Clone, Default, Eq, Hash, PartialEq, PartialOrd)]
16pub struct Actor<Q, A> {
17    /// the head of the tape
18    pub(crate) head: Head<Q, usize>,
19    /// the input alphabet
20    pub(crate) tape: Vec<A>,
21}
22
23impl<Q, A> Actor<Q, A> {
24    pub fn new(alpha: impl IntoIterator<Item = A>, state: State<Q>, symbol: usize) -> Self {
25        Self {
26            head: Head { state, symbol },
27            tape: Vec::from_iter(alpha),
28        }
29    }
30    /// Constructs a new [Actor] with the given state; assumes the tape is empty and the head
31    /// is located at `0`.
32    pub fn from_state(state: State<Q>) -> Self {
33        Self {
34            head: Head { state, symbol: 0 },
35            tape: Vec::new(),
36        }
37    }
38    /// Consumes the current instance and returns a new instance with the given alphabet
39    pub fn with_alpha<I>(self, alpha: I) -> Self
40    where
41        I: IntoIterator<Item = A>,
42    {
43        Self {
44            tape: Vec::from_iter(alpha),
45            ..self
46        }
47    }
48    /// Consumes the current instance and returns a new instance with the given head
49    pub fn with_head(self, head: Head<Q, usize>) -> Self {
50        Self { head, ..self }
51    }
52    /// Consumes the current instance and returns a new instance with the given position
53    pub fn with_position(self, symbol: usize) -> Self {
54        Self {
55            head: Head {
56                symbol,
57                ..self.head
58            },
59            ..self
60        }
61    }
62    /// Consumes the current instance and returns a new instance with the given state
63    pub fn with_state(self, state: State<Q>) -> Self {
64        Self {
65            head: Head { state, ..self.head },
66            ..self
67        }
68    }
69    /// Returns an immutable reference to the tape, as a slice
70    pub fn tape(&self) -> &[A] {
71        &self.tape
72    }
73    /// Returns a mutable reference of the tape as a slice
74    pub fn tape_mut(&mut self) -> &mut [A] {
75        &mut self.tape
76    }
77    /// Returns an immutable reference to the head of the tape
78    pub const fn head(&self) -> &Head<Q, usize> {
79        &self.head
80    }
81    /// Returns a mutable reference to the head of the tape
82    pub fn head_mut(&mut self) -> &mut Head<Q, usize> {
83        &mut self.head
84    }
85    /// Returns an instance of the [Head] with an immutable reference to the state's inner
86    /// value
87    pub fn head_ref(&self) -> Head<&Q, usize> {
88        Head {
89            state: self.head.state.to_ref(),
90            symbol: self.head.symbol,
91        }
92    }
93    /// Returns the current position of the head on the tape
94    pub fn position(&self) -> usize {
95        self.head().symbol
96    }
97    /// Returns an instance of the state with an immutable reference to the inner value
98    pub fn state(&self) -> State<&Q> {
99        self.head().state()
100    }
101    /// Returns an instance of the state with a mutable reference to the inner value
102    pub fn state_mut(&mut self) -> State<&mut Q> {
103        self.head_mut().state_mut()
104    }
105    /// Executes the given program; the method is lazy, meaning it will not compute immediately
106    /// but will return an [Executor] that is better suited for managing the runtime.
107    pub fn execute(self, program: RuleSet<Q, A>) -> Executor<Q, A> {
108        Executor::new(self, program)
109    }
110    /// Checks if the tape is empty
111    pub fn is_empty(&self) -> bool {
112        self.tape.is_empty()
113    }
114    /// Checks if the tape is halted
115    pub fn is_halted(&self) -> bool
116    where
117        Q: 'static,
118    {
119        self.head().state.is_halt()
120    }
121    /// Returns the length of the tape
122    #[inline]
123    pub fn len(&self) -> usize {
124        self.tape.len()
125    }
126    /// Reads the current symbol at the head of the tape
127    #[cfg_attr(
128        feature = "tracing",
129        tracing::instrument(skip_all, name = "read", target = "actor")
130    )]
131    pub fn read(&self) -> Result<Head<&'_ Q, &'_ A>, Error> {
132        #[cfg(feature = "tracing")]
133        tracing::trace!("Reading the tape...");
134        self.tape
135            .get(self.position())
136            .map(|symbol| Head {
137                state: self.state(),
138                symbol,
139            })
140            .ok_or(Error::index_out_of_bounds(self.position(), self.len()))
141    }
142
143    /// Writes the given symbol to the tape
144    #[cfg_attr(
145        feature = "tracing",
146        tracing::instrument(skip_all, name = "write", target = "actor")
147    )]
148    pub fn write(&mut self, value: A) {
149        #[cfg(feature = "tracing")]
150        tracing::trace!("Writing to the tape...");
151        let pos = self.position();
152
153        if pos == usize::MAX {
154            #[cfg(feature = "tracing")]
155            tracing::trace!("Prepending to the tape...");
156            // prepend to the tape
157            self.tape.insert(0, value);
158        } else if pos >= self.len() {
159            #[cfg(feature = "tracing")]
160            tracing::trace!("Appending to the tape...");
161            // append to the tape
162            self.tape.push(value);
163        } else {
164            self.tape[pos] = value;
165        }
166    }
167    /// Performs a single step of the Turing machine; returns the previous head of the tape.
168    /// Each step writes the given symbol to the tape, updates the state of the head, and moves
169    /// the head by a single unit in the specified direction.
170    #[cfg_attr(
171        feature = "tracing",
172        tracing::instrument(skip_all, name = "handle", target = "actor")
173    )]
174    pub(crate) fn step(
175        &mut self,
176        direction: Direction,
177        state: State<Q>,
178        symbol: A,
179    ) -> Head<Q, usize> {
180        #[cfg(feature = "tracing")]
181        tracing::trace!("Transitioning the actor...");
182        // write the symbol to the tape
183        self.write(symbol);
184        // update the head of the actor
185        self.head.replace(state, self.position() + direction)
186    }
187}
188
189impl<Q, S> core::fmt::Debug for Actor<Q, S>
190where
191    S: core::fmt::Debug,
192{
193    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
194        for (i, c) in self.tape.iter().enumerate() {
195            if i == self.position() {
196                write!(f, "[{c:?}]")?;
197            } else {
198                write!(f, "{c:?}")?;
199            }
200        }
201        Ok(())
202    }
203}
204
205impl<Q, S> core::fmt::Display for Actor<Q, S>
206where
207    S: core::fmt::Display,
208{
209    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
210        for (i, c) in self.tape.iter().enumerate() {
211            if i == self.position() {
212                write!(f, "[{c}]")?;
213            } else {
214                write!(f, "{c}")?;
215            }
216        }
217        Ok(())
218    }
219}