1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-12-18 09:13:09 +00:00

Merge branch 'avoid_copy' into 'master'

Follow-up for YAML API

See merge request OpenMW/openmw!3961
This commit is contained in:
Alexei Kotov 2024-03-23 07:13:39 +00:00
commit 63276e0f1f
4 changed files with 277 additions and 270 deletions

View file

@ -19,12 +19,11 @@ namespace MWLua
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
api["loadYaml"] = [lua = context.mLua, vfs](std::string_view fileName) { api["loadYaml"] = [lua = context.mLua, vfs](std::string_view fileName) {
auto normalizedName = VFS::Path::normalizeFilename(fileName); Files::IStreamPtr file = vfs->get(VFS::Path::Normalized(fileName));
auto file = vfs->getNormalized(normalizedName); return LuaUtil::loadYaml(*file, lua->sol());
return LuaUtil::YamlLoader::load(*file, lua->sol());
}; };
api["decodeYaml"] = [lua = context.mLua](std::string_view inputData) { api["decodeYaml"] = [lua = context.mLua](std::string_view inputData) {
return LuaUtil::YamlLoader::load(std::string(inputData), lua->sol()); return LuaUtil::loadYaml(std::string(inputData), lua->sol());
}; };
return LuaUtil::makeReadOnly(api); return LuaUtil::makeReadOnly(api);

View file

