builtin_fn_misc.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. package eval
  2. // Misc builtin functions.
  3. import (
  4. "errors"
  5. "fmt"
  6. "math"
  7. "math/big"
  8. "net"
  9. "strconv"
  10. "sync"
  11. "time"
  12. "src.elv.sh/pkg/diag"
  13. "src.elv.sh/pkg/eval/errs"
  14. "src.elv.sh/pkg/eval/vals"
  15. "src.elv.sh/pkg/parse"
  16. )
  17. var (
  18. ErrNegativeSleepDuration = errors.New("sleep duration must be >= zero")
  19. ErrInvalidSleepDuration = errors.New("invalid sleep duration")
  20. )
  21. // Builtins that have not been put into their own groups go here.
  22. func init() {
  23. addBuiltinFns(map[string]any{
  24. "kind-of": kindOf,
  25. "constantly": constantly,
  26. // Introspection
  27. "call": call,
  28. "resolve": resolve,
  29. "eval": eval,
  30. "use-mod": useMod,
  31. "deprecate": deprecate,
  32. // Time
  33. "sleep": sleep,
  34. "time": timeCmd,
  35. "benchmark": benchmark,
  36. "-ifaddrs": _ifaddrs,
  37. })
  38. }
  39. var nopGoFn = NewGoFn("nop", nop)
  40. func nop(opts RawOptions, args ...any) {
  41. // Do nothing
  42. }
  43. func kindOf(fm *Frame, args ...any) error {
  44. out := fm.ValueOutput()
  45. for _, a := range args {
  46. err := out.Put(vals.Kind(a))
  47. if err != nil {
  48. return err
  49. }
  50. }
  51. return nil
  52. }
  53. func constantly(args ...any) Callable {
  54. // TODO(xiaq): Repr of this function is not right.
  55. return NewGoFn(
  56. "created by constantly",
  57. func(fm *Frame) error {
  58. out := fm.ValueOutput()
  59. for _, v := range args {
  60. err := out.Put(v)
  61. if err != nil {
  62. return err
  63. }
  64. }
  65. return nil
  66. },
  67. )
  68. }
  69. func call(fm *Frame, fn Callable, argsVal vals.List, optsVal vals.Map) error {
  70. args := make([]any, 0, argsVal.Len())
  71. for it := argsVal.Iterator(); it.HasElem(); it.Next() {
  72. args = append(args, it.Elem())
  73. }
  74. opts := make(map[string]any, optsVal.Len())
  75. for it := optsVal.Iterator(); it.HasElem(); it.Next() {
  76. k, v := it.Elem()
  77. ks, ok := k.(string)
  78. if !ok {
  79. return errs.BadValue{What: "option key",
  80. Valid: "string", Actual: vals.Kind(k)}
  81. }
  82. opts[ks] = v
  83. }
  84. return fn.Call(fm.Fork("-call"), args, opts)
  85. }
  86. func resolve(fm *Frame, head string) string {
  87. special, fnRef := resolveCmdHeadInternally(fm, head, nil)
  88. switch {
  89. case special != nil:
  90. return "special"
  91. case fnRef != nil:
  92. return "$" + head + FnSuffix
  93. default:
  94. return "(external " + parse.Quote(head) + ")"
  95. }
  96. }
  97. type evalOpts struct {
  98. Ns *Ns
  99. OnEnd Callable
  100. }
  101. func (*evalOpts) SetDefaultOptions() {}
  102. func eval(fm *Frame, opts evalOpts, code string) error {
  103. src := parse.Source{Name: fmt.Sprintf("[eval %d]", nextEvalCount()), Code: code}
  104. ns := opts.Ns
  105. if ns == nil {
  106. ns = CombineNs(fm.up, fm.local)
  107. }
  108. // The stacktrace already contains the line that calls "eval", so we pass
  109. // nil as the second argument.
  110. newNs, exc := fm.Eval(src, nil, ns)
  111. if opts.OnEnd != nil {
  112. newFm := fm.Fork("on-end callback of eval")
  113. errCb := opts.OnEnd.Call(newFm, []any{newNs}, NoOpts)
  114. if exc == nil {
  115. return errCb
  116. }
  117. }
  118. return exc
  119. }
  120. // Used to generate unique names for each source passed to eval.
  121. var (
  122. evalCount int
  123. evalCountMutex sync.Mutex
  124. )
  125. func nextEvalCount() int {
  126. evalCountMutex.Lock()
  127. defer evalCountMutex.Unlock()
  128. evalCount++
  129. return evalCount
  130. }
  131. func useMod(fm *Frame, spec string) (*Ns, error) {
  132. return use(fm, spec, nil)
  133. }
  134. func deprecate(fm *Frame, msg string) {
  135. var ctx *diag.Context
  136. if fm.traceback.Next != nil {
  137. ctx = fm.traceback.Next.Head
  138. }
  139. fm.Deprecate(msg, ctx, 0)
  140. }
  141. // Reference to time.After, can be mutated for testing. Takes an additional
  142. // Frame argument to allow inspection of the value of d in tests.
  143. var timeAfter = func(fm *Frame, d time.Duration) <-chan time.Time { return time.After(d) }
  144. func sleep(fm *Frame, duration any) error {
  145. var f float64
  146. var d time.Duration
  147. if err := vals.ScanToGo(duration, &f); err == nil {
  148. d = time.Duration(f * float64(time.Second))
  149. } else {
  150. // See if it is a duration string rather than a simple number.
  151. switch duration := duration.(type) {
  152. case string:
  153. d, err = time.ParseDuration(duration)
  154. if err != nil {
  155. return ErrInvalidSleepDuration
  156. }
  157. default:
  158. return ErrInvalidSleepDuration
  159. }
  160. }
  161. if d < 0 {
  162. return ErrNegativeSleepDuration
  163. }
  164. select {
  165. case <-fm.Interrupts():
  166. return ErrInterrupted
  167. case <-timeAfter(fm, d):
  168. return nil
  169. }
  170. }
  171. type timeOpt struct{ OnEnd Callable }
  172. func (o *timeOpt) SetDefaultOptions() {}
  173. func timeCmd(fm *Frame, opts timeOpt, f Callable) error {
  174. t0 := time.Now()
  175. err := f.Call(fm, NoArgs, NoOpts)
  176. t1 := time.Now()
  177. dt := t1.Sub(t0)
  178. if opts.OnEnd != nil {
  179. newFm := fm.Fork("on-end callback of time")
  180. errCb := opts.OnEnd.Call(newFm, []any{dt.Seconds()}, NoOpts)
  181. if err == nil {
  182. err = errCb
  183. }
  184. } else {
  185. _, errWrite := fmt.Fprintln(fm.ByteOutput(), dt)
  186. if err == nil {
  187. err = errWrite
  188. }
  189. }
  190. return err
  191. }
  192. type benchmarkOpts struct {
  193. OnEnd Callable
  194. OnRunEnd Callable
  195. MinRuns int
  196. MinTime string
  197. minTime time.Duration
  198. }
  199. func (o *benchmarkOpts) SetDefaultOptions() {
  200. o.MinRuns = 5
  201. o.minTime = time.Second
  202. }
  203. func (opts *benchmarkOpts) parse() error {
  204. if opts.MinRuns < 0 {
  205. return errs.BadValue{What: "min-runs option",
  206. Valid: "non-negative integer", Actual: strconv.Itoa(opts.MinRuns)}
  207. }
  208. if opts.MinTime != "" {
  209. d, err := time.ParseDuration(opts.MinTime)
  210. if err != nil {
  211. return errs.BadValue{What: "min-time option",
  212. Valid: "duration string", Actual: parse.Quote(opts.MinTime)}
  213. }
  214. if d < 0 {
  215. return errs.BadValue{What: "min-time option",
  216. Valid: "non-negative duration", Actual: parse.Quote(opts.MinTime)}
  217. }
  218. opts.minTime = d
  219. }
  220. return nil
  221. }
  222. // TimeNow is a reference to [time.Now] that can be overridden in tests.
  223. var TimeNow = time.Now
  224. func benchmark(fm *Frame, opts benchmarkOpts, f Callable) error {
  225. if err := opts.parse(); err != nil {
  226. return err
  227. }
  228. // Standard deviation is calculated using https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm
  229. var (
  230. min = time.Duration(math.MaxInt64)
  231. max = time.Duration(math.MinInt64)
  232. runs int64
  233. total time.Duration
  234. m2 float64
  235. err error
  236. )
  237. for {
  238. t0 := TimeNow()
  239. err = f.Call(fm, NoArgs, NoOpts)
  240. if err != nil {
  241. break
  242. }
  243. dt := TimeNow().Sub(t0)
  244. if min > dt {
  245. min = dt
  246. }
  247. if max < dt {
  248. max = dt
  249. }
  250. var oldDelta float64
  251. if runs > 0 {
  252. oldDelta = float64(dt) - float64(total)/float64(runs)
  253. }
  254. runs++
  255. total += dt
  256. if runs > 0 {
  257. newDelta := float64(dt) - float64(total)/float64(runs)
  258. m2 += oldDelta * newDelta
  259. }
  260. if opts.OnRunEnd != nil {
  261. newFm := fm.Fork("on-run-end callback of benchmark")
  262. err = opts.OnRunEnd.Call(newFm, []any{dt.Seconds()}, NoOpts)
  263. if err != nil {
  264. break
  265. }
  266. }
  267. if runs >= int64(opts.MinRuns) && total >= opts.minTime {
  268. break
  269. }
  270. }
  271. if runs == 0 {
  272. return err
  273. }
  274. avg := total / time.Duration(runs)
  275. stddev := time.Duration(math.Sqrt(m2 / float64(runs)))
  276. if opts.OnEnd == nil {
  277. _, errOut := fmt.Fprintf(fm.ByteOutput(),
  278. "%v ± %v (min %v, max %v, %d runs)\n", avg, stddev, min, max, runs)
  279. if err == nil {
  280. err = errOut
  281. }
  282. } else {
  283. stats := vals.MakeMap(
  284. "avg", avg.Seconds(), "stddev", stddev.Seconds(),
  285. "min", min.Seconds(), "max", max.Seconds(), "runs", int64ToElv(runs))
  286. newFm := fm.Fork("on-end callback of benchmark")
  287. errOnEnd := opts.OnEnd.Call(newFm, []any{stats}, NoOpts)
  288. if err == nil {
  289. err = errOnEnd
  290. }
  291. }
  292. return err
  293. }
  294. func int64ToElv(i int64) any {
  295. if i <= int64(math.MaxInt) {
  296. return int(i)
  297. } else {
  298. return big.NewInt(i)
  299. }
  300. }
  301. func _ifaddrs(fm *Frame) error {
  302. addrs, err := net.InterfaceAddrs()
  303. if err != nil {
  304. return err
  305. }
  306. out := fm.ValueOutput()
  307. for _, addr := range addrs {
  308. err := out.Put(addr.String())
  309. if err != nil {
  310. return err
  311. }
  312. }
  313. return nil
  314. }