oak_core/parser/
mod.rs

1#![doc = include_str!("readme.md")]
2
3/// Pratt parser implementation for operator precedence parsing.
4pub mod pratt;
5/// Parser memory pool management.
6pub mod session;
7/// Internal parser state and checkpointing.
8pub mod state;
9
10pub use self::{
11    pratt::{Associativity, OperatorInfo, Pratt, PrattParser, binary, postfix, unary},
12    session::{ParseCache, ParseSession},
13    state::ParserState,
14};
15
16pub use triomphe::Arc;
17
18pub use crate::{
19    Language, Lexer,
20    errors::{OakDiagnostics, OakError},
21    source::{Source, TextEdit},
22    tree::GreenNode,
23};
24
25/// The output of a parsing operation, containing the result and diagnostics.
26pub type ParseOutput<'a, L: Language> = OakDiagnostics<&'a GreenNode<'a, L>>;
27
28/// Core parser trait that defines how to run the parser.
29pub trait Parser<L: Language + Send + Sync + 'static> {
30    /// The core parsing entry point.
31    ///
32    /// This method orchestrates the parsing process using the provided cache.
33    /// It should handle incremental reuse automatically if the cache contains a previous tree.
34    ///
35    /// # Arguments
36    /// * `text` - The source text
37    /// * `edits` - Edits applied to the source since the last parse
38    /// * `cache` - The cache for resources and incremental reuse
39    fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<L>) -> ParseOutput<'a, L>;
40}
41
42/// Standalone parsing function that coordinates lexing and parsing.
43///
44/// This is a convenience function for performing a complete parse (lexing + parsing)
45/// in one call.
46pub fn parse<'a, L, P, Lex, S>(parser: &P, _lexer: &Lex, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<L>) -> ParseOutput<'a, L>
47where
48    L: Language + Send + Sync + 'static,
49    P: Parser<L>,
50    Lex: Lexer<L>,
51    S: Source + ?Sized,
52{
53    parser.parse(text, edits, cache)
54}
55
56/// Standalone parsing function that performs a complete parse without incremental reuse.
57///
58/// This is a convenience function for parsing a source from scratch.
59pub fn parse_one_pass<'a, L, P, S>(parser: &P, text: &'a S, cache: &'a mut impl ParseCache<L>) -> ParseOutput<'a, L>
60where
61    L: Language + Send + Sync + 'static,
62    P: Parser<L>,
63    S: Source + ?Sized,
64{
65    parser.parse(text, &[], cache)
66}
67
68/// Helper for implementing `Parser::parse` with automatic lexing.
69///
70/// This function handles the boilerplate of preparing the cache, ensuring lexing is performed,
71/// setting up the parser state, and committing the result.
72pub fn parse_with_lexer<'a, L, S, Lex>(lexer: &Lex, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<L>, run: impl FnOnce(&mut ParserState<'a, L, S>) -> Result<&'a GreenNode<'a, L>, OakError>) -> ParseOutput<'a, L>
73where
74    L: Language + Send + Sync + 'static,
75    S: Source + ?Sized,
76    Lex: Lexer<L>,
77{
78    // 1. Prepare for new generation
79    cache.prepare_generation();
80
81    // 2. Get Lexing Result (Auto-lex if missing)
82    let lex_out = match cache.lex_output() {
83        Some(out) => out.clone(),
84        None => {
85            let out = lexer.lex(text, edits, cache);
86            cache.set_lex_output(out.clone());
87            out
88        }
89    };
90
91    let capacity_hint = cache.old_tree().map(|old| old.children.len().max(1024)).unwrap_or(1024);
92
93    // 3. Initialize Parser State
94    // Safety: We transmute the arena and old tree to 'a to satisfy the borrow checker.
95    // The ParseCache guarantees that the arena and old tree live long enough.
96    let arena: &'a crate::memory::arena::SyntaxArena = unsafe { std::mem::transmute(cache.arena()) };
97    let mut st = ParserState::new(arena, lex_out, text, capacity_hint);
98
99    if let Some(old) = cache.old_tree() {
100        let old: &'a GreenNode<'a, L> = unsafe { std::mem::transmute(old) };
101        st.set_incremental(old, edits);
102    }
103
104    // 4. Run Parser Logic
105    let result = run(&mut st);
106    let output = st.finish(result);
107
108    // 5. Commit Generation
109    if let Ok(root) = output.result {
110        cache.commit_generation(root);
111    }
112
113    output
114}