Skip to main content

oak_core/parser/
session.rs

1use crate::{
2    Language,
3    lexer::{LexOutput, LexerCache, Token},
4    memory::arena::SyntaxArena,
5    tree::GreenNode,
6};
7use std::{cell::Cell, ptr::NonNull};
8
9/// Trait for providing resources and caching for a parsing session.
10pub trait ParseCache<L: Language>: LexerCache<L> {
11    /// Returns the arena for allocating nodes in the current generation.
12    fn arena(&self) -> &SyntaxArena;
13
14    /// Returns the root of the previous tree for incremental parsing.
15    fn old_tree(&self) -> Option<&GreenNode<'_, L>>;
16
17    /// Returns the output of the lexing phase.
18    fn lex_output(&self) -> Option<&LexOutput<L>>;
19
20    /// Prepares for a new parsing generation (e.g. by swapping arenas).
21    fn prepare_generation(&mut self);
22
23    /// Commits the result of a parsing generation.
24    fn commit_generation(&self, root: &GreenNode<L>);
25}
26
27/// A memory pool that manages the lifecycle of parsing generations.
28///
29/// `ParseSession` handles the double-buffering of memory arenas (Active vs Old)
30/// to support efficient incremental reuse. It implements `ParseCache`.
31pub struct ParseSession<L: Language + Send + Sync> {
32    /// The arena holding the most recently parsed tree (or currently building).
33    arena_active: SyntaxArena,
34    /// The arena holding the previous tree (used for reuse).
35    arena_old: Option<SyntaxArena>,
36    /// Pointer to the root of the last parsed tree.
37    last_root: Cell<Option<NonNull<()>>>,
38    /// Full output from the last lexing pass.
39    last_lex: Option<LexOutput<L>>,
40}
41
42unsafe impl<L: Language + Send + Sync> Send for ParseSession<L> {}
43unsafe impl<L: Language + Send + Sync> Sync for ParseSession<L> {}
44
45impl<L: Language + Send + Sync> Default for ParseSession<L> {
46    fn default() -> Self {
47        Self::new(16)
48    }
49}
50
51impl<L: Language + Send + Sync> ParseSession<L> {
52    /// Creates a new parse session.
53    pub fn new(capacity: usize) -> Self {
54        Self { arena_active: SyntaxArena::new(capacity), arena_old: None, last_root: Cell::new(None), last_lex: None }
55    }
56
57    /// Returns the root of the last parsed tree.
58    pub fn last_root(&self) -> Option<&GreenNode<'_, L>> {
59        let ptr = self.last_root.get()?;
60        // Safety: last_root is guaranteed to be in arena_active after commit_generation
61        unsafe { Some(&*(ptr.as_ptr() as *const GreenNode<'_, L>)) }
62    }
63}
64
65impl<L: Language + Send + Sync> ParseCache<L> for ParseSession<L> {
66    fn arena(&self) -> &SyntaxArena {
67        &self.arena_active
68    }
69
70    fn old_tree(&self) -> Option<&GreenNode<'_, L>> {
71        let ptr = self.last_root.get()?;
72        if self.arena_old.is_some() {
73            // Safety: last_root is guaranteed to be in arena_old after swap_generations
74            unsafe { Some(&*(ptr.as_ptr() as *const GreenNode<'_, L>)) }
75        }
76        else {
77            None
78        }
79    }
80
81    fn lex_output(&self) -> Option<&LexOutput<L>> {
82        self.last_lex.as_ref()
83    }
84
85    fn prepare_generation(&mut self) {
86        // 1. Move active to old. Oldest is dropped.
87        self.arena_old = Some(std::mem::replace(&mut self.arena_active, SyntaxArena::new(16)));
88
89        // 2. last_root now correctly points into arena_old.
90        // 3. Clear last lex output to force re-lexing for the new generation.
91        self.last_lex = None
92    }
93
94    fn commit_generation(&self, root: &GreenNode<L>) {
95        // Safety: We cast to void pointer because ParseSession manages the lifetime.
96        unsafe { self.last_root.set(Some(NonNull::new_unchecked(root as *const _ as *mut ()))) }
97    }
98}
99
100impl<L: Language + Send + Sync> LexerCache<L> for ParseSession<L> {
101    fn set_lex_output(&mut self, output: LexOutput<L>) {
102        self.last_lex = Some(output)
103    }
104
105    fn get_token(&self, index: usize) -> Option<Token<L::TokenType>> {
106        self.last_lex.as_ref()?.result.as_ref().ok()?.get(index).cloned()
107    }
108
109    fn count_tokens(&self) -> usize {
110        self.last_lex.as_ref().and_then(|out| out.result.as_ref().ok()).map(|tokens| tokens.len()).unwrap_or(0)
111    }
112
113    fn has_tokens(&self) -> bool {
114        self.last_lex.as_ref().and_then(|out| out.result.as_ref().ok()).map(|tokens| !tokens.is_empty()).unwrap_or(false)
115    }
116
117    fn get_tokens(&self) -> Option<&[Token<L::TokenType>]> {
118        self.last_lex.as_ref()?.result.as_ref().ok().map(|tokens| &**tokens)
119    }
120}
121
122impl<'a, L: Language, C: ParseCache<L> + ?Sized> ParseCache<L> for &'a mut C {
123    fn arena(&self) -> &SyntaxArena {
124        (**self).arena()
125    }
126
127    fn old_tree(&self) -> Option<&GreenNode<'_, L>> {
128        (**self).old_tree()
129    }
130
131    fn lex_output(&self) -> Option<&LexOutput<L>> {
132        (**self).lex_output()
133    }
134
135    fn prepare_generation(&mut self) {
136        (**self).prepare_generation()
137    }
138
139    fn commit_generation(&self, root: &GreenNode<L>) {
140        (**self).commit_generation(root)
141    }
142}