FIX: Better error reporting.

This commit is contained in:
Jeremy Wall 2018-10-14 14:18:42 -05:00
parent 7f47dc3f38
commit c22d397545
6 changed files with 120 additions and 79 deletions

View File

@ -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)
};
}

View File

@ -249,6 +249,7 @@ impl<'a> Builder<'a> {
}
fn eval_span(&mut self, input: OffsetStrIter) -> Result<Rc<Val>, Box<Error>> {
// 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("<eval>")
),
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) => {

View File

@ -79,15 +79,17 @@ impl Error {
msg: S,
t: ErrorType,
cause: Box<error::Error>,
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<S: Into<String>>(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 {

View File

@ -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())
}
}

View File

@ -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<Token>) -> Result<SliceIter<Token>, 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<Vec<Statement>, Error> {
/// Parses a LocatedSpan into a list of Statements or an `error::Error`.
pub fn parse(input: OffsetStrIter) -> std::result::Result<Vec<Statement>, 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<Vec<Statement>, 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<Vec<Statement>, 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);
}
}
}

View File

@ -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<OffsetStrIter<'a>> 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<OffsetStrIter<'a>, u8> {
let mut _i = i.clone();
let c = match _i.next() {
@ -75,7 +70,7 @@ make_fn!(strtok<OffsetStrIter, Token>,
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<OffsetStrIter, Token>,
_ => 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<OffsetStrIter, Token>,
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<OffsetStrIter, Token>,
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<OffsetStrIter, Token>,
),
(Token{
typ: TokenType::BOOLEAN,
pos: Position::from(span),
pos: Position::from(&span),
fragment: token.to_string(),
})
)
@ -141,27 +136,27 @@ make_fn!(booleantok<OffsetStrIter, Token>,
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<OffsetStrIter, Token>,
_ => 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<OffsetStrIter, Token>,
_ => eoi,
(Token{
typ: TokenType::END,
pos: Position::from(span),
pos: Position::from(&span),
fragment: String::new(),
})
)
@ -389,31 +384,38 @@ make_fn!(token<OffsetStrIter, Token>,
);
/// Consumes an input OffsetStrIter and returns either a Vec<Token> or a error::Error.
pub fn tokenize(input: OffsetStrIter) -> std::result::Result<Vec<Token>, Error> {
pub fn tokenize(input: OffsetStrIter) -> std::result::Result<Vec<Token>, 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<Vec<Token>, Error>
out.push(Token {
fragment: String::new(),
typ: TokenType::END,
pos: i.into(),
pos: Position::from(&i),
});
Ok(out)
}