From e9e282980380786fbd44f33fa48cb4b1bc041389 Mon Sep 17 00:00:00 2001
From: Stephane Del Pino <stephane.delpino44@gmail.com>
Date: Thu, 19 Aug 2021 11:59:01 +0200
Subject: [PATCH] Add ofstream support within the language

On the way rewrite OStream handling: `<<` and `>>` are now treated as
standard binary operators.

This change required the introduction of "global" variable created by
module loading since now: cout, cerr and clog are no more keywords but
variables of type `ostream`.
---
 src/language/PEGGrammar.hpp                   |  14 +--
 src/language/ast/ASTBuilder.cpp               |  23 +---
 ...ASTNodeBinaryOperatorExpressionBuilder.cpp |   5 +
 src/language/ast/ASTNodeDataTypeBuilder.cpp   |   9 +-
 src/language/ast/ASTNodeExpressionBuilder.cpp |   8 +-
 src/language/modules/BuiltinModule.cpp        |  12 +++
 src/language/modules/BuiltinModule.hpp        |  10 ++
 src/language/modules/CoreModule.cpp           |  21 ++++
 src/language/modules/IModule.hpp              |   6 ++
 src/language/modules/ModuleRepository.cpp     |  32 ++++++
 src/language/modules/ModuleRepository.hpp     |   5 +
 .../BinaryExpressionProcessor.hpp             |  31 +++++-
 .../node_processor/OStreamProcessor.hpp       |  38 -------
 src/language/utils/BinaryOperatorMangler.hpp  |   7 ++
 .../utils/BinaryOperatorRegisterForB.cpp      |  13 +++
 .../utils/BinaryOperatorRegisterForB.hpp      |   1 +
 .../utils/BinaryOperatorRegisterForN.cpp      |  13 +++
 .../utils/BinaryOperatorRegisterForN.hpp      |   1 +
 .../utils/BinaryOperatorRegisterForR.cpp      |  13 +++
 .../utils/BinaryOperatorRegisterForR.hpp      |   1 +
 .../utils/BinaryOperatorRegisterForRn.cpp     |  17 +++
 .../utils/BinaryOperatorRegisterForRn.hpp     |   3 +-
 .../utils/BinaryOperatorRegisterForRnxn.cpp   |  17 +++
 .../utils/BinaryOperatorRegisterForRnxn.hpp   |   3 +-
 .../utils/BinaryOperatorRegisterForString.cpp |  14 +++
 .../utils/BinaryOperatorRegisterForString.hpp |   2 +
 .../utils/BinaryOperatorRegisterForZ.cpp      |  13 +++
 .../utils/BinaryOperatorRegisterForZ.hpp      |   1 +
 src/language/utils/CMakeLists.txt             |   1 +
 src/language/utils/OFStream.cpp               |  12 +++
 src/language/utils/OFStream.hpp               |  20 ++++
 src/language/utils/OStream.hpp                |  36 +++++++
 src/language/utils/ValueDescriptor.hpp        |  36 +++++++
 tests/CMakeLists.txt                          |   1 -
 tests/test_ASTBuilder.cpp                     |  24 +++--
 tests/test_ASTNodeDataTypeBuilder.cpp         |  15 ++-
 tests/test_ASTNodeExpressionBuilder.cpp       |   6 +-
 tests/test_ASTNodeTypeCleaner.cpp             |   9 +-
 tests/test_OStreamProcessor.cpp               | 100 ------------------
 39 files changed, 391 insertions(+), 202 deletions(-)
 delete mode 100644 src/language/node_processor/OStreamProcessor.hpp
 create mode 100644 src/language/utils/OFStream.cpp
 create mode 100644 src/language/utils/OFStream.hpp
 create mode 100644 src/language/utils/OStream.hpp
 create mode 100644 src/language/utils/ValueDescriptor.hpp
 delete mode 100644 tests/test_OStreamProcessor.cpp

diff --git a/src/language/PEGGrammar.hpp b/src/language/PEGGrammar.hpp
index a3ad03428..723d9a710 100644
--- a/src/language/PEGGrammar.hpp
+++ b/src/language/PEGGrammar.hpp
@@ -126,11 +126,7 @@ struct BREAK : seq < break_kw, ignored > {};
 struct continue_kw : TAO_PEGTL_KEYWORD("continue") {};
 struct CONTINUE : seq < continue_kw, ignored > {};
 
-struct cout_kw : TAO_PEGTL_KEYWORD("cout") {};
-struct cerr_kw : TAO_PEGTL_KEYWORD("cerr") {};
-struct clog_kw : TAO_PEGTL_KEYWORD("clog") {};
-
-struct keyword : sor < basic_type, import_kw, true_kw, false_kw, let_kw, do_kw, while_kw, for_kw, if_kw, else_kw, and_kw, or_kw, xor_kw, not_kw, break_kw, continue_kw, cout_kw, cerr_kw, clog_kw > {};
+struct keyword : sor < basic_type, import_kw, true_kw, false_kw, let_kw, do_kw, while_kw, for_kw, if_kw, else_kw, and_kw, or_kw, xor_kw, not_kw, break_kw, continue_kw> {};
 
 struct identifier_minus_keyword : minus< identifier, keyword > {};
 
@@ -218,7 +214,9 @@ struct product : list_must< unary_expression, sor< multiply_op, divide_op > > {}
 
 struct sum : list_must< product, sor< plus_op, minus_op > > {};
 
-struct compare : list_must<sum, sor< lesser_or_eq_op, greater_or_eq_op, lesser_op, greater_op > >{};
+struct shift : list_must<sum, sor< shift_left_op, shift_right_op > >{};
+
+struct compare : list_must<shift, sor< lesser_or_eq_op, greater_or_eq_op, lesser_op, greater_op > >{};
 
 struct equality : list_must< compare, sor< eqeq_op, not_eq_op > >{};
 
@@ -289,15 +287,11 @@ struct for_statement_block;
 
 struct for_statement : if_must< FOR, open_parent, for_init, SEMICOL, for_test, SEMICOL, for_post, close_parent, for_statement_block >{};
 
-struct ostream_object : seq< sor< cout_kw, cerr_kw, clog_kw >, ignored >{};
-struct ostream_statement : seq< ostream_object, star< if_must< shift_left_op, expression, ignored > > >{};
-
 struct instruction
     : sor<if_statement,
           if_must<do_while_statement, semicol>,
           while_statement,
           for_statement,
-          if_must< ostream_statement, semicol >,
           if_must< BREAK, semicol >,
           if_must< CONTINUE, semicol >,
           block,
diff --git a/src/language/ast/ASTBuilder.cpp b/src/language/ast/ASTBuilder.cpp
index 0c433bbfd..a4dc8c822 100644
--- a/src/language/ast/ASTBuilder.cpp
+++ b/src/language/ast/ASTBuilder.cpp
@@ -214,20 +214,6 @@ struct ASTBuilder::simplify_for_post : TAO_PEGTL_NAMESPACE::parse_tree::apply<AS
   }
 };
 
-struct ASTBuilder::simplify_stream_statement
-  : TAO_PEGTL_NAMESPACE::parse_tree::apply<ASTBuilder::simplify_stream_statement>
-{
-  template <typename... States>
-  static void
-  transform(std::unique_ptr<ASTNode>& n, States&&...)
-  {
-    for (size_t i = 1; i < n->children.size(); ++i) {
-      n->children[0]->children.emplace_back(std::move(n->children[i]));
-    }
-    n = std::move(n->children[0]);
-  }
-};
-
 template <typename Rule>
 using selector = TAO_PEGTL_NAMESPACE::parse_tree::selector<
   Rule,
