summaryrefslogtreecommitdiff
path: root/tiger-compiler/src/task
diff options
context:
space:
mode:
authorMartial Simon <msimon_fr@hotmail.com>2025-09-15 01:07:58 +0200
committerMartial Simon <msimon_fr@hotmail.com>2025-09-15 01:07:58 +0200
commit967be9e750221ab2ab783f95df79bb26d290a45e (patch)
tree6802900a5e975f9f68b169f0f503f040056d6952 /tiger-compiler/src/task
add: added projectsHEADmain
Diffstat (limited to 'tiger-compiler/src/task')
-rw-r--r--tiger-compiler/src/task/argument-task.cc29
-rw-r--r--tiger-compiler/src/task/argument-task.hh58
-rw-r--r--tiger-compiler/src/task/boolean-task.cc22
-rw-r--r--tiger-compiler/src/task/boolean-task.hh28
-rw-r--r--tiger-compiler/src/task/disjunctive-task.cc32
-rw-r--r--tiger-compiler/src/task/disjunctive-task.hh27
-rw-r--r--tiger-compiler/src/task/function-task.cc22
-rw-r--r--tiger-compiler/src/task/function-task.hh31
-rw-r--r--tiger-compiler/src/task/fwd.hh22
-rw-r--r--tiger-compiler/src/task/int-task.cc55
-rw-r--r--tiger-compiler/src/task/int-task.hh33
-rw-r--r--tiger-compiler/src/task/libtask.hh107
-rw-r--r--tiger-compiler/src/task/local.am15
-rw-r--r--tiger-compiler/src/task/multiple-string-task.cc21
-rw-r--r--tiger-compiler/src/task/multiple-string-task.hh34
-rw-r--r--tiger-compiler/src/task/simple-task.cc21
-rw-r--r--tiger-compiler/src/task/simple-task.hh34
-rw-r--r--tiger-compiler/src/task/string-task.cc22
-rw-r--r--tiger-compiler/src/task/string-task.hh28
-rw-r--r--tiger-compiler/src/task/task-register.cc353
-rw-r--r--tiger-compiler/src/task/task-register.hh118
-rw-r--r--tiger-compiler/src/task/task-register.hxx15
-rw-r--r--tiger-compiler/src/task/task.cc68
-rw-r--r--tiger-compiler/src/task/task.hh103
-rw-r--r--tiger-compiler/src/task/task.hxx24
-rw-r--r--tiger-compiler/src/task/tasks.cc28
-rw-r--r--tiger-compiler/src/task/tasks.hh23
27 files changed, 1373 insertions, 0 deletions
diff --git a/tiger-compiler/src/task/argument-task.cc b/tiger-compiler/src/task/argument-task.cc
new file mode 100644
index 0000000..82194f3
--- /dev/null
+++ b/tiger-compiler/src/task/argument-task.cc
@@ -0,0 +1,29 @@
+/**
+ ** \file task/argument-task.cc
+ ** \brief ArgumentTask class implementation.
+ */
+
+#include <task/argument-task.hh>
+#include <task/task-register.hh>
+
+namespace task
+{
+ ArgumentTask::ArgumentTask(const char* name,
+ const char* module_name,
+ const char* desc,
+ const char* argname,
+ std::string deps)
+ : Task(name, module_name, desc, deps)
+ , argname_(argname)
+ {
+ // Register this task.
+ TaskRegister::instance().register_task(*this);
+ }
+
+ const std::string& ArgumentTask::arg_get() const { return arg_; }
+
+ void ArgumentTask::arg_set(const std::string& arg) const { arg_ = arg; }
+
+ const char* ArgumentTask::argname_get() const { return argname_; }
+
+} // namespace task
diff --git a/tiger-compiler/src/task/argument-task.hh b/tiger-compiler/src/task/argument-task.hh
new file mode 100644
index 0000000..775b084
--- /dev/null
+++ b/tiger-compiler/src/task/argument-task.hh
@@ -0,0 +1,58 @@
+/**
+ ** \file task/argument-task.hh
+ ** \brief Declare the task::ArgumentTask class.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <task/task.hh>
+
+namespace task
+{
+ /** \brief A `Task' expecting arguments prior to be executed.
+
+ This class embodies tasks which need an additional parameter for their
+ `execute()' method. Therefore a call to `arg_set()' must be made prior to
+ the `execute()' method, to set the value of this expected argument.
+ This class also automates the registering of its derived classes, as does its
+ sibling class `SimpleTask' for tasks without additional argument.
+ */
+ class ArgumentTask : public Task
+ {
+ /** \name Ctor & dtor.
+ ** \{ */
+ public:
+ /// Construct and register an ArgumentTask.
+ ArgumentTask(const char* name,
+ const char* module_name,
+ const char* desc,
+ const char* argname,
+ std::string deps = "");
+
+ /** \} */
+
+ /** \name Accessors.
+ ** \{ */
+ public:
+ /// Access to 'arg'.
+ const std::string& arg_get() const;
+
+ /// Access to `arg'.
+ virtual void arg_set(const std::string& arg) const;
+
+ /// Access to `argname'.
+ const char* argname_get() const;
+
+ /** \} */
+
+ protected:
+ /// ArgumentTask argument value.
+ mutable std::string arg_;
+
+ /// Argument name to be displayed when printing.
+ const char* argname_;
+ };
+
+} // namespace task
diff --git a/tiger-compiler/src/task/boolean-task.cc b/tiger-compiler/src/task/boolean-task.cc
new file mode 100644
index 0000000..9923082
--- /dev/null
+++ b/tiger-compiler/src/task/boolean-task.cc
@@ -0,0 +1,22 @@
+/**
+ ** \file task/boolean-task.cc
+ ** \brief Implementation of task::BooleanTask.
+ **
+ */
+
+#include <task/boolean-task.hh>
+
+namespace task
+{
+ BooleanTask::BooleanTask(bool& flag,
+ const char* module_name,
+ const char* desc,
+ const char* name,
+ std::string deps)
+ : SimpleTask(name, module_name, desc, deps)
+ , flag_(flag)
+ {}
+
+ void BooleanTask::execute() const { flag_ = true; }
+
+} //namespace task
diff --git a/tiger-compiler/src/task/boolean-task.hh b/tiger-compiler/src/task/boolean-task.hh
new file mode 100644
index 0000000..88298b3
--- /dev/null
+++ b/tiger-compiler/src/task/boolean-task.hh
@@ -0,0 +1,28 @@
+/**
+ ** \file task/boolean-task.hh
+ ** \brief Declare the task::BooleanTask class.
+ **
+ */
+#pragma once
+
+#include <task/simple-task.hh>
+
+namespace task
+{
+ /// A simple Task that sets a Boolean variable to true.
+ class BooleanTask : public SimpleTask
+ {
+ public:
+ BooleanTask(bool& flag,
+ const char* module_name,
+ const char* desc,
+ const char* name,
+ std::string deps);
+
+ void execute() const override;
+
+ private:
+ bool& flag_;
+ };
+
+} // namespace task
diff --git a/tiger-compiler/src/task/disjunctive-task.cc b/tiger-compiler/src/task/disjunctive-task.cc
new file mode 100644
index 0000000..07661bf
--- /dev/null
+++ b/tiger-compiler/src/task/disjunctive-task.cc
@@ -0,0 +1,32 @@
+/**
+ ** \file task/disjunctive-task.cc
+ ** \brief Implementation of task::DisjunctiveTask.
+ **
+ */
+
+#include <task/disjunctive-task.hh>
+
+namespace task
+{
+ DisjunctiveTask::DisjunctiveTask(const char* module_name,
+ const char* desc,
+ const char* name,
+ std::string deps)
+ : SimpleTask(name, module_name, desc, deps)
+ {}
+
+ void DisjunctiveTask::execute() const
+ {
+ // Nothing to do.
+ }
+
+ Task::deps_type
+ DisjunctiveTask::resolve_dependencies(tasks_list_type& active_tasks) const
+ {
+ if (!active_tasks.empty() || dependencies_.empty())
+ return Task::deps_type();
+ else
+ return Task::deps_type(1, *dependencies_.begin());
+ }
+
+} // namespace task
diff --git a/tiger-compiler/src/task/disjunctive-task.hh b/tiger-compiler/src/task/disjunctive-task.hh
new file mode 100644
index 0000000..6cb52fc
--- /dev/null
+++ b/tiger-compiler/src/task/disjunctive-task.hh
@@ -0,0 +1,27 @@
+/**
+ ** \file task/disjunctive-task.hh
+ ** \brief Declare the DisjunctiveTask class.
+ **
+ */
+#pragma once
+
+#include <task/simple-task.hh>
+
+namespace task
+{
+ /// A Task that makes sure that AT LEAST one of its dependencies is
+ /// scheduled.
+ class DisjunctiveTask : public SimpleTask
+ {
+ public:
+ DisjunctiveTask(const char* module_name,
+ const char* desc,
+ const char* name,
+ std::string deps);
+
+ void execute() const override;
+ deps_type
+ resolve_dependencies(tasks_list_type& active_tasks) const override;
+ };
+
+} //namespace task
diff --git a/tiger-compiler/src/task/function-task.cc b/tiger-compiler/src/task/function-task.cc
new file mode 100644
index 0000000..309a276
--- /dev/null
+++ b/tiger-compiler/src/task/function-task.cc
@@ -0,0 +1,22 @@
+/**
+ ** \file task/function-task.cc
+ ** \brief Implementation of task::FunctionTask.
+ **
+ */
+
+#include <task/function-task.hh>
+
+namespace task
+{
+ FunctionTask::FunctionTask(callback_type& callback,
+ const char* module_name,
+ const char* desc,
+ const char* name,
+ std::string deps)
+ : SimpleTask(name, module_name, desc, deps)
+ , execute_(callback)
+ {}
+
+ void FunctionTask::execute() const { execute_(); }
+
+} //namespace task
diff --git a/tiger-compiler/src/task/function-task.hh b/tiger-compiler/src/task/function-task.hh
new file mode 100644
index 0000000..a08b285
--- /dev/null
+++ b/tiger-compiler/src/task/function-task.hh
@@ -0,0 +1,31 @@
+/**
+ ** \file task/function-task.hh
+ ** \brief Declare the task::FunctionTask class.
+ **
+ */
+#pragma once
+
+#include <task/simple-task.hh>
+
+namespace task
+{
+ /// A simple Task that invokes a callback function.
+ class FunctionTask : public SimpleTask
+ {
+ public:
+ using callback_type = auto() -> void;
+
+ FunctionTask(callback_type& callback,
+ const char* module_name,
+ const char* desc,
+ const char* name,
+ std::string deps);
+
+ public:
+ void execute() const override;
+
+ private:
+ callback_type& execute_;
+ };
+
+} //namespace task
diff --git a/tiger-compiler/src/task/fwd.hh b/tiger-compiler/src/task/fwd.hh
new file mode 100644
index 0000000..620016f
--- /dev/null
+++ b/tiger-compiler/src/task/fwd.hh
@@ -0,0 +1,22 @@
+/**
+ ** \file task/fwd.hh
+ ** \brief Interface to the Task module.
+ **
+ */
+#pragma once
+
+namespace task
+{
+ // task.hh
+ class Task;
+
+ // argument-task.hh
+ class ArgumentTask;
+
+ // simple-task.hh
+ class SimpleTask;
+
+ // task-register.hh
+ class TaskRegister;
+
+} // namespace task
diff --git a/tiger-compiler/src/task/int-task.cc b/tiger-compiler/src/task/int-task.cc
new file mode 100644
index 0000000..bf26271
--- /dev/null
+++ b/tiger-compiler/src/task/int-task.cc
@@ -0,0 +1,55 @@
+/**
+ ** \file task/int-task.cc
+ ** \brief IntTask class implementation.
+ **
+ */
+
+#include <exception>
+#include <iostream>
+
+#include <common.hh>
+#include <task/int-task.hh>
+
+namespace task
+{
+ IntTask::IntTask(int& var,
+ int min,
+ int max,
+ const char* module_name,
+ const char* desc,
+ const char* name,
+ std::string deps)
+ : ArgumentTask(name, module_name, desc, "NUM", deps)
+ , var_(var)
+ , min_(min)
+ , max_(max)
+ {}
+
+ void IntTask::arg_set(const std::string& arg) const
+ {
+ arg_ = arg;
+
+ try
+ {
+ var_ = stol(arg);
+ }
+ catch (const std::invalid_argument& e)
+ {
+ std::cerr << program_name << ": expected an integer: " << arg << '\n';
+ throw;
+ }
+
+ if (!(min_ <= var_ && var_ <= max_))
+ {
+ std::cerr << program_name << ": invalid integer: " << var_ << '\n';
+
+ throw std::invalid_argument(std::string("invalid integer: ") + arg_);
+ }
+ }
+
+ void IntTask::execute() const
+ {
+ // Assignment done in arg_set.
+ }
+
+} //namespace task
diff --git a/tiger-compiler/src/task/int-task.hh b/tiger-compiler/src/task/int-task.hh
new file mode 100644
index 0000000..a45518d
--- /dev/null
+++ b/tiger-compiler/src/task/int-task.hh
@@ -0,0 +1,33 @@
+/**
+ ** \file task/int-task.hh
+ ** \brief Declare the IntTask class.
+ **
+ */
+#pragma once
+
+#include <task/argument-task.hh>
+
+namespace task
+{
+ /// A simple Task that sets an Int variable.
+ class IntTask : public ArgumentTask
+ {
+ public:
+ IntTask(int& var,
+ int min,
+ int max,
+ const char* module_name,
+ const char* desc,
+ const char* name,
+ std::string deps);
+
+ void execute() const override;
+ void arg_set(const std::string& arg) const override;
+
+ private:
+ int& var_;
+ int min_;
+ int max_;
+ };
+
+} //namespace task
diff --git a/tiger-compiler/src/task/libtask.hh b/tiger-compiler/src/task/libtask.hh
new file mode 100644
index 0000000..518d377
--- /dev/null
+++ b/tiger-compiler/src/task/libtask.hh
@@ -0,0 +1,107 @@
+/**
+ ** \file task/libtask.hh
+ ** \brief Interface to the Task module.
+ **
+ */
+
+/* This file must not be protected against multiple inclusion,
+ because it might be read to define or declare the tasks. */
+
+#include <task/boolean-task.hh>
+#include <task/disjunctive-task.hh>
+#include <task/function-task.hh>
+#include <task/int-task.hh>
+#include <task/multiple-string-task.hh>
+#include <task/string-task.hh>
+
+/// Handling of Task.
+namespace task
+{} // namespace task
+
+/** A means to concatenate tokens with delayed evaluation. */
+#define CONCAT_(A, B) A##B
+#define CONCAT(A, B) CONCAT_(A, B)
+
+/** Make a "unique" token using the line number of invocation of this
+ macro.
+
+ Conflicts are possible, but unlikely: tasks declared at the same
+ line number, but in different files, are not a problem, since
+ anyway the tasks are static, and in different namespaces.
+
+ Just don't declare two tasks on the same line! */
+#define TASK_UNIQUE() CONCAT(task_, __LINE__)
+
+/*------------------------------.
+| Easy instantiation of Tasks. |
+`------------------------------*/
+
+#undef TASK_GROUP
+#undef TASK_DECLARE
+#undef BOOLEAN_TASK_DECLARE
+#undef INT_TASK_DECLARE
+#undef STRING_TASK_DECLARE
+#undef MULTIPLE_STRING_TASK_DECLARE
+#undef DISJUNCTIVE_TASK_DECLARE
+
+// Should we define the objects, or just declare them?
+#if DEFINE_TASKS
+
+/// Define the current Task group name.
+# define TASK_GROUP(Name) const char group_name[] = Name
+
+/// Instantiate a FunctionTask.
+# define TASK_DECLARE(Name, Help, Routine, Deps) \
+ extern void(Routine)(); \
+ static task::FunctionTask task_##Routine(Routine, group_name, Help, Name, \
+ Deps)
+
+/// Instantiate a BooleanTask.
+# define BOOLEAN_TASK_DECLARE(Name, Help, Flag, Deps) \
+ bool Flag; \
+ static task::BooleanTask task_##Flag(Flag, group_name, Help, Name, Deps)
+
+# define TASK_UNIQUE_NAME_(Line) task_##Line
+
+# define TASK_UNIQUE_NAME TASK_UNIQUE_NAME_(__LINE__)
+
+/// Instantiate an IntTask.
+# define INT_TASK_DECLARE(Name, Min, Max, Help, Flag, Deps) \
+ static task::IntTask TASK_UNIQUE()(Flag, Min, Max, group_name, Help, Name, \
+ Deps)
+
+/// Instantiate a StringTask.
+# define STRING_TASK_DECLARE(Name, Default, Help, Flag, Deps) \
+ std::string Flag = Default; \
+ static task::StringTask task_##Flag(Flag, group_name, Help, Name, Deps)
+
+/// Instanciate a MultipleStringTask
+# define MULTIPLE_STRING_TASK_DECLARE(Name, Help, Routine, Deps) \
+ extern task::MultipleStringTask::callback_type Routine; \
+ static task::MultipleStringTask task_##Routine(Routine, group_name, Help, \
+ Name, Deps)
+
+/// Instantiate a DisjunctiveTask.
+# define DISJUNCTIVE_TASK_DECLARE(Name, Help, Deps) \
+ static task::DisjunctiveTask task_##Routine(group_name, Help, Name, Deps)
+
+#else // !DEFINE_TASKS
+
+/// Define the current Task group name.
+# define TASK_GROUP(Name) extern const char* group_name
+/// Instantiate a FunctionTask.
+# define TASK_DECLARE(Name, Help, Routine, Deps) extern void(Routine)()
+/// Instantiate a BooleanTask.
+# define BOOLEAN_TASK_DECLARE(Name, Help, Flag, Deps) extern bool Flag;
+/// Instantiate an IntTask.
+# define INT_TASK_DECLARE(Name, Min, Max, Help, Flag, Deps)
+/// Instantiate a StringTask.
+# define STRING_TASK_DECLARE(Name, Default, Help, Flag, Deps) \
+ extern std::string Flag;
+/// Instantiate a MultipleStringTask.
+# define MULTIPLE_STRING_TASK_DECLARE(Name, Help, Routine, Deps) \
+ extern void(Routine)(std::string);
+/// Instantiate a DisjunctiveTask.
+# define DISJUNCTIVE_TASK_DECLARE(Name, Help, Deps)
+
+#endif // !DEFINE_TASKS
diff --git a/tiger-compiler/src/task/local.am b/tiger-compiler/src/task/local.am
new file mode 100644
index 0000000..a9bd6aa
--- /dev/null
+++ b/tiger-compiler/src/task/local.am
@@ -0,0 +1,15 @@
+## Task module.
+src_libtc_la_SOURCES += \
+ %D%/fwd.hh %D%/libtask.hh \
+ %D%/task.hh %D%/task.hxx %D%/task.cc \
+ %D%/boolean-task.hh %D%/boolean-task.cc \
+ %D%/function-task.hh %D%/function-task.cc \
+ %D%/int-task.hh %D%/int-task.cc \
+ %D%/string-task.hh %D%/string-task.cc \
+ %D%/multiple-string-task.hh %D%/multiple-string-task.cc \
+ %D%/disjunctive-task.hh %D%/disjunctive-task.cc \
+ %D%/task-register.hh %D%/task-register.cc %D%/task-register.hxx \
+ %D%/simple-task.hh %D%/simple-task.cc \
+ %D%/argument-task.hh %D%/argument-task.cc
+
+TASKS += %D%/tasks.hh %D%/tasks.cc
diff --git a/tiger-compiler/src/task/multiple-string-task.cc b/tiger-compiler/src/task/multiple-string-task.cc
new file mode 100644
index 0000000..a789663
--- /dev/null
+++ b/tiger-compiler/src/task/multiple-string-task.cc
@@ -0,0 +1,21 @@
+/**
+ ** \file task/multiple-string-task.cc
+ ** \brief Implementation of task::MultipleStringTask.
+ */
+
+#include <task/multiple-string-task.hh>
+
+namespace task
+{
+ MultipleStringTask::MultipleStringTask(callback_type& cb,
+ const char* module_name,
+ const char* desc,
+ const char* name,
+ std::string deps)
+ : ArgumentTask(name, module_name, desc, "DIR", deps)
+ , execute_(cb)
+ {}
+
+ void MultipleStringTask::execute() const { execute_(arg_get()); }
+
+} //namespace task
diff --git a/tiger-compiler/src/task/multiple-string-task.hh b/tiger-compiler/src/task/multiple-string-task.hh
new file mode 100644
index 0000000..a770b3b
--- /dev/null
+++ b/tiger-compiler/src/task/multiple-string-task.hh
@@ -0,0 +1,34 @@
+/**
+ ** \file task/multiple-string-task.hh
+ ** \brief Declaration of task::MultipleStringTask.
+ **
+ */
+
+#pragma once
+
+#include <string>
+
+#include <task/argument-task.hh>
+
+namespace task
+{
+ /// A simple task that invokes callback function with string argument.
+ class MultipleStringTask : public ArgumentTask
+ {
+ public:
+ using callback_type = auto(const std::string&) -> void;
+
+ MultipleStringTask(callback_type& cb,
+ const char* module_name,
+ const char* desc,
+ const char* name,
+ std::string deps);
+
+ public:
+ void execute() const override;
+
+ public:
+ callback_type& execute_;
+ };
+
+} // namespace task
diff --git a/tiger-compiler/src/task/simple-task.cc b/tiger-compiler/src/task/simple-task.cc
new file mode 100644
index 0000000..031a182
--- /dev/null
+++ b/tiger-compiler/src/task/simple-task.cc
@@ -0,0 +1,21 @@
+/**
+ ** \file task/simple-task.cc
+ ** \brief SimpleTask class implementation.
+ */
+
+#include <task/simple-task.hh>
+#include <task/task-register.hh>
+
+namespace task
+{
+ SimpleTask::SimpleTask(const char* name,
+ const char* module_name,
+ const char* desc,
+ std::string deps)
+ : Task(name, module_name, desc, deps)
+ {
+ // Register this task.
+ TaskRegister::instance().register_task(*this);
+ }
+
+} // namespace task
diff --git a/tiger-compiler/src/task/simple-task.hh b/tiger-compiler/src/task/simple-task.hh
new file mode 100644
index 0000000..b44b8ea
--- /dev/null
+++ b/tiger-compiler/src/task/simple-task.hh
@@ -0,0 +1,34 @@
+/**
+ ** \file task/simple-task.hh
+ ** \brief Declare the task::SimpleTask class.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <task/task.hh>
+
+namespace task
+{
+ /** \brief A code factoring class for tasks without arguments.
+
+ The purpose of this class is to avoid duplicating the code which register a
+ task without arguments. The sibling class task::ArgumentTask does pretty much
+ the same (with extended functionnalities) for tasks with arguments.
+ */
+ class SimpleTask : public Task
+ {
+ /** \name Ctor. */
+ /** \{ */
+ public:
+ /// Construct and register a SimpleTask.
+ SimpleTask(const char* name,
+ const char* module_name,
+ const char* desc,
+ std::string deps = "");
+
+ /** \} */
+ };
+
+} // namespace task
diff --git a/tiger-compiler/src/task/string-task.cc b/tiger-compiler/src/task/string-task.cc
new file mode 100644
index 0000000..db7776c
--- /dev/null
+++ b/tiger-compiler/src/task/string-task.cc
@@ -0,0 +1,22 @@
+/**
+ ** \file task/string-task.cc
+ ** \brief Implementation of task::StringTask.
+ **
+ */
+
+#include <task/string-task.hh>
+
+namespace task
+{
+ StringTask::StringTask(std::string& var,
+ const char* module_name,
+ const char* desc,
+ const char* name,
+ std::string deps)
+ : ArgumentTask(name, module_name, desc, "STRING", deps)
+ , var_(var)
+ {}
+
+ void StringTask::execute() const { var_ = arg_get(); }
+
+} //namespace task
diff --git a/tiger-compiler/src/task/string-task.hh b/tiger-compiler/src/task/string-task.hh
new file mode 100644
index 0000000..9e06293
--- /dev/null
+++ b/tiger-compiler/src/task/string-task.hh
@@ -0,0 +1,28 @@
+/**
+ ** \file task/string-task.hh
+ ** \brief Declare the task::StringTask class.
+ **
+ */
+#pragma once
+
+#include <task/argument-task.hh>
+
+namespace task
+{
+ /// A simple Task that sets a String variable.
+ class StringTask : public ArgumentTask
+ {
+ public:
+ StringTask(std::string& var,
+ const char* module_name,
+ const char* desc,
+ const char* name,
+ std::string deps);
+
+ void execute() const override;
+
+ private:
+ std::string& var_;
+ };
+
+} //namespace task
diff --git a/tiger-compiler/src/task/task-register.cc b/tiger-compiler/src/task/task-register.cc
new file mode 100644
index 0000000..d449eed
--- /dev/null
+++ b/tiger-compiler/src/task/task-register.cc
@@ -0,0 +1,353 @@
+/**
+ ** \file task/task-register.cc
+ ** \brief Implementation of task::TaskRegister.
+ **
+ */
+
+#include <algorithm>
+#include <exception>
+#include <filesystem>
+#include <fstream>
+#include <map>
+
+#include <common.hh>
+#include <misc/algorithm.hh>
+#include <task/argument-task.hh>
+#include <task/disjunctive-task.hh>
+#include <task/simple-task.hh>
+#include <task/task-register.hh>
+
+namespace task
+{
+ // Singleton stuff
+ TaskRegister& TaskRegister::instance()
+ {
+ static TaskRegister instance_;
+ return instance_;
+ }
+
+ // Register this task.
+ void TaskRegister::register_task(const SimpleTask& task)
+ {
+ auto it = register_task_(task);
+ // The task was already registered.
+ if (it == modules_.end())
+ return;
+
+ // Callback when the option is parsed.
+ auto cb = [this, &task](bool f) {
+ if (f)
+ enable_task(task.name_get());
+ };
+
+ namespace po = boost::program_options;
+ auto v = po::bool_switch()->notifier(cb);
+
+ it->second.add_options()(task.fullname_get(), v, task.desc_get());
+ }
+
+ void TaskRegister::register_task(const ArgumentTask& task)
+ {
+ auto it = register_task_(task);
+ // The taks was already registered.
+ if (it == modules_.end())
+ return;
+
+ // Callback when the option is parsed.
+ auto cb = [this, &task](const std::string& arg) {
+ task.arg_set(arg);
+ enable_task(task.name_get());
+ };
+
+ namespace po = boost::program_options;
+ auto v = po::value<std::string>()
+ ->value_name(task.argname_get())
+ ->required()
+ ->notifier(cb);
+
+ it->second.add_options()(task.fullname_get(), v, task.desc_get());
+ }
+
+ // Internal task registration.
+ // Add the task to the list of tasks and return an iterrator to the module
+ // the option belongs to. The module is created if needed.
+ // In case something went wrong, the end of `modules_' is returned.
+ TaskRegister::indexed_module_type::iterator
+ TaskRegister::register_task_(const Task& task)
+ {
+ if (task_list_.find(task.name_get()) != task_list_.end())
+ {
+ task_error() << misc::error::error_type::failure << program_name
+ << ": TaskRegister::register_task(" << task.name_get()
+ << "): task name already registered.\n";
+ return modules_.end();
+ }
+ task_list_[task.name_get()] = &task;
+
+ // Short-hands.
+ namespace po = boost::program_options;
+ const std::string& module_name = task.module_name_get();
+ auto it = modules_.find(module_name);
+ if (it == modules_.end())
+ it = modules_.emplace(module_name, po::options_description(module_name))
+ .first;
+ // Return the iterator on the module the task belongs to.
+ return it;
+ }
+
+ // Request the execution of the task task_name.
+ void TaskRegister::enable_task(const std::string& task_name)
+ {
+ if (task_list_.find(task_name) == task_list_.end())
+ task_error() << misc::error::error_type::failure << program_name
+ << ": TaskRegister::enable_task(" << task_name
+ << "): this task has not been registered.\n";
+ else
+ {
+ const Task* task = task_list_.find(task_name)->second;
+ resolve_dependencies(*task);
+ // FIXME: for efficiency, resolve_dependency should be called once.
+ // FIXME: detect cycle.
+ task_order_.emplace_back(task);
+ }
+ }
+
+ // Return the number of tasks to execute.
+ int TaskRegister::nb_of_task_to_execute_get() { return task_order_.size(); }
+
+ // Resolve dependencies between tasks.
+ void TaskRegister::resolve_dependencies(const Task& task)
+ {
+ tasks_list_type enabled_tasks;
+
+ // Retrieved already active tasks.
+ for (const std::string& s : task.dependencies_get())
+ if (task_list_.find(s) == task_list_.end())
+ {
+ task_error() << misc::error::error_type::failure << program_name
+ << ": TaskRegister::resolve_dependencies(\""
+ << task.name_get() << "\"): unknown task: \"" << s << '"'
+ << std::endl;
+ }
+ else
+ {
+ const Task* task_dep = task_list_.find(s)->second;
+ if (misc::has(task_order_, task_dep))
+ enabled_tasks.emplace_back(task_dep);
+ }
+
+ // Ask the task which dependent tasks should be activated.
+ const Task::deps_type dep_tasks = task.resolve_dependencies(enabled_tasks);
+
+ // Activate them.
+ for (const std::string& s : dep_tasks)
+ {
+ if (task_list_.find(s) != task_list_.end())
+ if (!misc::has(task_order_, task_list_.find(s)->second))
+ enable_task(s);
+ }
+ }
+
+ // Check whether one of the options in os has the string_key s.
+ template <typename T>
+ static bool
+ is_parsed(const std::string& s,
+ const std::vector<boost::program_options::basic_option<T>>& os)
+ {
+ using option = boost::program_options::basic_option<T>;
+
+ return std::ranges::find_if(
+ os, [&s](const option& o) { return s == o.string_key; })
+ != end(os);
+ }
+
+ char* TaskRegister::parse_arg(int argc, char* argv[])
+ {
+ // Short-hand.
+ namespace po = boost::program_options;
+ std::string input_file;
+
+ // Create the category containing `help', `version' and `usage'.
+ po::options_description generic;
+ generic.add_options()("help,?", "Give this help list")(
+ "usage", "Give a short usage message")(
+ "version", "Print program version")("license", "Print program license");
+
+ // Positional parameter.
+ po::options_description hidden;
+ po::positional_options_description positional;
+ hidden.add_options()("input-file",
+ po::value<std::string>(&input_file)->required(),
+ "Input file");
+ positional.add("input-file", 1);
+
+ // Create the top-level category, with visible options.
+ po::options_description visible_desc(program_doc);
+
+ // Add each category to the top-level one.
+ for (const auto& i : modules_)
+ visible_desc.add(i.second);
+ visible_desc.add(generic);
+
+ // Sum of visible options and positional argument.
+ po::options_description all_opts = visible_desc;
+ all_opts.add(hidden);
+
+ // Give control to boost.
+ try
+ {
+ // Sadly we can't use `boost::program_options::variables_map'. By doing
+ // so, we would lose the chronological order of the tasks, which is
+ // capital for TC to work.
+ auto parsed = po::command_line_parser(argc, argv)
+ .options(all_opts)
+ .positional(positional)
+ .run();
+
+ // We don't want to fail if these options are present. So we take care
+ // of them right now.
+ if (is_parsed("help", parsed.options))
+ std::cout << visible_desc;
+ else if (is_parsed("version", parsed.options))
+ std::cout << program_version;
+ else if (is_parsed("usage", parsed.options))
+ std::cout << "tc [OPTIONS...] INPUT-FILE\n";
+ else if (is_parsed("license", parsed.options))
+ {
+ std::filesystem::path licenses("licenses");
+
+ if (!std::filesystem::exists(licenses))
+ {
+ std::cerr << program_name << ": cannot open licenses directory"
+ << ": No such file or directory\n";
+ }
+ else if (!std::filesystem::is_directory(licenses))
+ {
+ std::cerr << program_name << ": cannot open licenses directory"
+ << ": Not a directory\n";
+ }
+ else
+ {
+ using directory_iterator = std::filesystem::directory_iterator;
+ for (const auto& entry : directory_iterator(licenses))
+ {
+ const auto& path = entry.path();
+
+ if (!std::filesystem::is_regular_file(path)
+ || !path.has_extension()
+ || path.extension() != ".license")
+ continue;
+
+ std::ifstream license(path);
+ if (!license.is_open())
+ continue;
+
+ std::cout << "\n === " << path.stem().string()
+ << " license notice ===\n\n"
+ << license.rdbuf()
+ << "\n === " << path.stem().string()
+ << " license notice ===\n"
+ << std::endl;
+ }
+ }
+ }
+ else
+ {
+ // Replace the traditional calls to `vm.store()' and `vm.notify()'.
+ for (const auto& i : parsed.options)
+ {
+ auto option = parsed.description->find(i.string_key, false);
+
+ po::variable_value v;
+ option.semantic()->parse(v.value(), i.value, true);
+ option.semantic()->notify(v.value());
+ }
+
+ // If no input file is given, throw.
+ if (input_file.size() == 0)
+ throw po::error("no file name");
+ }
+ }
+ catch (const po::error& e)
+ {
+ std::cerr << program_name << ": " << e.what()
+ << "\ntc [OPTIONS...] INPUT-FILE\n"
+ "Try `tc --help' or `tc --usage' for more information.\n";
+ throw std::invalid_argument("command line parsing error");
+ }
+ catch (const std::invalid_argument& e)
+ {
+ throw;
+ }
+
+ char* input_file_ = nullptr;
+ if (!input_file.empty())
+ {
+ input_file_ = new char[input_file.size() + 1];
+ strcpy(input_file_, input_file.c_str());
+ }
+ return input_file_;
+ }
+
+ // Display registered Tasks.
+ std::ostream& TaskRegister::print_task_list(std::ostream& ostr)
+ {
+ ostr << "List of registered tasks:\n";
+ for (const tasks_by_name_type::value_type& i : task_list_)
+ ostr << "\t* " << i.first << '\n';
+ return ostr << std::endl;
+ }
+
+ // Dump task graph.
+ std::ostream& TaskRegister::print_task_graph(std::ostream& ostr)
+ {
+ ostr << "/* Task graph */\n"
+ << "digraph Tasks {\n"
+ << " node [shape=box, fontsize=14]\n"
+ // Preserve the order of the children.
+ << " graph [ordering=out]\n";
+
+ for (const tasks_by_name_type::value_type& i : task_list_)
+ {
+ const Task& task = *i.second;
+ if (dynamic_cast<const DisjunctiveTask*>(&task))
+ ostr << " \"" << task.name_get() << "\" [shape=diamond]\n";
+ ostr << " \"" << task.name_get() << "\"";
+ if (task.dependencies_get().size())
+ {
+ ostr << " -> {";
+ for (const std::string& s : task.dependencies_get())
+ ostr << " \"" << s << "\"";
+ ostr << " } ";
+ }
+ ostr << '\n';
+ }
+
+ return ostr << "}\n";
+ }
+
+ // Display registered Tasks execution order.
+ std::ostream& TaskRegister::print_task_order(std::ostream& ostr)
+ {
+ ostr << "List of Task Order:\n";
+ for (const Task* t : task_order_)
+ ostr << "\t* " << t->name_get() << std::endl;
+ return ostr << std::endl;
+ }
+
+ // Execute tasks, checking dependencies.
+ void TaskRegister::execute()
+ {
+ // FIXME: should be the only one to call resolve_dependency.
+ for (const Task* t : task_order_)
+ {
+ std::string pref(t->module_name_get());
+ if (!pref.empty())
+ pref = pref[0] + std::string(": ");
+ timer_.push(pref + t->name_get());
+ t->execute();
+ timer_.pop(pref + t->name_get());
+ }
+ }
+
+} // namespace task
diff --git a/tiger-compiler/src/task/task-register.hh b/tiger-compiler/src/task/task-register.hh
new file mode 100644
index 0000000..6608e02
--- /dev/null
+++ b/tiger-compiler/src/task/task-register.hh
@@ -0,0 +1,118 @@
+/**
+ ** \file task/task-register.hh
+ ** \brief Declare the task::TaskRegister class.
+ **
+ */
+
+#pragma once
+
+#include <iosfwd>
+#include <map>
+#include <string>
+#include <vector>
+
+#include <boost/program_options.hpp>
+
+#include <misc/timer.hh>
+#include <task/fwd.hh>
+
+namespace task
+{
+ /** \brief Register Tasks.
+
+ The purpose of the TaskRegister class is to collect tasks
+ and organize their execution using their dependencies (like 'make').
+ For modeling details, see Design Patterns: singleton.
+ */
+ class TaskRegister
+ {
+ // Make it non-copyable.
+ TaskRegister(const TaskRegister&) = delete;
+ TaskRegister& operator=(const TaskRegister&) = delete;
+
+ /// \name Ctor & dtor.
+ private:
+ /// Construct a TaskRegister.
+ TaskRegister() = default;
+
+ public:
+ /// Access to the unique TaskRegister.
+ static TaskRegister& instance();
+
+ /** \name Tasks registering.
+ ** \{ */
+ /// Register task \a task.
+ void register_task(const SimpleTask& task);
+ void register_task(const ArgumentTask& task);
+ /// Register the task \a task_name for execution.
+ void enable_task(const std::string& task_name);
+
+ /// Return the number of tasks to execute.
+ int nb_of_task_to_execute_get();
+ /** \} */
+
+ /// \name Task reordering.
+ private:
+ /** \brief Resolve dependencies between tasks.
+ **
+ ** Resolve dependencies on tasks registered for execution.
+ ** Make a depth first search of implicit tasks graph,
+ ** check cycles and build an ordered list of tasks. */
+ void resolve_dependencies(const Task& task);
+
+ public:
+ /** \brief Parse \a argv and determine which tasks to execute.
+ **
+ ** Use boost::program_options. */
+ char* parse_arg(int argc, char* argv[]);
+
+ /** \name Display TaskRegister content.
+ ** \{ */
+ /// Display registered Tasks.
+ std::ostream& print_task_list(std::ostream& ostr);
+ /// Display task graph.
+ std::ostream& print_task_graph(std::ostream& ostr);
+ /// Display registered Tasks execution order.
+ std::ostream& print_task_order(std::ostream& ostr);
+
+ /** \name Using registered Tasks.
+ ** \{ */
+ /// Execute tasks, checking dependencies.
+ void execute();
+ /** \} */
+
+ /** \name Time management.
+ ** \{ */
+ /// Access to the tasks timer.
+ const misc::timer& timer_get() const;
+ /** \} */
+
+ /// Ordered vector of tasks.
+ using tasks_list_type = std::vector<const Task*>;
+
+ private:
+ /// Associate a task name to a task.
+ using tasks_by_name_type = std::map<const std::string, Task const*>;
+ /// Associate a module name to a task module.
+ using indexed_module_type =
+ std::map<const std::string, boost::program_options::options_description>;
+
+ // Common code between the two overload of `register_task'.
+ indexed_module_type::iterator register_task_(const Task& task);
+
+ /// 'string to task' map.
+ tasks_by_name_type task_list_;
+
+ /// 'ordered for execution' tasks list.
+ tasks_list_type task_order_;
+
+ /// Tasks timer.
+ misc::timer timer_;
+
+ /// Task modules.
+ indexed_module_type modules_;
+ };
+
+} // namespace task
+
+#include <task/task-register.hxx>
diff --git a/tiger-compiler/src/task/task-register.hxx b/tiger-compiler/src/task/task-register.hxx
new file mode 100644
index 0000000..a72969a
--- /dev/null
+++ b/tiger-compiler/src/task/task-register.hxx
@@ -0,0 +1,15 @@
+/**
+ ** \file task/task-register.hxx
+ ** \brief Inline methods of task::TaskRegister.
+ **
+ */
+
+#pragma once
+
+#include <task/task-register.hh>
+
+namespace task
+{
+ inline const misc::timer& TaskRegister::timer_get() const { return timer_; }
+
+} // namespace task.
diff --git a/tiger-compiler/src/task/task.cc b/tiger-compiler/src/task/task.cc
new file mode 100644
index 0000000..c675ffa
--- /dev/null
+++ b/tiger-compiler/src/task/task.cc
@@ -0,0 +1,68 @@
+/**
+ ** \file task/task.cc
+ ** \brief Implementation of task::Task.
+ **
+ */
+
+#include <iostream>
+#include <iterator>
+
+#include <task/task.hh>
+
+namespace task
+{
+ Task::Task(const char* name,
+ const char* module_name,
+ const char* desc,
+ const std::string& deps)
+ : name_(normalize(name))
+ , fullname_(name_)
+ , module_name_(module_name)
+ , desc_(desc)
+ {
+ // Compute its dependencies.
+ std::string::size_type start = 0;
+ while (start < deps.size())
+ {
+ std::string::size_type end = deps.find(' ', start);
+ if (end > deps.size())
+ end = deps.size();
+ dependencies_.emplace_back(normalize(deps.substr(start, end - start)));
+ start = end + 1;
+ }
+
+ // See if it has a short option, such as "h|help".
+ if (name_.size() >= 2 && name_[1] == '|')
+ {
+ fullname_ += ',';
+ fullname_ += name_[0];
+ name_.erase(0, 2);
+ fullname_.erase(0, 2);
+ }
+ }
+
+ Task::deps_type Task::resolve_dependencies(tasks_list_type&) const
+ {
+ // By default, consider that all dependencies are required.
+ return dependencies_;
+ }
+
+ /// Display dependencies of this task.
+ void Task::print_dependencies() const
+ {
+ std::cout << "Dependencies for task " << name_ << ":\n";
+ for (const std::string& s : dependencies_)
+ std::cout << "\t" << s << '\n';
+ std::cout << std::flush;
+ }
+
+ std::string Task::normalize(const std::string& task_name)
+ {
+ std::string normalized_name;
+ std::ranges::replace_copy(
+ task_name, std::inserter(normalized_name, normalized_name.begin()), '_',
+ '-');
+ return normalized_name;
+ }
+
+} // namespace task
diff --git a/tiger-compiler/src/task/task.hh b/tiger-compiler/src/task/task.hh
new file mode 100644
index 0000000..6081f1f
--- /dev/null
+++ b/tiger-compiler/src/task/task.hh
@@ -0,0 +1,103 @@
+/**
+ ** \file task/task.hh
+ ** \brief Declare the task::Task class.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include <task/task-register.hh>
+
+namespace task
+{
+ /** \brief A function bound to a command line option.
+
+ The purpose of the Task class is to abstract the execution of a module
+ and the tasks on which its execution depends. This is an implementation
+ of the Command Design Pattern.
+ */
+ class Task
+ {
+ /** \name Ctor & dtor. */
+ /** \{ */
+ public:
+ /** \brief Construct a Task.
+
+ \param name name of this task (used for long option)
+ \param module_name name of the module to which the task belongs.
+ \param desc description of this task
+ \param deps optional space separated list of task names
+ */
+ Task(const char* name,
+ const char* module_name,
+ const char* desc,
+ const std::string& deps = "");
+
+ /// Destroy this Task.
+ virtual ~Task() = default;
+ /** \}*/
+
+ /** \name Abstract methods.
+ ** \{ */
+ public:
+ /// Execute this task.
+ virtual void execute() const = 0;
+ /** \} */
+
+ using tasks_list_type = TaskRegister::tasks_list_type;
+
+ using deps_type = std::vector<std::string>;
+
+ virtual deps_type resolve_dependencies(tasks_list_type& active_tasks) const;
+
+ /** \name Accessors.
+ ** \{ */
+ public:
+ /** Access to 'name'.
+
+ 'const char*' is preferred to 'std::string' because TaskRegister
+ calls function requiring 'const char*'.
+ The use of 'std::string::c_str()' is so forbidden and a call to
+ 'strdup(std::string::c_str())' would imply dummy memory leaks. */
+ const char* name_get() const;
+
+ /// Access to 'module_name'.
+ const char* module_name_get() const;
+
+ /// Access to 'fullname'.
+ const char* fullname_get() const;
+
+ /// Access to 'desc'.
+ const char* desc_get() const;
+
+ /// Access to tasks dependencies.
+ const deps_type& dependencies_get() const;
+
+ /** \} */
+
+ public:
+ /// Display dependencies of this task .
+ void print_dependencies() const;
+
+ public:
+ /// Normalize the name of a task.
+ static std::string normalize(const std::string& task_name);
+
+ protected:
+ /// Task name.
+ std::string name_;
+ /// Task name plus short name.
+ std::string fullname_;
+ /// Module name to which the task belongs.
+ const char* module_name_;
+ /// A short description of this task (displayed in program usage).
+ const char* desc_;
+ /// Contains the name of the tasks on which this one depends.
+ deps_type dependencies_;
+ };
+
+} // namespace task
+
+#include <task/task.hxx>
diff --git a/tiger-compiler/src/task/task.hxx b/tiger-compiler/src/task/task.hxx
new file mode 100644
index 0000000..02fad0e
--- /dev/null
+++ b/tiger-compiler/src/task/task.hxx
@@ -0,0 +1,24 @@
+/**
+ ** \file task/task.hxx
+ ** \brief Inline methods for task/task.hh.
+ */
+#pragma once
+
+#include <task/task.hh>
+
+namespace task
+{
+ inline const char* Task::name_get() const { return name_.c_str(); }
+
+ inline const char* Task::module_name_get() const { return module_name_; }
+
+ inline const char* Task::fullname_get() const { return fullname_.c_str(); }
+
+ inline const char* Task::desc_get() const { return desc_; }
+
+ inline const Task::deps_type& Task::dependencies_get() const
+ {
+ return dependencies_;
+ }
+
+} // namespace task
diff --git a/tiger-compiler/src/task/tasks.cc b/tiger-compiler/src/task/tasks.cc
new file mode 100644
index 0000000..33eb35f
--- /dev/null
+++ b/tiger-compiler/src/task/tasks.cc
@@ -0,0 +1,28 @@
+/**
+ ** \file task/tasks.cc
+ ** \brief Task module related tasks.
+ */
+
+#include <iostream>
+
+#include <common.hh>
+#include <task/task-register.hh>
+#define DEFINE_TASKS 1
+#include <task/tasks.hh>
+#undef DEFINE_TASKS
+
+// Task module related tasks' implementation.
+namespace task::tasks
+{
+ void tasks_list() { TaskRegister::instance().print_task_list(std::cout); }
+
+ void tasks_graph() { TaskRegister::instance().print_task_graph(std::cout); }
+
+ void tasks_selection()
+ {
+ TaskRegister::instance().print_task_order(std::cout);
+ }
+
+ void time_report() { task_timer.dump_on_destruction(std::cerr); }
+
+} // namespace task::tasks
diff --git a/tiger-compiler/src/task/tasks.hh b/tiger-compiler/src/task/tasks.hh
new file mode 100644
index 0000000..b18943d
--- /dev/null
+++ b/tiger-compiler/src/task/tasks.hh
@@ -0,0 +1,23 @@
+/**
+ ** \file task/tasks.hh
+ ** \brief Task module related tasks.
+ */
+
+#pragma once
+
+#include <task/libtask.hh>
+
+namespace task::tasks
+{
+ TASK_GROUP("0. Tasks");
+
+ /// List all the existing tasks.
+ TASK_DECLARE("task-list", "list registered tasks", tasks_list, "");
+ /// Dump task graph.
+ TASK_DECLARE("task-graph", "show task graph", tasks_graph, "");
+ /// List the selected tasks in order.
+ TASK_DECLARE("task-selection", "list tasks to be run", tasks_selection, "");
+ /// Ask for a time report at the end of the execution.
+ TASK_DECLARE("time-report", "report execution times", time_report, "");
+
+} // namespace task::tasks