package eval import ( "fmt" "unsafe" "github.com/elves/elvish/pkg/eval/vars" "github.com/xiaq/persistent/hash" ) // Ns is the runtime representation of a namespace. The zero value of Ns is an // empty namespace. To create a non-empty Ns, use either NsBuilder or CombineNs. // // An Ns is immutable after creation. type Ns struct { // All variables in the namespace. Static variable accesses are compiled // into indexed accesses into this slice. slots []vars.Var // Names for the variables, used for introspection. Typical real programs // only contain a small number of names in each namespace, in which case a // linear search in a slice is usually faster than map access. names []string } // CombineNs returns an *Ns that contains all the bindings from both ns1 and // ns2. Names in ns2 takes precedence over those in ns1. func CombineNs(ns1 *Ns, ns2 *Ns) *Ns { ns := &Ns{ append([]vars.Var(nil), ns2.slots...), append([]string(nil), ns2.names...)} hasName := map[string]bool{} for _, name := range ns.names { hasName[name] = true } for i, name := range ns1.names { if !hasName[name] { ns.slots = append(ns.slots, ns1.slots[i]) ns.names = append(ns.names, name) } } return ns } // Kind returns "ns". func (ns *Ns) Kind() string { return "ns" } // Hash returns a hash of the address of ns. func (ns *Ns) Hash() uint32 { return hash.Pointer(unsafe.Pointer(ns)) } // Equal returns whether rhs has the same identity as ns. func (ns *Ns) Equal(rhs interface{}) bool { if ns2, ok := rhs.(*Ns); ok { return ns == ns2 } return false } // Repr returns an opaque representation of the Ns showing its address. func (ns *Ns) Repr(int) string { return fmt.Sprintf("<ns %p>", ns) } // Index looks up a variable with the given name, and returns its value if it // exists. This is only used for introspection. func (ns *Ns) Index(k interface{}) (interface{}, bool) { if ks, ok := k.(string); ok { variable := ns.indexInner(ks) if variable == nil { return nil, false } return variable.Get(), true } return nil, false } func (ns *Ns) indexInner(k string) vars.Var { i := ns.lookup(k) if i != -1 { return ns.slots[i] } return nil } func (ns *Ns) lookup(k string) int { for i, name := range ns.names { if name == k { return i } } return -1 } // IterateKeys produces the names of all the variables in this Ns. func (ns *Ns) IterateKeys(f func(interface{}) bool) { for i, name := range ns.names { if ns.slots[i] == nil { continue } if !f(name) { break } } } // HasName reports whether the Ns has a variable with the given name. func (ns *Ns) HasName(k string) bool { for i, name := range ns.names { if name == k { return ns.slots[i] != nil } } return false } func (ns *Ns) static() *staticNs { return &staticNs{ns.names, make([]bool, len(ns.names))} } // NsBuilder is a helper type used for building an Ns. type NsBuilder map[string]vars.Var // Add adds a variable. func (nb NsBuilder) Add(name string, v vars.Var) NsBuilder { nb[name] = v return nb } // AddFn adds a function. func (nb NsBuilder) AddFn(name string, v Callable) NsBuilder { return nb.Add(name+FnSuffix, vars.FromPtr(&v)) } // AddNs adds a sub-namespace. func (nb NsBuilder) AddNs(name string, v *Ns) NsBuilder { return nb.Add(name+NsSuffix, vars.FromPtr(&v)) } // AddGoFn adds a Go function. func (nb NsBuilder) AddGoFn(nsName, name string, impl interface{}) NsBuilder { return nb.AddFn(name, NewGoFn(nsName+name, impl)) } // AddGoFns adds Go functions. func (nb NsBuilder) AddGoFns(nsName string, fns map[string]interface{}) NsBuilder { for name, impl := range fns { nb.AddGoFn(nsName, name, impl) } return nb } // Build builds an Ns. func (nb NsBuilder) Ns() *Ns { ns := &Ns{make([]vars.Var, len(nb)), make([]string, len(nb))} i := 0 for name, variable := range nb { ns.slots[i] = variable ns.names[i] = name i++ } return ns } // The compile-time representation of a namespace. Called "static" namespace // since it contains information that are known without executing the code. // The data structure itself, however, is not static, and gets mutated as the // compiler gains more information about the namespace. The zero value of // staticNs is an empty namespace. type staticNs struct { names []string deleted []bool } func (ns *staticNs) clone() *staticNs { return &staticNs{ append([]string{}, ns.names...), append([]bool{}, ns.deleted...)} } func (ns *staticNs) set(k string) int { if index := ns.lookup(k); index != -1 { return index } return ns.addInner(k) } func (ns *staticNs) del(k string) { if i := ns.lookup(k); i != -1 { ns.deleted[i] = true } } // Adds a name, shadowing any existing one. func (ns *staticNs) add(k string) int { ns.del(k) return ns.addInner(k) } // Adds a name, assuming that it either doesn't exist yet or has been deleted. func (ns *staticNs) addInner(k string) int { ns.names = append(ns.names, k) ns.deleted = append(ns.deleted, false) return len(ns.names) - 1 } func (ns *staticNs) has(name string) bool { return ns.lookup(name) != -1 } func (ns *staticNs) lookup(k string) int { for i, name := range ns.names { if name == k && !ns.deleted[i] { return i } } return -1 } type staticUpNs struct { names []string // For each name, whether the upvalue comes from the immediate outer scope, // i.e. the local scope a lambda is evaluated in. local []bool // Index of the upvalue variable, either into the local scope (if // the corresponding value in local is true) or the up scope (if the // corresponding value in local is false). index []int } func (up *staticUpNs) add(k string, local bool, index int) int { for i, name := range up.names { if name == k { return i } } up.names = append(up.names, k) up.local = append(up.local, local) up.index = append(up.index, index) return len(up.names) - 1 }