package luajit /* #include #include #include #include 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) }