use super::{PResult, Span};
use crate::input::SourcePos;
use nom::{character::complete::one_of, Finish};
use nom_language::error::{VerboseError, VerboseErrorKind};
use std::fmt;
#[derive(Debug, PartialEq, Eq)]
pub struct ParseError {
msg: String,
pos: SourcePos,
}
impl std::error::Error for ParseError {}
impl ParseError {
pub fn check<T>(res: PResult<T>) -> Result<T, Self> {
let (rest, value) = res.finish()?;
if rest.fragment().is_empty() {
Ok(value)
} else {
Err(Self::new("Expected end of file.", rest.to_owned()))
}
}
pub(crate) fn new<Msg>(msg: Msg, pos: SourcePos) -> Self
where
Msg: Into<String>,
{
Self {
msg: msg.into(),
pos,
}
}
}
impl From<VerboseError<Span<'_>>> for ParseError {
fn from(value: VerboseError<Span<'_>>) -> Self {
let (msg, pos) = value
.errors
.iter()
.filter_map(|(pos, kind)| {
match kind {
VerboseErrorKind::Context(ctx) => {
Some((ctx.to_string(), pos))
}
VerboseErrorKind::Char(ch) if *ch == '\'' => {
Some((format!("Expected {ch}."), pos))
}
VerboseErrorKind::Char(ch) => {
Some((format!("expected {:?}.", ch.to_string()), pos))
}
VerboseErrorKind::Nom(_) => None, }
})
.next()
.or_else(|| {
value.errors.first().map(|(pos, _)| {
if pos.is_at_end() {
("expected more input.".to_string(), pos)
} else if let PResult::Ok((_, b)) = one_of(")}]")(*pos) {
(format!("unmatched \"{b}\"."), pos)
} else {
("Parse error.".to_string(), pos)
}
})
})
.unwrap();
Self::new(msg, pos.sanitize_end().to_owned())
}
}
impl fmt::Display for ParseError {
fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
writeln!(out, "{}", self.msg)?;
self.pos.show(out)
}
}