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<(&str, Rune)>) -> Rune { // Initialize rune (the return value) with the default let mut rune = otherwise; // We need to peek the iterator let mut peekable = chars.clone().peekable(); // Help keep track of matched characters let mut j = 0; // For each tuple pair (string, rune)... for (s, r) in ahead.iter() { // For each character in in the string, starting at length j... for c in s[j..].chars().into_iter() { // If the next char matches... match peekable.peek() { Some(next) if next == &c => { // Increment counters chars.next(); peekable.next(); *i += 1; j += 1; // Only update the rune if j equals the length of the string if j == s.len() { rune = *r; } } Some(_) => {} None => {} } } } // Return whatever the rune was determined to be rune } /// 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'); }; }