cleanup unexected token handling

This commit is contained in:
Robin Appelman 2020-12-01 22:06:53 +01:00
commit 4c4cbefcf5
3 changed files with 73 additions and 48 deletions

View file

@ -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)
} }
} }
} }

View file

@ -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,
})
}
} }
} }

View file

@ -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###"