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