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 }