python_ast/symbols/
mod.rs1use std::collections::HashMap;
4use std::collections::VecDeque;
5use std::fmt;
6
7use crate::tree::{ClassDef, FunctionDef, Import, ImportFrom};
8
9use crate::tree::ExprType;
13
14#[derive(Clone, Debug)]
16pub struct SymbolTableScopes(VecDeque<SymbolTable>);
17
18impl SymbolTableScopes {
19 pub fn new() -> Self {
20 Self(VecDeque::new())
21 }
22
23 pub fn push(&mut self, table: SymbolTable) {
24 self.0.push_front(table);
25 }
26
27 pub fn pop(&mut self) -> Option<SymbolTable> {
28 self.0.pop_front()
29 }
30
31 pub fn new_scope(&mut self) {
32 self.0.push_front(SymbolTable::new());
33 }
34
35 pub fn insert(&mut self, key: String, value: SymbolTableNode) {
36 if let Some(table) = self.0.front_mut() {
37 table.insert(key, value);
38 }
39 }
40
41 pub fn get(&self, key: &str) -> Option<&SymbolTableNode> {
42 for table in self.0.iter() {
43 if let Some(value) = table.get(key) {
44 return Some(value);
45 }
46 }
47 None
48 }
49}
50
51impl Default for SymbolTableScopes {
52 fn default() -> Self {
53 Self::new()
54 }
55}
56
57impl Default for SymbolTable {
58 fn default() -> Self {
59 Self::new()
60 }
61}
62
63#[derive(Clone, Debug)]
64pub enum SymbolTableNode {
65 Assign { position: usize, value: ExprType },
66 ClassDef(ClassDef),
67 FunctionDef(FunctionDef),
68 Import(Import),
69 ImportFrom(ImportFrom),
70 Alias(String),
71}
72
73#[derive(Clone, Debug)]
74pub struct SymbolTable {
75 pub symbols: HashMap<String, SymbolTableNode>,
76}
77
78impl SymbolTable {
79 pub fn new() -> Self {
80 Self {
81 symbols: HashMap::new(),
82 }
83 }
84
85 pub fn insert(&mut self, key: String, value: SymbolTableNode) {
86 self.symbols.insert(key, value);
87 }
88
89 pub fn get(&self, key: &str) -> Option<&SymbolTableNode> {
90 self.symbols.get(key)
91 }
92}
93
94impl fmt::Display for SymbolTable {
95 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96 let mut s = String::new();
97 for (key, value) in self.symbols.iter() {
98 s.push_str(&format!("{}: {:#?}\n", key, value));
99 }
100 write!(f, "{}", s)
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107 use crate::tree::*;
108
109 #[test]
110 fn test_symbol_table_creation() {
111 let table = SymbolTable::new();
112 assert!(table.symbols.is_empty());
113 }
114
115 #[test]
116 fn test_symbol_table_insert_and_get() {
117 let mut table = SymbolTable::new();
118 let node = SymbolTableNode::Alias("test_alias".to_string());
119
120 table.insert("test_key".to_string(), node);
121
122 let retrieved = table.get("test_key");
123 assert!(retrieved.is_some());
124
125 match retrieved.unwrap() {
126 SymbolTableNode::Alias(alias) => assert_eq!(alias, "test_alias"),
127 _ => panic!("Expected Alias node"),
128 }
129 }
130
131 #[test]
132 fn test_symbol_table_get_nonexistent() {
133 let table = SymbolTable::new();
134 assert!(table.get("nonexistent").is_none());
135 }
136
137 #[test]
138 fn test_symbol_table_scopes_creation() {
139 let scopes = SymbolTableScopes::new();
140 assert_eq!(scopes.0.len(), 0);
141 }
142
143 #[test]
144 fn test_symbol_table_scopes_push_pop() {
145 let mut scopes = SymbolTableScopes::new();
146 let table = SymbolTable::new();
147
148 scopes.push(table);
149 assert_eq!(scopes.0.len(), 1);
150
151 let popped = scopes.pop();
152 assert!(popped.is_some());
153 assert_eq!(scopes.0.len(), 0);
154 }
155
156 #[test]
157 fn test_symbol_table_scopes_new_scope() {
158 let mut scopes = SymbolTableScopes::new();
159
160 scopes.new_scope();
161 assert_eq!(scopes.0.len(), 1);
162
163 scopes.new_scope();
164 assert_eq!(scopes.0.len(), 2);
165 }
166
167 #[test]
168 fn test_symbol_table_scopes_insert_and_get() {
169 let mut scopes = SymbolTableScopes::new();
170 scopes.new_scope();
171
172 let node = SymbolTableNode::Alias("scoped_alias".to_string());
173 scopes.insert("test_key".to_string(), node);
174
175 let retrieved = scopes.get("test_key");
176 assert!(retrieved.is_some());
177
178 match retrieved.unwrap() {
179 SymbolTableNode::Alias(alias) => assert_eq!(alias, "scoped_alias"),
180 _ => panic!("Expected Alias node"),
181 }
182 }
183
184 #[test]
185 fn test_symbol_table_scopes_nested_lookup() {
186 let mut scopes = SymbolTableScopes::new();
187
188 scopes.new_scope();
190 let outer_node = SymbolTableNode::Alias("outer_alias".to_string());
191 scopes.insert("outer_key".to_string(), outer_node);
192
193 scopes.new_scope();
195 let inner_node = SymbolTableNode::Alias("inner_alias".to_string());
196 scopes.insert("inner_key".to_string(), inner_node);
197
198 assert!(scopes.get("inner_key").is_some());
200 assert!(scopes.get("outer_key").is_some());
201
202 let shadow_node = SymbolTableNode::Alias("shadow_alias".to_string());
204 scopes.insert("outer_key".to_string(), shadow_node);
205
206 match scopes.get("outer_key").unwrap() {
207 SymbolTableNode::Alias(alias) => assert_eq!(alias, "shadow_alias"),
208 _ => panic!("Expected shadowed alias"),
209 }
210 }
211
212 #[test]
213 fn test_symbol_table_scopes_empty_get() {
214 let scopes = SymbolTableScopes::new();
215 assert!(scopes.get("any_key").is_none());
216 }
217
218 #[test]
219 fn test_symbol_table_scopes_insert_no_scope() {
220 let mut scopes = SymbolTableScopes::new();
221 let node = SymbolTableNode::Alias("test".to_string());
222
223 scopes.insert("key".to_string(), node);
225
226 assert!(scopes.get("key").is_none());
228 }
229
230 #[test]
231 fn test_symbol_table_node_variants() {
232 use crate::Name;
233
234 let assign_node = SymbolTableNode::Assign {
236 position: 42,
237 value: ExprType::Name(Name { id: "test".to_string() }),
238 };
239
240 match assign_node {
241 SymbolTableNode::Assign { position, .. } => assert_eq!(position, 42),
242 _ => panic!("Expected Assign node"),
243 }
244
245 let alias_node = SymbolTableNode::Alias("alias_name".to_string());
246
247 match alias_node {
248 SymbolTableNode::Alias(name) => assert_eq!(name, "alias_name"),
249 _ => panic!("Expected Alias node"),
250 }
251 }
252
253 #[test]
254 fn test_symbol_table_display() {
255 let mut table = SymbolTable::new();
256 let node = SymbolTableNode::Alias("test_display".to_string());
257 table.insert("display_key".to_string(), node);
258
259 let display_string = format!("{}", table);
260 assert!(display_string.contains("display_key"));
261 assert!(display_string.contains("test_display"));
262 }
263
264 #[test]
265 fn test_symbol_table_scopes_default() {
266 let scopes = SymbolTableScopes::default();
267 assert_eq!(scopes.0.len(), 0);
268 }
269
270 #[test]
271 fn test_symbol_table_default() {
272 let table = SymbolTable::default();
273 assert!(table.symbols.is_empty());
274 }
275}