udled 0.7.1

Tokenizer and parser
Documentation
use core::marker::PhantomData;

use alloc::{boxed::Box, vec::Vec};

use crate::{
    tokenizers::{AsDigits, Many, Opt, Or, Punctuated, Sliced, Spanned, Until},
    AsSlice, Buffer, Error, Item, Reader, Span, Tokenizer,
};

/// Extension methods for types that implement `Tokenizer`.
///
/// This trait provides a set of convenient combinators and helpers that
/// make it easier to compose and transform tokenizers. It is implemented
/// for all types that already implement `Tokenizer` so these methods can
/// be used directly on any tokenizer value.
///
/// The type parameter `B` is the buffer type used by the tokenizer and
/// must implement `Buffer<'input>`.
pub trait TokenizerExt<'input, B>: Tokenizer<'input, B>
where
    B: Buffer<'input>,
{
    /// Transform the successful token produced by this tokenizer using `func`.
    ///
    /// This leaves the tokenizer's error behavior unchanged but maps the
    /// `Token` type from `Self::Token` to `U` using the provided function.
    ///
    /// Example:
    /// let t = my_digit_tokenizer.map_ok(|tok| tok.to_string());
    fn map_ok<F, U>(self, func: F) -> MapOk<Self, F, B>
    where
        F: Fn(Self::Token) -> U,
        Self: Sized,
    {
        MapOk {
            tokenizer: self,
            func,
            ph: PhantomData,
        }
    }

    /// Replace errors produced by this tokenizer using `func`.
    ///
    /// The provided function is called with the error position and a reference
    /// to the buffer and should produce an error value (which will be boxed).
    /// This is useful to attach more context to parse failures or to convert
    /// error types.
    fn map_err<F, U>(self, func: F) -> MapErr<Self, F, B>
    where
        F: Fn(usize, &B) -> U,
        U: Into<Box<dyn core::error::Error + Send + Sync>>,
        Self: Sized,
    {
        MapErr {
            tokenizer: self,
            func,
            ph: PhantomData,
        }
    }

    /// Repeat this tokenizer `count` times and collect the results.
    ///
    /// On success returns an `Item<Vec<T::Token>>` where the span covers the
    /// repeated sequence. This will fail if the inner tokenizer fails before
    /// producing `count` successful tokens.
    fn repeat(self, count: i32) -> Repeat<Self, B>
    where
        Self: Sized,
    {
        Repeat {
            tokenizer: self,
            count,
            ph: PhantomData,
        }
    }

    /// Apply the `Many` combinator: parse the inner tokenizer zero or more times.
    ///
    /// The resulting tokenizer will return a vector of parsed tokens.
    fn many(self) -> Many<Self, B>
    where
        Self: Sized,
    {
        Many::new(self)
    }

    /// Parse until the `until` tokenizer matches, consuming the `until` token.
    ///
    /// This returns an `Until` combinator that runs the current tokenizer until
    /// the `until` tokenizer succeeds.
    fn until<U>(self, until: U) -> Until<Self, U, B>
    where
        Self: Sized,
        U: Tokenizer<'input, B>,
    {
        Until::new(self, until)
    }

    /// Combine this tokenizer with `other`, trying this one first and then `other`.
    ///
    /// Equivalent to the `Or` combinator: useful for alternatives.
    fn or<T>(self, other: T) -> Or<Self, T, B>
    where
        Self: Sized,
        T: Tokenizer<'input, B>,
    {
        Or::new(self, other)
    }

    /// Make this tokenizer optional.
    ///
    /// The `Opt` combinator returns an optional value: success with `None` if
    /// the tokenizer did not match, or `Some(token)` if it did.
    fn optional(self) -> Opt<Self, B>
    where
        Self: Sized,
    {
        Opt::new(self)
    }

    /// Wrap this tokenizer so it returns the matched span along with the token.
    ///
    /// The `Spanned` combinator produces an `Item` containing a `Span` and the
    /// inner token value.
    fn spanned(self) -> Spanned<Self, B>
    where
        Self: Sized,
    {
        Spanned::new(self)
    }

    /// Convert a token containing digits into an integer using the given `base`.
    ///
    /// The inner token type must implement `AsDigits`. The returned token is
    /// an `Item<i128>` whose span covers the consumed digits.
    fn into_integer(self, base: u32) -> IntoInteger<Self, B>
    where
        Self: Sized,
        Self::Token: AsDigits,
    {
        IntoInteger {
            tokenizer: self,
            base,
            ph: PhantomData,
        }
    }

    /// Parse a punctuated sequence: token, (punct, token)*
    ///
    /// Returns a `Punctuated` combinator that collects the values separated by
    /// the provided `punct` tokenizer.
    fn punctuated<P>(self, punct: P) -> Punctuated<Self, P>
    where
        Self: Sized,
        P: Tokenizer<'input, B>,
    {
        Punctuated::new(self, punct)
    }

    /// Return a slice view of the parsed input for this tokenizer.
    ///
    /// Requires the buffer source to implement `AsSlice<'input>`. The `Sliced`
    /// combinator produces tokens that reference the original input slice.
    fn slice(self) -> Sliced<Self, B>
    where
        Self: Sized,
        B: Buffer<'input>,
        B::Source: AsSlice<'input>,
    {
        Sliced::new(self)
    }

    /// Convenience helper: parse using this tokenizer against `reader`.
    ///
    /// Equivalent to calling `reader.parse(self)` directly; provided for
    /// ergonomic chaining in client code.
    fn parse(&self, reader: &mut Reader<'_, 'input, B>) -> Result<Self::Token, Error>
    where
        Self: Sized,
    {
        reader.parse(self)
    }
}

