pilota_thrift_parser/parser/
mod.rs

1//! rust language descriptor and parser for thrift
2//! powered by chumsky
3//! idl descriptor: https://thrift.apache.org/docs/idl
4
5mod annotation;
6mod constant;
7mod enum_;
8pub mod error;
9mod field;
10mod function;
11mod identifier;
12mod include;
13mod literal;
14mod namespace;
15mod service;
16mod struct_;
17pub mod thrift;
18mod ty;
19mod typedef;
20
21use chumsky::prelude::*;
22use faststr::FastStr;
23
24use super::descriptor::{Components, Path};
25use crate::Ident;
26
27impl Path {
28    pub fn parse<'a>() -> impl Parser<'a, &'a str, Path, extra::Err<Rich<'a, char>>> {
29        Components::blank()
30            .ignore_then(Ident::get_parser())
31            .separated_by(just('.'))
32            .at_least(1)
33            .collect()
34            .then_ignore(Components::blank_without_newline())
35            .map(|s: Vec<String>| {
36                let idents: Vec<Ident> = s.into_iter().map(Ident::from).collect();
37                Path {
38                    segments: idents.into(),
39                }
40            })
41    }
42}
43
44impl Components {
45    pub fn list_separator<'a>() -> impl Parser<'a, &'a str, (), extra::Err<Rich<'a, char>>> {
46        Components::blank()
47            .or_not()
48            .ignore_then(one_of(",;"))
49            .ignored()
50    }
51
52    pub fn blank<'a>() -> impl Parser<'a, &'a str, (), extra::Err<Rich<'a, char>>> {
53        one_of(" \t\r\n").repeated().ignored()
54    }
55
56    pub fn comment<'a>() -> impl Parser<'a, &'a str, FastStr, extra::Err<Rich<'a, char>>> {
57        choice((
58            just("//")
59                .then(
60                    any()
61                        .and_is(just('\n').not())
62                        .repeated()
63                        .collect::<String>(),
64                )
65                .padded_by(Components::blank().or_not())
66                .map(|(start, content)| FastStr::from(format!("{}{}", start, content))),
67            just("#")
68                .then(
69                    any()
70                        .and_is(just('\n').not())
71                        .repeated()
72                        .collect::<String>(),
73                )
74                .padded_by(Components::blank().or_not())
75                .map(|(_, content)| FastStr::from(format!("//{}", content))),
76            just("/*")
77                .then(
78                    any()
79                        .and_is(just("*/").not())
80                        .repeated()
81                        .collect::<String>(),
82                )
83                .then(just("*/"))
84                .padded_by(Components::blank().or_not())
85                .map(|((start, content), end)| {
86                    FastStr::from(format!("{}{}{}", start, content, end))
87                }),
88        ))
89    }
90
91    pub fn trailing_comment<'a>() -> impl Parser<'a, &'a str, FastStr, extra::Err<Rich<'a, char>>> {
92        just(" ")
93            .repeated()
94            .ignored()
95            .then(choice((
96                just("//")
97                    .then(
98                        any()
99                            .and_is(just('\n').not())
100                            .repeated()
101                            .collect::<String>(),
102                    )
103                    .then_ignore(Components::blank().or_not())
104                    .map(|(start, content)| FastStr::from(format!("{}{}", start, content))),
105                just("#")
106                    .then(
107                        any()
108                            .and_is(just('\n').not())
109                            .repeated()
110                            .collect::<String>(),
111                    )
112                    .then_ignore(Components::blank().or_not())
113                    .map(|(_, content)| FastStr::from(format!("//{}", content))),
114                just("/*")
115                    .then(
116                        any()
117                            .and_is(just("*/").not())
118                            .repeated()
119                            .collect::<String>(),
120                    )
121                    .then(just("*/"))
122                    .then_ignore(Components::blank().or_not())
123                    .map(|((start, content), end)| {
124                        FastStr::from(format!("{}{}{}", start, content, end))
125                    }),
126            )))
127            .map(|(_, c)| c)
128    }
129
130    pub fn blank_with_comments<'a>() -> impl Parser<'a, &'a str, (), extra::Err<Rich<'a, char>>> {
131        choice((
132            just("//")
133                .then(any().and_is(just('\n').not()).repeated())
134                .ignored(),
135            just("#")
136                .then(any().and_is(just('\n').not()).repeated())
137                .ignored(),
138            just("/*")
139                .then(any().and_is(just("*/").not()).repeated())
140                .then(just("*/"))
141                .ignored(),
142            one_of(" \t\r\n").ignored(),
143        ))
144        .repeated()
145        .ignored()
146    }
147
148    pub fn blank_without_newline<'a>() -> impl Parser<'a, &'a str, (), extra::Err<Rich<'a, char>>> {
149        one_of(" \t\r").repeated().ignored()
150    }
151
152    pub fn not_alphanumeric_or_underscore<'a>()
153    -> impl Parser<'a, &'a str, char, extra::Err<Rich<'a, char>>> {
154        any()
155            .rewind()
156            .filter(|c: &char| !c.is_alphanumeric() && *c != '_')
157    }
158}
159
160#[cfg(test)]
161mod tests {
162    use super::*;
163
164    #[test]
165    fn test_blank() {
166        let _ = Components::blank().parse(" \t\r\n").unwrap();
167    }
168
169    #[test]
170    fn test_path() {
171        let p = Path::parse().parse("foo.bar.baz").unwrap();
172        assert_eq!(p.segments.len(), 3);
173        assert_eq!(p.segments[0].as_str(), "foo");
174        assert_eq!(p.segments[1].as_str(), "bar");
175        assert_eq!(p.segments[2].as_str(), "baz");
176
177        let p = Path::parse().parse("foo").unwrap();
178        assert_eq!(p.segments.len(), 1);
179        assert_eq!(p.segments[0].as_str(), "foo");
180    }
181
182    #[test]
183    fn test_comment() {
184        let _ = Components::comment().parse("// foo").unwrap();
185        let _ = Components::comment()
186            .parse("# From 133120 ~ 134143\n")
187            .unwrap();
188        let _ = Components::comment().parse("/* foo */").unwrap();
189    }
190
191    #[test]
192    fn test_trailing_comment() {
193        let _ = Components::trailing_comment().parse(" // foo").unwrap();
194        let _ = Components::trailing_comment().parse(" # foo").unwrap();
195        let _ = Components::trailing_comment().parse(" /* foo */").unwrap();
196    }
197
198    #[test]
199    fn test_blank_with_comments() {
200        let _ = Components::blank_with_comments().parse(" // foo").unwrap();
201        let _ = Components::blank_with_comments().parse(" # foo").unwrap();
202        let _ = Components::blank_with_comments()
203            .parse(" /* foo */")
204            .unwrap();
205    }
206
207    #[test]
208    fn test_blank_without_newline() {
209        let _ = Components::blank_without_newline().parse(" \t\r").unwrap();
210    }
211}