1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
mod ast;
mod lexer;
use lalrpop_util::ParseError;

pub use ast::Ast;

lalrpop_util::lalrpop_mod!(
    #[allow(clippy::ptr_arg)]
    #[rustfmt::skip]
    pub grammar,
    "/parser/grammar.rs"
);

#[derive(Debug, Clone, PartialEq)]
pub enum Error {
    ParseError,
    DanglingEscape,
    UnnecessaryEscape,
}

impl Error {
    fn from_lalrpop(err: ParseError<usize, lexer::Tok, Error>) -> Self {
        match err {
            ParseError::User { error } => error,
            _ => Error::ParseError,
        }
    }
}

pub fn parse(code: &str) -> Result<ast::Ast, Error> {
    let tokens = lexer::Lexer::new(code).map(|tok| {
        if let Some(err) = tok.get_error() {
            Err(err)
        } else {
            let (start, end) = tok.bounds();
            Ok((start, tok.data, end))
        }
    });

    grammar::AstParser::new()
        .parse(tokens)
        .map_err(Error::from_lalrpop)
}

#[cfg(test)]
mod tests {
    use super::{
        parse,
        Ast::{Byte, CaseSensitive, Digit, Or, Seq, ZeroOrOne},
    };

    #[test]
    fn parse_group() {
        assert_eq!(
            parse(r"fallac(y|ies)"),
            Ok(Seq(vec![
                Byte(b'f'),
                Byte(b'a'),
                Byte(b'l'),
                Byte(b'l'),
                Byte(b'a'),
                Byte(b'c'),
                Or(
                    Box::new(Byte(b'y')),
                    Box::new(Seq(vec![Byte(b'i'), Byte(b'e'), Byte(b's')]))
                )
            ]))
        )
    }

    #[test]
    fn parse_basic_escape() {
        assert_eq!(
            parse(r"foo\??"),
            Ok(Seq(vec![
                Byte(b'f'),
                Byte(b'o'),
                Byte(b'o'),
                ZeroOrOne(Box::new(Byte(b'?')))
            ]))
        )
    }

    #[test]
    fn case_sensitive() {
        assert_eq!(
            parse(r"foo(!b|AR)"),
            Ok(Seq(vec![
                Byte(b'f'),
                Byte(b'o'),
                Byte(b'o'),
                CaseSensitive(Box::new(Or(
                    Box::new(Byte(b'b')),
                    Box::new(Seq(vec![Byte(b'A'), Byte(b'R'),]))
                )))
            ]))
        )
    }

    #[test]
    fn parse_digits() {
        assert_eq!(
            parse(r"\d?.\d\d"),
            Ok(Seq(vec![
                ZeroOrOne(Box::new(Digit)),
                Byte(b'.'),
                Digit,
                Digit
            ]))
        )
    }

    #[test]
    fn parse_unicode() {
        assert_eq!(
            parse(r"Ⲁ(ⲗⲗ)?ⲫⲁ"),
            Ok(Seq(vec![
                Byte(226),
                Byte(178),
                Byte(128),
                ZeroOrOne(Box::new(Seq(vec![
                    Byte(226),
                    Byte(178),
                    Byte(151),
                    Byte(226),
                    Byte(178),
                    Byte(151),
                ]))),
                Byte(226),
                Byte(178),
                Byte(171),
                Byte(226),
                Byte(178),
                Byte(129)
            ]))
        )
    }
}