From c22d397545e7575608e34561a274745b28be8c2c Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Sun, 14 Oct 2018 14:18:42 -0500 Subject: [PATCH] FIX: Better error reporting. --- src/ast/mod.rs | 20 +++++++---- src/build/mod.rs | 8 ++--- src/error.rs | 8 +++-- src/iter.rs | 38 +++++++++++--------- src/parse/mod.rs | 41 ++++++++++++++++----- src/tokenizer/mod.rs | 84 +++++++++++++++++++++++--------------------- 6 files changed, 120 insertions(+), 79 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 37748ed..320ea81 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -63,6 +63,12 @@ impl Position { } } +impl<'a> From<&'a Position> for Position { + fn from(source: &'a Position) -> Self { + source.clone() + } +} + /// Defines the types of tokens in UCG syntax. #[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)] pub enum TokenType { @@ -121,31 +127,31 @@ macro_rules! value_node { #[allow(unused_macros)] macro_rules! make_tok { (EOF => $i:expr) => { - Token::new("", TokenType::END, $i) + Token::new("", TokenType::END, &$i) }; (WS => $i:expr) => { - Token::new("", TokenType::WS, $i) + Token::new("", TokenType::WS, &$i) }; (CMT => $e:expr, $i:expr) => { - Token::new($e, TokenType::COMMENT, $i) + Token::new($e, TokenType::COMMENT, &$i) }; (QUOT => $e:expr, $i:expr) => { - Token::new($e, TokenType::QUOTED, $i) + Token::new($e, TokenType::QUOTED, &$i) }; (PUNCT => $e:expr, $i:expr) => { - Token::new($e, TokenType::PUNCT, $i) + Token::new($e, TokenType::PUNCT, &$i) }; (DIGIT => $e:expr, $i:expr) => { - Token::new($e, TokenType::DIGIT, $i) + Token::new($e, TokenType::DIGIT, &$i) }; ($e:expr, $i:expr) => { - Token::new($e, TokenType::BAREWORD, $i) + Token::new($e, TokenType::BAREWORD, &$i) }; } diff --git a/src/build/mod.rs b/src/build/mod.rs index 74215d2..9cfa178 100644 --- a/src/build/mod.rs +++ b/src/build/mod.rs @@ -249,6 +249,7 @@ impl<'a> Builder<'a> { } fn eval_span(&mut self, input: OffsetStrIter) -> Result, Box> { + // TODO(jwall): This should really return a better error. match parse(input) { Ok(stmts) => { //panic!("Successfully parsed {}", input); @@ -261,14 +262,13 @@ impl<'a> Builder<'a> { Some(val) => Ok(val), } } - // FIXME(jwall): We need to return a error::Error so we have position information. - Err(err) => Err(Box::new(error::Error::new_with_boxed_cause( + Err(err) => Err(Box::new(error::Error::new_with_cause( format!( "Error while parsing file: {}", self.curr_file.unwrap_or("") ), error::ErrorType::ParseError, - Box::new(err), + err, ))), } } @@ -998,7 +998,7 @@ impl<'a> Builder<'a> { expr_as_stmt.push_str(expr); expr_as_stmt.push_str(";"); let assert_input = - OffsetStrIter::new_with_offsets(&expr_as_stmt, 0, tok.pos.line - 1, tok.pos.column - 1); + OffsetStrIter::new_with_offsets(&expr_as_stmt, tok.pos.line - 1, tok.pos.column - 1); let ok = match self.eval_span(assert_input) { Ok(v) => v, Err(e) => { diff --git a/src/error.rs b/src/error.rs index a963f33..c564de0 100644 --- a/src/error.rs +++ b/src/error.rs @@ -79,15 +79,17 @@ impl Error { msg: S, t: ErrorType, cause: Box, + pos: Position, ) -> Self { - // TODO(jwall): This should take a real position instead of this fake one. - let mut e = Self::new(msg, t, Position::new(0, 0, 0)); + // FIXME(jwall): This should take a real position instead of this fake one. + let mut e = Self::new(msg, t, pos); e.cause = Some(cause); return e; } pub fn new_with_cause>(msg: S, t: ErrorType, cause: Self) -> Self { - Self::new_with_boxed_cause(msg, t, Box::new(cause)) + let pos = cause.pos.clone(); + Self::new_with_boxed_cause(msg, t, Box::new(cause), pos) } fn render(&self, w: &mut fmt::Formatter) -> fmt::Result { diff --git a/src/iter.rs b/src/iter.rs index 156374d..100d168 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -1,33 +1,28 @@ use std::convert::From; use std::iter::Iterator; -use abortable_parser::iter::StrIter; +use abortable_parser::iter::{SliceIter, StrIter}; use abortable_parser::{ InputIter, Offsetable, Peekable, Seekable, Span, SpanRange, TextPositionTracker, }; +use ast::{Position, Token}; + #[derive(Debug)] pub struct OffsetStrIter<'a> { contained: StrIter<'a>, - idx_offset: usize, line_offset: usize, col_offset: usize, } impl<'a> OffsetStrIter<'a> { pub fn new(input: &'a str) -> Self { - Self::new_with_offsets(input, 0, 0, 0) + Self::new_with_offsets(input, 0, 0) } - pub fn new_with_offsets( - input: &'a str, - idx_offset: usize, - line_offset: usize, - col_offset: usize, - ) -> Self { + pub fn new_with_offsets(input: &'a str, line_offset: usize, col_offset: usize) -> Self { OffsetStrIter { contained: StrIter::new(input), - idx_offset: idx_offset, line_offset: line_offset, col_offset: col_offset, } @@ -44,7 +39,7 @@ impl<'a> Iterator for OffsetStrIter<'a> { impl<'a> Offsetable for OffsetStrIter<'a> { fn get_offset(&self) -> usize { - self.contained.get_offset() + self.idx_offset + self.contained.get_offset() } } @@ -52,7 +47,6 @@ impl<'a> Clone for OffsetStrIter<'a> { fn clone(&self) -> Self { OffsetStrIter { contained: self.contained.clone(), - idx_offset: self.idx_offset, line_offset: self.line_offset, col_offset: self.col_offset, } @@ -63,7 +57,6 @@ impl<'a> From<&'a str> for OffsetStrIter<'a> { fn from(source: &'a str) -> Self { OffsetStrIter { contained: StrIter::new(source), - idx_offset: 0, line_offset: 0, col_offset: 0, } @@ -72,9 +65,7 @@ impl<'a> From<&'a str> for OffsetStrIter<'a> { impl<'a> Seekable for OffsetStrIter<'a> { fn seek(&mut self, to: usize) -> usize { - let contained_offset = self.contained.seek(to); - self.idx_offset += contained_offset; - contained_offset + self.contained.seek(to) } } @@ -101,3 +92,18 @@ impl<'a> TextPositionTracker for OffsetStrIter<'a> { } impl<'a> InputIter for OffsetStrIter<'a> {} + +impl<'a> From<&'a SliceIter<'a, Token>> for Position { + fn from(source: &'a SliceIter<'a, Token>) -> Self { + match source.peek_next() { + Some(t) => t.pos.clone(), + None => Position::new(0, 0, 0), + } + } +} + +impl<'a> From<&'a OffsetStrIter<'a>> for Position { + fn from(s: &'a OffsetStrIter<'a>) -> Position { + Position::new(s.line(), s.column(), s.get_offset()) + } +} diff --git a/src/parse/mod.rs b/src/parse/mod.rs index d5db62a..67f59ce 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -24,6 +24,7 @@ use abortable_parser::{Error, Peekable, Result}; use self::precedence::op_expression; use ast::*; +use error; use iter::OffsetStrIter; use tokenizer::*; @@ -890,8 +891,8 @@ fn statement(i: SliceIter) -> Result, Statement> { } //trace_macros!(false); -/// Parses a LocatedSpan into a list of Statements or an `abortable_parser::Error`. -pub fn parse(input: OffsetStrIter) -> std::result::Result, Error> { +/// Parses a LocatedSpan into a list of Statements or an `error::Error`. +pub fn parse(input: OffsetStrIter) -> std::result::Result, error::Error> { match tokenize(input.clone()) { Ok(tokenized) => { let mut out = Vec::new(); @@ -903,16 +904,35 @@ pub fn parse(input: OffsetStrIter) -> std::result::Result, Error> break; } } - // FIXME(jwall): We need to return a error::Error so we have position information. match statement(i.clone()) { Result::Abort(e) => { - return Err(e); + let pos: Position = (&i).into(); + let err = error::Error::new_with_boxed_cause( + "Statement Parse Error", + error::ErrorType::ParseError, + Box::new(e), + pos, + ); + return Err(err); } Result::Fail(e) => { - return Err(Error::caused_by("Statement Parse error", &i, Box::new(e))); + let pos: Position = (&i).into(); + let err = error::Error::new_with_boxed_cause( + "Statement Parse Error", + error::ErrorType::ParseError, + Box::new(e), + pos, + ); + return Err(err); } - Result::Incomplete(ei) => { - return Err(Error::new("Unexpected end of parsing input: {:?}", &ei)); + Result::Incomplete(_ei) => { + let pos: Position = (&i).into(); + let err = error::Error::new( + "Unexpected end of parse input", + error::ErrorType::IncompleteParsing, + pos, + ); + return Err(err); } Result::Complete(rest, stmt) => { out.push(stmt); @@ -926,7 +946,12 @@ pub fn parse(input: OffsetStrIter) -> std::result::Result, Error> return Ok(out); } Err(e) => { - return Err(Error::caused_by("Tokenization Error", &input, Box::new(e))); + let err = error::Error::new_with_cause( + "Tokenization Error", + error::ErrorType::UnexpectedToken, + e, + ); + return Err(err); } } } diff --git a/src/tokenizer/mod.rs b/src/tokenizer/mod.rs index 177ca28..b114a26 100644 --- a/src/tokenizer/mod.rs +++ b/src/tokenizer/mod.rs @@ -17,17 +17,12 @@ use std; use abortable_parser::combinators::*; use abortable_parser::iter::SliceIter; -use abortable_parser::{Error, Offsetable, Result, TextPositionTracker}; +use abortable_parser::{Error, Offsetable, Result}; use ast::*; +use error; use iter::OffsetStrIter; -impl<'a> From> for Position { - fn from(s: OffsetStrIter<'a>) -> Position { - Position::new(s.line(), s.column(), s.get_offset()) - } -} - fn is_symbol_char<'a>(i: OffsetStrIter<'a>) -> Result, u8> { let mut _i = i.clone(); let c = match _i.next() { @@ -75,7 +70,7 @@ make_fn!(strtok, frag => escapequoted, (Token{ typ: TokenType::QUOTED, - pos: Position::from(span), + pos: Position::from(&span), fragment: frag.to_string(), }) ) @@ -89,7 +84,7 @@ make_fn!(pipequotetok, _ => text_token!("|"), (Token{ typ: TokenType::PIPEQUOTE, - pos: Position::from(p), + pos: Position::from(&p), fragment: frag.to_string(), }) ) @@ -102,7 +97,7 @@ make_fn!(barewordtok, frag => consume_all!(is_symbol_char), (Token{ typ: TokenType::BAREWORD, - pos: Position::from(span), + pos: Position::from(&span), fragment: frag.to_string(), }) ) @@ -115,7 +110,7 @@ make_fn!(digittok, digits => consume_all!(ascii_digit), (Token{ typ: TokenType::DIGIT, - pos: Position::from(span), + pos: Position::from(&span), fragment: digits.to_string(), }) ) @@ -130,7 +125,7 @@ make_fn!(booleantok, ), (Token{ typ: TokenType::BOOLEAN, - pos: Position::from(span), + pos: Position::from(&span), fragment: token.to_string(), }) ) @@ -141,27 +136,27 @@ make_fn!(booleantok, macro_rules! do_text_token_tok { ($i:expr, $type:expr, $text_token:expr, WS) => { do_each!($i, - span => input!(), - frag => text_token!($text_token), - _ => either!(whitespace, comment), - (Token { - typ: $type, - pos: Position::from(span), - fragment: frag.to_string(), - }) - ) + span => input!(), + frag => text_token!($text_token), + _ => either!(whitespace, comment), + (Token { + typ: $type, + pos: Position::from(&span), + fragment: frag.to_string(), + }) + ) }; ($i:expr, $type:expr, $text_token:expr) => { do_each!($i, - span => input!(), - frag => text_token!($text_token), - (Token { - typ: $type, - pos: Position::from(span), - fragment: frag.to_string(), - }) - ) + span => input!(), + frag => text_token!($text_token), + (Token { + typ: $type, + pos: Position::from(&span), + fragment: frag.to_string(), + }) + ) }; } @@ -326,7 +321,7 @@ make_fn!(whitespace, _ => repeat!(ascii_ws), (Token{ typ: TokenType::WS, - pos: Position::from(span), + pos: Position::from(&span), fragment: String::new(), }) ) @@ -338,7 +333,7 @@ make_fn!(end_of_input, _ => eoi, (Token{ typ: TokenType::END, - pos: Position::from(span), + pos: Position::from(&span), fragment: String::new(), }) ) @@ -389,31 +384,38 @@ make_fn!(token, ); /// Consumes an input OffsetStrIter and returns either a Vec or a error::Error. -pub fn tokenize(input: OffsetStrIter) -> std::result::Result, Error> { +pub fn tokenize(input: OffsetStrIter) -> std::result::Result, error::Error> { let mut out = Vec::new(); let mut i = input.clone(); loop { if let Result::Complete(_, _) = eoi(i.clone()) { break; } + let pos: Position = Position::from(&i); // FIXME(jwall): We need to return a error::Error so we have position information. match token(i.clone()) { Result::Abort(e) => { - return Err(Error::caused_by( + return Err(error::Error::new_with_boxed_cause( "Invalid Token encountered", - &i, + error::ErrorType::UnexpectedToken, Box::new(e), - )); + pos, + )) } Result::Fail(e) => { - return Err(Error::caused_by( + return Err(error::Error::new_with_boxed_cause( "Invalid Token encountered", - &i, + error::ErrorType::UnexpectedToken, Box::new(e), - )); + pos, + )) } - Result::Incomplete(offset) => { - return Err(Error::new("Unexepcted end of Input", &offset)); + Result::Incomplete(_offset) => { + return Err(error::Error::new( + "Invalid Token encountered", + error::ErrorType::IncompleteParsing, + pos, + )) } Result::Complete(rest, tok) => { i = rest; @@ -429,7 +431,7 @@ pub fn tokenize(input: OffsetStrIter) -> std::result::Result, Error> out.push(Token { fragment: String::new(), typ: TokenType::END, - pos: i.into(), + pos: Position::from(&i), }); Ok(out) }