package compiler type SymbolTable struct { parent *SymbolTable block bool store map[string]Symbol numDefinition int maxDefinition int freeSymbols []Symbol } func NewSymbolTable() *SymbolTable { return &SymbolTable{ store: make(map[string]Symbol), } } func (t *SymbolTable) Define(name string) Symbol { symbol := Symbol{Name: name, Index: t.nextIndex()} t.numDefinition++ if t.Parent(true) == nil { symbol.Scope = ScopeGlobal } else { symbol.Scope = ScopeLocal } t.store[name] = symbol t.updateMaxDefs(symbol.Index + 1) return symbol } func (t *SymbolTable) DefineBuiltin(index int, name string) Symbol { symbol := Symbol{ Name: name, Index: index, Scope: ScopeBuiltin, } t.store[name] = symbol return symbol } func (t *SymbolTable) Resolve(name string) (symbol Symbol, depth int, ok bool) { symbol, ok = t.store[name] if !ok && t.parent != nil { symbol, depth, ok = t.parent.Resolve(name) if !ok { return } if !t.block { depth += 1 } // if symbol is defined in parent table and if it's not global/builtin // then it's free variable. if depth > 0 && symbol.Scope != ScopeGlobal && symbol.Scope != ScopeBuiltin { return t.defineFree(symbol), depth, true } return } return } func (t *SymbolTable) Fork(block bool) *SymbolTable { return &SymbolTable{ store: make(map[string]Symbol), parent: t, block: block, } } func (t *SymbolTable) Parent(skipBlock bool) *SymbolTable { if skipBlock && t.block { return t.parent.Parent(skipBlock) } return t.parent } func (t *SymbolTable) MaxSymbols() int { return t.maxDefinition } func (t *SymbolTable) FreeSymbols() []Symbol { return t.freeSymbols } func (t *SymbolTable) Names() []string { var names []string for name := range t.store { names = append(names, name) } return names } func (t *SymbolTable) nextIndex() int { if t.block { return t.parent.nextIndex() + t.numDefinition } return t.numDefinition } func (t *SymbolTable) updateMaxDefs(numDefs int) { if numDefs > t.maxDefinition { t.maxDefinition = numDefs } if t.block { t.parent.updateMaxDefs(numDefs) } } func (t *SymbolTable) defineFree(original Symbol) Symbol { // TODO: should we check duplicates? t.freeSymbols = append(t.freeSymbols, original) symbol := Symbol{ Name: original.Name, Index: len(t.freeSymbols) - 1, Scope: ScopeFree, } t.store[original.Name] = symbol return symbol }