178 lines
3.9 KiB
Go
178 lines
3.9 KiB
Go
package luajit
|
|
|
|
/*
|
|
#include <lua.h>
|
|
#include <lualib.h>
|
|
#include <lauxlib.h>
|
|
#include <stdlib.h>
|
|
|
|
static int get_table_length(lua_State *L, int index) {
|
|
return lua_objlen(L, index);
|
|
}
|
|
*/
|
|
import "C"
|
|
import (
|
|
"fmt"
|
|
)
|
|
|
|
// TableValue represents any value that can be stored in a Lua table
|
|
type TableValue interface {
|
|
~string | ~float64 | ~bool | ~int | ~map[string]interface{} | ~[]float64 | ~[]interface{}
|
|
}
|
|
|
|
func (s *State) GetTableLength(index int) int { return int(C.get_table_length(s.L, C.int(index))) }
|
|
|
|
// ToTable converts a Lua table to a Go map
|
|
func (s *State) ToTable(index int) (map[string]interface{}, error) {
|
|
if s.safeStack {
|
|
return stackGuardValue[map[string]interface{}](s, func() (map[string]interface{}, error) {
|
|
if !s.IsTable(index) {
|
|
return nil, fmt.Errorf("not a table at index %d", index)
|
|
}
|
|
return s.toTableUnsafe(index)
|
|
})
|
|
}
|
|
if !s.IsTable(index) {
|
|
return nil, fmt.Errorf("not a table at index %d", index)
|
|
}
|
|
return s.toTableUnsafe(index)
|
|
}
|
|
|
|
func (s *State) pushTableSafe(table map[string]interface{}) error {
|
|
size := 2
|
|
if err := s.checkStack(size); err != nil {
|
|
return fmt.Errorf("insufficient stack space: %w", err)
|
|
}
|
|
|
|
s.NewTable()
|
|
for k, v := range table {
|
|
if err := s.pushValueSafe(v); err != nil {
|
|
return err
|
|
}
|
|
s.SetField(-2, k)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *State) pushTableUnsafe(table map[string]interface{}) error {
|
|
s.NewTable()
|
|
for k, v := range table {
|
|
if err := s.pushValueUnsafe(v); err != nil {
|
|
return err
|
|
}
|
|
s.SetField(-2, k)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *State) toTableSafe(index int) (map[string]interface{}, error) {
|
|
if err := s.checkStack(2); err != nil {
|
|
return nil, err
|
|
}
|
|
return s.toTableUnsafe(index)
|
|
}
|
|
|
|
func (s *State) toTableUnsafe(index int) (map[string]interface{}, error) {
|
|
absIdx := s.absIndex(index)
|
|
table := make(map[string]interface{})
|
|
|
|
// Check if it's an array-like table
|
|
length := s.GetTableLength(absIdx)
|
|
if length > 0 {
|
|
array := make([]float64, length)
|
|
isArray := true
|
|
|
|
// Try to convert to array
|
|
for i := 1; i <= length; i++ {
|
|
s.PushNumber(float64(i))
|
|
s.GetTable(absIdx)
|
|
if s.GetType(-1) != TypeNumber {
|
|
isArray = false
|
|
s.Pop(1)
|
|
break
|
|
}
|
|
array[i-1] = s.ToNumber(-1)
|
|
s.Pop(1)
|
|
}
|
|
|
|
if isArray {
|
|
return map[string]interface{}{"": array}, nil
|
|
}
|
|
}
|
|
|
|
// Handle regular table
|
|
s.PushNil()
|
|
for C.lua_next(s.L, C.int(absIdx)) != 0 {
|
|
key := ""
|
|
valueType := C.lua_type(s.L, -2)
|
|
if valueType == C.LUA_TSTRING {
|
|
key = s.ToString(-2)
|
|
} else if valueType == C.LUA_TNUMBER {
|
|
key = fmt.Sprintf("%g", s.ToNumber(-2))
|
|
}
|
|
|
|
value, err := s.toValueUnsafe(-1)
|
|
if err != nil {
|
|
s.Pop(1)
|
|
return nil, err
|
|
}
|
|
|
|
// Handle nested array case
|
|
if m, ok := value.(map[string]interface{}); ok {
|
|
if arr, ok := m[""]; ok {
|
|
value = arr
|
|
}
|
|
}
|
|
|
|
table[key] = value
|
|
s.Pop(1)
|
|
}
|
|
|
|
return table, nil
|
|
}
|
|
|
|
// NewTable creates a new table and pushes it onto the stack
|
|
func (s *State) NewTable() {
|
|
if s.safeStack {
|
|
if err := s.checkStack(1); err != nil {
|
|
// Since we can't return an error, we'll push nil instead
|
|
s.PushNil()
|
|
return
|
|
}
|
|
}
|
|
C.lua_createtable(s.L, 0, 0)
|
|
}
|
|
|
|
// SetTable sets a table field with cached absolute index
|
|
func (s *State) SetTable(index int) {
|
|
absIdx := index
|
|
if s.safeStack && (index < 0 && index > LUA_REGISTRYINDEX) {
|
|
absIdx = s.GetTop() + index + 1
|
|
}
|
|
C.lua_settable(s.L, C.int(absIdx))
|
|
}
|
|
|
|
// GetTable gets a table field with cached absolute index
|
|
func (s *State) GetTable(index int) {
|
|
absIdx := index
|
|
if s.safeStack && (index < 0 && index > LUA_REGISTRYINDEX) {
|
|
absIdx = s.GetTop() + index + 1
|
|
}
|
|
|
|
if s.safeStack {
|
|
if err := s.checkStack(1); err != nil {
|
|
s.PushNil()
|
|
return
|
|
}
|
|
}
|
|
C.lua_gettable(s.L, C.int(absIdx))
|
|
}
|
|
|
|
// PushTable pushes a Go map onto the Lua stack as a table with stack checking
|
|
func (s *State) PushTable(table map[string]interface{}) error {
|
|
if s.safeStack {
|
|
return s.pushTableSafe(table)
|
|
}
|
|
return s.pushTableUnsafe(table)
|
|
}
|