367 lines
7.4 KiB
Go
367 lines
7.4 KiB
Go
package luajit
|
|
|
|
/*
|
|
#include <lua.h>
|
|
*/
|
|
import "C"
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
)
|
|
|
|
// LuaType represents Lua value types
|
|
type LuaType int
|
|
|
|
const (
|
|
TypeNone LuaType = -1
|
|
TypeNil LuaType = 0
|
|
TypeBoolean LuaType = 1
|
|
TypeLightUserData LuaType = 2
|
|
TypeNumber LuaType = 3
|
|
TypeString LuaType = 4
|
|
TypeTable LuaType = 5
|
|
TypeFunction LuaType = 6
|
|
TypeUserData LuaType = 7
|
|
TypeThread LuaType = 8
|
|
)
|
|
|
|
func (t LuaType) String() string {
|
|
switch t {
|
|
case TypeNone:
|
|
return "none"
|
|
case TypeNil:
|
|
return "nil"
|
|
case TypeBoolean:
|
|
return "boolean"
|
|
case TypeLightUserData:
|
|
return "lightuserdata"
|
|
case TypeNumber:
|
|
return "number"
|
|
case TypeString:
|
|
return "string"
|
|
case TypeTable:
|
|
return "table"
|
|
case TypeFunction:
|
|
return "function"
|
|
case TypeUserData:
|
|
return "userdata"
|
|
case TypeThread:
|
|
return "thread"
|
|
default:
|
|
return "unknown"
|
|
}
|
|
}
|
|
|
|
// ConvertValue converts a value to the requested type with comprehensive type conversion
|
|
func ConvertValue[T any](value any) (T, bool) {
|
|
var zero T
|
|
|
|
if value == nil {
|
|
return zero, false
|
|
}
|
|
|
|
if result, ok := value.(T); ok {
|
|
return result, true
|
|
}
|
|
|
|
switch any(zero).(type) {
|
|
case string:
|
|
return convertToString[T](value)
|
|
case int:
|
|
return convertToInt[T](value)
|
|
case float64:
|
|
return convertToFloat[T](value)
|
|
case bool:
|
|
return convertToBool[T](value)
|
|
case []int:
|
|
return convertToIntSlice[T](value)
|
|
case []string:
|
|
return convertToStringSlice[T](value)
|
|
case []bool:
|
|
return convertToBoolSlice[T](value)
|
|
case []float64:
|
|
return convertToFloatSlice[T](value)
|
|
case []any:
|
|
return convertToAnySlice[T](value)
|
|
case map[string]string:
|
|
return convertToStringMap[T](value)
|
|
case map[string]int:
|
|
return convertToIntMap[T](value)
|
|
case map[int]any:
|
|
return convertToIntKeyMap[T](value)
|
|
case map[string]any:
|
|
return convertToAnyMap[T](value)
|
|
}
|
|
|
|
return zero, false
|
|
}
|
|
|
|
func convertToString[T any](value any) (T, bool) {
|
|
var zero T
|
|
switch v := value.(type) {
|
|
case float64:
|
|
if v == float64(int(v)) {
|
|
return any(strconv.Itoa(int(v))).(T), true
|
|
}
|
|
return any(fmt.Sprintf("%g", v)).(T), true
|
|
case int:
|
|
return any(strconv.Itoa(v)).(T), true
|
|
case bool:
|
|
return any(strconv.FormatBool(v)).(T), true
|
|
}
|
|
return zero, false
|
|
}
|
|
|
|
func convertToInt[T any](value any) (T, bool) {
|
|
var zero T
|
|
switch v := value.(type) {
|
|
case float64:
|
|
return any(int(v)).(T), true
|
|
case string:
|
|
if i, err := strconv.Atoi(v); err == nil {
|
|
return any(i).(T), true
|
|
}
|
|
case bool:
|
|
if v {
|
|
return any(1).(T), true
|
|
}
|
|
return any(0).(T), true
|
|
}
|
|
return zero, false
|
|
}
|
|
|
|
func convertToFloat[T any](value any) (T, bool) {
|
|
var zero T
|
|
switch v := value.(type) {
|
|
case int:
|
|
return any(float64(v)).(T), true
|
|
case string:
|
|
if f, err := strconv.ParseFloat(v, 64); err == nil {
|
|
return any(f).(T), true
|
|
}
|
|
case bool:
|
|
if v {
|
|
return any(1.0).(T), true
|
|
}
|
|
return any(0.0).(T), true
|
|
}
|
|
return zero, false
|
|
}
|
|
|
|
func convertToBool[T any](value any) (T, bool) {
|
|
var zero T
|
|
switch v := value.(type) {
|
|
case string:
|
|
switch v {
|
|
case "true", "yes", "1":
|
|
return any(true).(T), true
|
|
case "false", "no", "0":
|
|
return any(false).(T), true
|
|
}
|
|
case int:
|
|
return any(v != 0).(T), true
|
|
case float64:
|
|
return any(v != 0).(T), true
|
|
}
|
|
return zero, false
|
|
}
|
|
|
|
func convertToIntSlice[T any](value any) (T, bool) {
|
|
var zero T
|
|
switch v := value.(type) {
|
|
case []float64:
|
|
result := make([]int, len(v))
|
|
for i, f := range v {
|
|
result[i] = int(f)
|
|
}
|
|
return any(result).(T), true
|
|
case []any:
|
|
result := make([]int, 0, len(v))
|
|
for _, item := range v {
|
|
if i, ok := ConvertValue[int](item); ok {
|
|
result = append(result, i)
|
|
} else {
|
|
return zero, false
|
|
}
|
|
}
|
|
return any(result).(T), true
|
|
}
|
|
return zero, false
|
|
}
|
|
|
|
func convertToStringSlice[T any](value any) (T, bool) {
|
|
var zero T
|
|
if v, ok := value.([]any); ok {
|
|
result := make([]string, 0, len(v))
|
|
for _, item := range v {
|
|
if s, ok := ConvertValue[string](item); ok {
|
|
result = append(result, s)
|
|
} else {
|
|
return zero, false
|
|
}
|
|
}
|
|
return any(result).(T), true
|
|
}
|
|
return zero, false
|
|
}
|
|
|
|
func convertToBoolSlice[T any](value any) (T, bool) {
|
|
var zero T
|
|
if v, ok := value.([]any); ok {
|
|
result := make([]bool, 0, len(v))
|
|
for _, item := range v {
|
|
if b, ok := ConvertValue[bool](item); ok {
|
|
result = append(result, b)
|
|
} else {
|
|
return zero, false
|
|
}
|
|
}
|
|
return any(result).(T), true
|
|
}
|
|
return zero, false
|
|
}
|
|
|
|
func convertToFloatSlice[T any](value any) (T, bool) {
|
|
var zero T
|
|
switch v := value.(type) {
|
|
case []int:
|
|
result := make([]float64, len(v))
|
|
for i, n := range v {
|
|
result[i] = float64(n)
|
|
}
|
|
return any(result).(T), true
|
|
case []any:
|
|
result := make([]float64, 0, len(v))
|
|
for _, item := range v {
|
|
if f, ok := ConvertValue[float64](item); ok {
|
|
result = append(result, f)
|
|
} else {
|
|
return zero, false
|
|
}
|
|
}
|
|
return any(result).(T), true
|
|
}
|
|
return zero, false
|
|
}
|
|
|
|
func convertToAnySlice[T any](value any) (T, bool) {
|
|
var zero T
|
|
switch v := value.(type) {
|
|
case []int:
|
|
result := make([]any, len(v))
|
|
for i, n := range v {
|
|
result[i] = n
|
|
}
|
|
return any(result).(T), true
|
|
case []string:
|
|
result := make([]any, len(v))
|
|
for i, s := range v {
|
|
result[i] = s
|
|
}
|
|
return any(result).(T), true
|
|
case []bool:
|
|
result := make([]any, len(v))
|
|
for i, b := range v {
|
|
result[i] = b
|
|
}
|
|
return any(result).(T), true
|
|
case []float64:
|
|
result := make([]any, len(v))
|
|
for i, f := range v {
|
|
result[i] = f
|
|
}
|
|
return any(result).(T), true
|
|
}
|
|
return zero, false
|
|
}
|
|
|
|
func convertToStringMap[T any](value any) (T, bool) {
|
|
var zero T
|
|
if v, ok := value.(map[string]any); ok {
|
|
result := make(map[string]string, len(v))
|
|
for k, val := range v {
|
|
if s, ok := ConvertValue[string](val); ok {
|
|
result[k] = s
|
|
} else {
|
|
return zero, false
|
|
}
|
|
}
|
|
return any(result).(T), true
|
|
}
|
|
return zero, false
|
|
}
|
|
|
|
func convertToIntMap[T any](value any) (T, bool) {
|
|
var zero T
|
|
if v, ok := value.(map[string]any); ok {
|
|
result := make(map[string]int, len(v))
|
|
for k, val := range v {
|
|
if i, ok := ConvertValue[int](val); ok {
|
|
result[k] = i
|
|
} else {
|
|
return zero, false
|
|
}
|
|
}
|
|
return any(result).(T), true
|
|
}
|
|
return zero, false
|
|
}
|
|
|
|
func convertToIntKeyMap[T any](value any) (T, bool) {
|
|
var zero T
|
|
if v, ok := value.(map[string]any); ok {
|
|
result := make(map[int]any, len(v))
|
|
for k, val := range v {
|
|
if i, err := strconv.Atoi(k); err == nil {
|
|
result[i] = val
|
|
} else {
|
|
return zero, false
|
|
}
|
|
}
|
|
return any(result).(T), true
|
|
}
|
|
return zero, false
|
|
}
|
|
|
|
func convertToAnyMap[T any](value any) (T, bool) {
|
|
var zero T
|
|
switch v := value.(type) {
|
|
case map[string]string:
|
|
result := make(map[string]any, len(v))
|
|
for k, s := range v {
|
|
result[k] = s
|
|
}
|
|
return any(result).(T), true
|
|
case map[string]int:
|
|
result := make(map[string]any, len(v))
|
|
for k, i := range v {
|
|
result[k] = i
|
|
}
|
|
return any(result).(T), true
|
|
case map[int]any:
|
|
result := make(map[string]any, len(v))
|
|
for k, val := range v {
|
|
result[strconv.Itoa(k)] = val
|
|
}
|
|
return any(result).(T), true
|
|
}
|
|
return zero, false
|
|
}
|
|
|
|
// GetTypedValue gets a value from the state with type conversion
|
|
func GetTypedValue[T any](s *State, index int) (T, bool) {
|
|
value, err := s.ToValue(index)
|
|
if err != nil {
|
|
var zero T
|
|
return zero, false
|
|
}
|
|
return ConvertValue[T](value)
|
|
}
|
|
|
|
// GetGlobalTyped gets a global variable with type conversion
|
|
func GetGlobalTyped[T any](s *State, name string) (T, bool) {
|
|
s.GetGlobal(name)
|
|
defer s.Pop(1)
|
|
return GetTypedValue[T](s, -1)
|
|
}
|