mirror of
https://github.com/go-sylixos/elvish.git
synced 2024-12-05 03:17:50 +08:00
a2790af67a
- Make Evaler mostly thread-safe. The only remaining thread-unsafe part is the modules field, which is more tricky than other fields. - Remove the state and evalerScopes type, and move their fields into Evaler. - Expose valuePrefix via a get method, and change PortsFromFiles to take the prefix instead of a *Evaler. Also expose a PortsFromStdFiles. - Make Evaler a normal field of Frame, instead of an embedded field. This makes access to global states more explicit.
292 lines
5.2 KiB
Go
292 lines
5.2 KiB
Go
package eval
|
|
|
|
import (
|
|
"math/rand"
|
|
|
|
"github.com/elves/elvish/pkg/eval/vals"
|
|
)
|
|
|
|
// Numerical operations.
|
|
|
|
//elvdoc:fn rand
|
|
//
|
|
// ```elvish
|
|
// rand
|
|
// ```
|
|
//
|
|
// Output a pseudo-random number in the interval [0, 1). Example:
|
|
//
|
|
// ```elvish-transcript
|
|
// ~> rand
|
|
// ▶ 0.17843564133528436
|
|
// ```
|
|
|
|
func init() {
|
|
addBuiltinFns(map[string]interface{}{
|
|
// Constructor
|
|
"float64": toFloat64,
|
|
|
|
// Comparison
|
|
"<": lt,
|
|
"<=": le,
|
|
"==": eqNum,
|
|
"!=": ne,
|
|
">": gt,
|
|
">=": ge,
|
|
|
|
// Arithmetic
|
|
"+": plus,
|
|
"-": minus,
|
|
"*": times,
|
|
"/": slash,
|
|
"%": mod,
|
|
|
|
// Random
|
|
"rand": rand.Float64,
|
|
"randint": randint,
|
|
})
|
|
}
|
|
|
|
//elvdoc:fn float64
|
|
//
|
|
// ```elvish
|
|
// float64 123
|
|
// float64 NaN
|
|
// float64 Inf
|
|
// ```
|
|
//
|
|
// Explicitly convert a string to a `float64` data type.
|
|
//
|
|
// This command is seldom needed since commands that operate on numbers will
|
|
// convert a string to a number. See the discussion of the
|
|
// [number](language.html#number) data type.
|
|
|
|
func toFloat64(f float64) float64 {
|
|
return f
|
|
}
|
|
|
|
//elvdoc:fn < <= == != > >=
|
|
//
|
|
// ```elvish
|
|
// < $number... # less
|
|
// <= $number... # less or equal
|
|
// == $number... # equal
|
|
// != $number... # not equal
|
|
// > $number... # greater
|
|
// >= $number... # greater or equal
|
|
// ```
|
|
//
|
|
// Number comparisons. All of them accept an arbitrary number of arguments:
|
|
//
|
|
// 1. When given fewer than two arguments, all output `$true`.
|
|
//
|
|
// 2. When given two arguments, output whether the two arguments satisfy the named
|
|
// relationship.
|
|
//
|
|
// 3. When given more than two arguments, output whether every adjacent pair of
|
|
// numbers satisfy the named relationship.
|
|
//
|
|
// Examples:
|
|
//
|
|
// ```elvish-transcript
|
|
// ~> == 3 3.0
|
|
// ▶ $true
|
|
// ~> < 3 4
|
|
// ▶ $true
|
|
// ~> < 3 4 10
|
|
// ▶ $true
|
|
// ~> < 6 9 1
|
|
// ▶ $false
|
|
// ```
|
|
//
|
|
// As a consequence of rule 3, the `!=` command outputs `$true` as long as any
|
|
// _adjacent_ pair of numbers are not equal, even if some numbers that are not
|
|
// adjacent are equal:
|
|
//
|
|
// ```elvish-transcript
|
|
// ~> != 5 5 4
|
|
// ▶ $false
|
|
// ~> != 5 6 5
|
|
// ▶ $true
|
|
// ```
|
|
|
|
func lt(nums ...float64) bool {
|
|
for i := 0; i < len(nums)-1; i++ {
|
|
if !(nums[i] < nums[i+1]) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func le(nums ...float64) bool {
|
|
for i := 0; i < len(nums)-1; i++ {
|
|
if !(nums[i] <= nums[i+1]) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func eqNum(nums ...float64) bool {
|
|
for i := 0; i < len(nums)-1; i++ {
|
|
if !(nums[i] == nums[i+1]) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func ne(nums ...float64) bool {
|
|
for i := 0; i < len(nums)-1; i++ {
|
|
if !(nums[i] != nums[i+1]) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func gt(nums ...float64) bool {
|
|
for i := 0; i < len(nums)-1; i++ {
|
|
if !(nums[i] > nums[i+1]) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func ge(nums ...float64) bool {
|
|
for i := 0; i < len(nums)-1; i++ {
|
|
if !(nums[i] >= nums[i+1]) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
//elvdoc:fn + - * /
|
|
//
|
|
// ```elvish
|
|
// + $summand...
|
|
// - $minuend $subtrahend...
|
|
// * $factor...
|
|
// / $dividend $divisor...
|
|
// ```
|
|
//
|
|
// Basic arithmetic operations of adding, subtraction, multiplication and division
|
|
// respectively.
|
|
//
|
|
// All of them can take multiple arguments:
|
|
//
|
|
// ```elvish-transcript
|
|
// ~> + 2 5 7 # 2 + 5 + 7
|
|
// ▶ 14
|
|
// ~> - 2 5 7 # 2 - 5 - 7
|
|
// ▶ -10
|
|
// ~> * 2 5 7 # 2 * 5 * 7
|
|
// ▶ 70
|
|
// ~> / 2 5 7 # 2 / 5 / 7
|
|
// ▶ 0.05714285714285715
|
|
// ```
|
|
//
|
|
// When given one element, they all output their sole argument (given that it is a
|
|
// valid number). When given no argument,
|
|
//
|
|
// - `+` outputs 0, and `*` outputs 1. You can think that they both have a
|
|
// "hidden" argument of 0 or 1, which does not alter their behaviors (in
|
|
// mathematical terms, 0 and 1 are
|
|
// [identity elements](https://en.wikipedia.org/wiki/Identity_element) of
|
|
// addition and multiplication, respectively).
|
|
//
|
|
// - `-` throws an exception.
|
|
//
|
|
// - `/` becomes a synonym for `cd /`, due to the implicit cd feature. (The
|
|
// implicit cd feature will probably change to avoid this oddity).
|
|
|
|
func plus(nums ...float64) float64 {
|
|
sum := 0.0
|
|
for _, f := range nums {
|
|
sum += f
|
|
}
|
|
return sum
|
|
}
|
|
|
|
func minus(sum float64, nums ...float64) float64 {
|
|
if len(nums) == 0 {
|
|
// Unary -
|
|
return -sum
|
|
}
|
|
for _, f := range nums {
|
|
sum -= f
|
|
}
|
|
return sum
|
|
}
|
|
|
|
func times(nums ...float64) float64 {
|
|
prod := 1.0
|
|
for _, f := range nums {
|
|
prod *= f
|
|
}
|
|
return prod
|
|
}
|
|
|
|
func slash(fm *Frame, args ...float64) error {
|
|
if len(args) == 0 {
|
|
// cd /
|
|
return fm.Evaler.Chdir("/")
|
|
}
|
|
// Division
|
|
divide(fm, args[0], args[1:]...)
|
|
return nil
|
|
}
|
|
|
|
func divide(fm *Frame, prod float64, nums ...float64) {
|
|
out := fm.OutputChan()
|
|
for _, f := range nums {
|
|
prod /= f
|
|
}
|
|
out <- vals.FromGo(prod)
|
|
}
|
|
|
|
//elvdoc:fn %
|
|
//
|
|
// ```elvish
|
|
// % $dividend $divisor
|
|
// ```
|
|
//
|
|
// Output the remainder after dividing `$dividend` by `$divisor`. Both must be
|
|
// integers. Example:
|
|
//
|
|
// ```elvish-transcript
|
|
// ~> % 23 7
|
|
// ▶ 2
|
|
// ```
|
|
|
|
func mod(a, b int) (int, error) {
|
|
if b == 0 {
|
|
return 0, ErrArgs
|
|
}
|
|
return a % b, nil
|
|
}
|
|
|
|
//elvdoc:fn randint
|
|
//
|
|
// ```elvish
|
|
// randint $low $high
|
|
// ```
|
|
//
|
|
// Output a pseudo-random integer in the interval [$low, $high). Example:
|
|
//
|
|
// ```elvish-transcript
|
|
// ~> # Emulate dice
|
|
// randint 1 7
|
|
// ▶ 6
|
|
// ```
|
|
|
|
func randint(low, high int) (int, error) {
|
|
if low >= high {
|
|
return 0, ErrArgs
|
|
}
|
|
return low + rand.Intn(high-low), nil
|
|
}
|