mirror of
https://codeberg.org/icewind/php-literal-parser.git
synced 2026-06-03 18:44:07 +02:00
cleanup unexected token handling
This commit is contained in:
parent
1f64e8af89
commit
4c4cbefcf5
3 changed files with 73 additions and 48 deletions
77
src/ast.rs
77
src/ast.rs
|
|
@ -1,6 +1,4 @@
|
||||||
use crate::error::{
|
use crate::error::{ExpectToken, InvalidArrayKeyError, ParseError, ResultExt, SpannedError};
|
||||||
ExpectToken, InvalidArrayKeyError, ParseError, ResultExt, SpannedError, UnexpectedTokenError,
|
|
||||||
};
|
|
||||||
use crate::lexer::Token;
|
use crate::lexer::Token;
|
||||||
use crate::string::{unescape_double, unescape_single, UnescapeError};
|
use crate::string::{unescape_double, unescape_single, UnescapeError};
|
||||||
use logos::{Lexer, Logos};
|
use logos::{Lexer, Logos};
|
||||||
|
|
@ -33,16 +31,15 @@ pub fn parse_lexer<'source>(
|
||||||
) -> Result<Value, SpannedError<'source, ParseError>> {
|
) -> Result<Value, SpannedError<'source, ParseError>> {
|
||||||
let token = lexer
|
let token = lexer
|
||||||
.next()
|
.next()
|
||||||
.expect_token("bool, int, float, string, array start")
|
.expect_token(&[
|
||||||
|
Token::Bool,
|
||||||
|
Token::Integer,
|
||||||
|
Token::Float,
|
||||||
|
Token::LiteralString,
|
||||||
|
Token::Array,
|
||||||
|
Token::SquareOpen,
|
||||||
|
])
|
||||||
.with_span(lexer.span(), source)?;
|
.with_span(lexer.span(), source)?;
|
||||||
parse_token(token, source, lexer)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_token<'source>(
|
|
||||||
token: Token,
|
|
||||||
source: &'source str,
|
|
||||||
lexer: &mut Lexer<Token>,
|
|
||||||
) -> Result<Value, SpannedError<'source, ParseError>> {
|
|
||||||
let value = match token {
|
let value = match token {
|
||||||
Token::Bool => parse_literal(token, lexer.slice()).with_span(lexer.span(), source)?,
|
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::Integer => parse_literal(token, lexer.slice()).with_span(lexer.span(), source)?,
|
||||||
|
|
@ -52,22 +49,25 @@ pub fn parse_token<'source>(
|
||||||
}
|
}
|
||||||
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)?),
|
||||||
_ => todo!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(value)
|
Ok(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_literal(token: Token, slice: &str) -> Result<Value, ParseError> {
|
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 {
|
match token {
|
||||||
Token::Bool => Ok(Value::Bool(slice.parse()?)),
|
Token::Bool => Ok(Value::Bool(slice.parse()?)),
|
||||||
Token::Integer => Ok(Value::Int(slice.parse()?)),
|
Token::Integer => Ok(Value::Int(slice.parse()?)),
|
||||||
Token::Float => Ok(Value::Float(slice.parse()?)),
|
Token::Float => Ok(Value::Float(slice.parse()?)),
|
||||||
Token::LiteralString => Ok(Value::String(parse_string(slice)?)),
|
Token::LiteralString => Ok(Value::String(parse_string(slice)?)),
|
||||||
token => Err(ParseError::UnexpectedToken(UnexpectedTokenError::new(
|
_ => unreachable!(),
|
||||||
"bool, int, float, string, array start",
|
|
||||||
Some(token),
|
|
||||||
))),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -117,17 +117,10 @@ fn parse_array<'source>(
|
||||||
let mut builder = ArrayBuilder::default();
|
let mut builder = ArrayBuilder::default();
|
||||||
|
|
||||||
if syntax == ArraySyntax::Long {
|
if syntax == ArraySyntax::Long {
|
||||||
let open = lexer
|
lexer
|
||||||
.next()
|
.next()
|
||||||
.expect_token("open bracket")
|
.expect_token(&[Token::BracketOpen])
|
||||||
.with_span(lexer.span(), source)?;
|
.with_span(lexer.span(), source)?;
|
||||||
if !matches!(open, Token::BracketOpen) {
|
|
||||||
return Err(ParseError::UnexpectedToken(UnexpectedTokenError::new(
|
|
||||||
"open bracket",
|
|
||||||
Some(open),
|
|
||||||
)))
|
|
||||||
.with_span(lexer.span(), source);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
|
@ -135,7 +128,11 @@ fn parse_array<'source>(
|
||||||
let key_or_value_span = lexer.span();
|
let key_or_value_span = lexer.span();
|
||||||
let next = lexer
|
let next = lexer
|
||||||
.next()
|
.next()
|
||||||
.expect_token("close bracket, comma, arrow")
|
.expect_token(if syntax == ArraySyntax::Long {
|
||||||
|
&[Token::BracketClose, Token::Comma, Token::Arrow]
|
||||||
|
} else {
|
||||||
|
&[Token::SquareClose, Token::Comma, Token::Arrow]
|
||||||
|
})
|
||||||
.with_span(lexer.span(), source)?;
|
.with_span(lexer.span(), source)?;
|
||||||
|
|
||||||
match next {
|
match next {
|
||||||
|
|
@ -166,31 +163,25 @@ fn parse_array<'source>(
|
||||||
|
|
||||||
match lexer
|
match lexer
|
||||||
.next()
|
.next()
|
||||||
.expect_token("close bracket, comma, arrow")
|
.expect_token(if syntax == ArraySyntax::Long {
|
||||||
|
&[Token::BracketClose, Token::Comma]
|
||||||
|
} else {
|
||||||
|
&[Token::SquareClose, Token::Comma]
|
||||||
|
})
|
||||||
.with_span(lexer.span(), source)?
|
.with_span(lexer.span(), source)?
|
||||||
{
|
{
|
||||||
Token::BracketClose if syntax == ArraySyntax::Long => {
|
Token::BracketClose => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Token::SquareClose if syntax == ArraySyntax::Short => {
|
Token::SquareClose => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Token::Comma => {}
|
Token::Comma => {}
|
||||||
token => {
|
_ => unreachable!(),
|
||||||
return Err(ParseError::UnexpectedToken(UnexpectedTokenError::new(
|
|
||||||
"close bracket, comma, arrow",
|
|
||||||
Some(token),
|
|
||||||
)))
|
|
||||||
.with_span(lexer.span(), source)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
token => {
|
_ => {
|
||||||
return Err(ParseError::UnexpectedToken(UnexpectedTokenError::new(
|
unreachable!();
|
||||||
"close bracket, comma, arrow",
|
|
||||||
Some(token),
|
|
||||||
)))
|
|
||||||
.with_span(lexer.span(), source)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
24
src/error.rs
24
src/error.rs
|
|
@ -92,14 +92,14 @@ impl From<UnescapeError> for ParseError {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
#[error("Unexpected token, found {found:?} expected one of {expected}")]
|
#[error("Unexpected token, found {found:?} expected one of {expected:?}")]
|
||||||
pub struct UnexpectedTokenError {
|
pub struct UnexpectedTokenError {
|
||||||
expected: &'static str,
|
pub expected: &'static [Token],
|
||||||
pub found: Option<Token>,
|
pub found: Option<Token>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UnexpectedTokenError {
|
impl UnexpectedTokenError {
|
||||||
pub fn new(expected: &'static str, found: Option<Token>) -> Self {
|
pub fn new(expected: &'static [Token], found: Option<Token>) -> Self {
|
||||||
UnexpectedTokenError { expected, found }
|
UnexpectedTokenError { expected, found }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -109,15 +109,29 @@ impl UnexpectedTokenError {
|
||||||
pub struct InvalidArrayKeyError(pub Value);
|
pub struct InvalidArrayKeyError(pub Value);
|
||||||
|
|
||||||
pub trait ExpectToken {
|
pub trait ExpectToken {
|
||||||
fn expect_token(self, expected: &'static str) -> Result<Token, UnexpectedTokenError>;
|
fn expect_token(self, expected: &'static [Token]) -> Result<Token, UnexpectedTokenError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExpectToken for Option<Token> {
|
impl ExpectToken for Option<Token> {
|
||||||
fn expect_token(self, expected: &'static str) -> Result<Token, UnexpectedTokenError> {
|
fn expect_token(self, expected: &'static [Token]) -> Result<Token, UnexpectedTokenError> {
|
||||||
self.ok_or_else(|| UnexpectedTokenError {
|
self.ok_or_else(|| UnexpectedTokenError {
|
||||||
expected,
|
expected,
|
||||||
found: None,
|
found: None,
|
||||||
})
|
})
|
||||||
|
.and_then(|token| token.expect_token(expected))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExpectToken for Token {
|
||||||
|
fn expect_token(self, expected: &'static [Token]) -> Result<Token, UnexpectedTokenError> {
|
||||||
|
if expected.iter().any(|expect| self.eq(expect)) {
|
||||||
|
Ok(self)
|
||||||
|
} else {
|
||||||
|
Err(UnexpectedTokenError {
|
||||||
|
expected,
|
||||||
|
found: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
20
src/lexer.rs
20
src/lexer.rs
|
|
@ -1,4 +1,5 @@
|
||||||
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 {
|
||||||
|
|
@ -29,6 +30,25 @@ 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###"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue