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