REFACTOR: Error handling to use BuildError throughout.

Also removed more extraneous error output.

Contributes toward issue #45
This commit is contained in:
Jeremy Wall 2019-05-29 18:57:24 -05:00
parent e5f406d4e9
commit 85f239a566
6 changed files with 38 additions and 96 deletions

View File

@ -159,8 +159,7 @@ fn test_assert_just_keyword_compile_failures() {
assert_build_failure( assert_build_failure(
"assert ", "assert ",
vec![ vec![
Regex::new(r"line: 1, column: 1").unwrap(), Regex::new(r"Expected Tuple \{ok=<bool>, desc=<str>\} at <eval> line: 1, column: 8")
Regex::new(r"Expected Tuple \{ok=<bool>, desc=<str>\}: at <eval> line: 1, column: 8")
.unwrap(), .unwrap(),
], ],
); );
@ -171,10 +170,9 @@ fn test_assert_partial_tuple_compile_failures() {
assert_build_failure( assert_build_failure(
"assert {", "assert {",
vec![ vec![
Regex::new(r"line: 1, column: 1").unwrap(), Regex::new(r"Expected Tuple \{ok=<bool>, desc=<str>\} at <eval> line: 1, column: 8")
Regex::new(r"Expected Tuple \{ok=<bool>, desc=<str>\}: at <eval> line: 1, column: 8")
.unwrap(), .unwrap(),
Regex::new(r"Expected \(\}\) but got \(\): at <eval> line: 1, column: 9").unwrap(), Regex::new(r"Expected \(\}\) but got \(\) at <eval> line: 1, column: 9").unwrap(),
], ],
); );
} }
@ -227,10 +225,7 @@ fn test_assert_partial_tuple_bad_desc_compile_failures() {
fn test_import_missing_path_compile_failure() { fn test_import_missing_path_compile_failure() {
assert_build_failure( assert_build_failure(
"import ;", "import ;",
vec![ vec![Regex::new(r"Expected import path at <eval> line: 1, column: 8").unwrap()],
Regex::new(r"Expected import path: at <eval> line: 1, column: 8").unwrap(),
Regex::new(r"Not a String: at <eval> line: 1, column: 8").unwrap(),
],
) )
} }
@ -238,10 +233,7 @@ fn test_import_missing_path_compile_failure() {
fn test_import_path_not_a_string_compile_failure() { fn test_import_path_not_a_string_compile_failure() {
assert_build_failure( assert_build_failure(
"import 1;", "import 1;",
vec![ vec![Regex::new(r"Expected import path at <eval> line: 1, column: 8").unwrap()],
Regex::new(r"Expected import path: at <eval> line: 1, column: 8").unwrap(),
Regex::new(r"Not a String: at <eval> line: 1, column: 8").unwrap(),
],
) )
} }
@ -250,7 +242,7 @@ fn test_binary_operator_missing_operand_compile_failure() {
assert_build_failure( assert_build_failure(
"1 +", "1 +",
vec![ vec![
Regex::new(r"Missing operand for binary expression: at <eval> line: 1, column: 4") Regex::new(r"Missing operand for binary expression at <eval> line: 1, column: 4")
.unwrap(), .unwrap(),
], ],
) )
@ -427,7 +419,6 @@ fn test_out_missing_type_compile_failure() {
"out", "out",
vec![ vec![
Regex::new(r"Expected converter name").unwrap(), Regex::new(r"Expected converter name").unwrap(),
Regex::new(r"Not a Bareword").unwrap(),
Regex::new(r"at <eval> line: 1, column: 4").unwrap(), Regex::new(r"at <eval> line: 1, column: 4").unwrap(),
], ],
) )

View File

@ -328,16 +328,7 @@ where
Some(val) => Ok(val), Some(val) => Ok(val),
} }
} }
Err(err) => { Err(err) => Err(Box::new(err)),
let cause = Box::new(simple_error::SimpleError::new(err));
Err(error::BuildError::with_pos(
"Unable to parse input",
error::ErrorType::ParseError,
(&input).into(),
)
.wrap_cause(cause)
.to_boxed())
}
} }
} }

View File

@ -145,48 +145,28 @@ impl error::Error for BuildError {
} }
} }
#[derive(Debug)] impl<'a, C> std::convert::From<abortable_parser::Error<C>> for BuildError
pub struct StackPrinter<C: FilePositioned> { where
pub err: abortable_parser::Error<C>, C: FilePositioned + 'a,
C: abortable_parser::Offsetable + Debug,
{
fn from(e: abortable_parser::Error<C>) -> BuildError {
BuildError::from(&e)
}
} }
impl<C: FilePositioned> StackPrinter<C> { impl<'a, C> std::convert::From<&'a abortable_parser::Error<C>> for BuildError
pub fn render(&self, w: &mut fmt::Formatter) -> fmt::Result { where
let mut curr_err = Some(&self.err); C: FilePositioned + 'a,
let mut tabstop = ""; C: abortable_parser::Offsetable + Debug,
loop { {
match curr_err { fn from(e: &'a abortable_parser::Error<C>) -> BuildError {
// our exit condition; let ctx = e.get_context();
None => break, let position = Position::new(ctx.line(), ctx.column(), ctx.get_offset());
Some(err) => { let err = BuildError::with_pos(e.get_msg(), ErrorType::ParseError, position);
let context = err.get_context(); match e.get_cause() {
let file = match context.file() { None => err,
Some(ref pb) => pb.to_string_lossy().to_string(), Some(cause) => err.wrap_cause(Box::new(BuildError::from(cause))),
None => "<eval>".to_string(),
};
writeln!(
w,
"{}{}: at {} line: {}, column: {}",
tabstop,
err.get_msg(),
file,
context.line(),
context.column(),
)?;
tabstop = "\t";
curr_err = err.get_cause();
if curr_err.is_some() {
writeln!(w, "Caused by:")?;
}
}
}
} }
Ok(())
}
}
impl<C: FilePositioned> fmt::Display for StackPrinter<C> {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
self.render(w)
} }
} }

