#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace ovdl { struct DiagnosticLogger : SymbolIntern { using AnnotationKind = lexy_ext::annotation_kind; using DiagnosticKind = lexy_ext::diagnostic_kind; using error_range = detail::error_range; explicit operator bool() const; bool errored() const; bool warned() const; NodeLocation location_of(const error::Error* error) const; template Logger> struct ErrorCallback { ErrorCallback(Logger& logger) : _logger(&logger) {} struct sink_t { using return_type = std::size_t; template void operator()(lexy::error_context const& context, lexy::error_for const& error) { using Reader = lexy::input_reader; error::Error* result; std::string production_name = context.production(); auto left_strip = production_name.find_first_of('<'); if (left_strip != std::string::npos) { auto right_strip = production_name.find_first_of('>', left_strip); if (right_strip != std::string::npos) { production_name.erase(left_strip, right_strip - left_strip + 1); } } auto production = _logger.intern_cstr(production_name); if constexpr (std::is_same_v) { auto string = lexy::_detail::make_literal_lexeme(error.string(), error.length()); NodeLocation loc = NodeLocation::make_from(context.position(), error.position() - 1); auto message = _logger.intern_cstr(fmt::format("expected '{}'", string.data())); result = _logger.template create(loc, message, production); } else if constexpr (std::is_same_v) { auto string = lexy::_detail::make_literal_lexeme(error.string(), error.length()); NodeLocation loc = NodeLocation::make_from(context.position(), error.position() - 1); auto message = _logger.intern_cstr(fmt::format("expected keyword '{}'", string.data())); result = _logger.template create(loc, message, production); } else if constexpr (std::is_same_v) { auto message = _logger.intern_cstr(fmt::format("expected {}", error.name())); result = _logger.template create(error.position(), message, production); } else { NodeLocation loc = NodeLocation::make_from(error.begin(), error.end()); auto message = _logger.intern_cstr(error.message()); result = _logger.template create(loc, message, production); } _logger.insert(result); _count++; } std::size_t finish() && { return _count; } Logger& _logger; std::size_t _count; }; constexpr auto sink() const { return sink_t { *_logger, 0 }; } mutable Logger* _logger; }; template T* create(NodeLocation loc, Args&&... args) { using node_creator = dryad::node_creator; T* result = _tree.create(DRYAD_FWD(args)...); _map.insert(result, loc); return result; } template T* create() { using node_creator = dryad::node_creator; T* result = _tree.create(); return result; } protected: bool _errored = false; bool _warned = false; dryad::node_map _map; dryad::tree _tree; symbol_interner_type _symbol_interner; void insert(error::Error* root) { _tree.root()->insert_back(root); } public: symbol_type intern(const char* str, std::size_t length) { return _symbol_interner.intern(str, length); } symbol_type intern(std::string_view str) { return intern(str.data(), str.size()); } const char* intern_cstr(const char* str, std::size_t length) { return intern(str, length).c_str(_symbol_interner); } const char* intern_cstr(std::string_view str) { return intern_cstr(str.data(), str.size()); } symbol_interner_type& symbol_interner() { return _symbol_interner; } const symbol_interner_type& symbol_interner() const { return _symbol_interner; } }; template struct BasicDiagnosticLogger : DiagnosticLogger { using file_type = FileT; template using format_str = fmt::basic_format_string...>; explicit BasicDiagnosticLogger(const file_type& file) : _file(&file) { _tree.set_root(_tree.create()); } struct Writer; template Writer error(format_str fmt, Args&&... args) { return log(DiagnosticKind::error, fmt, std::forward(args)...); } template Writer warning(format_str fmt, Args&&... args) { return log(DiagnosticKind::warning, fmt, std::forward(args)...); } template Writer note(format_str fmt, Args&&... args) { return log(DiagnosticKind::note, fmt, std::forward(args)...); } template Writer info(format_str fmt, Args&&... args) { return log(DiagnosticKind::info, fmt, std::forward(args)...); } template Writer debug(format_str fmt, Args&&... args) { return log(DiagnosticKind::debug, fmt, std::forward(args)...); } template Writer fixit(format_str fmt, Args&&... args) { return log(DiagnosticKind::fixit, fmt, std::forward(args)...); } template Writer help(format_str fmt, Args&&... args) { return log(DiagnosticKind::help, fmt, std::forward(args)...); } Writer error(std::string_view sv) { return log(DiagnosticKind::error, fmt::runtime(sv)); } Writer warning(std::string_view sv) { return log(DiagnosticKind::warning, fmt::runtime(sv)); } Writer note(std::string_view sv) { return log(DiagnosticKind::note, fmt::runtime(sv)); } Writer info(std::string_view sv) { return log(DiagnosticKind::info, fmt::runtime(sv)); } Writer debug(std::string_view sv) { return log(DiagnosticKind::debug, fmt::runtime(sv)); } Writer fixit(std::string_view sv) { return log(DiagnosticKind::fixit, fmt::runtime(sv)); } Writer help(std::string_view sv) { return log(DiagnosticKind::help, fmt::runtime(sv)); } auto error_callback() { return ErrorCallback(*this); } template static void _write_to_buffer(const CharT* s, std::streamsize n, void* output_str) { auto* output = reinterpret_cast*>(output_str); output->append(s, n); } template auto make_callback_stream(std::basic_string& output) { return detail::make_callback_stream(&_write_to_buffer, reinterpret_cast(&output)); } template detail::OStreamOutputIterator make_ostream_iterator(std::basic_ostream& stream) { return detail::OStreamOutputIterator { stream }; } struct Writer { template [[nodiscard]] Writer& primary(NodeLocation loc, format_str fmt, Args&&... args) { return annotation(AnnotationKind::primary, loc, fmt, std::forward(args)...); } template [[nodiscard]] Writer& secondary(NodeLocation loc, format_str fmt, Args&&... args) { return annotation(AnnotationKind::secondary, loc, fmt, std::forward(args)...); } [[nodiscard]] Writer& primary(NodeLocation loc, std::string_view sv) { return annotation(AnnotationKind::primary, loc, fmt::runtime(sv)); } [[nodiscard]] Writer& secondary(NodeLocation loc, std::string_view sv) { return annotation(AnnotationKind::secondary, loc, fmt::runtime(sv)); } void finish() {} template [[nodiscard]] Writer& annotation(AnnotationKind kind, NodeLocation loc, format_str fmt, Args&&... args) { auto begin_loc = lexy::get_input_location(_file->buffer(), loc.begin()); std::basic_string output; auto stream = _logger.make_callback_stream(output); auto iter = _logger.make_ostream_iterator(stream); _impl.write_empty_annotation(iter); _impl.write_annotation(iter, kind, begin_loc, loc.end(), [&](auto out, lexy::visualization_options) { return lexy::_detail::write_str(out, fmt::format(fmt, std::forward(args)...).c_str()); }); error::Annotation* annotation; auto message = _logger.intern_cstr(output); switch (kind) { case AnnotationKind::primary: annotation = _logger.create(loc, message); break; case AnnotationKind::secondary: annotation = _logger.create(loc, message); break; default: detail::unreachable(); } _semantic->push_back(annotation); return *this; } private: Writer(BasicDiagnosticLogger& logger, const file_type* file, error::Semantic* semantic) : _file(file), _impl(file->buffer(), { lexy::visualize_fancy }), _logger(logger), _semantic(semantic) {} const file_type* _file; lexy_ext::diagnostic_writer> _impl; BasicDiagnosticLogger& _logger; error::Semantic* _semantic; friend BasicDiagnosticLogger; }; using diagnostic_writer = lexy_ext::diagnostic_writer>; template T, typename... Args> void log_with_impl(diagnostic_writer& impl, T* error, DiagnosticKind kind, format_str fmt, Args&&... args) { std::basic_string output; auto stream = make_callback_stream(output); auto iter = make_ostream_iterator(stream); impl.write_message(iter, kind, [&](auto out, lexy::visualization_options) { return lexy::_detail::write_str(out, fmt::format(fmt, std::forward(args)...).c_str()); }); impl.write_path(iter, _file->path()); auto message = intern_cstr(output); error->_set_message(message); insert(error); } template T, typename... Args> void log_with_error(T* error, DiagnosticKind kind, format_str fmt, Args&&... args) { auto impl = diagnostic_writer { _file->buffer() }; log_with_impl(impl, error, kind, fmt, std::forward(args)...); } template T, typename... Args> void create_log(DiagnosticKind kind, format_str fmt, Args&&... args) { log_with_error(create(), kind, fmt, std::forward(args)...); } template Writer log(DiagnosticKind kind, format_str fmt, Args&&... args) { error::Semantic* semantic; switch (kind) { case DiagnosticKind::error: semantic = create(); break; case DiagnosticKind::warning: semantic = create(); break; case DiagnosticKind::info: semantic = create(); break; case DiagnosticKind::debug: semantic = create(); break; case DiagnosticKind::fixit: semantic = create(); break; case DiagnosticKind::help: semantic = create(); break; default: detail::unreachable(); } Writer result(*this, _file, semantic); log_with_impl(result._impl, semantic, kind, fmt, std::forward(args)...); if (kind == DiagnosticKind::error) _errored = true; if (kind == DiagnosticKind::warning) _warned = true; return result; } error_range get_errors() const { return _tree.root()->errors(); } private: const file_type* _file; }; }