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}