1use crate::literal::DefaultValue;
2use crate::{term, IResult, Parse};
3
4pub(crate) fn is_alphanum_underscore_dash(token: char) -> bool {
5 nom::AsChar::is_alphanum(token) || matches!(token, '_' | '-')
6}
7
8fn marker<S>(i: &str) -> IResult<&str, S>
9where
10 S: ::std::default::Default,
11{
12 Ok((i, S::default()))
13}
14
15impl<'a, T: Parse<'a>> Parse<'a> for Option<T> {
16 parser!(nom::combinator::opt(weedle!(T)));
17}
18
19impl<'a, T: Parse<'a>> Parse<'a> for Box<T> {
20 parser!(nom::combinator::map(weedle!(T), Box::new));
21}
22
23impl<'a, T: Parse<'a>> Parse<'a> for Vec<T> {
25 parser!(nom::multi::many0(T::parse));
26}
27
28impl<'a, T: Parse<'a>, U: Parse<'a>> Parse<'a> for (T, U) {
29 parser!(nom::sequence::tuple((T::parse, U::parse)));
30}
31
32impl<'a, T: Parse<'a>, U: Parse<'a>, V: Parse<'a>> Parse<'a> for (T, U, V) {
33 parser!(nom::sequence::tuple((T::parse, U::parse, V::parse)));
34}
35
36pub(crate) fn docstring(input: &str) -> IResult<&str, String> {
37 nom::multi::many1(nom::sequence::preceded(
38 nom::character::complete::multispace0,
39 nom::sequence::delimited(
40 nom::bytes::complete::tag("///"),
41 nom::bytes::complete::take_until("\n"),
42 nom::bytes::complete::tag("\n"),
43 ),
44 ))(input)
45 .map(|io| (io.0, io.1.join("\n")))
46}
47
48ast_types! {
49 #[derive(Copy, Default)]
51 struct Parenthesized<T> where [T: Parse<'a>] {
52 open_paren: term::OpenParen,
53 body: T,
54 close_paren: term::CloseParen,
55 }
56
57 #[derive(Copy, Default)]
59 struct Bracketed<T> where [T: Parse<'a>] {
60 open_bracket: term::OpenBracket,
61 body: T,
62 close_bracket: term::CloseBracket,
63 }
64
65 #[derive(Copy, Default)]
67 struct Braced<T> where [T: Parse<'a>] {
68 open_brace: term::OpenBrace,
69 body: T,
70 close_brace: term::CloseBrace,
71 }
72
73 #[derive(Copy, Default)]
75 struct Generics<T> where [T: Parse<'a>] {
76 open_angle: term::LessThan,
77 body: T,
78 close_angle: term::GreaterThan,
79 }
80
81 struct Punctuated<T, S> where [T: Parse<'a>, S: Parse<'a> + ::std::default::Default] {
83 list: Vec<T> = nom::multi::separated_list0(weedle!(S), weedle!(T)),
84 separator: S = marker,
85 }
86
87 struct PunctuatedNonEmpty<T, S> where [T: Parse<'a>, S: Parse<'a> + ::std::default::Default] {
89 list: Vec<T> = nom::sequence::terminated(
90 nom::multi::separated_list1(weedle!(S), weedle!(T)),
91 nom::combinator::opt(weedle!(S))
92 ),
93 separator: S = marker,
94 }
95
96 #[derive(Copy)]
100 struct Identifier<'a>(
101 &'a str = crate::whitespace::ws(nom::sequence::preceded(
104 nom::combinator::opt(nom::character::complete::char('_')),
105 nom::combinator::recognize(nom::sequence::tuple((
106 nom::bytes::complete::take_while1(nom::AsChar::is_alphanum),
107 nom::bytes::complete::take_while(is_alphanum_underscore_dash),
108 )))
109 )),
110 )
111
112 #[derive(Copy)]
114 struct Default<'a> {
115 assign: term!(=),
116 value: DefaultValue<'a>,
117 }
118
119 struct Docstring(
121 String = docstring,
122 )
123}
124
125#[cfg(test)]
126mod test {
127 use super::*;
128
129 test!(should_parse_optional_present { "one" =>
130 "";
131 Option<Identifier>;
132 is_some();
133 });
134
135 test!(should_parse_optional_not_present { "" =>
136 "";
137 Option<Identifier>;
138 is_none();
139 });
140
141 test!(should_parse_boxed { "one" =>
142 "";
143 Box<Identifier>;
144 });
145
146 test!(should_parse_vec { "one two three" =>
147 "";
148 Vec<Identifier>;
149 len() == 3;
150 });
151
152 test!(should_parse_parenthesized { "( one )" =>
153 "";
154 Parenthesized<Identifier>;
155 body.0 == "one";
156 });
157
158 test!(should_parse_bracketed { "[ one ]" =>
159 "";
160 Bracketed<Identifier>;
161 body.0 == "one";
162 });
163
164 test!(should_parse_braced { "{ one }" =>
165 "";
166 Braced<Identifier>;
167 body.0 == "one";
168 });
169
170 test!(should_parse_generics { "<one>" =>
171 "";
172 Generics<Identifier>;
173 body.0 == "one";
174 });
175
176 test!(should_parse_generics_two { "<one, two>" =>
177 "";
178 Generics<(Identifier, term!(,), Identifier)> =>
179 Generics {
180 open_angle: term!(<),
181 body: (Identifier("one"), term!(,), Identifier("two")),
182 close_angle: term!(>),
183 }
184 });
185
186 test!(should_parse_comma_separated_values { "one, two, three" =>
187 "";
188 Punctuated<Identifier, term!(,)>;
189 list.len() == 3;
190 });
191
192 test!(err should_not_parse_comma_separated_values_empty { "" =>
193 PunctuatedNonEmpty<Identifier, term!(,)>
194 });
195
196 test!(should_parse_identifier { "hello" =>
197 "";
198 Identifier;
199 0 == "hello";
200 });
201
202 test!(should_parse_numbered_identifier { "hello5" =>
203 "";
204 Identifier;
205 0 == "hello5";
206 });
207
208 test!(should_parse_underscored_identifier { "_hello_" =>
209 "";
210 Identifier;
211 0 == "hello_";
212 });
213
214 test!(should_parse_identifier_surrounding_with_spaces { " hello " =>
215 "";
216 Identifier;
217 0 == "hello";
218 });
219
220 test!(should_parse_identifier_preceding_others { "hello note" =>
221 "note";
222 Identifier;
223 0 == "hello";
224 });
225
226 test!(should_parse_identifier_attached_to_symbol { "hello=" =>
227 "=";
228 Identifier;
229 0 == "hello";
230 });
231
232 test!(should_parse_docstring { "///hello world\n" =>
233 "";
234 Docstring;
235 0 == "hello world";
236 });
237
238 test!(should_parse_multiline_docstring { "///hello\n///world\n" =>
239 "";
240 Docstring;
241 0 == "hello\nworld";
242 });
243
244 test!(should_parse_multiline_indented_docstring { "///hello\n ///world\n" =>
245 "";
246 Docstring;
247 0 == "hello\nworld";
248 });
249
250 test!(should_not_parse_docstring_with_comments { "///hello\n//comment1\n///world\n" =>
251 "//comment1\n///world\n";
252 Docstring;
253 0 == "hello";
254 });
255
256 test!(err should_not_parse_not_docstring { "" =>
257 Docstring
258 });
259}