diff options
| author | Martial Simon <msimon_fr@hotmail.com> | 2025-09-15 01:07:58 +0200 |
|---|---|---|
| committer | Martial Simon <msimon_fr@hotmail.com> | 2025-09-15 01:07:58 +0200 |
| commit | 967be9e750221ab2ab783f95df79bb26d290a45e (patch) | |
| tree | 6802900a5e975f9f68b169f0f503f040056d6952 /tiger-compiler/lib | |
Diffstat (limited to 'tiger-compiler/lib')
57 files changed, 4545 insertions, 0 deletions
diff --git a/tiger-compiler/lib/local.am b/tiger-compiler/lib/local.am new file mode 100644 index 0000000..a44cbcc --- /dev/null +++ b/tiger-compiler/lib/local.am @@ -0,0 +1 @@ +include %D%/misc/local.am diff --git a/tiger-compiler/lib/misc/algorithm.hh b/tiger-compiler/lib/misc/algorithm.hh new file mode 100644 index 0000000..14250bc --- /dev/null +++ b/tiger-compiler/lib/misc/algorithm.hh @@ -0,0 +1,48 @@ +/** + ** \file misc/algorithm.hh + ** \brief Some syntactic sugar to look for things in STL containers. + */ + +#pragma once + +#include <iosfwd> +#include <misc/concepts.hh> + +namespace misc +{ + /// Invoke delete on all the members of \a c, and clear it. + template <typename Container> + requires Iterable<Container> + void deep_clear(Container& c); + + /// Find \a v in the whole \a c. + template <typename Container> + requires ConstIterable<Container> + inline typename Container::const_iterator + find(const Container& c, const typename Container::value_type& v); + + /// Find \a v in the whole \a c. + template <typename Container> + requires Iterable<Container> + inline typename Container::iterator + find(Container& c, const typename Container::value_type& v); + + /// Apply \a f to all the members of \a c, and return it. + template <typename Container, typename Functor> + requires ConstIterable<Container> + inline Functor& for_each(Container& c, Functor& v); + + /// Is \a v member of \a c? + template <typename Container> + requires ConstIterable<Container> + inline bool has(const Container& c, const typename Container::value_type& v); + + /// Insert or update \a key -> \a value in \a map, return iterator to it. + template <Map Map> + typename Map::iterator put(Map& map, + const typename Map::key_type& key, + const typename Map::mapped_type& value); + +} // namespace misc + +#include <misc/algorithm.hxx> diff --git a/tiger-compiler/lib/misc/algorithm.hxx b/tiger-compiler/lib/misc/algorithm.hxx new file mode 100644 index 0000000..00ca8ad --- /dev/null +++ b/tiger-compiler/lib/misc/algorithm.hxx @@ -0,0 +1,84 @@ +/** + ** \file misc/algorithm.hxx + ** \brief Some syntactic sugar to look for things in STL containers. + */ + +#pragma once + +#include <algorithm> +#include <functional> +#include <ostream> + +#include <misc/algorithm.hh> + +namespace misc +{ + template <typename Container> + requires Iterable<Container> + void deep_clear(Container& c) + { + for (typename Container::value_type& x : c) + delete x; + c.clear(); + } + + // Find \a v in the whole \a c. + template <typename Container> + requires ConstIterable<Container> + inline typename Container::const_iterator + find(const Container& c, const typename Container::value_type& v) + { + return std::find(c.begin(), c.end(), v); + } + + // Find \a v in the whole \a c. + template <typename Container> + requires Iterable<Container> + inline typename Container::iterator + find(Container& c, const typename Container::value_type& v) + { + return std::find(c.begin(), c.end(), v); + } + + // Apply \a f to all the members of \a c, and return it. + template <typename Container, typename Functor> + requires ConstIterable<Container> + inline Functor& for_each(Container& c, Functor& f) + { + std::ranges::for_each(c, std::ref(f)); + return f; + } + + // Is \a v member of \a c? + template <typename Container> + requires ConstIterable<Container> + inline bool has(const Container& c, const typename Container::value_type& v) + { + // We specify the instance to solve a conflict between the + // two finds above, that compete against each other because + // the parameter Container can embed a "const". + return std::ranges::find(c, v) != c.end(); + } + + template <Map Map> + typename Map::iterator put(Map& map, + const typename Map::key_type& key, + const typename Map::mapped_type& value) + { + // See ``Efficient STL''. + auto i = map.lower_bound(key); + + if (i != map.end() && !(map.key_comp()(key, i->first))) + { + // Update. + i->second = value; + return i; + } + else + { + // First insertion. + return map.emplace(key, value).first; + } + } + +} // namespace misc diff --git a/tiger-compiler/lib/misc/concepts.hh b/tiger-compiler/lib/misc/concepts.hh new file mode 100644 index 0000000..a4a5926 --- /dev/null +++ b/tiger-compiler/lib/misc/concepts.hh @@ -0,0 +1,79 @@ +#pragma once + +#include <concepts> +#include <iterator> + +namespace misc +{ + // Concept describing a const Container through which you can iterate + // Needs methods begin() and end(). + template <typename Container> + concept ConstIterable = + requires(Container c) { + { + c.begin() != c.end() + } -> std::same_as<bool>; + { + c.begin() + } -> std::convertible_to<typename Container::const_iterator>; + { + *c.begin() + } -> std::convertible_to<const typename Container::value_type&>; + }; + + // Concept describing a Container through which you can iterate + // Needs methods begin() and end(). + template <typename Container> + concept Iterable = ConstIterable<Container> + && requires(Container c) { + { + c.begin() + } -> std::same_as<typename Container::iterator>; + { + *c.begin() + } -> std::same_as<typename Container::value_type&>; + }; + + // Concept discribing a map. + // Needs methods lower_bound() and emplace(key, value). + template <typename M> + concept Map = + requires(M map, + const typename M::key_type key, + const typename M::mapped_type value, + const typename M::iterator i) { + { + map.lower_bound(key) + } -> std::same_as<typename M::iterator>; + + { + map.key_comp() + }; + + { + map.emplace(key, value) + } -> std::same_as<std::pair<typename M::iterator, bool>>; + + requires std::same_as< + std::remove_cvref_t<decltype(*i)>, + std::pair<typename M::key_type const, typename M::mapped_type>>; + }; + + // Concept describing a ConstIterable but adds a constraint on the value_type + // the held type should be convertible into T + template <typename Container, typename T> + concept ConstIterableType = ConstIterable<Container> + && std::convertible_to<typename Container::value_type, T>; + + // Concept describing an Iterable but adds a constraint on the value_type + // the held type should be convertible into T + template <typename Container, typename T> + concept IterableType = Iterable<Container> + && std::convertible_to<typename Container::value_type, T>; + + // Concept describing an iterator on a type T + template <typename Iter, typename T> + concept Iterator = + std::forward_iterator<Iter> && std::same_as<typename Iter::value_type, T>; + +} // namespace misc diff --git a/tiger-compiler/lib/misc/contract.cc b/tiger-compiler/lib/misc/contract.cc new file mode 100644 index 0000000..3dc70be --- /dev/null +++ b/tiger-compiler/lib/misc/contract.cc @@ -0,0 +1,31 @@ +/** \file misc/contract.cc + ** \brief Implementation for contract.hh + */ + +#include <cstdlib> +#include <iostream> + +#include <misc/contract.hh> + +Contract::Contract(const char* condType, + const char* condText, + const char* fileName, + int fileLine) + : condType(condType) + , condText(condText) + , fileName(fileName) + , fileLine(fileLine) +{} + +Contract::~Contract() +{ + const std::string message = std::string(condType) + ' ' + condText + " was false"; + __Terminate(fileName, fileLine, message.c_str()); +} + +void __Terminate(const char* fileName, int fileLine, const char* reason) +{ + std::cerr << fileName << ":" << fileLine << ": Program abortion (" << reason + << ")\n"; + abort(); +} diff --git a/tiger-compiler/lib/misc/contract.hh b/tiger-compiler/lib/misc/contract.hh new file mode 100644 index 0000000..39942a6 --- /dev/null +++ b/tiger-compiler/lib/misc/contract.hh @@ -0,0 +1,72 @@ +/** + ** \file misc/contract.hh + ** \brief Replacement for cassert. + **/ + +#pragma once + +// Use GCC magic bits to specify we cannot return from these functions + +#ifndef __attribute__ +# if !defined __GNUC__ +# define __attribute__(Spec) /* empty */ +# endif +#endif + +[[noreturn]] void __Terminate(const char*, int, const char*); + +#define die(Reason) __Terminate(__FILE__, __LINE__, Reason) +#define unreachable() die("unreachable code reached") + +#ifdef NDEBUG + +// This a trick use to disable assertion and co in the project. We need a stream +// in a 'else' to keep the code syntactically correct. However we do not want +// to print anything. So the 'if' is always true and does nothing. +// 'std::cerr' << "" is here to avoid warnings. +# define __TestCondition \ + if (true) \ + { \ + ; \ + } \ + else \ + std::cerr << "" + +# define assertion(Expr) __TestCondition +# define invariant(Expr) __TestCondition +# define precondition(Expr) __TestCondition +# define postcondition(Expr) __TestCondition + +#else // NDEBUG + +# define __TestCondition(CondType, Expr) \ + if (Expr) \ + { \ + ; \ + } \ + else \ + Contract(#CondType, #Expr, __FILE__, __LINE__) + +# define assertion(Expr) __TestCondition(Assertion, Expr) +# define invariant(Expr) __TestCondition(Invariant, Expr) +# define precondition(Expr) __TestCondition(Precondition, Expr) +# define postcondition(Expr) __TestCondition(Postcondition, Expr) + +#endif // ! NDEBUG + +class Contract +{ +public: + Contract(const char* condType, + const char* condText, + const char* fileName, + int fileLine); + [[noreturn]] ~Contract(); + + const char* condType; + const char* condText; + const char* fileName; + int fileLine; +}; + +#include <misc/contract.hxx> diff --git a/tiger-compiler/lib/misc/contract.hxx b/tiger-compiler/lib/misc/contract.hxx new file mode 100644 index 0000000..6b00b59 --- /dev/null +++ b/tiger-compiler/lib/misc/contract.hxx @@ -0,0 +1,11 @@ +#pragma once + +#include <iostream> + +template <typename T> std::ostream& operator<<(Contract contract, T& t) +{ + return std::cerr << contract.fileName << ':' << contract.fileLine << ": " + << contract.condType << " `" << contract.condText + << "' failed.\n" + << t; +} diff --git a/tiger-compiler/lib/misc/deref.cc b/tiger-compiler/lib/misc/deref.cc new file mode 100644 index 0000000..fb5a086 --- /dev/null +++ b/tiger-compiler/lib/misc/deref.cc @@ -0,0 +1,14 @@ +/** + ** \file misc/deref.cc + ** \brief Implementation for misc/deref.hh + */ + +#include <misc/deref.hh> + +namespace misc +{ + Deref::Deref(std::ostream& o) + : ostr_(o) + {} + +} // namespace misc diff --git a/tiger-compiler/lib/misc/deref.hh b/tiger-compiler/lib/misc/deref.hh new file mode 100644 index 0000000..3d4598c --- /dev/null +++ b/tiger-compiler/lib/misc/deref.hh @@ -0,0 +1,41 @@ +/** + ** \file misc/deref.hh + ** \brief Automatically derefencing pointers on ostreams. + **/ + +#pragma once + +#include <iosfwd> + +#include <misc/ref.hh> + +namespace misc +{ + enum deref_e + { + deref + }; + + class Deref + { + protected: + Deref(std::ostream&); + + public: + template <typename T> std::ostream& operator<<(const T* t) const; + template <typename T> std::ostream& operator<<(T* t) const; + template <typename T> std::ostream& operator<<(const ref<T>& t) const; + template <typename T> std::ostream& operator<<(const T& t) const; + template <typename T> std::ostream& operator<<(T& t) const; + + protected: + friend Deref operator<<(std::ostream&, deref_e); + + std::ostream& ostr_; + }; + + Deref operator<<(std::ostream& ostr, deref_e); + +} // namespace misc + +#include <misc/deref.hxx> diff --git a/tiger-compiler/lib/misc/deref.hxx b/tiger-compiler/lib/misc/deref.hxx new file mode 100644 index 0000000..159b9fa --- /dev/null +++ b/tiger-compiler/lib/misc/deref.hxx @@ -0,0 +1,40 @@ +/** + ** \file misc/deref.hxx + ** \brief Inline methods for misc/deref.hh. + **/ + +#pragma once + +#include <misc/contract.hh> +#include <misc/deref.hh> + +namespace misc +{ + template <typename T> std::ostream& Deref::operator<<(const T* t) const + { + return this->ostr_ << *t; + } + + template <typename T> std::ostream& Deref::operator<<(T* t) const + { + return this->ostr_ << *t; + } + + template <typename T> std::ostream& Deref::operator<<(const ref<T>& t) const + { + return this->ostr_ << *t; + } + + template <typename T> std::ostream& Deref::operator<<(const T& t) const + { + return this->ostr_ << t; + } + + template <typename T> std::ostream& Deref::operator<<(T& t) const + { + return this->ostr_ << t; + } + + inline Deref operator<<(std::ostream& ostr, deref_e) { return Deref(ostr); } + +} // namespace misc diff --git a/tiger-compiler/lib/misc/endomap.hh b/tiger-compiler/lib/misc/endomap.hh new file mode 100644 index 0000000..4a69e87 --- /dev/null +++ b/tiger-compiler/lib/misc/endomap.hh @@ -0,0 +1,64 @@ +/** + ** \file misc/endomap.hh + ** \brief Declaration of misc::endomap. + */ + +#pragma once + +#include <list> +#include <map> + +#include <misc/map.hh> + +namespace misc +{ + /** \brief A renaming mapping defaulting to identity: + Mapping a class with itself (endomorphism). + Unless a value is already mapped onto another, map it to itself + if nonstrict. This is a special case of std::map. */ + template <class T> class endomap : public map<T, T> + { + public: + /// Super class type. + using super_type = map<T, T>; + + // Import overloaded virtual functions. + using super_type::operator[]; + using super_type::operator(); + + enum class strictness_type + { + /// Querying about an unknown identifier returns itself. + nonstrict, + /// Querying about an unknown identifier is an error. + strict + }; + + /// \name Constructors and destructor. + /// \{ + endomap(); + /// \} + + /// Return a duplicate of \a this. + endomap* clone() const override; + + /// Return a reference to the value associated to \a t. + /// + /// If \a t was not inserted before, then, contrary to std::map, + /// we insert t -> t into the map, instead of `t -> T()'. This + /// spares the construction of a new T, which may have nasty + /// effects if this constructor has side effects (e.g., + /// incrementing a counter). + virtual T& operator[](const T& t); + + /// Lookup for \a t, returning it if unknown. + T operator()(const T& t) const override; + + protected: + /// If strict, queries for an unknown (unmapped) Identifier is an error. + strictness_type strictness_; + }; + +} // namespace misc + +#include <misc/endomap.hxx> diff --git a/tiger-compiler/lib/misc/endomap.hxx b/tiger-compiler/lib/misc/endomap.hxx new file mode 100644 index 0000000..58c1912 --- /dev/null +++ b/tiger-compiler/lib/misc/endomap.hxx @@ -0,0 +1,44 @@ +/** -*- C++ -*- + ** \file misc/endomap.hxx + ** \brief Implementation of misc::endomap. + */ + +#pragma once + +#include <misc/endomap.hh> + +namespace misc +{ + template <class T> + endomap<T>::endomap() + : map<T, T>() + , strictness_(strictness_type::nonstrict) + {} + + template <class T> endomap<T>* endomap<T>::clone() const + { + return new endomap<T>(*this); + } + + template <class T> T endomap<T>::operator()(const T& t) const + { + if (const auto&& ires = this->find(t); ires != this->map_.end()) + return ires->second; + else if (this->strictness_ == strictness_type::nonstrict) + return t; + std::ostringstream err; + err << "map: no mapping for " << t; + throw std::range_error(err.str()); + } + + template <class T> T& endomap<T>::operator[](const T& t) + { + // Inspired by ``Efficient STL'' on efficient insert_or_update + // for maps. See also misc::put. + auto i = this->map_.lower_bound(t); + if (i == this->map_.end() || this->map_.key_comp()(t, i->first)) + i = this->map_.emplace(t, t).first; + return i->second; + } + +} // namespace misc diff --git a/tiger-compiler/lib/misc/error.cc b/tiger-compiler/lib/misc/error.cc new file mode 100644 index 0000000..565760b --- /dev/null +++ b/tiger-compiler/lib/misc/error.cc @@ -0,0 +1,107 @@ +/** + ** \file misc/error.cc + ** \brief Implement error. + */ + +#include <iostream> +#include <stdexcept> + +#include <misc/contract.hh> +#include <misc/error.hh> + +namespace misc +{ + /*--------. + | error. | + `--------*/ + + error::error() + : status_(error_type::success) + {} + + error::error(const error& e) { *this = e; } + + error& error::operator=(const error& rhs) + { + status_ = rhs.status_get(); + stream_.str(rhs.stream_get().str()); + return *this; + } + + /*----------------------------. + | Filling the error handler. | + `----------------------------*/ + + error& error::operator<<(error_type e) + { + auto status_value = static_cast<unsigned>(status_); + auto e_value = static_cast<unsigned>(e); + if ((e_value && e_value < status_value) || (!status_value)) + status_ = e; + return *this; + } + + // Import errors. + error& error::operator<<(const error& rhs) + { + *this << rhs.status_get() << rhs.stream_get().str(); + return *this; + } + + error& error::operator<<(std::ostream& (*f)(std::ostream&)) + { + stream_ << f; + return *this; + } + + /*---------------. + | Manipulators. | + `---------------*/ + + void error::exit() const { throw *this; } + + void error::exit_on_error() const + { + if (static_cast<unsigned>(status_)) + exit(); + } + + void error::ice(const char* file, int line) const + { + std::cerr << stream_.str(); + __Terminate(file, line, "Internal Compiler error"); + } + + void error::ice_on_error(const char* file, int line) const + { + if (static_cast<unsigned>(status_)) + ice(file, line); + } + + void error::clear() + { + status_ = error_type::success; + stream_.clear(); + } + + /*------------. + | Accessors. | + `------------*/ + + error::operator bool() const { return status_ != error_type::success; } + + error::error_type error::status_get() const { return status_; } + + unsigned error::status_get_value() const + { + return static_cast<unsigned>(status_); + } + + const std::ostringstream& error::stream_get() const { return stream_; } + + std::ostream& operator<<(std::ostream& o, const error& e) + { + return o << e.stream_get().str(); + } + +} // namespace misc diff --git a/tiger-compiler/lib/misc/error.hh b/tiger-compiler/lib/misc/error.hh new file mode 100644 index 0000000..cd0cc3a --- /dev/null +++ b/tiger-compiler/lib/misc/error.hh @@ -0,0 +1,141 @@ +/** + ** \file misc/error.hh + ** \brief Declare error. + */ + +#pragma once + +#include <iosfwd> +#include <sstream> + +/// Shortcuts. +/// \{ +#define ice_here() ice(__FILE__, __LINE__) +#define ice_on_error_here() ice_on_error(__FILE__, __LINE__) +/// \} + +#ifndef TC_NORETURN +# ifdef SWIG +# define TC_NORETURN +# else +# define TC_NORETURN [[noreturn]] +# endif +#endif + +namespace misc +{ + /** \brief Handle errors in the whole project. + ** + ** Each task has an error status depending on its exit code + ** described in the enum below. + ** + ** Several versions of operator<< are used to fill the handler. + ** + ** A global variable is defined to centralize all the error uses. + */ + + class error + { + public: + error(); + error(const error& e); + + /// Copy an error. + error& operator=(const error& e); + + /// \name Filling the error handler. + /// \{ + + /// With what exit status should we end? + enum class error_type : unsigned + { + /// Job successfully done. + success = 0, + /// Some unspecified error happened. + failure = 1, + /// Lexical error. + scan = 2, + /// Syntactic error. + parse = 3, + /// Binding error: undefined name. + bind = 4, + /// Type checking error. + type = 5 + }; + + /// General method: put the parameter in stream_. + template <typename T> error& operator<<(const T& t); + + /// Set the status if \a e is lower than the current status. + error& operator<<(error_type e); + + /// Accept std::endl etc. + error& operator<<(std::ostream& (*f)(std::ostream&)); + + /// Import errors. + error& operator<<(const error& rhs); + + /// Member manipulator signature. + using member_manip_type = void (error::*)(); + /// Const member manipulator signature. + using const_member_manip_type = void (error::*)() const; + + /// Hook for member manipulators. + error& operator<<(member_manip_type f); + /// Hooks for const member manipulators. + error& operator<<(const_member_manip_type f); + + /// \} + + /// \name Member manipulators. + /// \{ + + /// Throw an exception to exit. + TC_NORETURN void exit() const; + + /// If the error status is set, throw an exception to exit. + void exit_on_error() const; + + /// Throw an Internal Compiler Error. + void ice(const char* file, int line) const; + + /// If the error status is set, consider that an Internal Compiler + /// Error has happened and abort. + void ice_on_error(const char* file, int line) const; + + /// Reset to no error. + void clear(); + + /// \} + + /// \name Accessors. + /// \{ + + /// True if an error is recorded. + operator bool() const; + + /// Get the current status. + error_type status_get() const; + + /// Get the current status value. + unsigned status_get_value() const; + + /// Get the stream associated with the class. + const std::ostringstream& stream_get() const; + + /// \} + + private: + /// The current exit status. + error_type status_; + + /// The current error message. + std::ostringstream stream_; + }; + + /// Display the error message on the given ostream. + std::ostream& operator<<(std::ostream& o, const error& e); + +} // namespace misc + +#include <misc/error.hxx> diff --git a/tiger-compiler/lib/misc/error.hxx b/tiger-compiler/lib/misc/error.hxx new file mode 100644 index 0000000..ecc021f --- /dev/null +++ b/tiger-compiler/lib/misc/error.hxx @@ -0,0 +1,32 @@ +/** + ** \file misc/error.hxx + ** \brief Implement template parts of misc/error.hh. + */ + +#pragma once + +#include <misc/error.hh> + +namespace misc +{ + template <class T> error& error::operator<<(const T& t) + { + stream_ << t; + return *this; + } + + // Member manipulators. + inline error& error::operator<<(member_manip_type f) + { + (this->*f)(); + return *this; + } + + // Const member manipulators. + inline error& error::operator<<(const_member_manip_type f) + { + (this->*f)(); + return *this; + } + +} // namespace misc diff --git a/tiger-compiler/lib/misc/escape.cc b/tiger-compiler/lib/misc/escape.cc new file mode 100644 index 0000000..7585afb --- /dev/null +++ b/tiger-compiler/lib/misc/escape.cc @@ -0,0 +1,60 @@ +/** + ** \file misc/escape.cc + ** \brief Implementation for misc/escape.hh. + **/ + +#include <cctype> +#include <iomanip> +#include <ios> +#include <unordered_map> + +#include <misc/escape.hh> + +namespace misc +{ + std::ostream& escaped::print(std::ostream& ostr) const + { + return escape_(ostr, pobj_str_); + } + + std::ostream& escaped::escape_(std::ostream& o, const std::string& es) const + { + // For some reason yet to be found, when we use the locale for + // std::isprint, Valgrind goes berzerk. So we no longer do the + // following: + // + // static std::locale locale(""); + // + // if (std::isprint(*p, locale)) + std::ios_base::fmtflags flags = o.flags(std::ios_base::oct); + char fill = o.fill('0'); + + /* The GNU Assembler does not recognize `\a' as a valid + escape sequence, hence this explicit conversion to the + 007 octal character. For more information, see + http://sourceware.org/binutils/docs/as/Strings.html. */ + static const std::unordered_map<char, std::string> escapes = { + {'\a', R"(\007)"}, {'\b', R"(\b)"}, {'\f', R"(\f)"}, + {'\n', R"(\n)"}, {'\r', R"(\r)"}, {'\t', R"(\t)"}, + {'\v', R"(\v)"}, {'\\', R"(\\)"}, {'\"', R"(\")"}}; + + for (const auto& p : es) + { + if (const auto&& it = escapes.find(p); it != escapes.end()) + o << it->second; + else + { + if (std::isprint(p)) + o << p; + else + o << "\\" << std::setw(3) + << static_cast<int>(static_cast<unsigned char>(p)); + } + } + + o.fill(fill); + o.flags(flags); + return o; + } + +} // namespace misc diff --git a/tiger-compiler/lib/misc/escape.hh b/tiger-compiler/lib/misc/escape.hh new file mode 100644 index 0000000..199c8b2 --- /dev/null +++ b/tiger-compiler/lib/misc/escape.hh @@ -0,0 +1,36 @@ +/** + ** \file misc/escape.hh + ** \brief Escape: escaping special characters for output. + **/ + +#pragma once + +#include <iosfwd> +#include <string> + +namespace misc +{ + class escaped + { + public: + std::ostream& print(std::ostream& ostr) const; + + protected: + template <class T> escaped(const T&); + + template <class T> friend escaped escape(const T&); + + protected: + std::string pobj_str_; + + private: + std::ostream& escape_(std::ostream& o, const std::string& es) const; + }; + + template <class T> escaped escape(const T&); + + std::ostream& operator<<(std::ostream& o, const escaped&); + +} // namespace misc + +#include <misc/escape.hxx> diff --git a/tiger-compiler/lib/misc/escape.hxx b/tiger-compiler/lib/misc/escape.hxx new file mode 100644 index 0000000..6e08163 --- /dev/null +++ b/tiger-compiler/lib/misc/escape.hxx @@ -0,0 +1,27 @@ +/** + ** \file misc/escape.hxx + ** \brief Inline methods for misc/escape.hh. + **/ + +#pragma once + +#include <misc/contract.hh> +#include <misc/escape.hh> + +#include <boost/lexical_cast.hpp> + +namespace misc +{ + template <class T> escaped escape(const T& obj) { return escaped(obj); } + + inline std::ostream& operator<<(std::ostream& o, const escaped& rhs) + { + return rhs.print(o); + } + + template <class T> escaped::escaped(const T& obj) + { + pobj_str_ = boost::lexical_cast<std::string>(obj); + } + +} // namespace misc diff --git a/tiger-compiler/lib/misc/file-library.cc b/tiger-compiler/lib/misc/file-library.cc new file mode 100644 index 0000000..fe344d0 --- /dev/null +++ b/tiger-compiler/lib/misc/file-library.cc @@ -0,0 +1,152 @@ +/** + ** \file misc/file-library.cc + ** \brief Implements misc::file_library. + */ + +#include <cstdlib> +#include <iostream> +#include <stdexcept> +#include <sys/param.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <misc/contract.hh> +#include <misc/file-library.hh> + +namespace misc +{ + void file_library::push_cwd() + { + // Store the working directory + char cwd[MAXPATHLEN + 1]; + + if (nullptr == getcwd(cwd, MAXPATHLEN + 1)) + throw std::runtime_error("working directory name too long"); + + push_current_directory(path(std::string(cwd))); + } + + file_library::file_library() { push_cwd(); } + + file_library::file_library(path p) + { + push_cwd(); + // Then only process given path. + search_path_.emplace_back(p); + } + + void file_library::append_dir_list(std::string path_list) + { + std::string::size_type pos; + + while ((pos = path_list.find(':')) != std::string::npos) + { + append_dir(path_list.substr(0, pos)); + path_list.erase(0, pos + 1); + } + append_dir(path_list); + } + + path file_library::ensure_absolute_path(path p) const + { + if (p.is_absolute()) + return p; + else + return current_directory_get() / p; + } + + void file_library::append_dir(path p) + { + search_path_.emplace_back(ensure_absolute_path(p)); + } + + void file_library::prepend_dir(path p) + { + search_path_.insert(search_path_.begin(), ensure_absolute_path(p)); + } + + void file_library::push_current_directory(path p) + { + // Ensure that path is absolute. + if (!p.is_absolute()) + p = current_directory_get() / p; + + current_directory_.insert(current_directory_.begin(), p); + } + + void file_library::pop_current_directory() + { + precondition(!current_directory_.empty()); + + current_directory_.erase(current_directory_.begin()); + } + + path file_library::current_directory_get() const + { + precondition(!current_directory_.empty()); + + return current_directory_.front(); + } + + path file_library::find_file(const std::string& file) + { + // Split file in two components, basename and basedir. + path p = path(file); + path directory = p.filename(); + + if (directory.is_absolute()) + { + // If file is absolute, just check that it exists. + if (!std::filesystem::exists(path(file))) + return path(); + } + else + { + // Does the file can be found in current directory? + if (find_in_directory(current_directory_get(), file)) + return (current_directory_get() / file).parent_path(); + + directory = find_in_search_path(directory, p.filename().string()); + } + + return directory; + } + + bool file_library::find_in_directory(const path& dir, + const std::string& file) const + { + return std::filesystem::exists(path(dir / file)); + } + + path file_library::find_in_search_path(const path& relative_path, + const std::string& filename) const + { + path checked_dir; + + // Otherwise start scanning the search path. + for (const path& p : search_path_) + { + if (p.is_absolute()) + checked_dir = p; + else + checked_dir = current_directory_get() / p; + + checked_dir /= relative_path; + + if (find_in_directory(checked_dir, filename)) + return checked_dir; + } + + // File not found in search path. + return path(); + } + + std::ostream& file_library::dump(std::ostream& ostr) const + { + ostr << "."; + for (const path& p : search_path_) + ostr << ":" << p; + return ostr; + } + +} // namespace misc diff --git a/tiger-compiler/lib/misc/file-library.hh b/tiger-compiler/lib/misc/file-library.hh new file mode 100644 index 0000000..f59757f --- /dev/null +++ b/tiger-compiler/lib/misc/file-library.hh @@ -0,0 +1,95 @@ +/** + ** \file misc/file-library.hh + ** \brief Manage sets of inclusion paths. + */ + +#pragma once + +#include <filesystem> +#include <string> +#include <vector> + +namespace misc +{ + using path = std::filesystem::path; + + /** \brief Manage search path. + + Store search path and all informations used for handling + paths when processing import directives. */ + + class file_library + { + public: + /// \name Constructor. + /// \{ + /// An empty library. + file_library(); + /// Init the library with one path. + file_library(path p); + /// \} + + /// \name Managing inclusion paths. + /// \{ + void append_dir(path p); + void prepend_dir(path p); + /// \} + + /// \name current directory. + /// \{ + void push_current_directory(path p); + void pop_current_directory(); + path current_directory_get() const; + /// \} + + /// \name Findind file + /// \{ + /** \brief Search a file. + + Determine real path of \a file, by looking in search path if + necessary. + + \return Directory containing \a file, or "" if not found. */ + path find_file(const std::string& file); + + /// \brief Check if \a file exists in directory \a dir. + bool find_in_directory(const path& dir, const std::string& file) const; + /// \} + + /// \name Printing. + /// \{ + std::ostream& dump(std::ostream& ostr) const; + /// \} + + private: + /// Push the working directory on the stack. + void push_cwd(); + + /** \brief Find file "\a relative_path / \a filename" using include path. + \return Absolute path where the file lies or empty path + if the file does not exist. */ + path find_in_search_path(const path& relative_path, + const std::string& filename) const; + + /** \brief Split the string with character ':', and insert each + resultant string as a new search path. */ + void append_dir_list(std::string path_list); + + /** \brief Ensure that path is absolute by prepending current + directory if necessary */ + path ensure_absolute_path(path p) const; + + using path_list_type = std::vector<path>; + + /// Inclusion path list. + path_list_type search_path_; + + /// Current directory stack. + path_list_type current_directory_; + }; + + std::ostream& operator<<(std::ostream& ostr, const file_library& l); + +} // namespace misc + +#include <misc/file-library.hxx> diff --git a/tiger-compiler/lib/misc/file-library.hxx b/tiger-compiler/lib/misc/file-library.hxx new file mode 100644 index 0000000..37aead3 --- /dev/null +++ b/tiger-compiler/lib/misc/file-library.hxx @@ -0,0 +1,17 @@ +/** + ** \file misc/file-library.hxx + ** \brief implements inline function of misc/file-library.hh + */ + +#pragma once + +#include <misc/file-library.hh> + +namespace misc +{ + inline std::ostream& operator<<(std::ostream& ostr, const file_library& l) + { + return l.dump(ostr); + } + +} // namespace misc diff --git a/tiger-compiler/lib/misc/fwd.hh b/tiger-compiler/lib/misc/fwd.hh new file mode 100644 index 0000000..33f0cc1 --- /dev/null +++ b/tiger-compiler/lib/misc/fwd.hh @@ -0,0 +1,34 @@ +/** + ** \file misc/fwd.hh + ** \brief Forward declarations for misc:: items. + */ + +#pragma once + +namespace misc +{ + // From file-library.hh. + class file_library; + + // From map.hh. + template <typename T, typename N> class map; + // From endomap.hh. + template <typename T> class endomap; + + // From ref.hh. + template <typename T> class ref; + + // From select-const.hh. + template <typename T> struct constify_traits; + template <typename T> struct id_traits; + + // From symbol.hh. + class symbol; + + // From xalloc.hh. + template <class StoredType> class xalloc; + + // From timer.hh + class timer; + +} // namespace misc diff --git a/tiger-compiler/lib/misc/graph.hh b/tiger-compiler/lib/misc/graph.hh new file mode 100644 index 0000000..c21aa99 --- /dev/null +++ b/tiger-compiler/lib/misc/graph.hh @@ -0,0 +1,213 @@ +/** + ** \file misc/graph.hh + ** \brief Undirected/oriented graph definition. + */ + +#pragma once + +#include <iosfwd> +#include <set> +#include <string> +#include <vector> + +#include <boost/functional/hash.hpp> // Required by boost/graph/detail/edge.hpp. +/* Forward declarations to help the compiler not to mix up the + namespace `target' with `boost::target'. This happens in the + instantiation of graph concepts in boost/graph/graph_concepts.hpp + if this forward declaration is omitted. */ +#include <boost/graph/detail/edge.hpp> + +namespace boost +{ + template <class OutEdgeListS, + class VertexListS, + class DirectedS, + class VertexProperty, + class EdgeProperty, + class GraphProperty, + class EdgeListS> + class adjacency_list; + + template <class Directed, + class Vertex, + class OutEdgeListS, + class VertexListS, + class DirectedS, + class VertexProperty, + class EdgeProperty, + class GraphProperty, + class EdgeListS> + inline Vertex target(const detail::edge_base<Directed, Vertex>& e, + const adjacency_list<OutEdgeListS, + VertexListS, + DirectedS, + VertexProperty, + EdgeProperty, + GraphProperty, + EdgeListS>&); + +} // namespace boost + +#include <boost/graph/adjacency_list.hpp> + +#include <misc/set.hh> + +/// Handling of generic, oriented or undirected, graph. +namespace misc +{ + /*--------. + | Graph. | + `--------*/ + + /// Graph property: name. + using name_prop_type = boost::property<boost::graph_name_t, std::string>; + + template <typename Orientation, typename VertexLabel, typename EdgeLabel> + class graph + : public boost::adjacency_list<boost::setS, + boost::vecS, + Orientation, + VertexLabel, + EdgeLabel, + name_prop_type> + { + public: + virtual ~graph() = default; + + public: + using self_type = graph<Orientation, VertexLabel, EdgeLabel>; + using super_type = boost::adjacency_list<boost::setS, + boost::vecS, + Orientation, + VertexLabel, + EdgeLabel, + name_prop_type>; + using vertex_descriptor = typename super_type::vertex_descriptor; + + /// Iterators. + public: + /// \{ + /// Iterator on the vertices of a graph. + using vertex_iter_type = + typename boost::graph_traits<self_type>::vertex_iterator; + /// Iterator on the edges of a graph. + using edge_iter_type = + typename boost::graph_traits<self_type>::edge_iterator; + /// \} + + /// Graph manipulation. + /// \{ + public: + /// \brief Add a vertex to the graph. + /// + /// Just a wrapper around boost::add_vertex. + vertex_descriptor vertex_add(const VertexLabel& l); + /// Add an edge between two vertices. + virtual void edge_add(const vertex_descriptor& v1, + const vertex_descriptor& v2) = 0; + /// \} + + /// Graph pretty printing. + /// \{ + public: + virtual std::ostream& print(std::ostream& ostr) const; + virtual std::ostream& epilogue_print(std::ostream& ostr) const; + virtual void print(std::string file) const; + + private: + /// Print the label of vertex of a graph. + virtual std::ostream& vertex_print(vertex_descriptor v, + std::ostream& ostr) const = 0; + /// \} + + /// Graph name. + /// \{ + public: + const std::string& name_get() const; + void name_set(const std::string& name); + /// \} + }; + + template <typename Orientation, typename VertexLabel, typename EdgeLabel> + std::ostream& operator<<(std::ostream& ostr, + const graph<Orientation, VertexLabel, EdgeLabel>& g); + + // An helper class when we do not want any data attached to vertex + // or an edge. + struct empty + {}; + inline std::ostream& operator<<(std::ostream& ostr, empty); + + /*----------------------------------------------------. + | Specialization for directed (bidirectional) graph. | + `----------------------------------------------------*/ + + /* Note that boost::bidirectionalS is like boost::directedS, except + that it provides access to both in-edges and out-edges, whereas + boost::directedS gives us only out-edges. The consequence is + that a bidirectional graph is twice as large as its directed + version. */ + template <typename VertexLabel = empty, typename EdgeLabel = empty> + class directed_graph + : public graph<boost::bidirectionalS, VertexLabel, EdgeLabel> + { + public: + using self_type = directed_graph<VertexLabel, EdgeLabel>; + using super_type = graph<boost::bidirectionalS, VertexLabel, EdgeLabel>; + using vertex_descriptor = typename super_type::vertex_descriptor; + + public: + /// Iterator on the vertices of a directed_graph. + using vertex_iter_type = + typename boost::graph_traits<self_type>::vertex_iterator; + /// Iterator on the neighbors of the vertex of a directed_graph. + using neighb_iter_type = + typename boost::graph_traits<self_type>::adjacency_iterator; + + /// Iterators. + /// \{ + public: + /// \brief Add an edge between two vertices. + /// + /// This is just a wrapper around boost::add_edge. + void edge_add(const vertex_descriptor& v1, + const vertex_descriptor& v2) override; + /// \} + + /// \brief Topological sort. + /// + /// This version (algorithm 17.5 in Modern Compiler Implementation + /// in ML, p. 390) is not the same as boost::topological_sort since + /// the former works on directed acyclic graphs (DAGs) and the latter + /// doesn't. + /// + /// \{ + public: + std::vector<vertex_descriptor> topological_sort() const; + /// \} + }; + + /*--------------------------------------. + | Specialization for undirected graph. | + `--------------------------------------*/ + + template <typename VertexLabel = empty, typename EdgeLabel = empty> + class undirected_graph + : public graph<boost::undirectedS, VertexLabel, EdgeLabel> + { + public: + using super_type = graph<boost::undirectedS, VertexLabel, EdgeLabel>; + using vertex_descriptor = typename super_type::vertex_descriptor; + + public: + /// \brief Add an edge between two vertices. + /// + /// Use this method instead of boost::add_edge directly to keep + /// the order between the ends of an edge. + void edge_add(const vertex_descriptor& v1, + const vertex_descriptor& v2) override; + }; + +} // namespace misc + +#include <misc/graph.hxx> diff --git a/tiger-compiler/lib/misc/graph.hxx b/tiger-compiler/lib/misc/graph.hxx new file mode 100644 index 0000000..fe97cf7 --- /dev/null +++ b/tiger-compiler/lib/misc/graph.hxx @@ -0,0 +1,171 @@ +/** + ** \file misc/graph.hxx + ** \brief Undirected/oriented graph implementation. + */ + +#pragma once + +#include <algorithm> +#include <fstream> +#include <sstream> +#include <tuple> + +#include <misc/algorithm.hh> +#include <misc/contract.hh> +#include <misc/deref.hh> + +namespace misc +{ + /*--------. + | Graph. | + `--------*/ + + template <typename Orientation, typename VertexLabel, typename EdgeLabel> + typename graph<Orientation, VertexLabel, EdgeLabel>::vertex_descriptor + graph<Orientation, VertexLabel, EdgeLabel>::vertex_add(const VertexLabel& l) + { + return boost::add_vertex(l, *this); + } + + /*-----------------------------------------------------------------. + | graph::print. | + | | + | For oriented graph, keeping edges bound to the source node looks | + | nicer, while an edge oriented dump is more appropriate for | + | unoriented graphs. In the latter case, be sure not to output | + | each edge twice. | + | | + | FIXME: We do lack something similar to Escape when "deref"ing, | + | for if there are strings in there, they should be escaped for | + | dotty. | + | | + | FIXME: Enforce the order by sorting before outputting. | + `-----------------------------------------------------------------*/ + + template <typename Orientation, typename VertexLabel, typename EdgeLabel> + std::ostream& + graph<Orientation, VertexLabel, EdgeLabel>::print(std::ostream& ostr) const + { + using misc::deref; + + std::string graphtype = boost::is_undirected(*this) ? "graph" : "digraph"; + std::string link = boost::is_undirected(*this) ? "--" : "->"; + ostr << "/* Graph Visualization */\n"; + + ostr << graphtype << " \"" << name_get() << "\" {\n" + << " node [shape=box];\n"; + + for (auto [vi, vi_end] = boost::vertices(*this); vi != vi_end; ++vi) + { + ostr << " \"" << *vi << "\" " + << "[label=\""; + vertex_print(*vi, ostr); + ostr << "\"]\n"; + } + + for (auto [ei, ei_end] = boost::edges(*this); ei != ei_end; ++ei) + { + std::ostringstream label; + label << deref << (*this)[*ei]; + ostr << " \"" << boost::source(*ei, *this) << "\" " << link << " \"" + << boost::target(*ei, *this) << "\""; + if (label.str() != "") + ostr << " [label=\"" << label.str() << "\"]"; + ostr << '\n'; + } + + epilogue_print(ostr); + + return ostr << "}\n"; + } + + template <typename Orientation, typename VertexLabel, typename EdgeLabel> + std::ostream& graph<Orientation, VertexLabel, EdgeLabel>::epilogue_print( + std::ostream& ostr) const + { + return ostr; + } + + template <typename Orientation, typename VertexLabel, typename EdgeLabel> + void graph<Orientation, VertexLabel, EdgeLabel>::print(std::string file) const + { + std::ofstream ofs; + + static unsigned cpt = 0; + if (file.empty()) + { + std::stringstream sstr; + sstr << "graph-" << cpt << ".gv"; + ++cpt; + file = sstr.str(); + } + + file += ".gv"; + ofs.open(file.c_str()); + print(ofs); + ofs.close(); + } + + template <typename Orientation, typename VertexLabel, typename EdgeLabel> + const std::string& + graph<Orientation, VertexLabel, EdgeLabel>::name_get() const + { + // For some reason, the following cast is needed with Boost 1.45. + return boost::get_property(static_cast<const super_type&>(*this), + boost::graph_name); + } + + template <typename Orientation, typename VertexLabel, typename EdgeLabel> + void + graph<Orientation, VertexLabel, EdgeLabel>::name_set(const std::string& name) + { + boost::set_property(*this, boost::graph_name, name); + } + + template <typename Orientation, typename VertexLabel, typename EdgeLabel> + inline std::ostream& + operator<<(std::ostream& ostr, + const graph<Orientation, VertexLabel, EdgeLabel>& g) + { + return g.print(ostr); + } + + inline std::ostream& operator<<(std::ostream& ostr, empty) { return ostr; } + + /*----------------. + | DirectedGraph. | + `----------------*/ + + template <typename VertexLabel, typename EdgeLabel> + void + directed_graph<VertexLabel, EdgeLabel>::edge_add(const vertex_descriptor& v1, + const vertex_descriptor& v2) + { + boost::add_edge(v1, v2, *this); + } + + template <typename NodeLabel, typename EdgeLabel> + std::vector<typename directed_graph<NodeLabel, EdgeLabel>::vertex_descriptor> + directed_graph<NodeLabel, EdgeLabel>::topological_sort() const + { + std::vector<vertex_descriptor> res; + misc::set<vertex_descriptor> visited; + return res; + } + + /*------------------. + | UndirectedGraph. | + `------------------*/ + + template <typename VertexLabel, typename EdgeLabel> + void undirected_graph<VertexLabel, EdgeLabel>::edge_add( + const vertex_descriptor& v1, + const vertex_descriptor& v2) + { + if (v1 > v2) + boost::add_edge(v2, v1, *this); + else + boost::add_edge(v1, v2, *this); + } + +} // namespace misc diff --git a/tiger-compiler/lib/misc/indent.cc b/tiger-compiler/lib/misc/indent.cc new file mode 100644 index 0000000..03e15cd --- /dev/null +++ b/tiger-compiler/lib/misc/indent.cc @@ -0,0 +1,57 @@ +/** + ** \file misc/indent.cc + ** \brief Implementation of indentation relative functions. + **/ + +#include <iomanip> +#include <ostream> + +#include <misc/contract.hh> +#include <misc/indent.hh> + +namespace misc +{ + namespace + { + /// The current indentation level for \a o. + inline long int& indent(std::ostream& o) + { + // The slot to store the current indentation level. + static const long int indent_index = std::ios::xalloc(); + return o.iword(indent_index); + } + + } // namespace + + std::ostream& incindent(std::ostream& o) + { + indent(o) += 2; + return o; + } + + std::ostream& decindent(std::ostream& o) + { + precondition(indent(o)); + indent(o) -= 2; + return o; + } + + std::ostream& resetindent(std::ostream& o) + { + indent(o) = 0; + return o; + } + + std::ostream& iendl(std::ostream& o) + { + o << std::endl; + // Be sure to be able to restore the stream flags. + char fill = o.fill(' '); + return o << std::setw(indent(o)) << "" << std::setfill(fill); + } + + std::ostream& incendl(std::ostream& o) { return o << incindent << iendl; } + + std::ostream& decendl(std::ostream& o) { return o << decindent << iendl; } + +} // namespace misc diff --git a/tiger-compiler/lib/misc/indent.hh b/tiger-compiler/lib/misc/indent.hh new file mode 100644 index 0000000..04cd610 --- /dev/null +++ b/tiger-compiler/lib/misc/indent.hh @@ -0,0 +1,30 @@ +/** + ** \file misc/indent.hh + ** \brief Indentation relative functions. + **/ + +#pragma once + +#include <iosfwd> + +namespace misc +{ + /// Increment the indentation. + std::ostream& incindent(std::ostream& o); + + /// Decrement the indentation. + std::ostream& decindent(std::ostream& o); + + /// Reset the indentation. + std::ostream& resetindent(std::ostream& o); + + /// Print an end of line, then set the indentation. + std::ostream& iendl(std::ostream& o); + + /// Increment the indentation, print an end of line, and set the indentation. + std::ostream& incendl(std::ostream& o); + + /// Decrement the indentation, print an end of line, and set the indentation. + std::ostream& decendl(std::ostream& o); + +} // namespace misc diff --git a/tiger-compiler/lib/misc/lambda-visitor.hh b/tiger-compiler/lib/misc/lambda-visitor.hh new file mode 100644 index 0000000..db0b403 --- /dev/null +++ b/tiger-compiler/lib/misc/lambda-visitor.hh @@ -0,0 +1,89 @@ +/** + ** \file misc/lambda-visitor.hh + ** \brief Declaration of misc::LambdaVisitor. + **/ + +#pragma once + +namespace misc +{ + /** \brief Allows to build a visitor from lambdas to use with std::visit. + ** + ** This allows to use a syntax such as + ** std::visit( + ** LambdaVisitor{ + ** Lambda1, + ** Lambda2, + ** ... + ** }, variant); + ** to a call specific lambdas depending on the static type of the variant. + ** + ** For instance, the following code: + ** + ** std::visit( + ** LambdaVisitor{ + ** [](int) { std::cout << "It's an int!\n"; }, + ** [](std::string) { std::cout << "It's a string!\n"; }, + ** [](auto) { std::cout << "It's anything else!\n"; }, + ** }, var); + ** + ** would have the following results for these variants: + ** std::variant<int, std::string, char> v1 = 1; // It's an int! + ** std::variant<int, std::string, char> v2 = "Tigrou"; // It's a string! + ** std::variant<int, std::string, char> v3 = 'a'; // It's anything else! + ** + ** This allows to perform quick and efficient pattern matching on variants' + ** static type. + ** + ** The really powerful thing here is that this can be used to match multiple + ** variants. For instance the following code: + ** + ** std::visit( + ** LambdaVisitor{ + ** [](int, std::string) { ... }, + ** [](auto, std::string) { ... }, + ** [](auto, auto) { ... }, + ** }, var1, var2); + ** + ** would call the first lambda if var1 is an int and var2 a string, the + ** second lambda if var2 is a string and var1 anything else than an int, + ** and the third in any other case. + ** + ** Do note that you can have a return value for your lambdas, and have + ** something like so: + ** int a = std::visit( + ** LambdaVisitor{ + ** ... + ** }, var); + ** + ** + ** Be careful , you must necessarily have a matching lambda for any possible + ** values of the variants used as arguments! You won't be able to compile + ** otherwise. + **/ + template <class... Ts> struct LambdaVisitor : Ts... + { + /** C++ actually desugars lambdas to some kind of struct functors. + ** For instance, the following lambda could be desugared to the following + ** struct: + ** + ** auto f = [](int a) { std::cout << a; } + ** + ** struct { + ** auto operator()(int a) { + ** std::cout << a; + ** } + ** } f; + ** + ** We therefore want to access the operator() of every lambda/functor used + ** to create the LambdaVisitor. + **/ + using Ts::operator()...; + }; + + /// Class template argument deduction. This allows to implicitly deduce the + /// templated type of the visitor created from the types of the arguments + /// it is constructed with, without having to explicitly fill the template. + template <class... Ts> LambdaVisitor(Ts...) -> LambdaVisitor<Ts...>; + +} // namespace misc diff --git a/tiger-compiler/lib/misc/libmisc.hh b/tiger-compiler/lib/misc/libmisc.hh new file mode 100644 index 0000000..9303941 --- /dev/null +++ b/tiger-compiler/lib/misc/libmisc.hh @@ -0,0 +1,10 @@ +/** + ** \file misc/libmisc.hh + ** \brief A library of handy C++ tools. + */ + +#pragma once + +/// A set of handy C++ tools. +namespace misc +{} // namespace misc diff --git a/tiger-compiler/lib/misc/list.hh b/tiger-compiler/lib/misc/list.hh new file mode 100644 index 0000000..d27ab66 --- /dev/null +++ b/tiger-compiler/lib/misc/list.hh @@ -0,0 +1,25 @@ +/** + ** \file misc/list.hh + ** \brief Declaration of misc::list + */ + +#pragma once + +#include <misc/vector.hh> + +/// Wrappers on misc::vectors to provide functional-style list manipulation. +namespace misc::list +{ + /// Build a list of a \a head and a \a tail. + template <typename T> vector<T> cons(const T head, const vector<T>& tail); + + /// Get the head and tail of the list. Invert of cons. + template <typename T> std::pair<T, vector<T>> snoc(const vector<T>& v); + + /// Decompose the list into an array to use structured bindings. + template <std::size_t Size, typename T> + std::array<T, Size> decompose(const vector<T>& v); + +} // namespace misc::list + +#include <misc/list.hxx> diff --git a/tiger-compiler/lib/misc/list.hxx b/tiger-compiler/lib/misc/list.hxx new file mode 100644 index 0000000..63d7f0d --- /dev/null +++ b/tiger-compiler/lib/misc/list.hxx @@ -0,0 +1,43 @@ +/** + ** \file misc/list.hh + ** \brief Implementation of misc::list + */ + +#pragma once + +#include <misc/list.hh> + +namespace misc::list +{ + template <class T> inline vector<T> cons(const T head, const vector<T>& tail) + { + vector<T> res; + + res.emplace_back(head); + res.append(tail); + + return res; + } + + template <class T> inline std::pair<T, vector<T>> snoc(const vector<T>& v) + { + precondition(v.size() > 0); + + T head = v[0]; + vector<T> tail(v.begin() + 1, v.end()); + + return {head, tail}; + } + + template <std::size_t Size_, typename T> + inline std::array<T, Size_> decompose(const vector<T>& v) + { + precondition(v.size() == Size_); + + std::array<T, Size_> res; + std::copy(v.begin(), v.end(), res.begin()); + + return res; + } + +} // namespace misc::list diff --git a/tiger-compiler/lib/misc/local.am b/tiger-compiler/lib/misc/local.am new file mode 100644 index 0000000..e0e8bcb --- /dev/null +++ b/tiger-compiler/lib/misc/local.am @@ -0,0 +1,58 @@ +## --------- ## +## libmisc. ## +## --------- ## + +noinst_LTLIBRARIES = %D%/libmisc.la +%C%_libmisc_la_SOURCES = \ + %D%/fwd.hh \ + %D%/libmisc.hh \ + %D%/concepts.hh \ + %D%/algorithm.hh %D%/algorithm.hxx \ + %D%/contract.hh %D%/contract.hxx %D%/contract.cc \ + %D%/deref.hh %D%/deref.hxx %D%/deref.cc \ + %D%/error.hh %D%/error.hxx %D%/error.cc \ + %D%/escape.hh %D%/escape.hxx %D%/escape.cc \ + %D%/file-library.hh %D%/file-library.hxx %D%/file-library.cc \ + %D%/graph.hh %D%/graph.hxx \ + %D%/indent.hh %D%/indent.cc \ + %D%/map.hh %D%/map.hxx \ + %D%/endomap.hh %D%/endomap.hxx \ + %D%/ref.hh %D%/ref.hxx \ + %D%/select-const.hh \ + %D%/set.hh %D%/set.hxx \ + %D%/separator.hh %D%/separator.hxx \ + %D%/symbol.hh %D%/symbol.hxx %D%/symbol.cc \ + %D%/timer.hh %D%/timer.hxx %D%/timer.cc \ + %D%/unique.hh %D%/unique.hxx \ + %D%/variant.hh %D%/variant.hxx %D%/vector.hh %D%/vector.hxx \ + %D%/lambda-visitor.hh \ + %D%/list.hh %D%/list.hxx \ + %D%/xalloc.hh %D%/xalloc.hxx + +%C%_libmisc_la_SOURCES += %D%/scoped-map.hh %D%/scoped-map.hxx + +%C%_libmisc_la_SOURCES += %D%/singleton.hh + +%C%_libmisc_la_LDFLAGS = $(BOOST_SYSTEM_LDFLAGS) +%C%_libmisc_la_LIBADD = $(BOOST_SYSTEM_LIBS) + +## ------- ## +## Tests. ## +## ------- ## + +check_PROGRAMS += \ + %D%/test-deref \ + %D%/test-error \ + %D%/test-escape \ + %D%/test-graph \ + %D%/test-indent \ + %D%/test-separator \ + %D%/test-scoped \ + %D%/test-symbol \ + %D%/test-timer \ + %D%/test-unique \ + %D%/test-variant \ + %D%/test-xalloc +%C%_test_variant_CXXFLAGS = -Wno-unused + +LDADD = %D%/libmisc.la diff --git a/tiger-compiler/lib/misc/map.hh b/tiger-compiler/lib/misc/map.hh new file mode 100644 index 0000000..952a1e2 --- /dev/null +++ b/tiger-compiler/lib/misc/map.hh @@ -0,0 +1,141 @@ +/** + ** \file misc/map.hh + ** \brief Declaration of misc::Map. + */ + +#pragma once + +#include <map> +#include <stdexcept> +#include <vector> +#include <misc/concepts.hh> + +namespace misc +{ + /** \brief Wrapper around std::map providing syntactic sugar + for any mapping of types manipulation. */ + template <class T, class N> class map + { + protected: + using map_type = std::map<T, N>; + + public: + /// The map's key type. + using key_type = typename map_type::key_type; + /// The type of object associated with the keys. + using mapped_type = typename map_type::mapped_type; + /// The type of object, std::pair<const key_type, data_type>, + /// stored in the map. + using value_type = typename map_type::value_type; + /// Type of the function object that compares two keys for ordering. + using key_compare = typename map_type::key_compare; + /// Iterator used to iterate through a map. + using iterator = typename map_type::iterator; + /// Const iterator used to iterate through a map. + using const_iterator = typename map_type::const_iterator; + + public: + /// \name Constructors and destructor. + /// \{ + map() = default; + /// Return a duplicate of \a this. + virtual ~map() = default; + /// \} + + /// Return a duplicate of \a this. + virtual map* clone() const; + + /// Returns the key_compare object used by the map. + key_compare key_comp() const; + + /// Lookup for \a t. + /// \{ + virtual iterator find(const T& t); + virtual const_iterator find(const T& t) const; + /// \} + + /// Lookup for \a t and throw an exception if not available. + /// \{ + virtual iterator xfind(const T& t); + virtual const_iterator xfind(const T& t) const; + /// \} + + /// Lookup for the value associated to \a t. + virtual N operator[](const T& t) const; + + /// \brief Return a reference to the value associated to \a t. + /// + /// This method is not virtual, so that it is not automatically + /// instantiated at the instantiation of misc::map. This way, + /// there won't be errors from the compiler about \a N not being a + /// model of the concept Default Constructible. This is needed + /// for some instantiations of misc::map, notably the ones that + /// uses a symbol::Symbol as mapped type, which has no default + /// constructor (note that in this case everything will be fine as + /// long as this method is not called -- compile errors will occur + /// otherwise). + /// + /// We might want to provide two flavors of misc::map, one that + /// requires that its second parameter is a model of Default + /// Constructible and that provides an operator[] similar to + /// std::map's; and another one that doesn't. + N& operator[](const T& t); + + /// Lookup for \a t, throwing std::range_error if it is not found. + virtual N operator()(const T& t) const; + + /// Lookup for a list \a ts of T's, throwing std::range_error if + /// one or several elements are not found. + template <typename Container> + requires ConstIterableType<Container, T> + std::vector<N> operator()(const Container& ts) const; + + /// \name Iterating. + /// \{ + virtual iterator begin(); + virtual iterator end(); + virtual const_iterator begin() const; + virtual const_iterator end() const; + /// \} + + /// Finds the first element whose key is not less than \a k. + /// \{ + virtual iterator lower_bound(const T& k); + virtual const_iterator lower_bound(const T& k) const; + /// \} + + /// Inserts \a x into the map. + virtual std::pair<iterator, bool> insert(const std::pair<const T, N>& x); + /// Copy the contents of \a other to \a this. + virtual void insert(const map& other); + + /// Insert a new element built in-place using \a args into the map. + template <class... Args> std::pair<iterator, bool> emplace(Args&&... args); + + /// Is the map empty ? + virtual bool empty() const; + + /// Number of known identifiers. + virtual size_t size() const; + + /// Remove and return mapping for \a t from the map. + virtual N take(const T& t); + + /// Erase all of the elements. + virtual void clear(); + + /// Description of known mappings. + virtual std::ostream& print(std::ostream& ostr) const; + + protected: + /// Mapping from T to N. + map_type map_; + }; + + /// Output \a m onto \a ostr. + template <class T, class N> + std::ostream& operator<<(std::ostream& ostr, const map<T, N>& m); + +} // namespace misc + +#include <misc/map.hxx> diff --git a/tiger-compiler/lib/misc/map.hxx b/tiger-compiler/lib/misc/map.hxx new file mode 100644 index 0000000..fb429ac --- /dev/null +++ b/tiger-compiler/lib/misc/map.hxx @@ -0,0 +1,176 @@ +/** -*- C++ -*- + ** \file misc/map.hxx + ** \brief Implementation of misc::map. + */ + +#pragma once + +#include <iostream> +#include <sstream> + +#include <boost/lexical_cast.hpp> + +#include <misc/contract.hh> +#include <misc/indent.hh> +#include <misc/map.hh> + +namespace misc +{ + template <class T, class N> map<T, N>* map<T, N>::clone() const + { + return new map<T, N>(*this); + } + + template <class T, class N> + typename map<T, N>::const_iterator map<T, N>::find(const T& t) const + { + return map_.find(t); + } + + template <class T, class N> + typename map<T, N>::iterator map<T, N>::find(const T& t) + { + return map_.find(t); + } + + template <class T, class N> + typename map<T, N>::iterator map<T, N>::xfind(const T& t) + { + if (auto&& ires = find(t); ires != map_.end()) + return ires; + else + throw std::range_error(std::string("map: no mapping for ") + + boost::lexical_cast<std::string>(t)); + } + + template <class T, class N> + typename map<T, N>::const_iterator map<T, N>::xfind(const T& t) const + { + if (const auto&& ires = find(t); ires != map_.end()) + return ires; + else + throw std::range_error(std::string("map: no mapping for ") + + boost::lexical_cast<std::string>(t)); + } + + template <class T, class N> + typename map<T, N>::key_compare map<T, N>::key_comp() const + { + return map_.key_comp(); + } + + template <class T, class N> N map<T, N>::operator()(const T& t) const + { + return xfind(t)->second; + } + + template <class T, class N> + template <typename Container> + requires ConstIterableType<Container, T> + std::vector<N> map<T, N>::operator()(const Container& ts) const + { + std::vector<N> res; + for (const T& t : ts) + res.emplace_back(operator()(t)); + return res; + } + + template <class T, class N> + std::ostream& map<T, N>::print(std::ostream& ostr) const + { + for (const value_type& p : *this) + ostr << p.first << " -> " << p.second << misc::iendl; + return ostr; + } + + template <class T, class N> N map<T, N>::operator[](const T& t) const + { + return operator()(t); + } + + template <class T, class N> N& map<T, N>::operator[](const T& t) + { + return map_[t]; + } + + template <class T, class N> typename map<T, N>::iterator map<T, N>::begin() + { + return map_.begin(); + } + + template <class T, class N> typename map<T, N>::iterator map<T, N>::end() + { + return map_.end(); + } + + template <class T, class N> + typename map<T, N>::const_iterator map<T, N>::begin() const + { + return map_.begin(); + } + + template <class T, class N> + typename map<T, N>::const_iterator map<T, N>::end() const + { + return map_.end(); + } + + template <class T, class N> + typename map<T, N>::iterator map<T, N>::lower_bound(const T& k) + { + return map_.lower_bound(k); + } + + template <class T, class N> + typename map<T, N>::const_iterator map<T, N>::lower_bound(const T& k) const + { + return map_.lower_bound(k); + } + + template <class T, class N> + std::pair<typename map<T, N>::iterator, bool> + map<T, N>::insert(const std::pair<const T, N>& x) + { + return map_.insert(x); + } + + template <class T, class N> void map<T, N>::insert(const map& other) + { + map_.insert(other.begin(), other.end()); + } + + template <class T, class N> + template <class... Args> + std::pair<typename map<T, N>::iterator, bool> + map<T, N>::emplace(Args&&... args) + { + return map_.emplace(std::forward<Args>(args)...); + } + + template <class T, class N> bool map<T, N>::empty() const + { + return map_.empty(); + } + + template <class T, class N> size_t map<T, N>::size() const + { + return map_.size(); + } + + template <class T, class N> N map<T, N>::take(const T& t) + { + auto ires = xfind(t); + N elt = ires->second; + map_.erase(ires); + return elt; + } + + template <class T, class N> void map<T, N>::clear() { map_.clear(); } + + template <class T, class N> + std::ostream& operator<<(std::ostream& ostr, const map<T, N>& m) + { + return m.print(ostr); + } + +} // namespace misc diff --git a/tiger-compiler/lib/misc/ref.hh b/tiger-compiler/lib/misc/ref.hh new file mode 100644 index 0000000..8d70a7f --- /dev/null +++ b/tiger-compiler/lib/misc/ref.hh @@ -0,0 +1,91 @@ +/** + ** \file misc/ref.hh + ** \brief Declaration of misc::ref. + **/ + +#pragma once + +#include <memory> + +namespace misc +{ + /// A std::shared_ptr wrapper. + /// + /// Compared to its super type, this implementation provides + /// cast operators, and implicit constructors. + template <typename T> class ref : public std::shared_ptr<T> + { + public: + /// The parent class. + using super_type = std::shared_ptr<T>; + /// The type pointed to. + using element_type = T; + + /// \name Constructors & Destructor. + /// \{ + /** \brief Construct a new reference to the value pointed to by \a other. + ** The new reference shares the property of the object with \a other. */ + template <typename U> ref(const ref<U>& other); + + /** \brief Copy constructor. + ** + ** Although the implementation is subsumed by the previous, more + ** generic one, the C++ standard still mandates this specific + ** signature. Otherwise, the compiler will provide a default + ** implementation, which is of course wrong. Note that the + ** same applies for the assignment operator. */ + ref(const ref<T>& other); + + /** \brief Construct a counted reference to a newly allocated object. + ** The new reference takes the property of the object pointed to + ** by \a p. If \a p is null, then the reference is invalid and + ** must be \c reset() before use. */ + ref(T* p = nullptr); + + /// \name Equality operators. + /// \{ + + /** \brief Reference comparison. + ** Returns true if this points to \a p. */ + bool operator==(const T* p) const; + + /** \brief Reference comparison. + ** Returns false if this points to \a p. */ + bool operator!=(const T* p) const; + + /// \} + + /// \name Assignment operators. + /// \{ + + /** \brief Use default copy-assignment operator. */ + ref<T>& operator=(const ref<T>& r) = default; + + /// \} + + /// \name Casts. + /// \{ + + /** \brief Cast the reference. + ** Return a new reference, possibly throwing an exception if the + ** dynamic_cast is invalid. + **/ + template <typename U> ref<U> cast() const; + + /** \brief Cast the reference (unsafe). + ** Return a new reference, possibly a null reference if the + ** dynamic_cast is invalid. + **/ + template <typename U> ref<U> unsafe_cast() const; + /// \} + + /** \brief Test fellowship. + ** Return true if the reference points to an object which is + ** really of the specified type. + **/ + template <typename U> bool is_a() const; + }; + +} // namespace misc + +#include <misc/ref.hxx> diff --git a/tiger-compiler/lib/misc/ref.hxx b/tiger-compiler/lib/misc/ref.hxx new file mode 100644 index 0000000..ae740fd --- /dev/null +++ b/tiger-compiler/lib/misc/ref.hxx @@ -0,0 +1,71 @@ +/** + ** \file misc/ref.hxx + ** \brief Implementation of misc::ref. + **/ + +#pragma once + +#include <typeinfo> + +#include <misc/ref.hh> + +namespace misc +{ + /*-----------------. + | Ctors and dtor. | + `-----------------*/ + + template <typename T> + template <typename U> + ref<T>::ref(const ref<U>& other) + : super_type(other) + {} + + template <typename T> + ref<T>::ref(const ref<T>& other) + : super_type(other) + {} + + template <typename T> + ref<T>::ref(T* p) + : super_type(p) + {} + + /*---------------------. + | Equality operators. | + `---------------------*/ + + template <typename T> bool ref<T>::operator==(const T* other) const + { + return this->get() == other; + } + + template <typename T> bool ref<T>::operator!=(const T* other) const + { + return !(*this == other); + } + + /*--------. + | Casts. | + `--------*/ + + template <typename T> template <typename U> ref<U> ref<T>::unsafe_cast() const + { + ref<U> res; + (std::dynamic_pointer_cast<U, element_type>(*this)).swap(res); + return res; + } + + template <typename T> template <typename U> ref<U> ref<T>::cast() const + { + if (!this->get() || !this->is_a<U>()) + throw std::bad_cast(); + return unsafe_cast<U>(); + } + + template <typename T> template <typename U> bool ref<T>::is_a() const + { + return dynamic_cast<U*>(this->get()); + } + +} // namespace misc diff --git a/tiger-compiler/lib/misc/scoped-map.hh b/tiger-compiler/lib/misc/scoped-map.hh new file mode 100644 index 0000000..a193e24 --- /dev/null +++ b/tiger-compiler/lib/misc/scoped-map.hh @@ -0,0 +1,51 @@ +/** + ** \file misc/scoped-map.hh + ** \brief Declaration of misc::scoped_map. + ** + ** This implements a stack of dictionnaries. Each time a scope is + ** opened, a new dictionnary is added on the top of the stack; the + ** dictionary is removed when the scope is closed. Lookup of keys + ** is done in the last added dictionnary first (LIFO). + ** + ** In particular this class is used to implement symbol tables. + **/ + +#pragma once + +#include <map> +#include <ostream> +#include <stack> +#include <vector> + +namespace misc +{ + template <typename Key, typename Data> class scoped_map + { + // FIXME DONE: Some code was deleted here. + public: + // initializes a new stack with an empty map on top as the first scope + scoped_map() + { + scopes_.push_back(std::map<Key,Data>()); + } + void put(const Key& key, const Data& value); + Data get(const Key& key) const requires(!std::is_pointer_v<Data>); + Data get(const Key& key) const requires(std::is_pointer_v<Data>); + std::ostream& dump(std::ostream& ostr) const; + void scope_begin(); + void scope_end(); + + private: + std::vector<std::map<Key, Data>> scopes_; + }; + + template <typename Key, typename Data> + std::ostream& operator<<(std::ostream& ostr, + const scoped_map<Key, Data>& tbl); + + // FIXME DONE: Some code was deleted here. + // heehee + +} // namespace misc + +#include <misc/scoped-map.hxx> diff --git a/tiger-compiler/lib/misc/scoped-map.hxx b/tiger-compiler/lib/misc/scoped-map.hxx new file mode 100644 index 0000000..14258b9 --- /dev/null +++ b/tiger-compiler/lib/misc/scoped-map.hxx @@ -0,0 +1,86 @@ +/** \file misc/scoped-map.hxx + ** \brief Implementation of misc::scoped_map. + */ + +#include <misc/scoped-map.hh> + +#pragma once + +#include <sstream> +#include <stdexcept> +#include <type_traits> + +#include <ranges> +#include <misc/algorithm.hh> +#include <misc/contract.hh> +#include <misc/indent.hh> + +namespace misc +{ + // FIXME DONE: Some code was deleted here. + + template <typename Key, typename Data> + void scoped_map<Key, Data>::put(const Key& key, const Data& value) + { + scopes_.back().insert_or_assign(key, value); + } + + template <typename Key, typename Data> + Data scoped_map<Key, Data>::get(const Key& key) const + requires(std::is_pointer_v<Data>) + { + if (auto data = scopes_.back().find(key); data != scopes_.back().end()) + return data->second; + return nullptr; + } + + template <typename Key, typename Data> + Data scoped_map<Key, Data>::get(const Key& key) const + requires(!std::is_pointer_v<Data>) + { + if (auto data = scopes_.back().find(key); data != scopes_.back().end()) + return data->second; + throw std::range_error("Key is not in scope."); + } + + template <typename Key, typename Data> + std::ostream& scoped_map<Key, Data>::dump(std::ostream& ostr) const + { + ostr << "Printing\n"; + const char* sep = "-------------------------\n"; + int depth = 0; + for (auto scope : scopes_) + { + ostr << sep << "Scope " << depth++ << incendl; + for (auto kvp : scope) + { + ostr << kvp.first << " -> " << kvp.second << iendl; + } + ostr << decendl << sep; + } + ostr << "endprint\n"; + return ostr; + } + template <typename Key, typename Data> + void scoped_map<Key, Data>::scope_begin() + { + std::map<Key, Data> new_scope; + for (auto kvp : scopes_.back()) + { + new_scope.insert_or_assign(kvp.first, kvp.second); + } + scopes_.push_back(new_scope); + } + template <typename Key, typename Data> void scoped_map<Key, Data>::scope_end() + { + scopes_.pop_back(); + } + + template <typename Key, typename Data> + inline std::ostream& operator<<(std::ostream& ostr, + const scoped_map<Key, Data>& tbl) + { + return tbl.dump(ostr); + } + +} // namespace misc diff --git a/tiger-compiler/lib/misc/select-const.hh b/tiger-compiler/lib/misc/select-const.hh new file mode 100644 index 0000000..23ea6d0 --- /dev/null +++ b/tiger-compiler/lib/misc/select-const.hh @@ -0,0 +1,42 @@ +/** + ** \file misc/select-const.hh + ** \brief Select between a non const or a const type. + */ + +#pragma once + +namespace misc +{ + /*------------------. + | const selectors. | + `------------------*/ + + /// Return \a T constified. + template <typename T> struct constify_traits + { + using type = const T; + }; + + /// Return \a T as is. + template <typename T> struct id_traits + { + using type = T; + }; + + /*------------------. + | select_iterator. | + `------------------*/ + + /// The iterator over a non const structure is plain iterator. + template <typename T> struct select_iterator + { + using type = typename T::iterator; + }; + + /// The iterator over a const structure is a const_iterator. + template <typename T> struct select_iterator<const T> + { + using type = typename T::const_iterator; + }; + +} //namespace misc diff --git a/tiger-compiler/lib/misc/separator.hh b/tiger-compiler/lib/misc/separator.hh new file mode 100644 index 0000000..228e4eb --- /dev/null +++ b/tiger-compiler/lib/misc/separator.hh @@ -0,0 +1,43 @@ +/** + ** \file misc/separator.hh + ** \brief Output containers with a separator between items. + */ + +#pragma once + +#include <iosfwd> +#include <utility> + +namespace misc +{ + template <class C, typename S> class separator + { + public: + separator(const C& c, const S& s); + std::ostream& operator()(std::ostream& o) const; + + private: + const C& container_; + const S& separator_; + }; + + /// Shorthand to create separator(c, s). + template <class C, typename S> + separator<C, S> separate(const C& c, const S& s); + + /// Shorthand to create separator(c, '\n'). + template <class C> separator<C, char> separate(const C& c); + + /// Output the separator object \a s on \a ostr. + template <class C, typename S> + inline std::ostream& operator<<(std::ostream& ostr, const separator<C, S>& s); + + /// Output the pair \a p on \a ostr. + /// Usefull if you want to to use a std::pair("something", misc::iendl) + /// as your separator. + template <class A, typename B> + inline std::ostream& operator<<(std::ostream& ostr, const std::pair<A, B>& p); + +} // namespace misc + +#include <misc/separator.hxx> diff --git a/tiger-compiler/lib/misc/separator.hxx b/tiger-compiler/lib/misc/separator.hxx new file mode 100644 index 0000000..103b78d --- /dev/null +++ b/tiger-compiler/lib/misc/separator.hxx @@ -0,0 +1,55 @@ +/** + ** \file misc/separator.hxx + ** \brief Output containers with a separator between items. + */ + +#pragma once + +#include <ostream> + +#include <misc/deref.hh> +#include <misc/separator.hh> + +namespace misc +{ + template <class C, class S> + inline separator<C, S>::separator(const C& c, const S& s) + : container_(c) + , separator_(s) + {} + + template <class C, typename S> + inline std::ostream& separator<C, S>::operator()(std::ostream& o) const + { + for (auto i = container_.begin(); i != container_.end(); ++i) + { + if (i != container_.begin()) + o << separator_; + o << deref << *i; + } + return o; + } + + template <class C, typename S> + separator<C, S> separate(const C& c, const S& s) + { + return separator<C, S>(c, s); + } + + template <class C> separator<C, char> separate(const C& c) + { + return separate(c, '\n'); + } + + template <class C, typename S> + inline std::ostream& operator<<(std::ostream& o, const separator<C, S>& s) + { + return s(o); + } + + template <class A, typename B> + inline std::ostream& operator<<(std::ostream& o, const std::pair<A, B>& p) + { + return o << p.first << p.second; + } +} // namespace misc diff --git a/tiger-compiler/lib/misc/set.hh b/tiger-compiler/lib/misc/set.hh new file mode 100644 index 0000000..5ded61b --- /dev/null +++ b/tiger-compiler/lib/misc/set.hh @@ -0,0 +1,146 @@ +/** + ** \file misc/set.hh + ** \brief misc::set: wrapper around std::set. + ** + ** Set class is a std::set wrapper used to facilitate set + ** operations such as '+' (union) and '-' and also redefine + ** functions such as set_union, set_intersection etc. in + ** order to be more specific and more simple to handle set for our + ** purpose. + **/ + +#pragma once + +#include <algorithm> +#include <iosfwd> +#include <set> +#include <vector> +#include <misc/concepts.hh> + +namespace misc +{ + template <typename Key, + typename Compare = std::less<Key>, + typename Allocator = std::allocator<Key>> + /** + ** \brief The class misc::set is wrapper around std::set. + ** + ** Because Doxygen doesn't handle template + ** parameters names mix we keep the shorter version, + ** so K for Key, C for Compare and A for Allocator. */ + class set : public std::set<Key, Compare, Allocator> + { + public: + /// \name typedefs + /// \{ + using set_type = typename std::set<Key, Compare, Allocator>; + + using iterator = typename set_type::iterator; + using const_iterator = typename set_type::const_iterator; + using reverse_iterator = typename set_type::reverse_iterator; + using const_reverse_iterator = typename set_type::const_reverse_iterator; + using size_type = typename set_type::size_type; + using const_reference = typename set_type::const_reference; + /// \} + + /// \name constructor(s)/destructor. + /// \{ + + explicit set() = default; + + template <typename Iter> + requires Iterator<Iter, Key> + explicit set(Iter first, Iter last); + + /* Useful to convert a container (e.g. std::vector) in misc::set. */ + template <typename Container> + requires ConstIterableType<Container, Key> + explicit set(const Container v); + + /// \} + + /** \name Element vs. set. + \{ */ + + /// Is \a k part of \a this set? + bool has(const Key& k) const; + + /** \brief Return the addition of \a data into \a this. + \a data must not yet be part of \a this. */ + set operator+(const Key& data) const; + + /** \brief Insert \a data in \a this. + \a data must not yet be part of \a this. */ + set& operator+=(const Key& data); + + /** \brief Return the removal of \a data into \a this. + \a data must be part of \a this. */ + set operator-(const Key& data) const; + + /** \brief Remove \a data from \a this. + \a data must be part of \a this. */ + set& operator-=(const Key& data); + + /// \} + + /** \name Set vs. set. + \{ */ + + /// Union with another set \a s. + // FIXME: Deprecate this use, it ought to be direct sum. + set operator+(const set<Key, Compare, Allocator>& s) const; + + /// In place union with another set \a s. + set& operator+=(const set<Key, Compare, Allocator>& s); + + /// Subtraction with another set \a s. + set operator-(const set<Key, Compare, Allocator>& s) const; + + /// In place subtraction with another set \a s. + set& operator-=(const set<Key, Compare, Allocator>& s); + + /// Union with another set \a s. + set operator|(const set& s) const; + + /// In place union with another set \a s. + set& operator|=(const set& s); + + /// Intersection with another set \a s. + set operator&(const set& s) const; + + /// In place intersection with another set \a s. + set& operator&=(const set& s); + + /// \} + }; // class set + + template <typename Key, typename Compare, typename Allocator> + inline set<Key, Compare, Allocator> + set_difference(const set<Key, Compare, Allocator>& s1, + const set<Key, Compare, Allocator>& s2); + + template <typename Key, typename Compare, typename Allocator> + inline set<Key, Compare, Allocator> + set_intersection(const set<Key, Compare, Allocator>& s1, + const set<Key, Compare, Allocator>& s2); + + template <typename Key, typename Compare, typename Allocator> + inline set<Key, Compare, Allocator> + set_union(const set<Key, Compare, Allocator>& s1, + const set<Key, Compare, Allocator>& s2); + + /* Print a human-dump for debugging. + + Warning: this method requires that type Key overloads the operator + '<<'. If it is not the case do it or remove set print method + and << operator (see below). */ + template <typename Key, typename Compare, typename Allocator> + inline std::ostream& operator<<(std::ostream& ostr, + const set<Key, Compare, Allocator>& s); + + template <typename Key, typename Compare, typename Allocator> + inline bool operator%(const Key& k, const set<Key, Compare, Allocator>& s); + +} // namespace misc + +#include <misc/set.hxx> diff --git a/tiger-compiler/lib/misc/set.hxx b/tiger-compiler/lib/misc/set.hxx new file mode 100644 index 0000000..9645c95 --- /dev/null +++ b/tiger-compiler/lib/misc/set.hxx @@ -0,0 +1,208 @@ +/** + ** \file misc/set.hxx + ** \brief misc::set: wrapper around std::set. + **/ + +#pragma once + +#include <algorithm> +#include <ostream> + +#include <misc/contract.hh> +#include <misc/set.hh> + +namespace misc +{ + template <class Key, class Compare, class Allocator> + template <typename Iter> + requires Iterator<Iter, Key> + inline set<Key, Compare, Allocator>::set(Iter first, Iter last) + { + this->insert(first, last); + } + + /* Useful to convert a std::vector or other Compare in misc::set. */ + template <class Key, class Compare, class Allocator> + template <typename Container> + requires ConstIterableType<Container, Key> + inline set<Key, Compare, Allocator>::set(const Container v) + { + for (const Key& x : v) + this->insert(x); + } + + template <class Key, class Compare, class Allocator> + inline bool set<Key, Compare, Allocator>::has(const Key& k) const + { + return set_type::find(k) != this->end(); + } + + /* \brief Return the addition of \a data into \a this. + \a data must not yet be part of \a this. */ + template <class Key, class Compare, class Allocator> + inline set<Key, Compare, Allocator> + set<Key, Compare, Allocator>::operator+(const Key& data) const + { + precondition(!(data % *this)); + set<Key, Compare, Allocator> res(*this); + res.insert(data); + return res; + } + + /* \brief Insert \a data in \a this. + \a data must not yet be part of \a this. */ + template <class Key, class Compare, class Allocator> + inline set<Key, Compare, Allocator>& + set<Key, Compare, Allocator>::operator+=(const Key& data) + { + precondition(!(data % *this)); + this->insert(data); + return *this; + } + + /* \brief Return the removal of \a data into \a this. + \a data must be part of \a this. */ + template <class Key, class Compare, class Allocator> + inline set<Key, Compare, Allocator> + set<Key, Compare, Allocator>::operator-(const Key& data) const + { + precondition(data % *this); + set<Key, Compare, Allocator> res(*this); + res.erase(data); + return res; + } + + /* \brief Remove \a data from \a this. + \a data must be part of \a this. */ + template <class Key, class Compare, class Allocator> + inline set<Key, Compare, Allocator>& + set<Key, Compare, Allocator>::operator-=(const Key& data) + { + precondition(data % *this); + this->erase(data); + return *this; + } + + // Union with another set \a s. + // FIXME: Deprecate this use, it ought to be direct sum. + template <class Key, class Compare, class Allocator> + inline set<Key, Compare, Allocator> set<Key, Compare, Allocator>::operator+( + const set<Key, Compare, Allocator>& s) const + { + return set_union(*this, s); + } + + // In place union with another set \a s. + template <class Key, class Compare, class Allocator> + inline set<Key, Compare, Allocator>& set<Key, Compare, Allocator>::operator+=( + const set<Key, Compare, Allocator>& s) + { + return *this = *this + s; + } + + // Subtraction with another set \a s. + template <class Key, class Compare, class Allocator> + inline set<Key, Compare, Allocator> set<Key, Compare, Allocator>::operator-( + const set<Key, Compare, Allocator>& s) const + { + return set_difference(*this, s); + } + + // In place subtraction with another set \a s. + template <class Key, class Compare, class Allocator> + inline set<Key, Compare, Allocator>& set<Key, Compare, Allocator>::operator-=( + const set<Key, Compare, Allocator>& s) + { + *this = *this - s; + return *this; + } + + // Union with another set \a s. + template <class Key, class Compare, class Allocator> + inline set<Key, Compare, Allocator> set<Key, Compare, Allocator>::operator|( + const set<Key, Compare, Allocator>& s) const + { + return *this + s; + } + + // In place union with another set \a s. + template <class Key, class Compare, class Allocator> + inline set<Key, Compare, Allocator>& set<Key, Compare, Allocator>::operator|=( + const set<Key, Compare, Allocator>& s) + { + return *this += s; + } + + // Intersection with another set \a s. + template <class Key, class Compare, class Allocator> + inline set<Key, Compare, Allocator> set<Key, Compare, Allocator>::operator&( + const set<Key, Compare, Allocator>& s) const + { + return set_intersection(*this, s); + } + + // In place intersection with another set \a s. + template <class Key, class Compare, class Allocator> + inline set<Key, Compare, Allocator>& set<Key, Compare, Allocator>::operator&=( + const set<Key, Compare, Allocator>& s) + { + return *this = *this & s; + } + + template <class Key, class Compare, class Allocator> + inline set<Key, Compare, Allocator> + set_difference(const set<Key, Compare, Allocator>& s1, + const set<Key, Compare, Allocator>& s2) + { + set<Key, Compare, Allocator> res; + // Specifying the key_comp to use is required: without it, the + // first set is properly ordered using its own key_comp, but + // neither the second set nor the resulting set are ordering + // using it. Even if s1, s2, and res do use the same Compare! + std::set_difference(s1.begin(), s1.end(), s2.begin(), s2.end(), + inserter(res, res.begin()), s1.key_comp()); + return res; + } + + template <class Key, class Compare, class Allocator> + inline set<Key, Compare, Allocator> + set_intersection(const set<Key, Compare, Allocator>& s1, + const set<Key, Compare, Allocator>& s2) + { + set<Key, Compare, Allocator> res; + std::set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(), + inserter(res, res.begin()), s1.key_comp()); + return res; + } + + template <class Key, class Compare, class Allocator> + inline set<Key, Compare, Allocator> + set_union(const set<Key, Compare, Allocator>& s1, + const set<Key, Compare, Allocator>& s2) + { + set<Key, Compare, Allocator> res; + std::set_union(s1.begin(), s1.end(), s2.begin(), s2.end(), + inserter(res, res.begin()), s1.key_comp()); + return res; + } + + template <class Key, class Compare, class Allocator> + inline std::ostream& operator<<(std::ostream& ostr, + const set<Key, Compare, Allocator>& s) + { + for (auto i = s.begin(); i != s.end();) + { + ostr << *i; + if (++i != s.end()) + ostr << ", "; + } + return ostr; + } + + template <class Key, class Compare, class Allocator> + inline bool operator%(const Key& k, const set<Key, Compare, Allocator>& s) + { + return s.has(k); + } + +} // namespace misc diff --git a/tiger-compiler/lib/misc/singleton.hh b/tiger-compiler/lib/misc/singleton.hh new file mode 100644 index 0000000..609136f --- /dev/null +++ b/tiger-compiler/lib/misc/singleton.hh @@ -0,0 +1,28 @@ +/** + ** \file misc/singleton.hh + ** \brief Generic singleton + */ + +#pragma once + +namespace misc +{ + template <typename T> class Singleton + { + // FIXME DONE: Some code was deleted here. + protected: + Singleton() = default; + + private: + Singleton(const Singleton<T>&) = delete; + Singleton& operator=(const Singleton<T>&) = delete; + + public: + static T& instance() + { + static T instance_; + return instance_; + } + }; + +} // namespace misc diff --git a/tiger-compiler/lib/misc/symbol.cc b/tiger-compiler/lib/misc/symbol.cc new file mode 100644 index 0000000..edd8c86 --- /dev/null +++ b/tiger-compiler/lib/misc/symbol.cc @@ -0,0 +1,32 @@ +/** + ** \file misc/symbol.cc + ** \brief Implementation of misc::symbol. + */ + +#include <sstream> +#include <string> + +#include <misc/symbol.hh> + +namespace misc +{ + symbol::symbol(const std::string& s) + : unique<std::string>(s) + {} + + symbol::symbol(const char* s) + : unique<std::string>(std::string(s)) + {} + + symbol symbol::fresh() { return fresh("a"); } + + symbol symbol::fresh(const symbol& s) + { + /// Counter of unique symbols. + static unsigned counter_ = 0; + std::string str = s.get() + "_" + std::to_string(counter_); + ++counter_; + return symbol(str); + } + +} // namespace misc diff --git a/tiger-compiler/lib/misc/symbol.hh b/tiger-compiler/lib/misc/symbol.hh new file mode 100644 index 0000000..6d0a1e1 --- /dev/null +++ b/tiger-compiler/lib/misc/symbol.hh @@ -0,0 +1,77 @@ +/** + ** \file misc/symbol.hh + ** \brief Definition of misc::symbol. + */ + +#pragma once + +#include <iosfwd> +#include <set> +#include <string> + +#include <misc/unique.hh> + +namespace misc +{ + /** \brief Define symbol class. + ** + ** Map any string to a unique reference. + ** This allows to avoid an "strcmp()" style comparison of strings: + ** reference comparison is much faster. + */ + class symbol : public unique<std::string> + { + using super_type = unique<std::string>; + /// The type "set of strings". + using string_set_type = super_type::object_set_type; + /// The type for the size of string map. + using string_size_type = string_set_type::size_type; + + /** \name Ctor & Dtor. + ** \{ */ + public: + /** \brief Construct a symbol. + ** \param s referenced string */ + symbol(const std::string& s); + /** \brief Construct a symbol. + ** \param s referenced string */ + symbol(const char* s = ""); + /** \brief Construct a symbol. + ** \param s symbol to copy. */ + constexpr symbol(const symbol& s) = default; + /** \} */ + + /** \name Operators. + ** \{ */ + public: + /** \brief Assign a symbol to this symbol. + ** \param rhs symbol to copy. */ + symbol& operator=(const symbol& rhs); + + /** \brief Compare two symbol for equality. + ** \param rhs string to compare with. */ + bool operator==(const symbol& rhs) const; + /** \brief Compare two symbol for inequality. + ** \param rhs string to compare with. *//* */ + bool operator!=(const symbol& rhs) const; + /** \} */ + + public: + /** \name Factory methods. + ** \{ */ + /** \brief Create a new unique symbol. */ + static symbol fresh(); + /** \brief Create a new unique symbol, forged from \a s. */ + static symbol fresh(const symbol& s); + /** \} */ + }; + + /** \brief Intercept output stream redirection. + ** \param ostr the destination output stream + ** \param the a reference to the symbol to redirect + */ + std::ostream& operator<<(std::ostream& ostr, const symbol& the); + +} // namespace misc + +#include <misc/symbol.hxx> diff --git a/tiger-compiler/lib/misc/symbol.hxx b/tiger-compiler/lib/misc/symbol.hxx new file mode 100644 index 0000000..c31cb8c --- /dev/null +++ b/tiger-compiler/lib/misc/symbol.hxx @@ -0,0 +1,39 @@ +/** + ** \file misc/symbol.hxx + ** \brief Inline implementation of misc::symbol. + */ + +#pragma once + +#include <misc/symbol.hh> + +namespace misc +{ + inline symbol& symbol::operator=(const symbol& rhs) + { + // FIXME DONE: Some code was deleted here. + if (this != &rhs) + { + super_type::operator=(rhs); + } + return *this; + } + + inline bool symbol::operator==(const symbol& rhs) const + { + // FIXME DONE: Some code was deleted here. + return super_type::operator==(rhs); + } + + inline bool symbol::operator!=(const symbol& rhs) const + { + // FIXME DONE: Some code was deleted here. + return !(*this == rhs); + } + + inline std::ostream& operator<<(std::ostream& ostr, const symbol& the) + { + // FIXME DONE: Some code was deleted here. + return ostr << the.get(); + } +} // namespace misc diff --git a/tiger-compiler/lib/misc/timer.cc b/tiger-compiler/lib/misc/timer.cc new file mode 100644 index 0000000..703930f --- /dev/null +++ b/tiger-compiler/lib/misc/timer.cc @@ -0,0 +1,208 @@ +/** + ** \file misc/timer.cc + ** \brief Implementation for misc/timer.hh. + */ + +#include <iomanip> +#include <sys/times.h> +#include <unistd.h> + +#include <misc/contract.hh> +#include <misc/timer.hh> + +namespace misc +{ + /*-----------------. + | timer::time_var. | + `-----------------*/ + + timer::time_var::time_var() + : initial(true) + {} + + void timer::time_var::start() + { + struct tms tms; + + begin.wall = times(&tms); + begin.user = tms.tms_utime; + begin.sys = tms.tms_stime; + + if (initial) + { + initial = false; + first = begin; + } + } + + void timer::time_var::stop() + { + struct tms tms; + + last.wall = times(&tms); + last.user = tms.tms_utime; + last.sys = tms.tms_stime; + elapsed.wall += last.wall - begin.wall; + elapsed.user += last.user - begin.user; + elapsed.sys += last.sys - begin.sys; + } + + bool timer::time_var::is_zero() + { + return elapsed.wall == 0 && elapsed.user == 0 && elapsed.sys == 0; + } + + /*--------. + | timer. | + `--------*/ + + timer::timer() + : dump_stream(nullptr) + {} + + // Duplicate a timer. No tasks should be running. + timer::timer(const timer& rhs) + : intmap(rhs.intmap) + , total(rhs.total) + , dump_stream(rhs.dump_stream) + { + precondition(rhs.tasks.empty()); + + for (const task_map_type::value_type& p : rhs.tasksmap) + if (tasksmap.find(p.first) == tasksmap.end()) + tasksmap[p.first] = new time_var(*p.second); + } + + timer::~timer() + { + // Print information if automatic dumping has been enabled. + if (dump_stream) + { + // Consider that if the tasks were not properly closed, then + // stop was not invoked either. + if (!tasks.empty()) + { + do + pop(); + while (!tasks.empty()); + stop(); + } + dump(*dump_stream); + } + + // Deallocate all our time_var's. + for (task_map_type::value_type& p : tasksmap) + delete p.second; + } + + void timer::name(int i, const std::string& task_name) + { + intmap[i] = task_name; + } + + void timer::timeinfo(long time, long total_time, std::ostream& out) + { + out << std::setiosflags(std::ios::left) << std::setw(6) + << std::setprecision(6) << float(time) / clocks_per_sec + << std::resetiosflags(std::ios::left) << " (" << std::setw(5) + << std::setprecision(3) + << (total_time ? float(time) * 100 / total_time : float(time)) << "%) "; + } + + void timer::dump(std::ostream& out) + { + out << "Execution times (seconds)\n"; + for (const task_map_type::value_type& p : tasksmap) + { + if (!p.second->is_zero()) + { + out << " " << p.first << std::setw(26 - p.first.length()) << ": "; + timeinfo(p.second->elapsed.user, total.elapsed.user, out); + out << " "; + timeinfo(p.second->elapsed.sys, total.elapsed.sys, out); + out << " "; + timeinfo(p.second->elapsed.wall, total.elapsed.wall, out); + out << '\n'; + } + } + out << '\n'; + + out << "Cumulated times (seconds)\n"; + for (const task_map_type::value_type& p : tasksmap) + { + if (p.second->last.wall != p.second->first.wall) + { + out << " " << p.first << std::setw(26 - p.first.length()) << ": "; + timeinfo(p.second->last.user - p.second->first.user, + total.elapsed.user, out); + out << " "; + timeinfo(p.second->last.sys - p.second->first.sys, + total.elapsed.sys, out); + out << " "; + timeinfo(p.second->last.wall - p.second->first.wall, + total.elapsed.wall, out); + out << '\n'; + } + } + out << '\n'; + + out << " TOTAL (seconds)" << std::setw(11) << ": " + + << std::setiosflags(std::ios::left) << std::setw(7) + << float(total.elapsed.user) / clocks_per_sec << std::setw(11) + << "user," + + << std::setw(7) << float(total.elapsed.sys) / clocks_per_sec + << std::setw(11) << "system," + + << std::setw(7) << float(total.elapsed.wall) / clocks_per_sec << "wall" + + << std::resetiosflags(std::ios::left) << std::endl; + } + + void timer::push(const std::string& task_name) + { + time_var* current; + + // If the stack is not empty, set the elapsed time for the current + // task. + if (!tasks.empty()) + tasks.top()->stop(); + + if (tasksmap.find(task_name) == tasksmap.end()) + tasksmap[task_name] = new time_var; + + current = tasksmap[task_name]; + tasks.push(current); + current->start(); + } + + void timer::pop() + { + precondition(!tasks.empty()); + + // Set the elapsed time for the current task before popping it. + tasks.top()->stop(); + tasks.pop(); + + // Set the start time of the previous task to the current time. + if (!tasks.empty()) + tasks.top()->start(); + } + + timer& timer::operator<<(const timer& rhs) + { + // No task should be running when merging timers. + precondition(rhs.tasks.empty()); + + for (const task_map_type::value_type& p : rhs.tasksmap) + if (tasksmap.find(p.first) == tasksmap.end()) + tasksmap[p.first] = new time_var(*p.second); + + intmap.insert(rhs.intmap.begin(), rhs.intmap.end()); + return *this; + } + + const long timer::clocks_per_sec = sysconf(_SC_CLK_TCK); + +} // namespace misc diff --git a/tiger-compiler/lib/misc/timer.hh b/tiger-compiler/lib/misc/timer.hh new file mode 100644 index 0000000..5310b58 --- /dev/null +++ b/tiger-compiler/lib/misc/timer.hh @@ -0,0 +1,139 @@ +/** + ** \file misc/timer.hh + ** \brief timer: Timing nested tasks. + */ + +#pragma once + +#include <iosfwd> +#include <iostream> +#include <map> +#include <stack> +#include <string> + +namespace misc +{ + /// Timing nested tasks. + class timer + { + public: + timer(); + timer(const timer& rhs); + ~timer(); + + /// Start a sub timer for a named task. + /// \param name a constant string which is the task's name + void push(const std::string& name); + + /// Start a sub timer with an integer corresponding to a task. + /// \see push() + /// \see name() + void push(int i); + + /// Stop the current task and ensure its name matches the string + /// passed as argument. + /// \param task_name The name of the task to stop. + void pop(const std::string& task_name); + + /// \see pop() + /// \see pop(const std::string name) + void pop(int i); + + /// Stop the current task's timer(the last task pushed on the stack). + void pop(); + + /// Write results. + /// \param out An output stream, set to std::cerr by default. + void dump(std::ostream& out = std::cerr); + + /// Enable automatic information dumping upon destruction of the + /// timer on stream \a out. + void dump_on_destruction(std::ostream& out); + + /// Assign name \a task_name to task number \a i. + void name(int i, const std::string& task_name); + + /// Start the timer. + /// \see stop() + void start(); + + /// Stop the timer. + /// \see start() + void stop(); + + /// \brief Import timer. + /// + /// Import tasks defined in \a rhs. The total execution time of + /// \a rhs is ignored. + /// + /// \pre No task should be running in \a rhs. + timer& operator<<(const timer& rhs); + + private: + class time_var; + class time + { + friend class timer; + friend class timer::time_var; + + public: + time(); + + time& operator+=(const time& rhs); + + private: + long user; + long sys; + long wall; + }; + + class time_var + { + public: + time_var(); + + void start(); + void stop(); + + bool is_zero(); + + time begin; + time elapsed; + time first; + time last; + bool initial; + }; + + /// Write formatted timing results on \a out. + void timeinfo(long time, long total_time, std::ostream& out); + + using task_map_type = std::map<const std::string, time_var*>; + + /// Time information associated to each task name. + task_map_type tasksmap; + + /// Stack of timed tasks. + std::stack<time_var*> tasks; + + /// Dictionnary mapping an integer to a task name. + /// \see push(int) + std::map<int, std::string> intmap; + + /// Total time measured by the timer. + /// \see start() + /// \see stop() + time_var total; + + /// A potential stream onto which results are dumped when the + /// timer is destroyed. If this pointer is null, no action is + /// taken during the destruction of the timer. + std::ostream* dump_stream; + + /// Number of clocks ticks per second, set according to the system + /// timing function used. + static const long clocks_per_sec; + }; + +} // namespace misc + +#include <misc/timer.hxx> diff --git a/tiger-compiler/lib/misc/timer.hxx b/tiger-compiler/lib/misc/timer.hxx new file mode 100644 index 0000000..6e4ceb5 --- /dev/null +++ b/tiger-compiler/lib/misc/timer.hxx @@ -0,0 +1,50 @@ +/** + ** \file misc/timer.hxx + ** \brief Inline methods for misc/timer.hh. + */ + +#pragma once + +#include <misc/contract.hh> +#include <misc/timer.hh> + +namespace misc +{ + inline void timer::push(int i) + { + precondition(this->intmap.find(i) != this->intmap.end()); + this->push(this->intmap[i]); + } + + inline void timer::pop([[maybe_unused]] const std::string& task_name) + { + precondition(this->tasksmap[task_name] == this->tasks.top()); + this->pop(); + } + + inline void timer::pop(int i) { this->pop(this->intmap[i]); } + + inline void timer::dump_on_destruction(std::ostream& out) + { + this->dump_stream = &out; + } + + inline void timer::start() { this->total.start(); } + + inline void timer::stop() { this->total.stop(); } + + inline timer::time::time() + : user(0) + , sys(0) + , wall(0) + {} + + inline timer::time& timer::time::operator+=(const time& rhs) + { + this->wall += rhs.wall; + this->user += rhs.user; + this->sys += rhs.sys; + return *this; + } + +} // namespace misc diff --git a/tiger-compiler/lib/misc/unique.hh b/tiger-compiler/lib/misc/unique.hh new file mode 100644 index 0000000..27cd05e --- /dev/null +++ b/tiger-compiler/lib/misc/unique.hh @@ -0,0 +1,85 @@ +/** + ** \file misc/unique.hh + ** \brief Definition of misc::unique. + */ + +#pragma once + +#include <iosfwd> +#include <set> + +namespace misc +{ + /** \brief Define \c unique class. + ** + ** Implementation of the flyweight pattern. + ** Map identical objects to a unique reference. + */ + template <typename T, class C = std::less<T>> class unique + { + protected: + /// The unique's object type. + using object_set_type = std::set<T, C>; + /// The type for the size of the unique set. + using object_size_type = typename object_set_type::size_type; + + public: + using value_type = unique; + using data_type = T; + + /** \name Ctor & Dtor. + ** \{ */ + /** \brief Construct a \c unique. + ** \param s referenced object */ + unique(const data_type& s); + /** \brief Construct a \c unique. + ** \param u \c unique to copy. */ + constexpr unique(const unique& u) = default; + virtual ~unique() = default; + /** \} */ + + /** \name Accessors. + ** \{ */ + /// The object referenced by \c this. + const data_type& get() const; + operator const data_type&() const; + + /// The number of referenced objects. + static object_size_type object_map_size(); + /** \} */ + + /** \name Operators. + ** \{ */ + /** \brief Assign a \c unique to this \c unique. + ** \param rhs \c unique to copy. */ + value_type& operator=(const value_type& rhs); + + /** \brief Compare two \c unique for equality. + ** \param rhs \c unique to compare with. */ + bool operator==(const value_type& rhs) const; + /** \brief Compare two \c unique for inequality. + ** \param rhs \c unique to compare with. */ + bool operator!=(const value_type& rhs) const; + /** \brief Compare two \c unique for order. + ** \param rhs \c unique to compare with. */ + bool operator<(const value_type& rhs) const; + /** \} */ + + protected: + /// Return the set of uniques. + static object_set_type& object_set_instance(); + + /// Pointer to the unique referenced object. + const data_type* obj_; + }; + + /** \brief Intercept output stream redirection. + ** \param ostr the destination output stream + ** \param u a reference to the unique to redirect + */ + template <typename T, class C> + std::ostream& operator<<(std::ostream& ostr, const unique<T, C>& u); + +} // namespace misc + +#include <misc/unique.hxx> diff --git a/tiger-compiler/lib/misc/unique.hxx b/tiger-compiler/lib/misc/unique.hxx new file mode 100644 index 0000000..a8560fa --- /dev/null +++ b/tiger-compiler/lib/misc/unique.hxx @@ -0,0 +1,95 @@ +/** + ** \file misc/unique.hxx + ** \brief Inline implementation of misc::unique. + */ + +#pragma once + +#include <misc/contract.hh> +#include <misc/unique.hh> + +namespace misc +{ + template <typename T, class C> unique<T, C>::unique(const data_type& s) + // FIXME DONE: Some code was deleted here (Initializations). + /** \brief Following the Flyweight design pattern, set the attribute to a + unique reference of value s. You might want to check out std::set methods + on cppreference.com. */ + { + if (object_set_instance().insert(s).second) + { + obj_ = &(*(object_set_instance().find(s))); + } + else + { + obj_ = &(*(object_set_instance().find(s))); + } + } + + template <typename T, class C> + typename unique<T, C>::object_set_type& unique<T, C>::object_set_instance() + { + // FIXME DONE: Some code was deleted here (Classical Singleton pattern, a la Scott Meyers'). + /** \brief Create a persistent instance of a set which would hold each value. */ + static object_set_type res{}; + return res; + } + + template <typename T, class C> + typename unique<T, C>::object_size_type unique<T, C>::object_map_size() + { + // FIXME DONE: Some code was deleted here. + return object_set_instance().size(); + } + + template <typename T, class C> + inline const typename unique<T, C>::data_type& unique<T, C>::get() const + { + // FIXME DONE: Some code was deleted here. + return *obj_; + } + + template <typename T, class C> + inline unique<T, C>::operator const data_type&() const + { + // FIXME DONE: Some code was deleted here. + return *obj_; + } + + template <typename T, class C> + inline typename unique<T, C>::value_type& + unique<T, C>::operator=(const value_type& rhs) + { + if (this != &rhs) + obj_ = rhs.obj_; + return *this; + } + + template <typename T, class C> + inline bool unique<T, C>::operator==(const value_type& rhs) const + { + return obj_ == rhs.obj_; + } + + template <typename T, class C> + inline bool unique<T, C>::operator!=(const value_type& rhs) const + { + return !operator==(rhs); + } + + template <typename T, class C> + inline bool unique<T, C>::operator<(const value_type& rhs) const + { + C cmp; + assertion(obj_); + assertion(rhs.obj_); + return cmp(*obj_, *rhs.obj_); + } + + template <typename T, class C> + inline std::ostream& operator<<(std::ostream& ostr, const unique<T, C>& the) + { + return ostr << the.get(); + } + +} // namespace misc diff --git a/tiger-compiler/lib/misc/variant.hh b/tiger-compiler/lib/misc/variant.hh new file mode 100644 index 0000000..71a8c1a --- /dev/null +++ b/tiger-compiler/lib/misc/variant.hh @@ -0,0 +1,111 @@ +/** + ** \file misc/variant.hh + ** \brief Interface of misc::variant. + ** + ** misc::variant is a wrapper over std::variant that adds + ** conversion operators to the original standard variant class. + ** misc::variant is used just like std::variant, and you + ** won't be disturbed when using it. + **/ + +#pragma once + +#include <variant> + +namespace misc +{ + // Defining some concepts sepecific to variants: + + // Type T can be converted into another type in Ts, + // used to set the variant's value. + template <typename T, typename... Ts> + concept ContainsTypeSet = + std::disjunction<std::is_convertible<T, Ts>...>::value; + + // A type in Ts can be converted into T, used to get the variant's value. + template <typename T, typename... Ts> + concept ContainsTypeGet = + std::disjunction<std::is_convertible<Ts, T>...>::value; + + /// Type V is a visitor on each Ts. + template <typename V, typename... Ts> + concept Visits = std::conjunction<std::is_invocable<V, Ts>...>::value; + + /// A wrapper over std::variant supporting conversion operators. + /// + /// Constraints: + /// - No type may be const-qualified. + /// - Each type must be unique. + /// - The first type must be default constructible. + /// + /// Proper declaration form: + /// misc::variant<T, T1, ..., Tn> + + template <typename T, typename... Ts> + class variant : public std::variant<T, Ts...> + { + public: + /// The type of this variant. + using self_type = variant<T, Ts...>; + /// Super type. + + using super_type = std::variant<T, Ts...>; + + /// Constructors. + /// \{ + variant() = default; + + template <typename U> + requires ContainsTypeSet<U, T, Ts...> + variant(const U& rhs); + /// \} + + template <typename U> + requires ContainsTypeSet<U, T, Ts...> + self_type& operator=(const U&); + + /// \brief Convert this variant to a value of type \a U. + /// + /// This conversion relies on std::get. In particular, if the + /// conversion fails, a std::bad_variant_access exception is thrown. + template <typename U> + requires ContainsTypeGet<U, T, Ts...> + operator U&(); + + /// Likewise, const version. + template <typename U> + requires ContainsTypeGet<U, T, Ts...> + operator const U&() const; + + /** \brief Visit variants of this class. + ** std::visit does not handle classes inheriting from std::variant, hence + ** these wrappers. + ** \{ */ + template <typename V> + requires Visits<V, T, Ts...> + auto visit(V&& visitor) const; + + template <typename V, class... Variants> + static auto visit(V&& visitor, Variants&&... vars); + /** \} */ + }; + + // Here add variadic template recursion on std::get + template <typename T, typename... Ts> + std::ostream& operator<<(std::ostream& os, const variant<T, Ts...>& obj); + + class PrintVisitor + { + public: + PrintVisitor(std::ostream& os) + : os_(os) + {} + + template <typename T> std::ostream& operator()(const T& value) const; + + private: + std::ostream& os_; + }; +} // namespace misc + +#include <misc/variant.hxx> diff --git a/tiger-compiler/lib/misc/variant.hxx b/tiger-compiler/lib/misc/variant.hxx new file mode 100644 index 0000000..1f7feab --- /dev/null +++ b/tiger-compiler/lib/misc/variant.hxx @@ -0,0 +1,77 @@ +/** + ** \file misc/variant.hxx + ** \brief Implementation of misc::variant. + **/ + +#pragma once + +#include <variant> + +#include <type_traits> +#include <misc/variant.hh> + +namespace misc +{ + template <typename T, typename... Ts> + template <typename U> + requires ContainsTypeSet<U, T, Ts...> + variant<T, Ts...>::variant(const U& rhs) + : super_type(rhs) + {} + + template <typename T, typename... Ts> + template <typename U> + requires ContainsTypeSet<U, T, Ts...> + variant<T, Ts...>& variant<T, Ts...>::operator=(const U& rhs) + { + // Don't assign to oneself. + if (static_cast<const void*>(&rhs) != static_cast<const void*>(this)) + super_type::operator=(rhs); + return *this; + } + + template <typename T, typename... Ts> + template <typename U> + requires ContainsTypeGet<U, T, Ts...> + variant<T, Ts...>::operator U&() + { + return std::get<U>(*this); + } + + template <typename T, typename... Ts> + template <typename U> + requires ContainsTypeGet<U, T, Ts...> + variant<T, Ts...>::operator const U&() const + { + // FIXME DONE: Some code was deleted here. + return std::get<U>(*this); + } + + template <typename T, typename... Ts> + template <typename V> + requires Visits<V, T, Ts...> + auto variant<T, Ts...>::visit(V&& visitor) const + { + return std::visit(visitor, static_cast<super_type>(*this)); + } + + template <typename T, typename... Ts> + template <typename V, class... Variants> + auto variant<T, Ts...>::visit(V&& visitor, Variants&&... vars) + { + return std::visit(visitor, static_cast<super_type>(vars)...); + } + + template <typename T, typename... Ts> + std::ostream& operator<<(std::ostream& os, const variant<T, Ts...>& obj) + { + PrintVisitor pv(os); + return std::visit(pv, static_cast<const std::variant<T, Ts...>>(obj)); + } + + template <typename T> + std::ostream& PrintVisitor::operator()(const T& value) const + { + return os_ << value; + } +} // namespace misc diff --git a/tiger-compiler/lib/misc/vector.hh b/tiger-compiler/lib/misc/vector.hh new file mode 100644 index 0000000..2207552 --- /dev/null +++ b/tiger-compiler/lib/misc/vector.hh @@ -0,0 +1,105 @@ +/** + ** \file misc/vector.hh + ** \brief Declaration of misc::vector + */ + +#pragma once + +#include <algorithm> +#include <iosfwd> +#include <vector> + +namespace misc +{ + template <class Ident_> class vector : public std::vector<Ident_> + { + public: + /// Super class type. + using super_type = std::vector<Ident_>; + + /// \name Constructors and destructor. + /// \{ + /// Build an empty vector. + vector(); + + /// Build a one-element vector. + vector(const Ident_& t); + + /// Build a two-element vector. + vector(const Ident_& t1, const Ident_& t2); + + /// Concat an element and a vector. + vector(const Ident_& t, const vector& v); + + // Build a vector from an initializer list. + vector(std::initializer_list<Ident_> l); + + /// Build a vector from 2 iterators. + template <class InputIterator> vector(InputIterator b, InputIterator e); + + /// Build a vector fro, another container. + template <class C> vector(const C& c); + /// \} + + /// \name Content management. + /// \{ + /// Is \a data part of \a this vector? + bool has(const Ident_& data) const; + + /// Prepend \a v into \a this and return the new iterator. + typename super_type::iterator prepend(const vector<Ident_>& v); + typename super_type::iterator prepend(const vector<Ident_>&& v); + + /// Append \a v into \a this and return the new iterator. + typename super_type::iterator append(const vector<Ident_>& v); + typename super_type::iterator append(const vector<Ident_>&& v); + + /// Append \a v into \a this. + vector operator+(const vector& v) const; + /// Append \a v into \a this. + vector& operator+=(const vector& v); + + /// Append \a v into \a this from it and return the new iterator. + template <typename Iterator> + typename super_type::iterator position_append(const vector& v, Iterator it); + template <typename Iterator> + typename super_type::iterator position_append(const vector&& v, + Iterator it); + + /** \brief Remove \a data from \a this. + \a data must be part of \a this. */ + vector& remove(const Ident_& data); + /// \} + + virtual std::ostream& dump(std::ostream& ostr) const; + }; + + /// Output \a v onto \a ostr. + template <class Ident_> + std::ostream& operator<<(std::ostream& ostr, const vector<Ident_>& v); + + template <typename T> + auto append_and_move(std::vector<T>& res, std::vector<T>&& to_append); + + template <typename T> + auto append_and_move(std::vector<T>& res, std::vector<T>& to_append); + + template <typename T> + auto prepend_and_move(std::vector<T>& res, std::vector<T>&& to_prepend); + + template <typename T> + auto prepend_and_move(std::vector<T>& res, std::vector<T>& to_prepend); + + template <typename T, typename Iterator> + auto position_append_and_move(std::vector<T>& res, + Iterator res_iterator, + std::vector<T>&& to_append); + + template <typename T, typename Iterator> + auto position_append_and_move(std::vector<T>& res, + Iterator res_iterator, + std::vector<T>& to_append); + +} // namespace misc + +#include <misc/vector.hxx> diff --git a/tiger-compiler/lib/misc/vector.hxx b/tiger-compiler/lib/misc/vector.hxx new file mode 100644 index 0000000..5c516cb --- /dev/null +++ b/tiger-compiler/lib/misc/vector.hxx @@ -0,0 +1,193 @@ +/** -*- C++ -*- + ** \file misc/vector.hxx + ** \brief Implementation of misc::vector. + */ + +#pragma once + +#include <ostream> + +#include <misc/contract.hh> + +namespace misc +{ + template <class T> vector<T>::vector() {} + + template <class T> vector<T>::vector(const T& t) { this->push_back(t); } + + template <class T> vector<T>::vector(const T& t1, const T& t2) + { + this->push_back(t1); + this->push_back(t2); + } + + template <class T> vector<T>::vector(const T& t, const vector<T>& v) + { + this->push_back(t); + this->insert(this->end(), v.begin(), v.end()); + } + + template <class T> vector<T>::vector(std::initializer_list<T> l) + { + this->insert(this->begin(), l.begin(), l.end()); + } + + template <class T> + template <class InputIterator> + vector<T>::vector(InputIterator b, InputIterator e) + : super_type(b, e) + {} + + template <class T> + template <class C> + vector<T>::vector(const C& c) + : super_type(c.begin(), c.end()) + {} + + template <class T> bool vector<T>::has(const T& data) const + { + return std::ranges::find(*this, data) != this->end(); + } + + template <class T> + inline typename std::vector<T>::iterator + vector<T>::prepend(const vector<T>& v) + { + auto it = this->insert(this->begin(), v.begin(), v.end()); + return it; + } + + template <class T> + inline typename std::vector<T>::iterator + vector<T>::prepend(const vector<T>&& v) + { + auto it = this->insert(this->begin(), v.begin(), v.end()); + return it; + } + + template <class T> + inline typename std::vector<T>::iterator vector<T>::append(const vector<T>& v) + { + auto it = this->insert(this->end(), v.begin(), v.end()); + return it; + } + + template <class T> + inline typename std::vector<T>::iterator + vector<T>::append(const vector<T>&& v) + { + auto it = this->insert(this->end(), v.begin(), v.end()); + return it; + } + + template <class T> + inline vector<T> vector<T>::operator+(const vector<T>& v) const + { + vector<T> res = *this; + return res += v; + } + + template <class T> inline vector<T>& vector<T>::operator+=(const vector<T>& v) + { + this->insert(this->end(), v.begin(), v.end()); + return *this; + } + + template <class T> + template <typename Iterator> + inline typename std::vector<T>::iterator + vector<T>::position_append(const vector<T>& v, Iterator it) + { + auto new_it = this->insert(it, v.begin(), v.end()); + return new_it; + } + + template <class T> + template <typename Iterator> + inline typename std::vector<T>::iterator + vector<T>::position_append(const vector<T>&& v, Iterator it) + { + auto new_it = this->insert(it, v.begin(), v.end()); + return new_it; + } + + template <class T> inline vector<T>& vector<T>::remove(const T& data) + { + precondition(has(data)); + this->erase(std::ranges::find(*this, data)); + return *this; + } + + template <class T> std::ostream& vector<T>::dump(std::ostream& ostr) const + { + for (typename super_type::const_iterator i = this->begin(); + i != this->end(); ++i) + ostr << (i == this->begin() ? "" : ", ") << *i; + return ostr; + } + + template <class T> + std::ostream& operator<<(std::ostream& ostr, const vector<T>& v) + { + return v.dump(ostr); + } + + template <typename T> + auto append_and_move(std::vector<T>& v1, std::vector<T>&& v2) + { + auto it = v1.insert(v1.end(), make_move_iterator(v2.begin()), + make_move_iterator(v2.end())); + v2.clear(); + return it; + } + + template <typename T> + auto append_and_move(std::vector<T>& v1, std::vector<T>& v2) + { + auto it = v1.insert(v1.end(), make_move_iterator(v2.begin()), + make_move_iterator(v2.end())); + v2.clear(); + return it; + } + + template <typename T> + auto prepend_and_move(std::vector<T>& v1, std::vector<T>&& v2) + { + auto it = v1.insert(v1.begin(), make_move_iterator(v2.begin()), + make_move_iterator(v2.end())); + v2.clear(); + return it; + } + + template <typename T> + auto prepend_and_move(std::vector<T>& v1, std::vector<T>& v2) + { + auto it = v1.insert(v1.begin(), make_move_iterator(v2.begin()), + make_move_iterator(v2.end())); + v2.clear(); + return it; + } + + template <typename T, typename Iterator> + auto position_append_and_move(std::vector<T>& v1, + Iterator v1_iterator, + std::vector<T>&& v2) + { + auto it = v1.insert(v1_iterator, make_move_iterator(v2.begin()), + make_move_iterator(v2.end())); + v2.clear(); + return it; + } + + template <typename T, typename Iterator> + auto position_append_and_move(std::vector<T>& v1, + Iterator v1_iterator, + std::vector<T>& v2) + { + auto it = v1.insert(v1_iterator, make_move_iterator(v2.begin()), + make_move_iterator(v2.end())); + v2.clear(); + return it; + } + +} // namespace misc diff --git a/tiger-compiler/lib/misc/xalloc.hh b/tiger-compiler/lib/misc/xalloc.hh new file mode 100644 index 0000000..1d819ef --- /dev/null +++ b/tiger-compiler/lib/misc/xalloc.hh @@ -0,0 +1,135 @@ +/** + ** \file misc/xalloc.hh + ** \brief Declaration of misc::xalloc. + **/ + +#pragma once + +#include <functional> +#include <iostream> + +namespace misc +{ + /// Defines the operator() for the classes get_type, set_type and swap_type. + struct iomanipulator + { + virtual ~iomanipulator() = default; + virtual void operator()(std::ostream& o) const = 0; + }; + + /** \brief Launch the iomanipulator action on o. + ** + ** Call the operator() of g (so set, get or swap the + ** xalloced data with data_). + */ + std::ostream& operator<<(std::ostream& o, const iomanipulator& g); + + /** \brief Allocate slots in std::ostreams. + ** + ** Used to store flags in streams so that calling print + ** with additional parameters is no longer required (for + ** example in ast/libast.hh). + ** + ** Set, get or swap data. + ** The idea is to build the right inner class with set, get or + ** swap methods, then use this class as a parameter of operator<<. + ** + ** `StoredType` must be `CopyConstructible`. If it's not + ** `DefaultConstructible`, then arguments for its constructor are + ** to be passed to the `xalloc` constructor. + */ + template <class StoredType> class xalloc + { + private: + /** \brief Handle the data to put in the xalloced place. + ** + ** This inner class is used only with the set method. + */ + class set_type : public iomanipulator + { + public: + /// Set data_ to data. + set_type(const xalloc& slot, StoredType& data); + /// Set the data in the xalloced place. + void operator()(std::ostream& ostr) const override; + + private: + /// The xalloced data. + const xalloc& slot_; + /// Data to put in the stream. + StoredType& data_; + }; + + /** \brief Handle the data to get from the xalloced place. + ** + ** This inner class is used only with the get method. + */ + class get_type : public iomanipulator + { + public: + /// Set data_ to data. + get_type(const xalloc& slot, StoredType& data); + + /// Get the data from the xalloced place. + void operator()(std::ostream& ostr) const override; + + private: + /// The xalloced data. + const xalloc& slot_; + /// Variable in which we return the xalloc data. + StoredType& data_; + }; + + /** \brief Swap the data stored in the stream for a given one. + ** + ** This inner class is used only with the swap method. + */ + class swap_type : public iomanipulator + { + public: + /// Set data_ to data. + swap_type(const xalloc& slot, StoredType& data); + + /// Swap the data from the xalloced place for a given one. + void operator()(std::ostream& ostr) const override; + + private: + /// The xalloced data. + const xalloc& slot_; + /// Variable in which we store the data to be swapped. + StoredType& data_; + }; + + /// The index of the slot. + const long int index_; + // The model that is used to create new instances. + const StoredType model_; + + public: + /// Allocates the slot. + template <typename... Args> xalloc(Args&&... args); + + static void + deallocate(std::ios_base::event type, std::ios_base& ios, int index); + + /// The xalloc index. + long int index() const; + /// The stored data as an lvalue. + StoredType& operator()(std::ostream& ostr) const; + + /// A setter. + /// \param data where to find the value when the setter is executed. + set_type set(StoredType& data) const; + + /// A getter. + /// \param data where to store the value when the getter is executed. + get_type get(StoredType& data) const; + + /// A swapper. + /// \param data value to exchange when the swapper is executed. + swap_type swap(StoredType& data) const; + }; + +} // namespace misc + +#include <misc/xalloc.hxx> diff --git a/tiger-compiler/lib/misc/xalloc.hxx b/tiger-compiler/lib/misc/xalloc.hxx new file mode 100644 index 0000000..8ae692a --- /dev/null +++ b/tiger-compiler/lib/misc/xalloc.hxx @@ -0,0 +1,135 @@ +/** + ** \file misc/xalloc.hxx + ** \brief Implementation for misc/xalloc.hh. + */ + +#pragma once + +#include <utility> + +#include <misc/xalloc.hh> + +namespace misc +{ + /*----------------. + | iomanipulator. | + `----------------*/ + + inline std::ostream& operator<<(std::ostream& o, const iomanipulator& g) + { + g(o); + return o; + } + + /*---------------------. + | xalloc<StoredType>. | + `---------------------*/ + + template <class StoredType> + template <typename... Args> + xalloc<StoredType>::xalloc(Args&&... args) + : index_(std::ios::xalloc()) + , model_{std::forward<Args>(args)...} + {} + + template <class StoredType> long int xalloc<StoredType>::index() const + { + return index_; + } + + template <class StoredType> + StoredType& xalloc<StoredType>::operator()(std::ostream& ostr) const + { + void*& storage_ptr = ostr.pword(index_); + + if (!storage_ptr) + { + storage_ptr = new StoredType{model_}; + ostr.register_callback(xalloc::deallocate, index_); + } + + return *static_cast<StoredType*>(storage_ptr); + } + + template <class StoredType> + void xalloc<StoredType>::deallocate(std::ios_base::event type, + std::ios_base& ios, + int index) + { + if (type == std::ios_base::erase_event) + { + StoredType* ptr = static_cast<StoredType*>(ios.pword(index)); + delete ptr; + } + } + + /*-----------. + | set_type. | + `-----------*/ + + template <class StoredType> + xalloc<StoredType>::set_type::set_type(const xalloc& slot, StoredType& data) + : slot_(slot) + , data_(data) + {} + + template <class StoredType> + void xalloc<StoredType>::set_type::operator()(std::ostream& ostr) const + { + slot_(ostr) = data_; + } + + template <class StoredType> + typename xalloc<StoredType>::set_type + xalloc<StoredType>::set(StoredType& data) const + { + return set_type(*this, data); + } + + /*-----------. + | get_type. | + `-----------*/ + + template <class StoredType> + xalloc<StoredType>::get_type::get_type(const xalloc& slot, StoredType& data) + : slot_(slot) + , data_(data) + {} + + template <class StoredType> + void xalloc<StoredType>::get_type::operator()(std::ostream& ostr) const + { + data_ = slot_(ostr); + } + + template <class StoredType> + typename xalloc<StoredType>::get_type + xalloc<StoredType>::get(StoredType& data) const + { + return get_type(*this, data); + } + + /*------------. + | swap_type. | + `------------*/ + + template <class StoredType> + xalloc<StoredType>::swap_type::swap_type(const xalloc& slot, StoredType& data) + : slot_(slot) + , data_(data) + {} + + template <class StoredType> + void xalloc<StoredType>::swap_type::operator()(std::ostream& ostr) const + { + std::swap(slot_(ostr), data_); + } + + template <class StoredType> + typename xalloc<StoredType>::swap_type + xalloc<StoredType>::swap(StoredType& data) const + { + return swap_type(*this, data); + } + +} // namespace misc |
