diff --git a/src/language/ast/ASTNodeDataTypeBuilder.cpp b/src/language/ast/ASTNodeDataTypeBuilder.cpp index a71df9a3d662ee01d95e40366faeaadc081ea561..74806be26a1d95f07a33cf682f78a407a11845d5 100644 --- a/src/language/ast/ASTNodeDataTypeBuilder.cpp +++ b/src/language/ast/ASTNodeDataTypeBuilder.cpp @@ -3,6 +3,7 @@ #include <language/PEGGrammar.hpp> #include <language/ast/ASTNodeNaturalConversionChecker.hpp> #include <language/utils/BuiltinFunctionEmbedder.hpp> +#include <language/utils/OperatorRepository.hpp> #include <language/utils/ParseError.hpp> #include <language/utils/SymbolTable.hpp> #include <utils/PugsAssert.hpp> @@ -352,10 +353,20 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const ASTNodeNaturalConversionChecker{test_node, ASTNodeDataType::build<ASTNodeDataType::bool_t>()}; } else if (n.is_type<language::unary_not>()) { - n.m_data_type = ASTNodeDataType::build<ASTNodeDataType::bool_t>(); + auto& operator_repository = OperatorRepository::instance(); + + auto optional_value_type = operator_repository.getUnaryOperatorValueType( + unaryOperatorMangler<language::unary_not>(n.children[0]->m_data_type)); - const ASTNode& operand_node = *n.children[0]; - ASTNodeNaturalConversionChecker{operand_node, ASTNodeDataType::build<ASTNodeDataType::bool_t>()}; + if (optional_value_type.has_value()) { + n.m_data_type = optional_value_type.value(); + } else { + std::ostringstream message; + message << "undefined unary operator\n" + << "note: unexpected operand type " << rang::fgB::red << dataTypeName(n.children[0]->m_data_type) + << rang::style::reset; + throw ParseError(message.str(), n.begin()); + } } else if (n.is_type<language::lesser_op>() or n.is_type<language::lesser_or_eq_op>() or n.is_type<language::greater_op>() or n.is_type<language::greater_or_eq_op>() or @@ -371,15 +382,54 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const ASTNodeNaturalConversionChecker{rhs_node, ASTNodeDataType::build<ASTNodeDataType::bool_t>()}; } else if (n.is_type<language::unary_minus>()) { - n.m_data_type = n.children[0]->m_data_type; - if ((n.children[0]->m_data_type == ASTNodeDataType::unsigned_int_t) or - (n.children[0]->m_data_type == ASTNodeDataType::bool_t)) { - n.m_data_type = ASTNodeDataType::build<ASTNodeDataType::int_t>(); + auto& operator_repository = OperatorRepository::instance(); + + auto optional_value_type = operator_repository.getUnaryOperatorValueType( + unaryOperatorMangler<language::unary_minus>(n.children[0]->m_data_type)); + + if (optional_value_type.has_value()) { + n.m_data_type = optional_value_type.value(); } else { - n.m_data_type = n.children[0]->m_data_type; + std::ostringstream message; + message << "undefined unary operator\n" + << "note: unexpected operand type " << rang::fgB::red << dataTypeName(n.children[0]->m_data_type) + << rang::style::reset; + throw ParseError(message.str(), n.begin()); } } else if (n.is_type<language::unary_plusplus>() or n.is_type<language::unary_minusminus>() or n.is_type<language::post_plusplus>() or n.is_type<language::post_minusminus>()) { + auto& operator_repository = OperatorRepository::instance(); + + auto optional_value_type = [&] { + if (n.is_type<language::unary_plusplus>()) { + return operator_repository.getIncDecOperatorValueType( + incDecOperatorMangler<language::unary_plusplus>(n.children[0]->m_data_type)); + } else if (n.is_type<language::unary_minusminus>()) { + return operator_repository.getIncDecOperatorValueType( + incDecOperatorMangler<language::unary_minusminus>(n.children[0]->m_data_type)); + } else if (n.is_type<language::post_minusminus>()) { + return operator_repository.getIncDecOperatorValueType( + incDecOperatorMangler<language::post_minusminus>(n.children[0]->m_data_type)); + } else if (n.is_type<language::post_plusplus>()) { + return operator_repository.getIncDecOperatorValueType( + incDecOperatorMangler<language::post_plusplus>(n.children[0]->m_data_type)); + } else { + // LCOV_EXCL_START + throw UnexpectedError("unexpected operator type"); + // LCOV_EXCL_STOP + } + }(); + + if (optional_value_type.has_value()) { + n.m_data_type = optional_value_type.value(); + } else { + std::ostringstream message; + message << "undefined unary operator\n" + << "note: unexpected operand type " << rang::fgB::red << dataTypeName(n.children[0]->m_data_type) + << rang::style::reset; + throw ParseError(message.str(), n.begin()); + } + n.m_data_type = n.children[0]->m_data_type; } else if (n.is_type<language::plus_op>() or n.is_type<language::minus_op>() or n.is_type<language::multiply_op>() or n.is_type<language::divide_op>()) { @@ -393,8 +443,7 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const if (n.m_data_type == ASTNodeDataType::undefined_t) { std::ostringstream message; message << "undefined binary operator\n" - << "note: incompatible operand types " << n.children[0]->string() << " (" << dataTypeName(type_0) - << ") and " << n.children[1]->string() << " (" << dataTypeName(type_1) << ')'; + << "note: incompatible operand types " << dataTypeName(type_0) << " and " << dataTypeName(type_1); throw ParseError(message.str(), n.begin()); } } else if (n.is_type<language::function_evaluation>()) { diff --git a/src/language/utils/IIncDecOperatorProcessorBuilder.hpp b/src/language/utils/IIncDecOperatorProcessorBuilder.hpp index 050bb60f37fd4ada7c7649e6efbd83eb96e90373..918cef6a047828459a9b2937823e99308dcd137c 100644 --- a/src/language/utils/IIncDecOperatorProcessorBuilder.hpp +++ b/src/language/utils/IIncDecOperatorProcessorBuilder.hpp @@ -3,6 +3,7 @@ class ASTNode; class INodeProcessor; +#include <language/utils/ASTNodeDataType.hpp> #include <memory> @@ -11,6 +12,8 @@ class IIncDecOperatorProcessorBuilder public: virtual std::unique_ptr<INodeProcessor> getNodeProcessor(ASTNode& node) const = 0; + virtual ASTNodeDataType getReturnValueType() const = 0; + virtual ~IIncDecOperatorProcessorBuilder() = default; }; diff --git a/src/language/utils/IUnaryOperatorProcessorBuilder.hpp b/src/language/utils/IUnaryOperatorProcessorBuilder.hpp index 7aca7c2c26d4c004d1977e10ca51aee290bea5b6..b56ca3f466c1eb8ca886a20b64df4cbb7ea3b003 100644 --- a/src/language/utils/IUnaryOperatorProcessorBuilder.hpp +++ b/src/language/utils/IUnaryOperatorProcessorBuilder.hpp @@ -4,6 +4,8 @@ class ASTNode; class INodeProcessor; +#include <language/utils/ASTNodeDataType.hpp> + #include <memory> class IUnaryOperatorProcessorBuilder @@ -11,6 +13,8 @@ class IUnaryOperatorProcessorBuilder public: virtual std::unique_ptr<INodeProcessor> getNodeProcessor(ASTNode& node) const = 0; + virtual ASTNodeDataType getReturnValueType() const = 0; + virtual ~IUnaryOperatorProcessorBuilder() = default; }; diff --git a/src/language/utils/IncDecOperatorProcessorBuilder.hpp b/src/language/utils/IncDecOperatorProcessorBuilder.hpp index c3eeb10baa44d6668b0ac6e88e9f622aea9a827a..40da865ddf9c6ed480a859e9a158d21cb5562848 100644 --- a/src/language/utils/IncDecOperatorProcessorBuilder.hpp +++ b/src/language/utils/IncDecOperatorProcessorBuilder.hpp @@ -4,6 +4,7 @@ #include <algebra/TinyVector.hpp> #include <language/PEGGrammar.hpp> #include <language/node_processor/IncDecExpressionProcessor.hpp> +#include <language/utils/ASTNodeDataTypeTraits.hpp> #include <language/utils/IIncDecOperatorProcessorBuilder.hpp> #include <type_traits> @@ -14,6 +15,12 @@ class IncDecOperatorProcessorBuilder final : public IIncDecOperatorProcessorBuil public: IncDecOperatorProcessorBuilder() = default; + ASTNodeDataType + getReturnValueType() const + { + return ast_node_data_type_from<DataT>; + } + std::unique_ptr<INodeProcessor> getNodeProcessor(ASTNode& node) const { diff --git a/src/language/utils/OperatorRepository.hpp b/src/language/utils/OperatorRepository.hpp index ae6b2a9844fa67fa8cdae7f1a55115c56126a30c..e7c1ea2f3a9bfc7ee21ddae4977f688894ae909a 100644 --- a/src/language/utils/OperatorRepository.hpp +++ b/src/language/utils/OperatorRepository.hpp @@ -17,74 +17,144 @@ class OperatorRepository { private: - std::unordered_map<std::string, std::shared_ptr<const IAffectationProcessorBuilder>> m_affectation_builder_list; - std::unordered_map<std::string, std::shared_ptr<const IIncDecOperatorProcessorBuilder>> - m_inc_dec_operator_builder_list; - std::unordered_map<std::string, std::shared_ptr<const IUnaryOperatorProcessorBuilder>> m_unary_operator_builder_list; + template <typename ProcessorBuilderT> + class Descriptor + { + private: + ASTNodeDataType m_value_type; + std::shared_ptr<const ProcessorBuilderT> m_processor_builder; + + public: + const ASTNodeDataType& + valueType() const + { + return m_value_type; + } + + const std::shared_ptr<const ProcessorBuilderT>& + processorBuilder() const + { + return m_processor_builder; + } + + Descriptor(const ASTNodeDataType& value_type, const std::shared_ptr<const ProcessorBuilderT>& processor_builder) + : m_value_type{value_type}, m_processor_builder{processor_builder} + {} + + Descriptor(const Descriptor&) = default; + Descriptor(Descriptor&&) = default; + Descriptor() = default; + ~Descriptor() = default; + }; + + std::unordered_map<std::string, Descriptor<IAffectationProcessorBuilder>> m_affectation_builder_list; + + std::unordered_map<std::string, Descriptor<IIncDecOperatorProcessorBuilder>> m_inc_dec_operator_builder_list; + + std::unordered_map<std::string, Descriptor<IUnaryOperatorProcessorBuilder>> m_unary_operator_builder_list; void _initialize(); public: void reset(); - template <typename OperatorTypeT, typename AffectationProcessorBuilderT> + template <typename OperatorTypeT> void - addAffectation(const ASTNodeDataType& lhs, - const ASTNodeDataType& rhs, - const std::shared_ptr<AffectationProcessorBuilderT>& processor_builder) + addAffectation(const ASTNodeDataType& lhs_type, + const ASTNodeDataType& rhs_type, + const std::shared_ptr<const IAffectationProcessorBuilder>& processor_builder) { - const std::string affectation_type_name = affectationMangler<OperatorTypeT>(lhs, rhs); - if (not m_affectation_builder_list.try_emplace(affectation_type_name, processor_builder).second) { + const std::string affectation_type_name = affectationMangler<OperatorTypeT>(lhs_type, rhs_type); + if (not m_affectation_builder_list + .try_emplace(affectation_type_name, + Descriptor{ASTNodeDataType::build<ASTNodeDataType::void_t>(), processor_builder}) + .second) { + // LCOV_EXCL_START throw UnexpectedError(affectation_type_name + " has already an entry"); + // LCOV_EXCL_STOP } } - template <typename OperatorTypeT, typename IncDecProcessorBuilderT> + template <typename OperatorTypeT> void - addIncDecOperator(const ASTNodeDataType& operand, const std::shared_ptr<IncDecProcessorBuilderT>& processor_builder) + addIncDecOperator(const ASTNodeDataType& operand_type, + const std::shared_ptr<const IIncDecOperatorProcessorBuilder>& processor_builder) { - const std::string inc_dec_operator_type_name = incDecOperatorMangler<OperatorTypeT>(operand); - if (not m_inc_dec_operator_builder_list.try_emplace(inc_dec_operator_type_name, processor_builder).second) { + const std::string inc_dec_operator_type_name = incDecOperatorMangler<OperatorTypeT>(operand_type); + if (auto [i_descriptor, success] = + m_inc_dec_operator_builder_list.try_emplace(inc_dec_operator_type_name, + Descriptor{processor_builder->getReturnValueType(), + processor_builder}); + not success) { + // LCOV_EXCL_START throw UnexpectedError(inc_dec_operator_type_name + " has already an entry"); + // LCOV_EXCL_STOP } } - template <typename OperatorTypeT, typename UnaryProcessorBuilderT> + template <typename OperatorTypeT> void - addUnaryOperator(const ASTNodeDataType& operand, const std::shared_ptr<UnaryProcessorBuilderT>& processor_builder) + addUnaryOperator(const ASTNodeDataType& operand_type, + const std::shared_ptr<const IUnaryOperatorProcessorBuilder>& processor_builder) { - const std::string unary_operator_type_name = unaryOperatorMangler<OperatorTypeT>(operand); - if (not m_unary_operator_builder_list.try_emplace(unary_operator_type_name, processor_builder).second) { + const std::string unary_operator_type_name = unaryOperatorMangler<OperatorTypeT>(operand_type); + if (auto [i_descriptor, success] = + m_unary_operator_builder_list.try_emplace(unary_operator_type_name, + Descriptor{processor_builder->getReturnValueType(), + processor_builder}); + not success) { + // LCOV_EXCL_START throw UnexpectedError(unary_operator_type_name + " has already an entry"); + // LCOV_EXCL_STOP } } - std::optional<std::shared_ptr<const IAffectationProcessorBuilder>> + [[nodiscard]] std::optional<std::shared_ptr<const IAffectationProcessorBuilder>> getAffectationProcessorBuilder(const std::string& name) const { auto&& processor_builder = m_affectation_builder_list.find(name); if (processor_builder != m_affectation_builder_list.end()) { - return processor_builder->second; + return processor_builder->second.processorBuilder(); } return {}; } - std::optional<std::shared_ptr<const IIncDecOperatorProcessorBuilder>> + [[nodiscard]] std::optional<std::shared_ptr<const IIncDecOperatorProcessorBuilder>> getIncDecProcessorBuilder(const std::string& name) const { auto&& processor_builder = m_inc_dec_operator_builder_list.find(name); if (processor_builder != m_inc_dec_operator_builder_list.end()) { - return processor_builder->second; + return processor_builder->second.processorBuilder(); } return {}; } - std::optional<std::shared_ptr<const IUnaryOperatorProcessorBuilder>> + [[nodiscard]] std::optional<std::shared_ptr<const IUnaryOperatorProcessorBuilder>> getUnaryProcessorBuilder(const std::string& name) const { auto&& processor_builder = m_unary_operator_builder_list.find(name); if (processor_builder != m_unary_operator_builder_list.end()) { - return processor_builder->second; + return processor_builder->second.processorBuilder(); + } + return {}; + } + + [[nodiscard]] std::optional<ASTNodeDataType> + getIncDecOperatorValueType(const std::string& name) const + { + auto&& processor_builder = m_inc_dec_operator_builder_list.find(name); + if (processor_builder != m_inc_dec_operator_builder_list.end()) { + return processor_builder->second.valueType(); + } + return {}; + } + + [[nodiscard]] std::optional<ASTNodeDataType> + getUnaryOperatorValueType(const std::string& name) const + { + auto&& processor_builder = m_unary_operator_builder_list.find(name); + if (processor_builder != m_unary_operator_builder_list.end()) { + return processor_builder->second.valueType(); } return {}; } diff --git a/src/language/utils/UnaryOperatorProcessorBuilder.hpp b/src/language/utils/UnaryOperatorProcessorBuilder.hpp index a4a3a22a4d190621016b57abad87ec8aa9e32514..d99d2e17536de2b31a3ac2c48f39800a2461f17d 100644 --- a/src/language/utils/UnaryOperatorProcessorBuilder.hpp +++ b/src/language/utils/UnaryOperatorProcessorBuilder.hpp @@ -4,6 +4,7 @@ #include <algebra/TinyVector.hpp> #include <language/PEGGrammar.hpp> #include <language/node_processor/UnaryExpressionProcessor.hpp> +#include <language/utils/ASTNodeDataTypeTraits.hpp> #include <language/utils/IUnaryOperatorProcessorBuilder.hpp> #include <type_traits> @@ -14,6 +15,12 @@ class UnaryOperatorProcessorBuilder final : public IUnaryOperatorProcessorBuilde public: UnaryOperatorProcessorBuilder() = default; + ASTNodeDataType + getReturnValueType() const + { + return ast_node_data_type_from<ValueT>; + } + std::unique_ptr<INodeProcessor> getNodeProcessor(ASTNode& node) const { diff --git a/tests/test_ASTNodeExpressionBuilder.cpp b/tests/test_ASTNodeExpressionBuilder.cpp index 956b5a2527e8e80bb1cef2287f9b2c72a16768aa..8cca350f1d165303634b86a7958ab55b81c72cb3 100644 --- a/tests/test_ASTNodeExpressionBuilder.cpp +++ b/tests/test_ASTNodeExpressionBuilder.cpp @@ -373,7 +373,10 @@ let n:N; SECTION("unary not") { - CHECK_AST_THROWS_WITH(R"(not 1;)", "invalid implicit conversion: Z -> B"); + std::string error_message = R"(undefined unary operator +note: unexpected operand type Z)"; + + CHECK_AST_THROWS_WITH(R"(not 1;)", error_message); } SECTION("pre-increment operator") diff --git a/tests/test_ASTNodeIncDecExpressionBuilder.cpp b/tests/test_ASTNodeIncDecExpressionBuilder.cpp index 5dd0f4e37ab19f852e05d222b703b7cac0eaab0f..2b64fcd03c2aeedb6ab3902cf374791205344815 100644 --- a/tests/test_ASTNodeIncDecExpressionBuilder.cpp +++ b/tests/test_ASTNodeIncDecExpressionBuilder.cpp @@ -8,6 +8,7 @@ #include <language/ast/ASTNodeTypeCleaner.hpp> #include <language/ast/ASTSymbolTableBuilder.hpp> #include <language/utils/ASTPrinter.hpp> +#include <language/utils/OperatorRepository.hpp> #include <utils/Demangle.hpp> #include <pegtl/string_input.hpp> @@ -299,6 +300,13 @@ x--; SECTION("Errors") { + SECTION("Undefined operator") + { + auto& operator_repository = OperatorRepository::instance(); + auto optional_value_type = operator_repository.getIncDecOperatorValueType("string ++"); + REQUIRE(not optional_value_type.has_value()); + } + SECTION("Invalid operand type") { auto ast = std::make_unique<ASTNode>(); diff --git a/tests/test_ASTNodeUnaryOperatorExpressionBuilder.cpp b/tests/test_ASTNodeUnaryOperatorExpressionBuilder.cpp index 96290c4499e9c3de205a6c0a3769644e6cea2525..2ebe22cab68dc735836054b58ca4382340e09030 100644 --- a/tests/test_ASTNodeUnaryOperatorExpressionBuilder.cpp +++ b/tests/test_ASTNodeUnaryOperatorExpressionBuilder.cpp @@ -202,10 +202,16 @@ not b; SECTION("errors") { - CHECK_AST_THROWS_WITH(R"(let n:N; not n;)", "invalid implicit conversion: N -> B"); - CHECK_AST_THROWS_WITH(R"(not 2;)", "invalid implicit conversion: Z -> B"); - CHECK_AST_THROWS_WITH(R"(not -2.3;)", "invalid implicit conversion: R -> B"); - CHECK_AST_THROWS_WITH(R"(not "foo";)", "invalid implicit conversion: string -> B"); + auto error_message = [](std::string type_name) { + return std::string{R"(undefined unary operator +note: unexpected operand type )"} + + type_name; + }; + + CHECK_AST_THROWS_WITH(R"(let n:N; not n;)", error_message("N")); + CHECK_AST_THROWS_WITH(R"(not 2;)", error_message("Z")); + CHECK_AST_THROWS_WITH(R"(not -2.3;)", error_message("R")); + CHECK_AST_THROWS_WITH(R"(not "foo";)", error_message("string")); } SECTION("Invalid value type for unary not") diff --git a/tests/test_UnaryExpressionProcessor.cpp b/tests/test_UnaryExpressionProcessor.cpp index e933902adaefd968cc3036c8bbf007b29d054f39..b2cec8c797a48457b8d19c112c7490e9e275247a 100644 --- a/tests/test_UnaryExpressionProcessor.cpp +++ b/tests/test_UnaryExpressionProcessor.cpp @@ -70,10 +70,16 @@ TEST_CASE("UnaryExpressionProcessor", "[language]") { SECTION("bad implicit conversions") { - CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(let n:N, n = 0; not n;)", "invalid implicit conversion: N -> B"); - CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(not 1;)", "invalid implicit conversion: Z -> B"); - CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(not 1.3;)", "invalid implicit conversion: R -> B"); - CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(not "foo";)", "invalid implicit conversion: string -> B"); + auto error_message = [](std::string type_name) { + return std::string{R"(undefined unary operator +note: unexpected operand type )"} + + type_name; + }; + + CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(let n:N, n = 0; not n;)", error_message("N")); + CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(not 1;)", error_message("Z")); + CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(not 1.3;)", error_message("R")); + CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(not "foo";)", error_message("string")); } } }