// Copy matching Lua table entries to a struct, given the struct type // and the index on the Lua stack. func CopyTableToStruct(L *lua.State, t reflect.Type, idx int) interface{} { was_ptr := t.Kind() == reflect.Ptr if was_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(strings.Title(key)) if f.IsValid() { val := luaToGoValue(L, f.Type(), -1) f.Set(val) } L.Pop(1) } if was_ptr { return s.Interface() } return s.Elem().Interface() }
func LuaIntColumnQuery(L *lua.State) int { if (L.GetTop() != 1) && (L.GetTop() != 3) { panic(errors.New("Wrong number of arguments to columnq")) return 0 } name := L.ToString(1) op := "" value := "" if L.GetTop() == 3 { op = L.ToString(2) value = L.ToString(3) L.Pop(3) } else { L.Pop(1) } if name[0] == ':' { panic(errors.New("Column name can not start with ':'")) return 0 } L.CheckStack(1) tl := GetTasklistFromLua(L) tl.luaState.PushGoStruct(&SimpleExpr{name, op, value, nil, 0, ""}) return 1 }
func copyTableToMap(L *lua.State, t reflect.Type, idx int, visited map[uintptr]interface{}) interface{} { if t == nil { t = tmap } te, tk := t.Elem(), t.Key() m := reflect.MakeMap(t) // See copyTableToSlice. ptr := L.ToPointer(idx) if !luaIsEmpty(L, idx) { visited[ptr] = m.Interface() } L.PushNil() if idx < 0 { idx-- } for L.Next(idx) != 0 { // key at -2, value at -1 key := reflect.ValueOf(luaToGo(L, tk, -2, visited)) val := reflect.ValueOf(luaToGo(L, te, -1, visited)) if val.Interface() == nullv.Interface() { val = reflect.Zero(te) } m.SetMapIndex(key, val) L.Pop(1) } return m.Interface() }
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 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 MessThingIndex(state *lua.State) int { log.Println("HEY WE MADE IT") printStackTypes(state) state.GetMetaTable(1) state.LGetMetaTable(ThingMetaTableName) isThing := state.RawEqual(-1, -2) state.Pop(2) if !isThing { log.Println("!!! OMG ARG #1 IS NOT A MESS.THING !!!") } fieldName := state.CheckString(2) log.Println("Arg #2 checks out, it's a string") thing := checkThing(state, 1) log.Println("So we're tryin'a look up", fieldName, "on thing", thing.Id) if member, ok := MessThingMembers[fieldName]; ok { return member(state, thing) } // That wasn't one of our members, so look it up in our Table. if data, ok := thing.Table[fieldName]; ok { // TODO: instead of pushing a whole map if the script asks for one, maybe we should use another kind of userdata that tracks the name & can access its submembers until the script asks for the leaf (or a non-existent branch)? pushValue(state, data) return 1 } // uh... I guess we didn't do anything, so...? return 0 }
func GetTasklistFromLua(L *lua.State) *Tasklist { L.CheckStack(1) L.GetGlobal(TASKLIST) rawptr := L.ToUserdata(-1) var ptr **Tasklist = (**Tasklist)(rawptr) L.Pop(1) return *ptr }
func GetTableInt(L *lua.State, name string) int { // Remember to check stack for 1 extra location L.PushString(name) L.GetTable(-2) r := L.ToInteger(-1) L.Pop(1) return r }
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 luaIsEmpty(L *lua.State, idx int) bool { L.PushNil() if idx < 0 { idx-- } if L.Next(idx) != 0 { L.Pop(2) return false } return true }
func GetEntryFromLua(L *lua.State, name string, fname string) *Entry { L.CheckStack(1) L.GetGlobal(name) rawptr := L.ToUserdata(-1) var ptr **Entry = (**Entry)(rawptr) L.Pop(1) if ptr == nil { panic(errors.New("No cursor set, can not use " + fname)) } return *ptr }
func sandboxCompile(L *lua.State, registryIndex string, name, code string) { L.PushString(registryIndex) L.GetTable(lua.LUA_REGISTRYINDEX) L.PushString(name) err := L.LoadString(code) if err != 0 { log.Fatalf("%s: %s", name, L.ToString(-1)) L.Pop(2) } else { L.SetTable(-3) } }
func copyTableToStruct(L *lua.State, t reflect.Type, idx int, visited map[uintptr]interface{}) interface{} { if t == nil { RaiseError(L, "type argument must be non-nill") } wasPtr := t.Kind() == reflect.Ptr if wasPtr { t = t.Elem() } s := reflect.New(t) // T -> *T ref := s.Elem() // See copyTableToSlice. ptr := L.ToPointer(idx) if !luaIsEmpty(L, idx) { if wasPtr { visited[ptr] = s.Interface() } else { visited[ptr] = s.Elem().Interface() } } // Associate Lua keys with Go fields: tags have priority over matching field // name. fields := map[string]string{} st := ref.Type() for i := 0; i < ref.NumField(); i++ { field := st.Field(i) tag := field.Tag.Get("lua") if tag != "" { fields[tag] = field.Name continue } fields[field.Name] = field.Name } L.PushNil() if idx < 0 { idx-- } for L.Next(idx) != 0 { key := L.ToString(-2) f := ref.FieldByName(fields[key]) if f.CanSet() && f.IsValid() { val := reflect.ValueOf(luaToGo(L, f.Type(), -1, visited)) f.Set(val) } L.Pop(1) } if wasPtr { return s.Interface() } return s.Elem().Interface() }
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 }
func MessThingTellMethod(state *lua.State, thing *Thing) int { state.PushGoFunction(func(state *lua.State) int { thing := checkThing(state, 1) text := state.CheckString(2) if thing.Client != nil { thing.Client.Send(text) } state.Pop(2) // ( udataThing strText -- ) return 0 }) return 1 }
func LuaIntStringFunction(L *lua.State, name string, n int, fn func(tl *Tasklist, argv []string) int) int { luaAssertArgnum(L, n, name) argv := make([]string, 0) for i := 1; i <= n; i++ { argv = append(argv, L.ToString(i)) } L.Pop(n) L.CheckStack(1) tl := GetTasklistFromLua(L) return fn(tl, argv) }
// 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() }
// return the Lua table at 'idx' as a copied Go map. If 't' is nil then the map // type is map[string]interface{} func CopyTableToMap(L *lua.State, t reflect.Type, idx int) interface{} { if t == nil { t = reflect.TypeOf(tmap) } te, tk := t.Elem(), t.Key() m := reflect.MakeMap(t) L.PushNil() if idx < 0 { idx-- } for L.Next(idx) != 0 { // key at -2, value at -1 key := valueOf(LuaToGo(L, tk, -2)) val := valueOf(LuaToGo(L, te, -1)) m.SetMapIndex(key, val) L.Pop(1) } return m.Interface() }
func initializeProxies(L *lua.State) { flagValue := func() { L.SetMetaMethod("__tostring", proxy__tostring) L.SetMetaMethod("__gc", proxy__gc) L.SetMetaMethod("__eq", proxy__eq) L.PushBoolean(true) L.SetField(-2, "luago.value") L.Pop(1) } L.NewMetaTable(cSLICE_META) L.SetMetaMethod("__index", slice__index) L.SetMetaMethod("__newindex", slice__newindex) L.SetMetaMethod("__len", slicemap__len) L.SetMetaMethod("__ipairs", slice__ipairs) flagValue() L.NewMetaTable(cMAP_META) L.SetMetaMethod("__index", map__index) L.SetMetaMethod("__newindex", map__newindex) L.SetMetaMethod("__len", slicemap__len) L.SetMetaMethod("__pairs", map__pairs) flagValue() L.NewMetaTable(cSTRUCT_META) L.SetMetaMethod("__index", struct__index) L.SetMetaMethod("__newindex", struct__newindex) flagValue() L.NewMetaTable(cINTERFACE_META) L.SetMetaMethod("__index", interface__index) flagValue() L.NewMetaTable(cCHANNEL_META) //~ RegisterFunctions(L,"*",FMap { //~ "Send":channel_send, //~ "Recv":channel_recv, //~ }) L.NewTable() L.PushGoFunction(channel_send) L.SetField(-2, "Send") L.PushGoFunction(channel_recv) L.SetField(-2, "Recv") L.SetField(-2, "__index") flagValue() }
func MessThingPronounsubMethod(state *lua.State, thing *Thing) int { state.PushGoFunction(func(state *lua.State) int { thing := checkThing(state, 1) text := state.CheckString(2) for code, pronoun := range thing.Pronouns() { lowerCode := fmt.Sprintf(`%%%s`, code) upperCode := fmt.Sprintf(`%%%s`, strings.ToUpper(code)) text = strings.Replace(text, lowerCode, pronoun, -1) text = strings.Replace(text, upperCode, strings.ToTitle(pronoun), -1) } text = strings.Replace(text, `%n`, thing.Name, -1) text = strings.Replace(text, `%N`, thing.Name, -1) state.Pop(2) // ( udataThing str -- ) state.PushString(text) // ( -- str' ) return 1 }) return 1 }
// Copy matching Lua table entries to a struct, given the struct type // and the index on the Lua stack. func CopyTableToStruct(L *lua.State, t reflect.Type, idx int) interface{} { was_ptr := t.Kind() == reflect.Ptr if was_ptr { t = t.Elem() } s := reflect.New(t) // T -> *T ref := s.Elem() // Associate Lua keys with Go fields: tags have priority over matching field // name. fields := map[string]string{} st := ref.Type() for i := 0; i < ref.NumField(); i++ { field := st.Field(i) tag := field.Tag.Get("lua") if tag != "" { fields[tag] = field.Name continue } fields[field.Name] = field.Name } L.PushNil() if idx < 0 { idx-- } for L.Next(idx) != 0 { key := L.ToString(-2) f := ref.FieldByName(fields[key]) if f.CanSet() && f.IsValid() { val := luaToGoValue(L, f.Type(), -1) f.Set(val) } L.Pop(1) } if was_ptr { return s.Interface() } return s.Elem().Interface() }
func outputNumbersToStrings(L *lua.State) { L.GetGlobal("output") if !L.IsTable(-1) { L.NewTable() L.SetGlobal("output") } L.GetField(-1, "tags") if L.IsTable(-1) { // First key. L.PushNil() for L.Next(-2) != 0 { // Use 'key' at index -2 and 'value' at index -1. if L.IsString(-2) && L.IsString(-1) { // Convert numbers to strings. L.ToString(-1) L.SetField(-3, L.ToString(-2)) } else { // Remove 'value' and keep 'key' for next iteration. L.Pop(1) } } } L.Pop(1) L.Pop(1) }
// Return the Lua table at 'idx' as a copied Go map. If 't' is nil then the map // type is map[string]interface{} func CopyTableToMap(L *lua.State, t reflect.Type, idx int) interface{} { if t == nil { t = tmap } te, tk := t.Elem(), t.Key() m := reflect.MakeMap(t) L.PushNil() if idx < 0 { idx-- } for L.Next(idx) != 0 { // key at -2, value at -1 key := luaToGoValue(L, tk, -2) val := luaToGoValue(L, te, -1) if val == nullv { val = reflect.Zero(te) } m.SetMapIndex(key, val) L.Pop(1) } return m.Interface() }
// 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) } }
func installWorld(state *lua.State) { log.Println("Installing world") printStackTypes(state) state.NewMetaTable(ThingMetaTableName) // ( -- mtbl ) state.SetMetaMethod("__index", MessThingIndex) // ( mtbl -- mtbl ) state.Pop(1) // ( mtbl -- ) worldTable := map[string]interface{}{ /* "Root": "Create": func... */ } pushValue(state, worldTable) // Install Thing types as singleton sentinel values. As userdata, these will only compare if the values are exactly equal. state.NewUserdata(uintptr(0)) state.SetField(-2, "Player") state.NewUserdata(uintptr(0)) state.SetField(-2, "Place") state.NewUserdata(uintptr(0)) state.SetField(-2, "Program") state.NewUserdata(uintptr(0)) state.SetField(-2, "Action") state.NewUserdata(uintptr(0)) state.SetField(-2, "Thing") state.SetGlobal("world") state.GetGlobal("table") // ( -- tblTable ) state.PushGoFunction(TableFormat) state.SetField(-2, "format") state.Pop(1) // ( tblTable -- ) log.Println("Finished installing world") printStackTypes(state) }
// new LuaObject refering to the global environment func Global(L *lua.State) *LuaObject { L.GetGlobal("_G") val := NewLuaObject(L, -1) L.Pop(1) return val }
// A new LuaObject from global qualified name, using Lookup. func NewLuaObjectFromName(L *lua.State, path string) *LuaObject { Lookup(L, path, 0) val := NewLuaObject(L, -1) L.Pop(1) return val }
// raise a Lua error from Go code func RaiseError(L *lua.State, msg string) { L.Where(1) pos := L.ToString(-1) L.Pop(1) panic(L.NewError(pos + " " + msg)) }