197 lines
4.0 KiB
Go
197 lines
4.0 KiB
Go
package glua
|
|
|
|
/*
|
|
* Self-contained Go-Lua binding, which handles inter-FFI errors seamlessly unlike others.
|
|
* */
|
|
|
|
// #cgo pkg-config: lua
|
|
// #cgo LDFLAGS: -L./ -lcglua
|
|
// #include<lua.h>
|
|
// #include<lualib.h>
|
|
// #include<lauxlib.h>
|
|
// #include<stdlib.h>
|
|
// extern int glua_metatable_call(lua_State*);
|
|
// extern int glua_metatable_gc(lua_State*);
|
|
// extern int glua_metatable_tostring(lua_State*);
|
|
import "C"
|
|
|
|
import "fmt"
|
|
|
|
import "unsafe"
|
|
|
|
type Function func(l *Lua) int
|
|
|
|
type FuncStatePair struct {
|
|
fn Function
|
|
l *Lua
|
|
}
|
|
var FuncIDs map[uint64]FuncStatePair = make(map[uint64]FuncStatePair)
|
|
var NextFuncID uint64 = 0
|
|
|
|
//export glua_go_call_func
|
|
func glua_go_call_func(id C.uint64_t) (ret C.int) {
|
|
pair := FuncIDs[uint64(id)]
|
|
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
pair.l.PushString(fmt.Sprint(r))
|
|
ret = -1
|
|
}
|
|
}()
|
|
|
|
return C.int(pair.fn(pair.l))
|
|
}
|
|
|
|
//export glua_go_remove_func
|
|
func glua_go_remove_func(id C.uint64_t) {
|
|
delete(FuncIDs, uint64(id))
|
|
}
|
|
|
|
type Lua struct {
|
|
state *C.lua_State
|
|
}
|
|
|
|
func NewState() Lua {
|
|
l := Lua{C.luaL_newstate()}
|
|
|
|
C.luaL_newmetatable(l.state, C.CString("GLUA_FUNCTION"))
|
|
C.lua_pushcclosure(l.state, C.lua_CFunction(C.glua_metatable_call), 0)
|
|
l.SetField(-2, "__call")
|
|
|
|
C.lua_pushcclosure(l.state, C.lua_CFunction(C.glua_metatable_gc), 0)
|
|
l.SetField(-2, "__gc")
|
|
|
|
C.lua_pushcclosure(l.state, C.lua_CFunction(C.glua_metatable_tostring), 0)
|
|
l.SetField(-2, "__tostring")
|
|
l.Pop(1)
|
|
|
|
return l
|
|
}
|
|
|
|
func (l Lua) Close() {
|
|
C.lua_close(l.state)
|
|
}
|
|
|
|
func (l Lua) OpenLibs() {
|
|
C.luaL_openlibs(l.state)
|
|
}
|
|
|
|
func (l Lua) NewTable() {
|
|
C.lua_createtable(l.state, 0, 0)
|
|
}
|
|
|
|
func (l Lua) SetField(offset int, name string) {
|
|
cstr := C.CString(name)
|
|
C.lua_setfield(l.state, C.int(offset), cstr)
|
|
C.free(unsafe.Pointer(cstr))
|
|
}
|
|
|
|
func (l Lua) SetGlobal(name string) {
|
|
cstr := C.CString(name)
|
|
C.lua_setglobal(l.state, cstr)
|
|
C.free(unsafe.Pointer(cstr))
|
|
}
|
|
|
|
func (l Lua) GetGlobal(name string) {
|
|
cstr := C.CString(name)
|
|
C.lua_getglobal(l.state, cstr)
|
|
C.free(unsafe.Pointer(cstr))
|
|
}
|
|
|
|
func (l Lua) DoFile(filepath string) int {
|
|
cstr := C.CString(filepath)
|
|
defer C.free(unsafe.Pointer(cstr))
|
|
|
|
err := int(C.luaL_loadfilex(l.state, cstr, nil))
|
|
if err != 0 {
|
|
return err
|
|
}
|
|
|
|
err = int(C.lua_pcallk(l.state, 0, -1, 0, 0, nil))
|
|
if err != 0 {
|
|
return err
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
func (l Lua) ToInteger(offset int) int64 {
|
|
return int64(C.lua_tointegerx(l.state, C.int(offset), nil))
|
|
}
|
|
|
|
func (l Lua) ToNumber(offset int) float64 {
|
|
return float64(C.lua_tonumberx(l.state, C.int(offset), nil))
|
|
}
|
|
|
|
func (l Lua) ToBoolean(offset int) bool {
|
|
return C.lua_toboolean(l.state, C.int(offset)) != 0
|
|
}
|
|
|
|
func (l Lua) ToString(offset int) string {
|
|
var len C.size_t
|
|
cstr := C.lua_tolstring(l.state, C.int(offset), &len)
|
|
return C.GoStringN(cstr, C.int(len))
|
|
}
|
|
|
|
func (l Lua) PushInteger(i int64) {
|
|
C.lua_pushinteger(l.state, C.lua_Integer(i))
|
|
}
|
|
|
|
func (l Lua) PushNumber(f float64) {
|
|
C.lua_pushnumber(l.state, C.lua_Number(f))
|
|
}
|
|
|
|
func (l Lua) PushString(s string) {
|
|
cstr := C.CString(s)
|
|
C.lua_pushstring(l.state, cstr)
|
|
C.free(unsafe.Pointer(cstr))
|
|
}
|
|
|
|
func (l Lua) PushFunction(fn Function) {
|
|
i := NextFuncID
|
|
for true {
|
|
if _, exists := FuncIDs[i]; !exists {
|
|
break
|
|
}
|
|
i += 1
|
|
}
|
|
|
|
ud := C.lua_newuserdatauv(l.state, 8, 1)
|
|
|
|
*(*C.uint64_t)(ud) = C.uint64_t(i)
|
|
|
|
cstr := C.CString("GLUA_FUNCTION")
|
|
C.luaL_setmetatable(l.state, cstr)
|
|
C.free(unsafe.Pointer(cstr))
|
|
|
|
FuncIDs[i] = FuncStatePair{fn, &l};
|
|
}
|
|
|
|
func (l Lua) PushValue(offset int) {
|
|
C.lua_pushvalue(l.state, C.int(offset));
|
|
}
|
|
|
|
func (l Lua) Pop(count int) {
|
|
C.lua_settop(l.state, C.int(-count - 1));
|
|
}
|
|
|
|
func (l Lua) Call(nargs int, nresults int, msgh int) int {
|
|
err := int(C.lua_pcallk(l.state, C.int(nargs), C.int(nresults), C.int(msgh), 0, nil))
|
|
if err == 2 {
|
|
l.PushValue(-1)
|
|
why := l.ToString(-1)
|
|
l.Pop(1)
|
|
|
|
panic(why)
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (l Lua) ProtectedCall(nargs int, nresults int, msgh int) int {
|
|
return int(C.lua_pcallk(l.state, C.int(nargs), C.int(nresults), C.int(msgh), 0, nil))
|
|
}
|
|
|
|
func (l Lua) UnsafeCall(nargs int, nresults int) {
|
|
C.lua_callk(l.state, C.int(nargs), C.int(nresults), 0, nil)
|
|
}
|