neotoma 0.1.1

A flexible, cached parser combinator framework for Rust.
Documentation
//! Self-referential parser support through deferred construction.
//!
//! This module provides the [`Recursive`] wrapper that solves the circular
//! dependency problem when building parsers that need to reference themselves
//! directly or indirectly. Instead of constructing the parser immediately,
//! it defers construction using a closure and caches the result.
//!
//! Recursive parsers are essential for parsing nested structures like
//! arithmetic expressions, JSON objects, or any grammar with recursive
//! production rules.

use std::cell::OnceCell;

use crate::{cache::ParsingCache, parser::Parser, result::ParseResult};

/// A wrapper that enables self-referential parsers by deferring construction.
///
/// This solves the circular dependency problem where a parser needs to contain
/// itself (directly or indirectly). Instead of creating the parser during
/// construction, `Recursive<P>` creates it lazily on first use and caches it.
///
/// # The Problem
///
/// Self-referential parsers are common in language grammars but cause compilation issues:
///
/// ```compile_fail
/// // This won't compile - infinite type size!
/// struct Expression {
///     parenthesized: Expression,  // ERROR: recursive without indirection
/// }
/// ```
///
/// # The Solution  
///
/// `Recursive<P>` breaks the cycle during construction while enabling unlimited recursion at parse time:
///
/// ```rust
/// use std::io::Cursor;
/// use neotoma::{
///     recursive::Recursive,
///     parser::{Parser, Source, parse},
///     literal::Literal,
///     result::ParseResult,
///     cache::ParsingCache
/// };
///
/// // Example AST for expressions
/// #[derive(Debug, PartialEq, Clone)]
/// enum MyAst {
///     Atom(String),
///     Parenthesized(Box<MyAst>),
/// }
///
/// #[derive(Clone, Default)]
/// struct Expression {
///     // This compiles and works!
///     parenthesized: Recursive<Expression>,
/// }
///
/// impl Parser for Expression {
///     type Output = MyAst;
///     
///     fn read<S>(
///         &self,
///         source: &mut Source<S>,
///         cache: &mut impl ParsingCache,
///         context: &mut ()
///     ) -> ParseResult<Self::Output>
///     where
///         S: neotoma::parser::Parsable,
///     {
///         // Try to parse parentheses like: (inner_expression)
///         let open_paren = Literal::from_str("(");
///         if open_paren.parse(source, cache, context).is_ok() {
///             // This creates unlimited recursion depth as needed
///             let inner = self.parenthesized.parse(source, cache, context)?;
///             let close_paren = Literal::from_str(")");
///             close_paren.parse(source, cache, context)?;
///             Ok(MyAst::Parenthesized(Box::new(inner)))
///         } else {
///             // Parse a simple atom (single letter)
///             let atom = Literal::from_str("x");
///             atom.parse(source, cache, context)?;
///             Ok(MyAst::Atom("x".to_string()))
///         }
///     }
/// }
///
/// // Test it works with nested parentheses
/// let parser = Expression::default();
/// let cursor = Cursor::new(b"((x))");
/// let mut source = Source::new(cursor);
///
/// let result = parse(parser, &mut source).unwrap();
/// assert_eq!(result, MyAst::Parenthesized(Box::new(
///     MyAst::Parenthesized(Box::new(MyAst::Atom("x".to_string())))
/// )));
/// ```
///
/// # How it works
///
/// 1. **Construction**: `Recursive::new()` creates an empty wrapper (no cycles)
/// 2. **First parse**: Creates the wrapped parser using `P::default()` and caches it  
/// 3. **Subsequent parses**: Reuses the same cached instance for efficiency
///
/// This enables parsers that can handle arbitrarily deep nesting (like deeply nested parentheses)
/// without construction-time infinite recursion.
///
/// # Requirements
///
/// The wrapped parser type `P` must implement `Default` so that `Recursive<P>` can create
/// an instance when first needed.
pub struct Recursive<P> {
    cached_parser: OnceCell<Box<P>>,
}

impl<P> Recursive<P>
where
    P: Default,
{
    /// Creates a new `Recursive` wrapper that will construct the parser
    /// using `P::default()` on first use.
    ///
    /// The wrapped parser is created lazily - only when first needed during parsing.
    pub fn new() -> Self {
        Self {
            cached_parser: OnceCell::new(),
        }
    }

    /// Gets or creates the cached parser instance.
    fn get_parser(&self) -> &P {
        self.cached_parser.get_or_init(|| Box::new(P::default()))
    }
}

impl<P, Ctx> Parser<Ctx> for Recursive<P>
where
    P: Parser<Ctx> + Default,
{
    type Output = P::Output;

    fn read<S>(
        &self,
        source: &mut crate::parser::Source<S>,
        cache: &mut impl ParsingCache,
        context: &mut Ctx,
    ) -> ParseResult<Self::Output>
    where
        S: crate::parser::Parsable,
    {
        // Delegate to the cached parser instance
        self.get_parser().read(source, cache, context)
    }

    fn id(&self) -> u64 {
        // Use the same cached instance for consistent ID
        self.get_parser().id()
    }
}

impl<P> Default for Recursive<P> {
    fn default() -> Self {
        Self {
            cached_parser: OnceCell::new(),
        }
    }
}

// Implement Clone by creating a new empty Recursive
// (we don't want to share the cached instance between clones)
impl<P> Clone for Recursive<P> {
    fn clone(&self) -> Self {
        Self::default()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{literal::Literal, parser::Source};
    use std::io::Cursor;

    // Simple test parser that just parses "hello"
    #[derive(Clone)]
    struct HelloParser;

    impl Default for HelloParser {
        fn default() -> Self {
            Self
        }
    }

    impl<Ctx> Parser<Ctx> for HelloParser {
        type Output = String;

        fn read<S>(
            &self,
            source: &mut Source<S>,
            cache: &mut impl ParsingCache,
            context: &mut Ctx,
        ) -> ParseResult<Self::Output>
        where
            S: crate::parser::Parsable,
        {
            let literal = Literal::from_str_const("hello");
            literal
                .parse(source, cache, context)
                .map(|_| "hello".to_string())
        }
    }

    #[test]
    fn recursive_parser_works() {
        use crate::parser::parse;
        let recursive = Recursive::<HelloParser>::new();
        let cursor = Cursor::new(b"hello");
        let mut source = Source::new(cursor);

        let result = parse(recursive, &mut source);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), "hello");
    }

    #[test]
    fn recursive_parser_caches_instance() {
        let recursive = Recursive::<HelloParser>::new();

        // First access should create the parser
        let id1 = <Recursive<HelloParser> as crate::parser::Parser<()>>::id(&recursive);

        // Second access should use the same cached instance
        let id2 = <Recursive<HelloParser> as crate::parser::Parser<()>>::id(&recursive);

        assert_eq!(id1, id2, "IDs should be identical for cached instance");
    }
}