mirror of
https://github.com/zaphar/abortable_parser.git
synced 2025-07-21 20:29:49 -04:00
FEATURE: Error Wrapping as a first class citizen.
This commit is contained in:
parent
00bd55874c
commit
d24df3b852
@ -2,7 +2,7 @@
|
||||
use std::iter::Iterator;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use super::InputIter;
|
||||
use super::{Offsetable, InputIter};
|
||||
|
||||
/// Implements `InputIter` for any slice of T.
|
||||
#[derive(Debug)]
|
||||
@ -35,7 +35,7 @@ impl<'a, T: Debug + 'a> Iterator for SliceIter<'a, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Debug + 'a> InputIter for SliceIter<'a, T> {
|
||||
impl<'a, T: Debug + 'a> Offsetable for SliceIter<'a, T> {
|
||||
fn get_offset(&self) -> usize {
|
||||
self.offset
|
||||
}
|
||||
@ -49,3 +49,5 @@ impl<'a, T: Debug + 'a> Clone for SliceIter<'a, T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Debug + 'a> InputIter for SliceIter<'a, T> {}
|
75
src/lib.rs
75
src/lib.rs
@ -1,26 +1,89 @@
|
||||
//! A parser combinator library with a focus on fully abortable parsing and error handling.
|
||||
use std::iter::Iterator;
|
||||
use std::fmt::Display;
|
||||
|
||||
pub trait Offsetable {
|
||||
fn get_offset(&self) -> usize;
|
||||
}
|
||||
|
||||
impl Offsetable for usize {
|
||||
fn get_offset(&self) -> usize {
|
||||
return *self;
|
||||
}
|
||||
}
|
||||
|
||||
/// A Cloneable Iterator that can report an offset as a count of processed Items.
|
||||
pub trait InputIter: Iterator + Clone {
|
||||
fn get_offset(&self) -> usize;
|
||||
pub trait InputIter: Iterator + Clone + Offsetable {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Error<E: Display> {
|
||||
err: E,
|
||||
offset: usize,
|
||||
cause: Option<Box<Error<E>>>,
|
||||
}
|
||||
|
||||
impl<E: Display> Error<E> {
|
||||
// Constructs a new Error with an offset and no cause.
|
||||
pub fn new<S: Offsetable>(err: E, offset: &S) -> Self {
|
||||
Error{
|
||||
err: err,
|
||||
offset: offset.get_offset(),
|
||||
cause: None,
|
||||
}
|
||||
}
|
||||
|
||||
// Constructs a new Error with an offset and a cause.
|
||||
pub fn caused_by<S: Offsetable>(err: E, offset: &S, cause: Self) -> Self {
|
||||
Error{
|
||||
err: err,
|
||||
offset: offset.get_offset(),
|
||||
cause: Some(Box::new(cause)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_err<'a>(&'a self) -> &'a E {
|
||||
&self.err
|
||||
}
|
||||
|
||||
pub fn get_cause<'a>(&'a self) -> Option<&'a Error<E>> {
|
||||
match self.cause {
|
||||
Some(ref cause) => Some(cause),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_offset(&self) -> usize {
|
||||
self.offset
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Display> Display for Error<E> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
|
||||
try!(write!(f, "{}", self.err));
|
||||
match self.cause {
|
||||
Some(ref c) => write!(f, "\n\tCaused By:{}", c),
|
||||
None => {
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The result of a parsing attempt.
|
||||
#[derive(Debug)]
|
||||
pub enum Result<I: InputIter, O, E> {
|
||||
pub enum Result<I: InputIter, O, E: Display> {
|
||||
/// Complete represents a successful match.
|
||||
Complete(I, O),
|
||||
/// 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.
|
||||
Incomplete(usize),
|
||||
/// Fail represents a failed match.
|
||||
Fail(E),
|
||||
Fail(Error<E>),
|
||||
/// Abort represents a match failure that the parser cannot recover from.
|
||||
Abort(E),
|
||||
Abort(Error<E>),
|
||||
}
|
||||
|
||||
impl<I: InputIter, O, E> Result<I, O, E> {
|
||||
impl<I: InputIter, O, E: Display> Result<I, O, E> {
|
||||
/// Returns true if the Result is Complete.
|
||||
pub fn is_complete(&self) -> bool {
|
||||
if let &Result::Complete(_, _) = self {
|
||||
|
@ -25,6 +25,23 @@ macro_rules! must {
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! wrap_err {
|
||||
($i:expr, $f:ident!( $( $args:tt )* ), $e:expr) => {{
|
||||
let _i = $i.clone();
|
||||
match $f!($i, $($args)*) {
|
||||
$crate::Result::Complete(i, o) => $crate::Result::Complete(i, o),
|
||||
$crate::Result::Incomplete(offset) => $crate::Result::Incomplete(offset),
|
||||
$crate::Result::Fail(e) => $crate::Result::Fail($crate::Error::caused_by($e, &_i, e)),
|
||||
$crate::Result::Abort(e) => $crate::Result::Abort($crate::Error::caused_by($e, &_i, e)),
|
||||
}
|
||||
}};
|
||||
|
||||
($i:expr, $f:ident, $e:expr) => {
|
||||
wrap_err!($i, run!($f), $e)
|
||||
};
|
||||
}
|
||||
|
||||
/// Turns Aborts into fails allowing you to trap and then convert an Abort into a normal Fail.
|
||||
#[macro_export]
|
||||
macro_rules! trap {
|
||||
@ -46,14 +63,15 @@ macro_rules! trap {
|
||||
/// to construct the errors for the Incomplete case.
|
||||
#[macro_export]
|
||||
macro_rules! must_complete {
|
||||
($i:expr, $efn:expr, $f:ident!( $( $args:tt )* ) ) => {
|
||||
($i:expr, $e:expr, $f:ident!( $( $args:tt )* ) ) => {{
|
||||
let _i = $i.clone();
|
||||
match $f!($i, $($args)*) {
|
||||
$crate::Result::Complete(i, o) => $crate::Result::Complete(i, o),
|
||||
$crate::Result::Incomplete(offset) => $crate::Result::Abort($efn(offset)),
|
||||
$crate::Result::Incomplete(ref offset) => $crate::Result::Abort($crate::Error::new($e, offset)),
|
||||
$crate::Result::Fail(e) => $crate::Result::Abort(e),
|
||||
$crate::Result::Abort(e) => $crate::Result::Abort(e),
|
||||
}
|
||||
};
|
||||
}};
|
||||
|
||||
($i:expr, $efn:expr, $f:ident) => {
|
||||
must_complete!($i, $efn, run!($f))
|
||||
@ -107,7 +125,6 @@ macro_rules! do_each {
|
||||
do_each!($i, _ => run!($f), $( $rest )* )
|
||||
};
|
||||
|
||||
// FIXME(jwall): Make this internal only.
|
||||
// Our Terminal condition
|
||||
($i:expr, ( $($rest:tt)* ) ) => {
|
||||
Result::Complete($i, ($($rest)*))
|
||||
|
34
src/test.rs
34
src/test.rs
@ -1,5 +1,5 @@
|
||||
use super::iter::SliceIter;
|
||||
use super::{Result, InputIter};
|
||||
use super::{Result, Offsetable};
|
||||
|
||||
#[test]
|
||||
fn test_slice_iter() {
|
||||
@ -31,8 +31,8 @@ fn test_slice_iter() {
|
||||
assert_eq!('o' as u8, out[2]);
|
||||
}
|
||||
|
||||
fn will_fail(_: SliceIter<u8>) -> Result<SliceIter<u8>, String, String> {
|
||||
Result::Fail("AAAAHHH!!!".to_string())
|
||||
fn will_fail(i: SliceIter<u8>) -> Result<SliceIter<u8>, String, String> {
|
||||
Result::Fail(super::Error::new("AAAAHHH!!!".to_string(), &i))
|
||||
}
|
||||
|
||||
fn parse_byte(mut i: SliceIter<u8>) -> Result<SliceIter<u8>, u8, String> {
|
||||
@ -66,6 +66,30 @@ fn parse_three(i: SliceIter<u8>) -> Result<SliceIter<u8>, String, String> {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wrap_err_fail() {
|
||||
let input_str = "foo";
|
||||
let iter = SliceIter::new(input_str.as_bytes());
|
||||
let result = wrap_err!(iter, will_fail, "haha!".to_string());
|
||||
assert!(result.is_fail());
|
||||
if let Result::Fail(e) = result {
|
||||
assert!(e.get_cause().is_some());
|
||||
assert_eq!("AAAAHHH!!!", e.get_cause().unwrap().get_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wrap_err_abort() {
|
||||
let input_str = "foo";
|
||||
let iter = SliceIter::new(input_str.as_bytes());
|
||||
let result = wrap_err!(iter, must!(will_fail), "haha!".to_string());
|
||||
assert!(result.is_abort());
|
||||
if let Result::Abort(e) = result {
|
||||
assert!(e.get_cause().is_some());
|
||||
assert_eq!("AAAAHHH!!!", e.get_cause().unwrap().get_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_must_fails() {
|
||||
let input_str = "foo";
|
||||
@ -119,9 +143,9 @@ fn test_must_complete() {
|
||||
let input_str = "foo";
|
||||
let iter = SliceIter::new(input_str.as_bytes());
|
||||
let iter_fail = iter.clone();
|
||||
let mut result = must_complete!(iter, |_| "AHHH".to_string(), will_not_complete);
|
||||
let mut result = must_complete!(iter, "AHHH".to_string(), will_not_complete);
|
||||
assert!(result.is_abort());
|
||||
result = must_complete!(iter_fail, |_| "AHHH".to_string(), will_fail);
|
||||
result = must_complete!(iter_fail, "AHHH".to_string(), will_fail);
|
||||
assert!(result.is_abort());
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user