diff options
Diffstat (limited to 'src/chomp/error.rs')
-rw-r--r-- | src/chomp/error.rs | 389 |
1 files changed, 389 insertions, 0 deletions
diff --git a/src/chomp/error.rs b/src/chomp/error.rs new file mode 100644 index 0000000..6733d06 --- /dev/null +++ b/src/chomp/error.rs @@ -0,0 +1,389 @@ +use std::{ + error::Error, + fmt::{self, Display}, +}; + +use proc_macro2::Span; + +use super::{ + ast::{Alt, Call, Cat, Fix, Parameter, Variable}, + check::Spanning, + typed::Type, + visit::Visitable, +}; + +/// A type error when using a fix point variable. +#[derive(Debug)] +pub enum VariableError { + /// Usage of a free variable. + FreeVariable(Variable), + /// Usage of a guarded variable. + GuardedVariable(Variable), +} + +impl From<VariableError> for syn::Error { + fn from(other: VariableError) -> Self { + match other { + VariableError::FreeVariable(_) => todo!(), + VariableError::GuardedVariable(_) => todo!(), + } + } +} + +impl Display for VariableError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::FreeVariable(var) => { + let start = var.name().span().start(); + write!( + f, + "{}:{}: unbound variable '{}'", + start.line, + start.column, + var.name() + ) + } + Self::GuardedVariable(var) => { + let start = var.name().span().start(); + write!( + f, + "{}:{}: variable '{}' is guarded", + start.line, + start.column, + var.name() + ) + } + } + } +} + +impl Error for VariableError {} + +/// A type error when concatenating two terms. +#[derive(Debug)] +pub enum CatError { + /// The first term was unexpectedly nullable. + FirstNullable(Cat), + /// The flast set of the first term intersects the first set of the second. + FirstFlastOverlap(Cat), +} + +impl From<CatError> for syn::Error { + fn from(other: CatError) -> Self { + match other { + CatError::FirstNullable(cat) => { + let mut err = Self::new( + cat.punct().map_or_else(Span::call_site, |p| p.span), + "first item in sequence cannot accept the empty string", + ); + err.combine(Self::new( + cat.first() + .visit(&mut Spanning) + .unwrap_or_else(Span::call_site), + "this can accept empty string", + )); + err + } + CatError::FirstFlastOverlap(cat) => { + let mut err = Self::new( + cat.punct().map_or_else(Span::call_site, |p| p.span), + "cannot decide whether to repeat first or start accepting second", + ); + err.combine(Self::new( + cat.first() + .visit(&mut Spanning) + .unwrap_or_else(Span::call_site), + "a repetition of this", + )); + err.combine(Self::new( + cat.second() + .visit(&mut Spanning) + .unwrap_or_else(Span::call_site), + "collides with the start of this", + )); + err + } + } + } +} + +impl Display for CatError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::FirstNullable(cat) => { + let start = cat.punct().map_or_else(Span::call_site, |p| p.span).start(); + let fst_start = cat + .first() + .visit(&mut Spanning) + .unwrap_or_else(Span::call_site) + .start(); + write!( + f, + "{}:{}: term `{}' ({}:{}) accepts the empty string", + start.line, + start.column, + cat.first(), + fst_start.line, + fst_start.column + ) + } + Self::FirstFlastOverlap(cat) => { + let start = cat.punct().map_or_else(Span::call_site, |p| p.span).start(); + let fst_start = cat + .first() + .visit(&mut Spanning) + .unwrap_or_else(Span::call_site) + .start(); + let snd_start = cat + .second() + .visit(&mut Spanning) + .unwrap_or_else(Span::call_site) + .start(); + write!( + f, + "{}:{}: cannot decide whether to repeat `{}' ({}:{}) or start accepting `{}' ({}:{}).", + start.line, + start.column, + cat.first(), + fst_start.line, + fst_start.column, + cat.second(), + snd_start.line, + snd_start.column + ) + } + } + } +} + +impl Error for CatError {} + +/// A type error when alternating two terms. +#[derive(Debug)] +pub enum AltError { + /// Both terms are nullable. + BothNullable(Alt), + /// The first sets of the two terms intersect. + FirstOverlap(Alt), +} + +impl From<AltError> for syn::Error { + fn from(other: AltError) -> Self { + match other { + AltError::BothNullable(alt) => { + let mut err = Self::new( + alt.punct().map_or_else(Span::call_site, |p| p.span), + "both branches accept the empty string", + ); + err.combine(Self::new( + alt.left() + .visit(&mut Spanning) + .unwrap_or_else(Span::call_site), + "left branch accepts the empty string", + )); + err.combine(Self::new( + alt.right() + .visit(&mut Spanning) + .unwrap_or_else(Span::call_site), + "right branch accepts the empty string", + )); + err + } + AltError::FirstOverlap(alt) => { + let mut err = Self::new( + alt.punct().map_or_else(Span::call_site, |p| p.span), + "cannot decide whether to accept the left or right branch", + ); + err.combine(Self::new( + alt.left() + .visit(&mut Spanning) + .unwrap_or_else(Span::call_site), + "left branch starts with a character", + )); + err.combine(Self::new( + alt.right() + .visit(&mut Spanning) + .unwrap_or_else(Span::call_site), + "right branch starts with the same character", + )); + err + } + } + } +} + +impl Display for AltError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::BothNullable(alt) => { + let start = alt.punct().map_or_else(Span::call_site, |p| p.span).start(); + let left_start = alt + .left() + .visit(&mut Spanning) + .unwrap_or_else(Span::call_site) + .start(); + let right_start = alt + .right() + .visit(&mut Spanning) + .unwrap_or_else(Span::call_site) + .start(); + write!( + f, + "{}:{}: both `{}' ({}:{}) and `{}' ({}:{}) accept the empty string.", + start.line, + start.column, + alt.left(), + left_start.line, + left_start.column, + alt.right(), + right_start.line, + right_start.column, + ) + } + Self::FirstOverlap(alt) => { + let start = alt.punct().map_or_else(Span::call_site, |p| p.span).start(); + let left_start = alt + .left() + .visit(&mut Spanning) + .unwrap_or_else(Span::call_site) + .start(); + let right_start = alt + .right() + .visit(&mut Spanning) + .unwrap_or_else(Span::call_site) + .start(); + write!( + f, + "{}:{}: cannot decide whether to accept `{}' ({}:{}) or `{}' ({}:{}).", + start.line, + start.column, + alt.left(), + left_start.line, + left_start.column, + alt.right(), + right_start.line, + right_start.column, + ) + } + } + } +} + +impl Error for AltError {} + +#[derive(Debug)] +pub struct FixError(pub Fix, pub Type, pub Box<TypeError>); + +impl From<FixError> for syn::Error { + fn from(e: FixError) -> Self { + let mut error = Self::from(*e.2); + error.combine(Self::new( + e.0.span().unwrap_or_else(Span::call_site), + format!("assuming `{}' has type {:?}.", e.0.arg(), e.1), + )); + error + } +} + +impl Display for FixError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.2.fmt(f)?; + let start = self.0.span().unwrap_or_else(Span::call_site).start(); + write!( + f, + "\n({}:{}) assuming `{}' has type {:?}.", + start.line, + start.column, + self.0.arg(), + self.1 + ) + } +} + +impl Error for FixError {} + +#[derive(Debug)] +pub enum TypeError { + Cat(CatError), + Alt(AltError), + Variable(VariableError), + Fix(FixError), +} + +impl From<CatError> for TypeError { + fn from(other: CatError) -> Self { + Self::Cat(other) + } +} + +impl From<AltError> for TypeError { + fn from(other: AltError) -> Self { + Self::Alt(other) + } +} + +impl From<VariableError> for TypeError { + fn from(other: VariableError) -> Self { + Self::Variable(other) + } +} + +impl From<TypeError> for syn::Error { + fn from(other: TypeError) -> Self { + match other { + TypeError::Cat(e) => e.into(), + TypeError::Alt(e) => e.into(), + TypeError::Variable(e) => e.into(), + TypeError::Fix(e) => e.into(), + } + } +} + +impl Display for TypeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Cat(e) => e.fmt(f), + Self::Alt(e) => e.fmt(f), + Self::Variable(e) => e.fmt(f), + Self::Fix(e) => e.fmt(f), + } + } +} + +impl Error for TypeError {} + +#[derive(Debug)] +pub enum SubstituteError { + FreeParameter(Parameter), + WrongArgCount { call: Call, expected: usize }, +} + +impl Display for SubstituteError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::FreeParameter(param) => { + let start = param.name().span().start(); + write!( + f, + "{}:{}: undeclared variable `{}'", + start.line, + start.column, + param.name() + ) + } + SubstituteError::WrongArgCount { call, expected } => { + let start = call.span().unwrap_or_else(Span::call_site).start(); + write!( + f, + "{}:{}: wrong number of arguments. Expected {}, got {}", + start.line, + start.column, + call.args().len(), + expected + ) + } + } + } +} + +impl Error for SubstituteError {} |