1 use winnow::combinator::cut_err;
2 use winnow::combinator::delimited;
3 use winnow::combinator::opt;
4 use winnow::combinator::separated1;
5 use winnow::trace::trace;
6
7 use crate::parser::trivia::ws_comment_newline;
8 use crate::parser::value::value;
9 use crate::{Array, Item, RawString, Value};
10
11 use crate::parser::prelude::*;
12
13 // ;; Array
14
15 // array = array-open array-values array-close
array<'i>(check: RecursionCheck) -> impl Parser<Input<'i>, Array, ContextError>16 pub(crate) fn array<'i>(check: RecursionCheck) -> impl Parser<Input<'i>, Array, ContextError> {
17 trace("array", move |input: &mut Input<'i>| {
18 delimited(
19 ARRAY_OPEN,
20 cut_err(array_values(check)),
21 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 ; [
32 pub(crate) const ARRAY_OPEN: u8 = b'[';
33 // array-close = ws-newline %x5D ; ]
34 const ARRAY_CLOSE: u8 = b']';
35 // array-sep = ws %x2C ws ; , Comma
36 const 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 ]
array_values<'i>( check: RecursionCheck, ) -> impl Parser<Input<'i>, Array, ContextError>41 pub(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
array_value<'i>( check: RecursionCheck, ) -> impl Parser<Input<'i>, Value, ContextError>69 pub(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, v, ws2)| v.decorated(RawString::with_span(ws1), RawString::with_span(ws2)))
79 .parse_next(input)
80 }
81 }
82
83 #[cfg(test)]
84 #[cfg(feature = "parse")]
85 #[cfg(feature = "display")]
86 mod test {
87 use super::*;
88
89 #[test]
arrays()90 fn arrays() {
91 let inputs = [
92 r#"[]"#,
93 r#"[ ]"#,
94 r#"[
95 1, 2, 3
96 ]"#,
97 r#"[
98 1,
99 2, # this is ok
100 ]"#,
101 r#"[# comment
102 # comment2
103
104
105 ]"#,
106 r#"[# comment
107 # comment2
108 1
109
110 #sd
111 ,
112 # comment3
113
114 ]"#,
115 r#"[1]"#,
116 r#"[1,]"#,
117 r#"[ "all", 'strings', """are the same""", '''type''']"#,
118 r#"[ 100, -2,]"#,
119 r#"[1, 2, 3]"#,
120 r#"[1.1, 2.1, 3.1]"#,
121 r#"["a", "b", "c"]"#,
122 r#"[ [ 1, 2 ], [3, 4, 5] ]"#,
123 r#"[ [ 1, 2 ], ["a", "b", "c"] ]"#,
124 r#"[ { x = 1, a = "2" }, {a = "a",b = "b", c = "c"} ]"#,
125 ];
126 for input in inputs {
127 dbg!(input);
128 let mut parsed = array(Default::default()).parse(new_input(input));
129 if let Ok(parsed) = &mut parsed {
130 parsed.despan(input);
131 }
132 assert_eq!(parsed.map(|a| a.to_string()), Ok(input.to_owned()));
133 }
134 }
135
136 #[test]
invalid_arrays()137 fn invalid_arrays() {
138 let invalid_inputs = [r#"["#, r#"[,]"#, r#"[,2]"#, r#"[1e165,,]"#];
139 for input in invalid_inputs {
140 dbg!(input);
141 let mut parsed = array(Default::default()).parse(new_input(input));
142 if let Ok(parsed) = &mut parsed {
143 parsed.despan(input);
144 }
145 assert!(parsed.is_err());
146 }
147 }
148 }
149