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

     1  package eval
     2  
     3  import (
     4  	"math"
     5  	"math/big"
     6  
     7  	"github.com/markusbkk/elvish/pkg/eval/errs"
     8  	"github.com/markusbkk/elvish/pkg/eval/vals"
     9  )
    10  
    11  // Basic predicate commands.
    12  
    13  //elvdoc:fn bool
    14  //
    15  // ```elvish
    16  // bool $value
    17  // ```
    18  //
    19  // Convert a value to boolean. In Elvish, only `$false` and errors are booleanly
    20  // false. Everything else, including 0, empty strings and empty lists, is booleanly
    21  // true:
    22  //
    23  // ```elvish-transcript
    24  // ~> bool $true
    25  // ▶ $true
    26  // ~> bool $false
    27  // ▶ $false
    28  // ~> bool $ok
    29  // ▶ $true
    30  // ~> bool ?(fail haha)
    31  // ▶ $false
    32  // ~> bool ''
    33  // ▶ $true
    34  // ~> bool []
    35  // ▶ $true
    36  // ~> bool abc
    37  // ▶ $true
    38  // ```
    39  //
    40  // @cf not
    41  
    42  func init() {
    43  	addBuiltinFns(map[string]interface{}{
    44  		"bool":    vals.Bool,
    45  		"not":     not,
    46  		"is":      is,
    47  		"eq":      eq,
    48  		"not-eq":  notEq,
    49  		"compare": compare,
    50  	})
    51  }
    52  
    53  //elvdoc:fn not
    54  //
    55  // ```elvish
    56  // not $value
    57  // ```
    58  //
    59  // Boolean negation. Examples:
    60  //
    61  // ```elvish-transcript
    62  // ~> not $true
    63  // ▶ $false
    64  // ~> not $false
    65  // ▶ $true
    66  // ~> not $ok
    67  // ▶ $false
    68  // ~> not ?(fail error)
    69  // ▶ $true
    70  // ```
    71  //
    72  // **Note**: The related logical commands `and` and `or` are implemented as
    73  // [special commands](language.html#special-commands) instead, since they do not
    74  // always evaluate all their arguments. The `not` command always evaluates its
    75  // only argument, and is thus a normal command.
    76  //
    77  // @cf bool
    78  
    79  func not(v interface{}) bool {
    80  	return !vals.Bool(v)
    81  }
    82  
    83  //elvdoc:fn is
    84  //
    85  // ```elvish
    86  // is $values...
    87  // ```
    88  //
    89  // Determine whether all `$value`s have the same identity. Writes `$true` when
    90  // given no or one argument.
    91  //
    92  // The definition of identity is subject to change. Do not rely on its behavior.
    93  //
    94  // ```elvish-transcript
    95  // ~> is a a
    96  // ▶ $true
    97  // ~> is a b
    98  // ▶ $false
    99  // ~> is [] []
   100  // ▶ $true
   101  // ~> is [a] [a]
   102  // ▶ $false
   103  // ```
   104  //
   105  // @cf eq
   106  //
   107  // Etymology: [Python](https://docs.python.org/3/reference/expressions.html#is).
   108  
   109  func is(args ...interface{}) bool {
   110  	for i := 0; i+1 < len(args); i++ {
   111  		if args[i] != args[i+1] {
   112  			return false
   113  		}
   114  	}
   115  	return true
   116  }
   117  
   118  //elvdoc:fn eq
   119  //
   120  // ```elvish
   121  // eq $values...
   122  // ```
   123  //
   124  // Determines whether all `$value`s are equal. Writes `$true` when
   125  // given no or one argument.
   126  //
   127  // Two values are equal when they have the same type and value.
   128  //
   129  // For complex data structures like lists and maps, comparison is done
   130  // recursively. A pseudo-map is equal to another pseudo-map with the same
   131  // internal type (which is not exposed to Elvish code now) and value.
   132  //
   133  // ```elvish-transcript
   134  // ~> eq a a
   135  // ▶ $true
   136  // ~> eq [a] [a]
   137  // ▶ $true
   138  // ~> eq [&k=v] [&k=v]
   139  // ▶ $true
   140  // ~> eq a [b]
   141  // ▶ $false
   142  // ```
   143  //
   144  // @cf is not-eq
   145  //
   146  // Etymology: [Perl](https://perldoc.perl.org/perlop.html#Equality-Operators).
   147  
   148  func eq(args ...interface{}) bool {
   149  	for i := 0; i+1 < len(args); i++ {
   150  		if !vals.Equal(args[i], args[i+1]) {
   151  			return false
   152  		}
   153  	}
   154  	return true
   155  }
   156  
   157  //elvdoc:fn not-eq
   158  //
   159  // ```elvish
   160  // not-eq $values...
   161  // ```
   162  //
   163  // Determines whether every adjacent pair of `$value`s are not equal. Note that
   164  // this does not imply that `$value`s are all distinct. Examples:
   165  //
   166  // ```elvish-transcript
   167  // ~> not-eq 1 2 3
   168  // ▶ $true
   169  // ~> not-eq 1 2 1
   170  // ▶ $true
   171  // ~> not-eq 1 1 2
   172  // ▶ $false
   173  // ```
   174  //
   175  // @cf eq
   176  
   177  func notEq(args ...interface{}) bool {
   178  	for i := 0; i+1 < len(args); i++ {
   179  		if vals.Equal(args[i], args[i+1]) {
   180  			return false
   181  		}
   182  	}
   183  	return true
   184  }
   185  
   186  //elvdoc:fn compare
   187  //
   188  // ```elvish
   189  // compare $a $b
   190  // ```
   191  //
   192  // Outputs -1 if `$a` < `$b`, 0 if `$a` = `$b`, and 1 if `$a` > `$b`.
   193  //
   194  // The following comparison algorithm is used:
   195  //
   196  // - Typed numbers are compared numerically. The comparison is consistent with
   197  //   the [number comparison commands](#num-cmp), except that `NaN` values are
   198  //   considered equal to each other and smaller than all other numbers.
   199  //
   200  // - Strings are compared lexicographically by bytes, consistent with the
   201  //   [string comparison commands](#str-cmp). For UTF-8 encoded strings, this is
   202  //   equivalent to comparing by codepoints.
   203  //
   204  // - Lists are compared lexicographically by elements, if the elements at the
   205  //   same positions are comparable.
   206  //
   207  // If the ordering between two elements is not defined by the conditions above,
   208  // i.e. if the value of `$a` or `$b` is not covered by any of the cases above or
   209  // if they belong to different cases, a "bad value" exception is thrown.
   210  //
   211  // Examples:
   212  //
   213  // ```elvish-transcript
   214  // ~> compare a b
   215  // ▶ (num 1)
   216  // ~> compare b a
   217  // ▶ (num -1)
   218  // ~> compare x x
   219  // ▶ (num 0)
   220  // ~> compare (float64 10) (float64 1)
   221  // ▶ (num 1)
   222  // ```
   223  //
   224  // Beware that strings that look like numbers are treated as strings, not
   225  // numbers.
   226  //
   227  // @cf order
   228  
   229  // ErrUncomparable is raised by the compare and order commands when inputs contain
   230  // uncomparable values.
   231  var ErrUncomparable = errs.BadValue{
   232  	What:  `inputs to "compare" or "order"`,
   233  	Valid: "comparable values", Actual: "uncomparable values"}
   234  
   235  func compare(fm *Frame, a, b interface{}) (int, error) {
   236  	switch cmp(a, b) {
   237  	case less:
   238  		return -1, nil
   239  	case equal:
   240  		return 0, nil
   241  	case more:
   242  		return 1, nil
   243  	default:
   244  		return 0, ErrUncomparable
   245  	}
   246  }
   247  
   248  type ordering uint8
   249  
   250  const (
   251  	less ordering = iota
   252  	equal
   253  	more
   254  	uncomparable
   255  )
   256  
   257  func cmp(a, b interface{}) ordering {
   258  	switch a := a.(type) {
   259  	case int, *big.Int, *big.Rat, float64:
   260  		switch b.(type) {
   261  		case int, *big.Int, *big.Rat, float64:
   262  			a, b := vals.UnifyNums2(a, b, 0)
   263  			switch a := a.(type) {
   264  			case int:
   265  				return compareInt(a, b.(int))
   266  			case *big.Int:
   267  				return compareInt(a.Cmp(b.(*big.Int)), 0)
   268  			case *big.Rat:
   269  				return compareInt(a.Cmp(b.(*big.Rat)), 0)
   270  			case float64:
   271  				return compareFloat(a, b.(float64))
   272  			default:
   273  				panic("unreachable")
   274  			}
   275  		}
   276  	case string:
   277  		if b, ok := b.(string); ok {
   278  			switch {
   279  			case a == b:
   280  				return equal
   281  			case a < b:
   282  				return less
   283  			default: // a > b
   284  				return more
   285  			}
   286  		}
   287  	case vals.List:
   288  		if b, ok := b.(vals.List); ok {
   289  			aIt := a.Iterator()
   290  			bIt := b.Iterator()
   291  			for aIt.HasElem() && bIt.HasElem() {
   292  				o := cmp(aIt.Elem(), bIt.Elem())
   293  				if o != equal {
   294  					return o
   295  				}
   296  				aIt.Next()
   297  				bIt.Next()
   298  			}
   299  			switch {
   300  			case a.Len() == b.Len():
   301  				return equal
   302  			case a.Len() < b.Len():
   303  				return less
   304  			default: // a.Len() > b.Len()
   305  				return more
   306  			}
   307  		}
   308  	}
   309  	return uncomparable
   310  }
   311  
   312  func compareInt(a, b int) ordering {
   313  	if a < b {
   314  		return less
   315  	} else if a > b {
   316  		return more
   317  	}
   318  	return equal
   319  }
   320  
   321  func compareFloat(a, b float64) ordering {
   322  	// For the sake of ordering, NaN's are considered equal to each
   323  	// other and smaller than all numbers
   324  	switch {
   325  	case math.IsNaN(a):
   326  		if math.IsNaN(b) {
   327  			return equal
   328  		}
   329  		return less
   330  	case math.IsNaN(b):
   331  		return more
   332  	case a < b:
   333  		return less
   334  	case a > b:
   335  		return more
   336  	default: // a == b
   337  		return equal
   338  	}
   339  }