#include "values.hpp" #include "luax.hpp" using rapidjson::Value; using rapidjson::SizeType; namespace values { namespace details { static Value NumberValue(lua_State* L, int idx); static Value StringValue(lua_State* L, int idx, Allocator& allocator); static Value TableValue(lua_State* L, int idx, int depth, Allocator& allocator); static Value ObjectValue(lua_State* L, int idx, int depth, Allocator& allocator); static Value ArrayValue(lua_State* L, int idx, int depth, Allocator& allocator); Value toValue(lua_State* L, int idx, int depth, Allocator& allocator) { auto t = lua_type(L, idx); switch (t) { case LUA_TBOOLEAN: return Value(lua_toboolean(L, idx) != 0); case LUA_TNUMBER: return NumberValue(L, idx); case LUA_TSTRING: return StringValue(L, idx, allocator); case LUA_TTABLE: return TableValue(L, idx, depth + 1, allocator); case LUA_TNIL: return Value(); case LUA_TFUNCTION: if (isnull(L, idx)) return Value(); // otherwise fall thought case LUA_TLIGHTUSERDATA: // fall thought case LUA_TUSERDATA: // fall thought case LUA_TTHREAD: // fall thought case LUA_TNONE: // fall thought default: luaL_error(L, "value type %s is not a valid json value", lua_typename(L, t)); return Value(); // Just make compiler happy } } Value NumberValue(lua_State* L, int idx) { int64_t integer; return luax::isinteger(L, idx, &integer) ? Value(integer) : Value(lua_tonumber(L, idx)); } Value StringValue(lua_State* L, int idx, Allocator& allocator) { size_t len; const char* s = lua_tolstring(L, idx, &len); return Value(s, static_cast(len), allocator); } Value TableValue(lua_State* L, int idx, int depth, Allocator& allocator) { if (depth > 1024) luaL_error(L, "nested too depth"); if (!lua_checkstack(L, 4)) // requires at least 4 slots in stack: table, key, value, key luaL_error(L, "stack overflow"); return isarray(L, idx) ? ArrayValue(L, idx, depth, allocator) : ObjectValue(L, idx, depth, allocator); } Value ObjectValue(lua_State* L, int idx, int depth, Allocator& allocator) { Value object(rapidjson::kObjectType); lua_pushvalue(L, idx); lua_pushnil(L); while (lua_next(L, -2)) { if (lua_type(L, -2) == LUA_TSTRING) { object.AddMember(StringValue(L, -2, allocator), toValue(L, -1, depth, allocator), allocator); } // pop value, leaving original key lua_pop(L, 1); } lua_pop(L, 1); return object; } Value ArrayValue(lua_State* L, int idx, int depth, Allocator& allocator) { Value array(rapidjson::kArrayType); auto MAX = static_cast(luax::rawlen(L, idx)); // luax::rawlen always returns size_t (>= 0) for (auto n = 1; n <= MAX; ++n) { lua_rawgeti(L, idx, n); array.PushBack(toValue(L, -1, depth, allocator), allocator); lua_pop(L, 1); } return array; } } }