diff --git a/src/language/BuiltinModule.cpp b/src/language/BuiltinModule.cpp
index 2d2b1b556642353c53b93f7f54b75d96a074643f..88967ad750e0a37dba9fdfb2f535a57410405066 100644
--- a/src/language/BuiltinModule.cpp
+++ b/src/language/BuiltinModule.cpp
@@ -1,6 +1,7 @@
 #include <BuiltinModule.hpp>
 
 #include <CFunctionEmbedder.hpp>
+#include <TypeDescriptor.hpp>
 
 #include <iostream>
 
@@ -10,7 +11,19 @@ BuiltinModule::_addFunction(const std::string& name, std::shared_ptr<ICFunctionE
   auto [i_function, success] = m_name_cfunction_map.insert(std::make_pair(name, c_function_embedder));
   // LCOV_EXCL_START
   if (not success) {
-    std::cerr << "function " << name << " cannot be add!\n";
+    std::cerr << "function " << name << " cannot be added!\n";
+    std::exit(1);
+  }
+  // LCOV_EXCL_STOP
+}
+
+void
+BuiltinModule::_addTypeDescriptor(std::shared_ptr<TypeDescriptor> type_descriptor)
+{
+  auto [i_type, success] = m_type_descriptor_map.insert(std::make_pair(type_descriptor->name(), type_descriptor));
+  // LCOV_EXCL_START
+  if (not success) {
+    std::cerr << "type '" << type_descriptor->name() << "' cannot be added!\n";
     std::exit(1);
   }
   // LCOV_EXCL_STOP
diff --git a/src/language/BuiltinModule.hpp b/src/language/BuiltinModule.hpp
index 2a076f511022c7aed15a25684865d9c7210b0b94..67adb09e1a79aa1589d12fc979ab431bd87909ec 100644
--- a/src/language/BuiltinModule.hpp
+++ b/src/language/BuiltinModule.hpp
@@ -1,18 +1,21 @@
 #ifndef BUILTIN_MODULE_HPP
 #define BUILTIN_MODULE_HPP
 
-#include <PugsMacros.hpp>
-
 #include <IModule.hpp>
 
 class ICFunctionEmbedder;
+class TypeDescriptor;
+
 class BuiltinModule : public IModule
 {
  protected:
   NameCFunctionMap m_name_cfunction_map;
+  TypeDescriptorMap m_type_descriptor_map;
 
   void _addFunction(const std::string& name, std::shared_ptr<ICFunctionEmbedder> c_function_embedder);
 
+  void _addTypeDescriptor(std::shared_ptr<TypeDescriptor> type_descriptor);
+
  public:
   const NameCFunctionMap&
   getNameCFunctionsMap() const final
@@ -20,6 +23,12 @@ class BuiltinModule : public IModule
     return m_name_cfunction_map;
   }
 
+  const TypeDescriptorMap&
+  getTypeDescriptorMap() const final
+  {
+    return m_type_descriptor_map;
+  }
+
   BuiltinModule() = default;
 
   ~BuiltinModule() = default;
diff --git a/src/language/CMakeLists.txt b/src/language/CMakeLists.txt
index e901374f5167cdaa43750890f6e31d40fe2de91e..14f349b46e2a2956eff77bd18cd5ae338b0ad3c7 100644
--- a/src/language/CMakeLists.txt
+++ b/src/language/CMakeLists.txt
@@ -33,6 +33,7 @@ add_library(
   ASTSymbolInitializationChecker.cpp
   BuiltinModule.cpp
   MathModule.cpp
+  MeshModule.cpp
   ModuleRepository.cpp
   PugsParser.cpp)
 
diff --git a/src/language/IModule.hpp b/src/language/IModule.hpp
index 01afaca3c8872a2ae0b8db6c2d7fac7531049a44..459a914b9bd332b4bbf9744a700fe659f5b6629d 100644
--- a/src/language/IModule.hpp
+++ b/src/language/IModule.hpp
@@ -7,17 +7,20 @@
 #include <unordered_map>
 
 class ICFunctionEmbedder;
+class TypeDescriptor;
 
 class IModule
 {
  public:
-  using NameCFunctionMap = std::unordered_map<std::string, std::shared_ptr<ICFunctionEmbedder>>;
+  using NameCFunctionMap  = std::unordered_map<std::string, std::shared_ptr<ICFunctionEmbedder>>;
+  using TypeDescriptorMap = std::unordered_map<std::string, std::shared_ptr<TypeDescriptor>>;
 
   IModule()          = default;
   IModule(IModule&&) = default;
   IModule& operator=(IModule&&) = default;
 
-  virtual const NameCFunctionMap& getNameCFunctionsMap() const = 0;
+  virtual const NameCFunctionMap& getNameCFunctionsMap() const  = 0;
+  virtual const TypeDescriptorMap& getTypeDescriptorMap() const = 0;
 
   virtual std::string_view name() const = 0;
 
diff --git a/src/language/MeshModule.cpp b/src/language/MeshModule.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7edad9c355ad93668ab3fbb0bdb36436cc81978b
--- /dev/null
+++ b/src/language/MeshModule.cpp
@@ -0,0 +1,9 @@
+#include <MeshModule.hpp>
+#include <TypeDescriptor.hpp>
+
+#include <memory>
+
+MeshModule::MeshModule()
+{
+  this->_addTypeDescriptor(std::make_shared<TypeDescriptor>(std::string{"mesh"}));
+}
diff --git a/src/language/MeshModule.hpp b/src/language/MeshModule.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..5310e54ab979e65a9d85b71432f9993a6db9e547
--- /dev/null
+++ b/src/language/MeshModule.hpp
@@ -0,0 +1,22 @@
+#ifndef MESH_MODULE_HPP
+#define MESH_MODULE_HPP
+
+#include <PugsMacros.hpp>
+
+#include <BuiltinModule.hpp>
+
+class MeshModule : public BuiltinModule
+{
+ public:
+  std::string_view
+  name() const final
+  {
+    return "mesh";
+  }
+
+  MeshModule();
+
+  ~MeshModule() = default;
+};
+
+#endif   // MESH_MODULE_HPP
diff --git a/src/language/ModuleRepository.cpp b/src/language/ModuleRepository.cpp
index 1b3fee7d3878f66ed7c5120d542c107e0b2983d7..f6b0b0d8fb667597d0400f4d7e44b978daa19edc 100644
--- a/src/language/ModuleRepository.cpp
+++ b/src/language/ModuleRepository.cpp
@@ -4,10 +4,11 @@
 
 #include <ASTNode.hpp>
 
+#include <CFunctionEmbedder.hpp>
 #include <SymbolTable.hpp>
 
-#include <CFunctionEmbedder.hpp>
 #include <MathModule.hpp>
+#include <MeshModule.hpp>
 
 void
 ModuleRepository::_subscribe(std::unique_ptr<IModule> m)
@@ -19,35 +20,72 @@ ModuleRepository::_subscribe(std::unique_ptr<IModule> m)
 ModuleRepository::ModuleRepository()
 {
   this->_subscribe(std::make_unique<MathModule>());
+  this->_subscribe(std::make_unique<MeshModule>());
 }
 
 void
-ModuleRepository::populateSymbolTable(const ASTNode& module_name_node, SymbolTable& symbol_table)
+ModuleRepository::_populateCFunctionsEmbedderTable(const IModule& populating_module,
+                                                   const ASTNode& module_name_node,
+                                                   SymbolTable& symbol_table)
 {
-  std::string module_name = module_name_node.string();
+  const std::string& module_name = module_name_node.string();
 
-  auto i_module = m_module_set.find(module_name);
-  if (i_module != m_module_set.end()) {
-    CFunctionEmbedderTable& c_function_embedder_table = symbol_table.cFunctionEbedderTable();
+  CFunctionEmbedderTable& c_function_embedder_table = symbol_table.cFunctionEbedderTable();
+
+  for (auto [symbol_name, c_function] : populating_module.getNameCFunctionsMap()) {
+    auto [i_symbol, success] = symbol_table.add(symbol_name, module_name_node.begin());
 
-    const IModule& m = *i_module->second;
+    if (not success) {
+      std::ostringstream error_message;
+      error_message << "importing module '" << module_name << "', cannot add symbol '" << symbol_name
+                    << "', it is already defined!";
+      throw parse_error(error_message.str(), module_name_node.begin());
+    }
+
+    i_symbol->attributes().setDataType(ASTNodeDataType::c_function_t);
+    i_symbol->attributes().setIsInitialized();
+    i_symbol->attributes().value() = c_function_embedder_table.size();
+
+    c_function_embedder_table.add(c_function);
+  }
+}
 
-    for (auto [symbol_name, c_function] : m.getNameCFunctionsMap()) {
-      auto [i_symbol, success] = symbol_table.add(symbol_name, module_name_node.begin());
+void
+ModuleRepository::_populateTypeDescriptor(const IModule& populating_module,
+                                          const ASTNode& module_name_node,
+                                          SymbolTable& symbol_table)
+{
+  const std::string& module_name = module_name_node.string();
 
-      if (not success) {
-        std::ostringstream error_message;
-        error_message << "importing module '" << module_name << "', cannot add symbol '" << symbol_name
-                      << "', it is already defined!";
-        throw parse_error(error_message.str(), module_name_node.begin());
-      }
+  TypeTable& type_table = symbol_table.typeTable();
 
-      i_symbol->attributes().setDataType(ASTNodeDataType::c_function_t);
-      i_symbol->attributes().setIsInitialized();
-      i_symbol->attributes().value() = c_function_embedder_table.size();
+  for (auto [symbol_name, type_descriptor] : populating_module.getTypeDescriptorMap()) {
+    auto [i_symbol, success] = symbol_table.add(symbol_name, module_name_node.begin());
 
-      c_function_embedder_table.add(c_function);
+    if (not success) {
+      std::ostringstream error_message;
+      error_message << "importing module '" << module_name << "', cannot add symbol '" << symbol_name
+                    << "', it is already defined!";
+      throw parse_error(error_message.str(), module_name_node.begin());
     }
+
+    i_symbol->attributes().setDataType(ASTNodeDataType::type_id_t);
+    i_symbol->attributes().setIsInitialized();
+    i_symbol->attributes().value() = type_table.size();
+
+    type_table.add(type_descriptor);
+  }
+}
+
+void
+ModuleRepository::populateSymbolTable(const ASTNode& module_name_node, SymbolTable& symbol_table)
+{
+  const std::string& module_name = module_name_node.string();
+
+  auto i_module = m_module_set.find(module_name);
+  if (i_module != m_module_set.end()) {
+    this->_populateCFunctionsEmbedderTable(*i_module->second, module_name_node, symbol_table);
+    this->_populateTypeDescriptor(*i_module->second, module_name_node, symbol_table);
   } else {
     throw parse_error(std::string{"could not find module "} + module_name, std::vector{module_name_node.begin()});
   }
diff --git a/src/language/ModuleRepository.hpp b/src/language/ModuleRepository.hpp
index 467ffdb091c4691fb449f7a6825885521c54d89d..e71a56587dc3a18f1a3d3c62471cdb43b4efe181 100644
--- a/src/language/ModuleRepository.hpp
+++ b/src/language/ModuleRepository.hpp
@@ -17,8 +17,16 @@ class ModuleRepository
 
   void _subscribe(std::unique_ptr<IModule> a);
 
+  void _populateCFunctionsEmbedderTable(const IModule& populating_module,
+                                        const ASTNode& module_name_node,
+                                        SymbolTable& symbol_table);
+
+  void _populateTypeDescriptor(const IModule& populating_module,
+                               const ASTNode& module_name_node,
+                               SymbolTable& symbol_table);
+
  public:
-  void populateSymbolTable(const ASTNode& node_module_name, SymbolTable& symbol_table);
+  void populateSymbolTable(const ASTNode& module_name_node, SymbolTable& symbol_table);
 
   const ModuleRepository& operator=(const ModuleRepository&) = delete;
   const ModuleRepository& operator=(ModuleRepository&&) = delete;
diff --git a/src/language/SymbolTable.hpp b/src/language/SymbolTable.hpp
index d6a735151681bacc622eeb0c38912f9a75773c17..4dbbc5b2ca0965e89cf949ceb7cd14965d2873ea 100644
--- a/src/language/SymbolTable.hpp
+++ b/src/language/SymbolTable.hpp
@@ -13,6 +13,8 @@
 #include <CFunctionEmbedderTable.hpp>
 #include <FunctionTable.hpp>
 
+#include <TypeTable.hpp>
+
 class SymbolTable
 {
  public:
@@ -87,6 +89,8 @@ class SymbolTable
     {
       if (attributes.m_data_type == ASTNodeDataType::function_t) {
         os << "function_id:";
+      } else if (attributes.m_data_type == ASTNodeDataType::type_id_t) {
+        os << "type_id:";
       }
       std::visit(
         [&](const auto& value) {
@@ -196,6 +200,8 @@ class SymbolTable
   std::shared_ptr<FunctionTable> m_function_table;
   std::shared_ptr<CFunctionEmbedderTable> m_c_function_embedder_table;
 
+  std::shared_ptr<TypeTable> m_type_table;
+
  public:
   bool
   hasContext() const
@@ -238,6 +244,20 @@ class SymbolTable
     return *m_c_function_embedder_table;
   }
 
+  TypeTable&
+  typeTable()
+  {
+    Assert(m_type_table);
+    return *m_type_table;
+  }
+
+  const TypeTable&
+  typeTable() const
+  {
+    Assert(m_type_table);
+    return *m_type_table;
+  }
+
   friend std::ostream&
   operator<<(std::ostream& os, const SymbolTable& symbol_table)
   {
@@ -299,7 +319,8 @@ class SymbolTable
     : m_parent_table{parent_table},
       m_context{context},
       m_function_table{parent_table->m_function_table},
-      m_c_function_embedder_table{parent_table->m_c_function_embedder_table}
+      m_c_function_embedder_table{parent_table->m_c_function_embedder_table},
+      m_type_table{parent_table->m_type_table}
   {
     ;
   }
@@ -308,7 +329,8 @@ class SymbolTable
     : m_parent_table{parent_table},
       m_context{parent_table->m_context},
       m_function_table{parent_table->m_function_table},
-      m_c_function_embedder_table{parent_table->m_c_function_embedder_table}
+      m_c_function_embedder_table{parent_table->m_c_function_embedder_table},
+      m_type_table{parent_table->m_type_table}
   {
     ;
   }
@@ -317,7 +339,8 @@ class SymbolTable
     : m_parent_table{nullptr},
       m_context{nullptr},
       m_function_table{std::make_shared<FunctionTable>()},
-      m_c_function_embedder_table{std::make_shared<CFunctionEmbedderTable>()}
+      m_c_function_embedder_table{std::make_shared<CFunctionEmbedderTable>()},
+      m_type_table{std::make_shared<TypeTable>()}
   {
     ;
   }
diff --git a/src/language/TypeDescriptor.hpp b/src/language/TypeDescriptor.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9da24e0d42096b93998867923ac26196bd694eb4
--- /dev/null
+++ b/src/language/TypeDescriptor.hpp
@@ -0,0 +1,22 @@
+#ifndef TYPE_DESCRIPTOR_HPP
+#define TYPE_DESCRIPTOR_HPP
+
+#include <string>
+
+class TypeDescriptor
+{
+ private:
+  std::string m_name;
+
+ public:
+  const std::string&
+  name() const
+  {
+    return m_name;
+  }
+
+  TypeDescriptor(std::string_view name) : m_name(name) {}
+  ~TypeDescriptor() = default;
+};
+
+#endif   // TYPE_DESCRIPTOR_HPP
diff --git a/src/language/TypeTable.hpp b/src/language/TypeTable.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..059e686e1a7d644bab4c7406420eac165813a12b
--- /dev/null
+++ b/src/language/TypeTable.hpp
@@ -0,0 +1,30 @@
+#ifndef TYPE_TABLE_HPP
+#define TYPE_TABLE_HPP
+
+#include <TypeDescriptor.hpp>
+
+#include <vector>
+
+class TypeTable
+{
+ private:
+  std::vector<std::shared_ptr<TypeDescriptor>> m_type_list;
+
+ public:
+  size_t
+  size() const
+  {
+    return m_type_list.size();
+  }
+
+  void
+  add(std::shared_ptr<TypeDescriptor> type_descriptor)
+  {
+    m_type_list.emplace_back(type_descriptor);
+  }
+
+  TypeTable()  = default;
+  ~TypeTable() = default;
+};
+
+#endif   // TYPE_TABLE_HPP