75 lines
2 KiB
Rust
75 lines
2 KiB
Rust
use num::Num;
|
|
use std::fmt::Debug;
|
|
use yap::{IntoTokens, Tokens};
|
|
pub fn space(tokens: &mut impl Tokens<Item = char>) -> bool {
|
|
tokens.token(' ')
|
|
}
|
|
|
|
pub fn newline(tokens: &mut impl Tokens<Item = char>) -> bool {
|
|
tokens.tokens("\r\n".into_tokens()) || tokens.token('\n')
|
|
}
|
|
|
|
pub fn parse_number<T: Num>(tokens: &mut impl Tokens<Item = char>) -> Option<T> {
|
|
parse_number_with_radix(tokens, 10, None)
|
|
}
|
|
|
|
pub fn parse_digit<T: Num>(tokens: &mut impl Tokens<Item = char>) -> Option<T> {
|
|
parse_number_with_radix(tokens, 10, Some(1))
|
|
}
|
|
|
|
pub fn parse_number_with_radix<T: Num>(
|
|
tokens: &mut impl Tokens<Item = char>,
|
|
base: u32,
|
|
limit: Option<usize>,
|
|
) -> Option<T> {
|
|
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<R, T, I, P, S, const N: usize>(
|
|
tokens: &mut T,
|
|
parser: P,
|
|
separator: S,
|
|
) -> Option<[R; N]>
|
|
where
|
|
R: Debug,
|
|
T: Tokens<Item = I>,
|
|
P: FnMut(&mut T) -> Option<R>,
|
|
S: FnMut(&mut T) -> bool,
|
|
{
|
|
tokens
|
|
.sep_by(parser, separator)
|
|
.collect::<Vec<R>>()
|
|
.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));
|
|
}
|
|
}
|