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 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 }
func callGoMethod(L *lua.State, name string, st reflect.Value) { ret := st.MethodByName(name) if !ret.IsValid() { fmt.Println("whoops") } L.PushGoFunction(GoLuaFunc(L, ret)) }
// goToLua copies Go values to Lua and sets the result to global 'name'. // Compound types are deep-copied. // Functions are automatically converted to 'func (L *lua.State) int'. func goToLua(L *lua.State, name string, val interface{}) { t := reflect.TypeOf(val) if t.Kind() == reflect.Func { L.PushGoFunction(luar.GoLuaFunc(L, val)) } else { luar.GoToLua(L, t, reflect.ValueOf(val), true) } L.SetGlobal(name) }
func MessThingMovetoMethod(state *lua.State, thing *Thing) int { state.PushGoFunction(func(state *lua.State) int { source := checkThing(state, 1) target := checkThing(state, 2) ok := source.MoveTo(target) state.PushBoolean(ok) return 1 }) return 1 }
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 }
// ProxyPairs implements Lua 5.2 'pairs' functions. // It respects the __pairs metamethod. // // It is only useful for compatibility with Lua 5.1. func ProxyPairs(L *lua.State) int { // See Lua >=5.2 source code. if L.GetMetaField(1, "__pairs") { L.PushValue(1) L.Call(1, 3) return 3 } L.CheckType(1, lua.LUA_TTABLE) L.PushGoFunction(pairsAux) L.PushValue(1) L.PushNil() return 3 }
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 MessThingFindinsideMethod(state *lua.State, thing *Thing) int { state.PushGoFunction(func(state *lua.State) int { thing := checkThing(state, 1) text := state.CheckString(2) if text == "" { state.ArgError(2, "cannot find empty string") } otherThing := thing.FindInside(text) if otherThing == nil { return 0 } pushValue(state, otherThing.Id) return 1 }) return 1 }
func slice__ipairs(L *lua.State) int { s, _ := valueOfProxy(L, 1) n := s.Len() idx := -1 iter := func(L *lua.State) int { idx++ if idx == n { L.PushNil() return 1 } GoToLua(L, nil, valueOf(idx+1), false) // report as 1-based index val := s.Index(idx) GoToLua(L, nil, val, false) return 2 } L.PushGoFunction(iter) return 1 }
func callGoMethod(L *lua.State, name string, st reflect.Value) { ret := st.MethodByName(name) if !ret.IsValid() { T := st.Type() // Could not resolve this method. Perhaps it's defined on the pointer? if T.Kind() != reflect.Ptr { if st.CanAddr() { // easy if we can get a pointer directly st = st.Addr() } else { // otherwise have to create and initialize one... VP := reflect.New(T) VP.Elem().Set(st) st = VP } } ret = st.MethodByName(name) assertValid(L, ret, st, name, "method") } L.PushGoFunction(GoLuaFunc(L, ret)) }
func map__pairs(L *lua.State) int { m, _ := valueOfProxy(L, 1) keys := m.MapKeys() idx := -1 n := m.Len() iter := func(L *lua.State) int { idx++ if idx == n { L.PushNil() return 1 } GoToLua(L, nil, keys[idx], false) val := m.MapIndex(keys[idx]) GoToLua(L, nil, val, false) return 2 } L.PushGoFunction(iter) return 1 }
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 }
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) }
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) } }
// TODO: Check if we really need multiple pointer levels since pointer methods // can be called on non-pointers. func goToLua(L *lua.State, t reflect.Type, val reflect.Value, dontproxify bool, visited visitor) { if !val.IsValid() { L.PushNil() return } // Unbox interface. if val.Kind() == reflect.Interface && !val.IsNil() { val = reflect.ValueOf(val.Interface()) } // Follow pointers if not proxifying. We save the original pointer Value in case we proxify. ptrVal := val for val.Kind() == reflect.Ptr { val = val.Elem() } if !val.IsValid() { L.PushNil() return } // As a special case, we always proxify nullv, the empty element for slices and maps. if val.CanInterface() && val.Interface() == nullv.Interface() { makeValueProxy(L, val, cInterfaceMeta) return } switch val.Kind() { case reflect.Float64, reflect.Float32: if !dontproxify && predeclaredScalarType(val.Type()) != nil { makeValueProxy(L, ptrVal, cNumberMeta) } else { L.PushNumber(val.Float()) } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: if !dontproxify && predeclaredScalarType(val.Type()) != nil { makeValueProxy(L, ptrVal, cNumberMeta) } else { L.PushNumber(float64(val.Int())) } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: if !dontproxify && predeclaredScalarType(val.Type()) != nil { makeValueProxy(L, ptrVal, cNumberMeta) } else { L.PushNumber(float64(val.Uint())) } case reflect.String: if !dontproxify && predeclaredScalarType(val.Type()) != nil { makeValueProxy(L, ptrVal, cStringMeta) } else { L.PushString(val.String()) } case reflect.Bool: if !dontproxify && predeclaredScalarType(val.Type()) != nil { makeValueProxy(L, ptrVal, cInterfaceMeta) } else { L.PushBoolean(val.Bool()) } case reflect.Complex128, reflect.Complex64: makeValueProxy(L, ptrVal, cComplexMeta) case reflect.Array: // It needs be a pointer to be a proxy, otherwise values won't be settable. if !dontproxify && ptrVal.Kind() == reflect.Ptr { makeValueProxy(L, ptrVal, cSliceMeta) } else { // See the case of struct. if ptrVal.Kind() == reflect.Ptr && visited.push(ptrVal) { return } copySliceToTable(L, ptrVal, visited) } case reflect.Slice: if !dontproxify { makeValueProxy(L, ptrVal, cSliceMeta) } else { if visited.push(val) { return } copySliceToTable(L, val, visited) } case reflect.Map: if !dontproxify { makeValueProxy(L, ptrVal, cMapMeta) } else { if visited.push(val) { return } copyMapToTable(L, val, visited) } case reflect.Struct: if !dontproxify && ptrVal.Kind() == reflect.Ptr { if ptrVal.CanInterface() { switch v := ptrVal.Interface().(type) { case error: L.PushString(v.Error()) case *LuaObject: v.Push() default: makeValueProxy(L, ptrVal, cStructMeta) } } else { makeValueProxy(L, ptrVal, cStructMeta) } } else { // Use ptrVal instead of val to detect cycles from the very first element, if a pointer. if ptrVal.Kind() == reflect.Ptr && visited.push(ptrVal) { return } copyStructToTable(L, ptrVal, visited) } case reflect.Chan: makeValueProxy(L, ptrVal, cChannelMeta) case reflect.Func: L.PushGoFunction(goLuaFunc(L, val)) default: if v, ok := val.Interface().(error); ok { L.PushString(v.Error()) } else if val.IsNil() { L.PushNil() } else { makeValueProxy(L, ptrVal, cInterfaceMeta) } } }