From e1f64548b4d19932f5c8c1be79a53baa428ed9e3 Mon Sep 17 00:00:00 2001 From: NotAFile Date: Thu, 3 Feb 2022 00:05:10 +0100 Subject: [PATCH] add custom error type --- src/main.rs | 3 +- src/parser/error.rs | 127 ++++++++++++++++++++++++++++++++++++++----- src/parser/mod.rs | 3 +- src/parser/module.rs | 31 ++++++----- src/parser/tokens.rs | 31 ++++++----- 5 files changed, 149 insertions(+), 46 deletions(-) diff --git a/src/main.rs b/src/main.rs index 97af334..13e6dcb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,7 @@ mod package; mod parser; mod rtlil; +use ariadne::Source; use std::fs::File; use std::io::prelude::*; use std::path::PathBuf; @@ -48,11 +49,9 @@ fn main() { if opt.debug { println!("{err:#?}"); } - /* parser::error::convert_error(input, err) .eprint(Source::from(input.fragment())) .unwrap(); - */ } Err(_) => (unreachable!()), Ok(res) => { diff --git a/src/parser/error.rs b/src/parser/error.rs index 1c37f1e..f479262 100644 --- a/src/parser/error.rs +++ b/src/parser/error.rs @@ -1,23 +1,124 @@ +use super::tokens::{TokenKind, TokenSpan}; use super::{IErr, Span}; use ariadne::{Label, Report, ReportKind}; -use nom_greedyerror::{GreedyErrorKind, Position}; +use nom::error::{ContextError, ParseError}; +use nom::InputTake; -fn span_to_range(input: Span) -> std::ops::Range { - input.position()..(input.position() + input.len()) +#[derive(Debug)] +pub struct Error { + pub errors: Vec<(I, ErrorKind)>, } -pub fn convert_error(_input: Span, e: IErr) -> Report { - let mut labels = Vec::new(); - for err in e.errors { - let label = match err.1 { - GreedyErrorKind::Context(ctx) => { - Label::new(span_to_range(err.0)).with_message(format!("in {ctx}")) +#[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) } - GreedyErrorKind::Char(c) => Label::new(err.0.position()..err.0.position()) - .with_message(format!("expected {c:?}")), - GreedyErrorKind::Nom(kind) => Label::new(err.0.position()..err.0.position()) - .with_message(format!("nom error {kind:?}")), }; + let label = label.with_color(colors.next()); labels.push(label); } let mut rep = Report::build(ReportKind::Error, (), 0).with_message("Parse Error"); diff --git a/src/parser/mod.rs b/src/parser/mod.rs index ecf3b48..f1618a0 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -15,13 +15,12 @@ use nom::{ multi::{many0, separated_list0}, sequence::{delimited, pair, preceded, separated_pair, tuple}, }; -use nom_greedyerror::GreedyError; use nom_locate::LocatedSpan; // custom span type for nom_locate pub type Span<'a> = LocatedSpan<&'a str>; -pub type IErr = GreedyError; +pub type IErr = error::Error; // custom IResult type for VerboseError pub type IResult> = nom::IResult; diff --git a/src/parser/module.rs b/src/parser/module.rs index e5672fd..7d11e0a 100644 --- a/src/parser/module.rs +++ b/src/parser/module.rs @@ -1,6 +1,6 @@ use nom::{ branch::alt, - combinator::map, + combinator::{cut, map}, error::context, multi::{many0, separated_list0}, sequence::{delimited, preceded, tuple}, @@ -52,7 +52,11 @@ fn inputs_list(input: TokenSpan) -> IResult> { fn assign_item(input: TokenSpan) -> IResult { context( "assignment", - delimited(token(tk::Assign), assign_statement, token(tk::Semicolon)), + delimited( + token(tk::Assign), + cut(assign_statement), + cut(token(tk::Semicolon)), + ), )(input) } @@ -64,23 +68,22 @@ fn module_item(input: TokenSpan) -> IResult { } pub fn module(input: TokenSpan) -> IResult { - context( - "module", - map( - tuple(( - token(tk::Module), + map( + preceded( + token(tk::Module), + cut(tuple(( token(tk::Ident), delimited(token(tk::LParen), inputs_list, token(tk::RParen)), preceded(token(tk::RArrow), typename), delimited(token(tk::LBrace), many0(module_item), token(tk::RBrace)), - )), - |(_, name, inputs, _ret, items)| Module { - // TODO: bring back returns - name: name.span(), - ports: inputs, - items, - }, + ))), ), + |(name, inputs, _ret, items)| Module { + // TODO: bring back returns + name: name.span(), + ports: inputs, + items, + }, )(input) } diff --git a/src/parser/tokens.rs b/src/parser/tokens.rs index 42ee7d2..43ac876 100644 --- a/src/parser/tokens.rs +++ b/src/parser/tokens.rs @@ -1,6 +1,7 @@ //! convert text into a token stream use super::{ + error::{Error, InputPos}, literals::{identifier, ws0}, IResult, Span, }; @@ -9,7 +10,6 @@ use nom::{ bytes::complete::tag, character::complete::{anychar, digit1}, combinator::{consumed, map, recognize}, - error::ParseError, multi::many0, }; use std::fmt; @@ -40,9 +40,12 @@ impl<'a> Token<'a> { pub fn span(&self) -> Span { self.span } + pub fn kind(&self) -> TokenKind { + self.kind + } } -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Copy, Clone)] pub enum TokenKind { // no whitespace, for now // no token trees either, for now @@ -92,6 +95,12 @@ impl<'a> TokenSpan<'a> { pub fn with_pos(rest: &'a [Token<'a>], pos: usize) -> Self { Self { rest, pos } } + pub fn first(&self) -> Option<&Token> { + self.rest.first() + } + pub fn last(&self) -> Option<&Token> { + self.rest.last() + } } impl nom::InputTake for TokenSpan<'_> { @@ -114,30 +123,22 @@ impl nom::InputLength for TokenSpan<'_> { } } -impl nom_greedyerror::Position for TokenSpan<'_> { +impl InputPos for TokenSpan<'_> { fn position(&self) -> usize { self.pos } } /// combinator that matches a token kind -pub fn token<'a, E>( - kind: TokenKind, -) -> impl FnMut(TokenSpan<'a>) -> nom::IResult -where - E: ParseError>, -{ +pub fn token<'a>(kind: TokenKind) -> impl FnMut(TokenSpan<'a>) -> IResult { move |input: TokenSpan| { let next = &input.rest[0]; - if next.kind == kind.clone() { + let kind = kind.clone(); + if next.kind == kind { let rest = TokenSpan::with_pos(&input.rest[1..], input.pos + 1); Ok((rest, next)) } else { - Err(nom::Err::Error(E::from_error_kind( - input, - // TODO: Proper errors here - nom::error::ErrorKind::Tag, - ))) + Err(nom::Err::Error(Error::from_tokenkind(input, kind))) } } }