aboutsummaryrefslogtreecommitdiff
path: root/src/openvic-dataloader/detail/LexyReportError.hpp
blob: 684b5dbbe05832fe2ad48090095f725f783a651c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#pragma once

#include <cstddef>
#include <sstream>
#include <string>
#include <utility>
#include <vector>

#include <openvic-dataloader/ParseData.hpp>
#include <openvic-dataloader/ParseError.hpp>

#include <lexy/input_location.hpp>
#include <lexy/visualize.hpp>

#include <lexy_ext/report_error.hpp>

namespace ovdl::detail {
   template<typename OutputIterator>
   struct _ReportError {
      OutputIterator _iter;
      lexy::visualization_options _opts;
      const char* _path;

      struct _sink {
         OutputIterator _iter;
         lexy::visualization_options _opts;
         const char* _path;
         std::size_t _count;
         std::vector<ParseError> _errors;

         using return_type = std::vector<ParseError>;

         template<typename Input, typename Reader, typename Tag>
         void operator()(const lexy::error_context<Input>& context, const lexy::error<Reader, Tag>& error) {
            _iter = lexy_ext::_detail::write_error(_iter, context, error, _opts, _path);
            ++_count;

            // Convert the context location and error location into line/column information.
            auto context_location = lexy::get_input_location(context.input(), context.position());
            auto location = lexy::get_input_location(context.input(), error.position(), context_location.anchor());

            std::basic_stringstream<typename Reader::encoding::char_type> message;

            // Write the main annotation.
            if constexpr (std::is_same_v<Tag, lexy::expected_literal>) {
               auto string = lexy::_detail::make_literal_lexeme<typename Reader::encoding>(error.string(), error.length());

               message << "expected '" << string.data() << '\'';
            } else if constexpr (std::is_same_v<Tag, lexy::expected_keyword>) {
               auto string = lexy::_detail::make_literal_lexeme<typename Reader::encoding>(error.string(), error.length());

               message << "expected keyword '" << string.data() << '\'';
            } else if constexpr (std::is_same_v<Tag, lexy::expected_char_class>) {
               message << "expected " << error.name();
            } else {
               message << error.message();
            }

            _errors.push_back(
               ParseError {
                  ParseError::Type::Fatal, // TODO: distinguish recoverable errors from fatal errors
                  std::move(message.str()),
                  0, // TODO: implement proper error codes
                  ParseData {
                     context.production(),
                     context_location.line_nr(),
                     context_location.column_nr(),
                  },
                  location.line_nr(),
                  location.column_nr(),
               });
         }

         return_type finish() && {
            if (_count != 0)
               *_iter++ = '\n';
            return _errors;
         }
      };
      constexpr auto sink() const {
         return _sink { _iter, _opts, _path, 0 };
      }

      /// Specifies a path that will be printed alongside the diagnostic.
      constexpr _ReportError path(const char* path) const {
         return { _iter, _opts, path };
      }

      /// Specifies an output iterator where the errors are written to.
      template<typename OI>
      constexpr _ReportError<OI> to(OI out) const {
         return { out, _opts, _path };
      }

      /// Overrides visualization options.
      constexpr _ReportError opts(lexy::visualization_options opts) const {
         return { _iter, opts, _path };
      }
   };

   constexpr auto ReporError = _ReportError<lexy::stderr_output_iterator> {};
}