Some minor enhancements
- Make if-elif-else possible - Add more tests - Ensure that failed tests fail tools like xargs - Refactor printing logic out of ConsoleDiagnostics
This commit is contained in:
parent
d278456290
commit
d81e92231f
9 changed files with 188 additions and 1081 deletions
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
|
@ -9,7 +9,7 @@
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"name": "Debug",
|
"name": "Debug",
|
||||||
"program": "${workspaceFolder}/build/bolt",
|
"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}",
|
"cwd": "${workspaceFolder}",
|
||||||
"preLaunchTask": "CMake: build"
|
"preLaunchTask": "CMake: build"
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ add_library(
|
||||||
#src/Text.cc
|
#src/Text.cc
|
||||||
src/CST.cc
|
src/CST.cc
|
||||||
src/Diagnostics.cc
|
src/Diagnostics.cc
|
||||||
|
src/ConsolePrinter.cc
|
||||||
src/Scanner.cc
|
src/Scanner.cc
|
||||||
src/Parser.cc
|
src/Parser.cc
|
||||||
src/Types.cc
|
src/Types.cc
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
#ifndef BOLT_CST_HPP
|
#ifndef BOLT_CST_HPP
|
||||||
#define BOLT_CST_HPP
|
#define BOLT_CST_HPP
|
||||||
|
|
||||||
#include <cctype>
|
|
||||||
#include <istream>
|
|
||||||
#include <iterator>
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
@ -988,6 +985,11 @@ namespace bolt {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class AnnotationContainer {
|
||||||
|
public:
|
||||||
|
std::vector<Annotation*> Annotations;
|
||||||
|
};
|
||||||
|
|
||||||
class ExpressionAnnotation : public Annotation {
|
class ExpressionAnnotation : public Annotation {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -1058,11 +1060,11 @@ namespace bolt {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class TypeExpression : public TypedNode {
|
class TypeExpression : public TypedNode, AnnotationContainer {
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
TypeExpression(NodeKind Kind):
|
inline TypeExpression(NodeKind Kind, std::vector<Annotation*> Annotations = {}):
|
||||||
TypedNode(Kind) {}
|
TypedNode(Kind), AnnotationContainer(Annotations) {}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1385,17 +1387,11 @@ namespace bolt {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Expression : public TypedNode, public AnnotationContainer {
|
||||||
class Expression : public TypedNode {
|
|
||||||
public:
|
|
||||||
|
|
||||||
std::vector<Annotation*> Annotations;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
inline Expression(NodeKind Kind, std::vector<Annotation*> Annotations = {}):
|
inline Expression(NodeKind Kind, std::vector<Annotation*> Annotations = {}):
|
||||||
TypedNode(Kind), Annotations(Annotations) {}
|
TypedNode(Kind), AnnotationContainer(Annotations) {}
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1688,6 +1684,14 @@ namespace bolt {
|
||||||
Operator(Operator),
|
Operator(Operator),
|
||||||
Argument(Argument) {}
|
Argument(Argument) {}
|
||||||
|
|
||||||
|
PrefixExpression(
|
||||||
|
std::vector<Annotation*> Annotations,
|
||||||
|
Token* Operator,
|
||||||
|
Expression* Argument
|
||||||
|
): Expression(NodeKind::PrefixExpression, Annotations),
|
||||||
|
Operator(Operator),
|
||||||
|
Argument(Argument) {}
|
||||||
|
|
||||||
Token* getFirstToken() const override;
|
Token* getFirstToken() const override;
|
||||||
Token* getLastToken() const override;
|
Token* getLastToken() const override;
|
||||||
|
|
||||||
|
@ -1734,16 +1738,26 @@ namespace bolt {
|
||||||
Fields(Fields),
|
Fields(Fields),
|
||||||
RBrace(RBrace) {}
|
RBrace(RBrace) {}
|
||||||
|
|
||||||
|
inline RecordExpression(
|
||||||
|
std::vector<Annotation*> Annotations,
|
||||||
|
class LBrace* LBrace,
|
||||||
|
std::vector<std::tuple<RecordExpressionField*, Comma*>> Fields,
|
||||||
|
class RBrace* RBrace
|
||||||
|
): Expression(NodeKind::RecordExpression, Annotations),
|
||||||
|
LBrace(LBrace),
|
||||||
|
Fields(Fields),
|
||||||
|
RBrace(RBrace) {}
|
||||||
|
|
||||||
Token* getFirstToken() const override;
|
Token* getFirstToken() const override;
|
||||||
Token* getLastToken() const override;
|
Token* getLastToken() const override;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Statement : public Node {
|
class Statement : public Node, public AnnotationContainer {
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
inline Statement(NodeKind Type):
|
inline Statement(NodeKind Type, std::vector<Annotation*> Annotations = {}):
|
||||||
Node(Type) {}
|
Node(Type), AnnotationContainer(Annotations) {}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1755,12 +1769,18 @@ namespace bolt {
|
||||||
ExpressionStatement(class Expression* Expression):
|
ExpressionStatement(class Expression* Expression):
|
||||||
Statement(NodeKind::ExpressionStatement), Expression(Expression) {}
|
Statement(NodeKind::ExpressionStatement), Expression(Expression) {}
|
||||||
|
|
||||||
|
ExpressionStatement(
|
||||||
|
std::vector<Annotation*> Annotations,
|
||||||
|
class Expression* Expression
|
||||||
|
): Statement(NodeKind::ExpressionStatement, Annotations),
|
||||||
|
Expression(Expression) {}
|
||||||
|
|
||||||
Token* getFirstToken() const override;
|
Token* getFirstToken() const override;
|
||||||
Token* getLastToken() const override;
|
Token* getLastToken() const override;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class IfStatementPart : public Node {
|
class IfStatementPart : public Node, public AnnotationContainer {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Token* Keyword;
|
Token* Keyword;
|
||||||
|
@ -1779,6 +1799,19 @@ namespace bolt {
|
||||||
BlockStart(BlockStart),
|
BlockStart(BlockStart),
|
||||||
Elements(Elements) {}
|
Elements(Elements) {}
|
||||||
|
|
||||||
|
inline IfStatementPart(
|
||||||
|
std::vector<Annotation*> Annotations,
|
||||||
|
Token* Keyword,
|
||||||
|
Expression* Test,
|
||||||
|
class BlockStart* BlockStart,
|
||||||
|
std::vector<Node*> Elements
|
||||||
|
): Node(NodeKind::IfStatementPart),
|
||||||
|
AnnotationContainer(Annotations),
|
||||||
|
Keyword(Keyword),
|
||||||
|
Test(Test),
|
||||||
|
BlockStart(BlockStart),
|
||||||
|
Elements(Elements) {}
|
||||||
|
|
||||||
Token* getFirstToken() const override;
|
Token* getFirstToken() const override;
|
||||||
Token* getLastToken() const override;
|
Token* getLastToken() const override;
|
||||||
|
|
||||||
|
@ -1810,6 +1843,14 @@ namespace bolt {
|
||||||
ReturnKeyword(ReturnKeyword),
|
ReturnKeyword(ReturnKeyword),
|
||||||
Expression(Expression) {}
|
Expression(Expression) {}
|
||||||
|
|
||||||
|
ReturnStatement(
|
||||||
|
std::vector<Annotation*> Annotations,
|
||||||
|
class ReturnKeyword* ReturnKeyword,
|
||||||
|
class Expression* Expression
|
||||||
|
): Statement(NodeKind::ReturnStatement, Annotations),
|
||||||
|
ReturnKeyword(ReturnKeyword),
|
||||||
|
Expression(Expression) {}
|
||||||
|
|
||||||
Token* getFirstToken() const override;
|
Token* getFirstToken() const override;
|
||||||
Token* getLastToken() 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;
|
Scope* TheScope = nullptr;
|
||||||
|
|
||||||
|
@ -1932,6 +1973,27 @@ namespace bolt {
|
||||||
TypeAssert(TypeAssert),
|
TypeAssert(TypeAssert),
|
||||||
Body(Body) {}
|
Body(Body) {}
|
||||||
|
|
||||||
|
LetDeclaration(
|
||||||
|
std::vector<Annotation*> Annotations,
|
||||||
|
class PubKeyword* PubKeyword,
|
||||||
|
class ForeignKeyword* ForeignKeyword,
|
||||||
|
class LetKeyword* LetKeyword,
|
||||||
|
class MutKeyword* MutKeyword,
|
||||||
|
class Pattern* Pattern,
|
||||||
|
std::vector<Parameter*> 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 {
|
inline Scope* getScope() override {
|
||||||
if (TheScope == nullptr) {
|
if (TheScope == nullptr) {
|
||||||
TheScope = new Scope(this);
|
TheScope = new Scope(this);
|
||||||
|
|
|
@ -849,11 +849,11 @@ namespace bolt {
|
||||||
for (auto A: N->Annotations) {
|
for (auto A: N->Annotations) {
|
||||||
BOLT_VISIT(A);
|
BOLT_VISIT(A);
|
||||||
}
|
}
|
||||||
for (auto [Name, Dot]: N->ModulePath) {
|
for (auto [Name, Dot]: N->ModulePath) {
|
||||||
BOLT_VISIT(Name);
|
BOLT_VISIT(Name);
|
||||||
BOLT_VISIT(Dot);
|
BOLT_VISIT(Dot);
|
||||||
}
|
}
|
||||||
BOLT_VISIT(N->Name);
|
BOLT_VISIT(N->Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void visitEachChild(MatchCase* N) {
|
void visitEachChild(MatchCase* N) {
|
||||||
|
@ -963,10 +963,16 @@ namespace bolt {
|
||||||
}
|
}
|
||||||
|
|
||||||
void visitEachChild(ExpressionStatement* N) {
|
void visitEachChild(ExpressionStatement* N) {
|
||||||
|
for (auto A: N->Annotations) {
|
||||||
|
BOLT_VISIT(A);
|
||||||
|
}
|
||||||
BOLT_VISIT(N->Expression);
|
BOLT_VISIT(N->Expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
void visitEachChild(ReturnStatement* N) {
|
void visitEachChild(ReturnStatement* N) {
|
||||||
|
for (auto A: N->Annotations) {
|
||||||
|
BOLT_VISIT(A);
|
||||||
|
}
|
||||||
BOLT_VISIT(N->ReturnKeyword);
|
BOLT_VISIT(N->ReturnKeyword);
|
||||||
BOLT_VISIT(N->Expression);
|
BOLT_VISIT(N->Expression);
|
||||||
}
|
}
|
||||||
|
@ -978,6 +984,9 @@ namespace bolt {
|
||||||
}
|
}
|
||||||
|
|
||||||
void visitEachChild(IfStatementPart* N) {
|
void visitEachChild(IfStatementPart* N) {
|
||||||
|
for (auto A: N->Annotations) {
|
||||||
|
BOLT_VISIT(A);
|
||||||
|
}
|
||||||
BOLT_VISIT(N->Keyword);
|
BOLT_VISIT(N->Keyword);
|
||||||
if (N->Test != nullptr) {
|
if (N->Test != nullptr) {
|
||||||
BOLT_VISIT(N->Test);
|
BOLT_VISIT(N->Test);
|
||||||
|
@ -1013,6 +1022,9 @@ namespace bolt {
|
||||||
}
|
}
|
||||||
|
|
||||||
void visitEachChild(LetDeclaration* N) {
|
void visitEachChild(LetDeclaration* N) {
|
||||||
|
for (auto A: N->Annotations) {
|
||||||
|
BOLT_VISIT(A);
|
||||||
|
}
|
||||||
if (N->PubKeyword) {
|
if (N->PubKeyword) {
|
||||||
BOLT_VISIT(N->PubKeyword);
|
BOLT_VISIT(N->PubKeyword);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,29 +5,27 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "bolt/ByteString.hpp"
|
|
||||||
#include "bolt/CST.hpp"
|
|
||||||
#include "bolt/Type.hpp"
|
|
||||||
|
|
||||||
namespace bolt {
|
namespace bolt {
|
||||||
|
|
||||||
|
class ConsolePrinter;
|
||||||
class Diagnostic;
|
class Diagnostic;
|
||||||
class TypeclassSignature;
|
class TypeclassSignature;
|
||||||
class Type;
|
class Type;
|
||||||
class Node;
|
class Node;
|
||||||
|
|
||||||
class DiagnosticEngine {
|
class DiagnosticEngine {
|
||||||
|
protected:
|
||||||
|
|
||||||
bool HasError = false;
|
bool HasError = false;
|
||||||
|
|
||||||
|
virtual void addDiagnostic(Diagnostic* Diagnostic) = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
inline bool hasError() const noexcept {
|
inline bool hasError() const noexcept {
|
||||||
return HasError;
|
return HasError;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void addDiagnostic(Diagnostic* Diagnostic) = 0;
|
|
||||||
|
|
||||||
template<typename D, typename ...Ts>
|
template<typename D, typename ...Ts>
|
||||||
void add(Ts&&... Args) {
|
void add(Ts&&... Args) {
|
||||||
HasError = true;
|
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 {
|
class ConsoleDiagnostics : public DiagnosticEngine {
|
||||||
|
|
||||||
std::ostream& Out;
|
ConsolePrinter& ThePrinter;
|
||||||
|
|
||||||
Style ActiveStyle;
|
protected:
|
||||||
|
|
||||||
void setForegroundColor(Color C);
|
void addDiagnostic(Diagnostic* Diagnostic) override;
|
||||||
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:
|
public:
|
||||||
|
|
||||||
unsigned ExcerptLinesPre = 2;
|
ConsoleDiagnostics(ConsolePrinter& ThePrinter);
|
||||||
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);
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "bolt/Type.hpp"
|
#include "bolt/Type.hpp"
|
||||||
#include "bolt/DiagnosticEngine.hpp"
|
#include "bolt/DiagnosticEngine.hpp"
|
||||||
#include "bolt/Diagnostics.hpp"
|
#include "bolt/Diagnostics.hpp"
|
||||||
|
#include "bolt/ConsolePrinter.hpp"
|
||||||
|
|
||||||
#define ANSI_RESET "\u001b[0m"
|
#define ANSI_RESET "\u001b[0m"
|
||||||
#define ANSI_BOLD "\u001b[1m"
|
#define ANSI_BOLD "\u001b[1m"
|
||||||
|
@ -37,14 +38,6 @@
|
||||||
|
|
||||||
namespace bolt {
|
namespace bolt {
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
T countDigits(T number) {
|
|
||||||
if (number == 0) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return std::ceil(std::log10(number+1));
|
|
||||||
}
|
|
||||||
|
|
||||||
Diagnostic::Diagnostic(DiagnosticKind Kind):
|
Diagnostic::Diagnostic(DiagnosticKind Kind):
|
||||||
std::runtime_error("a compiler error occurred without being caught"), Kind(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);
|
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<const TVar*>(Ty);
|
|
||||||
if (TV->getVarKind() == VarKind::Rigid) {
|
|
||||||
return static_cast<const TVarRigid*>(TV)->Name;
|
|
||||||
}
|
|
||||||
return "a" + std::to_string(TV->Id);
|
|
||||||
}
|
|
||||||
case TypeKind::Arrow:
|
|
||||||
{
|
|
||||||
auto Y = static_cast<const TArrow*>(Ty);
|
|
||||||
std::ostringstream Out;
|
|
||||||
Out << describe(Y->ParamType) << " -> " << describe(Y->ReturnType);
|
|
||||||
return Out.str();
|
|
||||||
}
|
|
||||||
case TypeKind::Con:
|
|
||||||
{
|
|
||||||
auto Y = static_cast<const TCon*>(Ty);
|
|
||||||
return Y->DisplayName;
|
|
||||||
}
|
|
||||||
case TypeKind::App:
|
|
||||||
{
|
|
||||||
auto Y = static_cast<const TApp*>(Ty);
|
|
||||||
return describe(Y->Op) + " " + describe(Y->Arg);
|
|
||||||
}
|
|
||||||
case TypeKind::Tuple:
|
|
||||||
{
|
|
||||||
std::ostringstream Out;
|
|
||||||
auto Y = static_cast<const TTuple*>(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<const TTupleIndex*>(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<const TPresent*>(Ty);
|
|
||||||
return describe(Y->Ty);
|
|
||||||
}
|
|
||||||
case TypeKind::Field:
|
|
||||||
{
|
|
||||||
auto Y = static_cast<const TField*>(Ty);
|
|
||||||
std::ostringstream out;
|
|
||||||
out << "{ " << Y->Name << ": " << describe(Y->Ty);
|
|
||||||
Ty = Y->RestTy;
|
|
||||||
while (Ty->getKind() == TypeKind::Field) {
|
|
||||||
auto Y = static_cast<const TField*>(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() {
|
DiagnosticStore::~DiagnosticStore() {
|
||||||
// for (auto D: Diagnostics) {
|
for (auto D: Diagnostics) {
|
||||||
// delete D;
|
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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConsoleDiagnostics::setBold(bool Enable) {
|
ConsoleDiagnostics::ConsoleDiagnostics(ConsolePrinter& P):
|
||||||
ActiveStyle.setBold(Enable);
|
ThePrinter(P) {}
|
||||||
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<std::size_t>(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<const TApp*>(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<const TVarRigid*>(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<const TField*>(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();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConsoleDiagnostics::addDiagnostic(Diagnostic* D) {
|
void ConsoleDiagnostics::addDiagnostic(Diagnostic* D) {
|
||||||
|
|
||||||
printDiagnostic(*D);
|
ThePrinter.writeDiagnostic(*D);
|
||||||
|
|
||||||
// Since this DiagnosticEngine is expected to own the diagnostic, we simply
|
// Since this DiagnosticEngine is expected to own the diagnostic, we simply
|
||||||
// destroy the processed diagnostic so that there are no memory leaks.
|
// destroy the processed diagnostic so that there are no memory leaks.
|
||||||
delete D;
|
delete D;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConsoleDiagnostics::printDiagnostic(const Diagnostic& D) {
|
|
||||||
|
|
||||||
switch (D.getKind()) {
|
|
||||||
|
|
||||||
case DiagnosticKind::BindingNotFound:
|
|
||||||
{
|
|
||||||
auto E = static_cast<const BindingNotFoundDiagnostic&>(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<const UnexpectedTokenDiagnostic&>(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<const UnexpectedStringDiagnostic&>(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<const UnificationErrorDiagnostic&>(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<const TypeclassMissingDiagnostic&>(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<const InstanceNotFoundDiagnostic&>(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<const TupleIndexOutOfRangeDiagnostic&>(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<const InvalidTypeToTypeclassDiagnostic&>(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<const FieldNotFoundDiagnostic&>(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,6 +86,14 @@ namespace bolt {
|
||||||
case NodeKind::PubKeyword:
|
case NodeKind::PubKeyword:
|
||||||
case NodeKind::MutKeyword:
|
case NodeKind::MutKeyword:
|
||||||
continue;
|
continue;
|
||||||
|
case NodeKind::At:
|
||||||
|
for (;;) {
|
||||||
|
auto T1 = Tokens.peek(I++);
|
||||||
|
if (T1->getKind() == NodeKind::LineFoldEnd) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
default:
|
default:
|
||||||
return T0;
|
return T0;
|
||||||
}
|
}
|
||||||
|
@ -852,6 +860,7 @@ finish:
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnStatement* Parser::parseReturnStatement() {
|
ReturnStatement* Parser::parseReturnStatement() {
|
||||||
|
auto Annotations = parseAnnotations();
|
||||||
auto ReturnKeyword = expectToken<class ReturnKeyword>();
|
auto ReturnKeyword = expectToken<class ReturnKeyword>();
|
||||||
if (!ReturnKeyword) {
|
if (!ReturnKeyword) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -870,7 +879,7 @@ finish:
|
||||||
}
|
}
|
||||||
checkLineFoldEnd();
|
checkLineFoldEnd();
|
||||||
}
|
}
|
||||||
return new ReturnStatement(ReturnKeyword, Expression);
|
return new ReturnStatement(Annotations, ReturnKeyword, Expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
IfStatement* Parser::parseIfStatement() {
|
IfStatement* Parser::parseIfStatement() {
|
||||||
|
@ -903,36 +912,46 @@ finish:
|
||||||
}
|
}
|
||||||
Tokens.get()->unref(); // Always a LineFoldEnd
|
Tokens.get()->unref(); // Always a LineFoldEnd
|
||||||
Parts.push_back(new IfStatementPart(IfKeyword, Test, T1, Then));
|
Parts.push_back(new IfStatementPart(IfKeyword, Test, T1, Then));
|
||||||
auto T3 = Tokens.peek();
|
for (;;) {
|
||||||
if (T3->getKind() == NodeKind::ElseKeyword) {
|
auto T3 = Tokens.peek();
|
||||||
Tokens.get();
|
if (T3->getKind() == NodeKind::ElseKeyword || T3->getKind() == NodeKind::ElifKeyword) {
|
||||||
auto T4 = expectToken<BlockStart>();
|
Tokens.get();
|
||||||
if (!T4) {
|
Expression* Test = nullptr;
|
||||||
for (auto Part: Parts) {
|
if (T3->getKind() == NodeKind::ElifKeyword) {
|
||||||
Part->unref();
|
Test = parseExpression();
|
||||||
}
|
}
|
||||||
return nullptr;
|
auto T4 = expectToken<BlockStart>();
|
||||||
}
|
if (!T4) {
|
||||||
std::vector<Node*> Else;
|
for (auto Part: Parts) {
|
||||||
for (;;) {
|
Part->unref();
|
||||||
auto T5 = Tokens.peek();
|
}
|
||||||
if (T5->getKind() == NodeKind::BlockEnd) {
|
return nullptr;
|
||||||
Tokens.get()->unref();
|
}
|
||||||
|
std::vector<Node*> 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;
|
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);
|
return new IfStatement(Parts);
|
||||||
}
|
}
|
||||||
|
|
||||||
LetDeclaration* Parser::parseLetDeclaration() {
|
LetDeclaration* Parser::parseLetDeclaration() {
|
||||||
|
|
||||||
|
auto Annotations = parseAnnotations();
|
||||||
PubKeyword* Pub = nullptr;
|
PubKeyword* Pub = nullptr;
|
||||||
ForeignKeyword* Foreign = nullptr;
|
ForeignKeyword* Foreign = nullptr;
|
||||||
LetKeyword* Let;
|
LetKeyword* Let;
|
||||||
|
@ -1065,6 +1084,7 @@ after_params:
|
||||||
finish:
|
finish:
|
||||||
|
|
||||||
return new LetDeclaration(
|
return new LetDeclaration(
|
||||||
|
Annotations,
|
||||||
Pub,
|
Pub,
|
||||||
Foreign,
|
Foreign,
|
||||||
Let,
|
Let,
|
||||||
|
@ -1565,7 +1585,7 @@ next_member:
|
||||||
}
|
}
|
||||||
checkLineFoldEnd();
|
checkLineFoldEnd();
|
||||||
Annotations.push_back(new ExpressionAnnotation { At, E });
|
Annotations.push_back(new ExpressionAnnotation { At, E });
|
||||||
break;
|
continue;
|
||||||
}
|
}
|
||||||
// default:
|
// default:
|
||||||
// DE.add<UnexpectedTokenDiagnostic>(File, T1, std::vector { NodeKind::Colon, NodeKind::Identifier });
|
// DE.add<UnexpectedTokenDiagnostic>(File, T1, std::vector { NodeKind::Colon, NodeKind::Identifier });
|
||||||
|
|
44
src/main.cc
44
src/main.cc
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
#include "bolt/CST.hpp"
|
#include "bolt/CST.hpp"
|
||||||
#include "bolt/CSTVisitor.hpp"
|
#include "bolt/CSTVisitor.hpp"
|
||||||
|
#include "bolt/ConsolePrinter.hpp"
|
||||||
#include "bolt/DiagnosticEngine.hpp"
|
#include "bolt/DiagnosticEngine.hpp"
|
||||||
#include "bolt/Diagnostics.hpp"
|
#include "bolt/Diagnostics.hpp"
|
||||||
#include "bolt/Scanner.hpp"
|
#include "bolt/Scanner.hpp"
|
||||||
|
@ -20,6 +21,12 @@
|
||||||
|
|
||||||
using namespace bolt;
|
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) {
|
ByteString readFile(std::string Path) {
|
||||||
|
|
||||||
std::ifstream File(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<bool>("direct-diagnostics") && !IsVerify;
|
auto DirectDiagnostics = Match.has_flag("direct-diagnostics") && Match.get_flag<bool>("direct-diagnostics") && !IsVerify;
|
||||||
auto AdditionalSyntax = Match.has_flag("additional-syntax") && Match.get_flag<bool>("additional-syntax");
|
auto AdditionalSyntax = Match.has_flag("additional-syntax") && Match.get_flag<bool>("additional-syntax");
|
||||||
|
|
||||||
ConsoleDiagnostics DE;
|
ConsolePrinter ThePrinter;
|
||||||
|
ConsoleDiagnostics DE(ThePrinter);
|
||||||
LanguageConfig Config;
|
LanguageConfig Config;
|
||||||
|
|
||||||
std::vector<SourceFile*> SourceFiles;
|
std::vector<SourceFile*> SourceFiles;
|
||||||
|
@ -96,7 +104,11 @@ int main(int Argc, const char* Argv[]) {
|
||||||
|
|
||||||
if (IsVerify) {
|
if (IsVerify) {
|
||||||
|
|
||||||
struct Visitor : public CSTVisitor<Visitor> {
|
// TODO make this work with mulitple source files at once
|
||||||
|
|
||||||
|
bool HasError = 0;
|
||||||
|
|
||||||
|
struct AssertVisitor : public CSTVisitor<AssertVisitor> {
|
||||||
Checker& C;
|
Checker& C;
|
||||||
DiagnosticEngine& DE;
|
DiagnosticEngine& DE;
|
||||||
void visitExpression(Expression* N) {
|
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) {
|
for (auto SF: SourceFiles) {
|
||||||
V.visit(SF);
|
V.visit(SF);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct EDVisitor : public CSTVisitor<EDVisitor> {
|
struct ExpectDiagnosticVisitor : public CSTVisitor<ExpectDiagnosticVisitor> {
|
||||||
std::multimap<std::size_t, unsigned> Expected;
|
std::multimap<std::size_t, unsigned> Expected;
|
||||||
void visitExpressionAnnotation(ExpressionAnnotation* N) {
|
void visitExpressionAnnotation(ExpressionAnnotation* N) {
|
||||||
if (N->getExpression()->is<CallExpression>()) {
|
if (N->getExpression()->is<CallExpression>()) {
|
||||||
|
@ -135,37 +147,41 @@ int main(int Argc, const char* Argv[]) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
EDVisitor V1;
|
ExpectDiagnosticVisitor V1;
|
||||||
for (auto SF: SourceFiles) {
|
for (auto SF: SourceFiles) {
|
||||||
V1.visit(SF);
|
V1.visit(SF);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto D: DS.Diagnostics) {
|
for (auto D: DS.Diagnostics) {
|
||||||
auto N = D->getNode();
|
auto N = D->getNode();
|
||||||
if (!N) {
|
if (N) {
|
||||||
DE.addDiagnostic(D);
|
|
||||||
} else {
|
|
||||||
auto Line = N->getStartLine();
|
auto Line = N->getStartLine();
|
||||||
auto Match = V1.Expected.find(Line);
|
auto Match = V1.Expected.find(Line);
|
||||||
if (Match != V1.Expected.end() && Match->second == D->getCode()) {
|
if (Match != V1.Expected.end() && Match->second == D->getCode()) {
|
||||||
std::cerr << "skipped 1 diagnostic" << std::endl;
|
std::cerr << "skipped 1 diagnostic" << std::endl;
|
||||||
} else {
|
continue;
|
||||||
DE.addDiagnostic(D);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 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 {
|
} else {
|
||||||
|
|
||||||
DS.sort();
|
DS.sort();
|
||||||
for (auto D: DS.Diagnostics) {
|
for (auto D: DS.Diagnostics) {
|
||||||
DE.addDiagnostic(D);
|
ThePrinter.writeDiagnostic(*D);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DE.hasError()) {
|
}
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (DE.hasError()) {
|
||||||
|
return 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Name == "eval") {
|
if (Name == "eval") {
|
||||||
|
|
|
@ -4,5 +4,6 @@ let fac n.
|
||||||
return n
|
return n
|
||||||
|
|
||||||
@expect_diagnostic 2010
|
@expect_diagnostic 2010
|
||||||
(@:Int fac 1) + (@:Bool True)
|
@:Int fac "foo"
|
||||||
|
@:Int fac 1
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue