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 |
16 | pub(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 ; [ |
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 ] |
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 | |
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: 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)] |
84 | #[cfg (feature = "parse" )] |
85 | #[cfg (feature = "display" )] |
86 | mod test { |
87 | use super::*; |
88 | |
89 | #[test ] |
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 ] |
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 | |