From a04286a83e3320caf90701cd71190248f3aa82f6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com>
Date: Tue, 10 Nov 2020 14:38:07 +0100
Subject: [PATCH] Add the CoreModule

This is a special module that is *always* imported

For this first version of the module we have three functions:

getAvailableModules() -> string
which returns the list of available pugs modules

getPugsVersion() -> string
getPugsBuildInfo() -> string
which are moved from the 'utils' module

There is no need to use `import core` in the preamble of a pugs script
file.
---
 src/language/PugsParser.cpp               |  4 +-
 src/language/ast/ASTModulesImporter.cpp   |  2 +
 src/language/ast/ASTModulesImporter.hpp   |  6 +++
 src/language/modules/CMakeLists.txt       |  1 +
 src/language/modules/CoreModule.cpp       | 33 ++++++++++++++++
 src/language/modules/CoreModule.hpp       | 19 +++++++++
 src/language/modules/ModuleRepository.cpp | 47 +++++++++++++++++++----
 src/language/modules/ModuleRepository.hpp |  6 ++-
 src/language/modules/UtilsModule.cpp      | 15 +-------
 src/language/utils/ASTExecutionInfo.cpp   |  3 +-
 src/language/utils/ASTExecutionInfo.hpp   | 11 +++++-
 11 files changed, 122 insertions(+), 25 deletions(-)
 create mode 100644 src/language/modules/CoreModule.cpp
 create mode 100644 src/language/modules/CoreModule.hpp

