Skip to content

Commit

Permalink
Merge pull request #313 from MichaelS11/conversion
Browse files Browse the repository at this point in the history
Added string to byte & rune conversion
  • Loading branch information
mattn authored Jun 28, 2019
2 parents ebe5b9b + c2f6f0c commit fec82c7
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 3 deletions.
1 change: 1 addition & 0 deletions packages/strings.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,5 @@ func init() {
"TrimSpace": strings.TrimSpace,
"TrimSuffix": strings.TrimSuffix,
}
stringsGo110()
}
13 changes: 13 additions & 0 deletions packages/stringsGo110.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// +build go1.10

package packages

import (
"strings"
)

func stringsGo110() {
PackageTypes["strings"] = map[string]interface{}{
"Builder": strings.Builder{},
}
}
16 changes: 16 additions & 0 deletions packages/stringsGo110_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package packages

import (
"os"
"testing"

"github.com/mattn/anko/internal/testlib"
)

func TestStringsGo110(t *testing.T) {
os.Setenv("ANKO_DEBUG", "1")
tests := []testlib.Test{
{Script: `strings = import("strings"); a = make(strings.Builder); _, err = a.WriteString("a"); if err != nil { return err.Error() }; _, err = a.WriteString("b"); if err != nil { return err.Error() }; _, err = a.WriteString("c"); if err != nil { return err.Error() }; a.String()`, RunOutput: "abc"},
}
testlib.Run(t, tests, &testlib.Options{EnvSetupFunc: &testPackagesEnvSetupFunc})
}
5 changes: 5 additions & 0 deletions packages/stringsNotGo110.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// +build !go1.10

package packages

