conversion.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. package vals
  2. import (
  3. "errors"
  4. "fmt"
  5. "math/big"
  6. "reflect"
  7. "strconv"
  8. "sync"
  9. "unicode/utf8"
  10. "src.elv.sh/pkg/eval/errs"
  11. "src.elv.sh/pkg/strutil"
  12. )
  13. // Conversion between "Go values" (those expected by native Go functions) and
  14. // "Elvish values" (those participating in the Elvish runtime).
  15. //
  16. // Among the conversion functions, ScanToGo and FromGo implement the implicit
  17. // conversion used when calling native Go functions from Elvish. The API is
  18. // asymmetric; this has to do with two characteristics of Elvish's type system:
  19. //
  20. // - Elvish doesn't have a dedicated rune type and uses strings to represent
  21. // them.
  22. //
  23. // - Elvish permits using strings that look like numbers in place of numbers.
  24. //
  25. // As a result, while FromGo can always convert a "Go value" to an "Elvish
  26. // value" unambiguously, ScanToGo can't do that in the opposite direction.
  27. // For example, "1" may be converted into "1", '1' or 1, depending on what
  28. // the destination type is, and the process may fail. Thus ScanToGo takes the
  29. // pointer to the destination as an argument, and returns an error.
  30. //
  31. // The rest of the conversion functions need to explicitly invoked.
  32. // WrongType is returned by ScanToGo if the source value doesn't have a
  33. // compatible type.
  34. type WrongType struct {
  35. wantKind string
  36. gotKind string
  37. }
  38. // Error implements the error interface.
  39. func (err WrongType) Error() string {
  40. return fmt.Sprintf("wrong type: need %s, got %s", err.wantKind, err.gotKind)
  41. }
  42. type cannotParseAs struct {
  43. want string
  44. repr string
  45. }
  46. func (err cannotParseAs) Error() string {
  47. return fmt.Sprintf("cannot parse as %s: %s", err.want, err.repr)
  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. // ScanToGo converts an Elvish value, and stores it in the destination of ptr,
  57. // which must be a pointer.
  58. //
  59. // If ptr has type *int, *float64, *Num or *rune, it performs a suitable
  60. // conversion, and returns an error if the conversion fails. In other cases,
  61. // this function just tries to perform "*ptr = src" via reflection and returns
  62. // an error if the assignment can't be done.
  63. func ScanToGo(src any, ptr any) 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 dstType.String() == "eval.Callable" && ValueOf(src) == ValueOf(nil) {
  97. // A Callable option is a special-case that allows assignment from $nil.
  98. ptr = nil
  99. return nil
  100. }
  101. if !TypeOf(src).AssignableTo(dstType) {
  102. var dstKind string
  103. if dstType.Kind() == reflect.Interface {
  104. dstKind = "!!" + dstType.String()
  105. } else {
  106. dstKind = Kind(reflect.Zero(dstType).Interface())
  107. }
  108. return WrongType{dstKind, Kind(src)}
  109. }
  110. ValueOf(ptr).Elem().Set(ValueOf(src))
  111. return nil
  112. }
  113. }
  114. func elvToInt(arg any) (int, error) {
  115. switch arg := arg.(type) {
  116. case int:
  117. return arg, nil
  118. case string:
  119. num, err := strconv.ParseInt(arg, 0, 0)
  120. if err == nil {
  121. return int(num), nil
  122. }
  123. return 0, cannotParseAs{"integer", ReprPlain(arg)}
  124. default:
  125. return 0, errMustBeInteger
  126. }
  127. }
  128. func elvToNum(arg any) (Num, error) {
  129. switch arg := arg.(type) {
  130. case int, *big.Int, *big.Rat, float64:
  131. return arg, nil
  132. case string:
  133. n := ParseNum(arg)
  134. if n == nil {
  135. return 0, cannotParseAs{"number", ReprPlain(arg)}
  136. }
  137. return n, nil
  138. default:
  139. return 0, errMustBeNumber
  140. }
  141. }
  142. func elvToRune(arg any) (rune, error) {
  143. ss, ok := arg.(string)
  144. if !ok {
  145. return -1, errMustBeString
  146. }
  147. s := ss
  148. r, size := utf8.DecodeRuneInString(s)
  149. if r == utf8.RuneError {
  150. return -1, errMustBeValidUTF8
  151. }
  152. if size != len(s) {
  153. return -1, errMustHaveSingleRune
  154. }
  155. return r, nil
  156. }
  157. // ScanListToGo converts a List to a slice, using ScanToGo to convert each
  158. // element.
  159. func ScanListToGo(src List, ptr any) error {
  160. n := src.Len()
  161. values := reflect.MakeSlice(reflect.TypeOf(ptr).Elem(), n, n)
  162. i := 0
  163. for it := src.Iterator(); it.HasElem(); it.Next() {
  164. err := ScanToGo(it.Elem(), values.Index(i).Addr().Interface())
  165. if err != nil {
  166. return err
  167. }
  168. i++
  169. }
  170. reflect.ValueOf(ptr).Elem().Set(values)
  171. return nil
  172. }
  173. // Optional wraps the last pointer passed to ScanListElementsToGo, to indicate
  174. // that it is optional.
  175. func Optional(ptr any) any { return optional{ptr} }
  176. type optional struct{ ptr any }
  177. // ScanListElementsToGo unpacks elements from a list, storing the each element
  178. // in the given pointers with ScanToGo.
  179. //
  180. // The last pointer may be wrapped with Optional to indicate that it is
  181. // optional.
  182. func ScanListElementsToGo(src List, ptrs ...any) error {
  183. if o, ok := ptrs[len(ptrs)-1].(optional); ok {
  184. switch src.Len() {
  185. case len(ptrs) - 1:
  186. ptrs = ptrs[:len(ptrs)-1]
  187. case len(ptrs):
  188. ptrs[len(ptrs)-1] = o.ptr
  189. default:
  190. return errs.ArityMismatch{What: "list elements",
  191. ValidLow: len(ptrs) - 1, ValidHigh: len(ptrs), Actual: src.Len()}
  192. }
  193. } else if src.Len() != len(ptrs) {
  194. return errs.ArityMismatch{What: "list elements",
  195. ValidLow: len(ptrs), ValidHigh: len(ptrs), Actual: src.Len()}
  196. }
  197. i := 0
  198. for it := src.Iterator(); it.HasElem(); it.Next() {
  199. err := ScanToGo(it.Elem(), ptrs[i])
  200. if err != nil {
  201. return err
  202. }
  203. i++
  204. }
  205. return nil
  206. }
  207. // ScanMapToGo scans map elements into ptr, which must be a pointer to a struct.
  208. // Struct field names are converted to map keys with CamelToDashed.
  209. //
  210. // The map may contains keys that don't correspond to struct fields, and it
  211. // doesn't have to contain all keys that correspond to struct fields.
  212. func ScanMapToGo(src Map, ptr any) error {
  213. // Iterate over the struct keys instead of the map: since extra keys are
  214. // allowed, the map may be very big, while the size of the struct is bound.
  215. keys, _ := StructFieldsInfo(reflect.TypeOf(ptr).Elem())
  216. structValue := reflect.ValueOf(ptr).Elem()
  217. for i, key := range keys {
  218. if key == "" {
  219. continue
  220. }
  221. val, ok := src.Index(key)
  222. if !ok {
  223. continue
  224. }
  225. err := ScanToGo(val, structValue.Field(i).Addr().Interface())
  226. if err != nil {
  227. return err
  228. }
  229. }
  230. return nil
  231. }
  232. // StructFieldsInfo takes a type for a struct, and returns a slice for each
  233. // field name, converted with CamelToDashed, and a reverse index. Unexported
  234. // fields result in an empty string in the slice, and is omitted from the
  235. // reverse index.
  236. func StructFieldsInfo(t reflect.Type) ([]string, map[string]int) {
  237. if info, ok := structFieldsInfoCache.Load(t); ok {
  238. info := info.(structFieldsInfo)
  239. return info.keys, info.keyIdx
  240. }
  241. info := makeStructFieldsInfo(t)
  242. structFieldsInfoCache.Store(t, info)
  243. return info.keys, info.keyIdx
  244. }
  245. var structFieldsInfoCache sync.Map
  246. type structFieldsInfo struct {
  247. keys []string
  248. keyIdx map[string]int
  249. }
  250. func makeStructFieldsInfo(t reflect.Type) structFieldsInfo {
  251. keys := make([]string, t.NumField())
  252. keyIdx := make(map[string]int)
  253. for i := 0; i < t.NumField(); i++ {
  254. field := t.Field(i)
  255. if field.PkgPath != "" {
  256. continue
  257. }
  258. key := strutil.CamelToDashed(field.Name)
  259. keyIdx[key] = i
  260. keys[i] = key
  261. }
  262. return structFieldsInfo{keys, keyIdx}
  263. }
  264. // FromGo converts a Go value to an Elvish value.
  265. //
  266. // Exact numbers are normalized to the smallest types that can hold them, and
  267. // runes are converted to strings. Values of other types are returned unchanged.
  268. func FromGo(a any) any {
  269. switch a := a.(type) {
  270. case *big.Int:
  271. return NormalizeBigInt(a)
  272. case *big.Rat:
  273. return NormalizeBigRat(a)
  274. case rune:
  275. return string(a)
  276. default:
  277. return a
  278. }
  279. }