1 /*
2     __ _____ _____ _____
3  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
4 |  |  |__   |  |  | | | |  version 3.9.1
5 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
6 
7 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
8 SPDX-License-Identifier: MIT
9 Copyright (c) 2013-2019 Niels Lohmann <http://nlohmann.me>.
10 
11 Permission is hereby  granted, free of charge, to any  person obtaining a copy
12 of this software and associated  documentation files (the "Software"), to deal
13 in the Software  without restriction, including without  limitation the rights
14 to  use, copy,  modify, merge,  publish, distribute,  sublicense, and/or  sell
15 copies  of  the Software,  and  to  permit persons  to  whom  the Software  is
16 furnished to do so, subject to the following conditions:
17 
18 The above copyright notice and this permission notice shall be included in all
19 copies or substantial portions of the Software.
20 
21 THE SOFTWARE  IS PROVIDED "AS  IS", WITHOUT WARRANTY  OF ANY KIND,  EXPRESS OR
22 IMPLIED,  INCLUDING BUT  NOT  LIMITED TO  THE  WARRANTIES OF  MERCHANTABILITY,
23 FITNESS FOR  A PARTICULAR PURPOSE AND  NONINFRINGEMENT. IN NO EVENT  SHALL THE
24 AUTHORS  OR COPYRIGHT  HOLDERS  BE  LIABLE FOR  ANY  CLAIM,  DAMAGES OR  OTHER
25 LIABILITY, WHETHER IN AN ACTION OF  CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 OUT OF OR IN CONNECTION WITH THE SOFTWARE  OR THE USE OR OTHER DEALINGS IN THE
27 SOFTWARE.
28 */
29 
30 #include "doctest_compatibility.h"
31 
32 #define private public
33 #include <nlohmann/json.hpp>
34 using nlohmann::json;
35 #undef private
36 
37 namespace
38 {
39 // shortcut to scan a string literal
40 json::lexer::token_type scan_string(const char* s, const bool ignore_comments = false);
scan_string(const char * s,const bool ignore_comments)41 json::lexer::token_type scan_string(const char* s, const bool ignore_comments)
42 {
43     auto ia = nlohmann::detail::input_adapter(s);
44     return nlohmann::detail::lexer<json, decltype(ia)>(std::move(ia), ignore_comments).scan();
45 }
46 }
47 
48 std::string get_error_message(const char* s, const bool ignore_comments = false);
get_error_message(const char * s,const bool ignore_comments)49 std::string get_error_message(const char* s, const bool ignore_comments)
50 {
51     auto ia = nlohmann::detail::input_adapter(s);
52     auto lexer = nlohmann::detail::lexer<json, decltype(ia)>(std::move(ia), ignore_comments);
53     lexer.scan();
54     return lexer.get_error_message();
55 }
56 
57 TEST_CASE("lexer class")
58 {
59     SECTION("scan")
60     {
61         SECTION("structural characters")
62         {
63             CHECK((scan_string("[") == json::lexer::token_type::begin_array));
64             CHECK((scan_string("]") == json::lexer::token_type::end_array));
65             CHECK((scan_string("{") == json::lexer::token_type::begin_object));
66             CHECK((scan_string("}") == json::lexer::token_type::end_object));
67             CHECK((scan_string(",") == json::lexer::token_type::value_separator));
68             CHECK((scan_string(":") == json::lexer::token_type::name_separator));
69         }
70 
71         SECTION("literal names")
72         {
73             CHECK((scan_string("null") == json::lexer::token_type::literal_null));
74             CHECK((scan_string("true") == json::lexer::token_type::literal_true));
75             CHECK((scan_string("false") == json::lexer::token_type::literal_false));
76         }
77 
78         SECTION("numbers")
79         {
80             CHECK((scan_string("0") == json::lexer::token_type::value_unsigned));
81             CHECK((scan_string("1") == json::lexer::token_type::value_unsigned));
82             CHECK((scan_string("2") == json::lexer::token_type::value_unsigned));
83             CHECK((scan_string("3") == json::lexer::token_type::value_unsigned));
84             CHECK((scan_string("4") == json::lexer::token_type::value_unsigned));
85             CHECK((scan_string("5") == json::lexer::token_type::value_unsigned));
86             CHECK((scan_string("6") == json::lexer::token_type::value_unsigned));
87             CHECK((scan_string("7") == json::lexer::token_type::value_unsigned));
88             CHECK((scan_string("8") == json::lexer::token_type::value_unsigned));
89             CHECK((scan_string("9") == json::lexer::token_type::value_unsigned));
90 
91             CHECK((scan_string("-0") == json::lexer::token_type::value_integer));
92             CHECK((scan_string("-1") == json::lexer::token_type::value_integer));
93 
94             CHECK((scan_string("1.1") == json::lexer::token_type::value_float));
95             CHECK((scan_string("-1.1") == json::lexer::token_type::value_float));
96             CHECK((scan_string("1E10") == json::lexer::token_type::value_float));
97         }
98 
99         SECTION("whitespace")
100         {
101             // result is end_of_input, because not token is following
102             CHECK((scan_string(" ") == json::lexer::token_type::end_of_input));
103             CHECK((scan_string("\t") == json::lexer::token_type::end_of_input));
104             CHECK((scan_string("\n") == json::lexer::token_type::end_of_input));
105             CHECK((scan_string("\r") == json::lexer::token_type::end_of_input));
106             CHECK((scan_string(" \t\n\r\n\t ") == json::lexer::token_type::end_of_input));
107         }
108     }
109 
110     SECTION("token_type_name")
111     {
112         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::uninitialized)) == "<uninitialized>"));
113         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::literal_true)) == "true literal"));
114         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::literal_false)) == "false literal"));
115         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::literal_null)) == "null literal"));
116         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::value_string)) == "string literal"));
117         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::value_unsigned)) == "number literal"));
118         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::value_integer)) == "number literal"));
119         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::value_float)) == "number literal"));
120         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::begin_array)) == "'['"));
121         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::begin_object)) == "'{'"));
122         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::end_array)) == "']'"));
123         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::end_object)) == "'}'"));
124         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::name_separator)) == "':'"));
125         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::value_separator)) == "','"));
126         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::parse_error)) == "<parse error>"));
127         CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::end_of_input)) == "end of input"));
128     }
129 
130     SECTION("parse errors on first character")
131     {
132         for (int c = 1; c < 128; ++c)
133         {
134             // create string from the ASCII code
135             const auto s = std::string(1, static_cast<char>(c));
136             // store scan() result
137             const auto res = scan_string(s.c_str());
138 
139             CAPTURE(s);
140 
141             switch (c)
142             {
143                 // single characters that are valid tokens
144                 case ('['):
145                 case (']'):
146                 case ('{'):
147                 case ('}'):
148                 case (','):
149                 case (':'):
150                 case ('0'):
151                 case ('1'):
152                 case ('2'):
153                 case ('3'):
154                 case ('4'):
155                 case ('5'):
156                 case ('6'):
157                 case ('7'):
158                 case ('8'):
159                 case ('9'):
160                 {
161                     CHECK((res != json::lexer::token_type::parse_error));
162                     break;
163                 }
164 
165                 // whitespace
166                 case (' '):
167                 case ('\t'):
168                 case ('\n'):
169                 case ('\r'):
170                 {
171                     CHECK((res == json::lexer::token_type::end_of_input));
172                     break;
173                 }
174 
175                 // anything else is not expected
176                 default:
177                 {
178                     CHECK((res == json::lexer::token_type::parse_error));
179                     break;
180                 }
181             }
182         }
183     }
184 
185     SECTION("very large string")
186     {
187         // strings larger than 1024 bytes yield a resize of the lexer's yytext buffer
188         std::string s("\"");
189         s += std::string(2048, 'x');
190         s += "\"";
191         CHECK((scan_string(s.c_str()) == json::lexer::token_type::value_string));
192     }
193 
194     SECTION("fail on comments")
195     {
196         CHECK((scan_string("/", false) == json::lexer::token_type::parse_error));
197         CHECK(get_error_message("/", false) == "invalid literal");
198 
199         CHECK((scan_string("/!", false) == json::lexer::token_type::parse_error));
200         CHECK(get_error_message("/!", false) == "invalid literal");
201         CHECK((scan_string("/*", false) == json::lexer::token_type::parse_error));
202         CHECK(get_error_message("/*", false) == "invalid literal");
203         CHECK((scan_string("/**", false) == json::lexer::token_type::parse_error));
204         CHECK(get_error_message("/**", false) == "invalid literal");
205 
206         CHECK((scan_string("//", false) == json::lexer::token_type::parse_error));
207         CHECK(get_error_message("//", false) == "invalid literal");
208         CHECK((scan_string("/**/", false) == json::lexer::token_type::parse_error));
209         CHECK(get_error_message("/**/", false) == "invalid literal");
210         CHECK((scan_string("/** /", false) == json::lexer::token_type::parse_error));
211         CHECK(get_error_message("/** /", false) == "invalid literal");
212 
213         CHECK((scan_string("/***/", false) == json::lexer::token_type::parse_error));
214         CHECK(get_error_message("/***/", false) == "invalid literal");
215         CHECK((scan_string("/* true */", false) == json::lexer::token_type::parse_error));
216         CHECK(get_error_message("/* true */", false) == "invalid literal");
217         CHECK((scan_string("/*/**/", false) == json::lexer::token_type::parse_error));
218         CHECK(get_error_message("/*/**/", false) == "invalid literal");
219         CHECK((scan_string("/*/* */", false) == json::lexer::token_type::parse_error));
220         CHECK(get_error_message("/*/* */", false) == "invalid literal");
221     }
222 
223     SECTION("ignore comments")
224     {
225         CHECK((scan_string("/", true) == json::lexer::token_type::parse_error));
226         CHECK(get_error_message("/", true) == "invalid comment; expecting '/' or '*' after '/'");
227 
228         CHECK((scan_string("/!", true) == json::lexer::token_type::parse_error));
229         CHECK(get_error_message("/!", true) == "invalid comment; expecting '/' or '*' after '/'");
230         CHECK((scan_string("/*", true) == json::lexer::token_type::parse_error));
231         CHECK(get_error_message("/*", true) == "invalid comment; missing closing '*/'");
232         CHECK((scan_string("/**", true) == json::lexer::token_type::parse_error));
233         CHECK(get_error_message("/**", true) == "invalid comment; missing closing '*/'");
234 
235         CHECK((scan_string("//", true) == json::lexer::token_type::end_of_input));
236         CHECK((scan_string("/**/", true) == json::lexer::token_type::end_of_input));
237         CHECK((scan_string("/** /", true) == json::lexer::token_type::parse_error));
238         CHECK(get_error_message("/** /", true) == "invalid comment; missing closing '*/'");
239 
240         CHECK((scan_string("/***/", true) == json::lexer::token_type::end_of_input));
241         CHECK((scan_string("/* true */", true) == json::lexer::token_type::end_of_input));
242         CHECK((scan_string("/*/**/", true) == json::lexer::token_type::end_of_input));
243         CHECK((scan_string("/*/* */", true) == json::lexer::token_type::end_of_input));
244 
245         CHECK((scan_string("//\n//\n", true) == json::lexer::token_type::end_of_input));
246         CHECK((scan_string("/**//**//**/", true) == json::lexer::token_type::end_of_input));
247     }
248 }
249