github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+incompatible/cmds/core/elvish/eval/vals/conversion.go (about)

     1  package vals
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strconv"
     7  	"unicode/utf8"
     8  )
     9  
    10  // Conversion between native and Elvish values.
    11  //
    12  // Elvish uses native Go types most of the time - string, bool, hashmap.Map,
    13  // vector.Vector, etc., and there is no need for any conversions. There are some
    14  // exceptions, for instance the numerical types int and float64: in native Go
    15  // code they are distinct types, but Elvish uses string for all numbers.
    16  // Similarly, Elvish uses string to represent runes. In all cases, there is a
    17  // many-to-one relationship between Go types and Elvish types.
    18  //
    19  // Conversion from Go value to Elvish value can happen without knowing the
    20  // destination type: int, float64 and rune values are converted to strings, and
    21  // values of other types remain unchanged. The opposite is not true: the native
    22  // Go value corresponding to the Elvish value "1" can be string("1"), int(1),
    23  // float64(1.0) or rune('1'). Conversion in this direction depends on the
    24  // destination type.
    25  
    26  // ScanToGo converts an Elvish value to a Go value. the pointer points to. It
    27  // uses the type of the pointer to determine the destination type, and puts the
    28  // converted value in the location the pointer points to. Conversion only
    29  // happens when the destination type is int, float64 or rune; in other cases,
    30  // this function just checks that the source value is already assignable to the
    31  // destination.
    32  func ScanToGo(src interface{}, ptr interface{}) error {
    33  	switch ptr := ptr.(type) {
    34  	case *int:
    35  		i, err := elvToInt(src)
    36  		if err == nil {
    37  			*ptr = i
    38  		}
    39  		return err
    40  	case *float64:
    41  		f, err := elvToFloat(src)
    42  		if err == nil {
    43  			*ptr = f
    44  		}
    45  		return err
    46  	case *rune:
    47  		r, err := elvToRune(src)
    48  		if err == nil {
    49  			*ptr = r
    50  		}
    51  		return err
    52  	case Scanner:
    53  		return ptr.ScanElvish(src)
    54  	default:
    55  		// Do a generic `*ptr = src` via reflection
    56  		ptrType := reflect.TypeOf(ptr)
    57  		if ptrType.Kind() != reflect.Ptr {
    58  			return fmt.Errorf("need pointer to scan into, got %T", ptr)
    59  		}
    60  		dstType := ptrType.Elem()
    61  		if !reflect.TypeOf(src).AssignableTo(dstType) {
    62  			return fmt.Errorf("need %s, got %s",
    63  				Kind(reflect.Zero(dstType).Interface()), Kind(src))
    64  		}
    65  		reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(src))
    66  		return nil
    67  	}
    68  }
    69  
    70  // Scanner is implemented by types that can scan an Elvish value into itself.
    71  type Scanner interface {
    72  	ScanElvish(interface{}) error
    73  }
    74  
    75  // FromGo converts a Go value to an Elvish value. Conversion happens when the
    76  // argument is int, float64 or rune (this is consistent with ScanToGo). In other
    77  // cases, this function just returns the argument.
    78  func FromGo(a interface{}) interface{} {
    79  	switch a := a.(type) {
    80  	case int:
    81  		return strconv.Itoa(a)
    82  	case float64:
    83  		return strconv.FormatFloat(a, 'g', -1, 64)
    84  	case rune:
    85  		return string(a)
    86  	default:
    87  		return a
    88  	}
    89  }
    90  
    91  func elvToFloat(arg interface{}) (float64, error) {
    92  	if _, ok := arg.(string); !ok {
    93  		return 0, fmt.Errorf("must be string")
    94  	}
    95  	s := arg.(string)
    96  	num, err := strconv.ParseFloat(s, 64)
    97  	if err != nil {
    98  		num, err2 := strconv.ParseInt(s, 0, 64)
    99  		if err2 != nil {
   100  			return 0, err
   101  		}
   102  		return float64(num), nil
   103  	}
   104  	return num, nil
   105  }
   106  
   107  func elvToInt(arg interface{}) (int, error) {
   108  	arg, ok := arg.(string)
   109  	if !ok {
   110  		return 0, fmt.Errorf("must be string")
   111  	}
   112  	num, err := strconv.ParseInt(arg.(string), 0, 0)
   113  	if err != nil {
   114  		return 0, err
   115  	}
   116  	return int(num), nil
   117  }
   118  
   119  func elvToRune(arg interface{}) (rune, error) {
   120  	ss, ok := arg.(string)
   121  	if !ok {
   122  		return -1, fmt.Errorf("must be string")
   123  	}
   124  	s := ss
   125  	r, size := utf8.DecodeRuneInString(s)
   126  	if r == utf8.RuneError {
   127  		return -1, fmt.Errorf("string is not valid UTF-8")
   128  	}
   129  	if size != len(s) {
   130  		return -1, fmt.Errorf("string has multiple runes")
   131  	}
   132  	return r, nil
   133  }