keyvalues_parser/text/parse/
escaped.rs

1use super::*;
2
3pub type PestError = pest::error::Error<Rule>;
4type BoxedState<'a> = Box<pest::ParserState<'a, Rule>>;
5type ParseResult<'a> = pest::ParseResult<BoxedState<'a>>;
6
7common_parsing!(pest_parse, Rule, true);
8
9#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
10#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
11pub enum Rule {
12    ///End-of-input
13    EOI,
14    WHITESPACE,
15    COMMENT,
16    vdf,
17    base_macro,
18    quoted_raw_string,
19    quoted_raw_inner,
20    pairs,
21    pair,
22    key,
23    value,
24    obj,
25    quoted_string,
26    quoted_inner,
27    char,
28    unquoted_string,
29    unquoted_char,
30}
31
32mod rules {
33    #![allow(non_snake_case)]
34
35    use super::{any, skip, soi, whitespace, BoxedState, ParseResult, Rule};
36
37    use pest::Atomicity;
38
39    #[inline]
40    pub fn vdf(s: BoxedState<'_>) -> ParseResult<'_> {
41        s.sequence(|s| {
42            soi(s)
43                .and_then(skip)
44                .and_then(|s| {
45                    s.sequence(|s| {
46                        s.optional(|s| {
47                            base_macro(s).and_then(|s| {
48                                s.repeat(|s| s.sequence(|s| skip(s).and_then(base_macro)))
49                            })
50                        })
51                    })
52                })
53                .and_then(skip)
54                .and_then(pair)
55                .and_then(skip)
56                .and_then(|s| s.optional(|s| s.match_string("\0")))
57                .and_then(skip)
58                .and_then(EOI)
59        })
60    }
61    pub fn base_macro(s: BoxedState<'_>) -> ParseResult<'_> {
62        s.rule(Rule::base_macro, |s| {
63            s.sequence(|s| {
64                s.match_string("#base")
65                    .and_then(skip)
66                    .and_then(|s| quoted_raw_string(s).or_else(unquoted_string))
67            })
68        })
69    }
70    #[inline]
71    pub fn quoted_raw_string(s: BoxedState<'_>) -> ParseResult<'_> {
72        s.atomic(Atomicity::CompoundAtomic, |s| {
73            s.rule(Rule::quoted_raw_string, |s| {
74                s.sequence(|s| {
75                    s.match_string("\"")
76                        .and_then(quoted_raw_inner)
77                        .and_then(|s| s.match_string("\""))
78                })
79            })
80        })
81    }
82    #[inline]
83    pub fn quoted_raw_inner(s: BoxedState<'_>) -> ParseResult<'_> {
84        s.rule(Rule::quoted_raw_inner, |s| {
85            s.atomic(Atomicity::Atomic, |s| s.skip_until(&["\""]))
86        })
87    }
88    #[inline]
89    pub fn pairs(s: BoxedState<'_>) -> ParseResult<'_> {
90        s.sequence(|s| {
91            s.optional(|s| {
92                pair(s).and_then(|s| s.repeat(|s| s.sequence(|s| skip(s).and_then(pair))))
93            })
94        })
95    }
96    #[inline]
97    pub fn pair(s: BoxedState<'_>) -> ParseResult<'_> {
98        s.rule(Rule::pair, |s| {
99            s.sequence(|s| key(s).and_then(skip).and_then(value))
100        })
101    }
102    #[inline]
103    pub fn key(s: BoxedState<'_>) -> ParseResult<'_> {
104        quoted_string(s).or_else(unquoted_string)
105    }
106    #[inline]
107    pub fn value(s: BoxedState<'_>) -> ParseResult<'_> {
108        quoted_string(s).or_else(obj).or_else(unquoted_string)
109    }
110    #[inline]
111    pub fn obj(s: BoxedState<'_>) -> ParseResult<'_> {
112        s.rule(Rule::obj, |s| {
113            s.sequence(|s| {
114                s.match_string("{")
115                    .and_then(skip)
116                    .and_then(pairs)
117                    .and_then(skip)
118                    .and_then(|s| s.match_string("}"))
119            })
120        })
121    }
122    #[inline]
123    pub fn quoted_string(s: BoxedState<'_>) -> ParseResult<'_> {
124        s.atomic(Atomicity::CompoundAtomic, |s| {
125            s.rule(Rule::quoted_string, |s| {
126                s.sequence(|s| {
127                    s.match_string("\"")
128                        .and_then(quoted_inner)
129                        .and_then(|s| s.match_string("\""))
130                })
131            })
132        })
133    }
134    #[inline]
135    pub fn quoted_inner(s: BoxedState<'_>) -> ParseResult<'_> {
136        s.rule(Rule::quoted_inner, |s| {
137            s.atomic(Atomicity::Atomic, |s| s.repeat(char))
138        })
139    }
140    #[inline]
141    pub fn char(s: BoxedState<'_>) -> ParseResult<'_> {
142        s.rule(Rule::char, |s| {
143            s.sequence(|s| {
144                s.lookahead(false, |s| {
145                    s.match_string("\"").or_else(|s| s.match_string("\\"))
146                })
147                .and_then(skip)
148                .and_then(any)
149            })
150            .or_else(|s| {
151                s.sequence(|s| {
152                    s.match_string("\\").and_then(skip).and_then(|s| {
153                        s.match_string("\"")
154                            .or_else(|s| s.match_string("\\"))
155                            .or_else(|s| s.match_string("n"))
156                            .or_else(|s| s.match_string("r"))
157                            .or_else(|s| s.match_string("t"))
158                    })
159                })
160            })
161        })
162    }
163    #[inline]
164    pub fn unquoted_string(s: BoxedState<'_>) -> ParseResult<'_> {
165        s.rule(Rule::unquoted_string, |s| {
166            s.atomic(Atomicity::Atomic, |s| {
167                s.sequence(|s| unquoted_char(s).and_then(|s| s.repeat(unquoted_char)))
168            })
169        })
170    }
171    #[inline]
172    pub fn unquoted_char(s: BoxedState<'_>) -> ParseResult<'_> {
173        s.rule(Rule::unquoted_char, |s| {
174            s.sequence(|s| {
175                s.lookahead(false, |s| {
176                    s.match_string("\"")
177                        .or_else(|s| s.match_string("{"))
178                        .or_else(|s| s.match_string("}"))
179                        .or_else(whitespace)
180                })
181                .and_then(skip)
182                .and_then(any)
183            })
184        })
185    }
186    pub fn EOI(s: BoxedState<'_>) -> ParseResult<'_> {
187        s.rule(Rule::EOI, |s| s.end_of_input())
188    }
189}
190
191pub fn pest_parse(input: &str) -> std::result::Result<pest::iterators::Pairs<'_, Rule>, PestError> {
192    pest::state(input, rules::vdf)
193}