From 3cd1d62ec00690a1b29070dd4903754e8f089a21 Mon Sep 17 00:00:00 2001 From: Hop311 Date: Wed, 6 Sep 2023 23:54:09 +0100 Subject: NodeTools cleanup+province definition csv loading --- src/openvic/dataloader/Dataloader.cpp | 147 +++++++++++--- src/openvic/dataloader/Dataloader.hpp | 9 +- src/openvic/dataloader/NodeTools.cpp | 373 +++++++++++++++++++--------------- src/openvic/dataloader/NodeTools.hpp | 169 ++++++++++++--- 4 files changed, 469 insertions(+), 229 deletions(-) (limited to 'src/openvic/dataloader') diff --git a/src/openvic/dataloader/Dataloader.cpp b/src/openvic/dataloader/Dataloader.cpp index f2b725e..685d987 100644 --- a/src/openvic/dataloader/Dataloader.cpp +++ b/src/openvic/dataloader/Dataloader.cpp @@ -3,11 +3,13 @@ #include "openvic/GameManager.hpp" #include "openvic/utility/Logger.hpp" +#include #include #include using namespace OpenVic; -using namespace ovdl::v2script; +using namespace OpenVic::NodeTools; +using namespace ovdl; return_t Dataloader::set_roots(std::vector new_roots) { if (!roots.empty()) { @@ -92,7 +94,8 @@ return_t Dataloader::apply_to_files_in_dir(std::filesystem::path const& path, return ret; } -Parser Dataloader::parse_defines(std::filesystem::path const& path) { +template Parser, bool(Parser::*parse_func)()> +static Parser _run_ovdl_parser(std::filesystem::path const& path) { Parser parser; std::string buffer; auto error_log_stream = ovdl::detail::CallbackStream { @@ -116,7 +119,9 @@ Parser Dataloader::parse_defines(std::filesystem::path const& path) { Logger::error("Parser errors while loading ", path); return parser; } - parser.simple_parse(); + if (!(parser.*parse_func)()) { + Logger::error("Parse function returned false!"); + } if (!buffer.empty()) { Logger::error("Parser parse errors:\n\n", buffer, "\n"); buffer.clear(); @@ -127,15 +132,21 @@ Parser Dataloader::parse_defines(std::filesystem::path const& path) { return parser; } -Parser Dataloader::parse_defines_lookup(std::filesystem::path const& path) const { - return parse_defines(lookup_file(path)); +static v2script::Parser _parse_defines(std::filesystem::path const& path) { + return _run_ovdl_parser(path); +} + +static csv::Windows1252Parser _parse_csv(std::filesystem::path const& path) { + return _run_ovdl_parser(path); } return_t Dataloader::_load_pop_types(PopManager& pop_manager, std::filesystem::path const& pop_type_directory) const { return_t ret = SUCCESS; - if (apply_to_files_in_dir(pop_type_directory, [&pop_manager](std::filesystem::path const& file) -> return_t { - return pop_manager.load_pop_type_file(file, parse_defines(file).get_file_node()); - }) != SUCCESS) { + if (apply_to_files_in_dir(pop_type_directory, + [&pop_manager](std::filesystem::path const& file) -> return_t { + return pop_manager.load_pop_type_file(file, _parse_defines(file).get_file_node()); + } + ) != SUCCESS) { Logger::error("Failed to load pop types!"); ret = FAILURE; } @@ -143,16 +154,98 @@ return_t Dataloader::_load_pop_types(PopManager& pop_manager, std::filesystem::p return ret; } +return_t Dataloader::_load_map_dir(Map& map, std::filesystem::path const& map_directory) const { + static const std::filesystem::path defaults_filename = "default.map"; + static const std::string default_definitions = "definition.csv"; + static const std::string default_provinces = "provinces.bmp"; + static const std::string default_positions = "positions.txt"; + static const std::string default_terrain = "terrain.bmp"; + static const std::string default_rivers = "rivers.bmp"; + static const std::string default_terrain_definition = "terrain.txt"; + static const std::string default_tree_definition = "trees.txt"; + static const std::string default_continent = "continent.txt"; + static const std::string default_adjacencies = "adjacencies.csv"; + static const std::string default_region = "region.txt"; + static const std::string default_region_sea = "region_sea.txt"; + static const std::string default_province_flag_sprite = "province_flag_sprites"; + + const v2script::Parser parser = _parse_defines(lookup_file(map_directory / defaults_filename)); + + std::vector water_province_identifiers; + +#define APPLY_TO_MAP_PATHS(F) \ + F(definitions) F(provinces) F(positions) F(terrain) F(rivers) \ + F(terrain_definition) F(tree_definition) F(continent) F(adjacencies) \ + F(region) F(region_sea) F(province_flag_sprite) + +#define MAP_PATH_VAR(X) std::string_view X = default_##X; + APPLY_TO_MAP_PATHS(MAP_PATH_VAR) +#undef MAP_PATH_VAR + + return_t ret = expect_dictionary_keys( + "max_provinces", ONE_EXACTLY, + expect_uint( + [&map](uint64_t val) -> return_t { + if (Province::NULL_INDEX < val && val <= Province::MAX_INDEX) { + return map.set_max_provinces(val); + } + Logger::error("Invalid max province count ", val, " (out of valid range ", Province::NULL_INDEX, " < max_provinces <= ", Province::MAX_INDEX, ")"); + return FAILURE; + } + ), + "sea_starts", ONE_EXACTLY, + expect_list_reserve_length( + water_province_identifiers, + expect_identifier( + [&water_province_identifiers](std::string_view identifier) -> return_t { + water_province_identifiers.push_back(identifier); + return SUCCESS; + } + ) + ), + +#define MAP_PATH_DICT_ENTRY(X) \ + #X, ONE_EXACTLY, expect_string(assign_variable_callback(X)), + APPLY_TO_MAP_PATHS(MAP_PATH_DICT_ENTRY) +#undef MAP_PATH_DICT_ENTRY + +#undef APPLY_TO_MAP_PATHS + + "border_heights", ZERO_OR_ONE, success_callback, + "terrain_sheet_heights", ZERO_OR_ONE, success_callback, + "tree", ZERO_OR_ONE, success_callback, + "border_cutoff", ZERO_OR_ONE, success_callback + )(parser.get_file_node()); + + if (ret != SUCCESS) { + Logger::error("Failed to load map default file!"); + } + + if (map.load_province_definitions(_parse_csv(lookup_file(map_directory / definitions)).get_lines()) != SUCCESS) { + Logger::error("Failed to load province definitions file!"); + ret = FAILURE; + } + + if (map.set_water_province_list(water_province_identifiers) != SUCCESS) { + Logger::error("Failed to set water provinces!"); + ret = FAILURE; + } + map.lock_water_provinces(); + + return ret; +} + return_t Dataloader::load_defines(GameManager& game_manager) const { static const std::filesystem::path good_file = "common/goods.txt"; static const std::filesystem::path pop_type_directory = "poptypes"; static const std::filesystem::path graphical_culture_type_file = "common/graphicalculturetype.txt"; static const std::filesystem::path culture_file = "common/cultures.txt"; static const std::filesystem::path religion_file = "common/religion.txt"; + static const std::filesystem::path map_directory = "map"; return_t ret = SUCCESS; - if (game_manager.good_manager.load_good_file(parse_defines_lookup(good_file).get_file_node()) != SUCCESS) { + if (game_manager.good_manager.load_good_file(_parse_defines(lookup_file(good_file)).get_file_node()) != SUCCESS) { Logger::error("Failed to load goods!"); ret = FAILURE; } @@ -160,35 +253,39 @@ return_t Dataloader::load_defines(GameManager& game_manager) const { Logger::error("Failed to load pop types!"); ret = FAILURE; } - if (game_manager.pop_manager.culture_manager.load_graphical_culture_type_file(parse_defines_lookup(graphical_culture_type_file).get_file_node()) != SUCCESS) { + if (game_manager.pop_manager.culture_manager.load_graphical_culture_type_file(_parse_defines(lookup_file(graphical_culture_type_file)).get_file_node()) != SUCCESS) { Logger::error("Failed to load graphical culture types!"); ret = FAILURE; } - if (game_manager.pop_manager.culture_manager.load_culture_file(parse_defines_lookup(culture_file).get_file_node()) != SUCCESS) { + if (game_manager.pop_manager.culture_manager.load_culture_file(_parse_defines(lookup_file(culture_file)).get_file_node()) != SUCCESS) { Logger::error("Failed to load cultures!"); ret = FAILURE; } - if (game_manager.pop_manager.religion_manager.load_religion_file(parse_defines_lookup(religion_file).get_file_node()) != SUCCESS) { + if (game_manager.pop_manager.religion_manager.load_religion_file(_parse_defines(lookup_file(religion_file)).get_file_node()) != SUCCESS) { Logger::error("Failed to load religions!"); ret = FAILURE; } + if (_load_map_dir(game_manager.map, map_directory) != SUCCESS) { + Logger::error("Failed to load map!"); + ret = FAILURE; + } return ret; } return_t Dataloader::load_pop_history(GameManager& game_manager, std::filesystem::path const& path) const { - return apply_to_files_in_dir(path, [&game_manager](std::filesystem::path const& file) -> return_t { - return NodeTools::expect_dictionary(parse_defines(file).get_file_node(), - [&game_manager](std::string_view province_key, ast::NodeCPtr province_node) -> return_t { - Province* province = game_manager.map.get_province_by_identifier(province_key); - if (province == nullptr) { - Logger::error("Invalid province id: ", province_key); - return FAILURE; - } - return NodeTools::expect_list(province_node, [&game_manager, &province](ast::NodeCPtr pop_node) -> return_t { - return game_manager.pop_manager.load_pop_into_province(*province, pop_node); + return apply_to_files_in_dir(path, + [&game_manager](std::filesystem::path const& file) -> return_t { + return expect_dictionary( + [&game_manager](std::string_view province_key, ast::NodeCPtr province_node) -> return_t { + Province* province = game_manager.map.get_province_by_identifier(province_key); + if (province == nullptr) { + Logger::error("Invalid province id: ", province_key); + return FAILURE; + } + return province->load_pop_list(game_manager.pop_manager, province_node); } - ); - }, true); - }); + )(_parse_defines(file).get_file_node()); + } + ); } diff --git a/src/openvic/dataloader/Dataloader.hpp b/src/openvic/dataloader/Dataloader.hpp index 3c868a3..9e5c24b 100644 --- a/src/openvic/dataloader/Dataloader.hpp +++ b/src/openvic/dataloader/Dataloader.hpp @@ -1,22 +1,21 @@ #pragma once #include +#include +#include #include "openvic/types/Return.hpp" -#include "openvic-dataloader/v2script/Parser.hpp" - namespace OpenVic { struct GameManager; struct PopManager; + struct Map; class Dataloader { std::vector roots; - static ovdl::v2script::Parser parse_defines(std::filesystem::path const& path); - ovdl::v2script::Parser parse_defines_lookup(std::filesystem::path const& path) const; - return_t _load_pop_types(PopManager& pop_manager, std::filesystem::path const& pop_type_directory) const; + return_t _load_map_dir(Map& map, std::filesystem::path const& map_directory) const; public: Dataloader() = default; diff --git a/src/openvic/dataloader/NodeTools.cpp b/src/openvic/dataloader/NodeTools.cpp index 6ab2ba6..6e5f7d0 100644 --- a/src/openvic/dataloader/NodeTools.cpp +++ b/src/openvic/dataloader/NodeTools.cpp @@ -3,214 +3,255 @@ #include using namespace OpenVic; +using namespace OpenVic::NodeTools; template -return_t NodeTools::expect_type(ast::NodeCPtr node, std::function callback) { - if (node != nullptr) { - if (node->is_type()) { - return callback(ast::cast_node_cptr(node)); +static node_callback_t _expect_type(std::function callback) { + return [callback](ast::NodeCPtr node) -> return_t { + if (node != nullptr) { + T const* cast_node = node->cast_to(); + if (cast_node != nullptr) { + return callback(*cast_node); + } + Logger::error("Invalid node type ", node->get_type(), " when expecting ", T::get_type_static()); + } else { + Logger::error("Null node when expecting ", T::get_type_static()); } - Logger::error("Invalid node type ", node->get_type(), " when expecting ", T::get_type_static()); - } else { - Logger::error("Null node when expecting ", T::get_type_static()); - } - return FAILURE; + return FAILURE; + }; } -static return_t identifier_callback_wrapper(std::function callback, std::string_view identifier) { - if (!identifier.empty()) { - return callback(identifier); - } - Logger::error("Empty identifier node string"); - return FAILURE; +template +requires(std::derived_from) +static std::function abstract_string_node_callback(std::function callback) { + return [callback](T const& node) -> return_t { + return callback(node._name); + }; } -return_t NodeTools::expect_identifier(ast::NodeCPtr node, std::function callback) { - return expect_type(node, [callback](ast::IdentifierNode const& identifier_node) -> return_t { - return identifier_callback_wrapper(callback, identifier_node._name); - }); +node_callback_t NodeTools::expect_identifier(std::function callback) { + return _expect_type(abstract_string_node_callback(callback)); } -return_t NodeTools::expect_string(ast::NodeCPtr node, std::function callback) { - return expect_type(node, [callback](ast::StringNode const& string_node) -> return_t { - return callback(string_node._name); - }); +node_callback_t NodeTools::expect_string(std::function callback) { + return _expect_type(abstract_string_node_callback(callback)); } -return_t NodeTools::expect_identifier_or_string(ast::NodeCPtr node, std::function callback) { - if (node != nullptr) { - if (node->is_type()) { - return identifier_callback_wrapper(callback, ast::cast_node_cptr(node)._name); - } else if (node->is_type()) { - return callback(ast::cast_node_cptr(node)._name); - } else { +node_callback_t NodeTools::expect_identifier_or_string(std::function callback) { + return [callback](ast::NodeCPtr node) -> return_t { + if (node != nullptr) { + ast::AbstractStringNode const* cast_node = node->cast_to(); + if (cast_node == nullptr) { + cast_node = node->cast_to(); + } + if (cast_node != nullptr) { + return abstract_string_node_callback(callback)(*cast_node); + } Logger::error("Invalid node type ", node->get_type(), " when expecting ", ast::IdentifierNode::get_type_static(), " or ", ast::StringNode::get_type_static()); + } else { + Logger::error("Null node when expecting ", ast::IdentifierNode::get_type_static(), " or ", ast::StringNode::get_type_static()); } - } else { - Logger::error("Null node when expecting ", ast::IdentifierNode::get_type_static(), " or ", ast::StringNode::get_type_static()); - } - return FAILURE; -} - -return_t NodeTools::expect_bool(ast::NodeCPtr node, std::function callback) { - return expect_identifier(node, [callback](std::string_view identifier) -> return_t { - if (identifier == "yes") { - return callback(true); - } else if (identifier == "no") { - return callback(false); - } - Logger::error("Invalid bool identifier text: ", identifier); return FAILURE; - }); + }; } -return_t NodeTools::expect_int(ast::NodeCPtr node, std::function callback) { - return expect_identifier(node, [callback](std::string_view identifier) -> return_t { - bool successful = false; - const int64_t val = StringUtils::string_to_int64(identifier, &successful, 10); - if (successful) { - return callback(val); +node_callback_t NodeTools::expect_bool(std::function callback) { + return expect_identifier( + [callback](std::string_view identifier) -> return_t { + if (identifier == "yes") { + return callback(true); + } else if (identifier == "no") { + return callback(false); + } + Logger::error("Invalid bool identifier text: ", identifier); + return FAILURE; } - Logger::error("Invalid int identifier text: ", identifier); - return FAILURE; - }); + ); } -return_t NodeTools::expect_uint(ast::NodeCPtr node, std::function callback) { - return expect_identifier(node, [callback](std::string_view identifier) -> return_t { - bool successful = false; - const uint64_t val = StringUtils::string_to_uint64(identifier, &successful, 10); - if (successful) { - return callback(val); +node_callback_t NodeTools::expect_int(std::function callback) { + return expect_identifier( + [callback](std::string_view identifier) -> return_t { + bool successful = false; + const int64_t val = StringUtils::string_to_int64(identifier, &successful, 10); + if (successful) { + return callback(val); + } + Logger::error("Invalid int identifier text: ", identifier); + return FAILURE; } - Logger::error("Invalid uint identifier text: ", identifier); - return FAILURE; - }); + ); } -return_t NodeTools::expect_fixed_point(ast::NodeCPtr node, std::function callback) { - return expect_identifier(node, [callback](std::string_view identifier) -> return_t { - bool successful = false; - const FP val = FP::parse(identifier.data(), identifier.length(), &successful); - if (successful) { - return callback(val); +node_callback_t NodeTools::expect_uint(std::function callback) { + return expect_identifier( + [callback](std::string_view identifier) -> return_t { + bool successful = false; + const uint64_t val = StringUtils::string_to_uint64(identifier, &successful, 10); + if (successful) { + return callback(val); + } + Logger::error("Invalid uint identifier text: ", identifier); + return FAILURE; } - Logger::error("Invalid fixed point identifier text: ", identifier); - return FAILURE; - }); + ); +} + +node_callback_t NodeTools::expect_fixed_point(std::function callback) { + return expect_identifier( + [callback](std::string_view identifier) -> return_t { + bool successful = false; + const FP val = FP::parse(identifier.data(), identifier.length(), &successful); + if (successful) { + return callback(val); + } + Logger::error("Invalid fixed point identifier text: ", identifier); + return FAILURE; + } + ); +} + +node_callback_t NodeTools::expect_colour(std::function callback) { + return [callback](ast::NodeCPtr node) -> return_t { + colour_t col = NULL_COLOUR; + uint32_t components = 0; + return_t ret = expect_list_of_length(3, + expect_fixed_point( + [&col, &components](FP val) -> return_t { + return_t ret = SUCCESS; + if (val < 0 || val > 255) { + Logger::error("Invalid colour component: ", val); + val = FP::_0(); + ret = FAILURE; + } + if (val <= 1) val *= 255; + col = (col << 8) | val.to_int32_t(); + components++; + return ret; + } + ) + )(node); + if (components < 3) col <<= 8 * (3 - components); + if (callback(col) != SUCCESS) ret = FAILURE; + return ret; + }; +} + +node_callback_t NodeTools::expect_date(std::function callback) { + return expect_identifier( + [callback](std::string_view identifier) -> return_t { + bool successful = false; + const Date date = Date::from_string(identifier, &successful); + if (successful) { + return callback(date); + } + Logger::error("Invalid date identifier text: ", identifier); + return FAILURE; + } + ); } -return_t NodeTools::expect_colour(ast::NodeCPtr node, std::function callback) { - colour_t col = NULL_COLOUR; - uint32_t components = 0; - return_t ret = expect_list_of_length(node, std::bind(expect_fixed_point, std::placeholders::_1, - [&col, &components](FP val) -> return_t { +node_callback_t NodeTools::expect_assign(key_value_callback_t callback) { + return _expect_type( + [callback](ast::AssignNode const& assign_node) -> return_t { + return callback(assign_node._name, assign_node._initializer.get()); + } + ); +} + +node_callback_t NodeTools::expect_list_and_length(length_callback_t length_callback, node_callback_t callback) { + return _expect_type( + [length_callback, callback](ast::AbstractListNode const& list_node) -> return_t { + std::vector const& list = list_node._statements; return_t ret = SUCCESS; - if (val < 0 || val > 255) { - Logger::error("Invalid colour component: ", val); - val = FP::_0(); + size_t size = length_callback(list.size()); + if (size > list.size()) { + Logger::error("Trying to read more values than the list contains: ", size, " > ", list.size()); + size = list.size(); ret = FAILURE; } - if (val <= 1) val *= 255; - col = (col << 8) | val.to_int32_t(); - components++; + std::for_each(list.begin(), list.begin() + size, + [callback, &ret](ast::NodeUPtr const& sub_node) -> void { + if (callback(sub_node.get()) != SUCCESS) ret = FAILURE; + } + ); return ret; - }), 3); - if (components < 3) col <<= 8 * (3 - components); - if (callback(col) != SUCCESS) ret = FAILURE; - return ret; -} - -return_t NodeTools::expect_date(ast::NodeCPtr node, std::function callback) { - return expect_identifier(node, [callback](std::string_view identifier) -> return_t { - bool successful = false; - const Date date = Date::from_string(identifier, &successful); - if (successful) { - return callback(date); } - Logger::error("Invalid date identifier text: ", identifier); - return FAILURE; - }); + ); } -return_t NodeTools::expect_assign(ast::NodeCPtr node, std::function callback) { - return expect_type(node, [callback](ast::AssignNode const& assign_node) -> return_t { - return callback(assign_node._name, assign_node._initializer.get()); - }); -} - -return_t NodeTools::expect_list_and_length(ast::NodeCPtr node, std::function length_callback, std::function callback, bool file_node) { - const std::function const&)> list_func = [length_callback, callback](std::vector const& list) -> return_t { +node_callback_t NodeTools::expect_list_of_length(size_t length, node_callback_t callback) { + return [length, callback](ast::NodeCPtr node) -> return_t { return_t ret = SUCCESS; - size_t size = length_callback(list.size()); - if (size > list.size()) { - Logger::error("Trying to read more values than the list contains: ", size, " > ", list.size()); - size = list.size(); - ret = FAILURE; - } - std::for_each(list.begin(), list.begin() + size, [callback, &ret](ast::NodeUPtr const& sub_node) -> void { - if (callback(sub_node.get()) != SUCCESS) ret = FAILURE; - }); + if (expect_list_and_length( + [length, &ret](size_t size) -> size_t { + if (size != length) { + Logger::error("List length ", size, " does not match expected length ", length); + ret = FAILURE; + if (length < size) return length; + } + return size; + }, + callback + )(node) != SUCCESS) ret = FAILURE; return ret; }; - if (!file_node) { - return expect_type(node, [list_func](ast::ListNode const& list_node) -> return_t { - return list_func(list_node._statements); - }); - } else { - return expect_type(node, [list_func](ast::FileNode const& file_node) -> return_t { - return list_func(file_node._statements); - }); - } -} - -return_t NodeTools::expect_list_of_length(ast::NodeCPtr node, std::function callback, size_t length, bool file_node) { - return_t ret = SUCCESS; - if (expect_list_and_length(node, [length, &ret](size_t size) -> size_t { - if (size != length) { - Logger::error("List length ", size, " does not match expected length ", length); - ret = FAILURE; - if (length < size) return length; - } - return size; - }, callback, file_node) != SUCCESS) ret = FAILURE; - return ret; } -return_t NodeTools::expect_list(ast::NodeCPtr node, std::function callback, bool file_node) { - return expect_list_and_length(node, default_length_callback, callback, file_node); +node_callback_t NodeTools::expect_list(node_callback_t callback) { + return expect_list_and_length(default_length_callback, callback); } -return_t NodeTools::expect_dictionary_and_length(ast::NodeCPtr node, std::function length_callback, std::function callback, bool file_node) { - return expect_list_and_length(node, length_callback, std::bind(expect_assign, std::placeholders::_1, callback), file_node); +node_callback_t NodeTools::expect_dictionary_and_length(length_callback_t length_callback, key_value_callback_t callback) { + return expect_list_and_length(length_callback, expect_assign(callback)); } -return_t NodeTools::expect_dictionary(ast::NodeCPtr node, std::function callback, bool file_node) { - return expect_dictionary_and_length(node, default_length_callback, callback, file_node); +node_callback_t NodeTools::expect_dictionary(key_value_callback_t callback) { + return expect_dictionary_and_length(default_length_callback, callback); } -return_t NodeTools::expect_dictionary_keys(ast::NodeCPtr node, dictionary_key_map_t const& keys, bool allow_other_keys, bool file_node) { - std::map> key_count; - return_t ret = expect_dictionary(node, [keys, allow_other_keys, &key_count](std::string_view key, ast::NodeCPtr value) -> return_t { - const dictionary_key_map_t::const_iterator it = keys.find(key); - if (it == keys.end()) { - if (allow_other_keys) return SUCCESS; - Logger::error("Invalid dictionary key: ", key); - return FAILURE; - } - const size_t count = ++key_count[it->first]; - dictionary_entry_t const& entry = it->second; - if (!entry.can_repeat && count > 1) { - Logger::error("Invalid repeat of dictionary key: ", key); - return FAILURE; - } - return entry.callback(value); - }, file_node); - for (dictionary_key_map_t::value_type const& entry : keys) { - if (entry.second.must_appear && key_count.find(entry.first) == key_count.end()) { - Logger::error("Mandatory dictionary key not present: ", entry.first); - ret = FAILURE; +node_callback_t NodeTools::_expect_dictionary_keys_and_length(length_callback_t length_callback, bool allow_other_keys, key_map_t&& key_map) { + return [length_callback, allow_other_keys, key_map = std::move(key_map)](ast::NodeCPtr node) mutable -> return_t { + return_t ret = expect_dictionary_and_length( + length_callback, + [&key_map, allow_other_keys](std::string_view key, ast::NodeCPtr value) -> return_t { + const key_map_t::iterator it = key_map.find(key); + if (it == key_map.end()) { + if (allow_other_keys) return SUCCESS; + Logger::error("Invalid dictionary key: ", key); + return FAILURE; + } + dictionary_entry_t& entry = it->second; + if (++entry.count > 1 && !entry.can_repeat()) { + Logger::error("Invalid repeat of dictionary key: ", key); + return FAILURE; + } + return entry.callback(value); + } + )(node); + for (key_map_t::value_type const& key_entry : key_map) { + dictionary_entry_t const& entry = key_entry.second; + if (entry.must_appear() && entry.count < 1) { + Logger::error("Mandatory dictionary key not present: ", key_entry.first); + ret = FAILURE; + } } - } - return ret; + return ret; + }; +} + +node_callback_t NodeTools::name_list_callback(std::vector& list) { + return expect_list_reserve_length( + list, + expect_identifier_or_string( + [&list](std::string_view str) -> return_t { + if (!str.empty()) { + list.push_back(std::string { str }); + return SUCCESS; + } + Logger::error("Empty identifier or string"); + return FAILURE; + } + ) + ); } diff --git a/src/openvic/dataloader/NodeTools.hpp b/src/openvic/dataloader/NodeTools.hpp index 48c3710..6dbaa1c 100644 --- a/src/openvic/dataloader/NodeTools.hpp +++ b/src/openvic/dataloader/NodeTools.hpp @@ -14,46 +14,149 @@ namespace OpenVic { namespace NodeTools { + using node_callback_t = std::function; + constexpr return_t success_callback(ast::NodeCPtr) { return SUCCESS; } + + using key_value_callback_t = std::function; + + node_callback_t expect_identifier(std::function callback); + node_callback_t expect_string(std::function callback); + node_callback_t expect_identifier_or_string(std::function callback); + node_callback_t expect_bool(std::function callback); + node_callback_t expect_int(std::function callback); + node_callback_t expect_uint(std::function callback); + node_callback_t expect_fixed_point(std::function callback); + node_callback_t expect_colour(std::function callback); + node_callback_t expect_date(std::function callback); + node_callback_t expect_assign(key_value_callback_t callback); + + using length_callback_t = std::function; + constexpr size_t default_length_callback(size_t size) { return size; }; + + node_callback_t expect_list_and_length(length_callback_t length_callback, node_callback_t callback); + node_callback_t expect_list_of_length(size_t length, node_callback_t callback); + node_callback_t expect_list(node_callback_t callback); + + node_callback_t expect_dictionary_and_length(length_callback_t length_callback, key_value_callback_t callback); + node_callback_t expect_dictionary(key_value_callback_t callback); + + struct dictionary_entry_t { + const enum class expected_count_t : uint8_t { + _MUST_APPEAR = 0b01, + _CAN_REPEAT = 0b10, + + ZERO_OR_ONE = 0, + ONE_EXACTLY = _MUST_APPEAR, + ZERO_OR_MORE = _CAN_REPEAT, + ONE_OR_MORE = _MUST_APPEAR | _CAN_REPEAT + } expected_count; + const node_callback_t callback; + size_t count; + + dictionary_entry_t(expected_count_t new_expected_count, node_callback_t new_callback) + : expected_count { new_expected_count }, callback { new_callback }, count { 0 } {} + + constexpr bool must_appear() const { + return static_cast(expected_count) & static_cast(expected_count_t::_MUST_APPEAR); + } + constexpr bool can_repeat() const { + return static_cast(expected_count) & static_cast(expected_count_t::_CAN_REPEAT); + } + }; + using enum dictionary_entry_t::expected_count_t; + using key_map_t = std::map>; + + constexpr struct allow_other_keys_t {} ALLOW_OTHER_KEYS; + + node_callback_t _expect_dictionary_keys_and_length(length_callback_t length_callback, bool allow_other_keys, key_map_t&& key_map); + + template + node_callback_t _expect_dictionary_keys_and_length(length_callback_t length_callback, + bool allow_other_keys, key_map_t&& key_map, + const std::string_view key, dictionary_entry_t::expected_count_t expected_count, node_callback_t callback, + Args... args) { + if (key_map.find(key) == key_map.end()) { + key_map.emplace(key, dictionary_entry_t { expected_count, callback }); + } else { + Logger::error("Duplicate expected dictionary key: ", key); + } + return _expect_dictionary_keys_and_length(length_callback, allow_other_keys, std::move(key_map), args...); + } + + template + node_callback_t expect_dictionary_keys_and_length(length_callback_t length_callback, + const std::string_view key, dictionary_entry_t::expected_count_t expected_count, node_callback_t callback, + Args... args) { + return _expect_dictionary_keys_and_length(length_callback, false, {}, key, expected_count, callback, args...); + } + + template + node_callback_t expect_dictionary_keys_and_length(length_callback_t length_callback, + allow_other_keys_t, Args... args) { + return _expect_dictionary_keys_and_length(length_callback, true, {}, args...); + } + + template + node_callback_t expect_dictionary_keys(Args... args) { + return expect_dictionary_keys_and_length(default_length_callback, args...); + } + template - return_t expect_type(ast::NodeCPtr node, std::function callback); - - return_t expect_identifier(ast::NodeCPtr node, std::function callback); - return_t expect_string(ast::NodeCPtr node, std::function callback); - return_t expect_identifier_or_string(ast::NodeCPtr node, std::function callback); - return_t expect_bool(ast::NodeCPtr node, std::function callback); - return_t expect_int(ast::NodeCPtr node, std::function callback); - return_t expect_uint(ast::NodeCPtr node, std::function callback); - return_t expect_fixed_point(ast::NodeCPtr node, std::function callback); - return_t expect_colour(ast::NodeCPtr node, std::function callback); - return_t expect_date(ast::NodeCPtr node, std::function callback); - return_t expect_assign(ast::NodeCPtr node, std::function callback); - - static const std::function default_length_callback = [](size_t size) -> size_t { return size; }; - - template requires requires(T& t) { + concept Reservable = requires(T& t) { { t.size() } -> std::same_as; t.reserve( size_t {} ); + }; + template + node_callback_t expect_list_reserve_length(T& t, node_callback_t callback) { + return expect_list_and_length( + [&t](size_t size) -> size_t { + t.reserve(t.size() + size); + return size; + }, + callback + ); } - std::function reserve_length_callback(T& t) { - return [&t](size_t size) -> size_t { - t.reserve(t.size() + size); - return size; - }; + template + node_callback_t expect_dictionary_reserve_length(T& t, key_value_callback_t callback) { + return expect_list_reserve_length(t, expect_assign(callback)); } - return_t expect_list_and_length(ast::NodeCPtr node, std::function length_callback, std::function callback, bool file_node = false); - return_t expect_list_of_length(ast::NodeCPtr node, std::function callback, size_t length, bool file_node = false); - return_t expect_list(ast::NodeCPtr node, std::function callback, bool file_node = false); - return_t expect_dictionary_and_length(ast::NodeCPtr node, std::function length_callback, std::function callback, bool file_node = false); - return_t expect_dictionary(ast::NodeCPtr node, std::function callback, bool file_node = false); + node_callback_t name_list_callback(std::vector& list); + + template + std::function assign_variable_callback(T& var) { + return [&var](T val) -> return_t { + var = val; + return SUCCESS; + }; + } - static const std::function success_callback = [](ast::NodeCPtr) -> return_t { return SUCCESS; }; + template + requires(std::integral) + std::function assign_variable_callback_uint(const std::string_view name, T& var) { + return [&var, name](uint64_t val) -> return_t { + if (val <= std::numeric_limits::max()) { + var = val; + return SUCCESS; + } + Logger::error("Invalid ", name, ": ", val, " (valid range: [0, ", static_cast(std::numeric_limits::max()), "])"); + return FAILURE; + }; + } - struct dictionary_entry_t { - bool must_appear, can_repeat; - std::function callback; - }; - using dictionary_key_map_t = std::map>; - return_t expect_dictionary_keys(ast::NodeCPtr node, dictionary_key_map_t const& keys, bool allow_other_keys = false, bool file_node = false); + template + requires(std::integral) + std::function assign_variable_callback_int(const std::string_view name, T& var) { + return [&var, name](int64_t val) -> return_t { + if (std::numeric_limits::lowest() <= val && val <= std::numeric_limits::max()) { + var = val; + return SUCCESS; + } + Logger::error("Invalid ", name, ": ", val, " (valid range: [", + static_cast(std::numeric_limits::lowest()), ", ", + static_cast(std::numeric_limits::max()), "])"); + return FAILURE; + }; + } } } -- cgit v1.2.3-56-ga3b1