- Updated all dependencies - Updated Rust edition to 2024 - Nix shell from flake - Added justfile - Mirgrate all changes to various packages
215 lines
6.3 KiB
Rust
215 lines
6.3 KiB
Rust
use nom::IResult;
|
|
use nom::bytes::complete::take_while_m_n;
|
|
use nom::character::complete::char;
|
|
use nom::character::complete::multispace0;
|
|
use nom::combinator::eof;
|
|
use nom::error::ParseError;
|
|
use nom::sequence::delimited;
|
|
use nom::sequence::terminated;
|
|
use nom::{Input, Parser};
|
|
|
|
/// Trim leading and trailing whitespace from the input Parser
|
|
/// - Parameters
|
|
/// - `inner`: The parser to trim
|
|
/// - Returns: A parser that trims leading and trailing whitespace from the input and then runs the value from the inner parser
|
|
pub fn trim<I, O, F, E: ParseError<I>>(inner: F) -> impl Parser<I, Output = O, Error = E>
|
|
where
|
|
I: Input,
|
|
F: Parser<I, Output = O, Error = E>,
|
|
<I as Input>::Item: nom::AsChar,
|
|
{
|
|
delimited(multispace0, inner, multispace0)
|
|
}
|
|
|
|
/// Parse a parenthesized expression. This parser will parse an expression that is surrounded by parentheses
|
|
/// and will trim the whitespace surrounding the expression.
|
|
/// - Parameters
|
|
/// - `inner`: The parser to run inside the parentheses
|
|
/// - Returns: A parser that parses a parenthesized expression
|
|
pub fn parenthesized<I, O, F, E: ParseError<I>>(inner: F) -> impl Parser<I, Output = O, Error = E>
|
|
where
|
|
I: Input,
|
|
F: Parser<I, Output = O, Error = E>,
|
|
<I as Input>::Item: nom::AsChar,
|
|
{
|
|
delimited(char('('), inner, char(')'))
|
|
}
|
|
|
|
/// Take where the predicate is true and the length is exactly `n`
|
|
/// - Parameters
|
|
/// - `n`: The length of the string to take
|
|
/// - `predicate`: The predicate to call to validate the input
|
|
/// - Returns: A parser that takes `n` characters from the input
|
|
pub fn take_where<F, I>(n: usize, predicate: F) -> impl FnMut(I) -> IResult<I, I>
|
|
where
|
|
I: Input,
|
|
F: Fn(<I as Input>::Item) -> bool,
|
|
{
|
|
take_while_m_n(n, n, predicate)
|
|
}
|
|
|
|
/// Parse the inner parser and then the end of the input.
|
|
/// Very useful for ensuring that the entire input is consumed.
|
|
/// - Parameters
|
|
/// - `inner`: The parser to run
|
|
/// - Returns: A parser that runs the inner parser and then the end of the input
|
|
/// # Example
|
|
/// ```
|
|
/// use lib::nom::combinators::exhausted;
|
|
/// use nom::bytes::complete::{tag};
|
|
/// use nom::Parser;
|
|
///
|
|
/// let input = "test";
|
|
/// let (remaining, result) = exhausted(tag::<&str, &str, nom::error::Error<&str>>("test")).parse(input).unwrap();
|
|
/// assert_eq!(remaining, "");
|
|
/// assert_eq!(result, "test");
|
|
/// ```
|
|
/// - Fails if the input is not exhausted
|
|
/// ```
|
|
/// use lib::nom::combinators::exhausted;
|
|
/// use nom::bytes::complete::{tag};
|
|
/// use nom::Parser;
|
|
///
|
|
/// let input = "test";
|
|
/// assert!(exhausted(tag::<&str, &str, nom::error::Error<&str>>("tes")).parse(input).is_err());
|
|
/// ```
|
|
pub fn exhausted<F, I, O, E: ParseError<I>>(inner: F) -> impl Parser<I, Output = O, Error = E>
|
|
where
|
|
I: Input,
|
|
F: Parser<I, Output = O, Error = E>,
|
|
{
|
|
terminated(inner, eof)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use nom::bytes::complete::take_while;
|
|
|
|
#[test]
|
|
fn test_trim_both_sides() {
|
|
let input = " test ";
|
|
let (remaining, result) = trim(take_where(4, |c: char| c.is_ascii_alphabetic()))
|
|
.parse(input)
|
|
.unwrap();
|
|
assert_eq!(remaining, "");
|
|
assert_eq!(result, "test");
|
|
}
|
|
|
|
#[test]
|
|
fn test_trim_leading() {
|
|
let input = " test";
|
|
let (remaining, result) = trim(take_where(4, |c: char| c.is_ascii_alphabetic()))
|
|
.parse(input)
|
|
.unwrap();
|
|
assert_eq!(remaining, "");
|
|
assert_eq!(result, "test");
|
|
}
|
|
|
|
#[test]
|
|
fn test_trim_trailing() {
|
|
let input = "test ";
|
|
let (remaining, result) = trim(take_where(4, |c: char| c.is_ascii_alphabetic()))
|
|
.parse(input)
|
|
.unwrap();
|
|
assert_eq!(remaining, "");
|
|
assert_eq!(result, "test");
|
|
}
|
|
|
|
#[test]
|
|
fn test_trim_no_trim() {
|
|
let input = "test";
|
|
let (remaining, result) = trim(take_where(4, |c: char| c.is_ascii_alphabetic()))
|
|
.parse(input)
|
|
.unwrap();
|
|
assert_eq!(remaining, "");
|
|
assert_eq!(result, "test");
|
|
}
|
|
|
|
#[test]
|
|
fn test_parenthesized() {
|
|
let input = "(test)";
|
|
let (remaining, result) = parenthesized(take_where(4, |c: char| c.is_ascii_alphabetic()))
|
|
.parse(input)
|
|
.unwrap();
|
|
assert_eq!(remaining, "");
|
|
assert_eq!(result, "test");
|
|
}
|
|
|
|
#[test]
|
|
fn test_parenthesized_parse_until_end() {
|
|
let input = "(test)";
|
|
assert!(
|
|
parenthesized::<&str, &str, _, nom::error::Error<&str>>(take_while(|_| true))
|
|
.parse(input)
|
|
.is_err()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_take_where() {
|
|
let input = "test";
|
|
let (remaining, result) = take_where(4, |c: char| c.is_ascii_alphabetic())(input).unwrap();
|
|
assert_eq!(remaining, "");
|
|
assert_eq!(result, "test");
|
|
}
|
|
|
|
#[test]
|
|
fn test_take_where_not_enough() {
|
|
let input = "tes";
|
|
assert!(take_where(4, |c: char| c.is_ascii_alphabetic())(input).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn test_take_where_too_much() {
|
|
let input = "testing";
|
|
assert_eq!(
|
|
take_where(4, |c: char| c.is_ascii_alphabetic()).parse(input),
|
|
Ok(("ing", "test"))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_take_where_predicate_false() {
|
|
let input = "test";
|
|
assert!(
|
|
take_where(4, |c: char| c.is_ascii_digit())
|
|
.parse(input)
|
|
.is_err()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_exhausted() {
|
|
let input = "test";
|
|
let (remaining, result) = exhausted(take_where(4, |c: char| c.is_ascii_alphabetic()))
|
|
.parse(input)
|
|
.unwrap();
|
|
assert_eq!(remaining, "");
|
|
assert_eq!(result, "test");
|
|
}
|
|
|
|
#[test]
|
|
fn test_exhausted_not_exhausted() {
|
|
let input = "test ";
|
|
assert!(
|
|
exhausted(take_where(4, |c: char| c.is_ascii_alphabetic()))
|
|
.parse(input)
|
|
.is_err()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_exhausted_tuple() {
|
|
let input = "test";
|
|
let (remaining, result) = exhausted((
|
|
take_where(3, |c: char| c.is_ascii_alphabetic()),
|
|
take_while(|c: char| c.is_ascii_alphabetic()),
|
|
))
|
|
.parse(input)
|
|
.unwrap();
|
|
assert_eq!(remaining, "");
|
|
assert_eq!(result, ("tes", "t"));
|
|
}
|
|
}
|