#pragma once #include "util.hh" #include #include #include #include using std::string; using std::optional; using boost::format; using std::cout; using std::endl; namespace nix { enum ErrLevel { elWarning , elError }; class ColumnRange { public: unsigned int start; unsigned int len; }; class ErrorInfo; class ErrLine { public: int lineNumber; optional columnRange; optional prevLineOfCode; string errLineOfCode; optional nextLineOfCode; }; class NixCode { public: optional nixFile; optional errLine; ErrLine& ensureErrLine() { if (!this->errLine.has_value()) this->errLine = 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; optional nixCode; optional hint; ErrorInfo& GetEI() { return *this; } static 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 = 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(optional prevloc, string loc, 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(); } }; 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; } // hint format shows templated values in yellow. class hintfmt { public: hintfmt(string format) :fmt(format) {} template hintfmt& operator%(const T &value) { fmt % yellowify(value); return *this; } template friend class AddHint; private: format fmt; }; template class AddHint : private T { public: T& hint(hintfmt &hfmt) { GetEI().hint = optional(hfmt.fmt.str()); return *this; } T& nohint() { GetEI().hint = std::nullopt; return *this; } protected: ErrorInfo& GetEI() { return T::GetEI(); } }; // -------------------------------------------------------- // error types typedef AddName>> StandardError; typedef AddName>> StandardWarning; typedef AddName< AddDescription< AddNixFile< AddLineNumber< AddColumnRange< AddLOC< AddHint>>>>>> MkNixError; typedef AddName< AddDescription< AddNixFile< AddLineNumber< AddColumnRange< AddLOC>>>>> MkNixWarning; // -------------------------------------------------------- // error printing void printErrorInfo(ErrorInfo &einfo); string showErrLine(ErrLine &errLine); void printCodeLines(string &prefix, NixCode &nixCode); }