shex_compact/
grammar.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
use crate::{shex_parser_error::ParseError as ShExParseError, IRes, Span};
use colored::*;
use nom::{
    branch::alt,
    bytes::complete::{is_not, tag, tag_no_case},
    character::complete::multispace1,
    combinator::value,
    multi::many0,
    sequence::{delimited, pair},
    Err,
};
use std::fmt::Debug;

// Create a [`Span`][nom_locate::LocatedSpan] over the input.
/* fn span_from_str(input: &str) -> Span<'_> {
    Span::new(input)
}*/

// The result of a parse
// type ParseResult<'a, T> = Result<T, LocatedParseError>;

/// A combinator that modifies the associated error.
pub(crate) fn map_error<'a, T: 'a>(
    mut parser: impl FnMut(Span<'a>) -> IRes<'a, T> + 'a,
    mut error: impl FnMut() -> ShExParseError + 'a,
) -> impl FnMut(Span<'a>) -> IRes<'a, T> + 'a {
    move |input| {
        parser(input).map_err(|e| match e {
            Err::Incomplete(_) => e,
            Err::Error(context) => {
                let mut err = error().at(input);
                err.append(context);
                Err::Error(err)
            }
            Err::Failure(context) => {
                let mut err = error().at(input);
                err.append(context);
                Err::Failure(err)
            }
        })
    }
}

/// A combinator to add tracing to the parser.
/// [fun] is an identifier for the parser and [parser] is the actual parser.
#[inline(always)]
pub(crate) fn traced<'a, T, P>(
    fun: &'static str,
    mut parser: P,
) -> impl FnMut(Span<'a>) -> IRes<'a, T>
where
    T: Debug,
    P: FnMut(Span<'a>) -> IRes<'a, T>,
{
    move |input| {
        tracing::trace!(target: "parser", "{fun}({input:?})");
        let result = parser(input);
        match &result {
            Ok(res) => {
                tracing::trace!(target: "parser", "{}", format!("{fun}({input:?}) -> {res:?}").green());
            }
            Err(e) => {
                tracing::trace!(target: "parser", "{}", format!("{fun}({input:?}) -> {e:?}").red());
            }
        }
        result
    }
}

/// A combinator that recognises a comment, starting at a `#`
/// character and ending at the end of the line.
fn comment(input: Span) -> IRes<()> {
    alt((
        value((), pair(tag("#"), is_not("\n\r"))),
        // a comment that immediately precedes the end of the line –
        // this must come after the normal line comment above
        value((), tag("#")),
        value((), multi_comment),
    ))(input)
}

fn multi_comment(i: Span) -> IRes<()> {
    value((), delimited(tag("/*"), is_not("*/"), tag("*/")))(i)
}

/// A combinator that recognises an arbitrary amount of whitespace and
/// comments.
pub(crate) fn tws0(input: Span) -> IRes<()> {
    value((), many0(alt((value((), multispace1), comment))))(input)
}

/*
/// A combinator that recognises any non-empty amount of whitespace
/// and comments.
pub(crate) fn tws1(input: Span) -> IRes<()> {
    value((), many1(alt((value((), multispace1), comment))))(input)
}
*/

/// A combinator that creates a parser for a specific token.
pub(crate) fn token<'a>(token: &'a str) -> impl FnMut(Span<'a>) -> IRes<Span<'a>> {
    map_error(tag(token), || {
        ShExParseError::ExpectedToken(token.to_string())
    })
}

/// A combinator that creates a parser for a specific token,
/// surrounded by trailing whitespace or comments.
pub(crate) fn token_tws<'a>(token: &'a str) -> impl FnMut(Span<'a>) -> IRes<Span<'a>> {
    map_error(delimited(tws0, tag(token), tws0), || {
        ShExParseError::ExpectedToken(token.to_string())
    })
}

/// A combinator that creates a parser for a case insensitive tag,
/// surrounded by trailing whitespace or comments.
pub(crate) fn tag_no_case_tws<'a>(token: &'a str) -> impl FnMut(Span<'a>) -> IRes<Span<'a>> {
    map_error(delimited(tws0, tag_no_case(token), tws0), || {
        ShExParseError::ExpectedToken(token.to_string())
    })
}