python_ast/symbols/
mod.rs

1//! Implements a Python-compatilble symbol table for Rust.
2
3use std::collections::HashMap;
4use std::collections::VecDeque;
5use std::fmt;
6
7use crate::tree::{ClassDef, FunctionDef, Import, ImportFrom};
8
9//use log::{debug, info};
10
11//use crate::codegen::{CodeGen, PythonOptions, CodeGenContext};
12use crate::tree::ExprType;
13
14/// A stack of symbol tables of different scopes. Topmost is the current scope.
15#[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        // Outer scope
189        scopes.new_scope();
190        let outer_node = SymbolTableNode::Alias("outer_alias".to_string());
191        scopes.insert("outer_key".to_string(), outer_node);
192        
193        // Inner scope
194        scopes.new_scope();
195        let inner_node = SymbolTableNode::Alias("inner_alias".to_string());
196        scopes.insert("inner_key".to_string(), inner_node);
197        
198        // Should find both keys
199        assert!(scopes.get("inner_key").is_some());
200        assert!(scopes.get("outer_key").is_some());
201        
202        // Inner scope should shadow outer scope for same key
203        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        // Should not panic when inserting with no scopes
224        scopes.insert("key".to_string(), node);
225        
226        // Should return None since no scopes exist
227        assert!(scopes.get("key").is_none());
228    }
229
230    #[test]
231    fn test_symbol_table_node_variants() {
232        use crate::Name;
233        
234        // Test different node types
235        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}