cypress/parser/
map_with_span.rs

1use std::sync::Arc;
2
3use crate::prelude::{Error, PInput, PSuccess, Parser, ParserCore, Span};
4
5/// Combinator to map result of parser with the span successfully parsed with
6/// the parser.
7///
8/// Useful for wrapping parsed tokens with their span for better error messages
9/// after parsing in evaluation or validation.
10///
11/// ## Example
12///
13/// ```rust
14/// use cypress::prelude::*;
15///
16/// #[derive(Clone, PartialEq, Debug)]
17/// struct SpannedToken(String, Span);
18///
19/// let input = "\"this is a string\"";
20///
21/// let parser = just('\"')
22///     .ignore_then(pletter().or(pws())
23///     .many()
24///     .map(|cs| String::from_utf8(cs).unwrap())
25///     .map_with_span(|str_, span| SpannedToken(str_, span)))
26///     .then_ignore(just('\"'));
27///
28/// match parser.parse(input.into_input()) {
29///     Ok(PSuccess { val, rest: _ }) => assert_eq!(val, SpannedToken("this is a string".to_string(), (1, 17))),
30///     Err(_) => assert!(false),
31/// }
32/// ```
33#[derive(Clone)]
34pub struct PMapWithSpan<P, O1, O2> {
35    parser: P,
36    f: Arc<dyn Fn(O1, Span) -> O2>,
37}
38
39/// Function to easily build new [`PMapWithSpan`] though see [`crate::parser::core::Parser::map_with_span`]
40/// for easiest usage as a combinator.
41///
42/// # Parameters
43/// - `parser`: The parser to apply
44/// - `f`: The transformation function that converts the parser’s result with the span it parsed
45///
46/// # Returns
47/// A new parser that parses with `p` and transforms its result using `f`.
48pub fn pmap_with_span<P, F, O1, O2>(parser: P, f: F) -> PMapWithSpan<P, O1, O2>
49where
50    F: Fn(O1, Span) -> O2 + 'static,
51{
52    PMapWithSpan {
53        parser,
54        f: Arc::new(f),
55    }
56}
57
58impl<'a, K, P, O1, O2> ParserCore<'a, K, O2> for PMapWithSpan<P, O1, O2>
59where
60    K: PartialEq + Clone + 'a,
61    O1: Clone + 'a,
62    P: Parser<'a, K, O1>,
63{
64    /// Applies the inner parser, then transforms its result along with its span using the function `f`.
65    ///
66    /// If parsing succeeds, the transformation function is applied to the result and its span,
67    /// and the transformed value is returned.
68    ///
69    /// # Errors
70    /// Returns a parse failure if the inner parser `p` fails.
71    fn parse(&self, i: PInput<'a, K>) -> Result<PSuccess<'a, K, O2>, Error<'a, K>> {
72        let start = i.loc;
73
74        let PSuccess { val, rest } = self.parser.parse(i)?;
75
76        let span = (start, rest.loc);
77
78        Ok(PSuccess {
79            val: (self.f)(val, span),
80            rest,
81        })
82    }
83}
84
85impl<'a, K, P, O1, O2> Parser<'a, K, O2> for PMapWithSpan<P, O1, O2>
86where
87    K: PartialEq + Clone + 'a,
88    O1: Clone + 'a,
89    O2: Clone + 'a,
90    P: Parser<'a, K, O1>,
91{
92}