package main /* #cgo LDFLAGS: -llua #include #include #include #include extern int luna_run_go_func(uintptr_t); static inline int luna_run(lua_State *l) { return luna_run_go_func(lua_tonumber(l, lua_upvalueindex(1))); } static inline void luna_push_function(lua_State *l, uintptr_t f) { lua_pushinteger(l, f); lua_pushcclosure(l, luna_run, 1); } */ import "C" import ( "errors" "log" "runtime/cgo" "unsafe" ) type LuaRef C.longlong type Lua struct { l *C.lua_State } //export luna_run_go_func func luna_run_go_func(f C.uintptr_t) C.int { log.Println(f) fn := cgo.Handle(f).Value().(func() int) return C.int(fn()) } func (l *Lua) Start() { l.l = C.luaL_newstate() C.luaL_openlibs(l.l) } func (l *Lua) Close() { C.lua_close(l.l) C.free(unsafe.Pointer(l.l)) } func (l *Lua) Require(file string) (LuaRef, error) { if !l.LoadFile(file) { errMsg := l.ToString(-1) l.Pop(1) return 0, errors.New("could not open the file:\n" + errMsg) } if !l.PCall(0, C.LUA_MULTRET) { errMsg := l.ToString(-1) l.Pop(1) return 0, errors.New("could not execute the file:\n" + errMsg) } if !l.IsTable(-1) { return 0, errors.New("module did not return a table") } return l.PopToRef(), nil } func (l *Lua) PCall(nargs int, nresults int) bool { res := C.lua_pcallk(l.l, C.int(nargs), C.int(nresults), 0, 0, nil) return res == C.LUA_OK } func (l *Lua) LoadFile(file string) bool { cfile := C.CString(file) defer C.free(unsafe.Pointer(cfile)) res := C.luaL_loadfilex(l.l, cfile, nil) return res == C.LUA_OK } func (l *Lua) StackLen() int { return int(C.lua_gettop(l.l)) } func (l *Lua) ToString(index int) string { return C.GoString(C.lua_tolstring(l.l, C.int(index), nil)) } func (l *Lua) ToInt(index int) int { return int(C.lua_tonumberx(l.l, C.int(index), nil)) } func (l *Lua) PopToRef() LuaRef { return LuaRef(C.luaL_ref(l.l, C.LUA_REGISTRYINDEX)) } func (l *Lua) Pop(n int) { C.lua_settop(l.l, C.int(-n - 1)) } func (l *Lua) PushNil() { C.lua_pushnil(l.l) } func (l *Lua) PushString(str string) { cstr := C.CString(str) defer C.free(unsafe.Pointer(cstr)) C.lua_pushstring(l.l, cstr) } // You may need to delete handle with .Delete() method later. func (l *Lua) PushGoFunction(f func (l *Lua) int) cgo.Handle { h := cgo.NewHandle(func () int { return f(l) }) C.luna_push_function(l.l, C.uintptr_t(h)) return h } // CreateTable pushes a new table onto the stack. func (l *Lua) CreateTable(len int) { C.lua_createtable(l.l, 0, C.int(len)) } func (l *Lua) SetTableItem(key string) { cstr := C.CString(key) defer C.free(unsafe.Pointer(cstr)) C.lua_setfield(l.l, -2, C.CString(key)) } func (l *Lua) PushStringTable(table map[string]string) { l.CreateTable(len(table)) for k, v := range table { l.PushString(v) C.lua_setfield(l.l, -2, C.CString(k)) } } func (l *Lua) PushFromRef(ref LuaRef) { C.lua_rawgeti(l.l, C.LUA_REGISTRYINDEX, C.longlong(ref)); } func (l *Lua) PushTableItem(key string) { ckey := C.CString(key) defer C.free(unsafe.Pointer(ckey)) C.lua_pushstring(l.l, ckey); C.lua_gettable(l.l, -2); } func (l *Lua) IsString(index int) bool { return C.lua_isstring(l.l, C.int(index)) == 1 } func (l *Lua) IsNumber(index int) bool { return C.lua_isnumber(l.l, C.int(index)) == 1 } func (l *Lua) IsFunction(index int) bool { return C.lua_type(l.l, C.int(index)) == C.LUA_TFUNCTION } func (l *Lua) IsTable(index int) bool { return C.lua_type(l.l, C.int(index)) == C.LUA_TTABLE } func (l *Lua) Next() bool { return C.lua_next(l.l, -2) != 0 } func (l *Lua) LoadString(code string) error { cstr := C.CString(code) defer C.free(unsafe.Pointer(cstr)) if C.luaL_loadstring(l.l, cstr) != C.LUA_OK { errMsg := l.ToString(-1) l.Pop(1) return errors.New(errMsg) } if !l.PCall(0, 0) { errMsg := l.ToString(-1) l.Pop(1) return errors.New(errMsg) } l.Pop(l.StackLen()) return nil } func (l *Lua) RestoreStackFunc() func () { before := l.StackLen() return func () { after := l.StackLen() diff := after - before if diff == 0 { return } else if diff < 0 { log.Fatalf( "too many stack pops: len before: %d, after: %d\n", before, after, ) } C.lua_settop(l.l, C.int(before)) } } func (l *Lua) SetGlobal(name string) { cstr := C.CString(name) defer C.free(unsafe.Pointer(cstr)) C.lua_setglobal(l.l, cstr) }