elvish/pkg/eval/builtin_fn_misc.go

171 lines
3.3 KiB
Go
Raw Normal View History

package eval
2019-04-19 05:57:14 +08:00
// Misc builtin functions.
2013-10-02 22:10:07 +08:00
import (
"errors"
2014-01-16 09:24:14 +08:00
"fmt"
2016-11-06 17:47:20 +08:00
"net"
"sync"
2016-02-14 04:05:35 +08:00
"src.elv.sh/pkg/diag"
2021-12-29 09:16:44 +08:00
"src.elv.sh/pkg/eval/errs"
"src.elv.sh/pkg/eval/vals"
"src.elv.sh/pkg/parse"
2013-10-02 22:10:07 +08:00
)
var (
ErrNegativeSleepDuration = errors.New("sleep duration must be >= zero")
ErrInvalidSleepDuration = errors.New("invalid sleep duration")
)
2017-12-17 13:20:03 +08:00
// Builtins that have not been put into their own groups go here.
func init() {
addBuiltinFns(map[string]any{
"kind-of": kindOf,
"constantly": constantly,
// Introspection
"call": call,
"resolve": resolve,
"eval": eval,
"use-mod": useMod,
2021-01-24 23:32:24 +08:00
"deprecate": deprecate,
"-ifaddrs": _ifaddrs,
})
}
var nopGoFn = NewGoFn("nop", nop)
func nop(opts RawOptions, args ...any) {
// Do nothing
}
func kindOf(fm *Frame, args ...any) error {
out := fm.ValueOutput()
for _, a := range args {
err := out.Put(vals.Kind(a))
if err != nil {
return err
}
}
return nil
}
func constantly(args ...any) Callable {
// TODO(xiaq): Repr of this function is not right.
return NewGoFn(
"created by constantly",
func(fm *Frame) error {
out := fm.ValueOutput()
for _, v := range args {
err := out.Put(v)
if err != nil {
return err
}
}
return nil
},
)
}
2021-12-29 09:16:44 +08:00
func call(fm *Frame, fn Callable, argsVal vals.List, optsVal vals.Map) error {
args := make([]any, 0, argsVal.Len())
2021-12-29 09:16:44 +08:00
for it := argsVal.Iterator(); it.HasElem(); it.Next() {
args = append(args, it.Elem())
}
opts := make(map[string]any, optsVal.Len())
2021-12-29 09:16:44 +08:00
for it := optsVal.Iterator(); it.HasElem(); it.Next() {
k, v := it.Elem()
ks, ok := k.(string)
if !ok {
return errs.BadValue{What: "option key",
Valid: "string", Actual: vals.Kind(k)}
}
opts[ks] = v
}
return fn.Call(fm.Fork("-call"), args, opts)
2021-12-29 09:16:44 +08:00
}
func resolve(fm *Frame, head string) string {
special, fnRef := resolveCmdHeadInternally(fm, head, nil)
switch {
case special != nil:
return "special"
case fnRef != nil:
return "$" + head + FnSuffix
default:
return "(external " + parse.Quote(head) + ")"
}
}
type evalOpts struct {
Ns *Ns
OnEnd Callable
}
func (*evalOpts) SetDefaultOptions() {}
func eval(fm *Frame, opts evalOpts, code string) error {
src := parse.Source{Name: fmt.Sprintf("[eval %d]", nextEvalCount()), Code: code}
ns := opts.Ns
if ns == nil {
ns = CombineNs(fm.up, fm.local)
}
// The stacktrace already contains the line that calls "eval", so we pass
// nil as the second argument.
newNs, exc := fm.Eval(src, nil, ns)
if opts.OnEnd != nil {
newFm := fm.Fork("on-end callback of eval")
errCb := opts.OnEnd.Call(newFm, []any{newNs}, NoOpts)
if exc == nil {
return errCb
}
}
return exc
}
// Used to generate unique names for each source passed to eval.
var (
evalCount int
evalCountMutex sync.Mutex
)
func nextEvalCount() int {
evalCountMutex.Lock()
defer evalCountMutex.Unlock()
evalCount++
return evalCount
}
func useMod(fm *Frame, spec string) (*Ns, error) {
return use(fm, spec, nil)
}
2021-01-24 23:32:24 +08:00
func deprecate(fm *Frame, msg string) {
var ctx *diag.Context
if fm.traceback.Next != nil {
ctx = fm.traceback.Next.Head
}
fm.Deprecate(msg, ctx, 0)
}
2018-02-05 15:35:49 +08:00
func _ifaddrs(fm *Frame) error {
2016-11-06 17:47:20 +08:00
addrs, err := net.InterfaceAddrs()
2018-02-05 15:35:49 +08:00
if err != nil {
return err
}
out := fm.ValueOutput()
2016-11-06 17:47:20 +08:00
for _, addr := range addrs {
err := out.Put(addr.String())
if err != nil {
return err
}
2016-11-06 17:47:20 +08:00
}
2018-02-05 15:35:49 +08:00
return nil
2016-11-06 17:47:20 +08:00
}