View File

@ -24,7 +24,7 @@ use abortable_parser::{Error, Peekable, Result};
use self::precedence::op_expression; use self::precedence::op_expression;
use crate::ast::*; use crate::ast::*;
use crate::error::StackPrinter; use crate::error::BuildError;
use crate::iter::OffsetStrIter; use crate::iter::OffsetStrIter;
use crate::tokenizer::*; use crate::tokenizer::*;
@ -857,7 +857,7 @@ fn statement(i: SliceIter<Token>) -> Result<SliceIter<Token>, Statement> {
pub fn parse<'a>( pub fn parse<'a>(
input: OffsetStrIter<'a>, input: OffsetStrIter<'a>,
comment_map: Option<&mut CommentMap>, comment_map: Option<&mut CommentMap>,
) -> std::result::Result<Vec<Statement>, String> { ) -> std::result::Result<Vec<Statement>, BuildError> {
match tokenize(input.clone(), comment_map) { match tokenize(input.clone(), comment_map) {
Ok(tokenized) => { Ok(tokenized) => {
let mut out = Vec::new(); let mut out = Vec::new();
@ -871,20 +871,17 @@ pub fn parse<'a>(
} }
match statement(i.clone()) { match statement(i.clone()) {
Result::Abort(e) => { Result::Abort(e) => {
let ctx_err = StackPrinter { err: e }; return Err(BuildError::from(e));
return Err(format!("{}", ctx_err));
} }
Result::Fail(e) => { Result::Fail(e) => {
let ctx_err = StackPrinter { err: e }; return Err(BuildError::from(e));
return Err(format!("{}", ctx_err));
} }
Result::Incomplete(_ei) => { Result::Incomplete(_ei) => {
let err = abortable_parser::Error::new( let err = abortable_parser::Error::new(
"Unexpected end of parse input", "Unexpected end of parse input",
Box::new(i.clone()), Box::new(i.clone()),
); );
let ctx_err = StackPrinter { err: err }; return Err(BuildError::from(err));
return Err(format!("{}", ctx_err));
} }
Result::Complete(rest, stmt) => { Result::Complete(rest, stmt) => {
out.push(stmt); out.push(stmt);

View File

@ -260,11 +260,7 @@ fn parse_operand_list<'a>(i: SliceIter<'a, Token>) -> ParseResult<'a, Vec<Elemen
// if we have successfully parsed an element and an operator then // if we have successfully parsed an element and an operator then
// failing to parse a second expression is an abort since we know // failing to parse a second expression is an abort since we know
// for sure now that the next expression is supposed to be there. // for sure now that the next expression is supposed to be there.
let err = Error::caused_by( let err = Error::new("Missing operand for binary expression", Box::new(_i));
"Missing operand for binary expression",
Box::new(e),
Box::new(_i),
);
return Result::Abort(err); return Result::Abort(err);
} }
return Result::Fail(e); return Result::Fail(e);

View File

@ -20,7 +20,7 @@ use abortable_parser::iter::SliceIter;
use abortable_parser::{Error, Result}; use abortable_parser::{Error, Result};
use crate::ast::*; use crate::ast::*;
use crate::error::StackPrinter; use crate::error::BuildError;
use crate::iter::OffsetStrIter; use crate::iter::OffsetStrIter;
pub type CommentGroup = Vec<Token>; pub type CommentGroup = Vec<Token>;
@ -466,7 +466,7 @@ fn token<'a>(input: OffsetStrIter<'a>) -> Result<OffsetStrIter<'a>, Token> {
pub fn tokenize<'a>( pub fn tokenize<'a>(
input: OffsetStrIter<'a>, input: OffsetStrIter<'a>,
mut comment_map: Option<&mut CommentMap>, mut comment_map: Option<&mut CommentMap>,
) -> std::result::Result<Vec<Token>, String> { ) -> std::result::Result<Vec<Token>, BuildError> {
let mut out = Vec::new(); let mut out = Vec::new();
let mut i = input.clone(); let mut i = input.clone();
let mut comment_group = Vec::new(); let mut comment_group = Vec::new();
@ -477,28 +477,15 @@ pub fn tokenize<'a>(
} }
match token(i.clone()) { match token(i.clone()) {
Result::Abort(e) => { Result::Abort(e) => {
let err = abortable_parser::Error::caused_by( return Err(BuildError::from(e));
"Invalid Token encountered",
Box::new(e),
Box::new(i.clone()),
);
let ctx_err = StackPrinter { err: err };
return Err(format!("{}", ctx_err));
} }
Result::Fail(e) => { Result::Fail(e) => {
let err = abortable_parser::Error::caused_by( return Err(BuildError::from(e));
"Invalid Token encountered",
Box::new(e),
Box::new(i.clone()),
);
let ctx_err = StackPrinter { err: err };
return Err(format!("{}", ctx_err));
} }
Result::Incomplete(_offset) => { Result::Incomplete(_offset) => {
let err = let err =
abortable_parser::Error::new("Invalid Token encountered", Box::new(i.clone())); abortable_parser::Error::new("Invalid Token encountered", Box::new(i.clone()));
let ctx_err = StackPrinter { err: err }; return Err(BuildError::from(err));
return Err(format!("{}", ctx_err));
} }
Result::Complete(rest, tok) => { Result::Complete(rest, tok) => {
i = rest; i = rest;