elvish/eval/compile_lvalue.go

174 lines
4.9 KiB
Go
Raw Normal View History

2016-02-20 07:48:13 +08:00
package eval
import (
"errors"
"strings"
2016-02-20 07:48:13 +08:00
"github.com/elves/elvish/eval/types"
"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.
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.
func (op LValuesOp) Exec(ec *Frame) []vartypes.Variable {
// Empty value is considered to generate no lvalues.
if op.Func == nil {
return []vartypes.Variable{}
}
2016-02-20 07:48:13 +08:00
ec.begin, ec.end = op.Begin, op.End
return op.Func(ec)
}
// 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.
if len(n.Indicies) > 0 {
cp.errorf("may not have indicies")
}
return cp.lvaluesMulti(n.Head.Braced)
}
2018-01-11 08:27:11 +08:00
rest, opFunc := cp.lvalueBase(n, "must be an lvalue or a braced list of those")
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
}
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 ")
// 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 {
op = LValuesOp{func(ec *Frame) []vartypes.Variable {
var variables []vartypes.Variable
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 {
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()
}
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
}
indicies := make([]types.Value, len(indexOps))
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"))
}
indicies[i] = values[0]
}
elemVar, err := vartypes.MakeElement(variable, indicies)
if err != nil {
level := vartypes.GetElementErrorLevel(err)
if level < 0 {
ec.errorpf(begin, end, "%s", err)
} else {
ec.errorpf(begin, ends[level], "%s", err)
}
2016-02-20 07:48:13 +08:00
}
return []vartypes.Variable{elemVar}
2016-02-20 07:48:13 +08:00
}
}