func stringsGo110() {}
7 changes: 7 additions & 0 deletions vm/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ type (
var (
nilType = reflect.TypeOf(nil)
stringType = reflect.TypeOf("a")
byteType = reflect.TypeOf(byte('a'))
runeType = reflect.TypeOf('a')
interfaceType = reflect.ValueOf([]interface{}{int64(1)}).Index(0).Type()
interfaceSliceType = reflect.TypeOf([]interface{}{})
reflectValueType = reflect.TypeOf(reflect.Value{})
Expand All @@ -50,6 +52,8 @@ var (
reflectValueNilValue = reflect.ValueOf(nilValue)
reflectValueErrorNilValue = reflect.ValueOf(reflect.New(errorType).Elem())

errInvalidTypeConversion = fmt.Errorf("invalid type conversion")

// ErrBreak when there is an unexpected break statement
ErrBreak = errors.New("unexpected break statement")
// ErrContinue when there is an unexpected continue statement
Expand Down Expand Up @@ -402,6 +406,9 @@ func makeValue(t reflect.Type) (reflect.Value, error) {
case reflect.Struct:
structV := reflect.New(t).Elem()
for i := 0; i < structV.NumField(); i++ {
if structV.Field(i).Kind() == reflect.Ptr {
continue
}
v, err := makeValue(structV.Field(i).Type())
if err != nil {
return nilValue, err
Expand Down
82 changes: 81 additions & 1 deletion vm/vmContainers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,84 @@ import (
"github.com/mattn/anko/parser"
)

type (
testStruct1 struct {
aInterface interface{}
aBool bool
aInt32 int32
aInt64 int64
aFloat32 float32
aFloat64 float32
aString string
aFunc func()

aPtrInterface *interface{}
aPtrBool *bool
aPtrInt32 *int32
aPtrInt64 *int64
aPtrFloat32 *float32
aPtrFloat64 *float32
aPtrString *string
aPtrSliceInterface *[]interface{}
aPtrSliceBool *[]bool
aPtrSliceInt32 *[]int32
aPtrSliceInt64 *[]int64
aPtrSliceFloat32 *[]float32
aPtrSliceFloat64 *[]float32
aPtrSliceString *[]string

aSliceInterface []interface{}
aSliceBool []bool
aSliceInt32 []int32
aSliceInt64 []int64
aSliceFloat32 []float32
aSliceFloat64 []float32
aSliceString []string
aSlicePtrInterface []*interface{}
aSlicePtrBool []*bool
aSlicePtrInt32 []*int32
aSlicePtrInt64 []*int64
aSlicePtrFloat32 []*float32
aSlicePtrFloat64 []*float32
aSlicePtrString []*string

aMapInterface map[string]interface{}
aMapBool map[string]bool
aMapInt32 map[string]int32
aMapInt64 map[string]int64
aMapFloat32 map[string]float32
aMapFloat64 map[string]float32
aMapString map[string]string
aMapPtrInterface map[string]*interface{}
aMapPtrBool map[string]*bool
aMapPtrInt32 map[string]*int32
aMapPtrInt64 map[string]*int64
aMapPtrFloat32 map[string]*float32
aMapPtrFloat64 map[string]*float32
aMapPtrString map[string]*string

aChanInterface chan interface{}
aChanBool chan bool
aChanInt32 chan int32
aChanInt64 chan int64
aChanFloat32 chan float32
aChanFloat64 chan float32
aChanString chan string
aChanPtrInterface chan *interface{}
aChanPtrBool chan *bool
aChanPtrInt32 chan *int32
aChanPtrInt64 chan *int64
aChanPtrFloat32 chan *float32
aChanPtrFloat64 chan *float32
aChanPtrString chan *string

aPtrStruct *testStruct1
}
testStruct2 struct {
aStruct testStruct1
}
)

var (
testSliceEmpty []interface{}
testSlice = []interface{}{nil, true, int64(1), float64(1.1), "a"}
Expand Down Expand Up @@ -236,7 +314,7 @@ func TestSlicesAutoAppend(t *testing.T) {
{Script: `a[2] = nil`, Input: map[string]interface{}{"a": []float64{1.5, 2.5}}, Output: map[string]interface{}{"a": []float64{1.5, 2.5, 0}}},
{Script: `a[2] = nil`, Input: map[string]interface{}{"a": []string{"a", "b"}}, Output: map[string]interface{}{"a": []string{"a", "b", ""}}},

{Script: `a[2] = "a"`, Input: map[string]interface{}{"a": []int32{1, 2}}, RunError: fmt.Errorf("type string cannot be assigned to type int32 for slice index"), Output: map[string]interface{}{"a": []int32{1, 2}}},
{Script: `a[2] = "a"`, Input: map[string]interface{}{"a": []int16{1, 2}}, RunError: fmt.Errorf("type string cannot be assigned to type int16 for slice index"), Output: map[string]interface{}{"a": []int16{1, 2}}},
{Script: `a[2] = true`, Input: map[string]interface{}{"a": []int64{1, 2}}, RunError: fmt.Errorf("type bool cannot be assigned to type int64 for slice index"), Output: map[string]interface{}{"a": []int64{1, 2}}},
{Script: `a[2] = "a"`, Input: map[string]interface{}{"a": []int64{1, 2}}, RunError: fmt.Errorf("type string cannot be assigned to type int64 for slice index"), Output: map[string]interface{}{"a": []int64{1, 2}}},
{Script: `a[2] = true`, Input: map[string]interface{}{"a": []float32{1.1, 2.2}}, RunError: fmt.Errorf("type bool cannot be assigned to type float32 for slice index"), Output: map[string]interface{}{"a": []float32{1.1, 2.2}}},
Expand Down Expand Up @@ -1769,6 +1847,8 @@ func TestStructs(t *testing.T) {
func TestMakeStructs(t *testing.T) {
os.Setenv("ANKO_DEBUG", "1")
tests := []testlib.Test{
{Script: `a = make(struct1)`, Types: map[string]interface{}{"struct1": &testStruct1{}}, RunOutput: &testStruct1{}, Output: map[string]interface{}{"a": &testStruct1{}}},
{Script: `a = make(struct2)`, Types: map[string]interface{}{"struct2": &testStruct2{}}, RunOutput: &testStruct2{}, Output: map[string]interface{}{"a": &testStruct2{}}},
{Script: `make(struct)`, Types: map[string]interface{}{"struct": &struct {
A interface{}
B interface{}
Expand Down
27 changes: 25 additions & 2 deletions vm/vmConvertToX.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,32 @@ func convertReflectValueToType(rv reflect.Value, rt reflect.Type) (reflect.Value
return convertReflectValueToType(rv.Elem(), rt)
}

if rv.Type() == stringType {
if rt == byteType {
aString := rv.String()
if len(aString) < 1 {
return reflect.Zero(rt), nil
}
if len(aString) > 1 {
return rv, errInvalidTypeConversion
}
return reflect.ValueOf(aString[0]), nil
}
if rt == runeType {
aString := rv.String()
if len(aString) < 1 {
return reflect.Zero(rt), nil
}
if len(aString) > 1 {
return rv, errInvalidTypeConversion
}
return reflect.ValueOf(rune(aString[0])), nil
}
}

// TODO: need to handle the case where either rv or rt are a pointer but not both

return rv, fmt.Errorf("invalid type conversion")
return rv, errInvalidTypeConversion
}

// convertSliceOrArray trys to covert the reflect.Value slice or array to the slice or array reflect.Type
Expand Down Expand Up @@ -145,7 +168,7 @@ func convertMap(rv reflect.Value, rt reflect.Type) (reflect.Value, error) {
func convertVMFunctionToType(rv reflect.Value, rt reflect.Type) (reflect.Value, error) {
// only translates runVMFunction type
if !checkIfRunVMFunction(rv.Type()) {
return rv, fmt.Errorf("invalid type conversion")
return rv, errInvalidTypeConversion
}

// create runVMConvertFunction to match reflect.Type
Expand Down
9 changes: 9 additions & 0 deletions vm/vmFunctions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,15 @@ func TestFunctionConversions(t *testing.T) {
return b()
}}, RunOutput: true, Output: map[string]interface{}{"c": true, "d": int32(1), "e": int64(2), "f": float32(3.3), "g": float64(4.4), "h": "5"}},

// string to byte
{Script: `b = a("yz"); b`, Input: map[string]interface{}{"a": func(b byte) string { return string(b) }}, RunError: fmt.Errorf("function wants argument type uint8 but received type string")},
{Script: `b = a("x"); b`, Input: map[string]interface{}{"a": func(b byte) string { return string(b) }}, RunOutput: "x"},
{Script: `b = a(""); b`, Input: map[string]interface{}{"a": func(b byte) string { return string(b) }}, RunOutput: "\x00"},
// string to rune
{Script: `b = a("yz"); b`, Input: map[string]interface{}{"a": func(b rune) string { return string(b) }}, RunError: fmt.Errorf("function wants argument type int32 but received type string")},
{Script: `b = a("x"); b`, Input: map[string]interface{}{"a": func(b rune) string { return string(b) }}, RunOutput: "x"},
{Script: `b = a(""); b`, Input: map[string]interface{}{"a": func(b rune) string { return string(b) }}, RunOutput: "\x00"},

// slice inteface unable to convert to int
{Script: `b = [1, 2.2, "3"]; a(b)`, Input: map[string]interface{}{"a": func(b []int) int { return len(b) }}, RunError: fmt.Errorf("function wants argument type []int but received type []interface {}"), Output: map[string]interface{}{"b": []interface{}{int64(1), float64(2.2), "3"}}},
// slice no sub convertible conversion
Expand Down

0 comments on commit fec82c7

Please sign in to comment.