Skip to main content

amethyst/
advanced_parser.rs

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