func MessThingTellallMethod(state *lua.State, thing *Thing) int { state.PushGoFunction(func(state *lua.State) int { place := checkThing(state, 1) text := state.CheckString(2) // If arg 3 is present, it should be a table of Things to exclude. excludes := make(map[ThingId]bool) if 2 < state.GetTop() { if !state.IsTable(3) { state.ArgError(3, "expected `table` for exclude argument if present") } numExcludes := int(state.ObjLen(3)) for i := 0; i < numExcludes; i++ { state.RawGeti(3, i+1) exclude := checkThing(state, -1) excludes[exclude.Id] = true } } for _, content := range place.GetContents() { if excludes[content.Id] { continue } if content.Client != nil { content.Client.Send(text) } } return 0 }) return 1 }
// return the Lua table at 'idx' as a copied Go slice. If 't' is nil then the slice // type is []interface{} func CopyTableToSlice(L *lua.State, t reflect.Type, idx int) interface{} { if t == nil { t = reflect.TypeOf(tslice) } te := t.Elem() n := int(L.ObjLen(idx)) slice := reflect.MakeSlice(t, n, n) for i := 1; i <= n; i++ { L.RawGeti(idx, i) val := LuaToGo(L, te, -1) slice.Index(i - 1).Set(valueOf(val)) L.Pop(1) } return slice.Interface() }
// Also for arrays. func copyTableToSlice(L *lua.State, t reflect.Type, idx int, visited map[uintptr]interface{}) interface{} { if t == nil { t = tslice } ref := t // There is probably no point at accepting more than one level of dreference. if t.Kind() == reflect.Ptr { t = t.Elem() } n := int(L.ObjLen(idx)) var slice reflect.Value if t.Kind() == reflect.Array { slice = reflect.New(t) slice = slice.Elem() } else { slice = reflect.MakeSlice(t, n, n) } // Do not add empty slices to the list of visited elements. // The empty Lua table is a single instance object and gets re-used across maps, slices and others. if n > 0 { ptr := L.ToPointer(idx) if ref.Kind() == reflect.Ptr { visited[ptr] = slice.Addr().Interface() } else { visited[ptr] = slice.Interface() } } te := t.Elem() for i := 1; i <= n; i++ { L.RawGeti(idx, i) val := reflect.ValueOf(luaToGo(L, te, -1, visited)) if val.Interface() == nullv.Interface() { val = reflect.Zero(te) } slice.Index(i - 1).Set(val) L.Pop(1) } if ref.Kind() == reflect.Ptr { return slice.Addr().Interface() } return slice.Interface() }
// Return the Lua table at 'idx' as a copied Go slice. If 't' is nil then the slice // type is []interface{} func CopyTableToSlice(L *lua.State, t reflect.Type, idx int) interface{} { if t == nil { t = tslice } te := t.Elem() n := int(L.ObjLen(idx)) slice := reflect.MakeSlice(t, n, n) for i := 1; i <= n; i++ { L.RawGeti(idx, i) val := luaToGoValue(L, te, -1) if val == nullv { val = reflect.Zero(te) } slice.Index(i - 1).Set(val) L.Pop(1) } return slice.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... 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 TableFormat(state *lua.State) int { state.CheckType(1, lua.LUA_TTABLE) state.CheckType(2, lua.LUA_TTABLE) // ( ??? -- tbl tblFields ) log.Println("Formatting a table by a table") printStackTypes(state) numFields := int(state.ObjLen(-1)) fields := make([]string, numFields) maxFieldLen := make(map[string]int) for i := 0; i < numFields; i++ { state.RawGeti(-1, i+1) // ( tbl tblFields -- tbl tblFields strHeader ) fieldName := state.ToString(-1) fields[i] = fieldName maxFieldLen[fieldName] = len(fieldName) state.Pop(1) // ( tbl tblFields strField -- tbl tblFields ) } state.Pop(1) // ( tbl tblFields -- tbl ) log.Println("Slurped up the fields list (table #2)") printStackTypes(state) numRows := int(state.ObjLen(-1)) rows := make([]map[string]string, numRows) for i := 0; i < numRows; i++ { state.RawGeti(-1, i+1) // ( tbl -- tbl tblRow ) row := make(map[string]string) for _, field := range fields { state.PushString(field) // ( tbl tblRow -- tbl tblRow strField ) state.RawGet(-2) // ( tbl tblRow strField -- tbl tblRow tblField ) row[field] = state.ToString(-1) if maxFieldLen[field] < len(row[field]) { maxFieldLen[field] = len(row[field]) } state.Pop(1) // ( tbl tblRow tblField -- tbl tblRow ) } rows[i] = row state.Pop(1) // ( tbl tblRow -- tbl ) } state.Pop(1) // ( tbl -- ) log.Println("Slurped up the data table (table #1)") printStackTypes(state) // %5s %10s %13s fmtStrings := make([]string, numFields) for i, field := range fields { fmtStrings[i] = fmt.Sprintf("%%-%ds", maxFieldLen[field]) } fmtString := strings.Join(fmtStrings, " ") log.Println("Figured out the format string:", fmtString) rowStrings := make([]string, numRows+1) rowFields := make([]interface{}, numFields) for i, row := range rows { for j, field := range fields { rowFields[j] = row[field] } rowStrings[i+1] = fmt.Sprintf(fmtString, rowFields...) } for i := 0; i < numFields; i++ { rowFields[i] = strings.Title(fields[i]) } rowStrings[0] = fmt.Sprintf(fmtString, rowFields...) log.Println("Yay formatted all the strings") formattedTable := strings.Join(rowStrings, "\n") state.PushString(formattedTable) // ( -- str ) log.Println("All done formatting this table!") printStackTypes(state) return 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... 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 }