1mod instruction;
2mod output;
3mod turing;
4mod warnings;
5
6use std::{borrow::Cow, collections::HashMap};
7
8pub use instruction::{Movement, TuringInstruction};
9pub use output::TuringOutput;
10use pest::Parser;
11use serde::{Deserialize, Serialize};
12pub use turing::{Rule, TuringMachine, TuringParser};
13pub use warnings::{CompilerError, CompilerWarning, ErrorPosition};
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct Library {
17 pub name: Cow<'static, str>,
18 pub description: Cow<'static, str>,
19 pub initial_state: Cow<'static, str>,
20 pub final_state: Cow<'static, str>,
21 pub used_states: Cow<'static, [Cow<'static, str>]>,
22 pub code: Cow<'static, str>,
23}
24
25impl Library {
26 pub fn get_instructions(
27 &self,
28 ) -> Result<HashMap<(String, bool), TuringInstruction>, CompilerError> {
29 let mut instructions: HashMap<(String, bool), TuringInstruction> = HashMap::new();
30
31 let file = match TuringParser::parse(Rule::instructions, self.code.as_ref()) {
32 Ok(mut f) => f.next().unwrap(),
33 Err(e) => panic!("{}", e),
34 };
35
36 for record in file.into_inner() {
37 let tmp = match TuringInstruction::from(record.into_inner()) {
38 Ok(i) => i,
39 Err(e) => return Err(e),
40 };
41 instructions.insert((tmp.from_state.clone(), tmp.from_value), tmp.clone());
42 }
43
44 Ok(instructions)
45 }
46}
47
48pub const LIBRARIES: [Library; 5] = [
66 Library {
67 name: Cow::Borrowed("sum"),
68 description: Cow::Borrowed("x + y"),
69 initial_state: Cow::Borrowed("q0"),
70 final_state: Cow::Borrowed("q2"),
71 used_states: Cow::Borrowed(&[
72 Cow::Borrowed("q0"),
73 Cow::Borrowed("q1"),
74 Cow::Borrowed("q2"),
75 ]),
76 code: Cow::Borrowed(include_str!("./composition/sum.tm")),
77 },
78 Library {
79 name: Cow::Borrowed("x2"),
80 description: Cow::Borrowed("x * 2"),
81 initial_state: Cow::Borrowed("q0"),
82 final_state: Cow::Borrowed("qf"),
83 used_states: Cow::Borrowed(&[
84 Cow::Borrowed("q0"),
85 Cow::Borrowed("q1"),
86 Cow::Borrowed("q2"),
87 Cow::Borrowed("q3"),
88 Cow::Borrowed("q4"),
89 Cow::Borrowed("q5"),
90 Cow::Borrowed("qf"),
91 ]),
92 code: Cow::Borrowed(include_str!("./composition/duplicate.tm")),
93 },
94 Library {
95 name: Cow::Borrowed("mod"),
96 description: Cow::Borrowed("x mod y"),
97 initial_state: Cow::Borrowed("q0"),
98 final_state: Cow::Borrowed("qf"),
99 used_states: Cow::Borrowed(&[
100 Cow::Borrowed("q0"),
101 Cow::Borrowed("q1"),
102 Cow::Borrowed("q2"),
103 Cow::Borrowed("q2"),
104 Cow::Borrowed("q4"),
105 Cow::Borrowed("q5"),
106 Cow::Borrowed("q5"),
107 Cow::Borrowed("q6"),
108 Cow::Borrowed("q7"),
109 Cow::Borrowed("q8"),
110 Cow::Borrowed("q9"),
111 Cow::Borrowed("q10"),
112 Cow::Borrowed("q11"),
113 Cow::Borrowed("qf"),
114 ]),
115 code: Cow::Borrowed(include_str!("./composition/mod.tm")),
116 },
117 Library {
118 name: Cow::Borrowed("div2"),
119 description: Cow::Borrowed("x div 2"),
120 initial_state: Cow::Borrowed("q0"),
121 final_state: Cow::Borrowed("qf"),
122 used_states: Cow::Borrowed(&[
123 Cow::Borrowed("q0"),
124 Cow::Borrowed("q1"),
125 Cow::Borrowed("q2"),
126 Cow::Borrowed("qf"),
127 ]),
128 code: Cow::Borrowed(include_str!("./composition/div2.tm")),
129 },
130 Library {
131 name: Cow::Borrowed("bound_diff"),
132 description: Cow::Borrowed("x ∸ y"),
133 initial_state: Cow::Borrowed("q0"),
134 final_state: Cow::Borrowed("qf"),
135 used_states: Cow::Borrowed(&[
136 Cow::Borrowed("q0"),
137 Cow::Borrowed("q1"),
138 Cow::Borrowed("q2"),
139 Cow::Borrowed("q3"),
140 Cow::Borrowed("q4"),
141 Cow::Borrowed("q5"),
142 Cow::Borrowed("q6"),
143 Cow::Borrowed("qf"),
144 ]),
145 code: Cow::Borrowed(include_str!("./composition/bound_diff.tm")),
146 },
147];
148
149#[cfg(test)]
150mod test_parsing {
151 use std::fs;
152
153 use crate::warnings::ErrorPosition;
154 use crate::CompilerError;
155 use crate::Rule;
156 use crate::TuringMachine;
157 use crate::TuringParser;
158 use pest::{consumes_to, parses_to};
159
160 #[test]
161 fn parse_description() {
162 let test = "/// a + b\r\n";
163
164 parses_to! {
165 parser: TuringParser,
166 input: test,
167 rule: Rule::description,
168 tokens: [
169 description(0, 11),
170 ]
171 }
172 }
173
174 #[test]
175 fn parse_tape_valid() {
176 let test = "{111011};";
177
178 parses_to! {
179 parser: TuringParser,
180 input: test,
181 rule: Rule::tape,
182 tokens: [
183 tape(0, 9, [
184 value(1, 2),
185 value(2, 3),
186 value(3, 4),
187 value(4, 5),
188 value(5, 6),
189 value(6, 7),
190 ]),
191 ]
192 }
193 }
194
195 #[test]
196 fn parse_tape_zeros() {
198 let test = "
199 {000};
200 I = {q0};
201 F = {q2};
202
203 (q0, 1, 0, R, q1);
204
205 (q1, 1, 1, R, q1);
206 (q1, 0, 0, R, q2);
207
208 (q2, 1, 0, H, q2);
209 (q2, 0, 0, H, q2);
210 ";
211
212 let tm_error = TuringMachine::new(test);
213
214 let expected: CompilerError = CompilerError::SyntaxError {
215 position: ErrorPosition::new((1, 9), None), message: String::from("Expected at least a 1 in the tape"),
217 code: String::from("000"),
218 expected: Rule::tape,
219 found: None,
220 };
221
222 assert_eq!(tm_error.unwrap_err(), expected);
223 }
224
225 #[test]
226 fn parse_initial_state() {
227 let test = "I = {q0};";
228
229 parses_to! {
230 parser: TuringParser,
231 input: test,
232 rule: Rule::initial_state,
233 tokens: [
234 initial_state(0, 9, [
235 state(5, 7)
236 ])
237 ]
238 }
239 }
240
241 #[test]
242 fn parse_final_state() {
243 let test = "F = {q2};";
244
245 parses_to! {
246 parser: TuringParser,
247 input: test,
248 rule: Rule::final_state,
249 tokens: [
250 final_state(0, 9, [
251 state(5, 7)
252 ])
253 ]
254 }
255 }
256
257 #[test]
258 fn parse_instruction() {
259 let test = "(q0, 1, 0, R, q1);";
260
261 parses_to! {
262 parser: TuringParser,
263 input: test,
264 rule: Rule::instruction,
265 tokens: [
266 instruction(0, 18, [
267 state(1, 3),
268 value(5, 6),
269 value(8, 9),
270 movement(11, 12),
271 state(14, 16)
272 ]),
273 ]
274 }
275 }
276
277 #[test]
278 fn parse_file() {
279 let unparsed_file = fs::read_to_string("Examples/Example1.tm").expect("cannot read file");
280 let (tm, _) = match TuringMachine::new(&unparsed_file) {
281 Ok(t) => t,
282 Err(e) => {
283 TuringMachine::handle_error(e);
284 std::process::exit(1);
285 }
286 };
287
288 assert_eq!(
289 tm.to_string(),
290 "0 0 0 1 1 1 1 1 0 1 1 \n ^ "
291 )
292 }
293}
294
295#[cfg(test)]
296mod test_composition {
297 use crate::Rule;
298 use crate::TuringMachine;
299 use crate::TuringOutput;
300 use crate::TuringParser;
301 use crate::LIBRARIES;
302 use pest::{consumes_to, parses_to};
303
304 #[test]
305 fn parse_composition_function_name_valid() {
306 let test = "sum_test";
307
308 parses_to! {
309 parser: TuringParser,
310 input: test,
311 rule: Rule::function_name,
312 tokens: [
313 function_name(0, 8)
314 ]
315 }
316 }
317
318 #[test]
319 fn parse_composition_valid() {
320 let test = "compose = { sum_test };";
321
322 parses_to! {
323 parser: TuringParser,
324 input: test,
325 rule: Rule::composition,
326 tokens: [
327 composition(0, 23, [
328 function_name(12, 20)
329 ])
330 ]
331 }
332 }
333
334 #[test]
335 fn parse_multiple_compositions() {
336 let test = "compose = {sum, diff};";
337
338 parses_to! {
339 parser: TuringParser,
340 input: test,
341 rule: Rule::composition,
342 tokens: [
343 composition(0, 22, [
344 function_name(11, 14),
345 function_name(16, 20)
346 ])
347 ]
348 }
349 }
350
351 #[test]
352 fn libraries() {
355 for lib in LIBRARIES {
356 let _ = lib.get_instructions();
357 }
358 }
359
360 #[test]
361 fn composition() {
364 let test = "
365 compose = {sum};
366
367 F = {q2};
368 {111011};
369 I = {q0};
370 ";
371
372 let mut tm = match TuringMachine::new(test) {
373 Ok(t) => t.0,
374 Err(e) => {
375 println!("{:?}", e);
376 std::process::exit(1);
377 }
378 };
379
380 assert_eq!(tm.final_result(), TuringOutput::Defined((5, 3)));
381
382 assert_eq!(
383 tm.to_string(),
384 "0 0 0 0 1 1 0 0 1 0 0 \n ^ "
385 );
386 }
387}