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