Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[zero-copy] Add basic docs, fix up ctx #283

Merged
merged 2 commits into from
Feb 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 4 additions & 36 deletions src/zero_copy/combinator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -735,38 +735,6 @@ where
go_extra!(O);
}

/// See [`Parser::map_ctx`].
pub struct MapCtx<A, F> {
pub(crate) parser: A,
pub(crate) mapper: F,
}

impl<A: Copy, F: Copy> Copy for MapCtx<A, F> {}
impl<A: Clone, F: Clone> Clone for MapCtx<A, F> {
fn clone(&self) -> Self {
MapCtx {
parser: self.parser.clone(),
mapper: self.mapper.clone(),
}
}
}

impl<'a, I, O, E, A, F, Ctx> Parser<'a, I, O, E> for MapCtx<A, F>
where
I: Input + ?Sized,
E: ParserExtra<'a, I>,
A: Parser<'a, I, O, extra::Full<E::Error, E::State, Ctx>>,
F: Fn(&E::Context) -> Ctx,
Ctx: 'a,
{
#[inline]
fn go<M: Mode>(&self, inp: &mut InputRef<'a, '_, I, E>) -> PResult<M, O, E::Error> {
inp.with_ctx((self.mapper)(inp.ctx()), |inp| self.parser.go::<M>(inp))
}

go_extra!(O);
}

/// See [`Parser::delimited_by`].
pub struct DelimitedBy<A, B, C, OB, OC> {
pub(crate) parser: A,
Expand Down Expand Up @@ -875,27 +843,27 @@ where
go_extra!(O);
}

/// TODO
/// Configuration for [`Parser::repeated`], used in [`ConfigParser::configure`].
#[derive(Default)]
pub struct RepeatedCfg {
at_least: Option<usize>,
at_most: Option<usize>,
}

impl RepeatedCfg {
/// TODO
/// Set the minimum number of repetitions accepted
pub fn at_least(mut self, n: usize) -> Self {
self.at_least = Some(n);
self
}

/// TODO
/// Set the maximum number of repetitions accepted
pub fn at_most(mut self, n: usize) -> Self {
self.at_most = Some(n);
self
}

/// TODO
/// Set an exact number of repetitions to accept
pub fn exactly(mut self, n: usize) -> Self {
self.at_least = Some(n);
self.at_most = Some(n);
Expand Down
3 changes: 2 additions & 1 deletion src/zero_copy/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ impl<T: Clone> SliceInput for [T] {
}
}

///
/// An input wrapper contains a user-defined context in its span, in addition to the span of the
/// wrapped input.
pub struct WithContext<'a, Ctx, I: ?Sized>(pub Ctx, pub &'a I);

