diff --git a/src/language/node_processor/BinaryExpressionProcessor.hpp b/src/language/node_processor/BinaryExpressionProcessor.hpp index 3af45c3871d6d6b4886f3bd3bf510d7c53f333aa..2c5d7460f26577633abed7281425d234b4d95432 100644 --- a/src/language/node_processor/BinaryExpressionProcessor.hpp +++ b/src/language/node_processor/BinaryExpressionProcessor.hpp @@ -232,7 +232,7 @@ struct BinaryExpressionProcessor<BinaryOpT, std::shared_ptr<ValueT>, std::shared return this->_eval(m_node.children[0]->execute(exec_policy), m_node.children[1]->execute(exec_policy)); } catch (const NormalError& error) { - throw ParseError(error.what(), m_node.begin()); + throw ParseError(error.what(), m_node.begin()); // LCOV_EXCL_LINE } } @@ -269,7 +269,7 @@ struct BinaryExpressionProcessor<BinaryOpT, std::shared_ptr<ValueT>, A_DataT, st return this->_eval(m_node.children[0]->execute(exec_policy), m_node.children[1]->execute(exec_policy)); } catch (const NormalError& error) { - throw ParseError(error.what(), m_node.begin()); + throw ParseError(error.what(), m_node.begin()); // LCOV_EXCL_LINE } } @@ -306,7 +306,7 @@ struct BinaryExpressionProcessor<BinaryOpT, std::shared_ptr<ValueT>, std::shared return this->_eval(m_node.children[0]->execute(exec_policy), m_node.children[1]->execute(exec_policy)); } catch (const NormalError& error) { - throw ParseError(error.what(), m_node.begin()); + throw ParseError(error.what(), m_node.begin()); // LCOV_EXCL_LINE } } diff --git a/tests/test_BinaryExpressionProcessor_arithmetic.cpp b/tests/test_BinaryExpressionProcessor_arithmetic.cpp index 2c91674b2fd300bb59ef4faae100c4ea0423df33..198b24e7096a7d8d4d18b5fd0774f67cdf5cf659 100644 --- a/tests/test_BinaryExpressionProcessor_arithmetic.cpp +++ b/tests/test_BinaryExpressionProcessor_arithmetic.cpp @@ -1,10 +1,58 @@ #include <catch2/catch_test_macros.hpp> #include <catch2/matchers/catch_matchers_all.hpp> +#include <language/utils/ASTNodeDataTypeTraits.hpp> +#include <language/utils/BasicAffectationRegistrerFor.hpp> +#include <language/utils/BinaryOperatorProcessorBuilder.hpp> +#include <language/utils/DataHandler.hpp> +#include <language/utils/OperatorRepository.hpp> +#include <language/utils/TypeDescriptor.hpp> + #include <test_BinaryExpressionProcessor_utils.hpp> // clazy:excludeall=non-pod-global-static +template <> +inline ASTNodeDataType ast_node_data_type_from<std::shared_ptr<const double>> = + ASTNodeDataType::build<ASTNodeDataType::type_id_t>("builtin_t"); +const auto builtin_data_type = ast_node_data_type_from<std::shared_ptr<const double>>; + +inline std::shared_ptr<const double> +operator+(const std::shared_ptr<const double>& p_a, const std::shared_ptr<const double>& p_b) +{ + return std::make_shared<double>(*p_a + *p_b); +} + +inline std::shared_ptr<const double> +operator/(const std::shared_ptr<const double>&, const std::shared_ptr<const double>&) +{ + throw std::runtime_error("runtime error both"); +} + +inline std::shared_ptr<const double> +operator/(const std::shared_ptr<const double>&, double) +{ + throw std::runtime_error("runtime error lhs"); +} + +inline std::shared_ptr<const double> +operator/(double, const std::shared_ptr<const double>&) +{ + throw std::runtime_error("runtime error rhs"); +} + +inline std::shared_ptr<const double> +operator+(const std::shared_ptr<const double>& p_a, double b) +{ + return std::make_shared<double>(*p_a + b); +} + +inline std::shared_ptr<const double> +operator+(double a, const std::shared_ptr<const double>& p_b) +{ + return std::make_shared<double>(a + *p_b); +} + TEST_CASE("BinaryExpressionProcessor arithmetic", "[language]") { SECTION("+") @@ -151,4 +199,186 @@ TEST_CASE("BinaryExpressionProcessor arithmetic", "[language]") CHECK_BINARY_EXPRESSION_RESULT(R"(let r:R, r = -1.2 / 2.3;)", "r", (-1.2 / 2.3)); } } + + SECTION("binary operator [builtin]") + { +#define CHECK_BUILTIN_BINARY_EXPRESSION_RESULT(data, result) \ + { \ + TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; \ + auto ast = ASTBuilder::build(input); \ + \ + ASTModulesImporter{*ast}; \ + \ + BasicAffectationRegisterFor<EmbeddedData>{ASTNodeDataType::build<ASTNodeDataType::type_id_t>("builtin_t")}; \ + \ + OperatorRepository& repository = OperatorRepository::instance(); \ + \ + repository.addBinaryOperator<language::plus_op>( \ + std::make_shared< \ + BinaryOperatorProcessorBuilder<language::plus_op, std::shared_ptr<const double>, \ + std::shared_ptr<const double>, std::shared_ptr<const double>>>()); \ + \ + repository.addBinaryOperator<language::plus_op>( \ + std::make_shared<BinaryOperatorProcessorBuilder<language::plus_op, std::shared_ptr<const double>, \ + std::shared_ptr<const double>, double>>()); \ + \ + repository.addBinaryOperator<language::plus_op>( \ + std::make_shared<BinaryOperatorProcessorBuilder<language::plus_op, std::shared_ptr<const double>, double, \ + std::shared_ptr<const double>>>()); \ + \ + SymbolTable& symbol_table = *ast->m_symbol_table; \ + auto [i_symbol, success] = symbol_table.add(builtin_data_type.nameOfTypeId(), ast->begin()); \ + if (not success) { \ + throw UnexpectedError("cannot add '" + builtin_data_type.nameOfTypeId() + "' type for testing"); \ + } \ + \ + i_symbol->attributes().setDataType(ASTNodeDataType::build<ASTNodeDataType::type_name_id_t>()); \ + i_symbol->attributes().setIsInitialized(); \ + i_symbol->attributes().value() = symbol_table.typeEmbedderTable().size(); \ + symbol_table.typeEmbedderTable().add(std::make_shared<TypeDescriptor>(builtin_data_type.nameOfTypeId())); \ + \ + auto [i_symbol_bt_a, success_bt_a] = symbol_table.add("bt_a", ast->begin()); \ + if (not success_bt_a) { \ + throw UnexpectedError("cannot add 'bt_a' of type builtin_t for testing"); \ + } \ + i_symbol_bt_a->attributes().setDataType(ast_node_data_type_from<std::shared_ptr<const double>>); \ + i_symbol_bt_a->attributes().setIsInitialized(); \ + i_symbol_bt_a->attributes().value() = \ + EmbeddedData(std::make_shared<DataHandler<const double>>(std::make_shared<double>(3.2))); \ + \ + auto [i_symbol_bt_b, success_bt_b] = symbol_table.add("bt_b", ast->begin()); \ + if (not success_bt_b) { \ + throw UnexpectedError("cannot add 'bt_b' of type builtin_t for testing"); \ + } \ + i_symbol_bt_b->attributes().setDataType(ast_node_data_type_from<std::shared_ptr<const double>>); \ + i_symbol_bt_b->attributes().setIsInitialized(); \ + i_symbol_bt_b->attributes().value() = \ + EmbeddedData(std::make_shared<DataHandler<const double>>(std::make_shared<double>(5.3))); \ + \ + ASTNodeTypeCleaner<language::import_instruction>{*ast}; \ + \ + ASTSymbolTableBuilder{*ast}; \ + ASTNodeDataTypeBuilder{*ast}; \ + \ + ASTNodeDeclarationToAffectationConverter{*ast}; \ + ASTNodeTypeCleaner<language::var_declaration>{*ast}; \ + \ + ASTNodeExpressionBuilder{*ast}; \ + ExecutionPolicy exec_policy; \ + ast->execute(exec_policy); \ + \ + using namespace TAO_PEGTL_NAMESPACE; \ + position use_position{internal::iterator{"fixture"}, "fixture"}; \ + use_position.byte = 10000; \ + auto [symbol, found] = symbol_table.find("r", use_position); \ + \ + auto attributes = symbol->attributes(); \ + auto embedded_value = std::get<EmbeddedData>(attributes.value()); \ + \ + double value = *dynamic_cast<const DataHandler<const double>&>(embedded_value.get()).data_ptr(); \ + REQUIRE(value == expected); \ + } + + SECTION("builtin both side") + { + std::string_view data = R"(let r:builtin_t, r = bt_a + bt_b;)"; + const double expected = double{3.2} + double{5.3}; + + CHECK_BUILTIN_BINARY_EXPRESSION_RESULT(data, expected); + } + + SECTION("builtin lhs") + { + std::string_view data = R"(let r:builtin_t, r = bt_a + 5.;)"; + const double expected = double{3.2} + double{5}; + + CHECK_BUILTIN_BINARY_EXPRESSION_RESULT(data, expected); + } + + SECTION("builtin rhs") + { + std::string_view data = R"(let r:builtin_t, r = 5. + bt_a;)"; + const double expected = double{3.2} + double{5}; + + CHECK_BUILTIN_BINARY_EXPRESSION_RESULT(data, expected); + } + +#define CHECK_BUILTIN_BINARY_EXPRESSION_ERROR(data, error_msg) \ + { \ + TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; \ + auto ast = ASTBuilder::build(input); \ + \ + ASTModulesImporter{*ast}; \ + \ + BasicAffectationRegisterFor<EmbeddedData>{ASTNodeDataType::build<ASTNodeDataType::type_id_t>("builtin_t")}; \ + \ + OperatorRepository& repository = OperatorRepository::instance(); \ + \ + repository.addBinaryOperator<language::divide_op>( \ + std::make_shared< \ + BinaryOperatorProcessorBuilder<language::divide_op, std::shared_ptr<const double>, \ + std::shared_ptr<const double>, std::shared_ptr<const double>>>()); \ + \ + repository.addBinaryOperator<language::divide_op>( \ + std::make_shared<BinaryOperatorProcessorBuilder<language::divide_op, std::shared_ptr<const double>, \ + std::shared_ptr<const double>, double>>()); \ + \ + repository.addBinaryOperator<language::divide_op>( \ + std::make_shared<BinaryOperatorProcessorBuilder<language::divide_op, std::shared_ptr<const double>, double, \ + std::shared_ptr<const double>>>()); \ + \ + SymbolTable& symbol_table = *ast->m_symbol_table; \ + auto [i_symbol, success] = symbol_table.add(builtin_data_type.nameOfTypeId(), ast->begin()); \ + if (not success) { \ + throw UnexpectedError("cannot add '" + builtin_data_type.nameOfTypeId() + "' type for testing"); \ + } \ + \ + i_symbol->attributes().setDataType(ASTNodeDataType::build<ASTNodeDataType::type_name_id_t>()); \ + i_symbol->attributes().setIsInitialized(); \ + i_symbol->attributes().value() = symbol_table.typeEmbedderTable().size(); \ + symbol_table.typeEmbedderTable().add(std::make_shared<TypeDescriptor>(builtin_data_type.nameOfTypeId())); \ + \ + auto [i_symbol_bt_a, success_bt_a] = symbol_table.add("bt_a", ast->begin()); \ + if (not success_bt_a) { \ + throw UnexpectedError("cannot add 'bt_a' of type builtin_t for testing"); \ + } \ + i_symbol_bt_a->attributes().setDataType(ast_node_data_type_from<std::shared_ptr<const double>>); \ + i_symbol_bt_a->attributes().setIsInitialized(); \ + i_symbol_bt_a->attributes().value() = \ + EmbeddedData(std::make_shared<DataHandler<const double>>(std::make_shared<double>(3.2))); \ + \ + auto [i_symbol_bt_b, success_bt_b] = symbol_table.add("bt_b", ast->begin()); \ + if (not success_bt_b) { \ + throw UnexpectedError("cannot add 'bt_b' of type builtin_t for testing"); \ + } \ + i_symbol_bt_b->attributes().setDataType(ast_node_data_type_from<std::shared_ptr<const double>>); \ + i_symbol_bt_b->attributes().setIsInitialized(); \ + i_symbol_bt_b->attributes().value() = \ + EmbeddedData(std::make_shared<DataHandler<const double>>(std::make_shared<double>(5.3))); \ + \ + ASTNodeTypeCleaner<language::import_instruction>{*ast}; \ + \ + ASTSymbolTableBuilder{*ast}; \ + ASTNodeDataTypeBuilder{*ast}; \ + \ + ASTNodeDeclarationToAffectationConverter{*ast}; \ + ASTNodeTypeCleaner<language::var_declaration>{*ast}; \ + \ + ASTNodeExpressionBuilder{*ast}; \ + ExecutionPolicy exec_policy; \ + REQUIRE_THROWS_WITH(ast->execute(exec_policy), error_msg); \ + } + + SECTION("runtime error") + { + std::string_view data_both = R"(let r:builtin_t, r = bt_a / bt_b;)"; + CHECK_BUILTIN_BINARY_EXPRESSION_ERROR(data_both, "runtime error both"); + + std::string_view data_lhs = R"(let r:builtin_t, r = bt_a / 2.3;)"; + CHECK_BUILTIN_BINARY_EXPRESSION_ERROR(data_lhs, "runtime error lhs"); + + std::string_view data_rhs = R"(let r:builtin_t, r = 2.3/ bt_a;)"; + CHECK_BUILTIN_BINARY_EXPRESSION_ERROR(data_rhs, "runtime error rhs"); + } + } }