Refactor: Make traits more composable and eliminate some redundancy.

This commit is contained in:
Jeremy Wall 2018-11-05 21:07:58 -06:00
parent 5cb8effcca
commit 374212aecd
5 changed files with 119 additions and 80 deletions

View File

@ -803,10 +803,16 @@ pub fn ascii_ws<'a, I: InputIter<Item = &'a u8>>(mut i: I) -> Result<I, u8> {
if (*b as char).is_whitespace() {
Result::Complete(i, *b)
} else {
Result::Fail(Error::new("Not whitespace".to_string(), Box::new(i.clone())))
Result::Fail(Error::new(
"Not whitespace".to_string(),
Box::new(i.clone()),
))
}
},
None => Result::Fail(Error::new("Unexpected End Of Input".to_string(), Box::new(i.clone()))),
}
None => Result::Fail(Error::new(
"Unexpected End Of Input".to_string(),
Box::new(i.clone()),
)),
}
}
@ -815,7 +821,10 @@ 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, ()> {
let mut _i = i.clone();
match _i.next() {
Some(_) => Result::Fail(Error::new("Expected End Of Input".to_string(), Box::new(i.clone()))),
Some(_) => Result::Fail(Error::new(
"Expected End Of Input".to_string(),
Box::new(i.clone()),
)),
None => Result::Complete(i, ()),
}
}
@ -932,17 +941,23 @@ macro_rules! consume_all {
/// ascii_digit parses a single ascii alphabetic or digit character from an InputIter of bytes.
#[inline(always)]
pub fn ascii_alphanumeric<'a, I: InputIter<Item=&'a u8>>(mut i: I) -> Result<I, u8> {
pub fn ascii_alphanumeric<'a, I: InputIter<Item = &'a u8>>(mut i: I) -> Result<I, u8> {
match i.next() {
Some(b) => {
let c = *b as char;
if c.is_ascii_alphabetic() || c.is_ascii_digit() {
Result::Complete(i, *b)
} else {
Result::Fail(Error::new("Not an alphanumeric character".to_string(), Box::new(i.clone())))
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(), Box::new(i.clone()))),
}
None => Result::Fail(Error::new(
"Unexpected End Of Input.".to_string(),
Box::new(i.clone()),
)),
}
}
@ -954,10 +969,16 @@ pub fn ascii_digit<'a, I: InputIter<Item = &'a u8>>(mut i: I) -> Result<I, u8> {
if (*b as char).is_ascii_digit() {
Result::Complete(i, *b)
} else {
Result::Fail(Error::new("Not an digit character".to_string(), Box::new(i.clone())))
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(), Box::new(i.clone()))),
}
None => Result::Fail(Error::new(
"Unexpected End Of Input.".to_string(),
Box::new(i.clone()),
)),
}
}
@ -969,10 +990,16 @@ pub fn ascii_alpha<'a, I: InputIter<Item = &'a u8>>(mut i: I) -> Result<I, u8> {
if (*b as char).is_ascii_alphabetic() {
Result::Complete(i, *b)
} else {
Result::Fail(Error::new("Not an alpha character".to_string(), Box::new(i.clone())))
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(), Box::new(i.clone()))),
}
None => Result::Fail(Error::new(
"Unexpected End Of Input.".to_string(),
Box::new(i.clone()),
)),
}
}

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use super::{ascii_ws, eoi, Result};
use iter::{StrIter, SliceIter};
use iter::{SliceIter, StrIter};
make_fn!(proto<StrIter, &str>,
do_each!(
@ -33,7 +33,8 @@ make_fn!(path<StrIter, &str>,
until!(either!(discard!(ascii_ws), eoi))
);
make_fn!(sliceit<SliceIter<u8>, ()>,
make_fn!(
sliceit<SliceIter<u8>, ()>,
do_each!(
_ => input!(),
end_of_input => eoi,
@ -41,7 +42,8 @@ make_fn!(sliceit<SliceIter<u8>, ()>,
)
);
make_fn!(long_string_path<SliceIter<u8>, ()>,
make_fn!(
long_string_path<SliceIter<u8>, ()>,
do_each!(
_ => input!(),
end_of_input => eoi,

View File

@ -16,7 +16,7 @@
use std::fmt::Debug;
use std::iter::Iterator;
use super::{InputIter, Offsetable, Seekable, Span, SpanRange, TextPositionTracker};
use super::{InputIter, Offsetable, Positioned, Seekable, Span, SpanRange};
/// Implements `InputIter` for any slice of T.
#[derive(Debug)]
@ -55,6 +55,25 @@ impl<'a, T: Debug + 'a> Offsetable for SliceIter<'a, T> {
}
}
impl<'a, It> Positioned for SliceIter<'a, It>
where
It: Positioned + Debug,
{
fn line(&self) -> usize {
match self.peek_next() {
Some(i) => i.line(),
None => 0,
}
}
fn column(&self) -> usize {
match self.peek_next() {
Some(i) => i.column(),
None => 0,
}
}
}
impl<'a, T: Debug + 'a> Clone for SliceIter<'a, T> {
fn clone(&self) -> Self {
SliceIter {
@ -149,7 +168,7 @@ impl<'a> Offsetable for StrIter<'a> {
}
}
impl<'a> TextPositionTracker for StrIter<'a> {
impl<'a> Positioned for StrIter<'a> {
fn line(&self) -> usize {
self.line
}
@ -194,11 +213,7 @@ impl<'a> Span<&'a str> for StrIter<'a> {
impl<'a> Seekable for StrIter<'a> {
fn seek(&mut self, to: usize) -> usize {
let self_len = self.source.len();
let offset = if self_len > to {
to
} else {
self_len
};
let offset = if self_len > to { to } else { self_len };
self.offset = offset;
self.offset
}

View File

@ -102,7 +102,7 @@
//! assert!(bad_result.is_abort());
//! # }
//! ```
use std::fmt::{Display,Debug};
use std::fmt::{Debug, Display};
use std::iter::Iterator;
use std::result;
@ -121,8 +121,8 @@ pub trait Seekable {
fn seek(&mut self, usize) -> usize;
}
/// Trait for Inputs that can track lines and columns in a text input.
pub trait TextPositionTracker {
/// Trait for Inputs that can report current lines and columns in a text input.
pub trait Positioned {
fn line(&self) -> usize;
fn column(&self) -> usize;
}
@ -150,20 +150,16 @@ pub trait InputIter: Iterator + Clone + Offsetable {}
/// The custom error type for use in `Result::{Fail, Abort}`.
/// Stores a wrapped err that must implement Display as well as an offset and
/// an optional cause.
#[derive(Debug,Clone)]
pub struct Error<C>
where
C: InputIter,
{
#[derive(Debug, Clone)]
pub struct Error<C> {
msg: String,
cause: Option<Box<Error<C>>>,
context: Box<C>,
}
impl<C: InputIter> Error<C> {
impl<C> Error<C> {
/// Constructs a new Error with an offset and no cause.
pub fn new<D: Into<String>>(msg: D, ctx: Box<C>) -> Self
{
pub fn new<D: Into<String>>(msg: D, ctx: Box<C>) -> Self {
Error {
msg: msg.into(),
cause: None,
@ -172,8 +168,7 @@ impl<C: InputIter> Error<C> {
}
/// Constructs a new Error with an offset and a cause.
pub fn caused_by<'a, D: Into<String>>(msg: D, cause: Box<Self>, ctx: Box<C>) -> Self
{
pub fn caused_by<'a, D: Into<String>>(msg: D, cause: Box<Self>, ctx: Box<C>) -> Self {
Error {
msg: msg.into(),
cause: Some(cause),
@ -182,32 +177,31 @@ impl<C: InputIter> Error<C> {
}
/// Returns the msg.
pub fn get_msg<'a>(&'a self) -> String {
format!("{}", &self.msg)
pub fn get_msg<'a>(&'a self) -> &str {
&self.msg
}
/// Returns `Some(cause)` if there is one, None otherwise.
pub fn get_cause<'a>(&'a self) -> Option<&'a Error<C>> {
match self.cause {
Some(ref e) => Some(e),
None => None
None => None,
}
}
// Returns the offset at which this Error happened.
pub fn get_offset(&self) -> usize {
self.context.get_offset()
}
pub fn get_context(&self) -> &C
where
C: InputIter,
{
pub fn get_context(&self) -> &C {
self.context.as_ref()
}
}
impl<C: InputIter> Display for Error<C> {
impl<C: Offsetable> Offsetable for Error<C> {
// Returns the offset at which this Error happened.
fn get_offset(&self) -> usize {
self.context.get_offset()
}
}
impl<C: Offsetable> Display for Error<C> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> result::Result<(), std::fmt::Error> {
try!(write!(f, "{}", self.msg));
match self.cause {
@ -217,12 +211,11 @@ impl<C: InputIter> Display for Error<C> {
}
}
impl<C: InputIter + Debug> std::error::Error for Error<C> {}
impl<C: Offsetable + Debug> std::error::Error for Error<C> {}
/// The result of a parsing attempt.
#[derive(Debug)]
pub enum Result<I: InputIter, O>
{
pub enum Result<I: InputIter, O> {
/// Complete represents a successful match.
Complete(I, O),
/// Incomplete indicates input ended before a match could be completed.
@ -234,8 +227,7 @@ pub enum Result<I: InputIter, O>
Abort(Error<I>),
}
impl<I: InputIter, O> Result<I, O>
{
impl<I: InputIter, O> Result<I, O> {
/// Returns true if the Result is Complete.
pub fn is_complete(&self) -> bool {
if let &Result::Complete(_, _) = self {

View File

@ -14,7 +14,7 @@
use std::fmt::{Debug, Display};
use super::{InputIter, Offsetable, Result, TextPositionTracker};
use super::{InputIter, Offsetable, Positioned, Result};
use combinators::*;
use iter::{SliceIter, StrIter};
@ -53,7 +53,10 @@ where
I: InputIter<Item = C>,
C: Debug + Display,
{
Result::Fail(super::Error::new("AAAAHHH!!!".to_string(), Box::new(i.clone())))
Result::Fail(super::Error::new(
"AAAAHHH!!!".to_string(),
Box::new(i.clone()),
))
}
fn parse_byte<'a, I>(mut i: I) -> Result<I, u8>
@ -574,7 +577,7 @@ fn test_ascii_alphanumeric() {
let iter = StrIter::new(input_str);
let result = repeat!(iter, ascii_alphanumeric);
assert!(result.is_complete());
if let Result::Complete(i,list) = result {
if let Result::Complete(i, list) = result {
assert_eq!(list.len(), 2);
assert_eq!(list[0], b'a');
assert_eq!(list[1], b'1');
@ -596,7 +599,7 @@ fn test_ascii_digit() {
let iter = StrIter::new(input_str);
let result = repeat!(iter, ascii_digit);
assert!(result.is_complete());
if let Result::Complete(i,list) = result {
if let Result::Complete(i, list) = result {
assert_eq!(list.len(), 2);
assert_eq!(list[0], b'1');
assert_eq!(list[1], b'2');
@ -618,7 +621,7 @@ fn test_ascii_alpha() {
let iter = StrIter::new(input_str);
let result = repeat!(iter, ascii_alpha);
assert!(result.is_complete());
if let Result::Complete(i,list) = result {
if let Result::Complete(i, list) = result {
assert_eq!(list.len(), 2);
assert_eq!(list[0], b'a');
assert_eq!(list[1], b'b');