1use winnow::combinator::cut_err;
2use winnow::combinator::delimited;
3use winnow::combinator::opt;
4use winnow::combinator::separated1;
5use winnow::trace::trace;
6
7use crate::parser::trivia::ws_comment_newline;
8use crate::parser::value::value;
9use crate::{Array, Item, RawString, Value};
10
11use crate::parser::prelude::*;
12
13// ;; Array
14
15// array = array-open array-values array-close
16pub(crate) fn array<'i>(check: RecursionCheck) -> impl Parser<Input<'i>, Array, ContextError> {
17 trace(name:"array", parser:move |input: &mut Input<'i>| {
18 delimitedimpl Parser, …>(
19 ARRAY_OPEN,
20 second:cut_err(array_values(check)),
21 third:cut_err(ARRAY_CLOSE)
22 .context(StrContext::Label("array"))
23 .context(StrContext::Expected(StrContextValue::CharLiteral(']'))),
24 )
25 .parse_next(input)
26 })
27}
28
29// note: we're omitting ws and newlines here, because
30// they should be part of the formatted values
31// array-open = %x5B ws-newline ; [
32pub(crate) const ARRAY_OPEN: u8 = b'[';
33// array-close = ws-newline %x5D ; ]
34const ARRAY_CLOSE: u8 = b']';
35// array-sep = ws %x2C ws ; , Comma
36const ARRAY_SEP: u8 = b',';
37
38// note: this rule is modified
39// array-values = [ ( array-value array-sep array-values ) /
40// array-value / ws-comment-newline ]
41pub(crate) fn array_values<'i>(
42 check: RecursionCheck,
43) -> impl Parser<Input<'i>, Array, ContextError> {
44 move |input: &mut Input<'i>| {
45 let check = check.recursing(input)?;
46 (
47 opt(
48 (separated1(array_value(check), ARRAY_SEP), opt(ARRAY_SEP)).map(
49 |(v, trailing): (Vec<Value>, Option<u8>)| {
50 (
51 Array::with_vec(v.into_iter().map(Item::Value).collect()),
52 trailing.is_some(),
53 )
54 },
55 ),
56 ),
57 ws_comment_newline.span(),
58 )
59 .try_map::<_, _, std::str::Utf8Error>(|(array, trailing)| {
60 let (mut array, comma) = array.unwrap_or_default();
61 array.set_trailing_comma(comma);
62 array.set_trailing(RawString::with_span(trailing));
63 Ok(array)
64 })
65 .parse_next(input)
66 }
67}
68
69pub(crate) fn array_value<'i>(
70 check: RecursionCheck,
71) -> impl Parser<Input<'i>, Value, ContextError> {
72 move |input: &mut Input<'i>| {
73 (
74 ws_comment_newline.span(),
75 value(check),
76 ws_comment_newline.span(),
77 )
78 .map(|(ws1: Range, v: Value, ws2: Range)| v.decorated(prefix:RawString::with_span(ws1), suffix:RawString::with_span(ws2)))
79 .parse_next(input)
80 }
81}
82
83#[cfg(test)]
84mod test {
85 use super::*;
86
87 #[test]
88 fn arrays() {
89 let inputs = [
90 r#"[]"#,
91 r#"[ ]"#,
92 r#"[
93 1, 2, 3
94]"#,
95 r#"[
96 1,
97 2, # this is ok
98]"#,
99 r#"[# comment
100# comment2
101
102
103 ]"#,
104 r#"[# comment
105# comment2
106 1
107
108#sd
109,
110# comment3
111
112 ]"#,
113 r#"[1]"#,
114 r#"[1,]"#,
115 r#"[ "all", 'strings', """are the same""", '''type''']"#,
116 r#"[ 100, -2,]"#,
117 r#"[1, 2, 3]"#,
118 r#"[1.1, 2.1, 3.1]"#,
119 r#"["a", "b", "c"]"#,
120 r#"[ [ 1, 2 ], [3, 4, 5] ]"#,
121 r#"[ [ 1, 2 ], ["a", "b", "c"] ]"#,
122 r#"[ { x = 1, a = "2" }, {a = "a",b = "b", c = "c"} ]"#,
123 ];
124 for input in inputs {
125 dbg!(input);
126 let mut parsed = array(Default::default()).parse(new_input(input));
127 if let Ok(parsed) = &mut parsed {
128 parsed.despan(input);
129 }
130 assert_eq!(parsed.map(|a| a.to_string()), Ok(input.to_owned()));
131 }
132 }
133
134 #[test]
135 fn invalid_arrays() {
136 let invalid_inputs = [r#"["#, r#"[,]"#, r#"[,2]"#, r#"[1e165,,]"#];
137 for input in invalid_inputs {
138 dbg!(input);
139 let mut parsed = array(Default::default()).parse(new_input(input));
140 if let Ok(parsed) = &mut parsed {
141 parsed.despan(input);
142 }
143 assert!(parsed.is_err());
144 }
145 }
146}
147