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