Revision control
Copy as Markdown
Other Tools
//! 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:
//!
//! 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<Definitions<'_>, Err<Error<&str>>> {
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<Definition<'a>>;
ast_types! {
/// Parses a definition
enum Definition<'a> {
/// Parses `[attributes]? callback identifier = type ( (arg1, arg2, ..., argN)? );`
Callback(struct CallbackDefinition<'a> {
attributes: Option<ExtendedAttributeList<'a>>,
callback: term!(callback),
identifier: Identifier<'a>,
assign: term!(=),
return_type: ReturnType<'a>,
arguments: Parenthesized<ArgumentList<'a>>,
semi_colon: term!(;),
}),
/// Parses `[attributes]? callback interface identifier ( : inheritance )? { members };`
CallbackInterface(struct CallbackInterfaceDefinition<'a> {
docstring: Option<Docstring>,
attributes: Option<ExtendedAttributeList<'a>>,
callback: term!(callback),
interface: term!(interface),
identifier: Identifier<'a>,
inheritance: Option<Inheritance<'a>>,
members: Braced<InterfaceMembers<'a>>,
semi_colon: term!(;),
}),
/// Parses `[attributes]? interface identifier ( : inheritance )? { members };`
Interface(struct InterfaceDefinition<'a> {
docstring: Option<Docstring>,
attributes: Option<ExtendedAttributeList<'a>>,
interface: term!(interface),
identifier: Identifier<'a>,
inheritance: Option<Inheritance<'a>>,
members: Braced<InterfaceMembers<'a>>,
semi_colon: term!(;),
}),
/// Parses `[attributes]? interface mixin identifier { members };`
InterfaceMixin(struct InterfaceMixinDefinition<'a> {
attributes: Option<ExtendedAttributeList<'a>>,
interface: term!(interface),
mixin: term!(mixin),
identifier: Identifier<'a>,
members: Braced<MixinMembers<'a>>,
semi_colon: term!(;),
}),
/// Parses `[attributes]? namespace identifier { members };`
Namespace(struct NamespaceDefinition<'a> {
docstring: Option<Docstring>,
attributes: Option<ExtendedAttributeList<'a>>,
namespace: term!(namespace),
identifier: Identifier<'a>,
members: Braced<NamespaceMembers<'a>>,
semi_colon: term!(;),
}),
/// Parses `[attributes]? dictionary identifier ( : inheritance )? { members };`
Dictionary(struct DictionaryDefinition<'a> {
docstring: Option<Docstring>,
attributes: Option<ExtendedAttributeList<'a>>,
dictionary: term!(dictionary),
identifier: Identifier<'a>,
inheritance: Option<Inheritance<'a>>,
members: Braced<DictionaryMembers<'a>>,
semi_colon: term!(;),
}),
/// Parses `[attributes]? partial interface identifier { members };`
PartialInterface(struct PartialInterfaceDefinition<'a> {
attributes: Option<ExtendedAttributeList<'a>>,
partial: term!(partial),
interface: term!(interface),
identifier: Identifier<'a>,
members: Braced<InterfaceMembers<'a>>,
semi_colon: term!(;),
}),
/// Parses `[attributes]? partial interface mixin identifier { members };`
PartialInterfaceMixin(struct PartialInterfaceMixinDefinition<'a> {
attributes: Option<ExtendedAttributeList<'a>>,
partial: term!(partial),
interface: term!(interface),
mixin: term!(mixin),
identifier: Identifier<'a>,
members: Braced<MixinMembers<'a>>,
semi_colon: term!(;),
}),
/// Parses `[attributes]? partial dictionary identifier { members };`
PartialDictionary(struct PartialDictionaryDefinition<'a> {
attributes: Option<ExtendedAttributeList<'a>>,
partial: term!(partial),
dictionary: term!(dictionary),
identifier: Identifier<'a>,
members: Braced<DictionaryMembers<'a>>,
semi_colon: term!(;),
}),
/// Parses `[attributes]? partial namespace identifier { members };`
PartialNamespace(struct PartialNamespaceDefinition<'a> {
attributes: Option<ExtendedAttributeList<'a>>,
partial: term!(partial),
namespace: term!(namespace),
identifier: Identifier<'a>,
members: Braced<NamespaceMembers<'a>>,
semi_colon: term!(;),
}),
/// Parses `[attributes]? enum identifier { values };`
Enum(struct EnumDefinition<'a> {
docstring: Option<Docstring>,
attributes: Option<ExtendedAttributeList<'a>>,
enum_: term!(enum),
identifier: Identifier<'a>,
values: Braced<EnumValueList<'a>>,
semi_colon: term!(;),
}),
/// Parses `[attributes]? typedef attributedtype identifier;`
Typedef(struct TypedefDefinition<'a> {
attributes: Option<ExtendedAttributeList<'a>>,
typedef: term!(typedef),
type_: AttributedType<'a>,
identifier: Identifier<'a>,
semi_colon: term!(;),
}),
/// Parses `[attributes]? identifier includes identifier;`
IncludesStatement(struct IncludesStatementDefinition<'a> {
attributes: Option<ExtendedAttributeList<'a>>,
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<ExtendedAttributeList<'a>>,
lhs_identifier: Identifier<'a>,
includes: term!(implements),
rhs_identifier: Identifier<'a>,
semi_colon: term!(;),
}),
}
}
ast_types! {
struct EnumVariant<'a> {
docstring: Option<Docstring>,
value: StringLit<'a>,
}
}
/// Parses a non-empty enum value list
pub type EnumValueList<'a> = PunctuatedNonEmpty<EnumVariant<'a>, 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;
});
}