amethyst/
advanced_parser.rs

1#![allow(dead_code)]
2
3use crate::syntax::{
4    AutomatonType, Machine, MacroType, Move, Program, State, StateType, Transition,
5};
6
7const ALLOWED_NAME_SYMBOLS: &str = "abcdefghijklmnopqrstuvwxyz0123456789_.";
8const ALLOWED_TAPE_SYMBOLS: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*[]-+=/?_:";
9const BREAK_SYMBOLS: &str = "(){};=, \n\"\'";
10
11// Check if a character is valid for a name
12fn allowed_name(x: &char) -> bool {
13    ALLOWED_NAME_SYMBOLS.contains(*x)
14}
15
16// Check if a character is valid for a name
17fn allowed_tape(x: &char) -> bool {
18    ALLOWED_TAPE_SYMBOLS.contains(*x)
19}
20
21// Check if a character is valid for a literal
22fn literal(x: &char) -> bool {
23    !BREAK_SYMBOLS.contains(*x)
24}
25
26// Content left to parse, with a header to the current position
27#[derive(Copy, Clone, PartialEq, Debug)]
28pub struct Leftover<'a> {
29    pub input: &'a str,
30    pub row: u32,
31    pub col: u32,
32}
33
34// mutating and non-mutating lookahead
35impl<'a> Leftover<'a> {
36    // mutating lookahead
37    fn advance(&mut self) -> Option<char> {
38        let mut chars = self.input.chars();
39        match chars.next() {
40            Some(c) => {
41                self.input = chars.as_str();
42                if c != '\n' {
43                    self.col += 1;
44                } else {
45                    self.row += 1;
46                    self.col = 0;
47                }
48                Some(c)
49            }
50            None => None,
51        }
52    }
53    // non mutating lookahead
54    fn look(&self) -> Option<char> {
55        match self.input.chars().nth(0) {
56            Some(c) => Some(c),
57            None => None,
58        }
59    }
60    // mutating literal lookahead
61    fn advance_literal(&mut self) -> usize {
62        let index = self
63            .input
64            .char_indices()
65            .find(|(_, c)| !literal(c))
66            .map(|(i, _)| i)
67            .unwrap_or(self.input.len());
68        self.col += index as u32;
69        index
70    }
71    // non-mutating literal lookahead
72    fn look_literal(&self) -> &str {
73        let index = self
74            .input
75            .char_indices()
76            .find(|(_, c)| !literal(c))
77            .map(|(i, _)| i)
78            .unwrap_or(self.input.len());
79
80        &self.input[..index]
81    }
82}
83
84// Helper trait for parsers that return vectors
85trait IsEmpty {
86    fn is_empty(&self) -> bool;
87}
88
89impl<T> IsEmpty for Vec<T> {
90    fn is_empty(&self) -> bool {
91        self.len() == 0
92    }
93}
94
95impl IsEmpty for &str {
96    fn is_empty(&self) -> bool {
97        self.len() == 0
98    }
99}
100
101// Encapsulated function for parser combinators
102struct Parser<'a, T> {
103    parse: Box<dyn Fn(Leftover<'a>) -> Option<(Leftover<'a>, Result<T, String>)> + 'a>,
104}
105
106// Implementation functor, applicative, and alternative instances + condition, not_null, many, and some
107impl<'a, T: 'a> Parser<'a, T> {
108    // (<$>) operator
109    fn fmap<B: 'a, F>(self, f: F) -> Parser<'a, B>
110    where
111        F: Fn(T) -> B + 'a,
112    {
113        Parser {
114            parse: Box::new(move |input| match (self.parse)(input) {
115                None => None,
116                Some((leftover, res)) => Some((
117                    leftover,
118                    match res {
119                        Err(msg) => Err(msg),
120                        Ok(x) => Ok(f(x)),
121                    },
122                )),
123            }),
124        }
125    }
126
127    // (<*>) operator
128    fn ap<A: 'a, B: 'a>(self, p: Parser<'a, A>) -> Parser<'a, B>
129    where
130        T: Fn(A) -> B,
131    {
132        Parser {
133            parse: Box::new(move |input| match (self.parse)(input) {
134                None => None,
135                Some((leftover1, res1)) => match res1 {
136                    Err(msg) => Some((leftover1, Err(msg))),
137                    Ok(f) => match (p.parse)(leftover1) {
138                        None => None,
139                        Some((leftover2, res2)) => Some((
140                            leftover2,
141                            match res2 {
142                                Err(msg) => Err(msg),
143                                Ok(x) => Ok(f(x)),
144                            },
145                        )),
146                    },
147                },
148            }),
149        }
150    }
151
152    // (<*) operator
153    fn left<B: 'a>(self, p: Parser<'a, B>) -> Parser<'a, T> {
154        Parser {
155            parse: Box::new(move |input| match (self.parse)(input) {
156                None => None,
157                Some((leftover1, res1)) => match res1 {
158                    Err(msg) => Some((leftover1, Err(msg))),
159                    Ok(x) => match (p.parse)(leftover1) {
160                        None => None,
161                        Some((leftover2, res2)) => match res2 {
162                            Err(msg) => Some((leftover2, Err(msg))),
163                            Ok(_) => Some((leftover2, Ok(x))),
164                        },
165                    },
166                },
167            }),
168        }
169    }
170
171    // (*>) operator
172    fn right<B: 'a>(self, p: Parser<'a, B>) -> Parser<'a, B> {
173        Parser {
174            parse: Box::new(move |input| match (self.parse)(input) {
175                None => None,
176                Some((leftover1, res1)) => match res1 {
177                    Err(msg) => Some((leftover1, Err(msg))),
178                    Ok(_) => match (p.parse)(leftover1) {
179                        None => None,
180                        Some((leftover2, res2)) => match res2 {
181                            Err(msg) => Some((leftover2, Err(msg))),
182                            Ok(y) => Some((leftover2, Ok(y))),
183                        },
184                    },
185                },
186            }),
187        }
188    }
189
190    // (<|>) operator
191    fn or(self, p2: Parser<'a, T>) -> Parser<'a, T> {
192        Parser {
193            parse: Box::new(move |input| match (self.parse)(input) {
194                Some(output) => Some(output),
195                None => (p2.parse)(input),
196            }),
197        }
198    }
199
200    // check if the parsed content passes a certain condition
201    fn condition<C>(self, cond: C) -> Parser<'a, T>
202    where
203        C: Fn(&T) -> bool + 'a,
204    {
205        Parser {
206            parse: Box::new(move |input| match (self.parse)(input) {
207                None => None,
208                Some((leftover, res)) => match res {
209                    Ok(results) => {
210                        if cond(&results) {
211                            Some((leftover, Ok(results)))
212                        } else {
213                            None
214                        }
215                    }
216                    err => Some((leftover, err)),
217                },
218            }),
219        }
220    }
221
222    // condition parser with custom error
223    fn condition_e<C>(self, cond: C, msg: &'a str) -> Parser<'a, T>
224    where
225        C: Fn(&T) -> bool + 'a,
226    {
227        Parser {
228            parse: Box::new(move |input| match (self.parse)(input) {
229                None => None,
230                Some((leftover, res)) => Some((
231                    leftover,
232                    match res {
233                        Ok(results) => {
234                            if cond(&results) {
235                                Ok(results)
236                            } else {
237                                Err(msg.to_string())
238                            }
239                        }
240                        err => err,
241                    },
242                )),
243            }),
244        }
245    }
246
247    // check that the vector has at least one element
248    fn not_null(self) -> Parser<'a, T>
249    where
250        T: IsEmpty,
251    {
252        self.condition(|v| !v.is_empty())
253    }
254
255    // not_null with custom error
256    fn not_null_e(self, msg: &'a str) -> Parser<'a, T>
257    where
258        T: IsEmpty,
259    {
260        self.condition_e(|v| !v.is_empty(), msg)
261    }
262
263    // 0 or more chained parsers of the same type
264    fn many(self) -> Parser<'a, Vec<T>> {
265        Parser {
266            parse: Box::new(move |input| {
267                let mut results = Vec::new();
268                let mut current_input = input;
269                while let Some((next_input, res)) = (self.parse)(current_input) {
270                    match res {
271                        Err(msg) => return Some((next_input, Err(msg))),
272                        Ok(x) => {
273                            results.push(x);
274                            current_input = next_input;
275                        }
276                    }
277                }
278                Some((current_input, Ok(results)))
279            }),
280        }
281    }
282
283    // 1 or more chained parsers of the same type
284    fn some(self) -> Parser<'a, Vec<T>> {
285        self.many().not_null()
286    }
287
288    // chaining parsers that pass a condition
289    fn span<C>(self, cond: C) -> Parser<'a, Vec<T>>
290    where
291        C: Fn(&T) -> bool + 'a,
292    {
293        self.condition(cond).many()
294    }
295
296    // span parser with custom error
297    fn span_e<C>(self, cond: C, msg: &'a str) -> Parser<'a, Vec<T>>
298    where
299        C: Fn(&T) -> bool + 'a,
300    {
301        self.condition_e(cond, msg).many()
302    }
303
304    // raise an error if the parser fails
305    fn raise(self, msg: &'a str) -> Parser<'a, T> {
306        Parser {
307            parse: Box::new(move |input| match (self.parse)(input) {
308                Some(result) => Some(result),
309                None => Some((input, Err(msg.to_string()))),
310            }),
311        }
312    }
313    // raise an error and append the current char
314    fn raise_look(self, msg: &'a str) -> Parser<'a, T> {
315        Parser {
316            parse: Box::new(move |input| match (self.parse)(input) {
317                Some(result) => Some(result),
318                None => Some((
319                    input,
320                    Err(format!(
321                        "{}{}",
322                        msg,
323                        match input.look() {
324                            None => "no more input".to_string(),
325                            Some(c) => c.to_string(),
326                        }
327                    )),
328                )),
329            }),
330        }
331    }
332    // raise an error and append the literal to the end
333    fn raise_literal(self, prefix: &'a str, msg: &'a str, postfix: &'a str) -> Parser<'a, T> {
334        Parser {
335            parse: Box::new(move |input| match (self.parse)(input) {
336                Some(result) => Some(result),
337                None => Some((
338                    input,
339                    Err(format!(
340                        "{}{}{}{}",
341                        prefix,
342                        msg,
343                        postfix,
344                        input.look_literal()
345                    )),
346                )),
347            }),
348        }
349    }
350}
351
352// parse a single character
353fn any_char_p<'a>() -> Parser<'a, char> {
354    Parser {
355        parse: Box::new(move |mut input| {
356            let current = input.advance();
357            match current {
358                None => None,
359                Some(c) => Some((input, Ok(c))),
360            }
361        }),
362    }
363}
364
365// fail if the character differs
366fn char_p<'a>(x: char) -> Parser<'a, char> {
367    Parser {
368        parse: Box::new(move |mut input| {
369            let current = input.advance();
370            match current {
371                None => None,
372                Some(c) if c != x => None,
373                _ => Some((input, Ok(x))),
374            }
375        }),
376    }
377}
378
379// raise an error if the character differs
380fn char_pe<'a>(x: char) -> Parser<'a, char> {
381    Parser {
382        parse: Box::new(move |mut input| {
383            let current = input.advance();
384            match current {
385                None => Some((input, Err(format!("Expected {} found no more input", x)))),
386                Some(c) if c != x => Some((input, Err(format!("Expected {} found {}", x, c)))),
387                _ => Some((input, Ok(x))),
388            }
389        }),
390    }
391}
392
393// make sure the current character is not x without consuming it
394fn not_char_p<'a>(x: char) -> Parser<'a, ()> {
395    Parser {
396        parse: Box::new(move |input| match input.look() {
397            Some(c) if c == x => None,
398            _ => Some((input, Ok(()))),
399        }),
400    }
401}
402
403// parser for a single move symbol
404fn move_pe<'a>() -> Parser<'a, Move> {
405    (char_p('L').fmap(|_| Move::Left))
406        .or(char_p('R').fmap(|_| Move::Right))
407        .or(char_p('N').fmap(|_| Move::Neutral))
408        .raise_look("Expected move symbol found ")
409}
410
411// whitespace parser, never files
412fn ws0<'a>() -> Parser<'a, &'a str> {
413    char_p(' ').many().fmap(|_| "")
414}
415
416// whitespace parser + new lines, never fails
417fn ws1<'a>() -> Parser<'a, &'a str> {
418    (char_p(' ').or(char_p('\n'))).many().fmap(|_| "")
419}
420
421// parser for a single tape symbol
422fn symbol_p<'a>() -> Parser<'a, char> {
423    any_char_p().condition(allowed_tape)
424}
425
426// symbol parser that raises an error
427fn symbol_pe<'a>() -> Parser<'a, char> {
428    symbol_p().raise_look("Expected tape symbol found ")
429}
430
431// parse any literal (string that has no break symbols)
432fn literal_p<'a>() -> Parser<'a, &'a str> {
433    Parser {
434        parse: Box::new(|mut input| {
435            let index = input.advance_literal();
436            let lit = &input.input[..index];
437            input.input = &input.input[index..];
438            Some((input, Ok(lit)))
439        }),
440    }
441}
442
443// literal with allowed name characters
444fn word_pe<'a>() -> Parser<'a, &'a str> {
445    literal_p()
446        .condition(|s| s.chars().all(|c| allowed_name(&c)))
447        .raise_literal("", "Forbidden symbol in word ", "")
448}
449
450// literal with allowed tape characters
451fn tape_pe<'a>() -> Parser<'a, &'a str> {
452    literal_p()
453        .condition(|s| s.chars().all(|c| allowed_tape(&c)))
454        .raise_literal("", "Forbidden symbol in tape sequence ", "")
455}
456
457// parse a specific string, using &str for efficiency
458fn string_p<'a>(s: &'a str) -> Parser<'a, &'a str> {
459    Parser {
460        parse: Box::new(move |mut input| {
461            let mut chars = s.chars();
462            while let Some(expected) = chars.next() {
463                match input.advance() {
464                    Some(c) if c == expected => {}
465                    _ => return None,
466                }
467            }
468            Some((input, Ok(s)))
469        }),
470    }
471}
472
473// string parser that raises an error
474fn string_pe<'a>(s: &'a str) -> Parser<'a, &'a str> {
475    string_p(s).raise_literal("Expected ", s, " found ")
476}
477
478// flipped arguments for fmap
479fn fmake<'a, A: 'a, B: 'a, F>(f: F) -> Parser<'a, F>
480where
481    F: Fn(A) -> B + 'a,
482    F: Clone,
483{
484    Parser {
485        parse: Box::new(move |input| Some((input, Ok(f.clone())))),
486    }
487}
488
489// parser for transition
490fn transition_pe<'a>() -> Parser<'a, Transition> {
491    fmake(|read_sym| {
492        move |write_sym| move |move_sym| move |new_state| (read_sym, write_sym, move_sym, new_state)
493    })
494    .ap(ws1().right(symbol_pe()).left(ws0()).left(char_pe('/')))
495    .ap(ws0().right(symbol_pe()).left(ws0()).left(char_pe(',')))
496    .ap(ws0().right(move_pe()).left(ws1()).left(string_pe("->")))
497    .ap(ws0().right(
498        word_pe()
499            .not_null_e("Expected new state")
500            .left(ws0())
501            .left(char_pe(';')),
502    ))
503    .fmap(
504        |(read_symbol, write_symbol, move_symbol, new_state)| Transition {
505            read_symbol,
506            write_symbol,
507            move_symbol,
508            new_state: new_state.to_string(),
509        },
510    )
511}
512
513// whitespace parser that requires at least one blank character
514fn ws2<'a>() -> Parser<'a, &'a str> {
515    (char_p(' ').or(char_p('\n')))
516        .some()
517        .fmap(|_| "")
518        .raise("Expected space")
519}
520
521// make sure a character is not followed by another, consume only the first char
522fn not_followed<'a>(x: char, y: char) -> Parser<'a, char> {
523    Parser {
524        parse: Box::new(move |mut input| {
525            let current = input.advance();
526            match current {
527                None => None,
528                Some(c) if c != x => Some((input, Ok(x))),
529                _ => match input.look() {
530                    None => Some((input, Ok(x))),
531                    Some(z) if z != y => Some((input, Ok(x))),
532                    _ => None,
533                },
534            }
535        }),
536    }
537}
538
539// parser for line and block comments
540fn comment_pe<'a>() -> Parser<'a, &'a str> {
541    string_p("--")
542        .right(any_char_p().span(|x| *x != '\n'))
543        .or(string_p("{-")
544            .right(not_followed('-', '}').many())
545            .left(string_pe("-}")))
546        .fmap(|_| "")
547}
548
549// whitespace + comments parser
550fn ws3<'a>() -> Parser<'a, &'a str> {
551    ws1().left(comment_pe().right(ws1()).many())
552}
553
554// parser for state
555fn state_pe<'a>() -> Parser<'a, StateType> {
556    let final_p = fmake(|b: bool| {
557        move |s: &str| {
558            if b {
559                StateType::Accept(s.to_string())
560            } else {
561                StateType::Reject(s.to_string())
562            }
563        }
564    })
565    .ap(string_p("reject")
566        .fmap(|_| false)
567        .or(string_p("accept").fmap(|_| true)))
568    .ap(ws2()
569        .right(string_pe("state"))
570        .right(ws2())
571        .right(word_pe().not_null_e("Expected state name"))
572        .left(ws0())
573        .left(char_pe(';')));
574    let initial_p = || (string_p("initial").right(ws2()).fmap(|_| true)).or(ws1().fmap(|_| false));
575    let arrow_p = fmake(move |b: bool| {
576        move |from: &'a str| {
577            move |to: &'a str| {
578                StateType::State(
579                    from.to_string(),
580                    State {
581                        initial: b,
582                        transitions: Box::new(vec![Transition {
583                            read_symbol: '_',
584                            write_symbol: '_',
585                            move_symbol: Move::Neutral,
586                            new_state: to.to_string(),
587                        }]),
588                    },
589                )
590            }
591        }
592    })
593    .ap(initial_p())
594    .ap(string_pe("state")
595        .right(ws2())
596        .right(word_pe().not_null_e("Expected state name")))
597    .ap(ws1()
598        .right(string_p("->"))
599        .right(ws1())
600        .right(word_pe().not_null_e("Expected state name"))
601        .left(ws0())
602        .left(char_pe(';')));
603    let tr_p = ws3()
604        .right(not_char_p('}').right(transition_pe()).left(ws3()).many())
605        .not_null_e("States can't have 0 transitions");
606    let normal_p = fmake(move |b| {
607        move |from: &'a str| {
608            move |trans| {
609                StateType::State(
610                    from.to_string(),
611                    State {
612                        initial: b,
613                        transitions: Box::new(trans),
614                    },
615                )
616            }
617        }
618    })
619    .ap(initial_p())
620    .ap(string_pe("state")
621        .right(ws2())
622        .right(word_pe().not_null_e("Expected state name")))
623    .ap(ws1().right(char_pe('{')).right(tr_p).left(char_pe('}')));
624    final_p.or(arrow_p).or(normal_p)
625}
626
627// parse list of &str separated by the parser argument
628fn sep_by<'a, B: 'a>(
629    p1: Parser<'a, &'a str>,
630    p2: Parser<'a, &'a str>,
631    sep: Parser<'a, B>,
632) -> Parser<'a, Vec<&'a str>> {
633    fmake(move |element: &'a str| {
634        move |mut list: Vec<&'a str>| {
635            list.insert(0, element);
636            list
637        }
638    })
639    .ap(p1)
640    .ap(sep.right(p2).many())
641    .or(Parser {
642        parse: Box::new(move |input| Some((input, Ok(vec![])))),
643    })
644}
645
646// parse list of (&str, &str) separated by the parser argument
647fn sep_by2<'a, B: 'a>(
648    p1: Parser<'a, (&'a str, &'a str)>,
649    p2: Parser<'a, (&'a str, &'a str)>,
650    sep: Parser<'a, B>,
651) -> Parser<'a, Vec<(&'a str, &'a str)>> {
652    fmake(move |element: (&'a str, &'a str)| {
653        move |mut list: Vec<(&'a str, &'a str)>| {
654            list.insert(0, element);
655            list
656        }
657    })
658    .ap(p1)
659    .ap(sep.right(p2).many())
660    .or(Parser {
661        parse: Box::new(move |input| Some((input, Ok(vec![])))),
662    })
663}
664
665// used as separator
666fn comma_p<'a>() -> Parser<'a, char> {
667    ws1().right(char_p(',')).left(ws1())
668}
669
670// a single pair of two string
671fn pair_p<'a>() -> Parser<'a, (&'a str, &'a str)> {
672    fmake(move |first: &'a str| move |second: &'a str| (first, second))
673        .ap(word_pe().not_null())
674        .ap(ws2().right(word_pe()))
675}
676
677// pair parser that raises a component related error
678fn pair_pe<'a>() -> Parser<'a, (&'a str, &'a str)> {
679    fmake(move |first: &'a str| move |second: &'a str| (first, second))
680        .ap(word_pe().not_null_e("Expected component type"))
681        .ap(ws2().right(word_pe().not_null_e("Expected component name")))
682}
683
684// parser for machine
685fn machine_pe<'a>() -> Parser<'a, AutomatonType> {
686    fmake(move |name: &'a str| {
687        move |components: Vec<(&'a str, &'a str)>| {
688            move |states| {
689                AutomatonType::Machine(
690                    name.to_string(),
691                    Machine {
692                        components: Box::new(
693                            components
694                                .iter()
695                                .map(|(c_type, c_name)| (c_type.to_string(), c_name.to_string()))
696                                .collect(),
697                        ),
698                        states: Box::new(states),
699                    },
700                )
701            }
702        }
703    })
704    .ap(string_p("automaton")
705        .right(ws2())
706        .right(word_pe().not_null_e("Expected automaton name"))
707        .left(ws1()))
708    .ap(not_char_p('=')
709        .right(char_pe('('))
710        .right(ws1())
711        .right(sep_by2(pair_p(), pair_pe(), comma_p()))
712        .left(ws1())
713        .left(char_pe(')'))
714        .left(ws1()))
715    .ap(char_pe('{')
716        .right(ws3())
717        .right(
718            not_char_p('}')
719                .right(state_pe())
720                .left(ws3())
721                .many()
722                .not_null_e("Automaton can't have 0 states"),
723        )
724        .left(char_pe('}')))
725}
726
727// macro for parsing numbers
728fn number_pe<'a>() -> Parser<'a, u32> {
729    literal_p()
730        .fmap(|s| s.to_string())
731        .condition(|s| s.chars().all(|c| c >= '0' && c <= '9'))
732        .raise_literal("", "Expected number literal found ", "")
733        .fmap(|s| s.parse::<u32>().unwrap())
734}
735
736// macro keyword parsers
737fn complement_pe<'a>() -> Parser<'a, MacroType> {
738    string_p("complement")
739        .right(ws1())
740        .right(char_pe('('))
741        .right(ws1())
742        .right(word_pe())
743        .not_null_e("Expected component name")
744        .left(char_pe(')'))
745        .fmap(|component: &'a str| MacroType::Complement(component.to_string()))
746}
747
748fn intersect_pe<'a>() -> Parser<'a, MacroType> {
749    string_p("intersect")
750        .right(ws1())
751        .right(char_pe('('))
752        .right(ws1())
753        .right(sep_by(
754            word_pe().not_null_e("Expected component list"),
755            word_pe(),
756            comma_p(),
757        ))
758        .condition_e(|v| v.len() > 1, "Expected two or more components")
759        .left(char_pe(')'))
760        .fmap(|components| {
761            MacroType::Intersect(Box::new(components.iter().map(|c| c.to_string()).collect()))
762        })
763}
764
765fn reunion_pe<'a>() -> Parser<'a, MacroType> {
766    string_p("reunion")
767        .right(ws1())
768        .right(char_pe('('))
769        .right(ws1())
770        .right(sep_by(
771            word_pe().not_null_e("Expected component list"),
772            word_pe(),
773            comma_p(),
774        ))
775        .condition_e(|v| v.len() > 1, "Expected two or more components")
776        .left(char_pe(')'))
777        .fmap(|components| {
778            MacroType::Reunion(Box::new(components.iter().map(|c| c.to_string()).collect()))
779        })
780}
781
782fn chain_pe<'a>() -> Parser<'a, MacroType> {
783    string_p("chain")
784        .right(ws1())
785        .right(char_pe('('))
786        .right(ws1())
787        .right(sep_by(
788            word_pe().not_null_e("Expected component list"),
789            word_pe(),
790            comma_p(),
791        ))
792        .condition_e(|v| v.len() > 1, "Expected two or more components")
793        .left(char_pe(')'))
794        .fmap(|components| {
795            MacroType::Chain(Box::new(components.iter().map(|c| c.to_string()).collect()))
796        })
797}
798
799fn repeat_pe<'a>() -> Parser<'a, MacroType> {
800    fmake(move |num| move |component: &'a str| MacroType::Repeat(num, component.to_string()))
801        .ap(string_p("repeat")
802            .right(ws1())
803            .right(char_pe('('))
804            .right(ws1())
805            .right(number_pe())
806            .left(ws1()))
807        .ap(char_pe(',')
808            .right(ws1())
809            .right(word_pe().not_null_e("Expected component name"))
810            .left(ws1())
811            .left(char_pe(')')))
812}
813
814fn move_mpe<'a>() -> Parser<'a, MacroType> {
815    fmake(move |move_symbol| move |num| MacroType::Move(move_symbol, num))
816        .ap(string_p("move")
817            .right(ws1())
818            .right(char_pe('('))
819            .right(ws1())
820            .right(move_pe())
821            .left(ws1()))
822        .ap(char_pe(',')
823            .right(ws1())
824            .right(number_pe())
825            .left(ws1())
826            .left(char_pe(')')))
827}
828
829fn override_pe<'a>() -> Parser<'a, MacroType> {
830    fmake(move |move_symbol| {
831        move |num| move |tape_symbol| MacroType::Override(move_symbol, num, tape_symbol)
832    })
833    .ap(string_p("override")
834        .right(ws1())
835        .right(char_pe('('))
836        .right(ws1())
837        .right(move_pe())
838        .left(ws1()))
839    .ap(char_pe(',').right(ws1()).right(number_pe()).left(ws1()))
840    .ap(char_pe(',')
841        .right(ws1())
842        .right(char_pe('\''))
843        .right(symbol_pe())
844        .left(char_pe('\''))
845        .left(ws1())
846        .left(char_pe(')')))
847}
848
849fn place_pe<'a>() -> Parser<'a, MacroType> {
850    string_p("place")
851        .right(ws1())
852        .right(char_pe('('))
853        .right(ws1())
854        .right(char_pe('\"'))
855        .right(tape_pe())
856        .left(char_pe('\"'))
857        .left(ws1())
858        .left(char_pe(')'))
859        .fmap(move |content: &'a str| MacroType::Place(content.to_string()))
860}
861
862fn shift<'a>() -> Parser<'a, MacroType> {
863    fmake(move |move_symbol| move |num| MacroType::Shift(move_symbol, num))
864        .ap(string_p("shift")
865            .right(ws1())
866            .right(char_pe('('))
867            .right(ws1())
868            .right(move_pe())
869            .left(ws1()))
870        .ap(char_pe(',')
871            .right(ws1())
872            .right(number_pe())
873            .left(ws1())
874            .left(char_pe(')')))
875}
876
877// macro type parser
878fn macro_pe<'a>() -> Parser<'a, AutomatonType> {
879    fmake(move |name: &'a str| move |macro_type| AutomatonType::Macro(name.to_string(), macro_type))
880        .ap(string_p("automaton")
881            .right(ws2())
882            .right(word_pe())
883            .not_null_e("Expected automaton name"))
884        .left(ws1())
885        .left(char_p('='))
886        .left(ws1())
887        .ap(complement_pe()
888            .or(intersect_pe())
889            .or(reunion_pe())
890            .or(chain_pe())
891            .or(repeat_pe())
892            .or(move_mpe())
893            .or(override_pe())
894            .or(place_pe())
895            .or(shift())
896            .raise_literal("", "Expected macro keyword found ", ""))
897        .left(ws0())
898        .left(char_pe(';'))
899}
900
901// parser for automaton
902fn automaton_pe<'a>() -> Parser<'a, AutomatonType> {
903    macro_pe().or(machine_pe())
904}
905
906// make sure there's no more input left to consume
907fn done_p<'a>() -> Parser<'a, ()> {
908    Parser {
909        parse: Box::new(move |input| match input.look() {
910            Some(_) => None,
911            _ => Some((input, Ok(()))),
912        }),
913    }
914}
915
916// parser for program
917fn program_pe<'a>() -> Parser<'a, Program> {
918    ws3()
919        .right(automaton_pe())
920        .left(ws3())
921        .many()
922        .left(done_p().raise_literal("", "Expected automaton found unexpected keyword ", ""))
923        .fmap(move |automata| Program {
924            automata: Box::new(automata),
925        })
926}
927
928// Exposed functions for parsing code
929
930pub fn parse_move(input: &str) -> Result<Move, String> {
931    match (move_pe().parse)(Leftover {
932        input,
933        row: 0,
934        col: 0,
935    }) {
936        None => panic!("move parser should never return none"),
937        Some((_, res)) => res,
938    }
939}
940
941pub fn parse_transition(input: &str) -> Result<Transition, String> {
942    match (transition_pe().parse)(Leftover {
943        input,
944        row: 0,
945        col: 0,
946    }) {
947        None => panic!("transition parser should never return none"),
948        Some((_, res)) => res,
949    }
950}
951
952pub fn parse_state(input: &str) -> Result<StateType, String> {
953    match (state_pe().parse)(Leftover {
954        input,
955        row: 0,
956        col: 0,
957    }) {
958        None => panic!("state parser should never return none"),
959        Some((_, res)) => res,
960    }
961}
962
963pub fn parse_automaton(input: &str) -> Result<AutomatonType, String> {
964    match (automaton_pe().parse)(Leftover {
965        input,
966        row: 0,
967        col: 0,
968    }) {
969        None => panic!("automaton parser should never return none"),
970        Some((_, res)) => res,
971    }
972}
973
974pub fn parse_program(input: &str) -> Result<Program, (Leftover, String)> {
975    match (program_pe().parse)(Leftover {
976        input,
977        row: 0,
978        col: 0,
979    }) {
980        None => panic!("program parser should never return none"),
981        Some((leftover, res)) => match res {
982            Ok(prog) => Ok(prog),
983            Err(err) => Err((leftover, err)),
984        },
985    }
986}