diff --git a/.vscode/launch.json b/.vscode/launch.json index a96936a12..e81d80199 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,7 @@ "request": "launch", "name": "Debug", "program": "${workspaceFolder}/build/bolt", - "args": [ "--direct-diagnostics", "verify", "test/checker/local_constraints_polymorphic_variable.bolt" ], + "args": [ "--direct-diagnostics", "verify", "test/checker/wrong_return_type.bolt" ], "cwd": "${workspaceFolder}", "preLaunchTask": "CMake: build" } diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f23b7581..cacbe99b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,7 @@ add_library( #src/Text.cc src/CST.cc src/Diagnostics.cc + src/ConsolePrinter.cc src/Scanner.cc src/Parser.cc src/Types.cc diff --git a/include/bolt/CST.hpp b/include/bolt/CST.hpp index bdfda4e4e..3d890ddc1 100644 --- a/include/bolt/CST.hpp +++ b/include/bolt/CST.hpp @@ -1,9 +1,6 @@ #ifndef BOLT_CST_HPP #define BOLT_CST_HPP -#include -#include -#include #include #include #include @@ -988,6 +985,11 @@ namespace bolt { }; + class AnnotationContainer { + public: + std::vector Annotations; + }; + class ExpressionAnnotation : public Annotation { public: @@ -1058,11 +1060,11 @@ namespace bolt { }; - class TypeExpression : public TypedNode { + class TypeExpression : public TypedNode, AnnotationContainer { protected: - TypeExpression(NodeKind Kind): - TypedNode(Kind) {} + inline TypeExpression(NodeKind Kind, std::vector Annotations = {}): + TypedNode(Kind), AnnotationContainer(Annotations) {} }; @@ -1385,17 +1387,11 @@ namespace bolt { }; - - class Expression : public TypedNode { - public: - - std::vector Annotations; - + class Expression : public TypedNode, public AnnotationContainer { protected: inline Expression(NodeKind Kind, std::vector Annotations = {}): - TypedNode(Kind), Annotations(Annotations) {} - + TypedNode(Kind), AnnotationContainer(Annotations) {} }; @@ -1688,6 +1684,14 @@ namespace bolt { Operator(Operator), Argument(Argument) {} + PrefixExpression( + std::vector Annotations, + Token* Operator, + Expression* Argument + ): Expression(NodeKind::PrefixExpression, Annotations), + Operator(Operator), + Argument(Argument) {} + Token* getFirstToken() const override; Token* getLastToken() const override; @@ -1734,16 +1738,26 @@ namespace bolt { Fields(Fields), RBrace(RBrace) {} + inline RecordExpression( + std::vector Annotations, + class LBrace* LBrace, + std::vector> Fields, + class RBrace* RBrace + ): Expression(NodeKind::RecordExpression, Annotations), + LBrace(LBrace), + Fields(Fields), + RBrace(RBrace) {} + Token* getFirstToken() const override; Token* getLastToken() const override; }; - class Statement : public Node { + class Statement : public Node, public AnnotationContainer { protected: - inline Statement(NodeKind Type): - Node(Type) {} + inline Statement(NodeKind Type, std::vector Annotations = {}): + Node(Type), AnnotationContainer(Annotations) {} }; @@ -1755,12 +1769,18 @@ namespace bolt { ExpressionStatement(class Expression* Expression): Statement(NodeKind::ExpressionStatement), Expression(Expression) {} + ExpressionStatement( + std::vector Annotations, + class Expression* Expression + ): Statement(NodeKind::ExpressionStatement, Annotations), + Expression(Expression) {} + Token* getFirstToken() const override; Token* getLastToken() const override; }; - class IfStatementPart : public Node { + class IfStatementPart : public Node, public AnnotationContainer { public: Token* Keyword; @@ -1779,6 +1799,19 @@ namespace bolt { BlockStart(BlockStart), Elements(Elements) {} + inline IfStatementPart( + std::vector Annotations, + Token* Keyword, + Expression* Test, + class BlockStart* BlockStart, + std::vector Elements + ): Node(NodeKind::IfStatementPart), + AnnotationContainer(Annotations), + Keyword(Keyword), + Test(Test), + BlockStart(BlockStart), + Elements(Elements) {} + Token* getFirstToken() const override; Token* getLastToken() const override; @@ -1810,6 +1843,14 @@ namespace bolt { ReturnKeyword(ReturnKeyword), Expression(Expression) {} + ReturnStatement( + std::vector Annotations, + class ReturnKeyword* ReturnKeyword, + class Expression* Expression + ): Statement(NodeKind::ReturnStatement, Annotations), + ReturnKeyword(ReturnKeyword), + Expression(Expression) {} + Token* getFirstToken() const override; Token* getLastToken() const override; @@ -1894,7 +1935,7 @@ namespace bolt { }; - class LetDeclaration : public TypedNode { + class LetDeclaration : public TypedNode, public AnnotationContainer { Scope* TheScope = nullptr; @@ -1932,6 +1973,27 @@ namespace bolt { TypeAssert(TypeAssert), Body(Body) {} + LetDeclaration( + std::vector Annotations, + class PubKeyword* PubKeyword, + class ForeignKeyword* ForeignKeyword, + class LetKeyword* LetKeyword, + class MutKeyword* MutKeyword, + class Pattern* Pattern, + std::vector Params, + class TypeAssert* TypeAssert, + LetBody* Body + ): TypedNode(NodeKind::LetDeclaration), + AnnotationContainer(Annotations), + PubKeyword(PubKeyword), + ForeignKeyword(ForeignKeyword), + LetKeyword(LetKeyword), + MutKeyword(MutKeyword), + Pattern(Pattern), + Params(Params), + TypeAssert(TypeAssert), + Body(Body) {} + inline Scope* getScope() override { if (TheScope == nullptr) { TheScope = new Scope(this); diff --git a/include/bolt/CSTVisitor.hpp b/include/bolt/CSTVisitor.hpp index 733b010bc..8fa14006f 100644 --- a/include/bolt/CSTVisitor.hpp +++ b/include/bolt/CSTVisitor.hpp @@ -849,11 +849,11 @@ namespace bolt { for (auto A: N->Annotations) { BOLT_VISIT(A); } - for (auto [Name, Dot]: N->ModulePath) { - BOLT_VISIT(Name); - BOLT_VISIT(Dot); - } - BOLT_VISIT(N->Name); + for (auto [Name, Dot]: N->ModulePath) { + BOLT_VISIT(Name); + BOLT_VISIT(Dot); + } + BOLT_VISIT(N->Name); } void visitEachChild(MatchCase* N) { @@ -963,10 +963,16 @@ namespace bolt { } void visitEachChild(ExpressionStatement* N) { + for (auto A: N->Annotations) { + BOLT_VISIT(A); + } BOLT_VISIT(N->Expression); } void visitEachChild(ReturnStatement* N) { + for (auto A: N->Annotations) { + BOLT_VISIT(A); + } BOLT_VISIT(N->ReturnKeyword); BOLT_VISIT(N->Expression); } @@ -978,6 +984,9 @@ namespace bolt { } void visitEachChild(IfStatementPart* N) { + for (auto A: N->Annotations) { + BOLT_VISIT(A); + } BOLT_VISIT(N->Keyword); if (N->Test != nullptr) { BOLT_VISIT(N->Test); @@ -1013,6 +1022,9 @@ namespace bolt { } void visitEachChild(LetDeclaration* N) { + for (auto A: N->Annotations) { + BOLT_VISIT(A); + } if (N->PubKeyword) { BOLT_VISIT(N->PubKeyword); } diff --git a/include/bolt/DiagnosticEngine.hpp b/include/bolt/DiagnosticEngine.hpp index d3f73bdf5..d7018c319 100644 --- a/include/bolt/DiagnosticEngine.hpp +++ b/include/bolt/DiagnosticEngine.hpp @@ -5,29 +5,27 @@ #include #include -#include "bolt/ByteString.hpp" -#include "bolt/CST.hpp" -#include "bolt/Type.hpp" - namespace bolt { + class ConsolePrinter; class Diagnostic; class TypeclassSignature; class Type; class Node; class DiagnosticEngine { + protected: bool HasError = false; + virtual void addDiagnostic(Diagnostic* Diagnostic) = 0; + public: inline bool hasError() const noexcept { return HasError; } - virtual void addDiagnostic(Diagnostic* Diagnostic) = 0; - template void add(Ts&&... Args) { HasError = true; @@ -64,180 +62,17 @@ namespace bolt { }; - 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 ConsoleDiagnostics : public DiagnosticEngine { - std::ostream& Out; + ConsolePrinter& ThePrinter; - Style ActiveStyle; + protected: - 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); + void addDiagnostic(Diagnostic* Diagnostic) override; public: - unsigned ExcerptLinesPre = 2; - unsigned ExcerptLinesPost = 2; - std::size_t MaxTypeSubsitutionCount = 0; - bool PrintFilePosition = true; - bool PrintExcerpts = true; - bool EnableColors = true; - - ConsoleDiagnostics(std::ostream& Out = std::cerr); - - /** - * Assumes the diagnostic is to be owned by this ConsoleDiagnostics. - */ - void addDiagnostic(Diagnostic* Diagnostic); - - void printDiagnostic(const Diagnostic& D); + ConsoleDiagnostics(ConsolePrinter& ThePrinter); }; diff --git a/src/Diagnostics.cc b/src/Diagnostics.cc index 99c7f283f..ed4f85654 100644 --- a/src/Diagnostics.cc +++ b/src/Diagnostics.cc @@ -10,6 +10,7 @@ #include "bolt/Type.hpp" #include "bolt/DiagnosticEngine.hpp" #include "bolt/Diagnostics.hpp" +#include "bolt/ConsolePrinter.hpp" #define ANSI_RESET "\u001b[0m" #define ANSI_BOLD "\u001b[1m" @@ -37,14 +38,6 @@ namespace bolt { - template - T countDigits(T number) { - if (number == 0) { - return 1; - } - return std::ceil(std::log10(number+1)); - } - Diagnostic::Diagnostic(DiagnosticKind Kind): std::runtime_error("a compiler error occurred without being caught"), Kind(Kind) {} @@ -67,855 +60,22 @@ namespace bolt { std::sort(Diagnostics.begin(), Diagnostics.end(), sourceLocLessThan); } - 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::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::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::ReferenceTypeExpression: - return "a type reference"; - 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 function or variable reference"; - 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::ListPattern: - return "a list pattern"; - case NodeKind::TypeAssertAnnotation: - return "an annotation for a type assertion"; - 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; - } - } - DiagnosticStore::~DiagnosticStore() { - // for (auto D: Diagnostics) { - // delete D; - // } - } - - ConsoleDiagnostics::ConsoleDiagnostics(std::ostream& Out): - Out(Out) {} - - void ConsoleDiagnostics::setForegroundColor(Color C) { - ActiveStyle.setForegroundColor(C); - if (!EnableColors) { - return; - } - writeForegroundANSI(C, Out); - } - - void ConsoleDiagnostics::setBackgroundColor(Color C) { - ActiveStyle.setBackgroundColor(C); - if (!EnableColors) { - return; - } - if (C == Color::None) { - Out << ANSI_RESET; - applyStyles(); - } - writeBackgroundANSI(C, Out); - } - - void ConsoleDiagnostics::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()); + for (auto D: Diagnostics) { + delete D; } } - void ConsoleDiagnostics::setBold(bool Enable) { - ActiveStyle.setBold(Enable); - if (!EnableColors) { - return; - } - if (Enable) { - Out << ANSI_BOLD; - } else { - Out << ANSI_RESET; - applyStyles(); - } - } - - void ConsoleDiagnostics::setItalic(bool Enable) { - ActiveStyle.setItalic(Enable); - if (!EnableColors) { - return; - } - if (Enable) { - Out << ANSI_ITALIC; - } else { - Out << ANSI_RESET; - applyStyles(); - } - } - - void ConsoleDiagnostics::setUnderline(bool Enable) { - ActiveStyle.setItalic(Enable); - if (!EnableColors) { - return; - } - if (Enable) { - Out << ANSI_UNDERLINE; - } else { - Out << ANSI_RESET; - applyStyles(); - } - } - - void ConsoleDiagnostics::resetStyles() { - ActiveStyle.reset(); - if (EnableColors) { - Out << ANSI_RESET; - } - } - - void ConsoleDiagnostics::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 ConsoleDiagnostics::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 ConsoleDiagnostics::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 ConsoleDiagnostics::write(const std::string_view& S) { - Out << S; - } - - void ConsoleDiagnostics::write(char C) { - Out << C; - } - - void ConsoleDiagnostics::write(std::size_t I) { - Out << I; - } - - void ConsoleDiagnostics::writeBinding(const ByteString& Name) { - write("'"); - write(Name); - write("'"); - } - - void ConsoleDiagnostics::writeType(const Type* Ty) { - TypePath Path; - writeType(Ty, Path); - } - - void ConsoleDiagnostics::writeType(const Type* Ty, const TypePath& Underline) { - - setForegroundColor(Color::Green); - - class TypePrinter : public ConstTypeVisitor { - - TypePath Path; - ConsoleDiagnostics& W; - const TypePath& Underline; - - public: - - TypePrinter(ConsoleDiagnostics& 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 ConsoleDiagnostics::writeType(std::size_t I) { - setForegroundColor(Color::Green); - write(I); - resetStyles(); - } - - void ConsoleDiagnostics::writeNode(const Node* N) { - auto Range = N->getRange(); - writeExcerpt(N->getSourceFile()->getTextFile(), Range, Range, Color::Red); - } - - void ConsoleDiagnostics::writeLoc(const TextFile& File, const TextLoc& Loc) { - setForegroundColor(Color::Yellow); - write(File.getPath()); - write(":"); - write(Loc.Line); - write(":"); - write(Loc.Column); - write(":"); - resetStyles(); - } - - void ConsoleDiagnostics::writePrefix(const Diagnostic& D) { - setForegroundColor(Color::Red); - setBold(true); - write("error: "); - resetStyles(); - } - - void ConsoleDiagnostics::writeTypeclassName(const ByteString& Name) { - setForegroundColor(Color::Magenta); - write(Name); - resetStyles(); - } - - void ConsoleDiagnostics::writeTypeclassSignature(const TypeclassSignature& Sig) { - setForegroundColor(Color::Magenta); - write(Sig.Id); - for (auto TV: Sig.Params) { - write(" "); - write(describe(TV)); - } - resetStyles(); - } + ConsoleDiagnostics::ConsoleDiagnostics(ConsolePrinter& P): + ThePrinter(P) {} void ConsoleDiagnostics::addDiagnostic(Diagnostic* D) { - printDiagnostic(*D); + ThePrinter.writeDiagnostic(*D); // Since this DiagnosticEngine is expected to own the diagnostic, we simply // destroy the processed diagnostic so that there are no memory leaks. delete D; } - void ConsoleDiagnostics::printDiagnostic(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; - } - - } - - } - } diff --git a/src/Parser.cc b/src/Parser.cc index 7215e20e2..852935d25 100644 --- a/src/Parser.cc +++ b/src/Parser.cc @@ -86,6 +86,14 @@ namespace bolt { case NodeKind::PubKeyword: case NodeKind::MutKeyword: continue; + case NodeKind::At: + for (;;) { + auto T1 = Tokens.peek(I++); + if (T1->getKind() == NodeKind::LineFoldEnd) { + break; + } + } + continue; default: return T0; } @@ -852,6 +860,7 @@ finish: } ReturnStatement* Parser::parseReturnStatement() { + auto Annotations = parseAnnotations(); auto ReturnKeyword = expectToken(); if (!ReturnKeyword) { return nullptr; @@ -870,7 +879,7 @@ finish: } checkLineFoldEnd(); } - return new ReturnStatement(ReturnKeyword, Expression); + return new ReturnStatement(Annotations, ReturnKeyword, Expression); } IfStatement* Parser::parseIfStatement() { @@ -903,36 +912,46 @@ finish: } Tokens.get()->unref(); // Always a LineFoldEnd Parts.push_back(new IfStatementPart(IfKeyword, Test, T1, Then)); - auto T3 = Tokens.peek(); - if (T3->getKind() == NodeKind::ElseKeyword) { - Tokens.get(); - auto T4 = expectToken(); - if (!T4) { - for (auto Part: Parts) { - Part->unref(); + for (;;) { + auto T3 = Tokens.peek(); + if (T3->getKind() == NodeKind::ElseKeyword || T3->getKind() == NodeKind::ElifKeyword) { + Tokens.get(); + Expression* Test = nullptr; + if (T3->getKind() == NodeKind::ElifKeyword) { + Test = parseExpression(); } - return nullptr; - } - std::vector Else; - for (;;) { - auto T5 = Tokens.peek(); - if (T5->getKind() == NodeKind::BlockEnd) { - Tokens.get()->unref(); + auto T4 = expectToken(); + if (!T4) { + for (auto Part: Parts) { + Part->unref(); + } + return nullptr; + } + std::vector Alt; + for (;;) { + auto T5 = Tokens.peek(); + if (T5->getKind() == NodeKind::BlockEnd) { + Tokens.get()->unref(); + break; + } + auto Element = parseLetBodyElement(); + if (Element) { + Alt.push_back(Element); + } + } + Tokens.get()->unref(); // Always a LineFoldEnd + Parts.push_back(new IfStatementPart(T3, Test, T4, Alt)); + if (T3->getKind() == NodeKind::ElseKeyword) { break; } - auto Element = parseLetBodyElement(); - if (Element) { - Else.push_back(Element); - } } - Tokens.get()->unref(); // Always a LineFoldEnd - Parts.push_back(new IfStatementPart(T3, nullptr, T4, Else)); } return new IfStatement(Parts); } LetDeclaration* Parser::parseLetDeclaration() { + auto Annotations = parseAnnotations(); PubKeyword* Pub = nullptr; ForeignKeyword* Foreign = nullptr; LetKeyword* Let; @@ -1065,6 +1084,7 @@ after_params: finish: return new LetDeclaration( + Annotations, Pub, Foreign, Let, @@ -1565,7 +1585,7 @@ next_member: } checkLineFoldEnd(); Annotations.push_back(new ExpressionAnnotation { At, E }); - break; + continue; } // default: // DE.add(File, T1, std::vector { NodeKind::Colon, NodeKind::Identifier }); diff --git a/src/main.cc b/src/main.cc index 812899caf..1eb81c381 100644 --- a/src/main.cc +++ b/src/main.cc @@ -11,6 +11,7 @@ #include "bolt/CST.hpp" #include "bolt/CSTVisitor.hpp" +#include "bolt/ConsolePrinter.hpp" #include "bolt/DiagnosticEngine.hpp" #include "bolt/Diagnostics.hpp" #include "bolt/Scanner.hpp" @@ -20,6 +21,12 @@ using namespace bolt; +/** + * Status code that can be returned and should according to documentation + * terminate xargs's looping. + */ +const constexpr int XARGS_STOP_LOOP = 255; + ByteString readFile(std::string Path) { std::ifstream File(Path); @@ -63,7 +70,8 @@ int main(int Argc, const char* Argv[]) { auto DirectDiagnostics = Match.has_flag("direct-diagnostics") && Match.get_flag("direct-diagnostics") && !IsVerify; auto AdditionalSyntax = Match.has_flag("additional-syntax") && Match.get_flag("additional-syntax"); - ConsoleDiagnostics DE; + ConsolePrinter ThePrinter; + ConsoleDiagnostics DE(ThePrinter); LanguageConfig Config; std::vector SourceFiles; @@ -96,7 +104,11 @@ int main(int Argc, const char* Argv[]) { if (IsVerify) { - struct Visitor : public CSTVisitor { + // TODO make this work with mulitple source files at once + + bool HasError = 0; + + struct AssertVisitor : public CSTVisitor { Checker& C; DiagnosticEngine& DE; void visitExpression(Expression* N) { @@ -114,12 +126,12 @@ int main(int Argc, const char* Argv[]) { } }; - Visitor V { {}, TheChecker, DE }; + AssertVisitor V { {}, TheChecker, DE }; for (auto SF: SourceFiles) { V.visit(SF); } - struct EDVisitor : public CSTVisitor { + struct ExpectDiagnosticVisitor : public CSTVisitor { std::multimap Expected; void visitExpressionAnnotation(ExpressionAnnotation* N) { if (N->getExpression()->is()) { @@ -135,37 +147,41 @@ int main(int Argc, const char* Argv[]) { } }; - EDVisitor V1; + ExpectDiagnosticVisitor V1; for (auto SF: SourceFiles) { V1.visit(SF); } for (auto D: DS.Diagnostics) { auto N = D->getNode(); - if (!N) { - DE.addDiagnostic(D); - } else { + if (N) { auto Line = N->getStartLine(); auto Match = V1.Expected.find(Line); if (Match != V1.Expected.end() && Match->second == D->getCode()) { std::cerr << "skipped 1 diagnostic" << std::endl; - } else { - DE.addDiagnostic(D); + continue; } } + // Whenever D did not succeed to match we have to print the diagnostic error + ThePrinter.writeDiagnostic(*D); + HasError = true; + } + + if (HasError) { + return XARGS_STOP_LOOP; } } else { DS.sort(); for (auto D: DS.Diagnostics) { - DE.addDiagnostic(D); + ThePrinter.writeDiagnostic(*D); } - if (DE.hasError()) { - return 1; - } + } + if (DE.hasError()) { + return 255; } if (Name == "eval") { diff --git a/test/checker/local_constraints_polymorphic_variable.bolt b/test/checker/local_constraints_polymorphic_variable.bolt index 92c70c837..80948d85c 100644 --- a/test/checker/local_constraints_polymorphic_variable.bolt +++ b/test/checker/local_constraints_polymorphic_variable.bolt @@ -4,5 +4,6 @@ let fac n. return n @expect_diagnostic 2010 -(@:Int fac 1) + (@:Bool True) +@:Int fac "foo" +@:Int fac 1