Skip to content

Commit

Permalink
feat: print errors in JSON format
Browse files Browse the repository at this point in the history
  • Loading branch information
jsilll committed Sep 17, 2024
1 parent 31216b3 commit 43c7a83
Show file tree
Hide file tree
Showing 18 changed files with 277 additions and 127 deletions.
13 changes: 8 additions & 5 deletions include/AST/AST.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ struct DeclAST {
: kind(kind), ident(ident) {}
};

// == ModuleAST ==

struct ModuleAST {
std::string_view ident;
List<DeclAST *> decls;
Expand All @@ -100,7 +102,8 @@ struct ModuleAST {
// === Expressions ===

struct NumberExprAST : public ExprAST {
NumberExprAST(std::string_view span) : ExprAST(ExprASTKind::Number, span) {}
explicit NumberExprAST(std::string_view span)
: ExprAST(ExprASTKind::Number, span) {}
};

struct UnaryExprAST : public ExprAST {
Expand Down Expand Up @@ -151,7 +154,7 @@ struct ExprStmtAST : public StmtAST {

struct BreakStmtAST : public StmtAST {
StmtAST *stmt;
BreakStmtAST(std::string_view span)
explicit BreakStmtAST(std::string_view span)
: StmtAST(StmtASTKind::Break, span), stmt(nullptr) {}
};

Expand Down Expand Up @@ -215,12 +218,12 @@ struct FunctionDeclAST : public DeclAST {

/// === Identifier Expressions ===

using IdentDecl =
using IdentifierDecl =
std::variant<std::monostate, LocalStmtAST *, FunctionDeclAST *>;

struct IdentifierExprAST : public ExprAST {
IdentDecl decl;
IdentifierExprAST(std::string_view span)
IdentifierDecl decl;
explicit IdentifierExprAST(std::string_view span)
: ExprAST(ExprASTKind::Identifier, span) {}
};

Expand Down
4 changes: 2 additions & 2 deletions include/AST/ASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ namespace lang {

template <typename Derived, bool IsConst = true> class ASTVisitor {
protected:
ASTVisitor() = default;

template <typename T>
using MaybeConst = std::conditional_t<IsConst, const T, T>;

ASTVisitor() = default;

void visit(MaybeConst<ExprAST> &node) {
switch (node.kind) {
case ExprASTKind::Number:
Expand Down
7 changes: 4 additions & 3 deletions include/Lex/Lexer.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ struct LexError {
std::string_view span;
LexError(LexErrorKind kind, std::string_view span)
: kind(kind), span(span) {}
PrettyError toPretty() const;
TextError toTextError() const;
JSONError toJSONError() const;
};

struct LexResult {
Expand All @@ -33,10 +34,10 @@ class Lexer {
LexResult lexAll(bool includeComments = false);

private:
Token lexAlt(char c, TokenKind altKind, TokenKind defaultKind);

size_t idx;
std::string_view buffer;

Token lexAlt(char c, TokenKind altKind, TokenKind defaultKind);
};

} // namespace lang
Expand Down
11 changes: 7 additions & 4 deletions include/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@

#include "Alloc/Arena.h"

#include "Sema/Type.h"
#include "Support/Reporting.h"

#include "Lex/Token.h"

#include "AST/AST.h"

#include "Lex/Token.h"
#include "Sema/Type.h"

#include <unordered_set>

Expand All @@ -27,7 +28,8 @@ struct ParseError {
TokenKind expected;
ParseError(ParseErrorKind kind, std::string_view span, TokenKind expected)
: kind(kind), span(span), expected(expected) {}
PrettyError toPretty() const;
TextError toTextError() const;
JSONError toJSONError() const;
};

struct ParseResult {
Expand All @@ -38,7 +40,8 @@ struct ParseResult {
class Parser {
public:
Parser(Arena &arena, TypeContext &typeCtx, const std::vector<Token> &tokens)
: arena(&arena), typeCtx(&typeCtx), cur(tokens.begin()), end(tokens.end()) {}
: arena(&arena), typeCtx(&typeCtx), cur(tokens.begin()),
end(tokens.end()) {}

ParseResult parseModuleAST();

Expand Down
24 changes: 14 additions & 10 deletions include/Sema/Resolver.h
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
#ifndef LANG_RESOLVER_H
#define LANG_RESOLVER_H

#include "Support/Reporting.h"

#include "AST/AST.h"
#include "AST/ASTVisitor.h"

#include "Support/Reporting.h"

#include <stack>
#include <unordered_map>
#include <vector>

namespace lang {

enum class ResolveErrorKind {
InvalidBreakStmt,
UnresolvedIdentifier,
};

Expand All @@ -20,7 +22,8 @@ struct ResolveError {
std::string_view span;
ResolveError(ResolveErrorKind kind, std::string_view span)
: kind(kind), span(span) {}
PrettyError toPretty() const;
TextError toTextError() const;
JSONError toJSONError() const;
};

struct ResolveResult {
Expand All @@ -36,6 +39,14 @@ class Resolver : public MutableASTVisitor<Resolver> {
ResolveResult resolveModuleAST(ModuleAST &module);

private:
bool deepResolution;
std::stack<StmtAST *> breakableStack;
std::unordered_map<std::string_view, FunctionDeclAST *> functionsMap;
std::vector<std::unordered_map<std::string_view, LocalStmtAST *>> localsMap;
std::vector<ResolveError> errors;

LocalStmtAST *lookupLocal(std::string_view ident) const;

void visit(FunctionDeclAST &node);

void visit(ExprStmtAST &node);
Expand Down Expand Up @@ -67,13 +78,6 @@ class Resolver : public MutableASTVisitor<Resolver> {
void visit(IndexExprAST &node);

void visit(GroupedExprAST &node);

LocalStmtAST *lookupLocal(std::string_view ident) const;

bool deepResolution;
std::unordered_map<std::string_view, FunctionDeclAST *> functions;
std::vector<std::unordered_map<std::string_view, LocalStmtAST *>> locals;
std::vector<ResolveError> errors;
};

} // namespace lang
Expand Down
11 changes: 6 additions & 5 deletions include/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ enum class SemaErrorKind {
struct SemaError {
SemaErrorKind kind;
std::string_view span;
PrettyError toPretty() const;
TextError toTextError() const;
JSONError toJSONError() const;
};

struct SemaResult {
Expand All @@ -35,6 +36,10 @@ class Sema : public MutableASTVisitor<Sema> {
SemaResult analyzeModuleAST(ModuleAST &module);

private:
TypeContext &typeCtx;
FunctionDeclAST *currentFunction;
std::vector<SemaError> errors;

void visit(FunctionDeclAST &node);

void visit(ExprStmtAST &node);
Expand Down Expand Up @@ -66,10 +71,6 @@ class Sema : public MutableASTVisitor<Sema> {
void visit(IndexExprAST &node);

void visit(GroupedExprAST &node);

TypeContext &typeCtx;
FunctionDeclAST *currentFunction;
std::vector<SemaError> errors;
};

} // namespace lang
Expand Down
16 changes: 15 additions & 1 deletion include/Sema/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@ namespace lang {
enum class TypeKind {
Void,
Number,
Pointer,
Function,
};

struct Type {
TypeKind kind;
Type(TypeKind kind) : kind(kind) {}
explicit Type(TypeKind kind) : kind(kind) {}
std::string toString() const;
template<typename T> T& as() { return static_cast<T&>(this); }
template<typename T> const T& as() { return static_cast<const T&>(this); }
};

class TypeContext {
Expand All @@ -39,6 +43,16 @@ class TypeContext {

Type *tyVoid;
Type *tyNumber;

// TODO: declare hashCons(Type &type);
};

struct PointerType : public Type {
PointerType() : Type(TypeKind::Pointer) {}
};

struct FunctionType : public Type {
FunctionType() : Type(TypeKind::Function) {}
};

} // namespace lang
Expand Down
51 changes: 38 additions & 13 deletions include/Support/Reporting.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,41 +17,66 @@ constexpr unsigned getNumDigits(unsigned n) {
return digits;
}

struct PrettyError {
struct TextError {
std::string_view span;
std::string_view title;
std::string label;
};

void reportError(const SourceFile &file, const PrettyError &error,
unsigned lineNoWidthHint = 0);
void reportTextError(const SourceFile &file, const TextError &error,
unsigned lineNoWidthHint = 0);

/// @brief Reports a vector of errors in batch
struct JSONError {
std::string_view span;
std::string_view title;
};

void reportJSONError(const SourceFile &file, const JSONError &error);

/// @brief Reports a vector of errors in batch in plain text
/// @pre errors only contains errors from the same file
/// @pre errors is sorted in the order of appearence within the file
template <typename T>
void reportErrors(
const lang::SourceFile &file, const std::vector<T> &errors,
const std::size_t maxErrors = std::numeric_limits<std::size_t>::max()) {
const std::size_t numErrors = std::min(errors.size(), maxErrors);
if (numErrors == 0) {
void reportTextErrors(
const SourceFile &file, const std::vector<T> &errors,
std::size_t maxErrors = std::numeric_limits<std::size_t>::max()) {
maxErrors = std::min(errors.size(), maxErrors);
if (maxErrors == 0) {
return;
}

const std::size_t maxLine = file.getLocation(errors.back().span).line;
const unsigned lineNoMaxWidth = getNumDigits(maxLine);
const std::string lineNoSpacesBody = std::string(lineNoMaxWidth + 2, ' ');

const std::size_t lastButOne = numErrors - 1;
for (std::size_t i = 0; i < numErrors; ++i) {
const std::size_t lastButOne = maxErrors - 1;
for (std::size_t i = 0; i < maxErrors; ++i) {
const auto &error = errors[i];
lang::reportError(file, error.toPretty(), lineNoMaxWidth);
lang::reportTextError(file, error.toTextError(), lineNoMaxWidth);
if (i < lastButOne) {
llvm::outs() << lineNoSpacesBody << "|\n";
llvm::errs() << lineNoSpacesBody << "|\n";
}
}
}

/// @brief Reports a vector of errors in batch in JSON format
template <typename T>
void reportJSONErrors(
const SourceFile &file, const std::vector<T> &errors,
std::size_t maxErrors = std::numeric_limits<std::size_t>::max()) {
maxErrors = std::min(errors.size(), maxErrors);
if (maxErrors == 0) {
return;
}

llvm::errs() << "[\n";
for (const auto &error : errors) {
llvm::errs() << " ";
lang::reportJSONError(file, error.toJSONError());
}
llvm::errs() << "]\n";
}

} // namespace lang

#endif // LANG_REPORTING_H
3 changes: 3 additions & 0 deletions samples/11.lang
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main(): void {
break;
}
Empty file added samples/12.lang
Empty file.
2 changes: 1 addition & 1 deletion src/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ void ASTPrinter::visit(const BreakStmtAST &node) {
if (node.stmt == nullptr) {
os << "(unresolved)\n";
} else {
os << "StmtAST(" << static_cast<const void *>(node.stmt) << ")";
os << "StmtAST(" << static_cast<const void *>(node.stmt) << ")\n";
}
}

Expand Down
30 changes: 19 additions & 11 deletions src/Lex/Lexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,32 @@ const std::unordered_map<std::string_view, lang::TokenKind> keywordToTokenKind =

namespace lang {

PrettyError LexError::toPretty() const {
TextError LexError::toTextError() const {
switch (kind) {
case LexErrorKind::InvalidCharacter:
return {span, "Invalid character", "Invalid character"};
}
return {span, "Unknown lex error title", "Unknown lex error label"};
}

JSONError LexError::toJSONError() const {
switch (kind) {
case LexErrorKind::InvalidCharacter:
return {span, "lex-invalid-character"};
}
return {span, "lex-unknown-error"};
}

// NOLINTNEXTLINE
Token Lexer::lexAlt(char c, TokenKind altKind, TokenKind defaultKind) {
if (buffer[idx + 1] == c) {
idx += 2;
return {altKind, buffer.substr(idx - 2, 2)};
}
++idx;
return {defaultKind, buffer.substr(idx - 1, 1)};
}

LexResult Lexer::lexAll(bool includeComments) {
LexResult result;

Expand Down Expand Up @@ -164,14 +182,4 @@ LexResult Lexer::lexAll(bool includeComments) {
return result;
}

// NOLINTNEXTLINE
Token Lexer::lexAlt(char c, TokenKind altKind, TokenKind defaultKind) {
if (buffer[idx + 1] == c) {
idx += 2;
return {altKind, buffer.substr(idx - 2, 2)};
}
++idx;
return {defaultKind, buffer.substr(idx - 1, 1)};
}

} // namespace lang
Loading

0 comments on commit 43c7a83

Please sign in to comment.