Skip to content
Snippets Groups Projects
Commit 0756812b authored by Stéphane Del Pino's avatar Stéphane Del Pino
Browse files

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.
parent 8ebbb941
No related branches found
No related tags found
1 merge request!70Feature/language reduce static
This commit is part of merge request !70. Comments created here will be created in the context of that merge request.
......@@ -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>()) {
......
......@@ -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;
};
......
......@@ -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;
};
......
......@@ -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
{
......
......@@ -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 {};
}
......
......@@ -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
{
......
......@@ -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")
......
......@@ -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>();
......
......@@ -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")
......
......@@ -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"));
}
}
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment