mirror of
https://github.com/zaphar/abortable_parser.git
synced 2025-07-21 20:29:49 -04:00
REFACTOR: Combinator functions and no more matchers module.
We also made the Error struct take string messages instead of arbitary displayable types.
This commit is contained in:
parent
832a43db14
commit
ed908e7c13
@ -1,5 +1,26 @@
|
|||||||
//! Contains combinators that can assemble other matchers or combinators into more complex grammars.
|
//! Contains combinators that can assemble other matchers or combinators into more complex grammars.
|
||||||
|
|
||||||
|
use super::{InputIter, Error, Result};
|
||||||
|
|
||||||
|
/// Turns a `Result` to it's inverse.
|
||||||
|
///
|
||||||
|
/// `Result::Fail` becomes `Result::Complete` and `Result::Complete` becomes `Result::Fail`.
|
||||||
|
/// You must pass in an iterator at the appropriate spot for the next combinator
|
||||||
|
/// to start at.
|
||||||
|
///
|
||||||
|
/// The `not!` macro provides syntactic sugar for using this combinator properly.
|
||||||
|
pub fn not<I, O>(i: I, result: Result<I, O>) -> Result<I, ()>
|
||||||
|
where
|
||||||
|
I: InputIter,
|
||||||
|
{
|
||||||
|
match result {
|
||||||
|
Result::Complete(i, _) => Result::Fail(Error::new("Matched on input when we shouldn't have.".to_string(), &i)),
|
||||||
|
Result::Abort(e) => Result::Abort(e),
|
||||||
|
Result::Incomplete(offset) => Result::Incomplete(offset),
|
||||||
|
Result::Fail(_) => Result::Complete(i, ()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Turns a matcher into it's inverse, only succeeding if the the matcher returns a Fail.
|
/// Turns a matcher into it's inverse, only succeeding if the the matcher returns a Fail.
|
||||||
/// Does not consume it's input and only returns ().
|
/// Does not consume it's input and only returns ().
|
||||||
///
|
///
|
||||||
@ -21,14 +42,8 @@
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! not {
|
macro_rules! not {
|
||||||
($i:expr, $f:ident!( $( $args:tt )* ) ) => {{
|
($i:expr, $f:ident!( $( $args:tt )* ) ) => {{
|
||||||
use $crate::{Result, Error};
|
|
||||||
let _i = $i.clone();
|
let _i = $i.clone();
|
||||||
match trap!(_i, $f!($($args)*)) {
|
$crate::combinators::not(_i, trap!($i.clone(), $f!($($args)*)))
|
||||||
Result::Complete(i, _) => Result::Fail(Error::new("Matched on input when we shouldn't have.".to_string(), &i)),
|
|
||||||
Result::Abort(e) => Result::Abort(e),
|
|
||||||
Result::Incomplete(offset) => Result::Incomplete(offset),
|
|
||||||
Result::Fail(_) => Result::Complete($i, ()),
|
|
||||||
}
|
|
||||||
}};
|
}};
|
||||||
|
|
||||||
($i:expr, $f:ident( $( $args:tt )* ) ) => {
|
($i:expr, $f:ident( $( $args:tt )* ) ) => {
|
||||||
@ -87,8 +102,27 @@ macro_rules! run {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Turns Fails into Aborts. Allows you to turn any parse failure into a hard abort of
|
/// Maps a `Result::Fail` to a `Result::Abort`.
|
||||||
/// the parser.
|
///
|
||||||
|
/// It leaves the rest of the Result variants untouched.
|
||||||
|
///
|
||||||
|
/// The `must!` macro provided syntactice sugar for using this combinator.
|
||||||
|
pub fn must<I, O>(result: Result<I, O>) -> Result<I, O>
|
||||||
|
where
|
||||||
|
I: InputIter
|
||||||
|
{
|
||||||
|
match result {
|
||||||
|
Result::Complete(i, o) => Result::Complete(i, o),
|
||||||
|
Result::Incomplete(offset) => Result::Incomplete(offset),
|
||||||
|
Result::Fail(e) => Result::Abort(e),
|
||||||
|
Result::Abort(e) => Result::Abort(e),
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Turns `Result::Fail` into `Result::Abort`.
|
||||||
|
///
|
||||||
|
/// Allows you to turn any parse failure into a hard abort of the parser.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # #[macro_use] extern crate abortable_parser;
|
/// # #[macro_use] extern crate abortable_parser;
|
||||||
@ -109,12 +143,7 @@ macro_rules! run {
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! must {
|
macro_rules! must {
|
||||||
($i:expr, $f:ident!( $( $args:tt )* ) ) => {
|
($i:expr, $f:ident!( $( $args:tt )* ) ) => {
|
||||||
match $f!($i, $($args)*) {
|
$crate::combinators::must($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::Abort(e),
|
|
||||||
$crate::Result::Abort(e) => $crate::Result::Abort(e),
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
($i:expr, $f:ident) => {
|
($i:expr, $f:ident) => {
|
||||||
@ -145,8 +174,25 @@ macro_rules! wrap_err {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Turns Aborts into fails allowing you to trap and then convert an Abort into a
|
/// Traps a `Result::Abort` and converts it into a `Result::Fail`.
|
||||||
/// normal Fail.
|
///
|
||||||
|
/// This is the semantic inverse of `must`.
|
||||||
|
///
|
||||||
|
/// The `trap!` macro provides syntactic sugar for using this combinator.
|
||||||
|
pub fn trap<I, O>(result: Result<I, O>) -> Result<I, O>
|
||||||
|
where
|
||||||
|
I: InputIter
|
||||||
|
{
|
||||||
|
match result {
|
||||||
|
Result::Complete(i, o) => Result::Complete(i, o),
|
||||||
|
Result::Incomplete(offset) => Result::Incomplete(offset),
|
||||||
|
Result::Fail(e) => Result::Fail(e),
|
||||||
|
Result::Abort(e) => Result::Fail(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Turns `Result::Abort` into `Result::Fail` allowing you to trap and then convert any `Result::Abort`
|
||||||
|
/// into a normal Fail.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # #[macro_use] extern crate abortable_parser;
|
/// # #[macro_use] extern crate abortable_parser;
|
||||||
@ -162,12 +208,7 @@ macro_rules! wrap_err {
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! trap {
|
macro_rules! trap {
|
||||||
($i:expr, $f:ident!( $( $args:tt )* ) ) => {
|
($i:expr, $f:ident!( $( $args:tt )* ) ) => {
|
||||||
match $f!($i, $($args)*) {
|
$crate::combinators::trap($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(e),
|
|
||||||
$crate::Result::Abort(e) => $crate::Result::Fail(e),
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
($i:expr, $f:ident) => {
|
($i:expr, $f:ident) => {
|
||||||
@ -175,8 +216,27 @@ macro_rules! trap {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Turns Fails and Incompletes into Aborts. You must specify the error message
|
/// Turns `Result::Fail` or `Result::Incomplete` into `Result::Abort`.
|
||||||
/// to use in case the matcher is Incomplete.
|
///
|
||||||
|
/// You must specify the error message to use in case the matcher is incomplete.
|
||||||
|
///
|
||||||
|
/// The must_complete! macro provides syntactic sugar for using this combinator.
|
||||||
|
pub fn must_complete<I, O, M>(result: Result<I, O>, msg: M) -> Result<I, O>
|
||||||
|
where
|
||||||
|
I: InputIter,
|
||||||
|
M: Into<String>,
|
||||||
|
{
|
||||||
|
match result {
|
||||||
|
Result::Complete(i, o) => Result::Complete(i, o),
|
||||||
|
Result::Incomplete(ref offset) => Result::Abort(Error::new(msg, offset)),
|
||||||
|
Result::Fail(e) => Result::Abort(e),
|
||||||
|
Result::Abort(e) => Result::Abort(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Turns `Result::Fail` and `Result::Incomplete` into `Result::Abort`.
|
||||||
|
///
|
||||||
|
/// You must specify the error message to use in case the matcher is incomplete.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # #[macro_use] extern crate abortable_parser;
|
/// # #[macro_use] extern crate abortable_parser;
|
||||||
@ -191,13 +251,7 @@ macro_rules! trap {
|
|||||||
#[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 )* ) ) => {{
|
||||||
let _i = $i.clone();
|
$crate::combinators::must_complete($f!($i, $($args)*), $e)
|
||||||
match $f!($i, $($args)*) {
|
|
||||||
$crate::Result::Complete(i, o) => $crate::Result::Complete(i, o),
|
|
||||||
$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) => {
|
($i:expr, $efn:expr, $f:ident) => {
|
||||||
@ -407,8 +461,37 @@ macro_rules! either {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Maps a `Result` to be optional.
|
||||||
|
///
|
||||||
|
/// `Result::Fail` maps to None and `Result::Complete` maps to Some. The rest of the
|
||||||
|
/// `Result` variants are left untouched. You must pass in the iterator that the
|
||||||
|
/// next matcher should use in the event of a fail.
|
||||||
|
///
|
||||||
|
/// The `optional!` macro provides some syntactice sugar for using this combinator
|
||||||
|
/// properly.
|
||||||
|
pub fn optional<I, O>(iter: I, result: Result<I, O>) -> Result<I, Option<O>>
|
||||||
|
where
|
||||||
|
I: InputIter,
|
||||||
|
{
|
||||||
|
match result {
|
||||||
|
Result::Complete(i, o) => {
|
||||||
|
Result::Complete(i, Some(o))
|
||||||
|
}
|
||||||
|
// Incomplete could still work possibly parse.
|
||||||
|
Result::Incomplete(i) => {
|
||||||
|
Result::Incomplete(i)
|
||||||
|
}
|
||||||
|
// Fail just means it didn't match.
|
||||||
|
Result::Fail(_) => {
|
||||||
|
Result::Complete(iter, None)
|
||||||
|
},
|
||||||
|
// Aborts are hard failures that the parser can't recover from.
|
||||||
|
Result::Abort(e) => Result::Abort(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Treats a sub parser as optional. It returns Some(output) for a successful match
|
/// Treats a sub parser as optional. It returns Some(output) for a successful match
|
||||||
/// and None for Fails.
|
/// and None for failures.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # #[macro_use] extern crate abortable_parser;
|
/// # #[macro_use] extern crate abortable_parser;
|
||||||
@ -437,21 +520,7 @@ macro_rules! optional {
|
|||||||
|
|
||||||
(__impl $i:expr, $f:ident!( $( $args:tt )* )) => {{
|
(__impl $i:expr, $f:ident!( $( $args:tt )* )) => {{
|
||||||
let _i = $i.clone();
|
let _i = $i.clone();
|
||||||
match $f!($i, $($args)*) {
|
$crate::combinators::optional(_i, $f!($i, $($args)*))
|
||||||
$crate::Result::Complete(i, o) => {
|
|
||||||
Result::Complete(i, Some(o))
|
|
||||||
}
|
|
||||||
// Incomplete could still work possibly parse.
|
|
||||||
$crate::Result::Incomplete(i) => {
|
|
||||||
Result::Incomplete(i)
|
|
||||||
}
|
|
||||||
// Fail just means it didn't match.
|
|
||||||
$crate::Result::Fail(_) => {
|
|
||||||
Result::Complete(_i, None)
|
|
||||||
},
|
|
||||||
// Aborts are hard failures that the parser can't recover from.
|
|
||||||
$crate::Result::Abort(e) => Result::Abort(e),
|
|
||||||
}
|
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -514,3 +583,115 @@ macro_rules! repeat {
|
|||||||
repeat!($i, run!($f))
|
repeat!($i, run!($f))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convenience macro for looking for a specific text token in a byte input stream.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[macro_use] extern crate abortable_parser;
|
||||||
|
/// use abortable_parser::iter;
|
||||||
|
/// # use abortable_parser::{Result, Offsetable};
|
||||||
|
/// use std::convert::From;
|
||||||
|
/// # fn main() {
|
||||||
|
/// let iter: iter::SliceIter<u8> = "foo bar".into();
|
||||||
|
/// let tok = text_token!(iter, "foo");
|
||||||
|
/// # assert!(tok.is_complete());
|
||||||
|
/// if let Result::Complete(i, o) = tok {
|
||||||
|
/// assert_eq!(i.get_offset(), 3);
|
||||||
|
/// assert_eq!(o, "foo");
|
||||||
|
/// }
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! text_token {
|
||||||
|
($i:expr, $e:expr) => {{
|
||||||
|
use $crate::Error;
|
||||||
|
use $crate::Result;
|
||||||
|
let mut _i = $i.clone();
|
||||||
|
let mut count = 0;
|
||||||
|
for expected in $e.bytes() {
|
||||||
|
let item = match _i.next() {
|
||||||
|
Some(item) => item,
|
||||||
|
None => break,
|
||||||
|
};
|
||||||
|
if item == &expected {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if count == $e.len() {
|
||||||
|
Result::Complete(_i.clone(), $e)
|
||||||
|
} else {
|
||||||
|
Result::Fail(Error::new(
|
||||||
|
format!("Expected {} but didn't get it.", $e),
|
||||||
|
&$i,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes an input until it reaches the term combinator matches.
|
||||||
|
///
|
||||||
|
/// If the term never matches then returns incomplete.
|
||||||
|
/// ```
|
||||||
|
/// # #[macro_use] extern crate abortable_parser;
|
||||||
|
/// use abortable_parser::iter;
|
||||||
|
/// # use abortable_parser::{Result, Offsetable};
|
||||||
|
/// use std::convert::From;
|
||||||
|
/// # fn main() {
|
||||||
|
/// let iter: iter::SliceIter<u8> = "foo;".into();
|
||||||
|
/// let tok = until!(iter, text_token!(";"));
|
||||||
|
/// # assert!(tok.is_complete());
|
||||||
|
/// if let Result::Complete(i, o) = tok {
|
||||||
|
/// assert_eq!(i.get_offset(), 3);
|
||||||
|
/// }
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! until {
|
||||||
|
($i:expr, $term:ident!( $( $args:tt )* ) ) => {{
|
||||||
|
use $crate::{Result, Offsetable};
|
||||||
|
let mut acc = Vec::new();
|
||||||
|
let mut _i = $i.clone();
|
||||||
|
let pfn = || {
|
||||||
|
loop {
|
||||||
|
match $term!(_i.clone(), $($args)*) {
|
||||||
|
Result::Complete(_, _) => return Result::Complete(_i, acc),
|
||||||
|
Result::Abort(e) => return Result::Abort(e),
|
||||||
|
Result::Incomplete(offset) => return Result::Incomplete(offset),
|
||||||
|
Result::Fail(_) => {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let item = match _i.next() {
|
||||||
|
Some(it) => it,
|
||||||
|
None => return Result::Incomplete(_i.get_offset()),
|
||||||
|
};
|
||||||
|
acc.push(item);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
pfn()
|
||||||
|
}};
|
||||||
|
|
||||||
|
($i:expr, $term:ident) => {
|
||||||
|
consume_until!($i, run!($term))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Maps a Result of type Vec<&u8> to a Result of type String.
|
||||||
|
pub fn must_string<'a, I, E>(matched: Result<I, Vec<&'a u8>>, msg: E) -> Result<I, String>
|
||||||
|
where
|
||||||
|
I: InputIter<Item=&'a u8>,
|
||||||
|
E: Into<String>,
|
||||||
|
{
|
||||||
|
match matched {
|
||||||
|
Result::Complete(i, mut o) => {
|
||||||
|
let new_string = String::from_utf8(o.drain(0..).map(|b| *b).collect());
|
||||||
|
match new_string {
|
||||||
|
Ok(s) => Result::Complete(i, s),
|
||||||
|
Err(_) => Result::Abort(Error::new(msg, &i)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Result::Incomplete(offset) => Result::Incomplete(offset),
|
||||||
|
Result::Abort(e) => Result::Abort(e),
|
||||||
|
Result::Fail(e) => Result::Fail(e),
|
||||||
|
}
|
||||||
|
}
|
43
src/lib.rs
43
src/lib.rs
@ -1,5 +1,5 @@
|
|||||||
//! A 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.
|
||||||
use std::fmt::{Debug, Display};
|
use std::fmt::Display;
|
||||||
use std::iter::Iterator;
|
use std::iter::Iterator;
|
||||||
|
|
||||||
/// A trait for types that can have an offset as a count of processed items.
|
/// A trait for types that can have an offset as a count of processed items.
|
||||||
@ -20,38 +20,45 @@ pub trait InputIter: Iterator + Clone + Offsetable {}
|
|||||||
/// 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)]
|
||||||
pub struct Error<E: Display + Debug> {
|
pub struct Error {
|
||||||
msg: E,
|
msg: String,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
cause: Option<Box<Error<E>>>,
|
cause: Option<Box<Error>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Display + Debug> Error<E> {
|
impl Error {
|
||||||
/// Constructs a new Error with an offset and no cause.
|
/// Constructs a new Error with an offset and no cause.
|
||||||
pub fn new<S: Offsetable>(err: E, offset: &S) -> Self {
|
pub fn new<S, M>(msg: M, offset: &S) -> Self
|
||||||
|
where
|
||||||
|
S: Offsetable,
|
||||||
|
M: Into<String> {
|
||||||
Error {
|
Error {
|
||||||
msg: err,
|
msg: msg.into(),
|
||||||
offset: offset.get_offset(),
|
offset: offset.get_offset(),
|
||||||
cause: None,
|
cause: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a new Error with an offset and a cause.
|
/// Constructs a new Error with an offset and a cause.
|
||||||
pub fn caused_by<S: Offsetable>(msg: E, offset: &S, cause: Self) -> Self {
|
pub fn caused_by<S, M>(msg: M, offset: &S, cause: Self) -> Self
|
||||||
|
where
|
||||||
|
S: Offsetable,
|
||||||
|
M: Into<String> {
|
||||||
|
|
||||||
Error {
|
Error {
|
||||||
msg: msg,
|
msg: msg.into(),
|
||||||
offset: offset.get_offset(),
|
offset: offset.get_offset(),
|
||||||
cause: Some(Box::new(cause)),
|
cause: Some(Box::new(cause)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the contained err.
|
/// Returns the contained err.
|
||||||
pub fn get_msg<'a>(&'a self) -> &'a E {
|
pub fn get_msg<'a>(&'a self) -> &'a str {
|
||||||
&self.msg
|
&self.msg
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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<E>> {
|
pub fn get_cause<'a>(&'a self) -> Option<&'a Error> {
|
||||||
match self.cause {
|
match self.cause {
|
||||||
Some(ref cause) => Some(cause),
|
Some(ref cause) => Some(cause),
|
||||||
None => None,
|
None => None,
|
||||||
@ -64,7 +71,7 @@ impl<E: Display + Debug> Error<E> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Display + Debug> Display for Error<E> {
|
impl Display for Error {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
|
||||||
try!(write!(f, "{}", self.msg));
|
try!(write!(f, "{}", self.msg));
|
||||||
match self.cause {
|
match self.cause {
|
||||||
@ -76,19 +83,19 @@ impl<E: Display + Debug> Display for Error<E> {
|
|||||||
|
|
||||||
/// The result of a parsing attempt.
|
/// The result of a parsing attempt.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Result<I: InputIter, O, E: Display + Debug> {
|
pub enum Result<I: InputIter, O> {
|
||||||
/// Complete represents a successful match.
|
/// Complete represents a successful match.
|
||||||
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(usize),
|
||||||
/// Fail represents a failed match.
|
/// Fail represents a failed match.
|
||||||
Fail(Error<E>),
|
Fail(Error),
|
||||||
/// Abort represents a match failure that the parser cannot recover from.
|
/// Abort represents a match failure that the parser cannot recover from.
|
||||||
Abort(Error<E>),
|
Abort(Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: InputIter, O, E: Display + Debug> Result<I, O, E> {
|
impl<I: InputIter, O> Result<I, O> {
|
||||||
/// Returns true if the Result is Complete.
|
/// Returns true if the Result is Complete.
|
||||||
pub fn is_complete(&self) -> bool {
|
pub fn is_complete(&self) -> bool {
|
||||||
if let &Result::Complete(_, _) = self {
|
if let &Result::Complete(_, _) = self {
|
||||||
@ -126,8 +133,6 @@ pub use iter::SliceIter;
|
|||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod combinators;
|
pub mod combinators;
|
||||||
#[macro_use]
|
|
||||||
pub mod matchers;
|
|
||||||
pub mod iter;
|
pub mod iter;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
116
src/matchers.rs
116
src/matchers.rs
@ -1,116 +0,0 @@
|
|||||||
//! Contains matchers for matching specific patterns or tokens.
|
|
||||||
use super::{InputIter, Result, Error};
|
|
||||||
|
|
||||||
use std::fmt::{Debug, Display};
|
|
||||||
|
|
||||||
/// Convenience macro for looking for a specific text token in a byte input stream.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # #[macro_use] extern crate abortable_parser;
|
|
||||||
/// use abortable_parser::iter;
|
|
||||||
/// # use abortable_parser::{Result, Offsetable};
|
|
||||||
/// use std::convert::From;
|
|
||||||
/// # fn main() {
|
|
||||||
/// let iter: iter::SliceIter<u8> = "foo bar".into();
|
|
||||||
/// let tok = text_token!(iter, "foo");
|
|
||||||
/// # assert!(tok.is_complete());
|
|
||||||
/// if let Result::Complete(i, o) = tok {
|
|
||||||
/// assert_eq!(i.get_offset(), 3);
|
|
||||||
/// assert_eq!(o, "foo");
|
|
||||||
/// }
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! text_token {
|
|
||||||
($i:expr, $e:expr) => {{
|
|
||||||
use $crate::Error;
|
|
||||||
use $crate::Result;
|
|
||||||
let mut _i = $i.clone();
|
|
||||||
let mut count = 0;
|
|
||||||
for expected in $e.bytes() {
|
|
||||||
let item = match _i.next() {
|
|
||||||
Some(item) => item,
|
|
||||||
None => break,
|
|
||||||
};
|
|
||||||
if item == &expected {
|
|
||||||
count += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if count == $e.len() {
|
|
||||||
Result::Complete(_i.clone(), $e)
|
|
||||||
} else {
|
|
||||||
Result::Fail(Error::new(
|
|
||||||
format!("Expected {} but didn't get it.", $e),
|
|
||||||
&$i,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Consumes an input until it reaches the term combinator matches.
|
|
||||||
///
|
|
||||||
/// If the term never matches then returns incomplete.
|
|
||||||
/// ```
|
|
||||||
/// # #[macro_use] extern crate abortable_parser;
|
|
||||||
/// use abortable_parser::iter;
|
|
||||||
/// # use abortable_parser::{Result, Offsetable};
|
|
||||||
/// use std::convert::From;
|
|
||||||
/// # fn main() {
|
|
||||||
/// let iter: iter::SliceIter<u8> = "foo;".into();
|
|
||||||
/// let tok = until!(iter, text_token!(";"));
|
|
||||||
/// # assert!(tok.is_complete());
|
|
||||||
/// if let Result::Complete(i, o) = tok {
|
|
||||||
/// assert_eq!(i.get_offset(), 3);
|
|
||||||
/// }
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! until {
|
|
||||||
($i:expr, $term:ident!( $( $args:tt )* ) ) => {{
|
|
||||||
use $crate::{Result, Offsetable};
|
|
||||||
let mut acc = Vec::new();
|
|
||||||
let mut _i = $i.clone();
|
|
||||||
let pfn = || {
|
|
||||||
loop {
|
|
||||||
match $term!(_i.clone(), $($args)*) {
|
|
||||||
Result::Complete(_, _) => return Result::Complete(_i, acc),
|
|
||||||
Result::Abort(e) => return Result::Abort(e),
|
|
||||||
Result::Incomplete(offset) => return Result::Incomplete(offset),
|
|
||||||
Result::Fail(_) => {
|
|
||||||
// noop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let item = match _i.next() {
|
|
||||||
Some(it) => it,
|
|
||||||
None => return Result::Incomplete(_i.get_offset()),
|
|
||||||
};
|
|
||||||
acc.push(item);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
pfn()
|
|
||||||
}};
|
|
||||||
|
|
||||||
($i:expr, $term:ident) => {
|
|
||||||
consume_until!($i, run!($term))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Maps a Result of type Vec<&u8> to a Result of type String.
|
|
||||||
pub fn must_string<'a, I, E>(matched: Result<I, Vec<&'a u8>, E>, msg: E) -> Result<I, String, E>
|
|
||||||
where
|
|
||||||
I: InputIter<Item=&'a u8>,
|
|
||||||
E: Debug + Display,
|
|
||||||
{
|
|
||||||
match matched {
|
|
||||||
Result::Complete(i, mut o) => {
|
|
||||||
let new_string = String::from_utf8(o.drain(0..).map(|b| *b).collect());
|
|
||||||
match new_string {
|
|
||||||
Ok(s) => Result::Complete(i, s),
|
|
||||||
Err(_) => Result::Abort(Error::new(msg, &i)),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Result::Incomplete(offset) => Result::Incomplete(offset),
|
|
||||||
Result::Abort(e) => Result::Abort(e),
|
|
||||||
Result::Fail(e) => Result::Fail(e),
|
|
||||||
}
|
|
||||||
}
|
|
10
src/test.rs
10
src/test.rs
@ -2,7 +2,7 @@ use std::fmt::{Debug, Display};
|
|||||||
|
|
||||||
use super::{InputIter, Offsetable, Result};
|
use super::{InputIter, Offsetable, Result};
|
||||||
use iter::SliceIter;
|
use iter::SliceIter;
|
||||||
use matchers::must_string;
|
use combinators::must_string;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_slice_iter() {
|
fn test_slice_iter() {
|
||||||
@ -34,7 +34,7 @@ fn test_slice_iter() {
|
|||||||
assert_eq!('o' as u8, out[2]);
|
assert_eq!('o' as u8, out[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn will_fail<I, C>(i: I) -> Result<I, String, String>
|
fn will_fail<I, C>(i: I) -> Result<I, String>
|
||||||
where
|
where
|
||||||
I: InputIter<Item = C>,
|
I: InputIter<Item = C>,
|
||||||
C: Debug + Display,
|
C: Debug + Display,
|
||||||
@ -42,7 +42,7 @@ where
|
|||||||
Result::Fail(super::Error::new("AAAAHHH!!!".to_string(), &i))
|
Result::Fail(super::Error::new("AAAAHHH!!!".to_string(), &i))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_byte<'a, I>(mut i: I) -> Result<I, u8, String>
|
fn parse_byte<'a, I>(mut i: I) -> Result<I, u8>
|
||||||
where
|
where
|
||||||
I: InputIter<Item = &'a u8>,
|
I: InputIter<Item = &'a u8>,
|
||||||
{
|
{
|
||||||
@ -52,14 +52,14 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn will_not_complete<'a, I>(_: I) -> Result<I, String, String>
|
fn will_not_complete<'a, I>(_: I) -> Result<I, String>
|
||||||
where
|
where
|
||||||
I: InputIter<Item = &'a u8>,
|
I: InputIter<Item = &'a u8>,
|
||||||
{
|
{
|
||||||
Result::Incomplete(0)
|
Result::Incomplete(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_three<'a, I>(i: I) -> Result<I, String, String>
|
fn parse_three<'a, I>(i: I) -> Result<I, String>
|
||||||
where
|
where
|
||||||
I: InputIter<Item = &'a u8>,
|
I: InputIter<Item = &'a u8>,
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user