proper parse error messages

This commit is contained in:
elkowar 2021-07-05 20:09:18 +02:00
parent 98ef505a21
commit b6a6188b8a
No known key found for this signature in database
GPG key ID: E321AD71B1D1F27F
5 changed files with 76 additions and 18 deletions

View file

@ -1,25 +1,21 @@
use eww_config::{config::*, expr::*, lexer, parser}; use eww_config::{config::*, expr::*};
fn main() { fn main() {
let parser = parser::ExprParser::new();
let mut files = codespan_reporting::files::SimpleFiles::new(); let mut files = codespan_reporting::files::SimpleFiles::new();
let input = "(12 :bar 22 (foo) (baz)"; let input = "(12 :bar 22 (foo) (baz)";
let file_id = files.add("foo.eww", input); let file_id = files.add("foo.eww", input);
let lexer = lexer::Lexer::new(input); let ast = eww_config::parse_string(file_id, input);
match ast.and_then(Element::<Expr, Expr>::from_expr) {
let ast = parser.parse(file_id, lexer);
match ast {
Ok(ast) => { Ok(ast) => {
let element: Result<Element<Expr, Expr>, _> = Element::from_expr(ast); println!("{:?}", ast);
let err = element.unwrap_err(); }
Err(err) => {
let diag = err.pretty_diagnostic(&files); let diag = err.pretty_diagnostic(&files);
use codespan_reporting::term; use codespan_reporting::term;
let mut writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Always); let mut writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Always);
term::emit(&mut writer, &term::Config::default(), &files, &diag).unwrap(); term::emit(&mut writer, &term::Config::default(), &files, &diag).unwrap();
} }
Err(err) => eprintln!("{:?}", err),
} }
} }

View file

@ -1,4 +1,7 @@
use crate::expr::{Expr, ExprType, Span}; use crate::{
expr::{Expr, ExprType, Span},
lexer,
};
use codespan_reporting::{diagnostic, files}; use codespan_reporting::{diagnostic, files};
use thiserror::Error; use thiserror::Error;
@ -12,17 +15,46 @@ pub enum AstError {
MissingNode(Option<Span>, ExprType), MissingNode(Option<Span>, ExprType),
#[error("Wrong type of expression: Expected {1} but got {2}")] #[error("Wrong type of expression: Expected {1} but got {2}")]
WrongExprType(Option<Span>, ExprType, ExprType), WrongExprType(Option<Span>, ExprType, ExprType),
#[error("Parse error: {source}")]
ParseError { file_id: Option<usize>, source: lalrpop_util::ParseError<usize, lexer::Token, lexer::LexicalError> },
} }
impl AstError { impl AstError {
pub fn get_span(&self) -> Option<Span> {
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<usize> { pub fn pretty_diagnostic(&self, files: &files::SimpleFiles<&str, &str>) -> diagnostic::Diagnostic<usize> {
let diag = diagnostic::Diagnostic::error().with_message(format!("{}", self)); 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)]) diag.with_labels(vec![diagnostic::Label::primary(span.2, span.0..span.1)])
} else { } else {
diag diag
} }
} }
pub fn from_parse_error(file_id: usize, err: lalrpop_util::ParseError<usize, lexer::Token, lexer::LexicalError>) -> AstError {
AstError::ParseError { file_id: Some(file_id), source: err }
}
}
fn get_parse_error_span(
file_id: usize,
err: &lalrpop_util::ParseError<usize, lexer::Token, lexer::LexicalError>,
) -> Option<Span> {
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>) -> AstError { pub fn spanned(span: Span, err: impl Into<AstError>) -> AstError {

View file

@ -34,9 +34,32 @@ pub enum Token {
Error, 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)] #[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub struct LexicalError(usize, usize); 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<Tok, Loc, Error> = Result<(Loc, Tok, Loc), Error>; pub type SpannedResult<Tok, Loc, Error> = Result<(Loc, Tok, Loc), Error>;
pub struct Lexer<'input> { pub struct Lexer<'input> {

View file

@ -5,8 +5,9 @@
pub mod config; pub mod config;
pub mod error; pub mod error;
pub mod expr; pub mod expr;
pub mod lexer; mod lexer;
use error::AstError; use error::{AstError, AstResult};
use expr::Expr;
use std::{fmt::Display, ops::Deref}; use std::{fmt::Display, ops::Deref};
@ -16,6 +17,12 @@ use lalrpop_util::lalrpop_mod;
lalrpop_mod!(pub parser); lalrpop_mod!(pub parser);
pub fn parse_string(file_id: usize, s: &str) -> AstResult<Expr> {
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 { macro_rules! test_parser {
($($text:literal),*) => {{ ($($text:literal),*) => {{
let p = crate::parser::ExprParser::new(); let p = crate::parser::ExprParser::new();

View file

@ -13,8 +13,8 @@ extern {
")" => Token::RPren, ")" => Token::RPren,
"true" => Token::True, "true" => Token::True,
"false" => Token::False, "false" => Token::False,
"strLit" => Token::StrLit(<String>), "string" => Token::StrLit(<String>),
"numLit" => Token::NumLit(<String>), "number" => Token::NumLit(<String>),
"symbol" => Token::Symbol(<String>), "symbol" => Token::Symbol(<String>),
"keyword" => Token::Keyword(<String>), "keyword" => Token::Keyword(<String>),
"comment" => Token::Comment, "comment" => Token::Comment,
@ -40,13 +40,13 @@ Value: String = {
}; };
StrLit: String = { StrLit: String = {
<x:"strLit"> => { <x:"string"> => {
x[1..x.len() - 1].to_owned() x[1..x.len() - 1].to_owned()
}, },
}; };
Num: String = <"numLit"> => <>.to_string(); Num: String = <"number"> => <>.to_string();
Bool: String = { Bool: String = {
"true" => "true".to_string(), "true" => "true".to_string(),
"false" => "false".to_string(), "false" => "false".to_string(),