#include "NodeTools.hpp" #include #include #include #include #include #include #include #include #include "openvic-simulation/types/Colour.hpp" #include "openvic-simulation/utility/Getters.hpp" using namespace OpenVic; using namespace OpenVic::NodeTools; #define MOV(...) static_cast&&>(__VA_ARGS__) #define FWD(...) static_cast(__VA_ARGS__) template static NodeCallback auto _expect_type(Callback auto&& callback) { return [callback = FWD(callback)](ast::NodeCPtr node) -> bool { if (node != nullptr) { T const* cast_node = dryad::node_try_cast(node); if (cast_node != nullptr) { return callback(cast_node); } Logger::error("Invalid node type ", ast::get_type_name(node->kind()), " when expecting ", utility::type_name()); } else { Logger::error("Null node when expecting ", utility::type_name()); } return false; }; } using _NodeIterator = typename decltype(std::declval()->children())::iterator; using _NodeStatementRange = dryad::node_range<_NodeIterator, ast::Statement>; static NodeCallback auto _abstract_statement_node_callback(Callback<_NodeStatementRange> auto&& callback) { return [callback = FWD(callback)](ast::NodeCPtr node) -> bool { if (node != nullptr) { if (auto const* file_tree = dryad::node_try_cast(node)) { return callback(file_tree->statements()); } if (auto const* list_value = dryad::node_try_cast(node)) { return callback(list_value->statements()); } Logger::error( "Invalid node type ", ast::get_type_name(node->kind()), " when expecting ", utility::type_name(), " or ", utility::type_name() ); } else { Logger::error( "Null node when expecting ", utility::type_name(), " or ", utility::type_name() ); } return false; }; } template T> static Callback auto _abstract_symbol_node_callback(Callback> auto&& callback, bool allow_empty) { return [callback = FWD(callback), allow_empty](T const* node) -> bool { if (allow_empty) { return callback(node->value()); } else { if (node->value()) { return callback(node->value()); } else { Logger::error("Invalid string value - empty!"); return false; } } }; } template T> static Callback auto _abstract_string_node_callback(Callback auto callback, bool allow_empty) { return _abstract_symbol_node_callback( [callback](ovdl::symbol symbol) -> bool { return callback(symbol.view()); }, allow_empty ); } node_callback_t NodeTools::expect_identifier(callback_t callback) { return _expect_type(_abstract_string_node_callback(callback, false)); } static NodeCallback auto _expect_identifier(Callback> auto callback) { return _expect_type(_abstract_symbol_node_callback(callback, false)); } node_callback_t NodeTools::expect_string(callback_t callback, bool allow_empty) { return _expect_type(_abstract_string_node_callback(callback, allow_empty)); } static NodeCallback auto _expect_string(Callback> auto callback, bool allow_empty) { return _expect_type(_abstract_symbol_node_callback(callback, allow_empty)); } node_callback_t NodeTools::expect_identifier_or_string(callback_t callback, bool allow_empty) { return [callback, allow_empty](ast::NodeCPtr node) -> bool { if (node != nullptr) { auto const* cast_node = dryad::node_try_cast(node); if (cast_node != nullptr) { return _abstract_string_node_callback(callback, allow_empty)(cast_node); } Logger::error( "Invalid node type ", ast::get_type_name(node->kind()), " when expecting ", utility::type_name(), " or ", utility::type_name() ); } else { Logger::error( "Null node when expecting ", utility::type_name(), " or ", utility::type_name() ); } return false; }; } node_callback_t NodeTools::expect_bool(callback_t callback) { static const case_insensitive_string_map_t bool_map { { "yes", true }, { "no", false } }; return expect_identifier(expect_mapped_string(bool_map, callback)); } node_callback_t NodeTools::expect_int_bool(callback_t callback) { return expect_uint64([callback](uint64_t num) -> bool { if (num > 1) { Logger::warning("Found int bool with value >1: ", num); } return callback(num != 0); }); } node_callback_t NodeTools::expect_int64(callback_t callback, int base) { return expect_identifier([callback, base](std::string_view identifier) -> bool { bool successful = false; const int64_t val = StringUtils::string_to_int64(identifier, &successful, base); if (successful) { return callback(val); } Logger::error("Invalid int identifier text: ", identifier); return false; }); } node_callback_t NodeTools::expect_uint64(callback_t callback, int base) { return expect_identifier([callback, base](std::string_view identifier) -> bool { bool successful = false; const uint64_t val = StringUtils::string_to_uint64(identifier, &successful, base); if (successful) { return callback(val); } Logger::error("Invalid uint identifier text: ", identifier); return false; }); } callback_t NodeTools::expect_fixed_point_str(callback_t callback) { return [callback](std::string_view identifier) -> bool { bool successful = false; const fixed_point_t val = fixed_point_t::parse(identifier.data(), identifier.length(), &successful); if (successful) { return callback(val); } Logger::error("Invalid fixed point identifier text: ", identifier); return false; }; } node_callback_t NodeTools::expect_fixed_point(callback_t callback) { return expect_identifier(expect_fixed_point_str(callback)); } node_callback_t NodeTools::expect_colour(callback_t callback) { return [callback](ast::NodeCPtr node) -> bool { colour_t col = colour_t::null(); int32_t components = 0; bool ret = expect_list_of_length(3, expect_fixed_point( [&col, &components](fixed_point_t val) -> bool { if (val < 0 || val > 255) { Logger::error("Invalid colour component #", components++, ": ", val); return false; } else { if (val <= 1) { val *= 255; } else if (!val.is_integer()) { Logger::warning("Fractional part of colour component #", components, " will be truncated: ", val); } col[components++] = val.to_int64_t(); return true; } } ))(node); ret &= callback(col); return ret; }; } node_callback_t NodeTools::expect_colour_hex(callback_t callback) { return expect_uint([callback](colour_argb_t::integer_type val) -> bool { return callback(colour_argb_t::from_integer(val)); }, 16); } callback_t NodeTools::expect_date_str(callback_t callback) { return [callback](std::string_view identifier) -> bool { bool successful = false; const Date date = Date::from_string(identifier, &successful); if (successful) { return callback(date); } Logger::error("Invalid date identifier text: ", identifier); return false; }; } node_callback_t NodeTools::expect_date(callback_t callback) { return expect_identifier(expect_date_str(callback)); } node_callback_t NodeTools::expect_date_string(callback_t callback) { return expect_string(expect_date_str(callback)); } node_callback_t NodeTools::expect_date_identifier_or_string(callback_t callback) { return expect_identifier_or_string(expect_date_str(callback)); } node_callback_t NodeTools::expect_years(callback_t callback) { return expect_int([callback](Timespan::day_t val) -> bool { return callback(Timespan::from_years(val)); }); } node_callback_t NodeTools::expect_months(callback_t callback) { return expect_int([callback](Timespan::day_t val) -> bool { return callback(Timespan::from_months(val)); }); } node_callback_t NodeTools::expect_days(callback_t callback) { return expect_int([callback](Timespan::day_t val) -> bool { return callback(Timespan::from_days(val)); }); } template)> NodeCallback auto _expect_vec2(Callback> auto&& callback) { return [callback = FWD(callback)](ast::NodeCPtr node) -> bool { vec2_t vec; bool ret = expect_dictionary_keys( "x", ONE_EXACTLY, expect_func(assign_variable_callback(vec.x)), "y", ONE_EXACTLY, expect_func(assign_variable_callback(vec.y)) )(node); ret &= callback(vec); return ret; }; } static node_callback_t _expect_int(callback_t callback) { return expect_int(callback); } node_callback_t NodeTools::expect_ivec2(callback_t callback) { return _expect_vec2(callback); } node_callback_t NodeTools::expect_fvec2(callback_t callback) { return _expect_vec2(callback); } node_callback_t NodeTools::expect_assign(key_value_callback_t callback) { return _expect_type([callback](ast::AssignStatement const* assign_node) -> bool { std::string_view left; bool ret = expect_identifier(assign_variable_callback(left))(assign_node->left()); if (ret) { ret &= callback(left, assign_node->right()); if (!ret) { Logger::error("Callback failed for assign node with key: ", left); } } else { Logger::error("Callback key failed for assign node with key: ", left); } return ret; }); } node_callback_t NodeTools::expect_list_and_length(length_callback_t length_callback, node_callback_t callback) { return _abstract_statement_node_callback([length_callback, callback](_NodeStatementRange list) -> bool { bool ret = true; auto dist = ranges::distance(list); size_t size = length_callback(dist); if (size > dist) { Logger::error("Trying to read more values than the list contains: ", size, " > ", dist); size = dist; ret = false; } for (auto [index, sub_node] : list | ranges::views::enumerate) { if (index >= size) { break; } if (auto const* value = dryad::node_try_cast(sub_node)) { ret &= callback(value->value()); continue; } ret &= callback(sub_node); } return ret; }); } node_callback_t NodeTools::expect_list_of_length(size_t length, node_callback_t callback) { return [length, callback](ast::NodeCPtr node) -> bool { bool ret = true; ret &= 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 = false; if (length < size) { return length; } } return size; }, callback )(node); return ret; }; } node_callback_t NodeTools::expect_list(node_callback_t callback) { return expect_list_and_length(default_length_callback, callback); } node_callback_t NodeTools::expect_length(callback_t callback) { return [callback](ast::NodeCPtr node) -> bool { bool ret = true; ret &= expect_list_and_length( [callback, &ret](size_t size) -> size_t { ret &= callback(size); return 0; }, success_callback )(node); return ret; }; } template static node_callback_t _expect_key(Key key, NodeCallback auto callback, bool* key_found, bool allow_duplicates) { if constexpr (std::same_as>) { if (!key) { if (key_found != nullptr) { *key_found = false; } return [](ast::NodeCPtr) -> bool { Logger::error("Failed to find expected interned key."); return false; }; } } static constexpr auto assign_left = [](Key& left) { if constexpr (std::same_as) { return expect_identifier(assign_variable_callback(left)); } else if (std::same_as>) { return _expect_identifier(assign_variable_callback_cast>(left)); } }; return _abstract_statement_node_callback([key, callback, key_found, allow_duplicates](_NodeStatementRange list) -> bool { bool ret = true; size_t keys_found = 0; for (auto sub_node : list) { auto const* assign_node = dryad::node_try_cast(sub_node); if (assign_node == nullptr) { continue; } Key left; if (!assign_left(left)(assign_node->left())) { continue; } if (left == key) { if (keys_found++ == 0) { ret &= callback(assign_node->right()); if (allow_duplicates) { break; } } } } std::string_view key_str = [&] { if constexpr (std::same_as) { return key; } else { return key.view(); } }(); if (keys_found == 0) { if (key_found != nullptr) { *key_found = false; } else { Logger::error("Failed to find expected key: \"", key_str, "\""); } ret = false; } else { if (key_found != nullptr) { *key_found = true; } if (!allow_duplicates && keys_found > 1) { Logger::error("Found ", keys_found, " instances of key: \"", key_str, "\" (expected 1)"); ret = false; } } return ret; }); } node_callback_t NodeTools::expect_key(std::string_view key, node_callback_t callback, bool* key_found, bool allow_duplicates) { return _expect_key(key, callback, key_found, allow_duplicates); } node_callback_t NodeTools::expect_key(ovdl::symbol key, node_callback_t callback, bool* key_found, bool allow_duplicates) { return _expect_key(key, callback, key_found, allow_duplicates); } 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)); } node_callback_t NodeTools::expect_dictionary(key_value_callback_t callback) { return expect_dictionary_and_length(default_length_callback, callback); } node_callback_t NodeTools::name_list_callback(callback_t callback) { return [callback](ast::NodeCPtr node) -> bool { name_list_t list; bool ret = expect_list_reserve_length( list, expect_identifier_or_string(vector_callback(list)) )(node); ret &= callback(std::move(list)); return ret; }; } std::ostream& OpenVic::operator<<(std::ostream& stream, name_list_t const& name_list) { stream << '['; if (!name_list.empty()) { stream << name_list.front(); std::for_each(name_list.begin() + 1, name_list.end(), [&stream](std::string const& name) -> void { stream << ", " << name; }); } return stream << ']'; } callback_t NodeTools::assign_variable_callback_string(std::string& var) { return assign_variable_callback_cast(var); }