@@ -248,9 +234,6 @@ using selector = TAO_PEGTL_NAMESPACE::parse_tree::selector<
                                                      language::vector_type,
                                                      language::matrix_type,
                                                      language::string_type,
-                                                     language::cout_kw,
-                                                     language::cerr_kw,
-                                                     language::clog_kw,
                                                      language::var_declaration,
                                                      language::fct_declaration,
                                                      language::type_mapping,
@@ -269,6 +252,7 @@ using selector = TAO_PEGTL_NAMESPACE::parse_tree::selector<
                             language::equality,
                             language::compare,
                             language::sum,
+                            language::shift,
                             language::product,
                             language::affectation,
                             language::expression>,
@@ -282,6 +266,8 @@ using selector = TAO_PEGTL_NAMESPACE::parse_tree::selector<
                                  language::name_subscript_expression>,
   TAO_PEGTL_NAMESPACE::parse_tree::remove_content::on<language::plus_op,
                                                       language::minus_op,
+                                                      language::shift_left_op,
+                                                      language::shift_right_op,
                                                       language::multiply_op,
                                                       language::divide_op,
                                                       language::lesser_op,
@@ -308,8 +294,7 @@ using selector = TAO_PEGTL_NAMESPACE::parse_tree::selector<
   ASTBuilder::simplify_statement_block::on<language::statement_block>,
   ASTBuilder::simplify_for_init::on<language::for_init>,
   ASTBuilder::simplify_for_test::on<language::for_test>,
-  ASTBuilder::simplify_for_post::on<language::for_post>,
-  ASTBuilder::simplify_stream_statement::on<language::ostream_statement>>;
+  ASTBuilder::simplify_for_post::on<language::for_post>>;
 
 template <typename InputT>
 std::unique_ptr<ASTNode>
diff --git a/src/language/ast/ASTNodeBinaryOperatorExpressionBuilder.cpp b/src/language/ast/ASTNodeBinaryOperatorExpressionBuilder.cpp
index b6904220a..c96b8799e 100644
--- a/src/language/ast/ASTNodeBinaryOperatorExpressionBuilder.cpp
+++ b/src/language/ast/ASTNodeBinaryOperatorExpressionBuilder.cpp
@@ -22,6 +22,11 @@ ASTNodeBinaryOperatorExpressionBuilder::ASTNodeBinaryOperatorExpressionBuilder(A
     } else if (n.is_type<language::minus_op>()) {
       return binaryOperatorMangler<language::minus_op>(lhs_data_type, rhs_data_type);
 
+    } else if (n.is_type<language::shift_left_op>()) {
+      return binaryOperatorMangler<language::shift_left_op>(lhs_data_type, rhs_data_type);
+    } else if (n.is_type<language::shift_right_op>()) {
+      return binaryOperatorMangler<language::shift_right_op>(lhs_data_type, rhs_data_type);
+
     } else if (n.is_type<language::or_op>()) {
       return binaryOperatorMangler<language::or_op>(lhs_data_type, rhs_data_type);
     } else if (n.is_type<language::and_op>()) {
diff --git a/src/language/ast/ASTNodeDataTypeBuilder.cpp b/src/language/ast/ASTNodeDataTypeBuilder.cpp
index eb307e2c1..cb9e479df 100644
--- a/src/language/ast/ASTNodeDataTypeBuilder.cpp
+++ b/src/language/ast/ASTNodeDataTypeBuilder.cpp
@@ -161,8 +161,6 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const
 
       } else if (n.is_type<language::literal>()) {
         n.m_data_type = ASTNodeDataType::build<ASTNodeDataType::string_t>();
-      } else if (n.is_type<language::cout_kw>() or n.is_type<language::cerr_kw>() or n.is_type<language::clog_kw>()) {
-        n.m_data_type = ASTNodeDataType::build<ASTNodeDataType::void_t>();
       } else if (n.is_type<language::var_declaration>()) {
         auto& name_node = *(n.children[0]);
         auto& type_node = *(n.children[1]);
@@ -460,6 +458,7 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const
       }
     } 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>() or
+               n.is_type<language::shift_left_op>() or n.is_type<language::shift_right_op>() or
                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
                n.is_type<language::eqeq_op>() or n.is_type<language::not_eq_op>() or n.is_type<language::and_op>() or
@@ -482,6 +481,12 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const
           return operator_repository.getBinaryOperatorValueType(
             binaryOperatorMangler<language::divide_op>(type_0, type_1));
 
+        } else if (n.is_type<language::shift_left_op>()) {
+          return operator_repository.getBinaryOperatorValueType(
+            binaryOperatorMangler<language::shift_left_op>(type_0, type_1));
+        } else if (n.is_type<language::shift_right_op>()) {
+          return operator_repository.getBinaryOperatorValueType(
+            binaryOperatorMangler<language::shift_right_op>(type_0, type_1));
         } else if (n.is_type<language::lesser_op>()) {
           return operator_repository.getBinaryOperatorValueType(
             binaryOperatorMangler<language::lesser_op>(type_0, type_1));
diff --git a/src/language/ast/ASTNodeExpressionBuilder.cpp b/src/language/ast/ASTNodeExpressionBuilder.cpp
index d9b3b4e43..bbbcda43e 100644
--- a/src/language/ast/ASTNodeExpressionBuilder.cpp
+++ b/src/language/ast/ASTNodeExpressionBuilder.cpp
@@ -18,7 +18,6 @@
 #include <language/node_processor/IfProcessor.hpp>
 #include <language/node_processor/LocalNameProcessor.hpp>
 #include <language/node_processor/NameProcessor.hpp>
-#include <language/node_processor/OStreamProcessor.hpp>
 #include <language/node_processor/TupleToTinyVectorProcessor.hpp>
 #include <language/node_processor/TupleToVectorProcessor.hpp>
 #include <language/node_processor/ValueProcessor.hpp>
@@ -105,17 +104,12 @@ ASTNodeExpressionBuilder::_buildExpression(ASTNode& n)
   } else if (n.is_type<language::multiply_op>() or n.is_type<language::divide_op>() or n.is_type<language::plus_op>() or
              n.is_type<language::minus_op>() or n.is_type<language::or_op>() or n.is_type<language::and_op>() or
              n.is_type<language::xor_op>() or n.is_type<language::greater_op>() or
+             n.is_type<language::shift_left_op>() or n.is_type<language::shift_right_op>() or
              n.is_type<language::greater_or_eq_op>() or n.is_type<language::lesser_op>() or
              n.is_type<language::lesser_or_eq_op>() or n.is_type<language::eqeq_op>() or
              n.is_type<language::not_eq_op>()) {
     ASTNodeBinaryOperatorExpressionBuilder{n};
 
-  } else if (n.is_type<language::cout_kw>()) {
-    n.m_node_processor = std::make_unique<OStreamProcessor>(n, std::cout);
-  } else if (n.is_type<language::cerr_kw>()) {
-    n.m_node_processor = std::make_unique<OStreamProcessor>(n, std::cerr);
-  } else if (n.is_type<language::clog_kw>()) {
-    n.m_node_processor = std::make_unique<OStreamProcessor>(n, std::clog);
   } else if (n.is_type<language::if_statement>()) {
     n.m_node_processor = std::make_unique<IfProcessor>(n);
   } else if (n.is_type<language::statement_block>()) {
diff --git a/src/language/modules/BuiltinModule.cpp b/src/language/modules/BuiltinModule.cpp
index 98d8f71c6..cc76f78f7 100644
--- a/src/language/modules/BuiltinModule.cpp
+++ b/src/language/modules/BuiltinModule.cpp
@@ -2,6 +2,7 @@
 
 #include <language/utils/BuiltinFunctionEmbedder.hpp>
 #include <language/utils/TypeDescriptor.hpp>
+#include <language/utils/ValueDescriptor.hpp>
 #include <utils/Exceptions.hpp>
 
 #include <memory>
@@ -44,6 +45,17 @@ BuiltinModule::_addBuiltinFunction(const std::string& name,
   }
 }
 
