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}