eval: Convert return values in ReflectBuiltinFn.

This commit is contained in:
Qi Xiao 2018-02-03 22:47:08 -08:00
parent 098ca254f7
commit ff71246139
3 changed files with 41 additions and 28 deletions

View File

@ -3,7 +3,6 @@ package eval
import (
"math"
"math/rand"
"strconv"
)
// Numerical operations.
@ -22,11 +21,11 @@ func init() {
"+": plus,
"-": minus,
"*": times,
"^": pow,
"%": mod,
"^": math.Pow,
"%": func(a, b int) int { return a % b },
// Random
"rand": randFn,
"rand": rand.Float64,
"randint": randint,
})
addToBuiltinFns([]*BuiltinFn{
@ -34,31 +33,31 @@ func init() {
})
}
func plus(nums ...float64) string {
func plus(nums ...float64) float64 {
sum := 0.0
for _, f := range nums {
sum += f
}
return floatToString(sum)
return sum
}
func minus(sum float64, nums ...float64) string {
func minus(sum float64, nums ...float64) float64 {
if len(nums) == 0 {
// Unary -
return floatToString(-sum)
return -sum
}
for _, f := range nums {
sum -= f
}
return floatToString(sum)
return sum
}
func times(nums ...float64) string {
func times(nums ...float64) float64 {
prod := 1.0
for _, f := range nums {
prod *= f
}
return floatToString(prod)
return prod
}
func slash(ec *Frame, args []interface{}, opts map[string]interface{}) {
@ -87,21 +86,9 @@ func divide(ec *Frame, args []interface{}, opts map[string]interface{}) {
out <- floatToString(prod)
}
func pow(b, p float64) string {
return floatToString(math.Pow(b, p))
}
func mod(a, b int) string {
return strconv.Itoa(a % b)
}
func randFn() string {
return floatToString(rand.Float64())
}
func randint(low, high int) (string, error) {
func randint(low, high int) (int, error) {
if low >= high {
return "", ErrArgs
return 0, ErrArgs
}
return strconv.Itoa(low + rand.Intn(high-low)), nil
return low + rand.Intn(high-low), nil
}

View File

@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"reflect"
"strconv"
"unsafe"
"github.com/elves/elvish/eval/types"
@ -33,7 +34,8 @@ func addToReflectBuiltinFns(moreFns map[string]interface{}) {
// Return values go to the channel part of the stdout port. However, if the last
// return value has type error and is not nil, it is turned into an exception
// and no ouputting happens. If the last return value is a nil error, it is
// ignored.
// ignored. Return values of type int or float64 are converted to strings with
// strconv.Itoa and strconv.FormatFloat(f, 'g', -1, 64) respectively.
type ReflectBuiltinFn struct {
name string
impl interface{}
@ -157,7 +159,7 @@ func (b *ReflectBuiltinFn) Call(f *Frame, args []interface{}, opts map[string]in
}
for _, out := range outs {
f.OutputChan() <- out.Interface()
f.OutputChan() <- convertRet(out.Interface())
}
return nil
}
@ -188,3 +190,15 @@ func convertArg(arg interface{}, typ reflect.Type) (interface{}, error) {
types.Kind(reflect.Zero(typ).Interface()), types.Kind(arg))
}
}
// convertRet converts the return value.
func convertRet(ret interface{}) interface{} {
switch ret := ret.(type) {
case int:
return strconv.Itoa(ret)
case float64:
return strconv.FormatFloat(ret, 'g', -1, 64)
default:
return ret
}
}

View File

@ -95,6 +95,18 @@ func TestReflectBuiltinFnCall(t *testing.T) {
t.Errorf("Return value is not outputted")
}
// Conversion of return values.
f = NewReflectBuiltinFn("f", func() int { return 314 })
callGood(outFrame, nil, theOptions)
select {
case ret := <-ch:
if ret != "314" {
t.Errorf("Return value is not converted to string")
}
default:
t.Errorf("Return value is not outputted")
}
// Passing of error return value.
theError := errors.New("the error")
f = NewReflectBuiltinFn("f", func() (string, error) {