+void
+BuiltinModule::_addNameValue(const std::string& name, const ASTNodeDataType& type, const DataVariant& data)
+{
+  std::shared_ptr value_descriptor = std::make_shared<ValueDescriptor>(type, data);
+
+  auto [i_type, success] = m_name_value_map.insert(std::make_pair(name, value_descriptor));
+  if (not success) {
+    throw NormalError("variable '" + name + "' cannot be added!\n");
+  }
+}
+
 void
 BuiltinModule::_addTypeDescriptor(const ASTNodeDataType& ast_node_data_type)
 {
diff --git a/src/language/modules/BuiltinModule.hpp b/src/language/modules/BuiltinModule.hpp
index 2be1d1625..3dd9ad1db 100644
--- a/src/language/modules/BuiltinModule.hpp
+++ b/src/language/modules/BuiltinModule.hpp
@@ -6,18 +6,22 @@
 
 class IBuiltinFunctionEmbedder;
 class TypeDescriptor;
+class ValueDescriptor;
 
 class BuiltinModule : public IModule
 {
  protected:
   NameBuiltinFunctionMap m_name_builtin_function_map;
   NameTypeMap m_name_type_map;
+  NameValueMap m_name_value_map;
 
   void _addBuiltinFunction(const std::string& name,
                            std::shared_ptr<IBuiltinFunctionEmbedder> builtin_function_embedder);
 
   void _addTypeDescriptor(const ASTNodeDataType& type);
 
+  void _addNameValue(const std::string& name, const ASTNodeDataType& type, const DataVariant& data);
+
   const bool m_is_mandatory;
 
  public:
@@ -39,6 +43,12 @@ class BuiltinModule : public IModule
     return m_name_type_map;
   }
 
+  const NameValueMap&
+  getNameValueMap() const final
+  {
+    return m_name_value_map;
+  }
+
   BuiltinModule(bool is_mandatory = false);
 
   ~BuiltinModule() = default;
diff --git a/src/language/modules/CoreModule.cpp b/src/language/modules/CoreModule.cpp
index 1cc699455..9b70144e8 100644
--- a/src/language/modules/CoreModule.cpp
+++ b/src/language/modules/CoreModule.cpp
@@ -22,6 +22,8 @@
 #include <language/utils/IncDecOperatorRegisterForN.hpp>
 #include <language/utils/IncDecOperatorRegisterForR.hpp>
 #include <language/utils/IncDecOperatorRegisterForZ.hpp>
+#include <language/utils/OFStream.hpp>
+#include <language/utils/OStream.hpp>
 #include <language/utils/UnaryOperatorRegisterForB.hpp>
 #include <language/utils/UnaryOperatorRegisterForN.hpp>
 #include <language/utils/UnaryOperatorRegisterForR.hpp>
@@ -83,6 +85,25 @@ CoreModule::CoreModule() : BuiltinModule(true)
                                                  []() { RandomEngine::instance().resetRandomSeed(); }
 
                                                  ));
+
+  this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const OStream>>);
+
+  this
+    ->_addBuiltinFunction("ofstream",
+                          std::make_shared<BuiltinFunctionEmbedder<std::shared_ptr<const OStream>(const std::string&)>>(
+
+                            [](const std::string& filename) { return std::make_shared<const OFStream>(filename); }
+
+                            ));
+
+  this->_addNameValue("cout", ast_node_data_type_from<std::shared_ptr<const OStream>>,
+                      EmbeddedData{std::make_shared<DataHandler<const OStream>>(std::make_shared<OStream>(std::cout))});
+
+  this->_addNameValue("cerr", ast_node_data_type_from<std::shared_ptr<const OStream>>,
+                      EmbeddedData{std::make_shared<DataHandler<const OStream>>(std::make_shared<OStream>(std::cerr))});
+
+  this->_addNameValue("clog", ast_node_data_type_from<std::shared_ptr<const OStream>>,
+                      EmbeddedData{std::make_shared<DataHandler<const OStream>>(std::make_shared<OStream>(std::clog))});
 }
 
 void
diff --git a/src/language/modules/IModule.hpp b/src/language/modules/IModule.hpp
index ceb3fd3df..04aefbe91 100644
--- a/src/language/modules/IModule.hpp
+++ b/src/language/modules/IModule.hpp
@@ -1,6 +1,8 @@
 #ifndef IMODULE_HPP
 #define IMODULE_HPP
 
+#include <language/utils/DataVariant.hpp>
+
 #include <memory>
 #include <string>
 #include <string_view>
@@ -8,12 +10,14 @@
 
 class IBuiltinFunctionEmbedder;
 class TypeDescriptor;
+class ValueDescriptor;
 
 class IModule
 {
  public:
   using NameBuiltinFunctionMap = std::unordered_map<std::string, std::shared_ptr<IBuiltinFunctionEmbedder>>;
   using NameTypeMap            = std::unordered_map<std::string, std::shared_ptr<TypeDescriptor>>;
+  using NameValueMap           = std::unordered_map<std::string, std::shared_ptr<ValueDescriptor>>;
 
   IModule()          = default;
   IModule(IModule&&) = default;
@@ -25,6 +29,8 @@ class IModule
 
   virtual const NameTypeMap& getNameTypeMap() const = 0;
 
+  virtual const NameValueMap& getNameValueMap() const = 0;
+
   virtual void registerOperators() const = 0;
 
   virtual std::string_view name() const = 0;
diff --git a/src/language/modules/ModuleRepository.cpp b/src/language/modules/ModuleRepository.cpp
index e516ab800..71a1368b9 100644
--- a/src/language/modules/ModuleRepository.cpp
+++ b/src/language/modules/ModuleRepository.cpp
@@ -13,6 +13,8 @@
 #include <language/utils/ParseError.hpp>
 #include <language/utils/SymbolTable.hpp>
 #include <language/utils/TypeDescriptor.hpp>
+#include <language/utils/ValueDescriptor.hpp>
+
 #include <utils/PugsAssert.hpp>
 
 #include <algorithm>
@@ -85,6 +87,28 @@ ModuleRepository::_populateEmbedderTableT(const ASTNode& module_node,
   }
 }
 
