dont store source in SpannedError

This commit is contained in:
Robin Appelman 2020-12-13 17:08:11 +01:00
commit 5e8ad8f513
3 changed files with 74 additions and 37 deletions

View file

@ -13,7 +13,9 @@ use std::num::ParseFloatError;
use std::str::ParseBoolError; use std::str::ParseBoolError;
use thiserror::Error; use thiserror::Error;
/// An error and related source span, will print out the problematic code fragment and error on `Display` /// An error and related source span
///
/// You can pretty-print the error with the offending source by using `display_with_source`
/// ///
/// ## Example /// ## Example
/// ///
@ -28,19 +30,14 @@ use thiserror::Error;
/// ``` /// ```
/// ///
#[derive(Debug)] #[derive(Debug)]
pub struct SpannedError<'a, T: Error + Debug> { pub struct SpannedError<T: Error + Debug> {
span: Span, span: Span,
source: &'a str,
error: T, error: T,
} }
impl<'a, T: Error + Debug> SpannedError<'a, T> { impl<T: Error + Debug> SpannedError<T> {
pub fn new(error: T, span: Span, source: &'a str) -> Self { pub fn new(error: T, span: Span) -> Self {
SpannedError { SpannedError { span, error }
span,
source,
error,
}
} }
pub fn error(&self) -> &T { pub fn error(&self) -> &T {
@ -48,23 +45,29 @@ impl<'a, T: Error + Debug> SpannedError<'a, T> {
} }
} }
impl<'a, T: Error + Debug + 'static> Error for SpannedError<'a, T> { impl<T: Error + Debug + 'static> Error for SpannedError<T> {
fn source(&self) -> Option<&(dyn Error + 'static)> { fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(&self.error) Some(&self.error)
} }
} }
impl<T: Error + Debug> Display for SpannedError<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
<T as Display>::fmt(&self.error, f)
}
}
const METRICS: DefaultMetrics = DefaultMetrics::with_tab_stop(4); const METRICS: DefaultMetrics = DefaultMetrics::with_tab_stop(4);
impl<'a, T: Error + Debug> fmt::Display for SpannedError<'a, T> { impl<T: Error + Debug> SpannedError<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { pub fn display_with_source(&self, source: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let start = get_position(self.source, self.span.start); let start = get_position(source, self.span.start);
let end = get_position(self.source, self.span.end); let end = get_position(source, self.span.end);
let span = SourceSpan::new(start, end, end.next_line()); let span = SourceSpan::new(start, end, end.next_line());
let mut fmt = Formatter::with_margin_color(Color::Blue); let mut fmt = Formatter::with_margin_color(Color::Blue);
let buffer = SourceBuffer::new( let buffer = SourceBuffer::new(
self.source.chars().map(|char| Result::<char, ()>::Ok(char)), source.chars().map(|char| Result::<char, ()>::Ok(char)),
Position::default(), Position::default(),
METRICS, METRICS,
); );
@ -186,15 +189,14 @@ impl ExpectToken for Token {
} }
} }
pub trait ResultExt<'a, T, E: Error + Debug> { pub trait ResultExt<T, E: Error + Debug> {
fn with_span(self, span: Span, source: &'a str) -> Result<T, SpannedError<'a, E>>; fn with_span(self, span: Span) -> Result<T, SpannedError<E>>;
} }
impl<'a, T, E: Into<ParseError>> ResultExt<'a, T, ParseError> for Result<T, E> { impl<T, E: Into<ParseError>> ResultExt<T, ParseError> for Result<T, E> {
fn with_span(self, span: Span, source: &'a str) -> Result<T, SpannedError<'a, ParseError>> { fn with_span(self, span: Span) -> Result<T, SpannedError<ParseError>> {
self.map_err(|error| SpannedError { self.map_err(|error| SpannedError {
span, span,
source,
error: error.into(), error: error.into(),
}) })
} }

View file

@ -1,4 +1,4 @@
use logos::Logos; use logos::{Lexer, Logos, Span};
#[derive(Logos, Debug, PartialEq, Clone)] #[derive(Logos, Debug, PartialEq, Clone)]
pub enum Token { pub enum Token {
@ -165,3 +165,38 @@ fn test_lex_float() {
assert_eq!(lex.next(), Some(Token::Float)); assert_eq!(lex.next(), Some(Token::Float));
assert_eq!(lex.next(), None); assert_eq!(lex.next(), None);
} }
pub struct SpannedToken<'source> {
pub token: Token,
pub span: Span,
pub source: &'source str,
}
impl<'source> SpannedToken<'source> {
pub fn slice(&self) -> &'source str {
&self.source[self.span.clone()]
}
}
pub struct TokenStream<'source> {
lexer: Lexer<'source, Token>,
}
impl<'source> TokenStream<'source> {
pub fn source(&self) -> &'source str {
self.lexer.source()
}
}
impl<'source> Iterator for TokenStream<'source> {
type Item = SpannedToken<'source>;
fn next(&mut self) -> Option<Self::Item> {
let token = self.lexer.next()?;
Some(SpannedToken {
token,
span: self.lexer.span(),
source: self.lexer.source(),
})
}
}

