thrift_parser/
constant.rs

1use std::str::FromStr;
2
3use nom::branch::alt;
4use nom::character::complete::{char as cchar, digit0, digit1};
5use nom::combinator::{map, map_res, opt, recognize};
6use nom::multi::separated_list0;
7use nom::sequence::{delimited, pair, separated_pair, tuple};
8use nom::IResult;
9
10use crate::basic::{Identifier, IdentifierRef, ListSeparator, Literal, LiteralRef, Separator};
11use crate::Parser;
12
13// ConstValue      ::=  IntConstant | DoubleConstant | Literal | Identifier | ConstList | ConstMap
14#[derive(Debug, Clone, PartialEq)]
15pub enum ConstValueRef<'a> {
16    Identifier(IdentifierRef<'a>),
17    Literal(LiteralRef<'a>),
18    Double(DoubleConstant),
19    Int(IntConstant),
20    List(ConstListRef<'a>),
21    Map(ConstMapRef<'a>),
22}
23
24impl<'a> Parser<'a> for ConstValueRef<'a> {
25    fn parse(input: &'a str) -> IResult<&'a str, Self> {
26        alt((
27            map(IdentifierRef::parse, ConstValueRef::Identifier),
28            map(LiteralRef::parse, ConstValueRef::Literal),
29            map(DoubleConstant::parse2, ConstValueRef::Double),
30            map(IntConstant::parse, ConstValueRef::Int),
31            map(ConstListRef::parse, ConstValueRef::List),
32            map(ConstMapRef::parse, ConstValueRef::Map),
33        ))(input)
34    }
35}
36
37#[derive(Debug, Clone, PartialEq)]
38pub enum ConstValue {
39    Identifier(Identifier),
40    Literal(Literal),
41    Double(DoubleConstant),
42    Int(IntConstant),
43    List(ConstList),
44    Map(ConstMap),
45}
46
47impl<'a> From<ConstValueRef<'a>> for ConstValue {
48    fn from(r: ConstValueRef<'a>) -> Self {
49        match r {
50            ConstValueRef::Identifier(i) => Self::Identifier(i.into()),
51            ConstValueRef::Literal(i) => Self::Literal(i.into()),
52            ConstValueRef::Double(i) => Self::Double(i),
53            ConstValueRef::Int(i) => Self::Int(i),
54            ConstValueRef::List(i) => Self::List(i.into()),
55            ConstValueRef::Map(i) => Self::Map(i.into()),
56        }
57    }
58}
59
60impl<'a> Parser<'a> for ConstValue {
61    fn parse(input: &'a str) -> IResult<&'a str, Self> {
62        ConstValueRef::parse(input).map(|(remains, parsed)| (remains, parsed.into()))
63    }
64}
65
66// IntConstant     ::=  ('+' | '-')? Digit+
67#[derive(derive_newtype::NewType, Hash, Eq, PartialEq, Debug, Copy, Clone)]
68pub struct IntConstant(i64);
69
70impl<'a> Parser<'a> for IntConstant {
71    fn parse(input: &'a str) -> IResult<&'a str, Self> {
72        map_res(
73            recognize(tuple((opt(alt((cchar('-'), cchar('+')))), digit1))),
74            |d_str| -> Result<Self, std::num::ParseIntError> {
75                let d = FromStr::from_str(d_str)?;
76                Ok(Self(d))
77            },
78        )(input)
79    }
80}
81
82// DoubleConstant  ::=  ('+' | '-')? Digit* ('.' Digit+)? ( ('E' | 'e') IntConstant )?
83#[derive(derive_newtype::NewType, Debug, Copy, Clone)]
84pub struct DoubleConstant(f64);
85
86impl<'a> Parser<'a> for DoubleConstant {
87    fn parse(input: &'a str) -> IResult<&'a str, Self> {
88        map_res(
89            recognize(tuple((
90                opt(alt((cchar('-'), cchar('+')))),
91                digit0,
92                opt(pair(cchar('.'), digit1)),
93                opt(pair(alt((cchar('E'), cchar('e'))), IntConstant::parse)),
94            ))),
95            |d_str| -> Result<Self, std::num::ParseFloatError> {
96                let d = FromStr::from_str(d_str)?;
97                Ok(Self(d))
98            },
99        )(input)
100    }
101}
102// Double except int: If the double is indeed a int, it will fail!
103impl DoubleConstant {
104    fn parse2(input: &str) -> IResult<&str, Self> {
105        map_res(
106            recognize(tuple((
107                opt(alt((cchar('-'), cchar('+')))),
108                digit0,
109                opt(pair(cchar('.'), digit1)),
110                opt(pair(alt((cchar('E'), cchar('e'))), IntConstant::parse)),
111            ))),
112            |d_str| -> Result<Self, std::num::ParseFloatError> {
113                if !d_str.contains('.') && !d_str.contains('e') && !d_str.contains('E') {
114                    return Err(f64::from_str("").unwrap_err());
115                }
116                let d = FromStr::from_str(d_str)?;
117                Ok(Self(d))
118            },
119        )(input)
120    }
121}
122
123impl PartialEq for DoubleConstant {
124    fn eq(&self, other: &Self) -> bool {
125        float_cmp::approx_eq!(f64, self.0, other.0)
126    }
127}
128
129// ConstList       ::=  '[' (ConstValue ListSeparator?)* ']'
130#[derive(derive_newtype::NewType, PartialEq, Debug, Clone)]
131pub struct ConstListRef<'a>(Vec<ConstValueRef<'a>>);
132
133impl<'a> Parser<'a> for ConstListRef<'a> {
134    fn parse(input: &'a str) -> IResult<&'a str, Self> {
135        map(
136            delimited(
137                pair(cchar('['), opt(Separator::parse)),
138                separated_list0(parse_list_separator, ConstValueRef::parse),
139                pair(opt(Separator::parse), cchar(']')),
140            ),
141            Self,
142        )(input)
143    }
144}
145
146#[derive(derive_newtype::NewType, PartialEq, Debug, Clone)]
147pub struct ConstList(Vec<ConstValue>);
148
149impl<'a> From<ConstListRef<'a>> for ConstList {
150    fn from(r: ConstListRef<'a>) -> Self {
151        Self(r.0.into_iter().map(Into::into).collect())
152    }
153}
154
155impl<'a> Parser<'a> for ConstList {
156    fn parse(input: &'a str) -> IResult<&'a str, Self> {
157        ConstListRef::parse(input).map(|(remains, parsed)| (remains, parsed.into()))
158    }
159}
160
161// ConstMap        ::=  '{' (ConstValue ':' ConstValue ListSeparator?)* '}'
162#[derive(derive_newtype::NewType, PartialEq, Debug, Clone)]
163pub struct ConstMapRef<'a>(Vec<(ConstValueRef<'a>, ConstValueRef<'a>)>);
164
165impl<'a> Parser<'a> for ConstMapRef<'a> {
166    fn parse(input: &'a str) -> IResult<&'a str, Self> {
167        map(
168            delimited(
169                pair(cchar('{'), opt(Separator::parse)),
170                separated_list0(
171                    parse_list_separator,
172                    separated_pair(
173                        ConstValueRef::parse,
174                        delimited(opt(Separator::parse), cchar(':'), opt(Separator::parse)),
175                        ConstValueRef::parse,
176                    ),
177                ),
178                pair(opt(Separator::parse), cchar('}')),
179            ),
180            Self,
181        )(input)
182    }
183}
184
185#[derive(derive_newtype::NewType, PartialEq, Debug, Clone)]
186pub struct ConstMap(Vec<(ConstValue, ConstValue)>);
187
188impl<'a> From<ConstMapRef<'a>> for ConstMap {
189    fn from(r: ConstMapRef<'a>) -> Self {
190        Self(r.0.into_iter().map(|(a, b)| (a.into(), b.into())).collect())
191    }
192}
193
194impl<'a> Parser<'a> for ConstMap {
195    fn parse(input: &'a str) -> IResult<&'a str, Self> {
196        ConstMapRef::parse(input).map(|(remains, parsed)| (remains, parsed.into()))
197    }
198}
199
200// At least one Separator or ListSeparator
201pub fn parse_list_separator(input: &str) -> IResult<&str, ()> {
202    alt((
203        map(
204            tuple((
205                Separator::parse,
206                opt(ListSeparator::parse),
207                opt(Separator::parse),
208            )),
209            |_| (),
210        ),
211        map(tuple((ListSeparator::parse, opt(Separator::parse))), |_| ()),
212    ))(input)
213}
214
215#[cfg(test)]
216mod test {
217    use crate::utils::*;
218
219    use super::*;
220
221    #[test]
222    fn test_int_constant() {
223        assert_list_eq_with_f(
224            vec!["123", "+123", "-123"],
225            vec![123, 123, -123],
226            IntConstant::parse,
227            IntConstant,
228        );
229        assert_list_err_with_f(
230            vec![
231                "-+123",
232                "+-123",
233                "+",
234                "-",
235                "10000000000000000000000000000000000000000000000",
236            ],
237            IntConstant::parse,
238        );
239    }
240
241    #[test]
242    fn test_double_constant() {
243        assert_list_eq_with_f(
244            vec![
245                "123.0",
246                ".5",
247                "-.5",
248                "+123.2333333e10",
249                "+123.2333333E100",
250                "+123.1.THE.FOLLOWING",
251                "1.1",
252            ],
253            vec![
254                123.0,
255                0.5,
256                -0.5,
257                123.2333333e10,
258                123.2333333E100,
259                123.1,
260                1.1,
261            ],
262            DoubleConstant::parse,
263            DoubleConstant,
264        );
265        assert_list_err_with_f(vec!["+-123.THE.FOLLOWING"], DoubleConstant::parse);
266    }
267
268    #[test]
269    fn test_const_list() {
270        assert_list_eq_with_f(
271            vec![
272                "[ 1,  3 ; 5  6/**/7 , ihciah 1.1]",
273                "[6/**/7 ihciah 1.1   A ]",
274                "[]",
275            ],
276            vec![
277                vec![
278                    ConstValueRef::Int(IntConstant(1)),
279                    ConstValueRef::Int(IntConstant(3)),
280                    ConstValueRef::Int(IntConstant(5)),
281                    ConstValueRef::Int(IntConstant(6)),
282                    ConstValueRef::Int(IntConstant(7)),
283                    ConstValueRef::Identifier(IdentifierRef::from("ihciah")),
284                    ConstValueRef::Double(DoubleConstant(1.1)),
285                ],
286                vec![
287                    ConstValueRef::Int(IntConstant(6)),
288                    ConstValueRef::Int(IntConstant(7)),
289                    ConstValueRef::Identifier(IdentifierRef::from("ihciah")),
290                    ConstValueRef::Double(DoubleConstant(1.1)),
291                    ConstValueRef::Identifier(IdentifierRef::from("A")),
292                ],
293                vec![],
294            ],
295            ConstListRef::parse,
296            ConstListRef,
297        );
298        assert_list_err_with_f(vec!["[1,2,3A]"], ConstListRef::parse);
299    }
300
301    #[test]
302    fn test_const_map() {
303        assert_list_eq_with_f(
304            vec!["{1:2, 3:4}", "{}"],
305            vec![
306                vec![
307                    (
308                        ConstValueRef::Int(IntConstant(1)),
309                        ConstValueRef::Int(IntConstant(2)),
310                    ),
311                    (
312                        ConstValueRef::Int(IntConstant(3)),
313                        ConstValueRef::Int(IntConstant(4)),
314                    ),
315                ],
316                vec![],
317            ],
318            ConstMapRef::parse,
319            ConstMapRef,
320        );
321        assert_list_err_with_f(vec!["{1:34:5}"], ConstMapRef::parse);
322    }
323}