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() {
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::<Expr, Expr>::from_expr) {
Ok(ast) => {
let element: Result<Element<Expr, Expr>, _> = 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),
}
}

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 thiserror::Error;
@ -12,17 +15,46 @@ pub enum AstError {
MissingNode(Option<Span>, ExprType),
#[error("Wrong type of expression: Expected {1} but got {2}")]
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 {
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> {
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<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 {

View file

@ -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<Tok, Loc, Error> = Result<(Loc, Tok, Loc), Error>;
pub struct Lexer<'input> {

View file

@ -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<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 {
($($text:literal),*) => {{
let p = crate::parser::ExprParser::new();

View file

@ -13,8 +13,8 @@ extern {
")" => Token::RPren,
"true" => Token::True,
"false" => Token::False,
"strLit" => Token::StrLit(<String>),
"numLit" => Token::NumLit(<String>),
"string" => Token::StrLit(<String>),
"number" => Token::NumLit(<String>),
"symbol" => Token::Symbol(<String>),
"keyword" => Token::Keyword(<String>),
"comment" => Token::Comment,
@ -40,13 +40,13 @@ Value: String = {
};
StrLit: String = {
<x:"strLit"> => {
<x:"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(),