Skip to main content

amethyst/
basic_parser.rs

1use crate::syntax::{AutomatonType, Machine, MacroType, Program};
2use crate::syntax::{Move, State};
3use crate::syntax::{StateType, Transition};
4
5const ALLOWED_NAME_SYMBOLS: &str = "abcdefghijklmnopqrstuvwxyz0123456789_.";
6const ALLOWED_TAPE_SYMBOLS: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*[]-+=/?_:";
7
8/// Use string slices (`&str`) instead of `String` for more efficient parsing.
9enum Parser<'a, T> {
10    Parser(Box<dyn Fn(&'a str) -> Option<(&'a str, T)> + 'a>),
11}
12
13fn fmap<'a, A: 'a, B: 'a, F>(f: F, p: Parser<'a, A>) -> Parser<'a, B>
14where
15    F: Fn(A) -> B + 'a,
16{
17    match p {
18        Parser::Parser(parse) => Parser::Parser(Box::new(move |input| match parse(input) {
19            None => None,
20            Some((input2, x)) => Some((input2, f(x))),
21        })),
22    }
23}
24
25fn char_p<'a>(x: char) -> Parser<'a, char> {
26    Parser::Parser(Box::new(move |input: &'a str| {
27        let mut chars = input.char_indices();
28        match chars.next() {
29            Some((_, c)) if c == x => {
30                let next_index = c.len_utf8();
31                Some((&input[next_index..], c))
32            }
33            _ => None,
34        }
35    }))
36}
37fn pure<'a, T: Clone + 'a>(x: T) -> Parser<'a, T> {
38    Parser::Parser(Box::new(move |input| Some((input, x.clone()))))
39}
40
41fn ap<'a, A: 'a, B: 'a, F>(pf: Parser<'a, F>, pa: Parser<'a, A>) -> Parser<'a, B>
42where
43    F: Fn(A) -> B + 'a,
44{
45    Parser::Parser(Box::new(move |input| {
46        let Parser::Parser(ref pf_inner) = pf;
47        let Parser::Parser(ref pa_inner) = pa;
48        pf_inner(input).and_then(|(input1, f)| pa_inner(input1).map(|(input2, x)| (input2, f(x))))
49    }))
50}
51
52fn empty<'a, T: 'a>() -> Parser<'a, T> {
53    Parser::Parser(Box::new(|_| None))
54}
55
56fn or<'a, T: 'a>(p1: Parser<'a, T>, p2: Parser<'a, T>) -> Parser<'a, T> {
57    Parser::Parser(Box::new(move |input| {
58        let Parser::Parser(ref p1_inner) = p1;
59        let Parser::Parser(ref p2_inner) = p2;
60        p1_inner(input).or_else(|| p2_inner(input))
61    }))
62}
63
64fn string_p<'a>(s: &'a str) -> Parser<'a, String> {
65    s.chars().fold(pure(String::new()), |acc, c| {
66        let f = |s: &String, ch: char| {
67            let mut new_s = s.clone();
68            new_s.push(ch);
69            new_s
70        };
71        ap(
72            fmap(
73                move |s| {
74                    let s = s.clone();
75                    move |ch| f(&s, ch)
76                },
77                acc,
78            ),
79            char_p(c),
80        )
81    })
82}
83
84fn span_p<'a, F>(f: F) -> Parser<'a, String>
85where
86    F: Fn(char) -> bool + 'a,
87{
88    Parser::Parser(Box::new(move |input: &'a str| {
89        let token: String = input.chars().take_while(|&c| f(c)).collect();
90        let len = token.chars().map(|c| c.len_utf8()).sum();
91        if !token.is_empty() {
92            Some((&input[len..], token))
93        } else {
94            Some((input, String::new()))
95        }
96    }))
97}
98
99fn not_null<'a, T: 'a + Clone>(p: Parser<'a, Vec<T>>) -> Parser<'a, Vec<T>> {
100    Parser::Parser(Box::new(move |input| {
101        let Parser::Parser(ref inner) = p;
102        inner(input).and_then(|(input2, xs)| {
103            if xs.is_empty() {
104                None
105            } else {
106                Some((input2, xs))
107            }
108        })
109    }))
110}
111
112fn not_nulls<'a>(p: Parser<'a, String>) -> Parser<'a, String> {
113    Parser::Parser(Box::new(move |input| {
114        let Parser::Parser(ref inner) = p;
115        inner(input).and_then(|(input2, xs)| {
116            if xs.is_empty() {
117                None
118            } else {
119                Some((input2, xs))
120            }
121        })
122    }))
123}
124
125fn sep_by<'a, A: 'a, B: 'a>(sep: Parser<'a, A>, element: Parser<'a, B>) -> Parser<'a, Vec<B>> {
126    let rest = Parser::Parser(Box::new(move |input| {
127        let Parser::Parser(ref sep_inner) = sep;
128        let Parser::Parser(ref element_inner) = element;
129        let mut result = Vec::new();
130        let mut current_input = input;
131        if let Some((input2, first)) = element_inner(current_input) {
132            result.push(first);
133            current_input = input2;
134            while let Some((input3, _)) = sep_inner(current_input) {
135                if let Some((input4, next)) = element_inner(input3) {
136                    result.push(next);
137                    current_input = input4;
138                } else {
139                    break;
140                }
141            }
142            Some((current_input, result))
143        } else {
144            Some((input, Vec::new()))
145        }
146    }));
147    rest
148}
149
150fn ws<'a>() -> Parser<'a, String> {
151    span_p(|c| c == ' ' || c == '\n')
152}
153
154fn ws2<'a>() -> Parser<'a, String> {
155    fmap(
156        |chars: Vec<char>| chars.into_iter().collect::<String>(),
157        not_null(fmap(|s: String| s.chars().collect::<Vec<char>>(), ws())),
158    )
159}
160fn number_p<'a>() -> Parser<'a, i32> {
161    Parser::Parser(Box::new(move |input| {
162        let Parser::Parser(ref inner) = span_p(|c| "0123456789".contains(c));
163        inner(input).and_then(|(input2, s)| match s.parse::<i32>() {
164            Ok(num) => Some((input2, num)),
165            Err(_) => None,
166        })
167    }))
168}
169
170fn word_p<'a>() -> Parser<'a, String> {
171    span_p(|c| ALLOWED_NAME_SYMBOLS.contains(c))
172}
173
174fn tape_p<'a>() -> Parser<'a, String> {
175    span_p(|c| ALLOWED_TAPE_SYMBOLS.contains(c))
176}
177
178fn symbol_p<'a>() -> Parser<'a, char> {
179    ALLOWED_TAPE_SYMBOLS
180        .chars()
181        .fold(empty(), |acc, c| or(acc, char_p(c)))
182}
183
184fn move_p<'a>() -> Parser<'a, Move> {
185    or(
186        fmap(|_| Move::Left, char_p('L')),
187        or(
188            fmap(|_| Move::Right, char_p('R')),
189            fmap(|_| Move::Neutral, char_p('N')),
190        ),
191    )
192}
193fn transition_p<'a>() -> Parser<'a, Transition> {
194    fmap(
195        |(s, (t, (m, n)))| Transition {
196            read_symbol: s,
197            write_symbol: t,
198            move_symbol: m,
199            new_state: n,
200        },
201        ap(
202            ap(
203                ap(
204                    fmap(
205                        |s| move |t| move |m| move |n| (s, (t, (m, n))),
206                        ws().and_then(symbol_p())
207                            .and_keep(ws())
208                            .and_keep(char_p('/')),
209                    ),
210                    ws().and_then(symbol_p())
211                        .and_keep(ws())
212                        .and_keep(char_p(',')),
213                ),
214                ws().and_then(move_p())
215                    .and_keep(ws())
216                    .and_keep(string_p("->")),
217            ),
218            ws().and_then(word_p()).and_keep(ws()).and_keep(char_p(';')),
219        ),
220    )
221}
222
223fn some<'a, T: 'a + Clone>(p: Parser<'a, T>) -> Parser<'a, Vec<T>> {
224    not_null(many(p))
225}
226
227fn many<'a, T: 'a>(p: Parser<'a, T>) -> Parser<'a, Vec<T>> {
228    Parser::Parser(Box::new(move |mut input| {
229        let Parser::Parser(ref inner) = p;
230        let mut result = Vec::new();
231        while let Some((input2, x)) = inner(input) {
232            result.push(x);
233            input = input2;
234        }
235        Some((input, result))
236    }))
237}
238
239fn and_then<'a, A: 'a, B: 'a>(self_p: Parser<'a, A>, next: Parser<'a, B>) -> Parser<'a, B> {
240    Parser::Parser(Box::new(move |input| {
241        let Parser::Parser(ref self_inner) = self_p;
242        self_inner(input).and_then(|(input2, _)| {
243            let Parser::Parser(ref next_inner) = next;
244            next_inner(input2)
245        })
246    }))
247}
248
249fn and_keep<'a, A: 'a + Clone, B: 'a>(self_p: Parser<'a, A>, next: Parser<'a, B>) -> Parser<'a, A> {
250    Parser::Parser(Box::new(move |input| {
251        let Parser::Parser(ref self_inner) = self_p;
252        self_inner(input).and_then(|(input2, a)| {
253            let Parser::Parser(ref next_inner) = next;
254            next_inner(input2).map(|(input3, _)| (input3, a.clone()))
255        })
256    }))
257}
258
259trait ParserExt<'a, T: 'a> {
260    fn and_then<B: 'a>(self, next: Parser<'a, B>) -> Parser<'a, B>;
261    fn and_keep<B: 'a>(self, next: Parser<'a, B>) -> Parser<'a, T>
262    where
263        T: Clone;
264}
265impl<'a, T: 'a> ParserExt<'a, T> for Parser<'a, T> {
266    fn and_then<B: 'a>(self, next: Parser<'a, B>) -> Parser<'a, B> {
267        and_then(self, next)
268    }
269    fn and_keep<B: 'a>(self, next: Parser<'a, B>) -> Parser<'a, T>
270    where
271        T: Clone,
272    {
273        and_keep(self, next)
274    }
275}
276
277fn state_p<'a>() -> Parser<'a, StateType> {
278    // Helper to parse transitions block
279    let make_transitions_block_p = || {
280        char_p('{')
281            .and_then(ws())
282            .and_then(some(transition_p()))
283            .and_keep(ws())
284            .and_keep(char_p('}'))
285    };
286
287    // Reject state parser
288    let reject_p = string_p("reject")
289        .and_then(ws2())
290        .and_then(string_p("state"))
291        .and_then(ws2())
292        .and_then(not_nulls(word_p()))
293        .and_keep(ws())
294        .and_keep(char_p(';'));
295
296    // Accept state parser
297    let accept_p = string_p("accept")
298        .and_then(ws2())
299        .and_then(string_p("state"))
300        .and_then(ws2())
301        .and_then(not_nulls(word_p()))
302        .and_keep(ws())
303        .and_keep(char_p(';'));
304
305    // Initial state parser
306    let initial_p = string_p("initial")
307        .and_then(ws2())
308        .and_then(string_p("state"))
309        .and_then(ws2())
310        .and_then(not_nulls(word_p()))
311        .and_keep(ws());
312
313    let normal_p = string_p("state")
314        .and_then(ws())
315        .and_then(not_nulls(word_p()))
316        .and_keep(ws());
317
318    // Compose the parser
319    or(
320        or(
321            fmap(|name: String| StateType::Reject(name), reject_p),
322            fmap(|name: String| StateType::Accept(name), accept_p),
323        ),
324        or(
325            ap(
326                fmap(
327                    |name: String| {
328                        let name_clone = name.clone();
329                        move |transitions| {
330                            StateType::State(
331                                name_clone.clone(),
332                                State {
333                                    transitions: Box::new(transitions),
334                                    initial: true,
335                                },
336                            )
337                        }
338                    },
339                    initial_p,
340                ),
341                make_transitions_block_p(),
342            ),
343            ap(
344                fmap(
345                    |name: String| {
346                        let name_clone = name.clone();
347                        move |transitions| {
348                            StateType::State(
349                                name_clone.clone(),
350                                State {
351                                    transitions: Box::new(transitions),
352                                    initial: false,
353                                },
354                            )
355                        }
356                    },
357                    normal_p,
358                ),
359                make_transitions_block_p(),
360            ),
361        ),
362    )
363}
364
365fn complement_p<'a>() -> Parser<'a, MacroType> {
366    fmap(
367        |name| MacroType::Complement(name),
368        string_p("complement")
369            .and_then(ws())
370            .and_then(char_p('('))
371            .and_then(ws())
372            .and_then(not_nulls(word_p()))
373            .and_keep(ws())
374            .and_keep(char_p(')')),
375    )
376}
377
378fn sep_by_word_p<'a>() -> Parser<'a, Vec<String>> {
379    sep_by(
380        ws().and_then(char_p(',')).and_keep(ws()),
381        not_nulls(word_p()),
382    )
383}
384
385fn intersect_p<'a>() -> Parser<'a, MacroType> {
386    fmap(
387        |names| MacroType::Intersect(Box::new(names)),
388        string_p("intersect")
389            .and_then(ws())
390            .and_then(char_p('('))
391            .and_then(ws())
392            .and_then(not_null(sep_by_word_p()))
393            .and_keep(ws())
394            .and_keep(char_p(')')),
395    )
396}
397
398fn reunion_p<'a>() -> Parser<'a, MacroType> {
399    fmap(
400        |names| MacroType::Reunion(Box::new(names)),
401        string_p("reunion")
402            .and_then(ws())
403            .and_then(char_p('('))
404            .and_then(ws())
405            .and_then(not_null(sep_by_word_p()))
406            .and_keep(ws())
407            .and_keep(char_p(')')),
408    )
409}
410
411fn chain_p<'a>() -> Parser<'a, MacroType> {
412    fmap(
413        |names| MacroType::Chain(Box::new(names)),
414        string_p("chain")
415            .and_then(ws())
416            .and_then(char_p('('))
417            .and_then(ws())
418            .and_then(not_null(sep_by_word_p()))
419            .and_keep(ws())
420            .and_keep(char_p(')')),
421    )
422}
423
424fn repeat_p<'a>() -> Parser<'a, MacroType> {
425    fmap(
426        |(name, n)| MacroType::Repeat(name, n as u32),
427        ap(
428            fmap(
429                |name| move |n| (name.clone(), n),
430                string_p("repeat")
431                    .and_then(ws())
432                    .and_then(char_p('('))
433                    .and_then(ws())
434                    .and_then(not_nulls(word_p()))
435                    .and_keep(ws()),
436            ),
437            char_p(',')
438                .and_then(ws())
439                .and_then(number_p())
440                .and_keep(ws())
441                .and_keep(char_p(')')),
442        ),
443    )
444}
445
446fn move_mp<'a>() -> Parser<'a, MacroType> {
447    fmap(
448        |(mv, n)| MacroType::Move(mv, n as u32),
449        ap(
450            fmap(
451                |mv| move |n| (mv, n),
452                string_p("move")
453                    .and_then(ws())
454                    .and_then(char_p('('))
455                    .and_then(ws())
456                    .and_then(move_p())
457                    .and_keep(ws()),
458            ),
459            char_p(',')
460                .and_then(ws())
461                .and_then(number_p())
462                .and_keep(ws())
463                .and_keep(char_p(')')),
464        ),
465    )
466}
467
468fn override_p<'a>() -> Parser<'a, MacroType> {
469    fmap(
470        |((mv, n), sym)| MacroType::Override(mv, n as u32, sym),
471        ap(
472            ap(
473                fmap(
474                    |mv| move |n| move |sym| ((mv, n), sym),
475                    string_p("override")
476                        .and_then(ws())
477                        .and_then(char_p('('))
478                        .and_then(ws())
479                        .and_then(move_p())
480                        .and_keep(ws()),
481                ),
482                char_p(',')
483                    .and_then(ws())
484                    .and_then(number_p())
485                    .and_keep(ws())
486                    .and_keep(char_p(','))
487                    .and_keep(ws()),
488            ),
489            char_p('\'')
490                .and_then(symbol_p())
491                .and_keep(char_p('\''))
492                .and_keep(ws())
493                .and_keep(char_p(')')),
494        ),
495    )
496}
497
498fn place_p<'a>() -> Parser<'a, MacroType> {
499    fmap(
500        |tape| MacroType::Place(tape),
501        string_p("place")
502            .and_then(ws())
503            .and_then(char_p('('))
504            .and_then(ws())
505            .and_then(char_p('"'))
506            .and_then(tape_p())
507            .and_keep(char_p('"'))
508            .and_keep(ws())
509            .and_keep(char_p(')')),
510    )
511}
512
513fn shift_p<'a>() -> Parser<'a, MacroType> {
514    fmap(
515        |(mv, n)| MacroType::Shift(mv, n as u32),
516        ap(
517            fmap(
518                |mv| move |n| (mv, n),
519                string_p("shift")
520                    .and_then(ws())
521                    .and_then(char_p('('))
522                    .and_then(ws())
523                    .and_then(move_p())
524                    .and_keep(ws()),
525            ),
526            char_p(',')
527                .and_then(ws())
528                .and_then(number_p())
529                .and_keep(ws())
530                .and_keep(char_p(')')),
531        ),
532    )
533}
534
535fn macro_p<'a>() -> Parser<'a, AutomatonType> {
536    fmap(
537        |(name, macro_kw)| AutomatonType::Macro(name, macro_kw),
538        ap(
539            fmap(
540                |name: String| move |macro_kw| (name.clone(), macro_kw),
541                string_p("automaton")
542                    .and_then(ws2())
543                    .and_then(not_nulls(word_p()))
544                    .and_keep(ws())
545                    .and_keep(char_p('='))
546                    .and_keep(ws()),
547            ),
548            or(
549                complement_p(),
550                or(
551                    intersect_p(),
552                    or(
553                        reunion_p(),
554                        or(
555                            chain_p(),
556                            or(
557                                repeat_p(),
558                                or(move_mp(), or(override_p(), or(place_p(), shift_p()))),
559                            ),
560                        ),
561                    ),
562                ),
563            ),
564        )
565        .and_keep(ws())
566        .and_keep(char_p(';')),
567    )
568}
569
570fn pair_p<'a>() -> Parser<'a, (String, String)> {
571    ap(
572        fmap(|a: String| move |b: String| (a.clone(), b), word_p()),
573        ws2().and_then(word_p()),
574    )
575}
576
577fn machine_p<'a>() -> Parser<'a, AutomatonType> {
578    fmap(
579        |(name, (pairs, states))| {
580            AutomatonType::Machine(
581                name,
582                Machine {
583                    components: Box::new(pairs),
584                    states: Box::new(states),
585                },
586            )
587        },
588        ap(
589            ap(
590                fmap(
591                    |name: String| {
592                        // this is ugly, maybe there's a way around it?
593                        let name_clone = name.clone();
594                        move |pairs: Vec<(String, String)>| {
595                            let name_clone2 = name_clone.clone();
596                            let pairs_clone = pairs.clone();
597                            move |states| (name_clone2.clone(), (pairs_clone.clone(), states))
598                        }
599                    },
600                    string_p("automaton")
601                        .and_then(ws2())
602                        .and_then(not_nulls(word_p()))
603                        .and_keep(ws()),
604                ),
605                char_p('(')
606                    .and_then(sep_by(ws().and_then(char_p(',')).and_keep(ws()), pair_p()))
607                    .and_keep(char_p(')'))
608                    .and_keep(ws()),
609            ),
610            char_p('{')
611                .and_then(ws())
612                .and_then(some(state_p().and_keep(ws())))
613                .and_keep(char_p('}')),
614        ),
615    )
616}
617
618fn automata_p<'a>() -> Parser<'a, AutomatonType> {
619    or(macro_p(), machine_p())
620}
621
622fn program_p<'a>() -> Parser<'a, Program> {
623    fmap(
624        |automata| Program {
625            automata: Box::new(automata),
626        },
627        many(ws().and_then(automata_p()).and_keep(ws())),
628    )
629}
630
631pub fn parse_move(input: &str) -> Option<Move> {
632    let Parser::Parser(parse) = move_p();
633    match parse(input) {
634        None => None,
635        Some((_, move_symbol)) => Some(move_symbol),
636    }
637}
638
639pub fn parse_transition(input: &str) -> Option<Transition> {
640    let Parser::Parser(parse) = transition_p();
641    match parse(input) {
642        None => None,
643        Some((_, transition)) => Some(transition),
644    }
645}
646
647pub fn parse_state(input: &str) -> Option<StateType> {
648    let Parser::Parser(parse) = state_p();
649    match parse(input) {
650        None => None,
651        Some((_, state)) => Some(state),
652    }
653}
654
655pub fn parse_automaton(input: &str) -> Option<AutomatonType> {
656    let Parser::Parser(parse) = automata_p();
657    match parse(input) {
658        None => None,
659        Some((_, automaton)) => Some(automaton),
660    }
661}
662
663pub fn parse_program(input: &str) -> Option<Program> {
664    let Parser::Parser(parse) = program_p();
665    match parse(input) {
666        None => None,
667        Some((_, program)) => Some(program),
668    }
669}