Improve diagnostic output

This commit is contained in:
Sam Vervaeck 2022-08-24 20:57:26 +02:00
parent fb69ab745c
commit fcea25c9bb
14 changed files with 122 additions and 63 deletions

View file

@ -8,8 +8,10 @@ set(CMAKE_CXX_STANDARD 17)
add_subdirectory(deps/zen EXCLUDE_FROM_ALL) add_subdirectory(deps/zen EXCLUDE_FROM_ALL)
set(ICU_DIR "${CMAKE_CURRENT_SOURCE_DIR}/build/icu/install") set(ICU_DIR "${CMAKE_CURRENT_SOURCE_DIR}/build/icu/install")
set(ICU_CFLAGS "-DUNISTR_FROM_CHAR_EXPLICIT=explicit -DUNISTR_FROM_STRING_EXPLICIT=explicit -DU_NO_DEFAULT_INCLUDE_UTF_HEADERS=1 -DU_HIDE_OBSOLETE_UTF_OLD_H=1")
set(ICU_INCLUDE_DIRS "${ICU_DIR}/include") set(ICU_INCLUDE_DIRS "${ICU_DIR}/include")
set(ICU_LIBRARY_DIRS "${ICU_DIR}/lib") set(ICU_LIBRARY_DIRS "${ICU_DIR}/lib")
set(ICU_LIBRARIES icuuc)
add_library( add_library(
BoltCore BoltCore
@ -29,6 +31,7 @@ target_compile_options(
BoltCore BoltCore
PUBLIC PUBLIC
-fstandalone-debug -fstandalone-debug
${ICU_CFLAGS}
) )
target_include_directories( target_include_directories(
BoltCore BoltCore

View file

@ -122,11 +122,7 @@ namespace bolt {
return StartLoc; return StartLoc;
} }
inline TextLoc getEndLoc() { TextLoc getEndLoc();
TextLoc EndLoc;
EndLoc.advance(getText());
return EndLoc;
}
inline size_t getStartLine() { inline size_t getStartLine() {
return StartLoc.Line; return StartLoc.Line;

View file

@ -228,9 +228,10 @@ namespace bolt {
Type* Left; Type* Left;
Type* Right; Type* Right;
Node* Source;
inline CEqual(Type* Left, Type* Right): inline CEqual(Type* Left, Type* Right, Node* Source = nullptr):
Constraint(ConstraintKind::Equal), Left(Left), Right(Right) {} Constraint(ConstraintKind::Equal), Left(Left), Right(Right), Source(Source) {}
}; };

View file

@ -40,22 +40,24 @@ namespace bolt {
class UnexpectedTokenDiagnostic : public Diagnostic { class UnexpectedTokenDiagnostic : public Diagnostic {
public: public:
TextFile& File;
Token* Actual; Token* Actual;
std::vector<NodeType> Expected; std::vector<NodeType> Expected;
inline UnexpectedTokenDiagnostic(Token* Actual, std::vector<NodeType> Expected): inline UnexpectedTokenDiagnostic(TextFile& File, Token* Actual, std::vector<NodeType> Expected):
Diagnostic(DiagnosticKind::UnexpectedToken), Actual(Actual), Expected(Expected) {} Diagnostic(DiagnosticKind::UnexpectedToken), File(File), Actual(Actual), Expected(Expected) {}
}; };
class UnexpectedStringDiagnostic : public Diagnostic { class UnexpectedStringDiagnostic : public Diagnostic {
public: public:
TextFile& File;
TextLoc Location; TextLoc Location;
String Actual; String Actual;
inline UnexpectedStringDiagnostic(TextLoc Location, String Actual): inline UnexpectedStringDiagnostic(TextFile& File, TextLoc Location, String Actual):
Diagnostic(DiagnosticKind::UnexpectedString), Location(Location), Actual(Actual) {} Diagnostic(DiagnosticKind::UnexpectedString), File(File), Location(Location), Actual(Actual) {}
}; };
@ -75,9 +77,10 @@ namespace bolt {
Type* Left; Type* Left;
Type* Right; Type* Right;
Node* Source;
inline UnificationErrorDiagnostic(Type* Left, Type* Right): inline UnificationErrorDiagnostic(Type* Left, Type* Right, Node* Source):
Diagnostic(DiagnosticKind::UnificationError), Left(Left), Right(Right) {} Diagnostic(DiagnosticKind::UnificationError), Left(Left), Right(Right), Source(Source) {}
}; };
@ -116,6 +119,9 @@ namespace bolt {
void setForegroundColor(Color C); void setForegroundColor(Color C);
void setBackgroundColor(Color C); void setBackgroundColor(Color C);
void setBold(bool Enable);
void setItalic(bool Enable);
void setUnderline(bool Enable);
void resetStyles(); void resetStyles();
void writeGutter( void writeGutter(

View file

@ -24,11 +24,11 @@ namespace bolt {
}; };
template<typename ContainerT> template<typename ContainerT, typename T = typename ContainerT::value_type>
class VectorStream : public Stream<typename ContainerT::value_type> { class VectorStream : public Stream<T> {
public: public:
using value_type = typename ContainerT::value_type; using value_type = T;
ContainerT& Data; ContainerT& Data;
value_type Sentry; value_type Sentry;
@ -82,6 +82,8 @@ namespace bolt {
class Scanner : public BufferedStream<Token*> { class Scanner : public BufferedStream<Token*> {
TextFile& File;
Stream<Char>& Chars; Stream<Char>& Chars;
TextLoc CurrLoc; TextLoc CurrLoc;
@ -111,7 +113,7 @@ namespace bolt {
public: public:
Scanner(Stream<Char>& Chars); Scanner(TextFile& File, Stream<Char>& Chars);
}; };

View file

@ -5,9 +5,9 @@
namespace bolt { namespace bolt {
using Char = char32_t; using Char = int;
using String = std::basic_string<Char>; using String = std::string;
} }

View file

@ -17,7 +17,7 @@ namespace bolt {
size_t Line = 1; size_t Line = 1;
size_t Column = 1; size_t Column = 1;
inline void advance(const std::string& Text) { inline void advance(const String& Text) {
for (auto Chr: Text) { for (auto Chr: Text) {
if (Chr == '\n') { if (Chr == '\n') {
Line++; Line++;
@ -28,6 +28,12 @@ namespace bolt {
} }
} }
inline TextLoc operator+(const String& Text) const {
TextLoc Out { Line, Column };
Out.advance(Text);
return Out;
}
}; };
class TextRange { class TextRange {
@ -39,13 +45,13 @@ namespace bolt {
class TextFile { class TextFile {
ByteString Path; ByteString Path;
String Text; ByteString Text;
std::vector<size_t> LineOffsets; std::vector<size_t> LineOffsets;
public: public:
TextFile(ByteString Path, String Text); TextFile(ByteString Path, ByteString Text);
size_t getLine(size_t Offset); size_t getLine(size_t Offset);
size_t getColumn(size_t Offset); size_t getColumn(size_t Offset);
@ -55,7 +61,7 @@ namespace bolt {
ByteString getPath() const; ByteString getPath() const;
String getText() const; ByteString getText() const;
}; };

View file

@ -9,7 +9,7 @@ namespace bolt {
auto CurrNode = this; auto CurrNode = this;
for (;;) { for (;;) {
if (CurrNode->Type == NodeType::SourceFile) { if (CurrNode->Type == NodeType::SourceFile) {
return static_cast<SourceFile*>(this); return static_cast<SourceFile*>(CurrNode);
} }
CurrNode = CurrNode->Parent; CurrNode = CurrNode->Parent;
ZEN_ASSERT(CurrNode != nullptr); ZEN_ASSERT(CurrNode != nullptr);
@ -23,6 +23,12 @@ namespace bolt {
}; };
} }
TextLoc Token::getEndLoc() {
auto EndLoc = StartLoc;
EndLoc.advance(getText());
return EndLoc;
}
void Token::setParents() { void Token::setParents() {
} }

View file

@ -203,7 +203,7 @@ namespace bolt {
std::vector<Type*> ArgTys; std::vector<Type*> ArgTys;
ArgTys.push_back(inferExpression(Y->LHS, Ctx)); ArgTys.push_back(inferExpression(Y->LHS, Ctx));
ArgTys.push_back(inferExpression(Y->RHS, Ctx)); ArgTys.push_back(inferExpression(Y->RHS, Ctx));
Ctx.addConstraint(new CEqual { new TArrow(ArgTys, RetTy), OpTy }); Ctx.addConstraint(new CEqual { new TArrow(ArgTys, RetTy), OpTy, X });
return RetTy; return RetTy;
} }
@ -257,7 +257,7 @@ namespace bolt {
{ {
auto Y = static_cast<CEqual*>(Constraint); auto Y = static_cast<CEqual*>(Constraint);
if (!unify(Y->Left, Y->Right, Sub)) { if (!unify(Y->Left, Y->Right, Sub)) {
DE.add<UnificationErrorDiagnostic>(Y->Left, Y->Right); DE.add<UnificationErrorDiagnostic>(Y->Left, Y->Right, Y->Source);
} }
break; break;
} }

View file

@ -2,6 +2,7 @@
#include <sstream> #include <sstream>
#include <cmath> #include <cmath>
#include "bolt/CST.hpp"
#include "zen/config.hpp" #include "zen/config.hpp"
#include "bolt/Diagnostics.hpp" #include "bolt/Diagnostics.hpp"
@ -201,6 +202,24 @@ namespace bolt {
} }
} }
void ConsoleDiagnostics::setBold(bool Enable) {
if (Enable) {
Out << ANSI_BOLD;
}
}
void ConsoleDiagnostics::setItalic(bool Enable) {
if (Enable) {
// TODO
}
}
void ConsoleDiagnostics::setUnderline(bool Enable) {
if (Enable) {
Out << ANSI_UNDERLINE;
}
}
void ConsoleDiagnostics::resetStyles() { void ConsoleDiagnostics::resetStyles() {
if (EnableColors) { if (EnableColors) {
Out << ANSI_RESET; Out << ANSI_RESET;
@ -311,7 +330,14 @@ namespace bolt {
case DiagnosticKind::UnexpectedToken: case DiagnosticKind::UnexpectedToken:
{ {
auto E = static_cast<const UnexpectedTokenDiagnostic&>(D); auto E = static_cast<const UnexpectedTokenDiagnostic&>(D);
Out << "<unknown.bolt>:" << E.Actual->getStartLine() << ":" << E.Actual->getStartColumn() << ": expected "; setForegroundColor(Color::Red);
setBold(true);
Out << "error: ";
resetStyles();
setForegroundColor(Color::Yellow);
Out << E.File.getPath() << ":" << E.Actual->getStartLine() << ":" << E.Actual->getStartColumn() << ":";
resetStyles();
Out << " expected ";
switch (E.Expected.size()) { switch (E.Expected.size()) {
case 0: case 0:
Out << "nothing"; Out << "nothing";
@ -322,7 +348,7 @@ namespace bolt {
default: default:
auto Iter = E.Expected.begin(); auto Iter = E.Expected.begin();
Out << describe(*Iter++); Out << describe(*Iter++);
NodeType Prev; NodeType Prev = *Iter++;
while (Iter != E.Expected.end()) { while (Iter != E.Expected.end()) {
Out << ", " << describe(Prev); Out << ", " << describe(Prev);
Prev = *Iter++; Prev = *Iter++;
@ -330,15 +356,20 @@ namespace bolt {
Out << " or " << describe(Prev); Out << " or " << describe(Prev);
break; break;
} }
Out << " but instead got '" << E.Actual->getText() << "'\n"; Out << " but instead got '" << E.Actual->getText() << "'\n\n";
writeExcerpt(E.Actual->getSourceFile()->getTextFile(), E.Actual->getRange(), E.Actual->getRange(), Color::Red); writeExcerpt(E.File, E.Actual->getRange(), E.Actual->getRange(), Color::Red);
Out << "\n";
break; break;
} }
case DiagnosticKind::UnexpectedString: case DiagnosticKind::UnexpectedString:
{ {
auto E = static_cast<const UnexpectedStringDiagnostic&>(D); auto E = static_cast<const UnexpectedStringDiagnostic&>(D);
Out << "<unknown.bolt>:" << E.Location.Line << ":" << E.Location.Column << ": unexpected '"; setForegroundColor(Color::Red);
setBold(true);
Out << "error: ";
resetStyles();
Out << E.File.getPath() << ":" << E.Location.Line << ":" << E.Location.Column << ": unexpected '";
for (auto Chr: E.Actual) { for (auto Chr: E.Actual) {
switch (Chr) { switch (Chr) {
case '\\': case '\\':
@ -352,14 +383,28 @@ namespace bolt {
break; break;
} }
} }
Out << "'\n\n";
TextRange Range { E.Location, E.Location + E.Actual };
writeExcerpt(E.File, Range, Range, Color::Red);
Out << "\n";
break; break;
} }
case DiagnosticKind::UnificationError: case DiagnosticKind::UnificationError:
{ {
auto E = static_cast<const UnificationErrorDiagnostic&>(D); auto E = static_cast<const UnificationErrorDiagnostic&>(D);
Out << ANSI_FG_RED << ANSI_BOLD << "error: " << ANSI_RESET << "the types " << ANSI_FG_GREEN << describe(E.Left) << ANSI_RESET setForegroundColor(Color::Red);
<< " and " << ANSI_FG_GREEN << describe(E.Right) << ANSI_RESET << " failed to match\n"; setBold(true);
Out << "error: ";
resetStyles();
Out << "the types " << ANSI_FG_GREEN << describe(E.Left) << ANSI_RESET
<< " and " << ANSI_FG_GREEN << describe(E.Right) << ANSI_RESET << " failed to match\n\n";
if (E.Source) {
auto Range = E.Source->getRange();
//std::cerr << Range.Start.Line << ":" << Range.Start.Column << "-" << Range.End.Line << ":" << Range.End.Column << "\n";
writeExcerpt(E.Source->getSourceFile()->getTextFile(), Range, Range, Color::Red);
Out << "\n";
}
break; break;
} }

View file

@ -70,7 +70,7 @@ namespace bolt {
{ \ { \
auto __Token = Tokens.get(); \ auto __Token = Tokens.get(); \
if (__Token->Type != NodeType::name) { \ if (__Token->Type != NodeType::name) { \
throw UnexpectedTokenDiagnostic(__Token, std::vector<NodeType> { NodeType::name }); \ throw UnexpectedTokenDiagnostic(File, __Token, std::vector<NodeType> { NodeType::name }); \
} \ } \
} }
@ -81,7 +81,7 @@ namespace bolt {
Tokens.get(); Tokens.get();
return new BindPattern(static_cast<Identifier*>(T0)); return new BindPattern(static_cast<Identifier*>(T0));
default: default:
throw UnexpectedTokenDiagnostic(T0, std::vector { NodeType::Identifier }); throw UnexpectedTokenDiagnostic(File, T0, std::vector { NodeType::Identifier });
} }
} }
@ -89,18 +89,18 @@ namespace bolt {
std::vector<Identifier*> ModulePath; std::vector<Identifier*> ModulePath;
auto Name = Tokens.get(); auto Name = Tokens.get();
if (Name->Type != NodeType::Identifier) { if (Name->Type != NodeType::Identifier) {
throw UnexpectedTokenDiagnostic(Name, std::vector { NodeType::Identifier }); throw UnexpectedTokenDiagnostic(File, Name, std::vector { NodeType::Identifier });
} }
for (;;) { for (;;) {
auto T1 = Tokens.peek(); auto T1 = Tokens.peek();
if (T1->Type == NodeType::Dot) { if (T1->Type != NodeType::Dot) {
break; break;
} }
Tokens.get(); Tokens.get();
ModulePath.push_back(static_cast<Identifier*>(Name)); ModulePath.push_back(static_cast<Identifier*>(Name));
Name = Tokens.get(); Name = Tokens.get();
if (Name->Type != NodeType::Identifier) { if (Name->Type != NodeType::Identifier) {
throw UnexpectedTokenDiagnostic(Name, std::vector { NodeType::Identifier }); throw UnexpectedTokenDiagnostic(File, Name, std::vector { NodeType::Identifier });
} }
} }
return new QualifiedName(ModulePath, static_cast<Identifier*>(Name)); return new QualifiedName(ModulePath, static_cast<Identifier*>(Name));
@ -112,7 +112,7 @@ namespace bolt {
case NodeType::Identifier: case NodeType::Identifier:
return new ReferenceTypeExpression(parseQualifiedName()); return new ReferenceTypeExpression(parseQualifiedName());
default: default:
throw UnexpectedTokenDiagnostic(T0, std::vector { NodeType::Identifier }); throw UnexpectedTokenDiagnostic(File, T0, std::vector { NodeType::Identifier });
} }
} }
@ -129,7 +129,7 @@ namespace bolt {
Tokens.get(); Tokens.get();
return new ConstantExpression(T0); return new ConstantExpression(T0);
default: default:
throw UnexpectedTokenDiagnostic(T0, std::vector { NodeType::Identifier, NodeType::IntegerLiteral }); throw UnexpectedTokenDiagnostic(File, T0, std::vector { NodeType::Identifier, NodeType::IntegerLiteral, NodeType::StringLiteral });
} }
} }
@ -200,16 +200,16 @@ namespace bolt {
LetDeclaration* Parser::parseLetDeclaration() { LetDeclaration* Parser::parseLetDeclaration() {
PubKeyword* Pub; PubKeyword* Pub = nullptr;
LetKeyword* Let; LetKeyword* Let;
MutKeyword* Mut; MutKeyword* Mut = nullptr;
auto T0 = Tokens.get(); auto T0 = Tokens.get();
if (T0->Type == NodeType::PubKeyword) { if (T0->Type == NodeType::PubKeyword) {
Pub = static_cast<PubKeyword*>(T0); Pub = static_cast<PubKeyword*>(T0);
T0 = Tokens.get(); T0 = Tokens.get();
} }
if (T0->Type != NodeType::LetKeyword) { if (T0->Type != NodeType::LetKeyword) {
throw UnexpectedTokenDiagnostic(T0, std::vector { NodeType::LetKeyword }); throw UnexpectedTokenDiagnostic(File, T0, std::vector { NodeType::LetKeyword });
} }
Let = static_cast<LetKeyword*>(T0); Let = static_cast<LetKeyword*>(T0);
auto T1 = Tokens.peek(); auto T1 = Tokens.peek();
@ -277,7 +277,7 @@ after_params:
// First tokens of Pattern // First tokens of Pattern
Expected.push_back(NodeType::Identifier); Expected.push_back(NodeType::Identifier);
} }
throw UnexpectedTokenDiagnostic(T2, Expected); throw UnexpectedTokenDiagnostic(File, T2, Expected);
} }
BOLT_EXPECT_TOKEN(LineFoldEnd); BOLT_EXPECT_TOKEN(LineFoldEnd);

View file

@ -3,6 +3,7 @@
#include "zen/config.hpp" #include "zen/config.hpp"
#include "bolt/Text.hpp"
#include "bolt/Integer.hpp" #include "bolt/Integer.hpp"
#include "bolt/CST.hpp" #include "bolt/CST.hpp"
#include "bolt/Diagnostics.hpp" #include "bolt/Diagnostics.hpp"
@ -65,8 +66,8 @@ namespace bolt {
{ "mod", NodeType::ModKeyword }, { "mod", NodeType::ModKeyword },
}; };
Scanner::Scanner(Stream<Char>& Chars): Scanner::Scanner(TextFile& File, Stream<Char>& Chars):
Chars(Chars) {} File(File), Chars(Chars) {}
Token* Scanner::read() { Token* Scanner::read() {
@ -225,7 +226,7 @@ digit_finish:
case '\'': Text.push_back('\''); break; case '\'': Text.push_back('\''); break;
case '"': Text.push_back('"'); break; case '"': Text.push_back('"'); break;
default: default:
throw UnexpectedStringDiagnostic(Loc, String { C1 }); throw UnexpectedStringDiagnostic(File, Loc, String { static_cast<char>(C1) });
} }
Escaping = false; Escaping = false;
} else { } else {
@ -252,7 +253,7 @@ after_string_contents:
getChar(); getChar();
auto C2 = peekChar(); auto C2 = peekChar();
if (C2 == '.') { if (C2 == '.') {
throw UnexpectedStringDiagnostic(getCurrentLoc(), String { C2 }); throw UnexpectedStringDiagnostic(File, getCurrentLoc(), String { static_cast<char>(C2) });
} }
return new DotDot(StartLoc); return new DotDot(StartLoc);
} }
@ -304,11 +305,7 @@ after_string_contents:
BOLT_SIMPLE_TOKEN('}', RBrace) BOLT_SIMPLE_TOKEN('}', RBrace)
default: default:
throw UnexpectedStringDiagnostic(File, StartLoc, String { static_cast<char>(C0) });
throw UnexpectedStringDiagnostic(StartLoc, String { C0 });
// TODO Add a diagnostic message indicating that scanning failed.
//return new Invalid(StartLoc);
} }

View file

@ -6,7 +6,7 @@
namespace bolt { namespace bolt {
TextFile::TextFile(ByteString Path, String Text): TextFile::TextFile(ByteString Path, ByteString Text):
Path(Path), Text(Text) { Path(Path), Text(Text) {
LineOffsets.push_back(0); LineOffsets.push_back(0);
for (size_t I = 0; I < Text.size(); I++) { for (size_t I = 0; I < Text.size(); I++) {
@ -46,7 +46,7 @@ namespace bolt {
return Path; return Path;
} }
String TextFile::getText() const { ByteString TextFile::getText() const {
return Text; return Text;
} }

View file

@ -14,10 +14,10 @@
using namespace bolt; using namespace bolt;
String readFile(std::string Path) { ByteString readFile(std::string Path) {
std::ifstream File(Path); std::ifstream File(Path);
String Out; ByteString Out;
File.seekg(0, std::ios::end); File.seekg(0, std::ios::end);
Out.reserve(File.tellg()); Out.reserve(File.tellg());
@ -40,22 +40,19 @@ int main(int argc, const char* argv[]) {
auto Text = readFile(argv[1]); auto Text = readFile(argv[1]);
TextFile File { argv[1], Text }; TextFile File { argv[1], Text };
VectorStream<String> Chars(Text, EOF); VectorStream<ByteString, Char> Chars(Text, EOF);
Scanner S(Chars); Scanner S(File, Chars);
Punctuator PT(S); Punctuator PT(S);
Parser P(File, PT); Parser P(File, PT);
SourceFile* SF; SourceFile* SF;
#ifdef NDEBUG
try { try {
SF = P.parseSourceFile(); SF = P.parseSourceFile();
} catch (Diagnostic& D) { } catch (Diagnostic& D) {
DE.addDiagnostic(D); DE.addDiagnostic(D);
return 1;
} }
#else
SF = P.parseSourceFile();
#endif
SF->setParents(); SF->setParents();