From d48f139fccbaa83610d16a29f7fbb4ec8b17d96c Mon Sep 17 00:00:00 2001 From: Rune Tynan Date: Thu, 16 Feb 2023 13:25:39 -0500 Subject: [PATCH] Add at least basic docs to all TODO doc items, fix up ctx and add doctests --- src/zero_copy/combinator.rs | 39 ++----------------- src/zero_copy/input.rs | 3 +- src/zero_copy/mod.rs | 40 ++++++++++---------- src/zero_copy/primitive.rs | 75 ++++++++++++++++++++++++++++++++++++- 4 files changed, 100 insertions(+), 57 deletions(-) diff --git a/src/zero_copy/combinator.rs b/src/zero_copy/combinator.rs index 098e11e9..e06297a0 100644 --- a/src/zero_copy/combinator.rs +++ b/src/zero_copy/combinator.rs @@ -717,37 +717,6 @@ where go_extra!(O); } -/// See [`Parser::map_ctx`]. -pub struct MapCtx { - pub(crate) parser: A, - pub(crate) mapper: F, -} - -impl Copy for MapCtx {} -impl Clone for MapCtx { - 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 -where - I: Input + ?Sized, - E: ParserExtra<'a, I>, - A: Parser<'a, I, O, extra::Full>, - F: Fn(&E::Context) -> Ctx, - Ctx: 'a, -{ - fn go(&self, inp: &mut InputRef<'a, '_, I, E>) -> PResult { - inp.with_ctx((self.mapper)(inp.ctx()), |inp| self.parser.go::(inp)) - } - - go_extra!(O); -} - /// See [`Parser::delimited_by`]. pub struct DelimitedBy { pub(crate) parser: A, @@ -853,7 +822,7 @@ where go_extra!(O); } -/// TODO +/// Configuration for [`Parser::repeated`], used in [`ConfigParser::configure`]. #[derive(Default)] pub struct RepeatedCfg { at_least: Option, @@ -861,19 +830,19 @@ pub struct RepeatedCfg { } 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); diff --git a/src/zero_copy/input.rs b/src/zero_copy/input.rs index 6f075688..8ff096d2 100644 --- a/src/zero_copy/input.rs +++ b/src/zero_copy/input.rs @@ -122,7 +122,8 @@ impl 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> { diff --git a/src/zero_copy/mod.rs b/src/zero_copy/mod.rs index 3bd2909b..452d730d 100644 --- a/src/zero_copy/mod.rs +++ b/src/zero_copy/mod.rs @@ -55,7 +55,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, @@ -936,14 +936,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( + fn then_with_ctx( self, then: P, - ) -> ThenWithCtx> + ) -> ThenWithCtx> where Self: Sized, - CtxN: 'a, - P: Parser<'a, I, U, extra::Full>, + O: 'a, + P: Parser<'a, I, U, extra::Full>, { ThenWithCtx { parser: self, @@ -952,20 +952,22 @@ pub trait Parser<'a, I: Input + ?Sized, O, E: ParserExtra<'a, I> = extra::Defaul } } - /// TODO - fn map_ctx(self, mapper: F) -> MapCtx - 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::(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>(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(self, ctx: Ctx) -> WithCtx where Self: Sized, diff --git a/src/zero_copy/primitive.rs b/src/zero_copy/primitive.rs index 086731f7..a9ed8410 100644 --- a/src/zero_copy/primitive.rs +++ b/src/zero_copy/primitive.rs @@ -118,13 +118,13 @@ where // fn iter(&self) -> Self::Iter<'_> { (*self).iter() } // } -/// TODO +/// Configuration for [`just`], used in [`ConfigParser::configure`] pub struct JustCfg { seq: Option, } impl JustCfg { - /// TODO + /// Set the sequence to be used while parsing pub fn seq(mut self, new_seq: T) -> Self { self.seq = Some(new_seq); self @@ -552,6 +552,77 @@ where go_extra!((C, OP)); } +/// See [`map_ctx`]. +pub struct MapCtx { + pub(crate) parser: A, + pub(crate) mapper: F, +} + +impl Copy for MapCtx {} +impl Clone for MapCtx { + 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 +where + I: Input + ?Sized, + E: ParserExtra<'a, I>, + A: Parser<'a, I, O, extra::Full>, + F: Fn(&E::Context) -> Ctx, + Ctx: 'a, +{ + fn go(&self, inp: &mut InputRef<'a, '_, I, E>) -> PResult { + inp.with_ctx((self.mapper)(inp.ctx()), |inp| self.parser.go::(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::>(); +/// +/// 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 +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(PhantomData<(O, E, I)>);