From 7c9f1e967571edcc6f7c0756b88833468d1cba1c Mon Sep 17 00:00:00 2001 From: Simon Guest Date: Fri, 17 Nov 2023 10:51:55 +1300 Subject: [PATCH] Add one_of_ref() Works just like one_of() but for BorrowInput rather than ValueInput. --- src/lib.rs | 3 +- src/primitive.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 67190ad8..7a31feeb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -124,7 +124,8 @@ pub mod prelude { extra, input::Input, primitive::{ - any, any_ref, choice, custom, empty, end, group, just, map_ctx, none_of, one_of, todo, + any, any_ref, choice, custom, empty, end, group, just, map_ctx, none_of, one_of, + one_of_ref, todo, }, recovery::{nested_delimiters, skip_then_retry_until, skip_until, via_parser}, recursive::{recursive, Recursive}, diff --git a/src/primitive.rs b/src/primitive.rs index 89122b8b..7722eca3 100644 --- a/src/primitive.rs +++ b/src/primitive.rs @@ -282,6 +282,85 @@ where go_extra!(I::Token); } +/// See [`one_of_ref`]. +pub struct OneOfRef { + seq: T, + #[allow(dead_code)] + phantom: EmptyPhantom<(E, I)>, +} + +impl Copy for OneOfRef {} +impl Clone for OneOfRef { + fn clone(&self) -> Self { + Self { + seq: self.seq.clone(), + phantom: EmptyPhantom::new(), + } + } +} + +/// A parser that accepts one of a sequence of specific inputs. +/// +/// The output type of this parser is `&'a I::Token`, the input that was found. +/// +/// This function is the borrowing equivalent of [one_of]. Where possible, it's recommended to use [one_of] instead. +/// +/// # Examples +/// +/// ``` +/// # use chumsky::{prelude::*, error::Simple}; +/// let digits = one_of_ref::<_, _, extra::Err>>("0123456789") +/// .map(|c| *c) +/// .repeated() +/// .at_least(1) +/// .collect::(); +/// +/// let v1 = "48791".chars().collect::>(); +/// assert_eq!(digits.parse(v1.as_slice()).into_result(), Ok("48791".to_string())); +/// let v2 = "421!53".chars().collect::>(); +/// assert!(digits.parse(v2.as_slice()).has_errors()); +/// ``` +pub const fn one_of_ref<'a, T, I, E>(seq: T) -> OneOfRef +where + I: BorrowInput<'a>, + E: ParserExtra<'a, I>, + I::Token: PartialEq, + T: Seq<'a, I::Token>, +{ + OneOfRef { + seq, + phantom: EmptyPhantom::new(), + } +} + +impl<'a, I, E, T> ParserSealed<'a, I, &'a I::Token, E> for OneOfRef +where + I: BorrowInput<'a>, + E: ParserExtra<'a, I>, + I::Token: PartialEq, + T: Seq<'a, I::Token>, +{ + #[inline] + fn go(&self, inp: &mut InputRef<'a, '_, I, E>) -> PResult { + let before = inp.offset(); + match inp.next_ref_inner() { + (_, Some(tok)) if self.seq.contains(tok) => Ok(M::bind(|| tok)), + (at, found) => { + let err_span = inp.span_since(before); + inp.add_alt( + at, + self.seq.seq_iter().map(|e| Some(T::to_maybe_ref(e))), + found.map(|f| f.into()), + err_span, + ); + Err(()) + } + } + } + + go_extra!(&'a I::Token); +} + /// See [`none_of`]. pub struct NoneOf { seq: T,