use crate::poem::elements::rune::Rune; use std::str::Chars; /// Look ahead one character in the input /// /// May need to look ahead one character in the input string to determine the /// proper rune. For instance `&`, vs `&&`. /// /// # Arguments: /// `chars` - An iterator over the characters in the poem /// `i` - A counter to keep track of the current column /// `otherwise` - The rune to return if there are no matches /// `ahead` - A list of tuples, containing characters to look ahead for, /// alongside which rune they correspond to /// /// # Examples: /// ``` /// next(&mut chars, &mut i, Rune::Write, vec![('>', Rune::Addendum)]) /// next(&mut chars, &mut i, Rune::Quiet, vec![('&', Rune::And)]) /// ``` pub fn next(chars: &mut Chars, i: &mut usize, otherwise: Rune, ahead: Vec<(char, Rune)>) -> Rune { // Try to get the next character in the poem let next = match chars.peekable().peek() { Some(c) => *c, None => { return otherwise; } }; // Check if that next character matches any characters in ahead for (c, rune) in ahead.iter() { if next == *c { chars.into_iter().next(); *i += 1; return *rune; } } // If it doesn't match, return the default otherwise } /// Keep pushing to the [Word][super::super::elements::word::Word] stack /// /// If a [Rune::String][super::super::elements::rune::Rune] character is found, /// stop interpreting special characters, and push all characters to the /// [Word][super::super::elements::word::Word] stack, until the corresponding /// [Rune::String][super::super::elements::rune::Rune] character is found. #[macro_export] macro_rules! string { ($chars:expr, $j:expr, $i:expr, $c:expr, $word:expr) => { let token = $c; loop { match $chars.next() { None => return Err(Mishap::PartialMishap($j, $i, $c)), Some(c) if c == token => break, Some(c) if token == '\'' && c == '$' => { $word.push('\x0e'); $i += 1; } Some(c) => { $word.push(c); $i += 1; } } } }; } /// Same as [string!] macro, but look for newline or EOF #[macro_export] macro_rules! remark { ($chars:expr) => { loop { match $chars.next() { None => break, Some(c) if c == '\n' => break, Some(_) => {} } } continue; }; } /// Same as the [string!] macro, but don't `continue` #[macro_export] macro_rules! poem { ($chars:expr, $j:expr, $i:expr, $c:expr, $verse:expr, $word:expr, $env:expr) => { let token = $c; let mut poetry = Word::new(); loop { match $chars.next() { None => return Err(Mishap::PartialMishap($j, $i, $c)), Some(c) if c == token => break, Some(c) => { poetry.push(c); $i += 1; } } } let sp = Poem::read(poetry.iter().collect(), $env); let sp = match sp { Ok(sp) => sp, Err(e) => return Err(e), }; $verse.poems.push(sp); $word.push('\x0b'); }; }