diff options
Diffstat (limited to 'third_party/rust/weedle2/src')
-rw-r--r-- | third_party/rust/weedle2/src/argument.rs | 76 | ||||
-rw-r--r-- | third_party/rust/weedle2/src/attribute.rs | 99 | ||||
-rw-r--r-- | third_party/rust/weedle2/src/common.rs | 214 | ||||
-rw-r--r-- | third_party/rust/weedle2/src/dictionary.rs | 33 | ||||
-rw-r--r-- | third_party/rust/weedle2/src/interface.rs | 249 | ||||
-rw-r--r-- | third_party/rust/weedle2/src/lib.rs | 408 | ||||
-rw-r--r-- | third_party/rust/weedle2/src/literal.rs | 269 | ||||
-rw-r--r-- | third_party/rust/weedle2/src/macros.rs | 564 | ||||
-rw-r--r-- | third_party/rust/weedle2/src/mixin.rs | 60 | ||||
-rw-r--r-- | third_party/rust/weedle2/src/namespace.rs | 52 | ||||
-rw-r--r-- | third_party/rust/weedle2/src/term.rs | 699 | ||||
-rw-r--r-- | third_party/rust/weedle2/src/types.rs | 385 | ||||
-rw-r--r-- | third_party/rust/weedle2/src/whitespace.rs | 34 |
13 files changed, 3142 insertions, 0 deletions
diff --git a/third_party/rust/weedle2/src/argument.rs b/third_party/rust/weedle2/src/argument.rs new file mode 100644 index 0000000000..8c0e085f20 --- /dev/null +++ b/third_party/rust/weedle2/src/argument.rs @@ -0,0 +1,76 @@ +use crate::attribute::ExtendedAttributeList; +use crate::common::{Default, Identifier, Punctuated}; +use crate::types::{AttributedType, Type}; + +/// Parses a list of argument. Ex: `double v1, double v2, double v3, optional double alpha` +pub type ArgumentList<'a> = Punctuated<Argument<'a>, term!(,)>; + +ast_types! { + /// Parses an argument. Ex: `double v1|double... v1s` + enum Argument<'a> { + /// Parses `[attributes]? optional? attributedtype identifier ( = default )?` + /// + /// Note: `= default` is only allowed if `optional` is present + Single(struct SingleArgument<'a> { + attributes: Option<ExtendedAttributeList<'a>>, + optional: Option<term!(optional)>, + type_: AttributedType<'a>, + identifier: Identifier<'a>, + default: Option<Default<'a>> = nom::combinator::map( + nom::combinator::cond(optional.is_some(), weedle!(Option<Default<'a>>)), + |default| default.unwrap_or(None) + ), + }), + /// Parses `[attributes]? type... identifier` + Variadic(struct VariadicArgument<'a> { + attributes: Option<ExtendedAttributeList<'a>>, + type_: Type<'a>, + ellipsis: term!(...), + identifier: Identifier<'a>, + }), + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::literal::{DecLit, DefaultValue, IntegerLit}; + use crate::Parse; + + test!(should_parse_single_argument { "short a" => + ""; + SingleArgument; + attributes.is_none(); + optional.is_none(); + identifier.0 == "a"; + default.is_none(); + }); + + test!(should_parse_variadic_argument { "short... a" => + ""; + VariadicArgument; + attributes.is_none(); + identifier.0 == "a"; + }); + + test!(should_parse_optional_single_argument { "optional short a" => + ""; + SingleArgument; + attributes.is_none(); + optional.is_some(); + identifier.0 == "a"; + default.is_none(); + }); + + test!(should_parse_optional_single_argument_with_default { "optional short a = 5" => + ""; + SingleArgument; + attributes.is_none(); + optional.is_some(); + identifier.0 == "a"; + default == Some(Default { + assign: term!(=), + value: DefaultValue::Integer(IntegerLit::Dec(DecLit("5"))), + }); + }); +} diff --git a/third_party/rust/weedle2/src/attribute.rs b/third_party/rust/weedle2/src/attribute.rs new file mode 100644 index 0000000000..0a70450023 --- /dev/null +++ b/third_party/rust/weedle2/src/attribute.rs @@ -0,0 +1,99 @@ +use crate::argument::ArgumentList; +use crate::common::{Bracketed, Identifier, Parenthesized, Punctuated}; +use crate::literal::StringLit; + +/// Parses a list of attributes. Ex: `[ attribute1, attribute2 ]` +pub type ExtendedAttributeList<'a> = Bracketed<Punctuated<ExtendedAttribute<'a>, term!(,)>>; + +/// Matches comma separated identifier list +pub type IdentifierList<'a> = Punctuated<Identifier<'a>, term!(,)>; + +ast_types! { + /// Parses on of the forms of attribute + enum ExtendedAttribute<'a> { + /// Parses an argument list. Ex: `Constructor((double x, double y))` + /// + /// (( )) means ( ) chars + ArgList(struct ExtendedAttributeArgList<'a> { + identifier: Identifier<'a>, + args: Parenthesized<ArgumentList<'a>>, + }), + /// Parses a named argument list. Ex: `NamedConstructor=Image((DOMString src))` + /// + /// (( )) means ( ) chars + NamedArgList(struct ExtendedAttributeNamedArgList<'a> { + lhs_identifier: Identifier<'a>, + assign: term!(=), + rhs_identifier: Identifier<'a>, + args: Parenthesized<ArgumentList<'a>>, + + }), + /// Parses an identifier list. Ex: `Exposed=((Window,Worker))` + /// + /// (( )) means ( ) chars + IdentList(struct ExtendedAttributeIdentList<'a> { + identifier: Identifier<'a>, + assign: term!(=), + list: Parenthesized<IdentifierList<'a>>, + }), + /// Parses an attribute with an identifier. Ex: `PutForwards=name` + #[derive(Copy)] + Ident(struct ExtendedAttributeIdent<'a> { + lhs_identifier: Identifier<'a>, + assign: term!(=), + rhs: IdentifierOrString<'a>, + }), + /// Parses a plain attribute. Ex: `Replaceable` + #[derive(Copy)] + NoArgs(struct ExtendedAttributeNoArgs<'a>( + Identifier<'a>, + )), + } + + /// Parses `stringifier|static` + #[derive(Copy)] + enum IdentifierOrString<'a> { + Identifier(Identifier<'a>), + String(StringLit<'a>), + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::Parse; + + test!(should_parse_attribute_no_args { "Replaceable" => + ""; + ExtendedAttributeNoArgs => ExtendedAttributeNoArgs(Identifier("Replaceable")) + }); + + test!(should_parse_attribute_arg_list { "Constructor(double x, double y)" => + ""; + ExtendedAttributeArgList; + identifier.0 == "Constructor"; + args.body.list.len() == 2; + }); + + test!(should_parse_attribute_ident { "PutForwards=name" => + ""; + ExtendedAttributeIdent; + lhs_identifier.0 == "PutForwards"; + rhs == IdentifierOrString::Identifier(Identifier("name")); + }); + + test!(should_parse_ident_list { "Exposed=(Window,Worker)" => + ""; + ExtendedAttributeIdentList; + identifier.0 == "Exposed"; + list.body.list.len() == 2; + }); + + test!(should_parse_named_arg_list { "NamedConstructor=Image(DOMString src)" => + ""; + ExtendedAttributeNamedArgList; + lhs_identifier.0 == "NamedConstructor"; + rhs_identifier.0 == "Image"; + args.body.list.len() == 1; + }); +} diff --git a/third_party/rust/weedle2/src/common.rs b/third_party/rust/weedle2/src/common.rs new file mode 100644 index 0000000000..fadf89ba8b --- /dev/null +++ b/third_party/rust/weedle2/src/common.rs @@ -0,0 +1,214 @@ +use crate::literal::DefaultValue; +use crate::{term, IResult, Parse}; + +pub(crate) fn is_alphanum_underscore_dash(token: char) -> bool { + nom::AsChar::is_alphanum(token) || matches!(token, '_' | '-') +} + +fn marker<S>(i: &str) -> IResult<&str, S> +where + S: ::std::default::Default, +{ + Ok((i, S::default())) +} + +impl<'a, T: Parse<'a>> Parse<'a> for Option<T> { + parser!(nom::combinator::opt(weedle!(T))); +} + +impl<'a, T: Parse<'a>> Parse<'a> for Box<T> { + parser!(nom::combinator::map(weedle!(T), Box::new)); +} + +/// Parses `item1 item2 item3...` +impl<'a, T: Parse<'a>> Parse<'a> for Vec<T> { + parser!(nom::multi::many0(T::parse)); +} + +impl<'a, T: Parse<'a>, U: Parse<'a>> Parse<'a> for (T, U) { + parser!(nom::sequence::tuple((T::parse, U::parse))); +} + +impl<'a, T: Parse<'a>, U: Parse<'a>, V: Parse<'a>> Parse<'a> for (T, U, V) { + parser!(nom::sequence::tuple((T::parse, U::parse, V::parse))); +} + +ast_types! { + /// Parses `( body )` + #[derive(Copy, Default)] + struct Parenthesized<T> where [T: Parse<'a>] { + open_paren: term::OpenParen, + body: T, + close_paren: term::CloseParen, + } + + /// Parses `[ body ]` + #[derive(Copy, Default)] + struct Bracketed<T> where [T: Parse<'a>] { + open_bracket: term::OpenBracket, + body: T, + close_bracket: term::CloseBracket, + } + + /// Parses `{ body }` + #[derive(Copy, Default)] + struct Braced<T> where [T: Parse<'a>] { + open_brace: term::OpenBrace, + body: T, + close_brace: term::CloseBrace, + } + + /// Parses `< body >` + #[derive(Copy, Default)] + struct Generics<T> where [T: Parse<'a>] { + open_angle: term::LessThan, + body: T, + close_angle: term::GreaterThan, + } + + /// Parses `(item1, item2, item3,...)?` + struct Punctuated<T, S> where [T: Parse<'a>, S: Parse<'a> + ::std::default::Default] { + list: Vec<T> = nom::multi::separated_list0(weedle!(S), weedle!(T)), + separator: S = marker, + } + + /// Parses `item1, item2, item3, ...` + struct PunctuatedNonEmpty<T, S> where [T: Parse<'a>, S: Parse<'a> + ::std::default::Default] { + list: Vec<T> = nom::sequence::terminated( + nom::multi::separated_list1(weedle!(S), weedle!(T)), + nom::combinator::opt(weedle!(S)) + ), + separator: S = marker, + } + + /// Represents an identifier + /// + /// Follows `/_?[A-Za-z][0-9A-Z_a-z-]*/` + #[derive(Copy)] + struct Identifier<'a>( + // See https://heycam.github.io/webidl/#idl-names for why the leading + // underscore is trimmed + &'a str = crate::whitespace::ws(nom::sequence::preceded( + nom::combinator::opt(nom::character::complete::char('_')), + nom::combinator::recognize(nom::sequence::tuple(( + nom::bytes::complete::take_while1(nom::AsChar::is_alphanum), + nom::bytes::complete::take_while(is_alphanum_underscore_dash), + ))) + )), + ) + + /// Parses rhs of an assignment expression. Ex: `= 45` + #[derive(Copy)] + struct Default<'a> { + assign: term!(=), + value: DefaultValue<'a>, + } +} + +#[cfg(test)] +mod test { + use super::*; + + test!(should_parse_optional_present { "one" => + ""; + Option<Identifier>; + is_some(); + }); + + test!(should_parse_optional_not_present { "" => + ""; + Option<Identifier>; + is_none(); + }); + + test!(should_parse_boxed { "one" => + ""; + Box<Identifier>; + }); + + test!(should_parse_vec { "one two three" => + ""; + Vec<Identifier>; + len() == 3; + }); + + test!(should_parse_parenthesized { "( one )" => + ""; + Parenthesized<Identifier>; + body.0 == "one"; + }); + + test!(should_parse_bracketed { "[ one ]" => + ""; + Bracketed<Identifier>; + body.0 == "one"; + }); + + test!(should_parse_braced { "{ one }" => + ""; + Braced<Identifier>; + body.0 == "one"; + }); + + test!(should_parse_generics { "<one>" => + ""; + Generics<Identifier>; + body.0 == "one"; + }); + + test!(should_parse_generics_two { "<one, two>" => + ""; + Generics<(Identifier, term!(,), Identifier)> => + Generics { + open_angle: term!(<), + body: (Identifier("one"), term!(,), Identifier("two")), + close_angle: term!(>), + } + }); + + test!(should_parse_comma_separated_values { "one, two, three" => + ""; + Punctuated<Identifier, term!(,)>; + list.len() == 3; + }); + + test!(err should_not_parse_comma_separated_values_empty { "" => + PunctuatedNonEmpty<Identifier, term!(,)> + }); + + test!(should_parse_identifier { "hello" => + ""; + Identifier; + 0 == "hello"; + }); + + test!(should_parse_numbered_identifier { "hello5" => + ""; + Identifier; + 0 == "hello5"; + }); + + test!(should_parse_underscored_identifier { "_hello_" => + ""; + Identifier; + 0 == "hello_"; + }); + + test!(should_parse_identifier_surrounding_with_spaces { " hello " => + ""; + Identifier; + 0 == "hello"; + }); + + test!(should_parse_identifier_preceding_others { "hello note" => + "note"; + Identifier; + 0 == "hello"; + }); + + test!(should_parse_identifier_attached_to_symbol { "hello=" => + "="; + Identifier; + 0 == "hello"; + }); +} diff --git a/third_party/rust/weedle2/src/dictionary.rs b/third_party/rust/weedle2/src/dictionary.rs new file mode 100644 index 0000000000..3c9b23cac5 --- /dev/null +++ b/third_party/rust/weedle2/src/dictionary.rs @@ -0,0 +1,33 @@ +use crate::attribute::ExtendedAttributeList; +use crate::common::{Default, Identifier}; +use crate::types::Type; + +/// Parses dictionary members +pub type DictionaryMembers<'a> = Vec<DictionaryMember<'a>>; + +ast_types! { + /// Parses dictionary member `[attributes]? required? type identifier ( = default )?;` + struct DictionaryMember<'a> { + attributes: Option<ExtendedAttributeList<'a>>, + required: Option<term!(required)>, + type_: Type<'a>, + identifier: Identifier<'a>, + default: Option<Default<'a>>, + semi_colon: term!(;), + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::Parse; + + test!(should_parse_dictionary_member { "required long num = 5;" => + ""; + DictionaryMember; + attributes.is_none(); + required.is_some(); + identifier.0 == "num"; + default.is_some(); + }); +} diff --git a/third_party/rust/weedle2/src/interface.rs b/third_party/rust/weedle2/src/interface.rs new file mode 100644 index 0000000000..5e30909c38 --- /dev/null +++ b/third_party/rust/weedle2/src/interface.rs @@ -0,0 +1,249 @@ +use crate::argument::ArgumentList; +use crate::attribute::ExtendedAttributeList; +use crate::common::{Generics, Identifier, Parenthesized}; +use crate::literal::ConstValue; +use crate::types::{AttributedType, ConstType, ReturnType}; + +/// Parses interface members +pub type InterfaceMembers<'a> = Vec<InterfaceMember<'a>>; + +ast_types! { + /// Parses inheritance clause `: identifier` + #[derive(Copy)] + struct Inheritance<'a> { + colon: term!(:), + identifier: Identifier<'a>, + } + + /// Parses one of the interface member variants + enum InterfaceMember<'a> { + /// Parses a const interface member `[attributes]? const type identifier = value;` + Const(struct ConstMember<'a> { + attributes: Option<ExtendedAttributeList<'a>>, + const_: term!(const), + const_type: ConstType<'a>, + identifier: Identifier<'a>, + assign: term!(=), + const_value: ConstValue<'a>, + semi_colon: term!(;), + }), + /// Parses `[attributes]? (stringifier|inherit|static)? readonly? attribute attributedtype identifier;` + Attribute(struct AttributeInterfaceMember<'a> { + attributes: Option<ExtendedAttributeList<'a>>, + modifier: Option<StringifierOrInheritOrStatic>, + readonly: Option<term!(readonly)>, + attribute: term!(attribute), + type_: AttributedType<'a>, + identifier: Identifier<'a>, + semi_colon: term!(;), + }), + /// Parses `[attributes]? constructor(( args ));` + /// + /// (( )) means ( ) chars + Constructor(struct ConstructorInterfaceMember<'a> { + attributes: Option<ExtendedAttributeList<'a>>, + constructor: term!(constructor), + args: Parenthesized<ArgumentList<'a>>, + semi_colon: term!(;), + }), + /// Parses `[attributes]? (stringifier|static)? special? returntype identifier? (( args ));` + /// + /// (( )) means ( ) chars + Operation(struct OperationInterfaceMember<'a> { + attributes: Option<ExtendedAttributeList<'a>>, + modifier: Option<StringifierOrStatic>, + special: Option<Special>, + return_type: ReturnType<'a>, + identifier: Option<Identifier<'a>>, + args: Parenthesized<ArgumentList<'a>>, + semi_colon: term!(;), + }), + /// Parses an iterable declaration `[attributes]? (iterable<attributedtype> | iterable<attributedtype, attributedtype>) ;` + Iterable(enum IterableInterfaceMember<'a> { + /// Parses an iterable declaration `[attributes]? iterable<attributedtype>;` + Single(struct SingleTypedIterable<'a> { + attributes: Option<ExtendedAttributeList<'a>>, + iterable: term!(iterable), + generics: Generics<AttributedType<'a>>, + semi_colon: term!(;), + }), + /// Parses an iterable declaration `[attributes]? iterable<attributedtype, attributedtype>;` + Double(struct DoubleTypedIterable<'a> { + attributes: Option<ExtendedAttributeList<'a>>, + iterable: term!(iterable), + generics: Generics<(AttributedType<'a>, term!(,), AttributedType<'a>)>, + semi_colon: term!(;), + }), + }), + /// Parses an async iterable declaration `[attributes]? async (iterable<attributedtype> | iterable<attributedtype, attributedtype>) (( args ))? ;` + AsyncIterable(enum AsyncIterableInterfaceMember<'a> { + /// Parses an async iterable declaration `[attributes]? async iterable<attributedtype> (( args ))? ;` + Single(struct SingleTypedAsyncIterable<'a> { + attributes: Option<ExtendedAttributeList<'a>>, + async_iterable: (term!(async), term!(iterable)), + generics: Generics<AttributedType<'a>>, + args: Option<Parenthesized<ArgumentList<'a>>>, + semi_colon: term!(;), + }), + /// Parses an async iterable declaration `[attributes]? async iterable<attributedtype, attributedtype> (( args ))? ;` + Double(struct DoubleTypedAsyncIterable<'a> { + attributes: Option<ExtendedAttributeList<'a>>, + async_iterable: (term!(async), term!(iterable)), + generics: Generics<(AttributedType<'a>, term!(,), AttributedType<'a>)>, + args: Option<Parenthesized<ArgumentList<'a>>>, + semi_colon: term!(;), + }), + }), + /// Parses an maplike declaration `[attributes]? readonly? maplike<attributedtype, attributedtype>;` + Maplike(struct MaplikeInterfaceMember<'a> { + attributes: Option<ExtendedAttributeList<'a>>, + readonly: Option<term!(readonly)>, + maplike: term!(maplike), + generics: Generics<(AttributedType<'a>, term!(,), AttributedType<'a>)>, + semi_colon: term!(;), + }), + Setlike(struct SetlikeInterfaceMember<'a> { + attributes: Option<ExtendedAttributeList<'a>>, + readonly: Option<term!(readonly)>, + setlike: term!(setlike), + generics: Generics<AttributedType<'a>>, + semi_colon: term!(;), + }), + /// Parses `stringifier;` + #[derive(Default)] + Stringifier(struct StringifierMember<'a> { + attributes: Option<ExtendedAttributeList<'a>>, + stringifier: term!(stringifier), + semi_colon: term!(;), + }), + } + + /// Parses one of the special keyword `getter|setter|deleter` + #[derive(Copy)] + enum Special { + Getter(term!(getter)), + Setter(term!(setter)), + Deleter(term!(deleter)), + LegacyCaller(term!(legacycaller)), + } + + /// Parses `stringifier|inherit|static` + #[derive(Copy)] + enum StringifierOrInheritOrStatic { + Stringifier(term!(stringifier)), + Inherit(term!(inherit)), + Static(term!(static)), + } + + /// Parses `stringifier|static` + #[derive(Copy)] + enum StringifierOrStatic { + Stringifier(term!(stringifier)), + Static(term!(static)), + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::Parse; + + test!(should_parse_stringifier_member { "stringifier;" => + ""; + StringifierMember; + }); + + test!(should_parse_stringifier_or_static { "static" => + ""; + StringifierOrStatic; + }); + + test!(should_parse_stringifier_or_inherit_or_static { "inherit" => + ""; + StringifierOrInheritOrStatic; + }); + + test!(should_parse_setlike_interface_member { "readonly setlike<long>;" => + ""; + SetlikeInterfaceMember; + attributes.is_none(); + readonly == Some(term!(readonly)); + }); + + test!(should_parse_maplike_interface_member { "readonly maplike<long, short>;" => + ""; + MaplikeInterfaceMember; + attributes.is_none(); + readonly == Some(term!(readonly)); + }); + + test!(should_parse_attribute_interface_member { "readonly attribute unsigned long width;" => + ""; + AttributeInterfaceMember; + attributes.is_none(); + readonly == Some(term!(readonly)); + identifier.0 == "width"; + }); + + test!(should_parse_double_typed_iterable { "iterable<long, long>;" => + ""; + DoubleTypedIterable; + attributes.is_none(); + }); + + test!(should_parse_single_typed_iterable { "iterable<long>;" => + ""; + SingleTypedIterable; + attributes.is_none(); + }); + + test!(should_parse_double_typed_async_iterable { "async iterable<long, long>;" => + ""; + DoubleTypedAsyncIterable; + attributes.is_none(); + args.is_none(); + }); + + test!(should_parse_double_typed_async_iterable_with_args { "async iterable<long, long>(long a);" => + ""; + DoubleTypedAsyncIterable; + attributes.is_none(); + args.is_some(); + }); + + test!(should_parse_single_typed_async_iterable { "async iterable<long>;" => + ""; + SingleTypedAsyncIterable; + attributes.is_none(); + args.is_none(); + }); + + test!(should_parse_single_typed_async_iterable_with_args { "async iterable<long>(long a);" => + ""; + SingleTypedAsyncIterable; + attributes.is_none(); + args.is_some(); + }); + + test!(should_parse_constructor_interface_member { "constructor(long a);" => + ""; + ConstructorInterfaceMember; + attributes.is_none(); + }); + + test!(should_parse_operation_interface_member { "undefined readString(long a, long b);" => + ""; + OperationInterfaceMember; + attributes.is_none(); + modifier.is_none(); + special.is_none(); + identifier.is_some(); + }); + + test!(should_parse_const_member { "const long name = 5;" => + ""; + ConstMember; + attributes.is_none(); + identifier.0 == "name"; + }); +} diff --git a/third_party/rust/weedle2/src/lib.rs b/third_party/rust/weedle2/src/lib.rs new file mode 100644 index 0000000000..610a34fa14 --- /dev/null +++ b/third_party/rust/weedle2/src/lib.rs @@ -0,0 +1,408 @@ +//! 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, 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> { + 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> { + 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> { + 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> { + 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> { + 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!(;), + }), + } +} + +/// Parses a non-empty enum value list +pub type EnumValueList<'a> = PunctuatedNonEmpty<StringLit<'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; + }); +} diff --git a/third_party/rust/weedle2/src/literal.rs b/third_party/rust/weedle2/src/literal.rs new file mode 100644 index 0000000000..295afe583d --- /dev/null +++ b/third_party/rust/weedle2/src/literal.rs @@ -0,0 +1,269 @@ +ast_types! { + /// Represents an integer value + #[derive(Copy)] + enum IntegerLit<'a> { + /// Parses `-?[1-9][0-9]*` + #[derive(Copy)] + Dec(struct DecLit<'a>( + &'a str = crate::whitespace::ws(nom::combinator::recognize(nom::sequence::tuple(( + nom::combinator::opt(nom::character::complete::char('-')), + nom::character::complete::one_of("123456789"), + nom::bytes::complete::take_while(nom::AsChar::is_dec_digit) + )))), + )), + /// Parses `-?0[Xx][0-9A-Fa-f]+)` + #[derive(Copy)] + Hex(struct HexLit<'a>( + &'a str = crate::whitespace::ws(nom::combinator::recognize(nom::sequence::tuple(( + nom::combinator::opt(nom::character::complete::char('-')), + nom::character::complete::char('0'), + nom::character::complete::one_of("xX"), + nom::bytes::complete::take_while(nom::AsChar::is_hex_digit) + )))), + )), + /// Parses `-?0[0-7]*` + #[derive(Copy)] + Oct(struct OctLit<'a>( + &'a str = crate::whitespace::ws(nom::combinator::recognize(nom::sequence::tuple(( + nom::combinator::opt(nom::character::complete::char('-')), + nom::character::complete::char('0'), + nom::bytes::complete::take_while(nom::AsChar::is_oct_digit) + )))), + )), + } + + /// Represents a string value + /// + /// Follow `/"[^"]*"/` + #[derive(Copy)] + struct StringLit<'a>( + &'a str = crate::whitespace::ws(nom::sequence::delimited( + nom::character::complete::char('"'), + nom::bytes::complete::take_while(|c| c != '"'), + nom::character::complete::char('"'), + )), + ) + + /// Represents a default literal value. Ex: `34|34.23|"value"|[ ]|true|false|null` + #[derive(Copy)] + enum DefaultValue<'a> { + Boolean(BooleanLit), + /// Represents `[ ]` + #[derive(Copy, Default)] + EmptyArray(struct EmptyArrayLit { + open_bracket: term!(OpenBracket), + close_bracket: term!(CloseBracket), + }), + /// Represents `{ }` + #[derive(Copy, Default)] + EmptyDictionary(struct EmptyDictionaryLit { + open_brace: term!(OpenBrace), + close_brace: term!(CloseBrace), + }), + Float(FloatLit<'a>), + Integer(IntegerLit<'a>), + Null(term!(null)), + String(StringLit<'a>), + } + + /// Represents `true`, `false`, `34.23`, `null`, `56`, ... + #[derive(Copy)] + enum ConstValue<'a> { + Boolean(BooleanLit), + Float(FloatLit<'a>), + Integer(IntegerLit<'a>), + Null(term!(null)), + } + + /// Represents either `true` or `false` + #[derive(Copy)] + struct BooleanLit( + bool = nom::branch::alt(( + nom::combinator::value(true, weedle!(term!(true))), + nom::combinator::value(false, weedle!(term!(false))), + )), + ) + + /// Represents a floating point value, `NaN`, `Infinity`, '+Infinity` + #[derive(Copy)] + enum FloatLit<'a> { + /// Parses `/-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+)/` + #[derive(Copy)] + Value(struct FloatValueLit<'a>( + &'a str = crate::whitespace::ws(nom::combinator::recognize(nom::sequence::tuple(( + nom::combinator::opt(nom::character::complete::char('-')), + nom::branch::alt(( + nom::combinator::value((), nom::sequence::tuple(( + // (?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+) + nom::branch::alt(( + nom::sequence::tuple(( + nom::bytes::complete::take_while1(nom::AsChar::is_dec_digit), + nom::character::complete::char('.'), + nom::bytes::complete::take_while(nom::AsChar::is_dec_digit), + )), + nom::sequence::tuple(( + nom::bytes::complete::take_while(nom::AsChar::is_dec_digit), + nom::character::complete::char('.'), + nom::bytes::complete::take_while1(nom::AsChar::is_dec_digit), + )), + )), + // (?:[Ee][+-]?[0-9]+)? + nom::combinator::opt(nom::sequence::tuple(( + nom::character::complete::one_of("eE"), + nom::combinator::opt(nom::character::complete::one_of("+-")), + nom::bytes::complete::take_while1(nom::AsChar::is_dec_digit), + ))), + ))), + // [0-9]+[Ee][+-]?[0-9]+ + nom::combinator::value((), nom::sequence::tuple(( + nom::bytes::complete::take_while1(nom::AsChar::is_dec_digit), + nom::character::complete::one_of("eE"), + nom::combinator::opt(nom::character::complete::one_of("+-")), + nom::bytes::complete::take_while1(nom::AsChar::is_dec_digit), + ))), + )), + )))), + )), + NegInfinity(term!(-Infinity)), + Infinity(term!(Infinity)), + NaN(term!(NaN)), + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::term::*; + use crate::Parse; + + test!(should_parse_integer { "45" => + ""; + IntegerLit => IntegerLit::Dec(DecLit("45")) + }); + + test!(should_parse_integer_surrounding_with_spaces { " 123123 " => + ""; + IntegerLit => IntegerLit::Dec(DecLit("123123")) + }); + + test!(should_parse_integer_preceding_others { "3453 string" => + "string"; + IntegerLit => IntegerLit::Dec(DecLit("3453")) + }); + + test!(should_parse_neg_integer { "-435" => + ""; + IntegerLit => IntegerLit::Dec(DecLit("-435")) + }); + + test!(should_parse_hex_number { "0X08" => + ""; + IntegerLit => IntegerLit::Hex(HexLit("0X08")) + }); + + test!(should_parse_hex_large_number { "0xA" => + ""; + IntegerLit => IntegerLit::Hex(HexLit("0xA")) + }); + + test!(should_parse_zero { "0" => + ""; + IntegerLit => IntegerLit::Oct(OctLit("0")) + }); + + test!(should_parse_oct_number { "-07561" => + ""; + IntegerLit => IntegerLit::Oct(OctLit("-07561")) + }); + + test!(should_parse_float { "45.434" => + ""; + FloatLit => FloatLit::Value(FloatValueLit("45.434")) + }); + + test!(should_parse_float_surrounding_with_spaces { " 2345.2345 " => + ""; + FloatLit => FloatLit::Value(FloatValueLit("2345.2345")) + }); + + test!(should_parse_float_preceding_others { "3453.32334 string" => + "string"; + FloatLit => FloatLit::Value(FloatValueLit("3453.32334")) + }); + + test!(should_parse_neg_float { "-435.3435" => + ""; + FloatLit => FloatLit::Value(FloatValueLit("-435.3435")) + }); + + test!(should_parse_float_exp { "3e23" => + ""; + FloatLit => FloatLit::Value(FloatValueLit("3e23")) + }); + + test!(should_parse_float_exp_with_decimal { "5.3434e23" => + ""; + FloatLit => FloatLit::Value(FloatValueLit("5.3434e23")) + }); + + test!(should_parse_neg_infinity { "-Infinity" => + ""; + FloatLit => FloatLit::NegInfinity(term!(-Infinity)) + }); + + test!(should_parse_infinity { "Infinity" => + ""; + FloatLit => FloatLit::Infinity(term!(Infinity)) + }); + + test!(should_parse_string { r#""this is a string""# => + ""; + StringLit => StringLit("this is a string") + }); + + test!(should_parse_string_surround_with_spaces { r#" "this is a string" "# => + ""; + StringLit => StringLit("this is a string") + }); + + test!(should_parse_string_followed_by_string { r#" "this is first" "this is second" "# => + r#""this is second" "#; + StringLit => StringLit("this is first") + }); + + test!(should_parse_string_with_spaces { r#" " this is a string " "# => + ""; + StringLit => StringLit(" this is a string ") + }); + + test!(should_parse_string_with_comment { r#" "// this is still a string" + "# => + ""; + StringLit => StringLit("// this is still a string") + }); + + test!(should_parse_string_with_multiline_comment { r#" "/*" "*/" "# => + r#""*/" "#; + StringLit => StringLit("/*") + }); + + test!(should_parse_null { "null" => + ""; + Null => Null + }); + + test!(should_parse_empty_array { "[]" => + ""; + EmptyArrayLit => Default::default() + }); + + test!(should_parse_bool_true { "true" => + ""; + BooleanLit => BooleanLit(true) + }); + + test!(should_parse_bool_false { "false" => + ""; + BooleanLit => BooleanLit(false) + }); +} diff --git a/third_party/rust/weedle2/src/macros.rs b/third_party/rust/weedle2/src/macros.rs new file mode 100644 index 0000000000..0a29c499f9 --- /dev/null +++ b/third_party/rust/weedle2/src/macros.rs @@ -0,0 +1,564 @@ +macro_rules! parser { + ($parse:expr) => { + fn parse(input: &'a str) -> $crate::IResult<&'a str, Self> { + $parse(input) + } + }; +} + +macro_rules! weedle { + ($t:ty) => { + <$t as $crate::Parse<'a>>::parse + }; +} + +// nom::branch::alt supports at-most 21 parsers, increasing to 42 ones. +macro_rules! alt { + ($member0:expr, $($member1:expr, $member2:expr,)*) => { + alt!(@as_expr $member0, $(nom::branch::alt(($member1, $member2)),)+) + }; + ($($member0:expr, $member1:expr,)*) => { + alt!(@as_expr $(nom::branch::alt(($member0, $member1)),)+) + }; + (@as_expr $($member:expr,)*) => { + nom::branch::alt(($($member,)+)) + }; +} + +macro_rules! ast_types { + (@extract_type struct $name:ident<'a> $($rest:tt)*) => ($name<'a>); + (@extract_type struct $name:ident $($rest:tt)*) => ($name); + (@extract_type enum $name:ident<'a> $($rest:tt)*) => ($name<'a>); + (@extract_type enum $name:ident $($rest:tt)*) => ($name); + + () => (); + ( + $(#[$attr:meta])* + struct $name:ident<'a> { + $($fields:tt)* + } + $($rest:tt)* + ) => ( + __ast_struct! { + @launch_pad + $(#[$attr])* + $name + [ 'a ] + [ ] + { $($fields)* } + } + ast_types!($($rest)*); + ); + ( + $(#[$attr:meta])* + struct $name:ident<$($generics:ident),+> where [$($bounds:tt)+] { + $($fields:tt)* + } + $($rest:tt)* + ) => ( + __ast_struct! { + @launch_pad + $(#[$attr])* + $name + [$($generics)+] + [$($bounds)+] + { $($fields)* } + } + ast_types!($($rest)*); + ); + ( + $(#[$attr:meta])* + struct $name:ident { + $($fields:tt)* + } + $($rest:tt)* + ) => ( + __ast_struct! { + @launch_pad + $(#[$attr])* + $name + [ ] + [ ] + { $($fields)* } + } + ast_types!($($rest)*); + ); + + ( + $(#[$attr:meta])* + struct $name:ident<'a> ( + $($fields:tt)* + ) + $($rest:tt)* + ) => ( + __ast_tuple_struct! { + @launch_pad + $(#[$attr])* + $name + [ 'a ] + ( $($fields)* ) + } + ast_types!($($rest)*); + ); + ( + $(#[$attr:meta])* + struct $name:ident ( + $($fields:tt)* + ) + $($rest:tt)* + ) => ( + __ast_tuple_struct! { + @launch_pad + $(#[$attr])* + $name + [ ] + ( $($fields)* ) + } + ast_types!($($rest)*); + ); + + ( + $(#[$attr:meta])* + enum $name:ident<'a> { + $($variants:tt)* + } + $($rest:tt)* + ) => ( + __ast_enum! { + @launch_pad + $(#[$attr])* + $name + [ 'a ] + { $($variants)* } + } + ast_types!($($rest)*); + ); + ( + $(#[$attr:meta])* + enum $name:ident { + $($variants:tt)* + } + $($rest:tt)* + ) => ( + __ast_enum! { + @launch_pad + $(#[$attr])* + $name + [ ] + { $($variants)* } + } + ast_types!($($rest)*); + ); +} + +macro_rules! __ast_tuple_struct { + (@launch_pad + $(#[$attr:meta])* + $name:ident + [ $($maybe_a:tt)* ] + ( $inner:ty = $parser:expr, ) + ) => ( + $(#[$attr])* + #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] + pub struct $name<$($maybe_a)*>(pub $inner); + + impl<'a> $crate::Parse<'a> for $name<$($maybe_a)*> { + fn parse(input: &'a str) -> $crate::IResult<&'a str, Self> { + use nom::lib::std::result::Result::*; + + match $parser(input) { + Err(e) => Err(e), + Ok((i, inner)) => Ok((i, $name(inner))), + } + } + } + ); + (@launch_pad + $(#[$attr:meta])* + $name:ident + [ $($maybe_a:tt)* ] + ( $inner:ty, ) + ) => ( + __ast_tuple_struct! { + @launch_pad + $(#[$attr])* + $name + [ $($maybe_a)* ] + ( $inner = weedle!($inner), ) + } + ); +} + +macro_rules! __ast_struct { + (@build_struct_decl + { + $(#[$attr:meta])* + $name:ident + [ $($generics:tt)* ] + $($field:ident : $type:ty)* + } + { } + ) => { + $(#[$attr])* + #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] + pub struct $name<$($generics)*> { + $(pub $field : $type,)* + } + }; + (@build_struct_decl + { $($prev:tt)* } + { $field:ident : $type:ty, $($rest:tt)* } + ) => ( + __ast_struct! { + @build_struct_decl + { $($prev)* $field : $type } + { $($rest)* } + } + ); + (@build_struct_decl + { $($prev:tt)* } + { $field:ident : $type:ty = $parser:expr, $($rest:tt)* } + ) => ( + __ast_struct! { + @build_struct_decl + { $($prev)* $field : $type } + { $($rest)* } + } + ); + + (@build_parser + { $i:expr, $($field:ident)* } + { } + ) => ({ + use nom::lib::std::result::Result::Ok; + Ok(($i, Self { $($field,)* })) + }); + (@build_parser + { $i:expr, $($prev:tt)* } + { $field:ident : $type:ty = $parser:expr, $($rest:tt)* } + ) => ({ + use nom::lib::std::result::Result::*; + + match $parser($i) { + Err(e) => Err(e), + Ok((i, $field)) => { + __ast_struct! { + @build_parser + { i, $($prev)* $field } + { $($rest)* } + } + }, + } + }); + (@build_parser + { $($prev:tt)* } + { $field:ident : $type:ty, $($rest:tt)* } + ) => ( + __ast_struct! { + @build_parser + { $($prev)* } + { $field : $type = weedle!($type), $($rest)* } + } + ); + + ( + @launch_pad + $(#[$attr:meta])* + $name:ident + [ ] + [ ] + { $($fields:tt)* } + ) => { + __ast_struct! { + @build_struct_decl + { + $(#[$attr])* + $name + [ ] + } + { $($fields)* } + } + + impl<'a> $crate::Parse<'a> for $name { + fn parse(input: &'a str) -> $crate::IResult<&'a str, Self> { + __ast_struct! { + @build_parser + { input, } + { $($fields)* } + } + } + } + }; + ( + @launch_pad + $(#[$attr:meta])* + $name:ident + [ 'a ] + [ ] + { $($fields:tt)* } + ) => { + __ast_struct! { + @build_struct_decl + { + $(#[$attr])* + $name + [ 'a ] + } + { $($fields)* } + } + + impl<'a> $crate::Parse<'a> for $name<'a> { + fn parse(input: &'a str) -> $crate::IResult<&'a str, Self> { + __ast_struct! { + @build_parser + { input, } + { $($fields)* } + } + } + } + }; + ( + @launch_pad + $(#[$attr:meta])* + $name:ident + [$($generics:ident)+] + [$($bounds:tt)+] + { $($fields:tt)* } + ) => { + __ast_struct! { + @build_struct_decl + { + $(#[$attr])* + $name + [$($generics),+] + } + { $($fields)* } + } + + impl<'a, $($generics),+> $crate::Parse<'a> for $name<$($generics),+> where $($bounds)+ { + fn parse(input: &'a str) -> $crate::IResult<&'a str, Self> { + __ast_struct! { + @build_parser + { input, } + { $($fields)* } + } + } + } + }; +} + +macro_rules! __ast_enum { + (@build_enum_decl + { + $(#[$attr:meta])* + $name:ident + [ $($maybe_a:tt)* ] + $($variant:ident($member:ty))* + } + { } + ) => ( + $(#[$attr])* + #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] + pub enum $name<$($maybe_a)*> { + $($variant($member),)* + } + ); + (@build_enum_decl + { $($prev:tt)* } + { $variant:ident($member:ty), $($rest:tt)* } + ) => ( + __ast_enum! { + @build_enum_decl + { $($prev)* $variant($member) } + { $($rest)* } + } + ); + (@build_enum_decl + { $($prev:tt)* } + { $(#[$attr:meta])* $variant:ident( $($member:tt)* ), $($rest:tt)* } + ) => ( + __ast_enum! { + @build_enum_decl + { $($prev)* $variant(ast_types! { @extract_type $($member)* }) } + { $($rest)* } + } + ); + + (@build_sub_types { }) => (); + (@build_sub_types + { $variant:ident($member:ty), $($rest:tt)* } + ) => ( + __ast_enum! { + @build_sub_types + { $($rest)* } + } + ); + (@build_sub_types + { $(#[$attr:meta])* $variant:ident( $($member:tt)* ), $($rest:tt)* } + ) => ( + ast_types! { + $(#[$attr])* + $($member)* + } + __ast_enum! { + @build_sub_types + { $($rest)* } + } + ); + + + (@build_conversions $name:ident [ $($maybe_a:tt)* ] { }) => (); + (@build_conversions + $name:ident + [ $($maybe_a:tt)* ] + { $variant:ident($member:ty), $($rest:tt)* } + ) => ( + impl<$($maybe_a)*> From<$member> for $name<$($maybe_a)*> { + fn from(x: $member) -> Self { + $name::$variant(x) + } + } + __ast_enum! { + @build_conversions + $name + [ $($maybe_a)* ] + { $($rest)* } + } + ); + (@build_conversions + $name:ident + [ $($maybe_a:tt)* ] + { $(#[$attr:meta])* $variant:ident( $($member:tt)* ), $($rest:tt)* } + ) => ( + __ast_enum! { + @build_conversions + $name + [ $($maybe_a)* ] + { $variant(ast_types! { @extract_type $($member)* }), $($rest)* } + } + ); + + (@build_parse + { $name:ident [ $($maybe_a:tt)* ] $($member:ty)* } + { } + ) => ( + impl<'a> $crate::Parse<'a> for $name<$($maybe_a)*> { + parser!(alt!( + $(nom::combinator::map(weedle!($member), From::from),)* + )); + } + ); + (@build_parse + { $($prev:tt)* } + { $variant:ident($member:ty), $($rest:tt)* } + ) => ( + __ast_enum! { + @build_parse + { $($prev)* $member } + { $($rest)* } + } + ); + (@build_parse + { $($prev:tt)* } + { $(#[$attr:meta])* $variant:ident( $($member:tt)* ), $($rest:tt)* } + ) => ( + __ast_enum! { + @build_parse + { $($prev)* ast_types! { @extract_type $($member)* } } + { $($rest)* } + } + ); + + (@launch_pad + $(#[$attr:meta])* + $name:ident + [ $($maybe_a:tt)* ] + { $($variants:tt)* } + ) => ( + __ast_enum! { + @build_enum_decl + { $(#[$attr])* $name [ $($maybe_a)* ] } + { $($variants)* } + } + + __ast_enum! { + @build_sub_types + { $($variants)* } + } + + __ast_enum! { + @build_conversions + $name + [ $($maybe_a)* ] + { $($variants)* } + } + + __ast_enum! { + @build_parse + { $name [ $($maybe_a)* ] } + { $($variants)* } + } + ); +} + +#[cfg(test)] +macro_rules! test { + (@arg $parsed:ident) => {}; + (@arg $parsed:ident $($lhs:tt).+ == $rhs:expr; $($rest:tt)*) => { + assert_eq!($parsed.$($lhs).+, $rhs); + test!(@arg $parsed $($rest)*); + }; + (@arg $parsed:ident $($lhs:tt).+(); $($rest:tt)*) => { + assert!($parsed.$($lhs).+()); + test!(@arg $parsed $($rest)*); + }; + (@arg $parsed:ident $($lhs:tt).+() == $rhs:expr; $($rest:tt)*) => { + assert_eq!($parsed.$($lhs).+(), $rhs); + test!(@arg $parsed $($rest)*); + }; + (err $name:ident { $raw:expr => $typ:ty }) => { + #[test] + fn $name() { + <$typ>::parse($raw).unwrap_err(); + } + }; + ($name:ident { $raw:expr => $rem:expr; $typ:ty => $val:expr }) => { + #[test] + fn $name() { + let (rem, parsed) = <$typ>::parse($raw).unwrap(); + assert_eq!(rem, $rem); + assert_eq!(parsed, $val); + } + }; + ($name:ident { $raw:expr => $rem:expr; $typ:ty; $($body:tt)* }) => { + #[test] + fn $name() { + let (_rem, _parsed) = <$typ>::parse($raw).unwrap(); + assert_eq!(_rem, $rem); + test!(@arg _parsed $($body)*); + } + }; +} + +#[cfg(test)] +macro_rules! test_variants { + ($struct_:ident { $( $variant:ident == $value:expr ),* $(,)* }) => { + #[allow(non_snake_case)] + mod $struct_ { + $( + mod $variant { + use $crate::types::*; + #[test] + fn should_parse() { + let (rem, parsed) = $struct_::parse($value).unwrap(); + assert_eq!(rem, ""); + match parsed { + $struct_::$variant(_) => {}, + _ => { panic!("Failed to parse"); } + } + } + } + )* + } + }; +} diff --git a/third_party/rust/weedle2/src/mixin.rs b/third_party/rust/weedle2/src/mixin.rs new file mode 100644 index 0000000000..dcb40d50c5 --- /dev/null +++ b/third_party/rust/weedle2/src/mixin.rs @@ -0,0 +1,60 @@ +use crate::argument::ArgumentList; +use crate::attribute::ExtendedAttributeList; +use crate::common::{Identifier, Parenthesized}; +use crate::interface::{ConstMember, StringifierMember}; +use crate::types::{AttributedType, ReturnType}; + +/// Parses the members declarations of a mixin +pub type MixinMembers<'a> = Vec<MixinMember<'a>>; + +ast_types! { + /// Parses one of the variants of a mixin member + enum MixinMember<'a> { + Const(ConstMember<'a>), + /// Parses `[attributes]? stringifier? returntype identifier? (( args ));` + /// + /// (( )) means ( ) chars + Operation(struct OperationMixinMember<'a> { + attributes: Option<ExtendedAttributeList<'a>>, + stringifier: Option<term!(stringifier)>, + return_type: ReturnType<'a>, + identifier: Option<Identifier<'a>>, + args: Parenthesized<ArgumentList<'a>>, + semi_colon: term!(;), + }), + /// Parses `[attributes]? stringifier? readonly? attribute attributedtype identifier;` + Attribute(struct AttributeMixinMember<'a> { + attributes: Option<ExtendedAttributeList<'a>>, + stringifier: Option<term!(stringifier)>, + readonly: Option<term!(readonly)>, + attribute: term!(attribute), + type_: AttributedType<'a>, + identifier: Identifier<'a>, + semi_colon: term!(;), + }), + Stringifier(StringifierMember<'a>), + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::Parse; + + test!(should_parse_attribute_mixin_member { "stringifier readonly attribute short name;" => + ""; + AttributeMixinMember; + attributes.is_none(); + stringifier.is_some(); + readonly.is_some(); + identifier.0 == "name"; + }); + + test!(should_parse_operation_mixin_member { "short fnName(long a);" => + ""; + OperationMixinMember; + attributes.is_none(); + stringifier.is_none(); + identifier.is_some(); + }); +} diff --git a/third_party/rust/weedle2/src/namespace.rs b/third_party/rust/weedle2/src/namespace.rs new file mode 100644 index 0000000000..ed28573218 --- /dev/null +++ b/third_party/rust/weedle2/src/namespace.rs @@ -0,0 +1,52 @@ +use crate::argument::ArgumentList; +use crate::attribute::ExtendedAttributeList; +use crate::common::{Identifier, Parenthesized}; +use crate::types::{AttributedType, ReturnType}; + +/// Parses namespace members declaration +pub type NamespaceMembers<'a> = Vec<NamespaceMember<'a>>; + +ast_types! { + /// Parses namespace member declaration + enum NamespaceMember<'a> { + /// Parses `[attributes]? returntype identifier? (( args ));` + /// + /// (( )) means ( ) chars + Operation(struct OperationNamespaceMember<'a> { + attributes: Option<ExtendedAttributeList<'a>>, + return_type: ReturnType<'a>, + identifier: Option<Identifier<'a>>, + args: Parenthesized<ArgumentList<'a>>, + semi_colon: term!(;), + }), + /// Parses `[attribute]? readonly attributetype type identifier;` + Attribute(struct AttributeNamespaceMember<'a> { + attributes: Option<ExtendedAttributeList<'a>>, + readonly: term!(readonly), + attribute: term!(attribute), + type_: AttributedType<'a>, + identifier: Identifier<'a>, + semi_colon: term!(;), + }), + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::Parse; + + test!(should_parse_attribute_namespace_member { "readonly attribute short name;" => + ""; + AttributeNamespaceMember; + attributes.is_none(); + identifier.0 == "name"; + }); + + test!(should_parse_operation_namespace_member { "short (long a, long b);" => + ""; + OperationNamespaceMember; + attributes.is_none(); + identifier.is_none(); + }); +} diff --git a/third_party/rust/weedle2/src/term.rs b/third_party/rust/weedle2/src/term.rs new file mode 100644 index 0000000000..d73de79847 --- /dev/null +++ b/third_party/rust/weedle2/src/term.rs @@ -0,0 +1,699 @@ +macro_rules! generate_terms { + ($( $(#[$attr:meta])* $typ:ident => $tok:expr ),*) => { + $( + $(#[$attr])* + #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct $typ; + + impl<'a> $crate::Parse<'a> for $typ { + parser!(nom::combinator::value( + $typ, + crate::whitespace::ws( + nom::bytes::complete::tag($tok) + ) + )); + } + )* + }; +} + +struct AlphaNumUnderscoreDash; + +impl nom::FindToken<char> for AlphaNumUnderscoreDash { + fn find_token(&self, token: char) -> bool { + crate::common::is_alphanum_underscore_dash(token) + } +} + +pub(crate) fn ident_tag(tag: &'static str) -> impl FnMut(&str) -> nom::IResult<&str, &str> { + move |input| { + nom::sequence::terminated( + nom::bytes::complete::tag(tag), + nom::combinator::not(nom::combinator::map_parser( + nom::bytes::complete::take(1usize), + nom::bytes::complete::is_a(AlphaNumUnderscoreDash), + )), + )(input) + } +} + +macro_rules! generate_terms_for_names { + ($( $(#[$attr:meta])* $typ:ident => $tok:expr,)*) => { + $( + $(#[$attr])* + #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct $typ; + + impl<'a> $crate::Parse<'a> for $typ { + parser!(nom::combinator::value( + $typ, + $crate::whitespace::ws($crate::term::ident_tag($tok)) + )); + } + )* + }; +} + +generate_terms! { + /// Represents the terminal symbol `(` + OpenParen => "(", + + /// Represents the terminal symbol `)` + CloseParen => ")", + + /// Represents the terminal symbol `[` + OpenBracket => "[", + + /// Represents the terminal symbol `]` + CloseBracket => "]", + + /// Represents the terminal symbol `{` + OpenBrace => "{", + + /// Represents the terminal symbol `}` + CloseBrace => "}", + + /// Represents the terminal symbol `,` + Comma => ",", + + /// Represents the terminal symbol `-` + Minus => "-", + + /// Represents the terminal symbol `.` + Dot => ".", + + /// Represents the terminal symbol `...` + Ellipsis => "...", + + /// Represents the terminal symbol `:` + Colon => ":", + + /// Represents the terminal symbol `;` + SemiColon => ";", + + /// Represents the terminal symbol `<` + LessThan => "<", + + /// Represents the terminal symbol `=` + Assign => "=", + + /// Represents the terminal symbol `>` + GreaterThan => ">", + + /// Represents the terminal symbol `?` + QMark => "?" +} + +generate_terms_for_names! { + /// Represents the terminal symbol `or` + Or => "or", + + /// Represents the terminal symbol `optional` + Optional => "optional", + + /// Represents the terminal symbol `async` + Async => "async", + + /// Represents the terminal symbol `attribute` + Attribute => "attribute", + + /// Represents the terminal symbol `callback` + Callback => "callback", + + /// Represents the terminal symbol `const` + Const => "const", + + /// Represents the terminal symbol `deleter` + Deleter => "deleter", + + /// Represents the terminal symbol `dictionary` + Dictionary => "dictionary", + + /// Represents the terminal symbol `enum` + Enum => "enum", + + /// Represents the terminal symbol `getter` + Getter => "getter", + + /// Represents the terminal symbol `includes` + Includes => "includes", + + /// Represents the terminal symbol `inherit` + Inherit => "inherit", + + /// Represents the terminal symbol `interface` + Interface => "interface", + + /// Represents the terminal symbol `iterable` + Iterable => "iterable", + + /// Represents the terminal symbol `maplike` + Maplike => "maplike", + + /// Represents the terminal symbol `namespace` + Namespace => "namespace", + + /// Represents the terminal symbol `partial` + Partial => "partial", + + /// Represents the terminal symbol `required` + Required => "required", + + /// Represents the terminal symbol `setlike` + Setlike => "setlike", + + /// Represents the terminal symbol `setter` + Setter => "setter", + + /// Represents the terminal symbol `static` + Static => "static", + + /// Represents the terminal symbol `stringifier` + Stringifier => "stringifier", + + /// Represents the terminal symbol `typedef` + Typedef => "typedef", + + /// Represents the terminal symbol `unrestricted` + Unrestricted => "unrestricted", + + /// Represents the terminal symbol `symbol` + Symbol => "symbol", + + /// Represents the terminal symbol `Infinity` + NegInfinity => "-Infinity", + + /// Represents the terminal symbol `ByteString` + ByteString => "ByteString", + + /// Represents the terminal symbol `DOMString` + DOMString => "DOMString", + + /// Represents the terminal symbol `FrozenArray` + FrozenArray => "FrozenArray", + + /// Represents the terminal symbol `Infinity` + Infinity => "Infinity", + + /// Represents the terminal symbol `NaN` + NaN => "NaN", + + /// Represents the terminal symbol `USVString` + USVString => "USVString", + + /// Represents the terminal symbol `any` + Any => "any", + + /// Represents the terminal symbol `boolean` + Boolean => "boolean", + + /// Represents the terminal symbol `byte` + Byte => "byte", + + /// Represents the terminal symbol `double` + Double => "double", + + /// Represents the terminal symbol `false` + False => "false", + + /// Represents the terminal symbol `float` + Float => "float", + + /// Represents the terminal symbol `long` + Long => "long", + + /// Represents the terminal symbol `null` + Null => "null", + + /// Represents the terminal symbol `object` + Object => "object", + + /// Represents the terminal symbol `octet` + Octet => "octet", + + /// Represents the terminal symbol `sequence` + Sequence => "sequence", + + /// Represents the terminal symbol `short` + Short => "short", + + /// Represents the terminal symbol `true` + True => "true", + + /// Represents the terminal symbol `unsigned` + Unsigned => "unsigned", + + /// Represents the terminal symbol `undefined` + Undefined => "undefined", + + /// Represents the terminal symbol `record` + Record => "record", + + /// Represents the terminal symbol `ArrayBuffer` + ArrayBuffer => "ArrayBuffer", + + /// Represents the terminal symbol `DataView` + DataView => "DataView", + + /// Represents the terminal symbol `Int8Array` + Int8Array => "Int8Array", + + /// Represents the terminal symbol `Int16Array` + Int16Array => "Int16Array", + + /// Represents the terminal symbol `Int32Array` + Int32Array => "Int32Array", + + /// Represents the terminal symbol `Uint8Array` + Uint8Array => "Uint8Array", + + /// Represents the terminal symbol `Uint16Array` + Uint16Array => "Uint16Array", + + /// Represents the terminal symbol `Uint32Array` + Uint32Array => "Uint32Array", + + /// Represents the terminal symbol `Uint8ClampedArray` + Uint8ClampedArray => "Uint8ClampedArray", + + /// Represents the terminal symbol `Float32Array` + Float32Array => "Float32Array", + + /// Represents the terminal symbol `Float64Array` + Float64Array => "Float64Array", + + /// Represents the terminal symbol `ArrayBufferView` + ArrayBufferView => "ArrayBufferView", + + /// Represents the terminal symbol `BufferSource + BufferSource => "BufferSource", + + /// Represents the terminal symbol `Promise` + Promise => "Promise", + + /// Represents the terminal symbol `Error` + Error => "Error", + + /// Represents the terminal symbol `readonly` + ReadOnly => "readonly", + + /// Represents the terminal symbol `mixin` + Mixin => "mixin", + + /// Represents the terminal symbol `implements` + Implements => "implements", + + /// Represents the terminal symbol `legacycaller` + LegacyCaller => "legacycaller", + + /// Represents the terminal symbol `constructor` + Constructor => "constructor", +} + +#[macro_export] +macro_rules! term { + (OpenParen) => { + $crate::term::OpenParen + }; + (CloseParen) => { + $crate::term::CloseParen + }; + (OpenBracket) => { + $crate::term::OpenBracket + }; + (CloseBracket) => { + $crate::term::CloseBracket + }; + (OpenBrace) => { + $crate::term::OpenBrace + }; + (CloseBrace) => { + $crate::term::CloseBrace + }; + (,) => { + $crate::term::Comma + }; + (-) => { + $crate::term::Minus + }; + (.) => { + $crate::term::Dot + }; + (...) => { + $crate::term::Ellipsis + }; + (:) => { + $crate::term::Colon + }; + (;) => { + $crate::term::SemiColon + }; + (<) => { + $crate::term::LessThan + }; + (=) => { + $crate::term::Assign + }; + (>) => { + $crate::term::GreaterThan + }; + (?) => { + $crate::term::QMark + }; + (or) => { + $crate::term::Or + }; + (optional) => { + $crate::term::Optional + }; + (async) => { + $crate::term::Async + }; + (attribute) => { + $crate::term::Attribute + }; + (callback) => { + $crate::term::Callback + }; + (const) => { + $crate::term::Const + }; + (deleter) => { + $crate::term::Deleter + }; + (dictionary) => { + $crate::term::Dictionary + }; + (enum) => { + $crate::term::Enum + }; + (getter) => { + $crate::term::Getter + }; + (includes) => { + $crate::term::Includes + }; + (inherit) => { + $crate::term::Inherit + }; + (interface) => { + $crate::term::Interface + }; + (iterable) => { + $crate::term::Iterable + }; + (maplike) => { + $crate::term::Maplike + }; + (namespace) => { + $crate::term::Namespace + }; + (partial) => { + $crate::term::Partial + }; + (required) => { + $crate::term::Required + }; + (setlike) => { + $crate::term::Setlike + }; + (setter) => { + $crate::term::Setter + }; + (static) => { + $crate::term::Static + }; + (stringifier) => { + $crate::term::Stringifier + }; + (typedef) => { + $crate::term::Typedef + }; + (unrestricted) => { + $crate::term::Unrestricted + }; + (symbol) => { + $crate::term::Symbol + }; + (- Infinity) => { + $crate::term::NegInfinity + }; + (ByteString) => { + $crate::term::ByteString + }; + (DOMString) => { + $crate::term::DOMString + }; + (FrozenArray) => { + $crate::term::FrozenArray + }; + (Infinity) => { + $crate::term::Infinity + }; + (NaN) => { + $crate::term::NaN + }; + (USVString) => { + $crate::term::USVString + }; + (any) => { + $crate::term::Any + }; + (boolean) => { + $crate::term::Boolean + }; + (byte) => { + $crate::term::Byte + }; + (double) => { + $crate::term::Double + }; + (false) => { + $crate::term::False + }; + (float) => { + $crate::term::Float + }; + (long) => { + $crate::term::Long + }; + (null) => { + $crate::term::Null + }; + (object) => { + $crate::term::Object + }; + (octet) => { + $crate::term::Octet + }; + (sequence) => { + $crate::term::Sequence + }; + (short) => { + $crate::term::Short + }; + (true) => { + $crate::term::True + }; + (unsigned) => { + $crate::term::Unsigned + }; + (undefined) => { + $crate::term::Undefined + }; + (record) => { + $crate::term::Record + }; + (ArrayBuffer) => { + $crate::term::ArrayBuffer + }; + (DataView) => { + $crate::term::DataView + }; + (Int8Array) => { + $crate::term::Int8Array + }; + (Int16Array) => { + $crate::term::Int16Array + }; + (Int32Array) => { + $crate::term::Int32Array + }; + (Uint8Array) => { + $crate::term::Uint8Array + }; + (Uint16Array) => { + $crate::term::Uint16Array + }; + (Uint32Array) => { + $crate::term::Uint32Array + }; + (Uint8ClampedArray) => { + $crate::term::Uint8ClampedArray + }; + (Float32Array) => { + $crate::term::Float32Array + }; + (Float64Array) => { + $crate::term::Float64Array + }; + (ArrayBufferView) => { + $crate::term::ArrayBufferView + }; + (BufferSource) => { + $crate::term::BufferSource + }; + (Promise) => { + $crate::term::Promise + }; + (Error) => { + $crate::term::Error + }; + (readonly) => { + $crate::term::ReadOnly + }; + (mixin) => { + $crate::term::Mixin + }; + (implements) => { + $crate::term::Implements + }; + (legacycaller) => { + $crate::term::LegacyCaller + }; + (constructor) => { + $crate::term::Constructor + }; +} + +#[cfg(test)] +mod test { + macro_rules! generate_tests { + ($($m:ident, $typ:ident, $string:expr;)*) => { + $( + mod $m { + use super::super::$typ; + use crate::Parse; + + #[test] + fn should_parse() { + let (rem, parsed) = $typ::parse(concat!($string)).unwrap(); + assert_eq!(rem, ""); + assert_eq!(parsed, $typ); + } + + #[test] + fn should_parse_with_preceding_spaces() { + let (rem, parsed) = $typ::parse(concat!(" ", $string)).unwrap(); + assert_eq!(rem, ""); + assert_eq!(parsed, $typ); + } + + #[test] + fn should_parse_with_succeeding_spaces() { + let (rem, parsed) = $typ::parse(concat!($string, " ")).unwrap(); + assert_eq!(rem, ""); + assert_eq!(parsed, $typ); + } + + #[test] + fn should_parse_with_surrounding_spaces() { + let (rem, parsed) = $typ::parse(concat!(" ", $string, " ")).unwrap(); + assert_eq!(rem, ""); + assert_eq!(parsed, $typ); + } + + #[test] + fn should_parse_if_anything_next() { + let (rem, parsed) = $typ::parse(concat!($string, " anything")).unwrap(); + assert_eq!(rem, "anything"); + assert_eq!(parsed, $typ); + } + } + )* + }; + } + + generate_tests![ + openparen, OpenParen, "("; + closeparen, CloseParen, ")"; + openbracket, OpenBracket, "["; + closebracket, CloseBracket, "]"; + openbrace, OpenBrace, "{"; + closebrace, CloseBrace, "}"; + comma, Comma, ","; + minus, Minus, "-"; + dot, Dot, "."; + ellipsis, Ellipsis, "..."; + colon, Colon, ":"; + semicolon, SemiColon, ";"; + lessthan, LessThan, "<"; + assign, Assign, "="; + greaterthan, GreaterThan, ">"; + qmark, QMark, "?"; + or, Or, "or"; + optional, Optional, "optional"; + async_, Async, "async"; + attribute, Attribute, "attribute"; + callback, Callback, "callback"; + const_, Const, "const"; + deleter, Deleter, "deleter"; + dictionary, Dictionary, "dictionary"; + enum_, Enum, "enum"; + getter, Getter, "getter"; + includes, Includes, "includes"; + inherit, Inherit, "inherit"; + interface, Interface, "interface"; + iterable, Iterable, "iterable"; + maplike, Maplike, "maplike"; + namespace, Namespace, "namespace"; + partial, Partial, "partial"; + required, Required, "required"; + setlike, Setlike, "setlike"; + setter, Setter, "setter"; + static_, Static, "static"; + stringifier, Stringifier, "stringifier"; + typedef, Typedef, "typedef"; + unrestricted, Unrestricted, "unrestricted"; + symbol, Symbol, "symbol"; + neginfinity, NegInfinity, "-Infinity"; + bytestring, ByteString, "ByteString"; + domstring, DOMString, "DOMString"; + frozenarray, FrozenArray, "FrozenArray"; + infinity, Infinity, "Infinity"; + nan, NaN, "NaN"; + usvstring, USVString, "USVString"; + any, Any, "any"; + boolean, Boolean, "boolean"; + byte, Byte, "byte"; + double, Double, "double"; + false_, False, "false"; + float, Float, "float"; + long, Long, "long"; + null, Null, "null"; + object, Object, "object"; + octet, Octet, "octet"; + sequence, Sequence, "sequence"; + short, Short, "short"; + true_, True, "true"; + unsigned, Unsigned, "unsigned"; + undefined, Undefined, "undefined"; + record, Record, "record"; + arraybuffer, ArrayBuffer, "ArrayBuffer"; + dataview, DataView, "DataView"; + int8array, Int8Array, "Int8Array"; + int16array, Int16Array, "Int16Array"; + int32array, Int32Array, "Int32Array"; + uint8array, Uint8Array, "Uint8Array"; + uint16array, Uint16Array, "Uint16Array"; + uint32array, Uint32Array, "Uint32Array"; + uint8clampedarray, Uint8ClampedArray, "Uint8ClampedArray"; + float32array, Float32Array, "Float32Array"; + float64array, Float64Array, "Float64Array"; + promise, Promise, "Promise"; + error, Error, "Error"; + implements, Implements, "implements"; + legacycaller, LegacyCaller, "legacycaller"; + constructor, Constructor, "constructor"; + ]; +} diff --git a/third_party/rust/weedle2/src/types.rs b/third_party/rust/weedle2/src/types.rs new file mode 100644 index 0000000000..913c307da9 --- /dev/null +++ b/third_party/rust/weedle2/src/types.rs @@ -0,0 +1,385 @@ +use crate::attribute::ExtendedAttributeList; +use crate::common::{Generics, Identifier, Parenthesized, Punctuated}; +use crate::term; +use crate::Parse; + +/// Parses a union of types +pub type UnionType<'a> = Parenthesized<Punctuated<UnionMemberType<'a>, term!(or)>>; + +ast_types! { + /// Parses either single type or a union type + enum Type<'a> { + /// Parses one of the single types + Single(enum SingleType<'a> { + Any(term!(any)), + NonAny(NonAnyType<'a>), + }), + Union(MayBeNull<UnionType<'a>>), + } + + // Parses any single non-any type + enum NonAnyType<'a> { + Promise(PromiseType<'a>), + Integer(MayBeNull<IntegerType>), + FloatingPoint(MayBeNull<FloatingPointType>), + Boolean(MayBeNull<term!(boolean)>), + Byte(MayBeNull<term!(byte)>), + Octet(MayBeNull<term!(octet)>), + ByteString(MayBeNull<term!(ByteString)>), + DOMString(MayBeNull<term!(DOMString)>), + USVString(MayBeNull<term!(USVString)>), + Sequence(MayBeNull<SequenceType<'a>>), + Object(MayBeNull<term!(object)>), + Symbol(MayBeNull<term!(symbol)>), + Error(MayBeNull<term!(Error)>), + ArrayBuffer(MayBeNull<term!(ArrayBuffer)>), + DataView(MayBeNull<term!(DataView)>), + Int8Array(MayBeNull<term!(Int8Array)>), + Int16Array(MayBeNull<term!(Int16Array)>), + Int32Array(MayBeNull<term!(Int32Array)>), + Uint8Array(MayBeNull<term!(Uint8Array)>), + Uint16Array(MayBeNull<term!(Uint16Array)>), + Uint32Array(MayBeNull<term!(Uint32Array)>), + Uint8ClampedArray(MayBeNull<term!(Uint8ClampedArray)>), + Float32Array(MayBeNull<term!(Float32Array)>), + Float64Array(MayBeNull<term!(Float64Array)>), + ArrayBufferView(MayBeNull<term!(ArrayBufferView)>), + BufferSource(MayBeNull<term!(BufferSource)>), + FrozenArrayType(MayBeNull<FrozenArrayType<'a>>), + RecordType(MayBeNull<RecordType<'a>>), + Identifier(MayBeNull<Identifier<'a>>), + } + + /// Parses `sequence<Type>` + struct SequenceType<'a> { + sequence: term!(sequence), + generics: Generics<Box<Type<'a>>>, + } + + /// Parses `FrozenArray<Type>` + struct FrozenArrayType<'a> { + frozen_array: term!(FrozenArray), + generics: Generics<Box<Type<'a>>>, + } + + /// Parses a nullable type. Ex: `object | object??` + /// + /// `??` means an actual ? not an optional requirement + #[derive(Copy)] + struct MayBeNull<T> where [T: Parse<'a>] { + type_: T, + q_mark: Option<term::QMark>, + } + + /// Parses a `Promise<Type|undefined>` type + struct PromiseType<'a> { + promise: term!(Promise), + generics: Generics<Box<ReturnType<'a>>>, + } + + /// Parses `unsigned? short|long|(long long)` + #[derive(Copy)] + enum IntegerType { + /// Parses `unsigned? long long` + #[derive(Copy)] + LongLong(struct LongLongType { + unsigned: Option<term!(unsigned)>, + long_long: (term!(long), term!(long)), + }), + /// Parses `unsigned? long` + #[derive(Copy)] + Long(struct LongType { + unsigned: Option<term!(unsigned)>, + long: term!(long), + }), + /// Parses `unsigned? short` + #[derive(Copy)] + Short(struct ShortType { + unsigned: Option<term!(unsigned)>, + short: term!(short), + }), + } + + /// Parses `unrestricted? float|double` + #[derive(Copy)] + enum FloatingPointType { + /// Parses `unrestricted? float` + #[derive(Copy)] + Float(struct FloatType { + unrestricted: Option<term!(unrestricted)>, + float: term!(float), + }), + /// Parses `unrestricted? double` + #[derive(Copy)] + Double(struct DoubleType { + unrestricted: Option<term!(unrestricted)>, + double: term!(double), + }), + } + + /// Parses `record<StringType, Type>` + struct RecordType<'a> { + record: term!(record), + generics: Generics<(Box<RecordKeyType<'a>>, term!(,), Box<Type<'a>>)>, + } + + /// Parses one of the string types `ByteString|DOMString|USVString` or any other type. + enum RecordKeyType<'a> { + Byte(term!(ByteString)), + DOM(term!(DOMString)), + USV(term!(USVString)), + NonAny(NonAnyType<'a>), + } + + /// Parses one of the member of a union type + enum UnionMemberType<'a> { + Single(AttributedNonAnyType<'a>), + Union(MayBeNull<UnionType<'a>>), + } + + /// Parses a const type + enum ConstType<'a> { + Integer(MayBeNull<IntegerType>), + FloatingPoint(MayBeNull<FloatingPointType>), + Boolean(MayBeNull<term!(boolean)>), + Byte(MayBeNull<term!(byte)>), + Octet(MayBeNull<term!(octet)>), + Identifier(MayBeNull<Identifier<'a>>), + } + + /// Parses the return type which may be `undefined` or any given Type + enum ReturnType<'a> { + Undefined(term!(undefined)), + Type(Type<'a>), + } + + /// Parses `[attributes]? type` + struct AttributedType<'a> { + attributes: Option<ExtendedAttributeList<'a>>, + type_: Type<'a>, + } + + /// Parses `[attributes]? type` where the type is a single non-any type + struct AttributedNonAnyType<'a> { + attributes: Option<ExtendedAttributeList<'a>>, + type_: NonAnyType<'a>, + } +} + +#[cfg(test)] +mod test { + use super::*; + + test!(should_parse_may_be_null { "short" => + ""; + MayBeNull<crate::types::IntegerType>; + q_mark.is_none(); + }); + + test!(should_parse_nullable { "short?" => + ""; + MayBeNull<crate::types::IntegerType>; + q_mark.is_some(); + }); + + test_variants!( + ReturnType { + Undefined == "undefined", + Type == "any", + } + ); + + test_variants!( + ConstType { + Integer == "short", + FloatingPoint == "float", + Boolean == "boolean", + Byte == "byte", + Octet == "octet", + Identifier == "name", + } + ); + + test_variants!( + NonAnyType { + Promise == "Promise<long>", + Integer == "long", + FloatingPoint == "float", + Boolean == "boolean", + Byte == "byte", + Octet == "octet", + ByteString == "ByteString", + DOMString == "DOMString", + USVString == "USVString", + Sequence == "sequence<short>", + Object == "object", + Symbol == "symbol", + Error == "Error", + ArrayBuffer == "ArrayBuffer", + DataView == "DataView", + Int8Array == "Int8Array", + Int16Array == "Int16Array", + Int32Array == "Int32Array", + Uint8Array == "Uint8Array", + Uint16Array == "Uint16Array", + Uint32Array == "Uint32Array", + Uint8ClampedArray == "Uint8ClampedArray", + Float32Array == "Float32Array", + Float64Array == "Float64Array", + ArrayBufferView == "ArrayBufferView", + BufferSource == "BufferSource", + FrozenArrayType == "FrozenArray<short>", + RecordType == "record<DOMString, short>", + Identifier == "mango" + } + ); + + test_variants!( + UnionMemberType { + Single == "byte", + Union == "([Clamp] unsigned long or byte)" + } + ); + + test_variants!( + RecordKeyType { + DOM == "DOMString", + USV == "USVString", + Byte == "ByteString" + } + ); + + test!(should_parse_record_type { "record<DOMString, short>" => + ""; + RecordType; + }); + + test!(should_parse_record_type_alt_types { "record<u64, short>" => + ""; + RecordType; + }); + + test!(should_parse_double_type { "double" => + ""; + DoubleType; + }); + + test!(should_parse_float_type { "float" => + ""; + FloatType; + }); + + test_variants!( + FloatingPointType { + Float == "float", + Double == "double" + } + ); + + test!(should_parse_long_long_type { "long long" => + ""; + LongLongType; + }); + + test!(should_parse_long_type { "long" => + ""; + LongType; + }); + + test!(should_parse_short_type { "short" => + ""; + ShortType; + }); + + test_variants!( + IntegerType { + Short == "short", + Long == "long", + LongLong == "long long" + } + ); + + test!(should_parse_promise_type { "Promise<short>" => + ""; + PromiseType; + }); + + test!(should_parse_frozen_array_type { "FrozenArray<short>" => + ""; + FrozenArrayType; + }); + + test!(should_parse_sequence_type { "sequence<short>" => + ""; + SequenceType; + }); + + test_variants!( + SingleType { + Any == "any", + NonAny == "Promise<short>", + } + ); + + test_variants!( + Type { + Single == "short", + Union == "(short or float)" + } + ); + + test!(should_parse_attributed_type { "[Named] short" => + ""; + AttributedType; + attributes.is_some(); + }); + + test!(should_parse_type_as_identifier { "DOMStringMap" => + // if type is not parsed as identifier, it is parsed as `DOMString` and 'Map' is left + ""; + crate::types::Type; + }); + + #[test] + fn should_parse_union_member_type_attributed_union() { + use crate::types::UnionMemberType; + let (rem, parsed) = UnionMemberType::parse("([Clamp] byte or [Named] byte)").unwrap(); + assert_eq!(rem, ""); + match parsed { + UnionMemberType::Union(MayBeNull { + type_: + Parenthesized { + body: Punctuated { list, .. }, + .. + }, + .. + }) => { + assert_eq!(list.len(), 2); + + match list[0] { + UnionMemberType::Single(AttributedNonAnyType { ref attributes, .. }) => { + assert!(attributes.is_some()); + } + + _ => { + panic!("Failed to parse list[0] attributes"); + } + }; + + match list[1] { + UnionMemberType::Single(AttributedNonAnyType { ref attributes, .. }) => { + assert!(attributes.is_some()); + } + + _ => { + panic!("Failed to parse list[1] attributes"); + } + }; + } + + _ => { + panic!("Failed to parse"); + } + } + } +} diff --git a/third_party/rust/weedle2/src/whitespace.rs b/third_party/rust/weedle2/src/whitespace.rs new file mode 100644 index 0000000000..336e4784e1 --- /dev/null +++ b/third_party/rust/weedle2/src/whitespace.rs @@ -0,0 +1,34 @@ +use nom::{IResult, Parser}; + +pub(crate) fn sp(input: &str) -> IResult<&str, &str> { + nom::combinator::recognize(nom::multi::many0(nom::branch::alt(( + // ignores line comments + nom::combinator::value( + (), + nom::sequence::tuple(( + nom::bytes::complete::tag("//"), + nom::bytes::complete::take_until("\n"), + nom::bytes::complete::tag("\n"), + )), + ), + // ignores whitespace + nom::combinator::value((), nom::character::complete::multispace1), + // ignores block comments + nom::combinator::value( + (), + nom::sequence::tuple(( + nom::bytes::complete::tag("/*"), + nom::bytes::complete::take_until("*/"), + nom::bytes::complete::tag("*/"), + )), + ), + ))))(input) +} + +/// ws also ignores line & block comments +pub(crate) fn ws<'a, F>(inner: F) -> impl FnMut(&'a str) -> IResult<&str, &str> +where + F: Parser<&'a str, &'a str, nom::error::Error<&'a str>>, +{ + nom::sequence::delimited(sp, inner, sp) +} |