diff options
Diffstat (limited to '')
-rw-r--r-- | third_party/rust/weedle2/src/macros.rs | 564 |
1 files changed, 564 insertions, 0 deletions
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"); } + } + } + } + )* + } + }; +} |