mirror of
https://github.com/zaphar/abortable_parser.git
synced 2025-07-22 20:39:50 -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) => {
|
||||
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.
|
||||
//!
|
||||
//! # 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::iter::Iterator;
|
||||
|
||||
@ -142,6 +190,7 @@ impl<I: InputIter, O> Result<I, O> {
|
||||
}
|
||||
|
||||
pub use iter::SliceIter;
|
||||
pub use combinators::*;
|
||||
|
||||
#[macro_use]
|
||||
pub mod combinators;
|
||||
@ -149,3 +198,5 @@ pub mod iter;
|
||||
|
||||
#[cfg(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 iter::{StrIter, SliceIter};
|
||||
use combinators::*;
|
||||
|
||||
#[test]
|
||||
fn test_slice_iter() {
|
||||
@ -377,4 +378,87 @@ fn test_until_incomplete() {
|
||||
let iter = SliceIter::new(input_str.as_bytes());
|
||||
let result = until!(iter, text_token!("; "));
|
||||
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