lib.rs
raw
//! # Neotoma - Flexible Parser Combinator Library
//!
//! Neotoma is a Rust parsing library that provides a flexible, cached parser combinator framework.
//! It's designed to parse structured data from any source implementing `Read + Seek` traits, with
//! built-in memoization and backtracking capabilities.
//!
//! ## Key Features
//!
//! - **Composable parsers**: Build complex parsers from simple building blocks
//! - **Automatic memoization**: Built-in caching prevents redundant parsing work
//! - **Backtracking support**: Automatic position management for failed parses
//! - **UTF-8 aware**: Comprehensive Unicode handling with ASCII optimization
//! - **Thread-safe**: Uses efficient synchronization primitives from `parking_lot`
//! - **Generic input**: Works with any `Read + Seek` source
//!
//! ## Quick Start
//!
//! ```rust
//! use neotoma::prelude::*;
//! use neotoma::{seq, oneof};
//! use std::io::Cursor;
//!
//! // Build a parser for "hello" followed by optional whitespace and "world"
//! let parser = seq![
//! Literal::from_str("hello"),
//! Optional::new(Utf8Class::whitespace()),
//! Literal::from_str("world")
//! ];
//!
//! let input = Cursor::new(b"hello world");
//! let mut source = Source::new(input);
//!
//! let result = parse(parser, &mut source);
//! assert!(result.is_ok());
//! ```
//!
//! ## Core Concepts
//!
//! ### Parser Trait
//! All parsers implement the [`Parser`] trait, which provides:
//! - `read()`: Implement your parsing logic here
//! - `parse()`: Public API that handles caching and backtracking automatically
//! - `id()`: Must be overridden for parameterized parsers to avoid cache conflicts
//!
//! ### Composition
//! Build complex parsers using composition macros:
//! - [`seq!`] for sequential parsing
//! - [`oneof!`] for alternative choices
//!
//! ### Common Parser Types
//! - [`Literal`]: Match exact byte sequences or strings
//! - [`Class`]: Match character classes (byte-level)
//! - [`Utf8Class`]: Match Unicode character classes
//! - [`Repeat`]: Match repeated patterns with optional separators
//! - [`Optional`]: Match zero or one occurrence
//!
//! ## Architecture
//!
//! The library uses a **Template Method Pattern** for the Parser trait, where `parse()`
//! handles caching and backtracking, while implementations provide custom logic in `read()`.
//! The caching system uses a **Strategy Pattern** allowing different cache implementations.
pub mod cache;
pub mod class;
pub mod either;
pub mod eof;
pub mod grammar;
pub mod literal;
pub mod optional;
pub mod parser;
pub mod recursive;
pub mod repeat;
pub mod result;
pub mod sequence;
pub mod until;
pub mod utf8class;
pub mod utf8util;
// Re-export the most commonly used types and functions at the root level
// for better ergonomics
// Core parser trait and utilities
pub use parser::{Parser, Source, parse};
// Common result types
pub use result::{Error, ParseResult};
// Main parser types (used in almost every parser combination)
pub use class::Class;
pub use literal::Literal;
pub use utf8class::Utf8Class;
// Common combinators
pub use eof::EndOfFile;
pub use optional::Optional;
pub use recursive::Recursive;
pub use repeat::Repeat;
// Grammar parsing with named rules and recursion
pub use grammar::GrammarParser;
// Composition macros are already exported at crate root via #[macro_export]
// seq! and oneof! are available directly
// Prelude module for glob imports
pub mod prelude {
//! Commonly used imports for parser development
//!
//! # Example
//!
//! ```rust
//! use neotoma::prelude::*;
//! use neotoma::{seq, oneof}; // Macros are at crate root
//!
//! // Now you have access to all the commonly used types
//! let parser = seq![
//! Literal::from_str("hello"),
//! Optional::new(Utf8Class::whitespace()),
//! Literal::from_str("world")
//! ];
//! ```
pub use crate::{
Class,
// Combinators
EndOfFile,
Error,
// Grammar parsing
GrammarParser,
// Main parser types
Literal,
Optional,
ParseResult,
// Core traits and functions
Parser,
Recursive,
Repeat,
Source,
Utf8Class,
// Composition macros (re-exported from crate root)
// Note: seq! and oneof! macros are available directly, not as paths
// Cache trait for implementing custom parsers
cache::ParsingCache,
parse,
};
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Cursor;
// Tests moved from tests/test_root_exports.rs
#[test]
fn test_root_level_exports_basic() {
// Test that we can use the types directly from root
let literal = Literal::from_str("test");
let cursor = Cursor::new(b"test");
let mut source = Source::new(cursor);
let result = parse(literal, &mut source);
assert!(result.is_ok());
}
#[test]
fn test_root_level_exports_with_macros() {
// Test using the seq! macro with root-level imports
let parser = seq![
Literal::from_str("Hello"),
Optional::new(Utf8Class::whitespace()),
Literal::from_str("World")
];
let cursor = Cursor::new(b"Hello World");
let mut source = Source::new(cursor);
let result = parse(parser, &mut source);
assert!(result.is_ok());
}
#[test]
fn test_root_level_exports_oneof() {
// Test oneof! macro with root-level imports
let parser = oneof![Literal::from_str("hello"), Literal::from_str("world")];
let cursor = Cursor::new(b"hello");
let mut source = Source::new(cursor);
let result = parse(parser, &mut source);
assert!(result.is_ok());
}
#[test]
fn test_prelude_import() {
// Test the prelude module
use crate::prelude::*;
let parser = seq![Literal::from_str("test"), Optional::new(Utf8Class::alpha())];
let cursor = Cursor::new(b"testx");
let mut source = Source::new(cursor);
let result = parse(parser, &mut source);
assert!(result.is_ok());
}
#[test]
fn test_grammar_parser_exported() {
// Ensure GrammarParser is available at root level
let _parser = GrammarParser::new();
}
}