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 }