From d6b8830e03d25fdb52defd616ee9779624f52d1b Mon Sep 17 00:00:00 2001
From: Niels <niels.lohmann@gmail.com>
Date: Sat, 26 Dec 2015 13:17:32 +0100
Subject: [PATCH] more work on exceptions (#160)

---
 README.md         |  2 +-
 src/json.hpp      | 28 ++++++++--------
 src/json.hpp.re2c | 30 ++++++++---------
 test/unit.cpp     | 85 +++++++++++++++++++++++++++++++----------------
 4 files changed, 87 insertions(+), 58 deletions(-)

diff --git a/README.md b/README.md
index 42c49dd2..8c1ea449 100644
--- a/README.md
+++ b/README.md
@@ -398,7 +398,7 @@ $ make
 $ ./json_unit "*"
 
 ===============================================================================
-All tests passed (3341947 assertions in 28 test cases)
+All tests passed (3343239 assertions in 28 test cases)
 ```
 
 For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml).
diff --git a/src/json.hpp b/src/json.hpp
index 253bb311..dbb18576 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -6459,21 +6459,21 @@ class basic_json
                 case token_type::value_number:
                     return "number literal";
                 case token_type::begin_array:
-                    return "[";
+                    return "'['";
                 case token_type::begin_object:
-                    return "{";
+                    return "'{'";
                 case token_type::end_array:
-                    return "]";
+                    return "']'";
                 case token_type::end_object:
-                    return "}";
+                    return "'}'";
                 case token_type::name_separator:
-                    return ":";
+                    return "':'";
                 case token_type::value_separator:
-                    return ",";
+                    return "','";
                 case token_type::parse_error:
                     return "<parse error>";
                 case token_type::end_of_input:
-                    return "<end of input>";
+                    return "end of input";
                 default:
                 {
                     // catch non-enum values
@@ -7752,10 +7752,10 @@ basic_json_parser_64:
         {
             if (t != last_token)
             {
-                std::string error_msg = "parse error - unexpected \'";
-                error_msg += m_lexer.get_token();
-                error_msg += "\' (" + lexer::token_type_name(last_token);
-                error_msg += "); expected " + lexer::token_type_name(t);
+                std::string error_msg = "parse error - unexpected ";
+                error_msg += (last_token == lexer::token_type::parse_error ? ("'" +  m_lexer.get_token() + "'") :
+                              lexer::token_type_name(last_token));
+                error_msg += "; expected " + lexer::token_type_name(t);
                 throw std::invalid_argument(error_msg);
             }
         }
@@ -7764,9 +7764,9 @@ basic_json_parser_64:
         {
             if (t == last_token)
             {
-                std::string error_msg = "parse error - unexpected \'";
-                error_msg += m_lexer.get_token();
-                error_msg += "\'";
+                std::string error_msg = "parse error - unexpected ";
+                error_msg += (last_token == lexer::token_type::parse_error ? ("'" +  m_lexer.get_token() + "'") :
+                              lexer::token_type_name(last_token));
                 throw std::invalid_argument(error_msg);
             }
         }
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index 84ceccc3..9ef7f330 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -6441,7 +6441,7 @@ class basic_json
             return result;
         }
 
-        /// return name of values of type token_type
+        /// return name of values of type token_type (only used for errors)
         static std::string token_type_name(token_type t)
         {
             switch (t)
@@ -6459,21 +6459,21 @@ class basic_json
                 case token_type::value_number:
                     return "number literal";
                 case token_type::begin_array:
-                    return "[";
+                    return "'['";
                 case token_type::begin_object:
-                    return "{";
+                    return "'{'";
                 case token_type::end_array:
-                    return "]";
+                    return "']'";
                 case token_type::end_object:
-                    return "}";
+                    return "'}'";
                 case token_type::name_separator:
-                    return ":";
+                    return "':'";
                 case token_type::value_separator:
-                    return ",";
+                    return "','";
                 case token_type::parse_error:
                     return "<parse error>";
                 case token_type::end_of_input:
-                    return "<end of input>";
+                    return "end of input";
                 default:
                 {
                     // catch non-enum values
@@ -7031,10 +7031,10 @@ class basic_json
         {
             if (t != last_token)
             {
-                std::string error_msg = "parse error - unexpected \'";
-                error_msg += m_lexer.get_token();
-                error_msg += "\' (" + lexer::token_type_name(last_token);
-                error_msg += "); expected " + lexer::token_type_name(t);
+                std::string error_msg = "parse error - unexpected ";
+                error_msg += (last_token == lexer::token_type::parse_error ? ("'" +  m_lexer.get_token() + "'") :
+                              lexer::token_type_name(last_token));
+                error_msg += "; expected " + lexer::token_type_name(t);
                 throw std::invalid_argument(error_msg);
             }
         }
@@ -7043,9 +7043,9 @@ class basic_json
         {
             if (t == last_token)
             {
-                std::string error_msg = "parse error - unexpected \'";
-                error_msg += m_lexer.get_token();
-                error_msg += "\'";
+                std::string error_msg = "parse error - unexpected ";
+                error_msg += (last_token == lexer::token_type::parse_error ? ("'" +  m_lexer.get_token() + "'") :
+                              lexer::token_type_name(last_token));
                 throw std::invalid_argument(error_msg);
             }
         }
diff --git a/test/unit.cpp b/test/unit.cpp
index 57f6adb4..6386fd69 100644
--- a/test/unit.cpp
+++ b/test/unit.cpp
@@ -8647,14 +8647,14 @@ TEST_CASE("lexer class")
         CHECK(json::lexer::token_type_name(json::lexer::token_type::literal_null) == "null literal");
         CHECK(json::lexer::token_type_name(json::lexer::token_type::value_string) == "string literal");
         CHECK(json::lexer::token_type_name(json::lexer::token_type::value_number) == "number literal");
-        CHECK(json::lexer::token_type_name(json::lexer::token_type::begin_array) == "[");
-        CHECK(json::lexer::token_type_name(json::lexer::token_type::begin_object) == "{");
-        CHECK(json::lexer::token_type_name(json::lexer::token_type::end_array) == "]");
-        CHECK(json::lexer::token_type_name(json::lexer::token_type::end_object) == "}");
-        CHECK(json::lexer::token_type_name(json::lexer::token_type::name_separator) == ":");
-        CHECK(json::lexer::token_type_name(json::lexer::token_type::value_separator) == ",");
+        CHECK(json::lexer::token_type_name(json::lexer::token_type::begin_array) == "'['");
+        CHECK(json::lexer::token_type_name(json::lexer::token_type::begin_object) == "'{'");
+        CHECK(json::lexer::token_type_name(json::lexer::token_type::end_array) == "']'");
+        CHECK(json::lexer::token_type_name(json::lexer::token_type::end_object) == "'}'");
+        CHECK(json::lexer::token_type_name(json::lexer::token_type::name_separator) == "':'");
+        CHECK(json::lexer::token_type_name(json::lexer::token_type::value_separator) == "','");
         CHECK(json::lexer::token_type_name(json::lexer::token_type::parse_error) == "<parse error>");
-        CHECK(json::lexer::token_type_name(json::lexer::token_type::end_of_input) == "<end of input>");
+        CHECK(json::lexer::token_type_name(json::lexer::token_type::end_of_input) == "end of input");
     }
 
     SECTION("parse errors on first character")
@@ -8929,31 +8929,31 @@ TEST_CASE("parser class")
                 CHECK_THROWS_NAME(json::parser("1.").parse(), std::invalid_argument,
                                   "parse error - 1 is not a number");
                 CHECK_THROWS_NAME(json::parser("1E").parse(), std::invalid_argument,
-                                  "parse error - unexpected 'E' (<parse error>); expected <end of input>");
+                                  "parse error - unexpected 'E'; expected end of input");
                 CHECK_THROWS_NAME(json::parser("1E-").parse(), std::invalid_argument,
-                                  "parse error - unexpected 'E' (<parse error>); expected <end of input>");
+                                  "parse error - unexpected 'E'; expected end of input");
                 CHECK_THROWS_NAME(json::parser("1.E1").parse(), std::invalid_argument,
                                   "parse error - 1 is not a number");
                 CHECK_THROWS_NAME(json::parser("-1E").parse(), std::invalid_argument,
-                                  "parse error - unexpected 'E' (<parse error>); expected <end of input>");
+                                  "parse error - unexpected 'E'; expected end of input");
                 CHECK_THROWS_NAME(json::parser("-0E#").parse(), std::invalid_argument,
-                                  "parse error - unexpected 'E' (<parse error>); expected <end of input>");
+                                  "parse error - unexpected 'E'; expected end of input");
                 CHECK_THROWS_NAME(json::parser("-0E-#").parse(), std::invalid_argument,
-                                  "parse error - unexpected 'E' (<parse error>); expected <end of input>");
+                                  "parse error - unexpected 'E'; expected end of input");
                 CHECK_THROWS_NAME(json::parser("-0#").parse(), std::invalid_argument,
-                                  "parse error - unexpected '#' (<parse error>); expected <end of input>");
+                                  "parse error - unexpected '#'; expected end of input");
                 CHECK_THROWS_NAME(json::parser("-0.0:").parse(), std::invalid_argument,
-                                  "parse error - unexpected ':' (:); expected <end of input>");
+                                  "parse error - unexpected ':'; expected end of input");
                 CHECK_THROWS_NAME(json::parser("-0.0Z").parse(), std::invalid_argument,
-                                  "parse error - unexpected 'Z' (<parse error>); expected <end of input>");
+                                  "parse error - unexpected 'Z'; expected end of input");
                 CHECK_THROWS_NAME(json::parser("-0E123:").parse(), std::invalid_argument,
-                                  "parse error - unexpected ':' (:); expected <end of input>");
+                                  "parse error - unexpected ':'; expected end of input");
                 CHECK_THROWS_NAME(json::parser("-0e0-:").parse(), std::invalid_argument,
-                                  "parse error - unexpected '-' (<parse error>); expected <end of input>");
+                                  "parse error - unexpected '-'; expected end of input");
                 CHECK_THROWS_NAME(json::parser("-0e-:").parse(), std::invalid_argument,
-                                  "parse error - unexpected 'e' (<parse error>); expected <end of input>");
+                                  "parse error - unexpected 'e'; expected end of input");
                 CHECK_THROWS_NAME(json::parser("-0f").parse(), std::invalid_argument,
-                                  "parse error - unexpected 'f' (<parse error>); expected <end of input>");
+                                  "parse error - unexpected 'f'; expected end of input");
             }
         }
     }
@@ -8975,6 +8975,33 @@ TEST_CASE("parser class")
         CHECK_THROWS_AS(json::parser("1E.").parse(), std::invalid_argument);
         CHECK_THROWS_AS(json::parser("1E/").parse(), std::invalid_argument);
         CHECK_THROWS_AS(json::parser("1E:").parse(), std::invalid_argument);
+        CHECK_THROWS_NAME(json::parser("0.").parse(), std::invalid_argument,
+                          "parse error - 0 is not a number");
+        CHECK_THROWS_NAME(json::parser("-").parse(), std::invalid_argument, "parse error - unexpected '-'");
+        CHECK_THROWS_NAME(json::parser("--").parse(), std::invalid_argument,
+                          "parse error - unexpected '-'");
+        CHECK_THROWS_NAME(json::parser("-0.").parse(), std::invalid_argument,
+                          "parse error - -0 is not a number");
+        CHECK_THROWS_NAME(json::parser("-.").parse(), std::invalid_argument,
+                          "parse error - unexpected '-'");
+        CHECK_THROWS_NAME(json::parser("-:").parse(), std::invalid_argument,
+                          "parse error - unexpected '-'");
+        CHECK_THROWS_NAME(json::parser("0.:").parse(), std::invalid_argument,
+                          "parse error - 0 is not a number");
+        CHECK_THROWS_NAME(json::parser("e.").parse(), std::invalid_argument,
+                          "parse error - unexpected 'e'");
+        CHECK_THROWS_NAME(json::parser("1e.").parse(), std::invalid_argument,
+                          "parse error - unexpected 'e'; expected end of input");
+        CHECK_THROWS_NAME(json::parser("1e/").parse(), std::invalid_argument,
+                          "parse error - unexpected 'e'; expected end of input");
+        CHECK_THROWS_NAME(json::parser("1e:").parse(), std::invalid_argument,
+                          "parse error - unexpected 'e'; expected end of input");
+        CHECK_THROWS_NAME(json::parser("1E.").parse(), std::invalid_argument,
+                          "parse error - unexpected 'E'; expected end of input");
+        CHECK_THROWS_NAME(json::parser("1E/").parse(), std::invalid_argument,
+                          "parse error - unexpected 'E'; expected end of input");
+        CHECK_THROWS_NAME(json::parser("1E:").parse(), std::invalid_argument,
+                          "parse error - unexpected 'E'; expected end of input");
 
         // unexpected end of null
         CHECK_THROWS_AS(json::parser("n").parse(), std::invalid_argument);
@@ -9010,36 +9037,38 @@ TEST_CASE("parser class")
                           "parse error - unexpected 'f'");
 
         // missing/unexpected end of array
-        // TODO: Better error messages - "parse error - unexpected '" just ends
         CHECK_THROWS_AS(json::parser("[").parse(), std::invalid_argument);
         CHECK_THROWS_AS(json::parser("[1").parse(), std::invalid_argument);
         CHECK_THROWS_AS(json::parser("[1,").parse(), std::invalid_argument);
         CHECK_THROWS_AS(json::parser("[1,]").parse(), std::invalid_argument);
         CHECK_THROWS_AS(json::parser("]").parse(), std::invalid_argument);
-        CHECK_THROWS_NAME(json::parser("[").parse(), std::invalid_argument, "parse error - unexpected '");
-        CHECK_THROWS_NAME(json::parser("[1").parse(), std::invalid_argument, "parse error - unexpected '");
-        CHECK_THROWS_NAME(json::parser("[1,").parse(), std::invalid_argument, "parse error - unexpected '");
+        CHECK_THROWS_NAME(json::parser("[").parse(), std::invalid_argument,
+                          "parse error - unexpected end of input");
+        CHECK_THROWS_NAME(json::parser("[1").parse(), std::invalid_argument,
+                          "parse error - unexpected end of input; expected ']'");
+        CHECK_THROWS_NAME(json::parser("[1,").parse(), std::invalid_argument,
+                          "parse error - unexpected end of input");
         CHECK_THROWS_NAME(json::parser("[1,]").parse(), std::invalid_argument,
                           "parse error - unexpected ']'");
         CHECK_THROWS_NAME(json::parser("]").parse(), std::invalid_argument, "parse error - unexpected ']'");
 
         // missing/unexpected end of object
-        // TODO: Better error messages - "parse error - unexpected '" just ends
         CHECK_THROWS_AS(json::parser("{").parse(), std::invalid_argument);
         CHECK_THROWS_AS(json::parser("{\"foo\"").parse(), std::invalid_argument);
         CHECK_THROWS_AS(json::parser("{\"foo\":").parse(), std::invalid_argument);
         CHECK_THROWS_AS(json::parser("{\"foo\":}").parse(), std::invalid_argument);
         CHECK_THROWS_AS(json::parser("{\"foo\":1,}").parse(), std::invalid_argument);
         CHECK_THROWS_AS(json::parser("}").parse(), std::invalid_argument);
-        CHECK_THROWS_NAME(json::parser("{").parse(), std::invalid_argument, "parse error - unexpected '");
+        CHECK_THROWS_NAME(json::parser("{").parse(), std::invalid_argument,
+                          "parse error - unexpected end of input; expected string literal");
         CHECK_THROWS_NAME(json::parser("{\"foo\"").parse(), std::invalid_argument,
-                          "parse error - unexpected '");
+                          "parse error - unexpected end of input; expected ':'");
         CHECK_THROWS_NAME(json::parser("{\"foo\":").parse(), std::invalid_argument,
-                          "parse error - unexpected '");
+                          "parse error - unexpected end of input");
         CHECK_THROWS_NAME(json::parser("{\"foo\":}").parse(), std::invalid_argument,
                           "parse error - unexpected '}'");
         CHECK_THROWS_NAME(json::parser("{\"foo\":1,}").parse(), std::invalid_argument,
-                          "parse error - unexpected '}' (}); expected string literal");
+                          "parse error - unexpected '}'; expected string literal");
         CHECK_THROWS_NAME(json::parser("}").parse(), std::invalid_argument, "parse error - unexpected '}'");
 
         // missing/unexpected end of string