func struct__newindex(L *lua.State) int { st, t := valueOfProxy(L, 1) name := L.ToString(2) if t.Kind() == reflect.Ptr { st = st.Elem() } field := st.FieldByName(name) val := LuaToGo(L, field.Type(), 3) field.Set(valueOf(val)) return 0 }
// GoLuaFunc converts an arbitrary Go function into a Lua-compatible GoFunction. // There are special optimized cases for functions that go from strings to strings, // and doubles to doubles, but otherwise Go // reflection is used to provide a generic wrapper function func GoLuaFunc(L *lua.State, fun interface{}) lua.LuaGoFunction { switch f := fun.(type) { case func(*lua.State) int: return f case func(string) string: return func(L *lua.State) int { L.PushString(f(L.ToString(1))) return 1 } case func(float64) float64: return func(L *lua.State) int { L.PushNumber(f(L.ToNumber(1))) return 1 } default: } var funv reflect.Value switch ff := fun.(type) { case reflect.Value: funv = ff default: funv = valueOf(fun) } funt := funv.Type() targs, tout := functionArgRetTypes(funt) return func(L *lua.State) int { var lastT reflect.Type isVariadic := funt.IsVariadic() if isVariadic { lastT = targs[len(targs)-1].Elem() targs = targs[0 : len(targs)-1] } args := make([]reflect.Value, len(targs)) for i, t := range targs { val := LuaToGo(L, t, i+1) args[i] = valueOf(val) } if isVariadic { n := L.GetTop() for i := len(targs) + 1; i <= n; i++ { val := valueOf(LuaToGo(L, lastT, i)) args = append(args, val) } } resv := funv.Call(args) for i, val := range resv { GoToLua(L, tout[i], val) } return len(resv) } }
func struct__index(L *lua.State) int { st, t := valueOfProxy(L, 1) name := L.ToString(2) est := st if t.Kind() == reflect.Ptr { est = st.Elem() } ret := est.FieldByName(name) if !ret.IsValid() { // no such field, try for method? callGoMethod(L, name, st) } else { GoToLua(L, ret.Type(), ret) } return 1 }
func slice__index(L *lua.State) int { slice, _ := valueOfProxy(L, 1) if L.IsNumber(2) { idx := L.ToInteger(2) ret := slice.Index(idx - 1) GoToLua(L, ret.Type(), ret) } else { name := L.ToString(2) switch name { case "Slice": L.PushGoFunction(slice_slice) default: fmt.Println("unknown slice method") } } return 1 }
func CopyTableToStruct(L *lua.State, t reflect.Type, idx int) interface{} { if t.Kind() == reflect.Ptr { t = t.Elem() } s := reflect.New(t) // T -> *T ref := s.Elem() L.PushNil() if idx < 0 { idx-- } for L.Next(idx) != 0 { key := L.ToString(-2) f := ref.FieldByName(key) if f.IsValid() { val := valueOf(LuaToGo(L, f.Type(), -1)) f.Set(val) } L.Pop(1) } return s.Interface() }
// Convert a Lua value 'idx' on the stack to the Go value of desired type 't'. Handles // numerical and string types in a straightforward way, and will convert tables to // either map or slice types. func LuaToGo(L *lua.State, t reflect.Type, idx int) interface{} { var value interface{} var kind reflect.Kind if t == nil { // let the Lua type drive the conversion... switch L.Type(idx) { case lua.LUA_TNIL: return nil // well, d'oh case lua.LUA_TBOOLEAN: kind = reflect.Bool case lua.LUA_TSTRING: kind = reflect.String case lua.LUA_TTABLE: kind = reflect.Interface default: return NewLuaObject(L, idx) } } else if t.Kind() == reflect.Ptr { kind = t.Elem().Kind() } else { kind = t.Kind() } switch kind { // various numerical types are tedious but straightforward case reflect.Float64: { ptr := new(float64) *ptr = L.ToNumber(idx) value = *ptr } case reflect.Float32: { ptr := new(float32) *ptr = float32(L.ToNumber(idx)) value = *ptr } case reflect.Int: { ptr := new(int) *ptr = int(L.ToNumber(idx)) value = *ptr } case reflect.Int8: { ptr := new(byte) *ptr = byte(L.ToNumber(idx)) value = *ptr } case reflect.String: { tos := L.ToString(idx) ptr := new(string) *ptr = tos value = *ptr } case reflect.Bool: { ptr := new(bool) *ptr = bool(L.ToBoolean(idx)) value = *ptr } case reflect.Slice: { // if we get a table, then copy its values to a new slice if L.IsTable(idx) { value = CopyTableToSlice(L, t, idx) } else { value = unwrapProxy(L, idx) } } case reflect.Map: { if L.IsTable(idx) { value = CopyTableToMap(L, t, idx) } else { value = unwrapProxy(L, idx) } } case reflect.Struct: { if L.IsTable(idx) { value = CopyTableToStruct(L, t, idx) } else { value = unwrapProxy(L, idx) } } case reflect.Interface: { if L.IsTable(idx) { // have to make an executive decision here: tables with non-zero // length are assumed to be slices! if L.ObjLen(idx) > 0 { value = CopyTableToSlice(L, nil, idx) } else { value = CopyTableToMap(L, nil, idx) } } else if L.IsNumber(idx) { value = L.ToNumber(idx) } else if L.IsString(idx) { value = L.ToString(idx) } else if L.IsBoolean(idx) { value = L.ToBoolean(idx) } else { value = unwrapProxy(L, idx) } } default: { fmt.Println("unhandled type", t) value = 20 } } return value }
func interface__index(L *lua.State) int { st, _ := valueOfProxy(L, 1) name := L.ToString(2) callGoMethod(L, name, st) return 1 }