1use pear::{parse_error, parsers::*};
2use pear::combinators::*;
3use pear::macros::{parse, parser, switch};
4use pear::input::{Pear, Text};
5
6use crate::value::{Value, Dict, escape::escape};
7
8type Input<'a> = Pear<Text<'a>>;
9type Result<'a, T> = pear::input::Result<T, Input<'a>>;
10
11#[inline(always)]
12fn is_whitespace(&byte: &char) -> bool {
13 byte.is_ascii_whitespace()
14}
15
16#[inline(always)]
17fn is_not_separator(&byte: &char) -> bool {
18 !matches!(byte, ',' | '{' | '}' | '[' | ']')
19}
20
21#[inline(always)]
23fn is_ident_char(&byte: &char) -> bool {
24 byte.is_ascii_alphanumeric() || byte == '_' || byte == '-'
25}
26
27#[parser]
28fn string<'a>(input: &mut Input<'a>) -> Result<'a, String> {
29 let mut is_escaped = false;
30 let str_char = |&c: &char| -> bool {
31 if is_escaped { is_escaped = false; return true; }
32 if c == '\\' { is_escaped = true; return true; }
33 c != '"'
34 };
35
36 let inner = (eat('"')?, take_while(str_char)?, eat('"')?).1;
37 match escape(inner) {
38 Ok(string) => string.into_owned(),
39 Err(e) => parse_error!("invalid string: {}", e)?,
40 }
41}
42
43#[parser]
44fn key<'a>(input: &mut Input<'a>) -> Result<'a, String> {
45 switch! {
46 peek('"') => Ok(string()?),
47 _ => Ok(take_some_while(is_ident_char)?.to_string())
48 }
49}
50
51#[parser]
52fn key_value<'a>(input: &mut Input<'a>) -> Result<'a, (String, Value)> {
53 let key = (surrounded(key, is_whitespace)?, eat('=')?).0;
54 (key, surrounded(value, is_whitespace)?)
55}
56
57#[parser]
58fn array<'a>(input: &mut Input<'a>) -> Result<'a, Vec<Value>> {
59 Ok(delimited_collect('[', value, ',', ']')?)
60}
61
62#[parser]
63fn dict<'a>(input: &mut Input<'a>) -> Result<'a, Dict> {
64 Ok(delimited_collect('{', key_value, ',', '}')?)
65}
66
67#[parser]
68fn value<'a>(input: &mut Input<'a>) -> Result<'a, Value> {
69 skip_while(is_whitespace)?;
70 let val = switch! {
71 eat_slice("true") => Value::from(true),
72 eat_slice("false") => Value::from(false),
73 peek('{') => Value::from(dict()?),
74 peek('[') => Value::from(array()?),
75 peek('"') => Value::from(string()?),
76 peek('\'') => Value::from((eat('\'')?, eat_any()?, eat('\'')?).1),
77 _ => {
78 let value = take_while(is_not_separator)?.trim();
79 if value.contains('.') {
80 if let Ok(float) = value.parse::<f64>() {
81 return Ok(Value::from(float));
82 }
83 }
84
85 if let Ok(int) = value.parse::<usize>() {
86 Value::from(int)
87 } else if let Ok(int) = value.parse::<isize>() {
88 Value::from(int)
89 } else {
90 Value::from(value.to_string())
91 }
92 }
93 };
94
95 skip_while(is_whitespace)?;
96 val
97}
98
99impl std::str::FromStr for Value {
100 type Err = std::convert::Infallible;
101
102 fn from_str(s: &str) -> std::result::Result<Self, std::convert::Infallible> {
103 Ok(parse!(value: Text::from(s))
104 .unwrap_or_else(|_| Value::from(s.to_string())))
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111 use crate::util::map;
112
113 macro_rules! assert_parse_eq {
114 ($string:expr => $expected:expr) => ({
115 let expected = Value::from($expected);
116 let actual: Value = $string.parse().unwrap();
117 assert_eq!(actual, expected, "got {:?}, expected {:?}", actual, expected);
118 });
119
120 ($($string:expr => $expected:expr $(,)?)*) => (
121 $(assert_parse_eq!($string => $expected);)*
122 )
123 }
124
125 #[test]
126 #[allow(clippy::approx_constant)] fn check_simple_values_parse() {
128 assert_parse_eq! {
129 "true" => true,
130 "false" => false,
131 "\"false\"" => "false",
132 "\"true\"" => "true",
133 " false" => false,
134 " false " => false,
135 "true " => true,
136 "1" => 1u8,
137 " 0" => 0u8,
138 " -0" => 0i8,
139 " -2" => -2,
140 " 123 " => 123u8,
141 "\"a\"" => "a",
142 "a " => "a",
143 " a " => "a",
144 "\" a\"" => " a",
145 "\"a \"" => "a ",
146 "\" a \"" => " a ",
147 "1.2" => 1.2,
148 " 1.2" => 1.2,
149 "3.14159" => 3.14159,
150 "\"\\t\"" => "\t",
151 "\"abc\\td\"" => "abc\td",
152 "\"abc\\td\\n\"" => "abc\td\n",
153 "\"abc\\td\\n\\n\"" => "abc\td\n\n",
154 "\"abc\\td\"" => "abc\td",
155 "\"\\\"\"" => "\"",
156 "\"\\n\\f\\b\\\\\\r\\\"\"" => "\n\u{c}\u{8}\\\r\"",
157 "\"\\\"hi\\\"\"" => "\"hi\"",
158 "\"hi\\u1234there\"" => "hi\u{1234}there",
159 "\"\\\\\"" => "\\",
160 "\"\\\"" => "\"\\\"",
162 }
163 }
164
165 #[test]
166 fn check_compund_values_parse() {
167 fn v<T: Into<Value>>(v: T) -> Value { v.into() }
168
169 assert_parse_eq! {
170 "[1,2,3]" => vec![1u8, 2u8, 3u8],
171 "{a=b}" => map!["a" => "b"],
172 "{\"a\"=b}" => map!["a" => "b"],
173 "{\"a.b.c\"=b}" => map!["a.b.c" => "b"],
174 "{a=1,b=3}" => map!["a" => 1u8, "b" => 3u8],
175 "{a=1,b=hi}" => map!["a" => v(1u8), "b" => v("hi")],
176 "[1,[2],3]" => vec![v(1u8), v(vec![2u8]), v(3u8)],
177 "{a=[[-2]]}" => map!["a" => vec![vec![-2]]],
178 "{a=[[-2]],b=\" hi\"}" => map!["a" => v(vec![vec![-2]]), "b" => v(" hi")],
179 "[1,true,hi,\"a \"]" => vec![v(1u8), v(true), v("hi"), v("a ")],
180 "[1,{a=b},hi]" => vec![v(1u8), v(map!["a" => "b"]), v("hi")],
181 "[[ -1], {a=[ b ]}, hi ]" =>
182 vec![v(vec![-1]), v(map!["a" => vec!["b"]]), v("hi")],
183 }
184 }
185}