diff --git a/apps/openmw/mwlua/markupbindings.cpp b/apps/openmw/mwlua/markupbindings.cpp index 997674b45d..f0b9d67a51 100644 --- a/apps/openmw/mwlua/markupbindings.cpp +++ b/apps/openmw/mwlua/markupbindings.cpp @@ -19,12 +19,11 @@ namespace MWLua auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); api["loadYaml"] = [lua = context.mLua, vfs](std::string_view fileName) { - auto normalizedName = VFS::Path::normalizeFilename(fileName); - auto file = vfs->getNormalized(normalizedName); - return LuaUtil::YamlLoader::load(*file, lua->sol()); + Files::IStreamPtr file = vfs->get(VFS::Path::Normalized(fileName)); + return LuaUtil::loadYaml(*file, lua->sol()); }; 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); diff --git a/apps/openmw_test_suite/lua/test_yaml.cpp b/apps/openmw_test_suite/lua/test_yaml.cpp index c7d484cf51..fa28889440 100644 --- a/apps/openmw_test_suite/lua/test_yaml.cpp +++ b/apps/openmw_test_suite/lua/test_yaml.cpp @@ -1,16 +1,19 @@ -#include "gmock/gmock.h" #include -#include +#include +#include +#include + +#include -#include "../testing_util.hpp" +#include namespace { template 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) return false; @@ -19,7 +22,7 @@ namespace 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) return false; @@ -28,13 +31,13 @@ namespace 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; } 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) return false; @@ -43,7 +46,7 @@ namespace 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) return false; @@ -52,7 +55,7 @@ namespace 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) return false; @@ -164,7 +167,7 @@ namespace try { 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) { @@ -179,29 +182,29 @@ namespace { 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()["x"], sol::nil); ASSERT_EQ(map.as()["y"], 2); ASSERT_EQ(map.as()[4], 5); - sol::object array = LuaUtil::YamlLoader::load("[ 3, 4 ]", lua); + sol::object array = LuaUtil::loadYaml("[ 3, 4 ]", lua); ASSERT_EQ(array.as()[1], 3); - sol::object emptyTable = LuaUtil::YamlLoader::load("{}", lua); + sol::object emptyTable = LuaUtil::loadYaml("{}", lua); ASSERT_TRUE(emptyTable.as().empty()); - sol::object emptyArray = LuaUtil::YamlLoader::load("[]", lua); + sol::object emptyArray = LuaUtil::loadYaml("[]", lua); ASSERT_TRUE(emptyArray.as().empty()); - ASSERT_THROW(LuaUtil::YamlLoader::load("{ null: 1 }", lua), std::runtime_error); - ASSERT_THROW(LuaUtil::YamlLoader::load("{ .nan: 1 }", lua), std::runtime_error); + ASSERT_THROW(LuaUtil::loadYaml("{ null: 1 }", lua), std::runtime_error); + ASSERT_THROW(LuaUtil::loadYaml("{ .nan: 1 }", lua), std::runtime_error); const std::string scalarArrayInput = R"( - First Scalar - 1 - true)"; - sol::object scalarArray = LuaUtil::YamlLoader::load(scalarArrayInput, lua); + sol::object scalarArray = LuaUtil::loadYaml(scalarArrayInput, lua); ASSERT_EQ(scalarArray.as()[1], std::string("First Scalar")); ASSERT_EQ(scalarArray.as()[2], 1); ASSERT_EQ(scalarArray.as()[3], true); @@ -212,7 +215,7 @@ namespace float: 0.278 # Float value bool: false # Boolean value)"; - sol::object scalarMapWithComments = LuaUtil::YamlLoader::load(scalarMapWithCommentsInput, lua); + sol::object scalarMapWithComments = LuaUtil::loadYaml(scalarMapWithCommentsInput, lua); ASSERT_EQ(scalarMapWithComments.as()["string"], std::string("str")); ASSERT_EQ(scalarMapWithComments.as()["integer"], 65); ASSERT_EQ(scalarMapWithComments.as()["float"], 0.278); @@ -228,7 +231,7 @@ namespace - false - 1)"; - sol::object mapOfArrays = LuaUtil::YamlLoader::load(mapOfArraysInput, lua); + sol::object mapOfArrays = LuaUtil::loadYaml(mapOfArraysInput, lua); ASSERT_EQ(mapOfArrays.as()["x"][3], true); ASSERT_EQ(mapOfArrays.as()["y"][1], std::string("aaa")); @@ -242,7 +245,7 @@ namespace hr: 63 avg: 0.288)"; - sol::object arrayOfMaps = LuaUtil::YamlLoader::load(arrayOfMapsInput, lua); + sol::object arrayOfMaps = LuaUtil::loadYaml(arrayOfMapsInput, lua); ASSERT_EQ(arrayOfMaps.as()[1]["avg"], 0.278); ASSERT_EQ(arrayOfMaps.as()[2]["name"], std::string("Name2")); @@ -250,7 +253,7 @@ namespace - [Name1, 65, 0.278] - [Name2 , 63, 0.288])"; - sol::object arrayOfArrays = LuaUtil::YamlLoader::load(arrayOfArraysInput, lua); + sol::object arrayOfArrays = LuaUtil::loadYaml(arrayOfArraysInput, lua); ASSERT_EQ(arrayOfArrays.as()[1][2], 65); ASSERT_EQ(arrayOfArrays.as()[2][1], std::string("Name2")); @@ -261,7 +264,7 @@ namespace avg: 0.288, })"; - sol::object mapOfMaps = LuaUtil::YamlLoader::load(mapOfMapsInput, lua); + sol::object mapOfMaps = LuaUtil::loadYaml(mapOfMapsInput, lua); ASSERT_EQ(mapOfMaps.as()["Name1"]["hr"], 65); ASSERT_EQ(mapOfMaps.as()["Name2"]["avg"], 0.288); } @@ -281,7 +284,7 @@ namespace " - 3\n" " - false"; - sol::object twoDocuments = LuaUtil::YamlLoader::load(twoDocumentsInput, lua); + sol::object twoDocuments = LuaUtil::loadYaml(twoDocumentsInput, lua); ASSERT_EQ(twoDocuments.as()[1][1], std::string("First Scalar")); ASSERT_EQ(twoDocuments.as()[2][3], false); @@ -294,7 +297,7 @@ namespace - *a # Subsequent occurrence - Name2)"; - sol::object anchor = LuaUtil::YamlLoader::load(anchorInput, lua); + sol::object anchor = LuaUtil::loadYaml(anchorInput, lua); ASSERT_EQ(anchor.as()["y"][1], std::string("Value1")); const std::string compoundKeyInput = R"( @@ -306,7 +309,7 @@ namespace String4 ] : [ 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"( - item : Item1 @@ -316,7 +319,7 @@ namespace - item : Item3 quantity: 11)"; - sol::object compactNestedMapping = LuaUtil::YamlLoader::load(compactNestedMappingInput, lua); + sol::object compactNestedMapping = LuaUtil::loadYaml(compactNestedMappingInput, lua); ASSERT_EQ(compactNestedMapping.as()[2]["quantity"], 4); } @@ -346,7 +349,7 @@ namespace quoted: "So does this quoted scalar.\n")"; - sol::object multiLinePlanarScalars = LuaUtil::YamlLoader::load(multiLinePlanarScalarsInput, lua); + sol::object multiLinePlanarScalars = LuaUtil::loadYaml(multiLinePlanarScalarsInput, lua); ASSERT_TRUE( multiLinePlanarScalars.as()["plain"] == std::string("This unquoted scalar spans many lines.")); ASSERT_TRUE(multiLinePlanarScalars.as()["quoted"] == std::string("So does this quoted scalar.\n")); diff --git a/components/lua/yamlloader.cpp b/components/lua/yamlloader.cpp index 14553cfac4..df83af6253 100644 --- a/components/lua/yamlloader.cpp +++ b/components/lua/yamlloader.cpp @@ -1,7 +1,17 @@ #include "yamlloader.hpp" #include +#include +#include #include +#include +#include +#include + +#include +#include + +#include #include #include @@ -11,231 +21,260 @@ namespace LuaUtil namespace { constexpr uint64_t maxDepth = 250; - } - sol::object YamlLoader::load(const std::string& input, const sol::state_view& lua) - { - std::vector rootNodes = YAML::LoadAll(input); - return LuaUtil::YamlLoader::load(rootNodes, lua); - } - - sol::object YamlLoader::load(std::istream& input, const sol::state_view& lua) - { - std::vector rootNodes = YAML::LoadAll(input); - return load(rootNodes, lua); - } - - sol::object YamlLoader::load(const std::vector rootNodes, const sol::state_view& lua) - { - if (rootNodes.empty()) - return sol::nil; + enum class ScalarType + { + Boolean, + Decimal, + Float, + Hexadecimal, + Infinity, + NotNumber, + Null, + Octal, + String + }; - if (rootNodes.size() == 1) - return getNode(rootNodes[0], lua, 0); + sol::object loadAll(const std::vector& rootNodes, const sol::state_view& lua); - sol::table documentsTable(lua, sol::create); - for (const auto& root : rootNodes) - { - documentsTable.add(getNode(root, lua, 1)); - } + sol::object getNode(const YAML::Node& node, const sol::state_view& lua, uint64_t depth); - return documentsTable; - } + sol::table getMap(const YAML::Node& node, const sol::state_view& lua, uint64_t depth); - sol::object YamlLoader::getNode(const YAML::Node& node, const sol::state_view& lua, uint64_t depth) - { - if (depth >= maxDepth) - throw std::runtime_error("Maximum layers depth exceeded, probably caused by a circular reference"); + sol::table getArray(const YAML::Node& node, const sol::state_view& lua, uint64_t depth); - ++depth; + ScalarType getScalarType(const YAML::Node& node); - if (node.IsMap()) - return getMap(node, lua, depth); - else if (node.IsSequence()) - return getArray(node, lua, depth); - else if (node.IsScalar()) - return getScalar(node, lua); - else if (node.IsNull()) - return sol::nil; + sol::object getScalar(const YAML::Node& node, const sol::state_view& lua); - nodeError(node, "An unknown YAML node encountered"); + [[noreturn]] void nodeError(const YAML::Node& node, const std::string& message); } - sol::table YamlLoader::getMap(const YAML::Node& node, const sol::state_view& lua, uint64_t depth) + sol::object loadYaml(const std::string& input, const sol::state_view& lua) { - sol::table childTable(lua, sol::create); - - for (const auto& pair : node) - { - if (pair.first.IsMap()) - nodeError(pair.first, "Only scalar nodes can be used as keys, encountered map instead"); - if (pair.first.IsSequence()) - nodeError(pair.first, "Only scalar nodes can be used as keys, encountered array instead"); - if (pair.first.IsNull()) - nodeError(pair.first, "Only scalar nodes can be used as keys, encountered null instead"); - - auto key = getNode(pair.first, lua, depth); - if (key.get_type() == sol::type::number && std::isnan(key.as())) - nodeError(pair.first, "Only scalar nodes can be used as keys, encountered nan instead"); - - childTable[key] = getNode(pair.second, lua, depth); - } - - return childTable; + std::vector rootNodes = YAML::LoadAll(input); + return loadAll(rootNodes, lua); } - sol::table YamlLoader::getArray(const YAML::Node& node, const sol::state_view& lua, uint64_t depth) + sol::object loadYaml(std::istream& input, const sol::state_view& lua) { - sol::table childTable(lua, sol::create); - - for (const auto& child : node) - { - childTable.add(getNode(child, lua, depth)); - } - - return childTable; + std::vector rootNodes = YAML::LoadAll(input); + return loadAll(rootNodes, lua); } - YamlLoader::ScalarType YamlLoader::getScalarType(const YAML::Node& node) + namespace { - const auto& tag = node.Tag(); - const auto& value = node.Scalar(); - if (tag == "!") - return ScalarType::String; + sol::object loadAll(const std::vector& rootNodes, const sol::state_view& lua) + { + if (rootNodes.empty()) + return sol::nil; - // Note that YAML allows to explicitely specify a scalar type via tag (e.g. "!!bool"), but it makes no - // sense in Lua: - // 1. Both integers and floats use the "number" type prior to Lua 5.3 - // 2. Strings can be quoted, which is more readable than "!!str" - // 3. Most of possible conversions are invalid or their result is unclear - // So ignore this feature for now. - if (tag != "?") - nodeError(node, "An invalid tag'" + tag + "' encountered"); - - if (value.empty()) - return ScalarType::Null; - - // Resolve type according to YAML 1.2 Core Schema (see https://yaml.org/spec/1.2.2/#103-core-schema) - static const std::regex boolRegex("true|True|TRUE|false|False|FALSE", std::regex_constants::extended); - if (std::regex_match(node.Scalar(), boolRegex)) - return ScalarType::Boolean; - - static const std::regex decimalRegex("[-+]?[0-9]+", std::regex_constants::extended); - if (std::regex_match(node.Scalar(), decimalRegex)) - return ScalarType::Decimal; - - static const std::regex floatRegex( - "[-+]?([.][0-9]+|[0-9]+([.][0-9]*)?)([eE][-+]?[0-9]+)?", std::regex_constants::extended); - if (std::regex_match(node.Scalar(), floatRegex)) - return ScalarType::Float; - - static const std::regex octalRegex("0o[0-7]+", std::regex_constants::extended); - if (std::regex_match(node.Scalar(), octalRegex)) - return ScalarType::Octal; - - static const std::regex hexdecimalRegex("0x[0-9a-fA-F]+", std::regex_constants::extended); - if (std::regex_match(node.Scalar(), hexdecimalRegex)) - return ScalarType::Hexadecimal; - - static const std::regex infinityRegex("[-+]?([.]inf|[.]Inf|[.]INF)", std::regex_constants::extended); - if (std::regex_match(node.Scalar(), infinityRegex)) - return ScalarType::Infinity; - - static const std::regex nanRegex("[.]nan|[.]NaN|[.]NAN", std::regex_constants::extended); - if (std::regex_match(node.Scalar(), nanRegex)) - return ScalarType::NotNumber; - - static const std::regex nullRegex("null|Null|NULL|~", std::regex_constants::extended); - if (std::regex_match(node.Scalar(), nullRegex)) - return ScalarType::Null; - - return ScalarType::String; - } + if (rootNodes.size() == 1) + return getNode(rootNodes[0], lua, 0); - sol::object YamlLoader::getScalar(const YAML::Node& node, const sol::state_view& lua) - { - auto type = getScalarType(node); - const auto& value = node.Scalar(); + sol::table documentsTable(lua, sol::create); + for (const auto& root : rootNodes) + { + documentsTable.add(getNode(root, lua, 1)); + } + + return documentsTable; + } - switch (type) + sol::object getNode(const YAML::Node& node, const sol::state_view& lua, uint64_t depth) { - case ScalarType::Null: + if (depth >= maxDepth) + throw std::runtime_error("Maximum layers depth exceeded, probably caused by a circular reference"); + + ++depth; + + if (node.IsMap()) + return getMap(node, lua, depth); + else if (node.IsSequence()) + return getArray(node, lua, depth); + else if (node.IsScalar()) + return getScalar(node, lua); + else if (node.IsNull()) return sol::nil; - case ScalarType::String: - return sol::make_object(lua, value); - case ScalarType::NotNumber: - return sol::make_object(lua, std::nan("")); - case ScalarType::Infinity: - { - if (!value.empty() && value[0] == '-') - return sol::make_object(lua, -std::numeric_limits::infinity()); - return sol::make_object(lua, std::numeric_limits::infinity()); - } - case ScalarType::Boolean: - { - if (Misc::StringUtils::lowerCase(value) == "true") - return sol::make_object(lua, true); + nodeError(node, "An unknown YAML node encountered"); + } - if (Misc::StringUtils::lowerCase(value) == "false") - return sol::make_object(lua, false); + sol::table getMap(const YAML::Node& node, const sol::state_view& lua, uint64_t depth) + { + sol::table childTable(lua, sol::create); - nodeError(node, "Can not read a boolean value '" + value + "'"); - } - case ScalarType::Decimal: + for (const auto& pair : node) { - int offset = 0; + if (pair.first.IsMap()) + nodeError(pair.first, "Only scalar nodes can be used as keys, encountered map instead"); + if (pair.first.IsSequence()) + nodeError(pair.first, "Only scalar nodes can be used as keys, encountered array instead"); + if (pair.first.IsNull()) + nodeError(pair.first, "Only scalar nodes can be used as keys, encountered null instead"); + + auto key = getNode(pair.first, lua, depth); + if (key.get_type() == sol::type::number && std::isnan(key.as())) + nodeError(pair.first, "Only scalar nodes can be used as keys, encountered nan instead"); + + childTable[key] = getNode(pair.second, lua, depth); + } - // std::from_chars does not support "+" sign - if (!value.empty() && value[0] == '+') - ++offset; + return childTable; + } - int result = 0; - const auto status = std::from_chars(value.data() + offset, value.data() + value.size(), result); - if (status.ec == std::errc()) - return sol::make_object(lua, result); + sol::table getArray(const YAML::Node& node, const sol::state_view& lua, uint64_t depth) + { + sol::table childTable(lua, sol::create); - nodeError(node, "Can not read a decimal value '" + value + "'"); - } - case ScalarType::Float: + for (const auto& child : node) { - // Not all compilers support std::from_chars for floats - double result = 0.0; - bool success = YAML::convert::decode(node, result); - if (success) - return sol::make_object(lua, result); - - nodeError(node, "Can not read a float value '" + value + "'"); + childTable.add(getNode(child, lua, depth)); } - case ScalarType::Hexadecimal: - { - int result = 0; - const auto status = std::from_chars(value.data() + 2, value.data() + value.size(), result, 16); - if (status.ec == std::errc()) - return sol::make_object(lua, result); - nodeError(node, "Can not read a hexadecimal value '" + value + "'"); - } - case ScalarType::Octal: - { - int result = 0; - const auto status = std::from_chars(value.data() + 2, value.data() + value.size(), result, 8); - if (status.ec == std::errc()) - return sol::make_object(lua, result); + return childTable; + } + + ScalarType getScalarType(const YAML::Node& node) + { + const auto& tag = node.Tag(); + const auto& value = node.Scalar(); + if (tag == "!") + return ScalarType::String; + + // Note that YAML allows to explicitely specify a scalar type via tag (e.g. "!!bool"), but it makes no + // sense in Lua: + // 1. Both integers and floats use the "number" type prior to Lua 5.3 + // 2. Strings can be quoted, which is more readable than "!!str" + // 3. Most of possible conversions are invalid or their result is unclear + // So ignore this feature for now. + if (tag != "?") + nodeError(node, "An invalid tag '" + tag + "' encountered"); + + if (value.empty()) + return ScalarType::Null; + + // Resolve type according to YAML 1.2 Core Schema (see https://yaml.org/spec/1.2.2/#103-core-schema) + static const std::regex boolRegex("true|True|TRUE|false|False|FALSE", std::regex_constants::extended); + if (std::regex_match(node.Scalar(), boolRegex)) + return ScalarType::Boolean; + + static const std::regex decimalRegex("[-+]?[0-9]+", std::regex_constants::extended); + if (std::regex_match(node.Scalar(), decimalRegex)) + return ScalarType::Decimal; + + static const std::regex floatRegex( + "[-+]?([.][0-9]+|[0-9]+([.][0-9]*)?)([eE][-+]?[0-9]+)?", std::regex_constants::extended); + if (std::regex_match(node.Scalar(), floatRegex)) + return ScalarType::Float; + + static const std::regex octalRegex("0o[0-7]+", std::regex_constants::extended); + if (std::regex_match(node.Scalar(), octalRegex)) + return ScalarType::Octal; + + static const std::regex hexdecimalRegex("0x[0-9a-fA-F]+", std::regex_constants::extended); + if (std::regex_match(node.Scalar(), hexdecimalRegex)) + return ScalarType::Hexadecimal; + + static const std::regex infinityRegex("[-+]?([.]inf|[.]Inf|[.]INF)", std::regex_constants::extended); + if (std::regex_match(node.Scalar(), infinityRegex)) + return ScalarType::Infinity; + + static const std::regex nanRegex("[.]nan|[.]NaN|[.]NAN", std::regex_constants::extended); + if (std::regex_match(node.Scalar(), nanRegex)) + return ScalarType::NotNumber; + + static const std::regex nullRegex("null|Null|NULL|~", std::regex_constants::extended); + if (std::regex_match(node.Scalar(), nullRegex)) + return ScalarType::Null; + + return ScalarType::String; + } - nodeError(node, "Can not read an octal value '" + value + "'"); + sol::object getScalar(const YAML::Node& node, const sol::state_view& lua) + { + auto type = getScalarType(node); + const auto& value = node.Scalar(); + + switch (type) + { + case ScalarType::Null: + return sol::nil; + case ScalarType::String: + return sol::make_object(lua, value); + case ScalarType::NotNumber: + return sol::make_object(lua, std::nan("")); + case ScalarType::Infinity: + { + if (!value.empty() && value[0] == '-') + return sol::make_object(lua, -std::numeric_limits::infinity()); + + return sol::make_object(lua, std::numeric_limits::infinity()); + } + case ScalarType::Boolean: + { + if (Misc::StringUtils::lowerCase(value) == "true") + return sol::make_object(lua, true); + + if (Misc::StringUtils::lowerCase(value) == "false") + return sol::make_object(lua, false); + + nodeError(node, "Can not read a boolean value '" + value + "'"); + } + case ScalarType::Decimal: + { + int offset = 0; + + // std::from_chars does not support "+" sign + if (!value.empty() && value[0] == '+') + ++offset; + + int result = 0; + const auto status = std::from_chars(value.data() + offset, value.data() + value.size(), result); + if (status.ec == std::errc()) + return sol::make_object(lua, result); + + nodeError(node, "Can not read a decimal value '" + value + "'"); + } + case ScalarType::Float: + { + // Not all compilers support std::from_chars for floats + double result = 0.0; + bool success = YAML::convert::decode(node, result); + if (success) + return sol::make_object(lua, result); + + nodeError(node, "Can not read a float value '" + value + "'"); + } + case ScalarType::Hexadecimal: + { + int result = 0; + const auto status = std::from_chars(value.data() + 2, value.data() + value.size(), result, 16); + if (status.ec == std::errc()) + return sol::make_object(lua, result); + + nodeError(node, "Can not read a hexadecimal value '" + value + "'"); + } + case ScalarType::Octal: + { + int result = 0; + const auto status = std::from_chars(value.data() + 2, value.data() + value.size(), result, 8); + if (status.ec == std::errc()) + return sol::make_object(lua, result); + + nodeError(node, "Can not read an octal value '" + value + "'"); + } + default: + nodeError(node, "An unknown scalar '" + value + "' encountered"); } - default: - nodeError(node, "An unknown scalar '" + value + "' encountered"); } - } - [[noreturn]] void YamlLoader::nodeError(const YAML::Node& node, const std::string& message) - { - const auto& mark = node.Mark(); - std::string error = Misc::StringUtils::format( - " at line=%d column=%d position=%d", mark.line + 1, mark.column + 1, mark.pos + 1); - throw std::runtime_error(message + error); + [[noreturn]] void nodeError(const YAML::Node& node, const std::string& message) + { + const auto& mark = node.Mark(); + std::string error = Misc::StringUtils::format( + " at line=%d column=%d position=%d", mark.line + 1, mark.column + 1, mark.pos + 1); + throw std::runtime_error(message + error); + } } - } diff --git a/components/lua/yamlloader.hpp b/components/lua/yamlloader.hpp index 1ca95223cd..6f28da66ce 100644 --- a/components/lua/yamlloader.hpp +++ b/components/lua/yamlloader.hpp @@ -1,50 +1,16 @@ #ifndef COMPONENTS_LUA_YAMLLOADER_H #define COMPONENTS_LUA_YAMLLOADER_H -#include -#include -#include -#include +#include +#include + +#include namespace LuaUtil { + sol::object loadYaml(const std::string& input, const sol::state_view& lua); - class YamlLoader - { - 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 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); - }; - + sol::object loadYaml(std::istream& input, const sol::state_view& lua); } #endif // COMPONENTS_LUA_YAMLLOADER_H