1use crate::{pbuf::PBytes, Abstract, ValArray, Value};
2use arcstr::ArcStr;
3use base64::{engine::general_purpose::STANDARD as BASE64, Engine};
4use bytes::Bytes;
5use combine::{
6 attempt, between, choice, eof, from_str, look_ahead, many1, none_of, not_followed_by,
7 one_of, optional, parser,
8 parser::{
9 char::{alpha_num, digit, spaces, string},
10 combinator::recognize,
11 range::{take_while, take_while1},
12 repeat::escaped,
13 },
14 sep_by, sep_by1,
15 stream::{position, Range},
16 token, unexpected_any, EasyParser, ParseError, Parser, RangeStream,
17};
18use compact_str::CompactString;
19use escaping::Escape;
20use netidx_core::pack::Pack;
21use poolshark::local::LPooled;
22use rust_decimal::Decimal;
23use std::{borrow::Cow, str::FromStr, sync::LazyLock, time::Duration};
24use triomphe::Arc;
25
26pub fn sep_by1_tok<I, O, OC, EP, SP, TP>(
28 p: EP,
29 sep: SP,
30 term: TP,
31) -> impl Parser<I, Output = OC>
32where
33 I: RangeStream<Token = char>,
34 I::Error: ParseError<I::Token, I::Range, I::Position>,
35 I::Range: Range,
36 OC: Extend<O> + Default,
37 SP: Parser<I>,
38 EP: Parser<I, Output = O>,
39 TP: Parser<I>,
40{
41 sep_by1(choice((look_ahead(term).map(|_| None::<O>), p.map(Some))), sep).map(
42 |mut e: LPooled<Vec<Option<O>>>| {
43 let mut res = OC::default();
44 res.extend(e.drain(..).filter_map(|e| e));
45 res
46 },
47 )
48}
49
50pub fn sep_by_tok<I, O, OC, EP, SP, TP>(
52 p: EP,
53 sep: SP,
54 term: TP,
55) -> impl Parser<I, Output = OC>
56where
57 I: RangeStream<Token = char>,
58 I::Error: ParseError<I::Token, I::Range, I::Position>,
59 I::Range: Range,
60 OC: Extend<O> + Default,
61 SP: Parser<I>,
62 EP: Parser<I, Output = O>,
63 TP: Parser<I>,
64{
65 sep_by(choice((look_ahead(term).map(|_| None::<O>), p.map(Some))), sep).map(
66 |mut e: LPooled<Vec<Option<O>>>| {
67 let mut res = OC::default();
68 res.extend(e.drain(..).filter_map(|e| e));
69 res
70 },
71 )
72}
73
74pub fn not_prefix<I>() -> impl Parser<I, Output = ()>
75where
76 I: RangeStream<Token = char>,
77 I::Error: ParseError<I::Token, I::Range, I::Position>,
78 I::Range: Range,
79{
80 not_followed_by(choice((token('_'), alpha_num())))
81}
82
83fn sptoken<I>(t: char) -> impl Parser<I, Output = char>
84where
85 I: RangeStream<Token = char>,
86 I::Error: ParseError<I::Token, I::Range, I::Position>,
87 I::Range: Range,
88{
89 spaces().with(token(t))
90}
91
92fn spstring<I>(t: &'static str) -> impl Parser<I, Output = &'static str>
93where
94 I: RangeStream<Token = char>,
95 I::Error: ParseError<I::Token, I::Range, I::Position>,
96 I::Range: Range,
97{
98 spaces().with(string(t))
99}
100
101fn csep<I>() -> impl Parser<I, Output = char>
102where
103 I: RangeStream<Token = char>,
104 I::Error: ParseError<I::Token, I::Range, I::Position>,
105 I::Range: Range,
106{
107 attempt(spaces().with(token(','))).skip(spaces())
108}
109
110fn should_escape_generic(c: char) -> bool {
111 c.is_control()
112}
113
114pub const VAL_MUST_ESC: [char; 2] = ['\\', '"'];
115pub static VAL_ESC: LazyLock<Escape> = LazyLock::new(|| {
116 Escape::new(
117 '\\',
118 &['\\', '"', '\n', '\r', '\0', '\t'],
119 &[('\n', "n"), ('\r', "r"), ('\t', "t"), ('\0', "0")],
120 Some(should_escape_generic),
121 )
122 .unwrap()
123});
124
125pub fn escaped_string<I>(
126 must_esc: &'static [char],
127 esc: &Escape,
128) -> impl Parser<I, Output = String>
129where
130 I: RangeStream<Token = char>,
131 I::Error: ParseError<I::Token, I::Range, I::Position>,
132 I::Range: Range,
133{
134 recognize(escaped(
135 take_while1(move |c| !must_esc.contains(&c)),
136 esc.get_escape_char(),
137 one_of(
138 esc.get_tr()
139 .iter()
140 .filter_map(|(_, s)| s.chars().next())
141 .chain(must_esc.iter().copied()),
142 ),
143 ))
144 .map(|s| match esc.unescape(&s) {
145 Cow::Borrowed(_) => s, Cow::Owned(s) => s,
147 })
148}
149
150fn quoted<I>(
151 must_escape: &'static [char],
152 esc: &Escape,
153) -> impl Parser<I, Output = String>
154where
155 I: RangeStream<Token = char>,
156 I::Error: ParseError<I::Token, I::Range, I::Position>,
157 I::Range: Range,
158{
159 between(token('"'), token('"'), escaped_string(must_escape, esc))
160}
161
162fn uint<I, T: FromStr + Clone + Copy>() -> impl Parser<I, Output = T>
163where
164 I: RangeStream<Token = char>,
165 I::Error: ParseError<I::Token, I::Range, I::Position>,
166 I::Range: Range,
167{
168 many1(digit()).then(|s: CompactString| match s.parse::<T>() {
169 Ok(i) => combine::value(i).right(),
170 Err(_) => unexpected_any("invalid unsigned integer").left(),
171 })
172}
173
174pub fn int<I, T: FromStr + Clone + Copy>() -> impl Parser<I, Output = T>
175where
176 I: RangeStream<Token = char>,
177 I::Error: ParseError<I::Token, I::Range, I::Position>,
178 I::Range: Range,
179{
180 recognize((optional(token('-')), take_while1(|c: char| c.is_digit(10)))).then(
181 |s: CompactString| match s.parse::<T>() {
182 Ok(i) => combine::value(i).right(),
183 Err(_) => unexpected_any("invalid signed integer").left(),
184 },
185 )
186}
187
188fn flt<I, T: FromStr + Clone + Copy>() -> impl Parser<I, Output = T>
189where
190 I: RangeStream<Token = char>,
191 I::Error: ParseError<I::Token, I::Range, I::Position>,
192 I::Range: Range,
193{
194 choice((
195 attempt(recognize((
196 optional(token('-')),
197 take_while1(|c: char| c.is_digit(10)),
198 optional(token('.')),
199 take_while(|c: char| c.is_digit(10)),
200 token('e'),
201 optional(token('-')),
202 take_while1(|c: char| c.is_digit(10)),
203 ))),
204 attempt(recognize((
205 optional(token('-')),
206 take_while1(|c: char| c.is_digit(10)),
207 token('.'),
208 take_while(|c: char| c.is_digit(10)),
209 ))),
210 ))
211 .then(|s: CompactString| match s.parse::<T>() {
212 Ok(i) => combine::value(i).right(),
213 Err(_) => unexpected_any("invalid float").left(),
214 })
215}
216
217fn base64<I>() -> impl Parser<I, Output = LPooled<Vec<u8>>>
218where
219 I: RangeStream<Token = char>,
220 I::Error: ParseError<I::Token, I::Range, I::Position>,
221 I::Range: Range,
222{
223 recognize((
224 take_while(|c: char| c.is_ascii_alphanumeric() || c == '+' || c == '/'),
225 take_while(|c: char| c == '='),
226 ))
227 .then(|s: LPooled<String>| {
228 let s = if &*s == "==" { LPooled::take() } else { s };
229 let mut buf: LPooled<Vec<u8>> = LPooled::take();
230 match BASE64.decode_vec(&*s, &mut buf) {
231 Ok(()) => combine::value(buf).right(),
232 Err(_) => unexpected_any("base64 decode failed").left(),
233 }
234 })
235}
236
237fn constant<I>(typ: &'static str) -> impl Parser<I, Output = ()>
238where
239 I: RangeStream<Token = char>,
240 I::Error: ParseError<I::Token, I::Range, I::Position>,
241 I::Range: Range,
242{
243 string(typ).with(spaces()).with(token(':')).with(spaces()).map(|_| ())
244}
245
246pub fn close_expr<I>() -> impl Parser<I, Output = ()>
247where
248 I: RangeStream<Token = char>,
249 I::Error: ParseError<I::Token, I::Range, I::Position>,
250 I::Range: Range,
251{
252 not_followed_by(none_of([' ', '\n', '\t', ';', ')', ',', ']', '}', '"']))
253}
254
255fn value_<I>(must_escape: &'static [char], esc: &Escape) -> impl Parser<I, Output = Value>
256where
257 I: RangeStream<Token = char>,
258 I::Error: ParseError<I::Token, I::Range, I::Position>,
259 I::Range: Range,
260{
261 spaces().with(choice((
262 choice((
263 attempt(constant("u8")).with(uint::<_, u8>().map(Value::U8)),
264 attempt(constant("u16")).with(uint::<_, u16>().map(Value::U16)),
265 attempt(constant("u32")).with(uint::<_, u32>().map(Value::U32)),
266 constant("u64").with(uint::<_, u64>().map(Value::U64)),
267 attempt(constant("i8")).with(int::<_, i8>().map(Value::I8)),
268 attempt(constant("i16")).with(int::<_, i16>().map(Value::I16)),
269 attempt(constant("i32")).with(int::<_, i32>().map(Value::I32)),
270 constant("i64").with(int::<_, i64>().map(Value::I64)),
271 attempt(constant("v32")).with(uint::<_, u32>().map(Value::V32)),
272 constant("v64").with(uint::<_, u64>().map(Value::V64)),
273 attempt(constant("z32")).with(int::<_, i32>().map(Value::Z32)),
274 constant("z64").with(int::<_, i64>().map(Value::Z64)),
275 attempt(constant("f32")).with(flt::<_, f32>().map(Value::F32)),
276 attempt(constant("f64")).with(flt::<_, f64>().map(Value::F64)),
277 )),
278 between(
279 token('['),
280 sptoken(']'),
281 sep_by_tok(value(must_escape, esc), csep(), token(']')),
282 )
283 .map(|mut vals: LPooled<Vec<Value>>| {
284 Value::Array(ValArray::from_iter_exact(vals.drain(..)))
285 }),
286 between(
287 token('{'),
288 sptoken('}'),
289 sep_by_tok(
290 (value(must_escape, esc), spstring("=>").with(value(must_escape, esc))),
291 csep(),
292 token('}'),
293 )
294 .map(|mut vals: LPooled<Vec<(Value, Value)>>| {
295 Value::Map(immutable_chunkmap::map::Map::from_iter(vals.drain(..)))
296 }),
297 ),
298 quoted(must_escape, esc).map(|s| Value::String(ArcStr::from(s))),
299 flt::<_, f64>().map(Value::F64),
300 int::<_, i64>().map(Value::I64),
301 attempt(string("true").skip(not_prefix())).map(|_| Value::Bool(true)),
302 attempt(string("false").skip(not_prefix())).map(|_| Value::Bool(false)),
303 attempt(string("null").skip(not_prefix())).map(|_| Value::Null),
304 constant("bytes")
305 .with(base64())
306 .map(|v| Value::Bytes(PBytes::new(Bytes::from(LPooled::detach(v))))),
307 constant("abstract").with(base64()).then(|v| {
308 match Abstract::decode(&mut &v[..]) {
309 Ok(a) => combine::value(Value::Abstract(a)).right(),
310 Err(_) => unexpected_any("failed to unpack abstract").left(),
311 }
312 }),
313 constant("error")
314 .with(value(must_escape, esc))
315 .map(|v| Value::Error(Arc::new(v))),
316 attempt(constant("decimal"))
317 .with(flt::<_, Decimal>())
318 .map(|d| Value::Decimal(Arc::new(d))),
319 attempt(constant("datetime"))
320 .with(from_str(quoted(must_escape, esc)))
321 .map(|d| Value::DateTime(Arc::new(d))),
322 constant("duration")
323 .with(flt::<_, f64>().and(choice((
324 string("ns"),
325 string("us"),
326 string("ms"),
327 string("s"),
328 ))))
329 .map(|(n, suffix)| {
330 let d = match suffix {
331 "ns" => Duration::from_secs_f64(n / 1e9),
332 "us" => Duration::from_secs_f64(n / 1e6),
333 "ms" => Duration::from_secs_f64(n / 1e3),
334 "s" => Duration::from_secs_f64(n),
335 _ => unreachable!(),
336 };
337 Value::Duration(Arc::new(d))
338 }),
339 )))
340}
341
342parser! {
343 pub fn value['a, I](
344 must_escape: &'static [char],
345 esc: &'a Escape
346 )(I) -> Value
347 where [I: RangeStream<Token = char>, I::Range: Range]
348 {
349 value_(must_escape, esc)
350 }
351}
352
353pub fn parse_value(s: &str) -> anyhow::Result<Value> {
354 value(&VAL_MUST_ESC, &VAL_ESC)
355 .skip(spaces())
356 .skip(eof())
357 .easy_parse(position::Stream::new(s))
358 .map(|(r, _)| r)
359 .map_err(|e| anyhow::anyhow!(format!("{}", e)))
360}
361
362#[cfg(test)]
363mod tests {
364 use arcstr::literal;
365
366 use crate::Map;
367
368 use super::*;
369
370 #[test]
371 fn parse() {
372 assert_eq!(Value::U32(23), parse_value("u32:23").unwrap());
373 assert_eq!(Value::V32(42), parse_value("v32:42").unwrap());
374 assert_eq!(Value::I32(-10), parse_value("i32:-10").unwrap());
375 assert_eq!(Value::I32(12321), parse_value("i32:12321").unwrap());
376 assert_eq!(Value::Z32(-99), parse_value("z32:-99").unwrap());
377 assert_eq!(Value::U64(100), parse_value("u64:100").unwrap());
378 assert_eq!(Value::V64(100), parse_value("v64:100").unwrap());
379 assert_eq!(Value::I64(-100), parse_value("i64:-100").unwrap());
380 assert_eq!(Value::I64(-100), parse_value("-100").unwrap());
381 assert_eq!(Value::I64(100), parse_value("i64:100").unwrap());
382 assert_eq!(Value::I64(100), parse_value("100").unwrap());
383 assert_eq!(Value::Z64(-100), parse_value("z64:-100").unwrap());
384 assert_eq!(Value::Z64(100), parse_value("z64:100").unwrap());
385 assert_eq!(Value::F32(3.1415), parse_value("f32:3.1415").unwrap());
386 assert_eq!(Value::F32(675.6), parse_value("f32:675.6").unwrap());
387 assert_eq!(Value::F32(42.3435), parse_value("f32:42.3435").unwrap());
388 assert_eq!(Value::F32(1.123e9), parse_value("f32:1.123e9").unwrap());
389 assert_eq!(Value::F32(1e9), parse_value("f32:1e9").unwrap());
390 assert_eq!(Value::F32(21.2443e-6), parse_value("f32:21.2443e-6").unwrap());
391 assert_eq!(Value::F32(3.), parse_value("f32:3.").unwrap());
392 assert_eq!(Value::F64(3.1415), parse_value("f64:3.1415").unwrap());
393 assert_eq!(Value::F64(3.1415), parse_value("3.1415").unwrap());
394 assert_eq!(Value::F64(1.123e9), parse_value("1.123e9").unwrap());
395 assert_eq!(Value::F64(1e9), parse_value("1e9").unwrap());
396 assert_eq!(Value::F64(21.2443e-6), parse_value("21.2443e-6").unwrap());
397 assert_eq!(Value::F64(3.), parse_value("f64:3.").unwrap());
398 assert_eq!(Value::F64(3.), parse_value("3.").unwrap());
399 let c = ArcStr::from(r#"I've got a lovely "bunch" of (coconuts)"#);
400 let s = r#""I've got a lovely \"bunch\" of (coconuts)""#;
401 assert_eq!(Value::String(c), parse_value(s).unwrap());
402 let c = ArcStr::new();
403 assert_eq!(Value::String(c), parse_value(r#""""#).unwrap());
404 let c = ArcStr::from(r#"""#);
405 let s = r#""\"""#;
406 assert_eq!(Value::String(c), parse_value(s).unwrap());
407 assert_eq!(Value::Bool(true), parse_value("true").unwrap());
408 assert_eq!(Value::Bool(true), parse_value("true ").unwrap());
409 assert_eq!(Value::Bool(false), parse_value("false").unwrap());
410 assert_eq!(Value::Null, parse_value("null").unwrap());
411 assert_eq!(
412 Value::error(literal!("error")),
413 parse_value(r#"error:"error""#).unwrap()
414 );
415 let a = ValArray::from_iter_exact(
416 [Value::I64(42), Value::String(literal!("hello world"))].into_iter(),
417 );
418 assert_eq!(
419 Value::Array(a.clone()),
420 parse_value(r#"[42, "hello world", ]"#).unwrap()
421 );
422 assert_eq!(Value::Array(a), parse_value(r#"[42, "hello world"]"#).unwrap());
423 let m = Map::from_iter([
424 (Value::I64(42), Value::String(literal!("hello world"))),
425 (Value::String(literal!("hello world")), Value::I64(42)),
426 ]);
427 assert_eq!(
428 Value::Map(m.clone()),
429 parse_value(r#"{ 42 => "hello world", "hello world" => 42, }"#).unwrap()
430 );
431 assert_eq!(
432 Value::Map(m.clone()),
433 parse_value(r#"{ 42 => "hello world", "hello world" => 42}"#).unwrap()
434 )
435 }
436}