handle empty arrays and trailing commas

This commit is contained in:
Robin Appelman 2020-12-01 22:20:28 +01:00
commit 36b75790f0
4 changed files with 51 additions and 21 deletions

23
examples/parse.rs Normal file
View file

@ -0,0 +1,23 @@
use php_object_parser::parse;
fn main() {
let source = r###"
array (
"double" => "quote",
'single' => 'quote',
"escaped" => "\"quote\"",
1 => 2,
"nested" => [
"sub" => "key",
],
"array" => [1,2,3,4],
"bool" => false,
"negative" => -1,
)
"###;
match parse(source) {
Ok(result) => print!("{:#?}", result),
Err(err) => eprint!("{}", err),
}
}

View file

@ -1,3 +1,4 @@
use crate::error::UnexpectedTokenError;
use crate::error::{ExpectToken, InvalidArrayKeyError, ParseError, ResultExt, SpannedError}; use crate::error::{ExpectToken, InvalidArrayKeyError, ParseError, ResultExt, SpannedError};
use crate::lexer::Token; use crate::lexer::Token;
use crate::string::{unescape_double, unescape_single, UnescapeError}; use crate::string::{unescape_double, unescape_single, UnescapeError};
@ -124,7 +125,20 @@ fn parse_array<'source>(
} }
loop { loop {
let key_or_value = parse_lexer(source, lexer)?; let key_or_value = match parse_lexer(source, lexer) {
Ok(value) => value,
Err(err) => {
// trailing comma or empty array
if matches!(
err.error(),
ParseError::UnexpectedToken(UnexpectedTokenError { found: None, .. })
) {
break;
} else {
return Err(err);
}
}
};
let key_or_value_span = lexer.span(); let key_or_value_span = lexer.span();
let next = lexer let next = lexer
.next() .next()
@ -202,6 +216,7 @@ fn test_parse() {
Value::String("test".to_string()), Value::String("test".to_string()),
parse(r#""test""#).unwrap() parse(r#""test""#).unwrap()
); );
assert_eq!(Value::Array(hashmap! {}), parse(r#"array()"#).unwrap());
assert_eq!( assert_eq!(
Value::Array(hashmap! { Value::Array(hashmap! {
Key::Int(0) => Value::Int(3), Key::Int(0) => Value::Int(3),
@ -210,6 +225,14 @@ fn test_parse() {
}), }),
parse(r#"array(3,4,5)"#).unwrap() parse(r#"array(3,4,5)"#).unwrap()
); );
assert_eq!(
Value::Array(hashmap! {
Key::Int(0) => Value::Int(3),
Key::Int(1) => Value::Int(4),
Key::Int(2) => Value::Int(5),
}),
parse(r#"array(3,4,5,)"#).unwrap()
);
assert_eq!( assert_eq!(
Value::Array(hashmap! { Value::Array(hashmap! {
Key::Int(1) => Value::Int(3), Key::Int(1) => Value::Int(3),

View file

@ -27,6 +27,10 @@ impl<'a, T: Error + Debug> SpannedError<'a, T> {
error, error,
} }
} }
pub fn error(&self) -> &T {
&self.error
}
} }
const METRICS: DefaultMetrics = DefaultMetrics::with_tab_stop(4); const METRICS: DefaultMetrics = DefaultMetrics::with_tab_stop(4);

View file

@ -1,5 +1,4 @@
use logos::Logos; use logos::Logos;
use std::fmt::{Display, Formatter};
#[derive(Logos, Debug, PartialEq, Clone)] #[derive(Logos, Debug, PartialEq, Clone)]
pub enum Token { pub enum Token {
@ -30,25 +29,6 @@ pub enum Token {
Error, Error,
} }
impl Display for Token {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Token::Array => write!(f, "array"),
Token::Bool => write!(f, "bool"),
Token::Arrow => write!(f, "arrow"),
Token::BracketOpen => write!(f, "bracket open"),
Token::BracketClose => write!(f, "bracket close"),
Token::SquareOpen => write!(f, "square bracket open"),
Token::SquareClose => write!(f, "square bracket close"),
Token::Comma => write!(f, "comma"),
Token::LiteralString => write!(f, "string literal"),
Token::Float => write!(f, "float literal"),
Token::Integer => write!(f, "integer literal"),
Token::Error => write!(f, "invalid token"),
}
}
}
#[test] #[test]
fn test_lex() { fn test_lex() {
let source = r###" let source = r###"