src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/pkg/eval/builtin_fn_misc.go (about) 1 package eval 2 3 // Misc builtin functions. 4 5 import ( 6 "errors" 7 "fmt" 8 "net" 9 "sync" 10 11 "src.elv.sh/pkg/diag" 12 "src.elv.sh/pkg/eval/errs" 13 "src.elv.sh/pkg/eval/vals" 14 "src.elv.sh/pkg/parse" 15 ) 16 17 var ( 18 ErrNegativeSleepDuration = errors.New("sleep duration must be >= zero") 19 ErrInvalidSleepDuration = errors.New("invalid sleep duration") 20 ) 21 22 // Builtins that have not been put into their own groups go here. 23 24 func init() { 25 addBuiltinFns(map[string]any{ 26 "kind-of": kindOf, 27 "constantly": constantly, 28 29 // Introspection 30 "call": call, 31 "resolve": resolve, 32 "eval": eval, 33 "use-mod": useMod, 34 35 "deprecate": deprecate, 36 37 "-ifaddrs": _ifaddrs, 38 }) 39 40 } 41 42 var nopGoFn = NewGoFn("nop", nop) 43 44 func nop(opts RawOptions, args ...any) { 45 // Do nothing 46 } 47 48 func kindOf(fm *Frame, args ...any) error { 49 out := fm.ValueOutput() 50 for _, a := range args { 51 err := out.Put(vals.Kind(a)) 52 if err != nil { 53 return err 54 } 55 } 56 return nil 57 } 58 59 func constantly(args ...any) Callable { 60 // TODO(xiaq): Repr of this function is not right. 61 return NewGoFn( 62 "created by constantly", 63 func(fm *Frame) error { 64 out := fm.ValueOutput() 65 for _, v := range args { 66 err := out.Put(v) 67 if err != nil { 68 return err 69 } 70 } 71 return nil 72 }, 73 ) 74 } 75 76 func call(fm *Frame, fn Callable, argsVal vals.List, optsVal vals.Map) error { 77 args := make([]any, 0, argsVal.Len()) 78 for it := argsVal.Iterator(); it.HasElem(); it.Next() { 79 args = append(args, it.Elem()) 80 } 81 opts := make(map[string]any, optsVal.Len()) 82 for it := optsVal.Iterator(); it.HasElem(); it.Next() { 83 k, v := it.Elem() 84 ks, ok := k.(string) 85 if !ok { 86 return errs.BadValue{What: "option key", 87 Valid: "string", Actual: vals.Kind(k)} 88 } 89 opts[ks] = v 90 } 91 return fn.Call(fm.Fork("-call"), args, opts) 92 } 93 94 func resolve(fm *Frame, head string) string { 95 special, fnRef := resolveCmdHeadInternally(fm, head, nil) 96 switch { 97 case special != nil: 98 return "special" 99 case fnRef != nil: 100 return "$" + head + FnSuffix 101 default: 102 return "(external " + parse.Quote(head) + ")" 103 } 104 } 105 106 type evalOpts struct { 107 Ns *Ns 108 OnEnd Callable 109 } 110 111 func (*evalOpts) SetDefaultOptions() {} 112 113 func eval(fm *Frame, opts evalOpts, code string) error { 114 src := parse.Source{Name: fmt.Sprintf("[eval %d]", nextEvalCount()), Code: code} 115 ns := opts.Ns 116 if ns == nil { 117 ns = CombineNs(fm.up, fm.local) 118 } 119 // The stacktrace already contains the line that calls "eval", so we pass 120 // nil as the second argument. 121 newNs, exc := fm.Eval(src, nil, ns) 122 if opts.OnEnd != nil { 123 newFm := fm.Fork("on-end callback of eval") 124 errCb := opts.OnEnd.Call(newFm, []any{newNs}, NoOpts) 125 if exc == nil { 126 return errCb 127 } 128 } 129 return exc 130 } 131 132 // Used to generate unique names for each source passed to eval. 133 var ( 134 evalCount int 135 evalCountMutex sync.Mutex 136 nextEvalCount = nextEvalCountImpl 137 ) 138 139 func nextEvalCountImpl() int { 140 evalCountMutex.Lock() 141 defer evalCountMutex.Unlock() 142 evalCount++ 143 return evalCount 144 } 145 146 func useMod(fm *Frame, spec string) (*Ns, error) { 147 return use(fm, spec, nil) 148 } 149 150 func deprecate(fm *Frame, msg string) { 151 var ctx *diag.Context 152 if fm.traceback.Next != nil { 153 ctx = fm.traceback.Next.Head 154 } 155 fm.Deprecate(msg, ctx, 0) 156 } 157 158 func _ifaddrs(fm *Frame) error { 159 addrs, err := net.InterfaceAddrs() 160 if err != nil { 161 return err 162 } 163 out := fm.ValueOutput() 164 for _, addr := range addrs { 165 err := out.Put(addr.String()) 166 if err != nil { 167 return err 168 } 169 } 170 return nil 171 }