github.com/markusbkk/elvish@v0.0.0-20231204143114-91dc52438621/pkg/eval/builtin_fn_container.go (about)

     1  package eval
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/markusbkk/elvish/pkg/eval/errs"
     8  	"github.com/markusbkk/elvish/pkg/eval/vals"
     9  	"github.com/markusbkk/elvish/pkg/eval/vars"
    10  )
    11  
    12  // Lists and maps.
    13  
    14  func init() {
    15  	addBuiltinFns(map[string]interface{}{
    16  		"ns": nsFn,
    17  
    18  		"make-map": makeMap,
    19  
    20  		"assoc":  assoc,
    21  		"dissoc": dissoc,
    22  
    23  		"has-key":   hasKey,
    24  		"has-value": hasValue,
    25  
    26  		"keys": keys,
    27  	})
    28  }
    29  
    30  //elvdoc:fn ns
    31  //
    32  // ```elvish
    33  // ns $map
    34  // ```
    35  //
    36  // Constructs a namespace from `$map`, using the keys as variable names and the
    37  // values as their values. Examples:
    38  //
    39  // ```elvish-transcript
    40  // ~> var n = (ns [&name=value])
    41  // ~> put $n[name]
    42  // ▶ value
    43  // ~> var n: = (ns [&name=value])
    44  // ~> put $n:name
    45  // ▶ value
    46  // ```
    47  
    48  func nsFn(m vals.Map) (*Ns, error) {
    49  	nb := BuildNs()
    50  	for it := m.Iterator(); it.HasElem(); it.Next() {
    51  		k, v := it.Elem()
    52  		kstring, ok := k.(string)
    53  		if !ok {
    54  			return nil, errs.BadValue{
    55  				What:  `key of argument of "ns"`,
    56  				Valid: "string", Actual: vals.Kind(k)}
    57  		}
    58  		nb.AddVar(kstring, vars.FromInit(v))
    59  	}
    60  	return nb.Ns(), nil
    61  }
    62  
    63  //elvdoc:fn make-map
    64  //
    65  // ```elvish
    66  // make-map $input?
    67  // ```
    68  //
    69  // Outputs a map from the [value inputs](#value-inputs), each of which must be
    70  // an iterable value with with two elements. The first element of each value
    71  // is used as the key, and the second element is used as the value.
    72  //
    73  // If the same key appears multiple times, the last value is used.
    74  //
    75  // Examples:
    76  //
    77  // ```elvish-transcript
    78  // ~> make-map [[k v]]
    79  // ▶ [&k=v]
    80  // ~> make-map [[k v1] [k v2]]
    81  // ▶ [&k=v2]
    82  // ~> put [k1 v1] [k2 v2] | make-map
    83  // ▶ [&k1=v1 &k2=v2]
    84  // ~> put aA bB | make-map
    85  // ▶ [&a=A &b=B]
    86  // ```
    87  
    88  func makeMap(input Inputs) (vals.Map, error) {
    89  	m := vals.EmptyMap
    90  	var errMakeMap error
    91  	input(func(v interface{}) {
    92  		if errMakeMap != nil {
    93  			return
    94  		}
    95  		if !vals.CanIterate(v) {
    96  			errMakeMap = errs.BadValue{
    97  				What: "input to make-map", Valid: "iterable", Actual: vals.Kind(v)}
    98  			return
    99  		}
   100  		if l := vals.Len(v); l != 2 {
   101  			errMakeMap = errs.BadValue{
   102  				What: "input to make-map", Valid: "iterable with 2 elements",
   103  				Actual: fmt.Sprintf("%v with %v elements", vals.Kind(v), l)}
   104  			return
   105  		}
   106  		elems, err := vals.Collect(v)
   107  		if err != nil {
   108  			errMakeMap = err
   109  			return
   110  		}
   111  		if len(elems) != 2 {
   112  			errMakeMap = fmt.Errorf("internal bug: collected %v values", len(elems))
   113  			return
   114  		}
   115  		m = m.Assoc(elems[0], elems[1])
   116  	})
   117  	return m, errMakeMap
   118  }
   119  
   120  //elvdoc:fn assoc
   121  //
   122  // ```elvish
   123  // assoc $container $k $v
   124  // ```
   125  //
   126  // Output a slightly modified version of `$container`, such that its value at `$k`
   127  // is `$v`. Applies to both lists and to maps.
   128  //
   129  // When `$container` is a list, `$k` may be a negative index. However, slice is not
   130  // yet supported.
   131  //
   132  // ```elvish-transcript
   133  // ~> assoc [foo bar quux] 0 lorem
   134  // ▶ [lorem bar quux]
   135  // ~> assoc [foo bar quux] -1 ipsum
   136  // ▶ [foo bar ipsum]
   137  // ~> assoc [&k=v] k v2
   138  // ▶ [&k=v2]
   139  // ~> assoc [&k=v] k2 v2
   140  // ▶ [&k2=v2 &k=v]
   141  // ```
   142  //
   143  // Etymology: [Clojure](https://clojuredocs.org/clojure.core/assoc).
   144  //
   145  // @cf dissoc
   146  
   147  func assoc(a, k, v interface{}) (interface{}, error) {
   148  	return vals.Assoc(a, k, v)
   149  }
   150  
   151  var errCannotDissoc = errors.New("cannot dissoc")
   152  
   153  //elvdoc:fn dissoc
   154  //
   155  // ```elvish
   156  // dissoc $map $k
   157  // ```
   158  //
   159  // Output a slightly modified version of `$map`, with the key `$k` removed. If
   160  // `$map` does not contain `$k` as a key, the same map is returned.
   161  //
   162  // ```elvish-transcript
   163  // ~> dissoc [&foo=bar &lorem=ipsum] foo
   164  // ▶ [&lorem=ipsum]
   165  // ~> dissoc [&foo=bar &lorem=ipsum] k
   166  // ▶ [&lorem=ipsum &foo=bar]
   167  // ```
   168  //
   169  // @cf assoc
   170  
   171  func dissoc(a, k interface{}) (interface{}, error) {
   172  	a2 := vals.Dissoc(a, k)
   173  	if a2 == nil {
   174  		return nil, errCannotDissoc
   175  	}
   176  	return a2, nil
   177  }
   178  
   179  //elvdoc:fn has-value
   180  //
   181  // ```elvish
   182  // has-value $container $value
   183  // ```
   184  //
   185  // Determine whether `$value` is a value in `$container`.
   186  //
   187  // Examples, maps:
   188  //
   189  // ```elvish-transcript
   190  // ~> has-value [&k1=v1 &k2=v2] v1
   191  // ▶ $true
   192  // ~> has-value [&k1=v1 &k2=v2] k1
   193  // ▶ $false
   194  // ```
   195  //
   196  // Examples, lists:
   197  //
   198  // ```elvish-transcript
   199  // ~> has-value [v1 v2] v1
   200  // ▶ $true
   201  // ~> has-value [v1 v2] k1
   202  // ▶ $false
   203  // ```
   204  //
   205  // Examples, strings:
   206  //
   207  // ```elvish-transcript
   208  // ~> has-value ab b
   209  // ▶ $true
   210  // ~> has-value ab c
   211  // ▶ $false
   212  // ```
   213  
   214  func hasValue(container, value interface{}) (bool, error) {
   215  	switch container := container.(type) {
   216  	case vals.Map:
   217  		for it := container.Iterator(); it.HasElem(); it.Next() {
   218  			_, v := it.Elem()
   219  			if vals.Equal(v, value) {
   220  				return true, nil
   221  			}
   222  		}
   223  		return false, nil
   224  	default:
   225  		var found bool
   226  		err := vals.Iterate(container, func(v interface{}) bool {
   227  			found = (v == value)
   228  			return !found
   229  		})
   230  		return found, err
   231  	}
   232  }
   233  
   234  //elvdoc:fn has-key
   235  //
   236  // ```elvish
   237  // has-key $container $key
   238  // ```
   239  //
   240  // Determine whether `$key` is a key in `$container`. A key could be a map key or
   241  // an index on a list or string. This includes a range of indexes.
   242  //
   243  // Examples, maps:
   244  //
   245  // ```elvish-transcript
   246  // ~> has-key [&k1=v1 &k2=v2] k1
   247  // ▶ $true
   248  // ~> has-key [&k1=v1 &k2=v2] v1
   249  // ▶ $false
   250  // ```
   251  //
   252  // Examples, lists:
   253  //
   254  // ```elvish-transcript
   255  // ~> has-key [v1 v2] 0
   256  // ▶ $true
   257  // ~> has-key [v1 v2] 1
   258  // ▶ $true
   259  // ~> has-key [v1 v2] 2
   260  // ▶ $false
   261  // ~> has-key [v1 v2] 0:2
   262  // ▶ $true
   263  // ~> has-key [v1 v2] 0:3
   264  // ▶ $false
   265  // ```
   266  //
   267  // Examples, strings:
   268  //
   269  // ```elvish-transcript
   270  // ~> has-key ab 0
   271  // ▶ $true
   272  // ~> has-key ab 1
   273  // ▶ $true
   274  // ~> has-key ab 2
   275  // ▶ $false
   276  // ~> has-key ab 0:2
   277  // ▶ $true
   278  // ~> has-key ab 0:3
   279  // ▶ $false
   280  // ```
   281  
   282  func hasKey(container, key interface{}) bool {
   283  	return vals.HasKey(container, key)
   284  }
   285  
   286  //elvdoc:fn keys
   287  //
   288  // ```elvish
   289  // keys $map
   290  // ```
   291  //
   292  // Put all keys of `$map` on the structured stdout.
   293  //
   294  // Example:
   295  //
   296  // ```elvish-transcript
   297  // ~> keys [&a=foo &b=bar &c=baz]
   298  // ▶ a
   299  // ▶ c
   300  // ▶ b
   301  // ```
   302  //
   303  // Note that there is no guaranteed order for the keys of a map.
   304  
   305  func keys(fm *Frame, v interface{}) error {
   306  	out := fm.ValueOutput()
   307  	var errPut error
   308  	errIterate := vals.IterateKeys(v, func(k interface{}) bool {
   309  		errPut = out.Put(k)
   310  		return errPut == nil
   311  	})
   312  	if errIterate != nil {
   313  		return errIterate
   314  	}
   315  	return errPut
   316  }