elvish/pkg/eval/resolve.go

130 lines
2.5 KiB
Go
Raw Normal View History

2018-01-02 09:34:09 +08:00
package eval
2018-02-01 03:29:35 +08:00
import (
"os"
"strings"
2019-12-24 04:00:59 +08:00
"github.com/elves/elvish/pkg/eval/vars"
2018-02-01 03:29:35 +08:00
)
// Resolution and iteration of variables and namespaces.
// EachVariableInTop calls the passed function for each variable name in
// namespace ns that can be found from the top context.
func (ev *evalerScopes) EachVariableInTop(ns string, f func(s string)) {
switch ns {
case "builtin:":
2018-02-01 03:29:35 +08:00
for name := range ev.Builtin {
f(name)
}
case "", ":":
2018-02-01 03:29:35 +08:00
for name := range ev.Global {
f(name)
}
for name := range ev.Builtin {
f(name)
}
case "e:":
2018-02-01 03:29:35 +08:00
EachExternal(func(cmd string) {
f(cmd + FnSuffix)
})
case "E:":
2018-02-01 03:29:35 +08:00
for _, s := range os.Environ() {
if i := strings.IndexByte(s, '='); i > 0 {
f(s[:i])
2018-01-02 09:34:09 +08:00
}
}
2018-02-01 03:29:35 +08:00
default:
segs := SplitQNameNsSegs(ns)
2018-02-07 13:34:10 +08:00
mod := ev.Global[segs[0]]
2018-02-01 03:29:35 +08:00
if mod == nil {
2018-02-07 13:34:10 +08:00
mod = ev.Builtin[segs[0]]
}
for _, seg := range segs[1:] {
if mod == nil {
return
}
mod = mod.Get().(Ns)[seg]
2018-02-01 03:29:35 +08:00
}
if mod != nil {
for name := range mod.Get().(Ns) {
f(name)
}
}
}
}
// EachNsInTop calls the passed function for each namespace that can be used
// from the top context.
func (ev *evalerScopes) EachNsInTop(f func(s string)) {
f("builtin:")
f("e:")
f("E:")
2018-02-01 03:29:35 +08:00
for name := range ev.Global {
if strings.HasSuffix(name, NsSuffix) {
f(name)
2018-02-01 03:29:35 +08:00
}
2018-01-02 09:34:09 +08:00
}
2018-02-01 03:29:35 +08:00
for name := range ev.Builtin {
if strings.HasSuffix(name, NsSuffix) {
f(name)
2018-02-01 03:29:35 +08:00
}
}
}
2018-01-02 09:34:09 +08:00
2018-02-01 03:29:35 +08:00
// ResolveVar resolves a variable. When the variable cannot be found, nil is
// returned.
func (fm *Frame) ResolveVar(qname string) vars.Var {
ns, name := SplitQNameNsFirst(qname)
switch ns {
case "E:":
return vars.FromEnv(name)
case "e:":
if strings.HasSuffix(name, FnSuffix) {
return vars.NewReadOnly(ExternalCmd{name[:len(name)-len(FnSuffix)]})
2018-02-01 03:29:35 +08:00
}
return nil
case "local:":
return resolveNested(fm.local, name)
case "up:":
return resolveNested(fm.up, name)
case "builtin:":
return resolveNested(fm.Builtin, name)
case "", ":":
return fm.resolveNonPseudo(name)
2018-02-01 03:29:35 +08:00
default:
return fm.resolveNonPseudo(qname)
}
2018-02-01 03:29:35 +08:00
}
func (fm *Frame) resolveNonPseudo(name string) vars.Var {
if v := resolveNested(fm.local, name); v != nil {
return v
}
if v := resolveNested(fm.up, name); v != nil {
return v
}
return resolveNested(fm.Builtin, name)
}
func resolveNested(ns Ns, name string) vars.Var {
if name == "" {
return nil
}
segs := SplitQNameNsSegs(name)
for _, seg := range segs[:len(segs)-1] {
variable := ns[seg]
if variable == nil {
return nil
}
nestedNs, ok := variable.Get().(Ns)
if !ok {
return nil
}
ns = nestedNs
2018-02-01 03:29:35 +08:00
}
return ns[segs[len(segs)-1]]
2018-01-02 09:34:09 +08:00
}