@@ -47,12 +47,39 @@ impl SymbolTable {
4747 return symbol;
4848 }
4949
50- pub fn resolve ( & self , name : String ) -> Option < Rc < Symbol > > {
51- let symbol = self . symbols . get ( & name ) ;
52- if symbol . is_none ( ) && self . outer . is_some ( ) {
53- return self . outer . as_ref ( ) . unwrap ( ) . resolve ( name ) ;
50+ // Resolve a name in the current scope, capturing free variables from outers when needed.
51+ pub fn resolve ( & mut self , name : String ) -> Option < Rc < Symbol > > {
52+ if let Some ( sym ) = self . symbols . get ( & name ) {
53+ return Some ( sym . clone ( ) ) ;
5454 }
55- return symbol. cloned ( ) ;
55+
56+ // If not found locally, try outer scopes.
57+ if let Some ( outer) = & self . outer {
58+ // We can't mutate outer here, so use a read-only helper to locate the original symbol.
59+ if let Some ( original) = outer. resolve_readonly ( & name) {
60+ return match original. scope {
61+ // Globals and builtins are accessed directly.
62+ SymbolScope :: Global | SymbolScope :: Builtin => Some ( original) ,
63+ // Locals (from outer scope) or already-free symbols should be captured as a new free symbol here.
64+ SymbolScope :: LOCAL | SymbolScope :: Free | SymbolScope :: Function => {
65+ Some ( self . define_free ( original) )
66+ }
67+ } ;
68+ }
69+ }
70+
71+ None
72+ }
73+
74+ // Read-only resolver used internally to search outer scopes without mutating them.
75+ fn resolve_readonly ( & self , name : & str ) -> Option < Rc < Symbol > > {
76+ if let Some ( sym) = self . symbols . get ( name) {
77+ return Some ( sym. clone ( ) ) ;
78+ }
79+ if let Some ( outer) = & self . outer {
80+ return outer. resolve_readonly ( name) ;
81+ }
82+ None
5683 }
5784
5885 pub fn define_builtin ( & mut self , index : usize , name : String ) -> Rc < Symbol > {
0 commit comments