diff --git a/src/parser.rs b/src/parser.rs index f767973..03b7d90 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -7,6 +7,8 @@ use self::Token::*; pub use crate::term::Notation::*; use crate::term::Term::*; use crate::term::{abs, app, Notation, Term}; +use std::error::Error; +use std::fmt; /// An error returned by `parse()` when a parsing issue is encountered. #[derive(Debug, PartialEq, Eq)] @@ -19,6 +21,24 @@ pub enum ParseError { EmptyExpression, } +impl fmt::Display for ParseError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + ParseError::InvalidCharacter((idx, char)) => { + write!(f, "lexical error; invalid character '{}' at {}", char, idx) + } + ParseError::InvalidExpression => write!(f, "syntax error; the expression is invalid"), + ParseError::EmptyExpression => write!(f, "syntax error; the expression is empty"), + } + } +} + +impl Error for ParseError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + None + } +} + #[derive(Debug, PartialEq, Eq)] #[doc(hidden)] pub enum Token { diff --git a/src/term.rs b/src/term.rs index 7477f30..3526558 100644 --- a/src/term.rs +++ b/src/term.rs @@ -5,6 +5,7 @@ pub use self::Term::*; use self::TermError::*; use std::borrow::Cow; use std::char::from_u32; +use std::error::Error; use std::fmt; /// The character used to display lambda abstractions (a backslash). @@ -61,6 +62,22 @@ pub enum TermError { NotApp, } +impl fmt::Display for TermError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + TermError::NotVar => write!(f, "the term is not a variable",), + TermError::NotAbs => write!(f, "the term is not an abstraction"), + TermError::NotApp => write!(f, "the term is not an application"), + } + } +} + +impl Error for TermError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + None + } +} + impl Term { /// Returns a variable's De Bruijn index, consuming it in the process. /// diff --git a/tests/parse_error.rs b/tests/parse_error.rs new file mode 100644 index 0000000..c376083 --- /dev/null +++ b/tests/parse_error.rs @@ -0,0 +1,17 @@ +extern crate lambda_calculus as lambda; + +use lambda::{parser::parse, term::Notation::Classic}; +use std::error::Error; + +#[test] +fn parse_error_question_mark_operator() { + match using_question_mark_operator() { + Result::Ok(_) => panic!("Should not be Ok"), + Result::Err(e) => assert_eq!(e.to_string(), "syntax error; the expression is empty"), + } +} + +fn using_question_mark_operator() -> Result<(), Box> { + parse("λλλ", Classic)?; + Ok(()) +} diff --git a/tests/term_error.rs b/tests/term_error.rs new file mode 100644 index 0000000..fa41333 --- /dev/null +++ b/tests/term_error.rs @@ -0,0 +1,17 @@ +extern crate lambda_calculus as lambda; + +use lambda::term::Term; +use std::error::Error; + +#[test] +fn term_error_question_mark_operator() { + match using_question_mark_operator() { + Result::Ok(_) => panic!("Should not be Ok"), + Result::Err(e) => assert_eq!(e.to_string(), "the term is not an abstraction"), + } +} + +fn using_question_mark_operator() -> Result<(), Box> { + Term::Var(0).unabs()?; + Ok(()) +}