@ -1,16 +1,19 @@
#include "gmock/gmock.h"
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <components/lua/yamlloader.hpp> #include <sol/object.hpp>
#include <sol/state.hpp>
#include <sol/table.hpp>
#include "../testing_util.hpp" #include <yaml-cpp/yaml.h>
#include <components/lua/yamlloader.hpp>
namespace namespace
{ {
template <typename T> template <typename T>
bool checkNumber(sol::state_view& lua, const std::string& inputData, T requiredValue) bool checkNumber(sol::state_view& lua, const std::string& inputData, T requiredValue)
{ {
sol::object result = LuaUtil::YamlLoader::load(inputData, lua); sol::object result = LuaUtil::loadYaml(inputData, lua);
if (result.get_type() != sol::type::number) if (result.get_type() != sol::type::number)
return false; return false;
@ -19,7 +22,7 @@ namespace
bool checkBool(sol::state_view& lua, const std::string& inputData, bool requiredValue) bool checkBool(sol::state_view& lua, const std::string& inputData, bool requiredValue)
{ {
sol::object result = LuaUtil::YamlLoader::load(inputData, lua); sol::object result = LuaUtil::loadYaml(inputData, lua);
if (result.get_type() != sol::type::boolean) if (result.get_type() != sol::type::boolean)
return false; return false;
@ -28,13 +31,13 @@ namespace
bool checkNil(sol::state_view& lua, const std::string& inputData) bool checkNil(sol::state_view& lua, const std::string& inputData)
{ {
sol::object result = LuaUtil::YamlLoader::load(inputData, lua); sol::object result = LuaUtil::loadYaml(inputData, lua);
return result == sol::nil; return result == sol::nil;
} }
bool checkNan(sol::state_view& lua, const std::string& inputData) bool checkNan(sol::state_view& lua, const std::string& inputData)
{ {
sol::object result = LuaUtil::YamlLoader::load(inputData, lua); sol::object result = LuaUtil::loadYaml(inputData, lua);
if (result.get_type() != sol::type::number) if (result.get_type() != sol::type::number)
return false; return false;
@ -43,7 +46,7 @@ namespace
bool checkString(sol::state_view& lua, const std::string& inputData, const std::string& requiredValue) bool checkString(sol::state_view& lua, const std::string& inputData, const std::string& requiredValue)
{ {
sol::object result = LuaUtil::YamlLoader::load(inputData, lua); sol::object result = LuaUtil::loadYaml(inputData, lua);
if (result.get_type() != sol::type::string) if (result.get_type() != sol::type::string)
return false; return false;
@ -52,7 +55,7 @@ namespace
bool checkString(sol::state_view& lua, const std::string& inputData) bool checkString(sol::state_view& lua, const std::string& inputData)
{ {
sol::object result = LuaUtil::YamlLoader::load(inputData, lua); sol::object result = LuaUtil::loadYaml(inputData, lua);
if (result.get_type() != sol::type::string) if (result.get_type() != sol::type::string)
return false; return false;
@ -164,7 +167,7 @@ namespace
try try
{ {
YAML::Node root = YAML::Load(input); YAML::Node root = YAML::Load(input);
sol::object result = LuaUtil::YamlLoader::load(input, lua); sol::object result = LuaUtil::loadYaml(input, lua);
} }
catch (const std::runtime_error& e) catch (const std::runtime_error& e)
{ {
@ -179,29 +182,29 @@ namespace
{ {
sol::state lua; sol::state lua;
sol::object map = LuaUtil::YamlLoader::load("{ x: , y: 2, 4: 5 }", lua); sol::object map = LuaUtil::loadYaml("{ x: , y: 2, 4: 5 }", lua);
ASSERT_EQ(map.as<sol::table>()["x"], sol::nil); ASSERT_EQ(map.as<sol::table>()["x"], sol::nil);
ASSERT_EQ(map.as<sol::table>()["y"], 2); ASSERT_EQ(map.as<sol::table>()["y"], 2);
ASSERT_EQ(map.as<sol::table>()[4], 5); ASSERT_EQ(map.as<sol::table>()[4], 5);
sol::object array = LuaUtil::YamlLoader::load("[ 3, 4 ]", lua); sol::object array = LuaUtil::loadYaml("[ 3, 4 ]", lua);
ASSERT_EQ(array.as<sol::table>()[1], 3); ASSERT_EQ(array.as<sol::table>()[1], 3);
sol::object emptyTable = LuaUtil::YamlLoader::load("{}", lua); sol::object emptyTable = LuaUtil::loadYaml("{}", lua);
ASSERT_TRUE(emptyTable.as<sol::table>().empty()); ASSERT_TRUE(emptyTable.as<sol::table>().empty());
sol::object emptyArray = LuaUtil::YamlLoader::load("[]", lua); sol::object emptyArray = LuaUtil::loadYaml("[]", lua);
ASSERT_TRUE(emptyArray.as<sol::table>().empty()); ASSERT_TRUE(emptyArray.as<sol::table>().empty());
ASSERT_THROW(LuaUtil::YamlLoader::load("{ null: 1 }", lua), std::runtime_error); ASSERT_THROW(LuaUtil::loadYaml("{ null: 1 }", lua), std::runtime_error);
ASSERT_THROW(LuaUtil::YamlLoader::load("{ .nan: 1 }", lua), std::runtime_error); ASSERT_THROW(LuaUtil::loadYaml("{ .nan: 1 }", lua), std::runtime_error);
const std::string scalarArrayInput = R"( const std::string scalarArrayInput = R"(
- First Scalar - First Scalar
- 1 - 1
- true)"; - true)";
sol::object scalarArray = LuaUtil::YamlLoader::load(scalarArrayInput, lua); sol::object scalarArray = LuaUtil::loadYaml(scalarArrayInput, lua);
ASSERT_EQ(scalarArray.as<sol::table>()[1], std::string("First Scalar")); ASSERT_EQ(scalarArray.as<sol::table>()[1], std::string("First Scalar"));
ASSERT_EQ(scalarArray.as<sol::table>()[2], 1); ASSERT_EQ(scalarArray.as<sol::table>()[2], 1);
ASSERT_EQ(scalarArray.as<sol::table>()[3], true); ASSERT_EQ(scalarArray.as<sol::table>()[3], true);
@ -212,7 +215,7 @@ namespace
float: 0.278 # Float value float: 0.278 # Float value
bool: false # Boolean value)"; bool: false # Boolean value)";
sol::object scalarMapWithComments = LuaUtil::YamlLoader::load(scalarMapWithCommentsInput, lua); sol::object scalarMapWithComments = LuaUtil::loadYaml(scalarMapWithCommentsInput, lua);
ASSERT_EQ(scalarMapWithComments.as<sol::table>()["string"], std::string("str")); ASSERT_EQ(scalarMapWithComments.as<sol::table>()["string"], std::string("str"));
ASSERT_EQ(scalarMapWithComments.as<sol::table>()["integer"], 65); ASSERT_EQ(scalarMapWithComments.as<sol::table>()["integer"], 65);
ASSERT_EQ(scalarMapWithComments.as<sol::table>()["float"], 0.278); ASSERT_EQ(scalarMapWithComments.as<sol::table>()["float"], 0.278);
@ -228,7 +231,7 @@ namespace
- false - false
- 1)"; - 1)";
sol::object mapOfArrays = LuaUtil::YamlLoader::load(mapOfArraysInput, lua); sol::object mapOfArrays = LuaUtil::loadYaml(mapOfArraysInput, lua);
ASSERT_EQ(mapOfArrays.as<sol::table>()["x"][3], true); ASSERT_EQ(mapOfArrays.as<sol::table>()["x"][3], true);
ASSERT_EQ(mapOfArrays.as<sol::table>()["y"][1], std::string("aaa")); ASSERT_EQ(mapOfArrays.as<sol::table>()["y"][1], std::string("aaa"));
@ -242,7 +245,7 @@ namespace
hr: 63 hr: 63
avg: 0.288)"; avg: 0.288)";
sol::object arrayOfMaps = LuaUtil::YamlLoader::load(arrayOfMapsInput, lua); sol::object arrayOfMaps = LuaUtil::loadYaml(arrayOfMapsInput, lua);
ASSERT_EQ(arrayOfMaps.as<sol::table>()[1]["avg"], 0.278); ASSERT_EQ(arrayOfMaps.as<sol::table>()[1]["avg"], 0.278);
ASSERT_EQ(arrayOfMaps.as<sol::table>()[2]["name"], std::string("Name2")); ASSERT_EQ(arrayOfMaps.as<sol::table>()[2]["name"], std::string("Name2"));
@ -250,7 +253,7 @@ namespace
- [Name1, 65, 0.278] - [Name1, 65, 0.278]
- [Name2 , 63, 0.288])"; - [Name2 , 63, 0.288])";
sol::object arrayOfArrays = LuaUtil::YamlLoader::load(arrayOfArraysInput, lua); sol::object arrayOfArrays = LuaUtil::loadYaml(arrayOfArraysInput, lua);
ASSERT_EQ(arrayOfArrays.as<sol::table>()[1][2], 65); ASSERT_EQ(arrayOfArrays.as<sol::table>()[1][2], 65);
ASSERT_EQ(arrayOfArrays.as<sol::table>()[2][1], std::string("Name2")); ASSERT_EQ(arrayOfArrays.as<sol::table>()[2][1], std::string("Name2"));
@ -261,7 +264,7 @@ namespace
avg: 0.288, avg: 0.288,
})"; })";
sol::object mapOfMaps = LuaUtil::YamlLoader::load(mapOfMapsInput, lua); sol::object mapOfMaps = LuaUtil::loadYaml(mapOfMapsInput, lua);
ASSERT_EQ(mapOfMaps.as<sol::table>()["Name1"]["hr"], 65); ASSERT_EQ(mapOfMaps.as<sol::table>()["Name1"]["hr"], 65);
ASSERT_EQ(mapOfMaps.as<sol::table>()["Name2"]["avg"], 0.288); ASSERT_EQ(mapOfMaps.as<sol::table>()["Name2"]["avg"], 0.288);
} }
@ -281,7 +284,7 @@ namespace
" - 3\n" " - 3\n"
" - false"; " - false";
sol::object twoDocuments = LuaUtil::YamlLoader::load(twoDocumentsInput, lua); sol::object twoDocuments = LuaUtil::loadYaml(twoDocumentsInput, lua);
ASSERT_EQ(twoDocuments.as<sol::table>()[1][1], std::string("First Scalar")); ASSERT_EQ(twoDocuments.as<sol::table>()[1][1], std::string("First Scalar"));
ASSERT_EQ(twoDocuments.as<sol::table>()[2][3], false); ASSERT_EQ(twoDocuments.as<sol::table>()[2][3], false);
@ -294,7 +297,7 @@ namespace
- *a # Subsequent occurrence - *a # Subsequent occurrence
- Name2)"; - Name2)";
sol::object anchor = LuaUtil::YamlLoader::load(anchorInput, lua); sol::object anchor = LuaUtil::loadYaml(anchorInput, lua);
ASSERT_EQ(anchor.as<sol::table>()["y"][1], std::string("Value1")); ASSERT_EQ(anchor.as<sol::table>()["y"][1], std::string("Value1"));
const std::string compoundKeyInput = R"( const std::string compoundKeyInput = R"(
@ -306,7 +309,7 @@ namespace
String4 ] String4 ]
: [ 2, 3, 4 ])"; : [ 2, 3, 4 ])";
ASSERT_THROW(LuaUtil::YamlLoader::load(compoundKeyInput, lua), std::runtime_error); ASSERT_THROW(LuaUtil::loadYaml(compoundKeyInput, lua), std::runtime_error);
const std::string compactNestedMappingInput = R"( const std::string compactNestedMappingInput = R"(
- item : Item1 - item : Item1
@ -316,7 +319,7 @@ namespace
- item : Item3 - item : Item3
quantity: 11)"; quantity: 11)";
sol::object compactNestedMapping = LuaUtil::YamlLoader::load(compactNestedMappingInput, lua); sol::object compactNestedMapping = LuaUtil::loadYaml(compactNestedMappingInput, lua);
ASSERT_EQ(compactNestedMapping.as<sol::table>()[2]["quantity"], 4); ASSERT_EQ(compactNestedMapping.as<sol::table>()[2]["quantity"], 4);
} }
@ -346,7 +349,7 @@ namespace
quoted: "So does this quoted: "So does this
quoted scalar.\n")"; quoted scalar.\n")";
sol::object multiLinePlanarScalars = LuaUtil::YamlLoader::load(multiLinePlanarScalarsInput, lua); sol::object multiLinePlanarScalars = LuaUtil::loadYaml(multiLinePlanarScalarsInput, lua);
ASSERT_TRUE( ASSERT_TRUE(
multiLinePlanarScalars.as<sol::table>()["plain"] == std::string("This unquoted scalar spans many lines.")); multiLinePlanarScalars.as<sol::table>()["plain"] == std::string("This unquoted scalar spans many lines."));
ASSERT_TRUE(multiLinePlanarScalars.as<sol::table>()["quoted"] == std::string("So does this quoted scalar.\n")); ASSERT_TRUE(multiLinePlanarScalars.as<sol::table>()["quoted"] == std::string("So does this quoted scalar.\n"));

View file

@ -1,7 +1,17 @@
#include "yamlloader.hpp" #include "yamlloader.hpp"
#include <charconv> #include <charconv>
#include <cmath>
#include <limits>
#include <regex> #include <regex>
#include <stdexcept>
#include <system_error>
#include <vector>
#include <sol/object.hpp>
#include <sol/state_view.hpp>
#include <yaml-cpp/yaml.h>
#include <components/misc/strings/format.hpp> #include <components/misc/strings/format.hpp>
#include <components/misc/strings/lower.hpp> #include <components/misc/strings/lower.hpp>
@ -11,21 +21,50 @@ namespace LuaUtil
namespace namespace
{ {
constexpr uint64_t maxDepth = 250; constexpr uint64_t maxDepth = 250;
enum class ScalarType
{
Boolean,
Decimal,
Float,
Hexadecimal,
Infinity,
NotNumber,
Null,
Octal,
String
};
sol::object loadAll(const std::vector<YAML::Node>& rootNodes, const sol::state_view& lua);
sol::object getNode(const YAML::Node& node, const sol::state_view& lua, uint64_t depth);
sol::table getMap(const YAML::Node& node, const sol::state_view& lua, uint64_t depth);
sol::table getArray(const YAML::Node& node, const sol::state_view& lua, uint64_t depth);
ScalarType getScalarType(const YAML::Node& node);
sol::object getScalar(const YAML::Node& node, const sol::state_view& lua);
[[noreturn]] void nodeError(const YAML::Node& node, const std::string& message);
} }
sol::object YamlLoader::load(const std::string& input, const sol::state_view& lua) sol::object loadYaml(const std::string& input, const sol::state_view& lua)
{ {
std::vector<YAML::Node> rootNodes = YAML::LoadAll(input); std::vector<YAML::Node> rootNodes = YAML::LoadAll(input);
return LuaUtil::YamlLoader::load(rootNodes, lua); return loadAll(rootNodes, lua);
} }
sol::object YamlLoader::load(std::istream& input, const sol::state_view& lua) sol::object loadYaml(std::istream& input, const sol::state_view& lua)
{ {
std::vector<YAML::Node> rootNodes = YAML::LoadAll(input); std::vector<YAML::Node> rootNodes = YAML::LoadAll(input);
return load(rootNodes, lua); return loadAll(rootNodes, lua);
} }
sol::object YamlLoader::load(const std::vector<YAML::Node> rootNodes, const sol::state_view& lua) namespace
{
sol::object loadAll(const std::vector<YAML::Node>& rootNodes, const sol::state_view& lua)
{ {
if (rootNodes.empty()) if (rootNodes.empty())
return sol::nil; return sol::nil;
@ -42,7 +81,7 @@ namespace LuaUtil
return documentsTable; return documentsTable;
} }
sol::object YamlLoader::getNode(const YAML::Node& node, const sol::state_view& lua, uint64_t depth) sol::object getNode(const YAML::Node& node, const sol::state_view& lua, uint64_t depth)
{ {
if (depth >= maxDepth) if (depth >= maxDepth)
throw std::runtime_error("Maximum layers depth exceeded, probably caused by a circular reference"); throw std::runtime_error("Maximum layers depth exceeded, probably caused by a circular reference");
@ -61,7 +100,7 @@ namespace LuaUtil
nodeError(node, "An unknown YAML node encountered"); nodeError(node, "An unknown YAML node encountered");
} }
sol::table YamlLoader::getMap(const YAML::Node& node, const sol::state_view& lua, uint64_t depth) sol::table getMap(const YAML::Node& node, const sol::state_view& lua, uint64_t depth)
{ {
sol::table childTable(lua, sol::create); sol::table childTable(lua, sol::create);
@ -84,7 +123,7 @@ namespace LuaUtil
return childTable; return childTable;
} }
sol::table YamlLoader::getArray(const YAML::Node& node, const sol::state_view& lua, uint64_t depth) sol::table getArray(const YAML::Node& node, const sol::state_view& lua, uint64_t depth)
{ {
sol::table childTable(lua, sol::create); sol::table childTable(lua, sol::create);
@ -96,7 +135,7 @@ namespace LuaUtil
return childTable; return childTable;
} }
YamlLoader::ScalarType YamlLoader::getScalarType(const YAML::Node& node) ScalarType getScalarType(const YAML::Node& node)
{ {
const auto& tag = node.Tag(); const auto& tag = node.Tag();
const auto& value = node.Scalar(); const auto& value = node.Scalar();
@ -152,7 +191,7 @@ namespace LuaUtil
return ScalarType::String; return ScalarType::String;
} }
sol::object YamlLoader::getScalar(const YAML::Node& node, const sol::state_view& lua) sol::object getScalar(const YAML::Node& node, const sol::state_view& lua)
{ {
auto type = getScalarType(node); auto type = getScalarType(node);
const auto& value = node.Scalar(); const auto& value = node.Scalar();
@ -230,12 +269,12 @@ namespace LuaUtil
} }
} }
[[noreturn]] void YamlLoader::nodeError(const YAML::Node& node, const std::string& message) [[noreturn]] void nodeError(const YAML::Node& node, const std::string& message)
{ {
const auto& mark = node.Mark(); const auto& mark = node.Mark();
std::string error = Misc::StringUtils::format( std::string error = Misc::StringUtils::format(
" at line=%d column=%d position=%d", mark.line + 1, mark.column + 1, mark.pos + 1); " at line=%d column=%d position=%d", mark.line + 1, mark.column + 1, mark.pos + 1);
throw std::runtime_error(message + error); throw std::runtime_error(message + error);
} }
}
} }

