use {
super::lexer::Error as LexError,
crate::syntax::{
Directive,
lexer::{Lexer, Token},
},
alloc::{
borrow::{Cow, ToOwned},
string::String,
},
core::error::Error as ErrorTrait,
derive_more::{Display, Error},
descape::InvalidEscape,
logos::Span,
};
pub struct Parser<'lexer, 'src> {
pub lexer: &'lexer mut Lexer<'src>,
}
impl<'lexer, 'src> Parser<'lexer, 'src>
where
'src: 'lexer,
{
pub fn run(mut self) -> Result<Option<Directive<'src>>, Error> {
let keyword = loop {
match self.lexer.next() {
Some(Ok(Token::LineBreak)) => (),
Some(Ok(Token::UnquotedString(string))) => break string,
Some(Ok(Token::QuotedString(_))) => {
return Err(self.span_error(ErrorKind::QuotedKeyword));
}
Some(Err(e)) => return Err(self.span_error(ErrorKind::from_lex(e, self.lexer))),
None => return Ok(None),
}
};
match keyword {
"package" => self.package(),
"config" => self.config(),
"option" => self.option(),
"list" => self.list(),
_ => Err(self.span_error(ErrorKind::UnexpectedToken {
expected: "package, config, option, list",
})),
}
.map(Some)
}
fn package(&mut self) -> Result<Directive<'src>, Error> {
let name = self.next()?.into_cow_str();
Ok(Directive::Package(name))
}
fn config(&mut self) -> Result<Directive<'src>, Error> {
let type_ = self.next()?.into_cow_str();
let name = self.maybe_next_not_nl_string()?;
Ok(Directive::Section { type_, name })
}
fn option(&mut self) -> Result<Directive<'src>, Error> {
let key = self.next()?.into_cow_str();
let value = self.maybe_next_not_nl_string()?;
Ok(Directive::Option { key, value })
}
fn list(&mut self) -> Result<Directive<'src>, Error> {
let key = self.next()?.into_cow_str();
let value = self.next()?.into_cow_str();
Ok(Directive::List { key, value })
}
fn next(&mut self) -> Result<Token<'src>, Error> {
match self.lexer.next() {
Some(Ok(token)) => Ok(token),
Some(Err(e)) => Err(ErrorKind::from_lex(e, self.lexer)),
None => Err(ErrorKind::UnexpectedEnd),
}
.map_err(|kind| self.span_error(kind))
}
fn maybe_next_not_nl_string(&mut self) -> Result<Option<Cow<'src, str>>, Error> {
match self.lexer.next() {
Some(Ok(Token::LineBreak)) | None => Ok(None),
Some(Ok(token)) => Ok(Some(token.into_cow_str())),
Some(Err(e)) => Err(self.span_error(ErrorKind::from_lex(e, self.lexer))),
}
}
fn span_error(&self, kind: ErrorKind) -> Error {
Error {
kind,
at: self.lexer.span(),
}
}
}
#[derive(Clone, Debug, Display, PartialEq, Eq, Error)]
pub enum ErrorKind {
#[display("encountered invalid string escape: {_0}")]
Unescape(InvalidEscape),
#[display("invalid token: \"{_0}\"")]
InvalidToken(#[error(not(source))] String),
#[display("unexpected end of input")]
UnexpectedEnd,
#[display("unexpected token, expected: {expected}")]
UnexpectedToken {
expected: &'static str,
},
#[display("keywords can't be quoted")]
QuotedKeyword,
}
impl ErrorKind {
fn from_lex(error: LexError, lexer: &Lexer) -> Self {
match error {
LexError::UnexpectedEndOfPattern => Self::UnexpectedEnd,
LexError::InvalidToken => Self::InvalidToken(lexer.slice().to_owned()),
LexError::Escape(error) => Self::Unescape(error),
}
}
}
#[derive(Clone, Debug, Display, PartialEq, Eq)]
#[display("parse error: {kind} at {at:?}")]
pub struct Error {
pub kind: ErrorKind,
pub at: Span,
}
impl ErrorTrait for Error {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
ErrorTrait::source(&self.kind)
}
}