package eval // Misc builtin functions. import ( "errors" "fmt" "net" "sync" "src.elv.sh/pkg/diag" "src.elv.sh/pkg/eval/errs" "src.elv.sh/pkg/eval/vals" "src.elv.sh/pkg/parse" ) var ( ErrNegativeSleepDuration = errors.New("sleep duration must be >= zero") ErrInvalidSleepDuration = errors.New("invalid sleep duration") ) // 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, "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 }, ) } func call(fm *Frame, fn Callable, argsVal vals.List, optsVal vals.Map) error { args := make([]any, 0, argsVal.Len()) for it := argsVal.Iterator(); it.HasElem(); it.Next() { args = append(args, it.Elem()) } opts := make(map[string]any, optsVal.Len()) 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) } 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) } 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) } func _ifaddrs(fm *Frame) error { addrs, err := net.InterfaceAddrs() if err != nil { return err } out := fm.ValueOutput() for _, addr := range addrs { err := out.Put(addr.String()) if err != nil { return err } } return nil }