scream/vendor/src/github.com/layeh/gopher-luar/luar.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")
}