001package gudusoft.gsqlparser.ir.bound; 002 003import gudusoft.gsqlparser.ir.common.SourceAnchor; 004 005import java.util.ArrayList; 006import java.util.HashMap; 007import java.util.List; 008import java.util.Map; 009import java.util.concurrent.atomic.AtomicLong; 010 011/** 012 * A binding scope in the IR scope tree. 013 * <p> 014 * Scopes form a tree structure mirroring the lexical nesting of SQL/PL/SQL constructs. 015 * Each scope maintains its own symbol table for name resolution. 016 */ 017public class BoundScope { 018 019 private static final AtomicLong SCOPE_ID_GENERATOR = new AtomicLong(0); 020 021 private final long scopeId; 022 private final BoundScope parent; 023 private final EScopeKind kind; 024 private final List<BoundScope> children; 025 private final Map<String, BoundSymbol> symbols; 026 private final SourceAnchor anchor; 027 028 public BoundScope(EScopeKind kind, BoundScope parent, SourceAnchor anchor) { 029 this.scopeId = SCOPE_ID_GENERATOR.incrementAndGet(); 030 this.kind = kind; 031 this.parent = parent; 032 this.children = new ArrayList<BoundScope>(); 033 this.symbols = new HashMap<String, BoundSymbol>(); 034 this.anchor = anchor; 035 if (parent != null) { 036 parent.children.add(this); 037 } 038 } 039 040 public long getScopeId() { return scopeId; } 041 public BoundScope getParent() { return parent; } 042 public EScopeKind getKind() { return kind; } 043 public List<BoundScope> getChildren() { return children; } 044 public SourceAnchor getAnchor() { return anchor; } 045 046 /** 047 * Declares a symbol in this scope. 048 */ 049 public void declareSymbol(String name, BoundSymbol symbol) { 050 symbols.put(name.toUpperCase(), symbol); 051 } 052 053 /** 054 * Looks up a symbol in this scope only (no parent search). 055 */ 056 public BoundSymbol lookupLocal(String name) { 057 return symbols.get(name.toUpperCase()); 058 } 059 060 /** 061 * Looks up a symbol by searching this scope and all ancestor scopes. 062 */ 063 public BoundSymbol lookup(String name) { 064 String key = name.toUpperCase(); 065 BoundScope current = this; 066 while (current != null) { 067 BoundSymbol symbol = current.symbols.get(key); 068 if (symbol != null) { 069 return symbol; 070 } 071 current = current.parent; 072 } 073 return null; 074 } 075 076 /** 077 * Returns all symbols declared in this scope. 078 */ 079 public Map<String, BoundSymbol> getSymbols() { 080 return symbols; 081 } 082 083 /** 084 * Resets the scope ID generator. For testing only. 085 */ 086 public static void resetIdGenerator() { 087 SCOPE_ID_GENERATOR.set(0); 088 } 089 090 @Override 091 public String toString() { 092 return "BoundScope{" + kind + "#" + scopeId + ", symbols=" + symbols.size() + "}"; 093 } 094}