/// Add a Verse to a Poem /// /// Takes the current word stack, and pushes it onto the stanza. Then, takes /// the stanza, meter, and some metadata details to create a Verse. That Verse /// then gets added to the Poem. /// /// # Arguments /// * `$word` - The word stack, used to hold chars dilineated via whitespace /// * `$stanza` - The stanza stack, used to hold words when they are popped off /// the word stack /// * `$cprev` - Indicates this is the second half of a couplet (normally this /// is just Verse::couplet(prev), but we may need to set it in /// case of Meter::Read), since this basically just tells the /// shell to use `$out` in the `task!` macro /// * `$prev` - The previous verse (or none if there is no previous verse) /// * `$verses` - The list of verses that make up the poem /// * `$meter` - The meter corresponding to the verse #[macro_export] macro_rules! push { ($word:expr, $stanza:expr, $cprev:expr, $prev:expr, $verses:expr, $meter:expr) => { // If there are chars on the word stack, push that word onto the stanza if !$word.is_empty() { $stanza.push($word.iter().collect()); } // Check if the last verse was a meter of Read, Write, or // Addendum, and throw an error if it is if Verse::rw($prev) && $stanza.is_empty() { let rw = match $prev.unwrap().meter { Meter::Read => "read", Meter::Write => "write", Meter::Addendum => "append", _ => "", }; eprintln!("dwvsh: parse error: no {} file(s) specified", rw); return None; } // A meter indicates the end of a verse if !$stanza.is_empty() { $verses.push(Verse::new( Stanza::new($stanza.clone()), $meter, $cprev, Verse::rw($prev), )); } // Clear the stacks $stanza.clear(); $word.clear(); }; } /// Add a Verse to a Poem, but allow looking ahead by one char /// /// This works the exact same as [[push]], except that it doesn't take /// `$cprev` (since there is no need to set it manually right now), and it /// takes `$ahead`, which is the next character to look for in the pattern, /// along with `$aheadm`, which is the corresponding Meter if that `$ahead` is /// found. /// /// # Examples /// ``` /// push1!(word, stanza, prev, verses, Meter::Quiet, '&', Meter::And); /// push1!(word, stanza, prev, verses, Meter::Write, '>', Meter::Addendum); /// ``` #[macro_export] macro_rules! push1 { ($word:expr, $stanza:expr, $chars:expr, $prev:expr, $verses: expr, $meter:expr, $ahead:expr, $aheadm:expr) => { // If there are chars on the word stack, push that word // onto the stanza if !$word.is_empty() { $stanza.push($word.iter().collect()); } // Check if the last verse was a meter of Read, Write, or // Addendum, and throw an error if it is if Verse::rw($prev) && $stanza.is_empty() { let rw = match $prev.unwrap().meter { Meter::Read => "read", Meter::Write => "write", Meter::Addendum => "append", _ => "", }; eprintln!("dwvsh: parse error: no {} file(s) specified", rw); return None; } // Need to peek at the next character, since '>' can mean // Meter::Write, or '>>' can mean Meter::Addendum match $chars.clone().peekable().peek() { // Indicated Meter::Addendum Some(c) if c == &$ahead => { // Pop the next character from the input string // (since we know what it is) $chars.next(); // A meter indicates the end of a verse $verses.push(Verse::new( Stanza::new($stanza.clone()), $aheadm, Verse::couplet($prev), Verse::rw($prev), )); } // Indicates Meter::Write Some(_) => { // A meter indicates the end of a verse $verses.push(Verse::new( Stanza::new($stanza.clone()), $meter, Verse::couplet($prev), Verse::rw($prev), )); } // Indicated the end of the input None => { // A meter indicates the end of a verse $verses.push(Verse::new( Stanza::new($stanza.clone()), $meter, Verse::couplet($prev), Verse::rw($prev), )); } } // Clear the stacks $stanza.clear(); $word.clear(); }; }