diff options
Diffstat (limited to 'src/func/helpers.rs')
| -rw-r--r-- | src/func/helpers.rs | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/src/func/helpers.rs b/src/func/helpers.rs new file mode 100644 index 00000000..d562284f --- /dev/null +++ b/src/func/helpers.rs @@ -0,0 +1,129 @@ +use super::prelude::*; +use std::iter::Peekable; +use std::slice::Iter; + +/// Implement the function trait more concisely. +#[macro_export] +macro_rules! function { + (data: $ident:ident, $($tts:tt)*) => { + impl Function for $ident { + function!(@parse $ident, $($tts)*); + } + }; + + (@parse $ident:ident, parse: plain, $($tts:tt)*) => { + fn parse(header: &FuncHeader, body: Option<&str>, _: ParseContext) + -> ParseResult<Self> where Self: Sized + { + Arguments::new(header).done()?; + if body.is_some() { + err!("expected no body"); + } + Ok($ident) + } + function!(@layout $($tts)*); + }; + + ( + @parse $ident:ident, + parse($args:ident, $body:ident, $ctx:ident) + $block:block + $($tts:tt)* + ) => { + fn parse(header: &FuncHeader, body: Option<&str>, ctx: ParseContext) + -> ParseResult<Self> where Self: Sized + { + #[allow(unused_mut)] let mut $args = Arguments::new(header); + let $body = body; + let $ctx = ctx; + $block + } + function!(@layout $($tts)*); + }; + + (@layout layout($this:pat, $ctx:pat) $block:block) => { + fn layout(&self, ctx: LayoutContext) -> LayoutResult<CommandList> { + let $ctx = ctx; + let $this = self; + $block + } + }; +} + +/// Parse the body of a function. +/// - If the function does not expect a body, use `forbidden`. +/// - If the function can have a body, use `optional`. +/// - If the function must have a body, use `required`. +#[macro_export] +macro_rules! parse { + (forbidden: $body:expr) => { + if $body.is_some() { + err!("unexpected body"); + } + }; + + (optional: $body:expr, $ctx:expr) => { + if let Some(body) = $body { + Some($crate::parsing::parse(body, $ctx)?) + } else { + None + } + }; + + (required: $body:expr, $ctx:expr) => { + if let Some(body) = $body { + $crate::parsing::parse(body, $ctx)? + } else { + err!("expected body"); + } + } +} + +/// Return a formatted parsing error. +#[macro_export] +macro_rules! err { + ($($tts:tt)*) => { + return Err(ParseError::new(format!($($tts)*))); + }; +} + +/// Convenient interface for parsing function arguments. +pub struct Arguments<'a> { + args: Peekable<Iter<'a, Expression>>, +} + +impl<'a> Arguments<'a> { + pub fn new(header: &'a FuncHeader) -> Arguments<'a> { + Arguments { + args: header.args.iter().peekable() + } + } + + pub fn get_expr(&mut self) -> ParseResult<&'a Expression> { + self.args.next() + .ok_or_else(|| ParseError::new("expected expression")) + } + + pub fn get_ident(&mut self) -> ParseResult<&'a str> { + match self.get_expr()? { + Expression::Ident(s) => Ok(s.as_str()), + _ => Err(ParseError::new("expected identifier")), + } + } + + pub fn get_ident_if_present(&mut self) -> ParseResult<Option<&'a str>> { + if self.args.peek().is_some() { + self.get_ident().map(|s| Some(s)) + } else { + Ok(None) + } + } + + pub fn done(&mut self) -> ParseResult<()> { + if self.args.peek().is_none() { + Ok(()) + } else { + Err(ParseError::new("unexpected argument")) + } + } +} |
