neotoma/lib.rs
1//! # Neotoma - Flexible Parser Combinator Library
2//!
3//! Neotoma is a Rust parsing library that provides a flexible, cached parser combinator framework.
4//! It's designed to parse structured data from any source implementing `Read + Seek` traits, with
5//! built-in memoization and backtracking capabilities.
6//!
7//! ## Key Features
8//!
9//! - **Composable parsers**: Build complex parsers from simple building blocks
10//! - **Automatic memoization**: Built-in caching prevents redundant parsing work
11//! - **Backtracking support**: Automatic position management for failed parses
12//! - **UTF-8 aware**: Comprehensive Unicode handling with ASCII optimization
13//! - **Thread-safe**: Uses efficient synchronization primitives from `parking_lot`
14//! - **Generic input**: Works with any `Read + Seek` source
15//!
16//! ## Quick Start
17//!
18//! ```rust
19//! use neotoma::prelude::*;
20//! use neotoma::{seq, oneof};
21//! use std::io::Cursor;
22//!
23//! // Build a parser for "hello" followed by optional whitespace and "world"
24//! let parser = seq![
25//! Literal::from_str("hello"),
26//! Optional::new(Utf8Class::whitespace()),
27//! Literal::from_str("world")
28//! ];
29//!
30//! let input = Cursor::new(b"hello world");
31//! let mut source = Source::new(input);
32//!
33//! let result = parse(parser, &mut source);
34//! assert!(result.is_ok());
35//! ```
36//!
37//! ## Core Concepts
38//!
39//! ### Parser Trait
40//! All parsers implement the [`Parser`] trait, which provides:
41//! - `read()`: Implement your parsing logic here
42//! - `parse()`: Public API that handles caching and backtracking automatically
43//! - `id()`: Must be overridden for parameterized parsers to avoid cache conflicts
44//!
45//! ### Composition
46//! Build complex parsers using composition macros:
47//! - [`seq!`] for sequential parsing
48//! - [`oneof!`] for alternative choices
49//!
50//! ### Common Parser Types
51//! - [`Literal`]: Match exact byte sequences or strings
52//! - [`Class`]: Match character classes (byte-level)
53//! - [`Utf8Class`]: Match Unicode character classes
54//! - [`Repeat`]: Match repeated patterns with optional separators
55//! - [`Optional`]: Match zero or one occurrence
56//!
57//! ## Architecture
58//!
59//! The library uses a **Template Method Pattern** for the Parser trait, where `parse()`
60//! handles caching and backtracking, while implementations provide custom logic in `read()`.
61//! The caching system uses a **Strategy Pattern** allowing different cache implementations.
62
63pub mod cache;
64pub mod class;
65pub mod either;
66pub mod eof;
67pub mod grammar;
68pub mod literal;
69pub mod optional;
70pub mod parser;
71pub mod recursive;
72pub mod repeat;
73pub mod result;
74pub mod sequence;
75pub mod until;
76pub mod utf8class;
77pub mod utf8util;
78
79// Re-export the most commonly used types and functions at the root level
80// for better ergonomics
81
82// Core parser trait and utilities
83pub use parser::{Parser, Source, parse};
84
85// Common result types
86pub use result::{Error, ParseResult};
87
88// Main parser types (used in almost every parser combination)
89pub use class::Class;
90pub use literal::Literal;
91pub use utf8class::Utf8Class;
92
93// Common combinators
94pub use eof::EndOfFile;
95pub use optional::Optional;
96pub use recursive::Recursive;
97pub use repeat::Repeat;
98
99// Grammar parsing with named rules and recursion
100pub use grammar::GrammarParser;
101
102// Composition macros are already exported at crate root via #[macro_export]
103// seq! and oneof! are available directly
104
105// Prelude module for glob imports
106pub mod prelude {
107 //! Commonly used imports for parser development
108 //!
109 //! # Example
110 //!
111 //! ```rust
112 //! use neotoma::prelude::*;
113 //! use neotoma::{seq, oneof}; // Macros are at crate root
114 //!
115 //! // Now you have access to all the commonly used types
116 //! let parser = seq![
117 //! Literal::from_str("hello"),
118 //! Optional::new(Utf8Class::whitespace()),
119 //! Literal::from_str("world")
120 //! ];
121 //! ```
122
123 pub use crate::{
124 Class,
125 // Combinators
126 EndOfFile,
127 Error,
128 // Grammar parsing
129 GrammarParser,
130 // Main parser types
131 Literal,
132 Optional,
133 ParseResult,
134 // Core traits and functions
135 Parser,
136 Recursive,
137 Repeat,
138 Source,
139 Utf8Class,
140 // Composition macros (re-exported from crate root)
141 // Note: seq! and oneof! macros are available directly, not as paths
142 // Cache trait for implementing custom parsers
143 cache::ParsingCache,
144 parse,
145 };
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151 use std::io::Cursor;
152
153 // Tests moved from tests/test_root_exports.rs
154
155 #[test]
156 fn test_root_level_exports_basic() {
157 // Test that we can use the types directly from root
158 let literal = Literal::from_str("test");
159
160 let cursor = Cursor::new(b"test");
161 let mut source = Source::new(cursor);
162
163 let result = parse(literal, &mut source);
164 assert!(result.is_ok());
165 }
166
167 #[test]
168 fn test_root_level_exports_with_macros() {
169 // Test using the seq! macro with root-level imports
170 let parser = seq![
171 Literal::from_str("Hello"),
172 Optional::new(Utf8Class::whitespace()),
173 Literal::from_str("World")
174 ];
175
176 let cursor = Cursor::new(b"Hello World");
177 let mut source = Source::new(cursor);
178
179 let result = parse(parser, &mut source);
180 assert!(result.is_ok());
181 }
182
183 #[test]
184 fn test_root_level_exports_oneof() {
185 // Test oneof! macro with root-level imports
186 let parser = oneof![Literal::from_str("hello"), Literal::from_str("world")];
187
188 let cursor = Cursor::new(b"hello");
189 let mut source = Source::new(cursor);
190
191 let result = parse(parser, &mut source);
192 assert!(result.is_ok());
193 }
194
195 #[test]
196 fn test_prelude_import() {
197 // Test the prelude module
198 use crate::prelude::*;
199
200 let parser = seq![Literal::from_str("test"), Optional::new(Utf8Class::alpha())];
201
202 let cursor = Cursor::new(b"testx");
203 let mut source = Source::new(cursor);
204
205 let result = parse(parser, &mut source);
206 assert!(result.is_ok());
207 }
208
209 #[test]
210 fn test_grammar_parser_exported() {
211 // Ensure GrammarParser is available at root level
212 let _parser = GrammarParser::new();
213 }
214}