futilehdl/src/parser/error.rs

130 lines
3.8 KiB
Rust

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<I> {
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<TokenKind>),
/// A nom error
Nom(nom::error::ErrorKind),
}
impl<I> ParseError<I> for Error<I>
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(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, *o]);
other.errors[0] = (io.clone(), combined_kind);
other
}
_ => {
other.errors.extend_from_slice(&self.errors[..]);
other
}
}
}
}
impl<I> ContextError<I> for Error<I> {
fn add_context(input: I, ctx: &'static str, mut other: Self) -> Self {
other.errors.push((input, ErrorKind::Context(ctx)));
other
}
}
impl<I> Error<I> {
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<usize> {
let first = input.first().expect("eof in token").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<TokenSpan>) -> 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()
}