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)]
21pub struct TuringMachine {
23 pub instructions: HashMap<(String, bool), TuringInstruction>,
25
26 pub final_states: Vec<String>,
28
29 pub current_state: String,
31
32 pub previous_state: Option<String>,
34
35 pub tape_position: usize,
37
38 pub tape: Vec<bool>,
40
41 pub frequencies: HashMap<String, usize>,
43
44 pub description: Option<String>,
46
47 pub composed_libs: Vec<Library>,
50
51 pub code: String,
53}
54
55impl TuringMachine {
56 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 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 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 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 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 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 pub fn is_undefined(&self) -> bool {
360 self.get_instruction().is_none()
361 }
362
363 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 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 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 pub fn reset_frequencies(&mut self) {
442 self.frequencies = HashMap::new();
443 }
444
445 pub fn finished(&self) -> bool {
447 self.final_states.contains(&self.current_state)
448 }
449
450 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 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 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 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}