| 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 | mod test { |
| 85 | use super::*; |
| 86 | |
| 87 | #[test ] |
| 88 | fn arrays() { |
| 89 | let inputs = [ |
| 90 | r#"[]"# , |
| 91 | r#"[ ]"# , |
| 92 | r#"[ |
| 93 | 1, 2, 3 |
| 94 | ]"# , |
| 95 | r#"[ |
| 96 | 1, |
| 97 | 2, # this is ok |
| 98 | ]"# , |
| 99 | r#"[# comment |
| 100 | # comment2 |
| 101 | |
| 102 | |
| 103 | ]"# , |
| 104 | r#"[# comment |
| 105 | # comment2 |
| 106 | 1 |
| 107 | |
| 108 | #sd |
| 109 | , |
| 110 | # comment3 |
| 111 | |
| 112 | ]"# , |
| 113 | r#"[1]"# , |
| 114 | r#"[1,]"# , |
| 115 | r#"[ "all", 'strings', """are the same""", '''type''']"# , |
| 116 | r#"[ 100, -2,]"# , |
| 117 | r#"[1, 2, 3]"# , |
| 118 | r#"[1.1, 2.1, 3.1]"# , |
| 119 | r#"["a", "b", "c"]"# , |
| 120 | r#"[ [ 1, 2 ], [3, 4, 5] ]"# , |
| 121 | r#"[ [ 1, 2 ], ["a", "b", "c"] ]"# , |
| 122 | r#"[ { x = 1, a = "2" }, {a = "a",b = "b", c = "c"} ]"# , |
| 123 | ]; |
| 124 | for input in inputs { |
| 125 | dbg!(input); |
| 126 | let mut parsed = array(Default::default()).parse(new_input(input)); |
| 127 | if let Ok(parsed) = &mut parsed { |
| 128 | parsed.despan(input); |
| 129 | } |
| 130 | assert_eq!(parsed.map(|a| a.to_string()), Ok(input.to_owned())); |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | #[test ] |
| 135 | fn invalid_arrays() { |
| 136 | let invalid_inputs = [r#"["# , r#"[,]"# , r#"[,2]"# , r#"[1e165,,]"# ]; |
| 137 | for input in invalid_inputs { |
| 138 | dbg!(input); |
| 139 | let mut parsed = array(Default::default()).parse(new_input(input)); |
| 140 | if let Ok(parsed) = &mut parsed { |
| 141 | parsed.despan(input); |
| 142 | } |
| 143 | assert!(parsed.is_err()); |
| 144 | } |
| 145 | } |
| 146 | } |
| 147 | |