//! Weedle - A WebIDL Parser //! //! Parses valid WebIDL definitions & produces a data structure starting from //! [`Definitions`](struct.Definitions.html). //! //! ### Example //! //! ``` //! extern crate weedle; //! //! let parsed = weedle::parse(" //! interface Window { //! readonly attribute Storage sessionStorage; //! }; //! ").unwrap(); //! println!("{:?}", parsed); //! ``` //! //! Note: //! This parser follows the grammar given at [WebIDL](https://heycam.github.io/webidl). //! //! If any flaws found when parsing string with a valid grammar, create an issue. use self::argument::ArgumentList; use self::attribute::ExtendedAttributeList; use self::common::{Braced, Docstring, Identifier, Parenthesized, PunctuatedNonEmpty}; use self::dictionary::DictionaryMembers; use self::interface::{Inheritance, InterfaceMembers}; use self::literal::StringLit; use self::mixin::MixinMembers; use self::namespace::NamespaceMembers; use self::types::{AttributedType, ReturnType}; pub use nom::{error::Error, Err, IResult}; #[macro_use] mod macros; #[macro_use] mod whitespace; #[macro_use] pub mod term; pub mod argument; pub mod attribute; pub mod common; pub mod dictionary; pub mod interface; pub mod literal; pub mod mixin; pub mod namespace; pub mod types; /// A convenient parse function /// /// ### Example /// /// ``` /// extern crate weedle; /// /// let parsed = weedle::parse(" /// interface Window { /// readonly attribute Storage sessionStorage; /// }; /// ").unwrap(); /// /// println!("{:?}", parsed); /// ``` pub fn parse(raw: &str) -> Result, Err>> { let (remaining, parsed) = Definitions::parse(raw)?; assert!( remaining.is_empty(), "There is redundant raw data after parsing" ); Ok(parsed) } pub trait Parse<'a>: Sized { fn parse(input: &'a str) -> IResult<&'a str, Self>; } /// Parses WebIDL definitions. It is the root struct for a complete WebIDL definition. /// /// ### Example /// ``` /// use weedle::{Definitions, Parse}; /// /// let (_, parsed) = Definitions::parse(" /// interface Window { /// readonly attribute Storage sessionStorage; /// }; /// ").unwrap(); /// /// println!("{:?}", parsed); /// ``` /// /// It is recommended to use [`parse`](fn.parse.html) instead. pub type Definitions<'a> = Vec>; ast_types! { /// Parses a definition enum Definition<'a> { /// Parses `[attributes]? callback identifier = type ( (arg1, arg2, ..., argN)? );` Callback(struct CallbackDefinition<'a> { attributes: Option>, callback: term!(callback), identifier: Identifier<'a>, assign: term!(=), return_type: ReturnType<'a>, arguments: Parenthesized>, semi_colon: term!(;), }), /// Parses `[attributes]? callback interface identifier ( : inheritance )? { members };` CallbackInterface(struct CallbackInterfaceDefinition<'a> { docstring: Option, attributes: Option>, callback: term!(callback), interface: term!(interface), identifier: Identifier<'a>, inheritance: Option>, members: Braced>, semi_colon: term!(;), }), /// Parses `[attributes]? interface identifier ( : inheritance )? { members };` Interface(struct InterfaceDefinition<'a> { docstring: Option, attributes: Option>, interface: term!(interface), identifier: Identifier<'a>, inheritance: Option>, members: Braced>, semi_colon: term!(;), }), /// Parses `[attributes]? interface mixin identifier { members };` InterfaceMixin(struct InterfaceMixinDefinition<'a> { attributes: Option>, interface: term!(interface), mixin: term!(mixin), identifier: Identifier<'a>, members: Braced>, semi_colon: term!(;), }), /// Parses `[attributes]? namespace identifier { members };` Namespace(struct NamespaceDefinition<'a> { docstring: Option, attributes: Option>, namespace: term!(namespace), identifier: Identifier<'a>, members: Braced>, semi_colon: term!(;), }), /// Parses `[attributes]? dictionary identifier ( : inheritance )? { members };` Dictionary(struct DictionaryDefinition<'a> { docstring: Option, attributes: Option>, dictionary: term!(dictionary), identifier: Identifier<'a>, inheritance: Option>, members: Braced>, semi_colon: term!(;), }), /// Parses `[attributes]? partial interface identifier { members };` PartialInterface(struct PartialInterfaceDefinition<'a> { attributes: Option>, partial: term!(partial), interface: term!(interface), identifier: Identifier<'a>, members: Braced>, semi_colon: term!(;), }), /// Parses `[attributes]? partial interface mixin identifier { members };` PartialInterfaceMixin(struct PartialInterfaceMixinDefinition<'a> { attributes: Option>, partial: term!(partial), interface: term!(interface), mixin: term!(mixin), identifier: Identifier<'a>, members: Braced>, semi_colon: term!(;), }), /// Parses `[attributes]? partial dictionary identifier { members };` PartialDictionary(struct PartialDictionaryDefinition<'a> { attributes: Option>, partial: term!(partial), dictionary: term!(dictionary), identifier: Identifier<'a>, members: Braced>, semi_colon: term!(;), }), /// Parses `[attributes]? partial namespace identifier { members };` PartialNamespace(struct PartialNamespaceDefinition<'a> { attributes: Option>, partial: term!(partial), namespace: term!(namespace), identifier: Identifier<'a>, members: Braced>, semi_colon: term!(;), }), /// Parses `[attributes]? enum identifier { values };` Enum(struct EnumDefinition<'a> { docstring: Option, attributes: Option>, enum_: term!(enum), identifier: Identifier<'a>, values: Braced>, semi_colon: term!(;), }), /// Parses `[attributes]? typedef attributedtype identifier;` Typedef(struct TypedefDefinition<'a> { attributes: Option>, typedef: term!(typedef), type_: AttributedType<'a>, identifier: Identifier<'a>, semi_colon: term!(;), }), /// Parses `[attributes]? identifier includes identifier;` IncludesStatement(struct IncludesStatementDefinition<'a> { attributes: Option>, lhs_identifier: Identifier<'a>, includes: term!(includes), rhs_identifier: Identifier<'a>, semi_colon: term!(;), }), /// Parses `[attributes]? identifier implements identifier;` Implements(struct ImplementsDefinition<'a> { attributes: Option>, lhs_identifier: Identifier<'a>, includes: term!(implements), rhs_identifier: Identifier<'a>, semi_colon: term!(;), }), } } ast_types! { struct EnumVariant<'a> { docstring: Option, value: StringLit<'a>, } } /// Parses a non-empty enum value list pub type EnumValueList<'a> = PunctuatedNonEmpty, term!(,)>; #[cfg(test)] mod test { use super::*; test!(should_parse_includes_statement { "first includes second;" => ""; IncludesStatementDefinition; attributes.is_none(); lhs_identifier.0 == "first"; rhs_identifier.0 == "second"; }); test!(should_parse_typedef { "typedef short Short;" => ""; TypedefDefinition; attributes.is_none(); identifier.0 == "Short"; }); test!(should_parse_enum { r#"enum name { "first", "second" }; "# => ""; EnumDefinition; attributes.is_none(); identifier.0 == "name"; values.body.list.len() == 2; }); test!(should_parse_dictionary { "dictionary A { long c; long g; };" => ""; DictionaryDefinition; attributes.is_none(); identifier.0 == "A"; inheritance.is_none(); members.body.len() == 2; }); test!(should_parse_dictionary_inherited { "dictionary C : B { long e; long f; };" => ""; DictionaryDefinition; attributes.is_none(); identifier.0 == "C"; inheritance.is_some(); members.body.len() == 2; }); test!(should_parse_partial_namespace { " partial namespace VectorUtils { readonly attribute Vector unit; double dotProduct(Vector x, Vector y); Vector crossProduct(Vector x, Vector y); }; " => ""; PartialNamespaceDefinition; attributes.is_none(); identifier.0 == "VectorUtils"; members.body.len() == 3; }); test!(should_parse_partial_dictionary { "partial dictionary C { long e; long f; };" => ""; PartialDictionaryDefinition; attributes.is_none(); identifier.0 == "C"; members.body.len() == 2; }); test!(should_parse_partial_interface_mixin { " partial interface mixin WindowSessionStorage { readonly attribute Storage sessionStorage; }; " => ""; PartialInterfaceMixinDefinition; attributes.is_none(); identifier.0 == "WindowSessionStorage"; members.body.len() == 1; }); test!(should_parse_partial_interface { " partial interface Window { readonly attribute Storage sessionStorage; }; " => ""; PartialInterfaceDefinition; attributes.is_none(); identifier.0 == "Window"; members.body.len() == 1; }); test!(should_parse_namespace { " namespace VectorUtils { readonly attribute Vector unit; double dotProduct(Vector x, Vector y); Vector crossProduct(Vector x, Vector y); }; " => ""; NamespaceDefinition; attributes.is_none(); identifier.0 == "VectorUtils"; members.body.len() == 3; }); test!(should_parse_interface_mixin { " interface mixin WindowSessionStorage { readonly attribute Storage sessionStorage; }; " => ""; InterfaceMixinDefinition; attributes.is_none(); identifier.0 == "WindowSessionStorage"; members.body.len() == 1; }); test!(should_parse_interface { " interface Window { readonly attribute Storage sessionStorage; }; " => ""; InterfaceDefinition; attributes.is_none(); identifier.0 == "Window"; members.body.len() == 1; }); test!(should_parse_callback_interface {" callback interface Options { attribute DOMString? option1; attribute DOMString? option2; attribute long? option3; }; " => ""; CallbackInterfaceDefinition; attributes.is_none(); identifier.0 == "Options"; members.body.len() == 3; }); test!(should_parse_callback { "callback AsyncOperationCallback = undefined (DOMString status);" => ""; CallbackDefinition; attributes.is_none(); identifier.0 == "AsyncOperationCallback"; arguments.body.list.len() == 1; }); test!(should_parse_with_line_comments { " // This is a comment callback AsyncOperationCallback = undefined (DOMString status); " => ""; CallbackDefinition; }); test!(should_parse_with_block_comments { " /* This is a comment */ callback AsyncOperationCallback = undefined (DOMString status); " => ""; CallbackDefinition; }); test!(should_parse_with_multiple_comments { " // This is a comment // This is a comment // This is a comment // This is a comment callback AsyncOperationCallback = undefined (DOMString status); " => ""; CallbackDefinition; }); }