From 5b2ce267213cbcd900aed6fde1fda87b9d706da3 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 14 Mar 2019 00:56:35 +0900 Subject: [PATCH 1/4] refactor: remove redundant function since N region format_underline() has been implemented, overloads for 1 and 2 region(s) are not needed. --- toml/region.hpp | 149 +----------------------------------------------- 1 file changed, 3 insertions(+), 146 deletions(-) diff --git a/toml/region.hpp b/toml/region.hpp index 62a28e1..0601bce 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -237,149 +237,6 @@ struct region final : public region_base const_iterator first_, last_; }; -// to show a better error message. -inline std::string format_underline(const std::string& message, - const region_base& reg, const std::string& comment_for_underline, - std::vector helps = {}) -{ -#ifdef _WIN32 - const auto newline = "\r\n"; -#else - const char newline = '\n'; -#endif - const auto line = reg.line(); - const auto line_number = reg.line_num(); - - std::string retval; - retval += message; - retval += newline; - retval += " --> "; - retval += reg.name(); - retval += newline; - retval += ' '; - retval += line_number; - retval += " | "; - retval += line; - retval += newline; - retval += make_string(line_number.size() + 1, ' '); - retval += " | "; - retval += make_string(reg.before(), ' '); - if(reg.size() == 1) - { - // invalid - // ^------ - retval += '^'; - retval += make_string(reg.after(), '-'); - } - else - { - // invalid - // ~~~~~~~ - retval += make_string(reg.size(), '~'); - } - retval += ' '; - retval += comment_for_underline; - if(helps.size() != 0) - { - retval += newline; - retval += make_string(line_number.size() + 1, ' '); - retval += " | "; - for(const auto help : helps) - { - retval += newline; - retval += "Hint: "; - retval += help; - } - } - return retval; -} - -// to show a better error message. -inline std::string format_underline(const std::string& message, - const region_base& reg1, const std::string& comment_for_underline1, - const region_base& reg2, const std::string& comment_for_underline2, - std::vector helps = {}) -{ -#ifdef _WIN32 - const auto newline = "\r\n"; -#else - const char newline = '\n'; -#endif - const auto line1 = reg1.line(); - const auto line_number1 = reg1.line_num(); - const auto line2 = reg2.line(); - const auto line_number2 = reg2.line_num(); - const auto line_num_width = - std::max(line_number1.size(), line_number2.size()); - - std::ostringstream retval; - retval << message << newline; - retval << " --> " << reg1.name() << newline; -// --------------------------------------- - retval << ' ' << std::setw(line_num_width) << line_number1; - retval << " | " << line1 << newline; - retval << make_string(line_num_width + 1, ' '); - retval << " | "; - retval << make_string(reg1.before(), ' '); - if(reg1.size() == 1) - { - // invalid - // ^------ - retval << '^'; - retval << make_string(reg1.after(), '-'); - } - else - { - // invalid - // ~~~~~~~ - retval << make_string(reg1.size(), '~'); - } - retval << ' '; - retval << comment_for_underline1 << newline; -// --------------------------------------- - if(reg2.name() != reg1.name()) - { - retval << " --> " << reg2.name() << newline; - } - else - { - retval << " ..." << newline; - } - retval << ' ' << std::setw(line_num_width) << line_number2; - retval << " | " << line2 << newline; - retval << make_string(line_num_width + 1, ' '); - retval << " | "; - retval << make_string(reg2.before(), ' '); - if(reg2.size() == 1) - { - // invalid - // ^------ - retval << '^'; - retval << make_string(reg2.after(), '-'); - } - else - { - // invalid - // ~~~~~~~ - retval << make_string(reg2.size(), '~'); - } - retval << ' '; - retval << comment_for_underline2; - if(helps.size() != 0) - { - retval << newline; - retval << make_string(line_num_width + 1, ' '); - retval << " | "; - for(const auto help : helps) - { - retval << newline; - retval << "Hint: "; - retval << help; - } - } - return retval.str(); -} - // to show a better error message. inline std::string format_underline(const std::string& message, std::vector> reg_com, @@ -408,10 +265,11 @@ inline std::string format_underline(const std::string& message, { if(i!=0 && reg_com.at(i-1).first->name() == reg_com.at(i).first->name()) { - retval << " ..." << newline; + retval << newline << " ..." << newline; } else { + if(i != 0) {retval << newline;} retval << " --> " << reg_com.at(i).first->name() << newline; } @@ -439,7 +297,7 @@ inline std::string format_underline(const std::string& message, } retval << ' '; - retval << comment << newline; + retval << comment; } if(helps.size() != 0) @@ -457,7 +315,6 @@ inline std::string format_underline(const std::string& message, return retval.str(); } - } // detail } // toml #endif// TOML11_REGION_H From 0babe8d589a8a527b39eb23b99b3741bfa97fe1b Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 14 Mar 2019 00:59:10 +0900 Subject: [PATCH 2/4] fix: use format_underline for N regions everywhere --- toml/get.hpp | 28 ++-- toml/parser.hpp | 352 +++++++++++++++++++++++++++--------------------- toml/value.hpp | 36 +++-- 3 files changed, 236 insertions(+), 180 deletions(-) diff --git a/toml/get.hpp b/toml/get.hpp index fe51622..80336ce 100644 --- a/toml/get.hpp +++ b/toml/get.hpp @@ -210,8 +210,9 @@ T get(const value& v) { throw std::out_of_range(detail::format_underline(concat_to_string( "[erorr] toml::get specified container size is ", container.size(), - " but there are ", ar.size(), " elements in toml array."), - detail::get_region(v), "here")); + " but there are ", ar.size(), " elements in toml array."), { + {std::addressof(detail::get_region(v)), "here"} + })); } std::transform(ar.cbegin(), ar.cend(), container.begin(), [](const value& x){return ::toml::get(x);}); @@ -233,7 +234,9 @@ T get(const value& v) { throw std::out_of_range(detail::format_underline(concat_to_string( "[erorr] toml::get specified std::pair but there are ", ar.size(), - " elements in toml array."), detail::get_region(v), "here")); + " elements in toml array."), { + {std::addressof(detail::get_region(v)), "here"} + })); } return std::make_pair(::toml::get(ar.at(0)), ::toml::get(ar.at(1))); @@ -264,7 +267,9 @@ T get(const value& v) throw std::out_of_range(detail::format_underline(concat_to_string( "[erorr] toml::get specified std::tuple with ", std::tuple_size::value, "elements, but there are ", ar.size(), - " elements in toml array."), detail::get_region(v), "here")); + " elements in toml array."), { + {std::addressof(detail::get_region(v)), "here"} + })); } return detail::get_tuple_impl(ar, detail::make_index_sequence::value>{}); @@ -340,8 +345,9 @@ find(const toml::value& v, const toml::key& ky) if(tab.count(ky) == 0) { throw std::out_of_range(detail::format_underline(concat_to_string( - "[error] key \"", ky, "\" not found"), detail::get_region(v), - "in this table")); + "[error] key \"", ky, "\" not found"), { + {std::addressof(detail::get_region(v)), "in this table"} + })); } return ::toml::get(tab.at(ky)); } @@ -353,8 +359,9 @@ find(toml::value& v, const toml::key& ky) if(tab.count(ky) == 0) { throw std::out_of_range(detail::format_underline(concat_to_string( - "[error] key \"", ky, "\" not found"), detail::get_region(v), - "in this table")); + "[error] key \"", ky, "\" not found"), { + {std::addressof(detail::get_region(v)), "in this table"} + })); } return ::toml::get(tab.at(ky)); } @@ -366,8 +373,9 @@ find(toml::value&& v, const toml::key& ky) if(tab.count(ky) == 0) { throw std::out_of_range(detail::format_underline(concat_to_string( - "[error] key \"", ky, "\" not found"), detail::get_region(v), - "in this table")); + "[error] key \"", ky, "\" not found"), { + {std::addressof(detail::get_region(v)), "in this table"} + })); } return ::toml::get(std::move(tab[ky])); } diff --git a/toml/parser.hpp b/toml/parser.hpp index 26b13b1..9c1d562 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -29,13 +29,13 @@ parse_boolean(location& loc) else // internal error. { throw toml::internal_error(format_underline( - "[error] toml::parse_boolean: internal error", reg, - "invalid token")); + "[error] toml::parse_boolean: internal error", + {{std::addressof(reg), "invalid token"}})); } } loc.iter() = first; //rollback - return err(format_underline("[error] toml::parse_boolean: ", loc, - "the next token is not a boolean")); + return err(format_underline("[error] toml::parse_boolean: ", + {{std::addressof(loc), "the next token is not a boolean"}})); } template @@ -57,14 +57,14 @@ parse_binary_integer(location& loc) { throw toml::internal_error(format_underline( "[error] toml::parse_integer: internal error", - token.unwrap(), "invalid token")); + {{std::addressof(token.unwrap()), "invalid token"}})); } } return ok(std::make_pair(retval, token.unwrap())); } loc.iter() = first; - return err(format_underline("[error] toml::parse_binary_integer:", loc, - "the next token is not an integer")); + return err(format_underline("[error] toml::parse_binary_integer:", + {{std::addressof(loc), "the next token is not an integer"}})); } template @@ -84,8 +84,8 @@ parse_octal_integer(location& loc) return ok(std::make_pair(retval, token.unwrap())); } loc.iter() = first; - return err(format_underline("[error] toml::parse_octal_integer:", loc, - "the next token is not an integer")); + return err(format_underline("[error] toml::parse_octal_integer:", + {{std::addressof(loc), "the next token is not an integer"}})); } template @@ -105,8 +105,8 @@ parse_hexadecimal_integer(location& loc) return ok(std::make_pair(retval, token.unwrap())); } loc.iter() = first; - return err(format_underline("[error] toml::parse_hexadecimal_integer", loc, - "the next token is not an integer")); + return err(format_underline("[error] toml::parse_hexadecimal_integer", + {{std::addressof(loc), "the next token is not an integer"}})); } template @@ -133,8 +133,8 @@ parse_integer(location& loc) return ok(std::make_pair(retval, token.unwrap())); } loc.iter() = first; - return err(format_underline("[error] toml::parse_integer: ", loc, - "the next token is not an integer")); + return err(format_underline("[error] toml::parse_integer: ", + {{std::addressof(loc), "the next token is not an integer"}})); } template @@ -222,8 +222,8 @@ parse_floating(location& loc) return ok(std::make_pair(v, token.unwrap())); } loc.iter() = first; - return err(format_underline("[error] toml::parse_floating: ", loc, - "the next token is not a float")); + return err(format_underline("[error] toml::parse_floating: ", + {{std::addressof(loc), "the next token is not a float"}})); } template @@ -252,8 +252,9 @@ std::string read_utf8_codepoint(const region& reg, { std::cerr << format_underline("[warning] " "toml::read_utf8_codepoint: codepoints in the range " - "[0xD800, 0xDFFF] are not valid UTF-8.", - loc, "not a valid UTF-8 codepoint") << std::endl; + "[0xD800, 0xDFFF] are not valid UTF-8.", {{ + std::addressof(loc), "not a valid UTF-8 codepoint" + }}) << std::endl; } assert(codepoint < 0xD800 || 0xDFFF < codepoint); // 1110yyyy 10yxxxxx 10xxxxxx @@ -267,8 +268,8 @@ std::string read_utf8_codepoint(const region& reg, { std::cerr << format_underline("[error] " "toml::read_utf8_codepoint: input codepoint is too large to " - "decode as a unicode character.", loc, - "should be in [0x00..0x10FFFF]") << std::endl; + "decode as a unicode character.", {{std::addressof(loc), + "should be in [0x00..0x10FFFF]"}}) << std::endl; } // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx character += static_cast(0xF0| codepoint >> 18); @@ -280,7 +281,7 @@ std::string read_utf8_codepoint(const region& reg, { throw std::range_error(format_underline(concat_to_string("[error] " "input codepoint (", str, ") is too large to encode as utf-8."), - reg, "should be in [0x00..0x10FFFF]")); + {{std::addressof(reg), "should be in [0x00..0x10FFFF]"}})); } return character; } @@ -291,8 +292,8 @@ result parse_escape_sequence(location& loc) const auto first = loc.iter(); if(first == loc.end() || *first != '\\') { - return err(format_underline("[error]: toml::parse_escape_sequence: ", loc, - "the next token is not an escape sequence \"\\\"")); + return err(format_underline("[error]: toml::parse_escape_sequence: ", {{ + std::addressof(loc), "the next token is not a backslash \"\\\""}})); } ++loc.iter(); switch(*loc.iter()) @@ -314,7 +315,7 @@ result parse_escape_sequence(location& loc) { return err(format_underline("[error] parse_escape_sequence: " "invalid token found in UTF-8 codepoint uXXXX.", - loc, token.unwrap_err())); + {{std::addressof(loc), token.unwrap_err()}})); } } case 'U': @@ -327,16 +328,16 @@ result parse_escape_sequence(location& loc) { return err(format_underline("[error] parse_escape_sequence: " "invalid token found in UTF-8 codepoint Uxxxxxxxx", - loc, token.unwrap_err())); + {{std::addressof(loc), token.unwrap_err()}})); } } } const auto msg = format_underline("[error] parse_escape_sequence: " - "unknown escape sequence appeared.", loc, "escape sequence is one of" - " \\, \", b, t, n, f, r, uxxxx, Uxxxxxxxx", {"if you want to write " - "backslash as just one backslash, use literal string like:", - "regex = '<\\i\\c*\\s*>'"}); + "unknown escape sequence appeared.", {{std::addressof(loc), + "escape sequence is one of \\, \", b, t, n, f, r, uxxxx, Uxxxxxxxx"}}, + /* Hints = */{"if you want to write backslash as just one backslash, " + "use literal string like: regex = '<\\i\\c*\\s*>'"}); loc.iter() = first; return err(msg); } @@ -359,7 +360,7 @@ parse_ml_basic_string(location& loc) { throw internal_error(format_underline("[error] " "parse_ml_basic_string: invalid token", - inner_loc, "should be \"\"\"")); + {{std::addressof(inner_loc), "should be \"\"\""}})); } // immediate newline is ignored (if exists) /* discard return value */ lex_newline::invoke(inner_loc); @@ -385,7 +386,7 @@ parse_ml_basic_string(location& loc) { throw internal_error(format_underline("[error] " "parse_ml_basic_string: unexpected end of region", - inner_loc, "not sufficient token")); + {{std::addressof(inner_loc), "not sufficient token"}})); } delim = lex_ml_basic_string_delim::invoke(inner_loc); } @@ -412,7 +413,7 @@ parse_basic_string(location& loc) if(!quot) { throw internal_error(format_underline("[error] parse_basic_string: " - "invalid token", inner_loc, "should be \"")); + "invalid token", {{std::addressof(inner_loc), "should be \""}})); } std::string retval; @@ -434,7 +435,7 @@ parse_basic_string(location& loc) { throw internal_error(format_underline("[error] " "parse_ml_basic_string: unexpected end of region", - inner_loc, "not sufficient token")); + {{std::addressof(inner_loc), "not sufficient token"}})); } quot = lex_quotation_mark::invoke(inner_loc); } @@ -461,7 +462,7 @@ parse_ml_literal_string(location& loc) { throw internal_error(format_underline("[error] " "parse_ml_literal_string: invalid token", - inner_loc, "should be '''")); + {{std::addressof(inner_loc), "should be '''"}})); } // immediate newline is ignored (if exists) /* discard return value */ lex_newline::invoke(inner_loc); @@ -473,7 +474,7 @@ parse_ml_literal_string(location& loc) { throw internal_error(format_underline("[error] " "parse_ml_literal_string: invalid token", - inner_loc, "should be '''")); + {{std::addressof(inner_loc), "should be '''"}})); } return ok(std::make_pair( toml::string(body.unwrap().str(), toml::string_t::literal), @@ -500,7 +501,7 @@ parse_literal_string(location& loc) { throw internal_error(format_underline("[error] " "parse_literal_string: invalid token", - inner_loc, "should be '")); + {{std::addressof(inner_loc), "should be '"}})); } const auto body = repeat::invoke(inner_loc); @@ -510,7 +511,7 @@ parse_literal_string(location& loc) { throw internal_error(format_underline("[error] " "parse_literal_string: invalid token", - inner_loc, "should be '")); + {{std::addressof(inner_loc), "should be '"}})); } return ok(std::make_pair( toml::string(body.unwrap().str(), toml::string_t::literal), @@ -531,8 +532,8 @@ parse_string(location& loc) if(const auto rslt = parse_ml_literal_string(loc)) {return rslt;} if(const auto rslt = parse_basic_string(loc)) {return rslt;} if(const auto rslt = parse_literal_string(loc)) {return rslt;} - return err(format_underline("[error] toml::parse_string: ", loc, - "the next token is not a string")); + return err(format_underline("[error] toml::parse_string: ", + {{std::addressof(loc), "the next token is not a string"}})); } template @@ -547,21 +548,23 @@ parse_local_date(location& loc) const auto y = lex_date_fullyear::invoke(inner_loc); if(!y || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-') { + const std::string msg = y.map_err_or_else( + [](const std::string& msg) {return msg;}, "should be `-`"); + throw internal_error(format_underline("[error]: " "toml::parse_inner_local_date: invalid year format", - inner_loc, y.map_err_or_else([](const std::string& msg) { - return msg; - }, "should be `-`"))); + {{std::addressof(inner_loc), msg}})); } ++inner_loc.iter(); const auto m = lex_date_month::invoke(inner_loc); if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-') { + const std::string msg = m.map_err_or_else( + [](const std::string& msg) {return msg;}, "should be `-`"); + throw internal_error(format_underline("[error]: " "toml::parse_local_date: invalid month format", - inner_loc, m.map_err_or_else([](const std::string& msg) { - return msg; - }, "should be `-`"))); + {{std::addressof(inner_loc), msg}})); } ++inner_loc.iter(); const auto d = lex_date_mday::invoke(inner_loc); @@ -569,7 +572,7 @@ parse_local_date(location& loc) { throw internal_error(format_underline("[error]: " "toml::parse_local_date: invalid day format", - inner_loc, d.unwrap_err())); + {{std::addressof(inner_loc), d.unwrap_err()}})); } return ok(std::make_pair(local_date( static_cast(from_string(y.unwrap().str(), 0)), @@ -581,8 +584,8 @@ parse_local_date(location& loc) else { loc.iter() = first; - return err(format_underline("[error]: toml::parse_local_date: ", loc, - "the next token is not a local_date")); + return err(format_underline("[error]: toml::parse_local_date: ", + {{std::addressof(loc), "the next token is not a local_date"}})); } } @@ -598,21 +601,23 @@ parse_local_time(location& loc) const auto h = lex_time_hour::invoke(inner_loc); if(!h || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':') { + const std::string msg = h.map_err_or_else( + [](const std::string& msg) {return msg;}, "should be `:`"); + throw internal_error(format_underline("[error]: " "toml::parse_local_time: invalid year format", - inner_loc, h.map_err_or_else([](const std::string& msg) { - return msg; - }, "should be `:`"))); + {{std::addressof(inner_loc), msg}})); } ++inner_loc.iter(); const auto m = lex_time_minute::invoke(inner_loc); if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':') { + const std::string msg = m.map_err_or_else( + [](const std::string& msg) {return msg;}, "should be `:`"); + throw internal_error(format_underline("[error]: " "toml::parse_local_time: invalid month format", - inner_loc, m.map_err_or_else([](const std::string& msg) { - return msg; - }, "should be `:`"))); + {{std::addressof(inner_loc), msg}})); } ++inner_loc.iter(); const auto s = lex_time_second::invoke(inner_loc); @@ -620,7 +625,7 @@ parse_local_time(location& loc) { throw internal_error(format_underline("[error]: " "toml::parse_local_time: invalid second format", - inner_loc, s.unwrap_err())); + {{std::addressof(inner_loc), s.unwrap_err()}})); } local_time time( static_cast(from_string(h.unwrap().str(), 0)), @@ -656,7 +661,7 @@ parse_local_time(location& loc) { throw internal_error(format_underline("[error]: " "toml::parse_local_time: invalid subsecond format", - inner_loc, secfrac.unwrap_err())); + {{std::addressof(inner_loc), secfrac.unwrap_err()}})); } } return ok(std::make_pair(time, token.unwrap())); @@ -664,8 +669,8 @@ parse_local_time(location& loc) else { loc.iter() = first; - return err(format_underline("[error]: toml::parse_local_time: ", loc, - "the next token is not a local_time")); + return err(format_underline("[error]: toml::parse_local_time: ", + {{std::addressof(loc), "the next token is not a local_time"}})); } } @@ -680,25 +685,26 @@ parse_local_datetime(location& loc) const auto date = parse_local_date(inner_loc); if(!date || inner_loc.iter() == inner_loc.end()) { + const std::string msg = date.map_err_or_else( + [](const std::string& msg) {return msg;}, "date, not datetime"); + throw internal_error(format_underline("[error]: " "toml::parse_local_datetime: invalid datetime format", - inner_loc, date.map_err_or_else([](const std::string& msg){ - return msg; - }, "date, not datetime"))); + {{std::addressof(inner_loc), msg}})); } const char delim = *(inner_loc.iter()++); if(delim != 'T' && delim != 't' && delim != ' ') { throw internal_error(format_underline("[error]: " "toml::parse_local_datetime: invalid datetime format", - inner_loc, "should be `T` or ` ` (space)")); + {{std::addressof(inner_loc), "should be `T` or ` ` (space)"}})); } const auto time = parse_local_time(inner_loc); if(!time) { throw internal_error(format_underline("[error]: " "toml::parse_local_datetime: invalid datetime format", - inner_loc, "invalid time fomrat")); + {{std::addressof(inner_loc), "invalid time fomrat"}})); } return ok(std::make_pair( local_datetime(date.unwrap().first, time.unwrap().first), @@ -707,8 +713,8 @@ parse_local_datetime(location& loc) else { loc.iter() = first; - return err(format_underline("[error]: toml::parse_local_datetime: ", loc, - "the next token is not a local_datetime")); + return err(format_underline("[error]: toml::parse_local_datetime: ", + {{std::addressof(loc), "the next token is not a local_datetime"}})); } } @@ -723,11 +729,12 @@ parse_offset_datetime(location& loc) const auto datetime = parse_local_datetime(inner_loc); if(!datetime || inner_loc.iter() == inner_loc.end()) { + const std::string msg = datetime.map_err_or_else( + [](const std::string& msg){return msg;}, "date, not datetime"); + throw internal_error(format_underline("[error]: " "toml::parse_offset_datetime: invalid datetime format", - inner_loc, datetime.map_err_or_else([](const std::string& msg){ - return msg; - }, "date, not datetime"))); + {{std::addressof(inner_loc), msg}})); } time_offset offset(0, 0); if(const auto ofs = lex_time_numoffset::invoke(inner_loc)) @@ -748,7 +755,7 @@ parse_offset_datetime(location& loc) { throw internal_error(format_underline("[error]: " "toml::parse_offset_datetime: invalid datetime format", - inner_loc, "should be `Z` or `+HH:MM`")); + {{std::addressof(inner_loc), "should be `Z` or `+HH:MM`"}})); } return ok(std::make_pair(offset_datetime(datetime.unwrap().first, offset), token.unwrap())); @@ -756,8 +763,8 @@ parse_offset_datetime(location& loc) else { loc.iter() = first; - return err(format_underline("[error]: toml::parse_offset_datetime: ", loc, - "the next token is not a local_datetime")); + return err(format_underline("[error]: toml::parse_offset_datetime: ", + {{std::addressof(loc), "the next token is not a local_datetime"}})); } } @@ -778,8 +785,8 @@ parse_simple_key(location& loc) const auto reg = bare.unwrap(); return ok(std::make_pair(reg.str(), reg)); } - return err(format_underline("[error] toml::parse_simple_key: ", loc, - "the next token is not a simple key")); + return err(format_underline("[error] toml::parse_simple_key: ", + {{std::addressof(loc), "the next token is not a simple key"}})); } // dotted key become vector of keys @@ -806,7 +813,7 @@ parse_key(location& loc) { throw internal_error(format_underline("[error] " "toml::detail::parse_key: dotted key contains invalid key", - inner_loc, k.unwrap_err())); + {{std::addressof(inner_loc), k.unwrap_err()}})); } lex_ws::invoke(inner_loc); @@ -821,8 +828,8 @@ parse_key(location& loc) else { throw internal_error(format_underline("[error] toml::parse_key: " - "dotted key contains invalid key ", inner_loc, - "should be `.`")); + "dotted key contains invalid key ", + {{std::addressof(inner_loc), "should be `.`"}})); } } return ok(std::make_pair(keys, reg)); @@ -835,7 +842,8 @@ parse_key(location& loc) return ok(std::make_pair(std::vector(1, smpl.unwrap().first), smpl.unwrap().second)); } - return err(format_underline("[error] toml::parse_key: ", loc, "is not a valid key")); + return err(format_underline("[error] toml::parse_key: ", + {{std::addressof(loc), "is not a valid key"}})); } // forward-decl to implement parse_array and parse_table @@ -880,22 +888,16 @@ parse_array(location& loc) array_start_loc.iter() = first; throw syntax_error(format_underline("[error] toml::parse_array: " - "type of elements should be the same each other.", - std::vector>{ - std::make_pair( - std::addressof(array_start_loc), - std::string("array starts here") - ), - std::make_pair( + "type of elements should be the same each other.", { + {std::addressof(array_start_loc), "array starts here"}, + { std::addressof(get_region(retval.front())), - std::string("value has type ") + - stringize(retval.front().type()) - ), - std::make_pair( + "value has type " + stringize(retval.front().type()) + }, + { std::addressof(get_region(val.unwrap())), - std::string("value has different type, ") + - stringize(val.unwrap().type()) - ) + "value has different type, " + stringize(val.unwrap().type()) + } })); } retval.push_back(std::move(val.unwrap())); @@ -906,9 +908,10 @@ parse_array(location& loc) array_start_loc.iter() = first; throw syntax_error(format_underline("[error] toml::parse_array: " - "value having invalid format appeared in an array", - array_start_loc, "array starts here", - loc, "it is not a valid value.")); + "value having invalid format appeared in an array", { + {std::addressof(array_start_loc), "array starts here"}, + {std::addressof(loc), "it is not a valid value."} + })); } using lex_array_separator = sequence, character<','>>; @@ -928,14 +931,17 @@ parse_array(location& loc) array_start_loc.iter() = first; throw syntax_error(format_underline("[error] toml::parse_array:" - " missing array separator `,` after a value", - array_start_loc, "array starts here", loc, "should be `,`")); + " missing array separator `,` after a value", { + {std::addressof(array_start_loc), "array starts here"}, + {std::addressof(loc), "should be `,`"} + })); } } } loc.iter() = first; throw syntax_error(format_underline("[error] toml::parse_array: " - "array did not closed by `]`", loc, "should be closed")); + "array did not closed by `]`", + {{std::addressof(loc), "should be closed"}})); } template @@ -953,7 +959,8 @@ parse_key_value_pair(location& loc) { loc.iter() = first; msg = format_underline("[error] toml::parse_key_value_pair: " - "empty key is not allowed.", loc, "key expected before '='"); + "empty key is not allowed.", + {{std::addressof(loc), "key expected before '='"}}); } return err(std::move(msg)); } @@ -968,14 +975,16 @@ parse_key_value_pair(location& loc) if(std::find(loc.iter(), line_end, '=') != line_end) { msg = format_underline("[error] toml::parse_key_value_pair: " - "invalid format for key", loc, "invalid character in key", { - "Did you forget '.' to separate dotted-key?", + "invalid format for key", + {{std::addressof(loc), "invalid character in key"}}, + {"Did you forget '.' to separate dotted-key?", "Allowed characters for bare key are [0-9a-zA-Z_-]."}); } else // if not, the error is lack of key-value separator. { msg = format_underline("[error] toml::parse_key_value_pair: " - "missing key-value separator `=`", loc, "should be `=`"); + "missing key-value separator `=`", + {{std::addressof(loc), "should be `=`"}}); } loc.iter() = first; return err(std::move(msg)); @@ -992,8 +1001,8 @@ parse_key_value_pair(location& loc) { loc.iter() = after_kvsp; msg = format_underline("[error] toml::parse_key_value_pair: " - "missing value after key-value separator '='", loc, - "expected value, but got nothing"); + "missing value after key-value separator '='", + {{std::addressof(loc), "expected value, but got nothing"}}); } else // there is something not a comment/whitespace, so invalid format. { @@ -1122,19 +1131,26 @@ insert_nested_key(table& root, const toml::value& v, // show special err msg for conflicting table throw syntax_error(format_underline(concat_to_string( "[error] toml::insert_value: array of table (\"", - format_dotted_keys(first, last), "\") cannot insert" - "ed"), get_region(tab->at(k)), "table already defined", - get_region(v), "this conflicts with the previous table")); + format_dotted_keys(first, last), + "\") cannot be defined"), { + {std::addressof(get_region(tab->at(k))), + "table already defined"}, + {std::addressof(get_region(v)), + "this conflicts with the previous table"} + })); } else if(!(tab->at(k).is(value_t::Array))) { throw syntax_error(format_underline(concat_to_string( "[error] toml::insert_value: array of table (\"", format_dotted_keys(first, last), "\") collides with" - " existing value"), get_region(tab->at(k)), - concat_to_string("this ", tab->at(k).type(), - " value already exists"), get_region(v), - "while inserting this array-of-tables")); + " existing value"), { + {std::addressof(get_region(tab->at(k))), + concat_to_string("this ", tab->at(k).type(), + " value already exists")}, + {std::addressof(get_region(v)), + "while inserting this array-of-tables"} + })); } array& a = tab->at(k).template cast(); if(!(a.front().is(value_t::Table))) @@ -1142,10 +1158,13 @@ insert_nested_key(table& root, const toml::value& v, throw syntax_error(format_underline(concat_to_string( "[error] toml::insert_value: array of table (\"", format_dotted_keys(first, last), "\") collides with" - " existing value"), get_region(tab->at(k)), - concat_to_string("this ", tab->at(k).type(), - " value already exists"), get_region(v), - "while inserting this array-of-tables")); + " existing value"), { + {std::addressof(get_region(tab->at(k))), + concat_to_string("this ", tab->at(k).type(), + " value already exists")}, + {std::addressof(get_region(v)), + "while inserting this array-of-tables"} + })); } // avoid conflicting array of table like the following. // ```toml @@ -1167,10 +1186,13 @@ insert_nested_key(table& root, const toml::value& v, throw syntax_error(format_underline(concat_to_string( "[error] toml::insert_value: array of table (\"", format_dotted_keys(first, last), "\") collides with" - " existing array-of-tables"), get_region(tab->at(k)), - concat_to_string("this ", tab->at(k).type(), - " value has static size"), get_region(v), - "appending this to the statically sized array")); + " existing array-of-tables"), { + {std::addressof(get_region(tab->at(k))), + concat_to_string("this ", tab->at(k).type(), + " value has static size")}, + {std::addressof(get_region(v)), + "appending it to the statically sized array"} + })); } a.push_back(v); return ok(true); @@ -1191,11 +1213,14 @@ insert_nested_key(table& root, const toml::value& v, tab->at(k), first, iter, last)) { throw syntax_error(format_underline(concat_to_string( - "[error] toml::insert_value: table (\"", - format_dotted_keys(first, last), - "\") already exists."), - get_region(tab->at(k)), "table already exists here", - get_region(v), "table defined twice")); + "[error] toml::insert_value: table (\"", + format_dotted_keys(first, last), + "\") already exists."), { + {std::addressof(get_region(tab->at(k))), + "table already exists here"}, + {std::addressof(get_region(v)), + "table defined twice"} + })); } // to allow the following toml file. // [a.b.c] @@ -1217,18 +1242,23 @@ insert_nested_key(table& root, const toml::value& v, { throw syntax_error(format_underline(concat_to_string( "[error] toml::insert_value: array of tables (\"", - format_dotted_keys(first, last), "\") already exists."), - get_region(tab->at(k)), "array of tables defined here", - get_region(v), "table conflicts with the previous array" - " of table")); + format_dotted_keys(first, last), "\") already exists."), { + {std::addressof(get_region(tab->at(k))), + "array of tables defined here"}, + {std::addressof(get_region(v)), + "table conflicts with the previous array of table"} + })); } else { throw syntax_error(format_underline(concat_to_string( "[error] toml::insert_value: value (\"", - format_dotted_keys(first, last), "\") already exists."), - get_region(tab->at(k)), "value already exists here", - get_region(v), "value defined twice")); + format_dotted_keys(first, last), "\") already exists."), { + {std::addressof(get_region(tab->at(k))), + "value already exists here"}, + {std::addressof(get_region(v)), + "value defined twice"} + })); } } tab->insert(std::make_pair(k, v)); @@ -1260,9 +1290,11 @@ insert_nested_key(table& root, const toml::value& v, throw syntax_error(format_underline(concat_to_string( "[error] toml::insert_value: target (", format_dotted_keys(first, std::next(iter)), - ") is neither table nor an array of tables"), - get_region(a.back()), concat_to_string("actual type is ", - a.back().type()), get_region(v), "inserting this")); + ") is neither table nor an array of tables"), { + {std::addressof(get_region(a.back())), + concat_to_string("actual type is ", a.back().type())}, + {std::addressof(get_region(v)), "inserting this"} + })); } tab = std::addressof(a.back().template cast()); } @@ -1271,9 +1303,11 @@ insert_nested_key(table& root, const toml::value& v, throw syntax_error(format_underline(concat_to_string( "[error] toml::insert_value: target (", format_dotted_keys(first, std::next(iter)), - ") is neither table nor an array of tables"), - get_region(tab->at(k)), concat_to_string("actual type is ", - tab->at(k).type()), get_region(v), "inserting this")); + ") is neither table nor an array of tables"), { + {std::addressof(get_region(tab->at(k))), + concat_to_string("actual type is ", tab->at(k).type())}, + {std::addressof(get_region(v)), "inserting this"} + })); } } } @@ -1288,8 +1322,8 @@ parse_inline_table(location& loc) table retval; if(!(loc.iter() != loc.end() && *loc.iter() == '{')) { - return err(format_underline("[error] toml::parse_inline_table: ", loc, - "the next token is not an inline table")); + return err(format_underline("[error] toml::parse_inline_table: ", + {{std::addressof(loc), "the next token is not an inline table"}})); } ++loc.iter(); // it starts from "{". it should be formatted as inline-table @@ -1335,13 +1369,14 @@ parse_inline_table(location& loc) { throw syntax_error(format_underline("[error] " "toml:::parse_inline_table: missing table separator `,` ", - loc, "should be `,`")); + {{std::addressof(loc), "should be `,`"}})); } } } loc.iter() = first; throw syntax_error(format_underline("[error] toml::parse_inline_table: " - "inline table did not closed by `}`", loc, "should be closed")); + "inline table did not closed by `}`", + {{std::addressof(loc), "should be closed"}})); } template @@ -1350,7 +1385,8 @@ result parse_value(location& loc) const auto first = loc.iter(); if(first == loc.end()) { - return err(format_underline("[error] toml::parse_value: input is empty", loc, "")); + return err(format_underline("[error] toml::parse_value: input is empty", + {{std::addressof(loc), ""}})); } if(auto r = parse_string (loc)) {return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));} @@ -1374,7 +1410,7 @@ result parse_value(location& loc) {return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));} const auto msg = format_underline("[error] toml::parse_value: " - "unknown token appeared", loc, "unknown"); + "unknown token appeared", {{std::addressof(loc), "unknown"}}); loc.iter() = first; return err(msg); } @@ -1391,7 +1427,8 @@ parse_table_key(location& loc) if(!open || inner_loc.iter() == inner_loc.end()) { throw internal_error(format_underline("[error] " - "toml::parse_table_key: no `[`", inner_loc, "should be `[`")); + "toml::parse_table_key: no `[`", + {{std::addressof(inner_loc), "should be `[`"}})); } // to skip [ a . b . c ] // ^----------- this whitespace @@ -1400,7 +1437,8 @@ parse_table_key(location& loc) if(!keys) { throw internal_error(format_underline("[error] " - "toml::parse_table_key: invalid key", inner_loc, "not key")); + "toml::parse_table_key: invalid key", + {{std::addressof(inner_loc), "not key"}})); } // to skip [ a . b . c ] // ^-- this whitespace @@ -1409,7 +1447,8 @@ parse_table_key(location& loc) if(!close) { throw internal_error(format_underline("[error] " - "toml::parse_table_key: no `]`", inner_loc, "should be `]`")); + "toml::parse_table_key: no `]`", + {{std::addressof(inner_loc), "should be `]`"}})); } // after [table.key], newline or EOF(empty table) requried. @@ -1422,7 +1461,7 @@ parse_table_key(location& loc) { throw syntax_error(format_underline("[error] " "toml::parse_table_key: newline required after [table.key]", - loc, "expected newline")); + {{std::addressof(loc), "expected newline"}})); } } return ok(std::make_pair(keys.unwrap().first, token.unwrap())); @@ -1445,23 +1484,24 @@ parse_array_table_key(location& loc) if(!open || inner_loc.iter() == inner_loc.end()) { throw internal_error(format_underline("[error] " - "toml::parse_array_table_key: no `[[`", inner_loc, - "should be `[[`")); + "toml::parse_array_table_key: no `[[`", + {{std::addressof(inner_loc), "should be `[[`"}})); } lex_ws::invoke(inner_loc); const auto keys = parse_key(inner_loc); if(!keys) { throw internal_error(format_underline("[error] " - "toml::parse_array_table_key: invalid key", inner_loc, - "not key")); + "toml::parse_array_table_key: invalid key", + {{std::addressof(inner_loc), "not a key"}})); } lex_ws::invoke(inner_loc); const auto close = lex_array_table_close::invoke(inner_loc); if(!close) { throw internal_error(format_underline("[error] " - "toml::parse_table_key: no `]]`", inner_loc, "should be `]]`")); + "toml::parse_table_key: no `]]`", + {{std::addressof(inner_loc), "should be `]]`"}})); } // after [[table.key]], newline or EOF(empty table) requried. @@ -1472,9 +1512,9 @@ parse_array_table_key(location& loc) const auto nl = lex_newline_after_table_key::invoke(loc); if(!nl) { - throw syntax_error(format_underline("[error] " - "toml::parse_array_table_key: newline required after " - "[[table.key]]", loc, "expected newline")); + throw syntax_error(format_underline("[error] toml::" + "parse_array_table_key: newline required after [[table.key]]", + {{std::addressof(loc), "expected newline"}})); } } return ok(std::make_pair(keys.unwrap().first, token.unwrap())); @@ -1550,8 +1590,8 @@ result parse_ml_table(location& loc) const auto before = loc.iter(); lex_ws::invoke(loc); // skip whitespace const auto msg = format_underline("[error] toml::parse_table: " - "invalid line format", loc, concat_to_string( - "expected newline, but got '", show_char(*loc.iter()), "'.")); + "invalid line format", {{std::addressof(loc), concat_to_string( + "expected newline, but got '", show_char(*loc.iter()), "'.")}}); loc.iter() = before; return err(msg); } @@ -1621,7 +1661,7 @@ result parse_toml_file(location& loc) continue; } return err(format_underline("[error]: toml::parse_toml_file: " - "unknown line appeared", loc, "unknown format")); + "unknown line appeared", {{std::addressof(loc), "unknown format"}})); } return ok(data); } diff --git a/toml/value.hpp b/toml/value.hpp index 03812c7..27c2d8a 100644 --- a/toml/value.hpp +++ b/toml/value.hpp @@ -681,9 +681,11 @@ typename detail::toml_default_type::type& value::cast() & { if(T != this->type_) { - throw type_error(format_underline(concat_to_string( - "[error] toml::value bad_cast to ", T), *region_info_, - concat_to_string("the actual type is ", this->type_))); + throw type_error(detail::format_underline(concat_to_string( + "[error] toml::value bad_cast to ", T), { + {this->region_info_.get(), + concat_to_string("the actual type is ", this->type_)} + })); } return switch_cast::invoke(*this); } @@ -692,9 +694,11 @@ typename detail::toml_default_type::type const& value::cast() const& { if(T != this->type_) { - throw type_error(format_underline(concat_to_string( - "[error] toml::value bad_cast to ", T), *region_info_, - concat_to_string("the actual type is ", this->type_))); + throw type_error(detail::format_underline(concat_to_string( + "[error] toml::value bad_cast to ", T), { + {this->region_info_.get(), + concat_to_string("the actual type is ", this->type_)} + })); } return switch_cast::invoke(*this); } @@ -703,9 +707,11 @@ typename detail::toml_default_type::type&& value::cast() && { if(T != this->type_) { - throw type_error(format_underline(concat_to_string( - "[error] toml::value bad_cast to ", T), *region_info_, - concat_to_string("the actual type is ", this->type_))); + throw type_error(detail::format_underline(concat_to_string( + "[error] toml::value bad_cast to ", T), { + {this->region_info_.get(), + concat_to_string("the actual type is ", this->type_)} + })); } return switch_cast::invoke(std::move(*this)); } @@ -792,8 +798,9 @@ inline std::string format_error(const std::string& err_msg, const toml::value& v, const std::string& comment, std::vector hints = {}) { - return detail::format_underline(err_msg, detail::get_region(v), comment, - std::move(hints)); + return detail::format_underline(err_msg, { + {std::addressof(detail::get_region(v)), comment} + }, std::move(hints)); } inline std::string format_error(const std::string& err_msg, @@ -801,9 +808,10 @@ inline std::string format_error(const std::string& err_msg, const toml::value& v2, const std::string& comment2, std::vector hints = {}) { - return detail::format_underline(err_msg, detail::get_region(v1), comment1, - detail::get_region(v2), comment2, - std::move(hints)); + return detail::format_underline(err_msg, { + {std::addressof(detail::get_region(v1)), comment1}, + {std::addressof(detail::get_region(v2)), comment2} + }, std::move(hints)); } template From 61dfa4a2dce2f8a328f7ec3d5c3d1e735b8049d8 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Fri, 15 Mar 2019 12:38:37 +0900 Subject: [PATCH 3/4] feat: format any number of values into an err msg ```cpp toml::format_error("[error] message", v1, "v1", v2, "v2", ...); ``` --- toml/value.hpp | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/toml/value.hpp b/toml/value.hpp index 27c2d8a..a866b04 100644 --- a/toml/value.hpp +++ b/toml/value.hpp @@ -794,24 +794,38 @@ inline bool operator>=(const toml::value& lhs, const toml::value& rhs) return !(lhs < rhs); } -inline std::string format_error(const std::string& err_msg, - const toml::value& v, const std::string& comment, - std::vector hints = {}) +namespace detail { - return detail::format_underline(err_msg, { - {std::addressof(detail::get_region(v)), comment} - }, std::move(hints)); +inline std::string format_error_impl(const std::string& err_msg, + std::vector> val, + std::vector hints) +{ + return format_underline(err_msg, std::move(val), std::move(hints)); +} +inline std::string format_error_impl(const std::string& err_msg, + std::vector> val) +{ + return format_underline(err_msg, std::move(val)); } -inline std::string format_error(const std::string& err_msg, - const toml::value& v1, const std::string& comment1, - const toml::value& v2, const std::string& comment2, - std::vector hints = {}) +template +std::string format_error_impl(const std::string& err_msg, + std::vector> val, + const toml::value& v, const std::string& comment, + Ts&& ... args) { - return detail::format_underline(err_msg, { - {std::addressof(detail::get_region(v1)), comment1}, - {std::addressof(detail::get_region(v2)), comment2} - }, std::move(hints)); + val.push_back(std::make_pair(std::addressof(get_region(v)), comment)); + return format_error_impl(err_msg, std::move(val), std::forward(args)...); +} +} // detail + +template +std::string format_error(const std::string& err_msg, Ts&& ... args) +{ + std::vector> val; + val.reserve(sizeof...(args) / 2); + return detail::format_error_impl(err_msg, std::move(val), + std::forward(args)...); } template From 59aaaab4360bb6c03d2b3b1c8d6ed7b04dfa61d2 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Fri, 15 Mar 2019 12:40:01 +0900 Subject: [PATCH 4/4] test: add test to check format_error compiles --- tests/CMakeLists.txt | 1 + tests/test_format_error.cpp | 75 +++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 tests/test_format_error.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0664150..8db2420 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -28,6 +28,7 @@ set(TEST_NAMES test_serialize_file test_parse_unicode test_error_detection + test_format_error ) CHECK_CXX_COMPILER_FLAG("-Wall" COMPILER_SUPPORTS_WALL) diff --git a/tests/test_format_error.cpp b/tests/test_format_error.cpp new file mode 100644 index 0000000..36cb171 --- /dev/null +++ b/tests/test_format_error.cpp @@ -0,0 +1,75 @@ +#define BOOST_TEST_MODULE "test_value" +#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST +#include +#else +#define BOOST_TEST_NO_LIB +#include +#endif +#include + +// to check it successfully compiles. it does not check the formatted string. + +BOOST_AUTO_TEST_CASE(test_1_value) +{ + toml::value val(42); + + { + const std::string pretty_error = + toml::format_error("[error] test error", val, "this is a value"); + std::cout << pretty_error << std::endl; + } + + { + const std::string pretty_error = + toml::format_error("[error] test error", val, "this is a value", + std::vector{"this is a hint"}); + std::cout << pretty_error << std::endl; + } +} + +BOOST_AUTO_TEST_CASE(test_2_values) +{ + toml::value v1(42); + toml::value v2(3.14); + { + const std::string pretty_error = + toml::format_error("[error] test error with two values", + v1, "this is the answer", + v2, "this is the pi"); + std::cout << pretty_error << std::endl; + } + + { + const std::string pretty_error = + toml::format_error("[error] test error with two values", + v1, "this is the answer", + v2, "this is the pi", + std::vector{"hint"}); + std::cout << pretty_error << std::endl; + } +} + +BOOST_AUTO_TEST_CASE(test_3_values) +{ + toml::value v1(42); + toml::value v2(3.14); + toml::value v3("foo"); + { + const std::string pretty_error = + toml::format_error("[error] test error with two values", + v1, "this is the answer", + v2, "this is the pi", + v3, "this is a meta-syntactic variable"); + std::cout << pretty_error << std::endl; + } + + { + const std::string pretty_error = + toml::format_error("[error] test error with two values", + v1, "this is the answer", + v2, "this is the pi", + v3, "this is a meta-syntactic variable", + std::vector{"hint 1", "hint 2"}); + std::cout << pretty_error << std::endl; + } +}