diff --git a/examples/errors.rs b/examples/errors.rs index 061047a..6476432 100644 --- a/examples/errors.rs +++ b/examples/errors.rs @@ -1,25 +1,21 @@ -use eww_config::{config::*, expr::*, lexer, parser}; +use eww_config::{config::*, expr::*}; fn main() { - let parser = parser::ExprParser::new(); let mut files = codespan_reporting::files::SimpleFiles::new(); let input = "(12 :bar 22 (foo) (baz)"; let file_id = files.add("foo.eww", input); - let lexer = lexer::Lexer::new(input); - - let ast = parser.parse(file_id, lexer); - match ast { + let ast = eww_config::parse_string(file_id, input); + match ast.and_then(Element::::from_expr) { Ok(ast) => { - let element: Result, _> = Element::from_expr(ast); - let err = element.unwrap_err(); - + println!("{:?}", ast); + } + Err(err) => { let diag = err.pretty_diagnostic(&files); use codespan_reporting::term; let mut writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Always); term::emit(&mut writer, &term::Config::default(), &files, &diag).unwrap(); } - Err(err) => eprintln!("{:?}", err), } } diff --git a/src/error.rs b/src/error.rs index 14cf169..384df0a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,7 @@ -use crate::expr::{Expr, ExprType, Span}; +use crate::{ + expr::{Expr, ExprType, Span}, + lexer, +}; use codespan_reporting::{diagnostic, files}; use thiserror::Error; @@ -12,17 +15,46 @@ pub enum AstError { MissingNode(Option, ExprType), #[error("Wrong type of expression: Expected {1} but got {2}")] WrongExprType(Option, ExprType, ExprType), + + #[error("Parse error: {source}")] + ParseError { file_id: Option, source: lalrpop_util::ParseError }, } impl AstError { + pub fn get_span(&self) -> Option { + match self { + AstError::InvalidDefinition(span) => *span, + AstError::MissingNode(span, _) => *span, + AstError::WrongExprType(span, ..) => *span, + AstError::ParseError { file_id, source } => file_id.and_then(|id| get_parse_error_span(id, source)), + } + } + pub fn pretty_diagnostic(&self, files: &files::SimpleFiles<&str, &str>) -> diagnostic::Diagnostic { let diag = diagnostic::Diagnostic::error().with_message(format!("{}", self)); - if let AstError::WrongExprType(Some(span), ..) = self { + if let Some(span) = self.get_span() { diag.with_labels(vec![diagnostic::Label::primary(span.2, span.0..span.1)]) } else { diag } } + + pub fn from_parse_error(file_id: usize, err: lalrpop_util::ParseError) -> AstError { + AstError::ParseError { file_id: Some(file_id), source: err } + } +} + +fn get_parse_error_span( + file_id: usize, + err: &lalrpop_util::ParseError, +) -> Option { + match err { + lalrpop_util::ParseError::InvalidToken { location } => Some(Span(*location, *location, file_id)), + lalrpop_util::ParseError::UnrecognizedEOF { location, expected } => Some(Span(*location, *location, file_id)), + lalrpop_util::ParseError::UnrecognizedToken { token, expected } => Some(Span(token.0, token.2, file_id)), + lalrpop_util::ParseError::ExtraToken { token } => Some(Span(token.0, token.2, file_id)), + lalrpop_util::ParseError::User { error } => None, + } } pub fn spanned(span: Span, err: impl Into) -> AstError { diff --git a/src/lexer.rs b/src/lexer.rs index b0efdfe..7a85c67 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -34,9 +34,32 @@ pub enum Token { Error, } +impl std::fmt::Display for Token { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Token::LPren => write!(f, "'('"), + Token::RPren => write!(f, "')'"), + Token::True => write!(f, "true"), + Token::False => write!(f, "false"), + Token::StrLit(x) => write!(f, "\"{}\"", x), + Token::NumLit(x) => write!(f, "{}", x), + Token::Symbol(x) => write!(f, "{}", x), + Token::Keyword(x) => write!(f, "{}", x), + Token::Comment => write!(f, ""), + Token::Error => write!(f, ""), + } + } +} + #[derive(Debug, Eq, PartialEq, Copy, Clone)] pub struct LexicalError(usize, usize); +impl std::fmt::Display for LexicalError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Lexical error at {}..{}", self.0, self.1) + } +} + pub type SpannedResult = Result<(Loc, Tok, Loc), Error>; pub struct Lexer<'input> { diff --git a/src/lib.rs b/src/lib.rs index d9fab6f..dbd1316 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,8 +5,9 @@ pub mod config; pub mod error; pub mod expr; -pub mod lexer; -use error::AstError; +mod lexer; +use error::{AstError, AstResult}; +use expr::Expr; use std::{fmt::Display, ops::Deref}; @@ -16,6 +17,12 @@ use lalrpop_util::lalrpop_mod; lalrpop_mod!(pub parser); +pub fn parse_string(file_id: usize, s: &str) -> AstResult { + let lexer = lexer::Lexer::new(s); + let parser = parser::ExprParser::new(); + Ok(parser.parse(file_id, lexer).map_err(|e| AstError::from_parse_error(file_id, e))?) +} + macro_rules! test_parser { ($($text:literal),*) => {{ let p = crate::parser::ExprParser::new(); diff --git a/src/parser.lalrpop b/src/parser.lalrpop index 7734025..4070cef 100644 --- a/src/parser.lalrpop +++ b/src/parser.lalrpop @@ -13,8 +13,8 @@ extern { ")" => Token::RPren, "true" => Token::True, "false" => Token::False, - "strLit" => Token::StrLit(), - "numLit" => Token::NumLit(), + "string" => Token::StrLit(), + "number" => Token::NumLit(), "symbol" => Token::Symbol(), "keyword" => Token::Keyword(), "comment" => Token::Comment, @@ -40,13 +40,13 @@ Value: String = { }; StrLit: String = { - => { + => { x[1..x.len() - 1].to_owned() }, }; -Num: String = <"numLit"> => <>.to_string(); +Num: String = <"number"> => <>.to_string(); Bool: String = { "true" => "true".to_string(), "false" => "false".to_string(),