use crate::{syntax_error::SyntaxError, syntax_kind::SyntaxKind}; use super::{value, CompletedMarker, Parser}; pub(super) fn array(p: &mut Parser) -> Option { let array_start = p.start("array"); if !p.eat(SyntaxKind::BRACKET_OPEN) { array_start.abandon(p); return None; } let el = p.start("arr_el"); value(p); el.complete(p, SyntaxKind::ELEMENT); while p.at(SyntaxKind::COMMA) { let potential_trailing_comma = p.start("potential_trailing_comma"); p.eat(SyntaxKind::COMMA); let maybe_el = p.start("arr_el"); if !value(p) { maybe_el.abandon(p); potential_trailing_comma.complete(p, SyntaxKind::TRAILING_COMMA); } else { maybe_el.complete(p, SyntaxKind::ELEMENT); potential_trailing_comma.abandon(p); } } Some(if !p.eat(SyntaxKind::BRACKET_CLOSE) { array_start.error(p, SyntaxError::UnclosedArray) } else { array_start.complete(p, SyntaxKind::ARRAY) }) } #[cfg(test)] mod tests { use crate::grammar::{array::array, test_utils::gen_checks}; #[test] fn array_basic() { gen_checks! {array; r#"[1,2,3]"# => r#"ROOT { ARRAY { BRACKET_OPEN "["; ELEMENT { NUMBER "1"; } COMMA ","; ELEMENT { NUMBER "2"; } COMMA ","; ELEMENT { NUMBER "3"; } BRACKET_CLOSE "]"; } }"#, r#"[1,2,]"# => r#"ROOT { ARRAY { BRACKET_OPEN "["; ELEMENT { NUMBER "1"; } COMMA ","; ELEMENT { NUMBER "2"; } TRAILING_COMMA { COMMA ","; } BRACKET_CLOSE "]"; } }"#, r#"[1,2"# => r#"ROOT { PARSE_ERR: UnclosedArray { BRACKET_OPEN "["; ELEMENT { NUMBER "1"; } COMMA ","; ELEMENT { NUMBER "2"; } } }"#, r#"[1,2,"# => r#"ROOT { PARSE_ERR: UnclosedArray { BRACKET_OPEN "["; ELEMENT { NUMBER "1"; } COMMA ","; ELEMENT { NUMBER "2"; } TRAILING_COMMA { COMMA ","; } } }"#, r#"[{"hello":"world""# => r#"ROOT { PARSE_ERR: UnclosedArray { BRACKET_OPEN "["; ELEMENT { PARSE_ERR: UnclosedObject { BRACE_OPEN "{"; MEMBER { MEMBER_NAME { STRING "\"hello\""; } COLON ":"; MEMBER_VALUE { STRING "\"world\""; } } } } } }"# } } }