semantic/parser/
parser_core.rs1use std::sync::{Arc, OnceLock};
5
6use objects::object::{ContentHash, OccurrenceRole, ScopeEntry, ScopeKind};
7use tree_sitter::{Node, Tree as TSTree};
8
9use super::{
10 parser_language::Language,
11 parser_pool::{parse_fresh, parse_fresh_bounded},
12 parser_types::{CallSite, FunctionDef, Import},
13 syntax_index::{FunctionRef, ImportRef, SyntaxIndex},
14};
15
16#[derive(Clone)]
18pub struct ParseBudget {
19 pub cancelled: Arc<std::sync::atomic::AtomicBool>,
20 pub deadline: std::time::Instant,
21}
22impl ParseBudget {
23 pub fn interrupted(&self) -> bool {
24 self.cancelled.load(std::sync::atomic::Ordering::Acquire)
25 || std::time::Instant::now() >= self.deadline
26 }
27}
28
29#[derive(Debug)]
31pub struct ParsedFile {
32 pub language: Language,
33 pub source: Arc<str>,
34 content_hash: ContentHash,
35 tree: TSTree,
36 index: OnceLock<SyntaxIndex>,
37}
38
39impl ParsedFile {
40 pub fn parse(source: impl AsRef<str>, language: Language) -> Option<Self> {
42 let source = Arc::<str>::from(source.as_ref());
43 let content_hash = ContentHash::compute(source.as_bytes());
44 Self::parse_with_hash(source, language, content_hash)
45 }
46
47 pub fn parse_bounded(source: &str, language: Language, budget: &ParseBudget) -> Option<Self> {
48 let tree = parse_fresh_bounded(source.as_bytes(), language, Some(budget))?;
49 if tree.root_node().has_error() || budget.interrupted() {
50 return None;
51 }
52 Some(Self {
53 language,
54 source: Arc::from(source),
55 content_hash: ContentHash::compute(source.as_bytes()),
56 tree,
57 index: OnceLock::new(),
58 })
59 }
60
61 pub fn parse_owned(source: String, language: Language) -> Option<Self> {
63 let content_hash = ContentHash::compute(source.as_bytes());
64 Self::parse_with_hash(Arc::<str>::from(source), language, content_hash)
65 }
66
67 pub(crate) fn parse_with_hash(
68 source: Arc<str>,
69 language: Language,
70 content_hash: ContentHash,
71 ) -> Option<Self> {
72 let tree = parse_fresh(source.as_bytes(), language)?;
73 if tree.root_node().has_error() {
74 return None;
75 }
76
77 Some(Self {
78 language,
79 source,
80 content_hash,
81 tree,
82 index: OnceLock::new(),
83 })
84 }
85
86 pub fn content_hash(&self) -> ContentHash {
88 self.content_hash
89 }
90
91 pub fn source(&self) -> &str {
93 &self.source
94 }
95
96 pub fn root_node(&self) -> Node<'_> {
98 self.tree.root_node()
99 }
100
101 pub fn syntax_index(&self) -> &SyntaxIndex {
103 self.index.get_or_init(|| {
104 SyntaxIndex::build(self.language, self.source.as_ref(), self.root_node())
105 })
106 }
107
108 pub fn functions(&self) -> impl Iterator<Item = FunctionRef<'_>> + '_ {
110 self.syntax_index().functions(self.source.as_ref())
111 }
112
113 pub fn imports(&self) -> impl Iterator<Item = ImportRef<'_>> + '_ {
115 self.syntax_index().imports(self.source.as_ref())
116 }
117
118 pub fn extract_functions(&self) -> Vec<FunctionDef> {
120 self.functions().map(FunctionRef::to_owned).collect()
121 }
122
123 pub fn extract_calls(&self) -> Vec<CallSite> {
126 self.syntax_index()
127 .occurrences()
128 .iter()
129 .filter(|occurrence| occurrence.role == OccurrenceRole::Call)
130 .map(|occurrence| CallSite {
131 name: occurrence.name.clone(),
132 qualifier: occurrence.qualifier.clone(),
133 })
134 .collect()
135 }
136
137 pub fn extract_own_calls(&self) -> Vec<CallSite> {
140 let index = self.syntax_index();
141 let scopes = index.semantic_scopes();
142 let Some(owner) = outermost_function_scope(scopes) else {
143 return Vec::new();
144 };
145 index
146 .occurrences()
147 .iter()
148 .filter(|occurrence| {
149 occurrence.role == OccurrenceRole::Call
150 && nearest_function_scope(scopes, occurrence.scope) == Some(owner)
151 })
152 .map(|occurrence| CallSite {
153 name: occurrence.name.clone(),
154 qualifier: occurrence.qualifier.clone(),
155 })
156 .collect()
157 }
158
159 pub fn extract_imports(&self) -> Vec<Import> {
161 self.imports().map(ImportRef::to_owned).collect()
162 }
163
164 pub fn is_function_kind(kind: &str, language: Language) -> bool {
166 super::syntax_index::is_function_kind(kind, language)
167 }
168}
169
170fn outermost_function_scope(scopes: &[ScopeEntry]) -> Option<u32> {
171 scopes.iter().find_map(|scope| {
172 if scope.kind != ScopeKind::Function {
173 return None;
174 }
175 let mut parent = scope.parent;
176 while let Some(id) = parent {
177 let entry = scopes.get(id as usize)?;
178 if entry.kind == ScopeKind::Function {
179 return None;
180 }
181 parent = entry.parent;
182 }
183 Some(scope.local_id)
184 })
185}
186
187fn nearest_function_scope(scopes: &[ScopeEntry], scope: u32) -> Option<u32> {
188 let mut current = Some(scope);
189 while let Some(id) = current {
190 let entry = scopes.get(id as usize)?;
191 if entry.kind == ScopeKind::Function {
192 return Some(id);
193 }
194 current = entry.parent;
195 }
196 None
197}