- Use standard error types.

- Support float64 that stores an integer.

- Don't incur unnecessary overhead for int arguments.
This commit is contained in:
Qi Xiao 2024-01-30 20:28:01 +00:00
parent 183927cffc
commit 27495be1b9
2 changed files with 46 additions and 24 deletions

View File

@ -2,10 +2,14 @@ package eval
import (
"errors"
"fmt"
"math"
"math/big"
"regexp"
"strconv"
"strings"
"src.elv.sh/pkg/eval/errs"
"src.elv.sh/pkg/eval/vals"
"src.elv.sh/pkg/wcwidth"
)
@ -45,31 +49,39 @@ func toString(fm *Frame, args ...any) error {
return nil
}
// ErrBadBase is thrown by the "base" builtin if the base is smaller than 2 or
// greater than 36.
var ErrBadBase = errors.New("bad base")
// ErrInvalidInput is thrown by the "base" builtin if the input num is invalid
var ErrInvalidInput = errors.New("invalid input")
func base(fm *Frame, b int, nums ...vals.Num) error {
if b < 2 || b > 36 {
return ErrBadBase
return errs.OutOfRange{What: "base",
ValidLow: "2", ValidHigh: "36", Actual: strconv.Itoa(b)}
}
// Don't output anything yet in case some arguments are invalid.
results := make([]string, len(nums))
for i, num := range nums {
switch num := num.(type) {
case int:
results[i] = strconv.FormatInt(int64(num), b)
case *big.Int:
results[i] = num.Text(b)
case float64:
if i64 := int64(num); float64(i64) == num {
results[i] = strconv.FormatInt(i64, b)
} else if num == math.Trunc(num) {
var z big.Int
z.SetString(fmt.Sprintf("%.0f", num), 10)
results[i] = z.Text(b)
} else {
return errs.BadValue{What: "number",
Valid: "integer", Actual: vals.ReprPlain(num)}
}
default:
return errs.BadValue{What: "number",
Valid: "integer", Actual: vals.ReprPlain(num)}
}
}
out := fm.ValueOutput()
for _, num := range nums {
bigInt := new(big.Int)
switch num := num.(type) {
case int:
bigInt.SetInt64(int64(num))
case *big.Int:
bigInt.Set(num)
default:
return ErrInvalidInput
}
err := out.Put(bigInt.Text(b))
for _, s := range results {
err := out.Put(s)
if err != nil {
return err
}

View File

@ -62,19 +62,29 @@ Exception: port does not support value output
▶ 56bc75e2d63100000
~> base 10 0x56bc75e2d63100000
▶ 100000000000000000000
// float64 storing an integer
~> base 16 256.0
▶ 100
// float64 storing an integer that doesn't fit in int64
~> base 16 100000000000000000000.0
▶ 56bc75e2d63100000
// typed number as arguments
~> base (num 16) (num 256)
▶ 100
// bubbling output error
// bad arguments
~> base 16 1.2
Exception: bad value: number must be integer, but is (num 1.2)
[tty]:1:1-11: base 16 1.2
~> base 8 1/8
Exception: invalid input
Exception: bad value: number must be integer, but is (num 1/8)
[tty]:1:1-10: base 8 1/8
~> base 1 1
Exception: bad base
Exception: out of range: base must be from 2 to 36, but is 1
[tty]:1:1-8: base 1 1
~> base 37 10
Exception: bad base
Exception: out of range: base must be from 2 to 36, but is 37
[tty]:1:1-10: base 37 10
// bubbling output error
~> base 2 1 >&-
Exception: port does not support value output
[tty]:1:1-12: base 2 1 >&-