2018-02-15 16:59:49 +08:00
|
|
|
package vars
|
2018-01-05 06:54:04 +08:00
|
|
|
|
|
|
|
import (
|
2018-02-15 17:14:05 +08:00
|
|
|
"github.com/elves/elvish/eval/vals"
|
2018-01-05 06:54:04 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
type elem struct {
|
2018-03-08 21:20:31 +08:00
|
|
|
variable Var
|
2018-01-30 01:39:41 +08:00
|
|
|
assocers []interface{}
|
|
|
|
indices []interface{}
|
|
|
|
setValue interface{}
|
2018-01-05 06:54:04 +08:00
|
|
|
}
|
|
|
|
|
2018-01-30 01:39:41 +08:00
|
|
|
func (ev *elem) Set(v0 interface{}) error {
|
2018-01-21 07:47:53 +08:00
|
|
|
var err error
|
2018-01-05 06:54:04 +08:00
|
|
|
v := v0
|
|
|
|
// Evaluate the actual new value from inside out. See comments in
|
2018-01-11 08:56:14 +08:00
|
|
|
// MakeElement for how element assignment works.
|
2018-01-05 06:54:04 +08:00
|
|
|
for i := len(ev.assocers) - 1; i >= 0; i-- {
|
2018-02-15 17:14:05 +08:00
|
|
|
v, err = vals.Assoc(ev.assocers[i], ev.indices[i], v)
|
2018-01-21 07:47:53 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-01-05 06:54:04 +08:00
|
|
|
}
|
2018-01-21 07:47:53 +08:00
|
|
|
err = ev.variable.Set(v)
|
2018-01-05 06:54:04 +08:00
|
|
|
// XXX(xiaq): Remember the set value for use in Get.
|
|
|
|
ev.setValue = v0
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-01-30 01:39:41 +08:00
|
|
|
func (ev *elem) Get() interface{} {
|
2018-01-05 06:54:04 +08:00
|
|
|
// XXX(xiaq): This is only called from fixNilVariables. We don't want to
|
|
|
|
// waste time accessing the variable, so we simply return the value that was
|
|
|
|
// set.
|
|
|
|
return ev.setValue
|
|
|
|
}
|
|
|
|
|
2018-01-11 08:27:11 +08:00
|
|
|
// NewElement returns an ephemeral variable used for assigning variable element.
|
2018-03-08 21:20:31 +08:00
|
|
|
func NewElement(v Var, a []interface{}, i []interface{}) Var {
|
2018-01-27 23:58:27 +08:00
|
|
|
return &elem{v, a, i, ""}
|
2018-01-05 06:54:04 +08:00
|
|
|
}
|
|
|
|
|
2018-01-11 08:56:14 +08:00
|
|
|
// MakeElement returns a variable, that when set, simulates the mutation of an
|
|
|
|
// element.
|
2018-03-08 21:20:31 +08:00
|
|
|
func MakeElement(v Var, indicies []interface{}) (Var, error) {
|
2018-01-11 08:56:14 +08:00
|
|
|
// Assignment of indexed variables actually assignes the variable, with
|
|
|
|
// the right hand being a nested series of Assocs. As the simplest
|
|
|
|
// example, `a[0] = x` is equivalent to `a = (assoc $a 0 x)`. A more
|
|
|
|
// complex example is that `a[0][1][2] = x` is equivalent to
|
|
|
|
// `a = (assoc $a 0 (assoc $a[0] 1 (assoc $a[0][1] 2 x)))`.
|
|
|
|
// Note that in each assoc form, the first two arguments can be
|
|
|
|
// determined now, while the last argument is only known when the
|
|
|
|
// right-hand-side is known. So here we evaluate the first two arguments
|
|
|
|
// of each assoc form and put them in two slices, assocers and indicies.
|
|
|
|
// In the previous example, the two slices will contain:
|
|
|
|
//
|
|
|
|
// assocers: $a $a[0] $a[0][1]
|
|
|
|
// indicies: 0 1 2
|
|
|
|
//
|
|
|
|
// When the right-hand side of the assignment becomes available, the new
|
|
|
|
// value for $a is evaluated by doing Assoc from inside out.
|
2018-01-30 01:39:41 +08:00
|
|
|
assocers := make([]interface{}, len(indicies))
|
2018-01-25 08:26:46 +08:00
|
|
|
varValue := v.Get()
|
2018-01-11 08:56:14 +08:00
|
|
|
assocers[0] = varValue
|
|
|
|
for i, index := range indicies[:len(indicies)-1] {
|
2018-01-25 08:26:46 +08:00
|
|
|
lastAssocer := assocers[i]
|
2018-02-15 17:14:05 +08:00
|
|
|
v, err := vals.Index(lastAssocer, index)
|
2018-01-21 05:15:15 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-01-25 08:26:46 +08:00
|
|
|
assocers[i+1] = v
|
2018-01-11 08:56:14 +08:00
|
|
|
}
|
|
|
|
return NewElement(v, assocers, indicies), nil
|
|
|
|
}
|
|
|
|
|
2018-01-11 09:25:26 +08:00
|
|
|
type elemErr struct {
|
2018-01-11 08:56:14 +08:00
|
|
|
level int
|
|
|
|
msg string
|
|
|
|
}
|
|
|
|
|
2018-01-11 09:25:26 +08:00
|
|
|
func (err elemErr) Error() string {
|
2018-01-11 08:56:14 +08:00
|
|
|
return err.msg
|
|
|
|
}
|
|
|
|
|
2018-03-08 21:24:18 +08:00
|
|
|
// HeadOfElement gets the underlying head variable of an element variable, or
|
2018-01-11 08:27:11 +08:00
|
|
|
// nil if the argument is not an element variable.
|
2018-03-08 21:24:18 +08:00
|
|
|
func HeadOfElement(v Var) Var {
|
2018-01-05 06:54:04 +08:00
|
|
|
if ev, ok := v.(*elem); ok {
|
|
|
|
return ev.variable
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2018-01-11 08:56:14 +08:00
|
|
|
|
2018-03-08 21:24:18 +08:00
|
|
|
// ElementErrorLevel returns the level of an error returned by MakeElement or
|
2018-01-11 09:25:26 +08:00
|
|
|
// DelElement. Level 0 represents that the error is about the variable itself.
|
2018-01-11 08:56:14 +08:00
|
|
|
// If the argument was not returned from MakeVariable, -1 is returned.
|
2018-03-08 21:24:18 +08:00
|
|
|
func ElementErrorLevel(err error) int {
|
2018-01-11 09:25:26 +08:00
|
|
|
if err, ok := err.(elemErr); ok {
|
2018-01-11 08:56:14 +08:00
|
|
|
return err.level
|
|
|
|
}
|
|
|
|
return -1
|
|
|
|
}
|