1use std::collections::HashMap;
2
3#[derive(Debug)]
8pub struct ScopeStack {
9 scopes: Vec<Scope>,
11}
12
13#[derive(Debug)]
14struct Scope {
15 locals: HashMap<String, LocalDef>,
17}
18
19#[derive(Debug)]
20pub struct LocalDef {
21 pub line: usize,
22 pub column: usize,
23 pub referenced: bool,
24 pub class_type: Option<String>,
26}
27
28impl Default for ScopeStack {
29 fn default() -> Self {
30 Self::new()
31 }
32}
33
34impl ScopeStack {
35 pub fn new() -> Self {
36 Self {
37 scopes: vec![Scope {
38 locals: HashMap::new(),
39 }],
40 }
41 }
42
43 pub fn push_scope(&mut self) {
45 self.scopes.push(Scope {
46 locals: HashMap::new(),
47 });
48 }
49
50 pub fn pop_scope(&mut self) -> Vec<(String, LocalDef)> {
53 let scope = self.scopes.pop().unwrap_or(Scope {
54 locals: HashMap::new(),
55 });
56 scope
57 .locals
58 .into_iter()
59 .filter(|(_, def)| !def.referenced)
60 .collect()
61 }
62
63 pub fn define_local(&mut self, name: &str, line: usize, column: usize) {
65 if let Some(scope) = self.scopes.last_mut() {
66 scope.locals.insert(
67 name.to_string(),
68 LocalDef {
69 line,
70 column,
71 referenced: false,
72 class_type: None,
73 },
74 );
75 }
76 }
77
78 pub fn define_local_typed(
80 &mut self,
81 name: &str,
82 line: usize,
83 column: usize,
84 class_type: String,
85 ) {
86 if let Some(scope) = self.scopes.last_mut() {
87 scope.locals.insert(
88 name.to_string(),
89 LocalDef {
90 line,
91 column,
92 referenced: false,
93 class_type: Some(class_type),
94 },
95 );
96 }
97 }
98
99 pub fn class_type_of(&self, name: &str) -> Option<&str> {
101 for scope in self.scopes.iter().rev() {
102 if let Some(def) = scope.locals.get(name) {
103 return def.class_type.as_deref();
104 }
105 }
106 None
107 }
108
109 pub fn resolve_and_mark(&mut self, name: &str) -> bool {
112 for scope in self.scopes.iter_mut().rev() {
113 if let Some(def) = scope.locals.get_mut(name) {
114 def.referenced = true;
115 return true;
116 }
117 }
118 false
119 }
120
121 pub fn is_defined(&self, name: &str) -> bool {
123 self.scopes
124 .iter()
125 .rev()
126 .any(|scope| scope.locals.contains_key(name))
127 }
128}