diff options
| author | Laurenz <laurmaedje@gmail.com> | 2019-12-04 19:34:29 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2019-12-04 19:35:28 +0100 |
| commit | 9fb31defd037a90bf8f9e38fa33acae23a70b269 (patch) | |
| tree | e0fd887792a59cbb3262a5d3157d0c786df56d60 /src/func/macros.rs | |
| parent | ace57c34206a13b4bc3885b944cc51e274f30b0f (diff) | |
Expand functionality of function! macro 🛰
Diffstat (limited to 'src/func/macros.rs')
| -rw-r--r-- | src/func/macros.rs | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/src/func/macros.rs b/src/func/macros.rs new file mode 100644 index 00000000..78cf1f56 --- /dev/null +++ b/src/func/macros.rs @@ -0,0 +1,149 @@ +//! Helper types and macros for creating custom functions. + +/// Defines function types concisely. +#[macro_export] +macro_rules! function { + // Parse a unit struct. + ($(#[$outer:meta])* pub struct $type:ident; $($rest:tt)*) => { + $(#[$outer])* + pub struct $type; + function!(@meta $type | $($rest)*); + }; + + // Parse a struct with fields. + ($(#[$outer:meta])* pub struct $type:ident { $($fields:tt)* } $($rest:tt)*) => { + $(#[$outer])* + pub struct $type { $($fields)* } + function!(@meta $type | $($rest)*); + }; + + // Parse a metadata type definition. + (@meta $type:ident | type Meta = $meta:ty; $($rest:tt)*) => { + function!(@parse $type $meta | $($rest)*); + }; + + // Set the metadata to `()` if there is not type definition. + (@meta $type:ident | $($rest:tt)*) => { + function!(@parse $type () | $($rest)*); + }; + + // Parse a `parse(default)`. + (@parse $type:ident $meta:ty | parse(default) $($rest:tt)*) => { + function!(@parse $type $meta | + parse(_args, _body, _ctx, _meta) { Default::default() } + $($rest)* + ); + }; + + // (0-arg) Parse a parse-definition without arguments. + (@parse $type:ident $meta:ty | parse() $code:block $($rest:tt)*) => { + function!(@parse $type $meta | parse(_args, _body, _ctx, _meta) $code $($rest)*); + }; + + // (1-arg) Parse a parse-definition with only the first argument. + (@parse $type:ident $meta:ty | parse($args:ident) $code:block $($rest:tt)*) => { + function!(@parse $type $meta | parse($args, _body, _ctx, _meta) $code $($rest)*); + }; + + // (2-arg) Parse a parse-definition with only the first two arguments. + (@parse $type:ident $meta:ty | + parse($args:ident, $body:pat) $code:block $($rest:tt)* + ) => { + function!(@parse $type $meta | parse($args, $body, _ctx, _meta) $code $($rest)*); + }; + + // (3-arg) Parse a parse-definition with only the first three arguments. + (@parse $type:ident $meta:ty | + parse($args:ident, $body:pat, $ctx:pat) $code:block $($rest:tt)* + ) => { + function!(@parse $type $meta | parse($args, $body, $ctx, _meta) $code $($rest)*); + }; + + // (4-arg) Parse a parse-definition with all four arguments. + (@parse $type:ident $meta:ty | + parse($args:ident, $body:pat, $ctx:pat, $metadata:pat) $code:block + $($rest:tt)* + ) => { + impl $crate::func::ParseFunc for $type { + type Meta = $meta; + + fn parse( + header: &FuncHeader, + $body: Option<&str>, + $ctx: ParseContext, + $metadata: Self::Meta, + ) -> ParseResult<Self> where Self: Sized { + let mut $args = $crate::func::args::ArgParser::new(&header.args); + let val = $code; + $args.done()?; + Ok(val) + } + } + + function!(@layout $type | $($rest)*); + }; + + // (0-arg) Parse a layout-definition without arguments. + (@layout $type:ident | layout() $code:block) => { + function!(@layout $type | layout(self, _ctx) $code); + }; + + // (1-arg) Parse a layout-definition with only the first argument. + (@layout $type:ident | layout($this:ident) $code:block) => { + function!(@layout $type | layout($this, _ctx) $code); + }; + + // (2-arg) Parse a layout-definition with all arguments. + (@layout $type:ident | layout($this:ident, $ctx:pat) $code:block) => { + impl $crate::func::LayoutFunc for $type { + fn layout(&$this, $ctx: LayoutContext) -> LayoutResult<Commands> { + Ok($code) + } + } + }; +} + +/// Parse the body of a function. +/// - If the function does not expect a body, use `parse!(forbidden: body)`. +/// - If the function can have a body, use `parse!(optional: body, ctx)`. +/// - If the function must have a body, use `parse!(expected: body, ctx)`. +#[macro_export] +macro_rules! parse { + (forbidden: $body:expr) => { + if $body.is_some() { + pr!("unexpected body"); + } + }; + + (optional: $body:expr, $ctx:expr) => ( + if let Some(body) = $body { + Some($crate::syntax::parse(body, $ctx)?) + } else { + None + } + ); + + (expected: $body:expr, $ctx:expr) => ( + if let Some(body) = $body { + $crate::syntax::parse(body, $ctx)? + } else { + pr!("expected body"); + } + ) +} + +/// Early-return with a formatted parsing error or yield +/// an error expression without returning when prefixed with `@`. +#[macro_export] +macro_rules! pr { + (@$($tts:tt)*) => ($crate::syntax::ParseError::new(format!($($tts)*))); + ($($tts:tt)*) => (return Err(pr!(@$($tts)*));); +} + +/// Early-return with a formatted layouting error or yield +/// an error expression without returning when prefixed with `@`. +#[macro_export] +macro_rules! lr { + (@$($tts:tt)*) => ($crate::layout::LayoutError::new(format!($($tts)*))); + ($($tts:tt)*) => (return Err(lr!(@$($tts)*));); +} |
