github.com/kolbycrouch/elvish@v0.14.1-0.20210614162631-215b9ac1c423/pkg/eval/ns.go (about) 1 package eval 2 3 import ( 4 "fmt" 5 "unsafe" 6 7 "src.elv.sh/pkg/eval/vars" 8 "src.elv.sh/pkg/persistent/hash" 9 ) 10 11 // Ns is the runtime representation of a namespace. The zero value of Ns is an 12 // empty namespace. To create a non-empty Ns, use either NsBuilder or CombineNs. 13 // 14 // An Ns is immutable after creation. 15 type Ns struct { 16 // All variables in the namespace. Static variable accesses are compiled 17 // into indexed accesses into this slice. 18 slots []vars.Var 19 // Names for the variables, used for introspection. Typical real programs 20 // only contain a small number of names in each namespace, in which case a 21 // linear search in a slice is usually faster than map access. 22 names []string 23 // Whether the variable has been deleted. Deleted variables can still be 24 // kept in the Ns since there might be a reference to them in a closure. 25 // Shadowed variables are also considered deleted. 26 deleted []bool 27 } 28 29 // CombineNs returns an *Ns that contains all the bindings from both ns1 and 30 // ns2. Names in ns2 takes precedence over those in ns1. 31 func CombineNs(ns1 *Ns, ns2 *Ns) *Ns { 32 ns := &Ns{ 33 append([]vars.Var(nil), ns2.slots...), 34 append([]string(nil), ns2.names...), 35 append([]bool(nil), ns2.deleted...)} 36 hasName := map[string]bool{} 37 for i, name := range ns.names { 38 if !ns.deleted[i] { 39 hasName[name] = true 40 } 41 } 42 for i, name := range ns1.names { 43 if !ns1.deleted[i] && !hasName[name] { 44 ns.slots = append(ns.slots, ns1.slots[i]) 45 ns.names = append(ns.names, name) 46 ns.deleted = append(ns.deleted, false) 47 } 48 } 49 return ns 50 } 51 52 // Kind returns "ns". 53 func (ns *Ns) Kind() string { 54 return "ns" 55 } 56 57 // Hash returns a hash of the address of ns. 58 func (ns *Ns) Hash() uint32 { 59 return hash.Pointer(unsafe.Pointer(ns)) 60 } 61 62 // Equal returns whether rhs has the same identity as ns. 63 func (ns *Ns) Equal(rhs interface{}) bool { 64 if ns2, ok := rhs.(*Ns); ok { 65 return ns == ns2 66 } 67 return false 68 } 69 70 // Repr returns an opaque representation of the Ns showing its address. 71 func (ns *Ns) Repr(int) string { 72 return fmt.Sprintf("<ns %p>", ns) 73 } 74 75 // Index looks up a variable with the given name, and returns its value if it 76 // exists. This is only used for introspection from Elvish code; for 77 // introspection from Go code, use IndexName. 78 func (ns *Ns) Index(k interface{}) (interface{}, bool) { 79 if ks, ok := k.(string); ok { 80 variable := ns.IndexName(ks) 81 if variable == nil { 82 return nil, false 83 } 84 return variable.Get(), true 85 } 86 return nil, false 87 } 88 89 // IndexName looks up a variable with the given name, and returns its value if it exists, or nil if 90 // it does not. This is the type-safe version of Index and is useful for introspection from Go code. 91 func (ns *Ns) IndexName(k string) vars.Var { 92 i := ns.lookup(k) 93 if i != -1 { 94 return ns.slots[i] 95 } 96 return nil 97 } 98 99 func (ns *Ns) lookup(k string) int { 100 for i, name := range ns.names { 101 if name == k && !ns.deleted[i] { 102 return i 103 } 104 } 105 return -1 106 } 107 108 // IterateKeys produces the names of all the variables in this Ns. 109 func (ns *Ns) IterateKeys(f func(interface{}) bool) { 110 for i, name := range ns.names { 111 if ns.slots[i] == nil || ns.deleted[i] { 112 continue 113 } 114 if !f(name) { 115 break 116 } 117 } 118 } 119 120 // IterateNames produces the names of all variables in the Ns. It is the 121 // type-safe version of IterateKeys and is useful for introspection from Go 122 // code. It doesn't support breaking early. 123 func (ns *Ns) IterateNames(f func(string)) { 124 for i, name := range ns.names { 125 if ns.slots[i] != nil && !ns.deleted[i] { 126 f(name) 127 } 128 } 129 } 130 131 // HasName reports whether the Ns has a variable with the given name. 132 func (ns *Ns) HasName(k string) bool { 133 for i, name := range ns.names { 134 if name == k && !ns.deleted[i] { 135 return ns.slots[i] != nil 136 } 137 } 138 return false 139 } 140 141 func (ns *Ns) static() *staticNs { 142 return &staticNs{ns.names, ns.deleted} 143 } 144 145 // NsBuilder is a helper type used for building an Ns. 146 type NsBuilder map[string]vars.Var 147 148 // Add adds a variable. 149 func (nb NsBuilder) Add(name string, v vars.Var) NsBuilder { 150 nb[name] = v 151 return nb 152 } 153 154 // AddFn adds a function. The resulting variable will be read-only. 155 func (nb NsBuilder) AddFn(name string, v Callable) NsBuilder { 156 return nb.Add(name+FnSuffix, vars.NewReadOnly(v)) 157 } 158 159 // AddNs adds a sub-namespace. The resulting variable will be read-only. 160 func (nb NsBuilder) AddNs(name string, v *Ns) NsBuilder { 161 return nb.Add(name+NsSuffix, vars.NewReadOnly(v)) 162 } 163 164 // AddGoFn adds a Go function. The resulting variable will be read-only. 165 func (nb NsBuilder) AddGoFn(nsName, name string, impl interface{}) NsBuilder { 166 return nb.AddFn(name, NewGoFn(nsName+name, impl)) 167 } 168 169 // AddGoFns adds Go functions. The resulting variables will be read-only. 170 func (nb NsBuilder) AddGoFns(nsName string, fns map[string]interface{}) NsBuilder { 171 for name, impl := range fns { 172 nb.AddGoFn(nsName, name, impl) 173 } 174 return nb 175 } 176 177 // Ns builds a namespace. 178 func (nb NsBuilder) Ns() *Ns { 179 n := len(nb) 180 ns := &Ns{make([]vars.Var, n), make([]string, n), make([]bool, n)} 181 i := 0 182 for name, variable := range nb { 183 ns.slots[i] = variable 184 ns.names[i] = name 185 i++ 186 } 187 return ns 188 } 189 190 // The compile-time representation of a namespace. Called "static" namespace 191 // since it contains information that are known without executing the code. 192 // The data structure itself, however, is not static, and gets mutated as the 193 // compiler gains more information about the namespace. The zero value of 194 // staticNs is an empty namespace. 195 type staticNs struct { 196 names []string 197 deleted []bool 198 } 199 200 func (ns *staticNs) clone() *staticNs { 201 return &staticNs{ 202 append([]string{}, ns.names...), append([]bool{}, ns.deleted...)} 203 } 204 205 func (ns *staticNs) del(k string) { 206 if i := ns.lookup(k); i != -1 { 207 ns.deleted[i] = true 208 } 209 } 210 211 // Adds a name, shadowing any existing one. 212 func (ns *staticNs) add(k string) int { 213 ns.del(k) 214 return ns.addInner(k) 215 } 216 217 // Adds a name, assuming that it either doesn't exist yet or has been deleted. 218 func (ns *staticNs) addInner(k string) int { 219 ns.names = append(ns.names, k) 220 ns.deleted = append(ns.deleted, false) 221 return len(ns.names) - 1 222 } 223 224 func (ns *staticNs) lookup(k string) int { 225 for i, name := range ns.names { 226 if name == k && !ns.deleted[i] { 227 return i 228 } 229 } 230 return -1 231 } 232 233 type staticUpNs struct { 234 names []string 235 // For each name, whether the upvalue comes from the immediate outer scope, 236 // i.e. the local scope a lambda is evaluated in. 237 local []bool 238 // Index of the upvalue variable, either into the local scope (if 239 // the corresponding value in local is true) or the up scope (if the 240 // corresponding value in local is false). 241 index []int 242 } 243 244 func (up *staticUpNs) add(k string, local bool, index int) int { 245 for i, name := range up.names { 246 if name == k { 247 return i 248 } 249 } 250 up.names = append(up.names, k) 251 up.local = append(up.local, local) 252 up.index = append(up.index, index) 253 return len(up.names) - 1 254 }