solscript_typeck/
scope.rs

1//! Symbol table and scope management
2
3use indexmap::IndexMap;
4use smol_str::SmolStr;
5
6use crate::types::{FunctionType, Type, TypeDef};
7
8/// A symbol in the symbol table
9#[derive(Debug, Clone)]
10pub enum Symbol {
11    /// A variable binding
12    Variable(VariableSymbol),
13    /// A function
14    Function(FunctionSymbol),
15    /// A type definition
16    Type(TypeDef),
17    /// A module
18    Module(ModuleSymbol),
19}
20
21/// Variable symbol
22#[derive(Debug, Clone)]
23pub struct VariableSymbol {
24    pub name: SmolStr,
25    pub ty: Type,
26    pub is_mutable: bool,
27}
28
29/// Function symbol
30#[derive(Debug, Clone)]
31pub struct FunctionSymbol {
32    pub name: SmolStr,
33    pub ty: FunctionType,
34    pub is_public: bool,
35}
36
37/// Module symbol
38#[derive(Debug, Clone)]
39pub struct ModuleSymbol {
40    pub name: SmolStr,
41    pub symbols: IndexMap<SmolStr, Symbol>,
42}
43
44/// A scope in the symbol table
45#[derive(Debug, Clone)]
46pub struct Scope {
47    /// Symbols defined in this scope
48    pub symbols: IndexMap<SmolStr, Symbol>,
49    /// The kind of scope
50    pub kind: ScopeKind,
51}
52
53/// The kind of scope
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub enum ScopeKind {
56    /// Global/module scope
57    Global,
58    /// Contract scope
59    Contract,
60    /// Function scope
61    Function,
62    /// Block scope (if, while, for, etc.)
63    Block,
64}
65
66impl Scope {
67    pub fn new(kind: ScopeKind) -> Self {
68        Self {
69            symbols: IndexMap::new(),
70            kind,
71        }
72    }
73
74    /// Define a symbol in this scope
75    pub fn define(&mut self, name: SmolStr, symbol: Symbol) -> Option<Symbol> {
76        self.symbols.insert(name, symbol)
77    }
78
79    /// Look up a symbol in this scope
80    pub fn lookup(&self, name: &str) -> Option<&Symbol> {
81        self.symbols.get(name)
82    }
83}
84
85/// The symbol table managing nested scopes
86#[derive(Debug)]
87pub struct SymbolTable {
88    /// Stack of scopes
89    scopes: Vec<Scope>,
90    /// Type definitions (global)
91    type_defs: IndexMap<SmolStr, TypeDef>,
92}
93
94impl SymbolTable {
95    pub fn new() -> Self {
96        let mut table = Self {
97            scopes: vec![Scope::new(ScopeKind::Global)],
98            type_defs: IndexMap::new(),
99        };
100        table.define_builtins();
101        table
102    }
103
104    /// Define built-in types and functions
105    fn define_builtins(&mut self) {
106        // Built-in types are handled by PrimitiveType, no need to add here
107    }
108
109    /// Push a new scope
110    pub fn push_scope(&mut self, kind: ScopeKind) {
111        self.scopes.push(Scope::new(kind));
112    }
113
114    /// Pop the current scope
115    pub fn pop_scope(&mut self) -> Option<Scope> {
116        if self.scopes.len() > 1 {
117            self.scopes.pop()
118        } else {
119            None
120        }
121    }
122
123    /// Get the current scope kind
124    pub fn current_scope_kind(&self) -> ScopeKind {
125        self.scopes
126            .last()
127            .map(|s| s.kind)
128            .unwrap_or(ScopeKind::Global)
129    }
130
131    /// Check if we're in a contract scope
132    pub fn in_contract(&self) -> bool {
133        self.scopes.iter().any(|s| s.kind == ScopeKind::Contract)
134    }
135
136    /// Check if we're in a function scope
137    pub fn in_function(&self) -> bool {
138        self.scopes.iter().any(|s| s.kind == ScopeKind::Function)
139    }
140
141    /// Define a symbol in the current scope
142    pub fn define(&mut self, name: SmolStr, symbol: Symbol) -> Option<Symbol> {
143        self.scopes.last_mut()?.define(name, symbol)
144    }
145
146    /// Define a variable
147    pub fn define_variable(&mut self, name: SmolStr, ty: Type, is_mutable: bool) -> Option<Symbol> {
148        self.define(
149            name.clone(),
150            Symbol::Variable(VariableSymbol {
151                name,
152                ty,
153                is_mutable,
154            }),
155        )
156    }
157
158    /// Define a function
159    pub fn define_function(
160        &mut self,
161        name: SmolStr,
162        ty: FunctionType,
163        is_public: bool,
164    ) -> Option<Symbol> {
165        self.define(
166            name.clone(),
167            Symbol::Function(FunctionSymbol {
168                name,
169                ty,
170                is_public,
171            }),
172        )
173    }
174
175    /// Define a type
176    pub fn define_type(&mut self, name: SmolStr, def: TypeDef) -> Option<TypeDef> {
177        self.type_defs.insert(name, def)
178    }
179
180    /// Look up a symbol by name (searches all scopes from innermost to outermost)
181    pub fn lookup(&self, name: &str) -> Option<&Symbol> {
182        for scope in self.scopes.iter().rev() {
183            if let Some(symbol) = scope.lookup(name) {
184                return Some(symbol);
185            }
186        }
187        None
188    }
189
190    /// Look up a variable
191    pub fn lookup_variable(&self, name: &str) -> Option<&VariableSymbol> {
192        self.lookup(name).and_then(|s| match s {
193            Symbol::Variable(v) => Some(v),
194            _ => None,
195        })
196    }
197
198    /// Look up a function
199    pub fn lookup_function(&self, name: &str) -> Option<&FunctionSymbol> {
200        self.lookup(name).and_then(|s| match s {
201            Symbol::Function(f) => Some(f),
202            _ => None,
203        })
204    }
205
206    /// Look up a type definition
207    pub fn lookup_type(&self, name: &str) -> Option<&TypeDef> {
208        self.type_defs.get(name)
209    }
210
211    /// Look up a symbol in the current scope only
212    pub fn lookup_local(&self, name: &str) -> Option<&Symbol> {
213        self.scopes.last()?.lookup(name)
214    }
215}
216
217impl Default for SymbolTable {
218    fn default() -> Self {
219        Self::new()
220    }
221}