package muesli import "fmt" import "strings" // LookupFallback is a fallback function that can be set to be called when // lookup in a scope fails type LookupFallback func (name string) Value // Scope of symbols defined in the VM, hierarchical type Scope struct { parent *Scope children []*Scope symbols map[string]Value fallback LookupFallback } func NewScope(parent *Scope) *Scope { return &Scope{parent, make([]*Scope, 0), make(map[string]Value), nil} } // Sets a lookup fall back to be called before trying the parent scope. // Returns the previous lookup fallback, or nil if it was nil. func (scope * Scope) SetLookupFallback(fb LookupFallback) LookupFallback { res := scope.fallback scope.fallback = fb return res } // Lookup looks up a value of a name registered in the current scope, // and if not found there, recursively in the parent scope. // Returns NilValue if not found. func (scope *Scope) Lookup(name string) Value { value, ok := scope.symbols[name] if ok { return value } if scope.fallback != nil { return scope.fallback(name) } if scope.parent != nil { return scope.parent.Lookup(name) } return NilValue } func (scope *Scope) Register(name string, value Value) Value { scope.symbols[name] = value return value } func (scope * Scope) Known(filter func(string, Value) bool) map[string]Value { res := make(map[string]Value) if scope.parent != nil { res = scope.parent.Known(filter) } for k, v := range scope.symbols { if (filter == nil) || filter(k, v) { res[k] = v } } return res } func (scope * Scope) ForEachDefined(do func(string, Value) (bool, error)) (bool, error) { var res bool = true var err error if (do == nil) { return false, fmt.Errorf("do may not be nil") } if scope.parent != nil { res, err = scope.parent.ForEachDefined(do) } if res == false || err != nil { return res, err } for k, v := range scope.symbols { res, err = do(k, v) if res == false || err != nil { return res, err } } return res, err } func (scope* Scope) DefinedHelpers() []Helper { res := []Helper{} scope.ForEachDefined(func (k string, v Value) (bool, error) { helper, hok := v.(Helper) if hok { res = append(res, helper) } return true, nil }) return res } func (scope* Scope) DefinedNamesLike(prefix string) []string { res := []string{} scope.ForEachDefined(func (k string, v Value) (bool, error) { if strings.HasPrefix(k, prefix) { res = append(res, k) } return true, nil }) return res }