diff --git a/include/bolt/ConsolePrinter.hpp b/include/bolt/ConsolePrinter.hpp new file mode 100644 index 000000000..40d3c549b --- /dev/null +++ b/include/bolt/ConsolePrinter.hpp @@ -0,0 +1,189 @@ + +#pragma once + +#include + +#include "bolt/ByteString.hpp" +#include "bolt/CST.hpp" +#include "bolt/Type.hpp" + +namespace bolt { + + class Node; + class Type; + class TypeclassSignature; + class Diagnostic; + + enum class Color { + None, + Black, + White, + Red, + Yellow, + Green, + Blue, + Cyan, + Magenta, + }; + + enum StyleFlags : unsigned { + StyleFlags_None = 0, + StyleFlags_Bold = 1 << 0, + StyleFlags_Underline = 1 << 1, + StyleFlags_Italic = 1 << 2, + }; + + class Style { + + unsigned Flags = StyleFlags_None; + + Color FgColor = Color::None; + Color BgColor = Color::None; + + public: + + Color getForegroundColor() const noexcept { + return FgColor; + } + + Color getBackgroundColor() const noexcept { + return BgColor; + } + + void setForegroundColor(Color NewColor) noexcept { + FgColor = NewColor; + } + + void setBackgroundColor(Color NewColor) noexcept { + BgColor = NewColor; + } + + bool hasForegroundColor() const noexcept { + return FgColor != Color::None; + } + + bool hasBackgroundColor() const noexcept { + return BgColor != Color::None; + } + + void clearForegroundColor() noexcept { + FgColor = Color::None; + } + + void clearBackgroundColor() noexcept { + BgColor = Color::None; + } + + bool isUnderline() const noexcept { + return Flags & StyleFlags_Underline; + } + + bool isItalic() const noexcept { + return Flags & StyleFlags_Italic; + } + + bool isBold() const noexcept { + return Flags & StyleFlags_Bold; + } + + void setUnderline(bool Enable) noexcept { + if (Enable) { + Flags |= StyleFlags_Underline; + } else { + Flags &= ~StyleFlags_Underline; + } + } + + void setItalic(bool Enable) noexcept { + if (Enable) { + Flags |= StyleFlags_Italic; + } else { + Flags &= ~StyleFlags_Italic; + } + } + + void setBold(bool Enable) noexcept { + if (Enable) { + Flags |= StyleFlags_Bold; + } else { + Flags &= ~StyleFlags_Bold; + } + } + + void reset() noexcept { + FgColor = Color::None; + BgColor = Color::None; + Flags = 0; + } + + }; + + /** + * Prints any diagnostic message that was added to it to the console. + */ + class ConsolePrinter { + + std::ostream& Out; + + Style ActiveStyle; + + void setForegroundColor(Color C); + void setBackgroundColor(Color C); + void applyStyles(); + + void setBold(bool Enable); + void setItalic(bool Enable); + void setUnderline(bool Enable); + void resetStyles(); + + void writeGutter( + std::size_t GutterWidth, + std::string Text + ); + + void writeHighlight( + std::size_t GutterWidth, + TextRange Range, + Color HighlightColor, + std::size_t Line, + std::size_t LineLength + ); + + void writeExcerpt( + const TextFile& File, + TextRange ToPrint, + TextRange ToHighlight, + Color HighlightColor + ); + + void writeNode(const Node* N); + + void writePrefix(const Diagnostic& D); + void writeBinding(const ByteString& Name); + void writeType(std::size_t I); + void writeType(const Type* Ty, const TypePath& Underline); + void writeType(const Type* Ty); + void writeLoc(const TextFile& File, const TextLoc& Loc); + void writeTypeclassName(const ByteString& Name); + void writeTypeclassSignature(const TypeclassSignature& Sig); + + void write(const std::string_view& S); + void write(std::size_t N); + void write(char C); + + public: + + unsigned ExcerptLinesPre = 2; + unsigned ExcerptLinesPost = 2; + std::size_t MaxTypeSubsitutionCount = 0; + bool PrintFilePosition = true; + bool PrintExcerpts = true; + bool EnableColors = true; + + ConsolePrinter(std::ostream& Out = std::cerr); + + void writeDiagnostic(const Diagnostic& D); + + }; + +} diff --git a/src/ConsolePrinter.cc b/src/ConsolePrinter.cc new file mode 100644 index 000000000..d11aac455 --- /dev/null +++ b/src/ConsolePrinter.cc @@ -0,0 +1,928 @@ + +// FIXME writeExcerpt does not work well with the last line in a file + +#include +#include + +#include "zen/config.hpp" + +#include "bolt/CST.hpp" +#include "bolt/Type.hpp" +#include "bolt/Diagnostics.hpp" +#include "bolt/ConsolePrinter.hpp" + +#define ANSI_RESET "\u001b[0m" +#define ANSI_BOLD "\u001b[1m" +#define ANSI_ITALIC "\u001b[3m" +#define ANSI_UNDERLINE "\u001b[4m" +#define ANSI_REVERSED "\u001b[7m" + +#define ANSI_FG_BLACK "\u001b[30m" +#define ANSI_FG_RED "\u001b[31m" +#define ANSI_FG_GREEN "\u001b[32m" +#define ANSI_FG_YELLOW "\u001b[33m" +#define ANSI_FG_BLUE "\u001b[34m" +#define ANSI_FG_CYAN "\u001b[35m" +#define ANSI_FG_MAGENTA "\u001b[36m" +#define ANSI_FG_WHITE "\u001b[37m" + +#define ANSI_BG_BLACK "\u001b[40m" +#define ANSI_BG_RED "\u001b[41m" +#define ANSI_BG_GREEN "\u001b[42m" +#define ANSI_BG_YELLOW "\u001b[43m" +#define ANSI_BG_BLUE "\u001b[44m" +#define ANSI_BG_CYAN "\u001b[45m" +#define ANSI_BG_MAGENTA "\u001b[46m" +#define ANSI_BG_WHITE "\u001b[47m" + +namespace bolt { + + template + T countDigits(T number) { + if (number == 0) { + return 1; + } + return std::ceil(std::log10(number+1)); + } + + static std::string describe(NodeKind Type) { + switch (Type) { + case NodeKind::Identifier: + return "an identifier starting with a lowercase letter"; + case NodeKind::IdentifierAlt: + return "an identifier starting with a capital letter"; + case NodeKind::CustomOperator: + return "an operator"; + case NodeKind::IntegerLiteral: + return "an integer literal"; + case NodeKind::EndOfFile: + return "end-of-file"; + case NodeKind::BlockStart: + return "the start of a new indented block"; + case NodeKind::BlockEnd: + return "the end of the current indented block"; + case NodeKind::LineFoldEnd: + return "the end of the current line-fold"; + case NodeKind::Assignment: + return "an assignment such as := or +="; + case NodeKind::ExpressionAnnotation: + return "a user-defined annotation"; + case NodeKind::TypeAssertAnnotation: + return "a built-in annotation for a type assertion"; + case NodeKind::TypeclassConstraintExpression: + return "a type class constraint"; + case NodeKind::EqualityConstraintExpression: + return "an equality constraint"; + case NodeKind::QualifiedTypeExpression: + return "a type expression with some constraints"; + case NodeKind::ReferenceTypeExpression: + return "a reference to another type"; + case NodeKind::ArrowTypeExpression: + return "a function type signature"; + case NodeKind::AppTypeExpression: + return "an application of one type to another"; + case NodeKind::VarTypeExpression: + return "a rigid variable"; + case NodeKind::NestedTypeExpression: + return "a type expression wrapped in '(' and ')'"; + case NodeKind::TupleTypeExpression: + return "a tuple type expression"; + case NodeKind::BindPattern: + return "a variable binder"; + case NodeKind::NamedPattern: + return "a pattern for a variant member"; + case NodeKind::TuplePattern: + return "a pattern for a tuple"; + case NodeKind::ListPattern: + return "a pattern for a list"; + case NodeKind::LParen: + return "'('"; + case NodeKind::RParen: + return "')'"; + case NodeKind::LBrace: + return "'['"; + case NodeKind::RBrace: + return "']'"; + case NodeKind::LBracket: + return "'{'"; + case NodeKind::RBracket: + return "'}'"; + case NodeKind::Colon: + return "':'"; + case NodeKind::At: + return "'@'"; + case NodeKind::Comma: + return "','"; + case NodeKind::Equals: + return "'='"; + case NodeKind::StringLiteral: + return "a string literal"; + case NodeKind::Dot: + return "'.'"; + case NodeKind::DotDot: + return "'..'"; + case NodeKind::Tilde: + return "'~'"; + case NodeKind::RArrow: + return "'->'"; + case NodeKind::RArrowAlt: + return "'=>'"; + case NodeKind::PubKeyword: + return "'pub'"; + case NodeKind::LetKeyword: + return "'let'"; + case NodeKind::ForeignKeyword: + return "'foreign'"; + case NodeKind::MutKeyword: + return "'mut'"; + case NodeKind::MatchKeyword: + return "'match'"; + case NodeKind::ReturnKeyword: + return "'return'"; + case NodeKind::TypeKeyword: + return "'type'"; + case NodeKind::IfKeyword: + return "'if'"; + case NodeKind::ElifKeyword: + return "'elif'"; + case NodeKind::ElseKeyword: + return "'else'"; + case NodeKind::StructKeyword: + return "'struct'"; + case NodeKind::EnumKeyword: + return "'enum'"; + case NodeKind::ClassKeyword: + return "'class'"; + case NodeKind::InstanceKeyword: + return "'instance'"; + case NodeKind::LetDeclaration: + return "a let-declaration"; + case NodeKind::CallExpression: + return "a call-expression"; + case NodeKind::InfixExpression: + return "an infix-expression"; + case NodeKind::ReferenceExpression: + return "a reference to a function or variable"; + case NodeKind::MatchExpression: + return "a match-expression"; + case NodeKind::LiteralExpression: + return "a literal expression"; + case NodeKind::MemberExpression: + return "an accessor of a member"; + case NodeKind::IfStatement: + return "an if-statement"; + case NodeKind::IfStatementPart: + return "a branch of an if-statement"; + case NodeKind::VariantDeclaration: + return "a variant"; + default: + ZEN_UNREACHABLE + } + } + + static std::string describe(Token* T) { + switch (T->getKind()) { + case NodeKind::LineFoldEnd: + case NodeKind::BlockStart: + case NodeKind::BlockEnd: + case NodeKind::EndOfFile: + return describe(T->getKind()); + default: + return "'" + T->getText() + "'"; + } + } + + std::string describe(const Type* Ty) { + switch (Ty->getKind()) { + case TypeKind::Var: + { + auto TV = static_cast(Ty); + if (TV->getVarKind() == VarKind::Rigid) { + return static_cast(TV)->Name; + } + return "a" + std::to_string(TV->Id); + } + case TypeKind::Arrow: + { + auto Y = static_cast(Ty); + std::ostringstream Out; + Out << describe(Y->ParamType) << " -> " << describe(Y->ReturnType); + return Out.str(); + } + case TypeKind::Con: + { + auto Y = static_cast(Ty); + return Y->DisplayName; + } + case TypeKind::App: + { + auto Y = static_cast(Ty); + return describe(Y->Op) + " " + describe(Y->Arg); + } + case TypeKind::Tuple: + { + std::ostringstream Out; + auto Y = static_cast(Ty); + Out << "("; + if (Y->ElementTypes.size()) { + auto Iter = Y->ElementTypes.begin(); + Out << describe(*Iter++); + while (Iter != Y->ElementTypes.end()) { + Out << ", " << describe(*Iter++); + } + } + Out << ")"; + return Out.str(); + } + case TypeKind::TupleIndex: + { + auto Y = static_cast(Ty); + return describe(Y->Ty) + "." + std::to_string(Y->I); + } + case TypeKind::Nil: + return "{}"; + case TypeKind::Absent: + return "Abs"; + case TypeKind::Present: + { + auto Y = static_cast(Ty); + return describe(Y->Ty); + } + case TypeKind::Field: + { + auto Y = static_cast(Ty); + std::ostringstream out; + out << "{ " << Y->Name << ": " << describe(Y->Ty); + Ty = Y->RestTy; + while (Ty->getKind() == TypeKind::Field) { + auto Y = static_cast(Ty); + out << "; " + Y->Name + ": " + describe(Y->Ty); + Ty = Y->RestTy; + } + if (Ty->getKind() != TypeKind::Nil) { + out << "; " + describe(Ty); + } + out << " }"; + return out.str(); + } + } + } + + void writeForegroundANSI(Color C, std::ostream& Out) { + switch (C) { + case Color::None: + break; + case Color::Black: + Out << ANSI_FG_BLACK; + break; + case Color::White: + Out << ANSI_FG_WHITE; + break; + case Color::Red: + Out << ANSI_FG_RED; + break; + case Color::Yellow: + Out << ANSI_FG_YELLOW; + break; + case Color::Green: + Out << ANSI_FG_GREEN; + break; + case Color::Blue: + Out << ANSI_FG_BLUE; + break; + case Color::Cyan: + Out << ANSI_FG_CYAN; + break; + case Color::Magenta: + Out << ANSI_FG_MAGENTA; + break; + } + } + + void writeBackgroundANSI(Color C, std::ostream& Out) { + switch (C) { + case Color::None: + break; + case Color::Black: + Out << ANSI_BG_BLACK; + break; + case Color::White: + Out << ANSI_BG_WHITE; + break; + case Color::Red: + Out << ANSI_BG_RED; + break; + case Color::Yellow: + Out << ANSI_BG_YELLOW; + break; + case Color::Green: + Out << ANSI_BG_GREEN; + break; + case Color::Blue: + Out << ANSI_BG_BLUE; + break; + case Color::Cyan: + Out << ANSI_BG_CYAN; + break; + case Color::Magenta: + Out << ANSI_BG_MAGENTA; + break; + } + } + + ConsolePrinter::ConsolePrinter(std::ostream& Out): + Out(Out) {} + + void ConsolePrinter::setForegroundColor(Color C) { + ActiveStyle.setForegroundColor(C); + if (!EnableColors) { + return; + } + writeForegroundANSI(C, Out); + } + + void ConsolePrinter::setBackgroundColor(Color C) { + ActiveStyle.setBackgroundColor(C); + if (!EnableColors) { + return; + } + if (C == Color::None) { + Out << ANSI_RESET; + applyStyles(); + } + writeBackgroundANSI(C, Out); + } + + void ConsolePrinter::applyStyles() { + if (ActiveStyle.isBold()) { + Out << ANSI_BOLD; + } + if (ActiveStyle.isUnderline()) { + Out << ANSI_UNDERLINE; + } + if (ActiveStyle.isItalic()) { + Out << ANSI_ITALIC; + } + if (ActiveStyle.hasBackgroundColor()) { + setBackgroundColor(ActiveStyle.getBackgroundColor()); + } + if (ActiveStyle.hasForegroundColor()) { + setForegroundColor(ActiveStyle.getForegroundColor()); + } + } + + void ConsolePrinter::setBold(bool Enable) { + ActiveStyle.setBold(Enable); + if (!EnableColors) { + return; + } + if (Enable) { + Out << ANSI_BOLD; + } else { + Out << ANSI_RESET; + applyStyles(); + } + } + + void ConsolePrinter::setItalic(bool Enable) { + ActiveStyle.setItalic(Enable); + if (!EnableColors) { + return; + } + if (Enable) { + Out << ANSI_ITALIC; + } else { + Out << ANSI_RESET; + applyStyles(); + } + } + + void ConsolePrinter::setUnderline(bool Enable) { + ActiveStyle.setItalic(Enable); + if (!EnableColors) { + return; + } + if (Enable) { + Out << ANSI_UNDERLINE; + } else { + Out << ANSI_RESET; + applyStyles(); + } + } + + void ConsolePrinter::resetStyles() { + ActiveStyle.reset(); + if (EnableColors) { + Out << ANSI_RESET; + } + } + + void ConsolePrinter::writeGutter( + std::size_t GutterWidth, + std::string Text + ) { + ZEN_ASSERT(Text.size() <= GutterWidth); + auto LeadingSpaces = GutterWidth - Text.size(); + Out << " "; + setForegroundColor(Color::Black); + setBackgroundColor(Color::White); + for (std::size_t i = 0; i < LeadingSpaces; i++) { + Out << ' '; + } + Out << Text; + resetStyles(); + Out << " "; + } + + void ConsolePrinter::writeHighlight( + std::size_t GutterWidth, + TextRange Range, + Color HighlightColor, + std::size_t Line, + std::size_t LineLength + ) { + if (Line < Range.Start.Line || Range.End.Line < Line) { + return; + } + Out << " "; + setBackgroundColor(Color::White); + for (std::size_t i = 0; i < GutterWidth; i++) { + Out << ' '; + } + resetStyles(); + Out << ' '; + std::size_t start_column = Range.Start.Line == Line ? Range.Start.Column : 1; + std::size_t end_column = Range.End.Line == Line ? Range.End.Column : LineLength+1; + for (std::size_t i = 1; i < start_column; i++) { + Out << ' '; + } + setForegroundColor(HighlightColor); + if (start_column == end_column) { + Out << "↖"; + } else { + for (std::size_t i = start_column; i < end_column; i++) { + Out << '~'; + } + } + resetStyles(); + Out << '\n'; + } + + void ConsolePrinter::writeExcerpt( + const TextFile& File, + TextRange ToPrint, + TextRange ToHighlight, + Color HighlightColor + ) { + + auto LineCount = File.getLineCount(); + auto Text = File.getText(); + auto StartPos = ToPrint.Start; + auto EndPos = ToPrint.End; + auto StartLine = StartPos.Line-1 > ExcerptLinesPre ? StartPos.Line - ExcerptLinesPre : 1; + auto StartOffset = File.getStartOffsetOfLine(StartLine); + auto EndLine = std::min(LineCount, EndPos.Line + ExcerptLinesPost); + auto EndOffset = File.getEndOffsetOfLine(EndLine); + auto GutterWidth = std::max(2, countDigits(EndLine+1)); + auto HighlightStart = ToHighlight.Start; + auto HighlightEnd = ToHighlight.End; + auto HighlightRange = TextRange { HighlightStart, HighlightEnd }; + + std::size_t CurrColumn = 1; + std::size_t CurrLine = StartLine; + bool AtBlankLine = true; + for (std::size_t I = StartOffset; I < EndOffset; I++) { + auto C = Text[I]; + if (AtBlankLine) { + writeGutter(GutterWidth, std::to_string(CurrLine)); + } + if (C == '\n') { + Out << C; + writeHighlight(GutterWidth, HighlightRange, HighlightColor, CurrLine, CurrColumn); + CurrLine++; + CurrColumn = 1; + AtBlankLine = true; + } else { + AtBlankLine = false; + Out << C; + CurrColumn++; + } + } + + } + + void ConsolePrinter::write(const std::string_view& S) { + Out << S; + } + + void ConsolePrinter::write(char C) { + Out << C; + } + + void ConsolePrinter::write(std::size_t I) { + Out << I; + } + + void ConsolePrinter::writeBinding(const ByteString& Name) { + write("'"); + write(Name); + write("'"); + } + + void ConsolePrinter::writeType(const Type* Ty) { + TypePath Path; + writeType(Ty, Path); + } + + void ConsolePrinter::writeType(const Type* Ty, const TypePath& Underline) { + + setForegroundColor(Color::Green); + + class TypePrinter : public ConstTypeVisitor { + + TypePath Path; + ConsolePrinter& W; + const TypePath& Underline; + + public: + + TypePrinter(ConsolePrinter& W, const TypePath& Underline): + W(W), Underline(Underline) {} + + bool shouldUnderline() const { + return !Underline.empty() && Path == Underline; + } + + void enterType(const Type* Ty) override { + if (shouldUnderline()) { + W.setUnderline(true); + } + } + + void exitType(const Type* Ty) override { + if (shouldUnderline()) { + W.setUnderline(false); + } + } + + void visitAppType(const TApp *Ty) override { + auto Y = static_cast(Ty); + Path.push_back(TypeIndex::forAppOpType()); + visit(Y->Op); + Path.pop_back(); + W.write(" "); + Path.push_back(TypeIndex::forAppArgType()); + visit(Y->Arg); + Path.pop_back(); + } + + void visitVarType(const TVar* Ty) override { + if (Ty->getVarKind() == VarKind::Rigid) { + W.write(static_cast(Ty)->Name); + return; + } + W.write("a"); + W.write(Ty->Id); + } + + void visitConType(const TCon *Ty) override { + W.write(Ty->DisplayName); + } + + void visitArrowType(const TArrow* Ty) override { + Path.push_back(TypeIndex::forArrowParamType()); + visit(Ty->ParamType); + Path.pop_back(); + W.write(" -> "); + Path.push_back(TypeIndex::forArrowReturnType()); + visit(Ty->ReturnType); + Path.pop_back(); + } + + void visitTupleType(const TTuple *Ty) override { + W.write("("); + if (Ty->ElementTypes.size()) { + auto Iter = Ty->ElementTypes.begin(); + Path.push_back(TypeIndex::forTupleElement(0)); + visit(*Iter++); + Path.pop_back(); + std::size_t I = 1; + while (Iter != Ty->ElementTypes.end()) { + W.write(", "); + Path.push_back(TypeIndex::forTupleElement(I++)); + visit(*Iter++); + Path.pop_back(); + } + } + W.write(")"); + } + + void visitTupleIndexType(const TTupleIndex *Ty) override { + Path.push_back(TypeIndex::forTupleIndexType()); + visit(Ty->Ty); + Path.pop_back(); + W.write("."); + W.write(Ty->I); + } + + void visitNilType(const TNil *Ty) override { + W.write("{}"); + } + + void visitAbsentType(const TAbsent *Ty) override { + W.write("Abs"); + } + + void visitPresentType(const TPresent *Ty) override { + Path.push_back(TypeIndex::forPresentType()); + visit(Ty->Ty); + Path.pop_back(); + } + + void visitFieldType(const TField* Ty) override { + W.write("{ "); + W.write(Ty->Name); + W.write(": "); + Path.push_back(TypeIndex::forFieldType()); + visit(Ty->Ty); + Path.pop_back(); + auto Ty2 = Ty->RestTy; + Path.push_back(TypeIndex::forFieldRest()); + std::size_t I = 1; + while (Ty2->getKind() == TypeKind::Field) { + auto Y = static_cast(Ty2); + W.write("; "); + W.write(Y->Name); + W.write(": "); + Path.push_back(TypeIndex::forFieldType()); + visit(Y->Ty); + Path.pop_back(); + Ty2 = Y->RestTy; + Path.push_back(TypeIndex::forFieldRest()); + ++I; + } + if (Ty2->getKind() != TypeKind::Nil) { + W.write("; "); + visit(Ty2); + } + W.write(" }"); + for (auto K = 0; K < I; K++) { + Path.pop_back(); + } + } + + }; + + TypePrinter P { *this, Underline }; + P.visit(Ty); + + resetStyles(); + } + + void ConsolePrinter::writeType(std::size_t I) { + setForegroundColor(Color::Green); + write(I); + resetStyles(); + } + + void ConsolePrinter::writeNode(const Node* N) { + auto Range = N->getRange(); + writeExcerpt(N->getSourceFile()->getTextFile(), Range, Range, Color::Red); + } + + void ConsolePrinter::writeLoc(const TextFile& File, const TextLoc& Loc) { + setForegroundColor(Color::Yellow); + write(File.getPath()); + write(":"); + write(Loc.Line); + write(":"); + write(Loc.Column); + write(":"); + resetStyles(); + } + + void ConsolePrinter::writePrefix(const Diagnostic& D) { + setForegroundColor(Color::Red); + setBold(true); + write("error: "); + resetStyles(); + } + + void ConsolePrinter::writeTypeclassName(const ByteString& Name) { + setForegroundColor(Color::Magenta); + write(Name); + resetStyles(); + } + + void ConsolePrinter::writeTypeclassSignature(const TypeclassSignature& Sig) { + setForegroundColor(Color::Magenta); + write(Sig.Id); + for (auto TV: Sig.Params) { + write(" "); + write(describe(TV)); + } + resetStyles(); + } + + void ConsolePrinter::writeDiagnostic(const Diagnostic& D) { + + switch (D.getKind()) { + + case DiagnosticKind::BindingNotFound: + { + auto E = static_cast(D); + writePrefix(E); + write("binding "); + writeBinding(E.Name); + write(" was not found\n\n"); + if (E.Initiator != nullptr) { + auto Range = E.Initiator->getRange(); + //std::cerr << Range.Start.Line << ":" << Range.Start.Column << "-" << Range.End.Line << ":" << Range.End.Column << "\n"; + writeExcerpt(E.Initiator->getSourceFile()->getTextFile(), Range, Range, Color::Red); + Out << "\n"; + } + break; + } + + case DiagnosticKind::UnexpectedToken: + { + auto E = static_cast(D); + writePrefix(E); + writeLoc(E.File, E.Actual->getStartLoc()); + write(" expected "); + switch (E.Expected.size()) { + case 0: + write("nothing"); + break; + case 1: + write(describe(E.Expected[0])); + break; + default: + auto Iter = E.Expected.begin(); + Out << describe(*Iter++); + NodeKind Prev = *Iter++; + while (Iter != E.Expected.end()) { + write(", "); + write(describe(Prev)); + Prev = *Iter++; + } + write(" or "); + write(describe(Prev)); + break; + } + write(" but instead got "); + write(describe(E.Actual)); + write("\n\n"); + writeExcerpt(E.File, E.Actual->getRange(), E.Actual->getRange(), Color::Red); + write("\n"); + break; + } + + case DiagnosticKind::UnexpectedString: + { + auto E = static_cast(D); + writePrefix(E); + writeLoc(E.File, E.Location); + write(" unexpected '"); + for (auto Chr: E.Actual) { + switch (Chr) { + case '\\': + write("\\\\"); + break; + case '\'': + write("\\'"); + break; + default: + write(Chr); + break; + } + } + write("'\n\n"); + TextRange Range { E.Location, E.Location + E.Actual }; + writeExcerpt(E.File, Range, Range, Color::Red); + write("\n"); + break; + } + + case DiagnosticKind::UnificationError: + { + auto E = static_cast(D); + auto Left = E.OrigLeft->resolve(E.LeftPath); + auto Right = E.OrigRight->resolve(E.RightPath); + writePrefix(E); + write("the types "); + writeType(Left); + write(" and "); + writeType(Right); + write(" failed to match\n\n"); + setForegroundColor(Color::Yellow); + setBold(true); + write(" info: "); + resetStyles(); + write("due to an equality constraint on "); + write(describe(E.Source->getKind())); + write(":\n\n"); + // write(" - left type "); + // writeType(E.OrigLeft, E.LeftPath); + // write("\n"); + // write(" - right type "); + // writeType(E.OrigRight, E.RightPath); + // write("\n\n"); + writeNode(E.Source); + write("\n"); + // if (E.Left != E.OrigLeft) { + // setForegroundColor(Color::Yellow); + // setBold(true); + // write(" info: "); + // resetStyles(); + // write("the type "); + // writeType(E.Left); + // write(" occurs in the full type "); + // writeType(E.OrigLeft); + // write("\n\n"); + // } + // if (E.Right != E.OrigRight) { + // setForegroundColor(Color::Yellow); + // setBold(true); + // write(" info: "); + // resetStyles(); + // write("the type "); + // writeType(E.Right); + // write(" occurs in the full type "); + // writeType(E.OrigRight); + // write("\n\n"); + // } + break; + } + + case DiagnosticKind::TypeclassMissing: + { + auto E = static_cast(D); + writePrefix(E); + write("the type class "); + writeTypeclassSignature(E.Sig); + write(" is missing from the declaration's type signature\n\n"); + writeNode(E.Decl); + write("\n\n"); + break; + } + + case DiagnosticKind::InstanceNotFound: + { + auto E = static_cast(D); + writePrefix(E); + write("a type class instance "); + writeTypeclassName(E.TypeclassName); + write(" "); + writeType(E.Ty); + write(" was not found.\n\n"); + writeNode(E.Source); + write("\n"); + break; + } + + case DiagnosticKind::TupleIndexOutOfRange: + { + auto E = static_cast(D); + writePrefix(E); + write("the index "); + writeType(E.I); + write(" is out of range for tuple "); + writeType(E.Tuple); + break; + } + + case DiagnosticKind::InvalidTypeToTypeclass: + { + auto E = static_cast(D); + writePrefix(E); + write("the type "); + writeType(E.Actual); + write(" was applied to type class names "); + bool First = true; + for (auto Class: E.Classes) { + if (First) First = false; + else write(", "); + writeTypeclassName(Class); + } + write(" but this is invalid\n\n"); + break; + } + + case DiagnosticKind::FieldNotFound: + { + auto E = static_cast(D); + writePrefix(E); + write("the field '"); + write(E.Name); + write("' was required in one type but not found in another\n\n"); + writeNode(E.Source); + write("\n"); + break; + } + + } + + } + +}