1use sipha::types::Span;
4use std::collections::HashMap;
5
6use leekscript_core::doc_comment::DocComment;
7use leekscript_core::Type;
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
11pub struct ScopeId(pub usize);
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15pub enum ScopeKind {
16 Main,
18 Function,
20 Class,
22 Loop,
24 Block,
26}
27
28#[derive(Clone, Debug)]
30pub struct VariableInfo {
31 pub name: String,
32 pub kind: VariableKind,
33 pub span: Span,
34 pub declared_type: Option<Type>,
36}
37
38#[derive(Clone, Copy, Debug, PartialEq, Eq)]
39pub enum VariableKind {
40 Local,
41 Global,
42 Parameter,
43}
44
45#[derive(Clone, Debug)]
47pub struct FunctionOverload {
48 pub min_arity: usize,
49 pub max_arity: usize,
50 pub span: Span,
51 pub param_types: Option<Vec<Type>>,
53 pub return_type: Option<Type>,
54}
55
56#[derive(Clone, Debug, Default)]
58pub struct SigMeta {
59 pub doc: Option<DocComment>,
61 pub complexity: Option<u8>,
63}
64
65#[must_use]
67pub fn complexity_display_string(code: u8) -> &'static str {
68 match code {
69 1 => "O(1)",
70 2 => "O(log(n))",
71 3 => "O(√n)",
72 4 => "O(n)",
73 5 => "O(nlog*(n))",
74 6 => "O(nlog(n))",
75 7 => "O(n²)",
76 8 => "O(n³)",
77 9 => "2^poly(log(n))",
78 10 => "2^poly(n)",
79 11 => "O(n!)",
80 12 => "2^2^poly(n)",
81 13 => "∞",
82 _ => "?",
83 }
84}
85
86#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
88pub enum MemberVisibility {
89 #[default]
90 Public,
91 Protected,
92 Private,
93}
94
95#[derive(Clone, Debug, Default)]
97pub struct ClassMembers {
98 pub fields: HashMap<String, (Type, MemberVisibility)>,
100 pub methods: HashMap<String, (Vec<Type>, Type, MemberVisibility)>,
102 pub static_fields: HashMap<String, (Type, MemberVisibility)>,
104 pub static_methods: HashMap<String, (Vec<Type>, Type, MemberVisibility)>,
106}
107
108#[derive(Debug)]
110pub struct Scope {
111 pub kind: ScopeKind,
112 pub parent: Option<ScopeId>,
113 variables: HashMap<String, VariableInfo>,
114 globals: Option<std::collections::HashSet<String>>,
116 global_types: Option<HashMap<String, Type>>,
118 functions: Option<HashMap<String, Vec<FunctionOverload>>>,
120 classes: Option<HashMap<String, Span>>,
122}
123
124impl Scope {
125 #[must_use]
126 pub fn new_main() -> Self {
127 Self {
128 kind: ScopeKind::Main,
129 parent: None,
130 variables: HashMap::new(),
131 globals: Some(std::collections::HashSet::new()),
132 global_types: Some(HashMap::new()),
133 functions: Some(HashMap::new()),
134 classes: Some(HashMap::new()),
135 }
136 }
137
138 #[must_use]
139 pub fn new_child(kind: ScopeKind, parent: ScopeId) -> Self {
140 Self {
141 kind,
142 parent: Some(parent),
143 variables: HashMap::new(),
144 globals: None,
145 global_types: None,
146 functions: None,
147 classes: None,
148 }
149 }
150
151 pub fn add_variable(&mut self, info: VariableInfo) {
152 self.variables.insert(info.name.clone(), info);
153 }
154
155 #[must_use]
156 pub fn has_variable(&self, name: &str) -> bool {
157 self.variables.contains_key(name)
158 }
159
160 #[must_use]
162 pub fn variable_names(&self) -> Vec<String> {
163 self.variables.keys().cloned().collect()
164 }
165
166 #[must_use]
168 pub fn global_names(&self) -> Option<Vec<String>> {
169 self.globals.as_ref().map(|g| g.iter().cloned().collect())
170 }
171
172 #[must_use]
174 pub fn function_names(&self) -> Option<Vec<String>> {
175 self.functions.as_ref().map(|f| f.keys().cloned().collect())
176 }
177
178 #[must_use]
180 pub fn class_names(&self) -> Option<Vec<String>> {
181 self.classes.as_ref().map(|c| c.keys().cloned().collect())
182 }
183
184 pub fn add_global(&mut self, name: String) {
186 if let Some(g) = &mut self.globals {
187 g.insert(name);
188 }
189 }
190
191 pub fn add_function(&mut self, name: String, min_arity: usize, max_arity: usize, span: Span) {
193 if let Some(f) = &mut self.functions {
194 f.entry(name).or_default().push(FunctionOverload {
195 min_arity,
196 max_arity,
197 span,
198 param_types: None,
199 return_type: None,
200 });
201 }
202 }
203
204 pub fn add_function_with_types(
206 &mut self,
207 name: String,
208 min_arity: usize,
209 max_arity: usize,
210 span: Span,
211 param_types: Option<Vec<Type>>,
212 return_type: Option<Type>,
213 ) {
214 if let Some(f) = &mut self.functions {
215 f.entry(name).or_default().push(FunctionOverload {
216 min_arity,
217 max_arity,
218 span,
219 param_types,
220 return_type,
221 });
222 }
223 }
224
225 pub fn add_class(&mut self, name: String, span: Span) {
229 if let Some(c) = &mut self.classes {
230 let is_empty = span.start >= span.end;
231 match c.entry(name) {
232 std::collections::hash_map::Entry::Vacant(e) => {
233 e.insert(span);
234 }
235 std::collections::hash_map::Entry::Occupied(mut e) => {
236 let existing = e.get();
237 if existing.start >= existing.end && !is_empty {
238 e.insert(span);
239 }
240 }
241 }
242 }
243 }
244
245 #[must_use]
246 pub fn has_global(&self, name: &str) -> bool {
247 self.globals.as_ref().is_some_and(|g| g.contains(name))
248 }
249
250 #[must_use]
252 pub fn function_accepts_arity(&self, name: &str, arity: usize) -> bool {
253 self.functions.as_ref().is_some_and(|f| {
254 f.get(name).is_some_and(|ranges| {
255 ranges
256 .iter()
257 .any(|o| o.min_arity <= arity && arity <= o.max_arity)
258 })
259 })
260 }
261
262 #[must_use]
264 pub fn get_function_span(&self, name: &str) -> Option<Span> {
265 self.functions
266 .as_ref()
267 .and_then(|f| f.get(name).and_then(|v| v.first()).map(|o| o.span))
268 }
269
270 #[must_use]
272 pub fn get_function_span_for_arity_range(
273 &self,
274 name: &str,
275 min_arity: usize,
276 max_arity: usize,
277 ) -> Option<Span> {
278 self.functions.as_ref().and_then(|f| {
279 f.get(name).and_then(|ranges| {
280 ranges
281 .iter()
282 .find(|o| o.min_arity == min_arity && o.max_arity == max_arity)
283 .map(|o| o.span)
284 })
285 })
286 }
287
288 #[must_use]
290 pub fn get_function_arity(&self, name: &str) -> Option<usize> {
291 self.functions
292 .as_ref()
293 .and_then(|f| f.get(name).and_then(|v| v.first()).map(|o| o.max_arity))
294 }
295
296 #[must_use]
298 pub fn get_function_type(&self, name: &str, arity: usize) -> Option<(Vec<Type>, Type)> {
299 let overloads = self.functions.as_ref()?.get(name)?;
300 let o = overloads
301 .iter()
302 .find(|o| o.min_arity <= arity && arity <= o.max_arity)?;
303 let params = o.param_types.clone()?;
304 let ret = o.return_type.clone().unwrap_or(Type::any());
305 Some((params, ret))
306 }
307
308 #[must_use]
313 pub fn get_function_type_as_value(&self, name: &str) -> Option<Type> {
314 let overloads = self.functions.as_ref()?.get(name)?;
315 let o = overloads.first()?;
316 let ret = o.return_type.clone().unwrap_or(Type::any());
317 let param_types = o.param_types.clone().unwrap_or_default();
318 if o.min_arity < o.max_arity && o.min_arity <= param_types.len() {
319 let mut variants = Vec::with_capacity(o.max_arity - o.min_arity + 1);
321 for arity in o.min_arity..=o.max_arity {
322 let args: Vec<Type> = param_types.iter().take(arity).cloned().collect();
323 variants.push(Type::function(args, ret.clone()));
324 }
325 Some(if variants.len() == 1 {
326 variants.into_iter().next().unwrap()
327 } else {
328 Type::compound(variants)
329 })
330 } else {
331 Some(Type::function(param_types, ret))
332 }
333 }
334
335 pub fn add_global_with_type(&mut self, name: String, ty: Type) {
337 if let Some(g) = &mut self.globals {
338 g.insert(name.clone());
339 }
340 if let Some(gt) = &mut self.global_types {
341 gt.insert(name, ty);
342 }
343 }
344
345 #[must_use]
347 pub fn get_global_type(&self, name: &str) -> Option<Type> {
348 self.global_types.as_ref()?.get(name).cloned()
349 }
350
351 #[must_use]
352 pub fn has_class(&self, name: &str) -> bool {
353 self.classes.as_ref().is_some_and(|c| c.contains_key(name))
354 }
355
356 #[must_use]
358 pub fn get_class_first_span(&self, name: &str) -> Option<Span> {
359 self.classes.as_ref().and_then(|c| c.get(name).copied())
360 }
361
362 #[must_use]
363 pub fn get_variable(&self, name: &str) -> Option<&VariableInfo> {
364 self.variables.get(name)
365 }
366}
367
368#[derive(Debug)]
370pub struct ScopeStore {
371 scopes: Vec<Scope>,
372 class_members: HashMap<String, ClassMembers>,
374 root_function_meta: HashMap<String, SigMeta>,
376 root_global_meta: HashMap<String, SigMeta>,
378}
379
380impl Default for ScopeStore {
381 fn default() -> Self {
382 Self::new()
383 }
384}
385
386impl ScopeStore {
387 #[must_use]
388 pub fn new() -> Self {
389 let mut store = Self {
390 scopes: Vec::new(),
391 class_members: HashMap::new(),
392 root_function_meta: HashMap::new(),
393 root_global_meta: HashMap::new(),
394 };
395 store.scopes.push(Scope::new_main());
396 store
397 }
398
399 #[must_use]
400 pub fn root_id(&self) -> ScopeId {
401 ScopeId(0)
402 }
403
404 #[must_use]
405 pub fn get(&self, id: ScopeId) -> Option<&Scope> {
406 self.scopes.get(id.0)
407 }
408
409 pub fn get_mut(&mut self, id: ScopeId) -> Option<&mut Scope> {
410 self.scopes.get_mut(id.0)
411 }
412
413 pub fn push(&mut self, kind: ScopeKind, parent: ScopeId) -> ScopeId {
414 let id = ScopeId(self.scopes.len());
415 self.scopes.push(Scope::new_child(kind, parent));
416 id
417 }
418
419 pub fn add_root_global(&mut self, name: String) {
421 if let Some(root) = self.scopes.get_mut(0) {
422 root.add_global(name);
423 }
424 }
425
426 pub fn add_root_global_with_type(&mut self, name: String, ty: Type) {
428 if let Some(root) = self.scopes.get_mut(0) {
429 root.add_global_with_type(name, ty);
430 }
431 }
432
433 pub fn add_root_function(
436 &mut self,
437 name: String,
438 min_arity: usize,
439 max_arity: usize,
440 span: Span,
441 ) {
442 if let Some(root) = self.scopes.get_mut(0) {
443 root.add_function(name, min_arity, max_arity, span);
444 }
445 }
446
447 pub fn add_root_function_with_types(
449 &mut self,
450 name: String,
451 min_arity: usize,
452 max_arity: usize,
453 span: Span,
454 param_types: Option<Vec<Type>>,
455 return_type: Option<Type>,
456 ) {
457 if let Some(root) = self.scopes.get_mut(0) {
458 root.add_function_with_types(
459 name,
460 min_arity,
461 max_arity,
462 span,
463 param_types,
464 return_type,
465 );
466 }
467 }
468
469 pub fn set_root_function_meta(&mut self, name: String, meta: SigMeta) {
471 self.root_function_meta.insert(name, meta);
472 }
473
474 pub fn set_root_global_meta(&mut self, name: String, meta: SigMeta) {
476 self.root_global_meta.insert(name, meta);
477 }
478
479 #[must_use]
481 pub fn get_root_function_meta(&self, name: &str) -> Option<&SigMeta> {
482 self.root_function_meta.get(name)
483 }
484
485 #[must_use]
487 pub fn get_root_global_meta(&self, name: &str) -> Option<&SigMeta> {
488 self.root_global_meta.get(name)
489 }
490
491 pub fn add_root_class(&mut self, name: String, span: Span) {
494 if let Some(root) = self.scopes.get_mut(0) {
495 root.add_class(name, span);
496 }
497 }
498
499 pub fn add_class_field(
501 &mut self,
502 class_name: &str,
503 field_name: String,
504 ty: Type,
505 visibility: MemberVisibility,
506 ) {
507 self.class_members
508 .entry(class_name.to_string())
509 .or_default()
510 .fields
511 .insert(field_name, (ty, visibility));
512 }
513
514 pub fn add_class_method(
516 &mut self,
517 class_name: &str,
518 method_name: String,
519 param_types: Vec<Type>,
520 return_type: Type,
521 visibility: MemberVisibility,
522 ) {
523 self.class_members
524 .entry(class_name.to_string())
525 .or_default()
526 .methods
527 .insert(method_name, (param_types, return_type, visibility));
528 }
529
530 pub fn add_class_static_field(
532 &mut self,
533 class_name: &str,
534 field_name: String,
535 ty: Type,
536 visibility: MemberVisibility,
537 ) {
538 self.class_members
539 .entry(class_name.to_string())
540 .or_default()
541 .static_fields
542 .insert(field_name, (ty, visibility));
543 }
544
545 pub fn add_class_static_method(
547 &mut self,
548 class_name: &str,
549 method_name: String,
550 param_types: Vec<Type>,
551 return_type: Type,
552 visibility: MemberVisibility,
553 ) {
554 self.class_members
555 .entry(class_name.to_string())
556 .or_default()
557 .static_methods
558 .insert(method_name, (param_types, return_type, visibility));
559 }
560
561 #[must_use]
563 pub fn get_class_member_type(&self, class_name: &str, member_name: &str) -> Option<Type> {
564 let members = self.class_members.get(class_name)?;
565 if let Some((ty, _)) = members.fields.get(member_name) {
566 return Some(ty.clone());
567 }
568 if let Some((params, ret, _)) = members.methods.get(member_name) {
569 return Some(Type::function(params.clone(), ret.clone()));
570 }
571 None
572 }
573
574 #[must_use]
576 pub fn get_class_static_member_type(
577 &self,
578 class_name: &str,
579 member_name: &str,
580 ) -> Option<Type> {
581 let members = self.class_members.get(class_name)?;
582 if let Some((ty, _)) = members.static_fields.get(member_name) {
583 return Some(ty.clone());
584 }
585 if let Some((params, ret, _)) = members.static_methods.get(member_name) {
586 return Some(Type::function(params.clone(), ret.clone()));
587 }
588 None
589 }
590
591 #[must_use]
593 pub fn get_class_members(&self, class_name: &str) -> Option<&ClassMembers> {
594 self.class_members.get(class_name)
595 }
596
597 #[must_use]
599 pub fn get_function_type_as_value(&self, current: ScopeId, name: &str) -> Option<Type> {
600 let mut id = Some(current);
601 while let Some(scope_id) = id {
602 if let Some(scope) = self.get(scope_id) {
603 if let Some(ty) = scope.get_function_type_as_value(name) {
604 return Some(ty);
605 }
606 id = scope.parent;
607 } else {
608 break;
609 }
610 }
611 None
612 }
613
614 #[must_use]
616 pub fn root_has_class(&self, name: &str) -> bool {
617 self.get(ScopeId(0))
618 .is_some_and(|scope| scope.has_class(name))
619 }
620
621 #[must_use]
623 pub fn resolve(&self, current: ScopeId, name: &str) -> Option<ResolvedSymbol> {
624 let mut id = Some(current);
625 while let Some(scope_id) = id {
626 let scope = self.get(scope_id)?;
627 if let Some(v) = scope.get_variable(name) {
628 return Some(ResolvedSymbol::Variable(v.clone()));
629 }
630 if scope.has_class(name) {
632 return Some(ResolvedSymbol::Class(name.to_string()));
633 }
634 if scope.has_global(name) {
635 return Some(ResolvedSymbol::Global(name.to_string()));
636 }
637 if let Some(arity) = scope.get_function_arity(name) {
638 return Some(ResolvedSymbol::Function(name.to_string(), arity));
639 }
640 id = scope.parent;
641 }
642 None
643 }
644}
645
646#[derive(Clone, Debug)]
647pub enum ResolvedSymbol {
648 Variable(VariableInfo),
649 Global(String),
650 Function(String, usize),
651 Class(String),
652}