mirror of
https://github.com/zaphar/abortable_parser.git
synced 2025-07-23 20:49:49 -04:00
FEATURE: More helper macros and parser combinators.
This commit is contained in:
parent
beca789911
commit
a5ca5a92ce
@ -675,4 +675,78 @@ macro_rules! until {
|
|||||||
($i:expr, $term:ident) => {
|
($i:expr, $term:ident) => {
|
||||||
consume_until!($i, run!($term))
|
consume_until!($i, run!($term))
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Discards the output of a combinator rule when it completes and just returns `()`.
|
||||||
|
/// Leaves Failures, Aborts, and Incompletes untouched.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! discard {
|
||||||
|
($i:expr, $term:ident) => {
|
||||||
|
discard!($i, run!($term))
|
||||||
|
};
|
||||||
|
|
||||||
|
($i:expr, $term:ident!( $( $args:tt )* ) ) => {{
|
||||||
|
use $crate::Result;
|
||||||
|
match $term!($i, $($args)*) {
|
||||||
|
Result::Complete(i, _) => Result::Complete(i, ()),
|
||||||
|
Result::Incomplete(offset) => Result::Incomplete(offset),
|
||||||
|
Result::Fail(e) => Result::Fail(e),
|
||||||
|
Result::Abort(e) => Result::Abort(e),
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Matches and returns any ascii charactar whitespace byte.
|
||||||
|
pub fn ascii_ws<'a, I: InputIter<Item=&'a u8>>(mut i: I) -> Result<I, u8> {
|
||||||
|
match i.next() {
|
||||||
|
Some(b) => {
|
||||||
|
match b {
|
||||||
|
b'\r' => Result::Complete(i, *b),
|
||||||
|
b'\n' => Result::Complete(i, *b),
|
||||||
|
b'\t' => Result::Complete(i, *b),
|
||||||
|
b' ' => Result::Complete(i, *b),
|
||||||
|
_ => Result::Fail(Error::new("Not whitespace", &i)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
Result::Fail(Error::new("Unexpected End Of Input", &i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Matches the end of input for any InputIter.
|
||||||
|
/// Returns `()` for any match.
|
||||||
|
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", &i)),
|
||||||
|
None => Result::Complete(i, ()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// constructs a function named $name that takes an input of type $i and produces an output
|
||||||
|
/// of type $o.
|
||||||
|
///
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! make_fn {
|
||||||
|
($name:ident<$i:ty, $o:ty>, $rule:ident!($( $body:tt )* )) => {
|
||||||
|
fn $name(i: $i) -> Result<$i,$o> {
|
||||||
|
$rule!(i, $($body)*)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(pub $name:ident<$i:ty, $o:ty>, $rule:ident!($( $body:tt )* )) => {
|
||||||
|
pub fn $name(i: $i) -> Result<$i,$o> {
|
||||||
|
$rule!(i, $($body)*)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
($name:ident<$i:ty, $o:ty>, $rule:ident) => {
|
||||||
|
make_fn!($name<$i, $o>, run!($rule))
|
||||||
|
};
|
||||||
|
|
||||||
|
(pub $name:ident<$i:ty, $o:ty>, $rule:ident) => {
|
||||||
|
make_fn!(pub $name<$i, $o>, run!($rule))
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
42
src/integration_tests.rs
Normal file
42
src/integration_tests.rs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
use iter::StrIter;
|
||||||
|
use super::{Result, eoi, ascii_ws};
|
||||||
|
|
||||||
|
make_fn!(proto<StrIter, &str>,
|
||||||
|
do_each!(
|
||||||
|
proto => until!(text_token!("://")),
|
||||||
|
_ => must!(text_token!("://")),
|
||||||
|
(proto)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
make_fn!(domain<StrIter, &str>,
|
||||||
|
until!(either!(
|
||||||
|
discard!(text_token!("/")),
|
||||||
|
discard!(ascii_ws),
|
||||||
|
eoi))
|
||||||
|
);
|
||||||
|
|
||||||
|
make_fn!(path<StrIter, &str>,
|
||||||
|
until!(either!(discard!(ascii_ws), eoi))
|
||||||
|
);
|
||||||
|
|
||||||
|
make_fn!(pub url<StrIter, (Option<&str>, Option<&str>, &str)>,
|
||||||
|
do_each!(
|
||||||
|
protocol => optional!(proto),
|
||||||
|
domain => optional!(domain),
|
||||||
|
path => path,
|
||||||
|
(protocol, domain, path)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_url_parser() {
|
||||||
|
let iter = StrIter::new("http://example.com/some/path ");
|
||||||
|
let result = url(iter);
|
||||||
|
assert!(result.is_complete());
|
||||||
|
if let Result::Complete(_, (proto, domain, path)) = result {
|
||||||
|
assert!(proto.is_some());
|
||||||
|
assert!(domain.is_some());
|
||||||
|
assert_eq!(path, "/some/path");
|
||||||
|
}
|
||||||
|
}
|
51
src/lib.rs
51
src/lib.rs
@ -1,4 +1,52 @@
|
|||||||
//! An opinionated parser combinator library with a focus on fully abortable parsing and error handling.
|
//! An opinionated parser combinator library with a focus on fully abortable parsing and error handling.
|
||||||
|
//!
|
||||||
|
//! # Example
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! #[macro_use]
|
||||||
|
//! extern crate abortable_parser;
|
||||||
|
//! use abortable_parser::iter::StrIter;
|
||||||
|
//! use abortable_parser::{Result, eoi, ascii_ws};
|
||||||
|
//!
|
||||||
|
//! make_fn!(proto<StrIter, &str>,
|
||||||
|
//! do_each!(
|
||||||
|
//! proto => until!(text_token!("://")),
|
||||||
|
//! _ => must!(text_token!("://")),
|
||||||
|
//! (proto)
|
||||||
|
//! )
|
||||||
|
//! );
|
||||||
|
//!
|
||||||
|
//! make_fn!(domain<StrIter, &str>,
|
||||||
|
//! until!(either!(
|
||||||
|
//! discard!(text_token!("/")),
|
||||||
|
//! discard!(ascii_ws),
|
||||||
|
//! eoi))
|
||||||
|
//! );
|
||||||
|
//!
|
||||||
|
//! make_fn!(path<StrIter, &str>,
|
||||||
|
//! until!(either!(discard!(ascii_ws), eoi))
|
||||||
|
//! );
|
||||||
|
//!
|
||||||
|
//! make_fn!(url<StrIter, (Option<&str>, Option<&str>, &str)>,
|
||||||
|
//! do_each!(
|
||||||
|
//! protocol => optional!(proto),
|
||||||
|
//! domain => optional!(domain),
|
||||||
|
//! path => path,
|
||||||
|
//! (protocol, domain, path)
|
||||||
|
//! )
|
||||||
|
//! );
|
||||||
|
//!
|
||||||
|
//! # fn main() {
|
||||||
|
//! let iter = StrIter::new("http://example.com/some/path ");
|
||||||
|
//! let result = url(iter);
|
||||||
|
//! assert!(result.is_complete());
|
||||||
|
//! if let Result::Complete(_, (proto, domain, path)) = result {
|
||||||
|
//! assert!(proto.is_some());
|
||||||
|
//! assert!(domain.is_some());
|
||||||
|
//! assert_eq!(path, "/some/path");
|
||||||
|
//! }
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::iter::Iterator;
|
use std::iter::Iterator;
|
||||||
|
|
||||||
@ -142,6 +190,7 @@ impl<I: InputIter, O> Result<I, O> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub use iter::SliceIter;
|
pub use iter::SliceIter;
|
||||||
|
pub use combinators::*;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod combinators;
|
pub mod combinators;
|
||||||
@ -149,3 +198,5 @@ pub mod iter;
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test;
|
mod test;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod integration_tests;
|
84
src/test.rs
84
src/test.rs
@ -2,6 +2,7 @@ use std::fmt::{Debug, Display};
|
|||||||
|
|
||||||
use super::{InputIter, Offsetable, Result};
|
use super::{InputIter, Offsetable, Result};
|
||||||
use iter::{StrIter, SliceIter};
|
use iter::{StrIter, SliceIter};
|
||||||
|
use combinators::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_slice_iter() {
|
fn test_slice_iter() {
|
||||||
@ -377,4 +378,87 @@ fn test_until_incomplete() {
|
|||||||
let iter = SliceIter::new(input_str.as_bytes());
|
let iter = SliceIter::new(input_str.as_bytes());
|
||||||
let result = until!(iter, text_token!("; "));
|
let result = until!(iter, text_token!("; "));
|
||||||
assert!(result.is_incomplete());
|
assert!(result.is_incomplete());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_discard_success() {
|
||||||
|
let input_str = "foo";
|
||||||
|
let iter = SliceIter::new(input_str.as_bytes());
|
||||||
|
let result = discard!(iter, text_token!("foo"));
|
||||||
|
assert!(result.is_complete());
|
||||||
|
if let Result::Complete(_, o) = result {
|
||||||
|
assert_eq!(o, ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_discard_fail() {
|
||||||
|
let input_str = "foo";
|
||||||
|
let iter = SliceIter::new(input_str.as_bytes());
|
||||||
|
let result = discard!(iter, will_fail);
|
||||||
|
assert!(result.is_fail());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_discard_abort() {
|
||||||
|
let input_str = "foo";
|
||||||
|
let iter = SliceIter::new(input_str.as_bytes());
|
||||||
|
let result = discard!(iter, must!(will_fail));
|
||||||
|
assert!(result.is_abort());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_discard_incomplete() {
|
||||||
|
let input_str = "foo";
|
||||||
|
let iter = SliceIter::new(input_str.as_bytes());
|
||||||
|
let result = discard!(iter, will_not_complete);
|
||||||
|
assert!(result.is_incomplete());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_eoi_success() {
|
||||||
|
let input_str = "";
|
||||||
|
let iter = StrIter::new(input_str);
|
||||||
|
let result = eoi(iter);
|
||||||
|
assert!(result.is_complete());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_eoi_fail() {
|
||||||
|
let input_str = " ";
|
||||||
|
let iter = StrIter::new(input_str);
|
||||||
|
let result = eoi(iter);
|
||||||
|
assert!(result.is_fail());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ascii_ws_space() {
|
||||||
|
let input_str = " ";
|
||||||
|
let iter = StrIter::new(input_str);
|
||||||
|
let result = ascii_ws(iter);
|
||||||
|
assert!(result.is_complete());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ascii_ws_tab() {
|
||||||
|
let input_str = "\t";
|
||||||
|
let iter = StrIter::new(input_str);
|
||||||
|
let result = ascii_ws(iter);
|
||||||
|
assert!(result.is_complete());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ascii_ws_newline() {
|
||||||
|
let input_str = "\n";
|
||||||
|
let iter = StrIter::new(input_str);
|
||||||
|
let result = ascii_ws(iter);
|
||||||
|
assert!(result.is_complete());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ascii_ws_carriage_return() {
|
||||||
|
let input_str = "\r";
|
||||||
|
let iter = StrIter::new(input_str);
|
||||||
|
let result = ascii_ws(iter);
|
||||||
|
assert!(result.is_complete());
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user