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