diff --git a/src/data/dynamic.cpp b/src/data/dynamic.cpp index 206d731b..582f15e4 100644 --- a/src/data/dynamic.cpp +++ b/src/data/dynamic.cpp @@ -110,6 +110,11 @@ List& List::put(bool value) { return *this; } +List& List::put(std::unique_ptr value) { + values.emplace_back(std::move(value)); + return *this; +} + List& List::put(Map* value) { values.push_back(std::make_unique(valtype::map, value)); return *this; @@ -346,6 +351,10 @@ bool Map::has(std::string key) { return values.find(key) != values.end(); } +size_t Map::size() const { + return values.size(); +} + Value::Value(valtype type, valvalue value) : type(type), value(value) { } @@ -358,18 +367,18 @@ Value::~Value() { } } -Value Value::boolean(bool value) { - return Value(valtype::boolean, value); +std::unique_ptr Value::boolean(bool value) { + return std::make_unique(valtype::boolean, value); } -Value Value::of(number_u value) { +std::unique_ptr Value::of(number_u value) { if (std::holds_alternative(value)) { - return Value(valtype::integer, std::get(value)); + return std::make_unique(valtype::integer, std::get(value)); } else { - return Value(valtype::number, std::get(value)); + return std::make_unique(valtype::number, std::get(value)); } } -Value Value::of(const std::string& value) { - return Value(valtype::string, value); +std::unique_ptr Value::of(const std::string& value) { + return std::make_unique(valtype::string, value); } diff --git a/src/data/dynamic.h b/src/data/dynamic.h index 39f8e664..a47cd54f 100644 --- a/src/data/dynamic.h +++ b/src/data/dynamic.h @@ -33,9 +33,9 @@ namespace dynamic { Value(valtype type, valvalue value); ~Value(); - static Value boolean(bool value); - static Value of(number_u value); - static Value of(const std::string& value); + static std::unique_ptr boolean(bool value); + static std::unique_ptr of(number_u value); + static std::unique_ptr of(const std::string& value); }; class List { @@ -68,6 +68,7 @@ namespace dynamic { List& put(Map* value); List& put(List* value); List& put(bool value); + List& put(std::unique_ptr value); Value* getValueWriteable(size_t index) const; @@ -121,6 +122,7 @@ namespace dynamic { Map& putMap(std::string key); bool has(std::string key); + size_t size() const; }; } diff --git a/src/files/settings_io.cpp b/src/files/settings_io.cpp index 3cfe3bc4..b9105064 100644 --- a/src/files/settings_io.cpp +++ b/src/files/settings_io.cpp @@ -30,7 +30,7 @@ SettingsHandler::SettingsHandler(EngineSettings& settings) { map.emplace("graphics.backlight", &settings.graphics.backlight); } -dynamic::Value SettingsHandler::getValue(const std::string& name) const { +std::unique_ptr SettingsHandler::getValue(const std::string& name) const { auto found = map.find(name); if (found == map.end()) { throw std::runtime_error("setting '"+name+"' does not exist"); @@ -65,7 +65,7 @@ Setting* SettingsHandler::getSetting(const std::string& name) const { } template -static void set_numeric_value(T* setting, dynamic::Value& value) { +static void set_numeric_value(T* setting, const dynamic::Value& value) { switch (value.type) { case dynamic::valtype::integer: setting->set(std::get(value.value)); @@ -81,10 +81,10 @@ static void set_numeric_value(T* setting, dynamic::Value& value) { } } -void SettingsHandler::setValue(const std::string& name, dynamic::Value value) { +void SettingsHandler::setValue(const std::string& name, const dynamic::Value& value) { auto found = map.find(name); if (found == map.end()) { - throw std::runtime_error("setting '"+name+"' does not exist"); + throw std::runtime_error("setting '"+name+"' does Pnot exist"); } auto setting = found->second; if (auto number = dynamic_cast(setting)) { diff --git a/src/files/settings_io.h b/src/files/settings_io.h index 6a59a554..11bd9f46 100644 --- a/src/files/settings_io.h +++ b/src/files/settings_io.h @@ -2,6 +2,7 @@ #define FILES_SETTINGS_IO_H_ #include +#include #include #include "../settings.h" #include "../data/dynamic.h" @@ -15,8 +16,8 @@ class SettingsHandler { public: SettingsHandler(EngineSettings& settings); - dynamic::Value getValue(const std::string& name) const; - void setValue(const std::string& name, dynamic::Value value); + std::unique_ptr getValue(const std::string& name) const; + void setValue(const std::string& name, const dynamic::Value& value); std::string toString(const std::string& name) const; Setting* getSetting(const std::string& name) const; }; diff --git a/src/logic/scripting/lua/LuaState.cpp b/src/logic/scripting/lua/LuaState.cpp index 75a58e2e..21a64dbc 100644 --- a/src/logic/scripting/lua/LuaState.cpp +++ b/src/logic/scripting/lua/LuaState.cpp @@ -128,6 +128,7 @@ void lua::LuaState::createLibs() { openlib("player", playerlib, 0); openlib("time", timelib, 0); openlib("world", worldlib, 0); + openlib("json", jsonlib, 0); addfunc("print", lua_wrap_errors); } @@ -226,10 +227,24 @@ int lua::LuaState::pushvalue(const dynamic::Value& value) { case valtype::none: pushnil(); break; - case valtype::list: - throw std::runtime_error("type 'list' is not implemented"); - case valtype::map: - throw std::runtime_error("type 'map' is not implemented"); + case valtype::list: { + auto list = std::get(value.value); + lua_createtable(L, list->size(), 0); + for (size_t i = 0; i < list->size(); i++) { + pushvalue(*list->get(i)); + lua_rawseti(L, -2, i+1); + } + break; + } + case valtype::map: { + auto map = std::get(value.value); + lua_createtable(L, 0, map->size()); + for (auto& entry : map->values) { + pushvalue(*entry.second); + lua_setfield(L, -2, entry.first.c_str()); + } + break; + } } return 1; } @@ -277,28 +292,56 @@ const char* lua::LuaState::tostring(int idx) { return lua_tostring(L, idx); } -dynamic::Value lua::LuaState::tovalue(int idx) { - using dynamic::valtype; +std::unique_ptr lua::LuaState::tovalue(int idx) { + using namespace dynamic; auto type = lua_type(L, idx); switch (type) { case LUA_TNIL: case LUA_TNONE: - return dynamic::Value(valtype::none, (integer_t)0); + return std::make_unique(valtype::none, (integer_t)0); case LUA_TBOOLEAN: - return dynamic::Value::boolean(lua_toboolean(L, idx) == 1); + return Value::boolean(lua_toboolean(L, idx) == 1); case LUA_TNUMBER: { auto number = lua_tonumber(L, idx); auto integer = lua_tointeger(L, idx); if (number == (lua_Number)integer) { - return dynamic::Value::of(integer); + return Value::of(integer); } else { - return dynamic::Value::of(number); + return Value::of(number); } } case LUA_TSTRING: - return dynamic::Value::of(lua_tostring(L, idx)); + return Value::of(lua_tostring(L, idx)); + case LUA_TTABLE: { + int len = lua_objlen(L, idx); + if (len) { + // array + auto list = std::make_unique(); + for (int i = 1; i <= len; i++) { + lua_rawgeti(L, idx, i); + list->put(tovalue(-1)); + lua_pop(L, 1); + } + return std::make_unique(valtype::list, list.release()); + } else { + // table + auto map = std::make_unique(); + lua_pushvalue(L, idx); + lua_pushnil(L); + while (lua_next(L, -2)) { + lua_pushvalue(L, -2); + auto key = lua_tostring(L, -1); + map->put(key, tovalue(-2)); + lua_pop(L, 2); + } + lua_pop(L, 1); + return std::make_unique(valtype::map, map.release()); + } + } default: - throw std::runtime_error("lua type "+std::to_string(type)+" is not supported"); + throw std::runtime_error( + "lua type "+std::to_string(type)+" is not supported" + ); } } diff --git a/src/logic/scripting/lua/LuaState.h b/src/logic/scripting/lua/LuaState.h index 87eae32e..dce2e685 100644 --- a/src/logic/scripting/lua/LuaState.h +++ b/src/logic/scripting/lua/LuaState.h @@ -47,7 +47,7 @@ namespace lua { bool toboolean(int idx); luaint tointeger(int idx); luanumber tonumber(int idx); - dynamic::Value tovalue(int idx); + std::unique_ptr tovalue(int idx); const char* tostring(int idx); bool isstring(int idx); bool isfunction(int idx); diff --git a/src/logic/scripting/lua/api_lua.h b/src/logic/scripting/lua/api_lua.h index 0bbb8001..b3a84b5a 100644 --- a/src/logic/scripting/lua/api_lua.h +++ b/src/logic/scripting/lua/api_lua.h @@ -17,6 +17,7 @@ extern const luaL_Reg packlib []; extern const luaL_Reg playerlib []; extern const luaL_Reg timelib []; extern const luaL_Reg worldlib []; +extern const luaL_Reg jsonlib []; // Lua Overrides diff --git a/src/logic/scripting/lua/libcore.cpp b/src/logic/scripting/lua/libcore.cpp index 2efe9fac..356b577a 100644 --- a/src/logic/scripting/lua/libcore.cpp +++ b/src/logic/scripting/lua/libcore.cpp @@ -118,14 +118,14 @@ static int l_get_bindings(lua_State* L) { static int l_get_setting(lua_State* L) { auto name = lua_tostring(L, 1); const auto value = scripting::engine->getSettingsHandler().getValue(name); - scripting::state->pushvalue(value); + scripting::state->pushvalue(*value); return 1; } static int l_set_setting(lua_State* L) { auto name = lua_tostring(L, 1); const auto value = scripting::state->tovalue(2); - scripting::engine->getSettingsHandler().setValue(name, value); + scripting::engine->getSettingsHandler().setValue(name, *value); return 0; } diff --git a/src/logic/scripting/lua/libjson.cpp b/src/logic/scripting/lua/libjson.cpp new file mode 100644 index 00000000..6240fe4f --- /dev/null +++ b/src/logic/scripting/lua/libjson.cpp @@ -0,0 +1,39 @@ +#include "api_lua.h" +#include "lua_commons.h" + +#include "LuaState.h" + +#include "../../../coders/json.h" +#include "../../../data/dynamic.h" + +namespace scripting { + extern lua::LuaState* state; +} + +static int l_json_stringify(lua_State* L) { + auto value = scripting::state->tovalue(1); + if (value->type != dynamic::valtype::map) { + luaL_error(L, "table expected"); + return 0; + } + bool nice = lua_toboolean(L, 2); + auto string = json::stringify(std::get(value->value), nice, " "); + lua_pushstring(L, string.c_str()); + return 1; +} + +static int l_json_parse(lua_State* L) { + auto string = lua_tostring(L, 1); + auto element = json::parse("", string); + auto value = std::make_unique( + dynamic::valtype::map, element.release() + ); + scripting::state->pushvalue(*value); + return 1; +} + +const luaL_Reg jsonlib [] = { + {"stringify", lua_wrap_errors}, + {"parse", lua_wrap_errors}, + {NULL, NULL} +};