turing_machine_rs/machines/
debugger.rs

1use crate::instruction::{Head, Move, Tail};
2use crate::state::Configuration;
3use crate::{Symbol, TuringMachine};
4
5type CHandler<S> = Box<dyn Fn(&Configuration<S>)>;
6type IHandler<S> = Box<dyn Fn(&Head<S>, &Tail<S>)>;
7
8/// [`Debugger`] is an super useful [`TuringMachine`] for debugging another
9/// Turing machine! This machine debugged all Turing machines in tests.
10///
11/// Note: this machine is not implementing [`crate::With`].
12///
13/// # Examples
14/// ```rust
15/// use std::cell::RefCell;
16/// use std::ops::Deref;
17/// use std::rc::Rc;
18///
19/// use turing_machine_rs::TuringMachine;
20/// use turing_machine_rs::instruction::{Move, State};
21/// use turing_machine_rs::machines::{Debugger, Classic};
22/// use turing_machine_rs::program::{Extend, Program};
23/// use turing_machine_rs::state::{Configuration, Tape};
24///
25/// fn main() -> Result<(), String> {
26///     let mut program = Program::new(vec![' '], State(1));
27///     program.extend([(1, ' ', 1, ' ', Move::Right)])?;
28///
29///     let machine = Classic::new(program, ' ')?;
30///     let mut debugger = Debugger::new(machine);
31///
32///     let conf = Configuration::new_nrm(Tape::from("   "))?;
33///
34///     let buffer = Rc::new(RefCell::new(String::new()));
35///
36///     let c_buffer = buffer.clone();
37///     debugger.set_c_handler(move |_| {
38///         let mut buffer = c_buffer.borrow_mut();
39///         buffer.push('c');
40///     });
41///
42///     let conf = debugger.execute_once(conf)?;
43///     debugger.execute_once(conf)?;
44///
45///     assert_eq!(String::from("cc"), buffer.deref().borrow().as_ref());
46///     Ok(())
47/// }
48/// ```
49pub struct Debugger<Machine, S: Symbol>
50where
51    Machine: TuringMachine<S>,
52{
53    machine: Machine,
54    c_handler: Option<CHandler<S>>,
55    i_handler: Option<IHandler<S>>,
56}
57
58impl<Machine, S: Symbol> Debugger<Machine, S>
59where
60    Machine: TuringMachine<S>,
61{
62    /// Constructs a new [`Debugger`] with a [`TuringMachine`] and no handlers.
63    ///
64    /// For setup handlers, use the [`Debugger::set_c_handler`]
65    /// and the [`Debugger::set_i_handler`] methods.
66    pub fn new(machine: Machine) -> Self {
67        Debugger {
68            machine,
69            c_handler: None,
70            i_handler: None,
71        }
72    }
73
74    /// Sets a handler for configurations. Handler must implement
75    /// [`Fn(&Configuration<Symbol>)`] trait.
76    ///
77    /// This function is not permanent so handler can be changed.
78    pub fn set_c_handler(&mut self, c_handler: impl Fn(&Configuration<S>) + 'static) {
79        self.c_handler = Some(Box::new(c_handler));
80    }
81
82    /// Sets a handler for instructions. Handler must implement
83    /// [`Fn(&Head<Symbol>, &Tail<Symbol>)`] trait.
84    ///
85    /// This function is not permanent so handler can be changed.
86    pub fn set_i_handler(&mut self, i_handler: impl Fn(&Head<S>, &Tail<S>) + 'static) {
87        self.i_handler = Some(Box::new(i_handler));
88    }
89}
90
91impl<Machine, S: Symbol> TuringMachine<S> for Debugger<Machine, S>
92where
93    Machine: TuringMachine<S>,
94{
95    /// Executes [`Configuration`] once by mutation.
96    ///
97    /// Works quickly when no handler is set (but you probably don't want to
98    /// use the debugger without the debugging).
99    ///
100    /// # Panics
101    /// [`Debugger`] could panic only if source code is broken - this would be a bug.
102    /// All match cases must and are covered.
103    ///
104    /// So you could open an issue on [GitHub](https://github.com/Helltraitor/turing-machine-rs).
105    fn execute_once(&self, conf: Configuration<S>) -> Result<Configuration<S>, String> {
106        let next = self.machine.execute_once(conf.clone())?;
107        if let Some(ref c_handler) = self.c_handler {
108            c_handler(&conf);
109        }
110        if let Some(ref i_handler) = self.i_handler {
111            let head = Head::new(conf.state, conf.get_symbol().clone());
112            let movement = match (conf.index(), next.index()) {
113                (old, new) if old < new => Move::Right,
114                (old, new) if old == new => Move::None,
115                (old, new) if old > new => Move::Right,
116                (old, new) => panic!(
117                    "execute_once error: not all compare cases are covered for old {} and new {} indexes",
118                    old,
119                    new
120                )
121            };
122            let tail = Tail::new(next.state, next.get_symbol().clone(), movement);
123            i_handler(&head, &tail);
124        }
125        Ok(next)
126    }
127
128    /// Executes [`Configuration`] until predicate is `false` by mutation.
129    ///
130    /// Uses the [`Debugger::execute_once`] method in the loop. Works quickly
131    /// when no handler set (but probably you don't wnat to use the debugger without tools).
132    fn execute_until(
133        &self,
134        mut conf: Configuration<S>,
135        until: impl Fn(&Configuration<S>) -> bool,
136    ) -> Result<Configuration<S>, String> {
137        if self.c_handler.is_none() && self.i_handler.is_none() {
138            return self.machine.execute_until(conf, until);
139        }
140        while !until(&conf) {
141            conf = self.execute_once(conf)?;
142        }
143        Ok(conf)
144    }
145}