+void
+ModuleRepository::_populateSymbolTable(const ASTNode& module_node,
+                                       const std::string& module_name,
+                                       const IModule::NameValueMap& name_value_descriptor_map,
+                                       SymbolTable& symbol_table)
+{
+  for (auto [symbol_name, value_descriptor] : name_value_descriptor_map) {
+    auto [i_symbol, success] = symbol_table.add(symbol_name, module_node.begin());
+
+    if (not success) {
+      std::ostringstream error_message;
+      error_message << "importing module '" << module_name << "', cannot add symbol '" << symbol_name
+                    << "', it is already defined!";
+      throw ParseError(error_message.str(), module_node.begin());
+    }
+
+    i_symbol->attributes().setDataType(value_descriptor->type());
+    i_symbol->attributes().setIsInitialized();
+    i_symbol->attributes().value() = value_descriptor->value();
+  }
+}
+
 void
 ModuleRepository::populateSymbolTable(const ASTNode& module_name_node, SymbolTable& symbol_table)
 {
@@ -110,6 +134,8 @@ ModuleRepository::populateSymbolTable(const ASTNode& module_name_node, SymbolTab
                                   ASTNodeDataType::build<ASTNodeDataType::type_name_id_t>(), symbol_table,
                                   symbol_table.typeEmbedderTable());
 
+    this->_populateSymbolTable(module_name_node, module_name, populating_module.getNameValueMap(), symbol_table);
+
     for (auto [symbol_name, embedded] : populating_module.getNameTypeMap()) {
       BasicAffectationRegisterFor<EmbeddedData>(ASTNodeDataType::build<ASTNodeDataType::type_id_t>(symbol_name));
     }
@@ -132,6 +158,12 @@ ModuleRepository::populateMandatorySymbolTable(const ASTNode& root_node, SymbolT
                                     ASTNodeDataType::build<ASTNodeDataType::type_name_id_t>(), symbol_table,
                                     symbol_table.typeEmbedderTable());
 
+      this->_populateSymbolTable(root_node, module_name, i_module->getNameValueMap(), symbol_table);
+
+      for (auto [symbol_name, embedded] : i_module->getNameTypeMap()) {
+        BasicAffectationRegisterFor<EmbeddedData>(ASTNodeDataType::build<ASTNodeDataType::type_id_t>(symbol_name));
+      }
+
       i_module->registerOperators();
     }
   }
diff --git a/src/language/modules/ModuleRepository.hpp b/src/language/modules/ModuleRepository.hpp
index c4c9870fc..c7289c8d1 100644
--- a/src/language/modules/ModuleRepository.hpp
+++ b/src/language/modules/ModuleRepository.hpp
@@ -26,6 +26,11 @@ class ModuleRepository
                                SymbolTable& symbol_table,
                                EmbedderTableT& embedder_table);
 
+  void _populateSymbolTable(const ASTNode& module_node,
+                            const std::string& module_name,
+                            const IModule::NameValueMap& name_value_map,
+                            SymbolTable& symbol_table);
+
  public:
   void populateSymbolTable(const ASTNode& module_name_node, SymbolTable& symbol_table);
   void populateMandatorySymbolTable(const ASTNode& root_node, SymbolTable& symbol_table);
diff --git a/src/language/node_processor/BinaryExpressionProcessor.hpp b/src/language/node_processor/BinaryExpressionProcessor.hpp
index 3af45c387..8cc1a430f 100644
--- a/src/language/node_processor/BinaryExpressionProcessor.hpp
+++ b/src/language/node_processor/BinaryExpressionProcessor.hpp
@@ -82,6 +82,34 @@ struct BinOp<language::lesser_op>
   }
 };
 
