#pragma once #include // IWYU pragma: keep #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 { template struct BasicDiagnosticLogger; struct DiagnosticLogger { struct SymbolId; using index_type = std::uint32_t; using symbol_type = dryad::symbol; using symbol_interner_type = dryad::symbol_interner; 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; using Encoding = typename Reader::encoding; using char_type = typename Encoding::char_type; 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 context_location = lexy::get_input_location(context.input(), context.position()); auto location = lexy::get_input_location(context.input(), error.position(), context_location.anchor()); if constexpr (detail::is_instance_of_v) { lexy_ext::diagnostic_writer impl { context.input() }; BasicNodeLocation loc = [&] { if constexpr (std::is_same_v) { return BasicNodeLocation::make_from(error.position(), error.position() + error.index() + 1); } else if constexpr (std::is_same_v) { return BasicNodeLocation::make_from(error.position(), error.end()); } else if constexpr (std::is_same_v) { return BasicNodeLocation::make_from(error.position(), error.position() + 1); } else { return BasicNodeLocation::make_from(error.position(), error.end()); } }(); auto writer = _logger.template parse_error(impl, loc, production_name.c_str()); if (location.line_nr() != context_location.line_nr()) writer.secondary(BasicNodeLocation { context.position(), lexy::_detail::next(context.position()) }, "beginning here").finish(); if constexpr (std::is_same_v) { auto string = lexy::_detail::make_literal_lexeme(error.string(), error.length()); writer.primary(loc, "expected '{}'", string.data()) .finish(); } else if constexpr (std::is_same_v) { auto string = lexy::_detail::make_literal_lexeme(error.string(), error.length()); writer.primary(loc, "expected keyword '{}'", string.data()) .finish(); } else if constexpr (std::is_same_v) { writer.primary(loc, "expected {}", error.name()) .finish(); } else { writer.primary(loc, error.message()) .finish(); } result = writer.error(); } else { 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(BasicNodeLocation 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; } error_range get_errors() const { return _tree.root()->errors(); } 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 symbol_type intern(lexy::lexeme lexeme) { return intern(lexeme.data(), lexeme.size()); } template const char* intern_cstr(lexy::lexeme lexeme) { return intern_cstr(lexeme.data(), lexeme.size()); } }; template struct BasicDiagnosticLogger : DiagnosticLogger { using parse_state_type = ParseState; using file_type = typename parse_state_type::file_type; 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(BasicNodeLocation loc, format_str fmt, Args&&... args) { return annotation(AnnotationKind::primary, loc, fmt, std::forward(args)...); } template [[nodiscard]] Writer& secondary(BasicNodeLocation loc, format_str fmt, Args&&... args) { return annotation(AnnotationKind::secondary, loc, fmt, std::forward(args)...); } template [[nodiscard]] Writer& primary(BasicNodeLocation loc, const char* sv) { return annotation(AnnotationKind::primary, loc, fmt::runtime(sv)); } template [[nodiscard]] Writer& secondary(BasicNodeLocation loc, const char* sv) { return annotation(AnnotationKind::secondary, loc, fmt::runtime(sv)); } void finish() {} template [[nodiscard]] Writer& annotation(AnnotationKind kind, BasicNodeLocation loc, format_str fmt, Args&&... args) { std::basic_string output; _file.visit_buffer([&](auto&& buffer) { using char_type = typename std::decay_t::encoding::char_type; BasicNodeLocation converted_loc = loc; auto begin_loc = lexy::get_input_location(buffer, converted_loc.begin()); auto stream = _logger.make_callback_stream(output); auto iter = _logger.make_ostream_iterator(stream); lexy_ext::diagnostic_writer _impl { buffer, { lexy::visualize_fancy } }; _impl.write_empty_annotation(iter); _impl.write_annotation(iter, kind, begin_loc, converted_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(); } _annotated->push_back(annotation); return *this; } error::AnnotatedError* error() { return _annotated; } private: Writer(BasicDiagnosticLogger& logger, const file_type& file, error::AnnotatedError* annotated) : _file(file), _logger(logger), _annotated(annotated) {} const file_type& _file; BasicDiagnosticLogger& _logger; error::AnnotatedError* _annotated; friend BasicDiagnosticLogger; }; template T, typename Buffer, typename... Args> void log_with_impl(lexy_ext::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); if (!error->is_linked_in_tree()) insert(error); } template Writer parse_error(lexy_ext::diagnostic_writer& impl, NodeLocation loc, const char* production_name) { std::basic_string output; auto stream = make_callback_stream(output); auto iter = make_ostream_iterator(stream); impl.write_message(iter, DiagnosticKind::error, [&](auto out, lexy::visualization_options) { return lexy::_detail::write_str(out, fmt::format("while parsing {}", production_name).c_str()); }); impl.write_path(iter, file().path()); auto production = intern_cstr(production_name); auto message = intern_cstr(output); auto* error = [&] { if constexpr (std::is_same_v) { return create(loc, message, production); } else if constexpr (std::is_same_v) { return create(loc, message, production); } else if constexpr (std::is_same_v) { return create(loc, message, production); } else { return create(loc, message, production); } }(); Writer result(*this, file(), error); _errored = true; return result; } template T, typename... Args> void log_with_error(T* error, DiagnosticKind kind, format_str fmt, Args&&... args) { file().visit_buffer( [&](auto&& buffer) { lexy_ext::diagnostic_writer impl { 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); file().visit_buffer([&](auto&& buffer) { lexy_ext::diagnostic_writer impl { buffer }; log_with_impl(impl, semantic, kind, fmt, std::forward(args)...); }); if (kind == DiagnosticKind::error) _errored = true; if (kind == DiagnosticKind::warning) _warned = true; return result; } const auto& file() const { return *_file; } private: const file_type* _file; }; }