227 lines
5.6 KiB
Go
227 lines
5.6 KiB
Go
package luar
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
|
|
"github.com/yuin/gopher-lua"
|
|
)
|
|
|
|
var wrapperMetatable map[string]map[string]lua.LGFunction
|
|
|
|
func init() {
|
|
wrapperMetatable = map[string]map[string]lua.LGFunction{
|
|
"chan": {
|
|
"__index": chanIndex,
|
|
"__len": chanLen,
|
|
"__tostring": allTostring,
|
|
"__eq": chanEq,
|
|
},
|
|
"map": {
|
|
"__index": mapIndex,
|
|
"__newindex": mapNewIndex,
|
|
"__len": mapLen,
|
|
"__call": mapCall,
|
|
"__tostring": allTostring,
|
|
"__eq": mapEq,
|
|
},
|
|
"ptr": {
|
|
"__index": ptrIndex,
|
|
"__newindex": ptrNewIndex,
|
|
"__pow": ptrPow,
|
|
"__call": ptrCall,
|
|
"__tostring": allTostring,
|
|
"__unm": ptrUnm,
|
|
"__eq": ptrEq,
|
|
},
|
|
"slice": {
|
|
"__index": sliceIndex,
|
|
"__newindex": sliceNewIndex,
|
|
"__len": sliceLen,
|
|
"__tostring": allTostring,
|
|
"__eq": sliceEq,
|
|
},
|
|
"struct": {
|
|
"__index": structIndex,
|
|
"__newindex": structNewIndex,
|
|
"__call": structCall,
|
|
"__tostring": allTostring,
|
|
},
|
|
"type": {
|
|
"__call": typeCall,
|
|
"__tostring": allTostring,
|
|
"__eq": typeEq,
|
|
},
|
|
}
|
|
}
|
|
|
|
func ensureMetatable(L *lua.LState) *lua.LTable {
|
|
const metatableKey = lua.LString("github.com/layeh/gopher-luar")
|
|
v := L.G.Registry.RawGetH(metatableKey)
|
|
if v != lua.LNil {
|
|
return v.(*lua.LTable)
|
|
}
|
|
newTable := L.NewTable()
|
|
|
|
for typeName, meta := range wrapperMetatable {
|
|
typeTable := L.NewTable()
|
|
typeTable.RawSetH(lua.LString("__metatable"), lua.LTrue)
|
|
for methodName, methodFunc := range meta {
|
|
typeTable.RawSetH(lua.LString(methodName), L.NewFunction(methodFunc))
|
|
}
|
|
newTable.RawSetH(lua.LString(typeName), typeTable)
|
|
}
|
|
|
|
L.G.Registry.RawSetH(metatableKey, newTable)
|
|
return newTable
|
|
}
|
|
|
|
func allTostring(L *lua.LState) int {
|
|
ud := L.CheckUserData(1)
|
|
value := ud.Value
|
|
if stringer, ok := value.(fmt.Stringer); ok {
|
|
L.Push(lua.LString(stringer.String()))
|
|
} else {
|
|
L.Push(lua.LString(fmt.Sprintf("userdata (luar): %p", ud)))
|
|
}
|
|
return 1
|
|
}
|
|
|
|
// New creates and returns a new lua.LValue for the given value.
|
|
//
|
|
// The following types are supported:
|
|
// Kind gopher-lua Type
|
|
// -------------------------------
|
|
// nil LNil
|
|
// Bool LBool
|
|
// Int LNumber
|
|
// Int8 LNumber
|
|
// Int16 LNumber
|
|
// Int32 LNumber
|
|
// Int64 LNumber
|
|
// Uint LNumber
|
|
// Uint8 LNumber
|
|
// Uint32 LNumber
|
|
// Uint64 LNumber
|
|
// Float32 LNumber
|
|
// Float64 LNumber
|
|
// Complex64 *LUserData
|
|
// Complex128 *LUserData
|
|
// Array *LUserData
|
|
// Chan *LUserData
|
|
// Interface *LUserData
|
|
// Func *lua.LFunction
|
|
// Map *LUserData
|
|
// Ptr *LUserData
|
|
// Slice *LUserData
|
|
// String LString
|
|
// Struct *LUserData
|
|
// UnsafePointer *LUserData
|
|
func New(L *lua.LState, value interface{}) lua.LValue {
|
|
if value == nil {
|
|
return lua.LNil
|
|
}
|
|
if lval, ok := value.(lua.LValue); ok {
|
|
return lval
|
|
}
|
|
|
|
val := reflect.ValueOf(value)
|
|
switch val.Kind() {
|
|
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
|
|
if val.IsNil() {
|
|
return lua.LNil
|
|
}
|
|
}
|
|
|
|
table := ensureMetatable(L)
|
|
|
|
switch val.Kind() {
|
|
case reflect.Bool:
|
|
return lua.LBool(val.Bool())
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
return lua.LNumber(float64(val.Int()))
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
return lua.LNumber(float64(val.Uint()))
|
|
case reflect.Float32, reflect.Float64:
|
|
return lua.LNumber(val.Float())
|
|
case reflect.Chan:
|
|
ud := L.NewUserData()
|
|
ud.Value = val.Interface()
|
|
ud.Metatable = table.RawGetH(lua.LString("chan"))
|
|
return ud
|
|
case reflect.Func:
|
|
return funcWrapper(L, val)
|
|
case reflect.Interface:
|
|
ud := L.NewUserData()
|
|
ud.Value = val.Interface()
|
|
return ud
|
|
case reflect.Map:
|
|
ud := L.NewUserData()
|
|
ud.Value = val.Interface()
|
|
ud.Metatable = table.RawGetH(lua.LString("map"))
|
|
return ud
|
|
case reflect.Ptr:
|
|
ud := L.NewUserData()
|
|
ud.Value = val.Interface()
|
|
ud.Metatable = table.RawGetH(lua.LString("ptr"))
|
|
return ud
|
|
case reflect.Slice:
|
|
ud := L.NewUserData()
|
|
ud.Value = val.Interface()
|
|
ud.Metatable = table.RawGetH(lua.LString("slice"))
|
|
return ud
|
|
case reflect.String:
|
|
return lua.LString(val.String())
|
|
case reflect.Struct:
|
|
ud := L.NewUserData()
|
|
ud.Value = val.Interface()
|
|
ud.Metatable = table.RawGetH(lua.LString("struct"))
|
|
return ud
|
|
default:
|
|
ud := L.NewUserData()
|
|
ud.Value = val.Interface()
|
|
return ud
|
|
}
|
|
}
|
|
|
|
// NewType returns a new type creator for the given value's type.
|
|
//
|
|
// When the lua.LValue is called, a new value will be created that is the
|
|
// same type as value's type.
|
|
func NewType(L *lua.LState, value interface{}) lua.LValue {
|
|
table := ensureMetatable(L)
|
|
|
|
val := reflect.TypeOf(value)
|
|
ud := L.NewUserData()
|
|
ud.Value = val
|
|
ud.Metatable = table.RawGetH(lua.LString("type"))
|
|
return ud
|
|
}
|
|
|
|
func lValueToReflect(v lua.LValue, hint reflect.Type) reflect.Value {
|
|
if hint == refTypeLuaLValue {
|
|
return reflect.ValueOf(v)
|
|
}
|
|
switch converted := v.(type) {
|
|
case lua.LBool:
|
|
return reflect.ValueOf(bool(converted))
|
|
case lua.LChannel:
|
|
return reflect.ValueOf(converted)
|
|
case lua.LNumber:
|
|
return reflect.ValueOf(converted).Convert(hint)
|
|
case *lua.LFunction:
|
|
return reflect.ValueOf(converted)
|
|
case *lua.LNilType:
|
|
return reflect.Zero(hint)
|
|
case *lua.LState:
|
|
return reflect.ValueOf(converted)
|
|
case lua.LString:
|
|
return reflect.ValueOf(string(converted))
|
|
case *lua.LTable:
|
|
return reflect.ValueOf(converted)
|
|
case *lua.LUserData:
|
|
return reflect.ValueOf(converted.Value)
|
|
}
|
|
panic("fatal lValueToReflect error")
|
|
}
|