pilota_thrift_parser/parser/
mod.rs1mod 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}