summaryrefslogtreecommitdiffstats
path: root/src/recite/parse.rs
blob: 63b16ff89a88fe3668149dac3808fd56ca53689d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
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();
    };
}