github.com/mem/u-root@v2.0.1-0.20181004165302-9b18b4636a33+incompatible/cmds/elvish/eval/builtin_fn_flow.go (about)

     1  package eval
     2  
     3  import (
     4  	"errors"
     5  	"sync"
     6  
     7  	"github.com/u-root/u-root/cmds/elvish/util"
     8  )
     9  
    10  // Flow control.
    11  
    12  func init() {
    13  	addBuiltinFns(map[string]interface{}{
    14  		"run-parallel": runParallel,
    15  		// Exception and control
    16  		"fail":        fail,
    17  		"multi-error": multiErrorFn,
    18  		"return":      returnFn,
    19  		"break":       breakFn,
    20  		"continue":    continueFn,
    21  		// Iterations.
    22  		"each":  each,
    23  		"peach": peach,
    24  	})
    25  }
    26  
    27  func runParallel(fm *Frame, functions ...Callable) error {
    28  	var waitg sync.WaitGroup
    29  	waitg.Add(len(functions))
    30  	exceptions := make([]*Exception, len(functions))
    31  	for i, function := range functions {
    32  		go func(fm2 *Frame, function Callable, exception **Exception) {
    33  			err := fm2.Call(function, NoArgs, NoOpts)
    34  			if err != nil {
    35  				*exception = err.(*Exception)
    36  			}
    37  			waitg.Done()
    38  		}(fm.fork("[run-parallel function]"), function, &exceptions[i])
    39  	}
    40  
    41  	waitg.Wait()
    42  	return ComposeExceptionsFromPipeline(exceptions)
    43  }
    44  
    45  // each takes a single closure and applies it to all input values.
    46  func each(fm *Frame, f Callable, inputs Inputs) error {
    47  	broken := false
    48  	var err error
    49  	inputs(func(v interface{}) {
    50  		if broken {
    51  			return
    52  		}
    53  		newFm := fm.fork("closure of each")
    54  		newFm.ports[0] = DevNullClosedChan
    55  		ex := newFm.Call(f, []interface{}{v}, NoOpts)
    56  		newFm.Close()
    57  
    58  		if ex != nil {
    59  			switch ex.(*Exception).Cause {
    60  			case nil, Continue:
    61  				// nop
    62  			case Break:
    63  				broken = true
    64  			default:
    65  				broken = true
    66  				err = ex
    67  			}
    68  		}
    69  	})
    70  	return err
    71  }
    72  
    73  // peach takes a single closure and applies it to all input values in parallel.
    74  func peach(fm *Frame, f Callable, inputs Inputs) error {
    75  	var w sync.WaitGroup
    76  	broken := false
    77  	var err error
    78  	inputs(func(v interface{}) {
    79  		if broken || err != nil {
    80  			return
    81  		}
    82  		w.Add(1)
    83  		go func() {
    84  			newFm := fm.fork("closure of peach")
    85  			newFm.ports[0] = DevNullClosedChan
    86  			ex := newFm.Call(f, []interface{}{v}, NoOpts)
    87  			newFm.Close()
    88  
    89  			if ex != nil {
    90  				switch ex.(*Exception).Cause {
    91  				case nil, Continue:
    92  					// nop
    93  				case Break:
    94  					broken = true
    95  				default:
    96  					broken = true
    97  					err = util.Errors(err, ex)
    98  				}
    99  			}
   100  			w.Done()
   101  		}()
   102  	})
   103  	w.Wait()
   104  	return err
   105  }
   106  
   107  func fail(msg string) error {
   108  	return errors.New(msg)
   109  }
   110  
   111  func multiErrorFn(excs ...*Exception) error {
   112  	return PipelineError{excs}
   113  }
   114  
   115  func returnFn() error {
   116  	return Return
   117  }
   118  
   119  func breakFn() error {
   120  	return Break
   121  }
   122  
   123  func continueFn() error {
   124  	return Continue
   125  }