package compiler type ScopeID int type ScopeChain struct { scopes []Scope } func NewScopeChain() ScopeChain { scopes := make([]Scope, 1) scopes[0] = Scope{ kind: ScopeKindGlobal, nameToSymbol: make(map[string]int), symbols: make([]Symbol, 0), } return ScopeChain{ scopes: scopes, } } func (sc *ScopeChain) Current() *Scope { return &sc.scopes[len(sc.scopes)-1] } func (sc *ScopeChain) Enter(kind ScopeKind) { sc.scopes = append(sc.scopes, Scope{ kind: kind, nameToSymbol: make(map[string]int), symbols: make([]Symbol, 0), }) } func (sc *ScopeChain) Exit() { sc.scopes[len(sc.scopes)-1] = Scope{} sc.scopes = sc.scopes[:len(sc.scopes)-1] } func (sc *ScopeChain) Declare(name string) (int, bool) { // Check whether the symbol is already declared in any of the scopes. for _, scope := range sc.scopes { if _, ok := scope.nameToSymbol[name]; ok { return 0, false } } current := sc.Current() index := len(current.symbols) // Declare the symbol in the current scope. current.symbols = append(current.symbols, Symbol{ kind: SymbolKindVariable, name: name, localIndex: index, }) current.nameToSymbol[name] = index return index, true } func (sc *ScopeChain) DeclareAnonymous() int { current := sc.Current() index := len(current.symbols) // Declare the symbol in the current scope. current.symbols = append(current.symbols, Symbol{ kind: SymbolKindVariable, name: "", localIndex: index, }) return index } func (sc *ScopeChain) DeclareTemporary() int { return len(sc.Current().symbols) } func (sc *ScopeChain) Lookup(name string) (Symbol, bool) { for i := len(sc.scopes) - 1; i >= 0; i-- { if symbol, ok := sc.scopes[i].nameToSymbol[name]; ok { return sc.scopes[i].symbols[symbol], true } } return Symbol{}, false } type ScopeKind int const ( ScopeKindGlobal ScopeKind = iota ScopeKindFunction ScopeKindBlock ) type Scope struct { kind ScopeKind nameToSymbol map[string]int symbols []Symbol }