Skip to content

Commit

Permalink
eval: Remove more throws.
Browse files Browse the repository at this point in the history
This addresses #570.
  • Loading branch information
xiaq committed Mar 1, 2018
1 parent 1e0114e commit 7969c66
Show file tree
Hide file tree
Showing 9 changed files with 54 additions and 36 deletions.
19 changes: 9 additions & 10 deletions eval/builtin_fn_container.go
Expand Up @@ -38,17 +38,17 @@ func init() {

var errKeyMustBeString = errors.New("key must be string")

func nsFn(m hashmap.Map) Ns {
func nsFn(m hashmap.Map) (Ns, error) {
ns := make(Ns)
for it := m.Iterator(); it.HasElem(); it.Next() {
k, v := it.Elem()
kstring, ok := k.(string)
if !ok {
throw(errKeyMustBeString)
return nil, errKeyMustBeString
}
ns[kstring] = vars.NewAnyWithInit(v)
}
return ns
return ns, nil
}

func rangeFn(fm *Frame, rawOpts RawOptions, args ...float64) error {
Expand Down Expand Up @@ -81,13 +81,12 @@ func repeat(fm *Frame, n int, v interface{}) {
}

// explode puts each element of the argument.
func explode(fm *Frame, v interface{}) {
func explode(fm *Frame, v interface{}) error {
out := fm.ports[1].Chan
err := vals.Iterate(v, func(e interface{}) bool {
return vals.Iterate(v, func(e interface{}) bool {
out <- e
return true
})
maybeThrow(err)
}

func assoc(a, k, v interface{}) (interface{}, error) {
Expand Down Expand Up @@ -176,7 +175,7 @@ func hasKey(container, key interface{}) (bool, error) {
}
}

func count(fm *Frame, args ...interface{}) int {
func count(fm *Frame, args ...interface{}) (int, error) {
var n int
switch len(args) {
case 0:
Expand All @@ -195,13 +194,13 @@ func count(fm *Frame, args ...interface{}) int {
return true
})
if err != nil {
throw(fmt.Errorf("cannot get length of a %s", vals.Kind(v)))
return 0, fmt.Errorf("cannot get length of a %s", vals.Kind(v))
}
}
default:
throw(errors.New("want 0 or 1 argument"))
return 0, errors.New("want 0 or 1 argument")
}
return n
return n, nil
}

func keys(fm *Frame, m hashmap.Map) {
Expand Down
20 changes: 11 additions & 9 deletions eval/builtin_fn_flow.go
Expand Up @@ -3,6 +3,8 @@ package eval
import (
"errors"
"sync"

"github.com/elves/elvish/util"
)

// Flow control.
Expand Down Expand Up @@ -41,14 +43,13 @@ func runParallel(fm *Frame, functions ...Callable) error {
}

// each takes a single closure and applies it to all input values.
func each(fm *Frame, f Callable, inputs Inputs) {
func each(fm *Frame, f Callable, inputs Inputs) error {
broken := false
var err error
inputs(func(v interface{}) {
if broken {
return
}
// NOTE We don't have the position range of the closure in the source.
// Ideally, it should be kept in the Closure itself.
newFm := fm.fork("closure of each")
newFm.ports[0] = DevNullClosedChan
ex := newFm.Call(f, []interface{}{v}, NoOpts)
Expand All @@ -61,14 +62,16 @@ func each(fm *Frame, f Callable, inputs Inputs) {
case Break:
broken = true
default:
throw(ex)
broken = true
err = ex
}
}
})
return err
}

// peach takes a single closure and applies it to all input values in parallel.
func peach(fm *Frame, f Callable, inputs Inputs) {
func peach(fm *Frame, f Callable, inputs Inputs) error {
var w sync.WaitGroup
broken := false
var err error
Expand All @@ -78,8 +81,6 @@ func peach(fm *Frame, f Callable, inputs Inputs) {
}
w.Add(1)
go func() {
// NOTE We don't have the position range of the closure in the source.
// Ideally, it should be kept in the Closure itself.
newFm := fm.fork("closure of peach")
newFm.ports[0] = DevNullClosedChan
ex := newFm.Call(f, []interface{}{v}, NoOpts)
Expand All @@ -92,14 +93,15 @@ func peach(fm *Frame, f Callable, inputs Inputs) {
case Break:
broken = true
default:
err = ex
broken = true
err = util.Errors(err, ex)
}
}
w.Done()
}()
})
w.Wait()
maybeThrow(err)
return err
}

func fail(msg string) error {
Expand Down
2 changes: 1 addition & 1 deletion eval/builtin_fn_fs.go
Expand Up @@ -61,7 +61,7 @@ func newDirStruct(path string, score float64) *vals.Struct {

func dirs(fm *Frame) error {
if fm.DaemonClient == nil {
throw(ErrStoreNotConnected)
return ErrStoreNotConnected
}
dirs, err := fm.DaemonClient.Dirs(storedefs.NoBlacklist)
if err != nil {
Expand Down
11 changes: 8 additions & 3 deletions eval/builtin_fn_str.go
Expand Up @@ -115,15 +115,18 @@ var eawkWordSep = regexp.MustCompile("[ \t]+")
// stripping the line and splitting the line by whitespaces. The function may
// call break and continue. Overall this provides a similar functionality to
// awk, hence the name.
func eawk(fm *Frame, f Callable, inputs Inputs) {
func eawk(fm *Frame, f Callable, inputs Inputs) error {
broken := false
var err error
inputs(func(v interface{}) {
if broken {
return
}
line, ok := v.(string)
if !ok {
throw(ErrInput)
broken = true
err = ErrInput
return
}
args := []interface{}{line}
for _, field := range eawkWordSep.Split(strings.Trim(line, " \t"), -1) {
Expand All @@ -142,8 +145,10 @@ func eawk(fm *Frame, f Callable, inputs Inputs) {
case Break:
broken = true
default:
throw(ex)
broken = true
err = ex
}
}
})
return err
}
4 changes: 3 additions & 1 deletion eval/compile_lvalue.go
Expand Up @@ -182,7 +182,9 @@ func (op *elemOp) Invoke(fm *Frame) ([]vars.Type, error) {
indicies := make([]interface{}, len(op.indexOps))
for i, op := range op.indexOps {
values, err := op.Exec(fm)
maybeThrow(err)
if err != nil {
return nil, err
}
// TODO: Implement multi-indexing.
if len(values) != 1 {
return nil, errors.New("multi indexing not implemented")
Expand Down
2 changes: 1 addition & 1 deletion eval/daemon/daemon.go
Expand Up @@ -29,7 +29,7 @@ func Ns(daemon *daemon.Client, spawner *daemonp.Daemon) eval.Ns {

spawn := func() error {
if spawner == nil {
util.Throw(errDontKnowHowToSpawnDaemon)
return errDontKnowHowToSpawnDaemon
}
return spawner.Spawn()
}
Expand Down
15 changes: 8 additions & 7 deletions eval/re/re.go
Expand Up @@ -58,7 +58,7 @@ func find(fm *eval.Frame, rawOpts eval.RawOptions, argPattern, source string) {
}
}

func replace(fm *eval.Frame, rawOpts eval.RawOptions, argPattern string, argRepl interface{}, source string) string {
func replace(fm *eval.Frame, rawOpts eval.RawOptions, argPattern string, argRepl interface{}, source string) (string, error) {

opts := struct {
Posix bool
Expand All @@ -72,14 +72,15 @@ func replace(fm *eval.Frame, rawOpts eval.RawOptions, argPattern string, argRepl
if opts.Literal {
repl, ok := argRepl.(string)
if !ok {
throwf("replacement must be string when literal is set, got %s",
return "", fmt.Errorf(
"replacement must be string when literal is set, got %s",
vals.Kind(argRepl))
}
return pattern.ReplaceAllLiteralString(source, repl)
return pattern.ReplaceAllLiteralString(source, repl), nil
} else {
switch repl := argRepl.(type) {
case string:
return pattern.ReplaceAllString(source, repl)
return pattern.ReplaceAllString(source, repl), nil
case eval.Callable:
replFunc := func(s string) string {
values, err := fm.CaptureOutput(repl, []interface{}{s}, eval.NoOpts)
Expand All @@ -94,11 +95,11 @@ func replace(fm *eval.Frame, rawOpts eval.RawOptions, argPattern string, argRepl
}
return output
}
return pattern.ReplaceAllStringFunc(source, replFunc)
return pattern.ReplaceAllStringFunc(source, replFunc), nil
default:
throwf("replacement must be string or function, got %s",
return "", fmt.Errorf(
"replacement must be string or function, got %s",
vals.Kind(argRepl))
panic("unreachable")
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions sys/select_linux.go
Expand Up @@ -5,6 +5,10 @@ package sys
import "syscall"

func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *syscall.Timeval) error {
// On ARM64, MIPS64 and MIPS64LE, syscall.Select is emulated in userland and
// will dereference timeout. In that case, if the timeout argument is nil,
// the call will panic. This is not POSIX-conformant behavior, but we work
// around this by supplying a default value for timeout.
_, err := syscall.Select(nfd, r.s(), w.s(), e.s(), timeout)
return err
}
13 changes: 9 additions & 4 deletions util/multierror.go
Expand Up @@ -27,8 +27,9 @@ func (es MultiError) Error() string {
}

// Errors concatenate multiple errors into one. If all errors are nil, it
// returns nil, otherwise the return value is a MultiError containing all the
// non-nil arguments. Arguments of the type MultiError are flattened.
// returns nil. If there is one non-nil error, it is returned. Otherwise the
// return value is a MultiError containing all the non-nil arguments. Arguments
// of the type MultiError are flattened.
func Errors(errs ...error) error {
var nonNil []error
for _, err := range errs {
Expand All @@ -40,8 +41,12 @@ func Errors(errs ...error) error {
}
}
}
if len(nonNil) == 0 {
switch len(nonNil) {
case 0:
return nil
case 1:
return nonNil[0]
default:
return MultiError{nonNil}
}
return MultiError{nonNil}
}

0 comments on commit 7969c66

Please sign in to comment.