github.com/mem/u-root@v2.0.1-0.20181004165302-9b18b4636a33+incompatible/cmds/elvish/eval/vars/element.go (about)

     1  package vars
     2  
     3  import (
     4  	"github.com/u-root/u-root/cmds/elvish/eval/vals"
     5  )
     6  
     7  type elem struct {
     8  	variable Var
     9  	assocers []interface{}
    10  	indices  []interface{}
    11  	setValue interface{}
    12  }
    13  
    14  func (ev *elem) Set(v0 interface{}) error {
    15  	var err error
    16  	v := v0
    17  	// Evaluate the actual new value from inside out. See comments in
    18  	// MakeElement for how element assignment works.
    19  	for i := len(ev.assocers) - 1; i >= 0; i-- {
    20  		v, err = vals.Assoc(ev.assocers[i], ev.indices[i], v)
    21  		if err != nil {
    22  			return err
    23  		}
    24  	}
    25  	err = ev.variable.Set(v)
    26  	// XXX(xiaq): Remember the set value for use in Get.
    27  	ev.setValue = v0
    28  	return err
    29  }
    30  
    31  func (ev *elem) Get() interface{} {
    32  	// XXX(xiaq): This is only called from fixNilVariables. We don't want to
    33  	// waste time accessing the variable, so we simply return the value that was
    34  	// set.
    35  	return ev.setValue
    36  }
    37  
    38  // NewElement returns an ephemeral variable used for assigning variable element.
    39  func NewElement(v Var, a []interface{}, i []interface{}) Var {
    40  	return &elem{v, a, i, ""}
    41  }
    42  
    43  // MakeElement returns a variable, that when set, simulates the mutation of an
    44  // element.
    45  func MakeElement(v Var, indicies []interface{}) (Var, error) {
    46  	// Assignment of indexed variables actually assignes the variable, with
    47  	// the right hand being a nested series of Assocs. As the simplest
    48  	// example, `a[0] = x` is equivalent to `a = (assoc $a 0 x)`. A more
    49  	// complex example is that `a[0][1][2] = x` is equivalent to
    50  	//	`a = (assoc $a 0 (assoc $a[0] 1 (assoc $a[0][1] 2 x)))`.
    51  	// Note that in each assoc form, the first two arguments can be
    52  	// determined now, while the last argument is only known when the
    53  	// right-hand-side is known. So here we evaluate the first two arguments
    54  	// of each assoc form and put them in two slices, assocers and indicies.
    55  	// In the previous example, the two slices will contain:
    56  	//
    57  	// assocers: $a $a[0] $a[0][1]
    58  	// indicies:  0     1        2
    59  	//
    60  	// When the right-hand side of the assignment becomes available, the new
    61  	// value for $a is evaluated by doing Assoc from inside out.
    62  	assocers := make([]interface{}, len(indicies))
    63  	varValue := v.Get()
    64  	assocers[0] = varValue
    65  	for i, index := range indicies[:len(indicies)-1] {
    66  		lastAssocer := assocers[i]
    67  		v, err := vals.Index(lastAssocer, index)
    68  		if err != nil {
    69  			return nil, err
    70  		}
    71  		assocers[i+1] = v
    72  	}
    73  	return NewElement(v, assocers, indicies), nil
    74  }
    75  
    76  type elemErr struct {
    77  	level int
    78  	msg   string
    79  }
    80  
    81  func (err elemErr) Error() string {
    82  	return err.msg
    83  }
    84  
    85  // HeadOfElement gets the underlying head variable of an element variable, or
    86  // nil if the argument is not an element variable.
    87  func HeadOfElement(v Var) Var {
    88  	if ev, ok := v.(*elem); ok {
    89  		return ev.variable
    90  	}
    91  	return nil
    92  }
    93  
    94  // ElementErrorLevel returns the level of an error returned by MakeElement or
    95  // DelElement. Level 0 represents that the error is about the variable itself.
    96  // If the argument was not returned from MakeVariable, -1 is returned.
    97  func ElementErrorLevel(err error) int {
    98  	if err, ok := err.(elemErr); ok {
    99  		return err.level
   100  	}
   101  	return -1
   102  }