2016-02-20 07:48:13 +08:00
|
|
|
package eval
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2017-12-24 00:39:29 +08:00
|
|
|
"strings"
|
2016-02-20 07:48:13 +08:00
|
|
|
|
2018-01-01 04:31:45 +08:00
|
|
|
"github.com/elves/elvish/eval/types"
|
2018-01-03 09:13:51 +08:00
|
|
|
"github.com/elves/elvish/eval/vartypes"
|
2016-02-20 07:48:13 +08:00
|
|
|
"github.com/elves/elvish/parse"
|
|
|
|
)
|
|
|
|
|
2017-06-11 07:04:53 +08:00
|
|
|
// LValuesOp is an operation on an EvalCtx that produce Variable's.
|
2016-02-25 22:53:55 +08:00
|
|
|
type LValuesOp struct {
|
|
|
|
Func LValuesOpFunc
|
2016-02-20 07:48:13 +08:00
|
|
|
Begin, End int
|
|
|
|
}
|
|
|
|
|
2017-06-11 07:04:53 +08:00
|
|
|
// LValuesOpFunc is the body of an LValuesOp.
|
2018-01-03 09:13:51 +08:00
|
|
|
type LValuesOpFunc func(*Frame) []vartypes.Variable
|
2016-02-20 07:48:13 +08:00
|
|
|
|
2017-06-11 07:04:53 +08:00
|
|
|
// Exec executes an LValuesOp, producing Variable's.
|
2018-01-03 09:13:51 +08:00
|
|
|
func (op LValuesOp) Exec(ec *Frame) []vartypes.Variable {
|
2016-02-26 08:10:16 +08:00
|
|
|
// Empty value is considered to generate no lvalues.
|
|
|
|
if op.Func == nil {
|
2018-01-03 09:13:51 +08:00
|
|
|
return []vartypes.Variable{}
|
2016-02-26 08:10:16 +08:00
|
|
|
}
|
2016-02-20 07:48:13 +08:00
|
|
|
ec.begin, ec.end = op.Begin, op.End
|
|
|
|
return op.Func(ec)
|
|
|
|
}
|
|
|
|
|
2016-02-26 08:10:16 +08:00
|
|
|
// lvaluesOp compiles lvalues, returning the fixed part and, optionally a rest
|
|
|
|
// part.
|
|
|
|
//
|
|
|
|
// In the AST an lvalue is either an Indexing node where the head is a string
|
|
|
|
// literal, or a braced list of such Indexing nodes. The last Indexing node may
|
|
|
|
// be prefixed by @, in which case they become the rest part. For instance, in
|
|
|
|
// {a[x],b,@c[z]}, "a[x],b" is the fixed part and "c[z]" is the rest part.
|
|
|
|
func (cp *compiler) lvaluesOp(n *parse.Indexing) (LValuesOp, LValuesOp) {
|
2016-02-20 07:48:13 +08:00
|
|
|
if n.Head.Type == parse.Braced {
|
2018-01-11 08:27:11 +08:00
|
|
|
// Braced list of variable specs, possibly with indicies.
|
2016-02-26 08:10:16 +08:00
|
|
|
if len(n.Indicies) > 0 {
|
|
|
|
cp.errorf("may not have indicies")
|
|
|
|
}
|
2017-02-04 02:06:11 +08:00
|
|
|
return cp.lvaluesMulti(n.Head.Braced)
|
2016-02-26 08:10:16 +08:00
|
|
|
}
|
2018-01-11 08:27:11 +08:00
|
|
|
rest, opFunc := cp.lvalueBase(n, "must be an lvalue or a braced list of those")
|
2016-02-26 08:10:16 +08:00
|
|
|
op := LValuesOp{opFunc, n.Begin(), n.End()}
|
|
|
|
if rest {
|
|
|
|
return LValuesOp{}, op
|
2016-02-20 07:48:13 +08:00
|
|
|
}
|
2017-05-22 06:54:04 +08:00
|
|
|
return op, LValuesOp{}
|
2016-02-20 07:48:13 +08:00
|
|
|
}
|
|
|
|
|
2017-02-04 02:06:11 +08:00
|
|
|
func (cp *compiler) lvaluesMulti(nodes []*parse.Compound) (LValuesOp, LValuesOp) {
|
|
|
|
opFuncs := make([]LValuesOpFunc, len(nodes))
|
|
|
|
var restNode *parse.Indexing
|
|
|
|
var restOpFunc LValuesOpFunc
|
|
|
|
|
|
|
|
// Compile each spec inside the brace.
|
|
|
|
fixedEnd := 0
|
|
|
|
for i, cn := range nodes {
|
|
|
|
if len(cn.Indexings) != 1 {
|
|
|
|
cp.errorpf(cn.Begin(), cn.End(), "must be an lvalue")
|
|
|
|
}
|
|
|
|
var rest bool
|
2018-01-11 08:27:11 +08:00
|
|
|
rest, opFuncs[i] = cp.lvalueBase(cn.Indexings[0], "must be an lvalue ")
|
2017-02-04 02:06:11 +08:00
|
|
|
// Only the last one may a rest part.
|
|
|
|
if rest {
|
|
|
|
if i == len(nodes)-1 {
|
|
|
|
restNode = cn.Indexings[0]
|
|
|
|
restOpFunc = opFuncs[i]
|
|
|
|
} else {
|
|
|
|
cp.errorpf(cn.Begin(), cn.End(), "only the last lvalue may have @")
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
fixedEnd = cn.End()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var restOp LValuesOp
|
|
|
|
// If there is a rest part, make LValuesOp for it and remove it from opFuncs.
|
|
|
|
if restOpFunc != nil {
|
|
|
|
restOp = LValuesOp{restOpFunc, restNode.Begin(), restNode.End()}
|
|
|
|
opFuncs = opFuncs[:len(opFuncs)-1]
|
|
|
|
}
|
|
|
|
|
|
|
|
var op LValuesOp
|
|
|
|
// If there is still anything left in opFuncs, make LValuesOp for the fixed part.
|
|
|
|
if len(opFuncs) > 0 {
|
2018-01-03 09:13:51 +08:00
|
|
|
op = LValuesOp{func(ec *Frame) []vartypes.Variable {
|
|
|
|
var variables []vartypes.Variable
|
2017-02-04 02:06:11 +08:00
|
|
|
for _, opFunc := range opFuncs {
|
|
|
|
variables = append(variables, opFunc(ec)...)
|
|
|
|
}
|
|
|
|
return variables
|
|
|
|
}, nodes[0].Begin(), fixedEnd}
|
|
|
|
}
|
|
|
|
|
|
|
|
return op, restOp
|
|
|
|
}
|
|
|
|
|
2018-01-11 08:27:11 +08:00
|
|
|
func (cp *compiler) lvalueBase(n *parse.Indexing, msg string) (bool, LValuesOpFunc) {
|
|
|
|
qname := cp.literal(n.Head, msg)
|
|
|
|
explode, ns, name := ParseVariable(qname)
|
2016-02-20 07:48:13 +08:00
|
|
|
if len(n.Indicies) == 0 {
|
2018-01-11 08:27:11 +08:00
|
|
|
return explode, cp.lvalueVariable(ns, name)
|
|
|
|
}
|
|
|
|
return explode, cp.lvalueElement(ns, name, n)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cp *compiler) lvalueVariable(ns, name string) LValuesOpFunc {
|
|
|
|
cp.registerVariableSet(ns, name)
|
|
|
|
|
|
|
|
return func(ec *Frame) []vartypes.Variable {
|
|
|
|
variable := ec.ResolveVar(ns, name)
|
|
|
|
if variable == nil {
|
|
|
|
if ns == "" || ns == "local" {
|
|
|
|
// New variable.
|
|
|
|
// XXX We depend on the fact that this variable will
|
|
|
|
// immeidately be set.
|
|
|
|
if strings.HasSuffix(name, FnSuffix) {
|
|
|
|
variable = vartypes.NewValidatedPtr(nil, ShouldBeFn)
|
|
|
|
} else if strings.HasSuffix(name, NsSuffix) {
|
|
|
|
variable = vartypes.NewValidatedPtr(nil, ShouldBeNs)
|
2016-02-20 07:48:13 +08:00
|
|
|
} else {
|
2018-01-11 08:27:11 +08:00
|
|
|
variable = vartypes.NewPtr(nil)
|
2016-02-20 07:48:13 +08:00
|
|
|
}
|
2018-01-11 08:27:11 +08:00
|
|
|
ec.local[name] = variable
|
|
|
|
} else {
|
|
|
|
throwf("new variables can only be created in local scope")
|
2016-02-20 07:48:13 +08:00
|
|
|
}
|
|
|
|
}
|
2018-01-11 08:27:11 +08:00
|
|
|
return []vartypes.Variable{variable}
|
2016-02-20 07:48:13 +08:00
|
|
|
}
|
2018-01-11 08:27:11 +08:00
|
|
|
}
|
2016-02-20 07:48:13 +08:00
|
|
|
|
2018-01-11 08:27:11 +08:00
|
|
|
func (cp *compiler) lvalueElement(ns, name string, n *parse.Indexing) LValuesOpFunc {
|
2018-01-11 08:56:14 +08:00
|
|
|
begin, end := n.Begin(), n.End()
|
|
|
|
ends := make([]int, len(n.Indicies)+1)
|
|
|
|
ends[0] = n.Head.End()
|
|
|
|
for i, idx := range n.Indicies {
|
|
|
|
ends[i+1] = idx.End()
|
|
|
|
}
|
|
|
|
|
2016-02-26 08:10:16 +08:00
|
|
|
indexOps := cp.arrayOps(n.Indicies)
|
2016-02-20 07:48:13 +08:00
|
|
|
|
2018-01-11 08:27:11 +08:00
|
|
|
return func(ec *Frame) []vartypes.Variable {
|
|
|
|
variable := ec.ResolveVar(ns, name)
|
2016-02-20 07:48:13 +08:00
|
|
|
if variable == nil {
|
2018-01-11 08:27:11 +08:00
|
|
|
throwf("variable $%s:%s does not exist, compiler bug", ns, name)
|
2016-02-20 07:48:13 +08:00
|
|
|
}
|
|
|
|
|
2018-01-01 04:31:45 +08:00
|
|
|
indicies := make([]types.Value, len(indexOps))
|
2017-09-01 07:06:00 +08:00
|
|
|
for i, op := range indexOps {
|
|
|
|
values := op.Exec(ec)
|
|
|
|
// TODO: Implement multi-indexing.
|
2016-02-20 07:48:13 +08:00
|
|
|
if len(values) != 1 {
|
|
|
|
throw(errors.New("multi indexing not implemented"))
|
|
|
|
}
|
2018-01-11 08:56:14 +08:00
|
|
|
indicies[i] = values[0]
|
|
|
|
}
|
|
|
|
elemVar, err := vartypes.MakeElement(variable, indicies)
|
|
|
|
if err != nil {
|
2018-01-11 09:25:26 +08:00
|
|
|
level := vartypes.GetElementErrorLevel(err)
|
2018-01-11 08:56:14 +08:00
|
|
|
if level < 0 {
|
|
|
|
ec.errorpf(begin, end, "%s", err)
|
|
|
|
} else {
|
|
|
|
ec.errorpf(begin, ends[level], "%s", err)
|
2017-09-01 07:06:00 +08:00
|
|
|
}
|
2016-02-20 07:48:13 +08:00
|
|
|
}
|
2018-01-11 08:56:14 +08:00
|
|
|
return []vartypes.Variable{elemVar}
|
2016-02-20 07:48:13 +08:00
|
|
|
}
|
|
|
|
}
|