View file

@ -43,7 +43,7 @@ impl<'source> Parser<'source> {
} }
} }
pub fn parse(&mut self) -> Result<Value, SpannedError<'source, ParseError>> { pub fn parse(&mut self) -> Result<Value, SpannedError<ParseError>> {
let token = self let token = self
.lexer .lexer
.next() .next()
@ -56,24 +56,24 @@ impl<'source> Parser<'source> {
Token::Array, Token::Array,
Token::SquareOpen, Token::SquareOpen,
]) ])
.with_span(self.lexer.span(), self.source)?; .with_span(self.lexer.span())?;
let value = match token { let value = match token {
Token::Bool => Value::Bool( Token::Bool => Value::Bool(
self.lexer self.lexer
.slice() .slice()
.to_ascii_lowercase() .to_ascii_lowercase()
.parse() .parse()
.with_span(self.lexer.span(), self.source)?, .with_span(self.lexer.span())?,
), ),
Token::Integer => { Token::Integer => {
Value::Int(parse_int(self.lexer.slice()).with_span(self.lexer.span(), self.source)?) Value::Int(parse_int(self.lexer.slice()).with_span(self.lexer.span())?)
}
Token::Float => {
Value::Float(parse_float(self.lexer.slice()).with_span(self.lexer.span())?)
}
Token::LiteralString => {
Value::String(parse_string(self.lexer.slice()).with_span(self.lexer.span())?)
} }
Token::Float => Value::Float(
parse_float(self.lexer.slice()).with_span(self.lexer.span(), self.source)?,
),
Token::LiteralString => Value::String(
parse_string(self.lexer.slice()).with_span(self.lexer.span(), self.source)?,
),
Token::Null => Value::Null, Token::Null => Value::Null,
Token::Array => Value::Array(self.parse_array(ArraySyntax::Long)?), Token::Array => Value::Array(self.parse_array(ArraySyntax::Long)?),
Token::SquareOpen => Value::Array(self.parse_array(ArraySyntax::Short)?), Token::SquareOpen => Value::Array(self.parse_array(ArraySyntax::Short)?),
@ -86,14 +86,14 @@ impl<'source> Parser<'source> {
fn parse_array( fn parse_array(
&mut self, &mut self,
syntax: ArraySyntax, syntax: ArraySyntax,
) -> Result<HashMap<Key, Value>, SpannedError<'source, ParseError>> { ) -> Result<HashMap<Key, Value>, SpannedError<ParseError>> {
let mut builder = ArrayBuilder::default(); let mut builder = ArrayBuilder::default();
if syntax == ArraySyntax::Long { if syntax == ArraySyntax::Long {
self.lexer self.lexer
.next() .next()
.expect_token(&[Token::BracketOpen]) .expect_token(&[Token::BracketOpen])
.with_span(self.lexer.span(), self.source)?; .with_span(self.lexer.span())?;
} }
loop { loop {
@ -115,7 +115,7 @@ impl<'source> Parser<'source> {
.lexer .lexer
.next() .next()
.expect_token(&[syntax.close_bracket(), Token::Comma, Token::Arrow]) .expect_token(&[syntax.close_bracket(), Token::Comma, Token::Arrow])
.with_span(self.lexer.span(), self.source)?; .with_span(self.lexer.span())?;
match next { match next {
Token::BracketClose => { Token::BracketClose => {
@ -137,7 +137,7 @@ impl<'source> Parser<'source> {
Value::String(str) => Key::String(str), Value::String(str) => Key::String(str),
value => { value => {
let err = ParseError::InvalidArrayKey(InvalidArrayKeyError(value)); let err = ParseError::InvalidArrayKey(InvalidArrayKeyError(value));
let span_err = SpannedError::new(err, key_or_value_span, self.source); let span_err = SpannedError::new(err, key_or_value_span);
return Err(span_err); return Err(span_err);
} }
}; };
@ -147,7 +147,7 @@ impl<'source> Parser<'source> {
.lexer .lexer
.next() .next()
.expect_token(&[syntax.close_bracket(), Token::Comma]) .expect_token(&[syntax.close_bracket(), Token::Comma])
.with_span(self.lexer.span(), self.source)? .with_span(self.lexer.span())?
{ {
Token::BracketClose => { Token::BracketClose => {
break; break;