diff options
| author | Rory Dudley | 2024-03-05 00:42:28 -0700 | 
|---|---|---|
| committer | Rory Dudley | 2024-03-05 00:42:28 -0700 | 
| commit | 37e1ae98dc9309715e9415962f21484a807d2c56 (patch) | |
| tree | 69444755d1a0095f5a7cfad66ab966f83f46d4aa /src/recite/parse.rs | |
| parent | e03ceec834aeca1f383b19354e32987bc2d59506 (diff) | |
| download | dwarvish-37e1ae98dc9309715e9415962f21484a807d2c56.tar.gz | |
Poem::read macros
Added the following macros:
push!: Creates a Verse from a stanza, taking into account some extra
options (such as the Meter).
push1!: Creates a Verse from a stanza, but also allows looking ahead
by a single character, in order to pattern match certain meters (i.e.
And ('&&') and Addendum ('>>')).
Replaced the code in the huge, redundant match statements in Poem::read
with the macros described above.
Diffstat (limited to 'src/recite/parse.rs')
| -rw-r--r-- | src/recite/parse.rs | 135 | 
1 files changed, 135 insertions, 0 deletions
diff --git a/src/recite/parse.rs b/src/recite/parse.rs new file mode 100644 index 0000000..63b16ff --- /dev/null +++ b/src/recite/parse.rs @@ -0,0 +1,135 @@ +/// 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(); +    }; +}  | 
