turing_lib/
turing.rs

1use log::{debug, error, info, warn};
2use pest::Parser;
3use pest_derive::Parser;
4use std::{
5    collections::HashMap,
6    fmt::{self, Display},
7};
8
9use crate::{
10    instruction::Movement, warnings::ErrorPosition, CompilerError, CompilerWarning, Library,
11    TuringInstruction,
12};
13
14use super::TuringOutput;
15
16#[derive(Parser)]
17#[grammar = "../turing.pest"]
18pub struct TuringParser;
19
20#[derive(Debug, Clone)]
21/// A Turing machine
22pub struct TuringMachine {
23    /// The dictionary of instructions for the machine.
24    pub instructions: HashMap<(String, bool), TuringInstruction>,
25
26    /// The final states of the machine. If the machine reaches one of these states, it will stop.
27    pub final_states: Vec<String>,
28
29    /// The current state of the machine.
30    pub current_state: String,
31
32    /// The previous state of the machine.
33    pub previous_state: Option<String>,
34
35    /// The position of the head on the tape.
36    pub tape_position: usize,
37
38    /// The binary tape of the machine.
39    pub tape: Vec<bool>,
40
41    /// The frequencies of the states. Used to detect infinite loops.
42    pub frequencies: HashMap<String, usize>,
43
44    /// The description of the machine. Found in the `///` comments at the top of the file.
45    pub description: Option<String>,
46
47    /// The composed libraries that the machine uses.
48    /// Used only as information, since their instructions are already compiled into the machine.
49    pub composed_libs: Vec<Library>,
50
51    /// The actual code of the machine. Used for resetting the machine and debugging.
52    pub code: String,
53}
54
55impl TuringMachine {
56    /// Create a new Turing machine from a string of code
57    pub fn new(code: &str) -> Result<(Self, Vec<CompilerWarning>), CompilerError> {
58        let mut instructions: HashMap<(String, bool), TuringInstruction> = HashMap::new();
59        let mut final_states: Vec<String> = Vec::new();
60        let mut current_state: String = String::new();
61        let mut tape: Vec<bool> = Vec::new();
62        let mut description: Option<String> = None;
63        let mut composed: Vec<Library> = Vec::new();
64        let mut warnings: Vec<CompilerWarning> = Vec::new();
65
66        let file = match TuringParser::parse(Rule::file, code) {
67            Ok(mut f) => f.next().unwrap(),
68            Err(error) => {
69                return Err(CompilerError::FileRuleError {
70                    error: Box::new(error),
71                })
72            }
73        };
74
75        for record in file.into_inner() {
76            let record_span = &record.as_span();
77
78            match record.as_rule() {
79                Rule::description => {
80                    let s = record.as_str();
81                    if !s.is_empty() {
82                        description = Some(String::from(s.replace("///", "").trim()));
83                        debug!("Found description: \"{:?}\"", description);
84                    }
85                }
86                Rule::COMMENT => debug!("Found comment: \"{:?}\"", record.as_str()),
87                Rule::tape => {
88                    debug!(
89                        "Entered tape rule: {}",
90                        record.clone().into_inner().as_str()
91                    );
92
93                    // Used to extract the position of the error (if any)
94                    // A span contains the start and end position of the error, while a Pair only contains the start position
95                    let span = record.line_col();
96
97                    let code = record.clone().into_inner().as_str();
98
99                    for r in record.into_inner() {
100                        match r.as_rule() {
101                            Rule::value => {
102                                if tape.is_empty() && r.as_str() == "0" {
103                                    info!("The tape started with a 0, skipping it");
104                                } else {
105                                    tape.push(r.as_str() == "1");
106                                }
107                            }
108                            _ => warn!(
109                                "Unhandled: ({:?}, {})",
110                                r.as_rule(),
111                                r.into_inner().as_str()
112                            ),
113                        }
114                    }
115
116                    debug!("Initial state: {}", current_state);
117                    debug!("Tape: {:?}", tape);
118
119                    if tape.is_empty() || !tape.contains(&true) {
120                        error!("The tape did not contain at least a 1");
121
122                        return Err(CompilerError::SyntaxError {
123                            position: span.into(),
124                            message: String::from("Expected at least a 1 in the tape"),
125                            code: String::from(code),
126                            expected: Rule::tape,
127                            found: None,
128                        });
129                    }
130                }
131                Rule::initial_state => {
132                    current_state = String::from(record.into_inner().as_str());
133                    debug!("The initial tape state is \"{}\"", current_state);
134                }
135                Rule::final_state => {
136                    final_states = record
137                        .into_inner()
138                        .map(|v| String::from(v.as_span().as_str()))
139                        .collect();
140                    debug!("The final tape state is {:?}", final_states);
141                }
142                Rule::composition => {
143                    debug!("Entered composition rule");
144                    for r in record.into_inner() {
145                        match r.as_rule() {
146                            Rule::function_name => {
147                                debug!("Found composition of: {}", r.as_str());
148
149                                let mut lib: Option<Library> = None;
150
151                                for l in super::LIBRARIES {
152                                    if l.name == r.as_str() {
153                                        lib = Some(l);
154                                        break;
155                                    }
156                                }
157
158                                if let Some(library) = lib {
159                                    debug!("Found the library, composing...");
160
161                                    instructions.extend(match library.get_instructions() {
162                                        Ok(i) => i,
163                                        Err(c_err) => return Err(c_err),
164                                    });
165
166                                    composed.push(library.clone());
167                                } else {
168                                    error!("Could not find the library \"{}\"", r.as_str());
169
170                                    let (line, column) = r.line_col();
171
172                                    return Err(CompilerError::SyntaxError {
173                                        position: ErrorPosition::new((line, column), None),
174                                        message: format!(
175                                            "Could not find the library \"{}\"",
176                                            r.as_str()
177                                        ),
178                                        code: String::from(r.as_str()),
179                                        expected: r.as_rule(),
180                                        found: None,
181                                    });
182                                }
183                            }
184                            _ => warn!(
185                                "Unhandled: ({:?}, {})",
186                                r.as_rule(),
187                                r.into_inner().as_str()
188                            ),
189                        }
190                    }
191                }
192                Rule::instruction => {
193                    let tmp = match TuringInstruction::from(record.into_inner()) {
194                        Ok(i) => i,
195                        Err(c_err) => return Err(c_err),
196                    };
197
198                    if instructions.contains_key(&(tmp.from_state.clone(), tmp.from_value)) {
199                        warn!("Instruction {} already exists, overwriting it", tmp.clone());
200
201                        warnings.push(CompilerWarning::StateOverwrite {
202                            position: record_span.into(),
203                            state: tmp.from_state.clone(),
204                            value_from: tmp.from_value,
205                        })
206                    }
207                    instructions.insert((tmp.from_state.clone(), tmp.from_value), tmp.clone());
208
209                    debug!("Found instruction {}", tmp);
210                }
211                Rule::EOI => {
212                    debug!("End of file");
213                }
214                _ => {
215                    warn!("Unhandled: {}", record.into_inner().as_str());
216                }
217            }
218        }
219
220        if final_states.is_empty() {
221            error!("No final state given");
222
223            return Err(CompilerError::SyntaxError {
224                position: ErrorPosition::new((0, 0), None),
225                message: String::from("No final state given"),
226                code: String::from(code),
227                expected: Rule::final_state,
228                found: None,
229            });
230        }
231
232        if current_state.is_empty() {
233            error!("No initial state given");
234
235            return Err(CompilerError::SyntaxError {
236                position: ErrorPosition::new((0, 0), None),
237                message: String::from("No initial state given"),
238                code: String::from(code),
239                expected: Rule::initial_state,
240                found: None,
241            });
242        }
243
244        let mut tape_position = 0;
245        while tape_position <= 2 {
246            tape.insert(0, false);
247            tape_position += 1;
248        }
249
250        debug!("The instructions are {:?}", instructions);
251
252        Ok((
253            Self {
254                instructions,
255                final_states,
256                current_state,
257                previous_state: None,
258                tape_position,
259                tape,
260                frequencies: HashMap::new(),
261                description,
262                composed_libs: composed,
263                code: String::from(code),
264            },
265            warnings,
266        ))
267    }
268
269    /// Create a new empty Turing machine
270    pub fn none() -> Self {
271        let state = String::from("f");
272        let mut instructions: HashMap<(String, bool), TuringInstruction> = HashMap::new();
273        instructions.insert(
274            (String::from("F"), false),
275            TuringInstruction {
276                from_state: state.clone(),
277                from_value: false,
278                to_value: false,
279                movement: Movement::HALT,
280                to_state: state.clone(),
281            },
282        );
283        let final_states: Vec<String> = vec![state.clone()];
284        let current_state: String = state.clone();
285        let tape: Vec<bool> = vec![false, false, false, false, false];
286        let description: Option<String> = None;
287
288        Self {
289            instructions,
290            final_states,
291            current_state,
292            previous_state: None,
293            tape_position: 2,
294            tape,
295            frequencies: HashMap::new(),
296            description,
297            composed_libs: Vec::new(),
298            code: String::new(),
299        }
300    }
301
302    /// Parse a Turing machine code syntax error
303    /// and print it to the console
304    pub fn handle_error(error: CompilerError) {
305        error!("I found an error while parsing the file!");
306
307        let position = error.position();
308
309        debug!("Error position: {:?}", position);
310
311        error!(
312            "Error at {}: {}\n\t{}\n\t{:~>width1$}{:^<width2$}{:~<width3$}",
313            position,
314            error.message(),
315            error.code(),
316            "~",
317            "^",
318            "~",
319            width1 = position.start.1,
320            width2 = position.end.unwrap_or((0, position.start.1 + 1)).1 - position.start.1,
321            width3 = error.code().len() - position.end.unwrap_or((0, position.start.1 + 1)).1
322        );
323
324        println!("\nPress enter to exit");
325
326        let mut input = String::new();
327        std::io::stdin().read_line(&mut input).unwrap_or_default();
328    }
329
330    /// Gets the current instruction, or a halt instruction if the current state is a final state
331    /// even if there is no instruction for the current state and value
332    fn get_instruction(&self) -> Option<TuringInstruction> {
333        let current_val: bool = self.tape[self.tape_position];
334        let index = (self.current_state.clone(), current_val);
335
336        match self.instructions.get(&index) {
337            Some(i) => Some(i.to_owned()),
338            None => {
339                if !self.final_states.contains(&self.current_state) {
340                    return None;
341                }
342
343                Some(TuringInstruction::halt(index))
344            }
345        }
346    }
347
348    /// Gets the current instruction
349    pub fn get_current_instruction(&self) -> Option<TuringInstruction> {
350        let current_val: bool = self.tape[self.tape_position];
351        let index = (self.current_state.clone(), current_val);
352
353        self.instructions.get(&index).cloned()
354    }
355
356    /// Returns true if the current state is undefined
357    /// (i.e. there is no instruction for the current state and value)
358    /// except if the current state is a final state
359    pub fn is_undefined(&self) -> bool {
360        self.get_instruction().is_none()
361    }
362
363    /// Calculates the next step of the Turing machine and returns true if the current state is a final state
364    pub fn step(&mut self) -> bool {
365        let current_val: bool = self.tape[self.tape_position];
366
367        let Some(instruction) = self.get_instruction() else {
368            if self.final_states.contains(&self.current_state) {
369                return true;
370            }
371
372            error!(
373                "No instruction given for state ({}, {})",
374                self.current_state.clone(),
375                if current_val { "1" } else { "0" }
376            );
377
378            return true;
379        };
380        self.tape[self.tape_position] = instruction.to_value;
381
382        match instruction.movement {
383            Movement::LEFT => {
384                if self.tape_position == 0 {
385                    self.tape.insert(0, false);
386                } else {
387                    self.tape_position -= 1;
388                }
389            }
390            Movement::RIGHT => {
391                if self.tape_position == self.tape.len() - 1 {
392                    self.tape.push(false);
393                }
394
395                self.tape_position += 1;
396            }
397            Movement::HALT => {}
398        }
399
400        while self.tape_position <= 2 {
401            self.tape.insert(0, false);
402            self.tape_position += 1;
403        }
404
405        while self.tape_position >= self.tape.len() - 3 {
406            self.tape.push(false);
407        }
408
409        self.update_state(instruction.to_state.clone())
410    }
411
412    /// Updates the current state and returns true if the current state is a final state
413    fn update_state(&mut self, state: String) -> bool {
414        self.previous_state = Some(self.current_state.clone());
415        self.current_state = state.clone();
416
417        if self.frequencies.contains_key(&state) {
418            let Some(f) = self.frequencies.get_mut(&state) else {
419                return self.final_states.contains(&self.current_state);
420            };
421            *f += 1;
422        } else {
423            self.frequencies.insert(state.clone(), 1);
424        }
425
426        self.final_states.contains(&self.current_state)
427    }
428
429    /// Returns true if the current state has been reached more times than the given threshold
430    pub fn is_infinite_loop(&self, threshold: usize) -> bool {
431        for (_, v) in self.frequencies.iter() {
432            if *v > threshold {
433                return true;
434            }
435        }
436
437        false
438    }
439
440    /// Resets the frequencies of the states
441    pub fn reset_frequencies(&mut self) {
442        self.frequencies = HashMap::new();
443    }
444
445    /// Returns true if the current state is a final state and the motion is to Halt
446    pub fn finished(&self) -> bool {
447        self.final_states.contains(&self.current_state)
448    }
449
450    /// Returns the values of the tape
451    /// (i.e. the number of 1s between each 0)
452    pub fn values(&self) -> Vec<u32> {
453        let tmp: String = self
454            .tape
455            .iter()
456            .map(|v| if *v { "1" } else { "0" })
457            .collect();
458
459        tmp.split('0')
460            .filter_map(|s| {
461                if !s.is_empty() {
462                    Some(s.len() as u32 - 1)
463                } else {
464                    None
465                }
466            })
467            .collect()
468    }
469
470    /// Returns the current output of the Turing machine
471    /// (i.e. the number of steps and the number of 1s on the tape,
472    /// or undefined if the Turing machine is in an undefined state)
473    pub fn tape_value(&self) -> TuringOutput {
474        if self.is_undefined() {
475            return TuringOutput::Undefined(0);
476        }
477
478        TuringOutput::Defined((0, self.tape.iter().map(|v| if *v { 1 } else { 0 }).sum()))
479    }
480
481    /// Returns the final output of the Turing machine directly
482    /// (i.e. keeps calculating the next step until the current state is a final state)
483    pub fn final_result(&mut self) -> TuringOutput {
484        let mut steps = 0;
485
486        while !self.finished() {
487            self.step();
488            steps += 1;
489        }
490
491        self.step();
492        steps += 1;
493
494        TuringOutput::Defined((
495            steps,
496            self.tape.iter().map(|v| if *v { 1 } else { 0 }).sum(),
497        ))
498    }
499
500    /// Returns the value of the tape at the given index, or None if the index is out of bounds
501    pub fn get(&self, i: usize) -> Option<bool> {
502        if i >= self.tape.len() {
503            return None;
504        }
505
506        Some(self.tape[i])
507    }
508}
509
510impl Display for TuringMachine {
511    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
512        let mut tmp2 = String::new();
513        for (i, v) in self.tape.iter().enumerate() {
514            write!(f, "{} ", if *v { "1" } else { "0" }).unwrap();
515
516            if i == self.tape_position {
517                tmp2 += "^ ";
518            } else {
519                tmp2 += "  ";
520            }
521        }
522
523        write!(f, "\n{}", tmp2)
524    }
525}