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