#ifndef error_hh #define error_hh #include "ansicolor.hh" #include #include #include #include #include "types.hh" #include namespace nix { typedef enum { elWarning, elError } ErrLevel; class ColumnRange { public: unsigned int start; unsigned int len; }; class ErrorInfo; class ErrLine { public: int lineNumber; std::optional columnRange; std::optional prevLineOfCode; string errLineOfCode; std::optional nextLineOfCode; }; class NixCode { public: std::optional nixFile; std::optional errLine; ErrLine& ensureErrLine() { if (!this->errLine.has_value()) this->errLine = std::optional(ErrLine()); return *this->errLine; } }; // ------------------------------------------------- // ErrorInfo. // Forward friend class declarations. "builder classes" template class AddName; template class AddDescription; template class AddNixCode; template class AddNixFile; template class AddErrLine; template class AddLineNumber; template class AddColumnRange; template class AddLOC; // The error info class itself. class ErrorInfo { public: ErrLevel level; string name; string description; std::optional nixCode; std::optional hint; ErrorInfo& GetEI() { return *this; } static std::optional programName; // give these access to the private constructor, // when they are direct descendants (children but not grandchildren). friend AddName; friend AddDescription; friend AddNixCode; friend AddNixFile; friend AddErrLine; friend AddLineNumber; friend AddColumnRange; friend AddLOC; NixCode& ensureNixCode() { if (!this->nixCode.has_value()) this->nixCode = std::optional(NixCode()); return *this->nixCode; } protected: // constructor is protected, so only the builder classes can create an ErrorInfo. ErrorInfo(ErrLevel level) { this->level = level; } }; // Init as error class EIError : public ErrorInfo { protected: EIError() : ErrorInfo(elError) {} }; // Init as warning class EIWarning : public ErrorInfo { protected: EIWarning() : ErrorInfo(elWarning) {} }; // Builder class definitions. template class AddName : private T { public: T& name(const std::string &name) { GetEI().name = name; return *this; } protected: ErrorInfo& GetEI() { return T::GetEI(); } }; template class AddDescription : private T { public: T& description(const std::string &description) { GetEI().description = description; return *this; } protected: ErrorInfo& GetEI() { return T::GetEI(); } }; template class AddNixFile : private T { public: T& nixFile(string filename) { GetEI().ensureNixCode().nixFile = filename; return *this; } protected: ErrorInfo& GetEI() { return T::GetEI(); } }; template class AddLineNumber : private T { public: T& lineNumber(int lineNumber) { GetEI().ensureNixCode().ensureErrLine().lineNumber = lineNumber; return *this; } protected: ErrorInfo& GetEI() { return T::GetEI(); } }; template class AddColumnRange : private T { public: T& columnRange(unsigned int start, unsigned int len) { GetEI().ensureNixCode().ensureErrLine().columnRange = { start, len }; return *this; } protected: ErrorInfo& GetEI() { return T::GetEI(); } }; template class AddLOC : private T { public: T& linesOfCode(std::optional prevloc, string loc, std::optional nextloc) { GetEI().ensureNixCode().ensureErrLine().prevLineOfCode = prevloc; GetEI().ensureNixCode().ensureErrLine().errLineOfCode = loc; GetEI().ensureNixCode().ensureErrLine().nextLineOfCode = nextloc; return *this; } protected: ErrorInfo& GetEI() { return T::GetEI(); } }; // ---------------------------------------------------------------- // format for hints. same as fmt, except templated values // are always in yellow. template class yellowify { public: yellowify(T &s) : value(s) {} T &value; }; template std::ostream& operator<<(std::ostream &out, const yellowify &y) { return out << ANSI_YELLOW << y.value << ANSI_NORMAL; } class hintformat { public: hintformat(string format) :fmt(format) { fmt.exceptions(boost::io::all_error_bits ^ boost::io::too_many_args_bit); } template hintformat& operator%(const T &value) { fmt % yellowify(value); return *this; } std::string str() const { return fmt.str(); } template friend class AddHint; private: format fmt; }; template inline hintformat hintfmt(const std::string & fs, const Args & ... args) { hintformat f(fs); formatHelper(f, args...); return f; } // the template layer for adding a hint. template class AddHint : private T { public: T& hint(const hintformat &hf) { GetEI().hint = std::optional(hf.str()); return *this; } T& nohint() { GetEI().hint = std::nullopt; return *this; } protected: ErrorInfo& GetEI() { return T::GetEI(); } }; // -------------------------------------------------------- // error types typedef AddName< AddDescription< AddHint< EIError>>> ProgramError; typedef AddName< AddDescription< AddHint< EIWarning>>> ProgramWarning; typedef AddName< AddDescription< AddNixFile< AddLineNumber< AddColumnRange< AddLOC< AddHint< EIError>>>>>>> NixLangError; typedef AddName< AddDescription< AddNixFile< AddLineNumber< AddColumnRange< AddLOC< AddHint< EIWarning>>>>>>> NixLangWarning; // -------------------------------------------------------- // error printing // just to cout for now. void printErrorInfo(ErrorInfo &einfo); } #endif