From 8f98dc579af1993ec85bd849656c4835b4039dd6 Mon Sep 17 00:00:00 2001 From: Stefan Weigl-Bosker Date: Mon, 23 Feb 2026 22:18:22 -0500 Subject: [willow]: frontend plumbing (#13) ... --- willow/include/willow/IR/Diagnostic.h | 34 ++++++ willow/include/willow/IR/Location.h | 5 +- willow/include/willow/Util/Color.h | 219 ++++++++++++++++++++++++++++++++++ 3 files changed, 256 insertions(+), 2 deletions(-) create mode 100644 willow/include/willow/Util/Color.h (limited to 'willow/include') diff --git a/willow/include/willow/IR/Diagnostic.h b/willow/include/willow/IR/Diagnostic.h index 68483d9..137c537 100644 --- a/willow/include/willow/IR/Diagnostic.h +++ b/willow/include/willow/IR/Diagnostic.h @@ -2,10 +2,12 @@ #define WILLOW_INCLUDE_IR_DIAGNOSTIC_H #include +#include #include #include #include +#include namespace willow { @@ -18,6 +20,38 @@ struct Diagnostic { std::vector notes; }; +constexpr termcolor::TextStyle getSeverityColor(Severity sev) { + using namespace termcolor; + switch (sev) { + case Severity::Debug: + return TextStyle{AnsiColor::Green, Emphasis::Bold}; + case Severity::Remark: + return TextStyle{AnsiColor::Cyan, Emphasis::Bold}; + case Severity::Warning: + return TextStyle{AnsiColor::Magenta, Emphasis::Bold}; + case Severity::Error: + return TextStyle{AnsiColor::Red, Emphasis::Bold}; + default: + std::unreachable(); + } +} + +constexpr std::string_view getSeverityName(Severity sev) { + using namespace std::string_view_literals; + switch (sev) { + case Severity::Debug: + return "debug"sv; + case Severity::Remark: + return "info"sv; + case Severity::Warning: + return "warning"sv; + case Severity::Error: + return "error"sv; + default: + std::unreachable(); + } +} + } // namespace willow std::ostream &operator<<(std::ostream &os, const willow::Diagnostic &diagnostic); diff --git a/willow/include/willow/IR/Location.h b/willow/include/willow/IR/Location.h index c3c241d..0db7c58 100644 --- a/willow/include/willow/IR/Location.h +++ b/willow/include/willow/IR/Location.h @@ -9,8 +9,9 @@ namespace willow { /// A source location struct Location { std::string_view filename; - int line; - int col; + uint32_t line; + uint32_t col; + size_t offset; }; } // namespace willow diff --git a/willow/include/willow/Util/Color.h b/willow/include/willow/Util/Color.h new file mode 100644 index 0000000..d7c7840 --- /dev/null +++ b/willow/include/willow/Util/Color.h @@ -0,0 +1,219 @@ +#ifndef WILLOW_INCLUDE_UTIL_COLOR_H +#define WILLOW_INCLUDE_UTIL_COLOR_H + +#include +#include +#include +#include +#include + +/// \file Utilities for printing pretty terminal output +/// +/// This is heavily inspired by fmtlib include/fmt/color.h + +namespace willow { + +namespace termcolor { + +enum class AnsiColor : uint8_t { + None = 0, + Black = 30, + Red, + Green, + Yellow, + Blue, + Magenta, + Cyan, + White, + Default = 39, + BrightBlack = 90, + BrightRed, + BrightGreen, + BrightYellow, + BrightBlue, + BrightMagenta, + BrightCyan, + BrightWhite +}; + +enum class Emphasis : uint8_t { + None = 0, + Bold = 1, + Faint = 1 << 1, + Italic = 1 << 2, + Underline = 1 << 3, + Blink = 1 << 4, + Reverse = 1 << 5, + Conceal = 1 << 6, + Strikethrough = 1 << 7, +}; + +constexpr Emphasis operator|(Emphasis a, Emphasis b) noexcept { + return static_cast(static_cast(a) | + static_cast(b)); +} +constexpr Emphasis &operator|=(Emphasis &a, Emphasis b) noexcept { + return a = (a | b); +} +constexpr bool has(Emphasis a, Emphasis b) { + return (static_cast(a) & static_cast(b)) != 0; +} + +struct TextStyle { + AnsiColor fg = AnsiColor::None; + AnsiColor bg = AnsiColor::None; + Emphasis emph = Emphasis::None; + + explicit constexpr TextStyle() = default; + constexpr TextStyle(AnsiColor fg, AnsiColor bg = AnsiColor::None, + Emphasis emph = Emphasis::None) noexcept + : fg(fg), bg(bg) {} + constexpr TextStyle(AnsiColor fg, Emphasis emph = Emphasis::None) noexcept + : fg(fg), emph(emph) {} + + constexpr bool has_foreground() const noexcept { + return fg != AnsiColor::None; + } + + constexpr bool has_background() const noexcept { + return bg != AnsiColor::None; + } + + constexpr bool has_emphasis() const noexcept { + return emph != Emphasis::None; + } + + // special sentinel value for a full reset + constexpr bool is_reset() const noexcept { + return fg == AnsiColor::None && bg == AnsiColor::None && + emph == Emphasis::None; + } +}; + +template +struct AnsiColorEscape { + constexpr AnsiColorEscape(AnsiColor text_color, bool is_bg = false) noexcept { + if (is_bg) + text_color = + static_cast(static_cast(text_color) + 10); + + buffer[size++] = static_cast('\x1b'); + buffer[size++] = static_cast('['); + + uint8_t value = static_cast(text_color); + + // some bg colors need 3 digits + if (value >= 100u) { + buffer[size++] = static_cast('1'); + value %= 100u; + } + buffer[size++] = static_cast('0' + value / 10u); + buffer[size++] = static_cast('0' + value % 10u); + + buffer[size++] = static_cast('m'); + } + + constexpr AnsiColorEscape(Emphasis em) noexcept { + uint8_t em_codes[NUM_EMPHASES] = {}; + if (has_emphasis(em, Emphasis::Bold)) + em_codes[0] = 1; + if (has_emphasis(em, Emphasis::Faint)) + em_codes[1] = 2; + if (has_emphasis(em, Emphasis::Italic)) + em_codes[2] = 3; + if (has_emphasis(em, Emphasis::Underline)) + em_codes[3] = 4; + if (has_emphasis(em, Emphasis::Blink)) + em_codes[4] = 5; + if (has_emphasis(em, Emphasis::Reverse)) + em_codes[5] = 7; + if (has_emphasis(em, Emphasis::Conceal)) + em_codes[6] = 8; + if (has_emphasis(em, Emphasis::Strikethrough)) + em_codes[7] = 9; + + buffer[size++] = static_cast('\x1b'); + buffer[size++] = static_cast('['); + + for (unsigned char em_code : em_codes) { + if (!em_code) + continue; + buffer[size++] = static_cast('0' + em_code); + buffer[size++] = static_cast(';'); + } + + buffer[size - 1] = static_cast('m'); + } + + constexpr operator const char *() const noexcept { return buffer; } + constexpr operator std::string_view() const noexcept { + return std::string_view(buffer, size); + } + + constexpr const CharT *begin() const noexcept { return buffer; } + constexpr const CharT *end() const noexcept { return buffer + size; } + +private: + static constexpr size_t NUM_EMPHASES = 8; + CharT buffer[7u + 4u * NUM_EMPHASES]; + size_t size = 0; + + static constexpr bool has_emphasis(Emphasis em, Emphasis mask) noexcept { + return static_cast(em) & static_cast(mask); + } +}; + +template +AnsiColorEscape make_foreground_color(AnsiColor c) { + return AnsiColorEscape(c); +} + +template +AnsiColorEscape make_background_color(AnsiColor c) { + return AnsiColorEscape(c, true); +} + +template +AnsiColorEscape make_emphasis(Emphasis c) { + return AnsiColorEscape(c); +} + +}; // namespace termcolor + +}; // namespace willow + +template +struct std::formatter { + constexpr auto parse(const std::format_parse_context &ctx) { + auto it = ctx.begin(); + assert(it == ctx.end() || *it == '}'); + return it; + } + template + auto format(const willow::termcolor::TextStyle &style, + FormatContext &ctx) const { + auto out = ctx.out(); + if (style.is_reset()) + return std::format_to(out, "{}", "\x1b[0m"); + + if (style.has_emphasis()) { + auto emphasis = willow::termcolor::make_emphasis(style.emph); + out = std::format_to( + out, "{}", static_cast>(emphasis)); + } + if (style.has_foreground()) { + auto fg = willow::termcolor::make_foreground_color(style.fg); + out = std::format_to(out, "{}", + static_cast>(fg)); + } + if (style.has_background()) { + auto bg = willow::termcolor::make_background_color(style.bg); + out = std::format_to(out, "{}", + static_cast>(bg)); + } + + return out; + } +}; + +#endif // WILLOW_INCLUDE_UTIL_COLOR_H -- cgit v1.2.3