weedle/
common.rs

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
23/// Parses `item1 item2 item3...`
24impl<'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
36ast_types! {
37    /// Parses `( body )`
38    #[derive(Copy, Default)]
39    struct Parenthesized<T> where [T: Parse<'a>] {
40        open_paren: term::OpenParen,
41        body: T,
42        close_paren: term::CloseParen,
43    }
44
45    /// Parses `[ body ]`
46    #[derive(Copy, Default)]
47    struct Bracketed<T> where [T: Parse<'a>] {
48        open_bracket: term::OpenBracket,
49        body: T,
50        close_bracket: term::CloseBracket,
51    }
52
53    /// Parses `{ body }`
54    #[derive(Copy, Default)]
55    struct Braced<T> where [T: Parse<'a>] {
56        open_brace: term::OpenBrace,
57        body: T,
58        close_brace: term::CloseBrace,
59    }
60
61    /// Parses `< body >`
62    #[derive(Copy, Default)]
63    struct Generics<T> where [T: Parse<'a>] {
64        open_angle: term::LessThan,
65        body: T,
66        close_angle: term::GreaterThan,
67    }
68
69    /// Parses `(item1, item2, item3,...)?`
70    struct Punctuated<T, S> where [T: Parse<'a>, S: Parse<'a> + ::std::default::Default] {
71        list: Vec<T> = nom::multi::separated_list0(weedle!(S), weedle!(T)),
72        separator: S = marker,
73    }
74
75    /// Parses `item1, item2, item3, ...`
76    struct PunctuatedNonEmpty<T, S> where [T: Parse<'a>, S: Parse<'a> + ::std::default::Default] {
77        list: Vec<T> = nom::sequence::terminated(
78            nom::multi::separated_list1(weedle!(S), weedle!(T)),
79            nom::combinator::opt(weedle!(S))
80        ),
81        separator: S = marker,
82    }
83
84    /// Represents an identifier
85    ///
86    /// Follows `/_?[A-Za-z][0-9A-Z_a-z-]*/`
87    #[derive(Copy)]
88    struct Identifier<'a>(
89        // See https://heycam.github.io/webidl/#idl-names for why the leading
90        // underscore is trimmed
91        &'a str = crate::whitespace::ws(nom::sequence::preceded(
92            nom::combinator::opt(nom::character::complete::char('_')),
93            nom::combinator::recognize(nom::sequence::tuple((
94                nom::bytes::complete::take_while1(nom::AsChar::is_alphanum),
95                nom::bytes::complete::take_while(is_alphanum_underscore_dash),
96            )))
97        )),
98    )
99
100    /// Parses rhs of an assignment expression. Ex: `= 45`
101    #[derive(Copy)]
102    struct Default<'a> {
103        assign: term!(=),
104        value: DefaultValue<'a>,
105    }
106}
107
108#[cfg(test)]
109mod test {
110    use super::*;
111
112    test!(should_parse_optional_present { "one" =>
113        "";
114        Option<Identifier>;
115        is_some();
116    });
117
118    test!(should_parse_optional_not_present { "" =>
119        "";
120        Option<Identifier>;
121        is_none();
122    });
123
124    test!(should_parse_boxed { "one" =>
125        "";
126        Box<Identifier>;
127    });
128
129    test!(should_parse_vec { "one two three" =>
130        "";
131        Vec<Identifier>;
132        len() == 3;
133    });
134
135    test!(should_parse_parenthesized { "( one )" =>
136        "";
137        Parenthesized<Identifier>;
138        body.0 == "one";
139    });
140
141    test!(should_parse_bracketed { "[ one ]" =>
142        "";
143        Bracketed<Identifier>;
144        body.0 == "one";
145    });
146
147    test!(should_parse_braced { "{ one }" =>
148        "";
149        Braced<Identifier>;
150        body.0 == "one";
151    });
152
153    test!(should_parse_generics { "<one>" =>
154        "";
155        Generics<Identifier>;
156        body.0 == "one";
157    });
158
159    test!(should_parse_generics_two { "<one, two>" =>
160        "";
161        Generics<(Identifier, term!(,), Identifier)> =>
162            Generics {
163                open_angle: term!(<),
164                body: (Identifier("one"), term!(,), Identifier("two")),
165                close_angle: term!(>),
166            }
167    });
168
169    test!(should_parse_comma_separated_values { "one, two, three" =>
170        "";
171        Punctuated<Identifier, term!(,)>;
172        list.len() == 3;
173    });
174
175    test!(err should_not_parse_comma_separated_values_empty { "" =>
176        PunctuatedNonEmpty<Identifier, term!(,)>
177    });
178
179    test!(should_parse_identifier { "hello" =>
180        "";
181        Identifier;
182        0 == "hello";
183    });
184
185    test!(should_parse_numbered_identifier { "hello5" =>
186        "";
187        Identifier;
188        0 == "hello5";
189    });
190
191    test!(should_parse_underscored_identifier { "_hello_" =>
192        "";
193        Identifier;
194        0 == "hello_";
195    });
196
197    test!(should_parse_identifier_surrounding_with_spaces { "  hello  " =>
198        "";
199        Identifier;
200        0 == "hello";
201    });
202
203    test!(should_parse_identifier_preceding_others { "hello  note" =>
204        "note";
205        Identifier;
206        0 == "hello";
207    });
208
209    test!(should_parse_identifier_attached_to_symbol { "hello=" =>
210        "=";
211        Identifier;
212        0 == "hello";
213    });
214}