From 47601cee1e25fe4dab42c303a887e1602a0937ff Mon Sep 17 00:00:00 2001
From: Stephane Del Pino <stephane.delpino44@gmail.com>
Date: Fri, 22 Nov 2019 18:26:31 +0100
Subject: [PATCH] Add multi-component function evaluation

``
let f : R -> R*R, x -> (x, x+1);
f(3);         // computes (3, 4)
``
---
 src/language/ASTNodeDataTypeBuilder.cpp       |  3 +-
 .../ASTNodeFunctionExpressionBuilder.cpp      | 69 ++++++++++++++-----
 .../ASTNodeFunctionExpressionBuilder.hpp      |  4 +-
 .../node_processor/FunctionProcessor.hpp      | 19 ++---
 4 files changed, 63 insertions(+), 32 deletions(-)

diff --git a/src/language/ASTNodeDataTypeBuilder.cpp b/src/language/ASTNodeDataTypeBuilder.cpp
index dfde6bb6a..3e0033920 100644
--- a/src/language/ASTNodeDataTypeBuilder.cpp
+++ b/src/language/ASTNodeDataTypeBuilder.cpp
@@ -320,8 +320,9 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const
 
         ASTNodeDataType data_type{ASTNodeDataType::undefined_t};
         if (image_domain_node.children.size() > 0) {
-          throw parse_error("compound data type is not implemented yet", image_domain_node.begin());
+          data_type = image_domain_node.m_data_type;
         } else {
+#warning check if it is really useful to compute exact types here?
           if (image_domain_node.is_type<language::B_set>()) {
             data_type = ASTNodeDataType::bool_t;
           } else if (image_domain_node.is_type<language::Z_set>()) {
diff --git a/src/language/ASTNodeFunctionExpressionBuilder.cpp b/src/language/ASTNodeFunctionExpressionBuilder.cpp
index ebf00082e..8833958c6 100644
--- a/src/language/ASTNodeFunctionExpressionBuilder.cpp
+++ b/src/language/ASTNodeFunctionExpressionBuilder.cpp
@@ -140,26 +140,30 @@ PUGS_INLINE
 std::unique_ptr<INodeProcessor>
 ASTNodeFunctionExpressionBuilder::_getFunctionProcessor(const ASTNodeDataType expression_value_type,
                                                         const ASTNodeDataType return_value_type,
-                                                        ASTNode& node)
+                                                        ASTNode& node,
+                                                        ASTNode& function_component_expression,
+                                                        ASTNodeDataVariant& node_value)
 {
   auto get_function_processor_for_expression_value = [&](const auto& return_v) -> std::unique_ptr<INodeProcessor> {
     using ReturnT = std::decay_t<decltype(return_v)>;
     switch (expression_value_type) {
     case ASTNodeDataType::bool_t: {
-      return std::make_unique<FunctionExpressionProcessor<ReturnT, bool>>(node);
+      return std::make_unique<FunctionExpressionProcessor<ReturnT, bool>>(function_component_expression, node_value);
     }
     case ASTNodeDataType::unsigned_int_t: {
-      return std::make_unique<FunctionExpressionProcessor<ReturnT, uint64_t>>(node);
+      return std::make_unique<FunctionExpressionProcessor<ReturnT, uint64_t>>(function_component_expression,
+                                                                              node_value);
     }
     case ASTNodeDataType::int_t: {
-      return std::make_unique<FunctionExpressionProcessor<ReturnT, int64_t>>(node);
+      return std::make_unique<FunctionExpressionProcessor<ReturnT, int64_t>>(function_component_expression, node_value);
     }
     case ASTNodeDataType::double_t: {
-      return std::make_unique<FunctionExpressionProcessor<ReturnT, double>>(node);
+      return std::make_unique<FunctionExpressionProcessor<ReturnT, double>>(function_component_expression, node_value);
     }
     case ASTNodeDataType::string_t: {
       if constexpr (std::is_same_v<ReturnT, std::string>) {
-        return std::make_unique<FunctionExpressionProcessor<ReturnT, std::string>>(node);
+        return std::make_unique<FunctionExpressionProcessor<ReturnT, std::string>>(function_component_expression,
+                                                                                   node_value);
       } else {
         throw parse_error("invalid string conversion", std::vector{node.children[1]->begin()});
       }
@@ -211,21 +215,52 @@ ASTNodeFunctionExpressionBuilder::ASTNodeFunctionExpressionBuilder(ASTNode& node
 
   FunctionDescriptor& function_descriptor = node.m_symbol_table->functionTable()[function_id];
 
-  if (function_descriptor.definitionNode().children[1]->is_type<language::expression_list>()) {
-    // LCOV_EXCL_START
-    throw parse_error("unexpected error: function expression list is not implemented yet",
-                      std::vector{function_descriptor.definitionNode().children[1]->begin()});
-    // LCOV_EXCL_STOP
-  }
-
   std::unique_ptr function_processor = std::make_unique<FunctionProcessor>();
 
   this->_buildArgumentProcessors(function_descriptor, node, *function_processor);
 
-  const ASTNodeDataType expression_value_type = function_descriptor.definitionNode().children[1]->m_data_type;
-  const ASTNodeDataType return_value_type     = node.m_data_type;
-  function_processor->addFunctionExpressionProcessor(
-    this->_getFunctionProcessor(expression_value_type, return_value_type, node));
+  ASTNode& function_image_domain = *function_descriptor.domainMappingNode().children[1];
+  ASTNode& function_expression   = *function_descriptor.definitionNode().children[1];
+
+  auto add_component_expression = [&](ASTNode& expression_node, ASTNode& domain_node, ASTNodeDataVariant& value) {
+    ASTNodeDataType expression_value_type = expression_node.m_data_type;
+    ASTNodeDataType return_value_type     = ASTNodeDataType::undefined_t;
+
+    ASTNode& image_domain_node = domain_node;
+
+    if (image_domain_node.is_type<language::B_set>()) {
+      return_value_type = ASTNodeDataType::bool_t;
+    } else if (image_domain_node.is_type<language::Z_set>()) {
+      return_value_type = ASTNodeDataType::int_t;
+    } else if (image_domain_node.is_type<language::N_set>()) {
+      return_value_type = ASTNodeDataType::unsigned_int_t;
+    } else if (image_domain_node.is_type<language::R_set>()) {
+      return_value_type = ASTNodeDataType::double_t;
+    } else if (image_domain_node.is_type<language::string_type>()) {
+      return_value_type = ASTNodeDataType::string_t;
+    }
+
+    Assert(return_value_type != ASTNodeDataType::undefined_t);
+
+    function_processor->addFunctionExpressionProcessor(
+      this->_getFunctionProcessor(expression_value_type, return_value_type, node, expression_node, value));
+  };
+
+  if (function_expression.is_type<language::expression_list>()) {
+    Assert(function_image_domain.is_type<language::type_expression>());
+    ASTNode& image_domain_node = function_image_domain;
+    const size_t& nb_component = function_expression.children.size();
+
+    AggregateDataVariant aggregate_value(nb_component);
+
+    for (size_t i = 0; i < function_expression.children.size(); ++i) {
+      add_component_expression(*function_expression.children[i], *image_domain_node.children[i], aggregate_value[i]);
+    }
+
+    node.m_value = std::move(aggregate_value);
+  } else {
+    add_component_expression(function_expression, function_image_domain, node.m_value);
+  }
 
   node.m_node_processor = std::move(function_processor);
 }
diff --git a/src/language/ASTNodeFunctionExpressionBuilder.hpp b/src/language/ASTNodeFunctionExpressionBuilder.hpp
index 4bcd172b9..f8f69e9c6 100644
--- a/src/language/ASTNodeFunctionExpressionBuilder.hpp
+++ b/src/language/ASTNodeFunctionExpressionBuilder.hpp
@@ -27,7 +27,9 @@ class ASTNodeFunctionExpressionBuilder
   PUGS_INLINE
   std::unique_ptr<INodeProcessor> _getFunctionProcessor(const ASTNodeDataType expression_value_type,
                                                         const ASTNodeDataType return_value_type,
-                                                        ASTNode& node);
+                                                        ASTNode& node,
+                                                        ASTNode& function_component_expression,
+                                                        ASTNodeDataVariant& node_value);
 
  public:
   ASTNodeFunctionExpressionBuilder(ASTNode& node);
diff --git a/src/language/node_processor/FunctionProcessor.hpp b/src/language/node_processor/FunctionProcessor.hpp
index a4e803b14..ccc7ce560 100644
--- a/src/language/node_processor/FunctionProcessor.hpp
+++ b/src/language/node_processor/FunctionProcessor.hpp
@@ -39,7 +39,7 @@ template <typename ReturnType, typename ExpressionValueType>
 class FunctionExpressionProcessor final : public INodeProcessor
 {
  private:
-  ASTNode& m_node;
+  ASTNodeDataVariant& m_value;
   ASTNode& m_function_expression;
 
  public:
@@ -49,23 +49,16 @@ class FunctionExpressionProcessor final : public INodeProcessor
     m_function_expression.execute(exec_policy);
 
     if constexpr (std::is_same_v<ReturnType, ExpressionValueType>) {
-      m_node.m_value = m_function_expression.m_value;
+      m_value = m_function_expression.m_value;
     } else if constexpr (std::is_same_v<ReturnType, std::string>) {
-      m_node.m_value = std::to_string(std::get<ExpressionValueType>(m_function_expression.m_value));
+      m_value = std::to_string(std::get<ExpressionValueType>(m_function_expression.m_value));
     } else {
-      m_node.m_value = static_cast<ReturnType>(std::get<ExpressionValueType>(m_function_expression.m_value));
+      m_value = static_cast<ReturnType>(std::get<ExpressionValueType>(m_function_expression.m_value));
     }
   }
 
-  FunctionExpressionProcessor(ASTNode& node)
-    : m_node{node}, m_function_expression{[&]() -> ASTNode& {
-        auto [i_symbol, found] = m_node.m_symbol_table->find(m_node.children[0]->string(), m_node.begin());
-        Assert(found);
-        uint64_t function_id = std::get<uint64_t>(i_symbol->attributes().value());
-
-        FunctionDescriptor& function_descriptor = m_node.m_symbol_table->functionTable()[function_id];
-        return *function_descriptor.definitionNode().children[1];
-      }()}
+  FunctionExpressionProcessor(ASTNode& function_component_expression, ASTNodeDataVariant& value)
+    : m_value{value}, m_function_expression{function_component_expression}
   {}
 };
 
-- 
GitLab