| 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 | |