Skip to content

Commit

Permalink
Enhance error handling and logging; refactor lexer to include source …
Browse files Browse the repository at this point in the history
…file context
  • Loading branch information
mauro-balades committed Dec 24, 2024
1 parent 714f549 commit 8ec0246
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 23 deletions.
64 changes: 62 additions & 2 deletions src/common/error.cc
Original file line number Diff line number Diff line change
@@ -1,14 +1,75 @@

#include "common/error.h"
#include "common/stl.h"

#include "common/location.h"
#include "common/globals.h"

#include "common/utility/format.h"
#include "common/utility/logger.h"
#include "common/utility/string.h"

using namespace snowball::utils;

namespace snowball {
namespace {

SNOWBALL_NO_DISCARD inline auto ErrorTypeToLoggerType(SourceError::Kind kind) -> Logger::Level {
switch (kind) {
case SourceError::Error: return Logger::Level::Error;
case SourceError::Info: return Logger::Level::Info;
case SourceError::Note: return Logger::Level::Info;
case SourceError::Help: return Logger::Level::Help;
case SourceError::Warning: return Logger::Level::Warning;
}
UnreachableError("Invalid error kind!");
}

SNOWBALL_NO_DISCARD inline auto GetErrorLogTab(SourceError::Kind kind, u8 offset = 0) -> String {
i8 length = 0;
switch (kind) {
case SourceError::Error:
length = 5;
break;

case SourceError::Info:
case SourceError::Note:
case SourceError::Help:
length = 4;
break;

case SourceError::Warning:
length = 7;
break;
}
return Repeat(' ', length - offset);
}

auto DisplayInitialErrorLine(const SourceError::SourceErrorData& data, OStream& stream) -> void {
const auto& logger = GetLogger();
const auto lines = Split(data.mMessage, '\n');
logger.Log(ErrorTypeToLoggerType(data.mKind), lines[0], stream);
for (usize i = 1; i < lines.size(); i++) {
logger.Raw(stream, "{} {}\n", GetErrorLogTab(data.mKind), lines[i]);
}
}

auto DisplayErrorLine(const SourceLocation& location, OStream& stream) -> void {
const auto& logger = GetLogger();
auto sourceName = location.GetFile().has_value()
? location.GetFile().value()->string() : "<input>";
logger.Raw(stream, "{} --> {}[{}:{}]\n", GetErrorLogTab(SourceError::Error, /* Offset = */3),
sourceName, location.GetLine(), location.GetColumn());
}

auto DisplayErrorData(const SourceError::SourceErrorData& data, OStream& stream) -> void {
DisplayInitialErrorLine(data, stream);
if (data.mLocation.has_value()) {
DisplayErrorLine(data.mLocation.value(), stream);
}
}

}; // namespace

auto FatalError(const Error& error) -> void {
GetLogger().Error(error.GetMessage());
Expand Down Expand Up @@ -113,8 +174,7 @@ auto SourceError::GetLoggerType(Kind kind) -> Logger::Level {
auto SnowballFormat(const SourceError& error) -> String {
std::stringstream ss;
for (const auto& data : error.GetData()) {
GetLogger().Log(SourceError::GetLoggerType(data.mKind), data.mMessage, ss);
// TODO: Continue here.
DisplayErrorData(data, ss);
}
return ss.str();
}
Expand Down
12 changes: 10 additions & 2 deletions src/common/location.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,15 @@

namespace snowball {

using SourceFile = SharedPtr<fs::Path>;
using SourceFile = Opt<SharedPtr<fs::Path>>;

/// @brief Utility function to create a source file. If there's no path,
/// just asign it with `None`.
/// @param path The path to the source file.
/// @return The source file.
SNOWBALL_NO_DISCARD inline auto CreateSourceFile(const fs::Path& path) -> SourceFile {
return Some(std::make_shared<fs::Path>(path));
}

/// @brief A location represents a position in the source code.
class SourceLocation {
Expand All @@ -15,7 +23,7 @@ class SourceLocation {
/// @param line The line number.
/// @param column The column number.
/// @param width The width of the location.
SourceLocation(SourceFile file, u64 line, u64 column, u16 width = 0)
explicit SourceLocation(SourceFile file, u64 line, u64 column, u16 width = 0)
: mFile(file), mLine(line), mColumn(column), mWidth(width) {}
/// @brief Get the source file.
/// @return The source file.
Expand Down
8 changes: 4 additions & 4 deletions src/common/utility/logger.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@

namespace snowball::utils {

auto Logger::LogImpl(const String& message, OStream& stream) -> void {
auto Logger::LogImpl(const String& message, OStream& stream) const -> void {
// TODO: Log to file.
stream << fmt::format("{}\n", message);
}

auto Logger::Log(Level level, const String& message, OStream& stream) -> void {
auto Logger::Log(Level level, const String& message, OStream& stream) const -> void {
for (const auto& line : Split(message, '\n')) {
LogImpl(Format("{}: {}",
fmt::styled(GetLevelPrefix(level),
Expand All @@ -27,7 +27,7 @@ auto Logger::Log(Level level, const String& message, OStream& stream) -> void {
}

#define SNOWBALL_LOGGER_LEVEL(level) \
auto Logger::level(const String& message) -> void { \
auto Logger::level(const String& message) const -> void { \
Log(Level::level, message); \
}

Expand All @@ -36,7 +36,7 @@ SNOWBALL_LOGGER_LEVEL(Warning);
SNOWBALL_LOGGER_LEVEL(Error);
SNOWBALL_LOGGER_LEVEL(Fatal);

auto Logger::Debug(const String& message) -> void {
auto Logger::Debug(const String& message) const -> void {
if (opts::IsDebugVerbose() || opts::IsVerbose()) {
Log(Level::Debug, message);
}
Expand Down
23 changes: 15 additions & 8 deletions src/common/utility/logger.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,30 +26,37 @@ class Logger final {
/// @param level The log level.
/// @param message The log message.
auto Log(Level level, const String& message,
OStream& stream = std::cout) -> void;
OStream& stream = std::cout) const -> void;
/// @brief Log a debug message.
/// @param message The log message.
auto Debug(const String& message) -> void;
auto Debug(const String& message) const -> void;
/// @brief Log an info message.
/// @param message The log message.
auto Info(const String& message) -> void;
auto Info(const String& message) const -> void;
/// @brief Log a warning message.
/// @param message The log message.
auto Warning(const String& message) -> void;
auto Warning(const String& message) const -> void;
/// @brief Log an error message.
/// @param message The log message.
auto Error(const String& message) -> void;
auto Error(const String& message) const -> void;
/// @brief Log a help message.
/// @param message The log message.
auto Help(const String& message) -> void;
auto Help(const String& message) const -> void;
/// @brief Log a fatal error message.
/// @param message The log message.
auto Fatal(const String& message) -> void;
auto Fatal(const String& message) const -> void;
/// @brief Implementation of the log function.
/// @note With use this function because we want to
/// log the message to various outputs (e.g. console, file).
auto LogImpl(const String& message, OStream& stream
= std::cout) -> void;
= std::cout) const -> void;

/// @brief Log a raw message, similar to the log function but without
/// any formatting or color.
template <typename... Args>
auto Raw(OStream& stream, ConstStr<Args...> message, Args&&... args) const -> void {
LogImpl(Format(message, std::forward<Args>(args)...), stream);
}

/// @brief Get the prefix for the log level.
/// @param level The log level.
Expand Down
10 changes: 10 additions & 0 deletions src/common/utility/string.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ auto Split(const String& str, char delim) -> Vector<String> {
while (std::getline(ss, item, delim)) {
result.push_back(item);
}
SNOWBALL_ASSERT(!result.empty(), "Splitting a string should "
"always return at least one element.");
return result;
}

Expand All @@ -25,4 +27,12 @@ auto Join(const Vector<String>& vec, char delim) -> String {
return result;
}

auto Repeat(char c, usize n) -> String {
String result;
for (usize i = 0; i < n; i++) {
result += c;
}
return result;
}

}; // namespace snowball::utils
5 changes: 5 additions & 0 deletions src/common/utility/string.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,10 @@ Vector<String> Split(const String& str, char delim);
/// @param delim The delimiter to join by.
String Join(const Vector<String>& vec, char delim);

/// @brief Repeat a character a certain amount of times.
/// @param c The character to repeat.
/// @param n The amount of times to repeat the character.
String Repeat(char c, usize n);

} // namespace utils
} // namespace snowball
12 changes: 7 additions & 5 deletions src/frontend/lexer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,20 @@ using namespace snowball::utils;
namespace snowball::frontend {

auto Lexer::StartPathLexer(const fs::Path& path) -> Lexer {
return StartStreamLexer(ReadFile(path));
return StartStreamLexer(ReadFile(path), CreateSourceFile(path));
}

auto Lexer::StartStringLexer(const String& source) -> Lexer {
return StartStreamLexer(new std::istringstream(source));
}

auto Lexer::StartStreamLexer(IStream* stream) -> Lexer {
return Lexer(stream);
auto Lexer::StartStreamLexer(IStream* stream, SourceFile file) -> Lexer {
return Lexer(stream, file);
}

Lexer::Lexer(IStream* source) : mSource(source) {}
Lexer::Lexer(IStream* source, SourceFile file) : mSource(source), mFile(file) {
SNOWBALL_ASSERT(mSource, "The source stream is null.");
}

auto Lexer::GetCurrentToken() -> Token {
return mToken;
Expand Down Expand Up @@ -56,7 +58,7 @@ inline auto Lexer::ConsumeNewLine() -> void {
}

inline auto Lexer::CreateSourceLocation() -> SourceLocation {
return SourceLocation(nullptr, mLine, mColumn);
return SourceLocation(mFile, mLine, mColumn);
}

auto Lexer::LexNumber(char c) -> Token {
Expand Down
6 changes: 4 additions & 2 deletions src/frontend/lexer.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ class Lexer final : public NonCopyable, public Createable<Lexer>, public NonMova
static auto StartStringLexer(const String& source) -> Lexer;
/// @brief Create a new lexer instance and tokenize the input source code,
/// by using a file stream.
static auto StartStreamLexer(IStream* stream) -> Lexer;
static auto StartStreamLexer(IStream* stream, SourceFile file = None) -> Lexer;

/// @brief Default destructor.
~Lexer() = default;
private:
/// @brief Construct a new lexer.
/// @param source The source code.
explicit Lexer(IStream* source);
explicit Lexer(IStream* source, SourceFile file = None);

public:
/// @brief It returns the list of tokens.
Expand Down Expand Up @@ -97,6 +97,8 @@ class Lexer final : public NonCopyable, public Createable<Lexer>, public NonMova

private:
IStream* mSource;
SourceFile mFile;

u64 mLine{1};
u64 mColumn{1};

Expand Down

0 comments on commit 8ec0246

Please sign in to comment.