elvish/eval/purely_eval.go

88 lines
1.9 KiB
Go
Raw Normal View History

package eval
import (
2017-12-18 09:23:28 +08:00
"errors"
"strings"
"github.com/elves/elvish/eval/types"
"github.com/elves/elvish/parse"
"github.com/elves/elvish/util"
)
2017-12-18 09:23:28 +08:00
var ErrImpure = errors.New("expression is impure")
func PurelyEvalCompound(cn *parse.Compound) (string, error) {
return (*Evaler)(nil).PurelyEvalCompound(cn)
}
func (ev *Evaler) PurelyEvalCompound(cn *parse.Compound) (string, error) {
return ev.PurelyEvalPartialCompound(cn, nil)
2017-12-18 09:23:28 +08:00
}
func (ev *Evaler) PurelyEvalPartialCompound(cn *parse.Compound, upto *parse.Indexing) (string, error) {
tilde := false
head := ""
for _, in := range cn.Indexings {
if len(in.Indicies) > 0 {
2017-12-18 09:23:28 +08:00
return "", ErrImpure
}
switch in.Head.Type {
case parse.Tilde:
tilde = true
case parse.Bareword, parse.SingleQuoted, parse.DoubleQuoted:
head += in.Head.Value
case parse.Variable:
if ev == nil {
2017-12-18 09:23:28 +08:00
return "", ErrImpure
}
v := ev.PurelyEvalPrimary(in.Head)
2018-01-02 09:34:09 +08:00
if s, ok := v.(types.String); ok {
head += string(s)
} else {
2017-12-18 09:23:28 +08:00
return "", ErrImpure
}
default:
2017-12-18 09:23:28 +08:00
return "", ErrImpure
}
if in == upto {
break
}
}
if tilde {
i := strings.Index(head, "/")
if i == -1 {
i = len(head)
}
uname := head[:i]
home, err := util.GetHome(uname)
if err != nil {
2017-12-18 09:23:28 +08:00
return "", err
}
head = home + head[i:]
}
2017-12-18 09:23:28 +08:00
return head, nil
}
// PurelyEvalPrimary evaluates a primary node without causing any side effects.
// If this cannot be done, it returns nil.
//
// Currently, only string literals and variables with no @ can be evaluated.
func (ev *Evaler) PurelyEvalPrimary(pn *parse.Primary) types.Value {
switch pn.Type {
case parse.Bareword, parse.SingleQuoted, parse.DoubleQuoted:
2018-01-02 09:34:09 +08:00
return types.String(pn.Value)
case parse.Variable:
explode, ns, name := ParseVariable(pn.Value)
if explode {
return nil
}
ec := NewTopFrame(ev, NewInternalSource("[purely eval]"), nil)
variable := ec.ResolveVar(ns, name)
if variable != nil {
return variable.Get()
}
}
return nil
}