src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/pkg/eval/builtin_fn_container.go (about)

     1  package eval
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"src.elv.sh/pkg/eval/errs"
     8  	"src.elv.sh/pkg/eval/vals"
     9  	"src.elv.sh/pkg/eval/vars"
    10  )
    11  
    12  // Lists and maps.
    13  
    14  func init() {
    15  	addBuiltinFns(map[string]any{
    16  		"ns": nsFn,
    17  
    18  		"make-map": makeMap,
    19  
    20  		"conj":   conj,
    21  		"assoc":  assoc,
    22  		"dissoc": dissoc,
    23  
    24  		"has-key":   hasKey,
    25  		"has-value": hasValue,
    26  
    27  		"keys": keys,
    28  	})
    29  }
    30  
    31  func nsFn(m vals.Map) (*Ns, error) {
    32  	nb := BuildNs()
    33  	for it := m.Iterator(); it.HasElem(); it.Next() {
    34  		k, v := it.Elem()
    35  		kstring, ok := k.(string)
    36  		if !ok {
    37  			return nil, errs.BadValue{
    38  				What:  `key of argument of "ns"`,
    39  				Valid: "string", Actual: vals.Kind(k)}
    40  		}
    41  		nb.AddVar(kstring, vars.FromInit(v))
    42  	}
    43  	return nb.Ns(), nil
    44  }
    45  
    46  func makeMap(input Inputs) (vals.Map, error) {
    47  	m := vals.EmptyMap
    48  	var errMakeMap error
    49  	input(func(v any) {
    50  		if errMakeMap != nil {
    51  			return
    52  		}
    53  		if !vals.CanIterate(v) {
    54  			errMakeMap = errs.BadValue{
    55  				What: "input to make-map", Valid: "iterable", Actual: vals.Kind(v)}
    56  			return
    57  		}
    58  		if l := vals.Len(v); l != 2 {
    59  			errMakeMap = errs.BadValue{
    60  				What: "input to make-map", Valid: "iterable with 2 elements",
    61  				Actual: fmt.Sprintf("%v with %v elements", vals.Kind(v), l)}
    62  			return
    63  		}
    64  		elems, err := vals.Collect(v)
    65  		if err != nil {
    66  			errMakeMap = err
    67  			return
    68  		}
    69  		if len(elems) != 2 {
    70  			errMakeMap = fmt.Errorf("internal bug: collected %v values", len(elems))
    71  			return
    72  		}
    73  		m = m.Assoc(elems[0], elems[1])
    74  	})
    75  	return m, errMakeMap
    76  }
    77  
    78  func conj(li vals.List, more ...any) vals.List {
    79  	for _, val := range more {
    80  		li = li.Conj(val)
    81  	}
    82  	return li
    83  }
    84  
    85  func assoc(a, k, v any) (any, error) {
    86  	return vals.Assoc(a, k, v)
    87  }
    88  
    89  var errCannotDissoc = errors.New("cannot dissoc")
    90  
    91  func dissoc(a, k any) (any, error) {
    92  	a2 := vals.Dissoc(a, k)
    93  	if a2 == nil {
    94  		return nil, errCannotDissoc
    95  	}
    96  	return a2, nil
    97  }
    98  
    99  func hasValue(container, value any) (bool, error) {
   100  	switch container := container.(type) {
   101  	case vals.Map:
   102  		for it := container.Iterator(); it.HasElem(); it.Next() {
   103  			_, v := it.Elem()
   104  			if vals.Equal(v, value) {
   105  				return true, nil
   106  			}
   107  		}
   108  		return false, nil
   109  	default:
   110  		var found bool
   111  		err := vals.Iterate(container, func(v any) bool {
   112  			if vals.Equal(v, value) {
   113  				found = true
   114  				return false
   115  			}
   116  			return true
   117  		})
   118  		return found, err
   119  	}
   120  }
   121  
   122  func hasKey(container, key any) bool {
   123  	return vals.HasKey(container, key)
   124  }
   125  
   126  func keys(fm *Frame, v any) error {
   127  	out := fm.ValueOutput()
   128  	var errPut error
   129  	errIterate := vals.IterateKeys(v, func(k any) bool {
   130  		errPut = out.Put(k)
   131  		return errPut == nil
   132  	})
   133  	if errIterate != nil {
   134  		return errIterate
   135  	}
   136  	return errPut
   137  }