elvish/pkg/eval/vals/string.go

55 lines
1.5 KiB
Go

package vals
import (
"math"
"strconv"
"strings"
)
// Stringer wraps the String method.
type Stringer interface {
// Stringer converts the receiver to a string.
String() string
}
// ToString converts a Value to string. It is implemented for the builtin
// float64 and string types, and type satisfying the Stringer interface. It
// falls back to Repr(v).
func ToString(v any) string {
switch v := v.(type) {
case int:
return strconv.Itoa(v)
case float64:
return formatFloat64(v)
// Other number types handled by "case Stringer"
case string:
return v
case Stringer:
return v.String()
default:
return ReprPlain(v)
}
}
func formatFloat64(f float64) string {
// Go's 'g' format is not quite ideal for printing floating point numbers;
// it uses scientific notation too aggressively, and relatively small
// numbers like 1234567 are printed with scientific notations, something we
// don't really want.
//
// So we use a different algorithm for determining when to use scientific
// notation. The algorithm is reverse-engineered from Racket's; it may not
// be a perfect clone but hopefully good enough.
//
// See also b.elv.sh/811 for more context.
s := strconv.FormatFloat(f, 'f', -1, 64)
noPoint := !strings.ContainsRune(s, '.')
if (noPoint && len(s) > 14 && s[len(s)-1] == '0') ||
strings.HasPrefix(s, "0.0000") {
return strconv.FormatFloat(f, 'e', -1, 64)
} else if noPoint && !math.IsNaN(f) && !math.IsInf(f, 0) {
return s + ".0"
}
return s
}