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() {
|
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),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
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 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 {
|
||||||
|
|
23
src/lexer.rs
23
src/lexer.rs
|
@ -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> {
|
||||||
|
|
11
src/lib.rs
11
src/lib.rs
|
@ -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();
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
Loading…
Add table
Reference in a new issue