impl<'a, Ctx: Clone, I: Input + ?Sized> Input for WithContext<'a, Ctx, I> {
Expand Down
40 changes: 21 additions & 19 deletions src/zero_copy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ pub mod prelude {
pub use super::{
error::{EmptyErr, Error as _, Rich, Simple},
extra,
primitive::{any, choice, empty, end, group, just, none_of, one_of, take_until, todo},
primitive::{any, choice, empty, end, group, just, none_of, one_of, take_until, todo, map_ctx},
recovery::{nested_delimiters, skip_until},
recursive::{recursive, Recursive},
// select,
Expand Down Expand Up @@ -943,14 +943,14 @@ pub trait Parser<'a, I: Input + ?Sized, O, E: ParserExtra<'a, I> = extra::Defaul
/// assert_eq!(successive_letters.parse(b"ab").into_result(), Ok(b'b')); // 'b' follows 'a'
/// assert!(successive_letters.parse(b"ac").has_errors()); // 'c' does not follow 'a'
/// ```
fn then_with_ctx<U, CtxN, P>(
fn then_with_ctx<U, P>(
self,
then: P,
) -> ThenWithCtx<Self, P, O, I, extra::Full<E::Error, E::State, CtxN>>
) -> ThenWithCtx<Self, P, O, I, extra::Full<E::Error, E::State, O>>
where
Self: Sized,
CtxN: 'a,
P: Parser<'a, I, U, extra::Full<E::Error, E::State, CtxN>>,
O: 'a,
P: Parser<'a, I, U, extra::Full<E::Error, E::State, O>>,
{
ThenWithCtx {
parser: self,
Expand All @@ -959,20 +959,22 @@ pub trait Parser<'a, I: Input + ?Sized, O, E: ParserExtra<'a, I> = extra::Defaul
}
}

/// TODO
fn map_ctx<Ctx, F>(self, mapper: F) -> MapCtx<Self, F>
where
Self: Sized,
F: Fn(O, &E::Context) -> Ctx,
Ctx: 'a,
{
MapCtx {
parser: self,
mapper,
}
}

/// TODO
/// Run the previous contextual parser with the provided context
///
/// ```
/// # use chumsky::zero_copy::prelude::*;
/// # use chumsky::zero_copy::primitive::JustCfg;
///
/// let generic = just(b'0').configure(|cfg, ctx: &u8| cfg.seq(*ctx));
///
/// let parse_a = just::<_, _, extra::Default>(b'b').ignore_then(generic.with_ctx::<u8>(b'a'));
/// let parse_b = just::<_, _, extra::Default>(b'a').ignore_then(generic.with_ctx(b'b'));
///
/// assert_eq!(parse_a.parse(b"ba" as &[_]).into_result(), Ok::<_, Vec<EmptyErr>>(b'a'));
/// assert!(parse_a.parse(b"bb").has_errors());
/// assert_eq!(parse_b.parse(b"ab" as &[_]).into_result(), Ok(b'b'));
/// assert!(parse_b.parse(b"aa").has_errors());
/// ```
fn with_ctx<Ctx>(self, ctx: Ctx) -> WithCtx<Self, Ctx>
where
Self: Sized,
Expand Down
76 changes: 74 additions & 2 deletions src/zero_copy/primitive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,13 @@ where
// fn iter(&self) -> Self::Iter<'_> { (*self).iter() }
// }

/// TODO
/// Configuration for [`just`], used in [`ConfigParser::configure`]
pub struct JustCfg<T> {
seq: Option<T>,
}

impl<T> JustCfg<T> {
/// TODO
/// Set the sequence to be used while parsing
pub fn seq(mut self, new_seq: T) -> Self {
self.seq = Some(new_seq);
self
Expand Down Expand Up @@ -572,6 +572,78 @@ where
go_extra!((C, OP));
}

/// See [`map_ctx`].
pub struct MapCtx<A, F> {
pub(crate) parser: A,
pub(crate) mapper: F,
}

impl<A: Copy, F: Copy> Copy for MapCtx<A, F> {}
impl<A: Clone, F: Clone> Clone for MapCtx<A, F> {
fn clone(&self) -> Self {
MapCtx {
parser: self.parser.clone(),
mapper: self.mapper.clone(),
}
}
}

impl<'a, I, O, E, A, F, Ctx> Parser<'a, I, O, E> for MapCtx<A, F>
where
I: Input + ?Sized,
E: ParserExtra<'a, I>,
A: Parser<'a, I, O, extra::Full<E::Error, E::State, Ctx>>,
F: Fn(&E::Context) -> Ctx,
Ctx: 'a,
{
#[inline]
fn go<M: Mode>(&self, inp: &mut InputRef<'a, '_, I, E>) -> PResult<M, O, E::Error> {
inp.with_ctx((self.mapper)(inp.ctx()), |inp| self.parser.go::<M>(inp))
}

go_extra!(O);
}

/// Apply a mapping function to the context of this parser. Note that this combinator will
/// behave differently from all other maps, in terms of which parsers it effects - while
/// other maps apply to the output of the parser, and thus read left-to-right, this one
/// applies to the _input_ of the parser, and as such applies right-to-left.
///
/// More technically, if all combinators form a 'tree' of parsers, where each node executes
/// its children in turn, normal maps apply up the tree. This means a parent mapper gets the
/// result of its children, applies the map, then passes the new result to its parent. This map,
/// however, applies down the tree. Context is provided from the parent, such as [`Parser::then_with_ctx`],
/// and gets altered before being provided to the children.
///
/// ```
/// # use chumsky::zero_copy::{prelude::*, error::Simple};
///
/// let upper = just(b'0').configure(|cfg, ctx: &u8| cfg.seq(*ctx));
///
/// let inc = one_of::<_, _, extra::Default>(b'a'..=b'z')
/// .then_with_ctx(map_ctx(|c: &u8| c.to_ascii_uppercase(), upper))
/// .slice()
/// .repeated()
/// .at_least(1)
/// .collect::<Vec<_>>();
///
/// assert_eq!(inc.parse(b"aAbB" as &[_]).into_result(), Ok(vec![b"aA" as &[_], b"bB"]));
/// assert!(inc.parse(b"aB").has_errors());
/// ```
pub fn map_ctx<'a, P, OP, I, E, F, Ctx>(mapper: F, parser: P) -> MapCtx<P, F>
where
F: Fn(&E::Context) -> Ctx,
Ctx: 'a,
I: Input + ?Sized,
P: Parser<'a, I, OP, E>,
E: ParserExtra<'a, I>,
{
MapCtx {
parser,
mapper,
}
}

/// See [`fn@todo`].
pub struct Todo<I: ?Sized, O, E>(PhantomData<(O, E, I)>);

Expand Down