FEATURE: Pass our input around as context for errors and incompletes.

This commit is contained in:
Jeremy Wall 2018-10-15 17:22:15 -05:00
parent de0c03398c
commit 5cb8effcca
4 changed files with 70 additions and 65 deletions

View File

@ -1,6 +1,6 @@
[package] [package]
name = "abortable_parser" name = "abortable_parser"
version = "0.1.0" version = "0.2.0"
authors = ["Jeremy Wall <jeremy@marzhillstudios.com>"] authors = ["Jeremy Wall <jeremy@marzhillstudios.com>"]
description = "A parser combinator library with an emphasis on error handling" description = "A parser combinator library with an emphasis on error handling"
repository = "https://github.com/zaphar/abortable_parser" repository = "https://github.com/zaphar/abortable_parser"

View File

@ -28,11 +28,11 @@ where
{ {
match result { match result {
Result::Complete(i, _) => Result::Fail(Error::new( Result::Complete(i, _) => Result::Fail(Error::new(
"Matched on input when we shouldn't have.".to_string(), "Matched on input when we shouldn't have.",
&i, Box::new(i.clone()),
)), )),
Result::Abort(e) => Result::Abort(e), Result::Abort(e) => Result::Abort(e),
Result::Incomplete(offset) => Result::Incomplete(offset), Result::Incomplete(ctx) => Result::Incomplete(ctx),
Result::Fail(_) => Result::Complete(i, ()), Result::Fail(_) => Result::Complete(i, ()),
} }
} }
@ -95,7 +95,7 @@ macro_rules! peek {
let _i = $i.clone(); let _i = $i.clone();
match $f!(_i, $($args)*) { match $f!(_i, $($args)*) {
Result::Complete(_, o) => Result::Complete($i, o), Result::Complete(_, o) => Result::Complete($i, o),
Result::Incomplete(offset) => Result::Incomplete(offset), Result::Incomplete(ctx) => Result::Incomplete(ctx),
Result::Abort(e) => Result::Abort(e), Result::Abort(e) => Result::Abort(e),
Result::Fail(e) => Result::Fail(e), Result::Fail(e) => Result::Fail(e),
} }
@ -129,7 +129,7 @@ where
{ {
match result { match result {
Result::Complete(i, o) => Result::Complete(i, o), Result::Complete(i, o) => Result::Complete(i, o),
Result::Incomplete(offset) => Result::Incomplete(offset), Result::Incomplete(ctx) => Result::Incomplete(ctx),
Result::Fail(e) => Result::Abort(e), Result::Fail(e) => Result::Abort(e),
Result::Abort(e) => Result::Abort(e), Result::Abort(e) => Result::Abort(e),
} }
@ -174,9 +174,9 @@ macro_rules! wrap_err {
let _i = $i.clone(); let _i = $i.clone();
match $f!($i, $($args)*) { match $f!($i, $($args)*) {
$crate::Result::Complete(i, o) => $crate::Result::Complete(i, o), $crate::Result::Complete(i, o) => $crate::Result::Complete(i, o),
$crate::Result::Incomplete(offset) => $crate::Result::Incomplete(offset), $crate::Result::Incomplete(ctx) => $crate::Result::Incomplete(ctx),
$crate::Result::Fail(e) => $crate::Result::Fail($crate::Error::caused_by($e, &_i, Box::new(e))), $crate::Result::Fail(e) => $crate::Result::Fail($crate::Error::caused_by($e, Box::new(e), Box::new(_i.clone()))),
$crate::Result::Abort(e) => $crate::Result::Abort($crate::Error::caused_by($e, &_i, Box::new(e))), $crate::Result::Abort(e) => $crate::Result::Abort($crate::Error::caused_by($e, Box::new(e), Box::new(_i.clone()))),
} }
}}; }};
@ -200,7 +200,7 @@ where
{ {
match result { match result {
Result::Complete(i, o) => Result::Complete(i, o), Result::Complete(i, o) => Result::Complete(i, o),
Result::Incomplete(offset) => Result::Incomplete(offset), Result::Incomplete(ctx) => Result::Incomplete(ctx),
Result::Fail(e) => Result::Fail(e), Result::Fail(e) => Result::Fail(e),
Result::Abort(e) => Result::Fail(e), Result::Abort(e) => Result::Fail(e),
} }
@ -242,7 +242,7 @@ where
{ {
match result { match result {
Result::Complete(i, o) => Result::Complete(i, o), Result::Complete(i, o) => Result::Complete(i, o),
Result::Incomplete(ref offset) => Result::Abort(Error::new(msg, offset)), Result::Incomplete(ctx) => Result::Abort(Error::new(msg, Box::new(ctx))),
Result::Fail(e) => Result::Abort(e), Result::Fail(e) => Result::Abort(e),
Result::Abort(e) => Result::Abort(e), Result::Abort(e) => Result::Abort(e),
} }
@ -255,7 +255,7 @@ where
S: Into<String>, S: Into<String>,
{ {
match result { match result {
Result::Incomplete(offset) => Result::Fail(Error::new(msg.into(), &offset)), Result::Incomplete(ctx) => Result::Fail(Error::new(msg.into(), Box::new(ctx))),
Result::Complete(i, o) => Result::Complete(i, o), Result::Complete(i, o) => Result::Complete(i, o),
Result::Fail(e) => Result::Fail(e), Result::Fail(e) => Result::Fail(e),
Result::Abort(e) => Result::Abort(e), Result::Abort(e) => Result::Abort(e),
@ -291,7 +291,7 @@ macro_rules! complete {
#[macro_export] #[macro_export]
macro_rules! must_complete { macro_rules! must_complete {
($i:expr, $e:expr, $f:ident!( $( $args:tt )* ) ) => {{ ($i:expr, $e:expr, $f:ident!( $( $args:tt )* ) ) => {{
$crate::combinators::must_complete($f!($i, $($args)*), $e) $crate::combinators::must_complete($f!($i.clone(), $($args)*), $e)
}}; }};
($i:expr, $efn:expr, $f:ident) => { ($i:expr, $efn:expr, $f:ident) => {
@ -362,8 +362,8 @@ macro_rules! do_each {
let $val = o; let $val = o;
do_each!(i, $($rest)*) do_each!(i, $($rest)*)
} }
$crate::Result::Incomplete(offset) => { $crate::Result::Incomplete(ctx) => {
Result::Incomplete(offset) Result::Incomplete(ctx)
} }
$crate::Result::Fail(e) => Result::Fail(e), $crate::Result::Fail(e) => Result::Fail(e),
$crate::Result::Abort(e) => Result::Abort(e), $crate::Result::Abort(e) => Result::Abort(e),
@ -376,8 +376,8 @@ macro_rules! do_each {
$crate::Result::Complete(i, _) => { $crate::Result::Complete(i, _) => {
do_each!(i, $($rest)*) do_each!(i, $($rest)*)
} }
$crate::Result::Incomplete(offset) => { $crate::Result::Incomplete(ctx) => {
Result::Incomplete(offset) Result::Incomplete(ctx)
} }
$crate::Result::Fail(e) => Result::Fail(e), $crate::Result::Fail(e) => Result::Fail(e),
$crate::Result::Abort(e) => Result::Abort(e), $crate::Result::Abort(e) => Result::Abort(e),
@ -462,8 +462,8 @@ macro_rules! either {
Result::Complete(i, o) Result::Complete(i, o)
} }
// Incompletes may still be parseable. // Incompletes may still be parseable.
$crate::Result::Incomplete(i) => { $crate::Result::Incomplete(ctx) => {
Result::Incomplete(i) Result::Incomplete(ctx)
} }
// Fail means it didn't match so we are now done. // Fail means it didn't match so we are now done.
$crate::Result::Fail(e) => { $crate::Result::Fail(e) => {
@ -483,8 +483,8 @@ macro_rules! either {
Result::Complete(i, o) Result::Complete(i, o)
} }
// Incompletes may still be parseable. // Incompletes may still be parseable.
$crate::Result::Incomplete(i) => { $crate::Result::Incomplete(ctx) => {
Result::Incomplete(i) Result::Incomplete(ctx)
} }
// Fail means it didn't match so continue to next one. // Fail means it didn't match so continue to next one.
$crate::Result::Fail(_) => { $crate::Result::Fail(_) => {
@ -516,7 +516,7 @@ where
match result { match result {
Result::Complete(i, o) => Result::Complete(i, Some(o)), Result::Complete(i, o) => Result::Complete(i, Some(o)),
// Incomplete could still work possibly parse. // Incomplete could still work possibly parse.
Result::Incomplete(i) => Result::Incomplete(i), Result::Incomplete(ctx) => Result::Incomplete(ctx),
// Fail just means it didn't match. // Fail just means it didn't match.
Result::Fail(_) => Result::Complete(iter, None), Result::Fail(_) => Result::Complete(iter, None),
// Aborts are hard failures that the parser can't recover from. // Aborts are hard failures that the parser can't recover from.
@ -644,7 +644,7 @@ macro_rules! separated {
// We require at least one item for our list // We require at least one item for our list
let head = $item_rule!($i.clone(), $($item_args)*); let head = $item_rule!($i.clone(), $($item_args)*);
match head { match head {
Result::Incomplete(offset) => Result::Incomplete(offset), Result::Incomplete(ctx) => Result::Incomplete(ctx),
Result::Fail(e) => Result::Fail(e), Result::Fail(e) => Result::Fail(e),
Result::Abort(e) => Result::Abort(e), Result::Abort(e) => Result::Abort(e),
Result::Complete(i,item) => { Result::Complete(i,item) => {
@ -659,7 +659,7 @@ macro_rules! separated {
); );
match tail_result { match tail_result {
Result::Fail(e) => Result::Fail(e), Result::Fail(e) => Result::Fail(e),
Result::Incomplete(offset) => Result::Incomplete(offset), Result::Incomplete(ctx) => Result::Incomplete(ctx),
Result::Abort(e) => Result::Abort(e), Result::Abort(e) => Result::Abort(e),
Result::Complete(i, mut tail) => { Result::Complete(i, mut tail) => {
list.extend(tail.drain(0..)); list.extend(tail.drain(0..));
@ -721,7 +721,7 @@ macro_rules! text_token {
} else { } else {
Result::Fail(Error::new( Result::Fail(Error::new(
format!("Expected {} but didn't get it.", $e), format!("Expected {} but didn't get it.", $e),
&$i, Box::new($i.clone()),
)) ))
} }
}}; }};
@ -759,13 +759,13 @@ macro_rules! until {
return Result::Complete(_i, $i.span(range)); return Result::Complete(_i, $i.span(range));
}, },
Result::Abort(e) => return Result::Abort(e), Result::Abort(e) => return Result::Abort(e),
Result::Incomplete(offset) => return Result::Incomplete(offset), Result::Incomplete(ctx) => return Result::Incomplete(ctx),
Result::Fail(_) => { Result::Fail(_) => {
// noop // noop
} }
} }
if let None = _i.next() { if let None = _i.next() {
return Result::Incomplete(_i.get_offset()); return Result::Incomplete(_i.clone());
} }
} }
}; };
@ -789,7 +789,7 @@ macro_rules! discard {
use $crate::Result; use $crate::Result;
match $rule!($i, $($args)*) { match $rule!($i, $($args)*) {
Result::Complete(i, _) => Result::Complete(i, ()), Result::Complete(i, _) => Result::Complete(i, ()),
Result::Incomplete(offset) => Result::Incomplete(offset), Result::Incomplete(ctx) => Result::Incomplete(ctx),
Result::Fail(e) => Result::Fail(e), Result::Fail(e) => Result::Fail(e),
Result::Abort(e) => Result::Abort(e), Result::Abort(e) => Result::Abort(e),
} }
@ -803,10 +803,10 @@ pub fn ascii_ws<'a, I: InputIter<Item = &'a u8>>(mut i: I) -> Result<I, u8> {
if (*b as char).is_whitespace() { if (*b as char).is_whitespace() {
Result::Complete(i, *b) Result::Complete(i, *b)
} else { } else {
Result::Fail(Error::new("Not whitespace".to_string(), &i)) Result::Fail(Error::new("Not whitespace".to_string(), Box::new(i.clone())))
} }
}, },
None => Result::Fail(Error::new("Unexpected End Of Input".to_string(), &i)), None => Result::Fail(Error::new("Unexpected End Of Input".to_string(), Box::new(i.clone()))),
} }
} }
@ -815,7 +815,7 @@ pub fn ascii_ws<'a, I: InputIter<Item = &'a u8>>(mut i: I) -> Result<I, u8> {
pub fn eoi<I: InputIter>(i: I) -> Result<I, ()> { pub fn eoi<I: InputIter>(i: I) -> Result<I, ()> {
let mut _i = i.clone(); let mut _i = i.clone();
match _i.next() { match _i.next() {
Some(_) => Result::Fail(Error::new("Expected End Of Input".to_string(), &i)), Some(_) => Result::Fail(Error::new("Expected End Of Input".to_string(), Box::new(i.clone()))),
None => Result::Complete(i, ()), None => Result::Complete(i, ()),
} }
} }
@ -911,14 +911,14 @@ macro_rules! consume_all {
// noop // noop
}, },
Result::Abort(e) => return Result::Abort(e), Result::Abort(e) => return Result::Abort(e),
Result::Incomplete(offset) => return Result::Incomplete(offset), Result::Incomplete(ctx) => return Result::Incomplete(ctx),
Result::Fail(_) => { Result::Fail(_) => {
let range = SpanRange::Range(start_offset.._i.get_offset()); let range = SpanRange::Range(start_offset.._i.get_offset());
return Result::Complete(_i, $i.span(range)); return Result::Complete(_i, $i.span(range));
} }
} }
if let None = _i.next() { if let None = _i.next() {
return Result::Incomplete(_i.get_offset()); return Result::Incomplete(_i.clone());
} }
} }
}; };
@ -939,10 +939,10 @@ pub fn ascii_alphanumeric<'a, I: InputIter<Item=&'a u8>>(mut i: I) -> Result<I,
if c.is_ascii_alphabetic() || c.is_ascii_digit() { if c.is_ascii_alphabetic() || c.is_ascii_digit() {
Result::Complete(i, *b) Result::Complete(i, *b)
} else { } else {
Result::Fail(Error::new("Not an alphanumeric character".to_string(), &i)) Result::Fail(Error::new("Not an alphanumeric character".to_string(), Box::new(i.clone())))
} }
}, },
None => Result::Fail(Error::new("Unexpected End Of Input.".to_string(), &i)), None => Result::Fail(Error::new("Unexpected End Of Input.".to_string(), Box::new(i.clone()))),
} }
} }
@ -954,10 +954,10 @@ pub fn ascii_digit<'a, I: InputIter<Item = &'a u8>>(mut i: I) -> Result<I, u8> {
if (*b as char).is_ascii_digit() { if (*b as char).is_ascii_digit() {
Result::Complete(i, *b) Result::Complete(i, *b)
} else { } else {
Result::Fail(Error::new("Not an digit character".to_string(), &i)) Result::Fail(Error::new("Not an digit character".to_string(), Box::new(i.clone())))
} }
}, },
None => Result::Fail(Error::new("Unexpected End Of Input.".to_string(), &i)), None => Result::Fail(Error::new("Unexpected End Of Input.".to_string(), Box::new(i.clone()))),
} }
} }
@ -969,10 +969,10 @@ pub fn ascii_alpha<'a, I: InputIter<Item = &'a u8>>(mut i: I) -> Result<I, u8> {
if (*b as char).is_ascii_alphabetic() { if (*b as char).is_ascii_alphabetic() {
Result::Complete(i, *b) Result::Complete(i, *b)
} else { } else {
Result::Fail(Error::new("Not an alpha character".to_string(), &i)) Result::Fail(Error::new("Not an alpha character".to_string(), Box::new(i.clone())))
} }
}, },
None => Result::Fail(Error::new("Unexpected End Of Input.".to_string(), &i)), None => Result::Fail(Error::new("Unexpected End Of Input.".to_string(), Box::new(i.clone()))),
} }
} }

