From 0756812b3c4f1f8b5d53e16fc65999d805d916b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Fri, 20 Nov 2020 12:40:05 +0100 Subject: [PATCH] Use OperatorRepository to get return values to compute data types This is done for unary operators and increment/decrement operators This provides a consistent way to define basic operators and will allow to define easily this kind of operators for C++ EmbeddedData types. --- src/language/ast/ASTNodeDataTypeBuilder.cpp | 69 ++++++++-- .../utils/IIncDecOperatorProcessorBuilder.hpp | 3 + .../utils/IUnaryOperatorProcessorBuilder.hpp | 4 + .../utils/IncDecOperatorProcessorBuilder.hpp | 7 ++ src/language/utils/OperatorRepository.hpp | 118 ++++++++++++++---- .../utils/UnaryOperatorProcessorBuilder.hpp | 7 ++ tests/test_ASTNodeExpressionBuilder.cpp | 5 +- tests/test_ASTNodeIncDecExpressionBuilder.cpp | 8 ++ ..._ASTNodeUnaryOperatorExpressionBuilder.cpp | 14 ++- tests/test_UnaryExpressionProcessor.cpp | 14 ++- 10 files changed, 206 insertions(+), 43 deletions(-) diff --git a/src/language/ast/ASTNodeDataTypeBuilder.cpp b/src/language/ast/ASTNodeDataTypeBuilder.cpp index a71df9a3d..74806be26 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 050bb60f3..918cef6a0 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 7aca7c2c2..b56ca3f46 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 c3eeb10ba..40da865dd 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 ae6b2a984..e7c1ea2f3 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 a4a3a22a4..d99d2e175 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 956b5a252..8cca350f1 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 5dd0f4e37..2b64fcd03 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 96290c449..2ebe22cab 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 e933902ad..b2cec8c79 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")); } } } -- GitLab