impl<'input, T, B> TokenizerExt<'input, B> for T
where
    B: Buffer<'input>,
    T: Tokenizer<'input, B>,
{
}

pub struct MapOk<T, F, B> {
    tokenizer: T,
    func: F,
    ph: PhantomData<fn(&B)>,
}

impl<'input, T, F, U, B> Tokenizer<'input, B> for MapOk<T, F, B>
where
    B: Buffer<'input>,
    T: Tokenizer<'input, B>,
    F: Fn(T::Token) -> U,
{
    type Token = U;

    fn eat(&self, reader: &mut crate::Reader<'_, 'input, B>) -> Result<(), crate::Error> {
        self.tokenizer.eat(reader)
    }

    fn peek(&self, reader: &mut crate::Reader<'_, 'input, B>) -> bool {
        self.tokenizer.peek(reader)
    }

    fn to_token(
        &self,
        reader: &mut crate::Reader<'_, 'input, B>,
    ) -> Result<Self::Token, crate::Error> {
        match self.tokenizer.to_token(reader) {
            Ok(ret) => Ok((self.func)(ret)),
            Err(err) => Err(err),
        }
    }
}

pub struct MapErr<T, F, B> {
    tokenizer: T,
    func: F,
    ph: PhantomData<fn(&B)>,
}

impl<'input, T, F, U, B> Tokenizer<'input, B> for MapErr<T, F, B>
where
    B: Buffer<'input>,
    T: Tokenizer<'input, B>,
    F: Fn(usize, &B) -> U,
    U: Into<Box<dyn core::error::Error + Send + Sync>>,
{
    type Token = T::Token;

    fn eat(&self, reader: &mut crate::Reader<'_, 'input, B>) -> Result<(), crate::Error> {
        self.tokenizer
            .eat(reader)
            .map_err(|err| Error::new(err.position(), (self.func)(err.position(), reader.buffer())))
    }

    fn peek(&self, reader: &mut crate::Reader<'_, 'input, B>) -> bool {
        self.tokenizer.peek(reader)
    }

    fn to_token(
        &self,
        reader: &mut crate::Reader<'_, 'input, B>,
    ) -> Result<Self::Token, crate::Error> {
        self.tokenizer
            .to_token(reader)
            .map_err(|err| Error::new(err.position(), (self.func)(err.position(), reader.buffer())))
    }
}

pub struct IntoInteger<T, B> {
    tokenizer: T,
    base: u32,
    ph: PhantomData<fn(&B)>,
}

impl<'input, T, B> Tokenizer<'input, B> for IntoInteger<T, B>
where
    B: Buffer<'input>,
    T: Tokenizer<'input, B>,
    T::Token: AsDigits,
{
    type Token = Item<i128>;

    fn eat(&self, reader: &mut crate::Reader<'_, 'input, B>) -> Result<(), crate::Error> {
        self.tokenizer.eat(reader)
    }

    fn peek(&self, reader: &mut crate::Reader<'_, 'input, B>) -> bool {
        self.tokenizer.peek(reader)
    }

    fn to_token(
        &self,
        reader: &mut crate::Reader<'_, 'input, B>,
    ) -> Result<Self::Token, crate::Error> {
        let start = reader.position();
        let digits = self.tokenizer.to_token(reader)?;
        let end = reader.position();
        let mut val = 0i128;

        for digit in digits.digits() {
            val = (self.base as i128) * val + (digit as i128);
        }

        Ok(Item::new(Span::new(start, end), val))
    }
}

pub struct Repeat<T, B> {
    tokenizer: T,
    count: i32,
    ph: PhantomData<fn(&B)>,
}

impl<'input, T, B> Tokenizer<'input, B> for Repeat<T, B>
where
    B: Buffer<'input>,
    T: Tokenizer<'input, B>,
{
    type Token = Item<Vec<T::Token>>;

    fn eat(&self, reader: &mut crate::Reader<'_, 'input, B>) -> Result<(), crate::Error> {
        let mut count = 0;
        loop {
            self.tokenizer.eat(reader)?;
            count += 1;
            if count == self.count as usize {
                break;
            }
        }

        Ok(())
    }

    fn peek(&self, reader: &mut crate::Reader<'_, 'input, B>) -> bool {
        self.tokenizer.peek(reader)
    }

    fn to_token(
        &self,
        reader: &mut crate::Reader<'_, 'input, B>,
    ) -> Result<Self::Token, crate::Error> {
        let start = reader.position();

        let mut output = Vec::with_capacity(self.count as _);
        loop {
            let next = self.tokenizer.parse(reader)?;
            output.push(next);
            if output.len() == self.count as usize {
                break;
            }
        }

        let end = reader.position();

        Ok(Item::new(Span::new(start, end), output))
    }
}