weedle/
lib.rs

1//! Weedle - A WebIDL Parser
2//!
3//! Parses valid WebIDL definitions & produces a data structure starting from
4//! [`Definitions`](struct.Definitions.html).
5//!
6//! ### Example
7//!
8//! ```
9//! extern crate weedle;
10//!
11//! let parsed = weedle::parse("
12//!     interface Window {
13//!         readonly attribute Storage sessionStorage;
14//!     };
15//! ").unwrap();
16//! println!("{:?}", parsed);
17//! ```
18//!
19//! Note:
20//! This parser follows the grammar given at [WebIDL](https://heycam.github.io/webidl).
21//!
22//! If any flaws found when parsing string with a valid grammar, create an issue.
23
24use self::argument::ArgumentList;
25use self::attribute::ExtendedAttributeList;
26use self::common::{Braced, Identifier, Parenthesized, PunctuatedNonEmpty};
27use self::dictionary::DictionaryMembers;
28use self::interface::{Inheritance, InterfaceMembers};
29use self::literal::StringLit;
30use self::mixin::MixinMembers;
31use self::namespace::NamespaceMembers;
32use self::types::{AttributedType, ReturnType};
33pub use nom::{error::Error, Err, IResult};
34
35#[macro_use]
36mod macros;
37#[macro_use]
38mod whitespace;
39#[macro_use]
40pub mod term;
41pub mod argument;
42pub mod attribute;
43pub mod common;
44pub mod dictionary;
45pub mod interface;
46pub mod literal;
47pub mod mixin;
48pub mod namespace;
49pub mod types;
50
51/// A convenient parse function
52///
53/// ### Example
54///
55/// ```
56/// extern crate weedle;
57///
58/// let parsed = weedle::parse("
59///     interface Window {
60///         readonly attribute Storage sessionStorage;
61///     };
62/// ").unwrap();
63///
64/// println!("{:?}", parsed);
65/// ```
66pub fn parse(raw: &str) -> Result<Definitions<'_>, Err<Error<&str>>> {
67    let (remaining, parsed) = Definitions::parse(raw)?;
68
69    #[cfg(debug_assertions)]
70    assert!(
71        remaining.is_empty(),
72        "There is redundant raw data after parsing `{remaining}`"
73    );
74    #[cfg(not(debug_assertions))]
75    assert!(
76        remaining.is_empty(),
77        "There is redundant raw data after parsing"
78    );
79
80    Ok(parsed)
81}
82
83pub trait Parse<'a>: Sized {
84    fn parse(input: &'a str) -> IResult<&'a str, Self>;
85}
86
87/// Parses WebIDL definitions. It is the root struct for a complete WebIDL definition.
88///
89/// ### Example
90/// ```
91/// use weedle::{Definitions, Parse};
92///
93/// let (_, parsed) = Definitions::parse("
94///     interface Window {
95///         readonly attribute Storage sessionStorage;
96///     };
97/// ").unwrap();
98///
99/// println!("{:?}", parsed);
100/// ```
101///
102/// It is recommended to use [`parse`](fn.parse.html) instead.
103pub type Definitions<'a> = Vec<Definition<'a>>;
104
105ast_types! {
106    /// Parses a definition
107    enum Definition<'a> {
108        /// Parses `[attributes]? callback identifier = type ( (arg1, arg2, ..., argN)? );`
109        Callback(struct CallbackDefinition<'a> {
110            attributes: Option<ExtendedAttributeList<'a>>,
111            callback: term!(callback),
112            identifier: Identifier<'a>,
113            assign: term!(=),
114            return_type: ReturnType<'a>,
115            arguments: Parenthesized<ArgumentList<'a>>,
116            semi_colon: term!(;),
117        }),
118        /// Parses `[attributes]? callback interface identifier ( : inheritance )? { members };`
119        CallbackInterface(struct CallbackInterfaceDefinition<'a> {
120            attributes: Option<ExtendedAttributeList<'a>>,
121            callback: term!(callback),
122            interface: term!(interface),
123            identifier: Identifier<'a>,
124            inheritance: Option<Inheritance<'a>>,
125            members: Braced<InterfaceMembers<'a>>,
126            semi_colon: term!(;),
127        }),
128        /// Parses `[attributes]? interface identifier ( : inheritance )? { members };`
129        Interface(struct InterfaceDefinition<'a> {
130            attributes: Option<ExtendedAttributeList<'a>>,
131            interface: term!(interface),
132            identifier: Identifier<'a>,
133            inheritance: Option<Inheritance<'a>>,
134            members: Braced<InterfaceMembers<'a>>,
135            semi_colon: term!(;),
136        }),
137        /// Parses `[attributes]? interface mixin identifier { members };`
138        InterfaceMixin(struct InterfaceMixinDefinition<'a> {
139            attributes: Option<ExtendedAttributeList<'a>>,
140            interface: term!(interface),
141            mixin: term!(mixin),
142            identifier: Identifier<'a>,
143            members: Braced<MixinMembers<'a>>,
144            semi_colon: term!(;),
145        }),
146        /// Parses `[attributes]? namespace identifier { members };`
147        Namespace(struct NamespaceDefinition<'a> {
148            attributes: Option<ExtendedAttributeList<'a>>,
149            namespace: term!(namespace),
150            identifier: Identifier<'a>,
151            members: Braced<NamespaceMembers<'a>>,
152            semi_colon: term!(;),
153        }),
154        /// Parses `[attributes]? dictionary identifier ( : inheritance )? { members };`
155        Dictionary(struct DictionaryDefinition<'a> {
156            attributes: Option<ExtendedAttributeList<'a>>,
157            dictionary: term!(dictionary),
158            identifier: Identifier<'a>,
159            inheritance: Option<Inheritance<'a>>,
160            members: Braced<DictionaryMembers<'a>>,
161            semi_colon: term!(;),
162        }),
163        /// Parses `[attributes]? partial interface identifier { members };`
164        PartialInterface(struct PartialInterfaceDefinition<'a> {
165            attributes: Option<ExtendedAttributeList<'a>>,
166            partial: term!(partial),
167            interface: term!(interface),
168            identifier: Identifier<'a>,
169            members: Braced<InterfaceMembers<'a>>,
170            semi_colon: term!(;),
171        }),
172        /// Parses `[attributes]? partial interface mixin identifier { members };`
173        PartialInterfaceMixin(struct PartialInterfaceMixinDefinition<'a> {
174            attributes: Option<ExtendedAttributeList<'a>>,
175            partial: term!(partial),
176            interface: term!(interface),
177            mixin: term!(mixin),
178            identifier: Identifier<'a>,
179            members: Braced<MixinMembers<'a>>,
180            semi_colon: term!(;),
181        }),
182        /// Parses `[attributes]? partial dictionary identifier { members };`
183        PartialDictionary(struct PartialDictionaryDefinition<'a> {
184            attributes: Option<ExtendedAttributeList<'a>>,
185            partial: term!(partial),
186            dictionary: term!(dictionary),
187            identifier: Identifier<'a>,
188            members: Braced<DictionaryMembers<'a>>,
189            semi_colon: term!(;),
190        }),
191        /// Parses `[attributes]? partial namespace identifier { members };`
192        PartialNamespace(struct PartialNamespaceDefinition<'a> {
193            attributes: Option<ExtendedAttributeList<'a>>,
194            partial: term!(partial),
195            namespace: term!(namespace),
196            identifier: Identifier<'a>,
197            members: Braced<NamespaceMembers<'a>>,
198            semi_colon: term!(;),
199        }),
200        /// Parses `[attributes]? enum identifier { values };`
201        Enum(struct EnumDefinition<'a> {
202            attributes: Option<ExtendedAttributeList<'a>>,
203            enum_: term!(enum),
204            identifier: Identifier<'a>,
205            values: Braced<EnumValueList<'a>>,
206            semi_colon: term!(;),
207        }),
208        /// Parses `[attributes]? typedef attributedtype identifier;`
209        Typedef(struct TypedefDefinition<'a> {
210            attributes: Option<ExtendedAttributeList<'a>>,
211            typedef: term!(typedef),
212            type_: AttributedType<'a>,
213            identifier: Identifier<'a>,
214            semi_colon: term!(;),
215        }),
216        /// Parses `[attributes]? identifier includes identifier;`
217        IncludesStatement(struct IncludesStatementDefinition<'a> {
218            attributes: Option<ExtendedAttributeList<'a>>,
219            lhs_identifier: Identifier<'a>,
220            includes: term!(includes),
221            rhs_identifier: Identifier<'a>,
222            semi_colon: term!(;),
223        }),
224        /// Parses `[attributes]? identifier implements identifier;`
225        Implements(struct ImplementsDefinition<'a> {
226            attributes: Option<ExtendedAttributeList<'a>>,
227            lhs_identifier: Identifier<'a>,
228            includes: term!(implements),
229            rhs_identifier: Identifier<'a>,
230            semi_colon: term!(;),
231        }),
232    }
233}
234
235/// Parses a non-empty enum value list
236pub type EnumValueList<'a> = PunctuatedNonEmpty<StringLit<'a>, term!(,)>;
237
238#[cfg(test)]
239mod test {
240    use super::*;
241
242    test!(should_parse_includes_statement { "first includes second;" =>
243        "";
244        IncludesStatementDefinition;
245        attributes.is_none();
246        lhs_identifier.0 == "first";
247        rhs_identifier.0 == "second";
248    });
249
250    test!(should_parse_typedef { "typedef short Short;" =>
251        "";
252        TypedefDefinition;
253        attributes.is_none();
254        identifier.0 == "Short";
255    });
256
257    test!(should_parse_enum { r#"enum name { "first", "second" }; "# =>
258        "";
259        EnumDefinition;
260        attributes.is_none();
261        identifier.0 == "name";
262        values.body.list.len() == 2;
263    });
264
265    test!(should_parse_dictionary { "dictionary A { long c; long g; };" =>
266        "";
267        DictionaryDefinition;
268        attributes.is_none();
269        identifier.0 == "A";
270        inheritance.is_none();
271        members.body.len() == 2;
272    });
273
274    test!(should_parse_dictionary_inherited { "dictionary C : B { long e; long f; };" =>
275        "";
276        DictionaryDefinition;
277        attributes.is_none();
278        identifier.0 == "C";
279        inheritance.is_some();
280        members.body.len() == 2;
281    });
282
283    test!(should_parse_partial_namespace { "
284        partial namespace VectorUtils {
285            readonly attribute Vector unit;
286            double dotProduct(Vector x, Vector y);
287            Vector crossProduct(Vector x, Vector y);
288        };
289    " =>
290        "";
291        PartialNamespaceDefinition;
292        attributes.is_none();
293        identifier.0 == "VectorUtils";
294        members.body.len() == 3;
295    });
296
297    test!(should_parse_partial_dictionary { "partial dictionary C { long e; long f; };" =>
298        "";
299        PartialDictionaryDefinition;
300        attributes.is_none();
301        identifier.0 == "C";
302        members.body.len() == 2;
303    });
304
305    test!(should_parse_partial_interface_mixin { "
306        partial interface mixin WindowSessionStorage {
307          readonly attribute Storage sessionStorage;
308        };
309    " =>
310        "";
311        PartialInterfaceMixinDefinition;
312        attributes.is_none();
313        identifier.0 == "WindowSessionStorage";
314        members.body.len() == 1;
315    });
316
317    test!(should_parse_partial_interface { "
318        partial interface Window {
319          readonly attribute Storage sessionStorage;
320        };
321    " =>
322        "";
323        PartialInterfaceDefinition;
324        attributes.is_none();
325        identifier.0 == "Window";
326        members.body.len() == 1;
327    });
328
329    test!(should_parse_namespace { "
330        namespace VectorUtils {
331          readonly attribute Vector unit;
332          double dotProduct(Vector x, Vector y);
333          Vector crossProduct(Vector x, Vector y);
334        };
335    " =>
336        "";
337        NamespaceDefinition;
338        attributes.is_none();
339        identifier.0 == "VectorUtils";
340        members.body.len() == 3;
341    });
342
343    test!(should_parse_interface_mixin { "
344        interface mixin WindowSessionStorage {
345          readonly attribute Storage sessionStorage;
346        };
347    " =>
348        "";
349        InterfaceMixinDefinition;
350        attributes.is_none();
351        identifier.0 == "WindowSessionStorage";
352        members.body.len() == 1;
353    });
354
355    test!(should_parse_interface { "
356        interface Window {
357          readonly attribute Storage sessionStorage;
358        };
359    " =>
360        "";
361        InterfaceDefinition;
362        attributes.is_none();
363        identifier.0 == "Window";
364        members.body.len() == 1;
365    });
366
367    test!(should_parse_callback_interface {"
368        callback interface Options {
369          attribute DOMString? option1;
370          attribute DOMString? option2;
371          attribute long? option3;
372        };
373    " =>
374        "";
375        CallbackInterfaceDefinition;
376        attributes.is_none();
377        identifier.0 == "Options";
378        members.body.len() == 3;
379    });
380
381    test!(should_parse_callback { "callback AsyncOperationCallback = undefined (DOMString status);" =>
382        "";
383        CallbackDefinition;
384        attributes.is_none();
385        identifier.0 == "AsyncOperationCallback";
386        arguments.body.list.len() == 1;
387    });
388
389    test!(should_parse_with_line_comments { "
390        // This is a comment
391        callback AsyncOperationCallback = undefined (DOMString status);
392    " =>
393        "";
394        CallbackDefinition;
395    });
396
397    test!(should_parse_with_block_comments { "
398        /* This is a comment */
399        callback AsyncOperationCallback = undefined (DOMString status);
400    " =>
401        "";
402        CallbackDefinition;
403    });
404
405    test!(should_parse_with_multiple_comments { "
406        // This is a comment
407        // This is a comment
408        // This is a comment
409
410        // This is a comment
411        callback AsyncOperationCallback = undefined (DOMString status);
412    " =>
413        "";
414        CallbackDefinition;
415    });
416}