130 lines
3.8 KiB
Rust
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()
|
|
}
|