proper parse error messages
This commit is contained in:
parent
98ef505a21
commit
b6a6188b8a
5 changed files with 76 additions and 18 deletions
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
36
src/error.rs
36
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<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 {
|
||||
|
|
23
src/lexer.rs
23
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<Tok, Loc, Error> = Result<(Loc, Tok, Loc), Error>;
|
||||
|
||||
pub struct Lexer<'input> {
|
||||
|
|
11
src/lib.rs
11
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<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();
|
||||
|
|
|
@ -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(),
|
||||
|
|
Loading…
Add table
Reference in a new issue