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 |
17 | pub(crate) fn array<'i>(input: &mut Input<'i>) -> ModalResult<Array> { |
18 | traceimpl Parser, …>, …, …>(name:"array" , parser:move |input: &mut Input<'i>| { |
19 | delimitedimpl Parser, …>, …, …>( |
20 | ARRAY_OPEN, |
21 | parser:cut_err(array_values), |
22 | ignored2: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 ] |
42 | pub(crate) fn array_values(input: &mut Input<'_>) -> ModalResult<Array> { |
43 | if peek(parser: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: Vec = separated(occurrences:0.., parser:array_value, ARRAY_SEP).parse_next(input)?; |
49 | let mut array: Array = Array::with_vec(values:array); |
50 | if !array.is_empty() { |
51 | let comma: bool = opt(ARRAY_SEP).parse_next(input)?.is_some(); |
52 | array.set_trailing_comma(yes:comma); |
53 | } |
54 | let trailing: Range = ws_comment_newline.span().parse_next(input)?; |
55 | array.set_trailing(RawString::with_span(trailing)); |
56 | |
57 | Ok(array) |
58 | } |
59 | |
60 | pub(crate) fn array_value(input: &mut Input<'_>) -> ModalResult<Item> { |
61 | let prefix: Range = ws_comment_newline.span().parse_next(input)?; |
62 | let value: Value = value.parse_next(input)?; |
63 | let suffix: Range = ws_comment_newline.span().parse_next(input)?; |
64 | let value: Value = value.decorated(prefix:RawString::with_span(prefix), suffix:RawString::with_span(suffix)); |
65 | let value: Item = 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 ] |
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 ] |
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 | |