handle null

This commit is contained in:
Robin Appelman 2020-12-02 23:23:01 +01:00
commit 27b1552c94
4 changed files with 33 additions and 30 deletions

View file

@ -13,6 +13,7 @@ fn main() {
"array" => [1,2,3,4],
"bool" => false,
"negative" => -1,
"null" => null,
)
"###;

View file

@ -6,6 +6,8 @@ pub enum Token {
Array,
#[regex("true|false")]
Bool,
#[regex("null")]
Null,
#[token("=>")]
Arrow,
#[token("(")]
@ -43,6 +45,7 @@ fn test_lex() {
"array" => [1,2,3,4],
"bool" => false,
"negative" => -1,
"null" => null,
)
"###;
let mut lex = Token::lexer(source);
@ -105,6 +108,11 @@ fn test_lex() {
assert_eq!(lex.next(), Some(Token::Integer));
assert_eq!(lex.next(), Some(Token::Comma));
assert_eq!(lex.next(), Some(Token::LiteralString));
assert_eq!(lex.next(), Some(Token::Arrow));
assert_eq!(lex.next(), Some(Token::Null));
assert_eq!(lex.next(), Some(Token::Comma));
assert_eq!(lex.next(), Some(Token::BracketClose));
assert_eq!(lex.next(), None);

View file

@ -18,13 +18,13 @@
//! # }
//! ```
//!
mod ast;
mod error;
mod lexer;
mod parser;
mod string;
pub use ast::parse;
pub use error::{ParseError, SpannedError};
pub use parser::parse;
use std::borrow::Borrow;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
@ -37,6 +37,7 @@ pub enum Value {
Float(f64),
String(String),
Array(HashMap<Key, Value>),
Null,
}
/// A php value, can be either a bool, int, float, string or array
@ -46,7 +47,8 @@ pub enum Value {
///
/// ## Indexing
///
/// If the value is a php array, you can directly index into the `Value`, this will panic if the `Value` is not an array
/// If the value is a php array, you can directly index into the `Value`, this will null if the `Value` is not an array
/// or the key is not found
///
/// ```rust
/// # use maplit::hashmap;
@ -59,6 +61,7 @@ pub enum Value {
/// });
/// assert_eq!(value["key"], "value");
/// assert_eq!(value[10], false);
/// assert!(value["not"]["found"].is_null());
/// # }
/// ```
impl Value {
@ -87,6 +90,11 @@ impl Value {
matches!(self, Value::Array(_))
}
/// Check if the value is null
pub fn is_null(&self) -> bool {
matches!(self, Value::Null)
}
/// Convert the value into a bool if it is one
pub fn into_bool(self) -> Option<bool> {
match self {
@ -282,8 +290,8 @@ where
fn index(&self, index: &Q) -> &Self::Output {
match self {
Value::Array(map) => map.index(index),
_ => panic!("index into non array value"),
Value::Array(map) => map.get(index).unwrap_or(&Value::Null),
_ => &Value::Null,
}
}
}
@ -293,8 +301,8 @@ impl Index<Key> for Value {
fn index(&self, index: Key) -> &Self::Output {
match self {
Value::Array(map) => map.index(&index),
_ => panic!("index into non array value"),
Value::Array(map) => map.get(&index).unwrap_or(&Value::Null),
_ => &Value::Null,
}
}
}
@ -305,7 +313,7 @@ impl Index<i64> for Value {
fn index(&self, index: i64) -> &Self::Output {
match self {
Value::Array(map) => map.index(&Key::Int(index)),
_ => panic!("index into non array value"),
_ => &Value::Null,
}
}
}

View file

@ -40,17 +40,19 @@ pub fn parse_lexer<'source>(
Token::Integer,
Token::Float,
Token::LiteralString,
Token::Null,
Token::Array,
Token::SquareOpen,
])
.with_span(lexer.span(), source)?;
let value = match token {
Token::Bool => parse_literal(token, lexer.slice()).with_span(lexer.span(), source)?,
Token::Integer => parse_literal(token, lexer.slice()).with_span(lexer.span(), source)?,
Token::Float => parse_literal(token, lexer.slice()).with_span(lexer.span(), source)?,
Token::Bool => Value::Bool(lexer.slice().parse().with_span(lexer.span(), source)?),
Token::Integer => Value::Int(lexer.slice().parse().with_span(lexer.span(), source)?),
Token::Float => Value::Float(lexer.slice().parse().with_span(lexer.span(), source)?),
Token::LiteralString => {
parse_literal(token, lexer.slice()).with_span(lexer.span(), source)?
Value::String(parse_string(lexer.slice()).with_span(lexer.span(), source)?)
}
Token::Null => Value::Null,
Token::Array => Value::Array(parse_array(source, lexer, ArraySyntax::Long)?),
Token::SquareOpen => Value::Array(parse_array(source, lexer, ArraySyntax::Short)?),
_ => unreachable!(),
@ -59,22 +61,6 @@ pub fn parse_lexer<'source>(
Ok(value)
}
fn parse_literal(token: Token, slice: &str) -> Result<Value, ParseError> {
let token = token.expect_token(&[
Token::Bool,
Token::Integer,
Token::Float,
Token::LiteralString,
])?;
match token {
Token::Bool => Ok(Value::Bool(slice.parse()?)),
Token::Integer => Ok(Value::Int(slice.parse()?)),
Token::Float => Ok(Value::Float(slice.parse()?)),
Token::LiteralString => Ok(Value::String(parse_string(slice)?)),
_ => unreachable!(),
}
}
fn parse_string(literal: &str) -> Result<String, UnescapeError> {
let single_quote = literal.bytes().next().unwrap() == b'\'';
let inner = &literal[1..(literal.len()) - 1];
@ -273,9 +259,9 @@ fn test_parse() {
Value::Array(hashmap! {
Key::String("foo".into()) => Value::Bool(true),
Key::String("nested".into()) => Value::Array(hashmap! {
Key::String("foo".into()) => Value::Bool(false),
Key::String("foo".into()) => Value::Null,
}),
}),
parse(r#"["foo" => true, "nested" => ['foo' => false]]"#).unwrap()
parse(r#"["foo" => true, "nested" => ['foo' => null]]"#).unwrap()
);
}