mirror of
https://github.com/go-sylixos/elvish.git
synced 2024-12-05 03:17:50 +08:00
af906f1d76
Also change the variable name used to keep the Exception returned from "err" to "exc". This uncovers several error scenarios that were not returning Exception, and would result in the absense of stack traces when such errors occur.
182 lines
4.6 KiB
Go
182 lines
4.6 KiB
Go
package eval
|
|
|
|
import (
|
|
"errors"
|
|
|
|
"github.com/elves/elvish/pkg/diag"
|
|
"github.com/elves/elvish/pkg/eval/errs"
|
|
"github.com/elves/elvish/pkg/eval/vals"
|
|
"github.com/elves/elvish/pkg/eval/vars"
|
|
"github.com/elves/elvish/pkg/parse"
|
|
)
|
|
|
|
// Parsed group of lvalues.
|
|
type lvaluesGroup struct {
|
|
lvalues []lvalue
|
|
// Index of the rest variable within lvalues. If there is no rest variable,
|
|
// the index is -1.
|
|
rest int
|
|
}
|
|
|
|
// Parsed lvalue.
|
|
type lvalue struct {
|
|
diag.Ranging
|
|
ref *varRef
|
|
indexOps []valuesOp
|
|
ends []int
|
|
}
|
|
|
|
func (cp *compiler) parseCompoundLValues(ns []*parse.Compound) lvaluesGroup {
|
|
g := lvaluesGroup{nil, -1}
|
|
for _, n := range ns {
|
|
if len(n.Indexings) != 1 {
|
|
cp.errorpf(n, "lvalue may not be composite expressions")
|
|
}
|
|
more := cp.parseIndexingLValue(n.Indexings[0])
|
|
if more.rest == -1 {
|
|
g.lvalues = append(g.lvalues, more.lvalues...)
|
|
} else if g.rest != -1 {
|
|
cp.errorpf(n, "at most one rest variable is allowed")
|
|
} else {
|
|
g.rest = len(g.lvalues) + more.rest
|
|
g.lvalues = append(g.lvalues, more.lvalues...)
|
|
}
|
|
}
|
|
return g
|
|
}
|
|
|
|
func (cp *compiler) parseIndexingLValue(n *parse.Indexing) lvaluesGroup {
|
|
if n.Head.Type == parse.Braced {
|
|
// Braced list of lvalues may not have indices.
|
|
if len(n.Indicies) > 0 {
|
|
cp.errorpf(n, "braced list may not have indices when used as lvalue")
|
|
}
|
|
return cp.parseCompoundLValues(n.Head.Braced)
|
|
}
|
|
// A basic lvalue.
|
|
varUse := cp.literal(n.Head, "lvalue only supports literal variable names")
|
|
sigil, qname := SplitSigil(varUse)
|
|
var ref *varRef
|
|
if len(n.Indicies) == 0 {
|
|
ref = resolveVarRef(cp, qname, nil)
|
|
if ref == nil {
|
|
segs := SplitQNameSegs(qname)
|
|
if len(segs) == 1 {
|
|
// Unqualified name - implicit local
|
|
ref = &varRef{localScope, cp.thisScope().addInner(segs[0]), nil}
|
|
} else if len(segs) == 2 && (segs[0] == "local:" || segs[0] == ":") {
|
|
// Qualified local name
|
|
ref = &varRef{localScope, cp.thisScope().addInner(segs[1]), nil}
|
|
} else {
|
|
cp.errorpf(n, "cannot create variable $%s; new variables can only be created in the local scope", qname)
|
|
}
|
|
}
|
|
} else {
|
|
ref = resolveVarRef(cp, qname, n)
|
|
if ref == nil {
|
|
cp.errorpf(n, "cannot find variable $%s", qname)
|
|
}
|
|
}
|
|
ends := make([]int, len(n.Indicies)+1)
|
|
ends[0] = n.Head.Range().To
|
|
for i, idx := range n.Indicies {
|
|
ends[i+1] = idx.Range().To
|
|
}
|
|
lv := lvalue{n.Range(), ref, cp.arrayOps(n.Indicies), ends}
|
|
restIndex := -1
|
|
if sigil == "@" {
|
|
restIndex = 0
|
|
}
|
|
// TODO: Deal with other sigils when they exist.
|
|
return lvaluesGroup{[]lvalue{lv}, restIndex}
|
|
}
|
|
|
|
type assignOp struct {
|
|
diag.Ranging
|
|
lhs lvaluesGroup
|
|
rhs valuesOp
|
|
}
|
|
|
|
func (op *assignOp) exec(fm *Frame) Exception {
|
|
variables := make([]vars.Var, len(op.lhs.lvalues))
|
|
for i, lvalue := range op.lhs.lvalues {
|
|
variable, err := derefLValue(fm, lvalue)
|
|
if err != nil {
|
|
return fm.errorp(op, err)
|
|
}
|
|
variables[i] = variable
|
|
}
|
|
|
|
values, exc := op.rhs.exec(fm)
|
|
if exc != nil {
|
|
return exc
|
|
}
|
|
|
|
if op.lhs.rest == -1 {
|
|
if len(variables) != len(values) {
|
|
return fm.errorp(op, errs.ArityMismatch{
|
|
What: "assignment right-hand-side",
|
|
ValidLow: len(variables), ValidHigh: len(variables), Actual: len(values)})
|
|
}
|
|
for i, variable := range variables {
|
|
err := variable.Set(values[i])
|
|
if err != nil {
|
|
return fm.errorp(op, err)
|
|
}
|
|
}
|
|
} else {
|
|
if len(values) < len(variables)-1 {
|
|
return fm.errorp(op, errs.ArityMismatch{
|
|
What: "assignment right-hand-side",
|
|
ValidLow: len(variables) - 1, ValidHigh: -1, Actual: len(values)})
|
|
}
|
|
rest := op.lhs.rest
|
|
for i := 0; i < rest; i++ {
|
|
err := variables[i].Set(values[i])
|
|
if err != nil {
|
|
return fm.errorp(op, err)
|
|
}
|
|
}
|
|
restOff := len(values) - len(variables)
|
|
err := variables[rest].Set(vals.MakeList(values[rest : rest+restOff+1]...))
|
|
if err != nil {
|
|
return fm.errorp(op, err)
|
|
}
|
|
for i := rest + 1; i < len(variables); i++ {
|
|
err := variables[i].Set(values[i+restOff])
|
|
if err != nil {
|
|
return fm.errorp(op, err)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func derefLValue(fm *Frame, lv lvalue) (vars.Var, error) {
|
|
variable := deref(fm, lv.ref)
|
|
if len(lv.indexOps) == 0 {
|
|
return variable, nil
|
|
}
|
|
indices := make([]interface{}, len(lv.indexOps))
|
|
for i, op := range lv.indexOps {
|
|
values, exc := op.exec(fm)
|
|
if exc != nil {
|
|
return nil, exc
|
|
}
|
|
// TODO: Implement multi-indexing.
|
|
if len(values) != 1 {
|
|
return nil, errors.New("multi indexing not implemented")
|
|
}
|
|
indices[i] = values[0]
|
|
}
|
|
elemVar, err := vars.MakeElement(variable, indices)
|
|
if err != nil {
|
|
level := vars.ElementErrorLevel(err)
|
|
if level < 0 {
|
|
return nil, fm.errorp(lv, err)
|
|
}
|
|
return nil, fm.errorp(diag.Ranging{From: lv.From, To: lv.ends[level]}, err)
|
|
}
|
|
return elemVar, nil
|
|
}
|