github.com/oweisse/u-root@v0.0.0-20181109060735-d005ad25fef1/cmds/elvish/eval/builtin_fn.go (about)

     1  package eval
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"reflect"
     7  
     8  	"github.com/u-root/u-root/cmds/elvish/eval/vals"
     9  	"github.com/u-root/u-root/cmds/elvish/hash"
    10  )
    11  
    12  var ErrArgs = errors.New("args error")
    13  
    14  // BuiltinFn uses reflection to wrap arbitrary Go functions into Elvish
    15  // functions.
    16  //
    17  // Parameters are passed following these rules:
    18  //
    19  // 1. If the first parameter of function has type *Frame, it gets the current
    20  // call frame.
    21  //
    22  // 2. If (possibly after a *Frame parameter) the first parameter has type
    23  // RawOptions, it gets a map of options. If the function has not declared an
    24  // RawOptions parameter but is passed options, an error is thrown.
    25  //
    26  // 3. If the last parameter is non-variadic and has type Inputs, it represents
    27  // an optional parameter that contains the input to this function. If the
    28  // argument is not supplied, the input channel of the Frame will be used to
    29  // supply the inputs.
    30  //
    31  // 4. Other parameters are converted using elvToGo.
    32  //
    33  // Return values go to the channel part of the stdout port, after being
    34  // converted using goToElv. If the last return value has type error and is not
    35  // nil, it is turned into an exception and no ouputting happens. If the last
    36  // return value is a nil error, it is ignored.
    37  type BuiltinFn struct {
    38  	name string
    39  	impl interface{}
    40  
    41  	// Type information of impl.
    42  
    43  	frame   bool
    44  	options bool
    45  	inputs  bool
    46  	// Type of "normal" (non-Frame, non-Options, non-variadic) arguments.
    47  	normalArgs []reflect.Type
    48  	// Type of variadic arguments, nil if function is non-variadic
    49  	variadicArg reflect.Type
    50  }
    51  
    52  var _ Callable = &BuiltinFn{}
    53  
    54  type (
    55  	Inputs func(func(interface{}))
    56  )
    57  
    58  var (
    59  	frameType      = reflect.TypeOf((*Frame)(nil))
    60  	rawOptionsType = reflect.TypeOf(RawOptions(nil))
    61  	inputsType     = reflect.TypeOf(Inputs(nil))
    62  )
    63  
    64  // NewBuiltinFn creates a new ReflectBuiltinFn instance.
    65  func NewBuiltinFn(name string, impl interface{}) *BuiltinFn {
    66  	implType := reflect.TypeOf(impl)
    67  	b := &BuiltinFn{name: name, impl: impl}
    68  
    69  	i := 0
    70  	if i < implType.NumIn() && implType.In(i) == frameType {
    71  		b.frame = true
    72  		i++
    73  	}
    74  	if i < implType.NumIn() && implType.In(i) == rawOptionsType {
    75  		b.options = true
    76  		i++
    77  	}
    78  	for ; i < implType.NumIn(); i++ {
    79  		paramType := implType.In(i)
    80  		if i == implType.NumIn()-1 {
    81  			if implType.IsVariadic() {
    82  				b.variadicArg = paramType.Elem()
    83  				break
    84  			} else if paramType == inputsType {
    85  				b.inputs = true
    86  				break
    87  			}
    88  		}
    89  		b.normalArgs = append(b.normalArgs, paramType)
    90  	}
    91  	return b
    92  }
    93  
    94  // Kind returns "fn".
    95  func (*BuiltinFn) Kind() string {
    96  	return "fn"
    97  }
    98  
    99  // Equal compares identity.
   100  func (b *BuiltinFn) Equal(rhs interface{}) bool {
   101  	return b == rhs
   102  }
   103  
   104  // Hash hashes the address.
   105  func (b *BuiltinFn) Hash() uint32 {
   106  	return hash.Hash(b)
   107  }
   108  
   109  // Repr returns an opaque representation "<builtin $name>".
   110  func (b *BuiltinFn) Repr(int) string {
   111  	return "<builtin " + b.name + ">"
   112  }
   113  
   114  // error(nil) is treated as nil by reflect.TypeOf, so we first get the type of
   115  // *error and use Elem to obtain type of error.
   116  var errorType = reflect.TypeOf((*error)(nil)).Elem()
   117  
   118  var errNoOptions = errors.New("function does not accept any options")
   119  
   120  // Call calls the implementation using reflection.
   121  func (b *BuiltinFn) Call(f *Frame, args []interface{}, opts map[string]interface{}) error {
   122  	if b.variadicArg != nil {
   123  		if len(args) < len(b.normalArgs) {
   124  			return fmt.Errorf("want %d or more arguments, got %d",
   125  				len(b.normalArgs), len(args))
   126  		}
   127  	} else if b.inputs {
   128  		if len(args) != len(b.normalArgs) && len(args) != len(b.normalArgs)+1 {
   129  			return fmt.Errorf("want %d or %d arguments, got %d",
   130  				len(b.normalArgs), len(b.normalArgs)+1, len(args))
   131  		}
   132  	} else if len(args) != len(b.normalArgs) {
   133  		return fmt.Errorf("want %d arguments, got %d", len(b.normalArgs), len(args))
   134  	}
   135  	if !b.options && len(opts) > 0 {
   136  		return errNoOptions
   137  	}
   138  
   139  	var in []reflect.Value
   140  	if b.frame {
   141  		in = append(in, reflect.ValueOf(f))
   142  	}
   143  	if b.options {
   144  		in = append(in, reflect.ValueOf(opts))
   145  	}
   146  	for i, arg := range args {
   147  		var typ reflect.Type
   148  		if i < len(b.normalArgs) {
   149  			typ = b.normalArgs[i]
   150  		} else if b.variadicArg != nil {
   151  			typ = b.variadicArg
   152  		} else if b.inputs {
   153  			break // Handled after the loop
   154  		} else {
   155  			panic("impossible")
   156  		}
   157  		ptr := reflect.New(typ)
   158  		err := vals.ScanToGo(arg, ptr.Interface())
   159  		if err != nil {
   160  			return fmt.Errorf("wrong type of %d'th argument: %v", i+1, err)
   161  		}
   162  		in = append(in, ptr.Elem())
   163  	}
   164  
   165  	if b.inputs {
   166  		var inputs Inputs
   167  		if len(args) == len(b.normalArgs) {
   168  			inputs = Inputs(f.IterateInputs)
   169  		} else {
   170  			// Wrap an iterable argument in Inputs.
   171  			iterable := args[len(args)-1]
   172  			inputs = Inputs(func(f func(interface{})) {
   173  				err := vals.Iterate(iterable, func(v interface{}) bool {
   174  					f(v)
   175  					return true
   176  				})
   177  				maybeThrow(err)
   178  			})
   179  		}
   180  		in = append(in, reflect.ValueOf(inputs))
   181  	}
   182  
   183  	outs := reflect.ValueOf(b.impl).Call(in)
   184  
   185  	if len(outs) > 0 && outs[len(outs)-1].Type() == errorType {
   186  		err := outs[len(outs)-1].Interface()
   187  		if err != nil {
   188  			return err.(error)
   189  		}
   190  		outs = outs[:len(outs)-1]
   191  	}
   192  
   193  	for _, out := range outs {
   194  		f.OutputChan() <- vals.FromGo(out.Interface())
   195  	}
   196  	return nil
   197  }