1use sipha::red::SyntaxNode;
4use sipha::types::IntoSyntaxKind;
5use sipha::walk::{Visitor, WalkResult};
6
7use leekscript_core::syntax::Kind;
8
9use leekscript_core::Type;
10
11use super::node_helpers::{
12 class_decl_info, class_field_info, class_member_visibility, class_method_is_static,
13 for_in_loop_vars, function_decl_info, param_name, var_decl_info,
14};
15use super::scope::{ScopeId, ScopeKind, ScopeStore, VariableInfo, VariableKind};
16use super::type_expr::{
17 find_type_expr_child, param_and_return_types, parse_type_expr, TypeExprResult,
18};
19
20pub struct ScopeBuilder {
23 pub store: ScopeStore,
24 stack: Vec<ScopeId>,
25 pub scope_id_sequence: Vec<ScopeId>,
27 class_stack: Vec<String>,
29 root: Option<SyntaxNode>,
31}
32
33impl ScopeBuilder {
34 #[must_use]
35 pub fn new() -> Self {
36 let store = ScopeStore::new();
37 let stack = vec![store.root_id()];
38 Self {
39 store,
40 stack,
41 scope_id_sequence: Vec::new(),
42 class_stack: Vec::new(),
43 root: None,
44 }
45 }
46
47 #[must_use]
49 pub fn with_store(store: ScopeStore) -> Self {
50 let root_id = store.root_id();
51 Self {
52 store,
53 stack: vec![root_id],
54 scope_id_sequence: Vec::new(),
55 class_stack: Vec::new(),
56 root: None,
57 }
58 }
59
60 fn current(&self) -> Option<ScopeId> {
61 self.stack.last().copied()
62 }
63
64 fn push(&mut self, kind: ScopeKind) {
65 if self.stack.is_empty() {
67 self.stack.push(self.store.root_id());
68 }
69 let parent = self.current().unwrap_or_else(|| self.store.root_id());
70 let id = self.store.push(kind, parent);
71 self.stack.push(id);
72 self.scope_id_sequence.push(id);
73 }
74
75 fn pop(&mut self) {
76 if self.stack.len() > 1 {
77 self.stack.pop();
78 }
79 }
80
81 fn main_scope(&self) -> ScopeId {
82 self.store.root_id()
83 }
84
85 fn in_class_scope(&self) -> bool {
87 self.stack.iter().any(|&id| {
88 self.store
89 .get(id)
90 .is_some_and(|s| s.kind == ScopeKind::Class)
91 })
92 }
93}
94
95impl Default for ScopeBuilder {
96 fn default() -> Self {
97 Self::new()
98 }
99}
100
101fn is_top_level(node: &SyntaxNode, root: &SyntaxNode) -> bool {
103 for anc in node.ancestors(root) {
104 if let Some(Kind::NodeBlock | Kind::NodeFunctionDecl | Kind::NodeClassDecl) =
105 anc.kind_as::<Kind>()
106 {
107 return false;
108 }
109 }
110 true
111}
112
113pub fn seed_scope_from_program(store: &mut ScopeStore, root: &SyntaxNode) {
117 for kind in [
118 Kind::NodeClassDecl,
119 Kind::NodeFunctionDecl,
120 Kind::NodeVarDecl,
121 ] {
122 for node in root.find_all_nodes(kind.into_syntax_kind()) {
123 if !is_top_level(&node, root) {
124 continue;
125 }
126 match node.kind_as::<Kind>() {
127 Some(Kind::NodeClassDecl) => {
128 if let Some(info) = class_decl_info(&node) {
129 store.add_root_class(info.name.clone(), info.name_span);
130 }
131 }
132 Some(Kind::NodeFunctionDecl) => {
133 if let Some(info) = function_decl_info(&node) {
134 let (param_types, return_type) =
135 param_and_return_types(&node, Kind::NodeParam);
136 if let (Some(pt), Some(rt)) = (param_types, return_type) {
137 store.add_root_function_with_types(
138 info.name.clone(),
139 info.min_arity,
140 info.max_arity,
141 info.name_span,
142 Some(pt),
143 Some(rt),
144 );
145 } else {
146 store.add_root_function(
147 info.name.clone(),
148 info.min_arity,
149 info.max_arity,
150 info.name_span,
151 );
152 }
153 }
154 }
155 Some(Kind::NodeVarDecl) => {
156 if let Some(info) = var_decl_info(&node) {
157 if info.kind == super::node_helpers::VarDeclKind::Global {
158 let declared_type = find_type_expr_child(&node).and_then(|te| {
159 match parse_type_expr(&te) {
160 TypeExprResult::Ok(ty) => Some(ty),
161 TypeExprResult::Err(_) => None,
162 }
163 });
164 if let Some(ty) = declared_type {
165 store.add_root_global_with_type(info.name.clone(), ty);
166 } else {
167 store.add_root_global(info.name.clone());
168 }
169 }
170 }
171 }
172 _ => {}
173 }
174 }
175 }
176 seed_class_members_from_program(store, root);
177}
178
179fn seed_class_members_from_program(store: &mut ScopeStore, root: &SyntaxNode) {
182 let kind_class = Kind::NodeClassDecl.into_syntax_kind();
183 let class_decls: Vec<SyntaxNode> = root.find_all_nodes(kind_class);
184
185 for node in root.find_all_nodes(Kind::NodeClassField.into_syntax_kind()) {
186 let node_range = node.text_range();
187 let anc = class_decls.iter().find(|c| {
188 let r = c.text_range();
189 r.start <= node_range.start && node_range.end <= r.end
190 });
191 let class_name = match anc.and_then(class_decl_info) {
192 Some(info) => info.name,
193 None => continue,
194 };
195 if let Some((field_name, ty, is_static)) = class_field_info(&node) {
196 let ty = ty.unwrap_or(Type::any());
197 let vis = class_member_visibility(&node, root);
198 if is_static {
199 store.add_class_static_field(&class_name, field_name, ty, vis);
200 } else {
201 store.add_class_field(&class_name, field_name, ty, vis);
202 }
203 }
204 }
205
206 for node in root.find_all_nodes(Kind::NodeFunctionDecl.into_syntax_kind()) {
207 let node_range = node.text_range();
208 let anc = class_decls.iter().find(|c| {
209 let r = c.text_range();
210 r.start <= node_range.start && node_range.end <= r.end
211 });
212 let class_name = match anc.and_then(class_decl_info) {
213 Some(info) => info.name,
214 None => continue,
215 };
216 if let Some(info) = function_decl_info(&node) {
217 let (param_types, return_type) = param_and_return_types(&node, Kind::NodeParam);
218 let params = param_types.unwrap_or_default();
219 let ret = return_type.unwrap_or(Type::any());
220 let is_static = class_method_is_static(&node, root);
221 let vis = class_member_visibility(&node, root);
222 if is_static {
223 store.add_class_static_method(&class_name, info.name, params, ret, vis);
224 } else {
225 store.add_class_method(&class_name, info.name, params, ret, vis);
226 }
227 }
228 }
229}
230
231impl Visitor for ScopeBuilder {
232 fn enter_node(&mut self, node: &SyntaxNode) -> WalkResult {
233 if self.root.is_none() {
234 self.root = Some(node.clone());
235 }
236 let kind = match node.kind_as::<Kind>() {
237 Some(k) => k,
238 None => return WalkResult::Continue(()),
239 };
240
241 match kind {
242 Kind::NodeBlock => {
243 self.push(ScopeKind::Block);
244 }
245 Kind::NodeFunctionDecl => {
246 if self.in_class_scope() {
247 if let Some(class_name) = self.class_stack.last().cloned() {
248 if let Some(info) = function_decl_info(node) {
249 let (param_types, return_type) =
250 param_and_return_types(node, Kind::NodeParam);
251 let params = param_types.unwrap_or_default();
252 let ret = return_type.unwrap_or_else(|| {
254 if info.name == class_name {
255 Type::instance(class_name.clone())
256 } else {
257 Type::any()
258 }
259 });
260 let is_static = self
261 .root
262 .as_ref()
263 .is_some_and(|root| class_method_is_static(node, root));
264 let vis = class_member_visibility(node, self.root.as_ref().unwrap());
265 if is_static {
266 self.store.add_class_static_method(
267 &class_name,
268 info.name,
269 params,
270 ret,
271 vis,
272 );
273 } else {
274 self.store.add_class_method(
275 &class_name,
276 info.name,
277 params,
278 ret,
279 vis,
280 );
281 }
282 }
283 }
284 } else {
285 if let Some(info) = function_decl_info(node) {
287 if let Some(main_scope) = self.store.get_mut(self.main_scope()) {
288 let (param_types, return_type) =
289 param_and_return_types(node, Kind::NodeParam);
290 if param_types.is_some() || return_type.is_some() {
291 main_scope.add_function_with_types(
292 info.name.clone(),
293 info.min_arity,
294 info.max_arity,
295 info.name_span,
296 param_types,
297 return_type,
298 );
299 } else {
300 main_scope.add_function(
301 info.name.clone(),
302 info.min_arity,
303 info.max_arity,
304 info.name_span,
305 );
306 }
307 }
308 }
309 }
310 self.push(ScopeKind::Function);
311 }
312 Kind::NodeClassDecl => {
313 if let Some(info) = class_decl_info(node) {
314 if let Some(main_scope) = self.store.get_mut(self.main_scope()) {
315 main_scope.add_class(info.name.clone(), info.name_span);
316 }
317 self.class_stack.push(info.name.clone());
318 }
319 self.push(ScopeKind::Class);
320 }
321 Kind::NodeConstructorDecl => {
322 self.push(ScopeKind::Function);
324 }
325 Kind::NodeClassField => {
326 if let Some(class_name) = self.class_stack.last() {
327 if let Some((field_name, ty, is_static)) = class_field_info(node) {
328 let ty = ty.unwrap_or(Type::any());
329 let vis = class_member_visibility(node, self.root.as_ref().unwrap());
330 if is_static {
331 self.store
332 .add_class_static_field(class_name, field_name, ty, vis);
333 } else {
334 self.store.add_class_field(class_name, field_name, ty, vis);
335 }
336 }
337 }
338 }
339 Kind::NodeWhileStmt
340 | Kind::NodeForStmt
341 | Kind::NodeForInStmt
342 | Kind::NodeDoWhileStmt => {
343 self.push(ScopeKind::Loop);
344 if matches!(kind, Kind::NodeForInStmt) {
345 for (name, span) in for_in_loop_vars(node) {
346 if let Some(current_id) = self.current() {
347 if let Some(scope) = self.store.get_mut(current_id) {
348 scope.add_variable(VariableInfo {
349 name,
350 kind: VariableKind::Local,
351 span,
352 declared_type: None,
353 });
354 }
355 }
356 }
357 }
358 }
359 Kind::NodeVarDecl => {
360 if let Some(info) = var_decl_info(node) {
361 let declared_type = if info.kind == super::node_helpers::VarDeclKind::Var {
363 None
364 } else {
365 find_type_expr_child(node).and_then(|te| match parse_type_expr(&te) {
366 TypeExprResult::Ok(t) => Some(t),
367 TypeExprResult::Err(_) => None,
368 })
369 };
370 let var_kind = match info.kind {
371 super::node_helpers::VarDeclKind::Global => {
372 if let Some(main_scope) = self.store.get_mut(self.main_scope()) {
373 main_scope.add_global(info.name.clone());
374 }
375 VariableKind::Global
376 }
377 _ => VariableKind::Local,
378 };
379 if let Some(current_id) = self.current() {
380 if let Some(scope) = self.store.get_mut(current_id) {
381 scope.add_variable(VariableInfo {
382 name: info.name,
383 kind: var_kind,
384 span: info.name_span,
385 declared_type,
386 });
387 }
388 }
389 }
390 }
391 Kind::NodeParam => {
392 if let Some((name, span)) = param_name(node) {
393 let declared_type =
394 find_type_expr_child(node).and_then(|te| match parse_type_expr(&te) {
395 TypeExprResult::Ok(t) => Some(t),
396 TypeExprResult::Err(_) => None,
397 });
398 if let Some(current_id) = self.current() {
399 if let Some(scope) = self.store.get_mut(current_id) {
400 scope.add_variable(VariableInfo {
401 name,
402 kind: VariableKind::Parameter,
403 span,
404 declared_type,
405 });
406 }
407 }
408 }
409 }
410 _ => {}
411 }
412
413 WalkResult::Continue(())
414 }
415
416 fn leave_node(&mut self, node: &SyntaxNode) -> WalkResult {
417 let kind = match node.kind_as::<Kind>() {
418 Some(k) => k,
419 None => return WalkResult::Continue(()),
420 };
421
422 match kind {
423 Kind::NodeBlock
424 | Kind::NodeFunctionDecl
425 | Kind::NodeClassDecl
426 | Kind::NodeConstructorDecl
427 | Kind::NodeWhileStmt
428 | Kind::NodeForStmt
429 | Kind::NodeForInStmt
430 | Kind::NodeDoWhileStmt => {
431 if kind == Kind::NodeClassDecl {
432 self.class_stack.pop();
433 }
434 self.pop();
435 }
436 _ => {}
437 }
438
439 WalkResult::Continue(())
440 }
441}