github.com/elves/elvish@v0.15.0/pkg/eval/vars/element.go (about)

     1  package vars
     2  
     3  import (
     4  	"github.com/elves/elvish/pkg/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  	// TODO(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  	// TODO(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  // MakeElement returns a variable, that when set, simulates the mutation of an
    39  // element.
    40  func MakeElement(v Var, indices []interface{}) (Var, error) {
    41  	// Assignment of indexed variables actually assigns the variable, with
    42  	// the right hand being a nested series of Assocs. As the simplest
    43  	// example, `a[0] = x` is equivalent to `a = (assoc $a 0 x)`. A more
    44  	// complex example is that `a[0][1][2] = x` is equivalent to
    45  	//	`a = (assoc $a 0 (assoc $a[0] 1 (assoc $a[0][1] 2 x)))`.
    46  	// Note that in each assoc form, the first two arguments can be
    47  	// determined now, while the last argument is only known when the
    48  	// right-hand-side is known. So here we evaluate the first two arguments
    49  	// of each assoc form and put them in two slices, assocers and indices.
    50  	// In the previous example, the two slices will contain:
    51  	//
    52  	// assocers: $a $a[0] $a[0][1]
    53  	// indices:   0     1        2
    54  	//
    55  	// When the right-hand side of the assignment becomes available, the new
    56  	// value for $a is evaluated by doing Assoc from inside out.
    57  	assocers := make([]interface{}, len(indices))
    58  	varValue := v.Get()
    59  	assocers[0] = varValue
    60  	for i, index := range indices[:len(indices)-1] {
    61  		lastAssocer := assocers[i]
    62  		v, err := vals.Index(lastAssocer, index)
    63  		if err != nil {
    64  			return nil, err
    65  		}
    66  		assocers[i+1] = v
    67  	}
    68  	return &elem{v, assocers, indices, nil}, nil
    69  }
    70  
    71  // DelElement deletes an element. It uses a similar process to MakeElement,
    72  // except that the last level of container needs to be Dissoc-able instead of
    73  // Assoc-able.
    74  func DelElement(variable Var, indices []interface{}) error {
    75  	var err error
    76  	// In "del a[0][1][2]",
    77  	//
    78  	// indices:   0  1     2
    79  	// assocers: $a $a[0]
    80  	// dissocer:          $a[0][1]
    81  	assocers := make([]interface{}, len(indices)-1)
    82  	container := variable.Get()
    83  	for i, index := range indices[:len(indices)-1] {
    84  		assocers[i] = container
    85  
    86  		var err error
    87  		container, err = vals.Index(container, index)
    88  		if err != nil {
    89  			return err
    90  		}
    91  	}
    92  
    93  	v := vals.Dissoc(container, indices[len(indices)-1])
    94  	if v == nil {
    95  		return elemErr{len(indices), "value does not support element removal"}
    96  	}
    97  
    98  	for i := len(assocers) - 1; i >= 0; i-- {
    99  		v, err = vals.Assoc(assocers[i], indices[i], v)
   100  		if err != nil {
   101  			return err
   102  		}
   103  	}
   104  	return variable.Set(v)
   105  }
   106  
   107  type elemErr struct {
   108  	level int
   109  	msg   string
   110  }
   111  
   112  func (err elemErr) Error() string {
   113  	return err.msg
   114  }
   115  
   116  // HeadOfElement gets the underlying head variable of an element variable, or
   117  // nil if the argument is not an element variable.
   118  func HeadOfElement(v Var) Var {
   119  	if ev, ok := v.(*elem); ok {
   120  		return ev.variable
   121  	}
   122  	return nil
   123  }
   124  
   125  // ElementErrorLevel returns the level of an error returned by MakeElement or
   126  // DelElement. Level 0 represents that the error is about the variable itself.
   127  // If the argument was not returned from MakeVariable, -1 is returned.
   128  func ElementErrorLevel(err error) int {
   129  	if err, ok := err.(elemErr); ok {
   130  		return err.level
   131  	}
   132  	return -1
   133  }