package shared type SymbolTable[T any] struct { scopes []map[string]T } func NewSymbolTable[T any](_initial ...map[string]T) *SymbolTable[T] { initial := map[string]T{} if len(_initial) > 0 { initial = _initial[0] } return &SymbolTable[T]{scopes: []map[string]T{initial}} } func (st *SymbolTable[T]) EnterScope() { st.scopes = append(st.scopes, make(map[string]T)) } func (st *SymbolTable[T]) ExitScope() { if len(st.scopes) > 1 { st.scopes = st.scopes[:len(st.scopes)-1] } else { panic("cannot exit global scope") } } func (st *SymbolTable[T]) GetScope() map[string]T { return st.scopes[len(st.scopes)-1] } func (st *SymbolTable[T]) Define(name string, value T) bool { currentScope := st.scopes[len(st.scopes)-1] if _, ok := st.Lookup(name); ok { return false } currentScope[name] = value return true } func (st *SymbolTable[T]) Lookup(name string) (T, bool) { for i := len(st.scopes) - 1; i >= 0; i-- { if value, found := st.scopes[i][name]; found { return value, true } } var zeroVal T return zeroVal, false }