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  }