From a1e4c5087549c25a438781d8229f6f038dddf032 Mon Sep 17 00:00:00 2001 From: Eyal Kalderon Date: Mon, 14 Oct 2019 01:07:35 +0800 Subject: [PATCH] Add opt_partial() combinator --- nix-parser/src/parser/partial.rs | 36 +++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/nix-parser/src/parser/partial.rs b/nix-parser/src/parser/partial.rs index ecab3ca..1dcc78e 100644 --- a/nix-parser/src/parser/partial.rs +++ b/nix-parser/src/parser/partial.rs @@ -2,6 +2,7 @@ use std::iter::FromIterator; use codespan::Span; use nom::bytes::complete::take; +use nom::combinator::opt; use nom::sequence::{preceded, terminated}; use nom::InputLength; @@ -232,7 +233,7 @@ where } } -/// Combinator which behaves like `nom::combinator::map()`, except it is a shorthand for: +/// Combinator which behaves like `nom::combinator::map`, except it is a shorthand for: /// /// ```rust,ignore /// map(partial, |partial| partial.map(&f)) @@ -320,6 +321,39 @@ where } } +/// Combinator which returns `None` if the given partial parser fails. +/// +/// This combinator behaves like `nom::combinator::opt`, except it also transposes the result from +/// `Option>` to `Partial>`. This transposition allows you to utilize the +/// behavior of `opt` while remaining compatible with other partial combinators like +/// `pair_partial()` and `map_partial()`. +/// +/// # Examples +/// +/// `opt_partial()` is especially handy when combined with `pair_partial()` to create partial +/// parsers with predicates that trigger based on some non-partial condition. For example, compare +/// this regular `nom` parser with its partial equivalent: +/// +/// ```rust,ignore +/// // Regular version +/// pair(f, opt(preceded(sep, g))) +/// +/// // Partial version +/// pair_partial(f, opt_partial(preceded(sep, g))) +/// ``` +pub fn opt_partial<'a, O, F>(f: F) -> impl Fn(Tokens<'a>) -> IResult>> +where + F: Fn(Tokens<'a>) -> IResult>, +{ + move |input| { + let (remaining, value) = opt(&f)(input)?; + match value { + Some(partial) => Ok((remaining, partial.map(Some))), + None => Ok((remaining, Partial::new(Some(None)))), + } + } +} + /// Combinator which gets the result from the first partial parser, then gets the result from the /// second partial parser, and produces a partial value containing a tuple of the two results. ///