func register(L *lua.State, table string, values Map, convertFun bool) { pop := true if table == "*" { pop = false } else if len(table) > 0 { L.GetGlobal(table) if L.IsNil(-1) { L.NewTable() L.SetGlobal(table) L.GetGlobal(table) } } else { L.GetGlobal("_G") } for name, val := range values { t := reflect.TypeOf(val) if t.Kind() == reflect.Func { if convertFun { L.PushGoFunction(GoLuaFunc(L, val)) } else { lf := val.(func(*lua.State) int) L.PushGoFunction(lf) } } else { GoToLua(L, t, valueOf(val), false) } L.SetField(-2, name) } if pop { L.Pop(1) } }
func popMap(L *lua.State) map[string]interface{} { m := make(map[string]interface{}) L.PushNil() for L.Next(-2) != 0 { if L.IsString(-2) { var v interface{} if L.IsBoolean(-1) { v = L.ToBoolean(-1) } else if L.IsNumber(-1) { var t float64 t = L.ToNumber(-1) if t == float64(int64(t)) { v = int(t) } else { v = t } } else if L.IsString(-1) { v = L.ToString(-1) } else if L.IsNil(-1) { v = nil } else if L.IsTable(-1) { v = popMap(L) } m[L.ToString(-2)] = v } L.Pop(1) } return m }
func LuaTableGetString(L *lua.State, key string) string { L.PushString(key) L.GetTable(-2) r := "" if !L.IsNil(-1) { r = L.ToString(-1) } L.Pop(1) return r }
func isValueProxy(L *lua.State, idx int) bool { res := false if L.IsUserdata(idx) { L.GetMetaTable(idx) if !L.IsNil(-1) { L.GetField(-1, "luago.value") res = !L.IsNil(-1) L.Pop(1) } L.Pop(1) } return res }
// Look up a Lua value by its full name. If idx is 0, then this name // is assumed to start in the global table, e.g. "string.gsub". // With non-zero idx, can be used to look up subfields of a table. // It terminates with a nil value if we cannot continue the lookup... func Lookup(L *lua.State, path string, idx int) { parts := strings.Split(path, ".") if idx != 0 { L.PushValue(idx) } else { L.GetGlobal("_G") } for _, field := range parts { L.GetField(-1, field) L.Remove(-2) // remove table if L.IsNil(-1) { break } } }
// Register makes a number of Go values available in Lua code. // 'values' is a map of strings to Go values. // // - If table is non-nil, then create or reuse a global table of that name and // put the values in it. // // - If table is '' then put the values in the global table (_G). // // - If table is '*' then assume that the table is already on the stack. func Register(L *lua.State, table string, values Map) { pop := true if table == "*" { pop = false } else if len(table) > 0 { L.GetGlobal(table) if L.IsNil(-1) { L.NewTable() L.SetGlobal(table) L.GetGlobal(table) } } else { L.GetGlobal("_G") } for name, val := range values { GoToLua(L, nil, reflect.ValueOf(val), false) L.SetField(-2, name) } if pop { L.Pop(1) } }
// 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... if t.Kind() == reflect.Ptr { kind = t.Elem().Kind() } else if t.Kind() == reflect.Interface { t = nil } else { kind = t.Kind() } } switch L.Type(idx) { case lua.LUA_TNIL: if t == nil { return nil } switch kind { default: cannotConvert(L, idx, "nil", kind, t) } case lua.LUA_TBOOLEAN: if t == nil { kind = reflect.Bool } switch kind { case reflect.Bool: { ptr := new(bool) *ptr = bool(L.ToBoolean(idx)) value = *ptr } default: cannotConvert(L, idx, "bool", kind, t) } case lua.LUA_TSTRING: if t == nil { kind = reflect.String } switch kind { case reflect.String: { tos := L.ToString(idx) ptr := new(string) *ptr = tos value = *ptr } default: cannotConvert(L, idx, "string", kind, t) } case lua.LUA_TNUMBER: if t == nil { kind = reflect.Float64 } switch kind { 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(int8) *ptr = int8(L.ToNumber(idx)) value = *ptr } case reflect.Int16: { ptr := new(int16) *ptr = int16(L.ToNumber(idx)) value = *ptr } case reflect.Int32: { ptr := new(int32) *ptr = int32(L.ToNumber(idx)) value = *ptr } case reflect.Int64: { ptr := new(int64) *ptr = int64(L.ToNumber(idx)) value = *ptr } case reflect.Uint: { ptr := new(uint) *ptr = uint(L.ToNumber(idx)) value = *ptr } case reflect.Uint8: { ptr := new(uint8) *ptr = uint8(L.ToNumber(idx)) value = *ptr } case reflect.Uint16: { ptr := new(uint16) *ptr = uint16(L.ToNumber(idx)) value = *ptr } case reflect.Uint32: { ptr := new(uint32) *ptr = uint32(L.ToNumber(idx)) value = *ptr } case reflect.Uint64: { ptr := new(uint64) *ptr = uint64(L.ToNumber(idx)) value = *ptr } default: cannotConvert(L, idx, "number", kind, t) } case lua.LUA_TTABLE: if t == nil { kind = reflect.Interface } fallthrough default: istable := L.IsTable(idx) // if we don't know the type and the Lua object is userdata, // then it might be a proxy for a Go object. Otherwise wrap // it up as a LuaObject. if t == nil && !istable { if isValueProxy(L, idx) { return unwrapProxy(L, idx) } else { return NewLuaObject(L, idx) } } switch kind { case reflect.Slice: { // if we get a table, then copy its values to a new slice if istable { value = CopyTableToSlice(L, t, idx) } else { value = unwrapProxyOrComplain(L, idx) } } case reflect.Map: { if istable { value = CopyTableToMap(L, t, idx) } else { value = unwrapProxyOrComplain(L, idx) } } case reflect.Struct: { if istable { value = CopyTableToStruct(L, t, idx) } else { value = unwrapProxyOrComplain(L, idx) } } case reflect.Interface: { if istable { // 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 if L.IsNil(idx) { return nil } else { value = unwrapProxyOrComplain(L, idx) } } default: value = unwrapProxyOrComplain(L, idx) //cannotConvert(L,idx,"unknown",kind,t) } } return value }
func luaToGo(L *lua.State, t reflect.Type, idx int, visited map[uintptr]interface{}) interface{} { var value interface{} var kind reflect.Kind if t != nil { if t.Kind() == reflect.Ptr { kind = t.Elem().Kind() } else if t.Kind() == reflect.Interface { // Let the Lua type drive the conversion. t = nil } else { kind = t.Kind() } } switch L.Type(idx) { case lua.LUA_TNIL: if t == nil { return nil } switch kind { default: value = reflect.Zero(t).Interface() } case lua.LUA_TBOOLEAN: if t == nil { kind = reflect.Bool } switch kind { case reflect.Bool: ptr := new(bool) *ptr = L.ToBoolean(idx) value = *ptr default: value = reflect.Zero(t).Interface() } case lua.LUA_TSTRING: if t == nil { kind = reflect.String } switch kind { case reflect.String: tos := L.ToString(idx) ptr := new(string) *ptr = tos value = *ptr default: value = reflect.Zero(t).Interface() } case lua.LUA_TNUMBER: if t == nil { // Infering the type here (e.g. int if round value) would break backward // compatibility and drift away from Lua's design: the numeric type is // specified at compile time. kind = reflect.Float64 } switch kind { 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(int8) *ptr = int8(L.ToNumber(idx)) value = *ptr case reflect.Int16: ptr := new(int16) *ptr = int16(L.ToNumber(idx)) value = *ptr case reflect.Int32: ptr := new(int32) *ptr = int32(L.ToNumber(idx)) value = *ptr case reflect.Int64: ptr := new(int64) *ptr = int64(L.ToNumber(idx)) value = *ptr case reflect.Uint: ptr := new(uint) *ptr = uint(L.ToNumber(idx)) value = *ptr case reflect.Uint8: ptr := new(uint8) *ptr = uint8(L.ToNumber(idx)) value = *ptr case reflect.Uint16: ptr := new(uint16) *ptr = uint16(L.ToNumber(idx)) value = *ptr case reflect.Uint32: ptr := new(uint32) *ptr = uint32(L.ToNumber(idx)) value = *ptr case reflect.Uint64: ptr := new(uint64) *ptr = uint64(L.ToNumber(idx)) value = *ptr default: value = reflect.Zero(t).Interface() } case lua.LUA_TTABLE: if t == nil { kind = reflect.Interface } fallthrough default: istable := L.IsTable(idx) // If we don't know the type and the Lua object is userdata, // then it might be a proxy for a Go object. Otherwise wrap // it up as a LuaObject. if t == nil && !istable { if isValueProxy(L, idx) { v, _ := valueOfProxy(L, idx) return v.Interface() } return NewLuaObject(L, idx) } switch kind { case reflect.Array: if istable { ptr := L.ToPointer(idx) if val, ok := visited[ptr]; ok { return val } value = copyTableToSlice(L, t, idx, visited) } else { value = mustUnwrapProxy(L, idx) } case reflect.Slice: // if we get a table, then copy its values to a new slice if istable { ptr := L.ToPointer(idx) if val, ok := visited[ptr]; ok { return val } value = copyTableToSlice(L, t, idx, visited) } else { value = mustUnwrapProxy(L, idx) } case reflect.Map: if istable { ptr := L.ToPointer(idx) if val, ok := visited[ptr]; ok { return val } value = copyTableToMap(L, t, idx, visited) } else { value = mustUnwrapProxy(L, idx) } case reflect.Struct: if istable { ptr := L.ToPointer(idx) if val, ok := visited[ptr]; ok { return val } value = copyTableToStruct(L, t, idx, visited) } else { value = mustUnwrapProxy(L, idx) } case reflect.Interface: if istable { ptr := L.ToPointer(idx) if val, ok := visited[ptr]; ok { return val } // We 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, visited) } else { value = copyTableToMap(L, nil, idx, visited) } } 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 if L.IsNil(idx) { return nil } else { value = mustUnwrapProxy(L, idx) } default: value = mustUnwrapProxy(L, idx) } } return value }
func register(L *lua.State, table string, values Map, convertFun bool) { pop := true if table == "*" { pop = false } else if len(table) > 0 { L.GetGlobal(table) if L.IsNil(-1) { L.NewTable() L.SetGlobal(table) L.GetGlobal(table) } } else { L.GetGlobal("_G") } for name, val := range values { t := reflect.TypeOf(val) if t.Kind() == reflect.Func { if convertFun { L.PushGoFunction(GoLuaFunc(L, val)) } else { lf := val.(func(*lua.State) int) L.PushGoFunction(lf) } } else { GoToLua(L, t, valueOf(val), false) } L.SetField(-2, name) if t.Kind() == reflect.Func { var lf func(*lua.State) int if convertFun { lf = GoLuaFunc(L, val) } else { lf = val.(func(*lua.State) int) } L.PushGoFunction(func(L *lua.State) (ret int) { defer func() { if err2 := recover(); err2 != nil { GoToLua(L, typeof(err2), valueOf(err2), false) ret = 1 return } }() ret = lf(L) pos := L.GetTop() - ret + 1 L.PushNil() L.Insert(pos) for i := 0; i < L.GetTop(); i++ { fmt.Println(L.Typename(int(L.Type(i + 1)))) } return ret + 1 }) L.SetField(-2, "safe_"+name) } } if pop { L.Pop(1) } }