+class OStream;
+
+template <>
+struct BinOp<language::shift_left_op>
+{
+  template <typename A, typename B>
+  PUGS_INLINE auto
+  eval(const A& a, const B& b) -> decltype(a << b)
+  {
+    if constexpr (std::is_same_v<A, std::shared_ptr<const OStream>> and std::is_same_v<B, bool>) {
+      return a << std::boolalpha << b;
+    } else {
+      return a << b;
+    }
+  }
+};
+
+template <>
+struct BinOp<language::shift_right_op>
+{
+  template <typename A, typename B>
+  PUGS_INLINE auto
+  eval(const A& a, const B& b) -> decltype(a >> b)
+  {
+    return a >> b;
+  }
+};
+
 template <>
 struct BinOp<language::lesser_or_eq_op>
 {
@@ -286,7 +314,8 @@ struct BinaryExpressionProcessor<BinaryOpT, std::shared_ptr<ValueT>, std::shared
   PUGS_INLINE DataVariant
   _eval(const DataVariant& a, const DataVariant& b)
   {
-    if constexpr ((std::is_arithmetic_v<B_DataT>) or (is_tiny_matrix_v<B_DataT>) or (is_tiny_vector_v<B_DataT>)) {
+    if constexpr ((std::is_arithmetic_v<B_DataT>) or (is_tiny_matrix_v<B_DataT>) or (is_tiny_vector_v<B_DataT>) or
+                  (std::is_same_v<std::string, B_DataT>)) {
       const auto& embedded_a = std::get<EmbeddedData>(a);
       const auto& b_value    = std::get<B_DataT>(b);
 
diff --git a/src/language/node_processor/OStreamProcessor.hpp b/src/language/node_processor/OStreamProcessor.hpp
deleted file mode 100644
index e67dd7b6d..000000000
--- a/src/language/node_processor/OStreamProcessor.hpp
+++ /dev/null
@@ -1,38 +0,0 @@
-#ifndef OSTREAM_PROCESSOR_HPP
-#define OSTREAM_PROCESSOR_HPP
-
-#include <language/ast/ASTNode.hpp>
-#include <language/node_processor/INodeProcessor.hpp>
-#include <language/utils/ParseError.hpp>
-
-class OStreamProcessor final : public INodeProcessor
-{
- private:
-  ASTNode& m_node;
-  std::ostream& m_os;
-
- public:
-  DataVariant
-  execute(ExecutionPolicy& exec_policy)
-  {
-    for (size_t i = 0; i < m_node.children.size(); ++i) {
-      m_os << m_node.children[i]->execute(exec_policy);
-    }
-
-    return {};
-  }
-
-  OStreamProcessor(ASTNode& node, std::ostream& os) : m_node{node}, m_os(os)
-  {
-    for (auto& child : m_node.children) {
-      if ((child->m_data_type == ASTNodeDataType::type_name_id_t) or
-          (child->m_data_type == ASTNodeDataType::function_t) or
-          (child->m_data_type == ASTNodeDataType::builtin_function_t)) {
-        throw ParseError("invalid argument, cannot print a '" + dataTypeName(child->m_data_type) + "'",
-                         std::vector{child->begin()});
-      }
-    }
-  }
-};
-
-#endif   // OSTREAM_PROCESSOR_HPP
diff --git a/src/language/utils/BinaryOperatorMangler.hpp b/src/language/utils/BinaryOperatorMangler.hpp
index f3648f3fb..d7f0c6c79 100644
--- a/src/language/utils/BinaryOperatorMangler.hpp
+++ b/src/language/utils/BinaryOperatorMangler.hpp
@@ -13,6 +13,9 @@ struct divide_op;
 struct plus_op;
 struct minus_op;
 
+struct shift_left_op;
+struct shift_right_op;
+
 struct or_op;
 struct and_op;
 struct xor_op;
@@ -56,6 +59,10 @@ binaryOperatorMangler(const ASTNodeDataType& lhs_data_type, const ASTNodeDataTyp
       return "==";
     } else if constexpr (std::is_same_v<BinaryOperatorT, language::not_eq_op>) {
       return "!=";
+    } else if constexpr (std::is_same_v<BinaryOperatorT, language::shift_left_op>) {
+      return "<<";
+    } else if constexpr (std::is_same_v<BinaryOperatorT, language::shift_right_op>) {
+      return ">>";
     } else {
       static_assert(std::is_same_v<language::multiply_op, BinaryOperatorT>, "undefined binary operator");
     }
diff --git a/src/language/utils/BinaryOperatorRegisterForB.cpp b/src/language/utils/BinaryOperatorRegisterForB.cpp
index cba803aaf..dc228471d 100644
--- a/src/language/utils/BinaryOperatorRegisterForB.cpp
+++ b/src/language/utils/BinaryOperatorRegisterForB.cpp
@@ -1,6 +1,18 @@
 #include <language/utils/BinaryOperatorRegisterForB.hpp>
 
 #include <language/utils/BasicBinaryOperatorRegisterComparisonOf.hpp>
+#include <language/utils/DataHandler.hpp>
+#include <language/utils/OStream.hpp>
+
+void
+BinaryOperatorRegisterForB::_register_ostream()
+{
+  OperatorRepository& repository = OperatorRepository::instance();
+
+  repository.addBinaryOperator<language::shift_left_op>(
+    std::make_shared<BinaryOperatorProcessorBuilder<language::shift_left_op, std::shared_ptr<const OStream>,
+                                                    std::shared_ptr<const OStream>, bool>>());
+}
 
 void
 BinaryOperatorRegisterForB::_register_comparisons()
@@ -61,6 +73,7 @@ BinaryOperatorRegisterForB::_register_divide()
 
 BinaryOperatorRegisterForB::BinaryOperatorRegisterForB()
 {
+  this->_register_ostream();
   this->_register_comparisons();
   this->_register_logical_operators();
   this->_register_plus();
diff --git a/src/language/utils/BinaryOperatorRegisterForB.hpp b/src/language/utils/BinaryOperatorRegisterForB.hpp
index 651411bf7..545b49c50 100644
--- a/src/language/utils/BinaryOperatorRegisterForB.hpp
+++ b/src/language/utils/BinaryOperatorRegisterForB.hpp
@@ -4,6 +4,7 @@
 class BinaryOperatorRegisterForB
 {
  private:
+  void _register_ostream();
   void _register_comparisons();
   void _register_logical_operators();
   void _register_plus();
diff --git a/src/language/utils/BinaryOperatorRegisterForN.cpp b/src/language/utils/BinaryOperatorRegisterForN.cpp
index 66d589405..e32e2b53a 100644
--- a/src/language/utils/BinaryOperatorRegisterForN.cpp
+++ b/src/language/utils/BinaryOperatorRegisterForN.cpp
@@ -1,6 +1,18 @@
 #include <language/utils/BinaryOperatorRegisterForN.hpp>
 
 #include <language/utils/BasicBinaryOperatorRegisterComparisonOf.hpp>
+#include <language/utils/DataHandler.hpp>
+#include <language/utils/OStream.hpp>
+
+void
+BinaryOperatorRegisterForN::_register_ostream()
+{
+  OperatorRepository& repository = OperatorRepository::instance();
+
+  repository.addBinaryOperator<language::shift_left_op>(
+    std::make_shared<BinaryOperatorProcessorBuilder<language::shift_left_op, std::shared_ptr<const OStream>,
+                                                    std::shared_ptr<const OStream>, uint64_t>>());
+}
 
 void
 BinaryOperatorRegisterForN::_register_comparisons()
@@ -42,6 +54,7 @@ BinaryOperatorRegisterForN::_register_minus()
 
 BinaryOperatorRegisterForN::BinaryOperatorRegisterForN()
 {
+  this->_register_ostream();
   this->_register_comparisons();
   this->_register_arithmetic<language::plus_op>();
   this->_register_minus();
diff --git a/src/language/utils/BinaryOperatorRegisterForN.hpp b/src/language/utils/BinaryOperatorRegisterForN.hpp
index 80dc52b01..997b6be7a 100644
--- a/src/language/utils/BinaryOperatorRegisterForN.hpp
+++ b/src/language/utils/BinaryOperatorRegisterForN.hpp
@@ -4,6 +4,7 @@
 class BinaryOperatorRegisterForN
 {
  private:
+  void _register_ostream();
   template <typename OperatorT>
   void _register_arithmetic();
   void _register_comparisons();
diff --git a/src/language/utils/BinaryOperatorRegisterForR.cpp b/src/language/utils/BinaryOperatorRegisterForR.cpp
index 4f6998892..cc8ba8a79 100644
--- a/src/language/utils/BinaryOperatorRegisterForR.cpp
+++ b/src/language/utils/BinaryOperatorRegisterForR.cpp
@@ -1,6 +1,18 @@
 #include <language/utils/BinaryOperatorRegisterForR.hpp>
 
 #include <language/utils/BasicBinaryOperatorRegisterComparisonOf.hpp>
+#include <language/utils/DataHandler.hpp>
+#include <language/utils/OStream.hpp>
+
+void
+BinaryOperatorRegisterForR::_register_ostream()
+{
+  OperatorRepository& repository = OperatorRepository::instance();
+
+  repository.addBinaryOperator<language::shift_left_op>(
+    std::make_shared<BinaryOperatorProcessorBuilder<language::shift_left_op, std::shared_ptr<const OStream>,
+                                                    std::shared_ptr<const OStream>, double>>());
+}
 
 void
 BinaryOperatorRegisterForR::_register_comparisons()
@@ -44,6 +56,7 @@ BinaryOperatorRegisterForR::_register_arithmetic()
 
 BinaryOperatorRegisterForR::BinaryOperatorRegisterForR()
 {
+  this->_register_ostream();
   this->_register_comparisons();
   this->_register_arithmetic<language::plus_op>();
   this->_register_arithmetic<language::minus_op>();
diff --git a/src/language/utils/BinaryOperatorRegisterForR.hpp b/src/language/utils/BinaryOperatorRegisterForR.hpp
index cbde82fb3..c14fa410f 100644
--- a/src/language/utils/BinaryOperatorRegisterForR.hpp
+++ b/src/language/utils/BinaryOperatorRegisterForR.hpp
@@ -4,6 +4,7 @@
 class BinaryOperatorRegisterForR
 {
  private:
+  void _register_ostream();
   template <typename OperatorT>
   void _register_arithmetic();
   void _register_comparisons();
diff --git a/src/language/utils/BinaryOperatorRegisterForRn.cpp b/src/language/utils/BinaryOperatorRegisterForRn.cpp
index 41e5b031f..6e900d036 100644
--- a/src/language/utils/BinaryOperatorRegisterForRn.cpp
+++ b/src/language/utils/BinaryOperatorRegisterForRn.cpp
@@ -1,8 +1,23 @@
 #include <language/utils/BinaryOperatorRegisterForRn.hpp>
 
 #include <language/utils/BinaryOperatorProcessorBuilder.hpp>
+#include <language/utils/DataHandler.hpp>
+#include <language/utils/OStream.hpp>
 #include <language/utils/OperatorRepository.hpp>
 
+template <size_t Dimension>
+void
+BinaryOperatorRegisterForRn<Dimension>::_register_ostream()
+{
+  OperatorRepository& repository = OperatorRepository::instance();
+
+  using Rn = TinyVector<Dimension>;
+
+  repository.addBinaryOperator<language::shift_left_op>(
+    std::make_shared<BinaryOperatorProcessorBuilder<language::shift_left_op, std::shared_ptr<const OStream>,
+                                                    std::shared_ptr<const OStream>, Rn>>());
+}
+
 template <size_t Dimension>
 void
 BinaryOperatorRegisterForRn<Dimension>::_register_comparisons()
@@ -54,6 +69,8 @@ BinaryOperatorRegisterForRn<Dimension>::_register_arithmetic()
 template <size_t Dimension>
 BinaryOperatorRegisterForRn<Dimension>::BinaryOperatorRegisterForRn()
 {
+  this->_register_ostream();
+
   this->_register_comparisons();
 
   this->_register_product_by_a_scalar();
diff --git a/src/language/utils/BinaryOperatorRegisterForRn.hpp b/src/language/utils/BinaryOperatorRegisterForRn.hpp
index b9f27d416..fc83d3a67 100644
--- a/src/language/utils/BinaryOperatorRegisterForRn.hpp
+++ b/src/language/utils/BinaryOperatorRegisterForRn.hpp
@@ -7,10 +7,9 @@ template <size_t Dimension>
 class BinaryOperatorRegisterForRn
 {
  private:
+  void _register_ostream();
   void _register_comparisons();
-
   void _register_product_by_a_scalar();
-
   template <typename OperatorT>
   void _register_arithmetic();
 
diff --git a/src/language/utils/BinaryOperatorRegisterForRnxn.cpp b/src/language/utils/BinaryOperatorRegisterForRnxn.cpp
index 3d8850e79..f164203dd 100644
--- a/src/language/utils/BinaryOperatorRegisterForRnxn.cpp
+++ b/src/language/utils/BinaryOperatorRegisterForRnxn.cpp
@@ -1,8 +1,23 @@
 #include <language/utils/BinaryOperatorRegisterForRnxn.hpp>
 
 #include <language/utils/BinaryOperatorProcessorBuilder.hpp>
+#include <language/utils/DataHandler.hpp>
+#include <language/utils/OStream.hpp>
 #include <language/utils/OperatorRepository.hpp>
 
+template <size_t Dimension>
+void
+BinaryOperatorRegisterForRnxn<Dimension>::_register_ostream()
+{
+  OperatorRepository& repository = OperatorRepository::instance();
+
+  using Rnxn = TinyMatrix<Dimension>;
+
+  repository.addBinaryOperator<language::shift_left_op>(
+    std::make_shared<BinaryOperatorProcessorBuilder<language::shift_left_op, std::shared_ptr<const OStream>,
+                                                    std::shared_ptr<const OStream>, Rnxn>>());
+}
+
 template <size_t Dimension>
 void
 BinaryOperatorRegisterForRnxn<Dimension>::_register_comparisons()
@@ -68,6 +83,8 @@ BinaryOperatorRegisterForRnxn<Dimension>::_register_arithmetic()
 template <size_t Dimension>
 BinaryOperatorRegisterForRnxn<Dimension>::BinaryOperatorRegisterForRnxn()
 {
+  this->_register_ostream();
+
   this->_register_comparisons();
 
   this->_register_product_by_a_scalar();
diff --git a/src/language/utils/BinaryOperatorRegisterForRnxn.hpp b/src/language/utils/BinaryOperatorRegisterForRnxn.hpp
index 594740b62..9edad436b 100644
--- a/src/language/utils/BinaryOperatorRegisterForRnxn.hpp
+++ b/src/language/utils/BinaryOperatorRegisterForRnxn.hpp
@@ -7,11 +7,10 @@ template <size_t Dimension>
 class BinaryOperatorRegisterForRnxn
 {
  private:
+  void _register_ostream();
   void _register_comparisons();
-
   void _register_product_by_a_scalar();
   void _register_product_by_a_vector();
-
   template <typename OperatorT>
   void _register_arithmetic();
 
diff --git a/src/language/utils/BinaryOperatorRegisterForString.cpp b/src/language/utils/BinaryOperatorRegisterForString.cpp
index 0e89cbe00..999950cf1 100644
--- a/src/language/utils/BinaryOperatorRegisterForString.cpp
+++ b/src/language/utils/BinaryOperatorRegisterForString.cpp
@@ -2,8 +2,20 @@
 
 #include <language/utils/BinaryOperatorProcessorBuilder.hpp>
 #include <language/utils/ConcatExpressionProcessorBuilder.hpp>
+#include <language/utils/DataHandler.hpp>
+#include <language/utils/OStream.hpp>
 #include <language/utils/OperatorRepository.hpp>
 
+void
+BinaryOperatorRegisterForString::_register_ostream()
+{
+  OperatorRepository& repository = OperatorRepository::instance();
+
+  repository.addBinaryOperator<language::shift_left_op>(
+    std::make_shared<BinaryOperatorProcessorBuilder<language::shift_left_op, std::shared_ptr<const OStream>,
+                                                    std::shared_ptr<const OStream>, std::string>>());
+}
+
 void
 BinaryOperatorRegisterForString::_register_comparisons()
 {
@@ -27,6 +39,8 @@ BinaryOperatorRegisterForString::_register_concat()
 
 BinaryOperatorRegisterForString::BinaryOperatorRegisterForString()
 {
+  this->_register_ostream();
+
   this->_register_comparisons();
 
   this->_register_concat<bool>();
diff --git a/src/language/utils/BinaryOperatorRegisterForString.hpp b/src/language/utils/BinaryOperatorRegisterForString.hpp
index 36af13ee6..b7d4a7440 100644
--- a/src/language/utils/BinaryOperatorRegisterForString.hpp
+++ b/src/language/utils/BinaryOperatorRegisterForString.hpp
@@ -4,6 +4,8 @@
 class BinaryOperatorRegisterForString
 {
  private:
+  void _register_ostream();
+
   void _register_comparisons();
 
   template <typename RHS_T>
diff --git a/src/language/utils/BinaryOperatorRegisterForZ.cpp b/src/language/utils/BinaryOperatorRegisterForZ.cpp
index cc8b22d11..1d3188502 100644
--- a/src/language/utils/BinaryOperatorRegisterForZ.cpp
+++ b/src/language/utils/BinaryOperatorRegisterForZ.cpp
@@ -1,6 +1,18 @@
 #include <language/utils/BinaryOperatorRegisterForZ.hpp>
 
 #include <language/utils/BasicBinaryOperatorRegisterComparisonOf.hpp>
+#include <language/utils/DataHandler.hpp>
+#include <language/utils/OStream.hpp>
+
+void
+BinaryOperatorRegisterForZ::_register_ostream()
+{
+  OperatorRepository& repository = OperatorRepository::instance();
+
+  repository.addBinaryOperator<language::shift_left_op>(
+    std::make_shared<BinaryOperatorProcessorBuilder<language::shift_left_op, std::shared_ptr<const OStream>,
+                                                    std::shared_ptr<const OStream>, int64_t>>());
+}
 
 void
 BinaryOperatorRegisterForZ::_register_comparisons()
@@ -36,6 +48,7 @@ BinaryOperatorRegisterForZ::_register_arithmetic()
 
 BinaryOperatorRegisterForZ::BinaryOperatorRegisterForZ()
 {
+  this->_register_ostream();
   this->_register_comparisons();
   this->_register_arithmetic<language::plus_op>();
   this->_register_arithmetic<language::minus_op>();
diff --git a/src/language/utils/BinaryOperatorRegisterForZ.hpp b/src/language/utils/BinaryOperatorRegisterForZ.hpp
index 3f9a7c30c..cb734843d 100644
--- a/src/language/utils/BinaryOperatorRegisterForZ.hpp
+++ b/src/language/utils/BinaryOperatorRegisterForZ.hpp
@@ -4,6 +4,7 @@
 class BinaryOperatorRegisterForZ
 {
  private:
+  void _register_ostream();
   template <typename OperatorT>
   void _register_arithmetic();
   void _register_comparisons();
diff --git a/src/language/utils/CMakeLists.txt b/src/language/utils/CMakeLists.txt
index 216a6be6a..c8928003d 100644
--- a/src/language/utils/CMakeLists.txt
+++ b/src/language/utils/CMakeLists.txt
@@ -29,6 +29,7 @@ add_library(PugsLanguageUtils
   IncDecOperatorRegisterForN.cpp
   IncDecOperatorRegisterForR.cpp
   IncDecOperatorRegisterForZ.cpp
+  OFStream.cpp
   OperatorRepository.cpp
   UnaryOperatorRegisterForB.cpp
   UnaryOperatorRegisterForN.cpp
diff --git a/src/language/utils/OFStream.cpp b/src/language/utils/OFStream.cpp
new file mode 100644
index 000000000..3bc5e2991
--- /dev/null
+++ b/src/language/utils/OFStream.cpp
@@ -0,0 +1,12 @@
+#include <language/utils/OFStream.hpp>
+
+#include <utils/Messenger.hpp>
+
+OFStream::OFStream(const std::string& filename)
+{
+  if (parallel::rank() == 0) {
+    m_fstream.open(filename);
+    Assert(m_ostream == nullptr, "ostream was already defined");
+    m_ostream = &m_fstream;
+  }
+}
diff --git a/src/language/utils/OFStream.hpp b/src/language/utils/OFStream.hpp
new file mode 100644
index 000000000..589f0fe27
--- /dev/null
+++ b/src/language/utils/OFStream.hpp
@@ -0,0 +1,20 @@
+#ifndef OFSTREAM_HPP
+#define OFSTREAM_HPP
+
+#include <language/utils/OStream.hpp>
+
+#include <fstream>
+
+class OFStream : public OStream
+{
+ private:
+  std::ofstream m_fstream;
+
+ public:
+  OFStream(const std::string& filename);
+
+  OFStream()  = default;
+  ~OFStream() = default;
+};
+
+#endif   // OFSTREAM_HPP
diff --git a/src/language/utils/OStream.hpp b/src/language/utils/OStream.hpp
new file mode 100644
index 000000000..6abbcf668
--- /dev/null
+++ b/src/language/utils/OStream.hpp
@@ -0,0 +1,36 @@
+#ifndef OSTREAM_HPP
+#define OSTREAM_HPP
+
+#include <language/utils/ASTNodeDataTypeTraits.hpp>
+#include <utils/PugsAssert.hpp>
+
+#include <memory>
+
+class OStream
+{
+ protected:
+  mutable std::ostream* m_ostream = nullptr;
+
+ public:
+  template <typename DataT>
+  friend std::shared_ptr<const OStream>
+  operator<<(const std::shared_ptr<const OStream>& os, const DataT& t)
+  {
+    Assert(os.use_count() > 0);
+    if (os->m_ostream != nullptr) {
+      *os->m_ostream << t;
+    }
+    return os;
+  }
+
+  OStream(std::ostream& os) : m_ostream(&os) {}
+  OStream() = default;
+
+  virtual ~OStream() = default;
+};
+
+template <>
+inline ASTNodeDataType ast_node_data_type_from<std::shared_ptr<const OStream>> =
+  ASTNodeDataType::build<ASTNodeDataType::type_id_t>("ostream");
+
+#endif   // OSTREAM_HPP
diff --git a/src/language/utils/ValueDescriptor.hpp b/src/language/utils/ValueDescriptor.hpp
new file mode 100644
index 000000000..36bca3535
--- /dev/null
+++ b/src/language/utils/ValueDescriptor.hpp
@@ -0,0 +1,36 @@
+#ifndef VALUE_DESCRIPTOR_HPP
+#define VALUE_DESCRIPTOR_HPP
+
+#include <language/utils/ASTNodeDataType.hpp>
+#include <language/utils/DataVariant.hpp>
+
+#include <string>
+
+class ValueDescriptor
+{
+ private:
+  const ASTNodeDataType m_type;
+  const DataVariant m_value;
+
+ public:
+  const ASTNodeDataType
+  type() const
+  {
+    return m_type;
+  }
+
+  const DataVariant
+  value() const
+  {
+    return m_value;
+  }
+
+  ValueDescriptor(const ASTNodeDataType& type, const DataVariant& value) : m_type{type}, m_value{value} {}
+
+  ValueDescriptor(const ValueDescriptor&) = delete;
+  ValueDescriptor(ValueDescriptor&&)      = delete;
+
+  ~ValueDescriptor() = default;
+};
+
+#endif   // VALUE_DESCRIPTOR_HPP
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 62a70aad3..3c7ab2eb4 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -86,7 +86,6 @@ add_executable (unit_tests
   test_MathModule.cpp
   test_NameProcessor.cpp
   test_NaNHelper.cpp
-  test_OStreamProcessor.cpp
   test_ParseError.cpp
   test_PETScUtils.cpp
   test_PugsAssert.cpp
diff --git a/tests/test_ASTBuilder.cpp b/tests/test_ASTBuilder.cpp
index 387e84b75..3ce2855ef 100644
--- a/tests/test_ASTBuilder.cpp
+++ b/tests/test_ASTBuilder.cpp
@@ -600,7 +600,7 @@ for(let i:N, i=0; i<10; ++i) {
       CHECK_AST(data, result);
     }
 
-    SECTION("ostream simplifications")
+    SECTION("ostream")
     {
       std::string_view data = R"(
 cout << 1+2 << "\n";
@@ -610,16 +610,22 @@ clog << "log " << l << "\n";
 
       std::string_view result = R"(
 (root)
- +-(language::cout_kw)
- |   +-(language::plus_op)
- |   |   +-(language::integer:1)
- |   |   `-(language::integer:2)
+ +-(language::shift_left_op)
+ |   +-(language::shift_left_op)
+ |   |   +-(language::name:cout)
+ |   |   `-(language::plus_op)
+ |   |       +-(language::integer:1)
+ |   |       `-(language::integer:2)
  |   `-(language::literal:"\n")
- +-(language::cerr_kw)
+ +-(language::shift_left_op)
+ |   +-(language::name:cerr)
  |   `-(language::literal:"error?\n")
- `-(language::clog_kw)
-     +-(language::literal:"log ")
-     +-(language::name:l)
+ `-(language::shift_left_op)
+     +-(language::shift_left_op)
+     |   +-(language::shift_left_op)
+     |   |   +-(language::name:clog)
+     |   |   `-(language::literal:"log ")
+     |   `-(language::name:l)
      `-(language::literal:"\n")
 )";
       CHECK_AST(data, result);
diff --git a/tests/test_ASTNodeDataTypeBuilder.cpp b/tests/test_ASTNodeDataTypeBuilder.cpp
index 96af38a0b..2e3835024 100644
--- a/tests/test_ASTNodeDataTypeBuilder.cpp
+++ b/tests/test_ASTNodeDataTypeBuilder.cpp
@@ -1413,11 +1413,14 @@ clog << "clog\n";
 
     std::string_view result = R"(
 (root:void)
- +-(language::cout_kw:void)
+ +-(language::shift_left_op:ostream)
+ |   +-(language::name:cout:ostream)
  |   `-(language::literal:"cout\n":string)
- +-(language::cerr_kw:void)
+ +-(language::shift_left_op:ostream)
+ |   +-(language::name:cerr:ostream)
  |   `-(language::literal:"cerr\n":string)
- `-(language::clog_kw:void)
+ `-(language::shift_left_op:ostream)
+     +-(language::name:clog:ostream)
      `-(language::literal:"clog\n":string)
 )";
 
@@ -1445,8 +1448,10 @@ for (let i : N, i=0; i<3; ++i){
      |   `-(language::integer:3:Z)
      +-(language::unary_plusplus:N)
      |   `-(language::name:i:N)
-     `-(language::cout_kw:void)
-         +-(language::name:i:N)
+     `-(language::shift_left_op:ostream)
+         +-(language::shift_left_op:ostream)
+         |   +-(language::name:cout:ostream)
+         |   `-(language::name:i:N)
          `-(language::literal:"\n":string)
 )";
 
diff --git a/tests/test_ASTNodeExpressionBuilder.cpp b/tests/test_ASTNodeExpressionBuilder.cpp
index cb6fdbfef..25a3d3a93 100644
--- a/tests/test_ASTNodeExpressionBuilder.cpp
+++ b/tests/test_ASTNodeExpressionBuilder.cpp
@@ -684,7 +684,7 @@ cout;
 
       std::string result = R"(
 (root:ASTNodeListProcessor)
- `-(language::cout_kw:OStreamProcessor)
+ `-(language::name:cout:NameProcessor)
 )";
 
       CHECK_AST(data, result);
@@ -698,7 +698,7 @@ cerr;
 
       std::string result = R"(
 (root:ASTNodeListProcessor)
- `-(language::cerr_kw:OStreamProcessor)
+ `-(language::name:cerr:NameProcessor)
 )";
 
       CHECK_AST(data, result);
@@ -712,7 +712,7 @@ clog;
 
       std::string result = R"(
 (root:ASTNodeListProcessor)
- `-(language::clog_kw:OStreamProcessor)
+ `-(language::name:clog:NameProcessor)
 )";
 
       CHECK_AST(data, result);
diff --git a/tests/test_ASTNodeTypeCleaner.cpp b/tests/test_ASTNodeTypeCleaner.cpp
index e1abf88d6..d78d4ce17 100644
--- a/tests/test_ASTNodeTypeCleaner.cpp
+++ b/tests/test_ASTNodeTypeCleaner.cpp
@@ -42,9 +42,12 @@ cout << "two=" << 2 << "\n";
 
     std::string_view result = R"(
 (root)
- `-(language::cout_kw)
-     +-(language::literal:"two=")
-     +-(language::integer:2)
+ `-(language::shift_left_op)
+     +-(language::shift_left_op)
+     |   +-(language::shift_left_op)
+     |   |   +-(language::name:cout)
+     |   |   `-(language::literal:"two=")
+     |   `-(language::integer:2)
      `-(language::literal:"\n")
 )";
 
diff --git a/tests/test_OStreamProcessor.cpp b/tests/test_OStreamProcessor.cpp
deleted file mode 100644
index 22213d64b..000000000
--- a/tests/test_OStreamProcessor.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-#include <catch2/catch_test_macros.hpp>
-#include <catch2/matchers/catch_matchers_all.hpp>
-
-#include <language/ast/ASTBuilder.hpp>
-#include <language/ast/ASTNodeDataTypeBuilder.hpp>
-#include <language/ast/ASTNodeDeclarationToAffectationConverter.hpp>
-#include <language/ast/ASTNodeExpressionBuilder.hpp>
-#include <language/ast/ASTNodeTypeCleaner.hpp>
-#include <language/ast/ASTSymbolTableBuilder.hpp>
-#include <language/node_processor/OStreamProcessor.hpp>
-#include <utils/Demangle.hpp>
-
-#include <pegtl/string_input.hpp>
-
-#include <sstream>
-
-void
-_replaceOStream(ASTNode& node, std::ostringstream& sout)
-{
-  if (node.is_type<language::cout_kw>() or node.is_type<language::cerr_kw>()) {
-    node.m_node_processor = std::make_unique<OStreamProcessor>(node, sout);
-  } else {
-    for (auto& child_node : node.children) {
-      _replaceOStream(*child_node, sout);
-    }
-  }
-}
-
-#define CHECK_OSTREAM_EXPRESSION_RESULT(data, expected_value)  \
-  {                                                            \
-    TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; \
-    auto ast = ASTBuilder::build(input);                       \
-                                                               \
-    ASTSymbolTableBuilder{*ast};                               \
-    ASTNodeDataTypeBuilder{*ast};                              \
-                                                               \
-    ASTNodeDeclarationToAffectationConverter{*ast};            \
-    ASTNodeTypeCleaner<language::var_declaration>{*ast};       \
-                                                               \
-    ASTNodeExpressionBuilder{*ast};                            \
-    ExecutionPolicy exec_policy;                               \
-                                                               \
-    std::ostringstream sout;                                   \
-    _replaceOStream(*ast, sout);                               \
-                                                               \
-    ast->execute(exec_policy);                                 \
-                                                               \
-    REQUIRE(sout.str() == expected_value);                     \
-  }
-
-#define CHECK_OSTREAM_EXPRESSION_THROWS(data, expected_error)                                   \
-  {                                                                                             \
-    static_assert(std::is_same_v<std::decay_t<decltype(data)>, std::string_view>);              \
-    static_assert((std::is_same_v<std::decay_t<decltype(expected_error)>, std::string_view>) or \
-                  (std::is_same_v<std::decay_t<decltype(expected_error)>, std::string>));       \
-                                                                                                \
-    TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};                                  \
-    auto ast = ASTBuilder::build(input);                                                        \
-                                                                                                \
-    ASTSymbolTableBuilder{*ast};                                                                \
-    ASTNodeDataTypeBuilder{*ast};                                                               \
-                                                                                                \
-    ASTNodeDeclarationToAffectationConverter{*ast};                                             \
-    ASTNodeTypeCleaner<language::var_declaration>{*ast};                                        \
-    ASTNodeTypeCleaner<language::fct_declaration>{*ast};                                        \
-                                                                                                \
-    REQUIRE_THROWS_WITH(ASTNodeExpressionBuilder{*ast}, expected_error);                        \
-  }
-
-// clazy:excludeall=non-pod-global-static
-
-TEST_CASE("OStreamProcessor", "[language]")
-{
-  SECTION("cout")
-  {
-    CHECK_OSTREAM_EXPRESSION_RESULT(R"(cout << 2;)", "2");
-    CHECK_OSTREAM_EXPRESSION_RESULT(R"(cout << true;)", "true");
-    CHECK_OSTREAM_EXPRESSION_RESULT(R"(cout << false;)", "false");
-    CHECK_OSTREAM_EXPRESSION_RESULT(R"(cout << "x=" << 2 << "\n";)", "x=2\n");
-  }
-
-  SECTION("cerr")
-  {
-    CHECK_OSTREAM_EXPRESSION_RESULT(R"(cerr << 2;)", "2");
-    CHECK_OSTREAM_EXPRESSION_RESULT(R"(cerr << true;)", "true");
-    CHECK_OSTREAM_EXPRESSION_RESULT(R"(cerr << false;)", "false");
-    CHECK_OSTREAM_EXPRESSION_RESULT(R"(cerr << "x=" << 2 << "\n";)", "x=2\n");
-  }
-
-  SECTION("runtime error")
-  {
-    std::string_view data_type = R"(
-let f : R -> R, x -> 2;
-cerr << "f=" << f << "\n";
-)";
-
-    std::string error_msg = "invalid argument, cannot print a 'function'";
-    CHECK_OSTREAM_EXPRESSION_THROWS(data_type, error_msg);
-  }
-}
-- 
GitLab