elvish/pkg/eval/vars/element.go

134 lines
3.7 KiB
Go
Raw Normal View History

2018-02-15 16:59:49 +08:00
package vars
import (
2019-12-24 04:00:59 +08:00
"github.com/elves/elvish/pkg/eval/vals"
)
type elem struct {
2018-03-08 21:20:31 +08:00
variable Var
assocers []interface{}
indices []interface{}
setValue interface{}
}
func (ev *elem) Set(v0 interface{}) error {
var err error
v := v0
// Evaluate the actual new value from inside out. See comments in
// MakeElement for how element assignment works.
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)
if err != nil {
return err
}
}
err = ev.variable.Set(v)
// TODO(xiaq): Remember the set value for use in Get.
ev.setValue = v0
return err
}
func (ev *elem) Get() interface{} {
// TODO(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
}
// MakeElement returns a variable, that when set, simulates the mutation of an
// element.
2020-10-06 02:35:52 +08:00
func MakeElement(v Var, indices []interface{}) (Var, error) {
// Assignment of indexed variables actually assigns 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
2020-10-06 02:35:52 +08:00
// of each assoc form and put them in two slices, assocers and indices.
// In the previous example, the two slices will contain:
//
// assocers: $a $a[0] $a[0][1]
2020-10-06 02:35:52 +08:00
// indices: 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.
2020-10-06 02:35:52 +08:00
assocers := make([]interface{}, len(indices))
varValue := v.Get()
assocers[0] = varValue
2020-10-06 02:35:52 +08:00
for i, index := range indices[:len(indices)-1] {
lastAssocer := assocers[i]
2018-02-15 17:14:05 +08:00
v, err := vals.Index(lastAssocer, index)
if err != nil {
return nil, err
}
assocers[i+1] = v
}
2020-10-06 02:35:52 +08:00
return &elem{v, assocers, indices, nil}, nil
}
2018-11-18 22:23:31 +08:00
// DelElement deletes an element. It uses a similar process to MakeElement,
// except that the last level of container needs to be Dissoc-able instead of
// Assoc-able.
2020-10-06 02:35:52 +08:00
func DelElement(variable Var, indices []interface{}) error {
2018-11-18 22:23:31 +08:00
var err error
// In "del a[0][1][2]",
//
2020-10-06 02:35:52 +08:00
// indices: 0 1 2
2018-11-18 22:23:31 +08:00
// assocers: $a $a[0]
// dissocer: $a[0][1]
2020-10-06 02:35:52 +08:00
assocers := make([]interface{}, len(indices)-1)
2018-11-18 22:23:31 +08:00
container := variable.Get()
2020-10-06 02:35:52 +08:00
for i, index := range indices[:len(indices)-1] {
2018-11-18 22:23:31 +08:00
assocers[i] = container
var err error
container, err = vals.Index(container, index)
if err != nil {
return err
}
}
2020-10-06 02:35:52 +08:00
v := vals.Dissoc(container, indices[len(indices)-1])
2018-11-18 22:23:31 +08:00
if v == nil {
2020-10-06 02:35:52 +08:00
return elemErr{len(indices), "value does not support element removal"}
2018-11-18 22:23:31 +08:00
}
for i := len(assocers) - 1; i >= 0; i-- {
2020-10-06 02:35:52 +08:00
v, err = vals.Assoc(assocers[i], indices[i], v)
2018-11-18 22:23:31 +08:00
if err != nil {
return err
}
}
return variable.Set(v)
}
type elemErr struct {
level int
msg string
}
func (err elemErr) Error() string {
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 {
if ev, ok := v.(*elem); ok {
return ev.variable
}
return nil
}
2018-03-08 21:24:18 +08:00
// ElementErrorLevel returns the level of an error returned by MakeElement or
// DelElement. Level 0 represents that the error is about the variable itself.
// If the argument was not returned from MakeVariable, -1 is returned.
2018-03-08 21:24:18 +08:00
func ElementErrorLevel(err error) int {
if err, ok := err.(elemErr); ok {
return err.level
}
return -1
}