accept more array keys

This commit is contained in:
Robin Appelman 2020-12-13 17:59:53 +01:00
commit 2352ed9fd1
3 changed files with 66 additions and 35 deletions

View file

@ -1,7 +1,6 @@
use crate::lexer::{SpannedToken, Token}; use crate::lexer::{SpannedToken, Token};
use crate::num::ParseIntError; use crate::num::ParseIntError;
use crate::string::UnescapeError; use crate::string::UnescapeError;
use crate::Value;
use logos::Span; use logos::Span;
use source_span::{ use source_span::{
fmt::{Color, Formatter, Style}, fmt::{Color, Formatter, Style},
@ -101,8 +100,6 @@ fn get_position(text: &str, index: usize) -> Position {
pub enum ParseError { pub enum ParseError {
#[error("{0}")] #[error("{0}")]
UnexpectedToken(#[from] UnexpectedTokenError), UnexpectedToken(#[from] UnexpectedTokenError),
#[error("{0}")]
InvalidArrayKey(#[from] InvalidArrayKeyError),
#[error("Invalid boolean literal: {0}")] #[error("Invalid boolean literal: {0}")]
InvalidBoolLiteral(#[from] ParseBoolError), InvalidBoolLiteral(#[from] ParseBoolError),
#[error("Invalid integer literal: {0}")] #[error("Invalid integer literal: {0}")]
@ -158,10 +155,6 @@ impl Display for UnexpectedTokenError {
impl Error for UnexpectedTokenError {} impl Error for UnexpectedTokenError {}
#[derive(Error, Debug)]
#[error("Invalid array key {0:?} expected number or string")]
pub struct InvalidArrayKeyError(pub Value);
pub trait ExpectToken<'source> { pub trait ExpectToken<'source> {
fn expect_token( fn expect_token(
self, self,

View file

@ -1,10 +1,9 @@
use crate::error::UnexpectedTokenError; use crate::error::{ExpectToken, ParseError, ResultExt, SpannedError};
use crate::error::{ExpectToken, InvalidArrayKeyError, ParseError, ResultExt, SpannedError};
use crate::lexer::{SpannedToken, Token, TokenStream}; use crate::lexer::{SpannedToken, Token, TokenStream};
use crate::num::parse_int; use crate::num::parse_int;
use crate::string::parse_string; use crate::string::{is_array_key_numeric, parse_string};
use crate::{Key, Value}; use crate::{Key, Value};
use logos::{Lexer, Logos}; use logos::Logos;
use std::collections::HashMap; use std::collections::HashMap;
use std::num::ParseFloatError; use std::num::ParseFloatError;
@ -27,7 +26,7 @@ use std::num::ParseFloatError;
/// ``` /// ```
/// ///
pub fn parse(source: &str) -> Result<Value, SpannedError<ParseError>> { pub fn parse(source: &str) -> Result<Value, SpannedError<ParseError>> {
Parser::new(source).parse_any() Parser::new(source).run()
} }
pub struct Parser<'source> { pub struct Parser<'source> {
@ -43,7 +42,7 @@ impl<'source> Parser<'source> {
} }
} }
pub fn parse_any(&mut self) -> Result<Value, SpannedError<ParseError>> { pub fn run(&mut self) -> Result<Value, SpannedError<ParseError>> {
let token = self.tokens.next().expect_token(&[ let token = self.tokens.next().expect_token(&[
Token::Bool, Token::Bool,
Token::Integer, Token::Integer,
@ -53,10 +52,10 @@ impl<'source> Parser<'source> {
Token::Array, Token::Array,
Token::SquareOpen, Token::SquareOpen,
])?; ])?;
self.parse_token(token) self.parse_any(token)
} }
pub fn parse_token(&mut self, token: SpannedToken) -> Result<Value, SpannedError<ParseError>> { pub fn parse_any(&mut self, token: SpannedToken) -> Result<Value, SpannedError<ParseError>> {
let value = match token.token { let value = match token.token {
Token::Bool => Value::Bool(self.parse_bool(token)?), Token::Bool => Value::Bool(self.parse_bool(token)?),
Token::Integer => Value::Int(self.parse_int(token)?), Token::Integer => Value::Int(self.parse_int(token)?),
@ -125,33 +124,19 @@ impl<'source> Parser<'source> {
match next.token { match next.token {
Token::BracketClose => { Token::BracketClose => {
builder.push_value(self.parse_token(key_value_or_close_token)?); builder.push_value(self.parse_any(key_value_or_close_token)?);
break; break;
} }
Token::SquareClose => { Token::SquareClose => {
builder.push_value(self.parse_token(key_value_or_close_token)?); builder.push_value(self.parse_any(key_value_or_close_token)?);
break; break;
} }
Token::Comma => { Token::Comma => {
builder.push_value(self.parse_token(key_value_or_close_token)?); builder.push_value(self.parse_any(key_value_or_close_token)?);
} }
Token::Arrow => { Token::Arrow => {
let key_token = key_value_or_close_token.expect_token(&[ let key = self.parse_array_key(key_value_or_close_token)?;
Token::Bool, let value = self.run()?;
Token::Integer,
Token::Float,
Token::LiteralString,
Token::Null,
])?;
let value = self.parse_any()?;
let key = match self.parse_token(key_token)? {
Value::Int(int) => Key::Int(int),
Value::Float(float) => Key::Int(float as i64),
Value::String(str) => Key::String(str),
Value::Bool(bool) => Key::Int(if bool { 1 } else { 0 }),
Value::Null => Key::String(String::from("")),
_ => unreachable!(),
};
builder.push_key_value(key, value); builder.push_key_value(key, value);
match self match self
@ -178,6 +163,28 @@ impl<'source> Parser<'source> {
Ok(builder.data) Ok(builder.data)
} }
pub fn parse_array_key(
&mut self,
token: SpannedToken,
) -> Result<Key, SpannedError<ParseError>> {
let token = token.expect_token(&[
Token::Bool,
Token::Integer,
Token::Float,
Token::LiteralString,
Token::Null,
])?;
Ok(match self.parse_any(token)? {
Value::Int(int) => Key::Int(int),
Value::Float(float) => Key::Int(float as i64),
Value::String(str) if is_array_key_numeric(&str) => Key::Int(parse_int(&str).unwrap()),
Value::String(str) => Key::String(str),
Value::Bool(bool) => Key::Int(if bool { 1 } else { 0 }),
Value::Null => Key::String(String::from("")),
_ => unreachable!(),
})
}
} }
fn parse_float(literal: &str) -> Result<f64, ParseFloatError> { fn parse_float(literal: &str) -> Result<f64, ParseFloatError> {
@ -207,7 +214,7 @@ impl ArrayBuilder {
} }
#[derive(Eq, PartialEq)] #[derive(Eq, PartialEq)]
enum ArraySyntax { pub enum ArraySyntax {
Short, Short,
Long, Long,
} }
@ -306,4 +313,15 @@ fn test_parse() {
assert_eq!(Value::Float(1000.0), parse(r#"10e2"#).unwrap()); assert_eq!(Value::Float(1000.0), parse(r#"10e2"#).unwrap());
assert_eq!(Value::Float(1.0), parse(r#"10e-1"#).unwrap()); assert_eq!(Value::Float(1.0), parse(r#"10e-1"#).unwrap());
assert_eq!(Value::Float(1234.5), parse(r#"12_34.5"#).unwrap()); assert_eq!(Value::Float(1234.5), parse(r#"12_34.5"#).unwrap());
assert_eq!(
Value::Array(hashmap! {
Key::Int(2) => Value::Int(3),
Key::String("foo".into()) => Value::Int(4),
Key::String("".into()) => Value::Int(5),
Key::Int(1) => Value::Int(6),
Key::Int(0) => Value::Int(7),
}),
parse(r#"array("2"=>3,"foo" => 4, null => 5, true => 6, false => 7)"#).unwrap()
);
} }

View file

@ -198,10 +198,30 @@ impl<'a> PeekableBytes<'a> {
} }
} }
pub fn is_array_key_numeric(string: &str) -> bool {
let mut bytes = string.bytes();
if !matches!((bytes.next(), string.len()), (Some(b'-'), _) | (Some(b'0'..=b'9'), 1) | (Some(b'1'..=b'9'), _))
{
return false;
}
bytes.all(|byte| matches!(byte, b'0'..=b'9'))
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
#[test]
fn test_is_numeric() {
assert_eq!(true, is_array_key_numeric("123"));
assert_eq!(true, is_array_key_numeric("-123"));
assert_eq!(true, is_array_key_numeric("0"));
assert_eq!(false, is_array_key_numeric("0123"));
assert_eq!(false, is_array_key_numeric("123asd"));
assert_eq!(false, is_array_key_numeric("+123"));
}
#[test] #[test]
fn test_unescape_single() { fn test_unescape_single() {
assert_eq!(unescape::<SingleQuoteString>(&r#"abc"#), Ok("abc".into())); assert_eq!(unescape::<SingleQuoteString>(&r#"abc"#), Ok("abc".into()));