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  }