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