2016-02-20 07:48:13 +08:00
|
|
|
package eval
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
|
2021-01-27 09:28:38 +08:00
|
|
|
"src.elv.sh/pkg/diag"
|
|
|
|
"src.elv.sh/pkg/eval/errs"
|
|
|
|
"src.elv.sh/pkg/eval/vals"
|
|
|
|
"src.elv.sh/pkg/eval/vars"
|
|
|
|
"src.elv.sh/pkg/parse"
|
2016-02-20 07:48:13 +08:00
|
|
|
)
|
|
|
|
|
2020-08-20 20:15:00 +08:00
|
|
|
// 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
|
2016-02-20 07:48:13 +08:00
|
|
|
}
|
|
|
|
|
2020-08-20 20:15:00 +08:00
|
|
|
// Parsed lvalue.
|
|
|
|
type lvalue struct {
|
|
|
|
diag.Ranging
|
2020-12-25 01:39:51 +08:00
|
|
|
ref *varRef
|
2020-08-20 20:15:00 +08:00
|
|
|
indexOps []valuesOp
|
|
|
|
ends []int
|
|
|
|
}
|
2017-02-04 02:06:11 +08:00
|
|
|
|
2021-10-10 21:31:09 +08:00
|
|
|
type lvalueFlag uint
|
|
|
|
|
|
|
|
const (
|
|
|
|
setLValue lvalueFlag = 1 << iota
|
|
|
|
newLValue
|
|
|
|
)
|
|
|
|
|
|
|
|
func (cp *compiler) parseCompoundLValues(ns []*parse.Compound, f lvalueFlag) lvaluesGroup {
|
2020-08-20 20:15:00 +08:00
|
|
|
g := lvaluesGroup{nil, -1}
|
|
|
|
for _, n := range ns {
|
|
|
|
if len(n.Indexings) != 1 {
|
|
|
|
cp.errorpf(n, "lvalue may not be composite expressions")
|
2022-11-29 06:49:55 +08:00
|
|
|
break
|
2017-02-04 02:06:11 +08:00
|
|
|
}
|
2021-10-10 21:31:09 +08:00
|
|
|
more := cp.parseIndexingLValue(n.Indexings[0], f)
|
2020-08-20 20:15:00 +08:00
|
|
|
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")
|
2017-02-04 02:06:11 +08:00
|
|
|
} else {
|
2020-08-20 20:15:00 +08:00
|
|
|
g.rest = len(g.lvalues) + more.rest
|
|
|
|
g.lvalues = append(g.lvalues, more.lvalues...)
|
2017-02-04 02:06:11 +08:00
|
|
|
}
|
|
|
|
}
|
2020-08-20 20:15:00 +08:00
|
|
|
return g
|
2017-02-04 02:06:11 +08:00
|
|
|
}
|
|
|
|
|
2022-11-30 09:38:50 +08:00
|
|
|
var dummyLValuesGroup = lvaluesGroup{[]lvalue{{}}, -1}
|
|
|
|
|
2021-10-10 21:31:09 +08:00
|
|
|
func (cp *compiler) parseIndexingLValue(n *parse.Indexing, f lvalueFlag) lvaluesGroup {
|
2020-08-20 20:15:00 +08:00
|
|
|
if n.Head.Type == parse.Braced {
|
2020-10-06 02:35:52 +08:00
|
|
|
// Braced list of lvalues may not have indices.
|
2021-08-23 06:31:26 +08:00
|
|
|
if len(n.Indices) > 0 {
|
2020-10-06 02:35:52 +08:00
|
|
|
cp.errorpf(n, "braced list may not have indices when used as lvalue")
|
2022-11-30 09:38:50 +08:00
|
|
|
return dummyLValuesGroup
|
2020-08-20 20:15:00 +08:00
|
|
|
}
|
2021-10-10 21:31:09 +08:00
|
|
|
return cp.parseCompoundLValues(n.Head.Braced, f)
|
2020-01-12 21:23:37 +08:00
|
|
|
}
|
2020-08-20 20:15:00 +08:00
|
|
|
// A basic lvalue.
|
2021-01-16 13:00:15 +08:00
|
|
|
if !parse.ValidLHSVariable(n.Head, true) {
|
|
|
|
cp.errorpf(n.Head, "lvalue must be valid literal variable names")
|
2022-11-30 09:38:50 +08:00
|
|
|
return dummyLValuesGroup
|
2021-01-16 13:00:15 +08:00
|
|
|
}
|
|
|
|
varUse := n.Head.Value
|
2020-12-26 06:31:52 +08:00
|
|
|
sigil, qname := SplitSigil(varUse)
|
2022-10-03 11:34:05 +08:00
|
|
|
if qname == "" {
|
|
|
|
cp.errorpf(n, "variable name must not be empty")
|
2022-11-30 09:38:50 +08:00
|
|
|
return dummyLValuesGroup
|
2022-10-03 11:34:05 +08:00
|
|
|
}
|
2021-10-10 21:31:09 +08:00
|
|
|
|
2020-12-25 01:39:51 +08:00
|
|
|
var ref *varRef
|
2021-10-10 21:31:09 +08:00
|
|
|
if f&setLValue != 0 {
|
2020-12-25 01:39:51 +08:00
|
|
|
ref = resolveVarRef(cp, qname, n)
|
2021-10-14 06:57:14 +08:00
|
|
|
if ref != nil && len(ref.subNames) == 0 && ref.info.readOnly {
|
2022-10-03 11:22:46 +08:00
|
|
|
cp.errorpf(n, "variable $%s is read-only", parse.Quote(qname))
|
2022-11-30 09:38:50 +08:00
|
|
|
return dummyLValuesGroup
|
2021-10-14 06:57:14 +08:00
|
|
|
}
|
2021-10-10 21:31:09 +08:00
|
|
|
}
|
|
|
|
if ref == nil {
|
|
|
|
if f&newLValue == 0 {
|
2022-12-11 22:21:09 +08:00
|
|
|
cp.autofixUnresolvedVar(qname)
|
2022-10-03 11:22:46 +08:00
|
|
|
cp.errorpf(n, "cannot find variable $%s", parse.Quote(qname))
|
2022-11-30 09:38:50 +08:00
|
|
|
return dummyLValuesGroup
|
2020-12-25 01:39:51 +08:00
|
|
|
}
|
2021-10-10 21:31:09 +08:00
|
|
|
if len(n.Indices) > 0 {
|
2022-10-03 11:22:46 +08:00
|
|
|
cp.errorpf(n, "new variable $%s must not have indices", parse.Quote(qname))
|
2022-11-30 09:38:50 +08:00
|
|
|
return dummyLValuesGroup
|
2021-10-10 21:31:09 +08:00
|
|
|
}
|
|
|
|
segs := SplitQNameSegs(qname)
|
|
|
|
if len(segs) == 1 {
|
|
|
|
// Unqualified name - implicit local
|
2021-10-14 06:57:14 +08:00
|
|
|
name := segs[0]
|
|
|
|
ref = &varRef{localScope,
|
|
|
|
staticVarInfo{name, false, false}, cp.thisScope().add(name), nil}
|
2021-10-10 21:31:09 +08:00
|
|
|
} else {
|
2022-10-03 11:22:46 +08:00
|
|
|
cp.errorpf(n, "cannot create variable $%s; "+
|
|
|
|
"new variables can only be created in the current scope",
|
|
|
|
parse.Quote(qname))
|
2022-11-30 09:38:50 +08:00
|
|
|
return dummyLValuesGroup
|
2021-10-10 21:31:09 +08:00
|
|
|
}
|
2020-12-25 01:39:51 +08:00
|
|
|
}
|
2021-10-10 21:31:09 +08:00
|
|
|
|
2021-08-23 06:31:26 +08:00
|
|
|
ends := make([]int, len(n.Indices)+1)
|
2018-10-13 21:07:54 +08:00
|
|
|
ends[0] = n.Head.Range().To
|
2021-08-23 06:31:26 +08:00
|
|
|
for i, idx := range n.Indices {
|
2018-10-13 21:07:54 +08:00
|
|
|
ends[i+1] = idx.Range().To
|
2018-01-11 08:56:14 +08:00
|
|
|
}
|
2021-08-23 06:31:26 +08:00
|
|
|
lv := lvalue{n.Range(), ref, cp.arrayOps(n.Indices), ends}
|
2020-08-20 20:15:00 +08:00
|
|
|
restIndex := -1
|
|
|
|
if sigil == "@" {
|
|
|
|
restIndex = 0
|
|
|
|
}
|
2021-05-19 09:55:56 +08:00
|
|
|
// TODO: Support % (and other sigils?) if https://b.elv.sh/584 is implemented for map explosion.
|
2020-08-20 20:15:00 +08:00
|
|
|
return lvaluesGroup{[]lvalue{lv}, restIndex}
|
|
|
|
}
|
2018-01-11 08:56:14 +08:00
|
|
|
|
2020-08-20 20:15:00 +08:00
|
|
|
type assignOp struct {
|
2020-08-22 05:28:54 +08:00
|
|
|
diag.Ranging
|
2021-12-10 06:10:31 +08:00
|
|
|
lhs lvaluesGroup
|
|
|
|
rhs valuesOp
|
|
|
|
temp bool
|
2018-01-21 19:47:04 +08:00
|
|
|
}
|
|
|
|
|
2021-01-09 08:24:19 +08:00
|
|
|
func (op *assignOp) exec(fm *Frame) Exception {
|
2020-08-20 20:15:00 +08:00
|
|
|
variables := make([]vars.Var, len(op.lhs.lvalues))
|
|
|
|
for i, lvalue := range op.lhs.lvalues {
|
2020-12-25 01:39:51 +08:00
|
|
|
variable, err := derefLValue(fm, lvalue)
|
2018-01-11 08:56:14 +08:00
|
|
|
if err != nil {
|
2021-12-10 06:10:31 +08:00
|
|
|
return fm.errorp(op.lhs.lvalues[i], err)
|
2018-01-21 19:47:04 +08:00
|
|
|
}
|
2020-08-20 20:15:00 +08:00
|
|
|
variables[i] = variable
|
|
|
|
}
|
|
|
|
|
2021-01-09 08:24:19 +08:00
|
|
|
values, exc := op.rhs.exec(fm)
|
|
|
|
if exc != nil {
|
|
|
|
return exc
|
2018-01-21 19:47:04 +08:00
|
|
|
}
|
|
|
|
|
2021-12-10 06:10:31 +08:00
|
|
|
rest, temp := op.lhs.rest, op.temp
|
|
|
|
if rest == -1 {
|
2020-08-20 20:15:00 +08:00
|
|
|
if len(variables) != len(values) {
|
2021-06-04 10:51:38 +08:00
|
|
|
return fm.errorp(op, errs.ArityMismatch{What: "assignment right-hand-side",
|
2020-08-22 05:28:54 +08:00
|
|
|
ValidLow: len(variables), ValidHigh: len(variables), Actual: len(values)})
|
2020-08-20 20:15:00 +08:00
|
|
|
}
|
|
|
|
for i, variable := range variables {
|
2021-12-10 06:10:31 +08:00
|
|
|
exc := set(fm, op.lhs.lvalues[i], temp, variable, values[i])
|
|
|
|
if exc != nil {
|
|
|
|
return exc
|
2020-08-20 20:15:00 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if len(values) < len(variables)-1 {
|
2021-06-04 10:51:38 +08:00
|
|
|
return fm.errorp(op, errs.ArityMismatch{What: "assignment right-hand-side",
|
2020-08-22 05:28:54 +08:00
|
|
|
ValidLow: len(variables) - 1, ValidHigh: -1, Actual: len(values)})
|
2020-08-20 20:15:00 +08:00
|
|
|
}
|
|
|
|
for i := 0; i < rest; i++ {
|
2021-12-10 06:10:31 +08:00
|
|
|
exc := set(fm, op.lhs.lvalues[i], temp, variables[i], values[i])
|
|
|
|
if exc != nil {
|
|
|
|
return exc
|
2020-08-20 20:15:00 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
restOff := len(values) - len(variables)
|
2021-12-10 06:10:31 +08:00
|
|
|
exc := set(fm, op.lhs.lvalues[rest], temp,
|
|
|
|
variables[rest], vals.MakeList(values[rest:rest+restOff+1]...))
|
|
|
|
if exc != nil {
|
|
|
|
return exc
|
2020-08-20 20:15:00 +08:00
|
|
|
}
|
|
|
|
for i := rest + 1; i < len(variables); i++ {
|
2021-12-10 06:10:31 +08:00
|
|
|
exc := set(fm, op.lhs.lvalues[i], temp, variables[i], values[i+restOff])
|
|
|
|
if exc != nil {
|
|
|
|
return exc
|
2020-08-20 20:15:00 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
2018-01-21 19:47:04 +08:00
|
|
|
}
|
|
|
|
|
2022-03-20 23:50:25 +08:00
|
|
|
func set(fm *Frame, r diag.Ranger, temp bool, variable vars.Var, value any) Exception {
|
2021-12-10 06:10:31 +08:00
|
|
|
if temp {
|
|
|
|
saved := variable.Get()
|
2022-05-11 10:40:13 +08:00
|
|
|
|
2022-06-06 04:47:34 +08:00
|
|
|
needUnset := false
|
|
|
|
unsettable, ok := variable.(vars.UnsettableVar)
|
|
|
|
if ok {
|
|
|
|
needUnset = !unsettable.IsSet()
|
2022-05-11 10:40:13 +08:00
|
|
|
}
|
|
|
|
|
2021-12-10 06:10:31 +08:00
|
|
|
err := variable.Set(value)
|
|
|
|
if err != nil {
|
|
|
|
return fm.errorp(r, err)
|
|
|
|
}
|
|
|
|
fm.addDefer(func(fm *Frame) Exception {
|
2022-06-06 04:47:34 +08:00
|
|
|
if needUnset {
|
|
|
|
if err := unsettable.Unset(); err != nil {
|
|
|
|
return fm.errorpf(r, "unset variable: %w", err)
|
2022-05-11 10:40:13 +08:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-12-10 06:10:31 +08:00
|
|
|
err := variable.Set(saved)
|
|
|
|
if err != nil {
|
|
|
|
return fm.errorpf(r, "restore variable: %w", err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
err := variable.Set(value)
|
|
|
|
if err != nil {
|
|
|
|
return fm.errorp(r, err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-01-10 21:30:07 +08:00
|
|
|
// NoSuchVariable returns an error representing that a variable can't be found.
|
|
|
|
func NoSuchVariable(name string) error {
|
|
|
|
return noSuchVariableError{name}
|
|
|
|
}
|
|
|
|
|
|
|
|
type noSuchVariableError struct{ name string }
|
|
|
|
|
|
|
|
func (er noSuchVariableError) Error() string { return "no variable $" + er.name }
|
|
|
|
|
2020-12-25 01:39:51 +08:00
|
|
|
func derefLValue(fm *Frame, lv lvalue) (vars.Var, error) {
|
|
|
|
variable := deref(fm, lv.ref)
|
2021-01-10 21:30:07 +08:00
|
|
|
if variable == nil {
|
|
|
|
return nil, NoSuchVariable(fm.srcMeta.Code[lv.From:lv.To])
|
|
|
|
}
|
2020-08-20 20:15:00 +08:00
|
|
|
if len(lv.indexOps) == 0 {
|
|
|
|
return variable, nil
|
2018-01-21 19:47:04 +08:00
|
|
|
}
|
2022-03-20 23:50:25 +08:00
|
|
|
indices := make([]any, len(lv.indexOps))
|
2020-08-20 20:15:00 +08:00
|
|
|
for i, op := range lv.indexOps {
|
2021-01-09 08:24:19 +08:00
|
|
|
values, exc := op.exec(fm)
|
|
|
|
if exc != nil {
|
|
|
|
return nil, exc
|
2018-03-01 23:13:00 +08:00
|
|
|
}
|
2018-01-21 19:47:04 +08:00
|
|
|
// TODO: Implement multi-indexing.
|
|
|
|
if len(values) != 1 {
|
|
|
|
return nil, errors.New("multi indexing not implemented")
|
|
|
|
}
|
2020-10-06 02:35:52 +08:00
|
|
|
indices[i] = values[0]
|
2018-01-21 19:47:04 +08:00
|
|
|
}
|
2020-10-06 02:35:52 +08:00
|
|
|
elemVar, err := vars.MakeElement(variable, indices)
|
2018-01-21 19:47:04 +08:00
|
|
|
if err != nil {
|
2018-03-08 21:24:18 +08:00
|
|
|
level := vars.ElementErrorLevel(err)
|
2018-01-21 19:47:04 +08:00
|
|
|
if level < 0 {
|
2020-08-20 20:15:00 +08:00
|
|
|
return nil, fm.errorp(lv, err)
|
2016-02-20 07:48:13 +08:00
|
|
|
}
|
2020-08-20 20:15:00 +08:00
|
|
|
return nil, fm.errorp(diag.Ranging{From: lv.From, To: lv.ends[level]}, err)
|
2016-02-20 07:48:13 +08:00
|
|
|
}
|
2020-08-20 20:15:00 +08:00
|
|
|
return elemVar, nil
|
2016-02-20 07:48:13 +08:00
|
|
|
}
|