mirror of
https://codeberg.org/icewind/php-literal-parser.git
synced 2026-06-03 10:34:08 +02:00
handle null
This commit is contained in:
parent
6975a89b63
commit
27b1552c94
4 changed files with 33 additions and 30 deletions
|
|
@ -13,6 +13,7 @@ fn main() {
|
||||||
"array" => [1,2,3,4],
|
"array" => [1,2,3,4],
|
||||||
"bool" => false,
|
"bool" => false,
|
||||||
"negative" => -1,
|
"negative" => -1,
|
||||||
|
"null" => null,
|
||||||
)
|
)
|
||||||
"###;
|
"###;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ pub enum Token {
|
||||||
Array,
|
Array,
|
||||||
#[regex("true|false")]
|
#[regex("true|false")]
|
||||||
Bool,
|
Bool,
|
||||||
|
#[regex("null")]
|
||||||
|
Null,
|
||||||
#[token("=>")]
|
#[token("=>")]
|
||||||
Arrow,
|
Arrow,
|
||||||
#[token("(")]
|
#[token("(")]
|
||||||
|
|
@ -43,6 +45,7 @@ fn test_lex() {
|
||||||
"array" => [1,2,3,4],
|
"array" => [1,2,3,4],
|
||||||
"bool" => false,
|
"bool" => false,
|
||||||
"negative" => -1,
|
"negative" => -1,
|
||||||
|
"null" => null,
|
||||||
)
|
)
|
||||||
"###;
|
"###;
|
||||||
let mut lex = Token::lexer(source);
|
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::Integer));
|
||||||
assert_eq!(lex.next(), Some(Token::Comma));
|
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(), Some(Token::BracketClose));
|
||||||
|
|
||||||
assert_eq!(lex.next(), None);
|
assert_eq!(lex.next(), None);
|
||||||
|
|
|
||||||
24
src/lib.rs
24
src/lib.rs
|
|
@ -18,13 +18,13 @@
|
||||||
//! # }
|
//! # }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
mod ast;
|
|
||||||
mod error;
|
mod error;
|
||||||
mod lexer;
|
mod lexer;
|
||||||
|
mod parser;
|
||||||
mod string;
|
mod string;
|
||||||
|
|
||||||
pub use ast::parse;
|
|
||||||
pub use error::{ParseError, SpannedError};
|
pub use error::{ParseError, SpannedError};
|
||||||
|
pub use parser::parse;
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
@ -37,6 +37,7 @@ pub enum Value {
|
||||||
Float(f64),
|
Float(f64),
|
||||||
String(String),
|
String(String),
|
||||||
Array(HashMap<Key, Value>),
|
Array(HashMap<Key, Value>),
|
||||||
|
Null,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A php value, can be either a bool, int, float, string or array
|
/// A php value, can be either a bool, int, float, string or array
|
||||||
|
|
@ -46,7 +47,8 @@ pub enum Value {
|
||||||
///
|
///
|
||||||
/// ## Indexing
|
/// ## 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
|
/// ```rust
|
||||||
/// # use maplit::hashmap;
|
/// # use maplit::hashmap;
|
||||||
|
|
@ -59,6 +61,7 @@ pub enum Value {
|
||||||
/// });
|
/// });
|
||||||
/// assert_eq!(value["key"], "value");
|
/// assert_eq!(value["key"], "value");
|
||||||
/// assert_eq!(value[10], false);
|
/// assert_eq!(value[10], false);
|
||||||
|
/// assert!(value["not"]["found"].is_null());
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
impl Value {
|
impl Value {
|
||||||
|
|
@ -87,6 +90,11 @@ impl Value {
|
||||||
matches!(self, Value::Array(_))
|
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
|
/// Convert the value into a bool if it is one
|
||||||
pub fn into_bool(self) -> Option<bool> {
|
pub fn into_bool(self) -> Option<bool> {
|
||||||
match self {
|
match self {
|
||||||
|
|
@ -282,8 +290,8 @@ where
|
||||||
|
|
||||||
fn index(&self, index: &Q) -> &Self::Output {
|
fn index(&self, index: &Q) -> &Self::Output {
|
||||||
match self {
|
match self {
|
||||||
Value::Array(map) => map.index(index),
|
Value::Array(map) => map.get(index).unwrap_or(&Value::Null),
|
||||||
_ => panic!("index into non array value"),
|
_ => &Value::Null,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -293,8 +301,8 @@ impl Index<Key> for Value {
|
||||||
|
|
||||||
fn index(&self, index: Key) -> &Self::Output {
|
fn index(&self, index: Key) -> &Self::Output {
|
||||||
match self {
|
match self {
|
||||||
Value::Array(map) => map.index(&index),
|
Value::Array(map) => map.get(&index).unwrap_or(&Value::Null),
|
||||||
_ => panic!("index into non array value"),
|
_ => &Value::Null,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -305,7 +313,7 @@ impl Index<i64> for Value {
|
||||||
fn index(&self, index: i64) -> &Self::Output {
|
fn index(&self, index: i64) -> &Self::Output {
|
||||||
match self {
|
match self {
|
||||||
Value::Array(map) => map.index(&Key::Int(index)),
|
Value::Array(map) => map.index(&Key::Int(index)),
|
||||||
_ => panic!("index into non array value"),
|
_ => &Value::Null,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,17 +40,19 @@ pub fn parse_lexer<'source>(
|
||||||
Token::Integer,
|
Token::Integer,
|
||||||
Token::Float,
|
Token::Float,
|
||||||
Token::LiteralString,
|
Token::LiteralString,
|
||||||
|
Token::Null,
|
||||||
Token::Array,
|
Token::Array,
|
||||||
Token::SquareOpen,
|
Token::SquareOpen,
|
||||||
])
|
])
|
||||||
.with_span(lexer.span(), source)?;
|
.with_span(lexer.span(), source)?;
|
||||||
let value = match token {
|
let value = match token {
|
||||||
Token::Bool => parse_literal(token, lexer.slice()).with_span(lexer.span(), source)?,
|
Token::Bool => Value::Bool(lexer.slice().parse().with_span(lexer.span(), source)?),
|
||||||
Token::Integer => parse_literal(token, lexer.slice()).with_span(lexer.span(), source)?,
|
Token::Integer => Value::Int(lexer.slice().parse().with_span(lexer.span(), source)?),
|
||||||
Token::Float => parse_literal(token, lexer.slice()).with_span(lexer.span(), source)?,
|
Token::Float => Value::Float(lexer.slice().parse().with_span(lexer.span(), source)?),
|
||||||
Token::LiteralString => {
|
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::Array => Value::Array(parse_array(source, lexer, ArraySyntax::Long)?),
|
||||||
Token::SquareOpen => Value::Array(parse_array(source, lexer, ArraySyntax::Short)?),
|
Token::SquareOpen => Value::Array(parse_array(source, lexer, ArraySyntax::Short)?),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
|
|
@ -59,22 +61,6 @@ pub fn parse_lexer<'source>(
|
||||||
Ok(value)
|
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> {
|
fn parse_string(literal: &str) -> Result<String, UnescapeError> {
|
||||||
let single_quote = literal.bytes().next().unwrap() == b'\'';
|
let single_quote = literal.bytes().next().unwrap() == b'\'';
|
||||||
let inner = &literal[1..(literal.len()) - 1];
|
let inner = &literal[1..(literal.len()) - 1];
|
||||||
|
|
@ -273,9 +259,9 @@ fn test_parse() {
|
||||||
Value::Array(hashmap! {
|
Value::Array(hashmap! {
|
||||||
Key::String("foo".into()) => Value::Bool(true),
|
Key::String("foo".into()) => Value::Bool(true),
|
||||||
Key::String("nested".into()) => Value::Array(hashmap! {
|
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()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue