minparser 0.13.4

Simple parsing functions
Documentation
/*
 * Minparser Simple parsing functions
 *
 * Copyright (C) 2024-2026 Paolo De Donato
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
//! Other useful parsing tools.
#[allow(clippy::wildcard_imports)]
use crate::prelude::*;
use crate::always_impl;

/// Tool that matches the newline characters sequences `\n` and `\r\n`.
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
pub struct Newline;

impl<'a> Tool<'a> for Newline {
    type Data = ();
    type Error = View<'a>;

    fn parse(&self, st : View<'a>) -> Result<(Self::Data, usize, View<'a>), Self::Error>{
        Seq{
            first : RepeatAny::new_bounds('\r', TrueTool, 1),
            second :'\n'
        }
        .parse(st)
        .map(|d| ((), d.1, d.2))
        .map_err(Either::into_second)
    }
}

/// Tool that matches any sequence of Unicode whitespaces.
#[derive(Debug, Copy, Clone)]
pub struct WhiteSP;

impl<'a> AlwaysTool<'a> for WhiteSP{
    fn parse_always(&self, st : View<'a>) -> (Self::Data, usize, View<'a>){
        let (_, a, b) = RepeatAny::new_unbounded(Predicate::new(char::is_whitespace), TrueTool).parse_always(st);
        ((), a, b)
    }
}
always_impl!(WhiteSP);

/// Tool to retrieve a sequence of consecutive (Unicode) letters and numbers, which the first
/// character is not a number
#[derive(Debug, Copy, Clone)]
pub struct Ident;

impl<'a> Tool<'a> for Ident{
    type Data = &'a str;
    type Error = View<'a>;

    fn parse(&self, st : View<'a>) -> Result<(Self::Data, usize, View<'a>), Self::Error>{
        let su = Seq{
            first : Predicate::new(char::is_alphabetic),
            second : RepeatAny::new_unbounded(Predicate::new(char::is_alphanumeric), TrueTool)
        };
        st.match_tool_string(&su)
            .map(|(s, step)| (s, s.len(), step))
            .map_err(Either::into_first)
    }
}

struct NumberCount{
    num : usize,
    radix : u32,
}

impl<Z> InsertB<u32, Z> for NumberCount { 
    fn insert(&mut self, data : u32) {
        self.num = self.num * (self.radix as usize) + (data as usize);
    }
    fn insert_sep(&mut self, _ : Z) {}
}

/// Tool that returns an integer with the specified basis (from 2 to 36 as specified by
/// [`to_digit`](char::to_digit).
pub struct AsciiNumber(u32);

impl AsciiNumber{
    /// Create a new instance with the specified radix.
    ///
    /// # Panics
    /// Panics if the value is less than 2 or greater than 36.
    #[must_use]
    pub fn new(radix : u32) -> Self {
        assert!((2..=36).contains(&radix), "Invalid radix");
        Self(radix)
    }
}

impl<'a> Tool<'a> for AsciiNumber {
    type Data = usize;
    type Error = View<'a>;

    fn parse(&self, st : View<'a>) -> Result<(Self::Data, usize, View<'a>), Self::Error>{
        let mut count = NumberCount{num : 0, radix : self.0};
        let st = Repeat::new_unbounded(PredicateData::new(
                |c : char| c.to_digit(self.0)), 
                SetError::<_, View<'a>>::new(TrueTool), 1).parse_logic(st, &mut count)?.finalize();
        Ok((count.num, st.1, st.2))
    }
}

/// Tool that returns an integer and deduces the radix by the prefix:
/// - `0x`: 16;
/// - `0o`: 8;
/// - `0b`: 2;
/// - no prefix: 10.
pub struct PfxAsciiNumber;
impl<'a> Tool<'a> for PfxAsciiNumber {
    type Data = usize;
    type Error = View<'a>;

    fn parse(&self, st : View<'a>) -> Result<(Self::Data, usize, View<'a>), Self::Error>{
        st.match_if_else_data_len(&"0x", &AsciiNumber::new(16), |st| {
        st.match_if_else_data_len(&"0o", &AsciiNumber::new(8), |st| {
            st.match_if_else_data_len(&"0b", &AsciiNumber::new(2), |st| st.match_tool_data_len(&AsciiNumber::new(10)))
        })})
    }
}