From 5be1c00730e6f4dfd9c1628bb6650f1fac932ed7 Mon Sep 17 00:00:00 2001 From: Zach Laine Date: Thu, 27 Sep 2018 21:04:49 -0500 Subject: [PATCH] Emit parse errors via callback instead of just dumping them to the console. --- include/boost/yaml/json.hpp | 4 +- .../boost/yaml/parser/x3_error_reporting.hpp | 141 ++++++++++-------- src/json.cpp | 8 +- test/json_parser.cpp | 3 +- 4 files changed, 85 insertions(+), 71 deletions(-) diff --git a/include/boost/yaml/json.hpp b/include/boost/yaml/json.hpp index ceba55be..9e097453 100644 --- a/include/boost/yaml/json.hpp +++ b/include/boost/yaml/json.hpp @@ -303,7 +303,9 @@ namespace boost { namespace json { return static_cast *>(v.ptr_.get())->value_; } - boost::optional parse(boost::string_view const & str); + boost::optional parse( + boost::string_view const & str, + std::function parse_error); namespace detail { diff --git a/include/boost/yaml/parser/x3_error_reporting.hpp b/include/boost/yaml/parser/x3_error_reporting.hpp index 5c09cd37..f6bd20fc 100644 --- a/include/boost/yaml/parser/x3_error_reporting.hpp +++ b/include/boost/yaml/parser/x3_error_reporting.hpp @@ -8,12 +8,9 @@ #ifndef BOOST_YAML_PARSER_X3_ERROR_REPORTING_HPP #define BOOST_YAML_PARSER_X3_ERROR_REPORTING_HPP -// TODO -#include - #include #include -#include +#include // Clang-style error handling utilities @@ -40,87 +37,88 @@ namespace boost { namespace yaml { }; template - class x3_error_handler + struct x3_error_handler { - public: - typedef Iterator iterator_type; + using iterator_type = Iterator; + + static_assert( + std::is_integral())>::value); + static_assert(sizeof(decltype(*std::declval())) == 4); x3_error_handler( Iterator first, Iterator last, - std::ostream & err_out, - std::string file = "", - int tabs = 4) : - err_out(err_out), - file(file), - tabs(tabs), - pos_cache(first, last) + std::function error_fn, + std::string file = "") : + error_fn_(error_fn), + file_(file), + pos_cache_(first, last) {} - typedef void result_type; + using result_type = void; - void - operator()(Iterator err_pos, std::string const & error_message) const; + void operator()(Iterator err_pos, std::string const & error_message); void operator()( Iterator err_first, Iterator err_last, - std::string const & error_message) const; - void operator()( - spirit::x3::position_tagged pos, std::string const & message) const + std::string const & error_message); + void + operator()(spirit::x3::position_tagged pos, std::string const & message) { - auto where = pos_cache.position_of(pos); + auto where = pos_cache_.position_of(pos); (*this)(where.begin(), where.end(), message); + emit(); } template void tag(AST & ast, Iterator first, Iterator last) { - return pos_cache.annotate(ast, first, last); + return pos_cache_.annotate(ast, first, last); } boost::iterator_range position_of(spirit::x3::position_tagged pos) const { - return pos_cache.position_of(pos); + return pos_cache_.position_of(pos); } spirit::x3::position_cache> const & get_position_cache() const { - return pos_cache; + return pos_cache_; } private: - void print_file_line(std::size_t line) const; - void print_line(Iterator line_start, Iterator last) const; - void - print_indicator(Iterator & line_start, Iterator last, char ind) const; - void skip_whitespace(Iterator & err_pos, Iterator last) const; - void skip_non_whitespace(Iterator & err_pos, Iterator last) const; - Iterator get_line_start(Iterator first, Iterator pos) const; - std::size_t position(Iterator i) const; + void print_file_line(std::size_t line); + void print_line(Iterator line_start, Iterator last); + void print_indicator(Iterator & line_start, Iterator last, char ind); + void skip_whitespace(Iterator & err_pos, Iterator last); + void skip_non_whitespace(Iterator & err_pos, Iterator last); + Iterator get_line_start(Iterator first, Iterator pos); + std::size_t position(Iterator i); + void emit(); - std::ostream & err_out; - std::string file; - int tabs; - spirit::x3::position_cache> pos_cache; + std::stringstream stream_; + std::function error_fn_; + std::string file_; + spirit::x3::position_cache> pos_cache_; }; template - void x3_error_handler::print_file_line(std::size_t line) const + void x3_error_handler::print_file_line(std::size_t line) { - if (file != "") { - err_out << "In file " << file << ", "; + if (file_ != "") { + stream_ << "In file " << file_ << ", "; } else { - err_out << "In "; + stream_ << "In "; } - err_out << "line " << line << ':' << std::endl; + stream_ << "line " << line << ':' << std::endl; } template void - x3_error_handler::print_line(Iterator start, Iterator last) const + x3_error_handler::print_line(Iterator start, Iterator last) { auto end = start; while (end != last) { @@ -130,30 +128,31 @@ namespace boost { namespace yaml { else ++end; } - typedef typename std::iterator_traits::value_type char_type; - std::basic_string line{start, end}; - err_out << locale::conv::utf_to_utf(line) << std::endl; + std::string line( + text::utf8::make_from_utf32_iterator(start, start, last), + text::utf8::make_from_utf32_iterator(start, last, last)); + stream_ << line << std::endl; } template void x3_error_handler::print_indicator( - Iterator & start, Iterator last, char ind) const + Iterator & start, Iterator last, char ind) { for (; start != last; ++start) { auto c = *start; if (c == '\r' || c == '\n') break; else if (c == '\t') - for (int i = 0; i < tabs; ++i) - err_out << ind; + for (int i = 0; i < 4; ++i) + stream_ << ind; else - err_out << ind; + stream_ << ind; } } template void x3_error_handler::skip_whitespace( - Iterator & err_pos, Iterator last) const + Iterator & err_pos, Iterator last) { // make sure err_pos does not point to white space while (err_pos != last) { @@ -167,7 +166,7 @@ namespace boost { namespace yaml { template void x3_error_handler::skip_non_whitespace( - Iterator & err_pos, Iterator last) const + Iterator & err_pos, Iterator last) { // make sure err_pos does not point to white space while (err_pos != last) { @@ -181,7 +180,7 @@ namespace boost { namespace yaml { template inline Iterator - x3_error_handler::get_line_start(Iterator first, Iterator pos) const + x3_error_handler::get_line_start(Iterator first, Iterator pos) { Iterator latest = first; for (Iterator i = first; i != pos; ++i) @@ -191,12 +190,12 @@ namespace boost { namespace yaml { } template - std::size_t x3_error_handler::position(Iterator i) const + std::size_t x3_error_handler::position(Iterator i) { std::size_t line{1}; typename std::iterator_traits::value_type prev{0}; - for (Iterator pos = pos_cache.first(); pos != i; ++pos) { + for (Iterator pos = pos_cache_.first(); pos != i; ++pos) { auto c = *pos; switch (c) { case '\n': @@ -213,40 +212,50 @@ namespace boost { namespace yaml { } template - void x3_error_handler:: - operator()(Iterator err_pos, std::string const & error_message) const + void x3_error_handler::emit() { - Iterator first = pos_cache.first(); - Iterator last = pos_cache.last(); + if (error_fn_) + error_fn_(stream_.str()); + stream_.clear(); + } + + template + void x3_error_handler:: + operator()(Iterator err_pos, std::string const & error_message) + { + Iterator first = pos_cache_.first(); + Iterator last = pos_cache_.last(); // make sure err_pos does not point to white space skip_whitespace(err_pos, last); print_file_line(position(err_pos)); - err_out << error_message << std::endl; + stream_ << error_message << std::endl; Iterator start = get_line_start(first, err_pos); if (start != first) ++start; print_line(start, last); print_indicator(start, err_pos, '_'); - err_out << "^_" << std::endl; + stream_ << "^_" << std::endl; + + emit(); } template void x3_error_handler::operator()( Iterator err_first, Iterator err_last, - std::string const & error_message) const + std::string const & error_message) { - Iterator first = pos_cache.first(); - Iterator last = pos_cache.last(); + Iterator first = pos_cache_.first(); + Iterator last = pos_cache_.last(); // make sure err_pos does not point to white space skip_whitespace(err_first, last); print_file_line(position(err_first)); - err_out << error_message << std::endl; + stream_ << error_message << std::endl; Iterator start = get_line_start(first, err_first); if (start != first) @@ -254,7 +263,9 @@ namespace boost { namespace yaml { print_line(start, last); print_indicator(start, err_first, ' '); print_indicator(start, err_last, '~'); - err_out << " <<-- Here" << std::endl; + stream_ << " <<-- Here" << std::endl; + + emit(); } }} diff --git a/src/json.cpp b/src/json.cpp index f80e9d9e..f5adbeb5 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -130,16 +130,16 @@ namespace boost { namespace json { struct value_parser_struct : yaml::x3_error_handler_base {}; - boost::optional parse(boost::string_view const & str) + boost::optional parse( + boost::string_view const & str, + std::function parse_error) { auto const range = boost::text::make_to_utf32_range(str); using iter_t = decltype(range.begin()); auto first = range.begin(); auto const last = range.end(); - // TODO: Emit the error via a callback instead of just dumping it to - // the console. - yaml::x3_error_handler error_handler{first, last, std::cout}; + yaml::x3_error_handler error_handler{first, last, parse_error}; uint32_t first_surrogate = 0; auto parser = x3::with( diff --git a/test/json_parser.cpp b/test/json_parser.cpp index 30b97abe..5b4677db 100644 --- a/test/json_parser.cpp +++ b/test/json_parser.cpp @@ -9,7 +9,8 @@ int main() break; boost::optional value; - if ((value = boost::json::parse(str))) { + if ((value = boost::json::parse( + str, [](std::string const & msg) { std::cout << msg; }))) { std::cout << "Parsed value:\n"; std::cout << *value << std::endl; }