View file

@ -1,50 +1,16 @@
#ifndef COMPONENTS_LUA_YAMLLOADER_H #ifndef COMPONENTS_LUA_YAMLLOADER_H
#define COMPONENTS_LUA_YAMLLOADER_H #define COMPONENTS_LUA_YAMLLOADER_H
#include <map> #include <iosfwd>
#include <sol/sol.hpp> #include <string>
#include <stdexcept>
#include <yaml-cpp/yaml.h> #include <sol/forward.hpp>
namespace LuaUtil namespace LuaUtil
{ {
sol::object loadYaml(const std::string& input, const sol::state_view& lua);
class YamlLoader sol::object loadYaml(std::istream& input, const sol::state_view& lua);
{
public:
static sol::object load(const std::string& input, const sol::state_view& lua);
static sol::object load(std::istream& input, const sol::state_view& lua);
private:
enum class ScalarType
{
Boolean,
Decimal,
Float,
Hexadecimal,
Infinity,
NotNumber,
Null,
Octal,
String
};
static sol::object load(const std::vector<YAML::Node> rootNodes, const sol::state_view& lua);
static sol::object getNode(const YAML::Node& node, const sol::state_view& lua, uint64_t depth);
static sol::table getMap(const YAML::Node& node, const sol::state_view& lua, uint64_t depth);
static sol::table getArray(const YAML::Node& node, const sol::state_view& lua, uint64_t depth);
static ScalarType getScalarType(const YAML::Node& node);
static sol::object getScalar(const YAML::Node& node, const sol::state_view& lua);
[[noreturn]] static void nodeError(const YAML::Node& node, const std::string& message);
};
} }
#endif // COMPONENTS_LUA_YAMLLOADER_H #endif // COMPONENTS_LUA_YAMLLOADER_H