chonk/parsers/
delimiting.rs

1//! Parsers for matching delimited structures.
2use crate::framework::*;
3
4#[inline]
5/// Match a parser, delimited on the left and right with by other parsers, return the results.
6///
7/// ```
8/// # use chonk::prelude::*;
9/// fn parser<'a>() -> impl Parser<'a, &'a str, ()> {
10///     move |ctx| {
11///         delimited(is('['), take(1..3, is(any)), is(']')).parse(ctx)
12///     }
13/// }
14///
15/// assert_eq!(
16///     parser().parse("[123]"),
17///     Ok((
18///         ParserContext {
19///             input: "[123]",
20///             bounds: 4..5,
21///         },
22///         "123"
23///     ))
24/// );
25/// ```
26pub fn delimited<'a, Lhs, LhsRes, Par, ParRes, Rhs, RhsRes, Msg>(
27    mut prefix: Lhs,
28    mut parser: Par,
29    mut suffix: Rhs,
30) -> impl Parser<'a, ParRes, Msg>
31where
32    Lhs: Parser<'a, LhsRes, Msg>,
33    Par: Parser<'a, ParRes, Msg>,
34    Rhs: Parser<'a, RhsRes, Msg>,
35{
36    move |ctx| {
37        prefix
38            .parse(ctx)
39            .and_then(|(ctx, _)| parser.parse(ctx))
40            .and_then(|(ctx, res)| match suffix.parse(ctx) {
41                Ok((ctx, _)) => Ok(ctx.with_data(res)),
42                Err(error) => Err(error),
43            })
44    }
45}
46
47#[inline]
48/// Match a parser, delimited on the left by another parser, return the results.
49///
50/// ```
51/// # use chonk::prelude::*;
52/// fn parser<'a>() -> impl Parser<'a, &'a str, ()> {
53///     move |ctx| {
54///         left_delimited(is('$'), take(.., is(digit))).parse(ctx)
55///     }
56/// }
57///
58/// let ctx = ParserContext::from("$123");
59///
60/// assert_eq!(
61///     parser().parse(ctx.clone()),
62///     Ok((ctx.select(1..4), "123"))
63/// );
64/// ```
65pub fn left_delimited<'a, Lhs, LhsRes, Par, ParRes, Msg>(
66    mut left: Lhs,
67    mut parser: Par,
68) -> impl Parser<'a, ParRes, Msg>
69where
70    Lhs: Parser<'a, LhsRes, Msg>,
71    Par: Parser<'a, ParRes, Msg>,
72{
73    move |ctx| left.parse(ctx).and_then(|ctx| parser.parse(ctx))
74}
75
76#[inline]
77/// Match a parser, delimited on the right by another parser, return the results.
78///
79/// ```
80/// # use chonk::prelude::*;
81/// fn parser<'a>() -> impl Parser<'a, &'a str, ()> {
82///     move |ctx| {
83///         right_delimited(take(.., is(digit)), is('%')).parse(ctx)
84///     }
85/// }
86///
87/// let ctx = ParserContext::from("123%");
88///
89/// assert_eq!(
90///     parser().parse(ctx.clone()),
91///     Ok((ctx.select(3..4), "123"))
92/// );
93/// ```
94pub fn right_delimited<'a, Par, ParRes, Rhs, RhsRes, Msg>(
95    mut parser: Par,
96    mut right: Rhs,
97) -> impl Parser<'a, ParRes, Msg>
98where
99    Par: Parser<'a, ParRes, Msg>,
100    Rhs: Parser<'a, RhsRes, Msg>,
101{
102    move |ctx| {
103        parser
104            .parse(ctx)
105            .and_then(|(ctx, res)| match right.parse(ctx) {
106                Ok((ctx, _)) => Ok(ctx.with_data(res)),
107                Err(error) => Err(error),
108            })
109    }
110}
111
112#[inline]
113/// Match a parser surrounded by whitespace, return the results of the parser.
114///
115/// ```
116/// # use chonk::prelude::*;
117/// fn parser<'a>() -> impl Parser<'a, &'a str, ()> {
118///     move |ctx| {
119///         trim(is("foobar")).parse(ctx)
120///     }
121/// }
122///
123/// assert!(parser().test(" foobar "));
124/// ```
125pub fn trim<'a, Par, Res, Msg>(mut parser: Par) -> impl Parser<'a, Res, Msg>
126where
127    Par: Parser<'a, Res, Msg>,
128{
129    move |ctx| {
130        trim_left_delimiter()
131            .parse(ctx)
132            .and_then(|ctx| parser.parse(ctx))
133            .and_then(|(ctx, res)| match trim_right_delimiter().parse(ctx) {
134                Ok((ctx, _)) => Ok((ctx, res)),
135                Err((ctx, _)) => Ok((ctx, res)),
136            })
137    }
138}
139
140fn trim_left_delimiter<'a, Msg>() -> impl Parser<'a, &'a str, Msg> {
141    use crate::parsers::matching::{is, take};
142
143    move |ctx| take(0.., is(whitespace)).parse(ctx)
144}
145
146fn trim_right_delimiter<'a>() -> impl Parser<'a, &'a str, ()> {
147    use crate::parsers::matching::space;
148
149    move |ctx| space(0..).parse(ctx)
150}