use num::Num; use std::fmt::Debug; use yap::{IntoTokens, Tokens}; pub fn space(tokens: &mut impl Tokens) -> bool { tokens.token(' ') } pub fn newline(tokens: &mut impl Tokens) -> bool { tokens.tokens("\r\n".into_tokens()) || tokens.token('\n') } pub fn parse_number(tokens: &mut impl Tokens) -> Option { parse_number_with_radix(tokens, 10, None) } pub fn parse_digit(tokens: &mut impl Tokens) -> Option { parse_number_with_radix(tokens, 10, Some(1)) } pub fn parse_number_with_radix( tokens: &mut impl Tokens, base: u32, limit: Option, ) -> Option { tokens.optional(|t| { t.skip_tokens_while(|t| *t == ' '); let mut remaining = limit.map(|x| x as isize).unwrap_or(isize::MAX); let mut prefix = String::new(); if t.token('-') { prefix.push('-'); } let digits: String = t .tokens_while(|c| { remaining -= 1; c.is_digit(base) && remaining >= 0 }) .collect(); (!digits.is_empty()) .then(|| T::from_str_radix(&format!("{}{}", prefix, digits), base).ok()) .flatten() }) } pub fn parse_n( tokens: &mut T, parser: P, separator: S, ) -> Option<[R; N]> where R: Debug, T: Tokens, P: FnMut(&mut T) -> Option, S: FnMut(&mut T) -> bool, { tokens .sep_by(parser, separator) .collect::>() .try_into() .ok() } #[cfg(test)] mod test { use yap::IntoTokens; #[test] fn parse_digit() { let mut tokens = "1234".into_tokens(); assert_eq!(Some(1), super::parse_digit(&mut tokens)); assert_eq!(Some(2), super::parse_digit(&mut tokens)); assert_eq!(Some(3), super::parse_digit(&mut tokens)); assert_eq!(Some(4), super::parse_digit(&mut tokens)); } }