diff --git a/src/language/PugsParser.cpp b/src/language/PugsParser.cpp
index 0294206b7..ed00e3032 100644
--- a/src/language/PugsParser.cpp
+++ b/src/language/PugsParser.cpp
@@ -50,7 +50,7 @@ parser(const std::string& filename)
   auto parse_and_execute = [](auto& input) {
     std::unique_ptr<ASTNode> root_node = ASTBuilder::build(input);
 
-    ASTModulesImporter{*root_node};
+    ASTModulesImporter module_importer{*root_node};
     ASTNodeTypeCleaner<language::import_instruction>{*root_node};
 
     ASTSymbolTableBuilder{*root_node};
@@ -75,7 +75,7 @@ parser(const std::string& filename)
     std::cout << "-------------------------------------------------------\n";
     std::cout << rang::style::bold << "Executing AST..." << rang::style::reset << '\n';
 
-    ASTExecutionInfo execution_info{*root_node};
+    ASTExecutionInfo execution_info{*root_node, module_importer.moduleRepository()};
 
     ExecutionPolicy exec_all;
     root_node->execute(exec_all);
diff --git a/src/language/ast/ASTModulesImporter.cpp b/src/language/ast/ASTModulesImporter.cpp
index 197f9dcdd..5ef3b5804 100644
--- a/src/language/ast/ASTModulesImporter.cpp
+++ b/src/language/ast/ASTModulesImporter.cpp
@@ -37,6 +37,8 @@ ASTModulesImporter::_importAllModules(ASTNode& node)
 ASTModulesImporter::ASTModulesImporter(ASTNode& root_node) : m_symbol_table{*root_node.m_symbol_table}
 {
   Assert(root_node.is_root());
+  m_module_repository.populateCoreSymbolTable(root_node, m_symbol_table);
+
   this->_importAllModules(root_node);
   std::cout << " - loaded modules\n";
 }
diff --git a/src/language/ast/ASTModulesImporter.hpp b/src/language/ast/ASTModulesImporter.hpp
index 2b8c081fd..566155c4a 100644
--- a/src/language/ast/ASTModulesImporter.hpp
+++ b/src/language/ast/ASTModulesImporter.hpp
@@ -20,6 +20,12 @@ class ASTModulesImporter
   void _importAllModules(ASTNode& node);
 
  public:
+  const ModuleRepository&
+  moduleRepository() const
+  {
+    return m_module_repository;
+  }
+
   ASTModulesImporter(ASTNode& root_node);
 
   ASTModulesImporter(const ASTModulesImporter&) = delete;
diff --git a/src/language/modules/CMakeLists.txt b/src/language/modules/CMakeLists.txt
index cd4c99bd8..020727478 100644
--- a/src/language/modules/CMakeLists.txt
+++ b/src/language/modules/CMakeLists.txt
@@ -2,6 +2,7 @@
 
 add_library(PugsLanguageModules
   BuiltinModule.cpp
+  CoreModule.cpp
   LinearSolverModule.cpp
   MathModule.cpp
   MeshModule.cpp
diff --git a/src/language/modules/CoreModule.cpp b/src/language/modules/CoreModule.cpp
new file mode 100644
index 000000000..c9a214306
--- /dev/null
+++ b/src/language/modules/CoreModule.cpp
@@ -0,0 +1,33 @@
+#include <language/modules/CoreModule.hpp>
+
+#include <language/modules/CoreModule.hpp>
+#include <language/modules/ModuleRepository.hpp>
+#include <language/utils/ASTExecutionInfo.hpp>
+#include <language/utils/BuiltinFunctionEmbedder.hpp>
+#include <utils/PugsUtils.hpp>
+
+CoreModule::CoreModule()
+{
+  this->_addBuiltinFunction("getPugsVersion", std::make_shared<BuiltinFunctionEmbedder<std::string(void)>>(
+
+                                                []() -> std::string { return pugsVersion(); }
+
+                                                ));
+
+  this->_addBuiltinFunction("getPugsBuildInfo", std::make_shared<BuiltinFunctionEmbedder<std::string(void)>>(
+
+                                                  []() -> std::string { return pugsBuildInfo(); }
+
+                                                  ));
+
+  this->_addBuiltinFunction("getAvailableModules", std::make_shared<BuiltinFunctionEmbedder<std::string()>>(
+
+                                                     []() -> std::string {
+                                                       const ModuleRepository& repository =
+                                                         ASTExecutionInfo::current().moduleRepository();
+
+                                                       return repository.getAvailableModules();
+                                                     }
+
+                                                     ));
+}
diff --git a/src/language/modules/CoreModule.hpp b/src/language/modules/CoreModule.hpp
new file mode 100644
index 000000000..963719be2
--- /dev/null
+++ b/src/language/modules/CoreModule.hpp
@@ -0,0 +1,19 @@
+#ifndef CORE_MODULE_HPP
+#define CORE_MODULE_HPP
+
+#include <language/modules/BuiltinModule.hpp>
+
+class CoreModule : public BuiltinModule
+{
+ public:
+  std::string_view
+  name() const final
+  {
+    return "core";
+  }
+
+  CoreModule();
+  ~CoreModule() = default;
+};
+
+#endif   // CORE_MODULE_HPP
diff --git a/src/language/modules/ModuleRepository.cpp b/src/language/modules/ModuleRepository.cpp
index 83d44b23e..1fff5dda8 100644
--- a/src/language/modules/ModuleRepository.cpp
+++ b/src/language/modules/ModuleRepository.cpp
@@ -1,6 +1,7 @@
 #include <language/modules/ModuleRepository.hpp>
 
 #include <language/ast/ASTNode.hpp>
+#include <language/modules/CoreModule.hpp>
 #include <language/modules/LinearSolverModule.hpp>
 #include <language/modules/MathModule.hpp>
 #include <language/modules/MeshModule.hpp>
@@ -21,6 +22,7 @@ ModuleRepository::_subscribe(std::unique_ptr<IModule> m)
 
 ModuleRepository::ModuleRepository()
 {
+  this->_subscribe(std::make_unique<CoreModule>());
   this->_subscribe(std::make_unique<LinearSolverModule>());
   this->_subscribe(std::make_unique<MathModule>());
   this->_subscribe(std::make_unique<MeshModule>());
@@ -31,22 +33,21 @@ ModuleRepository::ModuleRepository()
 
 template <typename NameEmbedderMapT, typename EmbedderTableT>
 void
-ModuleRepository::_populateEmbedderTableT(const ASTNode& module_name_node,
+ModuleRepository::_populateEmbedderTableT(const ASTNode& module_node,
+                                          const std::string& module_name,
                                           const NameEmbedderMapT& name_embedder_map,
                                           const ASTNodeDataType& data_type,
                                           SymbolTable& symbol_table,
                                           EmbedderTableT& embedder_table)
 {
-  const std::string& module_name = module_name_node.string();
-
   for (auto [symbol_name, embedded] : name_embedder_map) {
-    auto [i_symbol, success] = symbol_table.add(symbol_name, module_name_node.begin());
+    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_name_node.begin());
+      throw ParseError(error_message.str(), module_node.begin());
     }
 
     i_symbol->attributes().setDataType(data_type);
@@ -66,14 +67,46 @@ ModuleRepository::populateSymbolTable(const ASTNode& module_name_node, SymbolTab
   if (i_module != m_module_set.end()) {
     const IModule& populating_module = *i_module->second;
 
-    this->_populateEmbedderTableT(module_name_node, populating_module.getNameBuiltinFunctionMap(),
+    this->_populateEmbedderTableT(module_name_node, module_name, populating_module.getNameBuiltinFunctionMap(),
                                   ASTNodeDataType::build<ASTNodeDataType::builtin_function_t>(), symbol_table,
                                   symbol_table.builtinFunctionEmbedderTable());
 
-    this->_populateEmbedderTableT(module_name_node, populating_module.getNameTypeMap(),
+    this->_populateEmbedderTableT(module_name_node, module_name, populating_module.getNameTypeMap(),
                                   ASTNodeDataType::build<ASTNodeDataType::type_name_id_t>(), symbol_table,
                                   symbol_table.typeEmbedderTable());
   } else {
     throw ParseError(std::string{"could not find module "} + module_name, std::vector{module_name_node.begin()});
   }
 }
+
+void
+ModuleRepository::populateCoreSymbolTable(const ASTNode& root_node, SymbolTable& symbol_table)
+{
+  const std::string& module_name = "core";
+
+  auto i_module = m_module_set.find(module_name);
+  if (i_module != m_module_set.end()) {
+    const IModule& populating_module = *i_module->second;
+
+    this->_populateEmbedderTableT(root_node, module_name, populating_module.getNameBuiltinFunctionMap(),
+                                  ASTNodeDataType::build<ASTNodeDataType::builtin_function_t>(), symbol_table,
+                                  symbol_table.builtinFunctionEmbedderTable());
+
+    this->_populateEmbedderTableT(root_node, module_name, populating_module.getNameTypeMap(),
+                                  ASTNodeDataType::build<ASTNodeDataType::type_name_id_t>(), symbol_table,
+                                  symbol_table.typeEmbedderTable());
+  } else {
+    throw ParseError(std::string{"could not find module "} + module_name, std::vector{root_node.begin()});
+  }
+}
+
+std::string
+ModuleRepository::getAvailableModules() const
+{
+  std::stringstream os;
+  os << rang::fgB::yellow << "Available modules" << rang::style::reset << '\n';
+  for (auto& [name, i_module] : m_module_set) {
+    os << "  " << rang::fg::green << name << rang::style::reset << '\n';
+  }
+  return os.str();
+}
diff --git a/src/language/modules/ModuleRepository.hpp b/src/language/modules/ModuleRepository.hpp
index f928a1552..b6983eb54 100644
--- a/src/language/modules/ModuleRepository.hpp
+++ b/src/language/modules/ModuleRepository.hpp
@@ -19,7 +19,8 @@ class ModuleRepository
   void _subscribe(std::unique_ptr<IModule> a);
 
   template <typename NameEmbedderMapT, typename EmbedderTableT>
-  void _populateEmbedderTableT(const ASTNode& module_name_node,
+  void _populateEmbedderTableT(const ASTNode& module_node,
+                               const std::string& module_name,
                                const NameEmbedderMapT& name_embedder_map,
                                const ASTNodeDataType& data_type,
                                SymbolTable& symbol_table,
@@ -27,6 +28,9 @@ class ModuleRepository
 
  public:
   void populateSymbolTable(const ASTNode& module_name_node, SymbolTable& symbol_table);
+  void populateCoreSymbolTable(const ASTNode& root_node, SymbolTable& symbol_table);
+
+  std::string getAvailableModules() const;
 
   const ModuleRepository& operator=(const ModuleRepository&) = delete;
   const ModuleRepository& operator=(ModuleRepository&&) = delete;
diff --git a/src/language/modules/UtilsModule.cpp b/src/language/modules/UtilsModule.cpp
index 90845af9a..8b2271158 100644
--- a/src/language/modules/UtilsModule.cpp
+++ b/src/language/modules/UtilsModule.cpp
@@ -5,22 +5,11 @@
 #include <language/utils/ASTPrinter.hpp>
 #include <language/utils/BuiltinFunctionEmbedder.hpp>
 #include <language/utils/SymbolTable.hpp>
-#include <utils/PugsUtils.hpp>
+
+#include <fstream>
 
 UtilsModule::UtilsModule()
 {
-  this->_addBuiltinFunction("getPugsVersion", std::make_shared<BuiltinFunctionEmbedder<std::string(void)>>(
-
-                                                []() -> std::string { return pugsVersion(); }
-
-                                                ));
-
-  this->_addBuiltinFunction("getPugsBuildInfo", std::make_shared<BuiltinFunctionEmbedder<std::string(void)>>(
-
-                                                  []() -> std::string { return pugsBuildInfo(); }
-
-                                                  ));
-
   this->_addBuiltinFunction("getAST", std::make_shared<BuiltinFunctionEmbedder<std::string(void)>>(
 
                                         []() -> std::string {
diff --git a/src/language/utils/ASTExecutionInfo.cpp b/src/language/utils/ASTExecutionInfo.cpp
index c0327d501..6015b6a8f 100644
--- a/src/language/utils/ASTExecutionInfo.cpp
+++ b/src/language/utils/ASTExecutionInfo.cpp
@@ -4,7 +4,8 @@
 
 const ASTExecutionInfo* ASTExecutionInfo::m_current_execution_info = nullptr;
 
-ASTExecutionInfo::ASTExecutionInfo(const ASTNode& root_node) : m_root_node(root_node)
+ASTExecutionInfo::ASTExecutionInfo(const ASTNode& root_node, const ModuleRepository& module_repository)
+  : m_root_node{root_node}, m_module_repository{module_repository}
 {
   Assert(m_current_execution_info == nullptr, "Can only define one ASTExecutionInfo");
 
diff --git a/src/language/utils/ASTExecutionInfo.hpp b/src/language/utils/ASTExecutionInfo.hpp
index 90baa8a6f..96fc874ce 100644
--- a/src/language/utils/ASTExecutionInfo.hpp
+++ b/src/language/utils/ASTExecutionInfo.hpp
@@ -3,6 +3,7 @@
 
 #include <string>
 
+class ModuleRepository;
 class ASTNode;
 class ASTExecutionInfo
 {
@@ -11,10 +12,12 @@ class ASTExecutionInfo
 
   const ASTNode& m_root_node;
 
+  const ModuleRepository& m_module_repository;
+
   // The only place where the ASTExecutionInfo can be built
   friend void parser(const std::string& filename);
 
-  ASTExecutionInfo(const ASTNode& root_node);
+  ASTExecutionInfo(const ASTNode& root_node, const ModuleRepository& module_repository);
 
  public:
   const ASTNode&
@@ -23,6 +26,12 @@ class ASTExecutionInfo
     return m_root_node;
   }
 
+  const ModuleRepository&
+  moduleRepository() const
+  {
+    return m_module_repository;
+  }
+
   static const ASTExecutionInfo& current();
 
   ASTExecutionInfo() = delete;
-- 
GitLab