View File

@ -102,7 +102,7 @@
//! assert!(bad_result.is_abort()); //! assert!(bad_result.is_abort());
//! # } //! # }
//! ``` //! ```
use std::fmt::Display; use std::fmt::{Display,Debug};
use std::iter::Iterator; use std::iter::Iterator;
use std::result; use std::result;
@ -150,36 +150,34 @@ pub trait InputIter: Iterator + Clone + Offsetable {}
/// The custom error type for use in `Result::{Fail, Abort}`. /// The custom error type for use in `Result::{Fail, Abort}`.
/// Stores a wrapped err that must implement Display as well as an offset and /// Stores a wrapped err that must implement Display as well as an offset and
/// an optional cause. /// an optional cause.
#[derive(Debug)] #[derive(Debug,Clone)]
pub struct Error pub struct Error<C>
where
C: InputIter,
{ {
msg: String, msg: String,
offset: usize, cause: Option<Box<Error<C>>>,
cause: Option<Box<Error>>, context: Box<C>,
} }
impl Error { impl<C: InputIter> Error<C> {
/// Constructs a new Error with an offset and no cause. /// Constructs a new Error with an offset and no cause.
pub fn new<S, D: Into<String>>(msg: D, offset: &S) -> Self pub fn new<D: Into<String>>(msg: D, ctx: Box<C>) -> Self
where
S: Offsetable,
{ {
Error { Error {
msg: msg.into(), msg: msg.into(),
offset: offset.get_offset(),
cause: None, cause: None,
context: ctx,
} }
} }
/// Constructs a new Error with an offset and a cause. /// Constructs a new Error with an offset and a cause.
pub fn caused_by<'a, S, D: Into<String>>(msg: D, offset: &'a S, cause: Box<Self>) -> Self pub fn caused_by<'a, D: Into<String>>(msg: D, cause: Box<Self>, ctx: Box<C>) -> Self
where
S: Offsetable,
{ {
Error { Error {
msg: msg.into(), msg: msg.into(),
offset: offset.get_offset(),
cause: Some(cause), cause: Some(cause),
context: ctx,
} }
} }
@ -189,7 +187,7 @@ impl Error {
} }
/// Returns `Some(cause)` if there is one, None otherwise. /// Returns `Some(cause)` if there is one, None otherwise.
pub fn get_cause<'a>(&'a self) -> Option<&'a Error> { pub fn get_cause<'a>(&'a self) -> Option<&'a Error<C>> {
match self.cause { match self.cause {
Some(ref e) => Some(e), Some(ref e) => Some(e),
None => None None => None
@ -198,11 +196,18 @@ impl Error {
// Returns the offset at which this Error happened. // Returns the offset at which this Error happened.
pub fn get_offset(&self) -> usize { pub fn get_offset(&self) -> usize {
self.offset self.context.get_offset()
}
pub fn get_context(&self) -> &C
where
C: InputIter,
{
self.context.as_ref()
} }
} }
impl Display for Error { impl<C: InputIter> Display for Error<C> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> result::Result<(), std::fmt::Error> { fn fmt(&self, f: &mut std::fmt::Formatter) -> result::Result<(), std::fmt::Error> {
try!(write!(f, "{}", self.msg)); try!(write!(f, "{}", self.msg));
match self.cause { match self.cause {
@ -212,7 +217,7 @@ impl Display for Error {
} }
} }
impl std::error::Error for Error {} impl<C: InputIter + Debug> std::error::Error for Error<C> {}
/// The result of a parsing attempt. /// The result of a parsing attempt.
#[derive(Debug)] #[derive(Debug)]
@ -222,11 +227,11 @@ pub enum Result<I: InputIter, O>
Complete(I, O), Complete(I, O),
/// Incomplete indicates input ended before a match could be completed. /// Incomplete indicates input ended before a match could be completed.
/// It contains the offset at which the input ended before a match could be completed. /// It contains the offset at which the input ended before a match could be completed.
Incomplete(usize), Incomplete(I),
/// Fail represents a failed match. /// Fail represents a failed match.
Fail(Error), Fail(Error<I>),
/// Abort represents a match failure that the parser cannot recover from. /// Abort represents a match failure that the parser cannot recover from.
Abort(Error), Abort(Error<I>),
} }
impl<I: InputIter, O> Result<I, O> impl<I: InputIter, O> Result<I, O>

View File

@ -53,7 +53,7 @@ where
I: InputIter<Item = C>, I: InputIter<Item = C>,
C: Debug + Display, C: Debug + Display,
{ {
Result::Fail(super::Error::new("AAAAHHH!!!".to_string(), &i)) Result::Fail(super::Error::new("AAAAHHH!!!".to_string(), Box::new(i.clone())))
} }
fn parse_byte<'a, I>(mut i: I) -> Result<I, u8> fn parse_byte<'a, I>(mut i: I) -> Result<I, u8>
@ -62,15 +62,15 @@ where
{ {
match i.next() { match i.next() {
Some(b) => Result::Complete(i, *b), Some(b) => Result::Complete(i, *b),
None => Result::Incomplete(i.get_offset()), None => Result::Incomplete(i.clone()),
} }
} }
fn will_not_complete<'a, I>(_: I) -> Result<I, String> fn will_not_complete<'a, I>(i: I) -> Result<I, String>
where where
I: InputIter<Item = &'a u8>, I: InputIter<Item = &'a u8>,
{ {
Result::Incomplete(0) Result::Incomplete(i)
} }
fn parse_three<'a, I>(i: I) -> Result<I, String> fn parse_three<'a, I>(i: I) -> Result<I, String>
@ -90,7 +90,7 @@ where
} }
} }
if out.len() != 3 { if out.len() != 3 {
Result::Incomplete(_i.get_offset()) Result::Incomplete(_i)
} else { } else {
Result::Complete(_i, out) Result::Complete(_i, out)
} }