use super::tokens::{TokenKind, TokenSpan}; use super::{IErr, Span}; use ariadne::{Label, Report, ReportKind}; use nom::error::{ContextError, ParseError}; use nom::InputTake; #[derive(Debug)] pub struct Error { pub errors: Vec<(I, ErrorKind)>, } #[derive(Debug, Clone)] pub enum ErrorKind { /// A static context string Context(&'static str), /// An expected character Char(char), /// An expected token Token(TokenKind), /// A list of valid tokens Tokens(Vec), /// A nom error Nom(nom::error::ErrorKind), } impl ParseError for Error where I: InputPos + std::fmt::Debug + Clone, { fn from_error_kind(input: I, kind: nom::error::ErrorKind) -> Self { Self { errors: vec![(input, ErrorKind::Nom(kind))], } } fn append(input: I, kind: nom::error::ErrorKind, mut other: Self) -> Self { other.errors.push((input, ErrorKind::Nom(kind))); other } fn from_char(input: I, c: char) -> Self { Self { errors: vec![(input, ErrorKind::Char(c))], } } fn or(mut self, mut other: Self) -> Self { let self_first = self.errors.first(); let other_first = other.errors.first(); match (self_first, other_first) { (Some((is, ErrorKind::Token(s))), Some((io, ErrorKind::Token(o)))) => { let combined_kind = ErrorKind::Tokens(vec![s.clone(), o.clone()]); other.errors[0] = (io.clone(), combined_kind); other } _ => { other.errors.extend_from_slice(&self.errors[..]); other } } } } impl ContextError for Error { fn add_context(input: I, ctx: &'static str, mut other: Self) -> Self { other.errors.push((input, ErrorKind::Context(ctx))); other } } impl Error { pub fn from_tokenkind(input: I, kind: TokenKind) -> Self { Self { errors: vec![(input, ErrorKind::Token(kind))], } } } pub trait InputPos { fn position(&self) -> usize; } impl InputPos for Span<'_> { fn position(&self) -> usize { self.location_offset() } } fn tokspan_to_range(input: TokenSpan) -> std::ops::Range { let first = input.first().unwrap().span().position(); let last_span = input.last().unwrap().span(); let last = last_span.position() + last_span.len(); first..last } fn label_tokspan(input: TokenSpan) -> Label { Label::new(tokspan_to_range(input)) } pub fn convert_error(_input: Span, e: IErr) -> Report { let mut labels = Vec::new(); let mut colors = ariadne::ColorGenerator::new(); for (input, err) in e.errors { let label = match err { ErrorKind::Context(ctx) => label_tokspan(input).with_message(format!("in {ctx}")), ErrorKind::Char(c) => label_tokspan(input).with_message(format!("expected {c:?}")), ErrorKind::Nom(kind) => { label_tokspan(input).with_message(format!("nom error {kind:?}")) } ErrorKind::Token(t) => { let next = input.first().unwrap().kind(); let msg = format!("expected {t:?} found {next:?}"); label_tokspan(input.take(1)).with_message(msg) } ErrorKind::Tokens(ts) => { let next = input.first().unwrap().kind(); let msg = format!("expected one of {ts:?} found {next:?}"); label_tokspan(input.take(1)).with_message(msg) } }; let label = label.with_color(colors.next()); labels.push(label); } let mut rep = Report::build(ReportKind::Error, (), 0).with_message("Parse Error"); for lbl in labels { rep = rep.with_label(lbl) } rep.finish() }