github.com/kolbycrouch/elvish@v0.14.1-0.20210614162631-215b9ac1c423/pkg/eval/vals/conversion.go (about) 1 package vals 2 3 import ( 4 "errors" 5 "fmt" 6 "math/big" 7 "reflect" 8 "strconv" 9 "unicode/utf8" 10 ) 11 12 // Conversion between native and Elvish values. 13 // 14 // Elvish uses native Go types most of the time - string, bool, hashmap.Map, 15 // vector.Vector, etc., and there is no need for any conversions. There are some 16 // exceptions, for instance int and rune, since Elvish currently lacks integer 17 // types. 18 // 19 // There is a many-to-one relationship between Go types and Elvish types. A 20 // Go value can always be converted to an Elvish value unambiguously, but to 21 // convert an Elvish value into a Go value one must know the destination type 22 // first. For example, all of the Go values int(1), rune('1') and string("1") 23 // convert to Elvish "1"; conversely, Elvish "1" may be converted to any of the 24 // aforementioned three possible values, depending on the destination type. 25 // 26 // In future, Elvish may gain distinct types for integers and characters, making 27 // the examples above unnecessary; however, the conversion logic may not 28 // entirely go away, as there might always be some mismatch between Elvish's 29 // type system and Go's. 30 31 type wrongType struct { 32 wantKind string 33 gotKind string 34 } 35 36 func (err wrongType) Error() string { 37 return fmt.Sprintf("wrong type: need %s, got %s", err.wantKind, err.gotKind) 38 } 39 40 type cannotParseAs struct { 41 want string 42 repr string 43 } 44 45 func (err cannotParseAs) Error() string { 46 return fmt.Sprintf("cannot parse as %s: %s", err.want, err.repr) 47 } 48 49 var ( 50 errMustBeString = errors.New("must be string") 51 errMustBeValidUTF8 = errors.New("must be valid UTF-8") 52 errMustHaveSingleRune = errors.New("must have a single rune") 53 errMustBeNumber = errors.New("must be number") 54 errMustBeInteger = errors.New("must be integer") 55 ) 56 57 // ScanToGo converts an Elvish value to a Go value that the pointer refers to. It 58 // uses the type of the pointer to determine the destination type, and puts the 59 // converted value in the location the pointer points to. Conversion only 60 // happens when the destination type is int, float64 or rune; in other cases, 61 // this function just checks that the source value is already assignable to the 62 // destination. 63 func ScanToGo(src interface{}, ptr interface{}) error { 64 switch ptr := ptr.(type) { 65 case *int: 66 i, err := elvToInt(src) 67 if err == nil { 68 *ptr = i 69 } 70 return err 71 case *float64: 72 n, err := elvToNum(src) 73 if err == nil { 74 *ptr = convertToFloat64(n) 75 } 76 return err 77 case *Num: 78 n, err := elvToNum(src) 79 if err == nil { 80 *ptr = n 81 } 82 return err 83 case *rune: 84 r, err := elvToRune(src) 85 if err == nil { 86 *ptr = r 87 } 88 return err 89 default: 90 // Do a generic `*ptr = src` via reflection 91 ptrType := TypeOf(ptr) 92 if ptrType.Kind() != reflect.Ptr { 93 return fmt.Errorf("internal bug: need pointer to scan to, got %T", ptr) 94 } 95 dstType := ptrType.Elem() 96 if !TypeOf(src).AssignableTo(dstType) { 97 return wrongType{Kind(reflect.Zero(dstType).Interface()), Kind(src)} 98 } 99 ValueOf(ptr).Elem().Set(ValueOf(src)) 100 return nil 101 } 102 } 103 104 // FromGo converts a Go value to an Elvish value. Most types are returned as 105 // is, but exact numerical types are normalized to one of int, *big.Int and 106 // *big.Rat, using the small representation that can hold the value, and runes 107 // are converted to strings. 108 func FromGo(a interface{}) interface{} { 109 switch a := a.(type) { 110 case *big.Int: 111 return NormalizeBigInt(a) 112 case *big.Rat: 113 return NormalizeBigRat(a) 114 case rune: 115 return string(a) 116 default: 117 return a 118 } 119 } 120 121 func elvToInt(arg interface{}) (int, error) { 122 switch arg := arg.(type) { 123 case int: 124 return arg, nil 125 case string: 126 num, err := strconv.ParseInt(arg, 0, 0) 127 if err == nil { 128 return int(num), nil 129 } 130 return 0, cannotParseAs{"integer", Repr(arg, -1)} 131 default: 132 return 0, errMustBeInteger 133 } 134 } 135 136 func elvToNum(arg interface{}) (Num, error) { 137 switch arg := arg.(type) { 138 case int, *big.Int, *big.Rat, float64: 139 return arg, nil 140 case string: 141 n := ParseNum(arg) 142 if n == nil { 143 return 0, cannotParseAs{"number", Repr(arg, -1)} 144 } 145 return n, nil 146 default: 147 return 0, errMustBeNumber 148 } 149 } 150 151 func elvToRune(arg interface{}) (rune, error) { 152 ss, ok := arg.(string) 153 if !ok { 154 return -1, errMustBeString 155 } 156 s := ss 157 r, size := utf8.DecodeRuneInString(s) 158 if r == utf8.RuneError { 159 return -1, errMustBeValidUTF8 160 } 161 if size != len(s) { 162 return -1, errMustHaveSingleRune 163 } 164 return r, nil 165 }