add type casting

This commit is contained in:
raziman 2021-02-15 12:37:05 +08:00
parent 9969c5eaf0
commit b7d969be17
3 changed files with 177 additions and 4 deletions

View File

@ -20,9 +20,11 @@ type Anko struct {
}
func NewAnko() Anko {
return Anko{
core.Import(env.NewEnv()),
}
env := core.Import(env.NewEnv())
importToX(env)
return Anko{env}
}
// Define defines new symbol and value to the Anko env.

171
anko/convert.go Normal file
View File

@ -0,0 +1,171 @@
package anko
import (
"fmt"
"reflect"
"strconv"
"strings"
"time"
"github.com/mattn/anko/env"
)
// importToX adds all the toX to the env given
func importToX(e *env.Env) {
e.Define("to_bool", func(v interface{}) bool {
rv := reflect.ValueOf(v)
if !rv.IsValid() {
return false
}
nt := reflect.TypeOf(true)
if rv.Type().ConvertibleTo(nt) {
return rv.Convert(nt).Bool()
}
if rv.Type().ConvertibleTo(reflect.TypeOf(1.0)) && rv.Convert(reflect.TypeOf(1.0)).Float() > 0.0 {
return true
}
if rv.Kind() == reflect.String {
s := strings.ToLower(v.(string))
if s == "y" || s == "yes" {
return true
}
b, err := strconv.ParseBool(s)
if err == nil {
return b
}
}
return false
})
e.Define("to_string", func(v interface{}) string {
if b, ok := v.([]byte); ok {
return string(b)
}
return fmt.Sprint(v)
})
e.Define("to_int", func(v interface{}) int64 {
rv := reflect.ValueOf(v)
if !rv.IsValid() {
return 0
}
nt := reflect.TypeOf(1)
if rv.Type().ConvertibleTo(nt) {
return rv.Convert(nt).Int()
}
if rv.Kind() == reflect.String {
i, err := strconv.ParseInt(v.(string), 10, 64)
if err == nil {
return i
}
f, err := strconv.ParseFloat(v.(string), 64)
if err == nil {
return int64(f)
}
}
if rv.Kind() == reflect.Bool {
if v.(bool) {
return 1
}
}
return 0
})
e.Define("to_float", func(v interface{}) float64 {
rv := reflect.ValueOf(v)
if !rv.IsValid() {
return 0
}
nt := reflect.TypeOf(1.0)
if rv.Type().ConvertibleTo(nt) {
return rv.Convert(nt).Float()
}
if rv.Kind() == reflect.String {
f, err := strconv.ParseFloat(v.(string), 64)
if err == nil {
return f
}
}
if rv.Kind() == reflect.Bool {
if v.(bool) {
return 1.0
}
}
return 0.0
})
e.Define("to_char", func(s rune) string {
return string(s)
})
e.Define("to_rune", func(s string) rune {
if len(s) == 0 {
return 0
}
return []rune(s)[0]
})
e.Define("to_bool_slice", func(v []interface{}) []bool {
var result []bool
toSlice(v, &result)
return result
})
e.Define("to_string_slice", func(v []interface{}) []string {
var result []string
toSlice(v, &result)
return result
})
e.Define("to_int_slice", func(v []interface{}) []int64 {
var result []int64
toSlice(v, &result)
return result
})
e.Define("to_float_slice", func(v []interface{}) []float64 {
var result []float64
toSlice(v, &result)
return result
})
e.Define("to_byte_slice", func(s string) []byte {
return []byte(s)
})
e.Define("to_runeSlice", func(s string) []rune {
return []rune(s)
})
e.Define("to_duration", func(v int64) time.Duration {
return time.Duration(v)
})
}
// toSlice takes in a "generic" slice and converts and copies
// it's elements into the typed slice pointed at by ptr.
// Note that this is a costly operation.
func toSlice(from []interface{}, ptr interface{}) {
// Value of the pointer to the target
obj := reflect.Indirect(reflect.ValueOf(ptr))
// We can't just convert from interface{} to whatever the target is (diff memory layout),
// so we need to create a New slice of the proper type and copy the values individually
t := reflect.TypeOf(ptr).Elem()
tt := t.Elem()
slice := reflect.MakeSlice(t, len(from), len(from))
// Copying the data, val is an addressable Pointer of the actual target type
val := reflect.Indirect(reflect.New(tt))
for i := 0; i < len(from); i++ {
v := reflect.ValueOf(from[i])
if v.IsValid() && v.Type().ConvertibleTo(tt) {
val.Set(v.Convert(tt))
} else {
val.Set(reflect.Zero(tt))
}
slice.Index(i).Set(val)
}
// Ok now assign our slice to the target pointer
obj.Set(slice)
}

View File

@ -65,7 +65,7 @@ module keybinds {
module playlist {
e = func() {
val = 10 + 10
debug_popup(val)
debug_popup(to_string(val))
}
}